diff --git a/pkg/api/transactions.go b/pkg/api/transactions.go index 174fdce8..53da06a4 100644 --- a/pkg/api/transactions.go +++ b/pkg/api/transactions.go @@ -110,101 +110,17 @@ func (a *TransactionsApi) TransactionListHandler(c *core.Context) (interface{}, transactions = transactions[:transactionListReq.Count] } - transactionIds := make([]int64, len(transactions)) - accountIds := make([]int64, 0, len(transactions)*2) - categoryIds := make([]int64, 0, len(transactions)) - - for i := 0; i < len(transactions); i++ { - transactionId := transactions[i].TransactionId - - if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - transactionId = transactions[i].RelatedId - } - - transactionIds[i] = transactionId - accountIds = append(accountIds, transactions[i].AccountId) - - if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN || transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - accountIds = append(accountIds, transactions[i].RelatedAccountId) - } - - categoryIds = append(categoryIds, transactions[i].CategoryId) - } - - allAccounts, err := a.accounts.GetAccountsByAccountIds(uid, utils.ToUniqueInt64Slice(accountIds)) + transactionResult, err := a.getTransactionListResult(c, user, transactions, utcOffset, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag) if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed + log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) } - transactions = a.filterTransactions(c, uid, transactions, allAccounts) - - allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, transactionIds) - - if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed + transactionResps := &models.TransactionInfoPageWrapperResponse{ + Items: transactionResult, } - var categoryMap map[int64]*models.TransactionCategory - var tagMap map[int64]*models.TransactionTag - - if !transactionListReq.TrimCategory { - categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(uid, utils.ToUniqueInt64Slice(categoryIds)) - - if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed - } - } - - if !transactionListReq.TrimTag { - tagMap, err = a.transactionTags.GetTagsByTagIds(uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds))) - - if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionListHandler] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed - } - } - - transactionResps := &models.TransactionInfoPageWrapperResponse{} - transactionResps.Items = make(models.TransactionInfoResponseSlice, len(transactions)) - - for i := 0; i < len(transactions); i++ { - transaction := transactions[i] - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId) - } - - transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) - transactionTagIds := allTransactionTagIds[transaction.TransactionId] - transactionResps.Items[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) - - if !transactionListReq.TrimAccount { - if sourceAccount := allAccounts[transaction.AccountId]; sourceAccount != nil { - transactionResps.Items[i].SourceAccount = sourceAccount.ToAccountInfoResponse() - } - - if destinationAccount := allAccounts[transaction.RelatedAccountId]; destinationAccount != nil { - transactionResps.Items[i].DestinationAccount = destinationAccount.ToAccountInfoResponse() - } - } - - if !transactionListReq.TrimCategory { - if category := categoryMap[transaction.CategoryId]; category != nil { - transactionResps.Items[i].Category = category.ToTransactionCategoryInfoResponse() - } - } - - if !transactionListReq.TrimTag { - transactionResps.Items[i].Tags = a.getTransactionTagInfoResponses(transactionTagIds, tagMap) - } - } - - sort.Sort(transactionResps.Items) - if hasMore { transactionResps.NextTimeSequenceId = nextTimeSequenceId } @@ -254,96 +170,23 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.Context) (interfac return nil, errs.ErrOperationFailed } - transactionIds := make([]int64, len(transactions)) - accountIds := make([]int64, 0, len(transactions)*2) - categoryIds := make([]int64, 0, len(transactions)) - - for i := 0; i < len(transactions); i++ { - transactionId := transactions[i].TransactionId - - if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - transactionId = transactions[i].RelatedId - } - - transactionIds[i] = transactionId - accountIds = append(accountIds, transactions[i].AccountId) - - if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN || transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - accountIds = append(accountIds, transactions[i].RelatedAccountId) - } - - categoryIds = append(categoryIds, transactions[i].CategoryId) - } - - allAccounts, err := a.accounts.GetAccountsByAccountIds(uid, utils.ToUniqueInt64Slice(accountIds)) + totalCount, err := a.transactions.GetMonthTransactionCount(uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, transactionListReq.AccountId, transactionListReq.Keyword, utcOffset) if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error()) + log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transaction count in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error()) return nil, errs.ErrOperationFailed } - transactions = a.filterTransactions(c, uid, transactions, allAccounts) - - allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, transactionIds) + transactionResult, err := a.getTransactionListResult(c, user, transactions, utcOffset, transactionListReq.TrimAccount, transactionListReq.TrimCategory, transactionListReq.TrimTag) if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed + log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to assemble transaction result for user \"uid:%d\", because %s", uid, err.Error()) + return nil, errs.Or(err, errs.ErrOperationFailed) } - var categoryMap map[int64]*models.TransactionCategory - var tagMap map[int64]*models.TransactionTag - - if !transactionListReq.TrimCategory { - categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(uid, utils.ToUniqueInt64Slice(categoryIds)) - - if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed - } - } - - if !transactionListReq.TrimTag { - tagMap, err = a.transactionTags.GetTagsByTagIds(uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds))) - - if err != nil { - log.ErrorfWithRequestId(c, "[transactions.TransactionMonthListHandler] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error()) - return nil, errs.ErrOperationFailed - } - } - - transactionResps := make([]*models.TransactionInfoResponse, len(transactions)) - - for i := 0; i < len(transactions); i++ { - transaction := transactions[i] - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId) - } - - transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) - transactionTagIds := allTransactionTagIds[transaction.TransactionId] - transactionResps[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) - - if !transactionListReq.TrimAccount { - if sourceAccount := allAccounts[transaction.AccountId]; sourceAccount != nil { - transactionResps[i].SourceAccount = sourceAccount.ToAccountInfoResponse() - } - - if destinationAccount := allAccounts[transaction.RelatedAccountId]; destinationAccount != nil { - transactionResps[i].DestinationAccount = destinationAccount.ToAccountInfoResponse() - } - } - - if !transactionListReq.TrimCategory { - if category := categoryMap[transaction.CategoryId]; category != nil { - transactionResps[i].Category = category.ToTransactionCategoryInfoResponse() - } - } - - if !transactionListReq.TrimTag { - transactionResps[i].Tags = a.getTransactionTagInfoResponses(transactionTagIds, tagMap) - } + transactionResps := &models.TransactionInfoPageWrapperResponse2{ + Items: transactionResult, + TotalCount: totalCount, } return transactionResps, nil @@ -777,6 +620,105 @@ func (a *TransactionsApi) getTransactionTagInfoResponses(tagIds []int64, allTran return allTags } +func (a *TransactionsApi) getTransactionListResult(c *core.Context, user *models.User, transactions []*models.Transaction, utcOffset int16, trimAccount bool, trimCategory bool, trimTag bool) (models.TransactionInfoResponseSlice, error) { + uid := user.Uid + transactionIds := make([]int64, len(transactions)) + accountIds := make([]int64, 0, len(transactions)*2) + categoryIds := make([]int64, 0, len(transactions)) + + for i := 0; i < len(transactions); i++ { + transactionId := transactions[i].TransactionId + + if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + transactionId = transactions[i].RelatedId + } + + transactionIds[i] = transactionId + accountIds = append(accountIds, transactions[i].AccountId) + + if transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN || transactions[i].Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + accountIds = append(accountIds, transactions[i].RelatedAccountId) + } + + categoryIds = append(categoryIds, transactions[i].CategoryId) + } + + allAccounts, err := a.accounts.GetAccountsByAccountIds(uid, utils.ToUniqueInt64Slice(accountIds)) + + if err != nil { + log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get accounts for user \"uid:%d\", because %s", uid, err.Error()) + return nil, err + } + + transactions = a.filterTransactions(c, uid, transactions, allAccounts) + + allTransactionTagIds, err := a.transactionTags.GetAllTagIdsOfTransactions(uid, transactionIds) + + if err != nil { + log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tag ids for user \"uid:%d\", because %s", uid, err.Error()) + return nil, err + } + + var categoryMap map[int64]*models.TransactionCategory + var tagMap map[int64]*models.TransactionTag + + if !trimCategory { + categoryMap, err = a.transactionCategories.GetCategoriesByCategoryIds(uid, utils.ToUniqueInt64Slice(categoryIds)) + + if err != nil { + log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions categories for user \"uid:%d\", because %s", uid, err.Error()) + return nil, err + } + } + + if !trimTag { + tagMap, err = a.transactionTags.GetTagsByTagIds(uid, utils.ToUniqueInt64Slice(a.getTransactionTagIds(allTransactionTagIds))) + + if err != nil { + log.ErrorfWithRequestId(c, "[transactions.getTransactionListResult] failed to get transactions tags for user \"uid:%d\", because %s", uid, err.Error()) + return nil, err + } + } + + result := make(models.TransactionInfoResponseSlice, len(transactions)) + + for i := 0; i < len(transactions); i++ { + transaction := transactions[i] + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + transaction = a.transactions.GetRelatedTransferTransaction(transaction, transaction.RelatedId) + } + + transactionEditable := transaction.IsEditable(user, utcOffset, allAccounts[transaction.AccountId], allAccounts[transaction.RelatedAccountId]) + transactionTagIds := allTransactionTagIds[transaction.TransactionId] + result[i] = transaction.ToTransactionInfoResponse(transactionTagIds, transactionEditable) + + if !trimAccount { + if sourceAccount := allAccounts[transaction.AccountId]; sourceAccount != nil { + result[i].SourceAccount = sourceAccount.ToAccountInfoResponse() + } + + if destinationAccount := allAccounts[transaction.RelatedAccountId]; destinationAccount != nil { + result[i].DestinationAccount = destinationAccount.ToAccountInfoResponse() + } + } + + if !trimCategory { + if category := categoryMap[transaction.CategoryId]; category != nil { + result[i].Category = category.ToTransactionCategoryInfoResponse() + } + } + + if !trimTag { + result[i].Tags = a.getTransactionTagInfoResponses(transactionTagIds, tagMap) + } + } + + sort.Sort(result) + + return result, nil +} + func (a *TransactionsApi) createNewTransactionModel(uid int64, transactionCreateReq *models.TransactionCreateRequest) *models.Transaction { var transactionDbType models.TransactionDbType diff --git a/pkg/models/transaction.go b/pkg/models/transaction.go index 360dcd7f..64dd80e0 100644 --- a/pkg/models/transaction.go +++ b/pkg/models/transaction.go @@ -155,7 +155,7 @@ type TransactionInfoResponse struct { // TransactionCountResponse represents transaction count response type TransactionCountResponse struct { - Count int64 `json:"count"` + Count int64 `json:"count"` } // TransactionInfoPageWrapperResponse represents a response of transaction which contains items and next id @@ -164,6 +164,12 @@ type TransactionInfoPageWrapperResponse struct { NextTimeSequenceId *int64 `json:"nextTimeSequenceId,string"` } +// TransactionInfoPageWrapperResponse2 represents a response of transaction which contains items and count +type TransactionInfoPageWrapperResponse2 struct { + Items TransactionInfoResponseSlice `json:"items"` + TotalCount int64 `json:"total_count"` +} + // IsEditable returns whether this transaction can be edited func (t *Transaction) IsEditable(currentUser *User, utcOffset int16, account *Account, relatedAccount *Account) bool { if currentUser == nil || !currentUser.CanEditTransactionByTransactionTime(t.TransactionTime, utcOffset) { diff --git a/pkg/services/transactions.go b/pkg/services/transactions.go index 5f031294..3b95733f 100644 --- a/pkg/services/transactions.go +++ b/pkg/services/transactions.go @@ -141,6 +141,26 @@ func (s *TransactionService) GetAllTransactionCount(uid int64) (int64, error) { return s.GetTransactionCount(uid, 0, 0, 0, nil, 0, "") } +// GetMonthTransactionCount returns total count of transactions in given year and month +func (s *TransactionService) GetMonthTransactionCount(uid int64, year int, month int, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string, utcOffset int16) (int64, error) { + if uid <= 0 { + return 0, errs.ErrUserIdInvalid + } + + startTime, err := utils.ParseFromLongDateTime(fmt.Sprintf("%d-%02d-01 00:00:00", year, month), utcOffset) + + if err != nil { + return 0, errs.ErrSystemError + } + + endTime := startTime.AddDate(0, 1, 0) + + minTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix()) + maxTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - 1 + + return s.GetTransactionCount(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountId, keyword) +} + // GetTransactionCount returns count of transactions func (s *TransactionService) GetTransactionCount(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountId int64, keyword string) (int64, error) { if uid <= 0 { @@ -151,26 +171,6 @@ func (s *TransactionService) GetTransactionCount(uid int64, maxTransactionTime i return s.UserDataDB(uid).Where(condition, conditionParams...).Count(&models.Transaction{}) } -// GetMonthTransactionCount returns total count of transactions in given year and month -func (s *TransactionService) GetMonthTransactionCount(uid int64, year int64, month int64, utcOffset int16) (int64, error) { - if uid <= 0 { - return 0, errs.ErrUserIdInvalid - } - - startTime, err := utils.ParseFromLongDateTime(fmt.Sprintf("%d-%d-01 00:00:00", year, month), utcOffset) - - if err != nil { - return 0, errs.ErrSystemError - } - - endTime := startTime.AddDate(0, 1, 0) - - startTransactionTime := utils.GetMinTransactionTimeFromUnixTime(startTime.Unix()) - endTransactionTime := utils.GetMinTransactionTimeFromUnixTime(endTime.Unix()) - - return s.UserDataDB(uid).Where("uid=? AND deleted=? AND transaction_time>=? AND transaction_time