add outflows / inflows / net cash flow in statistics & analysis
This commit is contained in:
+12
-2
@@ -426,7 +426,7 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any,
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, statisticReq.Keyword, utcOffset, statisticReq.UseTransactionTimezone)
|
||||
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalInflowAndOutflow(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, statisticReq.Keyword, utcOffset, statisticReq.UseTransactionTimezone)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||
@@ -447,6 +447,11 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any,
|
||||
AccountId: totalAmountItem.AccountId,
|
||||
TotalAmount: totalAmountItem.Amount,
|
||||
}
|
||||
|
||||
if totalAmountItem.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || totalAmountItem.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
statisticResp.Items[i].RelatedAccountId = totalAmountItem.RelatedAccountId
|
||||
statisticResp.Items[i].RelatedAccountType, _ = totalAmountItem.Type.ToTransactionRelatedAccountType()
|
||||
}
|
||||
}
|
||||
|
||||
return statisticResp, nil
|
||||
@@ -489,7 +494,7 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
||||
}
|
||||
|
||||
uid := c.GetCurrentUid()
|
||||
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, statisticTrendsReq.Keyword, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
||||
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyInflowAndOutflow(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, statisticTrendsReq.Keyword, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||
@@ -512,6 +517,11 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
||||
AccountId: totalAmountItem.AccountId,
|
||||
TotalAmount: totalAmountItem.Amount,
|
||||
}
|
||||
|
||||
if totalAmountItem.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || totalAmountItem.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
monthlyStatisticResp.Items[i].RelatedAccountId = totalAmountItem.RelatedAccountId
|
||||
monthlyStatisticResp.Items[i].RelatedAccountType, _ = totalAmountItem.Type.ToTransactionRelatedAccountType()
|
||||
}
|
||||
}
|
||||
|
||||
statisticTrendsResp = append(statisticTrendsResp, monthlyStatisticResp)
|
||||
|
||||
@@ -37,6 +37,15 @@ func (t TransactionType) ToTransactionDbType() (TransactionDbType, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// TransactionRelatedAccountType represents related account type in transaction
|
||||
type TransactionRelatedAccountType byte
|
||||
|
||||
// Transaction relation types
|
||||
const (
|
||||
TRANSACTION_RELATED_ACCOUNT_TYPE_TRANSFER_FROM TransactionRelatedAccountType = 1
|
||||
TRANSACTION_RELATED_ACCOUNT_TYPE_TRANSFER_TO TransactionRelatedAccountType = 2
|
||||
)
|
||||
|
||||
// TransactionDbType represents transaction type in database
|
||||
type TransactionDbType byte
|
||||
|
||||
@@ -84,6 +93,17 @@ func (t TransactionDbType) ToTransactionType() (TransactionType, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// ToTransactionRelatedAccountType returns the related account type for this db enum
|
||||
func (t TransactionDbType) ToTransactionRelatedAccountType() (TransactionRelatedAccountType, error) {
|
||||
if t == TRANSACTION_DB_TYPE_TRANSFER_OUT {
|
||||
return TRANSACTION_RELATED_ACCOUNT_TYPE_TRANSFER_TO, nil
|
||||
} else if t == TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
return TRANSACTION_RELATED_ACCOUNT_TYPE_TRANSFER_FROM, nil
|
||||
} else {
|
||||
return 0, errs.ErrTransactionTypeInvalid
|
||||
}
|
||||
}
|
||||
|
||||
// TransactionTagFilterType represents transaction tag filter type
|
||||
type TransactionTagFilterType byte
|
||||
|
||||
@@ -369,9 +389,11 @@ type TransactionStatisticResponse struct {
|
||||
|
||||
// TransactionStatisticResponseItem represents total amount item for a response
|
||||
type TransactionStatisticResponseItem struct {
|
||||
CategoryId int64 `json:"categoryId,string"`
|
||||
AccountId int64 `json:"accountId,string"`
|
||||
TotalAmount int64 `json:"amount"`
|
||||
CategoryId int64 `json:"categoryId,string"`
|
||||
AccountId int64 `json:"accountId,string"`
|
||||
RelatedAccountId int64 `json:"relatedAccountId,string,omitempty"`
|
||||
RelatedAccountType TransactionRelatedAccountType `json:"relatedAccountType,omitempty"`
|
||||
TotalAmount int64 `json:"amount"`
|
||||
}
|
||||
|
||||
// TransactionStatisticTrendsResponseItem represents the data within each statistic interval
|
||||
|
||||
@@ -1796,8 +1796,8 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(c core.Context, ui
|
||||
return incomeAmounts, expenseAmounts, nil
|
||||
}
|
||||
|
||||
// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range
|
||||
func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c core.Context, uid int64, startUnixTime int64, endUnixTime int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, keyword string, utcOffset int16, useTransactionTimezone bool) ([]*models.Transaction, error) {
|
||||
// GetAccountsAndCategoriesTotalInflowAndOutflow returns the every accounts and categories total inflows and outflows amount by specific date range
|
||||
func (s *TransactionService) GetAccountsAndCategoriesTotalInflowAndOutflow(c core.Context, uid int64, startUnixTime int64, endUnixTime int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, keyword string, utcOffset int16, useTransactionTimezone bool) ([]*models.Transaction, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -1817,12 +1817,14 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
||||
endTransactionTime = utils.GetMaxTransactionTimeFromUnixTime(endUnixTime)
|
||||
}
|
||||
|
||||
condition := "uid=? AND deleted=? AND (type=? OR type=?)"
|
||||
conditionParams := make([]any, 0, 4)
|
||||
condition := "uid=? AND deleted=? AND (type=? OR type=? OR type=? OR type=?)"
|
||||
conditionParams := make([]any, 0, 6)
|
||||
conditionParams = append(conditionParams, uid)
|
||||
conditionParams = append(conditionParams, false)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_EXPENSE)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN)
|
||||
|
||||
minTransactionTime := startTransactionTime
|
||||
maxTransactionTime := endTransactionTime
|
||||
@@ -1850,7 +1852,7 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
||||
finalConditionParams = append(finalConditionParams, "%%"+keyword+"%%")
|
||||
}
|
||||
|
||||
sess := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||
sess := s.UserDataDB(uid).NewSession(c).Select("type, category_id, account_id, related_account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||
|
||||
err := sess.Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||
@@ -1886,13 +1888,20 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
||||
}
|
||||
|
||||
groupKey := fmt.Sprintf("%d_%d", transaction.CategoryId, transaction.AccountId)
|
||||
|
||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
groupKey = fmt.Sprintf("%d_%d_%d_%d", transaction.CategoryId, transaction.AccountId, transaction.RelatedAccountId, transaction.Type)
|
||||
}
|
||||
|
||||
totalAmounts, exists := transactionTotalAmountsMap[groupKey]
|
||||
|
||||
if !exists {
|
||||
totalAmounts = &models.Transaction{
|
||||
CategoryId: transaction.CategoryId,
|
||||
AccountId: transaction.AccountId,
|
||||
Amount: 0,
|
||||
Type: transaction.Type,
|
||||
CategoryId: transaction.CategoryId,
|
||||
AccountId: transaction.AccountId,
|
||||
RelatedAccountId: transaction.RelatedAccountId,
|
||||
Amount: 0,
|
||||
}
|
||||
|
||||
transactionTotalAmountsMap[groupKey] = totalAmounts
|
||||
@@ -1910,8 +1919,8 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
||||
return transactionTotalAmounts, nil
|
||||
}
|
||||
|
||||
// GetAccountsAndCategoriesMonthlyIncomeAndExpense returns the every accounts monthly income and expense amount by specific date range
|
||||
func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c core.Context, uid int64, startYear int32, startMonth int32, endYear int32, endMonth int32, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, keyword string, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
|
||||
// GetAccountsAndCategoriesMonthlyInflowAndOutflow returns the every accounts monthly inflows and outflows amount by specific date range
|
||||
func (s *TransactionService) GetAccountsAndCategoriesMonthlyInflowAndOutflow(c core.Context, uid int64, startYear int32, startMonth int32, endYear int32, endMonth int32, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, keyword string, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -1936,12 +1945,14 @@ func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c c
|
||||
}
|
||||
}
|
||||
|
||||
condition := "uid=? AND deleted=? AND (type=? OR type=?)"
|
||||
conditionParams := make([]any, 0, 4)
|
||||
condition := "uid=? AND deleted=? AND (type=? OR type=? OR type=? OR type=?)"
|
||||
conditionParams := make([]any, 0, 6)
|
||||
conditionParams = append(conditionParams, uid)
|
||||
conditionParams = append(conditionParams, false)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_INCOME)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_EXPENSE)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_OUT)
|
||||
conditionParams = append(conditionParams, models.TRANSACTION_DB_TYPE_TRANSFER_IN)
|
||||
|
||||
minTransactionTime := startTransactionTime
|
||||
maxTransactionTime := endTransactionTime
|
||||
@@ -1969,7 +1980,7 @@ func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c c
|
||||
finalConditionParams = append(finalConditionParams, "%%"+keyword+"%%")
|
||||
}
|
||||
|
||||
sess := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||
sess := s.UserDataDB(uid).NewSession(c).Select("type, category_id, account_id, related_account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||
|
||||
err := sess.Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)
|
||||
@@ -2008,13 +2019,22 @@ func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c c
|
||||
}
|
||||
|
||||
groupKey := fmt.Sprintf("%d_%d_%d", yearMonth, transaction.CategoryId, transaction.AccountId)
|
||||
|
||||
if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN {
|
||||
groupKey = fmt.Sprintf("%d_%d_%d_%d_%d", yearMonth, transaction.CategoryId, transaction.AccountId, transaction.RelatedAccountId, transaction.Type)
|
||||
}
|
||||
|
||||
transactionAmounts, exists := transactionsMonthlyAmountsMap[groupKey]
|
||||
|
||||
if !exists {
|
||||
transactionAmounts = &models.Transaction{
|
||||
CategoryId: transaction.CategoryId,
|
||||
AccountId: transaction.AccountId,
|
||||
Type: transaction.Type,
|
||||
CategoryId: transaction.CategoryId,
|
||||
AccountId: transaction.AccountId,
|
||||
RelatedAccountId: transaction.RelatedAccountId,
|
||||
Amount: 0,
|
||||
}
|
||||
|
||||
transactionsMonthlyAmountsMap[groupKey] = transactionAmounts
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user