Skip to content

Commit

Permalink
add billing cycle date range filter
Browse files Browse the repository at this point in the history
  • Loading branch information
mayswind committed Dec 16, 2024
1 parent 8fdbb39 commit 647cd3c
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 23 deletions.
25 changes: 24 additions & 1 deletion src/consts/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,22 @@ const allDateRanges = {
[allDateRangeScenes.TrendAnalysis]: true
}
},
PreviousBillingCycle: {
type: 51,
name: 'Previous Billing Cycle',
isBillingCycle: true,
availableScenes: {
[allDateRangeScenes.Normal]: true
}
},
CurrentBillingCycle: {
type: 52,
name: 'Current Billing Cycle',
isBillingCycle: true,
availableScenes: {
[allDateRangeScenes.Normal]: true
}
},
RecentTwelveMonths: {
type: 101,
name: 'Recent 12 months',
Expand Down Expand Up @@ -316,7 +332,8 @@ const allDateRangesMap = {
[allDateRanges.LastMonth.type]: allDateRanges.LastMonth,
[allDateRanges.ThisYear.type]: allDateRanges.ThisYear,
[allDateRanges.LastYear.type]: allDateRanges.LastYear,
[allDateRanges.RecentTwelveMonths.type]: allDateRanges.RecentTwelveMonths,
[allDateRanges.PreviousBillingCycle.type]: allDateRanges.PreviousBillingCycle,
[allDateRanges.CurrentBillingCycle.type]: allDateRanges.CurrentBillingCycle,
[allDateRanges.RecentTwentyFourMonths.type]: allDateRanges.RecentTwentyFourMonths,
[allDateRanges.RecentThirtySixMonths.type]: allDateRanges.RecentThirtySixMonths,
[allDateRanges.RecentTwoYears.type]: allDateRanges.RecentTwoYears,
Expand All @@ -325,6 +342,11 @@ const allDateRangesMap = {
[allDateRanges.Custom.type]: allDateRanges.Custom
};

const allBillingCycleDateRangesMap = {
[allDateRanges.PreviousBillingCycle.type]: allDateRanges.PreviousBillingCycle,
[allDateRanges.CurrentBillingCycle.type]: allDateRanges.CurrentBillingCycle
};

const defaultFirstDayOfWeek = allWeekDays.Sunday.type;
const defaultLongDateFormat = allLongDateFormat.YYYYMMDD;
const defaultShortDateFormat = allShortDateFormat.YYYYMMDD;
Expand All @@ -349,6 +371,7 @@ export default {
allDateRangeScenes: allDateRangeScenes,
allDateRanges: allDateRanges,
allDateRangesMap: allDateRangesMap,
allBillingCycleDateRangesMap: allBillingCycleDateRangesMap,
defaultFirstDayOfWeek: defaultFirstDayOfWeek,
defaultLongDateFormat: defaultLongDateFormat,
defaultShortDateFormat: defaultShortDateFormat,
Expand Down
65 changes: 65 additions & 0 deletions src/lib/datetime.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ export function getCurrentYear() {
return moment().year();
}

export function getCurrentDay() {
return moment().date();
}

export function parseDateFromUnixTime(unixTime, utcOffset, currentUtcOffset) {
if (isNumber(utcOffset)) {
if (!isNumber(currentUtcOffset)) {
Expand Down Expand Up @@ -270,6 +274,14 @@ export function getThisMonthLastUnixTime() {
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
}

export function getThisMonthSpecifiedDayFirstUnixTime(date) {
return moment().set({ date: date, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
}

export function getThisMonthSpecifiedDayLastUnixTime(date) {
return moment.unix(getThisMonthSpecifiedDayFirstUnixTime(date)).add(1, 'days').subtract(1, 'seconds').unix();
}

export function getThisYearFirstUnixTime() {
const today = moment.unix(getTodayFirstUnixTime());
return today.subtract(today.dayOfYear() - 1, 'days').unix();
Expand Down Expand Up @@ -476,6 +488,16 @@ export function getShiftedDateRangeAndDateType(minTime, maxTime, scale, firstDay
};
}

export function getShiftedDateRangeAndDateTypeForBillingCycle(dateType, scale, firstDayOfWeek, statementDate) {
if (dateType === dateTimeConstants.allDateRanges.PreviousBillingCycle.type && scale === 1) {
return getDateRangeByBillingCycleDateType(dateTimeConstants.allDateRanges.CurrentBillingCycle.type, firstDayOfWeek, statementDate);
} else if (dateType === dateTimeConstants.allDateRanges.CurrentBillingCycle.type && scale === -1) {
return getDateRangeByBillingCycleDateType(dateTimeConstants.allDateRanges.PreviousBillingCycle.type, firstDayOfWeek, statementDate);
}

return null;
}

export function getDateTypeByDateRange(minTime, maxTime, firstDayOfWeek, scene) {
let newDateType = dateTimeConstants.allDateRanges.Custom.type;

Expand Down Expand Up @@ -567,6 +589,49 @@ export function getDateRangeByDateType(dateType, firstDayOfWeek) {
};
}

export function getDateRangeByBillingCycleDateType(dateType, firstDayOfWeek, statementDate) {
let maxTime = 0;
let minTime = 0;

if (dateType === dateTimeConstants.allDateRanges.PreviousBillingCycle.type || dateType === dateTimeConstants.allDateRanges.CurrentBillingCycle.type) { // Previous Billing Cycle | Current Billing Cycle
if (statementDate) {
if (getCurrentDay() <= statementDate) {
maxTime = getThisMonthSpecifiedDayLastUnixTime(statementDate);
minTime = getUnixTimeBeforeUnixTime(getUnixTimeAfterUnixTime(getThisMonthSpecifiedDayFirstUnixTime(statementDate), 1, 'days'), 1, 'months');
} else {
maxTime = getUnixTimeAfterUnixTime(getThisMonthSpecifiedDayLastUnixTime(statementDate), 1, 'months');
minTime = getUnixTimeAfterUnixTime(getThisMonthSpecifiedDayFirstUnixTime(statementDate), 1, 'days');
}

if (dateType === dateTimeConstants.allDateRanges.PreviousBillingCycle.type) {
maxTime = getUnixTimeBeforeUnixTime(maxTime, 1, 'months');
minTime = getUnixTimeBeforeUnixTime(minTime, 1, 'months');
}
} else {
let fallbackDateRange = null;

if (dateType === dateTimeConstants.allDateRanges.CurrentBillingCycle.type) { // same as This Month
fallbackDateRange = getDateRangeByDateType(dateTimeConstants.allDateRanges.ThisMonth.type, firstDayOfWeek);
} else if (dateType === dateTimeConstants.allDateRanges.PreviousBillingCycle.type) { // same as Last Month
fallbackDateRange = getDateRangeByDateType(dateTimeConstants.allDateRanges.LastMonth.type, firstDayOfWeek);
}

if (fallbackDateRange) {
maxTime = fallbackDateRange.maxTime;
minTime = fallbackDateRange.minTime;
}
}
} else {
return null;
}

return {
dateType: dateType,
maxTime: maxTime,
minTime: minTime
};
}

export function getRecentMonthDateRanges(monthCount) {
const recentDateRanges = [];
const thisMonthFirstUnixTime = getThisMonthFirstUnixTime();
Expand Down
16 changes: 14 additions & 2 deletions src/lib/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ function getAllWeekDays(firstDayOfWeek, translateFn) {
return allWeekDays;
}

function getAllDateRanges(scene, includeCustom, translateFn) {
function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) {
const allDateRanges = [];

for (let dateRangeField in datetimeConstants.allDateRanges) {
Expand All @@ -658,6 +658,18 @@ function getAllDateRanges(scene, includeCustom, translateFn) {
continue;
}

if (dateRangeType.isBillingCycle) {
if (includeBillingCycle) {
allDateRanges.push({
type: dateRangeType.type,
displayName: translateFn(dateRangeType.name),
isBillingCycle: dateRangeType.isBillingCycle
});
}

continue;
}

if (includeCustom || dateRangeType.type !== datetimeConstants.allDateRanges.Custom.type) {
allDateRanges.push({
type: dateRangeType.type,
Expand Down Expand Up @@ -1746,7 +1758,7 @@ export function i18nFunctions(i18nGlobal) {
getTimezoneDifferenceDisplayText: (utcOffset) => getTimezoneDifferenceDisplayText(utcOffset, i18nGlobal.t),
getAllCurrencies: () => getAllCurrencies(i18nGlobal.t),
getAllWeekDays: (firstDayOfWeek) => getAllWeekDays(firstDayOfWeek, i18nGlobal.t),
getAllDateRanges: (scene, includeCustom) => getAllDateRanges(scene, includeCustom, i18nGlobal.t),
getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t),
getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t),
getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t),
getAllTimezoneTypesUsedForStatistics: (currentTimezone) => getAllTimezoneTypesUsedForStatistics(currentTimezone, i18nGlobal.t),
Expand Down
2 changes: 2 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,8 @@
"Recent 2 years": "Recent 2 years",
"Recent 3 years": "Recent 3 years",
"Recent 5 years": "Recent 5 years",
"Previous Billing Cycle": "Previous Billing Cycle",
"Current Billing Cycle": "Current Billing Cycle",
"Custom Date": "Custom Date",
"Start Time": "Start Time",
"End Time": "End Time",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,8 @@
"Recent 2 years": "2 năm gần đây",
"Recent 3 years": "3 năm gần đây",
"Recent 5 years": "5 năm gần đây",
"Previous Billing Cycle": "Previous Billing Cycle",
"Current Billing Cycle": "Current Billing Cycle",
"Custom Date": "Ngày tùy chỉnh",
"Start Time": "Thời gian bắt đầu",
"End Time": "Thời gian kết thúc",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh_Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,8 @@
"Recent 2 years": "最近2年",
"Recent 3 years": "最近3年",
"Recent 5 years": "最近5年",
"Previous Billing Cycle": "上个账单周期",
"Current Billing Cycle": "当前账单周期",
"Custom Date": "自定义日期",
"Start Time": "开始时间",
"End Time": "结束时间",
Expand Down
41 changes: 41 additions & 0 deletions src/stores/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,47 @@ export const useAccountsStore = defineStore('accounts', {

return ret;
},
getAccountStatementDate(accountId) {
if (!accountId) {
return null;
}

const accountIds = accountId.split(',');
let mainAccount = null;

for (let i = 0; i < accountIds.length; i++) {
const id = accountIds[i];
let account = this.allAccountsMap[id];

if (!account) {
return null;
}

if (account.parentId !== '0') {
account = this.allAccountsMap[account.parentId];
}

if (mainAccount !== null) {
if (mainAccount.id !== account.id) {
return null;
} else {
continue;
}
}

mainAccount = account;
}

if (!mainAccount) {
return null;
}

if (mainAccount.category === accountConstants.creditCardCategoryType) {
return mainAccount.creditCardStatementDate;
}

return null;
},
getNetAssets(showAccountBalance) {
if (!showAccountBalance) {
return '***';
Expand Down
2 changes: 1 addition & 1 deletion src/stores/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ export const useTransactionsStore = defineStore('transactions', {

querys.push('dateType=' + this.transactionsFilter.dateType);

if (this.transactionsFilter.dateType === datetimeConstants.allDateRanges.Custom.type) {
if (datetimeConstants.allBillingCycleDateRangesMap[this.transactionsFilter.dateType] || this.transactionsFilter.dateType === datetimeConstants.allDateRanges.Custom.type) {
querys.push('maxTime=' + this.transactionsFilter.maxTime);
querys.push('minTime=' + this.transactionsFilter.minTime);
}
Expand Down
11 changes: 9 additions & 2 deletions src/views/desktop/common/cards/AccountFilterSettingsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ import { useAccountsStore } from '@/stores/account.js';
import { useTransactionsStore } from '@/stores/transaction.js';
import { useStatisticsStore } from '@/stores/statistics.js';
import datetimeConstants from '@/consts/datetime.js';
import accountConstants from '@/consts/account.js';
import { copyObjectTo } from '@/lib/common.js';
import {
Expand Down Expand Up @@ -322,9 +323,15 @@ export default {
self.statisticsStore.updateTransactionStatisticsInvalidState(true);
}
} else if (this.type === 'transactionListCurrent') {
changed = self.transactionsStore.updateTransactionListFilter({
const filter = {
accountIds: isAllSelected ? '' : finalAccountIds
});
};
if (datetimeConstants.allBillingCycleDateRangesMap[self.transactionsStore.transactionsFilter.dateType] && !self.accountsStore.getAccountStatementDate(filter.accountIds)) {
filter.dateType = datetimeConstants.allDateRanges.Custom.type;
}
changed = self.transactionsStore.updateTransactionListFilter(filter);
if (changed) {
self.transactionsStore.updateTransactionListInvalidState(true);
Expand Down
34 changes: 27 additions & 7 deletions src/views/desktop/transactions/ListPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
<span class="text-sm ml-3">{{ dateRange.displayName }}</span>
</div>
</v-list-item-title>
<div class="ml-3 smaller" v-if="dateRange.type === allDateRanges.Custom.type && query.dateType === allDateRanges.Custom.type && query.minTime && query.maxTime">
<div class="ml-3 smaller" v-if="((dateRange.isBillingCycle || dateRange.type === allDateRanges.Custom.type) && query.dateType === dateRange.type) && query.minTime && query.maxTime">
<span>{{ queryMinTime }}</span>
<span>&nbsp;-&nbsp;</span>
<br/>
Expand Down Expand Up @@ -615,7 +615,9 @@ import {
getBrowserTimezoneOffsetMinutes,
getActualUnixTimeForStore,
getShiftedDateRangeAndDateType,
getShiftedDateRangeAndDateTypeForBillingCycle,
getDateTypeByDateRange,
getDateRangeByBillingCycleDateType,
getDateRangeByDateType,
getRecentDateRangeType,
isDateRangeMatchOneMonth
Expand Down Expand Up @@ -743,7 +745,7 @@ export default {
return this.userStore.currentUserFirstDayOfWeek;
},
allDateRangesArray() {
return this.$locale.getAllDateRanges(datetimeConstants.allDateRangeScenes.Normal, true);
return this.$locale.getAllDateRanges(datetimeConstants.allDateRangeScenes.Normal, true, !!this.accountsStore.getAccountStatementDate(this.query.accountIds));
},
allDateRanges() {
return datetimeConstants.allDateRanges;
Expand Down Expand Up @@ -1115,7 +1117,7 @@ export default {
let dateRange = getDateRangeByDateType(query.dateType ? parseInt(query.dateType) : undefined, this.firstDayOfWeek);
if (!dateRange &&
query.dateType === datetimeConstants.allDateRanges.Custom.type.toString() &&
(datetimeConstants.allBillingCycleDateRangesMap[query.dateType] || query.dateType === datetimeConstants.allDateRanges.Custom.type.toString()) &&
parseInt(query.maxTime) > 0 && parseInt(query.minTime) > 0) {
dateRange = {
dateType: parseInt(query.dateType),
Expand Down Expand Up @@ -1209,7 +1211,15 @@ export default {
return;
}
const newDateRange = getShiftedDateRangeAndDateType(startTime, endTime, scale, this.firstDayOfWeek, datetimeConstants.allDateRangeScenes.Normal);
let newDateRange = null;
if (datetimeConstants.allBillingCycleDateRangesMap[this.query.dateType]) {
newDateRange = getShiftedDateRangeAndDateTypeForBillingCycle(this.query.dateType, scale, this.firstDayOfWeek, this.accountsStore.getAccountStatementDate(this.query.accountIds));
}
if (!newDateRange) {
newDateRange = getShiftedDateRangeAndDateType(startTime, endTime, scale, this.firstDayOfWeek, datetimeConstants.allDateRangeScenes.Normal);
}
const changed = this.transactionsStore.updateTransactionListFilter({
dateType: newDateRange.dateType,
Expand All @@ -1226,7 +1236,11 @@ export default {
},
changeDateFilter(dateRange) {
if (isNumber(dateRange)) {
dateRange = getDateRangeByDateType(dateRange, this.firstDayOfWeek);
if (datetimeConstants.allBillingCycleDateRangesMap[dateRange]) {
dateRange = getDateRangeByBillingCycleDateType(dateRange, this.firstDayOfWeek, this.accountsStore.getAccountStatementDate(this.query.accountIds));
} else {
dateRange = getDateRangeByDateType(dateRange, this.firstDayOfWeek);
}
}
if (dateRange.dateType === datetimeConstants.allDateRanges.Custom.type &&
Expand Down Expand Up @@ -1418,9 +1432,15 @@ export default {
return;
}
const changed = this.transactionsStore.updateTransactionListFilter({
const filter = {
accountIds: accountIds
});
};
if (datetimeConstants.allBillingCycleDateRangesMap[this.query.dateType] && !this.accountsStore.getAccountStatementDate(accountIds)) {
filter.dateType = datetimeConstants.allDateRanges.Custom.type;
}
const changed = this.transactionsStore.updateTransactionListFilter(filter);
if (changed) {
this.loading = true;
Expand Down
Loading

0 comments on commit 647cd3c

Please sign in to comment.