From a604737c7c648e82b5a9e22096207edf7102f5c6 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Fri, 10 Apr 2026 02:26:55 +0800 Subject: [PATCH] support credit card billing cycles as a time granularity option in the account balance trend chart on the account reconciliation statements page --- .../base/AccountBalanceTrendsChartBase.ts | 23 +++++++- .../desktop/AccountBalanceTrendsChart.vue | 4 ++ .../mobile/AccountBalanceTrendsBarChart.vue | 3 +- src/core/statistics.ts | 2 + src/lib/datetime.ts | 55 +++++++++++++++++++ src/lib/statistics.ts | 2 +- src/locales/de.json | 4 +- src/locales/en.json | 4 +- src/locales/es.json | 4 +- src/locales/fr.json | 4 +- src/locales/helpers.ts | 13 ++++- src/locales/it.json | 4 +- src/locales/ja.json | 4 +- src/locales/kn.json | 4 +- src/locales/ko.json | 4 +- src/locales/nl.json | 4 +- src/locales/pt_BR.json | 4 +- src/locales/ru.json | 4 +- src/locales/sl.json | 4 +- src/locales/ta.json | 4 +- src/locales/th.json | 4 +- src/locales/tr.json | 4 +- src/locales/uk.json | 4 +- src/locales/vi.json | 4 +- src/locales/zh_Hans.json | 4 +- src/locales/zh_Hant.json | 4 +- .../ReconciliationStatementPageBase.ts | 4 +- .../StatisticsTransactionPageBase.ts | 4 +- .../dialogs/ReconciliationStatementDialog.vue | 5 ++ .../accounts/ReconciliationStatementPage.vue | 2 + 30 files changed, 163 insertions(+), 30 deletions(-) diff --git a/src/components/base/AccountBalanceTrendsChartBase.ts b/src/components/base/AccountBalanceTrendsChartBase.ts index 341323a7..30807e5f 100644 --- a/src/components/base/AccountBalanceTrendsChartBase.ts +++ b/src/components/base/AccountBalanceTrendsChartBase.ts @@ -8,7 +8,8 @@ import { type YearUnixTime, type YearQuarterUnixTime, type YearMonthUnixTime, - YearMonthDayUnixTime + YearMonthDayUnixTime, + DateRange } from '@/core/datetime.ts'; import type { FiscalYearUnixTime } from '@/core/fiscalyear.ts'; import { ChartDateAggregationType } from '@/core/statistics.ts'; @@ -24,7 +25,9 @@ import { getQuarterFirstTimeTimeBySpecifiedUnixTime, getMonthFirstDateTimeBySpecifiedUnixTime, getDayFirstDateTimeBySpecifiedUnixTime, + getBillingCycleLastUnixTimeBySpecifiedUnixTime, getAllDaysStartAndEndUnixTimes, + getAllBillingCyclesStartAndEndUnixTimes, getFiscalYearStartDateTime } from '@/lib/datetime.ts'; import { TimezoneTypeForStatistics } from '@/core/timezone.ts'; @@ -43,6 +46,7 @@ export interface AccountBalanceTrendsChartItem { dateRangeKey: string; lastYearDateRangeKey: string; displayDate: string; + alternativeDisplayDate: string; openingBalance: number; closingBalance: number; minimumBalance: number; @@ -59,6 +63,7 @@ export interface CommonAccountBalanceTrendsChartProps { timezoneUsedForDateRange: number; fiscalYearStart: number; account: AccountInfoResponse; + statementDate: number | undefined; } export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTrendsChartProps) { @@ -67,11 +72,12 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren formatDateTimeToGregorianLikeShortYear, formatDateTimeToGregorianLikeShortYearMonth, formatDateTimeToGregorianLikeYearQuarter, - formatDateTimeToGregorianLikeFiscalYear + formatDateTimeToGregorianLikeFiscalYear, + formatDateRange } = useI18n(); const showYearOverYearOnTooltip = ref(true); - const showPeriodOverPeriodOnTooltip = computed(() => props.dateAggregationType === ChartDateAggregationType.Day.type || props.dateAggregationType === ChartDateAggregationType.Month.type || props.dateAggregationType === ChartDateAggregationType.Quarter.type); + const showPeriodOverPeriodOnTooltip = computed(() => props.dateAggregationType === ChartDateAggregationType.Day.type || props.dateAggregationType === ChartDateAggregationType.Month.type || props.dateAggregationType === ChartDateAggregationType.Quarter.type || props.dateAggregationType === ChartDateAggregationType.BillingCycle.type); const dataDateRange = computed(() => { if (!props.items || props.items.length < 1) { @@ -116,6 +122,8 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren if (props.dateAggregationType === ChartDateAggregationType.Day.type) { return getAllDaysStartAndEndUnixTimes(dataDateRange.value.minUnixTime, dataDateRange.value.maxUnixTime); + } else if (props.dateAggregationType === ChartDateAggregationType.BillingCycle.type) { + return getAllBillingCyclesStartAndEndUnixTimes(dataDateRange.value.minUnixTime, dataDateRange.value.maxUnixTime, props.statementDate ?? 1); } else { const startYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(dataDateRange.value.minUnixTime); const endYearMonth = getGregorianCalendarYearAndMonthFromUnixTime(dataDateRange.value.maxUnixTime); @@ -156,6 +164,9 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren } else if (props.dateAggregationType === ChartDateAggregationType.Day.type) { minDateTime = getDayFirstDateTimeBySpecifiedUnixTime(dateItem.time, transactionTimeUtfOffset); displayDate = formatDateTimeToShortDate(minDateTime); + } else if (props.dateAggregationType === ChartDateAggregationType.BillingCycle.type) { + minDateTime = getBillingCycleLastUnixTimeBySpecifiedUnixTime(dateItem.time, props.statementDate ?? 1, transactionTimeUtfOffset); + displayDate = formatDateTimeToGregorianLikeShortYearMonth(minDateTime); } else { return ret; } @@ -179,6 +190,7 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren const minDateTime = parseDateTimeFromUnixTime(dateRange.minUnixTime); let displayDate = ''; + let alternativeDisplayDate = ''; if (props.dateAggregationType === ChartDateAggregationType.Year.type) { displayDate = formatDateTimeToGregorianLikeShortYear(minDateTime); @@ -190,6 +202,10 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren displayDate = formatDateTimeToGregorianLikeShortYearMonth(minDateTime); } else if (props.dateAggregationType === ChartDateAggregationType.Day.type) { displayDate = formatDateTimeToShortDate(minDateTime); + } else if (props.dateAggregationType === ChartDateAggregationType.BillingCycle.type) { + const maxDateTime = parseDateTimeFromUnixTime(dateRange.maxUnixTime); + displayDate = formatDateTimeToGregorianLikeShortYearMonth(maxDateTime); + alternativeDisplayDate = formatDateRange(DateRange.Custom.type, dateRange.minUnixTime, dateRange.maxUnixTime); } else { return ret; } @@ -256,6 +272,7 @@ export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTren dateRangeKey: dateRangeKey, lastYearDateRangeKey: lastYearDateRangeKey, displayDate: displayDate, + alternativeDisplayDate: alternativeDisplayDate, openingBalance: lastOpeningBalance, closingBalance: lastClosingBalance, minimumBalance: lastMinimumBalance, diff --git a/src/components/desktop/AccountBalanceTrendsChart.vue b/src/components/desktop/AccountBalanceTrendsChart.vue index 339659e2..d85fd8db 100644 --- a/src/components/desktop/AccountBalanceTrendsChart.vue +++ b/src/components/desktop/AccountBalanceTrendsChart.vue @@ -211,6 +211,10 @@ const chartOptions = computed(() => { let periodOverPeriodDataItemDisplayItems: NameNumeralValue[] | undefined = undefined; let separatorLineIndex: number | undefined = undefined; + if (dataItem.alternativeDisplayDate) { + header = dataItem.alternativeDisplayDate; + } + if (props.type === AccountBalanceTrendChartType.Boxplot.type) { header += ` ${props.legendName}`; displayItems = getBoxplotChartTooltip(dataItem); diff --git a/src/components/mobile/AccountBalanceTrendsBarChart.vue b/src/components/mobile/AccountBalanceTrendsBarChart.vue index 2cc886c2..be82d927 100644 --- a/src/components/mobile/AccountBalanceTrendsBarChart.vue +++ b/src/components/mobile/AccountBalanceTrendsBarChart.vue @@ -27,7 +27,7 @@ :key="item.index" :style="`top: ${virtualDataItems.topPosition}px`" :virtual-list-index="item.index" - :title="item.displayDate" + :title="item.alternativeDisplayDate || item.displayDate" :after="formatAmountToLocalizedNumeralsWithCurrency(item.closingBalance, account.currency)" v-for="item in virtualDataItems.items" > @@ -99,6 +99,7 @@ const allVirtualListItems = computed(() = dateRangeKey: dataItem.dateRangeKey, lastYearDateRangeKey: dataItem.lastYearDateRangeKey, displayDate: dataItem.displayDate, + alternativeDisplayDate: dataItem.alternativeDisplayDate, openingBalance: dataItem.openingBalance, closingBalance: dataItem.closingBalance, medianBalance: dataItem.medianBalance, diff --git a/src/core/statistics.ts b/src/core/statistics.ts index 3f22022d..5649a1d9 100644 --- a/src/core/statistics.ts +++ b/src/core/statistics.ts @@ -236,6 +236,8 @@ export class ChartDateAggregationType { public static readonly Year = new ChartDateAggregationType(2, 'Yearly', 'Aggregate by Year', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends); public static readonly FiscalYear = new ChartDateAggregationType(3, 'FiscalYearly', 'Aggregate by Fiscal Year', StatisticsAnalysisType.TrendAnalysis, StatisticsAnalysisType.AssetTrends); + public static readonly BillingCycle = new ChartDateAggregationType(11, 'BillingCycle', 'Aggregate by Billing Cycle'); + public static readonly Default = ChartDateAggregationType.Month; public readonly type: number; diff --git a/src/lib/datetime.ts b/src/lib/datetime.ts index 8cdfc9c7..10eb4747 100644 --- a/src/lib/datetime.ts +++ b/src/lib/datetime.ts @@ -890,6 +890,26 @@ export function getDayLastDateTimeBySpecifiedUnixTime(unixTime: number, utcOffse return getDayFirstDateTimeBySpecifiedUnixTime(unixTime, utcOffset).add(1, 'days').subtract(1, 'seconds'); } +export function getBillingCycleFirstUnixTimeBySpecifiedUnixTime(unixTime: number, statementDate: number, utcOffset?: number): DateTime { + let date = moment.unix(unixTime); + + if (isNumber(utcOffset)) { + date = date.tz(getFixedTimezoneName(utcOffset)); + } + + if (date.date() > statementDate) { + date = date.set({ date: statementDate + 1, hour: 0, minute: 0, second: 0, millisecond: 0 }); + } else { + date = date.set({ date: statementDate, hour: 0, minute: 0, second: 0, millisecond: 0 }).add(-1, 'months').add(1, 'days'); + } + + return MomentDateTime.of(date); +} + +export function getBillingCycleLastUnixTimeBySpecifiedUnixTime(unixTime: number, statementDate: number, utcOffset?: number): DateTime { + return getBillingCycleFirstUnixTimeBySpecifiedUnixTime(unixTime, statementDate, utcOffset).add(1, 'months').subtract(1, 'seconds'); +} + export function getYearFirstUnixTime(year: number): number { return moment().set({ year: year, month: 0, date: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix(); } @@ -1106,6 +1126,41 @@ export function getAllMonthsStartAndEndUnixTimes(startYearMonth: Year0BasedMonth return allYearMonthTimes; } +export function getAllBillingCyclesStartAndEndUnixTimes(startUnixTime: number, endUnixTime: number, statementDate: number): YearMonthUnixTime[] { + const allYearMonthTimes: YearMonthUnixTime[] = []; + + if (!startUnixTime || !endUnixTime) { + return allYearMonthTimes; + } + + let unixTime: number = startUnixTime; + + while (unixTime <= endUnixTime) { + const currentDateTime = parseDateTimeFromUnixTime(unixTime); + let currentBillingCycleMinDateTime: DateTime; + + if (currentDateTime.getGregorianCalendarDay() > statementDate) { + const currentMonthMinDateTime = getMonthFirstDateTimeBySpecifiedUnixTime(unixTime); + currentBillingCycleMinDateTime = currentMonthMinDateTime.add(statementDate, 'days'); + } else { + const currentMonthMinDateTime = getMonthFirstDateTimeBySpecifiedUnixTime(unixTime); + const previousMonthMinDateTime = currentMonthMinDateTime.add(-1, 'months'); + currentBillingCycleMinDateTime = previousMonthMinDateTime.add(statementDate, 'days'); + } + + const currentBillingCycleMaxDateTime = currentBillingCycleMinDateTime.add(1, 'months').subtract(1, 'seconds'); + const yearMonth: Year0BasedMonth = { + year: currentBillingCycleMaxDateTime.getGregorianCalendarYear(), + month0base: currentBillingCycleMaxDateTime.getGregorianCalendarMonth() - 1 + }; + + allYearMonthTimes.push(YearMonthUnixTime.of(yearMonth, currentBillingCycleMinDateTime.getUnixTime(), currentBillingCycleMaxDateTime.getUnixTime())); + unixTime = currentBillingCycleMaxDateTime.getUnixTime() + 1; + } + + return allYearMonthTimes; +} + export function getAllDaysStartAndEndUnixTimes(startUnixTime: number, endUnixTime: number): YearMonthDayUnixTime[] { const allYearMonthDayTimes: YearMonthDayUnixTime[] = []; diff --git a/src/lib/statistics.ts b/src/lib/statistics.ts index 9b8b18ea..66dfeee8 100644 --- a/src/lib/statistics.ts +++ b/src/lib/statistics.ts @@ -109,7 +109,7 @@ export function getDateRangeKeyWithYearOffset(dateRange: YearUnixTime | FiscalYe return (dateRange.year + (yearOffset ?? 0)).toString(); } else if (dateAggregationType === ChartDateAggregationType.Quarter.type && 'quarter' in dateRange) { return `${dateRange.year + (yearOffset ?? 0)}-${dateRange.quarter}`; - } else if (dateAggregationType === ChartDateAggregationType.Month.type && 'month0base' in dateRange) { + } else if ((dateAggregationType === ChartDateAggregationType.Month.type || dateAggregationType === ChartDateAggregationType.BillingCycle.type) && 'month0base' in dateRange) { return `${dateRange.year + (yearOffset ?? 0)}-${dateRange.month0base + 1}`; } else if (dateAggregationType === ChartDateAggregationType.Day.type && 'day' in dateRange) { return `${dateRange.year + (yearOffset ?? 0)}-${dateRange.month}-${dateRange.day}`; diff --git a/src/locales/de.json b/src/locales/de.json index 49c73fb9..7ef0bd08 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -294,7 +294,8 @@ "Yearly": "Jährlich", "Quarterly": "Vierteljährlich", "Monthly": "Monatlich", - "Daily": "Täglich" + "Daily": "Täglich", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Westlich-arabische Ziffern", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Nach Quartal aggregieren", "Aggregate by Year": "Nach Jahr aggregieren", "Aggregate by Fiscal Year": "Nach Geschäftsjahr aggregieren", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Jahresvergleich", "Period-over-Period": "Periodenvergleich", "Filter Accounts": "Konten filtern", diff --git a/src/locales/en.json b/src/locales/en.json index d9c1fcc6..49940fdd 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -294,7 +294,8 @@ "Yearly": "Yearly", "Quarterly": "Quarterly", "Monthly": "Monthly", - "Daily": "Daily" + "Daily": "Daily", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Aggregate by Quarter", "Aggregate by Year": "Aggregate by Year", "Aggregate by Fiscal Year": "Aggregate by Fiscal Year", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filter Accounts", diff --git a/src/locales/es.json b/src/locales/es.json index fbfcb5d7..b1fbfc4c 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -294,7 +294,8 @@ "Yearly": "Anual", "Quarterly": "Trimestral", "Monthly": "Mensual", - "Daily": "Diario" + "Daily": "Diario", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Cifras Arábigas Occidentales", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Agregado por Trimestre", "Aggregate by Year": "Agregado por Año", "Aggregate by Fiscal Year": "Agregado por Año Fiscal", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filtrar cuentas", diff --git a/src/locales/fr.json b/src/locales/fr.json index a04374c9..1c6cb84c 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -294,7 +294,8 @@ "Yearly": "Annuel", "Quarterly": "Trimestriel", "Monthly": "Mensuel", - "Daily": "Quotidien" + "Daily": "Quotidien", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Chiffres arabes occidentaux", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Agréger par trimestre", "Aggregate by Year": "Agréger par année", "Aggregate by Fiscal Year": "Agréger par année fiscale", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filtrer les comptes", diff --git a/src/locales/helpers.ts b/src/locales/helpers.ts index 4989767f..d01574fa 100644 --- a/src/locales/helpers.ts +++ b/src/locales/helpers.ts @@ -634,7 +634,7 @@ export function useI18n() { return ret; } - function getLocalizedChartDateAggregationTypeAndDisplayName(analysisType: StatisticsAnalysisType, fullName: boolean): TypeAndDisplayName[] { + function getLocalizedChartDateAggregationTypeAndDisplayName(analysisType: StatisticsAnalysisType, fullName: boolean, includeBillingCycle: boolean): TypeAndDisplayName[] { const ret: TypeAndDisplayName[] = []; const allTypes: ChartDateAggregationType[] = ChartDateAggregationType.values(analysisType); @@ -645,6 +645,13 @@ export function useI18n() { }); } + if (includeBillingCycle) { + ret.push({ + type: ChartDateAggregationType.BillingCycle.type, + displayName: t(fullName ? ChartDateAggregationType.BillingCycle.fullName : `granularity.${ChartDateAggregationType.BillingCycle.shortName}`) + }); + } + return ret; } @@ -2432,8 +2439,8 @@ export function useI18n() { getAllAccountBalanceTrendChartTypes: () => getLocalizedDisplayNameAndType(AccountBalanceTrendChartType.values()), getAllStatisticsChartDataTypes: (analysisType: StatisticsAnalysisType, withDesktopOnlyChart?: boolean) => getLocalizedDisplayNameAndType(ChartDataType.values(analysisType, withDesktopOnlyChart)), getAllStatisticsSortingTypes: (useAlternativeName?: boolean) => getLocalizedDisplayNameAndType(ChartSortingType.values(), useAlternativeName), - getAllStatisticsDateAggregationTypes: (analysisType: StatisticsAnalysisType) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, true), - getAllStatisticsDateAggregationTypesWithShortName: (analysisType: StatisticsAnalysisType) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, false), + getAllStatisticsDateAggregationTypes: (analysisType: StatisticsAnalysisType, includeBillingCycle: boolean) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, true, includeBillingCycle), + getAllStatisticsDateAggregationTypesWithShortName: (analysisType: StatisticsAnalysisType, includeBillingCycle: boolean) => getLocalizedChartDateAggregationTypeAndDisplayName(analysisType, false, includeBillingCycle), getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()), getAllTransactionQuickSaveButtonStyles: () => getLocalizedDisplayNameAndType(TransactionQuickSaveButtonStyle.values()), getAllTransactionQuickAddButtonActionTypes: () => getLocalizedDisplayNameAndType(TransactionQuickAddButtonActionType.values()), diff --git a/src/locales/it.json b/src/locales/it.json index 78d22972..25e96f15 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -294,7 +294,8 @@ "Yearly": "Yearly", "Quarterly": "Quarterly", "Monthly": "Monthly", - "Daily": "Daily" + "Daily": "Daily", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Aggrega per trimestre", "Aggregate by Year": "Aggrega per anno", "Aggregate by Fiscal Year": "Aggregate by Fiscal Year", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filtra conti", diff --git a/src/locales/ja.json b/src/locales/ja.json index 88946cb0..d5ea9dd5 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -294,7 +294,8 @@ "Yearly": "Yearly", "Quarterly": "Quarterly", "Monthly": "Monthly", - "Daily": "Daily" + "Daily": "Daily", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "四半期ごとに集計", "Aggregate by Year": "年ごとに集計", "Aggregate by Fiscal Year": "Aggregate by Fiscal Year", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "口座で絞り込み", diff --git a/src/locales/kn.json b/src/locales/kn.json index 7d0e3a2f..9c070f5c 100644 --- a/src/locales/kn.json +++ b/src/locales/kn.json @@ -294,7 +294,8 @@ "Yearly": "ವಾರ್ಷಿಕ", "Quarterly": "ತ್ರೈಮಾಸಿಕ", "Monthly": "ಮಾಸಿಕ", - "Daily": "ದೈನಂದಿನ" + "Daily": "ದೈನಂದಿನ", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "ತ್ರೈಮಾಸಿಕ ಒಕ್ಕೂಟ", "Aggregate by Year": "ವರ್ಷವಾರು ಒಕ್ಕೂಟ", "Aggregate by Fiscal Year": "ಹಣಕಾಸು ವರ್ಷವಾರು ಒಕ್ಕೂಟ", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "ಖಾತೆಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ", diff --git a/src/locales/ko.json b/src/locales/ko.json index bd54ff0a..2cc4b1c9 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -294,7 +294,8 @@ "Yearly": "연간", "Quarterly": "분기별", "Monthly": "월간", - "Daily": "일간" + "Daily": "일간", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "서양 아라비아 숫자", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "분기별 집계", "Aggregate by Year": "연도별 집계", "Aggregate by Fiscal Year": "회계 연도별 집계", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "계좌 필터", diff --git a/src/locales/nl.json b/src/locales/nl.json index 5c56a974..8bbc0189 100644 --- a/src/locales/nl.json +++ b/src/locales/nl.json @@ -294,7 +294,8 @@ "Yearly": "Jaarlijks", "Quarterly": "Per kwartaal", "Monthly": "Maandelijks", - "Daily": "Dagelijks" + "Daily": "Dagelijks", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Groeperen per kwartaal", "Aggregate by Year": "Groeperen per jaar", "Aggregate by Fiscal Year": "Groeperen per boekjaar", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Rekeningen filteren", diff --git a/src/locales/pt_BR.json b/src/locales/pt_BR.json index 06212ca7..0262c98c 100644 --- a/src/locales/pt_BR.json +++ b/src/locales/pt_BR.json @@ -294,7 +294,8 @@ "Yearly": "Anual", "Quarterly": "Trimestral", "Monthly": "Mensal", - "Daily": "Diário" + "Daily": "Diário", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Algarismos Arábicos Ocidentais", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Agregar por Trimestre", "Aggregate by Year": "Agregar por Ano", "Aggregate by Fiscal Year": "Agregar por Ano Fiscal", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filtrar Contas", diff --git a/src/locales/ru.json b/src/locales/ru.json index 4aa87634..7165b8ae 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -294,7 +294,8 @@ "Yearly": "Ежегодно", "Quarterly": "Ежеквартально", "Monthly": "Ежемесячно", - "Daily": "Ежедневно" + "Daily": "Ежедневно", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Западно арабские цифры", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Агрегировать по кварталам", "Aggregate by Year": "Агрегировать по годам", "Aggregate by Fiscal Year": "Агрегировать по фискальным годам", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Фильтровать счета", diff --git a/src/locales/sl.json b/src/locales/sl.json index 007673e7..d0dbee99 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -294,7 +294,8 @@ "Yearly": "Letno", "Quarterly": "Četrtletno", "Monthly": "Mesečno", - "Daily": "Dnevno" + "Daily": "Dnevno", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Zahodne arabske številke", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Združi po četrtletjih", "Aggregate by Year": "Združi po letih", "Aggregate by Fiscal Year": "Združi po fiskalnih letih", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Filtriraj račune", diff --git a/src/locales/ta.json b/src/locales/ta.json index 06eb1438..af5e1c58 100644 --- a/src/locales/ta.json +++ b/src/locales/ta.json @@ -294,7 +294,8 @@ "Yearly": "ஆண்டுவாரியாக", "Quarterly": "காலாண்டு வாரியாக", "Monthly": "மாதாந்திர", - "Daily": "தினசரி" + "Daily": "தினசரி", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "மேற்கத்திய அரபு எண்கள்", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "காலாண்டு கூட்டமைப்பு", "Aggregate by Year": "ஆண்டுவாரி கூட்டமைப்பு", "Aggregate by Fiscal Year": "நிதி ஆண்டுவாரி கூட்டமைப்பு", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "கணக்குகளை வடிகட்டி செய்", diff --git a/src/locales/th.json b/src/locales/th.json index 392a0078..6bc530d2 100644 --- a/src/locales/th.json +++ b/src/locales/th.json @@ -294,7 +294,8 @@ "Yearly": "รายปี", "Quarterly": "รายไตรมาส", "Monthly": "รายเดือน", - "Daily": "รายวัน" + "Daily": "รายวัน", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "เลขอารบิกตะวันตก", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "รวมตามไตรมาส", "Aggregate by Year": "รวมตามปี", "Aggregate by Fiscal Year": "รวมตามปีงบประมาณ", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "กรองบัญชี", diff --git a/src/locales/tr.json b/src/locales/tr.json index ebcd7e2f..f6bbff2f 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -294,7 +294,8 @@ "Yearly": "Yıllık", "Quarterly": "Çeyreklik", "Monthly": "Aylık", - "Daily": "Günlük" + "Daily": "Günlük", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Batı Arap Rakamları", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Çeyreğe Göre Topla", "Aggregate by Year": "Yıla Göre Topla", "Aggregate by Fiscal Year": "Mali Yıla Göre Topla", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Hesapları Filtrele", diff --git a/src/locales/uk.json b/src/locales/uk.json index 2f3f6e1b..fe2325e6 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -294,7 +294,8 @@ "Yearly": "Yearly", "Quarterly": "Quarterly", "Monthly": "Monthly", - "Daily": "Daily" + "Daily": "Daily", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Агрегувати за кварталами", "Aggregate by Year": "Агрегувати за роками", "Aggregate by Fiscal Year": "Aggregate by Fiscal Year", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Фільтрувати рахунки", diff --git a/src/locales/vi.json b/src/locales/vi.json index 6b7d5597..9106b0a2 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -294,7 +294,8 @@ "Yearly": "Yearly", "Quarterly": "Quarterly", "Monthly": "Monthly", - "Daily": "Daily" + "Daily": "Daily", + "BillingCycle": "Billing Cycle" }, "numeral": { "Western Arabic Numerals": "Western Arabic Numerals", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "Tổng hợp theo quý", "Aggregate by Year": "Tổng hợp theo năm", "Aggregate by Fiscal Year": "Aggregate by Fiscal Year", + "Aggregate by Billing Cycle": "Aggregate by Billing Cycle", "Year-over-Year": "Year-over-Year", "Period-over-Period": "Period-over-Period", "Filter Accounts": "Lọc tài khoản", diff --git a/src/locales/zh_Hans.json b/src/locales/zh_Hans.json index 27ebbabf..133641e4 100644 --- a/src/locales/zh_Hans.json +++ b/src/locales/zh_Hans.json @@ -294,7 +294,8 @@ "Yearly": "按年", "Quarterly": "按季度", "Monthly": "按月", - "Daily": "按天" + "Daily": "按天", + "BillingCycle": "按账单周期" }, "numeral": { "Western Arabic Numerals": "阿拉伯数字", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "按季度聚合", "Aggregate by Year": "按年聚合", "Aggregate by Fiscal Year": "按财年聚合", + "Aggregate by Billing Cycle": "按账单周期聚合", "Year-over-Year": "同比", "Period-over-Period": "环比", "Filter Accounts": "过滤账户", diff --git a/src/locales/zh_Hant.json b/src/locales/zh_Hant.json index 076d8890..2b299c42 100644 --- a/src/locales/zh_Hant.json +++ b/src/locales/zh_Hant.json @@ -294,7 +294,8 @@ "Yearly": "依年份", "Quarterly": "依季度", "Monthly": "依月份", - "Daily": "依日期" + "Daily": "依日期", + "BillingCycle": "依帳單週期" }, "numeral": { "Western Arabic Numerals": "阿拉伯數字", @@ -2210,6 +2211,7 @@ "Aggregate by Quarter": "依季度彙整", "Aggregate by Year": "依年份彙整", "Aggregate by Fiscal Year": "依財年彙整", + "Aggregate by Billing Cycle": "依帳單週期彙整", "Year-over-Year": "同比", "Period-over-Period": "環比", "Filter Accounts": "篩選帳戶", diff --git a/src/views/base/accounts/ReconciliationStatementPageBase.ts b/src/views/base/accounts/ReconciliationStatementPageBase.ts index d4e95844..f1226bf7 100644 --- a/src/views/base/accounts/ReconciliationStatementPageBase.ts +++ b/src/views/base/accounts/ReconciliationStatementPageBase.ts @@ -63,11 +63,12 @@ export function useReconciliationStatementPageBase() { const defaultCurrency = computed(() => userStore.currentUserDefaultCurrency); const allChartTypes = computed(() => getAllAccountBalanceTrendChartTypes()); - const allDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypesWithShortName(StatisticsAnalysisType.AssetTrends)); + const allDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypesWithShortName(StatisticsAnalysisType.AssetTrends, !!currentAccountStatementDate.value)); const allTimezoneTypesUsedForDateRange = computed(() => getAllTimezoneTypesUsedForStatistics()); const currentAccount = computed(() => allAccountsMap.value[accountId.value]); const currentAccountCurrency = computed(() => currentAccount.value?.currency ?? defaultCurrency.value); + const currentAccountStatementDate = computed(() => accountsStore.getAccountStatementDate(accountId.value) || undefined); const isCurrentLiabilityAccount = computed(() => currentAccount.value?.isLiability ?? false); const exportFileName = computed(() => { @@ -309,6 +310,7 @@ export function useReconciliationStatementPageBase() { allTimezoneTypesUsedForDateRange, currentAccount, currentAccountCurrency, + currentAccountStatementDate, isCurrentLiabilityAccount, exportFileName, displayStartDateTime, diff --git a/src/views/base/statistics/StatisticsTransactionPageBase.ts b/src/views/base/statistics/StatisticsTransactionPageBase.ts index 211eae86..b686a78c 100644 --- a/src/views/base/statistics/StatisticsTransactionPageBase.ts +++ b/src/views/base/statistics/StatisticsTransactionPageBase.ts @@ -73,8 +73,8 @@ export function useStatisticsTransactionPageBase() { } }); const allSortingTypes = computed(() => getAllStatisticsSortingTypes()); - const allTrendAnalysisDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.TrendAnalysis)); - const allAssetTrendsDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.AssetTrends)); + const allTrendAnalysisDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.TrendAnalysis, false)); + const allAssetTrendsDateAggregationTypes = computed(() => getAllStatisticsDateAggregationTypes(StatisticsAnalysisType.AssetTrends, false)); const query = computed(() => statisticsStore.transactionStatisticsFilter); const queryChartDataCategory = computed(() => statisticsStore.categoricalAnalysisChartDataCategory); diff --git a/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue b/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue index 3fd3fcca..77f2e80a 100644 --- a/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue +++ b/src/views/desktop/accounts/list/dialogs/ReconciliationStatementDialog.vue @@ -246,6 +246,7 @@ :items="[]" :legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')" :account="currentAccount" + :statement-date="currentAccountStatementDate" :skeleton="true" v-if="showAccountBalanceTrendsCharts && loading" /> @@ -258,6 +259,7 @@ :items="reconciliationStatements?.transactions" :legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')" :account="currentAccount" + :statement-date="currentAccountStatementDate" v-if="showAccountBalanceTrendsCharts && !loading" /> @@ -320,6 +322,7 @@ import { mdiChartWaterfall, mdiCalendarTodayOutline, mdiCalendarMonthOutline, + mdiCalendarTextOutline, mdiHomeClockOutline, mdiInvoiceTextClockOutline, mdiLayersTripleOutline, @@ -353,6 +356,7 @@ const { allTimezoneTypesUsedForDateRange, currentAccount, currentAccountCurrency, + currentAccountStatementDate, isCurrentLiabilityAccount, exportFileName, displayStartDateTime, @@ -391,6 +395,7 @@ const chartDataDateAggregationTypeIconMap = { [ChartDateAggregationType.Quarter.type]: mdiLayersTripleOutline, [ChartDateAggregationType.Year.type]: mdiLayersTripleOutline, [ChartDateAggregationType.FiscalYear.type]: mdiLayersTripleOutline, + [ChartDateAggregationType.BillingCycle.type]: mdiCalendarTextOutline, }; const timezoneTypeIconMap = { diff --git a/src/views/mobile/accounts/ReconciliationStatementPage.vue b/src/views/mobile/accounts/ReconciliationStatementPage.vue index 6f245ae5..a4741bd6 100644 --- a/src/views/mobile/accounts/ReconciliationStatementPage.vue +++ b/src/views/mobile/accounts/ReconciliationStatementPage.vue @@ -277,6 +277,7 @@ :fiscal-year-start="fiscalYearStart" :items="reconciliationStatements?.transactions" :account="currentAccount" + :statement-date="currentAccountStatementDate" /> @@ -424,6 +425,7 @@ const { isCurrentLiabilityAccount, currentAccount, currentAccountCurrency, + currentAccountStatementDate, displayStartDateTime, displayEndDateTime, displayTotalInflows,