diff --git a/src/lib/category.js b/src/lib/category.js index 5543d200..b5b1afb9 100644 --- a/src/lib/category.js +++ b/src/lib/category.js @@ -262,6 +262,21 @@ export function selectInvert(filterCategoryIds, allTransactionCategoriesMap) { } } +export function isCategoryOrSubCategoriesAllChecked(category, filterCategoryIds) { + if (!category.subCategories) { + return !filterCategoryIds[category.id]; + } + + for (let i = 0; i < category.subCategories.length; i++) { + const subCategory = category.subCategories[i]; + if (filterCategoryIds[subCategory.id]) { + return false; + } + } + + return true; +} + export function isSubCategoriesAllChecked(category, filterCategoryIds) { for (let i = 0; i < category.subCategories.length; i++) { const subCategory = category.subCategories[i]; diff --git a/src/lib/common.js b/src/lib/common.js index 3f8a2333..b6c5f23f 100644 --- a/src/lib/common.js +++ b/src/lib/common.js @@ -359,6 +359,20 @@ export function arrayContainsFieldValue(array, fieldName, value) { return false; } +export function objectToArray(object) { + const ret = []; + + for (let field in object) { + if (!Object.prototype.hasOwnProperty.call(object, field)) { + continue; + } + + ret.push(field); + } + + return ret; +} + export function categorizedArrayToPlainArray(object) { const ret = []; diff --git a/src/styles/mobile/global.css b/src/styles/mobile/global.css index 2135933f..160c593d 100644 --- a/src/styles/mobile/global.css +++ b/src/styles/mobile/global.css @@ -270,6 +270,10 @@ i.icon.la, i.icon.las, i.icon.lab { font-weight: bold; } +.list > ul > li.item-in-multiple-selection > .item-content > .item-inner > .item-title { + font-weight: bold; +} + .list.list-dividers li.has-child-list-item .item-inner:after { content: ''; position: absolute; diff --git a/src/views/desktop/app/settings/tabs/AppStatisticsSettingTab.vue b/src/views/desktop/app/settings/tabs/AppStatisticsSettingTab.vue index 1447692d..6c778be6 100644 --- a/src/views/desktop/app/settings/tabs/AppStatisticsSettingTab.vue +++ b/src/views/desktop/app/settings/tabs/AppStatisticsSettingTab.vue @@ -115,11 +115,11 @@ - + - + diff --git a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue index b1f2bddd..82455a85 100644 --- a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue @@ -125,6 +125,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.js'; import { useAccountsStore } from '@/stores/account.js'; +import { useTransactionsStore } from '@/stores/transaction.js'; import { useStatisticsStore } from '@/stores/statistics.js'; import accountConstants from '@/consts/account.js'; @@ -149,7 +150,7 @@ import { export default { props: [ 'dialogMode', - 'modifyDefault', + 'type', 'autoSave' ], emits: [ @@ -169,16 +170,16 @@ export default { } }, computed: { - ...mapStores(useSettingsStore, useAccountsStore, useStatisticsStore), + ...mapStores(useSettingsStore, useAccountsStore, useTransactionsStore, useStatisticsStore), title() { - if (this.modifyDefault) { + if (this.type === 'statisticsDefault') { return 'Default Account Filter'; } else { return 'Filter Accounts'; } }, applyText() { - if (this.modifyDefault) { + if (this.type === 'statisticsDefault') { return 'Save'; } else { return 'Apply'; @@ -210,13 +211,33 @@ export default { } const account = self.accountsStore.allAccountsMap[accountId]; - allAccountIds[account.id] = false; + + if (this.type === 'transactionListCurrent' && self.transactionsStore.allFilterAccountIdsCount > 0) { + allAccountIds[account.id] = true; + } else { + allAccountIds[account.id] = false; + } } - if (self.modifyDefault) { + if (this.type === 'statisticsDefault') { self.filterAccountIds = copyObjectTo(self.settingsStore.appSettings.statistics.defaultAccountFilter, allAccountIds); - } else { + } else if (this.type === 'statisticsCurrent') { self.filterAccountIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterAccountIds, allAccountIds); + } else if (this.type === 'transactionListCurrent') { + for (let accountId in self.transactionsStore.allFilterAccountIds) { + if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterAccountIds, accountId)) { + continue; + } + + const account = self.accountsStore.allAccountsMap[accountId]; + + if (account) { + selectAccountOrSubAccounts(allAccountIds, account, false); + } + } + self.filterAccountIds = allAccountIds; + } else { + self.$refs.snackbar.showError('Parameter Invalid'); } }).catch(error => { self.loading = false; @@ -231,26 +252,40 @@ export default { const self = this; const filteredAccountIds = {}; + let finalAccountIds = ''; for (let accountId in self.filterAccountIds) { if (!Object.prototype.hasOwnProperty.call(self.filterAccountIds, accountId)) { continue; } - if (self.filterAccountIds[accountId]) { + const account = self.accountsStore.allAccountsMap[accountId]; + + if (!isAccountOrSubAccountsAllChecked(account, self.filterAccountIds)) { filteredAccountIds[accountId] = true; + } else { + if (finalAccountIds.length > 0) { + finalAccountIds += ','; + } + + finalAccountIds += accountId; } } - if (self.modifyDefault) { + if (this.type === 'statisticsDefault') { self.settingsStore.setStatisticsDefaultAccountFilter(filteredAccountIds); - } else { + } else if (this.type === 'statisticsCurrent') { self.statisticsStore.updateTransactionStatisticsFilter({ filterAccountIds: filteredAccountIds }); + } else if (this.type === 'transactionListCurrent') { + self.transactionsStore.updateTransactionListFilter({ + accountIds: finalAccountIds + }); + self.transactionsStore.updateTransactionListInvalidState(true); } - this.$emit('settings:change', true); + self.$emit('settings:change', true); }, cancel() { this.$emit('settings:change', false); diff --git a/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue b/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue index d93f8ad3..05d8aa21 100644 --- a/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue @@ -123,6 +123,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.js'; import { useTransactionCategoriesStore } from '@/stores/transactionCategory.js'; +import { useTransactionsStore } from '@/stores/transaction.js'; import { useStatisticsStore } from '@/stores/statistics.js'; import categoryConstants from '@/consts/category.js'; @@ -135,6 +136,7 @@ import { selectAll, selectNone, selectInvert, + isCategoryOrSubCategoriesAllChecked, isSubCategoriesAllChecked, isSubCategoriesHasButNotAllChecked } from '@/lib/category.js'; @@ -149,7 +151,7 @@ import { export default { props: [ 'dialogMode', - 'modifyDefault', + 'type', 'autoSave' ], emits: [ @@ -173,16 +175,16 @@ export default { } }, computed: { - ...mapStores(useSettingsStore, useTransactionCategoriesStore, useStatisticsStore), + ...mapStores(useSettingsStore, useTransactionCategoriesStore, useTransactionsStore, useStatisticsStore), title() { - if (this.modifyDefault) { + if (this.type === 'statisticsDefault') { return 'Default Transaction Category Filter'; } else { return 'Filter Transaction Categories'; } }, applyText() { - if (this.modifyDefault) { + if (this.type === 'statisticsDefault') { return 'Save'; } else { return 'Apply'; @@ -214,13 +216,36 @@ export default { } const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId]; - allCategoryIds[category.id] = false; + + if (this.type === 'transactionListCurrent' && self.transactionsStore.allFilterCategoryIdsCount > 0) { + allCategoryIds[category.id] = true; + } else { + allCategoryIds[category.id] = false; + } } - if (self.modifyDefault) { + if (this.type === 'statisticsDefault') { self.filterCategoryIds = copyObjectTo(self.settingsStore.appSettings.statistics.defaultTransactionCategoryFilter, allCategoryIds); - } else { + } else if (this.type === 'statisticsCurrent') { self.filterCategoryIds = copyObjectTo(self.statisticsStore.transactionStatisticsFilter.filterCategoryIds, allCategoryIds); + } else if (this.type === 'transactionListCurrent') { + for (let categoryId in self.transactionsStore.allFilterCategoryIds) { + if (!Object.prototype.hasOwnProperty.call(self.transactionsStore.allFilterCategoryIds, categoryId)) { + continue; + } + + const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId]; + + if (category && (!category.subCategories || !category.subCategories.length)) { + allCategoryIds[category.id] = false; + } else if (category) { + selectSubCategories(allCategoryIds, category, false); + } + } + + self.filterCategoryIds = allCategoryIds; + } else { + self.$refs.snackbar.showError('Parameter Invalid'); } }).catch(error => { self.loading = false; @@ -235,26 +260,40 @@ export default { const self = this; const filteredCategoryIds = {}; + let finalCategoryIds = ''; for (let categoryId in self.filterCategoryIds) { if (!Object.prototype.hasOwnProperty.call(self.filterCategoryIds, categoryId)) { continue; } - if (self.filterCategoryIds[categoryId]) { + const category = self.transactionCategoriesStore.allTransactionCategoriesMap[categoryId]; + + if (!isCategoryOrSubCategoriesAllChecked(category, self.filterCategoryIds)) { filteredCategoryIds[categoryId] = true; + } else { + if (finalCategoryIds.length > 0) { + finalCategoryIds += ','; + } + + finalCategoryIds += categoryId; } } - if (self.modifyDefault) { + if (this.type === 'statisticsDefault') { self.settingsStore.setStatisticsDefaultTransactionCategoryFilter(filteredCategoryIds); - } else { + } else if (this.type === 'statisticsCurrent') { self.statisticsStore.updateTransactionStatisticsFilter({ filterCategoryIds: filteredCategoryIds }); + } else if (this.type === 'transactionListCurrent') { + self.transactionsStore.updateTransactionListFilter({ + categoryIds: finalCategoryIds + }); + self.transactionsStore.updateTransactionListInvalidState(true); } - this.$emit('settings:change', true); + self.$emit('settings:change', true); }, cancel() { this.$emit('settings:change', false); diff --git a/src/views/desktop/statistics/TransactionPage.vue b/src/views/desktop/statistics/TransactionPage.vue index 100527fd..f67341a0 100644 --- a/src/views/desktop/statistics/TransactionPage.vue +++ b/src/views/desktop/statistics/TransactionPage.vue @@ -276,14 +276,12 @@ @dateRange:change="setCustomDateFilter" /> - - diff --git a/src/views/desktop/transactions/ListPage.vue b/src/views/desktop/transactions/ListPage.vue index 204febad..e0d9752e 100644 --- a/src/views/desktop/transactions/ListPage.vue +++ b/src/views/desktop/transactions/ListPage.vue @@ -121,7 +121,7 @@ - + @@ -137,7 +137,7 @@ :class="{ 'list-item-selected': query.categoryIds && queryAllFilterCategoryIdsCount > 1 }" :append-icon="(query.categoryIds && queryAllFilterCategoryIdsCount > 1 ? icons.check : null)"> + @click="showFilterCategoryDialog = true">
{{ $t('Multiple Categories') }} @@ -170,8 +170,9 @@ + :append-icon="(query.categoryIds === category.id ? icons.check : null)">
@@ -186,8 +187,8 @@ @@ -264,7 +265,7 @@
- + @@ -280,7 +281,7 @@ :class="{ 'list-item-selected': query.accountIds && queryAllFilterAccountIdsCount > 1 }" :append-icon="(query.accountIds && queryAllFilterAccountIdsCount > 1 ? icons.check : null)"> + @click="showFilterAccountDialog = true">
{{ $t('Multiple Accounts') }} @@ -292,8 +293,8 @@ @@ -406,12 +407,24 @@ @dateRange:change="changeCustomDateFilter" /> + + + + + + + +