mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-19 09:14:27 +08:00
filter transaction description keywords in statistics & analysis page
This commit is contained in:
@@ -316,7 +316,7 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, utcOffset, statisticReq.UseTransactionTimezone)
|
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, statisticReq.Keyword, utcOffset, statisticReq.UseTransactionTimezone)
|
||||||
|
|
||||||
if err != nil {
|
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())
|
log.Errorf(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
@@ -379,7 +379,7 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid := c.GetCurrentUid()
|
uid := c.GetCurrentUid()
|
||||||
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, statisticTrendsReq.Keyword, utcOffset, statisticTrendsReq.UseTransactionTimezone)
|
||||||
|
|
||||||
if err != nil {
|
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())
|
log.Errorf(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ type TransactionStatisticRequest struct {
|
|||||||
EndTime int64 `form:"end_time" binding:"min=0"`
|
EndTime int64 `form:"end_time" binding:"min=0"`
|
||||||
TagIds string `form:"tag_ids"`
|
TagIds string `form:"tag_ids"`
|
||||||
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
||||||
|
Keyword string `form:"keyword"`
|
||||||
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +237,7 @@ type TransactionStatisticTrendsRequest struct {
|
|||||||
YearMonthRangeRequest
|
YearMonthRangeRequest
|
||||||
TagIds string `form:"tag_ids"`
|
TagIds string `form:"tag_ids"`
|
||||||
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
|
||||||
|
Keyword string `form:"keyword"`
|
||||||
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
UseTransactionTimezone bool `form:"use_transaction_timezone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1421,7 +1421,7 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(c core.Context, ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range
|
// 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, utcOffset int16, useTransactionTimezone bool) ([]*models.Transaction, error) {
|
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) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -1469,6 +1469,11 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
|||||||
finalConditionParams = append(finalConditionParams, maxTransactionTime)
|
finalConditionParams = append(finalConditionParams, maxTransactionTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keyword != "" {
|
||||||
|
finalCondition = finalCondition + " AND comment LIKE ?"
|
||||||
|
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("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||||
|
|
||||||
@@ -1530,7 +1535,7 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountsAndCategoriesMonthlyIncomeAndExpense returns the every accounts monthly income and expense amount by specific date range
|
// 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, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
|
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) {
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
return nil, errs.ErrUserIdInvalid
|
return nil, errs.ErrUserIdInvalid
|
||||||
}
|
}
|
||||||
@@ -1583,6 +1588,11 @@ func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c c
|
|||||||
finalConditionParams = append(finalConditionParams, maxTransactionTime)
|
finalConditionParams = append(finalConditionParams, maxTransactionTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keyword != "" {
|
||||||
|
finalCondition = finalCondition + " AND comment LIKE ?"
|
||||||
|
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("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
|
||||||
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)
|
||||||
|
|
||||||
|
|||||||
@@ -432,6 +432,10 @@ export default {
|
|||||||
queryParams.push(`tag_filter_type=${req.tagFilterType}`);
|
queryParams.push(`tag_filter_type=${req.tagFilterType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.keyword) {
|
||||||
|
queryParams.push(`keyword=${encodeURIComponent(req.keyword)}`);
|
||||||
|
}
|
||||||
|
|
||||||
return axios.get<ApiResponse<TransactionStatisticResponse>>(`v1/transactions/statistics.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
return axios.get<ApiResponse<TransactionStatisticResponse>>(`v1/transactions/statistics.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
||||||
},
|
},
|
||||||
getTransactionStatisticsTrends: (req: TransactionStatisticTrendsRequest): ApiResponsePromise<TransactionStatisticTrendsResponseItem[]> => {
|
getTransactionStatisticsTrends: (req: TransactionStatisticTrendsRequest): ApiResponsePromise<TransactionStatisticTrendsResponseItem[]> => {
|
||||||
@@ -453,6 +457,10 @@ export default {
|
|||||||
queryParams.push(`tag_filter_type=${req.tagFilterType}`);
|
queryParams.push(`tag_filter_type=${req.tagFilterType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req.keyword) {
|
||||||
|
queryParams.push(`keyword=${encodeURIComponent(req.keyword)}`);
|
||||||
|
}
|
||||||
|
|
||||||
return axios.get<ApiResponse<TransactionStatisticTrendsResponseItem[]>>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
return axios.get<ApiResponse<TransactionStatisticTrendsResponseItem[]>>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
|
||||||
},
|
},
|
||||||
getTransactionAmounts: (params: TransactionAmountsRequestParams): ApiResponsePromise<TransactionAmountsResponse> => {
|
getTransactionAmounts: (params: TransactionAmountsRequestParams): ApiResponsePromise<TransactionAmountsResponse> => {
|
||||||
|
|||||||
@@ -249,6 +249,37 @@ export function useI18nUIComponents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showPrompt(message: string, currentValue?: string, confirmCallback?: (value: string, dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: (value: string, dialog: Dialog.Dialog, e: Event) => void): void {
|
||||||
|
f7ready((f7) => {
|
||||||
|
f7.dialog.create({
|
||||||
|
title: tt('global.app.title'),
|
||||||
|
text: tt(message),
|
||||||
|
content: `<div class="dialog-input-field input"><input type="text" class="dialog-input" value="${currentValue || ''}"></div>`,
|
||||||
|
animate: isEnableAnimate(),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: tt('Cancel'),
|
||||||
|
onClick: (dialog, event) => {
|
||||||
|
if (cancelCallback) {
|
||||||
|
const inputValue = dialog.$el.find('.dialog-input').val();
|
||||||
|
cancelCallback(inputValue, dialog, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: tt('OK'),
|
||||||
|
onClick: (dialog, event) => {
|
||||||
|
if (confirmCallback) {
|
||||||
|
const inputValue = dialog.$el.find('.dialog-input').val();
|
||||||
|
confirmCallback(inputValue, dialog, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function showToast(message: string, timeout?: number): void {
|
function showToast(message: string, timeout?: number): void {
|
||||||
f7ready((f7) => {
|
f7ready((f7) => {
|
||||||
f7.toast.create({
|
f7.toast.create({
|
||||||
@@ -262,6 +293,7 @@ export function useI18nUIComponents() {
|
|||||||
return {
|
return {
|
||||||
showAlert: showAlert,
|
showAlert: showAlert,
|
||||||
showConfirm: showConfirm,
|
showConfirm: showConfirm,
|
||||||
|
showPrompt: showPrompt,
|
||||||
showToast: showToast,
|
showToast: showToast,
|
||||||
routeBackOnError
|
routeBackOnError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Konten filtern",
|
"Filter Accounts": "Konten filtern",
|
||||||
"Filter Transaction Categories": "Transaktionskategorien filtern",
|
"Filter Transaction Categories": "Transaktionskategorien filtern",
|
||||||
"Filter Transaction Tags": "Transaktions-Tags filtern",
|
"Filter Transaction Tags": "Transaktions-Tags filtern",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Benutzereinstellungen",
|
"User Settings": "Benutzereinstellungen",
|
||||||
"User Profile": "Benutzerprofil",
|
"User Profile": "Benutzerprofil",
|
||||||
"Language": "Sprache",
|
"Language": "Sprache",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Filter Accounts",
|
"Filter Accounts": "Filter Accounts",
|
||||||
"Filter Transaction Categories": "Filter Transaction Categories",
|
"Filter Transaction Categories": "Filter Transaction Categories",
|
||||||
"Filter Transaction Tags": "Filter Transaction Tags",
|
"Filter Transaction Tags": "Filter Transaction Tags",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "User Settings",
|
"User Settings": "User Settings",
|
||||||
"User Profile": "User Profile",
|
"User Profile": "User Profile",
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Filtrar cuentas",
|
"Filter Accounts": "Filtrar cuentas",
|
||||||
"Filter Transaction Categories": "Filtrar categorías de transacciones",
|
"Filter Transaction Categories": "Filtrar categorías de transacciones",
|
||||||
"Filter Transaction Tags": "Filtrar etiquetas de transacciones",
|
"Filter Transaction Tags": "Filtrar etiquetas de transacciones",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Configuración de usuario",
|
"User Settings": "Configuración de usuario",
|
||||||
"User Profile": "Perfil de usuario",
|
"User Profile": "Perfil de usuario",
|
||||||
"Language": "Idioma",
|
"Language": "Idioma",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Filtra conti",
|
"Filter Accounts": "Filtra conti",
|
||||||
"Filter Transaction Categories": "Filtra categorie transazione",
|
"Filter Transaction Categories": "Filtra categorie transazione",
|
||||||
"Filter Transaction Tags": "Filtra tag transazione",
|
"Filter Transaction Tags": "Filtra tag transazione",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Impostazioni utente",
|
"User Settings": "Impostazioni utente",
|
||||||
"User Profile": "Profilo utente",
|
"User Profile": "Profilo utente",
|
||||||
"Language": "Lingua",
|
"Language": "Lingua",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "口座で絞り込み",
|
"Filter Accounts": "口座で絞り込み",
|
||||||
"Filter Transaction Categories": "取引カテゴリで絞り込み",
|
"Filter Transaction Categories": "取引カテゴリで絞り込み",
|
||||||
"Filter Transaction Tags": "取引タグで絞り込み",
|
"Filter Transaction Tags": "取引タグで絞り込み",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "ユーザー設定",
|
"User Settings": "ユーザー設定",
|
||||||
"User Profile": "ユーザープロフィール",
|
"User Profile": "ユーザープロフィール",
|
||||||
"Language": "言語",
|
"Language": "言語",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Filtrar Contas",
|
"Filter Accounts": "Filtrar Contas",
|
||||||
"Filter Transaction Categories": "Filtrar Categorias de Transações",
|
"Filter Transaction Categories": "Filtrar Categorias de Transações",
|
||||||
"Filter Transaction Tags": "Filtrar Tags de Transações",
|
"Filter Transaction Tags": "Filtrar Tags de Transações",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Configurações do Usuário",
|
"User Settings": "Configurações do Usuário",
|
||||||
"User Profile": "Perfil do Usuário",
|
"User Profile": "Perfil do Usuário",
|
||||||
"Language": "Idioma",
|
"Language": "Idioma",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Фильтровать счета",
|
"Filter Accounts": "Фильтровать счета",
|
||||||
"Filter Transaction Categories": "Фильтровать категории транзакций",
|
"Filter Transaction Categories": "Фильтровать категории транзакций",
|
||||||
"Filter Transaction Tags": "Фильтровать теги транзакций",
|
"Filter Transaction Tags": "Фильтровать теги транзакций",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Настройки пользователя",
|
"User Settings": "Настройки пользователя",
|
||||||
"User Profile": "Профиль пользователя",
|
"User Profile": "Профиль пользователя",
|
||||||
"Language": "Язык",
|
"Language": "Язык",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Фільтрувати рахунки",
|
"Filter Accounts": "Фільтрувати рахунки",
|
||||||
"Filter Transaction Categories": "Фільтрувати категорії транзакцій",
|
"Filter Transaction Categories": "Фільтрувати категорії транзакцій",
|
||||||
"Filter Transaction Tags": "Фільтрувати теги транзакцій",
|
"Filter Transaction Tags": "Фільтрувати теги транзакцій",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Налаштування користувача",
|
"User Settings": "Налаштування користувача",
|
||||||
"User Profile": "Профіль користувача",
|
"User Profile": "Профіль користувача",
|
||||||
"Language": "Мова",
|
"Language": "Мова",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "Lọc tài khoản",
|
"Filter Accounts": "Lọc tài khoản",
|
||||||
"Filter Transaction Categories": "Lọc danh mục giao dịch",
|
"Filter Transaction Categories": "Lọc danh mục giao dịch",
|
||||||
"Filter Transaction Tags": "Lọc thẻ giao dịch",
|
"Filter Transaction Tags": "Lọc thẻ giao dịch",
|
||||||
|
"Filter transaction description": "Filter transaction description",
|
||||||
"User Settings": "Cài đặt người dùng",
|
"User Settings": "Cài đặt người dùng",
|
||||||
"User Profile": "Hồ sơ người dùng",
|
"User Profile": "Hồ sơ người dùng",
|
||||||
"Language": "Ngôn ngữ",
|
"Language": "Ngôn ngữ",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "过滤账户",
|
"Filter Accounts": "过滤账户",
|
||||||
"Filter Transaction Categories": "过滤交易类型",
|
"Filter Transaction Categories": "过滤交易类型",
|
||||||
"Filter Transaction Tags": "过滤交易标签",
|
"Filter Transaction Tags": "过滤交易标签",
|
||||||
|
"Filter transaction description": "过滤交易描述",
|
||||||
"User Settings": "用户设置",
|
"User Settings": "用户设置",
|
||||||
"User Profile": "用户信息",
|
"User Profile": "用户信息",
|
||||||
"Language": "语言",
|
"Language": "语言",
|
||||||
|
|||||||
@@ -1851,6 +1851,7 @@
|
|||||||
"Filter Accounts": "篩選帳戶",
|
"Filter Accounts": "篩選帳戶",
|
||||||
"Filter Transaction Categories": "篩選交易類型",
|
"Filter Transaction Categories": "篩選交易類型",
|
||||||
"Filter Transaction Tags": "篩選交易標籤",
|
"Filter Transaction Tags": "篩選交易標籤",
|
||||||
|
"Filter transaction description": "篩選交易描述",
|
||||||
"User Settings": "使用者設定",
|
"User Settings": "使用者設定",
|
||||||
"User Profile": "使用者資料",
|
"User Profile": "使用者資料",
|
||||||
"Language": "語言",
|
"Language": "語言",
|
||||||
|
|||||||
@@ -548,6 +548,7 @@ export interface TransactionStatisticRequest {
|
|||||||
readonly endTime: number;
|
readonly endTime: number;
|
||||||
readonly tagIds: string;
|
readonly tagIds: string;
|
||||||
readonly tagFilterType: number;
|
readonly tagFilterType: number;
|
||||||
|
readonly keyword: string;
|
||||||
readonly useTransactionTimezone: boolean;
|
readonly useTransactionTimezone: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,6 +560,7 @@ export interface YearMonthRangeRequest {
|
|||||||
export interface TransactionStatisticTrendsRequest extends YearMonthRangeRequest {
|
export interface TransactionStatisticTrendsRequest extends YearMonthRangeRequest {
|
||||||
readonly tagIds: string;
|
readonly tagIds: string;
|
||||||
readonly tagFilterType: number;
|
readonly tagFilterType: number;
|
||||||
|
readonly keyword: string;
|
||||||
readonly useTransactionTimezone: boolean;
|
readonly useTransactionTimezone: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ const router = createRouter({
|
|||||||
initFilterCategoryIds: route.query['filterCategoryIds'],
|
initFilterCategoryIds: route.query['filterCategoryIds'],
|
||||||
initTagIds: route.query['tagIds'],
|
initTagIds: route.query['tagIds'],
|
||||||
initTagFilterType: route.query['tagFilterType'],
|
initTagFilterType: route.query['tagFilterType'],
|
||||||
|
initKeyword: route.query['keyword'],
|
||||||
initSortingType: route.query['sortingType'],
|
initSortingType: route.query['sortingType'],
|
||||||
initTrendDateAggregationType: route.query['trendDateAggregationType']
|
initTrendDateAggregationType: route.query['trendDateAggregationType']
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export interface TransactionStatisticsPartialFilter {
|
|||||||
filterCategoryIds?: Record<string, boolean>;
|
filterCategoryIds?: Record<string, boolean>;
|
||||||
tagIds?: string;
|
tagIds?: string;
|
||||||
tagFilterType?: number;
|
tagFilterType?: number;
|
||||||
|
keyword?: string;
|
||||||
sortingType?: number;
|
sortingType?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +142,7 @@ export interface TransactionStatisticsFilter extends TransactionStatisticsPartia
|
|||||||
filterCategoryIds: Record<string, boolean>;
|
filterCategoryIds: Record<string, boolean>;
|
||||||
tagIds: string;
|
tagIds: string;
|
||||||
tagFilterType: number;
|
tagFilterType: number;
|
||||||
|
keyword: string;
|
||||||
sortingType: number;
|
sortingType: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +167,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
filterCategoryIds: {},
|
filterCategoryIds: {},
|
||||||
tagIds: '',
|
tagIds: '',
|
||||||
tagFilterType: TransactionTagFilterType.Default.type,
|
tagFilterType: TransactionTagFilterType.Default.type,
|
||||||
|
keyword: '',
|
||||||
sortingType: ChartSortingType.Default.type
|
sortingType: ChartSortingType.Default.type
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -693,6 +696,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
transactionStatisticsFilter.value.filterCategoryIds = {};
|
transactionStatisticsFilter.value.filterCategoryIds = {};
|
||||||
transactionStatisticsFilter.value.tagIds = '';
|
transactionStatisticsFilter.value.tagIds = '';
|
||||||
transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type;
|
transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type;
|
||||||
|
transactionStatisticsFilter.value.keyword = '';
|
||||||
transactionCategoryStatisticsData.value = null;
|
transactionCategoryStatisticsData.value = null;
|
||||||
transactionCategoryTrendsData.value = [];
|
transactionCategoryTrendsData.value = [];
|
||||||
transactionStatisticsStateInvalid.value = true;
|
transactionStatisticsStateInvalid.value = true;
|
||||||
@@ -825,6 +829,12 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type;
|
transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter && isString(filter.keyword)) {
|
||||||
|
transactionStatisticsFilter.value.keyword = filter.keyword;
|
||||||
|
} else {
|
||||||
|
transactionStatisticsFilter.value.keyword = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (filter && isInteger(filter.sortingType)) {
|
if (filter && isInteger(filter.sortingType)) {
|
||||||
transactionStatisticsFilter.value.sortingType = filter.sortingType;
|
transactionStatisticsFilter.value.sortingType = filter.sortingType;
|
||||||
} else {
|
} else {
|
||||||
@@ -904,6 +914,11 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter && isString(filter.keyword) && transactionStatisticsFilter.value.keyword !== filter.keyword) {
|
||||||
|
transactionStatisticsFilter.value.keyword = filter.keyword;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter && isInteger(filter.sortingType) && transactionStatisticsFilter.value.sortingType !== filter.sortingType) {
|
if (filter && isInteger(filter.sortingType) && transactionStatisticsFilter.value.sortingType !== filter.sortingType) {
|
||||||
transactionStatisticsFilter.value.sortingType = filter.sortingType;
|
transactionStatisticsFilter.value.sortingType = filter.sortingType;
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -964,6 +979,10 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.keyword) {
|
||||||
|
querys.push('keyword=' + encodeURIComponent(transactionStatisticsFilter.value.keyword));
|
||||||
|
}
|
||||||
|
|
||||||
querys.push('sortingType=' + transactionStatisticsFilter.value.sortingType);
|
querys.push('sortingType=' + transactionStatisticsFilter.value.sortingType);
|
||||||
|
|
||||||
return querys.join('&');
|
return querys.join('&');
|
||||||
@@ -1020,6 +1039,10 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transactionStatisticsFilter.value.keyword) {
|
||||||
|
querys.push('keyword=' + encodeURIComponent(transactionStatisticsFilter.value.keyword));
|
||||||
|
}
|
||||||
|
|
||||||
if (analysisType === StatisticsAnalysisType.CategoricalAnalysis
|
if (analysisType === StatisticsAnalysisType.CategoricalAnalysis
|
||||||
&& transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalAssets.type
|
&& transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalAssets.type
|
||||||
&& transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalLiabilities.type) {
|
&& transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalLiabilities.type) {
|
||||||
@@ -1045,6 +1068,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
endTime: transactionStatisticsFilter.value.categoricalChartEndTime,
|
endTime: transactionStatisticsFilter.value.categoricalChartEndTime,
|
||||||
tagIds: transactionStatisticsFilter.value.tagIds,
|
tagIds: transactionStatisticsFilter.value.tagIds,
|
||||||
tagFilterType: transactionStatisticsFilter.value.tagFilterType,
|
tagFilterType: transactionStatisticsFilter.value.tagFilterType,
|
||||||
|
keyword: transactionStatisticsFilter.value.keyword,
|
||||||
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type
|
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
@@ -1087,6 +1111,7 @@ export const useStatisticsStore = defineStore('statistics', () => {
|
|||||||
endYearMonth: transactionStatisticsFilter.value.trendChartEndYearMonth,
|
endYearMonth: transactionStatisticsFilter.value.trendChartEndYearMonth,
|
||||||
tagIds: transactionStatisticsFilter.value.tagIds,
|
tagIds: transactionStatisticsFilter.value.tagIds,
|
||||||
tagFilterType: transactionStatisticsFilter.value.tagFilterType,
|
tagFilterType: transactionStatisticsFilter.value.tagFilterType,
|
||||||
|
keyword: transactionStatisticsFilter.value.keyword,
|
||||||
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type
|
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
|
|||||||
@@ -113,6 +113,16 @@
|
|||||||
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-spacer/>
|
<v-spacer/>
|
||||||
|
<div class="transaction-keyword-filter ml-2">
|
||||||
|
<v-text-field density="compact" :disabled="loading"
|
||||||
|
:prepend-inner-icon="mdiMagnify"
|
||||||
|
:append-inner-icon="filterKeyword !== query.keyword ? mdiCheck : undefined"
|
||||||
|
:placeholder="tt('Filter transaction description')"
|
||||||
|
v-model="filterKeyword"
|
||||||
|
@click:append-inner="setKeywordFilter(filterKeyword)"
|
||||||
|
@keyup.enter="setKeywordFilter(filterKeyword)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
:disabled="loading" :icon="true">
|
:disabled="loading" :icon="true">
|
||||||
<v-icon :icon="mdiDotsVertical" />
|
<v-icon :icon="mdiDotsVertical" />
|
||||||
@@ -383,6 +393,7 @@ import {
|
|||||||
mdiCalendarRangeOutline,
|
mdiCalendarRangeOutline,
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
mdiSquareRounded,
|
mdiSquareRounded,
|
||||||
|
mdiMagnify,
|
||||||
mdiMenu,
|
mdiMenu,
|
||||||
mdiFilterOutline,
|
mdiFilterOutline,
|
||||||
mdiFilterCogOutline,
|
mdiFilterCogOutline,
|
||||||
@@ -405,6 +416,7 @@ interface TransactionStatisticsProps {
|
|||||||
initFilterCategoryIds?: string,
|
initFilterCategoryIds?: string,
|
||||||
initTagIds?: string,
|
initTagIds?: string,
|
||||||
initTagFilterType?: string,
|
initTagFilterType?: string,
|
||||||
|
initKeyword?: string;
|
||||||
initSortingType?: string,
|
initSortingType?: string,
|
||||||
initTrendDateAggregationType?: string
|
initTrendDateAggregationType?: string
|
||||||
}
|
}
|
||||||
@@ -454,6 +466,7 @@ const exportDialog = useTemplateRef<ExportDialogType>('exportDialog');
|
|||||||
|
|
||||||
const activeTab = ref<string>('statisticsPage');
|
const activeTab = ref<string>('statisticsPage');
|
||||||
const initing = ref<boolean>(true);
|
const initing = ref<boolean>(true);
|
||||||
|
const filterKeyword = ref<string>('');
|
||||||
const alwaysShowNav = ref<boolean>(display.mdAndUp.value);
|
const alwaysShowNav = ref<boolean>(display.mdAndUp.value);
|
||||||
const showNav = ref<boolean>(display.mdAndUp.value);
|
const showNav = ref<boolean>(display.mdAndUp.value);
|
||||||
const showCustomDateRangeDialog = ref<boolean>(false);
|
const showCustomDateRangeDialog = ref<boolean>(false);
|
||||||
@@ -551,9 +564,12 @@ function init(initProps: TransactionStatisticsProps): void {
|
|||||||
filterCategoryIds: initProps.initFilterCategoryIds ? arrayItemToObjectField(initProps.initFilterCategoryIds.split(','), true) : {},
|
filterCategoryIds: initProps.initFilterCategoryIds ? arrayItemToObjectField(initProps.initFilterCategoryIds.split(','), true) : {},
|
||||||
tagIds: initProps.initTagIds,
|
tagIds: initProps.initTagIds,
|
||||||
tagFilterType: initProps.initTagFilterType && parseInt(initProps.initTagFilterType) >= 0 ? parseInt(initProps.initTagFilterType) : undefined,
|
tagFilterType: initProps.initTagFilterType && parseInt(initProps.initTagFilterType) >= 0 ? parseInt(initProps.initTagFilterType) : undefined,
|
||||||
|
keyword: initProps.initKeyword,
|
||||||
sortingType: initProps.initSortingType ? parseInt(initProps.initSortingType) : undefined
|
sortingType: initProps.initSortingType ? parseInt(initProps.initSortingType) : undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
filterKeyword.value = filter.keyword || '';
|
||||||
|
|
||||||
if (initProps.initAnalysisType === StatisticsAnalysisType.CategoricalAnalysis.toString()) {
|
if (initProps.initAnalysisType === StatisticsAnalysisType.CategoricalAnalysis.toString()) {
|
||||||
filter.categoricalChartType = initProps.initChartType ? parseInt(initProps.initChartType) : undefined;
|
filter.categoricalChartType = initProps.initChartType ? parseInt(initProps.initChartType) : undefined;
|
||||||
filter.categoricalChartDateType = initProps.initChartDateType ? parseInt(initProps.initChartDateType) : undefined;
|
filter.categoricalChartDateType = initProps.initChartDateType ? parseInt(initProps.initChartDateType) : undefined;
|
||||||
@@ -904,6 +920,30 @@ function setTagFilter(changed: boolean): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setKeywordFilter(keyword: string): void {
|
||||||
|
if (query.value.keyword === keyword) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
keyword: keyword
|
||||||
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
keyword: keyword
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
loading.value = true;
|
||||||
|
statisticsStore.updateTransactionStatisticsInvalidState(true);
|
||||||
|
router.push(getFilterLinkUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function exportResults(): void {
|
function exportResults(): void {
|
||||||
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis && categoricalAnalysisData.value && categoricalAnalysisData.value.items) {
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis && categoricalAnalysisData.value && categoricalAnalysisData.value.items) {
|
||||||
exportDialog.value?.open({
|
exportDialog.value?.open({
|
||||||
@@ -954,6 +994,7 @@ onBeforeRouteUpdate((to) => {
|
|||||||
initFilterCategoryIds: (to.query['filterCategoryIds'] as string | null) || undefined,
|
initFilterCategoryIds: (to.query['filterCategoryIds'] as string | null) || undefined,
|
||||||
initTagIds: (to.query['tagIds'] as string | null) || undefined,
|
initTagIds: (to.query['tagIds'] as string | null) || undefined,
|
||||||
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
|
initTagFilterType: (to.query['tagFilterType'] as string | null) || undefined,
|
||||||
|
initKeyword: (to.query['keyword'] as string | null) || undefined,
|
||||||
initSortingType: (to.query['sortingType'] as string | null) || undefined,
|
initSortingType: (to.query['sortingType'] as string | null) || undefined,
|
||||||
initTrendDateAggregationType: (to.query['trendDateAggregationType'] as string | null) || undefined
|
initTrendDateAggregationType: (to.query['trendDateAggregationType'] as string | null) || undefined
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -313,6 +313,10 @@
|
|||||||
<f7-actions-button @click="filterCategories">{{ tt('Filter Transaction Categories') }}</f7-actions-button>
|
<f7-actions-button @click="filterCategories">{{ tt('Filter Transaction Categories') }}</f7-actions-button>
|
||||||
<f7-actions-button @click="filterTags">{{ tt('Filter Transaction Tags') }}</f7-actions-button>
|
<f7-actions-button @click="filterTags">{{ tt('Filter Transaction Tags') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
|
<f7-actions-group>
|
||||||
|
<f7-actions-label v-if="query.keyword">{{ query.keyword }}</f7-actions-label>
|
||||||
|
<f7-actions-button @click="filterDescription">{{ tt('Filter transaction description') }}</f7-actions-button>
|
||||||
|
</f7-actions-group>
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button @click="settings">{{ tt('Settings') }}</f7-actions-button>
|
<f7-actions-button @click="settings">{{ tt('Settings') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
@@ -360,7 +364,7 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { tt, getAllCategoricalChartTypes, formatPercent } = useI18n();
|
const { tt, getAllCategoricalChartTypes, formatPercent } = useI18n();
|
||||||
const { showToast, routeBackOnError } = useI18nUIComponents();
|
const { showPrompt, showToast, routeBackOnError } = useI18nUIComponents();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
loading,
|
loading,
|
||||||
@@ -697,6 +701,30 @@ function filterTags(): void {
|
|||||||
props.f7router.navigate('/settings/filter/tag?type=statisticsCurrent');
|
props.f7router.navigate('/settings/filter/tag?type=statisticsCurrent');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterDescription(): void {
|
||||||
|
showPrompt('Filter transaction description', query.value.keyword, value => {
|
||||||
|
if (query.value.keyword === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
if (analysisType.value === StatisticsAnalysisType.CategoricalAnalysis) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
keyword: value
|
||||||
|
});
|
||||||
|
} else if (analysisType.value === StatisticsAnalysisType.TrendAnalysis) {
|
||||||
|
changed = statisticsStore.updateTransactionStatisticsFilter({
|
||||||
|
keyword: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function settings(): void {
|
function settings(): void {
|
||||||
props.f7router.navigate('/statistic/settings');
|
props.f7router.navigate('/statistic/settings');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -941,7 +941,8 @@ function init(): void {
|
|||||||
categoryIds: initQuery['categoryIds'],
|
categoryIds: initQuery['categoryIds'],
|
||||||
accountIds: initQuery['accountIds'],
|
accountIds: initQuery['accountIds'],
|
||||||
tagIds: initQuery['tagIds'],
|
tagIds: initQuery['tagIds'],
|
||||||
tagFilterType: initQuery['tagFilterType'] && parseInt(initQuery['tagFilterType']) >= 0 ? parseInt(initQuery['tagFilterType']) : undefined
|
tagFilterType: initQuery['tagFilterType'] && parseInt(initQuery['tagFilterType']) >= 0 ? parseInt(initQuery['tagFilterType']) : undefined,
|
||||||
|
keyword: initQuery['keyword']
|
||||||
});
|
});
|
||||||
|
|
||||||
reload();
|
reload();
|
||||||
|
|||||||
Reference in New Issue
Block a user