support transaction tag filter type

This commit is contained in:
MaysWind
2024-12-08 00:43:05 +08:00
parent 5003f8b3a2
commit dd35a85316
13 changed files with 275 additions and 95 deletions
+4 -4
View File
@@ -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
View File
@@ -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
+40 -52
View File
@@ -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 {
+23
View File
@@ -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,
+20
View File
@@ -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
View File
@@ -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 = [];
+5
View File
@@ -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",
+5
View File
@@ -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",
+5
View File
@@ -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": "定时交易周期",
+1
View File
@@ -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
})
+19
View File
@@ -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;