mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 01:04:25 +08:00
add asset trends in statistics & analysis (#314)
This commit is contained in:
@@ -385,6 +385,7 @@ func startWebServer(c *core.CliContext) error {
|
|||||||
apiV1Route.GET("/transactions/reconciliation_statements.json", bindApi(api.Transactions.TransactionReconciliationStatementHandler))
|
apiV1Route.GET("/transactions/reconciliation_statements.json", bindApi(api.Transactions.TransactionReconciliationStatementHandler))
|
||||||
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/statistics/trends.json", bindApi(api.Transactions.TransactionStatisticsTrendsHandler))
|
||||||
|
apiV1Route.GET("/transactions/statistics/asset_trends.json", bindApi(api.Transactions.TransactionStatisticsAssetTrendsHandler))
|
||||||
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))
|
||||||
|
|||||||
+66
-1
@@ -340,7 +340,7 @@ func (a *TransactionsApi) TransactionReconciliationStatementHandler(c *core.WebC
|
|||||||
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(reconciliationStatementRequest.StartTime)
|
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(reconciliationStatementRequest.StartTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionsWithAccountBalance, totalInflows, totalOutflows, openingBalance, closingBalance, err := a.transactions.GetAllTransactionsWithAccountBalanceByMaxTime(c, uid, pageCountForAccountStatement, maxTransactionTime, minTransactionTime, reconciliationStatementRequest.AccountId, account.Category)
|
transactionsWithAccountBalance, totalInflows, totalOutflows, openingBalance, closingBalance, err := a.transactions.GetAllTransactionsInOneAccountWithAccountBalanceByMaxTime(c, uid, pageCountForAccountStatement, maxTransactionTime, minTransactionTime, reconciliationStatementRequest.AccountId, account.Category)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", reconciliationStatementRequest.StartTime, reconciliationStatementRequest.EndTime, uid, err.Error())
|
log.Errorf(c, "[transactions.TransactionReconciliationStatementHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", reconciliationStatementRequest.StartTime, reconciliationStatementRequest.EndTime, uid, err.Error())
|
||||||
@@ -532,6 +532,71 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
|||||||
return statisticTrendsResp, nil
|
return statisticTrendsResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticsAssetTrendsHandler returns transaction statistics asset trends of current user
|
||||||
|
func (a *TransactionsApi) TransactionStatisticsAssetTrendsHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
|
var statisticAssetTrendsReq models.TransactionStatisticAssetTrendsRequest
|
||||||
|
err := c.ShouldBindQuery(&statisticAssetTrendsReq)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] parse request failed, because %s", err.Error())
|
||||||
|
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
utcOffset, err := c.GetClientTimezoneOffset()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] cannot get client timezone offset, because %s", err.Error())
|
||||||
|
return nil, errs.ErrClientTimezoneOffsetInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := c.GetCurrentUid()
|
||||||
|
|
||||||
|
maxTransactionTime := int64(0)
|
||||||
|
|
||||||
|
if statisticAssetTrendsReq.EndTime > 0 {
|
||||||
|
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(statisticAssetTrendsReq.EndTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
minTransactionTime := int64(0)
|
||||||
|
|
||||||
|
if statisticAssetTrendsReq.StartTime > 0 {
|
||||||
|
minTransactionTime = utils.GetMinTransactionTimeFromUnixTime(statisticAssetTrendsReq.StartTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountDailyBalances, err := a.transactions.GetAllAccountsDailyOpeningAndClosingBalance(c, uid, maxTransactionTime, minTransactionTime, utcOffset)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(c, "[transactions.TransactionStatisticsAssetTrendsHandler] failed to get transactions from \"%d\" to \"%d\" for user \"uid:%d\", because %s", statisticAssetTrendsReq.StartTime, statisticAssetTrendsReq.EndTime, uid, err.Error())
|
||||||
|
return nil, errs.Or(err, errs.ErrOperationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
statisticAssetTrendsResp := make(models.TransactionStatisticAssetTrendsResponseItemSlice, 0)
|
||||||
|
|
||||||
|
for yearMonthDay, dailyAccountBalances := range accountDailyBalances {
|
||||||
|
dailyStatisticResp := &models.TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: yearMonthDay / 10000,
|
||||||
|
Month: (yearMonthDay % 10000) / 100,
|
||||||
|
Day: yearMonthDay % 100,
|
||||||
|
Items: make([]*models.TransactionStatisticAssetTrendsResponseDataItem, len(dailyAccountBalances)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(dailyAccountBalances); i++ {
|
||||||
|
accountBalance := dailyAccountBalances[i]
|
||||||
|
dailyStatisticResp.Items[i] = &models.TransactionStatisticAssetTrendsResponseDataItem{
|
||||||
|
AccountId: accountBalance.AccountId,
|
||||||
|
AccountOpeningBalance: accountBalance.AccountOpeningBalance,
|
||||||
|
AccountClosingBalance: accountBalance.AccountClosingBalance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statisticAssetTrendsResp = append(statisticAssetTrendsResp, dailyStatisticResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(statisticAssetTrendsResp)
|
||||||
|
|
||||||
|
return statisticAssetTrendsResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsHandler returns transaction amounts of current user
|
// TransactionAmountsHandler returns transaction amounts of current user
|
||||||
func (a *TransactionsApi) TransactionAmountsHandler(c *core.WebContext) (any, *errs.Error) {
|
func (a *TransactionsApi) TransactionAmountsHandler(c *core.WebContext) (any, *errs.Error) {
|
||||||
var transactionAmountsReq models.TransactionAmountsRequest
|
var transactionAmountsReq models.TransactionAmountsRequest
|
||||||
|
|||||||
@@ -275,6 +275,12 @@ type TransactionStatisticTrendsRequest struct {
|
|||||||
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticAssetTrendsRequest represents all parameters of transaction statistic asset trends request
|
||||||
|
type TransactionStatisticAssetTrendsRequest struct {
|
||||||
|
StartTime int64 `form:"start_time"`
|
||||||
|
EndTime int64 `form:"end_time"`
|
||||||
|
}
|
||||||
|
|
||||||
// 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"`
|
||||||
@@ -403,6 +409,21 @@ type TransactionStatisticTrendsResponseItem struct {
|
|||||||
Items []*TransactionStatisticResponseItem `json:"items"`
|
Items []*TransactionStatisticResponseItem `json:"items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticAssetTrendsResponseItem represents the data within each statistic interval
|
||||||
|
type TransactionStatisticAssetTrendsResponseItem struct {
|
||||||
|
Year int32 `json:"year"`
|
||||||
|
Month int32 `json:"month"`
|
||||||
|
Day int32 `json:"day"`
|
||||||
|
Items []*TransactionStatisticAssetTrendsResponseDataItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticAssetTrendsResponseDataItem represents an asset trends data item
|
||||||
|
type TransactionStatisticAssetTrendsResponseDataItem struct {
|
||||||
|
AccountId int64 `json:"accountId,string"`
|
||||||
|
AccountOpeningBalance int64 `json:"accountOpeningBalance"`
|
||||||
|
AccountClosingBalance int64 `json:"accountClosingBalance"`
|
||||||
|
}
|
||||||
|
|
||||||
// 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"`
|
||||||
@@ -600,6 +621,32 @@ func (s TransactionStatisticTrendsResponseItemSlice) Less(i, j int) bool {
|
|||||||
return s[i].Month < s[j].Month
|
return s[i].Month < s[j].Month
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransactionStatisticAssetTrendsResponseItemSlice represents the slice data structure of TransactionStatisticAssetTrendsResponseItem
|
||||||
|
type TransactionStatisticAssetTrendsResponseItemSlice []*TransactionStatisticAssetTrendsResponseItem
|
||||||
|
|
||||||
|
// Len returns the count of items
|
||||||
|
func (s TransactionStatisticAssetTrendsResponseItemSlice) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps two items
|
||||||
|
func (s TransactionStatisticAssetTrendsResponseItemSlice) 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 TransactionStatisticAssetTrendsResponseItemSlice) Less(i, j int) bool {
|
||||||
|
if s[i].Year != s[j].Year {
|
||||||
|
return s[i].Year < s[j].Year
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[i].Month != s[j].Month {
|
||||||
|
return s[i].Month < s[j].Month
|
||||||
|
}
|
||||||
|
|
||||||
|
return s[i].Day < s[j].Day
|
||||||
|
}
|
||||||
|
|
||||||
// TransactionAmountsResponseItemAmountInfoSlice represents the slice data structure of TransactionAmountsResponseItemAmountInfo
|
// TransactionAmountsResponseItemAmountInfoSlice represents the slice data structure of TransactionAmountsResponseItemAmountInfo
|
||||||
type TransactionAmountsResponseItemAmountInfoSlice []*TransactionAmountsResponseItemAmountInfo
|
type TransactionAmountsResponseItemAmountInfoSlice []*TransactionAmountsResponseItemAmountInfo
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,61 @@ func TestTransactionStatisticTrendsResponseItemSliceLess(t *testing.T) {
|
|||||||
assert.Equal(t, int32(9), transactionTrendsSlice[4].Month)
|
assert.Equal(t, int32(9), transactionTrendsSlice[4].Month)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransactionStatisticAssetTrendsResponseItemSliceLess(t *testing.T) {
|
||||||
|
var transactionTrendsSlice TransactionStatisticAssetTrendsResponseItemSlice
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2024,
|
||||||
|
Month: 9,
|
||||||
|
Day: 1,
|
||||||
|
})
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2024,
|
||||||
|
Month: 9,
|
||||||
|
Day: 2,
|
||||||
|
})
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2024,
|
||||||
|
Month: 10,
|
||||||
|
Day: 1,
|
||||||
|
})
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2022,
|
||||||
|
Month: 10,
|
||||||
|
Day: 1,
|
||||||
|
})
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2023,
|
||||||
|
Month: 1,
|
||||||
|
Day: 1,
|
||||||
|
})
|
||||||
|
transactionTrendsSlice = append(transactionTrendsSlice, &TransactionStatisticAssetTrendsResponseItem{
|
||||||
|
Year: 2024,
|
||||||
|
Month: 2,
|
||||||
|
Day: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Sort(transactionTrendsSlice)
|
||||||
|
|
||||||
|
assert.Equal(t, int32(2022), transactionTrendsSlice[0].Year)
|
||||||
|
assert.Equal(t, int32(10), transactionTrendsSlice[0].Month)
|
||||||
|
assert.Equal(t, int32(1), transactionTrendsSlice[0].Day)
|
||||||
|
assert.Equal(t, int32(2023), transactionTrendsSlice[1].Year)
|
||||||
|
assert.Equal(t, int32(1), transactionTrendsSlice[1].Month)
|
||||||
|
assert.Equal(t, int32(1), transactionTrendsSlice[1].Day)
|
||||||
|
assert.Equal(t, int32(2024), transactionTrendsSlice[2].Year)
|
||||||
|
assert.Equal(t, int32(2), transactionTrendsSlice[2].Month)
|
||||||
|
assert.Equal(t, int32(2), transactionTrendsSlice[2].Day)
|
||||||
|
assert.Equal(t, int32(2024), transactionTrendsSlice[3].Year)
|
||||||
|
assert.Equal(t, int32(9), transactionTrendsSlice[3].Month)
|
||||||
|
assert.Equal(t, int32(1), transactionTrendsSlice[3].Day)
|
||||||
|
assert.Equal(t, int32(2024), transactionTrendsSlice[4].Year)
|
||||||
|
assert.Equal(t, int32(9), transactionTrendsSlice[4].Month)
|
||||||
|
assert.Equal(t, int32(2), transactionTrendsSlice[4].Day)
|
||||||
|
assert.Equal(t, int32(2024), transactionTrendsSlice[5].Year)
|
||||||
|
assert.Equal(t, int32(10), transactionTrendsSlice[5].Month)
|
||||||
|
assert.Equal(t, int32(1), transactionTrendsSlice[5].Day)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTransactionAmountsResponseItemAmountInfoSliceLess(t *testing.T) {
|
func TestTransactionAmountsResponseItemAmountInfoSliceLess(t *testing.T) {
|
||||||
var amountInfoSlice TransactionAmountsResponseItemAmountInfoSlice
|
var amountInfoSlice TransactionAmountsResponseItemAmountInfoSlice
|
||||||
amountInfoSlice = append(amountInfoSlice, &TransactionAmountsResponseItemAmountInfo{
|
amountInfoSlice = append(amountInfoSlice, &TransactionAmountsResponseItemAmountInfo{
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ var ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES = map[string]UserApplicationClo
|
|||||||
"statistics.defaultCategoricalChartDataRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
"statistics.defaultCategoricalChartDataRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
||||||
"statistics.defaultTrendChartType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
"statistics.defaultTrendChartType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
||||||
"statistics.defaultTrendChartDataRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
"statistics.defaultTrendChartDataRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
||||||
|
"statistics.defaultAssetTrendsChartType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
||||||
|
"statistics.defaultAssetTrendsChartDataRangeType": USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER,
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserApplicationCloudSetting represents user application cloud setting stored in database
|
// UserApplicationCloudSetting represents user application cloud setting stored in database
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ func (s *TransactionService) GetAllSpecifiedTransactions(c core.Context, uid int
|
|||||||
return allTransactions, nil
|
return allTransactions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllTransactionsWithAccountBalanceByMaxTime returns account statement within time range
|
// GetAllTransactionsInOneAccountWithAccountBalanceByMaxTime returns account statement within time range
|
||||||
func (s *TransactionService) GetAllTransactionsWithAccountBalanceByMaxTime(c core.Context, uid int64, pageCount int32, maxTransactionTime int64, minTransactionTime int64, accountId int64, accountCategory models.AccountCategory) ([]*models.TransactionWithAccountBalance, int64, int64, int64, int64, error) {
|
func (s *TransactionService) GetAllTransactionsInOneAccountWithAccountBalanceByMaxTime(c core.Context, uid int64, pageCount int32, maxTransactionTime int64, minTransactionTime int64, accountId int64, accountCategory models.AccountCategory) ([]*models.TransactionWithAccountBalance, int64, int64, int64, int64, error) {
|
||||||
if maxTransactionTime <= 0 {
|
if maxTransactionTime <= 0 {
|
||||||
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ func (s *TransactionService) GetAllTransactionsWithAccountBalanceByMaxTime(c cor
|
|||||||
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
accumulatedBalance = accumulatedBalance + transaction.Amount
|
accumulatedBalance = accumulatedBalance + transaction.Amount
|
||||||
} else {
|
} else {
|
||||||
log.Errorf(c, "[transactions.GetAllTransactionsWithAccountBalanceByMaxTime] trasaction type (%d) is invalid (id:%d)", transaction.TransactionId, transaction.Type)
|
log.Errorf(c, "[transactions.GetAllTransactionsInOneAccountWithAccountBalanceByMaxTime] trasaction type (%d) is invalid (id:%d)", transaction.TransactionId, transaction.Type)
|
||||||
return nil, 0, 0, 0, 0, errs.ErrTransactionTypeInvalid
|
return nil, 0, 0, 0, 0, errs.ErrTransactionTypeInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +197,132 @@ func (s *TransactionService) GetAllTransactionsWithAccountBalanceByMaxTime(c cor
|
|||||||
return allTransactionsAndAccountBalance, totalInflows, totalOutflows, openingBalance, accumulatedBalance, nil
|
return allTransactionsAndAccountBalance, totalInflows, totalOutflows, openingBalance, accumulatedBalance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllAccountsDailyOpeningAndClosingBalance returns daily opening and closing balance of all accounts within time range
|
||||||
|
func (s *TransactionService) GetAllAccountsDailyOpeningAndClosingBalance(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, utcOffset int16) (map[int32][]*models.TransactionWithAccountBalance, error) {
|
||||||
|
if maxTransactionTime <= 0 {
|
||||||
|
maxTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
clientLocation := time.FixedZone("Client Timezone", int(utcOffset)*60)
|
||||||
|
var allTransactions []*models.Transaction
|
||||||
|
|
||||||
|
for maxTransactionTime > 0 {
|
||||||
|
transactions, err := s.GetTransactionsByMaxTime(c, uid, maxTransactionTime, 0, 0, nil, nil, nil, false, models.TRANSACTION_TAG_FILTER_HAS_ANY, "", "", 1, pageCountForLoadTransactionAmounts, false, false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactions = append(allTransactions, transactions...)
|
||||||
|
|
||||||
|
if len(transactions) < pageCountForLoadTransactionAmounts {
|
||||||
|
maxTransactionTime = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTransactionTime = transactions[len(transactions)-1].TransactionTime - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
accountDailyLastBalances := make(map[string]*models.TransactionWithAccountBalance)
|
||||||
|
accountDailyBalances := make(map[int32][]*models.TransactionWithAccountBalance)
|
||||||
|
|
||||||
|
if len(allTransactions) < 1 {
|
||||||
|
return accountDailyBalances, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulatedBalances := make(map[int64]int64)
|
||||||
|
accumulatedBalancesBeforeStartTime := make(map[int64]int64)
|
||||||
|
|
||||||
|
for i := len(allTransactions) - 1; i >= 0; i-- {
|
||||||
|
transaction := allTransactions[i]
|
||||||
|
accumulatedBalance := accumulatedBalances[transaction.AccountId]
|
||||||
|
lastAccumulatedBalance := accumulatedBalances[transaction.AccountId]
|
||||||
|
|
||||||
|
if transaction.Type == models.TRANSACTION_DB_TYPE_MODIFY_BALANCE {
|
||||||
|
accumulatedBalance = accumulatedBalance + transaction.RelatedAccountAmount
|
||||||
|
} else if transaction.Type == models.TRANSACTION_DB_TYPE_INCOME {
|
||||||
|
accumulatedBalance = accumulatedBalance + transaction.Amount
|
||||||
|
} else if transaction.Type == models.TRANSACTION_DB_TYPE_EXPENSE {
|
||||||
|
accumulatedBalance = accumulatedBalance - transaction.Amount
|
||||||
|
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT {
|
||||||
|
accumulatedBalance = accumulatedBalance - transaction.Amount
|
||||||
|
} else if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||||
|
accumulatedBalance = accumulatedBalance + transaction.Amount
|
||||||
|
} else {
|
||||||
|
log.Errorf(c, "[transactions.GetAllTransactionsWithAccountBalanceByMaxTime] trasaction type (%d) is invalid (id:%d)", transaction.TransactionId, transaction.Type)
|
||||||
|
return nil, errs.ErrTransactionTypeInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulatedBalances[transaction.AccountId] = accumulatedBalance
|
||||||
|
|
||||||
|
if transaction.TransactionTime < minTransactionTime {
|
||||||
|
accumulatedBalancesBeforeStartTime[transaction.AccountId] = accumulatedBalance
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
yearMonthDay := utils.FormatUnixTimeToNumericYearMonthDay(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime), clientLocation)
|
||||||
|
groupKey := fmt.Sprintf("%d_%d", yearMonthDay, transaction.AccountId)
|
||||||
|
dailyAccountBalance, exists := accountDailyLastBalances[groupKey]
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
dailyAccountBalance.AccountClosingBalance = accumulatedBalance
|
||||||
|
} else {
|
||||||
|
dailyAccountBalance = &models.TransactionWithAccountBalance{
|
||||||
|
Transaction: &models.Transaction{
|
||||||
|
AccountId: transaction.AccountId,
|
||||||
|
},
|
||||||
|
AccountOpeningBalance: lastAccumulatedBalance,
|
||||||
|
AccountClosingBalance: accumulatedBalance,
|
||||||
|
}
|
||||||
|
accountDailyLastBalances[groupKey] = dailyAccountBalance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
firstTransactionTime := allTransactions[len(allTransactions)-1].TransactionTime
|
||||||
|
|
||||||
|
if minTransactionTime > firstTransactionTime {
|
||||||
|
firstTransactionTime = minTransactionTime
|
||||||
|
}
|
||||||
|
|
||||||
|
firstYearMonthDay := utils.FormatUnixTimeToNumericYearMonthDay(utils.GetUnixTimeFromTransactionTime(firstTransactionTime), clientLocation)
|
||||||
|
|
||||||
|
// fill in the opening balance for accounts that do not have transactions on the first day
|
||||||
|
for accountId, accumulatedBalance := range accumulatedBalancesBeforeStartTime {
|
||||||
|
if accumulatedBalance == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
groupKey := fmt.Sprintf("%d_%d", firstYearMonthDay, accountId)
|
||||||
|
|
||||||
|
if _, exists := accountDailyLastBalances[groupKey]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
accountDailyLastBalances[groupKey] = &models.TransactionWithAccountBalance{
|
||||||
|
Transaction: &models.Transaction{
|
||||||
|
AccountId: accountId,
|
||||||
|
},
|
||||||
|
AccountOpeningBalance: accumulatedBalance,
|
||||||
|
AccountClosingBalance: accumulatedBalance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for groupKey, transactionWithAccountBalance := range accountDailyLastBalances {
|
||||||
|
groupKeyParts := strings.Split(groupKey, "_")
|
||||||
|
yearMonthDay, _ := utils.StringToInt32(groupKeyParts[0])
|
||||||
|
dailyAccountBalances, exists := accountDailyBalances[yearMonthDay]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
dailyAccountBalances = make([]*models.TransactionWithAccountBalance, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
dailyAccountBalances = append(dailyAccountBalances, transactionWithAccountBalance)
|
||||||
|
accountDailyBalances[yearMonthDay] = dailyAccountBalances
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountDailyBalances, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetTransactionsByMaxTime returns transactions before given time
|
// GetTransactionsByMaxTime returns transactions before given time
|
||||||
func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, amountFilter string, keyword string, page int32, count int32, needOneMoreItem bool, noDuplicated bool) ([]*models.Transaction, error) {
|
func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, amountFilter string, keyword string, page int32, count int32, needOneMoreItem bool, noDuplicated bool) ([]*models.Transaction, error) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
|
|||||||
@@ -155,6 +155,17 @@ func FormatUnixTimeToNumericYearMonth(unixTime int64, timezone *time.Location) i
|
|||||||
return int32(t.Year())*100 + int32(t.Month())
|
return int32(t.Year())*100 + int32(t.Month())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatUnixTimeToNumericYearMonthDay returns numeric year, month and day of specified unix time
|
||||||
|
func FormatUnixTimeToNumericYearMonthDay(unixTime int64, timezone *time.Location) int32 {
|
||||||
|
t := parseFromUnixTime(unixTime)
|
||||||
|
|
||||||
|
if timezone != nil {
|
||||||
|
t = t.In(timezone)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(t.Year())*10000 + int32(t.Month())*100 + int32(t.Day())
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
|||||||
@@ -133,6 +133,20 @@ func TestFormatUnixTimeToNumericYearMonth(t *testing.T) {
|
|||||||
assert.Equal(t, expectedValue, actualValue)
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFormatUnixTimeToNumericYearMonthDay(t *testing.T) {
|
||||||
|
unixTime := int64(1617228083)
|
||||||
|
utcTimezone := time.FixedZone("Test Timezone", 0) // UTC
|
||||||
|
utc8Timezone := time.FixedZone("Test Timezone", 28800) // UTC+8
|
||||||
|
|
||||||
|
expectedValue := int32(20210331)
|
||||||
|
actualValue := FormatUnixTimeToNumericYearMonthDay(unixTime, utcTimezone)
|
||||||
|
assert.Equal(t, expectedValue, actualValue)
|
||||||
|
|
||||||
|
expectedValue = int32(20210401)
|
||||||
|
actualValue = FormatUnixTimeToNumericYearMonthDay(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
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { ChartDateAggregationType } from '@/core/statistics.ts';
|
|||||||
import type { AccountInfoResponse } from '@/models/account.ts';
|
import type { AccountInfoResponse } from '@/models/account.ts';
|
||||||
import type { TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
import type { TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
import { isDefined, isArray } from '@/lib/common.ts';
|
import { isArray } from '@/lib/common.ts';
|
||||||
import { sumAmounts } from '@/lib/numeral.ts';
|
import { sumAmounts } from '@/lib/numeral.ts';
|
||||||
import {
|
import {
|
||||||
getGregorianCalendarYearAndMonthFromUnixTime,
|
getGregorianCalendarYearAndMonthFromUnixTime,
|
||||||
@@ -45,7 +45,7 @@ export interface AccountBalanceTrendsChartItem {
|
|||||||
|
|
||||||
export interface CommonAccountBalanceTrendsChartProps {
|
export interface CommonAccountBalanceTrendsChartProps {
|
||||||
items: TransactionReconciliationStatementResponseItem[] | undefined;
|
items: TransactionReconciliationStatementResponseItem[] | undefined;
|
||||||
dateAggregationType?: number;
|
dateAggregationType: number;
|
||||||
fiscalYearStart: number;
|
fiscalYearStart: number;
|
||||||
account: AccountInfoResponse;
|
account: AccountInfoResponse;
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDefined(props.dateAggregationType)) {
|
if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
return getAllDaysStartAndEndUnixTimes(dataDateRange.value.minUnixTime, dataDateRange.value.maxUnixTime);
|
return getAllDaysStartAndEndUnixTimes(dataDateRange.value.minUnixTime, dataDateRange.value.maxUnixTime);
|
||||||
} else {
|
} else {
|
||||||
const startYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(dataDateRange.value.minUnixTime);
|
const startYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(dataDateRange.value.minUnixTime);
|
||||||
@@ -129,8 +129,10 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren
|
|||||||
dateRangeMinUnixTime = getQuarterFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
dateRangeMinUnixTime = getQuarterFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
dateRangeMinUnixTime = getMonthFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
dateRangeMinUnixTime = getMonthFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
} else {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
dateRangeMinUnixTime = getDayFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
dateRangeMinUnixTime = getDayFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
|
} else {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataItems: TransactionReconciliationStatementResponseItem[] = dayDataItemsMap[dateRangeMinUnixTime] || [];
|
const dataItems: TransactionReconciliationStatementResponseItem[] = dayDataItemsMap[dateRangeMinUnixTime] || [];
|
||||||
@@ -159,8 +161,10 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren
|
|||||||
displayDate = formatUnixTimeToGregorianLikeYearQuarter(dateRange.minUnixTime);
|
displayDate = formatUnixTimeToGregorianLikeYearQuarter(dateRange.minUnixTime);
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
displayDate = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
displayDate = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
||||||
} else {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
displayDate = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
displayDate = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
||||||
|
} else {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray(dataItems)) {
|
if (isArray(dataItems)) {
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
TextualYearMonth,
|
||||||
|
Year1BasedMonth,
|
||||||
|
YearMonthDay,
|
||||||
|
TimeRangeAndDateType,
|
||||||
|
YearUnixTime,
|
||||||
|
YearQuarterUnixTime,
|
||||||
|
YearMonthUnixTime,
|
||||||
|
YearMonthDayUnixTime
|
||||||
|
} from '@/core/datetime.ts';
|
||||||
|
import type { FiscalYearUnixTime } from '@/core/fiscalyear.ts';
|
||||||
|
import { ChartDataAggregationType, ChartDateAggregationType } from '@/core/statistics.ts';
|
||||||
|
import type { YearMonthItems, YearMonthDayItems } from '@/models/transaction.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getYearMonthDayDateTime,
|
||||||
|
getGregorianCalendarYearAndMonthFromUnixTime,
|
||||||
|
getAllDaysStartAndEndUnixTimes
|
||||||
|
} from '@/lib/datetime.ts';
|
||||||
|
import {
|
||||||
|
getAllDateRangesFromItems,
|
||||||
|
getAllDateRangesByYearMonthRange
|
||||||
|
} from '@/lib/statistics.ts';
|
||||||
|
|
||||||
|
export type TrendsChartDateType = 'daily' | 'monthly';
|
||||||
|
|
||||||
|
interface TrendsChartTypes {
|
||||||
|
daily: {
|
||||||
|
ItemsType: YearMonthDayItems<YearMonthDay>;
|
||||||
|
DateTimeRangeType: number;
|
||||||
|
MonthRangeType: undefined;
|
||||||
|
};
|
||||||
|
monthly: {
|
||||||
|
ItemsType: YearMonthItems<Year1BasedMonth>;
|
||||||
|
DateTimeRangeType: undefined;
|
||||||
|
MonthRangeType: TextualYearMonth | '';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommonTrendsChartProps<T extends TrendsChartDateType> {
|
||||||
|
chartMode: T;
|
||||||
|
items: TrendsChartTypes[T]['ItemsType'][];
|
||||||
|
stacked?: boolean;
|
||||||
|
startTime: TrendsChartTypes[T]['DateTimeRangeType'];
|
||||||
|
endTime: TrendsChartTypes[T]['DateTimeRangeType'];
|
||||||
|
startYearMonth: TrendsChartTypes[T]['MonthRangeType'];
|
||||||
|
endYearMonth: TrendsChartTypes[T]['MonthRangeType'];
|
||||||
|
fiscalYearStart: number;
|
||||||
|
sortingType: number;
|
||||||
|
dataAggregationType: ChartDataAggregationType;
|
||||||
|
dateAggregationType: number;
|
||||||
|
idField?: string;
|
||||||
|
nameField: string;
|
||||||
|
valueField: string;
|
||||||
|
colorField?: string;
|
||||||
|
hiddenField?: string;
|
||||||
|
displayOrdersField?: string;
|
||||||
|
translateName?: boolean;
|
||||||
|
defaultCurrency?: string;
|
||||||
|
enableClickItem?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrendsBarChartClickEvent {
|
||||||
|
itemId: string;
|
||||||
|
dateRange: TimeRangeAndDateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDailyAllDateRanges(props: CommonTrendsChartProps<'daily'>): YearUnixTime[] | FiscalYearUnixTime[] | YearQuarterUnixTime[] | YearMonthUnixTime[] | YearMonthDayUnixTime[] {
|
||||||
|
let startTime: number = props.startTime;
|
||||||
|
let endTime: number = props.endTime;
|
||||||
|
|
||||||
|
if ((!startTime || !endTime) && props.items && props.items.length) {
|
||||||
|
let minUnixTime = Number.MAX_SAFE_INTEGER, maxUnixTime = 0;
|
||||||
|
|
||||||
|
for (const accountItem of props.items) {
|
||||||
|
for (const dataItem of accountItem.items) {
|
||||||
|
const dateTime = getYearMonthDayDateTime(dataItem.year, dataItem.month, dataItem.day);
|
||||||
|
const unixTime = dateTime.getUnixTime();
|
||||||
|
|
||||||
|
if (unixTime < minUnixTime) {
|
||||||
|
minUnixTime = unixTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unixTime > maxUnixTime) {
|
||||||
|
maxUnixTime = unixTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minUnixTime < Number.MAX_SAFE_INTEGER && maxUnixTime > 0) {
|
||||||
|
startTime = minUnixTime;
|
||||||
|
endTime = maxUnixTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
|
return getAllDaysStartAndEndUnixTimes(startTime, endTime);
|
||||||
|
} else {
|
||||||
|
const startYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(startTime);
|
||||||
|
const endYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(endTime);
|
||||||
|
return getAllDateRangesByYearMonthRange(startYearMonth, endYearMonth, props.fiscalYearStart, props.dateAggregationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMonthlyAllDateRanges(props: CommonTrendsChartProps<'monthly'>): YearUnixTime[] | FiscalYearUnixTime[] | YearQuarterUnixTime[] | YearMonthUnixTime[] {
|
||||||
|
return getAllDateRangesFromItems(props.items, props.startYearMonth, props.endYearMonth, props.fiscalYearStart, props.dateAggregationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTrendsChartBase<T extends TrendsChartDateType>(props: CommonTrendsChartProps<T>) {
|
||||||
|
const { tt } = useI18n();
|
||||||
|
|
||||||
|
const allDateRanges = computed<YearUnixTime[] | FiscalYearUnixTime[] | YearQuarterUnixTime[] | YearMonthUnixTime[] | YearMonthDayUnixTime[]>(() => {
|
||||||
|
if (props.chartMode === 'daily') {
|
||||||
|
return buildDailyAllDateRanges(props as CommonTrendsChartProps<'daily'>);
|
||||||
|
} else if (props.chartMode === 'monthly') {
|
||||||
|
return buildMonthlyAllDateRanges(props as CommonTrendsChartProps<'monthly'>);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getItemName(name: string): string {
|
||||||
|
return props.translateName ? tt(name) : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// computed states
|
||||||
|
allDateRanges,
|
||||||
|
// functions
|
||||||
|
getItemName
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -263,7 +263,7 @@ function onLegendSelectChanged(e: { selected: Record<string, boolean> }): void {
|
|||||||
|
|
||||||
@media (min-width: 600px) {
|
@media (min-width: 600px) {
|
||||||
.pie-chart-container {
|
.pie-chart-container {
|
||||||
height: 610px;
|
height: 650px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ const chartOptions = computed<object>(() => {
|
|||||||
|
|
||||||
@media (min-width: 600px) {
|
@media (min-width: 600px) {
|
||||||
.radar-chart-container {
|
.radar-chart-container {
|
||||||
height: 610px;
|
height: 650px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
-44
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-chart autoresize class="monthly-trends-chart-container" :class="{ 'transition-in': skeleton }" :option="chartOptions"
|
<v-chart autoresize class="trends-chart-container" :class="{ 'transition-in': skeleton }" :option="chartOptions"
|
||||||
@click="clickItem" @legendselectchanged="onLegendSelectChanged" />
|
@click="clickItem" @legendselectchanged="onLegendSelectChanged" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -10,20 +10,33 @@ import type { ECElementEvent } from 'echarts/core';
|
|||||||
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { type CommonMonthlyTrendsChartProps, type MonthlyTrendsBarChartClickEvent, useMonthlyTrendsChartBase } from '@/components/base/MonthlyTrendsChartBase.ts'
|
import {
|
||||||
|
type TrendsChartDateType,
|
||||||
|
type CommonTrendsChartProps,
|
||||||
|
type TrendsBarChartClickEvent,
|
||||||
|
useTrendsChartBase
|
||||||
|
} from '@/components/base/TrendsChartBase.ts'
|
||||||
|
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
import { itemAndIndex } from '@/core/base.ts';
|
import { itemAndIndex } from '@/core/base.ts';
|
||||||
import { TextDirection } from '@/core/text.ts';
|
import { TextDirection } from '@/core/text.ts';
|
||||||
import { type Year1BasedMonth, DateRangeScene } from '@/core/datetime.ts';
|
import {
|
||||||
|
type Year1BasedMonth,
|
||||||
|
type YearMonthDay,
|
||||||
|
DateRangeScene
|
||||||
|
} from '@/core/datetime.ts';
|
||||||
import type { ColorStyleValue } from '@/core/color.ts';
|
import type { ColorStyleValue } from '@/core/color.ts';
|
||||||
import { ThemeType } from '@/core/theme.ts';
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
import { TrendChartType, ChartDateAggregationType } from '@/core/statistics.ts';
|
import {
|
||||||
|
ChartDataAggregationType,
|
||||||
|
TrendChartType,
|
||||||
|
ChartDateAggregationType
|
||||||
|
} from '@/core/statistics.ts';
|
||||||
|
|
||||||
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
||||||
|
|
||||||
import type { YearMonthDataItem, SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
import type { SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isArray,
|
isArray,
|
||||||
@@ -42,14 +55,14 @@ import {
|
|||||||
sortStatisticsItems
|
sortStatisticsItems
|
||||||
} from '@/lib/statistics.ts';
|
} from '@/lib/statistics.ts';
|
||||||
|
|
||||||
interface DesktopMonthlyTrendsChartProps<T extends Year1BasedMonth> extends CommonMonthlyTrendsChartProps<T> {
|
interface DesktopTrendsChartProps<T extends TrendsChartDateType> extends CommonTrendsChartProps<T> {
|
||||||
skeleton?: boolean;
|
skeleton?: boolean;
|
||||||
type?: number;
|
type?: number;
|
||||||
showValue?: boolean;
|
showValue?: boolean;
|
||||||
showTotalAmountInTooltip?: boolean;
|
showTotalAmountInTooltip?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MonthlyTrendsChartDataItem {
|
interface TrendsChartDataItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@@ -64,17 +77,17 @@ interface MonthlyTrendsChartDataItem {
|
|||||||
data: number[];
|
data: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MonthlyTrendsChartTooltipItem extends SortableTransactionStatisticDataItem {
|
interface TrendsChartTooltipItem extends SortableTransactionStatisticDataItem {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly color: unknown;
|
readonly color: unknown;
|
||||||
readonly displayOrders: number[];
|
readonly displayOrders: number[];
|
||||||
readonly totalAmount: number;
|
readonly totalAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<DesktopMonthlyTrendsChartProps<YearMonthDataItem>>();
|
const props = defineProps<DesktopTrendsChartProps<TrendsChartDateType>>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click', value: MonthlyTrendsBarChartClickEvent): void;
|
(e: 'click', value: TrendsBarChartClickEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -82,6 +95,7 @@ const theme = useTheme();
|
|||||||
const {
|
const {
|
||||||
tt,
|
tt,
|
||||||
getCurrentLanguageTextDirection,
|
getCurrentLanguageTextDirection,
|
||||||
|
formatUnixTimeToShortDate,
|
||||||
formatUnixTimeToGregorianLikeShortYear,
|
formatUnixTimeToGregorianLikeShortYear,
|
||||||
formatUnixTimeToGregorianLikeShortYearMonth,
|
formatUnixTimeToGregorianLikeShortYearMonth,
|
||||||
formatYearQuarterToGregorianLikeYearQuarter,
|
formatYearQuarterToGregorianLikeYearQuarter,
|
||||||
@@ -90,7 +104,7 @@ const {
|
|||||||
formatAmountToLocalizedNumeralsWithCurrency
|
formatAmountToLocalizedNumeralsWithCurrency
|
||||||
} = useI18n();
|
} = useI18n();
|
||||||
|
|
||||||
const { allDateRanges, getItemName } = useMonthlyTrendsChartBase(props);
|
const { allDateRanges, getItemName } = useTrendsChartBase(props);
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
@@ -143,16 +157,18 @@ const allDisplayDateRanges = computed<string[]>(() => {
|
|||||||
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime));
|
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime));
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
||||||
allDisplayDateRanges.push(formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter));
|
allDisplayDateRanges.push(formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter));
|
||||||
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime));
|
allDisplayDateRanges.push(formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime));
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && props.chartMode === 'daily') {
|
||||||
|
allDisplayDateRanges.push(formatUnixTimeToShortDate(dateRange.minUnixTime));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allDisplayDateRanges;
|
return allDisplayDateRanges;
|
||||||
});
|
});
|
||||||
|
|
||||||
const allSeries = computed<MonthlyTrendsChartDataItem[]>(() => {
|
const allSeries = computed<TrendsChartDataItem[]>(() => {
|
||||||
const allSeries: MonthlyTrendsChartDataItem[] = [];
|
const allSeries: TrendsChartDataItem[] = [];
|
||||||
let maxAmount: number = 0;
|
let maxAmount: number = 0;
|
||||||
|
|
||||||
for (const [item, index] of itemAndIndex(props.items)) {
|
for (const [item, index] of itemAndIndex(props.items)) {
|
||||||
@@ -161,23 +177,41 @@ const allSeries = computed<MonthlyTrendsChartDataItem[]>(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const allAmounts: number[] = [];
|
const allAmounts: number[] = [];
|
||||||
const dateRangeAmountMap: Record<string, YearMonthDataItem[]> = {};
|
const dateRangeAmountMap: Record<string, (Year1BasedMonth | YearMonthDay)[]> = {};
|
||||||
|
|
||||||
for (const dataItem of item.items) {
|
for (const dataItem of item.items) {
|
||||||
let dateRangeKey = '';
|
let dateRangeKey = '';
|
||||||
|
|
||||||
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
if (props.chartMode === 'daily' && 'month' in dataItem) {
|
||||||
dateRangeKey = dataItem.year.toString();
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
dateRangeKey = dataItem.year.toString();
|
||||||
const fiscalYear = getFiscalYearFromUnixTime(
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month1base }),
|
const fiscalYear = getFiscalYearFromUnixTime(
|
||||||
props.fiscalYearStart
|
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month }),
|
||||||
);
|
props.fiscalYearStart
|
||||||
dateRangeKey = fiscalYear.toString();
|
);
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
dateRangeKey = fiscalYear.toString();
|
||||||
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month1base - 1) / 3) + 1}`;
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month - 1) / 3) + 1}`;
|
||||||
dateRangeKey = `${dataItem.year}-${dataItem.month1base}`;
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month}`;
|
||||||
|
} else { // if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month}-${dataItem.day}`;
|
||||||
|
}
|
||||||
|
} else if (props.chartMode === 'monthly' && 'month1base' in dataItem) {
|
||||||
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
|
dateRangeKey = dataItem.year.toString();
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
|
const fiscalYear = getFiscalYearFromUnixTime(
|
||||||
|
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month1base }),
|
||||||
|
props.fiscalYearStart
|
||||||
|
);
|
||||||
|
dateRangeKey = fiscalYear.toString();
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month1base - 1) / 3) + 1}`;
|
||||||
|
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month1base}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataItems = dateRangeAmountMap[dateRangeKey] || [];
|
const dataItems = dateRangeAmountMap[dateRangeKey] || [];
|
||||||
@@ -197,6 +231,8 @@ const allSeries = computed<MonthlyTrendsChartDataItem[]>(() => {
|
|||||||
dateRangeKey = `${dateRange.year}-${dateRange.quarter}`;
|
dateRangeKey = `${dateRange.year}-${dateRange.quarter}`;
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type && 'month0base' in dateRange) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type && 'month0base' in dateRange) {
|
||||||
dateRangeKey = `${dateRange.year}-${dateRange.month0base + 1}`;
|
dateRangeKey = `${dateRange.year}-${dateRange.month0base + 1}`;
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && 'day' in dateRange && props.chartMode === 'daily') {
|
||||||
|
dateRangeKey = `${dateRange.year}-${dateRange.month}-${dateRange.day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let amount = 0;
|
let amount = 0;
|
||||||
@@ -204,8 +240,14 @@ const allSeries = computed<MonthlyTrendsChartDataItem[]>(() => {
|
|||||||
|
|
||||||
if (isArray(dataItems)) {
|
if (isArray(dataItems)) {
|
||||||
for (const dataItem of dataItems) {
|
for (const dataItem of dataItems) {
|
||||||
if (isNumber(dataItem[props.valueField])) {
|
const value = (dataItem as unknown as Record<string, unknown>)[props.valueField];
|
||||||
amount += dataItem[props.valueField] as number;
|
|
||||||
|
if (isNumber(value)) {
|
||||||
|
if (props.dataAggregationType === ChartDataAggregationType.Sum) {
|
||||||
|
amount += value;
|
||||||
|
} else if (props.dataAggregationType === ChartDataAggregationType.Last) {
|
||||||
|
amount = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +259,7 @@ const allSeries = computed<MonthlyTrendsChartDataItem[]>(() => {
|
|||||||
allAmounts.push(amount);
|
allAmounts.push(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalItem: MonthlyTrendsChartDataItem = {
|
const finalItem: TrendsChartDataItem = {
|
||||||
id: (props.idField && item[props.idField]) ? item[props.idField] as string : getItemName(item[props.nameField] as string),
|
id: (props.idField && item[props.idField]) ? item[props.idField] as string : getItemName(item[props.nameField] as string),
|
||||||
name: (props.idField && item[props.idField]) ? item[props.idField] as string : getItemName(item[props.nameField] as string),
|
name: (props.idField && item[props.idField]) ? item[props.idField] as string : getItemName(item[props.nameField] as string),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
@@ -317,7 +359,7 @@ const chartOptions = computed<object>(() => {
|
|||||||
let tooltip = '';
|
let tooltip = '';
|
||||||
let totalAmount = 0;
|
let totalAmount = 0;
|
||||||
let actualDisplayItemCount = 0;
|
let actualDisplayItemCount = 0;
|
||||||
const displayItems: MonthlyTrendsChartTooltipItem[] = [];
|
const displayItems: TrendsChartTooltipItem[] = [];
|
||||||
|
|
||||||
for (const param of params) {
|
for (const param of params) {
|
||||||
const id = param.seriesId as string;
|
const id = param.seriesId as string;
|
||||||
@@ -435,19 +477,33 @@ function clickItem(e: ECElementEvent): void {
|
|||||||
let minUnixTime = dateRange.minUnixTime;
|
let minUnixTime = dateRange.minUnixTime;
|
||||||
let maxUnixTime = dateRange.maxUnixTime;
|
let maxUnixTime = dateRange.maxUnixTime;
|
||||||
|
|
||||||
if (props.startYearMonth) {
|
if (props.chartMode === 'daily') {
|
||||||
const startMinUnixTime = getYearMonthFirstUnixTime(props.startYearMonth);
|
if (props.startTime) {
|
||||||
|
if (props.startTime > minUnixTime) {
|
||||||
if (startMinUnixTime > minUnixTime) {
|
minUnixTime = props.startTime;
|
||||||
minUnixTime = startMinUnixTime;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (props.endYearMonth) {
|
if (props.endTime) {
|
||||||
const endMaxUnixTime = getYearMonthLastUnixTime(props.endYearMonth);
|
if (props.endTime < maxUnixTime) {
|
||||||
|
maxUnixTime = props.endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (props.chartMode === 'monthly') {
|
||||||
|
if (props.startYearMonth) {
|
||||||
|
const startMinUnixTime = getYearMonthFirstUnixTime(props.startYearMonth);
|
||||||
|
|
||||||
if (endMaxUnixTime < maxUnixTime) {
|
if (startMinUnixTime > minUnixTime) {
|
||||||
maxUnixTime = endMaxUnixTime;
|
minUnixTime = startMinUnixTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.endYearMonth) {
|
||||||
|
const endMaxUnixTime = getYearMonthLastUnixTime(props.endYearMonth);
|
||||||
|
|
||||||
|
if (endMaxUnixTime < maxUnixTime) {
|
||||||
|
maxUnixTime = endMaxUnixTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,15 +554,15 @@ defineExpose({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.monthly-trends-chart-container {
|
.trends-chart-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 720px;
|
height: 720px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) {
|
@media (min-width: 600px) {
|
||||||
.monthly-trends-chart-container {
|
.trends-chart-container {
|
||||||
height: 760px;
|
height: 790px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
+148
-59
@@ -30,23 +30,31 @@
|
|||||||
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
<f7-list v-else-if="!loading && allDisplayDataItems && allDisplayDataItems.data && allDisplayDataItems.data.length">
|
<f7-list v-if="!loading && allDisplayDataItems && allDisplayDataItems.data && allDisplayDataItems.data.length">
|
||||||
<f7-list-item v-if="allDisplayDataItems.legends && allDisplayDataItems.legends.length > 1">
|
<f7-list-item v-if="allDisplayDataItems.legends && allDisplayDataItems.legends.length > 1">
|
||||||
<div class="display-flex" style="flex-wrap: wrap">
|
<div class="display-flex" style="flex-wrap: wrap">
|
||||||
<div class="monthly-trends-bar-chart-legend display-flex align-items-center"
|
<div class="trends-bar-chart-legend display-flex align-items-center"
|
||||||
:class="{ 'monthly-trends-bar-chart-legend-unselected': !!unselectedLegends[legend.id] }"
|
:class="{ 'trends-bar-chart-legend-unselected': !!unselectedLegends[legend.id] }"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
v-for="(legend, idx) in allDisplayDataItems.legends"
|
v-for="(legend, idx) in allDisplayDataItems.legends"
|
||||||
@click="toggleLegend(legend)">
|
@click="toggleLegend(legend)">
|
||||||
<f7-icon f7="app_fill" class="monthly-trends-bar-chart-legend-icon" :style="{ 'color': unselectedLegends[legend.id] ? '' : legend.color }"></f7-icon>
|
<f7-icon f7="app_fill" class="trends-bar-chart-legend-icon" :style="{ 'color': unselectedLegends[legend.id] ? '' : legend.color }"></f7-icon>
|
||||||
<span class="monthly-trends-bar-chart-legend-text">{{ legend.name }}</span>
|
<span class="trends-bar-chart-legend-text">{{ legend.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-list :key="`trends-bar-chart-${allDisplayDataItemsVersion}`"
|
||||||
|
:virtual-list="useVirtualList"
|
||||||
|
:virtual-list-params="useVirtualList ? { items: allDisplayDataItems.data, renderExternal, height: 'auto' } : undefined"
|
||||||
|
v-if="!loading && allDisplayDataItems && allDisplayDataItems.data && allDisplayDataItems.data.length">
|
||||||
<f7-list-item link="#"
|
<f7-list-item link="#"
|
||||||
:key="idx"
|
:key="item.index"
|
||||||
:class="{ 'statistics-list-item': true, 'statistics-list-item-stacked': stacked, 'statistics-list-item-non-stacked': !stacked }"
|
:class="{ 'statistics-list-item': true, 'statistics-list-item-stacked': stacked, 'statistics-list-item-non-stacked': !stacked }"
|
||||||
v-for="(item, idx) in allDisplayDataItems.data"
|
:style="useVirtualList ? `top: ${virtualDataItems.topPosition}px` : undefined"
|
||||||
|
:virtual-list-index="item.index"
|
||||||
|
v-for="item in (useVirtualList ? virtualDataItems.items : allDisplayDataItems.data)"
|
||||||
@click="clickItem(item)"
|
@click="clickItem(item)"
|
||||||
>
|
>
|
||||||
<template #media>
|
<template #media>
|
||||||
@@ -105,21 +113,32 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from '@/locales/helpers.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
import { type CommonMonthlyTrendsChartProps, type MonthlyTrendsBarChartClickEvent, useMonthlyTrendsChartBase } from '@/components/base/MonthlyTrendsChartBase.ts'
|
import {
|
||||||
|
type TrendsChartDateType,
|
||||||
|
type CommonTrendsChartProps,
|
||||||
|
type TrendsBarChartClickEvent,
|
||||||
|
useTrendsChartBase
|
||||||
|
} from '@/components/base/TrendsChartBase.ts'
|
||||||
|
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
|
|
||||||
import { itemAndIndex } from '@/core/base.ts';
|
import { itemAndIndex } from '@/core/base.ts';
|
||||||
import { type Year1BasedMonth, type UnixTimeRange, DateRangeScene } from '@/core/datetime.ts';
|
import {
|
||||||
|
type UnixTimeRange,
|
||||||
|
DateRangeScene
|
||||||
|
} from '@/core/datetime.ts';
|
||||||
import type { ColorStyleValue } from '@/core/color.ts';
|
import type { ColorStyleValue } from '@/core/color.ts';
|
||||||
import { ChartDateAggregationType } from '@/core/statistics.ts';
|
import {
|
||||||
|
ChartDataAggregationType,
|
||||||
|
ChartDateAggregationType
|
||||||
|
} from '@/core/statistics.ts';
|
||||||
|
|
||||||
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
||||||
|
|
||||||
import type { YearMonthDataItem, SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
import type { SortableTransactionStatisticDataItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isNumber
|
isNumber
|
||||||
@@ -144,37 +163,44 @@ interface TrendsBarChartLegend {
|
|||||||
readonly displayOrders: number[];
|
readonly displayOrders: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MonthlyTrendsBarChartDataAmount extends SortableTransactionStatisticDataItem, TrendsBarChartLegend {
|
interface TrendsBarChartDataAmount extends SortableTransactionStatisticDataItem, TrendsBarChartLegend {
|
||||||
totalAmount: number;
|
totalAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MonthlyTrendsBarChartDataItem {
|
interface TrendsBarChartDataItem {
|
||||||
|
index: number;
|
||||||
dateRange: UnixTimeRange;
|
dateRange: UnixTimeRange;
|
||||||
displayDateRange: string;
|
displayDateRange: string;
|
||||||
items: MonthlyTrendsBarChartDataAmount[];
|
items: TrendsBarChartDataAmount[];
|
||||||
totalAmount: number;
|
totalAmount: number;
|
||||||
totalPositiveAmount: number;
|
totalPositiveAmount: number;
|
||||||
maxAmount: number;
|
maxAmount: number;
|
||||||
percent: number;
|
percent: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MonthlyTrendsBarChartData {
|
interface TrendsBarChartVirtualListData {
|
||||||
readonly data: MonthlyTrendsBarChartDataItem[];
|
items: TrendsBarChartDataItem[],
|
||||||
|
topPosition: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TrendsBarChartData {
|
||||||
|
readonly data: TrendsBarChartDataItem[];
|
||||||
readonly legends: TrendsBarChartLegend[];
|
readonly legends: TrendsBarChartLegend[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MobileMonthlyTrendsChartProps<T extends Year1BasedMonth> extends CommonMonthlyTrendsChartProps<T> {
|
interface MobileTrendsChartProps<T extends TrendsChartDateType> extends CommonTrendsChartProps<T> {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<MobileMonthlyTrendsChartProps<YearMonthDataItem>>();
|
const props = defineProps<MobileTrendsChartProps<TrendsChartDateType>>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'click', value: MonthlyTrendsBarChartClickEvent): void;
|
(e: 'click', value: TrendsBarChartClickEvent): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tt,
|
tt,
|
||||||
|
formatUnixTimeToShortDate,
|
||||||
formatUnixTimeToGregorianLikeShortYear,
|
formatUnixTimeToGregorianLikeShortYear,
|
||||||
formatUnixTimeToGregorianLikeShortYearMonth,
|
formatUnixTimeToGregorianLikeShortYearMonth,
|
||||||
formatYearQuarterToGregorianLikeYearQuarter,
|
formatYearQuarterToGregorianLikeYearQuarter,
|
||||||
@@ -182,14 +208,22 @@ const {
|
|||||||
formatAmountToLocalizedNumeralsWithCurrency
|
formatAmountToLocalizedNumeralsWithCurrency
|
||||||
} = useI18n();
|
} = useI18n();
|
||||||
|
|
||||||
const { allDateRanges, getItemName } = useMonthlyTrendsChartBase(props);
|
const { allDateRanges, getItemName } = useTrendsChartBase(props);
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const allDisplayDataItemsVersion = ref<number>(0);
|
||||||
const unselectedLegends = ref<Record<string, boolean>>({});
|
const unselectedLegends = ref<Record<string, boolean>>({});
|
||||||
|
|
||||||
const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
const virtualDataItems = ref<TrendsBarChartVirtualListData>({
|
||||||
const allDateRangeItemsMap: Record<string, MonthlyTrendsBarChartDataAmount[]> = {};
|
items: [],
|
||||||
|
topPosition: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const useVirtualList = computed<boolean>(() => allDisplayDataItems.value.legends.length <= 1 || props.stacked);
|
||||||
|
|
||||||
|
const allDisplayDataItems = computed<TrendsBarChartData>(() => {
|
||||||
|
const allDateRangeItemsMap: Record<string, TrendsBarChartDataAmount[]> = {};
|
||||||
const legends: TrendsBarChartLegend[] = [];
|
const legends: TrendsBarChartLegend[] = [];
|
||||||
|
|
||||||
for (const [item, index] of itemAndIndex(props.items)) {
|
for (const [item, index] of itemAndIndex(props.items)) {
|
||||||
@@ -212,31 +246,57 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateRangeItemMap: Record<string, MonthlyTrendsBarChartDataAmount> = {};
|
const dateRangeItemMap: Record<string, TrendsBarChartDataAmount> = {};
|
||||||
|
|
||||||
for (const dataItem of item.items) {
|
for (const dataItem of item.items) {
|
||||||
let dateRangeKey = '';
|
let dateRangeKey = '';
|
||||||
|
|
||||||
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
if (props.chartMode === 'daily' && 'month' in dataItem) {
|
||||||
dateRangeKey = dataItem.year.toString();
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
dateRangeKey = dataItem.year.toString();
|
||||||
const fiscalYear = getFiscalYearFromUnixTime(
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month1base }),
|
const fiscalYear = getFiscalYearFromUnixTime(
|
||||||
props.fiscalYearStart
|
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month }),
|
||||||
);
|
props.fiscalYearStart
|
||||||
dateRangeKey = fiscalYear.toString();
|
);
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
dateRangeKey = fiscalYear.toString();
|
||||||
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month1base - 1) / 3) + 1}`;
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month - 1) / 3) + 1}`;
|
||||||
dateRangeKey = `${dataItem.year}-${dataItem.month1base}`;
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month}`;
|
||||||
|
} else { // if (props.dateAggregationType === ChartDateAggregationType.Day.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month}-${dataItem.day}`;
|
||||||
|
}
|
||||||
|
} else if (props.chartMode === 'monthly' && 'month1base' in dataItem) {
|
||||||
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
|
dateRangeKey = dataItem.year.toString();
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
|
const fiscalYear = getFiscalYearFromUnixTime(
|
||||||
|
getYearMonthFirstUnixTime({ year: dataItem.year, month1base: dataItem.month1base }),
|
||||||
|
props.fiscalYearStart
|
||||||
|
);
|
||||||
|
dateRangeKey = fiscalYear.toString();
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${Math.floor((dataItem.month1base - 1) / 3) + 1}`;
|
||||||
|
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
dateRangeKey = `${dataItem.year}-${dataItem.month1base}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const value = (dataItem as unknown as Record<string, unknown>)[props.valueField];
|
||||||
|
|
||||||
if (dateRangeItemMap[dateRangeKey]) {
|
if (dateRangeItemMap[dateRangeKey]) {
|
||||||
dateRangeItemMap[dateRangeKey]!.totalAmount += (props.valueField && isNumber(dataItem[props.valueField])) ? dataItem[props.valueField] as number : 0;
|
if (isNumber(value)) {
|
||||||
|
if (props.dataAggregationType === ChartDataAggregationType.Sum) {
|
||||||
|
dateRangeItemMap[dateRangeKey]!.totalAmount += value;
|
||||||
|
} else if (props.dataAggregationType === ChartDataAggregationType.Last) {
|
||||||
|
dateRangeItemMap[dateRangeKey]!.totalAmount = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const allDataItems: MonthlyTrendsBarChartDataAmount[] = allDateRangeItemsMap[dateRangeKey] || [];
|
const allDataItems: TrendsBarChartDataAmount[] = allDateRangeItemsMap[dateRangeKey] || [];
|
||||||
const finalDataItem: MonthlyTrendsBarChartDataAmount = Object.assign({}, legend, {
|
const finalDataItem: TrendsBarChartDataAmount = Object.assign({}, legend, {
|
||||||
totalAmount: (props.valueField && isNumber(dataItem[props.valueField])) ? dataItem[props.valueField] as number : 0
|
totalAmount: isNumber(value) ? value : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
allDataItems.push(finalDataItem);
|
allDataItems.push(finalDataItem);
|
||||||
@@ -246,7 +306,7 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalDataItems: MonthlyTrendsBarChartDataItem[] = [];
|
const finalDataItems: TrendsBarChartDataItem[] = [];
|
||||||
let maxTotalAmount = 0;
|
let maxTotalAmount = 0;
|
||||||
|
|
||||||
for (const dateRange of allDateRanges.value) {
|
for (const dateRange of allDateRanges.value) {
|
||||||
@@ -260,6 +320,8 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
dateRangeKey = `${dateRange.year}-${dateRange.quarter}`;
|
dateRangeKey = `${dateRange.year}-${dateRange.quarter}`;
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type && 'month0base' in dateRange) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type && 'month0base' in dateRange) {
|
||||||
dateRangeKey = `${dateRange.year}-${dateRange.month0base + 1}`;
|
dateRangeKey = `${dateRange.year}-${dateRange.month0base + 1}`;
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && 'day' in dateRange && props.chartMode === 'daily') {
|
||||||
|
dateRangeKey = `${dateRange.year}-${dateRange.month}-${dateRange.day}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let displayDateRange = '';
|
let displayDateRange = '';
|
||||||
@@ -270,8 +332,10 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
displayDateRange = formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime);
|
displayDateRange = formatUnixTimeToGregorianLikeFiscalYear(dateRange.minUnixTime);
|
||||||
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) {
|
||||||
displayDateRange = formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter);
|
displayDateRange = formatYearQuarterToGregorianLikeYearQuarter(dateRange.year, dateRange.quarter);
|
||||||
} else { // if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
displayDateRange = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
displayDateRange = formatUnixTimeToGregorianLikeShortYearMonth(dateRange.minUnixTime);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Day.type && props.chartMode === 'daily') {
|
||||||
|
displayDateRange = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataItems = allDateRangeItemsMap[dateRangeKey] || [];
|
const dataItems = allDateRangeItemsMap[dateRangeKey] || [];
|
||||||
@@ -297,7 +361,8 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
maxTotalAmount = totalAmount;
|
maxTotalAmount = totalAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalDataItem: MonthlyTrendsBarChartDataItem = {
|
const finalDataItem: TrendsBarChartDataItem = {
|
||||||
|
index: finalDataItems.length,
|
||||||
dateRange: dateRange,
|
dateRange: dateRange,
|
||||||
displayDateRange: displayDateRange,
|
displayDateRange: displayDateRange,
|
||||||
items: dataItems,
|
items: dataItems,
|
||||||
@@ -324,7 +389,7 @@ const allDisplayDataItems = computed<MonthlyTrendsBarChartData>(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function clickItem(item: MonthlyTrendsBarChartDataItem): void {
|
function clickItem(item: TrendsBarChartDataItem): void {
|
||||||
let itemId = '';
|
let itemId = '';
|
||||||
|
|
||||||
for (const item of props.items) {
|
for (const item of props.items) {
|
||||||
@@ -349,19 +414,33 @@ function clickItem(item: MonthlyTrendsBarChartDataItem): void {
|
|||||||
let minUnixTime = dateRange.minUnixTime;
|
let minUnixTime = dateRange.minUnixTime;
|
||||||
let maxUnixTime = dateRange.maxUnixTime;
|
let maxUnixTime = dateRange.maxUnixTime;
|
||||||
|
|
||||||
if (props.startYearMonth) {
|
if (props.chartMode === 'daily') {
|
||||||
const startMinUnixTime = getYearMonthFirstUnixTime(props.startYearMonth);
|
if (props.startTime) {
|
||||||
|
if (props.startTime > minUnixTime) {
|
||||||
if (startMinUnixTime > minUnixTime) {
|
minUnixTime = props.startTime;
|
||||||
minUnixTime = startMinUnixTime;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (props.endYearMonth) {
|
if (props.endTime) {
|
||||||
const endMaxUnixTime = getYearMonthLastUnixTime(props.endYearMonth);
|
if (props.endTime < maxUnixTime) {
|
||||||
|
maxUnixTime = props.endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (props.chartMode === 'monthly') {
|
||||||
|
if (props.startYearMonth) {
|
||||||
|
const startMinUnixTime = getYearMonthFirstUnixTime(props.startYearMonth);
|
||||||
|
|
||||||
if (endMaxUnixTime < maxUnixTime) {
|
if (startMinUnixTime > minUnixTime) {
|
||||||
maxUnixTime = endMaxUnixTime;
|
minUnixTime = startMinUnixTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.endYearMonth) {
|
||||||
|
const endMaxUnixTime = getYearMonthLastUnixTime(props.endYearMonth);
|
||||||
|
|
||||||
|
if (endMaxUnixTime < maxUnixTime) {
|
||||||
|
maxUnixTime = endMaxUnixTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,28 +463,38 @@ function toggleLegend(legend: TrendsBarChartLegend): void {
|
|||||||
unselectedLegends.value[legend.id] = true;
|
unselectedLegends.value[legend.id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderExternal(vl: unknown, vlData: TrendsBarChartVirtualListData): void {
|
||||||
|
virtualDataItems.value = vlData;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(allDisplayDataItems, () => {
|
||||||
|
allDisplayDataItemsVersion.value++;
|
||||||
|
}, {
|
||||||
|
deep: true
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.monthly-trends-bar-chart-legend {
|
.trends-bar-chart-legend {
|
||||||
margin-inline-end: 4px;
|
margin-inline-end: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monthly-trends-bar-chart-legend-icon.f7-icons {
|
.trends-bar-chart-legend-icon.f7-icons {
|
||||||
font-size: var(--ebk-trends-bar-chart-legend-icon-font-size);
|
font-size: var(--ebk-trends-bar-chart-legend-icon-font-size);
|
||||||
margin-inline-end: 2px;
|
margin-inline-end: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monthly-trends-bar-chart-legend-unselected .monthly-trends-bar-chart-legend-icon.f7-icons {
|
.trends-bar-chart-legend-unselected .trends-bar-chart-legend-icon.f7-icons {
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monthly-trends-bar-chart-legend-text {
|
.trends-bar-chart-legend-text {
|
||||||
font-size: var(--ebk-trends-bar-chart-legend-text-font-size);
|
font-size: var(--ebk-trends-bar-chart-legend-text-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.monthly-trends-bar-chart-legend-unselected .monthly-trends-bar-chart-legend-text {
|
.trends-bar-chart-legend-unselected .trends-bar-chart-legend-text {
|
||||||
color: #cccccc;
|
color: #cccccc;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
+21
-19
@@ -28,6 +28,7 @@ export interface DateTime {
|
|||||||
getSecond(): number;
|
getSecond(): number;
|
||||||
getDisplayAMPM(options: DateTimeFormatOptions): string;
|
getDisplayAMPM(options: DateTimeFormatOptions): string;
|
||||||
getTimezoneUtcOffsetMinutes(): number;
|
getTimezoneUtcOffsetMinutes(): number;
|
||||||
|
getDateTimeAfterDays(day: number): DateTime;
|
||||||
toGregorianCalendarYearMonthDay(): YearMonthDay;
|
toGregorianCalendarYearMonthDay(): YearMonthDay;
|
||||||
toGregorianCalendarYear0BasedMonth(): Year0BasedMonth;
|
toGregorianCalendarYear0BasedMonth(): Year0BasedMonth;
|
||||||
format(format: string, options: DateTimeFormatOptions): string;
|
format(format: string, options: DateTimeFormatOptions): string;
|
||||||
@@ -584,7 +585,8 @@ export class ShortTimeFormat implements TimeFormat {
|
|||||||
|
|
||||||
export enum DateRangeScene {
|
export enum DateRangeScene {
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
TrendAnalysis = 1
|
TrendAnalysis = 1,
|
||||||
|
AssetTrends = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DateRange implements TypeAndName {
|
export class DateRange implements TypeAndName {
|
||||||
@@ -592,38 +594,38 @@ export class DateRange implements TypeAndName {
|
|||||||
private static readonly allInstancesByType: Record<number, DateRange> = {};
|
private static readonly allInstancesByType: Record<number, DateRange> = {};
|
||||||
|
|
||||||
// All date range
|
// All date range
|
||||||
public static readonly All = new DateRange(0, 'All', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly All = new DateRange(0, 'All', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
// Date ranges for normal scene only
|
// Date ranges for normal scene only
|
||||||
public static readonly Today = new DateRange(1, 'Today', false, false, DateRangeScene.Normal);
|
public static readonly Today = new DateRange(1, 'Today', false, false, DateRangeScene.Normal);
|
||||||
public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, DateRangeScene.Normal);
|
public static readonly Yesterday = new DateRange(2, 'Yesterday', false, false, DateRangeScene.Normal);
|
||||||
public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, DateRangeScene.Normal);
|
public static readonly LastSevenDays = new DateRange(3, 'Recent 7 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, DateRangeScene.Normal);
|
public static readonly LastThirtyDays = new DateRange(4, 'Recent 30 days', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
public static readonly ThisWeek = new DateRange(5, 'This week', false, false, DateRangeScene.Normal);
|
public static readonly ThisWeek = new DateRange(5, 'This week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
public static readonly LastWeek = new DateRange(6, 'Last week', false, false, DateRangeScene.Normal);
|
public static readonly LastWeek = new DateRange(6, 'Last week', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
public static readonly ThisMonth = new DateRange(7, 'This month', false, false, DateRangeScene.Normal);
|
public static readonly ThisMonth = new DateRange(7, 'This month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
public static readonly LastMonth = new DateRange(8, 'Last month', false, false, DateRangeScene.Normal);
|
public static readonly LastMonth = new DateRange(8, 'Last month', false, false, DateRangeScene.Normal, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
// Date ranges for normal and trend analysis scene
|
// Date ranges for normal and trend analysis scene
|
||||||
public static readonly ThisYear = new DateRange(9, 'This year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly ThisYear = new DateRange(9, 'This year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly LastYear = new DateRange(10, 'Last year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly LastYear = new DateRange(10, 'Last year', false, false, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly ThisFiscalYear = new DateRange(11, 'This fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly LastFiscalYear = new DateRange(12, 'Last fiscal year', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
// Billing cycle date ranges for normal scene only
|
// Billing cycle date ranges for normal scene only
|
||||||
public static readonly CurrentBillingCycle = new DateRange(51, 'Current Billing Cycle', true, true, DateRangeScene.Normal);
|
public static readonly CurrentBillingCycle = new DateRange(51, 'Current Billing Cycle', true, true, DateRangeScene.Normal);
|
||||||
public static readonly PreviousBillingCycle = new DateRange(52, 'Previous Billing Cycle', true, true, DateRangeScene.Normal);
|
public static readonly PreviousBillingCycle = new DateRange(52, 'Previous Billing Cycle', true, true, DateRangeScene.Normal);
|
||||||
|
|
||||||
// Date ranges for trend analysis scene only
|
// Date ranges for trend analysis scene only
|
||||||
public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentTwelveMonths = new DateRange(101, 'Recent 12 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentTwentyFourMonths = new DateRange(102, 'Recent 24 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentThirtySixMonths = new DateRange(103, 'Recent 36 months', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentTwoYears = new DateRange(104, 'Recent 2 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentThreeYears = new DateRange(105, 'Recent 3 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, DateRangeScene.TrendAnalysis);
|
public static readonly RecentFiveYears = new DateRange(106, 'Recent 5 years', false, false, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
// Custom date range
|
// Custom date range
|
||||||
public static readonly Custom = new DateRange(255, 'Custom Date', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis);
|
public static readonly Custom = new DateRange(255, 'Custom Date', false, true, DateRangeScene.Normal, DateRangeScene.TrendAnalysis, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
public readonly type: number;
|
public readonly type: number;
|
||||||
public readonly name: string;
|
public readonly name: string;
|
||||||
|
|||||||
+8
-1
@@ -7,7 +7,8 @@ import {
|
|||||||
ChartDataType,
|
ChartDataType,
|
||||||
ChartSortingType,
|
ChartSortingType,
|
||||||
DEFAULT_CATEGORICAL_CHART_DATA_RANGE,
|
DEFAULT_CATEGORICAL_CHART_DATA_RANGE,
|
||||||
DEFAULT_TREND_CHART_DATA_RANGE
|
DEFAULT_TREND_CHART_DATA_RANGE,
|
||||||
|
DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE
|
||||||
} from './statistics.ts';
|
} from './statistics.ts';
|
||||||
import { DEFAULT_CURRENCY_CODE } from '@/consts/currency.ts';
|
import { DEFAULT_CURRENCY_CODE } from '@/consts/currency.ts';
|
||||||
|
|
||||||
@@ -63,6 +64,8 @@ export interface ApplicationSettings extends BaseApplicationSetting {
|
|||||||
defaultCategoricalChartDataRangeType: number;
|
defaultCategoricalChartDataRangeType: number;
|
||||||
defaultTrendChartType: number;
|
defaultTrendChartType: number;
|
||||||
defaultTrendChartDataRangeType: number;
|
defaultTrendChartDataRangeType: number;
|
||||||
|
defaultAssetTrendsChartType: number;
|
||||||
|
defaultAssetTrendsChartDataRangeType: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +125,8 @@ export const ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES: Record<string, UserAp
|
|||||||
'statistics.defaultCategoricalChartDataRangeType': UserApplicationCloudSettingType.Number,
|
'statistics.defaultCategoricalChartDataRangeType': UserApplicationCloudSettingType.Number,
|
||||||
'statistics.defaultTrendChartType': UserApplicationCloudSettingType.Number,
|
'statistics.defaultTrendChartType': UserApplicationCloudSettingType.Number,
|
||||||
'statistics.defaultTrendChartDataRangeType': UserApplicationCloudSettingType.Number,
|
'statistics.defaultTrendChartDataRangeType': UserApplicationCloudSettingType.Number,
|
||||||
|
'statistics.defaultAssetTrendsChartType': UserApplicationCloudSettingType.Number,
|
||||||
|
'statistics.defaultAssetTrendsChartDataRangeType': UserApplicationCloudSettingType.Number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_APPLICATION_SETTINGS: ApplicationSettings = {
|
export const DEFAULT_APPLICATION_SETTINGS: ApplicationSettings = {
|
||||||
@@ -168,6 +173,8 @@ export const DEFAULT_APPLICATION_SETTINGS: ApplicationSettings = {
|
|||||||
defaultCategoricalChartDataRangeType: DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type,
|
defaultCategoricalChartDataRangeType: DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type,
|
||||||
defaultTrendChartType: TrendChartType.Default.type,
|
defaultTrendChartType: TrendChartType.Default.type,
|
||||||
defaultTrendChartDataRangeType: DEFAULT_TREND_CHART_DATA_RANGE.type,
|
defaultTrendChartDataRangeType: DEFAULT_TREND_CHART_DATA_RANGE.type,
|
||||||
|
defaultAssetTrendsChartType: TrendChartType.Default.type,
|
||||||
|
defaultAssetTrendsChartDataRangeType: DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE.type,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+46
-10
@@ -3,7 +3,13 @@ import { DateRange } from '@/core/datetime.ts';
|
|||||||
|
|
||||||
export enum StatisticsAnalysisType {
|
export enum StatisticsAnalysisType {
|
||||||
CategoricalAnalysis = 0,
|
CategoricalAnalysis = 0,
|
||||||
TrendAnalysis = 1
|
TrendAnalysis = 1,
|
||||||
|
AssetTrends = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChartDataAggregationType {
|
||||||
|
Sum = 0,
|
||||||
|
Last = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CategoricalChartType implements TypeAndName {
|
export class CategoricalChartType implements TypeAndName {
|
||||||
@@ -115,16 +121,18 @@ export class ChartDataType implements TypeAndName {
|
|||||||
public static readonly IncomeByAccount = new ChartDataType(3, 'Income By Account', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly IncomeByAccount = new ChartDataType(3, 'Income By Account', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly IncomeByPrimaryCategory = new ChartDataType(4, 'Income By Primary Category', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly IncomeByPrimaryCategory = new ChartDataType(4, 'Income By Primary Category', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly IncomeBySecondaryCategory = new ChartDataType(5, 'Income By Secondary Category', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly IncomeBySecondaryCategory = new ChartDataType(5, 'Income By Secondary Category', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly AccountTotalAssets = new ChartDataType(6, 'Account Total Assets', false, false, StatisticsAnalysisType.CategoricalAnalysis);
|
public static readonly AccountTotalAssets = new ChartDataType(6, 'Account Total Assets', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
public static readonly AccountTotalLiabilities = new ChartDataType(7, 'Account Total Liabilities', false, false, StatisticsAnalysisType.CategoricalAnalysis);
|
public static readonly AccountTotalLiabilities = new ChartDataType(7, 'Account Total Liabilities', false, false, StatisticsAnalysisType.CategoricalAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
public static readonly TotalOutflows = new ChartDataType(13, 'Total Outflows', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly TotalOutflows = new ChartDataType(13, 'Total Outflows', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly TotalExpense = new ChartDataType(8, 'Total Expense', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly TotalExpense = new ChartDataType(8, 'Total Expense', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly TotalInflows = new ChartDataType(14, 'Total Inflows', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly TotalInflows = new ChartDataType(14, 'Total Inflows', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly TotalIncome = new ChartDataType(9, 'Total Income', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly TotalIncome = new ChartDataType(9, 'Total Income', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly NetCashFlow = new ChartDataType(15, 'Net Cash Flow', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly NetCashFlow = new ChartDataType(15, 'Net Cash Flow', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
public static readonly NetIncome = new ChartDataType(10, 'Net Income', false, false, StatisticsAnalysisType.TrendAnalysis);
|
public static readonly NetIncome = new ChartDataType(10, 'Net Income', false, false, StatisticsAnalysisType.TrendAnalysis);
|
||||||
|
public static readonly NetWorth = new ChartDataType(17, 'Net Worth', false, false, StatisticsAnalysisType.AssetTrends);
|
||||||
|
|
||||||
public static readonly Default = ChartDataType.ExpenseByPrimaryCategory;
|
public static readonly Default = ChartDataType.ExpenseByPrimaryCategory;
|
||||||
|
public static readonly DefaultForAssetTrends = ChartDataType.NetWorth;
|
||||||
|
|
||||||
public readonly type: number;
|
public readonly type: number;
|
||||||
public readonly name: string;
|
public readonly name: string;
|
||||||
@@ -221,28 +229,55 @@ export class ChartDateAggregationType {
|
|||||||
private static readonly allInstances: ChartDateAggregationType[] = [];
|
private static readonly allInstances: ChartDateAggregationType[] = [];
|
||||||
private static readonly allInstancesByType: Record<number, ChartDateAggregationType> = {};
|
private static readonly allInstancesByType: Record<number, ChartDateAggregationType> = {};
|
||||||
|
|
||||||
public static readonly Month = new ChartDateAggregationType(0, 'Monthly', 'Aggregate by Month');
|
public static readonly Day = new ChartDateAggregationType(4, 'Daily', 'Aggregate by Day', StatisticsAnalysisType.AssetTrends);
|
||||||
public static readonly Quarter = new ChartDateAggregationType(1, 'Quarterly', 'Aggregate by Quarter');
|
public static readonly Month = new ChartDateAggregationType(0, 'Monthly', 'Aggregate by Month', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
public static readonly Year = new ChartDateAggregationType(2, 'Yearly', 'Aggregate by Year');
|
public static readonly Quarter = new ChartDateAggregationType(1, 'Quarterly', 'Aggregate by Quarter', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
public static readonly FiscalYear = new ChartDateAggregationType(3, 'FiscalYearly', 'Aggregate by Fiscal Year');
|
public static readonly Year = new ChartDateAggregationType(2, 'Yearly', 'Aggregate by Year', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
|
public static readonly FiscalYear = new ChartDateAggregationType(3, 'FiscalYearly', 'Aggregate by Fiscal Year', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends);
|
||||||
|
|
||||||
public static readonly Default = ChartDateAggregationType.Month;
|
public static readonly Default = ChartDateAggregationType.Month;
|
||||||
|
|
||||||
public readonly type: number;
|
public readonly type: number;
|
||||||
public readonly shortName: string;
|
public readonly shortName: string;
|
||||||
public readonly fullName: string;
|
public readonly fullName: string;
|
||||||
|
private readonly availableAnalysisTypes: Record<number, boolean>;
|
||||||
|
|
||||||
private constructor(type: number, shortName: string, fullName: string) {
|
private constructor(type: number, shortName: string, fullName: string, ...availableAnalysisTypes: StatisticsAnalysisType[]) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.shortName = shortName;
|
this.shortName = shortName;
|
||||||
this.fullName = fullName;
|
this.fullName = fullName;
|
||||||
|
this.availableAnalysisTypes = {};
|
||||||
|
|
||||||
|
if (availableAnalysisTypes) {
|
||||||
|
for (const analysisType of availableAnalysisTypes) {
|
||||||
|
this.availableAnalysisTypes[analysisType] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ChartDateAggregationType.allInstances.push(this);
|
ChartDateAggregationType.allInstances.push(this);
|
||||||
ChartDateAggregationType.allInstancesByType[type] = this;
|
ChartDateAggregationType.allInstancesByType[type] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static values(): ChartDateAggregationType[] {
|
public isAvailableAnalysisType(analysisType: StatisticsAnalysisType): boolean {
|
||||||
return ChartDateAggregationType.allInstances;
|
return this.availableAnalysisTypes[analysisType] || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static values(analysisType?: StatisticsAnalysisType): ChartDateAggregationType[] {
|
||||||
|
const availableInstances: ChartDateAggregationType[] = ChartDateAggregationType.allInstances;
|
||||||
|
|
||||||
|
if (analysisType === undefined) {
|
||||||
|
return availableInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret: ChartDateAggregationType[] = [];
|
||||||
|
|
||||||
|
for (const chartDataType of availableInstances) {
|
||||||
|
if (chartDataType.isAvailableAnalysisType(analysisType)) {
|
||||||
|
ret.push(chartDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static valueOf(type: number): ChartDateAggregationType | undefined {
|
public static valueOf(type: number): ChartDateAggregationType | undefined {
|
||||||
@@ -252,3 +287,4 @@ export class ChartDateAggregationType {
|
|||||||
|
|
||||||
export const DEFAULT_CATEGORICAL_CHART_DATA_RANGE: DateRange = DateRange.ThisMonth;
|
export const DEFAULT_CATEGORICAL_CHART_DATA_RANGE: DateRange = DateRange.ThisMonth;
|
||||||
export const DEFAULT_TREND_CHART_DATA_RANGE: DateRange = DateRange.ThisYear;
|
export const DEFAULT_TREND_CHART_DATA_RANGE: DateRange = DateRange.ThisYear;
|
||||||
|
export const DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE: DateRange = DateRange.ThisYear;
|
||||||
|
|||||||
+2
-2
@@ -100,7 +100,7 @@ import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
|||||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||||
import PieChartComponent from '@/components/desktop/PieChart.vue';
|
import PieChartComponent from '@/components/desktop/PieChart.vue';
|
||||||
import RadarChartComponent from '@/components/desktop/RadarChart.vue';
|
import RadarChartComponent from '@/components/desktop/RadarChart.vue';
|
||||||
import MonthlyTrendsChart from '@/components/desktop/MonthlyTrendsChart.vue';
|
import TrendsChart from '@/components/desktop/TrendsChart.vue';
|
||||||
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
||||||
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
||||||
import MonthRangeSelectionDialog from '@/components/desktop/MonthRangeSelectionDialog.vue';
|
import MonthRangeSelectionDialog from '@/components/desktop/MonthRangeSelectionDialog.vue';
|
||||||
@@ -542,7 +542,7 @@ app.component('ConfirmDialog', ConfirmDialog);
|
|||||||
app.component('SnackBar', SnackBar);
|
app.component('SnackBar', SnackBar);
|
||||||
app.component('PieChart', PieChartComponent);
|
app.component('PieChart', PieChartComponent);
|
||||||
app.component('RadarChart', RadarChartComponent);
|
app.component('RadarChart', RadarChartComponent);
|
||||||
app.component('MonthlyTrendsChart', MonthlyTrendsChart);
|
app.component('TrendsChart', TrendsChart);
|
||||||
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
||||||
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
||||||
app.component('MonthRangeSelectionDialog', MonthRangeSelectionDialog);
|
app.component('MonthRangeSelectionDialog', MonthRangeSelectionDialog);
|
||||||
|
|||||||
@@ -282,6 +282,10 @@ class MomentDateTime implements DateTime {
|
|||||||
return this.instance.utcOffset();
|
return this.instance.utcOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getDateTimeAfterDays(days: number): DateTime {
|
||||||
|
return MomentDateTime.of(this.instance.clone().add(days, 'days'));
|
||||||
|
}
|
||||||
|
|
||||||
public toGregorianCalendarYearMonthDay(): YearMonthDay {
|
public toGregorianCalendarYearMonthDay(): YearMonthDay {
|
||||||
return {
|
return {
|
||||||
year: this.instance.year(),
|
year: this.instance.year(),
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ import type {
|
|||||||
TransactionStatisticResponse,
|
TransactionStatisticResponse,
|
||||||
TransactionStatisticTrendsRequest,
|
TransactionStatisticTrendsRequest,
|
||||||
TransactionStatisticTrendsResponseItem,
|
TransactionStatisticTrendsResponseItem,
|
||||||
|
TransactionStatisticAssetTrendsRequest,
|
||||||
|
TransactionStatisticAssetTrendsResponseItem,
|
||||||
TransactionAmountsRequestParams,
|
TransactionAmountsRequestParams,
|
||||||
TransactionAmountsResponse
|
TransactionAmountsResponse
|
||||||
} from '@/models/transaction.ts';
|
} from '@/models/transaction.ts';
|
||||||
@@ -536,6 +538,19 @@ export default {
|
|||||||
|
|
||||||
return axios.get<ApiResponse<TransactionStatisticTrendsResponseItem[]>>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
return axios.get<ApiResponse<TransactionStatisticTrendsResponseItem[]>>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
||||||
},
|
},
|
||||||
|
getTransactionStatisticsAssetTrends: (req: TransactionStatisticAssetTrendsRequest): ApiResponsePromise<TransactionStatisticAssetTrendsResponseItem[]> => {
|
||||||
|
const queryParams = [];
|
||||||
|
|
||||||
|
if (req.startTime) {
|
||||||
|
queryParams.push(`start_time=${req.startTime}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.endTime) {
|
||||||
|
queryParams.push(`end_time=${req.endTime}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios.get<ApiResponse<TransactionStatisticAssetTrendsResponseItem[]>>('v1/transactions/statistics/asset_trends.json' + (queryParams.length ? '?' + queryParams.join('&') : ''));
|
||||||
|
},
|
||||||
getTransactionAmounts: (params: TransactionAmountsRequestParams, excludeAccountIds: string[], excludeCategoryIds: string[]): ApiResponsePromise<TransactionAmountsResponse> => {
|
getTransactionAmounts: (params: TransactionAmountsRequestParams, excludeAccountIds: string[], excludeCategoryIds: string[]): ApiResponsePromise<TransactionAmountsResponse> => {
|
||||||
const req = TransactionAmountsRequest.of(params);
|
const req = TransactionAmountsRequest.of(params);
|
||||||
let queryParams = req.buildQuery();
|
let queryParams = req.buildQuery();
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Transaktionsstatistiken können nicht abgerufen werden",
|
"Unable to retrieve transaction statistics": "Transaktionsstatistiken können nicht abgerufen werden",
|
||||||
"Categorical Analysis": "Kategorische Analyse",
|
"Categorical Analysis": "Kategorische Analyse",
|
||||||
"Trend Analysis": "Trendanalyse",
|
"Trend Analysis": "Trendanalyse",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Gesamtbetrag",
|
"Total Amount": "Gesamtbetrag",
|
||||||
"Total Assets": "Gesamtvermögen",
|
"Total Assets": "Gesamtvermögen",
|
||||||
"Total Liabilities": "Gesamtverbindlichkeiten",
|
"Total Liabilities": "Gesamtverbindlichkeiten",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Allgemeine Einstellungen",
|
"Common Settings": "Allgemeine Einstellungen",
|
||||||
"Categorical Analysis Settings": "Einstellungen für kategorische Analyse",
|
"Categorical Analysis Settings": "Einstellungen für kategorische Analyse",
|
||||||
"Trend Analysis Settings": "Einstellungen für Trendanalyse",
|
"Trend Analysis Settings": "Einstellungen für Trendanalyse",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Diagrammtyp",
|
"Chart Type": "Diagrammtyp",
|
||||||
"Default Chart Type": "Standarddiagrammtyp",
|
"Default Chart Type": "Standarddiagrammtyp",
|
||||||
"Chart Data Type": "Diagrammdatentyp",
|
"Chart Data Type": "Diagrammdatentyp",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Nach Anzeigereihenfolge sortieren",
|
"Sort by Display Order": "Nach Anzeigereihenfolge sortieren",
|
||||||
"Sort by Name": "Nach Name sortieren",
|
"Sort by Name": "Nach Name sortieren",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Nach Monat aggregieren",
|
"Aggregate by Month": "Nach Monat aggregieren",
|
||||||
"Aggregate by Quarter": "Nach Quartal aggregieren",
|
"Aggregate by Quarter": "Nach Quartal aggregieren",
|
||||||
"Aggregate by Year": "Nach Jahr aggregieren",
|
"Aggregate by Year": "Nach Jahr aggregieren",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Unable to retrieve transaction statistics",
|
"Unable to retrieve transaction statistics": "Unable to retrieve transaction statistics",
|
||||||
"Categorical Analysis": "Categorical Analysis",
|
"Categorical Analysis": "Categorical Analysis",
|
||||||
"Trend Analysis": "Trend Analysis",
|
"Trend Analysis": "Trend Analysis",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Total Amount",
|
"Total Amount": "Total Amount",
|
||||||
"Total Assets": "Total Assets",
|
"Total Assets": "Total Assets",
|
||||||
"Total Liabilities": "Total Liabilities",
|
"Total Liabilities": "Total Liabilities",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Common Settings",
|
"Common Settings": "Common Settings",
|
||||||
"Categorical Analysis Settings": "Categorical Analysis Settings",
|
"Categorical Analysis Settings": "Categorical Analysis Settings",
|
||||||
"Trend Analysis Settings": "Trend Analysis Settings",
|
"Trend Analysis Settings": "Trend Analysis Settings",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Chart Type",
|
"Chart Type": "Chart Type",
|
||||||
"Default Chart Type": "Default Chart Type",
|
"Default Chart Type": "Default Chart Type",
|
||||||
"Chart Data Type": "Chart Data Type",
|
"Chart Data Type": "Chart Data Type",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Sort by Display Order",
|
"Sort by Display Order": "Sort by Display Order",
|
||||||
"Sort by Name": "Sort by Name",
|
"Sort by Name": "Sort by Name",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Aggregate by Month",
|
"Aggregate by Month": "Aggregate by Month",
|
||||||
"Aggregate by Quarter": "Aggregate by Quarter",
|
"Aggregate by Quarter": "Aggregate by Quarter",
|
||||||
"Aggregate by Year": "Aggregate by Year",
|
"Aggregate by Year": "Aggregate by Year",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "No se pueden recuperar estadísticas de transacciones",
|
"Unable to retrieve transaction statistics": "No se pueden recuperar estadísticas de transacciones",
|
||||||
"Categorical Analysis": "Análisis categórico",
|
"Categorical Analysis": "Análisis categórico",
|
||||||
"Trend Analysis": "Análisis de tendencias",
|
"Trend Analysis": "Análisis de tendencias",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Importe Total",
|
"Total Amount": "Importe Total",
|
||||||
"Total Assets": "Activos totales",
|
"Total Assets": "Activos totales",
|
||||||
"Total Liabilities": "Pasivos totales",
|
"Total Liabilities": "Pasivos totales",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Configuraciones comunes",
|
"Common Settings": "Configuraciones comunes",
|
||||||
"Categorical Analysis Settings": "Configuración de análisis categórico",
|
"Categorical Analysis Settings": "Configuración de análisis categórico",
|
||||||
"Trend Analysis Settings": "Configuración de análisis de tendencias",
|
"Trend Analysis Settings": "Configuración de análisis de tendencias",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Tipo de gráfico",
|
"Chart Type": "Tipo de gráfico",
|
||||||
"Default Chart Type": "Tipo de gráfico predeterminado",
|
"Default Chart Type": "Tipo de gráfico predeterminado",
|
||||||
"Chart Data Type": "Tipo de datos del gráfico",
|
"Chart Data Type": "Tipo de datos del gráfico",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Ordenar por orden de visualización",
|
"Sort by Display Order": "Ordenar por orden de visualización",
|
||||||
"Sort by Name": "Ordenar por Nombre",
|
"Sort by Name": "Ordenar por Nombre",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Agregado por mes",
|
"Aggregate by Month": "Agregado por mes",
|
||||||
"Aggregate by Quarter": "Agregado por trimestre",
|
"Aggregate by Quarter": "Agregado por trimestre",
|
||||||
"Aggregate by Year": "Agregado por año",
|
"Aggregate by Year": "Agregado por año",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Impossible de récupérer les statistiques de transaction",
|
"Unable to retrieve transaction statistics": "Impossible de récupérer les statistiques de transaction",
|
||||||
"Categorical Analysis": "Analyse catégorielle",
|
"Categorical Analysis": "Analyse catégorielle",
|
||||||
"Trend Analysis": "Analyse de tendance",
|
"Trend Analysis": "Analyse de tendance",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Montant total",
|
"Total Amount": "Montant total",
|
||||||
"Total Assets": "Total des actifs",
|
"Total Assets": "Total des actifs",
|
||||||
"Total Liabilities": "Total des passifs",
|
"Total Liabilities": "Total des passifs",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total des sorties",
|
"Total Outflows": "Total des sorties",
|
||||||
"Total Inflows": "Total des entrées",
|
"Total Inflows": "Total des entrées",
|
||||||
"Net Income": "Revenus nets",
|
"Net Income": "Revenus nets",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Flux de trésorerie net",
|
"Net Cash Flow": "Flux de trésorerie net",
|
||||||
"Total Transactions": "Total des transactions",
|
"Total Transactions": "Total des transactions",
|
||||||
"Opening Balance": "Solde d'ouverture",
|
"Opening Balance": "Solde d'ouverture",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Paramètres communs",
|
"Common Settings": "Paramètres communs",
|
||||||
"Categorical Analysis Settings": "Paramètres d'analyse catégorielle",
|
"Categorical Analysis Settings": "Paramètres d'analyse catégorielle",
|
||||||
"Trend Analysis Settings": "Paramètres d'analyse de tendance",
|
"Trend Analysis Settings": "Paramètres d'analyse de tendance",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Type de graphique",
|
"Chart Type": "Type de graphique",
|
||||||
"Default Chart Type": "Type de graphique par défaut",
|
"Default Chart Type": "Type de graphique par défaut",
|
||||||
"Chart Data Type": "Type de données du graphique",
|
"Chart Data Type": "Type de données du graphique",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Trier par ordre d'affichage",
|
"Sort by Display Order": "Trier par ordre d'affichage",
|
||||||
"Sort by Name": "Trier par nom",
|
"Sort by Name": "Trier par nom",
|
||||||
"Time Granularity": "Granularité temporelle",
|
"Time Granularity": "Granularité temporelle",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Agréger par mois",
|
"Aggregate by Month": "Agréger par mois",
|
||||||
"Aggregate by Quarter": "Agréger par trimestre",
|
"Aggregate by Quarter": "Agréger par trimestre",
|
||||||
"Aggregate by Year": "Agréger par année",
|
"Aggregate by Year": "Agréger par année",
|
||||||
|
|||||||
@@ -598,9 +598,9 @@ export function useI18n() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocalizedChartDateAggregationTypeAndDisplayName(fullName: boolean): TypeAndDisplayName[] {
|
function getLocalizedChartDateAggregationTypeAndDisplayName(analysisType: StatisticsAnalysisType, fullName: boolean): TypeAndDisplayName[] {
|
||||||
const ret: TypeAndDisplayName[] = [];
|
const ret: TypeAndDisplayName[] = [];
|
||||||
const allTypes: ChartDateAggregationType[] = ChartDateAggregationType.values();
|
const allTypes: ChartDateAggregationType[] = ChartDateAggregationType.values(analysisType);
|
||||||
|
|
||||||
for (const type of allTypes) {
|
for (const type of allTypes) {
|
||||||
ret.push({
|
ret.push({
|
||||||
@@ -2354,8 +2354,8 @@ export function useI18n() {
|
|||||||
getAllAccountBalanceTrendChartTypes: () => getLocalizedDisplayNameAndType(AccountBalanceTrendChartType.values()),
|
getAllAccountBalanceTrendChartTypes: () => getLocalizedDisplayNameAndType(AccountBalanceTrendChartType.values()),
|
||||||
getAllStatisticsChartDataTypes: (analysisType: StatisticsAnalysisType, withDesktopOnlyChart?: boolean) => getLocalizedDisplayNameAndType(ChartDataType.values(analysisType, withDesktopOnlyChart)),
|
getAllStatisticsChartDataTypes: (analysisType: StatisticsAnalysisType, withDesktopOnlyChart?: boolean) => getLocalizedDisplayNameAndType(ChartDataType.values(analysisType, withDesktopOnlyChart)),
|
||||||
getAllStatisticsSortingTypes: () => getLocalizedDisplayNameAndType(ChartSortingType.values()),
|
getAllStatisticsSortingTypes: () => getLocalizedDisplayNameAndType(ChartSortingType.values()),
|
||||||
getAllStatisticsDateAggregationTypes: () => getLocalizedChartDateAggregationTypeAndDisplayName(true),
|
getAllStatisticsDateAggregationTypes: (analysisType: StatisticsAnalysisType) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, true),
|
||||||
getAllStatisticsDateAggregationTypesWithShortName: () => getLocalizedChartDateAggregationTypeAndDisplayName(false),
|
getAllStatisticsDateAggregationTypesWithShortName: (analysisType: StatisticsAnalysisType) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, false),
|
||||||
getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()),
|
getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()),
|
||||||
getAllTransactionTagFilterTypes: () => getLocalizedDisplayNameAndType(TransactionTagFilterType.values()),
|
getAllTransactionTagFilterTypes: () => getLocalizedDisplayNameAndType(TransactionTagFilterType.values()),
|
||||||
getAllTransactionScheduledFrequencyTypes: () => getLocalizedDisplayNameAndType(ScheduledTemplateFrequencyType.values()),
|
getAllTransactionScheduledFrequencyTypes: () => getLocalizedDisplayNameAndType(ScheduledTemplateFrequencyType.values()),
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Impossibile recuperare le statistiche delle transazioni",
|
"Unable to retrieve transaction statistics": "Impossibile recuperare le statistiche delle transazioni",
|
||||||
"Categorical Analysis": "Analisi per categoria",
|
"Categorical Analysis": "Analisi per categoria",
|
||||||
"Trend Analysis": "Analisi dell'andamento",
|
"Trend Analysis": "Analisi dell'andamento",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Importo totale",
|
"Total Amount": "Importo totale",
|
||||||
"Total Assets": "Patrimonio totale",
|
"Total Assets": "Patrimonio totale",
|
||||||
"Total Liabilities": "Passività totali",
|
"Total Liabilities": "Passività totali",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Impostazioni comuni",
|
"Common Settings": "Impostazioni comuni",
|
||||||
"Categorical Analysis Settings": "Impostazioni analisi per categoria",
|
"Categorical Analysis Settings": "Impostazioni analisi per categoria",
|
||||||
"Trend Analysis Settings": "Impostazioni analisi dell'andamento",
|
"Trend Analysis Settings": "Impostazioni analisi dell'andamento",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Tipo di grafico",
|
"Chart Type": "Tipo di grafico",
|
||||||
"Default Chart Type": "Tipo di grafico predefinito",
|
"Default Chart Type": "Tipo di grafico predefinito",
|
||||||
"Chart Data Type": "Tipo di dati grafico",
|
"Chart Data Type": "Tipo di dati grafico",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Ordina per ordine di visualizzazione",
|
"Sort by Display Order": "Ordina per ordine di visualizzazione",
|
||||||
"Sort by Name": "Ordina per nome",
|
"Sort by Name": "Ordina per nome",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Aggrega per mese",
|
"Aggregate by Month": "Aggrega per mese",
|
||||||
"Aggregate by Quarter": "Aggrega per trimestre",
|
"Aggregate by Quarter": "Aggrega per trimestre",
|
||||||
"Aggregate by Year": "Aggrega per anno",
|
"Aggregate by Year": "Aggrega per anno",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "取引統計を取得できません",
|
"Unable to retrieve transaction statistics": "取引統計を取得できません",
|
||||||
"Categorical Analysis": "カテゴリ分析",
|
"Categorical Analysis": "カテゴリ分析",
|
||||||
"Trend Analysis": "傾向分析",
|
"Trend Analysis": "傾向分析",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "合計金額",
|
"Total Amount": "合計金額",
|
||||||
"Total Assets": "総資産",
|
"Total Assets": "総資産",
|
||||||
"Total Liabilities": "総負債",
|
"Total Liabilities": "総負債",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "共通設定",
|
"Common Settings": "共通設定",
|
||||||
"Categorical Analysis Settings": "カテゴリ分析設定",
|
"Categorical Analysis Settings": "カテゴリ分析設定",
|
||||||
"Trend Analysis Settings": "傾向分析設定",
|
"Trend Analysis Settings": "傾向分析設定",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "グラフの種類",
|
"Chart Type": "グラフの種類",
|
||||||
"Default Chart Type": "デフォルトのグラフの種類",
|
"Default Chart Type": "デフォルトのグラフの種類",
|
||||||
"Chart Data Type": "グラフデータの種類",
|
"Chart Data Type": "グラフデータの種類",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "表示で並べ替え",
|
"Sort by Display Order": "表示で並べ替え",
|
||||||
"Sort by Name": "名前で並べ替え",
|
"Sort by Name": "名前で並べ替え",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "月ごとに集計",
|
"Aggregate by Month": "月ごとに集計",
|
||||||
"Aggregate by Quarter": "四半期ごとに集計",
|
"Aggregate by Quarter": "四半期ごとに集計",
|
||||||
"Aggregate by Year": "年ごとに集計",
|
"Aggregate by Year": "年ごとに集計",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "거래 통계를 검색할 수 없습니다",
|
"Unable to retrieve transaction statistics": "거래 통계를 검색할 수 없습니다",
|
||||||
"Categorical Analysis": "범주 분석",
|
"Categorical Analysis": "범주 분석",
|
||||||
"Trend Analysis": "추세 분석",
|
"Trend Analysis": "추세 분석",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "총 금액",
|
"Total Amount": "총 금액",
|
||||||
"Total Assets": "총 자산",
|
"Total Assets": "총 자산",
|
||||||
"Total Liabilities": "총 부채",
|
"Total Liabilities": "총 부채",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "총 유출",
|
"Total Outflows": "총 유출",
|
||||||
"Total Inflows": "총 유입",
|
"Total Inflows": "총 유입",
|
||||||
"Net Income": "순수익",
|
"Net Income": "순수익",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "순현금흐름",
|
"Net Cash Flow": "순현금흐름",
|
||||||
"Total Transactions": "총 거래 수",
|
"Total Transactions": "총 거래 수",
|
||||||
"Opening Balance": "기초 잔액",
|
"Opening Balance": "기초 잔액",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "일반 설정",
|
"Common Settings": "일반 설정",
|
||||||
"Categorical Analysis Settings": "범주 분석 설정",
|
"Categorical Analysis Settings": "범주 분석 설정",
|
||||||
"Trend Analysis Settings": "추세 분석 설정",
|
"Trend Analysis Settings": "추세 분석 설정",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "차트 유형",
|
"Chart Type": "차트 유형",
|
||||||
"Default Chart Type": "기본 차트 유형",
|
"Default Chart Type": "기본 차트 유형",
|
||||||
"Chart Data Type": "차트 데이터 유형",
|
"Chart Data Type": "차트 데이터 유형",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "표시 순서별 정렬",
|
"Sort by Display Order": "표시 순서별 정렬",
|
||||||
"Sort by Name": "이름별 정렬",
|
"Sort by Name": "이름별 정렬",
|
||||||
"Time Granularity": "시간 세분화",
|
"Time Granularity": "시간 세분화",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "월별 집계",
|
"Aggregate by Month": "월별 집계",
|
||||||
"Aggregate by Quarter": "분기별 집계",
|
"Aggregate by Quarter": "분기별 집계",
|
||||||
"Aggregate by Year": "연도별 집계",
|
"Aggregate by Year": "연도별 집계",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Kan transactiestatistieken niet ophalen",
|
"Unable to retrieve transaction statistics": "Kan transactiestatistieken niet ophalen",
|
||||||
"Categorical Analysis": "Categorische analyse",
|
"Categorical Analysis": "Categorische analyse",
|
||||||
"Trend Analysis": "Trendanalyse",
|
"Trend Analysis": "Trendanalyse",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Totaalbedrag",
|
"Total Amount": "Totaalbedrag",
|
||||||
"Total Assets": "Totaal activa",
|
"Total Assets": "Totaal activa",
|
||||||
"Total Liabilities": "Totaal passiva",
|
"Total Liabilities": "Totaal passiva",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Totaal uitgaand",
|
"Total Outflows": "Totaal uitgaand",
|
||||||
"Total Inflows": "Totaal inkomend",
|
"Total Inflows": "Totaal inkomend",
|
||||||
"Net Income": "Netto-inkomen",
|
"Net Income": "Netto-inkomen",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Netto-kasstroom",
|
"Net Cash Flow": "Netto-kasstroom",
|
||||||
"Total Transactions": "Totaal transacties",
|
"Total Transactions": "Totaal transacties",
|
||||||
"Opening Balance": "Openingssaldo",
|
"Opening Balance": "Openingssaldo",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Algemene instellingen",
|
"Common Settings": "Algemene instellingen",
|
||||||
"Categorical Analysis Settings": "Instellingen categorische analyse",
|
"Categorical Analysis Settings": "Instellingen categorische analyse",
|
||||||
"Trend Analysis Settings": "Instellingen trendanalyse",
|
"Trend Analysis Settings": "Instellingen trendanalyse",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Diagramtype",
|
"Chart Type": "Diagramtype",
|
||||||
"Default Chart Type": "Standaard diagramtype",
|
"Default Chart Type": "Standaard diagramtype",
|
||||||
"Chart Data Type": "Diagram-gegevenstype",
|
"Chart Data Type": "Diagram-gegevenstype",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Sorteren op weergavevolgorde",
|
"Sort by Display Order": "Sorteren op weergavevolgorde",
|
||||||
"Sort by Name": "Sorteren op naam",
|
"Sort by Name": "Sorteren op naam",
|
||||||
"Time Granularity": "Tijdgranulariteit",
|
"Time Granularity": "Tijdgranulariteit",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Groeperen per maand",
|
"Aggregate by Month": "Groeperen per maand",
|
||||||
"Aggregate by Quarter": "Groeperen per kwartaal",
|
"Aggregate by Quarter": "Groeperen per kwartaal",
|
||||||
"Aggregate by Year": "Groeperen per jaar",
|
"Aggregate by Year": "Groeperen per jaar",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Não é possível recuperar estatísticas da transação",
|
"Unable to retrieve transaction statistics": "Não é possível recuperar estatísticas da transação",
|
||||||
"Categorical Analysis": "Análise Categórica",
|
"Categorical Analysis": "Análise Categórica",
|
||||||
"Trend Analysis": "Análise de Tendência",
|
"Trend Analysis": "Análise de Tendência",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Montante Total",
|
"Total Amount": "Montante Total",
|
||||||
"Total Assets": "Total de Ativos",
|
"Total Assets": "Total de Ativos",
|
||||||
"Total Liabilities": "Total de Passivos",
|
"Total Liabilities": "Total de Passivos",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Configurações Comuns",
|
"Common Settings": "Configurações Comuns",
|
||||||
"Categorical Analysis Settings": "Configurações de Análise Categórica",
|
"Categorical Analysis Settings": "Configurações de Análise Categórica",
|
||||||
"Trend Analysis Settings": "Configurações de Análise de Tendência",
|
"Trend Analysis Settings": "Configurações de Análise de Tendência",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Tipo de Gráfico",
|
"Chart Type": "Tipo de Gráfico",
|
||||||
"Default Chart Type": "Tipo de Gráfico Padrão",
|
"Default Chart Type": "Tipo de Gráfico Padrão",
|
||||||
"Chart Data Type": "Tipo de Dados do Gráfico",
|
"Chart Data Type": "Tipo de Dados do Gráfico",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Classificar por Ordem de Exibição",
|
"Sort by Display Order": "Classificar por Ordem de Exibição",
|
||||||
"Sort by Name": "Classificar por Nome",
|
"Sort by Name": "Classificar por Nome",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Agregado por Mês",
|
"Aggregate by Month": "Agregado por Mês",
|
||||||
"Aggregate by Quarter": "Agregado por Trimestre",
|
"Aggregate by Quarter": "Agregado por Trimestre",
|
||||||
"Aggregate by Year": "Agregado por Ano",
|
"Aggregate by Year": "Agregado por Ano",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Не удалось получить статистику транзакций",
|
"Unable to retrieve transaction statistics": "Не удалось получить статистику транзакций",
|
||||||
"Categorical Analysis": "Категориальный анализ",
|
"Categorical Analysis": "Категориальный анализ",
|
||||||
"Trend Analysis": "Анализ тенденций",
|
"Trend Analysis": "Анализ тенденций",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Общая сумма",
|
"Total Amount": "Общая сумма",
|
||||||
"Total Assets": "Общие активы",
|
"Total Assets": "Общие активы",
|
||||||
"Total Liabilities": "Общие обязательства",
|
"Total Liabilities": "Общие обязательства",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Общие настройки",
|
"Common Settings": "Общие настройки",
|
||||||
"Categorical Analysis Settings": "Настройки категориального анализа",
|
"Categorical Analysis Settings": "Настройки категориального анализа",
|
||||||
"Trend Analysis Settings": "Настройки анализа тенденций",
|
"Trend Analysis Settings": "Настройки анализа тенденций",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Тип диаграммы",
|
"Chart Type": "Тип диаграммы",
|
||||||
"Default Chart Type": "Тип диаграммы по умолчанию",
|
"Default Chart Type": "Тип диаграммы по умолчанию",
|
||||||
"Chart Data Type": "Тип данных диаграммы",
|
"Chart Data Type": "Тип данных диаграммы",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Сортировать по порядку отображения",
|
"Sort by Display Order": "Сортировать по порядку отображения",
|
||||||
"Sort by Name": "Сортировать по имени",
|
"Sort by Name": "Сортировать по имени",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Агрегировать по месяцам",
|
"Aggregate by Month": "Агрегировать по месяцам",
|
||||||
"Aggregate by Quarter": "Агрегировать по кварталам",
|
"Aggregate by Quarter": "Агрегировать по кварталам",
|
||||||
"Aggregate by Year": "Агрегировать по годам",
|
"Aggregate by Year": "Агрегировать по годам",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "ไม่สามารถดึงสถิติรายการได้",
|
"Unable to retrieve transaction statistics": "ไม่สามารถดึงสถิติรายการได้",
|
||||||
"Categorical Analysis": "วิเคราะห์ตามหมวดหมู่",
|
"Categorical Analysis": "วิเคราะห์ตามหมวดหมู่",
|
||||||
"Trend Analysis": "วิเคราะห์แนวโน้ม",
|
"Trend Analysis": "วิเคราะห์แนวโน้ม",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "จำนวนรวม",
|
"Total Amount": "จำนวนรวม",
|
||||||
"Total Assets": "สินทรัพย์รวม",
|
"Total Assets": "สินทรัพย์รวม",
|
||||||
"Total Liabilities": "หนี้สินรวม",
|
"Total Liabilities": "หนี้สินรวม",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "เงินไหลออกรวม",
|
"Total Outflows": "เงินไหลออกรวม",
|
||||||
"Total Inflows": "เงินไหลเข้ารวม",
|
"Total Inflows": "เงินไหลเข้ารวม",
|
||||||
"Net Income": "รายได้สุทธิ",
|
"Net Income": "รายได้สุทธิ",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "กระแสเงินสดสุทธิ",
|
"Net Cash Flow": "กระแสเงินสดสุทธิ",
|
||||||
"Total Transactions": "จำนวนรายการทั้งหมด",
|
"Total Transactions": "จำนวนรายการทั้งหมด",
|
||||||
"Opening Balance": "ยอดเริ่มต้น",
|
"Opening Balance": "ยอดเริ่มต้น",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "การตั้งค่าทั่วไป",
|
"Common Settings": "การตั้งค่าทั่วไป",
|
||||||
"Categorical Analysis Settings": "การตั้งค่าวิเคราะห์ตามหมวดหมู่",
|
"Categorical Analysis Settings": "การตั้งค่าวิเคราะห์ตามหมวดหมู่",
|
||||||
"Trend Analysis Settings": "การตั้งค่าวิเคราะห์แนวโน้ม",
|
"Trend Analysis Settings": "การตั้งค่าวิเคราะห์แนวโน้ม",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "ประเภทกราฟ",
|
"Chart Type": "ประเภทกราฟ",
|
||||||
"Default Chart Type": "ประเภทกราฟเริ่มต้น",
|
"Default Chart Type": "ประเภทกราฟเริ่มต้น",
|
||||||
"Chart Data Type": "ประเภทข้อมูลกราฟ",
|
"Chart Data Type": "ประเภทข้อมูลกราฟ",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "เรียงตามลำดับการแสดง",
|
"Sort by Display Order": "เรียงตามลำดับการแสดง",
|
||||||
"Sort by Name": "เรียงตามชื่อ",
|
"Sort by Name": "เรียงตามชื่อ",
|
||||||
"Time Granularity": "ความละเอียดเวลา",
|
"Time Granularity": "ความละเอียดเวลา",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "รวมตามเดือน",
|
"Aggregate by Month": "รวมตามเดือน",
|
||||||
"Aggregate by Quarter": "รวมตามไตรมาส",
|
"Aggregate by Quarter": "รวมตามไตรมาส",
|
||||||
"Aggregate by Year": "รวมตามปี",
|
"Aggregate by Year": "รวมตามปี",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Не вдалося отримати статистику транзакцій",
|
"Unable to retrieve transaction statistics": "Не вдалося отримати статистику транзакцій",
|
||||||
"Categorical Analysis": "Аналіз за категоріями",
|
"Categorical Analysis": "Аналіз за категоріями",
|
||||||
"Trend Analysis": "Аналіз трендів",
|
"Trend Analysis": "Аналіз трендів",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Загальна сума",
|
"Total Amount": "Загальна сума",
|
||||||
"Total Assets": "Загальні активи",
|
"Total Assets": "Загальні активи",
|
||||||
"Total Liabilities": "Загальні зобов’язання",
|
"Total Liabilities": "Загальні зобов’язання",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Загальні налаштування",
|
"Common Settings": "Загальні налаштування",
|
||||||
"Categorical Analysis Settings": "Налаштування аналізу за категоріями",
|
"Categorical Analysis Settings": "Налаштування аналізу за категоріями",
|
||||||
"Trend Analysis Settings": "Налаштування аналізу трендів",
|
"Trend Analysis Settings": "Налаштування аналізу трендів",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Тип діаграми",
|
"Chart Type": "Тип діаграми",
|
||||||
"Default Chart Type": "Тип діаграми за замовчуванням",
|
"Default Chart Type": "Тип діаграми за замовчуванням",
|
||||||
"Chart Data Type": "Тип даних діаграми",
|
"Chart Data Type": "Тип даних діаграми",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Сортувати за порядком відображення",
|
"Sort by Display Order": "Сортувати за порядком відображення",
|
||||||
"Sort by Name": "Сортувати за назвою",
|
"Sort by Name": "Сортувати за назвою",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Агрегувати за місяцями",
|
"Aggregate by Month": "Агрегувати за місяцями",
|
||||||
"Aggregate by Quarter": "Агрегувати за кварталами",
|
"Aggregate by Quarter": "Агрегувати за кварталами",
|
||||||
"Aggregate by Year": "Агрегувати за роками",
|
"Aggregate by Year": "Агрегувати за роками",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "Không thể lấy thống kê giao dịch",
|
"Unable to retrieve transaction statistics": "Không thể lấy thống kê giao dịch",
|
||||||
"Categorical Analysis": "Phân tích theo danh mục",
|
"Categorical Analysis": "Phân tích theo danh mục",
|
||||||
"Trend Analysis": "Phân tích xu hướng",
|
"Trend Analysis": "Phân tích xu hướng",
|
||||||
|
"Asset Trends": "Asset Trends",
|
||||||
"Total Amount": "Tổng số tiền",
|
"Total Amount": "Tổng số tiền",
|
||||||
"Total Assets": "Tổng tài sản",
|
"Total Assets": "Tổng tài sản",
|
||||||
"Total Liabilities": "Tổng nợ phải trả",
|
"Total Liabilities": "Tổng nợ phải trả",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "Total Outflows",
|
"Total Outflows": "Total Outflows",
|
||||||
"Total Inflows": "Total Inflows",
|
"Total Inflows": "Total Inflows",
|
||||||
"Net Income": "Net Income",
|
"Net Income": "Net Income",
|
||||||
|
"Net Worth": "Net Worth",
|
||||||
"Net Cash Flow": "Net Cash Flow",
|
"Net Cash Flow": "Net Cash Flow",
|
||||||
"Total Transactions": "Total Transactions",
|
"Total Transactions": "Total Transactions",
|
||||||
"Opening Balance": "Opening Balance",
|
"Opening Balance": "Opening Balance",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "Cài đặt chung",
|
"Common Settings": "Cài đặt chung",
|
||||||
"Categorical Analysis Settings": "Cài đặt phân tích theo danh mục",
|
"Categorical Analysis Settings": "Cài đặt phân tích theo danh mục",
|
||||||
"Trend Analysis Settings": "Cài đặt phân tích xu hướng",
|
"Trend Analysis Settings": "Cài đặt phân tích xu hướng",
|
||||||
|
"Asset Trends Settings": "Asset Trends Settings",
|
||||||
"Chart Type": "Loại biểu đồ",
|
"Chart Type": "Loại biểu đồ",
|
||||||
"Default Chart Type": "Loại biểu đồ mặc định",
|
"Default Chart Type": "Loại biểu đồ mặc định",
|
||||||
"Chart Data Type": "Loại dữ liệu biểu đồ",
|
"Chart Data Type": "Loại dữ liệu biểu đồ",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "Sắp xếp theo thứ tự hiển thị",
|
"Sort by Display Order": "Sắp xếp theo thứ tự hiển thị",
|
||||||
"Sort by Name": "Sắp xếp theo tên",
|
"Sort by Name": "Sắp xếp theo tên",
|
||||||
"Time Granularity": "Time Granularity",
|
"Time Granularity": "Time Granularity",
|
||||||
|
"Aggregate by Day": "Aggregate by Day",
|
||||||
"Aggregate by Month": "Tổng hợp theo tháng",
|
"Aggregate by Month": "Tổng hợp theo tháng",
|
||||||
"Aggregate by Quarter": "Tổng hợp theo quý",
|
"Aggregate by Quarter": "Tổng hợp theo quý",
|
||||||
"Aggregate by Year": "Tổng hợp theo năm",
|
"Aggregate by Year": "Tổng hợp theo năm",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "无法获取交易统计数据",
|
"Unable to retrieve transaction statistics": "无法获取交易统计数据",
|
||||||
"Categorical Analysis": "分类分析",
|
"Categorical Analysis": "分类分析",
|
||||||
"Trend Analysis": "趋势分析",
|
"Trend Analysis": "趋势分析",
|
||||||
|
"Asset Trends": "资产趋势",
|
||||||
"Total Amount": "总金额",
|
"Total Amount": "总金额",
|
||||||
"Total Assets": "总资产",
|
"Total Assets": "总资产",
|
||||||
"Total Liabilities": "总负债",
|
"Total Liabilities": "总负债",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "总流出",
|
"Total Outflows": "总流出",
|
||||||
"Total Inflows": "总流入",
|
"Total Inflows": "总流入",
|
||||||
"Net Income": "净收入",
|
"Net Income": "净收入",
|
||||||
|
"Net Worth": "净资产",
|
||||||
"Net Cash Flow": "净现金流",
|
"Net Cash Flow": "净现金流",
|
||||||
"Total Transactions": "总交易数",
|
"Total Transactions": "总交易数",
|
||||||
"Opening Balance": "期初余额",
|
"Opening Balance": "期初余额",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "通用设置",
|
"Common Settings": "通用设置",
|
||||||
"Categorical Analysis Settings": "分类分析设置",
|
"Categorical Analysis Settings": "分类分析设置",
|
||||||
"Trend Analysis Settings": "趋势分析设置",
|
"Trend Analysis Settings": "趋势分析设置",
|
||||||
|
"Asset Trends Settings": "资产趋势设置",
|
||||||
"Chart Type": "图表类型",
|
"Chart Type": "图表类型",
|
||||||
"Default Chart Type": "默认图表类型",
|
"Default Chart Type": "默认图表类型",
|
||||||
"Chart Data Type": "图表数据类型",
|
"Chart Data Type": "图表数据类型",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "按显示顺序排序",
|
"Sort by Display Order": "按显示顺序排序",
|
||||||
"Sort by Name": "按名称排序",
|
"Sort by Name": "按名称排序",
|
||||||
"Time Granularity": "时间粒度",
|
"Time Granularity": "时间粒度",
|
||||||
|
"Aggregate by Day": "按日聚合",
|
||||||
"Aggregate by Month": "按月聚合",
|
"Aggregate by Month": "按月聚合",
|
||||||
"Aggregate by Quarter": "按季度聚合",
|
"Aggregate by Quarter": "按季度聚合",
|
||||||
"Aggregate by Year": "按年聚合",
|
"Aggregate by Year": "按年聚合",
|
||||||
|
|||||||
@@ -1982,6 +1982,7 @@
|
|||||||
"Unable to retrieve transaction statistics": "無法取得交易統計資料",
|
"Unable to retrieve transaction statistics": "無法取得交易統計資料",
|
||||||
"Categorical Analysis": "分類分析",
|
"Categorical Analysis": "分類分析",
|
||||||
"Trend Analysis": "趨勢分析",
|
"Trend Analysis": "趨勢分析",
|
||||||
|
"Asset Trends": "資產趨勢",
|
||||||
"Total Amount": "總金額",
|
"Total Amount": "總金額",
|
||||||
"Total Assets": "總資產",
|
"Total Assets": "總資產",
|
||||||
"Total Liabilities": "總負債",
|
"Total Liabilities": "總負債",
|
||||||
@@ -1990,6 +1991,7 @@
|
|||||||
"Total Outflows": "總流出",
|
"Total Outflows": "總流出",
|
||||||
"Total Inflows": "總流入",
|
"Total Inflows": "總流入",
|
||||||
"Net Income": "淨收入",
|
"Net Income": "淨收入",
|
||||||
|
"Net Worth": "淨資產",
|
||||||
"Net Cash Flow": "淨現金流量",
|
"Net Cash Flow": "淨現金流量",
|
||||||
"Total Transactions": "總交易數",
|
"Total Transactions": "總交易數",
|
||||||
"Opening Balance": "期初餘額",
|
"Opening Balance": "期初餘額",
|
||||||
@@ -2012,6 +2014,7 @@
|
|||||||
"Common Settings": "一般設定",
|
"Common Settings": "一般設定",
|
||||||
"Categorical Analysis Settings": "分類分析設定",
|
"Categorical Analysis Settings": "分類分析設定",
|
||||||
"Trend Analysis Settings": "趨勢分析設定",
|
"Trend Analysis Settings": "趨勢分析設定",
|
||||||
|
"Asset Trends Settings": "資產趨勢設定",
|
||||||
"Chart Type": "圖表類型",
|
"Chart Type": "圖表類型",
|
||||||
"Default Chart Type": "預設圖表類型",
|
"Default Chart Type": "預設圖表類型",
|
||||||
"Chart Data Type": "圖表資料類型",
|
"Chart Data Type": "圖表資料類型",
|
||||||
@@ -2033,6 +2036,7 @@
|
|||||||
"Sort by Display Order": "依顯示順序排序",
|
"Sort by Display Order": "依顯示順序排序",
|
||||||
"Sort by Name": "依名稱排序",
|
"Sort by Name": "依名稱排序",
|
||||||
"Time Granularity": "時間粒度",
|
"Time Granularity": "時間粒度",
|
||||||
|
"Aggregate by Day": "依日彙整",
|
||||||
"Aggregate by Month": "依月份彙整",
|
"Aggregate by Month": "依月份彙整",
|
||||||
"Aggregate by Quarter": "依季度彙整",
|
"Aggregate by Quarter": "依季度彙整",
|
||||||
"Aggregate by Year": "依年份彙整",
|
"Aggregate by Year": "依年份彙整",
|
||||||
|
|||||||
+2
-2
@@ -56,7 +56,7 @@ import TransactionCalendar from '@/components/common/TransactionCalendar.vue';
|
|||||||
import ItemIcon from '@/components/mobile/ItemIcon.vue';
|
import ItemIcon from '@/components/mobile/ItemIcon.vue';
|
||||||
import LanguageSelectButton from '@/components/mobile/LanguageSelectButton.vue';
|
import LanguageSelectButton from '@/components/mobile/LanguageSelectButton.vue';
|
||||||
import PieChart from '@/components/mobile/PieChart.vue';
|
import PieChart from '@/components/mobile/PieChart.vue';
|
||||||
import MonthlyTrendsBarChart from '@/components/mobile/MonthlyTrendsBarChart.vue';
|
import TrendsBarChart from '@/components/mobile/TrendsBarChart.vue';
|
||||||
import PinCodeInputSheet from '@/components/mobile/PinCodeInputSheet.vue';
|
import PinCodeInputSheet from '@/components/mobile/PinCodeInputSheet.vue';
|
||||||
import PasswordInputSheet from '@/components/mobile/PasswordInputSheet.vue';
|
import PasswordInputSheet from '@/components/mobile/PasswordInputSheet.vue';
|
||||||
import PasscodeInputSheet from '@/components/mobile/PasscodeInputSheet.vue';
|
import PasscodeInputSheet from '@/components/mobile/PasscodeInputSheet.vue';
|
||||||
@@ -150,7 +150,7 @@ app.component('TransactionCalendar', TransactionCalendar);
|
|||||||
app.component('ItemIcon', ItemIcon);
|
app.component('ItemIcon', ItemIcon);
|
||||||
app.component('LanguageSelectButton', LanguageSelectButton);
|
app.component('LanguageSelectButton', LanguageSelectButton);
|
||||||
app.component('PieChart', PieChart);
|
app.component('PieChart', PieChart);
|
||||||
app.component('MonthlyTrendsBarChart', MonthlyTrendsBarChart);
|
app.component('TrendsBarChart', TrendsBarChart);
|
||||||
app.component('PinCodeInputSheet', PinCodeInputSheet);
|
app.component('PinCodeInputSheet', PinCodeInputSheet);
|
||||||
app.component('PasswordInputSheet', PasswordInputSheet);
|
app.component('PasswordInputSheet', PasswordInputSheet);
|
||||||
app.component('PasscodeInputSheet', PasscodeInputSheet);
|
app.component('PasscodeInputSheet', PasscodeInputSheet);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type PartialRecord, itemAndIndex } from '@/core/base.ts';
|
import { type PartialRecord, itemAndIndex } from '@/core/base.ts';
|
||||||
import type { Year1BasedMonth, TextualYearMonthDay, StartEndTime, WeekDay } from '@/core/datetime.ts';
|
import type { TextualYearMonthDay, Year1BasedMonth, YearMonthDay, StartEndTime, WeekDay } from '@/core/datetime.ts';
|
||||||
import { type Coordinate, getNormalizedCoordinate } from '@/core/coordinate.ts';
|
import { type Coordinate, getNormalizedCoordinate } from '@/core/coordinate.ts';
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
|
|
||||||
@@ -581,6 +581,11 @@ export interface TransactionStatisticTrendsRequest extends YearMonthRangeRequest
|
|||||||
readonly useTransactionTimezone: boolean;
|
readonly useTransactionTimezone: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionStatisticAssetTrendsRequest {
|
||||||
|
readonly startTime: number;
|
||||||
|
readonly endTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const ALL_TRANSACTION_AMOUNTS_REQUEST_TYPE = [
|
export const ALL_TRANSACTION_AMOUNTS_REQUEST_TYPE = [
|
||||||
'today',
|
'today',
|
||||||
'thisWeek',
|
'thisWeek',
|
||||||
@@ -710,12 +715,31 @@ export interface TransactionStatisticTrendsResponseItem {
|
|||||||
readonly items: TransactionStatisticResponseItem[];
|
readonly items: TransactionStatisticResponseItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionStatisticAssetTrendsResponseItem extends YearMonthDay {
|
||||||
|
readonly year: number;
|
||||||
|
readonly month: number; // 1-based (1 = January, 12 = December)
|
||||||
|
readonly day: number;
|
||||||
|
readonly items: TransactionStatisticAssetTrendsResponseDataItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionStatisticAssetTrendsResponseDataItem {
|
||||||
|
readonly accountId: string;
|
||||||
|
readonly accountOpeningBalance: number;
|
||||||
|
readonly accountClosingBalance: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface YearMonthDataItem extends Year1BasedMonth, Record<string, unknown> {}
|
export interface YearMonthDataItem extends Year1BasedMonth, Record<string, unknown> {}
|
||||||
|
|
||||||
|
export interface YearMonthDayDataItem extends YearMonthDay, Record<string, unknown> {}
|
||||||
|
|
||||||
export interface YearMonthItems<T extends Year1BasedMonth> extends Record<string, unknown> {
|
export interface YearMonthItems<T extends Year1BasedMonth> extends Record<string, unknown> {
|
||||||
readonly items: T[];
|
readonly items: T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface YearMonthDayItems<T extends YearMonthDay> extends Record<string, unknown> {
|
||||||
|
readonly items: T[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface SortableTransactionStatisticDataItem {
|
export interface SortableTransactionStatisticDataItem {
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly displayOrders: number[];
|
readonly displayOrders: number[];
|
||||||
@@ -749,6 +773,13 @@ export interface TransactionStatisticTrendsResponseItemWithInfo {
|
|||||||
readonly items: TransactionStatisticResponseItemWithInfo[];
|
readonly items: TransactionStatisticResponseItemWithInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionStatisticAssetTrendsResponseItemWithInfo {
|
||||||
|
readonly year: number;
|
||||||
|
readonly month: number; // 1-based (1 = January, 12 = December)
|
||||||
|
readonly day: number;
|
||||||
|
readonly items: TransactionStatisticResponseItemWithInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
export type TransactionStatisticDataItemType = 'category' | 'account' | 'total';
|
export type TransactionStatisticDataItemType = 'category' | 'account' | 'total';
|
||||||
|
|
||||||
export interface TransactionStatisticDataItemBase extends SortableTransactionStatisticDataItem {
|
export interface TransactionStatisticDataItemBase extends SortableTransactionStatisticDataItem {
|
||||||
@@ -820,6 +851,21 @@ export interface TransactionTrendsAnalysisDataAmount extends Record<string, unkn
|
|||||||
readonly totalAmount: number;
|
readonly totalAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TransactionAssetTrendsAnalysisData {
|
||||||
|
readonly items: TransactionAssetTrendsAnalysisDataItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionAssetTrendsAnalysisDataItem extends Record<string, unknown>, TransactionStatisticDataItemBase {
|
||||||
|
readonly items: TransactionAssetTrendsAnalysisDataAmount[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransactionAssetTrendsAnalysisDataAmount extends Record<string, unknown>, YearMonthDay {
|
||||||
|
readonly year: number;
|
||||||
|
readonly month: number;
|
||||||
|
readonly day: number;
|
||||||
|
readonly totalAmount: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type TransactionAmountsResponse = PartialRecord<TransactionAmountsRequestType, TransactionAmountsResponseItem>;
|
export type TransactionAmountsResponse = PartialRecord<TransactionAmountsRequestType, TransactionAmountsResponseItem>;
|
||||||
|
|
||||||
export interface TransactionAmountsResponseItem {
|
export interface TransactionAmountsResponseItem {
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ const router = createRouter({
|
|||||||
initTagFilterType: route.query['tagFilterType'],
|
initTagFilterType: route.query['tagFilterType'],
|
||||||
initKeyword: route.query['keyword'],
|
initKeyword: route.query['keyword'],
|
||||||
initSortingType: route.query['sortingType'],
|
initSortingType: route.query['sortingType'],
|
||||||
initTrendDateAggregationType: route.query['trendDateAggregationType']
|
initTrendDateAggregationType: route.query['trendDateAggregationType'],
|
||||||
|
initAssetTrendsDateAggregationType: route.query['assetTrendsDateAggregationType']
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -314,6 +314,18 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
updateUserApplicationCloudSettingValue('statistics.defaultTrendChartDataRangeType', value);
|
updateUserApplicationCloudSettingValue('statistics.defaultTrendChartDataRangeType', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setStatisticsDefaultAssetTrendsChartType(value: number): void {
|
||||||
|
updateApplicationSettingsSubValue('statistics', 'defaultAssetTrendsChartType', value);
|
||||||
|
appSettings.value.statistics.defaultAssetTrendsChartType = value;
|
||||||
|
updateUserApplicationCloudSettingValue('statistics.defaultAssetTrendsChartType', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatisticsDefaultAssetTrendsChartDateRange(value: number): void {
|
||||||
|
updateApplicationSettingsSubValue('statistics', 'defaultAssetTrendsChartDataRangeType', value);
|
||||||
|
appSettings.value.statistics.defaultAssetTrendsChartDataRangeType = value;
|
||||||
|
updateUserApplicationCloudSettingValue('statistics.defaultAssetTrendsChartDataRangeType', value);
|
||||||
|
}
|
||||||
|
|
||||||
function clearAppSettings(): void {
|
function clearAppSettings(): void {
|
||||||
clearSettings();
|
clearSettings();
|
||||||
appSettings.value = getApplicationSettings();
|
appSettings.value = getApplicationSettings();
|
||||||
@@ -469,6 +481,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||||||
setStatisticsDefaultCategoricalChartDateRange,
|
setStatisticsDefaultCategoricalChartDateRange,
|
||||||
setStatisticsDefaultTrendChartType,
|
setStatisticsDefaultTrendChartType,
|
||||||
setStatisticsDefaultTrendChartDateRange,
|
setStatisticsDefaultTrendChartDateRange,
|
||||||
|
setStatisticsDefaultAssetTrendsChartType,
|
||||||
|
setStatisticsDefaultAssetTrendsChartDateRange,
|
||||||
clearAppSettings,
|
clearAppSettings,
|
||||||
createApplicationCloudSettings,
|
createApplicationCloudSettings,
|
||||||
setApplicationSettingsFromCloudSettings,
|
setApplicationSettingsFromCloudSettings,
|
||||||
|
|||||||
+423
-27
@@ -8,7 +8,7 @@ import { useTransactionCategoriesStore } from './transactionCategory.ts';
|
|||||||
import { useExchangeRatesStore } from './exchangeRates.ts';
|
import { useExchangeRatesStore } from './exchangeRates.ts';
|
||||||
|
|
||||||
import { entries, values } from '@/core/base.ts';
|
import { entries, values } from '@/core/base.ts';
|
||||||
import { type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
import { type DateTime, type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||||
import { TimezoneTypeForStatistics } from '@/core/timezone.ts';
|
import { TimezoneTypeForStatistics } from '@/core/timezone.ts';
|
||||||
import { CategoryType } from '@/core/category.ts';
|
import { CategoryType } from '@/core/category.ts';
|
||||||
import {
|
import {
|
||||||
@@ -23,7 +23,8 @@ import {
|
|||||||
ChartSortingType,
|
ChartSortingType,
|
||||||
ChartDateAggregationType,
|
ChartDateAggregationType,
|
||||||
DEFAULT_CATEGORICAL_CHART_DATA_RANGE,
|
DEFAULT_CATEGORICAL_CHART_DATA_RANGE,
|
||||||
DEFAULT_TREND_CHART_DATA_RANGE
|
DEFAULT_TREND_CHART_DATA_RANGE,
|
||||||
|
DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE
|
||||||
} from '@/core/statistics.ts';
|
} from '@/core/statistics.ts';
|
||||||
import { DEFAULT_ACCOUNT_ICON, DEFAULT_CATEGORY_ICON } from '@/consts/icon.ts';
|
import { DEFAULT_ACCOUNT_ICON, DEFAULT_CATEGORY_ICON } from '@/consts/icon.ts';
|
||||||
import { DEFAULT_ACCOUNT_COLOR, DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts';
|
import { DEFAULT_ACCOUNT_COLOR, DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts';
|
||||||
@@ -32,9 +33,12 @@ import {
|
|||||||
type TransactionStatisticResponse,
|
type TransactionStatisticResponse,
|
||||||
type TransactionStatisticResponseItem,
|
type TransactionStatisticResponseItem,
|
||||||
type TransactionStatisticTrendsResponseItem,
|
type TransactionStatisticTrendsResponseItem,
|
||||||
|
type TransactionStatisticAssetTrendsResponseItem,
|
||||||
|
type TransactionStatisticAssetTrendsResponseDataItem,
|
||||||
type TransactionStatisticResponseItemWithInfo,
|
type TransactionStatisticResponseItemWithInfo,
|
||||||
type TransactionStatisticResponseWithInfo,
|
type TransactionStatisticResponseWithInfo,
|
||||||
type TransactionStatisticTrendsResponseItemWithInfo,
|
type TransactionStatisticTrendsResponseItemWithInfo,
|
||||||
|
type TransactionStatisticAssetTrendsResponseItemWithInfo,
|
||||||
type TransactionStatisticDataItemType,
|
type TransactionStatisticDataItemType,
|
||||||
type TransactionStatisticDataItemBase,
|
type TransactionStatisticDataItemBase,
|
||||||
type TransactionCategoricalOverviewAnalysisData,
|
type TransactionCategoricalOverviewAnalysisData,
|
||||||
@@ -44,6 +48,9 @@ import {
|
|||||||
type TransactionTrendsAnalysisData,
|
type TransactionTrendsAnalysisData,
|
||||||
type TransactionTrendsAnalysisDataItem,
|
type TransactionTrendsAnalysisDataItem,
|
||||||
type TransactionTrendsAnalysisDataAmount,
|
type TransactionTrendsAnalysisDataAmount,
|
||||||
|
type TransactionAssetTrendsAnalysisData,
|
||||||
|
type TransactionAssetTrendsAnalysisDataItem,
|
||||||
|
type TransactionAssetTrendsAnalysisDataAmount,
|
||||||
TransactionCategoricalOverviewAnalysisDataItemType
|
TransactionCategoricalOverviewAnalysisDataItemType
|
||||||
} from '@/models/transaction.ts';
|
} from '@/models/transaction.ts';
|
||||||
|
|
||||||
@@ -58,7 +65,12 @@ import {
|
|||||||
isObjectEmpty,
|
isObjectEmpty,
|
||||||
objectFieldToArrayItem
|
objectFieldToArrayItem
|
||||||
} from '@/lib/common.ts';
|
} from '@/lib/common.ts';
|
||||||
import { getGregorianCalendarYearAndMonthFromUnixTime, getDateRangeByDateType } from '@/lib/datetime.ts';
|
import {
|
||||||
|
getYearMonthDayDateTime,
|
||||||
|
getGregorianCalendarYearAndMonthFromUnixTime,
|
||||||
|
getDayDifference,
|
||||||
|
getDateRangeByDateType
|
||||||
|
} from '@/lib/datetime.ts';
|
||||||
import { getFinalAccountIdsByFilteredAccountIds } from '@/lib/account.ts';
|
import { getFinalAccountIdsByFilteredAccountIds } from '@/lib/account.ts';
|
||||||
import { getFinalCategoryIdsByFilteredCategoryIds } from '@/lib/category.ts';
|
import { getFinalCategoryIdsByFilteredCategoryIds } from '@/lib/category.ts';
|
||||||
import { sortStatisticsItems } from '@/lib/statistics.ts';
|
import { sortStatisticsItems } from '@/lib/statistics.ts';
|
||||||
@@ -83,7 +95,7 @@ interface WritableTransactionCategoricalAnalysisDataItem extends Record<string,
|
|||||||
percent?: number;
|
percent?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WritableTransactionTrendsAnalysisDataItem extends Record<string, unknown> {
|
interface WritableTransactionTrendsAnalysisDataItem extends Record<string, unknown>, TransactionTrendsAnalysisDataItem {
|
||||||
name: string;
|
name: string;
|
||||||
type: TransactionStatisticDataItemType;
|
type: TransactionStatisticDataItemType;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -95,6 +107,18 @@ interface WritableTransactionTrendsAnalysisDataItem extends Record<string, unkno
|
|||||||
items: TransactionTrendsAnalysisDataAmount[];
|
items: TransactionTrendsAnalysisDataAmount[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WritableTransactionAssetTrendsAnalysisDataItem extends Record<string, unknown>, TransactionAssetTrendsAnalysisDataItem {
|
||||||
|
name: string;
|
||||||
|
type: TransactionStatisticDataItemType;
|
||||||
|
id: string;
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
hidden: boolean;
|
||||||
|
displayOrders: number[];
|
||||||
|
totalAmount: number;
|
||||||
|
items: TransactionAssetTrendsAnalysisDataAmount[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface TransactionStatisticsPartialFilter {
|
export interface TransactionStatisticsPartialFilter {
|
||||||
chartDataType?: number;
|
chartDataType?: number;
|
||||||
categoricalChartType?: number;
|
categoricalChartType?: number;
|
||||||
@@ -105,6 +129,10 @@ export interface TransactionStatisticsPartialFilter {
|
|||||||
trendChartDateType?: number;
|
trendChartDateType?: number;
|
||||||
trendChartStartYearMonth?: TextualYearMonth | '';
|
trendChartStartYearMonth?: TextualYearMonth | '';
|
||||||
trendChartEndYearMonth?: TextualYearMonth | '';
|
trendChartEndYearMonth?: TextualYearMonth | '';
|
||||||
|
assetTrendsChartType?: number;
|
||||||
|
assetTrendsChartDateType?: number;
|
||||||
|
assetTrendsChartStartTime?: number;
|
||||||
|
assetTrendsChartEndTime?: number;
|
||||||
filterAccountIds?: Record<string, boolean>;
|
filterAccountIds?: Record<string, boolean>;
|
||||||
filterCategoryIds?: Record<string, boolean>;
|
filterCategoryIds?: Record<string, boolean>;
|
||||||
tagIds?: string;
|
tagIds?: string;
|
||||||
@@ -123,6 +151,10 @@ export interface TransactionStatisticsFilter extends TransactionStatisticsPartia
|
|||||||
trendChartDateType: number;
|
trendChartDateType: number;
|
||||||
trendChartStartYearMonth: TextualYearMonth | '';
|
trendChartStartYearMonth: TextualYearMonth | '';
|
||||||
trendChartEndYearMonth: TextualYearMonth | '';
|
trendChartEndYearMonth: TextualYearMonth | '';
|
||||||
|
assetTrendsChartType: number;
|
||||||
|
assetTrendsChartDateType: number;
|
||||||
|
assetTrendsChartStartTime: number;
|
||||||
|
assetTrendsChartEndTime: number;
|
||||||
filterAccountIds: Record<string, boolean>;
|
filterAccountIds: Record<string, boolean>;
|
||||||
filterCategoryIds: Record<string, boolean>;
|
filterCategoryIds: Record<string, boolean>;
|
||||||
tagIds: string;
|
tagIds: string;
|
||||||
@@ -148,6 +180,10 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
trendChartDateType: DEFAULT_TREND_CHART_DATA_RANGE.type,
|
trendChartDateType: DEFAULT_TREND_CHART_DATA_RANGE.type,
|
||||||
trendChartStartYearMonth: '',
|
trendChartStartYearMonth: '',
|
||||||
trendChartEndYearMonth: '',
|
trendChartEndYearMonth: '',
|
||||||
|
assetTrendsChartType: TrendChartType.Default.type,
|
||||||
|
assetTrendsChartDateType: DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE.type,
|
||||||
|
assetTrendsChartStartTime: 0,
|
||||||
|
assetTrendsChartEndTime: 0,
|
||||||
filterAccountIds: {},
|
filterAccountIds: {},
|
||||||
filterCategoryIds: {},
|
filterCategoryIds: {},
|
||||||
tagIds: '',
|
tagIds: '',
|
||||||
@@ -158,6 +194,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
|
|
||||||
const transactionCategoryStatisticsData = ref<TransactionStatisticResponse | null>(null);
|
const transactionCategoryStatisticsData = ref<TransactionStatisticResponse | null>(null);
|
||||||
const transactionCategoryTrendsData = ref<TransactionStatisticTrendsResponseItem[]>([]);
|
const transactionCategoryTrendsData = ref<TransactionStatisticTrendsResponseItem[]>([]);
|
||||||
|
const transactionAssetTrendsData = ref<TransactionStatisticAssetTrendsResponseItem[]>([]);
|
||||||
const transactionStatisticsStateInvalid = ref<boolean>(true);
|
const transactionStatisticsStateInvalid = ref<boolean>(true);
|
||||||
|
|
||||||
const categoricalAnalysisChartDataCategory = computed<string>(() => {
|
const categoricalAnalysisChartDataCategory = computed<string>(() => {
|
||||||
@@ -709,6 +746,227 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
return trendsData;
|
return trendsData;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const assetTrendsDataWithAccountInfo = computed<TransactionStatisticAssetTrendsResponseItemWithInfo[]>(() => {
|
||||||
|
const assetTrendsData = transactionAssetTrendsData.value;
|
||||||
|
const finalAssetTrendsData: TransactionStatisticAssetTrendsResponseItemWithInfo[] = [];
|
||||||
|
|
||||||
|
if (!assetTrendsData || !assetTrendsData.length) {
|
||||||
|
return finalAssetTrendsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstAssetTrendItem: TransactionStatisticAssetTrendsResponseItem | undefined = assetTrendsData[0];
|
||||||
|
|
||||||
|
if (!firstAssetTrendItem) {
|
||||||
|
return finalAssetTrendsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastAssetTrendItemMap: Record<string, TransactionStatisticAssetTrendsResponseDataItem> = {};
|
||||||
|
let lastAssetTrendItem: TransactionStatisticAssetTrendsResponseItem = firstAssetTrendItem;
|
||||||
|
|
||||||
|
for (const item of firstAssetTrendItem.items) {
|
||||||
|
lastAssetTrendItemMap[item.accountId] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const assetTrendItem of assetTrendsData) {
|
||||||
|
const statisticResponseItems: TransactionStatisticResponseItem[] = [];
|
||||||
|
const existedAccountIds: Record<string, boolean> = {};
|
||||||
|
const missingDays: number = getDayDifference(lastAssetTrendItem, assetTrendItem) - 1;
|
||||||
|
const lastAssetTrendItemDate: DateTime = getYearMonthDayDateTime(lastAssetTrendItem.year, lastAssetTrendItem.month, lastAssetTrendItem.day);
|
||||||
|
|
||||||
|
// fill in missing days with last known balance
|
||||||
|
for (let i = 1; i <= missingDays; i++) {
|
||||||
|
const missingStatisticResponseItems: TransactionStatisticResponseItem[] = [];
|
||||||
|
const dateTime: DateTime = lastAssetTrendItemDate.getDateTimeAfterDays(i);
|
||||||
|
|
||||||
|
for (const item of values(lastAssetTrendItemMap)) {
|
||||||
|
const statisticResponseItem: TransactionStatisticResponseItem = {
|
||||||
|
categoryId: '',
|
||||||
|
accountId: item.accountId,
|
||||||
|
amount: item.accountClosingBalance
|
||||||
|
};
|
||||||
|
|
||||||
|
missingStatisticResponseItems.push(statisticResponseItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalAssetTrendItem: TransactionStatisticAssetTrendsResponseItemWithInfo = {
|
||||||
|
year: dateTime.getGregorianCalendarYear(),
|
||||||
|
month: dateTime.getGregorianCalendarMonth(),
|
||||||
|
day: dateTime.getGregorianCalendarDay(),
|
||||||
|
items: assembleAccountAndCategoryInfo(missingStatisticResponseItems)
|
||||||
|
};
|
||||||
|
|
||||||
|
lastAssetTrendItem = assetTrendItem;
|
||||||
|
finalAssetTrendsData.push(finalAssetTrendItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in current day data
|
||||||
|
for (const item of assetTrendItem.items) {
|
||||||
|
const statisticResponseItem: TransactionStatisticResponseItem = {
|
||||||
|
categoryId: '',
|
||||||
|
accountId: item.accountId,
|
||||||
|
amount: item.accountClosingBalance
|
||||||
|
};
|
||||||
|
|
||||||
|
lastAssetTrendItemMap[item.accountId] = item;
|
||||||
|
existedAccountIds[item.accountId] = true;
|
||||||
|
statisticResponseItems.push(statisticResponseItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill in missing accounts with last known balance
|
||||||
|
for (const item of values(lastAssetTrendItemMap)) {
|
||||||
|
if (existedAccountIds[item.accountId]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statisticResponseItem: TransactionStatisticResponseItem = {
|
||||||
|
categoryId: '',
|
||||||
|
accountId: item.accountId,
|
||||||
|
amount: item.accountClosingBalance
|
||||||
|
};
|
||||||
|
|
||||||
|
existedAccountIds[item.accountId] = true;
|
||||||
|
statisticResponseItems.push(statisticResponseItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalAssetTrendItem: TransactionStatisticAssetTrendsResponseItemWithInfo = {
|
||||||
|
year: assetTrendItem.year,
|
||||||
|
month: assetTrendItem.month,
|
||||||
|
day: assetTrendItem.day,
|
||||||
|
items: assembleAccountAndCategoryInfo(statisticResponseItems)
|
||||||
|
};
|
||||||
|
|
||||||
|
lastAssetTrendItem = assetTrendItem;
|
||||||
|
finalAssetTrendsData.push(finalAssetTrendItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalAssetTrendsData;
|
||||||
|
});
|
||||||
|
|
||||||
|
const assetTrendsData = computed<TransactionAssetTrendsAnalysisData | null>(() => {
|
||||||
|
if (!assetTrendsDataWithAccountInfo.value || !assetTrendsDataWithAccountInfo.value.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinedDataMap: Record<string, WritableTransactionAssetTrendsAnalysisDataItem> = {};
|
||||||
|
|
||||||
|
for (const dailyData of assetTrendsDataWithAccountInfo.value) {
|
||||||
|
let dailyTotalAmount: number = 0;
|
||||||
|
|
||||||
|
for (const item of dailyData.items) {
|
||||||
|
if (!item.primaryAccount || !item.account) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.filterAccountIds && transactionStatisticsFilter.value.filterAccountIds[item.account.id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNumber(item.amountInDefaultCurrency)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type) {
|
||||||
|
if (!item.account.isAsset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
if (!item.account.isLiability) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.NetWorth.type) {
|
||||||
|
// Do Nothing
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let amount = item.amountInDefaultCurrency;
|
||||||
|
|
||||||
|
if (item.account.isLiability) {
|
||||||
|
amount = -amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
let data = combinedDataMap[item.account.id];
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data.totalAmount += amount;
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
name: item.account.name,
|
||||||
|
type: 'account',
|
||||||
|
id: item.account.id,
|
||||||
|
icon: item.account.icon || DEFAULT_ACCOUNT_ICON.icon,
|
||||||
|
color: item.account.color || DEFAULT_ACCOUNT_COLOR,
|
||||||
|
hidden: item.primaryAccount.hidden || item.account.hidden,
|
||||||
|
displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder],
|
||||||
|
totalAmount: amount,
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const amountItem: TransactionAssetTrendsAnalysisDataAmount = {
|
||||||
|
year: dailyData.year,
|
||||||
|
month: dailyData.month,
|
||||||
|
day: dailyData.day,
|
||||||
|
totalAmount: amount
|
||||||
|
};
|
||||||
|
data.items.push(amountItem);
|
||||||
|
combinedDataMap[item.account.id] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.account.isAsset) {
|
||||||
|
dailyTotalAmount += amount;
|
||||||
|
} else if (item.account.isLiability) {
|
||||||
|
dailyTotalAmount -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.chartDataType === ChartDataType.NetWorth.type) {
|
||||||
|
let data = combinedDataMap['total'];
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data.totalAmount += dailyTotalAmount;
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
name: ChartDataType.NetWorth.name,
|
||||||
|
type: 'total',
|
||||||
|
id: 'total',
|
||||||
|
icon: '',
|
||||||
|
color: '',
|
||||||
|
hidden: false,
|
||||||
|
displayOrders: [1],
|
||||||
|
totalAmount: dailyTotalAmount,
|
||||||
|
items: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const amountItem: TransactionAssetTrendsAnalysisDataAmount = {
|
||||||
|
year: dailyData.year,
|
||||||
|
month: dailyData.month,
|
||||||
|
day: dailyData.day,
|
||||||
|
totalAmount: dailyTotalAmount
|
||||||
|
};
|
||||||
|
data.items.push(amountItem);
|
||||||
|
combinedDataMap['total'] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const allAssetTrendsDataItems: TransactionAssetTrendsAnalysisDataItem[] = [];
|
||||||
|
|
||||||
|
for (const assetTrendsDataItem of values(combinedDataMap)) {
|
||||||
|
allAssetTrendsDataItems.push(assetTrendsDataItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
sortCategoryTotalAmountItems(allAssetTrendsDataItems, transactionStatisticsFilter.value);
|
||||||
|
|
||||||
|
const assetTrendsData: TransactionAssetTrendsAnalysisData = {
|
||||||
|
items: allAssetTrendsDataItems
|
||||||
|
};
|
||||||
|
|
||||||
|
return assetTrendsData;
|
||||||
|
});
|
||||||
|
|
||||||
function createNewTransactionCategoricalOverviewAnalysisDataItem(id: string, name: string, type: TransactionCategoricalOverviewAnalysisDataItemType, displayOrders: number[], hidden: boolean): TransactionCategoricalOverviewAnalysisDataItem {
|
function createNewTransactionCategoricalOverviewAnalysisDataItem(id: string, name: string, type: TransactionCategoricalOverviewAnalysisDataItemType, displayOrders: number[], hidden: boolean): TransactionCategoricalOverviewAnalysisDataItem {
|
||||||
const dataItem: TransactionCategoricalOverviewAnalysisDataItem = {
|
const dataItem: TransactionCategoricalOverviewAnalysisDataItem = {
|
||||||
id: id,
|
id: id,
|
||||||
@@ -1062,6 +1320,10 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
transactionStatisticsFilter.value.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type;
|
transactionStatisticsFilter.value.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type;
|
||||||
transactionStatisticsFilter.value.trendChartStartYearMonth = '';
|
transactionStatisticsFilter.value.trendChartStartYearMonth = '';
|
||||||
transactionStatisticsFilter.value.trendChartEndYearMonth = '';
|
transactionStatisticsFilter.value.trendChartEndYearMonth = '';
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartType = TrendChartType.Default.type;
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE.type;
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartStartTime = 0;
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartEndTime = 0;
|
||||||
transactionStatisticsFilter.value.filterAccountIds = {};
|
transactionStatisticsFilter.value.filterAccountIds = {};
|
||||||
transactionStatisticsFilter.value.filterCategoryIds = {};
|
transactionStatisticsFilter.value.filterCategoryIds = {};
|
||||||
transactionStatisticsFilter.value.tagIds = '';
|
transactionStatisticsFilter.value.tagIds = '';
|
||||||
@@ -1083,8 +1345,13 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
if (!ChartDataType.isAvailableForAnalysisType(transactionStatisticsFilter.value.chartDataType, analysisType)) {
|
if (!ChartDataType.isAvailableForAnalysisType(transactionStatisticsFilter.value.chartDataType, analysisType)) {
|
||||||
transactionStatisticsFilter.value.chartDataType = ChartDataType.Default.type;
|
transactionStatisticsFilter.value.chartDataType = ChartDataType.Default.type;
|
||||||
}
|
}
|
||||||
|
} else if (analysisType === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (!ChartDataType.isAvailableForAnalysisType(transactionStatisticsFilter.value.chartDataType, analysisType)) {
|
||||||
|
transactionStatisticsFilter.value.chartDataType = ChartDataType.DefaultForAssetTrends.type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Categorical Analysis filter initialization
|
||||||
if (filter && isInteger(filter.categoricalChartType)) {
|
if (filter && isInteger(filter.categoricalChartType)) {
|
||||||
transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType;
|
transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType;
|
||||||
} else {
|
} else {
|
||||||
@@ -1130,6 +1397,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trend Analysis filter initialization
|
||||||
if (filter && isInteger(filter.trendChartType)) {
|
if (filter && isInteger(filter.trendChartType)) {
|
||||||
transactionStatisticsFilter.value.trendChartType = filter.trendChartType;
|
transactionStatisticsFilter.value.trendChartType = filter.trendChartType;
|
||||||
} else {
|
} else {
|
||||||
@@ -1175,6 +1443,53 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Asset Trends filter initialization
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartType)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartType = filter.assetTrendsChartType;
|
||||||
|
} else {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartType = settingsStore.appSettings.statistics.defaultAssetTrendsChartType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TrendChartType.isValidType(transactionStatisticsFilter.value.assetTrendsChartType)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartType = TrendChartType.Default.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartDateType)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = filter.assetTrendsChartDateType;
|
||||||
|
} else {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = settingsStore.appSettings.statistics.defaultAssetTrendsChartDataRangeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
let assetTrendsChartDateTypeValid = true;
|
||||||
|
|
||||||
|
if (!DateRange.isAvailableForScene(transactionStatisticsFilter.value.assetTrendsChartDateType, DateRangeScene.AssetTrends)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = DEFAULT_ASSET_TRENDS_CHART_DATA_RANGE.type;
|
||||||
|
assetTrendsChartDateTypeValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assetTrendsChartDateTypeValid && transactionStatisticsFilter.value.assetTrendsChartDateType === DateRange.Custom.type) {
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartStartTime)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartStartTime = filter.assetTrendsChartStartTime;
|
||||||
|
} else {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartStartTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartEndTime)) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartEndTime = filter.assetTrendsChartEndTime;
|
||||||
|
} else {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartEndTime = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const assetTrendsChartDateRange = getDateRangeByDateType(transactionStatisticsFilter.value.assetTrendsChartDateType, userStore.currentUserFirstDayOfWeek, userStore.currentUserFiscalYearStart);
|
||||||
|
|
||||||
|
if (assetTrendsChartDateRange) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = assetTrendsChartDateRange.dateType;
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartStartTime = assetTrendsChartDateRange.minTime;
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartEndTime = assetTrendsChartDateRange.maxTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other filter initialization
|
||||||
if (filter && isObject(filter.filterAccountIds)) {
|
if (filter && isObject(filter.filterAccountIds)) {
|
||||||
transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds;
|
transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds;
|
||||||
} else {
|
} else {
|
||||||
@@ -1224,6 +1539,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Categorical Analysis filter update
|
||||||
if (filter && isInteger(filter.categoricalChartType) && transactionStatisticsFilter.value.categoricalChartType !== filter.categoricalChartType) {
|
if (filter && isInteger(filter.categoricalChartType) && transactionStatisticsFilter.value.categoricalChartType !== filter.categoricalChartType) {
|
||||||
transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType;
|
transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -1244,6 +1560,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trend Analysis filter update
|
||||||
if (filter && isInteger(filter.trendChartType) && transactionStatisticsFilter.value.trendChartType !== filter.trendChartType) {
|
if (filter && isInteger(filter.trendChartType) && transactionStatisticsFilter.value.trendChartType !== filter.trendChartType) {
|
||||||
transactionStatisticsFilter.value.trendChartType = filter.trendChartType;
|
transactionStatisticsFilter.value.trendChartType = filter.trendChartType;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -1264,6 +1581,28 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Asset Trends filter update
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartType) && transactionStatisticsFilter.value.assetTrendsChartType !== filter.assetTrendsChartType) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartType = filter.assetTrendsChartType;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartDateType) && transactionStatisticsFilter.value.assetTrendsChartDateType !== filter.assetTrendsChartDateType) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartDateType = filter.assetTrendsChartDateType;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartStartTime) && transactionStatisticsFilter.value.assetTrendsChartStartTime !== filter.assetTrendsChartStartTime) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartStartTime = filter.assetTrendsChartStartTime;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter && isInteger(filter.assetTrendsChartEndTime) && transactionStatisticsFilter.value.assetTrendsChartEndTime !== filter.assetTrendsChartEndTime) {
|
||||||
|
transactionStatisticsFilter.value.assetTrendsChartEndTime = filter.assetTrendsChartEndTime;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other filter update
|
||||||
if (filter && isObject(filter.filterAccountIds) && !isEquals(transactionStatisticsFilter.value.filterAccountIds, filter.filterAccountIds)) {
|
if (filter && isObject(filter.filterAccountIds) && !isEquals(transactionStatisticsFilter.value.filterAccountIds, filter.filterAccountIds)) {
|
||||||
transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds;
|
transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -1297,7 +1636,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransactionStatisticsPageParams(analysisType: StatisticsAnalysisType, trendDateAggregationType: number): string {
|
function getTransactionStatisticsPageParams(analysisType: StatisticsAnalysisType, trendDateAggregationType: number, assetTrendsDateAggregationType: number): string {
|
||||||
const querys: string[] = [];
|
const querys: string[] = [];
|
||||||
|
|
||||||
querys.push('analysisType=' + analysisType);
|
querys.push('analysisType=' + analysisType);
|
||||||
@@ -1320,9 +1659,21 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
querys.push('endTime=' + transactionStatisticsFilter.value.trendChartEndYearMonth);
|
querys.push('endTime=' + transactionStatisticsFilter.value.trendChartEndYearMonth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trendDateAggregationType !== ChartDateAggregationType.Month.type) {
|
if (trendDateAggregationType !== ChartDateAggregationType.Default.type) {
|
||||||
querys.push('trendDateAggregationType=' + trendDateAggregationType);
|
querys.push('trendDateAggregationType=' + trendDateAggregationType);
|
||||||
}
|
}
|
||||||
|
} else if (analysisType === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
querys.push('chartType=' + transactionStatisticsFilter.value.assetTrendsChartType);
|
||||||
|
querys.push('chartDateType=' + transactionStatisticsFilter.value.assetTrendsChartDateType);
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.assetTrendsChartDateType === DateRange.Custom.type) {
|
||||||
|
querys.push('startTime=' + transactionStatisticsFilter.value.assetTrendsChartStartTime);
|
||||||
|
querys.push('endTime=' + transactionStatisticsFilter.value.assetTrendsChartEndTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assetTrendsDateAggregationType !== ChartDateAggregationType.Default.type) {
|
||||||
|
querys.push('assetTrendsDateAggregationType=' + assetTrendsDateAggregationType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionStatisticsFilter.value.filterAccountIds) {
|
if (transactionStatisticsFilter.value.filterAccountIds) {
|
||||||
@@ -1414,21 +1765,23 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
} else {
|
} else {
|
||||||
querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds));
|
querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds));
|
||||||
}
|
}
|
||||||
} else if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.InflowsByAccount.type
|
} else if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.InflowsByAccount.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.OutflowsByAccount.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.OutflowsByAccount.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)
|
||||||
|
) {
|
||||||
querys.push('accountIds=' + itemId);
|
querys.push('accountIds=' + itemId);
|
||||||
|
|
||||||
if (!isObjectEmpty(transactionStatisticsFilter.value.filterCategoryIds)) {
|
if ((analysisType === StatisticsAnalysisType.CategoricalAnalysis || analysisType === StatisticsAnalysisType.TrendAnalysis) && !isObjectEmpty(transactionStatisticsFilter.value.filterCategoryIds)) {
|
||||||
querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds));
|
querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds));
|
||||||
}
|
}
|
||||||
} else if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type
|
} else if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type ||
|
||||||
|| transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type)) {
|
transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type)
|
||||||
|
) {
|
||||||
querys.push('categoryIds=' + itemId);
|
querys.push('categoryIds=' + itemId);
|
||||||
|
|
||||||
if (!isObjectEmpty(transactionStatisticsFilter.value.filterAccountIds)) {
|
if (!isObjectEmpty(transactionStatisticsFilter.value.filterAccountIds)) {
|
||||||
@@ -1444,16 +1797,18 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionStatisticsFilter.value.tagIds) {
|
if (analysisType === StatisticsAnalysisType.CategoricalAnalysis || analysisType === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
querys.push('tagIds=' + transactionStatisticsFilter.value.tagIds);
|
if (transactionStatisticsFilter.value.tagIds) {
|
||||||
}
|
querys.push('tagIds=' + transactionStatisticsFilter.value.tagIds);
|
||||||
|
}
|
||||||
|
|
||||||
if (transactionStatisticsFilter.value.tagFilterType) {
|
if (transactionStatisticsFilter.value.tagFilterType) {
|
||||||
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionStatisticsFilter.value.keyword) {
|
if (transactionStatisticsFilter.value.keyword) {
|
||||||
querys.push('keyword=' + encodeURIComponent(transactionStatisticsFilter.value.keyword));
|
querys.push('keyword=' + encodeURIComponent(transactionStatisticsFilter.value.keyword));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analysisType === StatisticsAnalysisType.CategoricalAnalysis
|
if (analysisType === StatisticsAnalysisType.CategoricalAnalysis
|
||||||
@@ -1465,7 +1820,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
querys.push('minTime=' + transactionStatisticsFilter.value.categoricalChartStartTime);
|
querys.push('minTime=' + transactionStatisticsFilter.value.categoricalChartStartTime);
|
||||||
querys.push('maxTime=' + transactionStatisticsFilter.value.categoricalChartEndTime);
|
querys.push('maxTime=' + transactionStatisticsFilter.value.categoricalChartEndTime);
|
||||||
}
|
}
|
||||||
} else if (analysisType === StatisticsAnalysisType.TrendAnalysis && dateRange) {
|
} else if ((analysisType === StatisticsAnalysisType.TrendAnalysis || analysisType === StatisticsAnalysisType.AssetTrends) && dateRange) {
|
||||||
querys.push('dateType=' + dateRange.dateType);
|
querys.push('dateType=' + dateRange.dateType);
|
||||||
querys.push('minTime=' + dateRange.minTime);
|
querys.push('minTime=' + dateRange.minTime);
|
||||||
querys.push('maxTime=' + dateRange.maxTime);
|
querys.push('maxTime=' + dateRange.maxTime);
|
||||||
@@ -1560,6 +1915,45 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadAssetTrends({ force }: { force: boolean }): Promise<TransactionStatisticAssetTrendsResponseItem[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
services.getTransactionStatisticsAssetTrends({
|
||||||
|
startTime: transactionStatisticsFilter.value.assetTrendsChartStartTime,
|
||||||
|
endTime: transactionStatisticsFilter.value.assetTrendsChartEndTime
|
||||||
|
}).then(response => {
|
||||||
|
const data = response.data;
|
||||||
|
|
||||||
|
if (!data || !data.success || !data.result) {
|
||||||
|
reject({ message: 'Unable to retrieve transaction statistics' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsStateInvalid.value) {
|
||||||
|
updateTransactionStatisticsInvalidState(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force && data.result && isEquals(transactionAssetTrendsData.value, data.result)) {
|
||||||
|
reject({ message: 'Data is up to date', isUpToDate: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionAssetTrendsData.value = data.result;
|
||||||
|
|
||||||
|
resolve(data.result);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('failed to retrieve transaction statistics', error);
|
||||||
|
|
||||||
|
if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||||
|
reject({ error: error.response.data });
|
||||||
|
} else if (!error.processed) {
|
||||||
|
reject({ message: 'Unable to retrieve transaction statistics' });
|
||||||
|
} else {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// states
|
// states
|
||||||
transactionStatisticsFilter,
|
transactionStatisticsFilter,
|
||||||
@@ -1571,6 +1965,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
categoricalOverviewAnalysisData,
|
categoricalOverviewAnalysisData,
|
||||||
categoricalAnalysisData,
|
categoricalAnalysisData,
|
||||||
trendsAnalysisData,
|
trendsAnalysisData,
|
||||||
|
assetTrendsData,
|
||||||
// functions
|
// functions
|
||||||
updateTransactionStatisticsInvalidState,
|
updateTransactionStatisticsInvalidState,
|
||||||
resetTransactionStatistics,
|
resetTransactionStatistics,
|
||||||
@@ -1579,6 +1974,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
getTransactionStatisticsPageParams,
|
getTransactionStatisticsPageParams,
|
||||||
getTransactionListPageParams,
|
getTransactionListPageParams,
|
||||||
loadCategoricalAnalysis,
|
loadCategoricalAnalysis,
|
||||||
loadTrendAnalysis
|
loadTrendAnalysis,
|
||||||
|
loadAssetTrends
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
|||||||
import type { TypeAndDisplayName } from '@/core/base.ts';
|
import type { TypeAndDisplayName } from '@/core/base.ts';
|
||||||
import type { WeekDayValue } from '@/core/datetime.ts';
|
import type { WeekDayValue } from '@/core/datetime.ts';
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
|
import { StatisticsAnalysisType } from '@/core/statistics.ts';
|
||||||
import { KnownFileType } from '@/core/file.ts';
|
import { KnownFileType } from '@/core/file.ts';
|
||||||
import type { Account } from '@/models/account.ts';
|
import type { Account } from '@/models/account.ts';
|
||||||
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||||
@@ -55,7 +56,7 @@ export function useReconciliationStatementPageBase() {
|
|||||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
|
|
||||||
const allChartTypes = computed<TypeAndDisplayName[]>(() => getAllAccountBalanceTrendChartTypes());
|
const allChartTypes = computed<TypeAndDisplayName[]>(() => getAllAccountBalanceTrendChartTypes());
|
||||||
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypesWithShortName());
|
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypesWithShortName(StatisticsAnalysisType.AssetTrends));
|
||||||
|
|
||||||
const currentAccount = computed(() => allAccountsMap.value[accountId.value]);
|
const currentAccount = computed(() => allAccountsMap.value[accountId.value]);
|
||||||
const currentAccountCurrency = computed<string>(() => currentAccount.value?.currency ?? defaultCurrency.value);
|
const currentAccountCurrency = computed<string>(() => currentAccount.value?.currency ?? defaultCurrency.value);
|
||||||
|
|||||||
@@ -88,6 +88,14 @@ export const ALL_APPLICATION_CLOUD_SETTINGS: CategorizedApplicationCloudSettingI
|
|||||||
{ settingKey: 'statistics.defaultTrendChartType', settingName: 'Default Chart Type', mobile: false, desktop: true },
|
{ settingKey: 'statistics.defaultTrendChartType', settingName: 'Default Chart Type', mobile: false, desktop: true },
|
||||||
{ settingKey: 'statistics.defaultTrendChartDataRangeType', settingName: 'Default Date Range', mobile: true, desktop: true }
|
{ settingKey: 'statistics.defaultTrendChartDataRangeType', settingName: 'Default Date Range', mobile: true, desktop: true }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryName: 'Statistics Settings',
|
||||||
|
categorySubName: 'Asset Trends Settings',
|
||||||
|
items: [
|
||||||
|
{ settingKey: 'statistics.defaultAssetTrendsChartType', settingName: 'Default Chart Type', mobile: false, desktop: true },
|
||||||
|
{ settingKey: 'statistics.defaultAssetTrendsChartDataRangeType', settingName: 'Default Date Range', mobile: true, desktop: true }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export function useStatisticsSettingPageBase() {
|
|||||||
const allCategoricalChartDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.Normal, false));
|
const allCategoricalChartDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.Normal, false));
|
||||||
const allTrendChartTypes = computed<TypeAndDisplayName[]>(() => getAllTrendChartTypes());
|
const allTrendChartTypes = computed<TypeAndDisplayName[]>(() => getAllTrendChartTypes());
|
||||||
const allTrendChartDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.TrendAnalysis, false));
|
const allTrendChartDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.TrendAnalysis, false));
|
||||||
|
const allAssetTrendsChartDateRanges = computed<LocalizedDateRange[]>(() => getAllDateRanges(DateRangeScene.AssetTrends, false));
|
||||||
|
|
||||||
const defaultChartDataType = computed<number>({
|
const defaultChartDataType = computed<number>({
|
||||||
get: () => settingsStore.appSettings.statistics.defaultChartDataType,
|
get: () => settingsStore.appSettings.statistics.defaultChartDataType,
|
||||||
@@ -63,6 +64,16 @@ export function useStatisticsSettingPageBase() {
|
|||||||
set: (value: number) => settingsStore.setStatisticsDefaultTrendChartDateRange(value)
|
set: (value: number) => settingsStore.setStatisticsDefaultTrendChartDateRange(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const defaultAssetTrendsChartType = computed<number>({
|
||||||
|
get: () => settingsStore.appSettings.statistics.defaultAssetTrendsChartType,
|
||||||
|
set: (value: number) => settingsStore.setStatisticsDefaultAssetTrendsChartType(value)
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultAssetTrendsChartDateRange = computed<number>({
|
||||||
|
get: () => settingsStore.appSettings.statistics.defaultAssetTrendsChartDataRangeType,
|
||||||
|
set: (value: number) => settingsStore.setStatisticsDefaultAssetTrendsChartDateRange(value)
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// computed states
|
// computed states
|
||||||
allChartDataTypes,
|
allChartDataTypes,
|
||||||
@@ -72,12 +83,15 @@ export function useStatisticsSettingPageBase() {
|
|||||||
allCategoricalChartDateRanges,
|
allCategoricalChartDateRanges,
|
||||||
allTrendChartTypes,
|
allTrendChartTypes,
|
||||||
allTrendChartDateRanges,
|
allTrendChartDateRanges,
|
||||||
|
allAssetTrendsChartDateRanges,
|
||||||
defaultChartDataType,
|
defaultChartDataType,
|
||||||
defaultTimezoneType,
|
defaultTimezoneType,
|
||||||
defaultSortingType,
|
defaultSortingType,
|
||||||
defaultCategoricalChartType,
|
defaultCategoricalChartType,
|
||||||
defaultCategoricalChartDateRange,
|
defaultCategoricalChartDateRange,
|
||||||
defaultTrendChartType,
|
defaultTrendChartType,
|
||||||
defaultTrendChartDateRange
|
defaultTrendChartDateRange,
|
||||||
|
defaultAssetTrendsChartType,
|
||||||
|
defaultAssetTrendsChartDateRange
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ import type {
|
|||||||
TransactionCategoricalOverviewAnalysisData,
|
TransactionCategoricalOverviewAnalysisData,
|
||||||
TransactionCategoricalAnalysisData,
|
TransactionCategoricalAnalysisData,
|
||||||
TransactionCategoricalAnalysisDataItem,
|
TransactionCategoricalAnalysisDataItem,
|
||||||
TransactionTrendsAnalysisData
|
TransactionTrendsAnalysisData,
|
||||||
|
TransactionAssetTrendsAnalysisData
|
||||||
} from '@/models/transaction.ts';
|
} from '@/models/transaction.ts';
|
||||||
|
|
||||||
import { limitText, findNameByType, findDisplayNameByType } from '@/lib/common.ts';
|
import { limitText, findNameByType, findDisplayNameByType } from '@/lib/common.ts';
|
||||||
@@ -49,6 +50,7 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
const loading = ref<boolean>(true);
|
const loading = ref<boolean>(true);
|
||||||
const analysisType = ref<StatisticsAnalysisType>(StatisticsAnalysisType.CategoricalAnalysis);
|
const analysisType = ref<StatisticsAnalysisType>(StatisticsAnalysisType.CategoricalAnalysis);
|
||||||
const trendDateAggregationType = ref<number>(ChartDateAggregationType.Default.type);
|
const trendDateAggregationType = ref<number>(ChartDateAggregationType.Default.type);
|
||||||
|
const assetTrendsDateAggregationType = ref<number>(ChartDateAggregationType.Default.type);
|
||||||
|
|
||||||
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
||||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
@@ -60,12 +62,15 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return getAllDateRanges(DateRangeScene.Normal, true);
|
return getAllDateRanges(DateRangeScene.Normal, true);
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return getAllDateRanges(DateRangeScene.TrendAnalysis, true);
|
return getAllDateRanges(DateRangeScene.TrendAnalysis, true);
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return getAllDateRanges(DateRangeScene.AssetTrends, true);
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const allSortingTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsSortingTypes());
|
const allSortingTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsSortingTypes());
|
||||||
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypes());
|
const allTrendAnalysisDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.TrendAnalysis));
|
||||||
|
const allAssetTrendsDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.AssetTrends));
|
||||||
|
|
||||||
const query = computed<TransactionStatisticsFilter>(() => statisticsStore.transactionStatisticsFilter);
|
const query = computed<TransactionStatisticsFilter>(() => statisticsStore.transactionStatisticsFilter);
|
||||||
const queryChartDataCategory = computed<string>(() => statisticsStore.categoricalAnalysisChartDataCategory);
|
const queryChartDataCategory = computed<string>(() => statisticsStore.categoricalAnalysisChartDataCategory);
|
||||||
@@ -74,6 +79,8 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return query.value.categoricalChartDateType;
|
return query.value.categoricalChartDateType;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return query.value.trendChartDateType;
|
return query.value.trendChartDateType;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return query.value.assetTrendsChartDateType;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -84,6 +91,8 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return formatUnixTimeToLongDateTime(query.value.categoricalChartStartTime);
|
return formatUnixTimeToLongDateTime(query.value.categoricalChartStartTime);
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth));
|
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth));
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return formatUnixTimeToLongDateTime(query.value.assetTrendsChartStartTime);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -94,21 +103,25 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return formatUnixTimeToLongDateTime(query.value.categoricalChartEndTime);
|
return formatUnixTimeToLongDateTime(query.value.categoricalChartEndTime);
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
return formatUnixTimeToGregorianLikeLongYearMonth(getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return formatUnixTimeToLongDateTime(query.value.assetTrendsChartEndTime);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryDateRangeName = computed<string>(() => {
|
const queryDateRangeName = computed<string>(() => {
|
||||||
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
|
||||||
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
|
||||||
return tt(DateRange.All.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return tt(DateRange.All.name);
|
||||||
|
}
|
||||||
|
|
||||||
return formatDateRange(query.value.categoricalChartDateType, query.value.categoricalChartStartTime, query.value.categoricalChartEndTime);
|
return formatDateRange(query.value.categoricalChartDateType, query.value.categoricalChartStartTime, query.value.categoricalChartEndTime);
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return formatDateRange(query.value.trendChartDateType, getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth), getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
return formatDateRange(query.value.trendChartDateType, getYearMonthFirstUnixTime(query.value.trendChartStartYearMonth), getYearMonthLastUnixTime(query.value.trendChartEndYearMonth));
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return formatDateRange(query.value.assetTrendsChartDateType, query.value.assetTrendsChartStartTime, query.value.assetTrendsChartEndTime);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -124,7 +137,8 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return tt(querySortingTypeName);
|
return tt(querySortingTypeName);
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryTrendDateAggregationTypeName = computed<string>(() => findDisplayNameByType(allDateAggregationTypes.value, trendDateAggregationType.value) || '');
|
const queryTrendDateAggregationTypeName = computed<string>(() => findDisplayNameByType(allTrendAnalysisDateAggregationTypes.value, trendDateAggregationType.value) || '');
|
||||||
|
const queryAssetTrendsDateAggregationTypeName = computed<string>(() => findDisplayNameByType(allAssetTrendsDateAggregationTypes.value, assetTrendsDateAggregationType.value) || '');
|
||||||
|
|
||||||
const isQueryDateRangeChanged = computed<boolean>(() => {
|
const isQueryDateRangeChanged = computed<boolean>(() => {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
@@ -144,13 +158,31 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return !!query.value.trendChartStartYearMonth || !!query.value.trendChartEndYearMonth;
|
return !!query.value.trendChartStartYearMonth || !!query.value.trendChartEndYearMonth;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (query.value.assetTrendsChartDateType === settingsStore.appSettings.statistics.defaultAssetTrendsChartDataRangeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!query.value.assetTrendsChartStartTime || !!query.value.assetTrendsChartEndTime;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canChangeDateRange = computed<boolean>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const canShiftDateRange = computed<boolean>(() => {
|
const canShiftDateRange = computed<boolean>(() => {
|
||||||
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
if (!canChangeDateRange.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,13 +190,31 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
return query.value.categoricalChartDateType !== DateRange.All.type;
|
return query.value.categoricalChartDateType !== DateRange.All.type;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return query.value.trendChartDateType !== DateRange.All.type;
|
return query.value.trendChartDateType !== DateRange.All.type;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return query.value.assetTrendsChartDateType !== DateRange.All.type;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const canUseCategoryFilter = computed<boolean>(() => {
|
const canUseCategoryFilter = computed<boolean>(() => {
|
||||||
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const canUseServerCustomFilter = computed<boolean>(() => {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,25 +222,19 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const canUseTagFilter = computed<boolean>(() => {
|
const canUseTagFilter = computed<boolean>(() => {
|
||||||
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
return canUseServerCustomFilter.value;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const canUseKeywordFilter = computed<boolean>(() => {
|
const canUseKeywordFilter = computed<boolean>(() => {
|
||||||
if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
return canUseServerCustomFilter.value;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const showAmountInChart = computed<boolean>(() => {
|
const showAmountInChart = computed<boolean>(() => {
|
||||||
if (!showAccountBalance.value
|
if (!showAccountBalance.value) {
|
||||||
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis
|
||||||
return false;
|
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -231,7 +275,8 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
query.value.chartDataType !== ChartDataType.TotalInflows.type &&
|
query.value.chartDataType !== ChartDataType.TotalInflows.type &&
|
||||||
query.value.chartDataType !== ChartDataType.TotalIncome.type &&
|
query.value.chartDataType !== ChartDataType.TotalIncome.type &&
|
||||||
query.value.chartDataType !== ChartDataType.NetCashFlow.type &&
|
query.value.chartDataType !== ChartDataType.NetCashFlow.type &&
|
||||||
query.value.chartDataType !== ChartDataType.NetIncome.type;
|
query.value.chartDataType !== ChartDataType.NetIncome.type &&
|
||||||
|
query.value.chartDataType !== ChartDataType.NetWorth.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
const showStackedInTrendsChart = computed<boolean>(() => {
|
const showStackedInTrendsChart = computed<boolean>(() => {
|
||||||
@@ -246,18 +291,22 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
||||||
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetIncome.type;
|
query.value.chartDataType === ChartDataType.NetIncome.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.NetWorth.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
const categoricalOverviewAnalysisData = computed<TransactionCategoricalOverviewAnalysisData | null>(() => statisticsStore.categoricalOverviewAnalysisData);
|
const categoricalOverviewAnalysisData = computed<TransactionCategoricalOverviewAnalysisData | null>(() => statisticsStore.categoricalOverviewAnalysisData);
|
||||||
const categoricalAnalysisData = computed<TransactionCategoricalAnalysisData>(() => statisticsStore.categoricalAnalysisData);
|
const categoricalAnalysisData = computed<TransactionCategoricalAnalysisData>(() => statisticsStore.categoricalAnalysisData);
|
||||||
const trendsAnalysisData = computed<TransactionTrendsAnalysisData | null>(() => statisticsStore.trendsAnalysisData);
|
const trendsAnalysisData = computed<TransactionTrendsAnalysisData | null>(() => statisticsStore.trendsAnalysisData);
|
||||||
|
const assetTrendsData = computed<TransactionAssetTrendsAnalysisData | null>(() => statisticsStore.assetTrendsData);
|
||||||
|
|
||||||
function canShowCustomDateRange(dateRangeType: number): boolean {
|
function canShowCustomDateRange(dateRangeType: number): boolean {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
return query.value.categoricalChartDateType === dateRangeType && !!query.value.categoricalChartStartTime && !!query.value.categoricalChartEndTime;
|
return query.value.categoricalChartDateType === dateRangeType && !!query.value.categoricalChartStartTime && !!query.value.categoricalChartEndTime;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return query.value.trendChartDateType === dateRangeType && !!query.value.trendChartStartYearMonth && !!query.value.trendChartEndYearMonth;
|
return query.value.trendChartDateType === dateRangeType && !!query.value.trendChartStartYearMonth && !!query.value.trendChartEndYearMonth;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return query.value.assetTrendsChartDateType === dateRangeType && !!query.value.assetTrendsChartStartTime && !!query.value.assetTrendsChartEndTime;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -276,11 +325,11 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
function getDisplayAmount(amount: number, currency: string, textLimit?: number): string {
|
function getDisplayAmount(amount: number, currency: string, textLimit?: number): string {
|
||||||
const finalAmount = formatAmountToLocalizedNumeralsWithCurrency(amount, currency);
|
const finalAmount = formatAmountToLocalizedNumeralsWithCurrency(amount, currency);
|
||||||
|
|
||||||
if (!showAccountBalance.value
|
if (!showAccountBalance.value) {
|
||||||
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis
|
||||||
|| query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)
|
&& (query.value.chartDataType === ChartDataType.AccountTotalAssets.type || query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) {
|
||||||
) {
|
return DISPLAY_HIDDEN_AMOUNT;
|
||||||
return DISPLAY_HIDDEN_AMOUNT;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textLimit) {
|
if (textLimit) {
|
||||||
@@ -295,6 +344,7 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
loading,
|
loading,
|
||||||
analysisType,
|
analysisType,
|
||||||
trendDateAggregationType,
|
trendDateAggregationType,
|
||||||
|
assetTrendsDateAggregationType,
|
||||||
// computed states
|
// computed states
|
||||||
showAccountBalance,
|
showAccountBalance,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
@@ -302,7 +352,8 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
fiscalYearStart,
|
fiscalYearStart,
|
||||||
allDateRanges,
|
allDateRanges,
|
||||||
allSortingTypes,
|
allSortingTypes,
|
||||||
allDateAggregationTypes,
|
allTrendAnalysisDateAggregationTypes,
|
||||||
|
allAssetTrendsDateAggregationTypes,
|
||||||
query,
|
query,
|
||||||
queryChartDataCategory,
|
queryChartDataCategory,
|
||||||
queryDateType,
|
queryDateType,
|
||||||
@@ -312,7 +363,9 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
queryChartDataTypeName,
|
queryChartDataTypeName,
|
||||||
querySortingTypeName,
|
querySortingTypeName,
|
||||||
queryTrendDateAggregationTypeName,
|
queryTrendDateAggregationTypeName,
|
||||||
|
queryAssetTrendsDateAggregationTypeName,
|
||||||
isQueryDateRangeChanged,
|
isQueryDateRangeChanged,
|
||||||
|
canChangeDateRange,
|
||||||
canShiftDateRange,
|
canShiftDateRange,
|
||||||
canUseCategoryFilter,
|
canUseCategoryFilter,
|
||||||
canUseTagFilter,
|
canUseTagFilter,
|
||||||
@@ -326,6 +379,7 @@ export function useStatisticsTransactionPageBase() {
|
|||||||
categoricalOverviewAnalysisData,
|
categoricalOverviewAnalysisData,
|
||||||
categoricalAnalysisData,
|
categoricalAnalysisData,
|
||||||
trendsAnalysisData,
|
trendsAnalysisData,
|
||||||
|
assetTrendsData,
|
||||||
// functions
|
// functions
|
||||||
canShowCustomDateRange,
|
canShowCustomDateRange,
|
||||||
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
||||||
|
|||||||
@@ -29,10 +29,6 @@
|
|||||||
v-for="type in allChartTypes"></v-list-item>
|
v-for="type in allChartTypes"></v-list-item>
|
||||||
<v-divider class="my-2"/>
|
<v-divider class="my-2"/>
|
||||||
<v-list-subheader :title="tt('Time Granularity')"/>
|
<v-list-subheader :title="tt('Time Granularity')"/>
|
||||||
<v-list-item :prepend-icon="mdiCalendarTodayOutline"
|
|
||||||
:append-icon="chartDataDateAggregationType === undefined ? mdiCheck : undefined"
|
|
||||||
:title="tt('granularity.Daily')"
|
|
||||||
@click="chartDataDateAggregationType = undefined"></v-list-item>
|
|
||||||
<v-list-item :key="dateAggregationType.type"
|
<v-list-item :key="dateAggregationType.type"
|
||||||
:prepend-icon="chartDataDateAggregationTypeIconMap[dateAggregationType.type]"
|
:prepend-icon="chartDataDateAggregationTypeIconMap[dateAggregationType.type]"
|
||||||
:append-icon="chartDataDateAggregationType === dateAggregationType.type ? mdiCheck : undefined"
|
:append-icon="chartDataDateAggregationType === dateAggregationType.type ? mdiCheck : undefined"
|
||||||
@@ -360,6 +356,7 @@ const chartTypeIconMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const chartDataDateAggregationTypeIconMap = {
|
const chartDataDateAggregationTypeIconMap = {
|
||||||
|
[ChartDateAggregationType.Day.type]: mdiCalendarTodayOutline,
|
||||||
[ChartDateAggregationType.Month.type]: mdiCalendarMonthOutline,
|
[ChartDateAggregationType.Month.type]: mdiCalendarMonthOutline,
|
||||||
[ChartDateAggregationType.Quarter.type]: mdiLayersTripleOutline,
|
[ChartDateAggregationType.Quarter.type]: mdiLayersTripleOutline,
|
||||||
[ChartDateAggregationType.Year.type]: mdiLayersTripleOutline,
|
[ChartDateAggregationType.Year.type]: mdiLayersTripleOutline,
|
||||||
@@ -376,7 +373,7 @@ const currentPage = ref<number>(1);
|
|||||||
const countPerPage = ref<number>(10);
|
const countPerPage = ref<number>(10);
|
||||||
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
||||||
const chartType = ref<number>(AccountBalanceTrendChartType.Default.type);
|
const chartType = ref<number>(AccountBalanceTrendChartType.Default.type);
|
||||||
const chartDataDateAggregationType = ref<number | undefined>(undefined);
|
const chartDataDateAggregationType = ref<number>(ChartDateAggregationType.Day.type);
|
||||||
|
|
||||||
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
||||||
|
|
||||||
@@ -453,7 +450,7 @@ function open(options: { accountId: string, startTime: number, endTime: number }
|
|||||||
countPerPage.value = 10;
|
countPerPage.value = 10;
|
||||||
showAccountBalanceTrendsCharts.value = false;
|
showAccountBalanceTrendsCharts.value = false;
|
||||||
chartType.value = AccountBalanceTrendChartType.Default.type;
|
chartType.value = AccountBalanceTrendChartType.Default.type;
|
||||||
chartDataDateAggregationType.value = undefined;
|
chartDataDateAggregationType.value = ChartDateAggregationType.Day.type;
|
||||||
showState.value = true;
|
showState.value = true;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,40 @@
|
|||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
<v-col cols="12">
|
||||||
|
<v-card :title="tt('Asset Trends Settings')">
|
||||||
|
<v-form>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-select
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="type"
|
||||||
|
persistent-placeholder
|
||||||
|
:label="tt('Default Chart Type')"
|
||||||
|
:placeholder="tt('Default Chart Type')"
|
||||||
|
:items="allTrendChartTypes"
|
||||||
|
v-model="defaultAssetTrendsChartType"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<v-col cols="12" md="6">
|
||||||
|
<v-select
|
||||||
|
item-title="displayName"
|
||||||
|
item-value="type"
|
||||||
|
persistent-placeholder
|
||||||
|
:label="tt('Default Date Range')"
|
||||||
|
:placeholder="tt('Default Date Range')"
|
||||||
|
:items="allAssetTrendsChartDateRanges"
|
||||||
|
v-model="defaultAssetTrendsChartDateRange"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-form>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<account-filter-settings-card type="statisticsDefault" :auto-save="true" />
|
<account-filter-settings-card type="statisticsDefault" :auto-save="true" />
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -140,13 +174,16 @@ const {
|
|||||||
allCategoricalChartDateRanges,
|
allCategoricalChartDateRanges,
|
||||||
allTrendChartTypes,
|
allTrendChartTypes,
|
||||||
allTrendChartDateRanges,
|
allTrendChartDateRanges,
|
||||||
|
allAssetTrendsChartDateRanges,
|
||||||
defaultChartDataType,
|
defaultChartDataType,
|
||||||
defaultTimezoneType,
|
defaultTimezoneType,
|
||||||
defaultSortingType,
|
defaultSortingType,
|
||||||
defaultCategoricalChartType,
|
defaultCategoricalChartType,
|
||||||
defaultCategoricalChartDateRange,
|
defaultCategoricalChartDateRange,
|
||||||
defaultTrendChartType,
|
defaultTrendChartType,
|
||||||
defaultTrendChartDateRange
|
defaultTrendChartDateRange,
|
||||||
|
defaultAssetTrendsChartType,
|
||||||
|
defaultAssetTrendsChartDateRange
|
||||||
} = useStatisticsSettingPageBase();
|
} = useStatisticsSettingPageBase();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
<div class="mx-6 my-4">
|
<div class="mx-6 my-4">
|
||||||
<btn-vertical-group :disabled="loading" :buttons="[
|
<btn-vertical-group :disabled="loading" :buttons="[
|
||||||
{ name: tt('Categorical Analysis'), value: StatisticsAnalysisType.CategoricalAnalysis },
|
{ name: tt('Categorical Analysis'), value: StatisticsAnalysisType.CategoricalAnalysis },
|
||||||
{ name: tt('Trend Analysis'), value: StatisticsAnalysisType.TrendAnalysis }
|
{ name: tt('Trend Analysis'), value: StatisticsAnalysisType.TrendAnalysis },
|
||||||
|
{ name: tt('Asset Trends'), value: StatisticsAnalysisType.AssetTrends }
|
||||||
]" v-model="queryAnalysisType" />
|
]" v-model="queryAnalysisType" />
|
||||||
</div>
|
</div>
|
||||||
<v-divider />
|
<v-divider />
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
<v-main>
|
<v-main>
|
||||||
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
<v-window class="d-flex flex-grow-1 disable-tab-transition w-100-window-container" v-model="activeTab">
|
||||||
<v-window-item value="statisticsPage">
|
<v-window-item value="statisticsPage">
|
||||||
<v-card variant="flat" :min-height="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis ? '860' : '760'">
|
<v-card variant="flat" :min-height="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis || queryAnalysisType === StatisticsAnalysisType.AssetTrends ? '900' : '800'">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="title-and-toolbar d-flex align-center">
|
<div class="title-and-toolbar d-flex align-center">
|
||||||
<v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
|
<v-btn class="me-3 d-md-none" density="compact" color="default" variant="plain"
|
||||||
@@ -73,7 +74,7 @@
|
|||||||
@click="shiftDateRange(-1)"/>
|
@click="shiftDateRange(-1)"/>
|
||||||
<v-menu location="bottom">
|
<v-menu location="bottom">
|
||||||
<template #activator="{ props }">
|
<template #activator="{ props }">
|
||||||
<v-btn :disabled="loading || queryChartDataType === ChartDataType.AccountTotalAssets.type || queryChartDataType === ChartDataType.AccountTotalLiabilities.type"
|
<v-btn :disabled="loading || !canChangeDateRange"
|
||||||
v-bind="props">{{ queryDateRangeName }}</v-btn>
|
v-bind="props">{{ queryDateRangeName }}</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list :selected="[queryDateType]">
|
<v-list :selected="[queryDateType]">
|
||||||
@@ -110,12 +111,28 @@
|
|||||||
<v-list-item class="cursor-pointer" :key="aggregationType.type" :value="aggregationType.type"
|
<v-list-item class="cursor-pointer" :key="aggregationType.type" :value="aggregationType.type"
|
||||||
:append-icon="(trendDateAggregationType === aggregationType.type ? mdiCheck : undefined)"
|
:append-icon="(trendDateAggregationType === aggregationType.type ? mdiCheck : undefined)"
|
||||||
:title="aggregationType.displayName"
|
:title="aggregationType.displayName"
|
||||||
v-for="aggregationType in allDateAggregationTypes"
|
v-for="aggregationType in allTrendAnalysisDateAggregationTypes"
|
||||||
@click="setTrendDateAggregationType(aggregationType.type)">
|
@click="setTrendDateAggregationType(aggregationType.type)">
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-menu>
|
</v-menu>
|
||||||
|
|
||||||
|
<v-menu location="bottom" v-if="queryAnalysisType === StatisticsAnalysisType.AssetTrends">
|
||||||
|
<template #activator="{ props }">
|
||||||
|
<v-btn class="ms-3" color="default" variant="outlined"
|
||||||
|
:prepend-icon="mdiCalendarRangeOutline" :disabled="loading"
|
||||||
|
v-bind="props">{{ queryAssetTrendsDateAggregationTypeName }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<v-list>
|
||||||
|
<v-list-item class="cursor-pointer" :key="aggregationType.type" :value="aggregationType.type"
|
||||||
|
:append-icon="(assetTrendsDateAggregationType === aggregationType.type ? mdiCheck : undefined)"
|
||||||
|
:title="aggregationType.displayName"
|
||||||
|
v-for="aggregationType in allAssetTrendsDateAggregationTypes"
|
||||||
|
@click="setAssetTrendsDateAggregationType(aggregationType.type)">
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
|
||||||
<v-btn density="compact" color="default" variant="text" size="24"
|
<v-btn density="compact" color="default" variant="text" size="24"
|
||||||
class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
|
class="ms-2" :icon="true" :loading="loading" @click="reload(true)">
|
||||||
<template #loader>
|
<template #loader>
|
||||||
@@ -205,10 +222,11 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-text class="statistics-overview-title pt-0"
|
<v-card-text class="statistics-overview-title pt-0"
|
||||||
v-else-if="!initing && (
|
v-else-if="!loading && (
|
||||||
(queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && isQuerySpecialChartType && queryChartDataType === ChartDataType.Overview.type && (!categoricalOverviewAnalysisData || !categoricalOverviewAnalysisData.items || !categoricalOverviewAnalysisData.items.length))
|
(queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && isQuerySpecialChartType && queryChartDataType === ChartDataType.Overview.type && (!categoricalOverviewAnalysisData || !categoricalOverviewAnalysisData.items || !categoricalOverviewAnalysisData.items.length))
|
||||||
|| (queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && !isQuerySpecialChartType && (!categoricalAnalysisData || !categoricalAnalysisData.items || !categoricalAnalysisData.items.length))
|
|| (queryAnalysisType === StatisticsAnalysisType.CategoricalAnalysis && !isQuerySpecialChartType && (!categoricalAnalysisData || !categoricalAnalysisData.items || !categoricalAnalysisData.items.length))
|
||||||
|| (queryAnalysisType === StatisticsAnalysisType.TrendAnalysis && (!trendsAnalysisData || !trendsAnalysisData.items || !trendsAnalysisData.items.length))
|
|| (queryAnalysisType === StatisticsAnalysisType.TrendAnalysis && (!trendsAnalysisData || !trendsAnalysisData.items || !trendsAnalysisData.items.length))
|
||||||
|
|| (queryAnalysisType === StatisticsAnalysisType.AssetTrends && (!assetTrendsData || !assetTrendsData.items || !assetTrendsData.items.length))
|
||||||
)">
|
)">
|
||||||
<span class="statistics-subtitle statistics-overview-empty-tip">{{ tt('No transaction data') }}</span>
|
<span class="statistics-subtitle statistics-overview-empty-tip">{{ tt('No transaction data') }}</span>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
@@ -345,11 +363,15 @@
|
|||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
<v-card-text :class="{ 'readonly': loading }" v-if="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis">
|
<v-card-text :class="{ 'readonly': loading }" v-if="queryAnalysisType === StatisticsAnalysisType.TrendAnalysis">
|
||||||
<monthly-trends-chart
|
<trends-chart
|
||||||
|
chart-mode="monthly"
|
||||||
:type="queryChartType"
|
:type="queryChartType"
|
||||||
|
:start-time="undefined"
|
||||||
|
:end-time="undefined"
|
||||||
:start-year-month="query.trendChartStartYearMonth"
|
:start-year-month="query.trendChartStartYearMonth"
|
||||||
:end-year-month="query.trendChartEndYearMonth"
|
:end-year-month="query.trendChartEndYearMonth"
|
||||||
:sorting-type="querySortingType"
|
:sorting-type="querySortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Sum"
|
||||||
:date-aggregation-type="trendDateAggregationType"
|
:date-aggregation-type="trendDateAggregationType"
|
||||||
:fiscal-year-start="fiscalYearStart"
|
:fiscal-year-start="fiscalYearStart"
|
||||||
:items="[]"
|
:items="[]"
|
||||||
@@ -360,11 +382,15 @@
|
|||||||
color-field="color"
|
color-field="color"
|
||||||
v-if="initing"
|
v-if="initing"
|
||||||
/>
|
/>
|
||||||
<monthly-trends-chart
|
<trends-chart
|
||||||
|
chart-mode="monthly"
|
||||||
:type="queryChartType"
|
:type="queryChartType"
|
||||||
|
:start-time="undefined"
|
||||||
|
:end-time="undefined"
|
||||||
:start-year-month="query.trendChartStartYearMonth"
|
:start-year-month="query.trendChartStartYearMonth"
|
||||||
:end-year-month="query.trendChartEndYearMonth"
|
:end-year-month="query.trendChartEndYearMonth"
|
||||||
:sorting-type="querySortingType"
|
:sorting-type="querySortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Sum"
|
||||||
:date-aggregation-type="trendDateAggregationType"
|
:date-aggregation-type="trendDateAggregationType"
|
||||||
:fiscal-year-start="fiscalYearStart"
|
:fiscal-year-start="fiscalYearStart"
|
||||||
:items="trendsAnalysisData && trendsAnalysisData.items && trendsAnalysisData.items.length ? trendsAnalysisData.items : []"
|
:items="trendsAnalysisData && trendsAnalysisData.items && trendsAnalysisData.items.length ? trendsAnalysisData.items : []"
|
||||||
@@ -384,6 +410,55 @@
|
|||||||
@click="onClickTrendChartItem"
|
@click="onClickTrendChartItem"
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-text :class="{ 'readonly': loading }" v-if="queryAnalysisType === StatisticsAnalysisType.AssetTrends">
|
||||||
|
<trends-chart
|
||||||
|
chart-mode="daily"
|
||||||
|
:type="queryChartType"
|
||||||
|
:start-time="query.assetTrendsChartStartTime"
|
||||||
|
:end-time="query.assetTrendsChartEndTime"
|
||||||
|
:start-year-month="undefined"
|
||||||
|
:end-year-month="undefined"
|
||||||
|
:sorting-type="querySortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Last"
|
||||||
|
:date-aggregation-type="assetTrendsDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="[]"
|
||||||
|
:skeleton="true"
|
||||||
|
id-field="id"
|
||||||
|
name-field="name"
|
||||||
|
value-field="value"
|
||||||
|
color-field="color"
|
||||||
|
v-if="initing"
|
||||||
|
/>
|
||||||
|
<trends-chart
|
||||||
|
chart-mode="daily"
|
||||||
|
:type="queryChartType"
|
||||||
|
:start-time="query.assetTrendsChartStartTime"
|
||||||
|
:end-time="query.assetTrendsChartEndTime"
|
||||||
|
:start-year-month="undefined"
|
||||||
|
:end-year-month="undefined"
|
||||||
|
:sorting-type="querySortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Last"
|
||||||
|
:date-aggregation-type="assetTrendsDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="assetTrendsData && assetTrendsData.items && assetTrendsData.items.length ? assetTrendsData.items : []"
|
||||||
|
:translate-name="translateNameInTrendsChart"
|
||||||
|
:show-value="showAmountInChart"
|
||||||
|
:enable-click-item="true"
|
||||||
|
:default-currency="defaultCurrency"
|
||||||
|
:stacked="showStackedInTrendsChart"
|
||||||
|
:show-total-amount-in-tooltip="showTotalAmountInTrendsChart"
|
||||||
|
ref="dailyTrendsChart"
|
||||||
|
id-field="id"
|
||||||
|
name-field="name"
|
||||||
|
value-field="totalAmount"
|
||||||
|
hidden-field="hidden"
|
||||||
|
display-orders-field="displayOrders"
|
||||||
|
v-else-if="!initing && assetTrendsData && assetTrendsData.items && assetTrendsData.items.length"
|
||||||
|
@click="onClickTrendChartItem"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-window-item>
|
</v-window-item>
|
||||||
</v-window>
|
</v-window>
|
||||||
@@ -429,7 +504,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||||
import MonthlyTrendsChart from '@/components/desktop/MonthlyTrendsChart.vue';
|
import TrendsChart from '@/components/desktop/TrendsChart.vue';
|
||||||
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
|
import AccountFilterSettingsCard from '@/views/desktop/common/cards/AccountFilterSettingsCard.vue';
|
||||||
import CategoryFilterSettingsCard from '@/views/desktop/common/cards/CategoryFilterSettingsCard.vue';
|
import CategoryFilterSettingsCard from '@/views/desktop/common/cards/CategoryFilterSettingsCard.vue';
|
||||||
import TransactionTagFilterSettingsCard from '@/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue';
|
import TransactionTagFilterSettingsCard from '@/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue';
|
||||||
@@ -450,6 +525,7 @@ import type { TypeAndDisplayName } from '@/core/base.ts';
|
|||||||
import { type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
import { type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||||
import { ThemeType } from '@/core/theme.ts';
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
import {
|
import {
|
||||||
|
ChartDataAggregationType,
|
||||||
StatisticsAnalysisType,
|
StatisticsAnalysisType,
|
||||||
CategoricalChartType,
|
CategoricalChartType,
|
||||||
ChartDataType,
|
ChartDataType,
|
||||||
@@ -488,7 +564,7 @@ import {
|
|||||||
} from '@mdi/js';
|
} from '@mdi/js';
|
||||||
|
|
||||||
type SnackBarType = InstanceType<typeof SnackBar>;
|
type SnackBarType = InstanceType<typeof SnackBar>;
|
||||||
type MonthlyTrendsChartType = InstanceType<typeof MonthlyTrendsChart>;
|
type TrendsChartType = InstanceType<typeof TrendsChart>;
|
||||||
type ExportDialogType = InstanceType<typeof ExportDialog>;
|
type ExportDialogType = InstanceType<typeof ExportDialog>;
|
||||||
|
|
||||||
interface TransactionStatisticsProps {
|
interface TransactionStatisticsProps {
|
||||||
@@ -505,6 +581,7 @@ interface TransactionStatisticsProps {
|
|||||||
initKeyword?: string;
|
initKeyword?: string;
|
||||||
initSortingType?: string,
|
initSortingType?: string,
|
||||||
initTrendDateAggregationType?: string
|
initTrendDateAggregationType?: string
|
||||||
|
initAssetTrendsDateAggregationType?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<TransactionStatisticsProps>();
|
const props = defineProps<TransactionStatisticsProps>();
|
||||||
@@ -525,12 +602,14 @@ const {
|
|||||||
loading,
|
loading,
|
||||||
analysisType,
|
analysisType,
|
||||||
trendDateAggregationType,
|
trendDateAggregationType,
|
||||||
|
assetTrendsDateAggregationType,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
firstDayOfWeek,
|
firstDayOfWeek,
|
||||||
fiscalYearStart,
|
fiscalYearStart,
|
||||||
allDateRanges,
|
allDateRanges,
|
||||||
allSortingTypes,
|
allSortingTypes,
|
||||||
allDateAggregationTypes,
|
allTrendAnalysisDateAggregationTypes,
|
||||||
|
allAssetTrendsDateAggregationTypes,
|
||||||
query,
|
query,
|
||||||
queryChartDataCategory,
|
queryChartDataCategory,
|
||||||
queryDateType,
|
queryDateType,
|
||||||
@@ -538,6 +617,8 @@ const {
|
|||||||
queryEndTime,
|
queryEndTime,
|
||||||
queryDateRangeName,
|
queryDateRangeName,
|
||||||
queryTrendDateAggregationTypeName,
|
queryTrendDateAggregationTypeName,
|
||||||
|
queryAssetTrendsDateAggregationTypeName,
|
||||||
|
canChangeDateRange,
|
||||||
canShiftDateRange,
|
canShiftDateRange,
|
||||||
canUseCategoryFilter,
|
canUseCategoryFilter,
|
||||||
canUseTagFilter,
|
canUseTagFilter,
|
||||||
@@ -551,6 +632,7 @@ const {
|
|||||||
categoricalOverviewAnalysisData,
|
categoricalOverviewAnalysisData,
|
||||||
categoricalAnalysisData,
|
categoricalAnalysisData,
|
||||||
trendsAnalysisData,
|
trendsAnalysisData,
|
||||||
|
assetTrendsData,
|
||||||
canShowCustomDateRange,
|
canShowCustomDateRange,
|
||||||
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
||||||
getDisplayAmount
|
getDisplayAmount
|
||||||
@@ -561,7 +643,8 @@ const transactionCategoriesStore = useTransactionCategoriesStore();
|
|||||||
const statisticsStore = useStatisticsStore();
|
const statisticsStore = useStatisticsStore();
|
||||||
|
|
||||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||||
const monthlyTrendsChart = useTemplateRef<MonthlyTrendsChartType>('monthlyTrendsChart');
|
const monthlyTrendsChart = useTemplateRef<TrendsChartType>('monthlyTrendsChart');
|
||||||
|
const dailyTrendsChart = useTemplateRef<TrendsChartType>('dailyTrendsChart');
|
||||||
const exportDialog = useTemplateRef<ExportDialogType>('exportDialog');
|
const exportDialog = useTemplateRef<ExportDialogType>('exportDialog');
|
||||||
|
|
||||||
const activeTab = ref<string>('statisticsPage');
|
const activeTab = ref<string>('statisticsPage');
|
||||||
@@ -582,6 +665,8 @@ const statisticsDataHasData = computed<boolean>(() => {
|
|||||||
return !!categoricalAnalysisData.value && !!categoricalAnalysisData.value.items && categoricalAnalysisData.value.items.length > 0;
|
return !!categoricalAnalysisData.value && !!categoricalAnalysisData.value.items && categoricalAnalysisData.value.items.length > 0;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return !!trendsAnalysisData.value && !!trendsAnalysisData.value.items && trendsAnalysisData.value.items.length > 0 && !!monthlyTrendsChart.value;
|
return !!trendsAnalysisData.value && !!trendsAnalysisData.value.items && trendsAnalysisData.value.items.length > 0 && !!monthlyTrendsChart.value;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return !!assetTrendsData.value && !!assetTrendsData.value.items && assetTrendsData.value.items.length > 0 && !!dailyTrendsChart.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -592,6 +677,8 @@ const allChartTypes = computed<TypeAndDisplayName[]>(() => {
|
|||||||
return getAllCategoricalChartTypes(true);
|
return getAllCategoricalChartTypes(true);
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return getAllTrendChartTypes();
|
return getAllTrendChartTypes();
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return getAllTrendChartTypes();
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -610,6 +697,8 @@ const queryChartType = computed<number | undefined>({
|
|||||||
return query.value.categoricalChartType;
|
return query.value.categoricalChartType;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return query.value.trendChartType;
|
return query.value.trendChartType;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return query.value.assetTrendsChartType;
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -654,7 +743,7 @@ const statisticsTextColor = computed<string>(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function getFilterLinkUrl(): string {
|
function getFilterLinkUrl(): string {
|
||||||
return `/statistics/transaction?${statisticsStore.getTransactionStatisticsPageParams(analysisType.value, trendDateAggregationType.value)}`;
|
return `/statistics/transaction?${statisticsStore.getTransactionStatisticsPageParams(analysisType.value, trendDateAggregationType.value, assetTrendsDateAggregationType.value)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransactionItemLinkUrl(itemId: string, dateRange?: TimeRangeAndDateType): string {
|
function getTransactionItemLinkUrl(itemId: string, dateRange?: TimeRangeAndDateType): string {
|
||||||
@@ -718,6 +807,29 @@ function init(initProps: TransactionStatisticsProps): void {
|
|||||||
if (initProps.initTrendDateAggregationType) {
|
if (initProps.initTrendDateAggregationType) {
|
||||||
trendDateAggregationType.value = parseInt(initProps.initTrendDateAggregationType);
|
trendDateAggregationType.value = parseInt(initProps.initTrendDateAggregationType);
|
||||||
}
|
}
|
||||||
|
} else if (initProps.initAnalysisType === StatisticsAnalysisType.AssetTrends.toString()) {
|
||||||
|
filter.assetTrendsChartType = initProps.initChartType ? parseInt(initProps.initChartType) : undefined;
|
||||||
|
filter.assetTrendsChartDateType = initProps.initChartDateType ? parseInt(initProps.initChartDateType) : undefined;
|
||||||
|
filter.assetTrendsChartStartTime = initProps.initStartTime ? parseInt(initProps.initStartTime) : undefined;
|
||||||
|
filter.assetTrendsChartEndTime = initProps.initEndTime ? parseInt(initProps.initEndTime) : undefined;
|
||||||
|
|
||||||
|
if (filter.assetTrendsChartDateType !== query.value.assetTrendsChartDateType) {
|
||||||
|
needReload = true;
|
||||||
|
} else if (filter.assetTrendsChartDateType === DateRange.Custom.type) {
|
||||||
|
if (filter.assetTrendsChartStartTime !== query.value.assetTrendsChartStartTime
|
||||||
|
|| filter.assetTrendsChartEndTime !== query.value.assetTrendsChartEndTime) {
|
||||||
|
needReload = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initProps.initAnalysisType !== analysisType.value.toString()) {
|
||||||
|
analysisType.value = StatisticsAnalysisType.AssetTrends;
|
||||||
|
needReload = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initProps.initAssetTrendsDateAggregationType) {
|
||||||
|
assetTrendsDateAggregationType.value = parseInt(initProps.initAssetTrendsDateAggregationType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDefined(initProps.initAnalysisType)) {
|
if (!isDefined(initProps.initAnalysisType)) {
|
||||||
@@ -745,6 +857,10 @@ function init(initProps: TransactionStatisticsProps): void {
|
|||||||
return statisticsStore.loadTrendAnalysis({
|
return statisticsStore.loadTrendAnalysis({
|
||||||
force: false
|
force: false
|
||||||
}) as Promise<unknown>;
|
}) as Promise<unknown>;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return statisticsStore.loadAssetTrends({
|
||||||
|
force: false
|
||||||
|
}) as Promise<unknown>;
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject('An error occurred');
|
return Promise.reject('An error occurred');
|
||||||
}
|
}
|
||||||
@@ -780,7 +896,8 @@ function reload(force: boolean): Promise<unknown> | null {
|
|||||||
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
||||||
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetIncome.type) {
|
query.value.chartDataType === ChartDataType.NetIncome.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.NetWorth.type) {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
dispatchPromise = statisticsStore.loadCategoricalAnalysis({
|
dispatchPromise = statisticsStore.loadCategoricalAnalysis({
|
||||||
force: force
|
force: force
|
||||||
@@ -789,12 +906,22 @@ function reload(force: boolean): Promise<unknown> | null {
|
|||||||
dispatchPromise = statisticsStore.loadTrendAnalysis({
|
dispatchPromise = statisticsStore.loadTrendAnalysis({
|
||||||
force: force
|
force: force
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
dispatchPromise = statisticsStore.loadAssetTrends({
|
||||||
|
force: force
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
dispatchPromise = accountsStore.loadAllAccounts({
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
force: force
|
dispatchPromise = accountsStore.loadAllAccounts({
|
||||||
});
|
force: force
|
||||||
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
dispatchPromise = statisticsStore.loadAssetTrends({
|
||||||
|
force: force
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatchPromise) {
|
if (dispatchPromise) {
|
||||||
@@ -822,13 +949,21 @@ function setAnalysisType(type: StatisticsAnalysisType): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ChartDataType.isAvailableForAnalysisType(query.value.chartDataType, type)) {
|
if (!ChartDataType.isAvailableForAnalysisType(query.value.chartDataType, type)) {
|
||||||
|
let defaultChartDataType: ChartDataType = ChartDataType.Default;
|
||||||
|
|
||||||
|
if (type === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
defaultChartDataType = ChartDataType.DefaultForAssetTrends;
|
||||||
|
}
|
||||||
|
|
||||||
statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
chartDataType: ChartDataType.Default.type
|
chartDataType: defaultChartDataType.type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (analysisType.value !== StatisticsAnalysisType.TrendAnalysis) {
|
if (analysisType.value !== StatisticsAnalysisType.TrendAnalysis && type === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
trendDateAggregationType.value = ChartDateAggregationType.Month.type;
|
trendDateAggregationType.value = ChartDateAggregationType.Default.type;
|
||||||
|
} else if (analysisType.value !== StatisticsAnalysisType.AssetTrends && type === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
assetTrendsDateAggregationType.value = ChartDateAggregationType.Default.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
analysisType.value = type;
|
analysisType.value = type;
|
||||||
@@ -848,6 +983,10 @@ function setChartType(type?: number): void {
|
|||||||
changed = statisticsStore.updateTransactionStatisticsFilter({
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartType: type
|
trendChartType: type
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartType: type
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -888,6 +1027,15 @@ function setTrendDateAggregationType(type: number): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAssetTrendsDateAggregationType(type: number): void {
|
||||||
|
const changed = assetTrendsDateAggregationType.value !== type;
|
||||||
|
assetTrendsDateAggregationType.value = type;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
router.push(getFilterLinkUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setDateFilter(dateType: number): void {
|
function setDateFilter(dateType: number): void {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
if (dateType === DateRange.Custom.type) { // Custom
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
@@ -903,6 +1051,13 @@ function setDateFilter(dateType: number): void {
|
|||||||
} else if (query.value.trendChartDateType === dateType) {
|
} else if (query.value.trendChartDateType === dateType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
|
showCustomDateRangeDialog.value = true;
|
||||||
|
return;
|
||||||
|
} else if (query.value.assetTrendsChartDateType === dateType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateRange = getDateRangeByDateType(dateType, firstDayOfWeek.value, fiscalYearStart.value);
|
const dateRange = getDateRangeByDateType(dateType, firstDayOfWeek.value, fiscalYearStart.value);
|
||||||
@@ -925,6 +1080,12 @@ function setDateFilter(dateType: number): void {
|
|||||||
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.minTime),
|
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.minTime),
|
||||||
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.maxTime)
|
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.maxTime)
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: dateRange.dateType,
|
||||||
|
assetTrendsChartStartTime: dateRange.minTime,
|
||||||
|
assetTrendsChartEndTime: dateRange.maxTime
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -961,6 +1122,16 @@ function setCustomDateFilter(startTime: number | TextualYearMonth, endTime: numb
|
|||||||
});
|
});
|
||||||
|
|
||||||
showCustomMonthRangeDialog.value = false;
|
showCustomMonthRangeDialog.value = false;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends && isNumber(startTime) && isNumber(endTime)) {
|
||||||
|
const chartDateType = getDateTypeByDateRange(startTime, endTime, firstDayOfWeek.value, fiscalYearStart.value, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: chartDateType,
|
||||||
|
assetTrendsChartStartTime: startTime,
|
||||||
|
assetTrendsChartEndTime: endTime
|
||||||
|
});
|
||||||
|
|
||||||
|
showCustomDateRangeDialog.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -993,6 +1164,18 @@ function shiftDateRange(scale: number): void {
|
|||||||
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.minTime),
|
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.minTime),
|
||||||
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.maxTime)
|
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.maxTime)
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (query.value.assetTrendsChartDateType === DateRange.All.type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDateRange = getShiftedDateRangeAndDateType(query.value.assetTrendsChartStartTime, query.value.assetTrendsChartEndTime, scale, firstDayOfWeek.value, fiscalYearStart.value, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: newDateRange.dateType,
|
||||||
|
assetTrendsChartStartTime: newDateRange.minTime,
|
||||||
|
assetTrendsChartEndTime: newDateRange.maxTime
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -1033,6 +1216,10 @@ function setTagFilter(changed: boolean): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setKeywordFilter(keyword: string): void {
|
function setKeywordFilter(keyword: string): void {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (query.value.keyword === keyword) {
|
if (query.value.keyword === keyword) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1078,6 +1265,12 @@ function exportResults(): void {
|
|||||||
headers: exportData.headers || [],
|
headers: exportData.headers || [],
|
||||||
data: exportData.data || []
|
data: exportData.data || []
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends && assetTrendsData.value && assetTrendsData.value.items && dailyTrendsChart.value) {
|
||||||
|
const exportData = dailyTrendsChart.value.exportData();
|
||||||
|
exportDialog.value?.open({
|
||||||
|
headers: exportData.headers || [],
|
||||||
|
data: exportData.data || []
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1125,7 +1318,8 @@ onBeforeRouteUpdate((to) => {
|
|||||||
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
|
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
|
||||||
initKeyword: (to.query['keyword'] as string | null) || undefined,
|
initKeyword: (to.query['keyword'] as string | null) || undefined,
|
||||||
initSortingType: (to.query['sortingType'] as string | null) || undefined,
|
initSortingType: (to.query['sortingType'] as string | null) || undefined,
|
||||||
initTrendDateAggregationType: (to.query['trendDateAggregationType'] as string | null) || undefined
|
initTrendDateAggregationType: (to.query['trendDateAggregationType'] as string | null) || undefined,
|
||||||
|
initAssetTrendsDateAggregationType: (to.query['assetTrendsDateAggregationType'] as string | null) || undefined
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
init({});
|
init({});
|
||||||
|
|||||||
@@ -1795,12 +1795,16 @@ init(props);
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.transaction-keyword-filter .v-input--density-compact {
|
.transaction-keyword-filter .v-input--density-compact {
|
||||||
--v-input-control-height: 36px !important;
|
--v-input-control-height: 38px !important;
|
||||||
--v-input-padding-top: 5px !important;
|
--v-input-padding-top: 5px !important;
|
||||||
--v-input-padding-bottom: 5px !important;
|
--v-input-padding-bottom: 5px !important;
|
||||||
--v-input-chips-margin-top: 0px !important;
|
--v-input-chips-margin-top: 0px !important;
|
||||||
--v-input-chips-margin-bottom: 0px !important;
|
--v-input-chips-margin-bottom: 0px !important;
|
||||||
inline-size: 20rem;
|
inline-size: 20rem;
|
||||||
|
|
||||||
|
.v-field__input {
|
||||||
|
min-block-size: 38px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.transaction-list-datetime-range {
|
.transaction-list-datetime-range {
|
||||||
|
|||||||
@@ -281,14 +281,6 @@
|
|||||||
<f7-popover class="chart-data-date-aggregation-type-popover-menu"
|
<f7-popover class="chart-data-date-aggregation-type-popover-menu"
|
||||||
v-model:opened="showChartDataDateAggregationTypePopover">
|
v-model:opened="showChartDataDateAggregationTypePopover">
|
||||||
<f7-list dividers>
|
<f7-list dividers>
|
||||||
<f7-list-item :title="tt('granularity.Daily')"
|
|
||||||
:class="{ 'list-item-selected': chartDataDateAggregationType === undefined }"
|
|
||||||
key="daily"
|
|
||||||
@click="setChartDataDateAggregationType(undefined)">
|
|
||||||
<template #after>
|
|
||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="chartDataDateAggregationType === undefined"></f7-icon>
|
|
||||||
</template>
|
|
||||||
</f7-list-item>
|
|
||||||
<f7-list-item :title="dateAggregationType.displayName"
|
<f7-list-item :title="dateAggregationType.displayName"
|
||||||
:class="{ 'list-item-selected': chartDataDateAggregationType === dateAggregationType.type }"
|
:class="{ 'list-item-selected': chartDataDateAggregationType === dateAggregationType.type }"
|
||||||
:key="dateAggregationType.type"
|
:key="dateAggregationType.type"
|
||||||
@@ -358,6 +350,7 @@ import { TextDirection } from '@/core/text.ts';
|
|||||||
import { type TimeRangeAndDateType, DateRange, DateRangeScene } from '@/core/datetime.ts';
|
import { type TimeRangeAndDateType, DateRange, DateRangeScene } from '@/core/datetime.ts';
|
||||||
import { AccountType } from '@/core/account.ts';
|
import { AccountType } from '@/core/account.ts';
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
|
import { ChartDateAggregationType } from '@/core/statistics.ts';
|
||||||
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
|
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
|
||||||
import { type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
import { type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
@@ -436,7 +429,7 @@ const loading = ref<boolean>(false);
|
|||||||
const loadingError = ref<unknown | null>(null);
|
const loadingError = ref<unknown | null>(null);
|
||||||
const queryDateRangeType = ref<number>(DateRange.ThisMonth.type);
|
const queryDateRangeType = ref<number>(DateRange.ThisMonth.type);
|
||||||
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
||||||
const chartDataDateAggregationType = ref<number | undefined>(undefined);
|
const chartDataDateAggregationType = ref<number>(ChartDateAggregationType.Day.type);
|
||||||
const transactionToDelete = ref<TransactionReconciliationStatementResponseItem | null>(null);
|
const transactionToDelete = ref<TransactionReconciliationStatementResponseItem | null>(null);
|
||||||
const newClosingBalance = ref<number>(0);
|
const newClosingBalance = ref<number>(0);
|
||||||
const showDisplayModePopover = ref<boolean>(false);
|
const showDisplayModePopover = ref<boolean>(false);
|
||||||
@@ -489,10 +482,6 @@ const allReconciliationStatementVirtualListItems = computed<ReconciliationStatem
|
|||||||
});
|
});
|
||||||
|
|
||||||
const chartDataDateAggregationTypeDisplayName = computed<string>(() => {
|
const chartDataDateAggregationTypeDisplayName = computed<string>(() => {
|
||||||
if (chartDataDateAggregationType.value === undefined) {
|
|
||||||
return tt('granularity.Daily');
|
|
||||||
}
|
|
||||||
|
|
||||||
return findDisplayNameByType(allDateAggregationTypes.value, chartDataDateAggregationType.value) || tt('Unknown');
|
return findDisplayNameByType(allDateAggregationTypes.value, chartDataDateAggregationType.value) || tt('Unknown');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -681,7 +670,7 @@ function removeTransaction(transaction: TransactionReconciliationStatementRespon
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setChartDataDateAggregationType(type: number | undefined): void {
|
function setChartDataDateAggregationType(type: number): void {
|
||||||
chartDataDateAggregationType.value = type;
|
chartDataDateAggregationType.value = type;
|
||||||
showChartDataDateAggregationTypePopover.value = false;
|
showChartDataDateAggregationTypePopover.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,28 @@
|
|||||||
</list-item-selection-popup>
|
</list-item-selection-popup>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-block-title>{{ tt('Asset Trends Settings') }}</f7-block-title>
|
||||||
|
<f7-list strong inset dividers>
|
||||||
|
<f7-list-item
|
||||||
|
link="#"
|
||||||
|
:title="tt('Default Date Range')"
|
||||||
|
:after="findDisplayNameByType(allAssetTrendsChartDateRanges, defaultAssetTrendsChartDateRange)"
|
||||||
|
@click="showDefaultAssetTrendsChartDateRangePopup = true"
|
||||||
|
>
|
||||||
|
<list-item-selection-popup value-type="item"
|
||||||
|
key-field="type" value-field="type"
|
||||||
|
title-field="displayName"
|
||||||
|
:title="tt('Default Date Range')"
|
||||||
|
:enable-filter="true"
|
||||||
|
:filter-placeholder="tt('Date Range')"
|
||||||
|
:filter-no-items-text="tt('No results')"
|
||||||
|
:items="allAssetTrendsChartDateRanges"
|
||||||
|
v-model:show="showDefaultAssetTrendsChartDateRangePopup"
|
||||||
|
v-model="defaultAssetTrendsChartDateRange">
|
||||||
|
</list-item-selection-popup>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
</f7-page>
|
</f7-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -145,12 +167,14 @@ const {
|
|||||||
allCategoricalChartTypes,
|
allCategoricalChartTypes,
|
||||||
allCategoricalChartDateRanges,
|
allCategoricalChartDateRanges,
|
||||||
allTrendChartDateRanges,
|
allTrendChartDateRanges,
|
||||||
|
allAssetTrendsChartDateRanges,
|
||||||
defaultChartDataType,
|
defaultChartDataType,
|
||||||
defaultTimezoneType,
|
defaultTimezoneType,
|
||||||
defaultSortingType,
|
defaultSortingType,
|
||||||
defaultCategoricalChartType,
|
defaultCategoricalChartType,
|
||||||
defaultCategoricalChartDateRange,
|
defaultCategoricalChartDateRange,
|
||||||
defaultTrendChartDateRange
|
defaultTrendChartDateRange,
|
||||||
|
defaultAssetTrendsChartDateRange
|
||||||
} = useStatisticsSettingPageBase();
|
} = useStatisticsSettingPageBase();
|
||||||
|
|
||||||
import { findDisplayNameByType } from '@/lib/common.ts';
|
import { findDisplayNameByType } from '@/lib/common.ts';
|
||||||
@@ -161,4 +185,5 @@ const showDefaultSortingTypePopup = ref<boolean>(false);
|
|||||||
const showDefaultCategoricalChartTypePopup = ref<boolean>(false);
|
const showDefaultCategoricalChartTypePopup = ref<boolean>(false);
|
||||||
const showDefaultCategoricalChartDateRangePopup = ref<boolean>(false);
|
const showDefaultCategoricalChartDateRangePopup = ref<boolean>(false);
|
||||||
const showDefaultTrendChartDateRangePopup = ref<boolean>(false);
|
const showDefaultTrendChartDateRangePopup = ref<boolean>(false);
|
||||||
|
const showDefaultAssetTrendsChartDateRangePopup = ref<boolean>(false);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -45,6 +45,20 @@
|
|||||||
</template>
|
</template>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
</f7-list-group>
|
</f7-list-group>
|
||||||
|
<f7-list-group>
|
||||||
|
<f7-list-item group-title>
|
||||||
|
<small>{{ tt('Asset Trends') }}</small>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item :title="tt(dataType.name)"
|
||||||
|
:class="{ 'list-item-selected': analysisType === StatisticsAnalysisType.AssetTrends && query.chartDataType === dataType.type }"
|
||||||
|
:key="dataType.type"
|
||||||
|
v-for="dataType in ChartDataType.values(StatisticsAnalysisType.AssetTrends)"
|
||||||
|
@click="setChartDataType(StatisticsAnalysisType.AssetTrends, dataType.type)">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="analysisType === StatisticsAnalysisType.AssetTrends && query.chartDataType === dataType.type"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list-group>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
@@ -203,11 +217,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</f7-card-header>
|
</f7-card-header>
|
||||||
<f7-card-content style="margin-top: -14px" :padding="false">
|
<f7-card-content style="margin-top: -14px" :padding="false">
|
||||||
<monthly-trends-bar-chart
|
<trends-bar-chart
|
||||||
|
chart-mode="monthly"
|
||||||
:loading="loading || reloading"
|
:loading="loading || reloading"
|
||||||
|
:start-time="undefined"
|
||||||
|
:end-time="undefined"
|
||||||
:start-year-month="query.trendChartStartYearMonth"
|
:start-year-month="query.trendChartStartYearMonth"
|
||||||
:end-year-month="query.trendChartEndYearMonth"
|
:end-year-month="query.trendChartEndYearMonth"
|
||||||
:sorting-type="query.sortingType"
|
:sorting-type="query.sortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Sum"
|
||||||
:date-aggregation-type="trendDateAggregationType"
|
:date-aggregation-type="trendDateAggregationType"
|
||||||
:fiscal-year-start="fiscalYearStart"
|
:fiscal-year-start="fiscalYearStart"
|
||||||
:items="trendsAnalysisData && trendsAnalysisData.items && trendsAnalysisData.items.length ? trendsAnalysisData.items : []"
|
:items="trendsAnalysisData && trendsAnalysisData.items && trendsAnalysisData.items.length ? trendsAnalysisData.items : []"
|
||||||
@@ -224,6 +242,42 @@
|
|||||||
</f7-card-content>
|
</f7-card-content>
|
||||||
</f7-card>
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-card v-else-if="analysisType === StatisticsAnalysisType.AssetTrends">
|
||||||
|
<f7-card-header class="no-border display-block">
|
||||||
|
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
||||||
|
<div></div>
|
||||||
|
<div class="align-self-flex-end">
|
||||||
|
<span style="margin-inline-end: 4px;">{{ tt('Sort by') }}</span>
|
||||||
|
<f7-link href="#" popover-open=".sorting-type-popover-menu">{{ querySortingTypeName }}</f7-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f7-card-header>
|
||||||
|
<f7-card-content style="margin-top: -14px" :padding="false">
|
||||||
|
<trends-bar-chart
|
||||||
|
chart-mode="daily"
|
||||||
|
:loading="loading || reloading"
|
||||||
|
:start-time="query.assetTrendsChartStartTime"
|
||||||
|
:end-time="query.assetTrendsChartEndTime"
|
||||||
|
:start-year-month="undefined"
|
||||||
|
:end-year-month="undefined"
|
||||||
|
:sorting-type="query.sortingType"
|
||||||
|
:data-aggregation-type="ChartDataAggregationType.Last"
|
||||||
|
:date-aggregation-type="assetTrendsDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="assetTrendsData && assetTrendsData.items && assetTrendsData.items.length ? assetTrendsData.items : []"
|
||||||
|
:stacked="showStackedInTrendsChart"
|
||||||
|
:translate-name="translateNameInTrendsChart"
|
||||||
|
:default-currency="defaultCurrency"
|
||||||
|
id-field="id"
|
||||||
|
name-field="name"
|
||||||
|
value-field="totalAmount"
|
||||||
|
hidden-field="hidden"
|
||||||
|
display-orders-field="displayOrders"
|
||||||
|
@click="onClickTrendChartItem"
|
||||||
|
/>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
<f7-popover class="sorting-type-popover-menu"
|
<f7-popover class="sorting-type-popover-menu"
|
||||||
v-model:opened="showSortingTypePopover">
|
v-model:opened="showSortingTypePopover">
|
||||||
<f7-list dividers>
|
<f7-list dividers>
|
||||||
@@ -243,7 +297,7 @@
|
|||||||
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(-1)">
|
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(-1)">
|
||||||
<f7-icon class="icon-with-direction" f7="arrow_left_square"></f7-icon>
|
<f7-icon class="icon-with-direction" f7="arrow_left_square"></f7-icon>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading || query.chartDataType === ChartDataType.AccountTotalAssets.type || query.chartDataType === ChartDataType.AccountTotalLiabilities.type }" popover-open=".date-popover-menu">
|
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading || !canChangeDateRange }" popover-open=".date-popover-menu">
|
||||||
<span :class="{ 'tabbar-item-changed': isQueryDateRangeChanged }">{{ queryDateRangeName }}</span>
|
<span :class="{ 'tabbar-item-changed': isQueryDateRangeChanged }">{{ queryDateRangeName }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(1)">
|
<f7-link :class="{ 'disabled': reloading || !canShiftDateRange }" @click="shiftDateRange(1)">
|
||||||
@@ -253,6 +307,10 @@
|
|||||||
v-if="analysisType === StatisticsAnalysisType.TrendAnalysis">
|
v-if="analysisType === StatisticsAnalysisType.TrendAnalysis">
|
||||||
<span :class="{ 'tabbar-item-changed': trendDateAggregationType !== ChartDateAggregationType.Default.type }">{{ queryTrendDateAggregationTypeName }}</span>
|
<span :class="{ 'tabbar-item-changed': trendDateAggregationType !== ChartDateAggregationType.Default.type }">{{ queryTrendDateAggregationTypeName }}</span>
|
||||||
</f7-link>
|
</f7-link>
|
||||||
|
<f7-link :class="{ 'tabbar-text-with-ellipsis': true, 'disabled': reloading }" popover-open=".date-aggregation-popover-menu"
|
||||||
|
v-if="analysisType === StatisticsAnalysisType.AssetTrends">
|
||||||
|
<span :class="{ 'tabbar-item-changed': assetTrendsDateAggregationType !== ChartDateAggregationType.Default.type }">{{ queryAssetTrendsDateAggregationTypeName }}</span>
|
||||||
|
</f7-link>
|
||||||
<f7-link class="tabbar-text-with-ellipsis" :key="chartType.type"
|
<f7-link class="tabbar-text-with-ellipsis" :key="chartType.type"
|
||||||
v-for="chartType in allChartTypes" @click="setChartType(chartType.type)">
|
v-for="chartType in allChartTypes" @click="setChartType(chartType.type)">
|
||||||
<span :class="{ 'tabbar-item-changed': queryChartType === chartType.type }">{{ chartType.displayName }}</span>
|
<span :class="{ 'tabbar-item-changed': queryChartType === chartType.type }">{{ chartType.displayName }}</span>
|
||||||
@@ -286,17 +344,28 @@
|
|||||||
<f7-popover class="date-aggregation-popover-menu"
|
<f7-popover class="date-aggregation-popover-menu"
|
||||||
v-model:opened="showDateAggregationPopover"
|
v-model:opened="showDateAggregationPopover"
|
||||||
@popover:open="scrollPopoverToSelectedItem">
|
@popover:open="scrollPopoverToSelectedItem">
|
||||||
<f7-list dividers>
|
<f7-list dividers v-if="analysisType === StatisticsAnalysisType.TrendAnalysis">
|
||||||
<f7-list-item :title="aggregationType.displayName"
|
<f7-list-item :title="aggregationType.displayName"
|
||||||
:class="{ 'list-item-selected': trendDateAggregationType === aggregationType.type }"
|
:class="{ 'list-item-selected': trendDateAggregationType === aggregationType.type }"
|
||||||
:key="aggregationType.type"
|
:key="aggregationType.type"
|
||||||
v-for="aggregationType in allDateAggregationTypes"
|
v-for="aggregationType in allTrendAnalysisDateAggregationTypes"
|
||||||
@click="setTrendDateAggregationType(aggregationType.type)">
|
@click="setTrendDateAggregationType(aggregationType.type)">
|
||||||
<template #after>
|
<template #after>
|
||||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="trendDateAggregationType === aggregationType.type"></f7-icon>
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="trendDateAggregationType === aggregationType.type"></f7-icon>
|
||||||
</template>
|
</template>
|
||||||
</f7-list-item>
|
</f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
<f7-list dividers v-else-if="analysisType === StatisticsAnalysisType.AssetTrends">
|
||||||
|
<f7-list-item :title="aggregationType.displayName"
|
||||||
|
:class="{ 'list-item-selected': assetTrendsDateAggregationType === aggregationType.type }"
|
||||||
|
:key="aggregationType.type"
|
||||||
|
v-for="aggregationType in allAssetTrendsDateAggregationTypes"
|
||||||
|
@click="setAssetTrendsDateAggregationType(aggregationType.type)">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="assetTrendsDateAggregationType === aggregationType.type"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
</f7-popover>
|
</f7-popover>
|
||||||
|
|
||||||
<date-range-selection-sheet :title="tt('Custom Date Range')"
|
<date-range-selection-sheet :title="tt('Custom Date Range')"
|
||||||
@@ -348,6 +417,7 @@ import type { TypeAndDisplayName } from '@/core/base.ts';
|
|||||||
import { TextDirection } from '@/core/text.ts';
|
import { TextDirection } from '@/core/text.ts';
|
||||||
import { type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
import { type TextualYearMonth, type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts';
|
||||||
import {
|
import {
|
||||||
|
ChartDataAggregationType,
|
||||||
StatisticsAnalysisType,
|
StatisticsAnalysisType,
|
||||||
CategoricalChartType,
|
CategoricalChartType,
|
||||||
ChartDataType,
|
ChartDataType,
|
||||||
@@ -383,12 +453,14 @@ const {
|
|||||||
loading,
|
loading,
|
||||||
analysisType,
|
analysisType,
|
||||||
trendDateAggregationType,
|
trendDateAggregationType,
|
||||||
|
assetTrendsDateAggregationType,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
firstDayOfWeek,
|
firstDayOfWeek,
|
||||||
fiscalYearStart,
|
fiscalYearStart,
|
||||||
allDateRanges,
|
allDateRanges,
|
||||||
allSortingTypes,
|
allSortingTypes,
|
||||||
allDateAggregationTypes,
|
allTrendAnalysisDateAggregationTypes,
|
||||||
|
allAssetTrendsDateAggregationTypes,
|
||||||
query,
|
query,
|
||||||
queryChartDataCategory,
|
queryChartDataCategory,
|
||||||
queryDateType,
|
queryDateType,
|
||||||
@@ -398,7 +470,9 @@ const {
|
|||||||
queryChartDataTypeName,
|
queryChartDataTypeName,
|
||||||
querySortingTypeName,
|
querySortingTypeName,
|
||||||
queryTrendDateAggregationTypeName,
|
queryTrendDateAggregationTypeName,
|
||||||
|
queryAssetTrendsDateAggregationTypeName,
|
||||||
isQueryDateRangeChanged,
|
isQueryDateRangeChanged,
|
||||||
|
canChangeDateRange,
|
||||||
canShiftDateRange,
|
canShiftDateRange,
|
||||||
canUseCategoryFilter,
|
canUseCategoryFilter,
|
||||||
canUseTagFilter,
|
canUseTagFilter,
|
||||||
@@ -410,6 +484,7 @@ const {
|
|||||||
translateNameInTrendsChart,
|
translateNameInTrendsChart,
|
||||||
categoricalAnalysisData,
|
categoricalAnalysisData,
|
||||||
trendsAnalysisData,
|
trendsAnalysisData,
|
||||||
|
assetTrendsData,
|
||||||
canShowCustomDateRange,
|
canShowCustomDateRange,
|
||||||
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
getTransactionCategoricalAnalysisDataItemDisplayColor,
|
||||||
getDisplayAmount
|
getDisplayAmount
|
||||||
@@ -445,6 +520,8 @@ const queryChartType = computed<number | undefined>({
|
|||||||
return query.value.categoricalChartType;
|
return query.value.categoricalChartType;
|
||||||
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
return query.value.trendChartType;
|
return query.value.trendChartType;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return query.value.assetTrendsChartType;
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -473,6 +550,10 @@ function init(): void {
|
|||||||
return statisticsStore.loadTrendAnalysis({
|
return statisticsStore.loadTrendAnalysis({
|
||||||
force: false
|
force: false
|
||||||
}) as Promise<unknown>;
|
}) as Promise<unknown>;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return statisticsStore.loadAssetTrends({
|
||||||
|
force: false
|
||||||
|
}) as Promise<unknown>;
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject('An error occurred');
|
return Promise.reject('An error occurred');
|
||||||
}
|
}
|
||||||
@@ -507,7 +588,8 @@ function reload(done?: () => void): void {
|
|||||||
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
query.value.chartDataType === ChartDataType.TotalInflows.type ||
|
||||||
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
query.value.chartDataType === ChartDataType.TotalIncome.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
query.value.chartDataType === ChartDataType.NetCashFlow.type ||
|
||||||
query.value.chartDataType === ChartDataType.NetIncome.type) {
|
query.value.chartDataType === ChartDataType.NetIncome.type ||
|
||||||
|
query.value.chartDataType === ChartDataType.NetWorth.type) {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
dispatchPromise = statisticsStore.loadCategoricalAnalysis({
|
dispatchPromise = statisticsStore.loadCategoricalAnalysis({
|
||||||
force: force
|
force: force
|
||||||
@@ -516,12 +598,22 @@ function reload(done?: () => void): void {
|
|||||||
dispatchPromise = statisticsStore.loadTrendAnalysis({
|
dispatchPromise = statisticsStore.loadTrendAnalysis({
|
||||||
force: force
|
force: force
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
dispatchPromise = statisticsStore.loadAssetTrends({
|
||||||
|
force: force
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
} else if (query.value.chartDataType === ChartDataType.AccountTotalAssets.type ||
|
||||||
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
query.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) {
|
||||||
dispatchPromise = accountsStore.loadAllAccounts({
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
force: force
|
dispatchPromise = accountsStore.loadAllAccounts({
|
||||||
});
|
force: force
|
||||||
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
dispatchPromise = statisticsStore.loadAssetTrends({
|
||||||
|
force: force
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispatchPromise) {
|
if (dispatchPromise) {
|
||||||
@@ -556,6 +648,10 @@ function setChartType(type?: number): void {
|
|||||||
statisticsStore.updateTransactionStatisticsFilter({
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
trendChartType: type
|
trendChartType: type
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartType: type
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,6 +699,11 @@ function setTrendDateAggregationType(type: number): void {
|
|||||||
showDateAggregationPopover.value = false;
|
showDateAggregationPopover.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAssetTrendsDateAggregationType(type: number): void {
|
||||||
|
assetTrendsDateAggregationType.value = type;
|
||||||
|
showDateAggregationPopover.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
function setDateFilter(dateType: number): void {
|
function setDateFilter(dateType: number): void {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
if (dateType === DateRange.Custom.type) { // Custom
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
@@ -620,6 +721,14 @@ function setDateFilter(dateType: number): void {
|
|||||||
} else if (query.value.trendChartDateType === dateType) {
|
} else if (query.value.trendChartDateType === dateType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
|
showCustomDateRangeSheet.value = true;
|
||||||
|
showDatePopover.value = false;
|
||||||
|
return;
|
||||||
|
} else if (query.value.assetTrendsChartDateType === dateType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateRange = getDateRangeByDateType(dateType, firstDayOfWeek.value, fiscalYearStart.value);
|
const dateRange = getDateRangeByDateType(dateType, firstDayOfWeek.value, fiscalYearStart.value);
|
||||||
@@ -642,6 +751,12 @@ function setDateFilter(dateType: number): void {
|
|||||||
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.minTime),
|
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.minTime),
|
||||||
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.maxTime)
|
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(dateRange.maxTime)
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: dateRange.dateType,
|
||||||
|
assetTrendsChartStartTime: dateRange.minTime,
|
||||||
|
assetTrendsChartEndTime: dateRange.maxTime
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
showDatePopover.value = false;
|
showDatePopover.value = false;
|
||||||
@@ -678,6 +793,16 @@ function setCustomDateFilter(startTime: number | TextualYearMonth, endTime: numb
|
|||||||
});
|
});
|
||||||
|
|
||||||
showCustomMonthRangeSheet.value = false;
|
showCustomMonthRangeSheet.value = false;
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends && isNumber(startTime) && isNumber(endTime)) {
|
||||||
|
const chartDateType = getDateTypeByDateRange(startTime, endTime, firstDayOfWeek.value, fiscalYearStart.value, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: chartDateType,
|
||||||
|
assetTrendsChartStartTime: startTime,
|
||||||
|
assetTrendsChartEndTime: endTime
|
||||||
|
});
|
||||||
|
|
||||||
|
showCustomDateRangeSheet.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -708,6 +833,18 @@ function shiftDateRange(scale: number): void {
|
|||||||
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.minTime),
|
trendChartStartYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.minTime),
|
||||||
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.maxTime)
|
trendChartEndYearMonth: getGregorianCalendarYearAndMonthFromUnixTime(newDateRange.maxTime)
|
||||||
});
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
if (query.value.assetTrendsChartDateType === DateRange.All.type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDateRange = getShiftedDateRangeAndDateType(query.value.assetTrendsChartStartTime, query.value.assetTrendsChartEndTime, scale, firstDayOfWeek.value, fiscalYearStart.value, DateRangeScene.AssetTrends);
|
||||||
|
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
assetTrendsChartDateType: newDateRange.dateType,
|
||||||
|
assetTrendsChartStartTime: newDateRange.minTime,
|
||||||
|
assetTrendsChartEndTime: newDateRange.maxTime
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
@@ -728,6 +865,10 @@ function filterTags(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function filterDescription(): void {
|
function filterDescription(): void {
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.AssetTrends) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
showPrompt('Filter transaction description', query.value.keyword, value => {
|
showPrompt('Filter transaction description', query.value.keyword, value => {
|
||||||
if (query.value.keyword === value) {
|
if (query.value.keyword === value) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user