mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 09:14:27 +08:00
add trend analysis api
This commit is contained in:
@@ -274,6 +274,7 @@ func startWebServer(c *cli.Context) error {
|
|||||||
apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler))
|
apiV1Route.GET("/transactions/list.json", bindApi(api.Transactions.TransactionListHandler))
|
||||||
apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionMonthListHandler))
|
apiV1Route.GET("/transactions/list/by_month.json", bindApi(api.Transactions.TransactionMonthListHandler))
|
||||||
apiV1Route.GET("/transactions/statistics.json", bindApi(api.Transactions.TransactionStatisticsHandler))
|
apiV1Route.GET("/transactions/statistics.json", bindApi(api.Transactions.TransactionStatisticsHandler))
|
||||||
|
apiV1Route.GET("/transactions/statistics/trends.json", bindApi(api.Transactions.TransactionStatisticsTrendsHandler))
|
||||||
apiV1Route.GET("/transactions/amounts.json", bindApi(api.Transactions.TransactionAmountsHandler))
|
apiV1Route.GET("/transactions/amounts.json", bindApi(api.Transactions.TransactionAmountsHandler))
|
||||||
apiV1Route.GET("/transactions/get.json", bindApi(api.Transactions.TransactionGetHandler))
|
apiV1Route.GET("/transactions/get.json", bindApi(api.Transactions.TransactionGetHandler))
|
||||||
apiV1Route.POST("/transactions/add.json", bindApi(api.Transactions.TransactionCreateHandler))
|
apiV1Route.POST("/transactions/add.json", bindApi(api.Transactions.TransactionCreateHandler))
|
||||||
|
|||||||
@@ -272,6 +272,64 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.Context) (any, *e
|
|||||||
return statisticResp, nil
|
return statisticResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticsTrendsHandler returns transaction statistics trends of current user
|
||||||
|
func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
|
var statisticTrendsReq models.TransactionStatisticTrendsRequest
|
||||||
|
err := c.ShouldBindQuery(&statisticTrendsReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionStatisticsTrendsHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
utcOffset, err := c.GetClientTimezoneOffset()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionStatisticsTrendsHandler] cannot get client timezone offset, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
startYear, startMonth, endYear, endMonth, err := statisticTrendsReq.GetNumericYearMonthRange()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[transactions.TransactionStatisticsTrendsHandler] cannot parse year month, because %s", err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorfWithRequestId(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
statisticTrendsResp := make(models.TransactionStatisticTrendsItemSlice, 0, len(allMonthlyTotalAmounts))
|
||||||
|
|
||||||
|
for yearMonth, monthlyTotalAmounts := range allMonthlyTotalAmounts {
|
||||||
|
monthlyStatisticResp := &models.TransactionStatisticTrendsItem{
|
||||||
|
Year: yearMonth / 100,
|
||||||
|
Month: yearMonth % 100,
|
||||||
|
Items: make([]*models.TransactionStatisticResponseItem, len(monthlyTotalAmounts)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(monthlyTotalAmounts); i++ {
|
||||||
|
totalAmountItem := monthlyTotalAmounts[i]
|
||||||
|
monthlyStatisticResp.Items[i] = &models.TransactionStatisticResponseItem{
|
||||||
|
CategoryId: totalAmountItem.CategoryId,
|
||||||
|
AccountId: totalAmountItem.AccountId,
|
||||||
|
TotalAmount: totalAmountItem.Amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statisticTrendsResp = append(statisticTrendsResp, monthlyStatisticResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(statisticTrendsResp)
|
||||||
|
|
||||||
|
return statisticTrendsResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsHandler returns transaction amounts of current user
|
// TransactionAmountsHandler returns transaction amounts of current user
|
||||||
func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (any, *errs.Error) {
|
func (a *TransactionsApi) TransactionAmountsHandler(c *core.Context) (any, *errs.Error) {
|
||||||
var transactionAmountsReq models.TransactionAmountsRequest
|
var transactionAmountsReq models.TransactionAmountsRequest
|
||||||
|
|||||||
+45
-24
@@ -1,9 +1,7 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
@@ -140,6 +138,12 @@ type TransactionStatisticRequest struct {
|
|||||||
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticTrendsRequest represents all parameters of transaction statistic trends request
|
||||||
|
type TransactionStatisticTrendsRequest struct {
|
||||||
|
YearMonthRangeRequest
|
||||||
|
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsRequest represents all parameters of transaction amounts request
|
// TransactionAmountsRequest represents all parameters of transaction amounts request
|
||||||
type TransactionAmountsRequest struct {
|
type TransactionAmountsRequest struct {
|
||||||
Query string `form:"query"`
|
Query string `form:"query"`
|
||||||
@@ -219,7 +223,7 @@ type TransactionInfoPageWrapperResponse2 struct {
|
|||||||
TotalCount int64 `json:"totalCount"`
|
TotalCount int64 `json:"totalCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionStatisticResponse represents an item of transaction amounts
|
// TransactionStatisticResponse represents transaction statistic response
|
||||||
type TransactionStatisticResponse struct {
|
type TransactionStatisticResponse struct {
|
||||||
StartTime int64 `json:"startTime"`
|
StartTime int64 `json:"startTime"`
|
||||||
EndTime int64 `json:"endTime"`
|
EndTime int64 `json:"endTime"`
|
||||||
@@ -233,6 +237,13 @@ type TransactionStatisticResponseItem struct {
|
|||||||
TotalAmount int64 `json:"amount"`
|
TotalAmount int64 `json:"amount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticTrendsItem represents the data within each statistic interval
|
||||||
|
type TransactionStatisticTrendsItem struct {
|
||||||
|
Year int32 `json:"year"`
|
||||||
|
Month int32 `json:"month"`
|
||||||
|
Items []*TransactionStatisticResponseItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsResponseItem represents an item of transaction amounts
|
// TransactionAmountsResponseItem represents an item of transaction amounts
|
||||||
type TransactionAmountsResponseItem struct {
|
type TransactionAmountsResponseItem struct {
|
||||||
StartTime int64 `json:"startTime"`
|
StartTime int64 `json:"startTime"`
|
||||||
@@ -372,33 +383,21 @@ func (t *TransactionAmountsRequest) GetTransactionAmountsRequestItems() ([]*Tran
|
|||||||
return requestItems, nil
|
return requestItems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStartTimeAndEndTime returns start unix time and end unix time by request parameter
|
// GetNumericYearMonthRange returns numeric start year, start month, end year and end month
|
||||||
func (t *YearMonthRangeRequest) GetStartTimeAndEndTime(utcOffset int16) (int64, int64, error) {
|
func (t *YearMonthRangeRequest) GetNumericYearMonthRange() (int32, int32, int32, int32, error) {
|
||||||
startUnixTime := int64(0)
|
startYear, startMonth, err := utils.ParseNumericYearMonth(t.StartYearMonth)
|
||||||
endUnixTime := time.Now().Unix()
|
|
||||||
|
|
||||||
if t.StartYearMonth != "" {
|
if err != nil {
|
||||||
startTime, err := utils.ParseFromShortDateTime(fmt.Sprintf("%s-1 0:0:0", t.StartYearMonth), utcOffset)
|
return 0, 0, 0, 0, err
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
startUnixTime = startTime.Unix()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.EndYearMonth != "" {
|
endYear, endMonth, err := utils.ParseNumericYearMonth(t.EndYearMonth)
|
||||||
endTime, err := utils.ParseFromShortDateTime(fmt.Sprintf("%s-1 0:0:0", t.EndYearMonth), utcOffset)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, 0, 0, err
|
||||||
}
|
|
||||||
|
|
||||||
endTime = endTime.AddDate(0, 1, 0)
|
|
||||||
endUnixTime = endTime.Unix() - 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return startUnixTime, endUnixTime, nil
|
return startYear, startMonth, endYear, endMonth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionInfoResponseSlice represents the slice data structure of TransactionInfoResponse
|
// TransactionInfoResponseSlice represents the slice data structure of TransactionInfoResponse
|
||||||
@@ -423,6 +422,28 @@ func (s TransactionInfoResponseSlice) Less(i, j int) bool {
|
|||||||
return s[i].Id > s[j].Id
|
return s[i].Id > s[j].Id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticTrendsItemSlice represents the slice data structure of TransactionStatisticTrendsItem
|
||||||
|
type TransactionStatisticTrendsItemSlice []*TransactionStatisticTrendsItem
|
||||||
|
|
||||||
|
// Len returns the count of items
|
||||||
|
func (s TransactionStatisticTrendsItemSlice) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps two items
|
||||||
|
func (s TransactionStatisticTrendsItemSlice) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less reports whether the first item is less than the second one
|
||||||
|
func (s TransactionStatisticTrendsItemSlice) Less(i, j int) bool {
|
||||||
|
if s[i].Year != s[j].Year {
|
||||||
|
return s[i].Year < s[j].Year
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[i].Month < s[j].Month
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsResponseItemAmountInfoSlice represents the slice data structure of TransactionAmountsResponseItemAmountInfo
|
// TransactionAmountsResponseItemAmountInfoSlice represents the slice data structure of TransactionAmountsResponseItemAmountInfo
|
||||||
type TransactionAmountsResponseItemAmountInfoSlice []*TransactionAmountsResponseItemAmountInfo
|
type TransactionAmountsResponseItemAmountInfoSlice []*TransactionAmountsResponseItemAmountInfo
|
||||||
|
|
||||||
|
|||||||
@@ -1200,6 +1200,109 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c *co
|
|||||||
return transactionTotalAmounts, nil
|
return transactionTotalAmounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAccountsAndCategoriesMonthlyIncomeAndExpense returns the every accounts monthly income and expense amount by specific date range
|
||||||
|
func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c *core.Context, uid int64, startYear int32, startMonth int32, endYear int32, endMonth int32, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
|
||||||
|
if uid <= 0 {
|
||||||
|
return nil, errs.ErrUserIdInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
clientLocation := time.FixedZone("Client Timezone", int(utcOffset)*60)
|
||||||
|
startTransactionTime, _, err := utils.GetTransactionTimeRangeByYearMonth(startYear, startMonth)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrSystemError
|
||||||
|
}
|
||||||
|
|
||||||
|
_, endTransactionTime, err := utils.GetTransactionTimeRangeByYearMonth(endYear, endMonth)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrSystemError
|
||||||
|
}
|
||||||
|
|
||||||
|
condition := "uid=? AND deleted=? AND (type=? OR type=?) AND transaction_time>=? AND transaction_time<=?"
|
||||||
|
conditionParams := make([]any, 0, 4)
|
||||||
|
conditionParams = append(conditionParams, uid)
|
||||||
|
conditionParams = append(conditionParams, false)
|
||||||
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
||||||
|
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_EXPENSE)
|
||||||
|
|
||||||
|
minTransactionTime := startTransactionTime
|
||||||
|
maxTransactionTime := endTransactionTime
|
||||||
|
var allTransactions []*models.Transaction
|
||||||
|
|
||||||
|
for maxTransactionTime > 0 {
|
||||||
|
var transactions []*models.Transaction
|
||||||
|
|
||||||
|
finalConditionParams := make([]any, 0, 6)
|
||||||
|
finalConditionParams = append(finalConditionParams, conditionParams...)
|
||||||
|
finalConditionParams = append(finalConditionParams, minTransactionTime)
|
||||||
|
finalConditionParams = append(finalConditionParams, maxTransactionTime)
|
||||||
|
|
||||||
|
err := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(condition, finalConditionParams...).Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactions = append(allTransactions, transactions...)
|
||||||
|
|
||||||
|
if len(transactions) < pageCountForLoadTransactionAmounts {
|
||||||
|
maxTransactionTime = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTransactionTime = transactions[len(transactions)-1].TransactionTime - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
startYearMonth := startYear*100 + startMonth
|
||||||
|
endYearMonth := endYear*100 + endMonth
|
||||||
|
transactionsMonthlyAmountsMap := make(map[string]*models.Transaction)
|
||||||
|
transactionsMonthlyAmounts := make(map[int32][]*models.Transaction)
|
||||||
|
|
||||||
|
for i := 0; i < len(allTransactions); i++ {
|
||||||
|
transaction := allTransactions[i]
|
||||||
|
timeZone := clientLocation
|
||||||
|
|
||||||
|
if useTransactionTimezone {
|
||||||
|
timeZone = time.FixedZone("Transaction Timezone", int(transaction.TimezoneUtcOffset)*60)
|
||||||
|
}
|
||||||
|
|
||||||
|
yearMonth := utils.FormatUnixTimeToNumericYearMonth(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), timeZone)
|
||||||
|
|
||||||
|
if yearMonth < startYearMonth || yearMonth > endYearMonth {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
groupKey := fmt.Sprintf("%d_%d_%d", yearMonth, transaction.CategoryId, transaction.AccountId)
|
||||||
|
transactionAmounts, exists := transactionsMonthlyAmountsMap[groupKey]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
transactionAmounts = &models.Transaction{
|
||||||
|
CategoryId: transaction.CategoryId,
|
||||||
|
AccountId: transaction.AccountId,
|
||||||
|
}
|
||||||
|
transactionsMonthlyAmountsMap[groupKey] = transactionAmounts
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionAmounts.Amount += transaction.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
for groupKey, transaction := range transactionsMonthlyAmountsMap {
|
||||||
|
groupKeyParts := strings.Split(groupKey, "_")
|
||||||
|
yearMonth, _ := utils.StringToInt32(groupKeyParts[0])
|
||||||
|
monthlyAmounts, exists := transactionsMonthlyAmounts[yearMonth]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
monthlyAmounts = make([]*models.Transaction, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
monthlyAmounts = append(monthlyAmounts, transaction)
|
||||||
|
transactionsMonthlyAmounts[yearMonth] = monthlyAmounts
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionsMonthlyAmounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTransactionMapByList returns a transaction map by a list
|
// GetTransactionMapByList returns a transaction map by a list
|
||||||
func (s *TransactionService) GetTransactionMapByList(transactions []*models.Transaction) map[int64]*models.Transaction {
|
func (s *TransactionService) GetTransactionMapByList(transactions []*models.Transaction) map[int64]*models.Transaction {
|
||||||
transactionMap := make(map[int64]*models.Transaction)
|
transactionMap := make(map[int64]*models.Transaction)
|
||||||
|
|||||||
@@ -17,6 +17,29 @@ const (
|
|||||||
easternmostTimezoneUtcOffset = 840 // Pacific/Kiritimati (UTC+14:00)
|
easternmostTimezoneUtcOffset = 840 // Pacific/Kiritimati (UTC+14:00)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ParseNumericYearMonth returns numeric year and month from textual content
|
||||||
|
func ParseNumericYearMonth(yearMonth string) (int32, int32, error) {
|
||||||
|
yearMonthParts := strings.Split(yearMonth, "-")
|
||||||
|
|
||||||
|
if len(yearMonthParts) != 2 {
|
||||||
|
return 0, 0, errs.ErrParameterInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
year, err := StringToInt32(yearMonthParts[0])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
month, err := StringToInt32(yearMonthParts[1])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return year, month, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FormatUnixTimeToLongDateTimeInServerTimezone returns a textual representation of the unix time formatted by long date time format
|
// FormatUnixTimeToLongDateTimeInServerTimezone returns a textual representation of the unix time formatted by long date time format
|
||||||
func FormatUnixTimeToLongDateTimeInServerTimezone(unixTime int64) string {
|
func FormatUnixTimeToLongDateTimeInServerTimezone(unixTime int64) string {
|
||||||
return parseFromUnixTime(unixTime).Format(longDateTimeFormat)
|
return parseFromUnixTime(unixTime).Format(longDateTimeFormat)
|
||||||
@@ -44,6 +67,17 @@ func FormatUnixTimeToYearMonth(unixTime int64, timezone *time.Location) string {
|
|||||||
return t.Format(yearMonthDateTimeFormat)
|
return t.Format(yearMonthDateTimeFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatUnixTimeToNumericYearMonth returns numeric year and month of specified unix time
|
||||||
|
func FormatUnixTimeToNumericYearMonth(unixTime int64, timezone *time.Location) int32 {
|
||||||
|
t := parseFromUnixTime(unixTime)
|
||||||
|
|
||||||
|
if timezone != nil {
|
||||||
|
t = t.In(timezone)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(t.Year())*100 + int32(t.Month())
|
||||||
|
}
|
||||||
|
|
||||||
// FormatUnixTimeToNumericLocalDateTime returns numeric year, month, day, hour, minute and second of specified unix time
|
// FormatUnixTimeToNumericLocalDateTime returns numeric year, month, day, hour, minute and second of specified unix time
|
||||||
func FormatUnixTimeToNumericLocalDateTime(unixTime int64, timezone *time.Location) int64 {
|
func FormatUnixTimeToNumericLocalDateTime(unixTime int64, timezone *time.Location) int64 {
|
||||||
t := parseFromUnixTime(unixTime)
|
t := parseFromUnixTime(unixTime)
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestParseNumericYearMonth(t *testing.T) {
|
||||||
|
expectedYear := int32(2024)
|
||||||
|
expectedMonth := int32(3)
|
||||||
|
actualYear, actualMonth, err := ParseNumericYearMonth("2024-03")
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, expectedYear, actualYear)
|
||||||
|
assert.Equal(t, expectedMonth, actualMonth)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFormatUnixTimeToLongDateTimeWithoutSecond(t *testing.T) {
|
func TestFormatUnixTimeToLongDateTimeWithoutSecond(t *testing.T) {
|
||||||
unixTime := int64(1617228083)
|
unixTime := int64(1617228083)
|
||||||
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
||||||
@@ -35,6 +44,20 @@ func TestFormatUnixTimeToYearMonth(t *testing.T) {
|
|||||||
assert.Equal(t, expectedValue, actualValue)
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFormatUnixTimeToNumericYearMonth(t *testing.T) {
|
||||||
|
unixTime := int64(1617228083)
|
||||||
|
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
||||||
|
utc8Timezone := time.FixedZone("Test Timezone", 28800) // UTC+8
|
||||||
|
|
||||||
|
expectedValue := int32(202103)
|
||||||
|
actualValue := FormatUnixTimeToNumericYearMonth(unixTime, utcTimezone)
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
|
||||||
|
expectedValue = int32(202104)
|
||||||
|
actualValue = FormatUnixTimeToNumericYearMonth(unixTime, utc8Timezone)
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFormatUnixTimeToNumericLocalDateTime(t *testing.T) {
|
func TestFormatUnixTimeToNumericLocalDateTime(t *testing.T) {
|
||||||
unixTime := int64(1617228083)
|
unixTime := int64(1617228083)
|
||||||
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
||||||
|
|||||||
@@ -296,6 +296,9 @@ export default {
|
|||||||
|
|
||||||
return axios.get(`v1/transactions/statistics.json?use_transaction_timezone=${useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
return axios.get(`v1/transactions/statistics.json?use_transaction_timezone=${useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
||||||
},
|
},
|
||||||
|
getTransactionStatisticsTrends: ({ startTime, endTime, useTransactionTimezone, rangeType }) => {
|
||||||
|
return axios.get(`v1/transactions/statistics/trends.json?start_time=${startTime}&end_time=${endTime}&use_transaction_timezone=${useTransactionTimezone}&range_type=${rangeType}`);
|
||||||
|
},
|
||||||
getTransactionAmounts: ({ useTransactionTimezone, today, thisWeek, thisMonth, thisYear, lastMonth, monthBeforeLastMonth, monthBeforeLast2Months, monthBeforeLast3Months, monthBeforeLast4Months, monthBeforeLast5Months, monthBeforeLast6Months, monthBeforeLast7Months, monthBeforeLast8Months, monthBeforeLast9Months, monthBeforeLast10Months }) => {
|
getTransactionAmounts: ({ useTransactionTimezone, today, thisWeek, thisMonth, thisYear, lastMonth, monthBeforeLastMonth, monthBeforeLast2Months, monthBeforeLast3Months, monthBeforeLast4Months, monthBeforeLast5Months, monthBeforeLast6Months, monthBeforeLast7Months, monthBeforeLast8Months, monthBeforeLast9Months, monthBeforeLast10Months }) => {
|
||||||
const queryParams = [];
|
const queryParams = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user