From 8207373a051ccda7de76733584262cbb6a78d9e5 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Fri, 24 Jan 2025 23:16:57 +0800 Subject: [PATCH] migrate statistics store to composition API and typescript --- src/components/desktop/TrendsChart.vue | 2 +- src/components/mobile/TrendsBarChart.vue | 2 +- src/lib/services.ts | 6 +- src/lib/statistics.ts | 3 +- src/models/transaction.ts | 60 +- src/stores/index.ts | 3 +- src/stores/statistics.js | 1006 --------------- src/stores/statistics.ts | 1119 +++++++++++++++++ src/stores/transaction.js | 2 +- .../base/settings/AppSettingsPageBase.ts | 3 +- .../cards/AccountFilterSettingsCard.vue | 2 +- .../cards/CategoryFilterSettingsCard.vue | 2 +- .../TransactionTagFilterSettingsCard.vue | 2 +- .../desktop/statistics/TransactionPage.vue | 2 +- .../list/dialogs/ImportDialog.vue | 2 +- .../settings/AccountFilterSettingsPage.vue | 2 +- .../settings/CategoryFilterSettingsPage.vue | 2 +- .../TransactionTagFilterSettingsPage.vue | 2 +- .../mobile/statistics/TransactionPage.vue | 2 +- 19 files changed, 1196 insertions(+), 1028 deletions(-) delete mode 100644 src/stores/statistics.js create mode 100644 src/stores/statistics.ts diff --git a/src/components/desktop/TrendsChart.vue b/src/components/desktop/TrendsChart.vue index 0cc290c1..12f0f7a8 100644 --- a/src/components/desktop/TrendsChart.vue +++ b/src/components/desktop/TrendsChart.vue @@ -383,7 +383,7 @@ export default { dateRange: { minTime: minUnixTime, maxTime: maxUnixTime, - type: dateRangeType + dateType: dateRangeType } }); }, diff --git a/src/components/mobile/TrendsBarChart.vue b/src/components/mobile/TrendsBarChart.vue index e5d563e4..86c9916a 100644 --- a/src/components/mobile/TrendsBarChart.vue +++ b/src/components/mobile/TrendsBarChart.vue @@ -310,7 +310,7 @@ export default { dateRange: { minTime: minUnixTime, maxTime: maxUnixTime, - type: dateRangeType + dateType: dateRangeType } }); }, diff --git a/src/lib/services.ts b/src/lib/services.ts index 2eccd036..ca8aea33 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -53,7 +53,7 @@ import type { TransactionStatisticRequest, TransactionStatisticResponse, TransactionStatisticTrendsRequest, - TransactionStatisticTrendsItem, + TransactionStatisticTrendsResponseItem, TransactionAmountsRequestParams, TransactionAmountsResponse } from '@/models/transaction.ts'; @@ -380,7 +380,7 @@ export default { return axios.get>(`v1/transactions/statistics.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : '')); }, - getTransactionStatisticsTrends: (req: TransactionStatisticTrendsRequest): ApiResponsePromise => { + getTransactionStatisticsTrends: (req: TransactionStatisticTrendsRequest): ApiResponsePromise => { const queryParams = []; if (req.startYearMonth) { @@ -399,7 +399,7 @@ export default { queryParams.push(`tag_filter_type=${req.tagFilterType}`); } - return axios.get>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : '')); + return axios.get>(`v1/transactions/statistics/trends.json?use_transaction_timezone=${req.useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : '')); }, getTransactionAmounts: (params: TransactionAmountsRequestParams): ApiResponsePromise => { const req = TransactionAmountsRequest.of(params); diff --git a/src/lib/statistics.ts b/src/lib/statistics.ts index 9562474f..6cd7832c 100644 --- a/src/lib/statistics.ts +++ b/src/lib/statistics.ts @@ -1,5 +1,6 @@ import type { YearMonth, YearUnixTime, YearQuarterUnixTime, YearMonthUnixTime } from '@/core/datetime.ts'; import { ChartSortingType, ChartDateAggregationType } from '@/core/statistics.ts'; +import type { TransactionStatisticDataItemBase } from '@/models/transaction.ts'; import { getAllMonthsStartAndEndUnixTimes, @@ -7,7 +8,7 @@ import { getAllYearsStartAndEndUnixTimes } from '@/lib/datetime.ts'; -export function sortStatisticsItems(items: { name: string, totalAmount: number, displayOrders: number[] }[], sortingType: number): void { +export function sortStatisticsItems(items: TransactionStatisticDataItemBase[], sortingType: number): void { if (sortingType === ChartSortingType.DisplayOrder.type) { items.sort(function (data1, data2) { for (let i = 0; i < Math.min(data1.displayOrders.length, data2.displayOrders.length); i++) { diff --git a/src/models/transaction.ts b/src/models/transaction.ts index c2a638e6..b1e8d56b 100644 --- a/src/models/transaction.ts +++ b/src/models/transaction.ts @@ -227,15 +227,71 @@ export interface TransactionStatisticResponse { export interface TransactionStatisticResponseItem { readonly categoryId: string; readonly accountId: string; - readonly totalAmount: number; + readonly amount: number; } -export interface TransactionStatisticTrendsItem { +export interface TransactionStatisticResponseWithInfo { + readonly startTime: number; + readonly endTime: number; + readonly items: TransactionStatisticResponseItemWithInfo[]; +} + +export interface TransactionStatisticResponseItemWithInfo extends TransactionStatisticResponseItem { + readonly account?: AccountInfoResponse; + readonly primaryAccount?: AccountInfoResponse; + readonly category?: TransactionCategoryInfoResponse; + readonly primaryCategory?: TransactionCategoryInfoResponse; + readonly amountInDefaultCurrency: number | null; +} + +export interface TransactionStatisticTrendsResponseItem { readonly year: number; readonly month: number; readonly items: TransactionStatisticResponseItem[]; } +export interface TransactionStatisticTrendsResponseItemWithInfo { + readonly year: number; + readonly month: number; + readonly items: TransactionStatisticResponseItemWithInfo[]; +} + +export type TransactionStatisticDataItemType = 'category' | 'account' | 'total'; + +export interface TransactionStatisticDataItemBase { + readonly name: string; + readonly type: TransactionStatisticDataItemType; + readonly id: string; + readonly icon: string; + readonly color: string; + readonly hidden: boolean; + readonly displayOrders: number[]; + readonly totalAmount: number; +} + +export interface TransactionCategoricalAnalysisData { + readonly totalAmount: number; + readonly items: TransactionCategoricalAnalysisDataItem[]; +} + +export interface TransactionCategoricalAnalysisDataItem extends TransactionStatisticDataItemBase { + readonly percent: number; +} + +export interface TransactionTrendsAnalysisData { + readonly items: TransactionTrendsAnalysisDataItem[]; +} + +export interface TransactionTrendsAnalysisDataItem extends TransactionStatisticDataItemBase { + readonly items: TransactionTrendsAnalysisDataAmount[]; +} + +export interface TransactionTrendsAnalysisDataAmount { + readonly year: number; + readonly month: number; + readonly totalAmount: number; +} + export type TransactionAmountsResponse = PartialRecord; export interface TransactionAmountsResponseItem { diff --git a/src/stores/index.ts b/src/stores/index.ts index 95ad80f1..3dd7eaab 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -11,8 +11,7 @@ import { useTransactionTemplatesStore } from './transactionTemplate.js'; // @ts-expect-error the above file is migrating to ts import { useTransactionsStore } from './transaction.js'; import { useOverviewStore } from './overview.ts'; -// @ts-expect-error the above file is migrating to ts -import { useStatisticsStore } from './statistics.js'; +import { useStatisticsStore } from './statistics.ts'; import { useExchangeRatesStore } from './exchangeRates.ts'; import type { AuthResponse, RegisterResponse } from '@/models/auth_response.ts'; diff --git a/src/stores/statistics.js b/src/stores/statistics.js deleted file mode 100644 index e2c7ba88..00000000 --- a/src/stores/statistics.js +++ /dev/null @@ -1,1006 +0,0 @@ -import { defineStore } from 'pinia'; - -import { useSettingsStore } from './setting.ts'; -import { useUserStore } from './user.ts'; -import { useAccountsStore } from './account.ts'; -import { useTransactionCategoriesStore } from './transactionCategory.ts'; -import { useExchangeRatesStore } from './exchangeRates.ts'; - -import { DateRangeScene, DateRange } from '@/core/datetime'; -import { CategoryType } from '@/core/category.ts'; -import { TransactionTagFilterType } from '@/core/transaction.ts'; -import { - StatisticsAnalysisType, - CategoricalChartType, - TrendChartType, - ChartDataType, - ChartSortingType, - ChartDateAggregationType, - DEFAULT_CATEGORICAL_CHART_DATA_RANGE, - DEFAULT_TREND_CHART_DATA_RANGE -} from '@/core/statistics.ts'; -import { DEFAULT_ACCOUNT_ICON, DEFAULT_CATEGORY_ICON } from '@/consts/icon.ts'; -import { DEFAULT_ACCOUNT_COLOR, DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts'; -import services from '@/lib/services.ts'; -import logger from '@/lib/logger.ts'; -import { - isEquals, - isNumber, - isString, - isObject, - isInteger, - isYearMonth, - isYearMonthEquals, - isObjectEmpty, - objectFieldToArrayItem -} from '@/lib/common.ts'; -import { - getYearAndMonthFromUnixTime, - getDateRangeByDateType -} from '@/lib/datetime.ts'; -import { - getFinalAccountIdsByFilteredAccountIds -} from '@/lib/account.ts'; -import { - getFinalCategoryIdsByFilteredCategoryIds -} from '@/lib/category.ts'; -import { - sortStatisticsItems -} from '@/lib/statistics.ts'; - -function assembleAccountAndCategoryInfo(userStore, accountsStore, transactionCategoriesStore, exchangeRatesStore, items) { - const finalItems = []; - const defaultCurrency = userStore.currentUserDefaultCurrency; - - for (let i = 0; i < items.length; i++) { - const dataItem = items[i]; - const item = { - categoryId: dataItem.categoryId, - accountId: dataItem.accountId, - amount: dataItem.amount - }; - - if (item.accountId) { - item.account = accountsStore.allAccountsMap[item.accountId]; - } - - if (item.account && item.account.parentId !== '0') { - item.primaryAccount = accountsStore.allAccountsMap[item.account.parentId]; - } else { - item.primaryAccount = item.account; - } - - if (item.categoryId) { - item.category = transactionCategoriesStore.allTransactionCategoriesMap[item.categoryId]; - } - - if (item.category && item.category.parentId !== '0') { - item.primaryCategory = transactionCategoriesStore.allTransactionCategoriesMap[item.category.parentId]; - } else { - item.primaryCategory = item.category; - } - - if (item.account && item.account.currency !== defaultCurrency) { - const amount = exchangeRatesStore.getExchangedAmount(item.amount, item.account.currency, defaultCurrency); - - if (isNumber(amount)) { - item.amountInDefaultCurrency = Math.floor(amount); - } - } else if (item.account && item.account.currency === defaultCurrency) { - item.amountInDefaultCurrency = item.amount; - } else { - item.amountInDefaultCurrency = null; - } - - finalItems.push(item); - } - - return finalItems; -} - -function getCategoryTotalAmountItems(items, transactionStatisticsFilter) { - const allDataItems = {}; - let totalAmount = 0; - let totalNonNegativeAmount = 0; - - for (let i = 0; i < items.length; i++) { - const item = items[i]; - - if (!item.primaryAccount || !item.account || !item.primaryCategory || !item.category) { - continue; - } - - if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || - transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type) { - if (item.category.type !== CategoryType.Expense) { - continue; - } - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type || - transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type) { - if (item.category.type !== CategoryType.Income) { - continue; - } - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { - // Do Nothing - } else { - continue; - } - - if (transactionStatisticsFilter.filterAccountIds && transactionStatisticsFilter.filterAccountIds[item.account.id]) { - continue; - } - - if (transactionStatisticsFilter.filterCategoryIds && transactionStatisticsFilter.filterCategoryIds[item.category.id]) { - continue; - } - - if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || - transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type) { - if (isNumber(item.amountInDefaultCurrency)) { - let data = allDataItems[item.account.id]; - - if (data) { - data.totalAmount += item.amountInDefaultCurrency; - } else { - data = { - name: item.account.name, - type: 'account', - id: item.account.id, - icon: item.account.icon || DEFAULT_ACCOUNT_ICON.icon, - color: item.account.color || DEFAULT_ACCOUNT_COLOR, - hidden: item.primaryAccount.hidden || item.account.hidden, - displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], - totalAmount: item.amountInDefaultCurrency - } - } - - totalAmount += item.amountInDefaultCurrency; - - if (item.amountInDefaultCurrency > 0) { - totalNonNegativeAmount += item.amountInDefaultCurrency; - } - - allDataItems[item.account.id] = data; - } - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type) { - if (isNumber(item.amountInDefaultCurrency)) { - let data = allDataItems[item.primaryCategory.id]; - - if (data) { - data.totalAmount += item.amountInDefaultCurrency; - } else { - data = { - name: item.primaryCategory.name, - type: 'category', - id: item.primaryCategory.id, - icon: item.primaryCategory.icon || DEFAULT_CATEGORY_ICON.icon, - color: item.primaryCategory.color || DEFAULT_CATEGORY_COLOR, - hidden: item.primaryCategory.hidden, - displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder], - totalAmount: item.amountInDefaultCurrency - } - } - - totalAmount += item.amountInDefaultCurrency; - - if (item.amountInDefaultCurrency > 0) { - totalNonNegativeAmount += item.amountInDefaultCurrency; - } - - allDataItems[item.primaryCategory.id] = data; - } - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || - transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { - if (isNumber(item.amountInDefaultCurrency)) { - let data = allDataItems[item.category.id]; - - if (data) { - data.totalAmount += item.amountInDefaultCurrency; - } else { - data = { - name: item.category.name, - type: 'category', - id: item.category.id, - icon: item.category.icon || DEFAULT_CATEGORY_ICON.icon, - color: item.category.color || DEFAULT_CATEGORY_COLOR, - hidden: item.primaryCategory.hidden || item.category.hidden, - displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder, item.category.displayOrder], - totalAmount: item.amountInDefaultCurrency - } - } - - totalAmount += item.amountInDefaultCurrency; - - if (item.amountInDefaultCurrency > 0) { - totalNonNegativeAmount += item.amountInDefaultCurrency; - } - - allDataItems[item.category.id] = data; - } - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type || - transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type || - transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { - if (isNumber(item.amountInDefaultCurrency)) { - let data = allDataItems['total']; - let amount = item.amountInDefaultCurrency; - - if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type && - item.category.type === CategoryType.Expense) { - amount = -amount; - } - - if (data) { - data.totalAmount += amount; - } else { - let name = ''; - - if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type) { - name = ChartDataType.TotalExpense.name; - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type) { - name = ChartDataType.TotalIncome.name; - } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { - name = ChartDataType.TotalBalance.name; - } - - data = { - name: name, - type: 'total', - id: 'total', - icon: '', - color: '', - hidden: false, - displayOrders: [1], - totalAmount: amount - } - } - - totalAmount += amount; - - if (item.amountInDefaultCurrency > 0) { - totalNonNegativeAmount += amount; - } - - allDataItems['total'] = data; - } - } - } - - return { - totalAmount: totalAmount, - totalNonNegativeAmount: totalNonNegativeAmount, - items: allDataItems - }; -} - -function sortCategoryTotalAmountItems(items, transactionStatisticsFilter) { - sortStatisticsItems(items, transactionStatisticsFilter.sortingType) -} - -export const useStatisticsStore = defineStore('statistics', { - state: () => ({ - transactionStatisticsFilter: { - chartDataType: ChartDataType.Default.type, - categoricalChartType: CategoricalChartType.Default.type, - categoricalChartDateType: DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type, - categoricalChartStartTime: 0, - categoricalChartEndTime: 0, - trendChartType: TrendChartType.Default.type, - trendChartDateType: DEFAULT_TREND_CHART_DATA_RANGE.type, - trendChartStartYearMonth: '', - trendChartEndYearMonth: '', - filterAccountIds: {}, - filterCategoryIds: {}, - tagIds: '', - tagFilterType: TransactionTagFilterType.Default.type - }, - transactionCategoryStatisticsData: {}, - transactionCategoryTrendsData: {}, - transactionStatisticsStateInvalid: true - }), - getters: { - categoricalAnalysisChartDataCategory(state) { - if (state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalAssets.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalLiabilities.type) { - return 'account'; - } else if (state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { - return 'category'; - } else { - return ''; - } - }, - transactionCategoryStatisticsDataWithCategoryAndAccountInfo(state) { - const statistics = state.transactionCategoryStatisticsData; - const finalStatistics = { - startTime: statistics.startTime, - endTime: statistics.endTime, - items: [] - }; - - if (statistics && statistics.items && statistics.items.length) { - const userStore = useUserStore(); - const accountsStore = useAccountsStore(); - const transactionCategoriesStore = useTransactionCategoriesStore(); - const exchangeRatesStore = useExchangeRatesStore(); - - finalStatistics.items = assembleAccountAndCategoryInfo(userStore, accountsStore, transactionCategoriesStore, exchangeRatesStore, statistics.items); - } - - return finalStatistics; - }, - transactionCategoryTotalAmountAnalysisData(state) { - if (!state.transactionCategoryStatisticsDataWithCategoryAndAccountInfo || !state.transactionCategoryStatisticsDataWithCategoryAndAccountInfo.items) { - return null; - } - - return getCategoryTotalAmountItems(state.transactionCategoryStatisticsDataWithCategoryAndAccountInfo.items, state.transactionStatisticsFilter); - }, - accountTotalAmountAnalysisData(state) { - const userStore = useUserStore(); - const accountsStore = useAccountsStore(); - const exchangeRatesStore = useExchangeRatesStore(); - - if (!accountsStore.allPlainAccounts) { - return null; - } - - const allDataItems = {}; - let totalAmount = 0; - let totalNonNegativeAmount = 0; - - for (let i = 0; i < accountsStore.allPlainAccounts.length; i++) { - const account = accountsStore.allPlainAccounts[i]; - - if (state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalAssets.type) { - if (!account.isAsset) { - continue; - } - } else if (state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalLiabilities.type) { - if (!account.isLiability) { - continue; - } - } - - if (state.transactionStatisticsFilter.filterAccountIds && state.transactionStatisticsFilter.filterAccountIds[account.id]) { - continue; - } - - let primaryAccount = accountsStore.allAccountsMap[account.parentId]; - - if (!primaryAccount) { - primaryAccount = account; - } - - let amount = account.balance; - - if (account.currency !== userStore.currentUserDefaultCurrency) { - amount = Math.floor(exchangeRatesStore.getExchangedAmount(amount, account.currency, userStore.currentUserDefaultCurrency)); - - if (!isNumber(amount)) { - continue; - } - } - - if (account.isLiability) { - amount = -amount; - } - - const data = { - name: account.name, - type: 'account', - id: account.id, - icon: account.icon || DEFAULT_ACCOUNT_ICON.icon, - color: account.color || DEFAULT_ACCOUNT_COLOR, - hidden: primaryAccount.hidden || account.hidden, - displayOrders: [primaryAccount.category, primaryAccount.displayOrder, account.displayOrder], - totalAmount: amount - }; - - totalAmount += amount; - - if (amount > 0) { - totalNonNegativeAmount += amount; - } - - allDataItems[account.id] = data; - } - - return { - totalAmount: totalAmount, - totalNonNegativeAmount: totalNonNegativeAmount, - items: allDataItems - } - }, - categoricalAnalysisData(state) { - let combinedData = { - items: [], - totalAmount: 0 - }; - - if (state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { - combinedData = state.transactionCategoryTotalAmountAnalysisData; - } else if (state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalAssets.type || - state.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalLiabilities.type) { - combinedData = state.accountTotalAmountAnalysisData; - } - - const allStatisticsItems = []; - - for (let id in combinedData.items) { - if (!Object.prototype.hasOwnProperty.call(combinedData.items, id)) { - continue; - } - - const data = combinedData.items[id]; - - if (data.totalAmount > 0) { - data.percent = data.totalAmount * 100 / combinedData.totalNonNegativeAmount; - } else { - data.percent = 0; - } - - if (data.percent < 0) { - data.percent = 0; - } - - allStatisticsItems.push(data); - } - - sortCategoryTotalAmountItems(allStatisticsItems, state.transactionStatisticsFilter); - - return { - totalAmount: combinedData.totalAmount, - items: allStatisticsItems - }; - }, - transactionCategoryTrendsDataWithCategoryAndAccountInfo(state) { - const trendsData = state.transactionCategoryTrendsData; - const finalTrendsData = []; - - if (trendsData && trendsData.length) { - const userStore = useUserStore(); - const accountsStore = useAccountsStore(); - const transactionCategoriesStore = useTransactionCategoriesStore(); - const exchangeRatesStore = useExchangeRatesStore(); - - for (let i = 0; i < trendsData.length; i++) { - const trendItem = trendsData[i]; - const finalTrendItem = { - year: trendItem.year, - month: trendItem.month, - items: [] - }; - - if (trendItem && trendItem.items && trendItem.items.length) { - finalTrendItem.items = assembleAccountAndCategoryInfo(userStore, accountsStore, transactionCategoriesStore, exchangeRatesStore, trendItem.items); - } - - finalTrendsData.push(finalTrendItem); - } - } - - return finalTrendsData; - }, - trendsAnalysisData(state) { - if (!state.transactionCategoryTrendsDataWithCategoryAndAccountInfo || !state.transactionCategoryTrendsDataWithCategoryAndAccountInfo.length) { - return null; - } - - const combinedDataMap = {}; - - for (let i = 0; i < state.transactionCategoryTrendsDataWithCategoryAndAccountInfo.length; i++) { - const trendItem = state.transactionCategoryTrendsDataWithCategoryAndAccountInfo[i]; - const totalAmountItems = getCategoryTotalAmountItems(trendItem.items, state.transactionStatisticsFilter); - - for (let id in totalAmountItems.items) { - if (!Object.prototype.hasOwnProperty.call(totalAmountItems.items, id)) { - continue; - } - - const item = totalAmountItems.items[id]; - let combinedData = combinedDataMap[id]; - - if (!combinedData) { - combinedData = { - name: item.name, - type: item.type, - id: item.id, - icon: item.icon, - color: item.color, - hidden: item.hidden, - displayOrders: item.displayOrders, - totalAmount: 0, - items: [] - }; - } - - combinedData.items.push({ - year: trendItem.year, - month: trendItem.month, - totalAmount: item.totalAmount - }); - - combinedData.totalAmount += item.totalAmount; - combinedDataMap[id] = combinedData; - } - } - - const totalAmountsTrends = []; - - for (let id in combinedDataMap) { - if (!Object.prototype.hasOwnProperty.call(combinedDataMap, id)) { - continue; - } - - const trendData = combinedDataMap[id]; - totalAmountsTrends.push(trendData); - } - - sortCategoryTotalAmountItems(totalAmountsTrends, state.transactionStatisticsFilter); - - return { - items: totalAmountsTrends - }; - } - }, - actions: { - updateTransactionStatisticsInvalidState(invalidState) { - this.transactionStatisticsStateInvalid = invalidState; - }, - resetTransactionStatistics() { - this.transactionStatisticsFilter.chartDataType = ChartDataType.Default.type; - this.transactionStatisticsFilter.categoricalChartType = CategoricalChartType.Default.type; - this.transactionStatisticsFilter.categoricalChartDateType = DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type; - this.transactionStatisticsFilter.categoricalChartStartTime = 0; - this.transactionStatisticsFilter.categoricalChartEndTime = 0; - this.transactionStatisticsFilter.trendChartType = TrendChartType.Default.type; - this.transactionStatisticsFilter.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type; - this.transactionStatisticsFilter.trendChartStartYearMonth = ''; - this.transactionStatisticsFilter.trendChartEndYearMonth = ''; - this.transactionStatisticsFilter.filterAccountIds = {}; - this.transactionStatisticsFilter.filterCategoryIds = {}; - this.transactionStatisticsFilter.tagIds = ''; - this.transactionStatisticsFilter.tagFilterType = TransactionTagFilterType.Default.type; - this.transactionCategoryStatisticsData = {}; - this.transactionCategoryTrendsData = {}; - this.transactionStatisticsStateInvalid = true; - }, - initTransactionStatisticsFilter(analysisType, filter) { - const settingsStore = useSettingsStore(); - const userStore = useUserStore(); - - if (filter && isInteger(filter.chartDataType)) { - this.transactionStatisticsFilter.chartDataType = filter.chartDataType; - } else { - this.transactionStatisticsFilter.chartDataType = settingsStore.appSettings.statistics.defaultChartDataType; - } - - if (analysisType === StatisticsAnalysisType.CategoricalAnalysis || analysisType === StatisticsAnalysisType.TrendAnalysis) { - if (!ChartDataType.isAvailableForAnalysisType(this.transactionStatisticsFilter.chartDataType, analysisType)) { - this.transactionStatisticsFilter.chartDataType = ChartDataType.Default.type; - } - } - - if (filter && isInteger(filter.categoricalChartType)) { - this.transactionStatisticsFilter.categoricalChartType = filter.categoricalChartType; - } else { - this.transactionStatisticsFilter.categoricalChartType = settingsStore.appSettings.statistics.defaultCategoricalChartType; - } - - if (this.transactionStatisticsFilter.categoricalChartType !== CategoricalChartType.Pie.type && this.transactionStatisticsFilter.categoricalChartType !== CategoricalChartType.Bar.type) { - this.transactionStatisticsFilter.categoricalChartType = CategoricalChartType.Default.type; - } - - if (filter && isInteger(filter.categoricalChartDateType)) { - this.transactionStatisticsFilter.categoricalChartDateType = filter.categoricalChartDateType; - } else { - this.transactionStatisticsFilter.categoricalChartDateType = settingsStore.appSettings.statistics.defaultCategoricalChartDataRangeType; - } - - let categoricalChartDateTypeValid = true; - - if (!DateRange.isAvailableForScene(this.transactionStatisticsFilter.categoricalChartDateType, DateRangeScene.Normal)) { - this.transactionStatisticsFilter.categoricalChartDateType = DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type; - categoricalChartDateTypeValid = false; - } - - if (categoricalChartDateTypeValid && this.transactionStatisticsFilter.categoricalChartDateType === DateRange.Custom.type) { - if (filter && isInteger(filter.categoricalChartStartTime)) { - this.transactionStatisticsFilter.categoricalChartStartTime = filter.categoricalChartStartTime; - } else { - this.transactionStatisticsFilter.categoricalChartStartTime = 0; - } - - if (filter && isInteger(filter.categoricalChartEndTime)) { - this.transactionStatisticsFilter.categoricalChartEndTime = filter.categoricalChartEndTime; - } else { - this.transactionStatisticsFilter.categoricalChartEndTime = 0; - } - } else { - const categoricalChartDateRange = getDateRangeByDateType(this.transactionStatisticsFilter.categoricalChartDateType, userStore.currentUserFirstDayOfWeek); - this.transactionStatisticsFilter.categoricalChartDateType = categoricalChartDateRange.dateType; - this.transactionStatisticsFilter.categoricalChartStartTime = categoricalChartDateRange.minTime; - this.transactionStatisticsFilter.categoricalChartEndTime = categoricalChartDateRange.maxTime; - } - - if (filter && isInteger(filter.trendChartType)) { - this.transactionStatisticsFilter.trendChartType = filter.trendChartType; - } else { - this.transactionStatisticsFilter.trendChartType = settingsStore.appSettings.statistics.defaultTrendChartType; - } - - if (this.transactionStatisticsFilter.trendChartType !== TrendChartType.Area.type && this.transactionStatisticsFilter.trendChartType !== TrendChartType.Column.type) { - this.transactionStatisticsFilter.trendChartType = TrendChartType.Default.type; - } - - if (filter && isInteger(filter.trendChartDateType)) { - this.transactionStatisticsFilter.trendChartDateType = filter.trendChartDateType; - } else { - this.transactionStatisticsFilter.trendChartDateType = settingsStore.appSettings.statistics.defaultTrendChartDataRangeType; - } - - let trendChartDateTypeValid = true; - - if (!DateRange.isAvailableForScene(this.transactionStatisticsFilter.trendChartDateType, DateRangeScene.TrendAnalysis)) { - this.transactionStatisticsFilter.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type; - trendChartDateTypeValid = false; - } - - if (trendChartDateTypeValid && this.transactionStatisticsFilter.trendChartDateType === DateRange.Custom.type) { - if (filter && isYearMonth(filter.trendChartStartYearMonth)) { - this.transactionStatisticsFilter.trendChartStartYearMonth = filter.trendChartStartYearMonth; - } else { - this.transactionStatisticsFilter.trendChartStartYearMonth = ''; - } - - if (filter && isYearMonth(filter.trendChartEndYearMonth)) { - this.transactionStatisticsFilter.trendChartEndYearMonth = filter.trendChartEndYearMonth; - } else { - this.transactionStatisticsFilter.trendChartEndYearMonth = ''; - } - } else { - const trendChartDateRange = getDateRangeByDateType(this.transactionStatisticsFilter.trendChartDateType, userStore.currentUserFirstDayOfWeek); - this.transactionStatisticsFilter.trendChartDateType = trendChartDateRange.dateType; - this.transactionStatisticsFilter.trendChartStartYearMonth = getYearAndMonthFromUnixTime(trendChartDateRange.minTime); - this.transactionStatisticsFilter.trendChartEndYearMonth = getYearAndMonthFromUnixTime(trendChartDateRange.maxTime); - } - - if (filter && isObject(filter.filterAccountIds)) { - this.transactionStatisticsFilter.filterAccountIds = filter.filterAccountIds; - } else { - this.transactionStatisticsFilter.filterAccountIds = settingsStore.appSettings.statistics.defaultAccountFilter || {}; - } - - if (filter && isObject(filter.filterCategoryIds)) { - this.transactionStatisticsFilter.filterCategoryIds = filter.filterCategoryIds; - } else { - this.transactionStatisticsFilter.filterCategoryIds = settingsStore.appSettings.statistics.defaultTransactionCategoryFilter || {}; - } - - if (filter && isString(filter.tagIds)) { - this.transactionStatisticsFilter.tagIds = filter.tagIds; - } else { - this.transactionStatisticsFilter.tagIds = ''; - } - - if (filter && isInteger(filter.tagFilterType)) { - this.transactionStatisticsFilter.tagFilterType = filter.tagFilterType; - } else { - this.transactionStatisticsFilter.tagFilterType = TransactionTagFilterType.Default.type; - } - - if (filter && isInteger(filter.sortingType)) { - this.transactionStatisticsFilter.sortingType = filter.sortingType; - } else { - this.transactionStatisticsFilter.sortingType = settingsStore.appSettings.statistics.defaultSortingType; - } - - if (this.transactionStatisticsFilter.sortingType < ChartSortingType.Amount.type || this.transactionStatisticsFilter.sortingType > ChartSortingType.Name.type) { - this.transactionStatisticsFilter.sortingType = ChartSortingType.Default.type; - } - }, - updateTransactionStatisticsFilter(filter) { - let changed = false; - - if (filter && isInteger(filter.chartDataType) && this.transactionStatisticsFilter.chartDataType !== filter.chartDataType) { - this.transactionStatisticsFilter.chartDataType = filter.chartDataType; - changed = true; - } - - if (filter && isInteger(filter.categoricalChartType) && this.transactionStatisticsFilter.categoricalChartType !== filter.categoricalChartType) { - this.transactionStatisticsFilter.categoricalChartType = filter.categoricalChartType; - changed = true; - } - - if (filter && isInteger(filter.categoricalChartDateType) && this.transactionStatisticsFilter.categoricalChartDateType !== filter.categoricalChartDateType) { - this.transactionStatisticsFilter.categoricalChartDateType = filter.categoricalChartDateType; - changed = true; - } - - if (filter && isInteger(filter.categoricalChartStartTime) && this.transactionStatisticsFilter.categoricalChartStartTime !== filter.categoricalChartStartTime) { - this.transactionStatisticsFilter.categoricalChartStartTime = filter.categoricalChartStartTime; - changed = true; - } - - if (filter && isInteger(filter.categoricalChartEndTime) && this.transactionStatisticsFilter.categoricalChartEndTime !== filter.categoricalChartEndTime) { - this.transactionStatisticsFilter.categoricalChartEndTime = filter.categoricalChartEndTime; - changed = true; - } - - if (filter && isInteger(filter.trendChartType) && this.transactionStatisticsFilter.trendChartType !== filter.trendChartType) { - this.transactionStatisticsFilter.trendChartType = filter.trendChartType; - changed = true; - } - - if (filter && isInteger(filter.trendChartDateType) && this.transactionStatisticsFilter.trendChartDateType !== filter.trendChartDateType) { - this.transactionStatisticsFilter.trendChartDateType = filter.trendChartDateType; - changed = true; - } - - if (filter && (isYearMonth(filter.trendChartStartYearMonth) || filter.trendChartStartYearMonth === '') && !isYearMonthEquals(this.transactionStatisticsFilter.trendChartStartYearMonth, filter.trendChartStartYearMonth)) { - this.transactionStatisticsFilter.trendChartStartYearMonth = filter.trendChartStartYearMonth; - changed = true; - } - - if (filter && (isYearMonth(filter.trendChartEndYearMonth) || filter.trendChartEndYearMonth === '') && !isYearMonthEquals(this.transactionStatisticsFilter.trendChartEndYearMonth, filter.trendChartEndYearMonth)) { - this.transactionStatisticsFilter.trendChartEndYearMonth = filter.trendChartEndYearMonth; - changed = true; - } - - if (filter && isObject(filter.filterAccountIds) && !isEquals(this.transactionStatisticsFilter.filterAccountIds, filter.filterAccountIds)) { - this.transactionStatisticsFilter.filterAccountIds = filter.filterAccountIds; - changed = true; - } - - if (filter && isObject(filter.filterCategoryIds) && !isEquals(this.transactionStatisticsFilter.filterCategoryIds, filter.filterCategoryIds)) { - this.transactionStatisticsFilter.filterCategoryIds = filter.filterCategoryIds; - changed = true; - } - - if (filter && isString(filter.tagIds) && this.transactionStatisticsFilter.tagIds !== filter.tagIds) { - this.transactionStatisticsFilter.tagIds = filter.tagIds; - changed = true; - } - - if (filter && isInteger(filter.tagFilterType) && this.transactionStatisticsFilter.tagFilterType !== filter.tagFilterType) { - this.transactionStatisticsFilter.tagFilterType = filter.tagFilterType; - changed = true; - } - - if (filter && isInteger(filter.sortingType) && this.transactionStatisticsFilter.sortingType !== filter.sortingType) { - this.transactionStatisticsFilter.sortingType = filter.sortingType; - changed = true; - } - - return changed; - }, - getTransactionStatisticsPageParams(analysisType, trendDateAggregationType) { - const querys = []; - - querys.push('analysisType=' + analysisType); - querys.push('chartDataType=' + this.transactionStatisticsFilter.chartDataType); - - if (analysisType === StatisticsAnalysisType.CategoricalAnalysis) { - querys.push('chartType=' + this.transactionStatisticsFilter.categoricalChartType); - querys.push('chartDateType=' + this.transactionStatisticsFilter.categoricalChartDateType); - - if (this.transactionStatisticsFilter.categoricalChartDateType === DateRange.Custom.type) { - querys.push('startTime=' + this.transactionStatisticsFilter.categoricalChartStartTime); - querys.push('endTime=' + this.transactionStatisticsFilter.categoricalChartEndTime); - } - } else if (analysisType === StatisticsAnalysisType.TrendAnalysis) { - querys.push('chartType=' + this.transactionStatisticsFilter.trendChartType); - querys.push('chartDateType=' + this.transactionStatisticsFilter.trendChartDateType); - - if (this.transactionStatisticsFilter.trendChartDateType === DateRange.Custom.type) { - querys.push('startTime=' + this.transactionStatisticsFilter.trendChartStartYearMonth); - querys.push('endTime=' + this.transactionStatisticsFilter.trendChartEndYearMonth); - } - - if (trendDateAggregationType !== ChartDateAggregationType.Month.type) { - querys.push('trendDateAggregationType=' + trendDateAggregationType); - } - } - - if (this.transactionStatisticsFilter.filterAccountIds) { - const ids = objectFieldToArrayItem(this.transactionStatisticsFilter.filterAccountIds); - - if (ids && ids.length) { - querys.push('filterAccountIds=' + ids.join(',')); - } - } - - if (this.transactionStatisticsFilter.filterCategoryIds) { - const ids = objectFieldToArrayItem(this.transactionStatisticsFilter.filterCategoryIds); - - if (ids && ids.length) { - querys.push('filterCategoryIds=' + ids.join(',')); - } - } - - if (this.transactionStatisticsFilter.tagIds) { - querys.push('tagIds=' + this.transactionStatisticsFilter.tagIds); - } - - if (this.transactionStatisticsFilter.tagFilterType) { - querys.push('tagFilterType=' + this.transactionStatisticsFilter.tagFilterType); - } - - querys.push('sortingType=' + this.transactionStatisticsFilter.sortingType); - - return querys.join('&'); - }, - getTransactionListPageParams(analysisType, itemId, dateRange) { - const accountsStore = useAccountsStore(); - const transactionCategoriesStore = useTransactionCategoriesStore(); - const querys = []; - - if (this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type) { - querys.push('type=2'); - } else if (this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type) { - querys.push('type=3'); - } - - if (itemId && (this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalAssets.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.AccountTotalLiabilities.type)) { - querys.push('accountIds=' + itemId); - - if (!isObjectEmpty(this.transactionStatisticsFilter.filterCategoryIds)) { - querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, this.transactionStatisticsFilter.filterCategoryIds)); - } - } else if (itemId && (this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type - || this.transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type)) { - querys.push('categoryIds=' + itemId); - - if (!isObjectEmpty(this.transactionStatisticsFilter.filterAccountIds)) { - querys.push('accountIds=' + getFinalAccountIdsByFilteredAccountIds(accountsStore.allAccountsMap, this.transactionStatisticsFilter.filterAccountIds)); - } - } else if (!itemId) { - if (!isObjectEmpty(this.transactionStatisticsFilter.filterCategoryIds)) { - querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, this.transactionStatisticsFilter.filterCategoryIds)); - } - - if (!isObjectEmpty(this.transactionStatisticsFilter.filterAccountIds)) { - querys.push('accountIds=' + getFinalAccountIdsByFilteredAccountIds(accountsStore.allAccountsMap, this.transactionStatisticsFilter.filterAccountIds)); - } - } - - if (this.transactionStatisticsFilter.tagIds) { - querys.push('tagIds=' + this.transactionStatisticsFilter.tagIds); - } - - if (this.transactionStatisticsFilter.tagFilterType) { - querys.push('tagFilterType=' + this.transactionStatisticsFilter.tagFilterType); - } - - if (analysisType === StatisticsAnalysisType.CategoricalAnalysis - && this.transactionStatisticsFilter.chartDataType !== ChartDataType.AccountTotalAssets.type - && this.transactionStatisticsFilter.chartDataType !== ChartDataType.AccountTotalLiabilities.type) { - querys.push('dateType=' + this.transactionStatisticsFilter.categoricalChartDateType); - - if (this.transactionStatisticsFilter.categoricalChartDateType === DateRange.Custom.type) { - querys.push('minTime=' + this.transactionStatisticsFilter.categoricalChartStartTime); - querys.push('maxTime=' + this.transactionStatisticsFilter.categoricalChartEndTime); - } - } else if (analysisType === StatisticsAnalysisType.TrendAnalysis && dateRange) { - querys.push('dateType=' + dateRange.type); - querys.push('minTime=' + dateRange.minTime); - querys.push('maxTime=' + dateRange.maxTime); - } - - return querys.join('&'); - }, - loadCategoricalAnalysis({ force }) { - const self = this; - const settingsStore = useSettingsStore(); - - return new Promise((resolve, reject) => { - services.getTransactionStatistics({ - startTime: self.transactionStatisticsFilter.categoricalChartStartTime, - endTime: self.transactionStatisticsFilter.categoricalChartEndTime, - tagIds: self.transactionStatisticsFilter.tagIds, - tagFilterType: self.transactionStatisticsFilter.tagFilterType, - useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to retrieve transaction statistics' }); - return; - } - - if (self.transactionStatisticsStateInvalid) { - self.updateTransactionStatisticsInvalidState(false); - } - - if (force && data.result && isEquals(self.transactionCategoryStatisticsData, data.result)) { - reject({ message: 'Data is up to date', isUpToDate: true }); - return; - } - - self.transactionCategoryStatisticsData = data.result; - - resolve(data.result); - }).catch(error => { - logger.error('failed to retrieve transaction statistics', error); - - if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else if (!error.processed) { - reject({ message: 'Unable to retrieve transaction statistics' }); - } else { - reject(error); - } - }); - }); - }, - loadTrendAnalysis({ force }) { - const self = this; - const settingsStore = useSettingsStore(); - - return new Promise((resolve, reject) => { - services.getTransactionStatisticsTrends({ - startYearMonth: self.transactionStatisticsFilter.trendChartStartYearMonth, - endYearMonth: self.transactionStatisticsFilter.trendChartEndYearMonth, - tagIds: self.transactionStatisticsFilter.tagIds, - tagFilterType: self.transactionStatisticsFilter.tagFilterType, - useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType - }).then(response => { - const data = response.data; - - if (!data || !data.success || !data.result) { - reject({ message: 'Unable to retrieve transaction statistics' }); - return; - } - - if (self.transactionStatisticsStateInvalid) { - self.updateTransactionStatisticsInvalidState(false); - } - - if (force && data.result && isEquals(self.transactionCategoryTrendsData, data.result)) { - reject({ message: 'Data is up to date', isUpToDate: true }); - return; - } - - self.transactionCategoryTrendsData = data.result; - - resolve(data.result); - }).catch(error => { - logger.error('failed to retrieve transaction statistics', error); - - if (error.response && error.response.data && error.response.data.errorMessage) { - reject({ error: error.response.data }); - } else if (!error.processed) { - reject({ message: 'Unable to retrieve transaction statistics' }); - } else { - reject(error); - } - }); - }); - }, - } -}); diff --git a/src/stores/statistics.ts b/src/stores/statistics.ts new file mode 100644 index 00000000..b8befaa2 --- /dev/null +++ b/src/stores/statistics.ts @@ -0,0 +1,1119 @@ +import { ref, computed } from 'vue'; +import { defineStore } from 'pinia'; + +import { useSettingsStore } from './setting.ts'; +import { useUserStore } from './user.ts'; +import { useAccountsStore } from './account.ts'; +import { useTransactionCategoriesStore } from './transactionCategory.ts'; +import { useExchangeRatesStore } from './exchangeRates.ts'; + +import { type TimeRangeAndDateType, DateRangeScene, DateRange } from '@/core/datetime.ts'; +import { TimezoneTypeForStatistics } from '@/core/timezone.ts'; +import { CategoryType } from '@/core/category.ts'; +import { TransactionTagFilterType } from '@/core/transaction.ts'; +import { + StatisticsAnalysisType, + CategoricalChartType, + TrendChartType, + ChartDataType, + ChartSortingType, + ChartDateAggregationType, + DEFAULT_CATEGORICAL_CHART_DATA_RANGE, + DEFAULT_TREND_CHART_DATA_RANGE +} from '@/core/statistics.ts'; +import { DEFAULT_ACCOUNT_ICON, DEFAULT_CATEGORY_ICON } from '@/consts/icon.ts'; +import { DEFAULT_ACCOUNT_COLOR, DEFAULT_CATEGORY_COLOR } from '@/consts/color.ts'; + +import type { AccountInfoResponse } from '@/models/account.ts'; +import type { TransactionCategoryInfoResponse } from '@/models/transaction_category.ts'; +import type { + TransactionStatisticResponse, + TransactionStatisticResponseItem, + TransactionStatisticResponseWithInfo, + TransactionStatisticResponseItemWithInfo, + TransactionStatisticTrendsResponseItem, + TransactionStatisticTrendsResponseItemWithInfo, + TransactionStatisticDataItemType, + TransactionStatisticDataItemBase, + TransactionCategoricalAnalysisData, + TransactionCategoricalAnalysisDataItem, + TransactionTrendsAnalysisData, + TransactionTrendsAnalysisDataItem, + TransactionTrendsAnalysisDataAmount +} from '@/models/transaction.ts'; + +import { + isEquals, + isNumber, + isString, + isObject, + isInteger, + isYearMonth, + isYearMonthEquals, + isObjectEmpty, + objectFieldToArrayItem +} from '@/lib/common.ts'; +import { getYearAndMonthFromUnixTime, getDateRangeByDateType } from '@/lib/datetime.ts'; +import { getFinalAccountIdsByFilteredAccountIds } from '@/lib/account.ts'; +import { getFinalCategoryIdsByFilteredCategoryIds } from '@/lib/category.ts'; +import { sortStatisticsItems } from '@/lib/statistics.ts'; +import logger from '@/lib/logger.ts'; +import services from '@/lib/services.ts'; + +interface WritableTransactionStatisticResponseItemWithInfo extends TransactionStatisticResponseItemWithInfo { + categoryId: string; + accountId: string; + amount: number; + account?: AccountInfoResponse; + primaryAccount?: AccountInfoResponse; + category?: TransactionCategoryInfoResponse; + primaryCategory?: TransactionCategoryInfoResponse; + amountInDefaultCurrency: number | null; +} + +interface WritableTransactionCategoricalAnalysisData { + totalAmount: number; + totalNonNegativeAmount: number; + items: Record; +} + +interface WritableTransactionCategoricalAnalysisDataItem { + name: string; + type: TransactionStatisticDataItemType; + id: string; + icon: string; + color: string; + hidden: boolean; + displayOrders: number[]; + totalAmount: number; + percent?: number; +} + +interface WritableTransactioTrendsAnalysisDataItem { + name: string; + type: TransactionStatisticDataItemType; + id: string; + icon: string; + color: string; + hidden: boolean; + displayOrders: number[]; + totalAmount: number; + items: TransactionTrendsAnalysisDataAmount[]; +} + +export interface TransactionStatisticsFilter { + chartDataType: number; + categoricalChartType: number; + categoricalChartDateType: number; + categoricalChartStartTime: number; + categoricalChartEndTime: number; + trendChartType: number; + trendChartDateType: number; + trendChartStartYearMonth: string; + trendChartEndYearMonth: string; + filterAccountIds: Record; + filterCategoryIds: Record; + tagIds: string; + tagFilterType: number; + sortingType: number; +} + +export const useStatisticsStore = defineStore('statistics', () => { + const settingsStore = useSettingsStore(); + const userStore = useUserStore(); + const accountsStore = useAccountsStore(); + const transactionCategoriesStore = useTransactionCategoriesStore(); + const exchangeRatesStore = useExchangeRatesStore(); + + const transactionStatisticsFilter = ref({ + chartDataType: ChartDataType.Default.type, + categoricalChartType: CategoricalChartType.Default.type, + categoricalChartDateType: DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type, + categoricalChartStartTime: 0, + categoricalChartEndTime: 0, + trendChartType: TrendChartType.Default.type, + trendChartDateType: DEFAULT_TREND_CHART_DATA_RANGE.type, + trendChartStartYearMonth: '', + trendChartEndYearMonth: '', + filterAccountIds: {}, + filterCategoryIds: {}, + tagIds: '', + tagFilterType: TransactionTagFilterType.Default.type, + sortingType: ChartSortingType.Default.type + }); + + const transactionCategoryStatisticsData = ref(null); + const transactionCategoryTrendsData = ref([]); + const transactionStatisticsStateInvalid = ref(true); + + const categoricalAnalysisChartDataCategory = computed(() => { + if (transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) { + return 'account'; + } else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { + return 'category'; + } else { + return ''; + } + }); + + const transactionCategoryStatisticsDataWithCategoryAndAccountInfo = computed(() => { + const statistics = transactionCategoryStatisticsData.value; + + if (!statistics) { + return null; + } + + const finalStatistics: TransactionStatisticResponseWithInfo = { + startTime: statistics.startTime, + endTime: statistics.endTime, + items: [] + }; + + if (statistics && statistics.items && statistics.items.length) { + finalStatistics.items.push(...assembleAccountAndCategoryInfo(statistics.items)); + } + + return finalStatistics; + }); + + const transactionCategoryTotalAmountAnalysisData = computed(() => { + if (!transactionCategoryStatisticsDataWithCategoryAndAccountInfo.value || !transactionCategoryStatisticsDataWithCategoryAndAccountInfo.value.items) { + return null; + } + + return getCategoryTotalAmountItems(transactionCategoryStatisticsDataWithCategoryAndAccountInfo.value.items, transactionStatisticsFilter.value); + }); + + const accountTotalAmountAnalysisData = computed(() => { + if (!accountsStore.allPlainAccounts) { + return null; + } + + const allDataItems: Record = {}; + let totalAmount = 0; + let totalNonNegativeAmount = 0; + + for (let i = 0; i < accountsStore.allPlainAccounts.length; i++) { + const account = accountsStore.allPlainAccounts[i]; + + if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type) { + if (!account.isAsset) { + continue; + } + } else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) { + if (!account.isLiability) { + continue; + } + } + + if (transactionStatisticsFilter.value.filterAccountIds && transactionStatisticsFilter.value.filterAccountIds[account.id]) { + continue; + } + + let primaryAccount = accountsStore.allAccountsMap[account.parentId]; + + if (!primaryAccount) { + primaryAccount = account; + } + + let amount = account.balance; + + if (account.currency !== userStore.currentUserDefaultCurrency) { + const finalAmount = exchangeRatesStore.getExchangedAmount(amount, account.currency, userStore.currentUserDefaultCurrency); + + if (!isNumber(finalAmount)) { + continue; + } + + amount = Math.floor(finalAmount); + } + + if (account.isLiability) { + amount = -amount; + } + + const data: WritableTransactionCategoricalAnalysisDataItem = { + name: account.name, + type: 'account', + id: account.id, + icon: account.icon || DEFAULT_ACCOUNT_ICON.icon, + color: account.color || DEFAULT_ACCOUNT_COLOR, + hidden: primaryAccount.hidden || account.hidden, + displayOrders: [primaryAccount.category, primaryAccount.displayOrder, account.displayOrder], + totalAmount: amount + }; + + totalAmount += amount; + + if (amount > 0) { + totalNonNegativeAmount += amount; + } + + allDataItems[account.id] = data; + } + + return { + totalAmount: totalAmount, + totalNonNegativeAmount: totalNonNegativeAmount, + items: allDataItems + }; + }); + + const categoricalAnalysisData = computed(() => { + let combinedData: WritableTransactionCategoricalAnalysisData | null = null; + + if (transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { + combinedData = transactionCategoryTotalAmountAnalysisData.value; + } else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type || + transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type) { + combinedData = accountTotalAmountAnalysisData.value; + } + + const allStatisticsItems: TransactionCategoricalAnalysisDataItem[] = []; + + if (combinedData && combinedData.items) { + for (const id in combinedData.items) { + if (!Object.prototype.hasOwnProperty.call(combinedData.items, id)) { + continue; + } + + const dataItem = combinedData.items[id]; + let percent = 0; + + if (dataItem.totalAmount > 0) { + percent = dataItem.totalAmount * 100 / combinedData.totalNonNegativeAmount; + } else { + percent = 0; + } + + if (percent < 0) { + percent = 0; + } + + const statisticDataItem: TransactionCategoricalAnalysisDataItem = { + name: dataItem.name, + type: dataItem.type, + id: dataItem.id, + icon: dataItem.icon, + color: dataItem.color, + hidden: dataItem.hidden, + displayOrders: dataItem.displayOrders, + totalAmount: dataItem.totalAmount, + percent: percent + }; + + allStatisticsItems.push(statisticDataItem); + } + } + + sortCategoryTotalAmountItems(allStatisticsItems, transactionStatisticsFilter.value); + + const statisticData: TransactionCategoricalAnalysisData = { + totalAmount: combinedData?.totalAmount || 0, + items: allStatisticsItems + }; + + return statisticData; + }); + + const transactionCategoryTrendsDataWithCategoryAndAccountInfo = computed(() => { + const trendsData = transactionCategoryTrendsData.value; + const finalTrendsData: TransactionStatisticTrendsResponseItemWithInfo[] = []; + + if (trendsData && trendsData.length) { + for (let i = 0; i < trendsData.length; i++) { + const trendItem = trendsData[i]; + const finalTrendItem: TransactionStatisticTrendsResponseItemWithInfo = { + year: trendItem.year, + month: trendItem.month, + items: [] + }; + + if (trendItem && trendItem.items && trendItem.items.length) { + finalTrendItem.items.push(...assembleAccountAndCategoryInfo(trendItem.items)); + } + + finalTrendsData.push(finalTrendItem); + } + } + + return finalTrendsData; + }); + + const trendsAnalysisData = computed(() => { + if (!transactionCategoryTrendsDataWithCategoryAndAccountInfo.value || !transactionCategoryTrendsDataWithCategoryAndAccountInfo.value.length) { + return null; + } + + const combinedDataMap: Record = {}; + + for (let i = 0; i < transactionCategoryTrendsDataWithCategoryAndAccountInfo.value.length; i++) { + const trendItem = transactionCategoryTrendsDataWithCategoryAndAccountInfo.value[i]; + const totalAmountItems = getCategoryTotalAmountItems(trendItem.items, transactionStatisticsFilter.value); + + for (const id in totalAmountItems.items) { + if (!Object.prototype.hasOwnProperty.call(totalAmountItems.items, id)) { + continue; + } + + const item = totalAmountItems.items[id]; + let combinedData = combinedDataMap[id]; + + if (!combinedData) { + combinedData = { + name: item.name, + type: item.type, + id: item.id, + icon: item.icon, + color: item.color, + hidden: item.hidden, + displayOrders: item.displayOrders, + totalAmount: 0, + items: [] + }; + } + + combinedData.items.push({ + year: trendItem.year, + month: trendItem.month, + totalAmount: item.totalAmount + }); + + combinedData.totalAmount += item.totalAmount; + combinedDataMap[id] = combinedData; + } + } + + const totalAmountsTrends: TransactionTrendsAnalysisDataItem[] = []; + + for (const id in combinedDataMap) { + if (!Object.prototype.hasOwnProperty.call(combinedDataMap, id)) { + continue; + } + + const trendData = combinedDataMap[id]; + totalAmountsTrends.push(trendData); + } + + sortCategoryTotalAmountItems(totalAmountsTrends, transactionStatisticsFilter.value); + + const trendsData: TransactionTrendsAnalysisData = { + items: totalAmountsTrends + }; + + return trendsData; + }); + + function assembleAccountAndCategoryInfo(items: TransactionStatisticResponseItem[]): TransactionStatisticResponseItemWithInfo[] { + const finalItems: TransactionStatisticResponseItemWithInfo[] = []; + const defaultCurrency = userStore.currentUserDefaultCurrency; + + for (let i = 0; i < items.length; i++) { + const dataItem = items[i]; + const item: WritableTransactionStatisticResponseItemWithInfo = { + categoryId: dataItem.categoryId, + accountId: dataItem.accountId, + amount: dataItem.amount, + amountInDefaultCurrency: null + }; + + if (item.accountId) { + item.account = accountsStore.allAccountsMap[item.accountId]; + } + + if (item.account && item.account.parentId !== '0') { + item.primaryAccount = accountsStore.allAccountsMap[item.account.parentId]; + } else { + item.primaryAccount = item.account; + } + + if (item.categoryId) { + item.category = transactionCategoriesStore.allTransactionCategoriesMap[item.categoryId]; + } + + if (item.category && item.category.parentId !== '0') { + item.primaryCategory = transactionCategoriesStore.allTransactionCategoriesMap[item.category.parentId]; + } else { + item.primaryCategory = item.category; + } + + if (item.account && item.account.currency !== defaultCurrency) { + const amount = exchangeRatesStore.getExchangedAmount(item.amount, item.account.currency, defaultCurrency); + + if (isNumber(amount)) { + item.amountInDefaultCurrency = Math.floor(amount); + } + } else if (item.account && item.account.currency === defaultCurrency) { + item.amountInDefaultCurrency = item.amount; + } else { + item.amountInDefaultCurrency = null; + } + + finalItems.push(item); + } + + return finalItems; + } + + function getCategoryTotalAmountItems(items: TransactionStatisticResponseItemWithInfo[], transactionStatisticsFilter: TransactionStatisticsFilter): WritableTransactionCategoricalAnalysisData { + const allDataItems: Record = {}; + let totalAmount = 0; + let totalNonNegativeAmount = 0; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + + if (!item.primaryAccount || !item.account || !item.primaryCategory || !item.category) { + continue; + } + + if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || + transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type) { + if (item.category.type !== CategoryType.Expense) { + continue; + } + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type || + transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type) { + if (item.category.type !== CategoryType.Income) { + continue; + } + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { + // Do Nothing + } else { + continue; + } + + if (transactionStatisticsFilter.filterAccountIds && transactionStatisticsFilter.filterAccountIds[item.account.id]) { + continue; + } + + if (transactionStatisticsFilter.filterCategoryIds && transactionStatisticsFilter.filterCategoryIds[item.category.id]) { + continue; + } + + if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByAccount.type || + transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByAccount.type) { + if (isNumber(item.amountInDefaultCurrency)) { + let data = allDataItems[item.account.id]; + + if (data) { + data.totalAmount += item.amountInDefaultCurrency; + } else { + data = { + name: item.account.name, + type: 'account', + id: item.account.id, + icon: item.account.icon || DEFAULT_ACCOUNT_ICON.icon, + color: item.account.color || DEFAULT_ACCOUNT_COLOR, + hidden: item.primaryAccount.hidden || item.account.hidden, + displayOrders: [item.primaryAccount.category, item.primaryAccount.displayOrder, item.account.displayOrder], + totalAmount: item.amountInDefaultCurrency + }; + } + + totalAmount += item.amountInDefaultCurrency; + + if (item.amountInDefaultCurrency > 0) { + totalNonNegativeAmount += item.amountInDefaultCurrency; + } + + allDataItems[item.account.id] = data; + } + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.IncomeByPrimaryCategory.type) { + if (isNumber(item.amountInDefaultCurrency)) { + let data = allDataItems[item.primaryCategory.id]; + + if (data) { + data.totalAmount += item.amountInDefaultCurrency; + } else { + data = { + name: item.primaryCategory.name, + type: 'category', + id: item.primaryCategory.id, + icon: item.primaryCategory.icon || DEFAULT_CATEGORY_ICON.icon, + color: item.primaryCategory.color || DEFAULT_CATEGORY_COLOR, + hidden: item.primaryCategory.hidden, + displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder], + totalAmount: item.amountInDefaultCurrency + }; + } + + totalAmount += item.amountInDefaultCurrency; + + if (item.amountInDefaultCurrency > 0) { + totalNonNegativeAmount += item.amountInDefaultCurrency; + } + + allDataItems[item.primaryCategory.id] = data; + } + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type || + transactionStatisticsFilter.chartDataType === ChartDataType.IncomeBySecondaryCategory.type) { + if (isNumber(item.amountInDefaultCurrency)) { + let data = allDataItems[item.category.id]; + + if (data) { + data.totalAmount += item.amountInDefaultCurrency; + } else { + data = { + name: item.category.name, + type: 'category', + id: item.category.id, + icon: item.category.icon || DEFAULT_CATEGORY_ICON.icon, + color: item.category.color || DEFAULT_CATEGORY_COLOR, + hidden: item.primaryCategory.hidden || item.category.hidden, + displayOrders: [item.primaryCategory.type, item.primaryCategory.displayOrder, item.category.displayOrder], + totalAmount: item.amountInDefaultCurrency + }; + } + + totalAmount += item.amountInDefaultCurrency; + + if (item.amountInDefaultCurrency > 0) { + totalNonNegativeAmount += item.amountInDefaultCurrency; + } + + allDataItems[item.category.id] = data; + } + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type || + transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type || + transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { + if (isNumber(item.amountInDefaultCurrency)) { + let data = allDataItems['total']; + let amount = item.amountInDefaultCurrency; + + if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type && + item.category.type === CategoryType.Expense) { + amount = -amount; + } + + if (data) { + data.totalAmount += amount; + } else { + let name = ''; + + if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalExpense.type) { + name = ChartDataType.TotalExpense.name; + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalIncome.type) { + name = ChartDataType.TotalIncome.name; + } else if (transactionStatisticsFilter.chartDataType === ChartDataType.TotalBalance.type) { + name = ChartDataType.TotalBalance.name; + } + + data = { + name: name, + type: 'total', + id: 'total', + icon: '', + color: '', + hidden: false, + displayOrders: [1], + totalAmount: amount + }; + } + + totalAmount += amount; + + if (item.amountInDefaultCurrency > 0) { + totalNonNegativeAmount += amount; + } + + allDataItems['total'] = data; + } + } + } + + return { + totalAmount: totalAmount, + totalNonNegativeAmount: totalNonNegativeAmount, + items: allDataItems + }; + } + + function sortCategoryTotalAmountItems(items: TransactionStatisticDataItemBase[], transactionStatisticsFilter: TransactionStatisticsFilter): void { + sortStatisticsItems(items, transactionStatisticsFilter.sortingType); + } + + function updateTransactionStatisticsInvalidState(invalidState: boolean): void { + transactionStatisticsStateInvalid.value = invalidState; + } + + function resetTransactionStatistics(): void { + transactionStatisticsFilter.value.chartDataType = ChartDataType.Default.type; + transactionStatisticsFilter.value.categoricalChartType = CategoricalChartType.Default.type; + transactionStatisticsFilter.value.categoricalChartDateType = DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type; + transactionStatisticsFilter.value.categoricalChartStartTime = 0; + transactionStatisticsFilter.value.categoricalChartEndTime = 0; + transactionStatisticsFilter.value.trendChartType = TrendChartType.Default.type; + transactionStatisticsFilter.value.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type; + transactionStatisticsFilter.value.trendChartStartYearMonth = ''; + transactionStatisticsFilter.value.trendChartEndYearMonth = ''; + transactionStatisticsFilter.value.filterAccountIds = {}; + transactionStatisticsFilter.value.filterCategoryIds = {}; + transactionStatisticsFilter.value.tagIds = ''; + transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type; + transactionCategoryStatisticsData.value = null; + transactionCategoryTrendsData.value = []; + transactionStatisticsStateInvalid.value = true; + } + + function initTransactionStatisticsFilter(analysisType: StatisticsAnalysisType, filter?: TransactionStatisticsFilter): void { + if (filter && isInteger(filter.chartDataType)) { + transactionStatisticsFilter.value.chartDataType = filter.chartDataType; + } else { + transactionStatisticsFilter.value.chartDataType = settingsStore.appSettings.statistics.defaultChartDataType; + } + + if (analysisType === StatisticsAnalysisType.CategoricalAnalysis || analysisType === StatisticsAnalysisType.TrendAnalysis) { + if (!ChartDataType.isAvailableForAnalysisType(transactionStatisticsFilter.value.chartDataType, analysisType)) { + transactionStatisticsFilter.value.chartDataType = ChartDataType.Default.type; + } + } + + if (filter && isInteger(filter.categoricalChartType)) { + transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType; + } else { + transactionStatisticsFilter.value.categoricalChartType = settingsStore.appSettings.statistics.defaultCategoricalChartType; + } + + if (transactionStatisticsFilter.value.categoricalChartType !== CategoricalChartType.Pie.type && transactionStatisticsFilter.value.categoricalChartType !== CategoricalChartType.Bar.type) { + transactionStatisticsFilter.value.categoricalChartType = CategoricalChartType.Default.type; + } + + if (filter && isInteger(filter.categoricalChartDateType)) { + transactionStatisticsFilter.value.categoricalChartDateType = filter.categoricalChartDateType; + } else { + transactionStatisticsFilter.value.categoricalChartDateType = settingsStore.appSettings.statistics.defaultCategoricalChartDataRangeType; + } + + let categoricalChartDateTypeValid = true; + + if (!DateRange.isAvailableForScene(transactionStatisticsFilter.value.categoricalChartDateType, DateRangeScene.Normal)) { + transactionStatisticsFilter.value.categoricalChartDateType = DEFAULT_CATEGORICAL_CHART_DATA_RANGE.type; + categoricalChartDateTypeValid = false; + } + + if (categoricalChartDateTypeValid && transactionStatisticsFilter.value.categoricalChartDateType === DateRange.Custom.type) { + if (filter && isInteger(filter.categoricalChartStartTime)) { + transactionStatisticsFilter.value.categoricalChartStartTime = filter.categoricalChartStartTime; + } else { + transactionStatisticsFilter.value.categoricalChartStartTime = 0; + } + + if (filter && isInteger(filter.categoricalChartEndTime)) { + transactionStatisticsFilter.value.categoricalChartEndTime = filter.categoricalChartEndTime; + } else { + transactionStatisticsFilter.value.categoricalChartEndTime = 0; + } + } else { + const categoricalChartDateRange = getDateRangeByDateType(transactionStatisticsFilter.value.categoricalChartDateType, userStore.currentUserFirstDayOfWeek); + + if (categoricalChartDateRange) { + transactionStatisticsFilter.value.categoricalChartDateType = categoricalChartDateRange.dateType; + transactionStatisticsFilter.value.categoricalChartStartTime = categoricalChartDateRange.minTime; + transactionStatisticsFilter.value.categoricalChartEndTime = categoricalChartDateRange.maxTime; + } + } + + if (filter && isInteger(filter.trendChartType)) { + transactionStatisticsFilter.value.trendChartType = filter.trendChartType; + } else { + transactionStatisticsFilter.value.trendChartType = settingsStore.appSettings.statistics.defaultTrendChartType; + } + + if (transactionStatisticsFilter.value.trendChartType !== TrendChartType.Area.type && transactionStatisticsFilter.value.trendChartType !== TrendChartType.Column.type) { + transactionStatisticsFilter.value.trendChartType = TrendChartType.Default.type; + } + + if (filter && isInteger(filter.trendChartDateType)) { + transactionStatisticsFilter.value.trendChartDateType = filter.trendChartDateType; + } else { + transactionStatisticsFilter.value.trendChartDateType = settingsStore.appSettings.statistics.defaultTrendChartDataRangeType; + } + + let trendChartDateTypeValid = true; + + if (!DateRange.isAvailableForScene(transactionStatisticsFilter.value.trendChartDateType, DateRangeScene.TrendAnalysis)) { + transactionStatisticsFilter.value.trendChartDateType = DEFAULT_TREND_CHART_DATA_RANGE.type; + trendChartDateTypeValid = false; + } + + if (trendChartDateTypeValid && transactionStatisticsFilter.value.trendChartDateType === DateRange.Custom.type) { + if (filter && isYearMonth(filter.trendChartStartYearMonth)) { + transactionStatisticsFilter.value.trendChartStartYearMonth = filter.trendChartStartYearMonth; + } else { + transactionStatisticsFilter.value.trendChartStartYearMonth = ''; + } + + if (filter && isYearMonth(filter.trendChartEndYearMonth)) { + transactionStatisticsFilter.value.trendChartEndYearMonth = filter.trendChartEndYearMonth; + } else { + transactionStatisticsFilter.value.trendChartEndYearMonth = ''; + } + } else { + const trendChartDateRange = getDateRangeByDateType(transactionStatisticsFilter.value.trendChartDateType, userStore.currentUserFirstDayOfWeek); + + if (trendChartDateRange) { + transactionStatisticsFilter.value.trendChartDateType = trendChartDateRange.dateType; + transactionStatisticsFilter.value.trendChartStartYearMonth = getYearAndMonthFromUnixTime(trendChartDateRange.minTime); + transactionStatisticsFilter.value.trendChartEndYearMonth = getYearAndMonthFromUnixTime(trendChartDateRange.maxTime); + } + } + + if (filter && isObject(filter.filterAccountIds)) { + transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds; + } else { + transactionStatisticsFilter.value.filterAccountIds = settingsStore.appSettings.statistics.defaultAccountFilter || {}; + } + + if (filter && isObject(filter.filterCategoryIds)) { + transactionStatisticsFilter.value.filterCategoryIds = filter.filterCategoryIds; + } else { + transactionStatisticsFilter.value.filterCategoryIds = settingsStore.appSettings.statistics.defaultTransactionCategoryFilter || {}; + } + + if (filter && isString(filter.tagIds)) { + transactionStatisticsFilter.value.tagIds = filter.tagIds; + } else { + transactionStatisticsFilter.value.tagIds = ''; + } + + if (filter && isInteger(filter.tagFilterType)) { + transactionStatisticsFilter.value.tagFilterType = filter.tagFilterType; + } else { + transactionStatisticsFilter.value.tagFilterType = TransactionTagFilterType.Default.type; + } + + if (filter && isInteger(filter.sortingType)) { + transactionStatisticsFilter.value.sortingType = filter.sortingType; + } else { + transactionStatisticsFilter.value.sortingType = settingsStore.appSettings.statistics.defaultSortingType; + } + + if (transactionStatisticsFilter.value.sortingType < ChartSortingType.Amount.type || transactionStatisticsFilter.value.sortingType > ChartSortingType.Name.type) { + transactionStatisticsFilter.value.sortingType = ChartSortingType.Default.type; + } + } + + function updateTransactionStatisticsFilter(filter: TransactionStatisticsFilter): boolean { + let changed = false; + + if (filter && isInteger(filter.chartDataType) && transactionStatisticsFilter.value.chartDataType !== filter.chartDataType) { + transactionStatisticsFilter.value.chartDataType = filter.chartDataType; + changed = true; + } + + if (filter && isInteger(filter.categoricalChartType) && transactionStatisticsFilter.value.categoricalChartType !== filter.categoricalChartType) { + transactionStatisticsFilter.value.categoricalChartType = filter.categoricalChartType; + changed = true; + } + + if (filter && isInteger(filter.categoricalChartDateType) && transactionStatisticsFilter.value.categoricalChartDateType !== filter.categoricalChartDateType) { + transactionStatisticsFilter.value.categoricalChartDateType = filter.categoricalChartDateType; + changed = true; + } + + if (filter && isInteger(filter.categoricalChartStartTime) && transactionStatisticsFilter.value.categoricalChartStartTime !== filter.categoricalChartStartTime) { + transactionStatisticsFilter.value.categoricalChartStartTime = filter.categoricalChartStartTime; + changed = true; + } + + if (filter && isInteger(filter.categoricalChartEndTime) && transactionStatisticsFilter.value.categoricalChartEndTime !== filter.categoricalChartEndTime) { + transactionStatisticsFilter.value.categoricalChartEndTime = filter.categoricalChartEndTime; + changed = true; + } + + if (filter && isInteger(filter.trendChartType) && transactionStatisticsFilter.value.trendChartType !== filter.trendChartType) { + transactionStatisticsFilter.value.trendChartType = filter.trendChartType; + changed = true; + } + + if (filter && isInteger(filter.trendChartDateType) && transactionStatisticsFilter.value.trendChartDateType !== filter.trendChartDateType) { + transactionStatisticsFilter.value.trendChartDateType = filter.trendChartDateType; + changed = true; + } + + if (filter && (isYearMonth(filter.trendChartStartYearMonth) || filter.trendChartStartYearMonth === '') && !isYearMonthEquals(transactionStatisticsFilter.value.trendChartStartYearMonth, filter.trendChartStartYearMonth)) { + transactionStatisticsFilter.value.trendChartStartYearMonth = filter.trendChartStartYearMonth; + changed = true; + } + + if (filter && (isYearMonth(filter.trendChartEndYearMonth) || filter.trendChartEndYearMonth === '') && !isYearMonthEquals(transactionStatisticsFilter.value.trendChartEndYearMonth, filter.trendChartEndYearMonth)) { + transactionStatisticsFilter.value.trendChartEndYearMonth = filter.trendChartEndYearMonth; + changed = true; + } + + if (filter && isObject(filter.filterAccountIds) && !isEquals(transactionStatisticsFilter.value.filterAccountIds, filter.filterAccountIds)) { + transactionStatisticsFilter.value.filterAccountIds = filter.filterAccountIds; + changed = true; + } + + if (filter && isObject(filter.filterCategoryIds) && !isEquals(transactionStatisticsFilter.value.filterCategoryIds, filter.filterCategoryIds)) { + transactionStatisticsFilter.value.filterCategoryIds = filter.filterCategoryIds; + changed = true; + } + + if (filter && isString(filter.tagIds) && transactionStatisticsFilter.value.tagIds !== filter.tagIds) { + transactionStatisticsFilter.value.tagIds = filter.tagIds; + changed = true; + } + + if (filter && isInteger(filter.tagFilterType) && transactionStatisticsFilter.value.tagFilterType !== filter.tagFilterType) { + transactionStatisticsFilter.value.tagFilterType = filter.tagFilterType; + changed = true; + } + + if (filter && isInteger(filter.sortingType) && transactionStatisticsFilter.value.sortingType !== filter.sortingType) { + transactionStatisticsFilter.value.sortingType = filter.sortingType; + changed = true; + } + + return changed; + } + + function getTransactionStatisticsPageParams(analysisType: StatisticsAnalysisType, trendDateAggregationType: number): string { + const querys: string[] = []; + + querys.push('analysisType=' + analysisType); + querys.push('chartDataType=' + transactionStatisticsFilter.value.chartDataType); + + if (analysisType === StatisticsAnalysisType.CategoricalAnalysis) { + querys.push('chartType=' + transactionStatisticsFilter.value.categoricalChartType); + querys.push('chartDateType=' + transactionStatisticsFilter.value.categoricalChartDateType); + + if (transactionStatisticsFilter.value.categoricalChartDateType === DateRange.Custom.type) { + querys.push('startTime=' + transactionStatisticsFilter.value.categoricalChartStartTime); + querys.push('endTime=' + transactionStatisticsFilter.value.categoricalChartEndTime); + } + } else if (analysisType === StatisticsAnalysisType.TrendAnalysis) { + querys.push('chartType=' + transactionStatisticsFilter.value.trendChartType); + querys.push('chartDateType=' + transactionStatisticsFilter.value.trendChartDateType); + + if (transactionStatisticsFilter.value.trendChartDateType === DateRange.Custom.type) { + querys.push('startTime=' + transactionStatisticsFilter.value.trendChartStartYearMonth); + querys.push('endTime=' + transactionStatisticsFilter.value.trendChartEndYearMonth); + } + + if (trendDateAggregationType !== ChartDateAggregationType.Month.type) { + querys.push('trendDateAggregationType=' + trendDateAggregationType); + } + } + + if (transactionStatisticsFilter.value.filterAccountIds) { + const ids = objectFieldToArrayItem(transactionStatisticsFilter.value.filterAccountIds); + + if (ids && ids.length) { + querys.push('filterAccountIds=' + ids.join(',')); + } + } + + if (transactionStatisticsFilter.value.filterCategoryIds) { + const ids = objectFieldToArrayItem(transactionStatisticsFilter.value.filterCategoryIds); + + if (ids && ids.length) { + querys.push('filterCategoryIds=' + ids.join(',')); + } + } + + if (transactionStatisticsFilter.value.tagIds) { + querys.push('tagIds=' + transactionStatisticsFilter.value.tagIds); + } + + if (transactionStatisticsFilter.value.tagFilterType) { + querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType); + } + + querys.push('sortingType=' + transactionStatisticsFilter.value.sortingType); + + return querys.join('&'); + } + + function getTransactionListPageParams(analysisType: StatisticsAnalysisType, itemId: string, dateRange: TimeRangeAndDateType): string { + const querys: string[] = []; + + if (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.TotalIncome.type) { + querys.push('type=2'); + } else if (transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.TotalExpense.type) { + querys.push('type=3'); + } + + if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByAccount.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByAccount.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalAssets.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.AccountTotalLiabilities.type)) { + querys.push('accountIds=' + itemId); + + if (!isObjectEmpty(transactionStatisticsFilter.value.filterCategoryIds)) { + querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds)); + } + } else if (itemId && (transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeByPrimaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.IncomeBySecondaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseByPrimaryCategory.type + || transactionStatisticsFilter.value.chartDataType === ChartDataType.ExpenseBySecondaryCategory.type)) { + querys.push('categoryIds=' + itemId); + + if (!isObjectEmpty(transactionStatisticsFilter.value.filterAccountIds)) { + querys.push('accountIds=' + getFinalAccountIdsByFilteredAccountIds(accountsStore.allAccountsMap, transactionStatisticsFilter.value.filterAccountIds)); + } + } else if (!itemId) { + if (!isObjectEmpty(transactionStatisticsFilter.value.filterCategoryIds)) { + querys.push('categoryIds=' + getFinalCategoryIdsByFilteredCategoryIds(transactionCategoriesStore.allTransactionCategoriesMap, transactionStatisticsFilter.value.filterCategoryIds)); + } + + if (!isObjectEmpty(transactionStatisticsFilter.value.filterAccountIds)) { + querys.push('accountIds=' + getFinalAccountIdsByFilteredAccountIds(accountsStore.allAccountsMap, transactionStatisticsFilter.value.filterAccountIds)); + } + } + + if (transactionStatisticsFilter.value.tagIds) { + querys.push('tagIds=' + transactionStatisticsFilter.value.tagIds); + } + + if (transactionStatisticsFilter.value.tagFilterType) { + querys.push('tagFilterType=' + transactionStatisticsFilter.value.tagFilterType); + } + + if (analysisType === StatisticsAnalysisType.CategoricalAnalysis + && transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalAssets.type + && transactionStatisticsFilter.value.chartDataType !== ChartDataType.AccountTotalLiabilities.type) { + querys.push('dateType=' + transactionStatisticsFilter.value.categoricalChartDateType); + + if (transactionStatisticsFilter.value.categoricalChartDateType === DateRange.Custom.type) { + querys.push('minTime=' + transactionStatisticsFilter.value.categoricalChartStartTime); + querys.push('maxTime=' + transactionStatisticsFilter.value.categoricalChartEndTime); + } + } else if (analysisType === StatisticsAnalysisType.TrendAnalysis && dateRange) { + querys.push('dateType=' + dateRange.dateType); + querys.push('minTime=' + dateRange.minTime); + querys.push('maxTime=' + dateRange.maxTime); + } + + return querys.join('&'); + } + + function loadCategoricalAnalysis({ force }: { force: boolean }): Promise { + return new Promise((resolve, reject) => { + services.getTransactionStatistics({ + startTime: transactionStatisticsFilter.value.categoricalChartStartTime, + endTime: transactionStatisticsFilter.value.categoricalChartEndTime, + tagIds: transactionStatisticsFilter.value.tagIds, + tagFilterType: transactionStatisticsFilter.value.tagFilterType, + useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to retrieve transaction statistics' }); + return; + } + + if (transactionStatisticsStateInvalid.value) { + updateTransactionStatisticsInvalidState(false); + } + + if (force && data.result && isEquals(transactionCategoryStatisticsData.value, data.result)) { + reject({ message: 'Data is up to date', isUpToDate: true }); + return; + } + + transactionCategoryStatisticsData.value = data.result; + + resolve(data.result); + }).catch(error => { + logger.error('failed to retrieve transaction statistics', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to retrieve transaction statistics' }); + } else { + reject(error); + } + }); + }); + } + + function loadTrendAnalysis({ force }: { force: boolean }): Promise { + return new Promise((resolve, reject) => { + services.getTransactionStatisticsTrends({ + startYearMonth: transactionStatisticsFilter.value.trendChartStartYearMonth, + endYearMonth: transactionStatisticsFilter.value.trendChartEndYearMonth, + tagIds: transactionStatisticsFilter.value.tagIds, + tagFilterType: transactionStatisticsFilter.value.tagFilterType, + useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType === TimezoneTypeForStatistics.TransactionTimezone.type + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to retrieve transaction statistics' }); + return; + } + + if (transactionStatisticsStateInvalid.value) { + updateTransactionStatisticsInvalidState(false); + } + + if (force && data.result && isEquals(transactionCategoryTrendsData.value, data.result)) { + reject({ message: 'Data is up to date', isUpToDate: true }); + return; + } + + transactionCategoryTrendsData.value = data.result; + + resolve(data.result); + }).catch(error => { + logger.error('failed to retrieve transaction statistics', error); + + if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else if (!error.processed) { + reject({ message: 'Unable to retrieve transaction statistics' }); + } else { + reject(error); + } + }); + }); + } + + return { + // states + transactionStatisticsFilter, + transactionCategoryStatisticsData, + transactionCategoryTrendsData, + transactionStatisticsStateInvalid, + // computed states + categoricalAnalysisChartDataCategory, + categoricalAnalysisData, + trendsAnalysisData, + // functions + updateTransactionStatisticsInvalidState, + resetTransactionStatistics, + initTransactionStatisticsFilter, + updateTransactionStatisticsFilter, + getTransactionStatisticsPageParams, + getTransactionListPageParams, + loadCategoricalAnalysis, + loadTrendAnalysis + }; +}); diff --git a/src/stores/transaction.js b/src/stores/transaction.js index fa7ffebf..4b45c5e0 100644 --- a/src/stores/transaction.js +++ b/src/stores/transaction.js @@ -5,7 +5,7 @@ import { useUserStore } from './user.ts'; import { useAccountsStore } from './account.ts'; import { useTransactionCategoriesStore } from './transactionCategory.ts'; import { useOverviewStore } from './overview.ts'; -import { useStatisticsStore } from './statistics.js'; +import { useStatisticsStore } from './statistics.ts'; import { useExchangeRatesStore } from './exchangeRates.ts'; import { DateRange } from '@/core/datetime.ts'; diff --git a/src/views/base/settings/AppSettingsPageBase.ts b/src/views/base/settings/AppSettingsPageBase.ts index d1948477..74a2f07b 100644 --- a/src/views/base/settings/AppSettingsPageBase.ts +++ b/src/views/base/settings/AppSettingsPageBase.ts @@ -6,8 +6,7 @@ import { useSettingsStore } from '@/stores/setting.ts'; // @ts-expect-error the above file is migrating to ts import { useTransactionsStore } from '@/stores/transaction.js'; import { useOverviewStore } from '@/stores/overview.ts'; -// @ts-expect-error the above file is migrating to ts -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import type { TypeAndDisplayName } from '@/core/base.ts'; import type { LocalizedTimezoneInfo } from '@/core/timezone.ts'; diff --git a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue index e6eee6ab..003b35f5 100644 --- a/src/views/desktop/common/cards/AccountFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/AccountFilterSettingsCard.vue @@ -150,7 +150,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.ts'; import { useAccountsStore } from '@/stores/account.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { AccountType, AccountCategory } from '@/core/account.ts'; import { copyObjectTo } from '@/lib/common.ts'; diff --git a/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue b/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue index 91569b14..53736371 100644 --- a/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/CategoryFilterSettingsCard.vue @@ -147,7 +147,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.ts'; import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { CategoryType } from '@/core/category.ts'; import { copyObjectTo, arrayItemToObjectField } from '@/lib/common.ts'; diff --git a/src/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue b/src/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue index 0d57ee7f..3f618213 100644 --- a/src/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue +++ b/src/views/desktop/common/cards/TransactionTagFilterSettingsCard.vue @@ -134,7 +134,7 @@ import { mapStores } from 'pinia'; import { useTransactionTagsStore } from '@/stores/transactionTag.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { TransactionTagFilterType } from '@/core/transaction.ts'; diff --git a/src/views/desktop/statistics/TransactionPage.vue b/src/views/desktop/statistics/TransactionPage.vue index 82c81b82..0074d005 100644 --- a/src/views/desktop/statistics/TransactionPage.vue +++ b/src/views/desktop/statistics/TransactionPage.vue @@ -323,7 +323,7 @@ import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; import { useAccountsStore } from '@/stores/account.ts'; import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { DateRangeScene, DateRange } from '@/core/datetime.ts'; import { ThemeType } from '@/core/theme.ts'; diff --git a/src/views/desktop/transactions/list/dialogs/ImportDialog.vue b/src/views/desktop/transactions/list/dialogs/ImportDialog.vue index 23b30cf7..2dade676 100644 --- a/src/views/desktop/transactions/list/dialogs/ImportDialog.vue +++ b/src/views/desktop/transactions/list/dialogs/ImportDialog.vue @@ -604,7 +604,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; import { useTransactionTagsStore } from '@/stores/transactionTag.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; import { useOverviewStore } from '@/stores/overview.ts'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; import { CategoryType } from '@/core/category.ts'; diff --git a/src/views/mobile/settings/AccountFilterSettingsPage.vue b/src/views/mobile/settings/AccountFilterSettingsPage.vue index e2d17c28..45c6b00e 100644 --- a/src/views/mobile/settings/AccountFilterSettingsPage.vue +++ b/src/views/mobile/settings/AccountFilterSettingsPage.vue @@ -136,7 +136,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.ts'; import { useAccountsStore } from '@/stores/account.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { AccountType, AccountCategory } from '@/core/account.ts'; import { copyObjectTo } from '@/lib/common.ts'; diff --git a/src/views/mobile/settings/CategoryFilterSettingsPage.vue b/src/views/mobile/settings/CategoryFilterSettingsPage.vue index aafe5945..d0311ab9 100644 --- a/src/views/mobile/settings/CategoryFilterSettingsPage.vue +++ b/src/views/mobile/settings/CategoryFilterSettingsPage.vue @@ -143,7 +143,7 @@ import { mapStores } from 'pinia'; import { useSettingsStore } from '@/stores/setting.ts'; import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { CategoryType } from '@/core/category.ts'; import { copyObjectTo, arrayItemToObjectField } from '@/lib/common.ts'; diff --git a/src/views/mobile/settings/TransactionTagFilterSettingsPage.vue b/src/views/mobile/settings/TransactionTagFilterSettingsPage.vue index 3f2fff95..dbb5ed36 100644 --- a/src/views/mobile/settings/TransactionTagFilterSettingsPage.vue +++ b/src/views/mobile/settings/TransactionTagFilterSettingsPage.vue @@ -114,7 +114,7 @@ import { mapStores } from 'pinia'; import { useTransactionTagsStore } from '@/stores/transactionTag.ts'; import { useTransactionsStore } from '@/stores/transaction.js'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { TransactionTagFilterType } from '@/core/transaction.ts'; diff --git a/src/views/mobile/statistics/TransactionPage.vue b/src/views/mobile/statistics/TransactionPage.vue index f88a2f0c..615681ae 100644 --- a/src/views/mobile/statistics/TransactionPage.vue +++ b/src/views/mobile/statistics/TransactionPage.vue @@ -330,7 +330,7 @@ import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; import { useAccountsStore } from '@/stores/account.ts'; import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts'; -import { useStatisticsStore } from '@/stores/statistics.js'; +import { useStatisticsStore } from '@/stores/statistics.ts'; import { DateRangeScene, DateRange } from '@/core/datetime.ts'; import {