mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-18 08:44:25 +08:00
support transaction tag filter type
This commit is contained in:
@@ -89,7 +89,7 @@ func (a *TransactionsApi) TransactionCountHandler(c *core.WebContext) (any, *err
|
||||
}
|
||||
}
|
||||
|
||||
totalCount, err := a.transactions.GetTransactionCount(c, uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionCountReq.AmountFilter, transactionCountReq.Keyword)
|
||||
totalCount, err := a.transactions.GetTransactionCount(c, uid, transactionCountReq.MaxTime, transactionCountReq.MinTime, transactionCountReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionCountReq.TagFilterType, transactionCountReq.AmountFilter, transactionCountReq.Keyword)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionCountHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||
@@ -160,7 +160,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
||||
var totalCount int64
|
||||
|
||||
if transactionListReq.WithCount {
|
||||
totalCount, err = a.transactions.GetTransactionCount(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||
totalCount, err = a.transactions.GetTransactionCount(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transaction count for user \"uid:%d\", because %s", uid, err.Error())
|
||||
@@ -168,7 +168,7 @@ func (a *TransactionsApi) TransactionListHandler(c *core.WebContext) (any, *errs
|
||||
}
|
||||
}
|
||||
|
||||
transactions, err := a.transactions.GetTransactionsByMaxTime(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, true, true)
|
||||
transactions, err := a.transactions.GetTransactionsByMaxTime(c, uid, transactionListReq.MaxTime, transactionListReq.MinTime, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword, transactionListReq.Page, transactionListReq.Count, true, true)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionListHandler] failed to get transactions earlier than \"%d\" for user \"uid:%d\", because %s", transactionListReq.MaxTime, uid, err.Error())
|
||||
@@ -260,7 +260,7 @@ func (a *TransactionsApi) TransactionMonthListHandler(c *core.WebContext) (any,
|
||||
}
|
||||
}
|
||||
|
||||
transactions, err := a.transactions.GetTransactionsInMonthByPage(c, uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||
transactions, err := a.transactions.GetTransactionsInMonthByPage(c, uid, transactionListReq.Year, transactionListReq.Month, transactionListReq.Type, allCategoryIds, allAccountIds, allTagIds, noTags, transactionListReq.TagFilterType, transactionListReq.AmountFilter, transactionListReq.Keyword)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf(c, "[transactions.TransactionMonthListHandler] failed to get transactions in month \"%d-%d\" for user \"uid:%d\", because %s", transactionListReq.Year, transactionListReq.Month, uid, err.Error())
|
||||
|
||||
+49
-35
@@ -66,6 +66,17 @@ func (s TransactionDbType) ToTransactionType() (TransactionType, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// TransactionTagFilterType represents transaction tag filter type
|
||||
type TransactionTagFilterType byte
|
||||
|
||||
// Transaction tag filter types
|
||||
const (
|
||||
TRANSACTION_TAG_FILTER_HAS_ANY TransactionTagFilterType = 0
|
||||
TRANSACTION_TAG_FILTER_HAS_ALL TransactionTagFilterType = 1
|
||||
TRANSACTION_TAG_FILTER_NOT_HAS_ANY TransactionTagFilterType = 2
|
||||
TRANSACTION_TAG_FILTER_NOT_HAS_ALL TransactionTagFilterType = 3
|
||||
)
|
||||
|
||||
// Transaction represents transaction data stored in database
|
||||
type Transaction struct {
|
||||
TransactionId int64 `xorm:"PK"`
|
||||
@@ -140,49 +151,52 @@ type TransactionImportRequest struct {
|
||||
|
||||
// TransactionCountRequest represents transaction count request
|
||||
type TransactionCountRequest struct {
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||
MinTime int64 `form:"min_time" binding:"min=0"`
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||
MinTime int64 `form:"min_time" binding:"min=0"`
|
||||
}
|
||||
|
||||
// TransactionListByMaxTimeRequest represents all parameters of transaction listing by max time request
|
||||
type TransactionListByMaxTimeRequest struct {
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||
MinTime int64 `form:"min_time" binding:"min=0"`
|
||||
Page int32 `form:"page" binding:"min=0"`
|
||||
Count int32 `form:"count" binding:"required,min=1,max=50"`
|
||||
WithCount bool `form:"with_count"`
|
||||
WithPictures bool `form:"with_pictures"`
|
||||
TrimAccount bool `form:"trim_account"`
|
||||
TrimCategory bool `form:"trim_category"`
|
||||
TrimTag bool `form:"trim_tag"`
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
MaxTime int64 `form:"max_time" binding:"min=0"`
|
||||
MinTime int64 `form:"min_time" binding:"min=0"`
|
||||
Page int32 `form:"page" binding:"min=0"`
|
||||
Count int32 `form:"count" binding:"required,min=1,max=50"`
|
||||
WithCount bool `form:"with_count"`
|
||||
WithPictures bool `form:"with_pictures"`
|
||||
TrimAccount bool `form:"trim_account"`
|
||||
TrimCategory bool `form:"trim_category"`
|
||||
TrimTag bool `form:"trim_tag"`
|
||||
}
|
||||
|
||||
// TransactionListInMonthByPageRequest represents all parameters of transaction listing by month request
|
||||
type TransactionListInMonthByPageRequest struct {
|
||||
Year int32 `form:"year" binding:"required,min=1"`
|
||||
Month int32 `form:"month" binding:"required,min=1"`
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
WithPictures bool `form:"with_pictures"`
|
||||
TrimAccount bool `form:"trim_account"`
|
||||
TrimCategory bool `form:"trim_category"`
|
||||
TrimTag bool `form:"trim_tag"`
|
||||
Year int32 `form:"year" binding:"required,min=1"`
|
||||
Month int32 `form:"month" binding:"required,min=1"`
|
||||
Type TransactionDbType `form:"type" binding:"min=0,max=4"`
|
||||
CategoryIds string `form:"category_ids"`
|
||||
AccountIds string `form:"account_ids"`
|
||||
TagIds string `form:"tag_ids"`
|
||||
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
||||
AmountFilter string `form:"amount_filter" binding:"validAmountFilter"`
|
||||
Keyword string `form:"keyword"`
|
||||
WithPictures bool `form:"with_pictures"`
|
||||
TrimAccount bool `form:"trim_account"`
|
||||
TrimCategory bool `form:"trim_category"`
|
||||
TrimTag bool `form:"trim_tag"`
|
||||
}
|
||||
|
||||
// TransactionStatisticRequest represents all parameters of transaction statistic request
|
||||
|
||||
@@ -75,11 +75,11 @@ func (s *TransactionService) GetAllTransactions(c core.Context, uid int64, pageC
|
||||
|
||||
// GetAllTransactionsByMaxTime returns all transactions before given time
|
||||
func (s *TransactionService) GetAllTransactionsByMaxTime(c core.Context, uid int64, maxTransactionTime int64, count int32, noDuplicated bool) ([]*models.Transaction, error) {
|
||||
return s.GetTransactionsByMaxTime(c, uid, maxTransactionTime, 0, 0, nil, nil, nil, false, "", "", 1, count, false, noDuplicated)
|
||||
return s.GetTransactionsByMaxTime(c, uid, maxTransactionTime, 0, 0, nil, nil, nil, false, models.TRANSACTION_TAG_FILTER_HAS_ANY, "", "", 1, count, false, noDuplicated)
|
||||
}
|
||||
|
||||
// GetTransactionsByMaxTime returns transactions before given time
|
||||
func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, 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.TransactionDbType, 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 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -103,14 +103,9 @@ func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64,
|
||||
actualCount++
|
||||
}
|
||||
|
||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, noDuplicated)
|
||||
condition, conditionParams := s.buildTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, noDuplicated)
|
||||
sess := s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...)
|
||||
|
||||
if len(tagIds) > 0 {
|
||||
sess.In("transaction_id", s.getTransactionQueryByTagIdsCondition(uid, maxTransactionTime, minTransactionTime, tagIds))
|
||||
} else if noTags {
|
||||
sess.NotIn("transaction_id", s.getTransactionQueryByAllTagIdsCondition(uid, maxTransactionTime, minTransactionTime))
|
||||
}
|
||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||
|
||||
err = sess.Limit(int(actualCount), int(count*(page-1))).OrderBy("transaction_time desc").Find(&transactions)
|
||||
|
||||
@@ -118,7 +113,7 @@ func (s *TransactionService) GetTransactionsByMaxTime(c core.Context, uid int64,
|
||||
}
|
||||
|
||||
// GetTransactionsInMonthByPage returns all transactions in given year and month
|
||||
func (s *TransactionService) GetTransactionsInMonthByPage(c core.Context, uid int64, year int32, month int32, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, amountFilter string, keyword string) ([]*models.Transaction, error) {
|
||||
func (s *TransactionService) GetTransactionsInMonthByPage(c core.Context, uid int64, year int32, month int32, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, amountFilter string, keyword string) ([]*models.Transaction, error) {
|
||||
if uid <= 0 {
|
||||
return nil, errs.ErrUserIdInvalid
|
||||
}
|
||||
@@ -131,14 +126,9 @@ func (s *TransactionService) GetTransactionsInMonthByPage(c core.Context, uid in
|
||||
|
||||
var transactions []*models.Transaction
|
||||
|
||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, true)
|
||||
condition, conditionParams := s.buildTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, true)
|
||||
sess := s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...)
|
||||
|
||||
if len(tagIds) > 0 {
|
||||
sess.In("transaction_id", s.getTransactionQueryByTagIdsCondition(uid, maxTransactionTime, minTransactionTime, tagIds))
|
||||
} else if noTags {
|
||||
sess.NotIn("transaction_id", s.getTransactionQueryByAllTagIdsCondition(uid, maxTransactionTime, minTransactionTime))
|
||||
}
|
||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||
|
||||
err = sess.OrderBy("transaction_time desc").Find(&transactions)
|
||||
|
||||
@@ -181,23 +171,18 @@ func (s *TransactionService) GetTransactionByTransactionId(c core.Context, uid i
|
||||
|
||||
// GetAllTransactionCount returns total count of transactions
|
||||
func (s *TransactionService) GetAllTransactionCount(c core.Context, uid int64) (int64, error) {
|
||||
return s.GetTransactionCount(c, uid, 0, 0, 0, nil, nil, nil, false, "", "")
|
||||
return s.GetTransactionCount(c, uid, 0, 0, 0, nil, nil, nil, false, models.TRANSACTION_TAG_FILTER_HAS_ANY, "", "")
|
||||
}
|
||||
|
||||
// GetTransactionCount returns count of transactions
|
||||
func (s *TransactionService) GetTransactionCount(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, amountFilter string, keyword string) (int64, error) {
|
||||
func (s *TransactionService) GetTransactionCount(c core.Context, uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, amountFilter string, keyword string) (int64, error) {
|
||||
if uid <= 0 {
|
||||
return 0, errs.ErrUserIdInvalid
|
||||
}
|
||||
|
||||
condition, conditionParams := s.getTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, true)
|
||||
condition, conditionParams := s.buildTransactionQueryCondition(uid, maxTransactionTime, minTransactionTime, transactionType, categoryIds, accountIds, tagIds, amountFilter, keyword, true)
|
||||
sess := s.UserDataDB(uid).NewSession(c).Where(condition, conditionParams...)
|
||||
|
||||
if len(tagIds) > 0 {
|
||||
sess.In("transaction_id", s.getTransactionQueryByTagIdsCondition(uid, maxTransactionTime, minTransactionTime, tagIds))
|
||||
} else if noTags {
|
||||
sess.NotIn("transaction_id", s.getTransactionQueryByAllTagIdsCondition(uid, maxTransactionTime, minTransactionTime))
|
||||
}
|
||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||
|
||||
return sess.Count(&models.Transaction{})
|
||||
}
|
||||
@@ -1753,7 +1738,7 @@ func (s *TransactionService) doCreateTransaction(sess *xorm.Session, transaction
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, amountFilter string, keyword string, noDuplicated bool) (string, []any) {
|
||||
func (s *TransactionService) buildTransactionQueryCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, transactionType models.TransactionDbType, categoryIds []int64, accountIds []int64, tagIds []int64, amountFilter string, keyword string, noDuplicated bool) (string, []any) {
|
||||
condition := "uid=? AND deleted=?"
|
||||
conditionParams := make([]any, 0, 16)
|
||||
conditionParams = append(conditionParams, uid)
|
||||
@@ -1909,38 +1894,41 @@ func (s *TransactionService) getTransactionQueryCondition(uid int64, maxTransact
|
||||
return condition, conditionParams
|
||||
}
|
||||
|
||||
func (s *TransactionService) getTransactionQueryByTagIdsCondition(uid int64, maxTransactionTime int64, minTransactionTime int64, tagIds []int64) *builder.Builder {
|
||||
if len(tagIds) > 0 {
|
||||
condition := builder.And(builder.Eq{"uid": uid}, builder.Eq{"deleted": false})
|
||||
|
||||
if maxTransactionTime > 0 {
|
||||
condition = condition.And(builder.Lte{"transaction_time": maxTransactionTime})
|
||||
}
|
||||
|
||||
if minTransactionTime > 0 {
|
||||
condition = condition.And(builder.Gte{"transaction_time": minTransactionTime})
|
||||
}
|
||||
|
||||
condition = condition.And(builder.In("tag_id", tagIds))
|
||||
|
||||
return builder.Select("transaction_id").From("transaction_tag_index").Where(condition)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TransactionService) getTransactionQueryByAllTagIdsCondition(uid int64, maxTransactionTime int64, minTransactionTime int64) *builder.Builder {
|
||||
condition := builder.And(builder.Eq{"uid": uid}, builder.Eq{"deleted": false})
|
||||
func (s *TransactionService) appendFilterTagIdsConditionToQuery(sess *xorm.Session, uid int64, maxTransactionTime int64, minTransactionTime int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType) *xorm.Session {
|
||||
subQueryCondition := builder.And(builder.Eq{"uid": uid}, builder.Eq{"deleted": false})
|
||||
|
||||
if maxTransactionTime > 0 {
|
||||
condition = condition.And(builder.Lte{"transaction_time": maxTransactionTime})
|
||||
subQueryCondition = subQueryCondition.And(builder.Lte{"transaction_time": maxTransactionTime})
|
||||
}
|
||||
|
||||
if minTransactionTime > 0 {
|
||||
condition = condition.And(builder.Gte{"transaction_time": minTransactionTime})
|
||||
subQueryCondition = subQueryCondition.And(builder.Gte{"transaction_time": minTransactionTime})
|
||||
}
|
||||
|
||||
return builder.Select("transaction_id").From("transaction_tag_index").Where(condition)
|
||||
if noTags {
|
||||
subQuery := builder.Select("transaction_id").From("transaction_tag_index").Where(subQueryCondition)
|
||||
sess.NotIn("transaction_id", subQuery)
|
||||
return sess
|
||||
}
|
||||
|
||||
if len(tagIds) < 1 {
|
||||
return sess
|
||||
}
|
||||
|
||||
subQueryCondition = subQueryCondition.And(builder.In("tag_id", tagIds))
|
||||
subQuery := builder.Select("transaction_id").From("transaction_tag_index").Where(subQueryCondition)
|
||||
|
||||
if tagFilterType == models.TRANSACTION_TAG_FILTER_HAS_ALL || tagFilterType == models.TRANSACTION_TAG_FILTER_NOT_HAS_ALL {
|
||||
subQuery = subQuery.GroupBy("transaction_id").Having(fmt.Sprintf("COUNT(DISTINCT tag_id) >= %d", len(tagIds)))
|
||||
}
|
||||
|
||||
if tagFilterType == models.TRANSACTION_TAG_FILTER_HAS_ANY || tagFilterType == models.TRANSACTION_TAG_FILTER_HAS_ALL {
|
||||
sess.In("transaction_id", subQuery)
|
||||
} else if tagFilterType == models.TRANSACTION_TAG_FILTER_NOT_HAS_ANY || tagFilterType == models.TRANSACTION_TAG_FILTER_NOT_HAS_ALL {
|
||||
sess.NotIn("transaction_id", subQuery)
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
func (s *TransactionService) isAccountIdValid(transaction *models.Transaction) error {
|
||||
|
||||
@@ -36,6 +36,27 @@ const allTransactionEditScopeTypes = {
|
||||
}
|
||||
};
|
||||
|
||||
const allTransactionTagFilterTypes = {
|
||||
HasAny: {
|
||||
type: 0,
|
||||
name: 'With Any Selected Tags'
|
||||
},
|
||||
HasAll: {
|
||||
type: 1,
|
||||
name: 'With All Selected Tags'
|
||||
},
|
||||
NotHasAny: {
|
||||
type: 2,
|
||||
name: 'Without Any Selected Tags'
|
||||
},
|
||||
NotHasAll: {
|
||||
type: 3,
|
||||
name: 'Without All Selected Tags'
|
||||
}
|
||||
};
|
||||
|
||||
const defaultTransactionTagFilterType = allTransactionTagFilterTypes.HasAny;
|
||||
|
||||
const minAmountNumber = -99999999999; // -999999999.99
|
||||
const maxAmountNumber = 99999999999; // 999999999.99
|
||||
const maxPictureCount = 10;
|
||||
@@ -43,6 +64,8 @@ const maxPictureCount = 10;
|
||||
export default {
|
||||
allTransactionTypes: allTransactionTypes,
|
||||
allTransactionEditScopeTypes: allTransactionEditScopeTypes,
|
||||
allTransactionTagFilterTypes: allTransactionTagFilterTypes,
|
||||
defaultTransactionTagFilterType: defaultTransactionTagFilterType,
|
||||
minAmountNumber: minAmountNumber,
|
||||
maxAmountNumber: maxAmountNumber,
|
||||
maxPictureCount: maxPictureCount,
|
||||
|
||||
@@ -1212,6 +1212,25 @@ function getAllTransactionEditScopeTypes(translateFn) {
|
||||
return allEditScopeTypes;
|
||||
}
|
||||
|
||||
function getAllTransactionTagFilterTypes(translateFn) {
|
||||
const allTagFilterTypes = [];
|
||||
|
||||
for (const typeName in transactionConstants.allTransactionTagFilterTypes) {
|
||||
if (!Object.prototype.hasOwnProperty.call(transactionConstants.allTransactionTagFilterTypes, typeName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tagFilterType = transactionConstants.allTransactionTagFilterTypes[typeName];
|
||||
|
||||
allTagFilterTypes.push({
|
||||
type: tagFilterType.type,
|
||||
displayName: translateFn(tagFilterType.name)
|
||||
});
|
||||
}
|
||||
|
||||
return allTagFilterTypes;
|
||||
}
|
||||
|
||||
function getAllTransactionScheduledFrequencyTypes(translateFn) {
|
||||
const allScheduledFrequencyTypes = [];
|
||||
|
||||
@@ -1756,6 +1775,7 @@ export function i18nFunctions(i18nGlobal) {
|
||||
getAllStatisticsSortingTypes: () => getAllStatisticsSortingTypes(i18nGlobal.t),
|
||||
getAllStatisticsDateAggregationTypes: () => getAllStatisticsDateAggregationTypes(i18nGlobal.t),
|
||||
getAllTransactionEditScopeTypes: () => getAllTransactionEditScopeTypes(i18nGlobal.t),
|
||||
getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t),
|
||||
getAllTransactionScheduledFrequencyTypes: () => getAllTransactionScheduledFrequencyTypes(i18nGlobal.t),
|
||||
getAllTransactionDefaultCategories: (categoryType, locale) => getAllTransactionDefaultCategories(categoryType, locale, i18nGlobal.t),
|
||||
getAllDisplayExchangeRates: (settingsStore, exchangeRatesData) => getAllDisplayExchangeRates(settingsStore, exchangeRatesData, i18nGlobal.t),
|
||||
|
||||
+4
-4
@@ -299,15 +299,15 @@ export default {
|
||||
id
|
||||
});
|
||||
},
|
||||
getTransactions: ({ maxTime, minTime, count, page, withCount, type, categoryIds, accountIds, tagIds, amountFilter, keyword }) => {
|
||||
getTransactions: ({ maxTime, minTime, count, page, withCount, type, categoryIds, accountIds, tagIds, tagFilterType, amountFilter, keyword }) => {
|
||||
amountFilter = encodeURIComponent(amountFilter);
|
||||
keyword = encodeURIComponent(keyword);
|
||||
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&amount_filter=${amountFilter}&keyword=${keyword}&count=${count}&page=${page}&with_count=${withCount}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||
return axios.get(`v1/transactions/list.json?max_time=${maxTime}&min_time=${minTime}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&tag_filter_type=${tagFilterType}&amount_filter=${amountFilter}&keyword=${keyword}&count=${count}&page=${page}&with_count=${withCount}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||
},
|
||||
getAllTransactionsByMonth: ({ year, month, type, categoryIds, accountIds, tagIds, amountFilter, keyword }) => {
|
||||
getAllTransactionsByMonth: ({ year, month, type, categoryIds, accountIds, tagIds, tagFilterType, amountFilter, keyword }) => {
|
||||
amountFilter = encodeURIComponent(amountFilter);
|
||||
keyword = encodeURIComponent(keyword);
|
||||
return axios.get(`v1/transactions/list/by_month.json?year=${year}&month=${month}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||
return axios.get(`v1/transactions/list/by_month.json?year=${year}&month=${month}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&tag_filter_type=${tagFilterType}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
|
||||
},
|
||||
getTransactionStatistics: ({ startTime, endTime, useTransactionTimezone }) => {
|
||||
const queryParams = [];
|
||||
|
||||
@@ -1177,6 +1177,7 @@
|
||||
"balanceTime": "Balance Time",
|
||||
"startTime": "Start Time",
|
||||
"endTime": "End Time",
|
||||
"tagFilterType": "Tag Filter Type",
|
||||
"amountFilter": "Amount Filter",
|
||||
"sourceAccountId": "Source Account ID",
|
||||
"destinationAccountId": "Destination Account ID",
|
||||
@@ -1515,6 +1516,10 @@
|
||||
"Source Account": "Source Account",
|
||||
"Destination Account": "Destination Account",
|
||||
"Without Tags": "Without Tags",
|
||||
"With Any Selected Tags": "With Any Selected Tags",
|
||||
"With All Selected Tags": "With All Selected Tags",
|
||||
"Without Any Selected Tags": "Without Any Selected Tags",
|
||||
"Without All Selected Tags": "Without All Selected Tags",
|
||||
"Multiple Tags": "Multiple Tags",
|
||||
"Transaction Time": "Transaction Time",
|
||||
"Scheduled Transaction Frequency": "Scheduled Transaction Frequency",
|
||||
|
||||
@@ -1177,6 +1177,7 @@
|
||||
"balanceTime": "Thời gian số dư",
|
||||
"startTime": "Thời gian bắt đầu",
|
||||
"endTime": "Thời gian kết thúc",
|
||||
"tagFilterType": "Tag Filter Type",
|
||||
"amountFilter": "Bộ lọc số tiền",
|
||||
"sourceAccountId": "ID tài khoản nguồn",
|
||||
"destinationAccountId": "ID tài khoản đích",
|
||||
@@ -1515,6 +1516,10 @@
|
||||
"Source Account": "Tài khoản nguồn",
|
||||
"Destination Account": "Tài khoản đích",
|
||||
"Without Tags": "Không có thẻ",
|
||||
"With Any Selected Tags": "With Any Selected Tags",
|
||||
"With All Selected Tags": "With All Selected Tags",
|
||||
"Without Any Selected Tags": "Without Any Selected Tags",
|
||||
"Without All Selected Tags": "Without All Selected Tags",
|
||||
"Multiple Tags": "Nhiều thẻ",
|
||||
"Transaction Time": "Thời gian giao dịch",
|
||||
"Scheduled Transaction Frequency": "Tần suất giao dịch theo lịch trình",
|
||||
|
||||
@@ -1177,6 +1177,7 @@
|
||||
"balanceTime": "余额时间",
|
||||
"startTime": "开始时间",
|
||||
"endTime": "结束时间",
|
||||
"tagFilterType": "标签过滤类型",
|
||||
"amountFilter": "金额过滤",
|
||||
"sourceAccountId": "来源账户ID",
|
||||
"destinationAccountId": "目标账户ID",
|
||||
@@ -1515,6 +1516,10 @@
|
||||
"Source Account": "来源账户",
|
||||
"Destination Account": "目标账户",
|
||||
"Without Tags": "没有标签",
|
||||
"With Any Selected Tags": "包含任意选中的标签",
|
||||
"With All Selected Tags": "包含全部选中的标签",
|
||||
"Without Any Selected Tags": "不包含任意选中的标签",
|
||||
"Without All Selected Tags": "不包含全部选中的标签",
|
||||
"Multiple Tags": "多个标签",
|
||||
"Transaction Time": "交易时间",
|
||||
"Scheduled Transaction Frequency": "定时交易周期",
|
||||
|
||||
@@ -104,6 +104,7 @@ const router = createRouter({
|
||||
initCategoryIds: route.query.categoryIds,
|
||||
initAccountIds: route.query.accountIds,
|
||||
initTagIds: route.query.tagIds,
|
||||
initTagFilterType: route.query.tagFilterType,
|
||||
initAmountFilter: route.query.amountFilter,
|
||||
initKeyword: route.query.keyword
|
||||
})
|
||||
|
||||
@@ -374,6 +374,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
categoryIds: '',
|
||||
accountIds: '',
|
||||
tagIds: '',
|
||||
tagFilterType: transactionConstants.defaultTransactionTagFilterType.type,
|
||||
amountFilter: '',
|
||||
keyword: ''
|
||||
},
|
||||
@@ -671,6 +672,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
this.transactionsFilter.categoryIds = '';
|
||||
this.transactionsFilter.accountIds = '';
|
||||
this.transactionsFilter.tagIds = '';
|
||||
this.transactionsFilter.tagFilterType = transactionConstants.defaultTransactionTagFilterType.type;
|
||||
this.transactionsFilter.amountFilter = '';
|
||||
this.transactionsFilter.keyword = '';
|
||||
this.transactions = [];
|
||||
@@ -725,6 +727,12 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
this.transactionsFilter.tagIds = '';
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.tagFilterType)) {
|
||||
this.transactionsFilter.tagFilterType = filter.tagFilterType;
|
||||
} else {
|
||||
this.transactionsFilter.tagFilterType = transactionConstants.defaultTransactionTagFilterType.type;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.amountFilter)) {
|
||||
this.transactionsFilter.amountFilter = filter.amountFilter;
|
||||
} else {
|
||||
@@ -775,6 +783,11 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (filter && isNumber(filter.tagFilterType) && this.transactionsFilter.tagFilterType !== filter.tagFilterType) {
|
||||
this.transactionsFilter.tagFilterType = filter.tagFilterType;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (filter && isString(filter.amountFilter) && this.transactionsFilter.amountFilter !== filter.amountFilter) {
|
||||
this.transactionsFilter.amountFilter = filter.amountFilter;
|
||||
changed = true;
|
||||
@@ -806,6 +819,10 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
querys.push('tagIds=' + this.transactionsFilter.tagIds);
|
||||
}
|
||||
|
||||
if (this.transactionsFilter.tagFilterType) {
|
||||
querys.push('tagFilterType=' + this.transactionsFilter.tagFilterType);
|
||||
}
|
||||
|
||||
querys.push('dateType=' + this.transactionsFilter.dateType);
|
||||
|
||||
if (this.transactionsFilter.dateType === datetimeConstants.allDateRanges.Custom.type) {
|
||||
@@ -846,6 +863,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
categoryIds: self.transactionsFilter.categoryIds,
|
||||
accountIds: self.transactionsFilter.accountIds,
|
||||
tagIds: self.transactionsFilter.tagIds,
|
||||
tagFilterType: self.transactionsFilter.tagFilterType,
|
||||
amountFilter: self.transactionsFilter.amountFilter,
|
||||
keyword: self.transactionsFilter.keyword
|
||||
}).then(response => {
|
||||
@@ -922,6 +940,7 @@ export const useTransactionsStore = defineStore('transactions', {
|
||||
categoryIds: self.transactionsFilter.categoryIds,
|
||||
accountIds: self.transactionsFilter.accountIds,
|
||||
tagIds: self.transactionsFilter.tagIds,
|
||||
tagFilterType: self.transactionsFilter.tagFilterType,
|
||||
amountFilter: self.transactionsFilter.amountFilter,
|
||||
keyword: self.transactionsFilter.keyword
|
||||
}).then(response => {
|
||||
|
||||
@@ -376,6 +376,25 @@
|
||||
</div>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider v-if="query.tagIds && query.tagIds !== 'none'" />
|
||||
|
||||
<template :key="filterType.type"
|
||||
v-for="filterType in allTransactionTagFilterTypes"
|
||||
v-if="query.tagIds && query.tagIds !== 'none'">
|
||||
<v-list-item class="text-sm" density="compact"
|
||||
:value="filterType.type"
|
||||
:append-icon="(query.tagFilterType === filterType.type ? icons.check : null)">
|
||||
<v-list-item-title class="cursor-pointer"
|
||||
@click="changeTagFilterType(filterType.type)">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon size="24" :icon="filterType.icon"/>
|
||||
<span class="text-sm ml-3">{{ filterType.displayName }}</span>
|
||||
</div>
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<template :key="transactionTag.id"
|
||||
v-for="transactionTag in allTransactionTags">
|
||||
<v-divider v-if="!transactionTag.hidden || query.tagIds === transactionTag.id" />
|
||||
@@ -588,6 +607,10 @@ import {
|
||||
mdiPencilBoxOutline,
|
||||
mdiArrowLeft,
|
||||
mdiArrowRight,
|
||||
mdiPlusBoxMultipleOutline,
|
||||
mdiCheckboxMultipleOutline,
|
||||
mdiMinusBoxMultipleOutline,
|
||||
mdiCloseBoxMultipleOutline,
|
||||
mdiPound,
|
||||
mdiTextBoxOutline,
|
||||
mdiDotsVertical
|
||||
@@ -609,6 +632,7 @@ export default {
|
||||
'initCategoryIds',
|
||||
'initAccountIds',
|
||||
'initTagIds',
|
||||
'initTagFilterType',
|
||||
'initAmountFilter',
|
||||
'initKeyword'
|
||||
],
|
||||
@@ -649,6 +673,10 @@ export default {
|
||||
modifyBalance: mdiPencilBoxOutline,
|
||||
arrowLeft: mdiArrowLeft,
|
||||
arrowRight: mdiArrowRight,
|
||||
withAnyTags: mdiPlusBoxMultipleOutline,
|
||||
withAllTags: mdiCheckboxMultipleOutline,
|
||||
withoutAnyTags: mdiMinusBoxMultipleOutline,
|
||||
withoutAllTags: mdiCloseBoxMultipleOutline,
|
||||
tag: mdiPound,
|
||||
templates: mdiTextBoxOutline,
|
||||
more: mdiDotsVertical
|
||||
@@ -911,6 +939,26 @@ export default {
|
||||
allTransactionTypes() {
|
||||
return transactionConstants.allTransactionTypes;
|
||||
},
|
||||
allTransactionTagFilterTypes() {
|
||||
const allTagFilterTypes = this.$locale.getAllTransactionTagFilterTypes();
|
||||
const allTagFilterTypesWithIcon = [];
|
||||
const tagFilterIconMap = {
|
||||
[transactionConstants.allTransactionTagFilterTypes.HasAny.type]: this.icons.withAnyTags,
|
||||
[transactionConstants.allTransactionTagFilterTypes.HasAll.type]: this.icons.withAllTags,
|
||||
[transactionConstants.allTransactionTagFilterTypes.NotHasAny.type]: this.icons.withoutAnyTags,
|
||||
[transactionConstants.allTransactionTagFilterTypes.NotHasAll.type]: this.icons.withoutAllTags
|
||||
};
|
||||
|
||||
for (let i = 0; i < allTagFilterTypes.length; i++) {
|
||||
allTagFilterTypesWithIcon.push({
|
||||
type: allTagFilterTypes[i].type,
|
||||
displayName: allTagFilterTypes[i].displayName,
|
||||
icon: tagFilterIconMap[allTagFilterTypes[i].type]
|
||||
});
|
||||
}
|
||||
|
||||
return allTagFilterTypesWithIcon;
|
||||
},
|
||||
allAccounts() {
|
||||
return this.accountsStore.allAccountsMap;
|
||||
},
|
||||
@@ -985,6 +1033,7 @@ export default {
|
||||
categoryIds: this.initCategoryIds,
|
||||
accountIds: this.initAccountIds,
|
||||
tagIds: this.initTagIds,
|
||||
tagFilterType: this.initTagFilterType,
|
||||
amountFilter: this.initAmountFilter,
|
||||
keyword: this.initKeyword
|
||||
});
|
||||
@@ -1015,6 +1064,7 @@ export default {
|
||||
categoryIds: to.query.categoryIds,
|
||||
accountIds: to.query.accountIds,
|
||||
tagIds: to.query.tagIds,
|
||||
tagFilterType: to.query.tagFilterType,
|
||||
amountFilter: to.query.amountFilter,
|
||||
keyword: to.query.keyword
|
||||
});
|
||||
@@ -1042,6 +1092,7 @@ export default {
|
||||
categoryIds: query.categoryIds,
|
||||
accountIds: query.accountIds,
|
||||
tagIds: query.tagIds,
|
||||
tagFilterType: parseInt(query.tagFilterType) >= 0 ? parseInt(query.tagFilterType) : undefined,
|
||||
amountFilter: query.amountFilter || '',
|
||||
keyword: query.keyword || ''
|
||||
});
|
||||
@@ -1256,6 +1307,22 @@ export default {
|
||||
this.$router.push(this.getFilterLinkUrl());
|
||||
}
|
||||
},
|
||||
changeTagFilterType(filterType) {
|
||||
if (this.query.tagFilterType === filterType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed = this.transactionsStore.updateTransactionListFilter({
|
||||
tagFilterType: filterType
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
this.loading = true;
|
||||
this.currentPageTransactions = [];
|
||||
this.transactionsStore.clearTransactions();
|
||||
this.$router.push(this.getFilterLinkUrl());
|
||||
}
|
||||
},
|
||||
changeAmountFilter(filterType) {
|
||||
this.currentAmountFilterType = '';
|
||||
this.amountMenuState = false;
|
||||
|
||||
@@ -470,6 +470,21 @@
|
||||
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="query.tagIds && queryAllFilterTagIdsCount > 1"></f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :title="filterType.displayName"
|
||||
:key="filterType.type"
|
||||
v-for="filterType in allTransactionTagFilterTypes"
|
||||
v-if="query.tagIds && query.tagIds !== 'none'"
|
||||
@click="changeTagFilterType(filterType.type)"
|
||||
>
|
||||
<template #after>
|
||||
<f7-icon class="list-item-checked-icon"
|
||||
f7="checkmark_alt"
|
||||
v-if="query.tagFilterType === filterType.type">
|
||||
</f7-icon>
|
||||
</template>
|
||||
</f7-list-item>
|
||||
|
||||
<f7-list-item :title="transactionTag.name"
|
||||
:class="{ 'list-item-selected': query.tagIds === transactionTag.id, 'item-in-multiple-selection': queryAllFilterTagIdsCount > 1 && queryAllFilterTagIds[transactionTag.id] }"
|
||||
:key="transactionTag.id"
|
||||
@@ -662,6 +677,9 @@ export default {
|
||||
allTransactionTypes() {
|
||||
return transactionConstants.allTransactionTypes;
|
||||
},
|
||||
allTransactionTagFilterTypes() {
|
||||
return this.$locale.getAllTransactionTagFilterTypes();
|
||||
},
|
||||
allAccounts() {
|
||||
return this.accountsStore.allAccountsMap;
|
||||
},
|
||||
@@ -979,6 +997,21 @@ export default {
|
||||
this.reload(null);
|
||||
}
|
||||
},
|
||||
changeTagFilterType(filterType) {
|
||||
if (this.query.tagFilterType === filterType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed = this.transactionsStore.updateTransactionListFilter({
|
||||
tagFilterType: filterType
|
||||
});
|
||||
|
||||
this.showMorePopover = false;
|
||||
|
||||
if (changed) {
|
||||
this.reload(null);
|
||||
}
|
||||
},
|
||||
changeAmountFilter(filterType) {
|
||||
if (this.query.amountFilter === filterType) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user