mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-20 09:44:26 +08:00
migrate transaction edit page to composition API and typescript
This commit is contained in:
@@ -5,7 +5,6 @@ type TemplateTypeName = 'Normal' | 'Schedule';
|
|||||||
export class TemplateType implements TypeAndName {
|
export class TemplateType implements TypeAndName {
|
||||||
private static readonly allInstances: TemplateType[] = [];
|
private static readonly allInstances: TemplateType[] = [];
|
||||||
private static readonly allInstancesByType: Record<number, TemplateType> = {};
|
private static readonly allInstancesByType: Record<number, TemplateType> = {};
|
||||||
private static readonly allInstancesByTypeName: Record<string, TemplateType> = {};
|
|
||||||
|
|
||||||
public static readonly Normal = new TemplateType(1, 'Normal');
|
public static readonly Normal = new TemplateType(1, 'Normal');
|
||||||
public static readonly Schedule = new TemplateType(2, 'Schedule');
|
public static readonly Schedule = new TemplateType(2, 'Schedule');
|
||||||
@@ -19,17 +18,12 @@ export class TemplateType implements TypeAndName {
|
|||||||
|
|
||||||
TemplateType.allInstances.push(this);
|
TemplateType.allInstances.push(this);
|
||||||
TemplateType.allInstancesByType[type] = this;
|
TemplateType.allInstancesByType[type] = this;
|
||||||
TemplateType.allInstancesByTypeName[name] = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static values(): TemplateType[] {
|
public static values(): TemplateType[] {
|
||||||
return TemplateType.allInstances;
|
return TemplateType.allInstances;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static all(): Record<TemplateTypeName, TemplateType> {
|
|
||||||
return TemplateType.allInstancesByTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static valueOf(type: number): TemplateType | undefined {
|
public static valueOf(type: number): TemplateType | undefined {
|
||||||
return TemplateType.allInstancesByType[type];
|
return TemplateType.allInstancesByType[type];
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -434,7 +434,7 @@ export default {
|
|||||||
timeout: DEFAULT_IMPORT_API_TIMEOUT
|
timeout: DEFAULT_IMPORT_API_TIMEOUT
|
||||||
} as ApiRequestConfig);
|
} as ApiRequestConfig);
|
||||||
},
|
},
|
||||||
uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => {
|
uploadTransactionPicture: ({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): ApiResponsePromise<TransactionPictureInfoBasicResponse> => {
|
||||||
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
|
return axios.postForm<ApiResponse<TransactionPictureInfoBasicResponse>>('v1/transaction/pictures/upload.json', {
|
||||||
picture: pictureFile,
|
picture: pictureFile,
|
||||||
clientSessionId: clientSessionId
|
clientSessionId: clientSessionId
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ import {
|
|||||||
} from './category.ts';
|
} from './category.ts';
|
||||||
|
|
||||||
export interface SetTransactionOptions {
|
export interface SetTransactionOptions {
|
||||||
type: number;
|
type?: number;
|
||||||
categoryId: string;
|
categoryId?: string;
|
||||||
accountId: string;
|
accountId?: string;
|
||||||
destinationAccountId: string;
|
destinationAccountId?: string;
|
||||||
amount: number;
|
amount?: number;
|
||||||
destinationAmount: number;
|
destinationAmount?: number;
|
||||||
tagIds: string;
|
tagIds?: string;
|
||||||
comment: string;
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDisplayAmount(amount: number, currency: string, hideAmount: boolean, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string {
|
function getDisplayAmount(amount: number, currency: string, hideAmount: boolean, formatAmountWithCurrencyFunc: (value: number | string, currencyCode?: string) => string): string {
|
||||||
|
|||||||
+24
-24
@@ -37,26 +37,6 @@ export function showAlert(message: string, confirmCallback: ((dialog: Dialog.Dia
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showConfirm(message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined, translateFn: TranslateFunction): void {
|
|
||||||
f7ready((f7) => {
|
|
||||||
f7.dialog.create({
|
|
||||||
title: translateFn('global.app.title'),
|
|
||||||
text: translateFn(message),
|
|
||||||
animate: isEnableAnimate(),
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: translateFn('Cancel'),
|
|
||||||
onClick: cancelCallback
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: translateFn('OK'),
|
|
||||||
onClick: confirmCallback
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}).open();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showToast(message: string, timeout: number | undefined, translateFn: TranslateFunction): void {
|
export function showToast(message: string, timeout: number | undefined, translateFn: TranslateFunction): void {
|
||||||
f7ready((f7) => {
|
f7ready((f7) => {
|
||||||
f7.toast.create({
|
f7.toast.create({
|
||||||
@@ -210,7 +190,7 @@ export function scrollToSelectedItem(parentEl: Framework7Dom, containerSelector:
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useI18nUIComponents() {
|
export function useI18nUIComponents() {
|
||||||
const i18nGlobal = useVueI18n();
|
const { t } = useVueI18n();
|
||||||
|
|
||||||
function routeBackOnError<T>(f7router: Router.Router, errorRef: Ref<T>): void {
|
function routeBackOnError<T>(f7router: Router.Router, errorRef: Ref<T>): void {
|
||||||
const unwatch = watch(errorRef, (newValue) => {
|
const unwatch = watch(errorRef, (newValue) => {
|
||||||
@@ -228,10 +208,30 @@ export function useI18nUIComponents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showConfirm(message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: ((dialog: Dialog.Dialog, e: Event) => void) | undefined): void {
|
||||||
|
f7ready((f7) => {
|
||||||
|
f7.dialog.create({
|
||||||
|
title: t('global.app.title'),
|
||||||
|
text: t(message),
|
||||||
|
animate: isEnableAnimate(),
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: t('Cancel'),
|
||||||
|
onClick: cancelCallback
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t('OK'),
|
||||||
|
onClick: confirmCallback
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showAlert: (message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void) => showAlert(message, confirmCallback, i18nGlobal.t),
|
showAlert: (message: string, confirmCallback?: (dialog: Dialog.Dialog, e: Event) => void) => showAlert(message, confirmCallback, t),
|
||||||
showConfirm: (message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback?: (dialog: Dialog.Dialog, e: Event) => void): void => showConfirm(message, confirmCallback, cancelCallback, i18nGlobal.t),
|
showConfirm: showConfirm,
|
||||||
showToast: (message: string, timeout?: number): void => showToast(message, timeout, i18nGlobal.t),
|
showToast: (message: string, timeout?: number): void => showToast(message, timeout, t),
|
||||||
routeBackOnError
|
routeBackOnError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-269
@@ -1,29 +1,21 @@
|
|||||||
import { WeekDay, LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat, DateRange } from '@/core/datetime.ts';
|
import { LongDateFormat, ShortDateFormat, LongTimeFormat, ShortTimeFormat, DateRange } from '@/core/datetime.ts';
|
||||||
import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts';
|
import { DecimalSeparator, DigitGroupingSymbol, DigitGroupingType } from '@/core/numeral.ts';
|
||||||
import { CurrencyDisplayType } from '@/core/currency.ts'
|
import { CurrencyDisplayType } from '@/core/currency.ts'
|
||||||
import { AccountCategory } from '@/core/account.ts';
|
|
||||||
import { TransactionTagFilterType } from '@/core/transaction.ts';
|
import { TransactionTagFilterType } from '@/core/transaction.ts';
|
||||||
|
|
||||||
import { UTC_TIMEZONE, ALL_TIMEZONES } from '@/consts/timezone.ts';
|
|
||||||
import { ALL_CURRENCIES } from '@/consts/currency.ts';
|
import { ALL_CURRENCIES } from '@/consts/currency.ts';
|
||||||
import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts';
|
import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isString,
|
isString,
|
||||||
isNumber,
|
isNumber,
|
||||||
isBoolean,
|
isBoolean
|
||||||
copyObjectTo
|
|
||||||
} from '@/lib/common.ts';
|
} from '@/lib/common.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
parseDateFromUnixTime,
|
parseDateFromUnixTime,
|
||||||
formatUnixTime,
|
formatUnixTime,
|
||||||
getYear,
|
getYear,
|
||||||
getTimezoneOffset,
|
|
||||||
getTimezoneOffsetMinutes,
|
|
||||||
getBrowserTimezoneOffset,
|
|
||||||
getBrowserTimezoneOffsetMinutes,
|
|
||||||
getTimeDifferenceHoursAndMinutes,
|
|
||||||
getDateTimeFormatType,
|
getDateTimeFormatType,
|
||||||
getRecentMonthDateRanges,
|
getRecentMonthDateRanges,
|
||||||
isDateRangeMatchFullYears,
|
isDateRangeMatchFullYears,
|
||||||
@@ -31,8 +23,7 @@ import {
|
|||||||
} from '@/lib/datetime.ts';
|
} from '@/lib/datetime.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatAmount,
|
formatAmount
|
||||||
getAdaptiveDisplayAmountRate
|
|
||||||
} from '@/lib/numeral.ts';
|
} from '@/lib/numeral.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -40,11 +31,6 @@ import {
|
|||||||
appendCurrencySymbol
|
appendCurrencySymbol
|
||||||
} from '@/lib/currency.ts';
|
} from '@/lib/currency.ts';
|
||||||
|
|
||||||
import {
|
|
||||||
getCategorizedAccountsMap,
|
|
||||||
getAllFilteredAccountsBalance
|
|
||||||
} from '@/lib/account.ts';
|
|
||||||
|
|
||||||
function getLocalizedDisplayNameAndType(typeAndNames, translateFn) {
|
function getLocalizedDisplayNameAndType(typeAndNames, translateFn) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
|
|
||||||
@@ -78,10 +64,6 @@ function getCurrencyUnitName(currencyCode, isPlural, translateFn) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMonthdayOrdinal(monthDay, translateFn) {
|
|
||||||
return translateFn(`datetime.monthDayOrdinal.${monthDay}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWeekdayShortName(weekDayName, translateFn) {
|
function getWeekdayShortName(weekDayName, translateFn) {
|
||||||
return translateFn(`datetime.${weekDayName}.short`);
|
return translateFn(`datetime.${weekDayName}.short`);
|
||||||
}
|
}
|
||||||
@@ -90,50 +72,6 @@ function getWeekdayLongName(weekDayName, translateFn) {
|
|||||||
return translateFn(`datetime.${weekDayName}.long`);
|
return translateFn(`datetime.${weekDayName}.long`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMultiMonthdayShortNames(monthDays, translateFn) {
|
|
||||||
if (!monthDays) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monthDays.length === 1) {
|
|
||||||
return translateFn('format.misc.monthDay', {
|
|
||||||
ordinal: getMonthdayOrdinal(monthDays[0], translateFn)
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return translateFn('format.misc.monthDays', {
|
|
||||||
multiMonthDays: joinMultiText(monthDays.map(monthDay =>
|
|
||||||
translateFn('format.misc.eachMonthDayInMonthDays', {
|
|
||||||
ordinal: getMonthdayOrdinal(monthDay, translateFn)
|
|
||||||
})), translateFn)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMultiWeekdayLongNames(weekdayTypes, firstDayOfWeek, translateFn) {
|
|
||||||
const weekdayTypesMap = {};
|
|
||||||
|
|
||||||
if (!isNumber(firstDayOfWeek)) {
|
|
||||||
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < weekdayTypes.length; i++) {
|
|
||||||
weekdayTypesMap[weekdayTypes[i]] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allWeekDays = getAllWeekDays(firstDayOfWeek, translateFn);
|
|
||||||
const finalWeekdayNames = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < allWeekDays.length; i++) {
|
|
||||||
const weekDay = allWeekDays[i];
|
|
||||||
|
|
||||||
if (weekdayTypesMap[weekDay.type]) {
|
|
||||||
finalWeekdayNames.push(weekDay.displayName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return joinMultiText(finalWeekdayNames, translateFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getI18nLongDateFormat(translateFn, formatTypeValue) {
|
function getI18nLongDateFormat(translateFn, formatTypeValue) {
|
||||||
const defaultLongDateFormatTypeName = translateFn('default.longDateFormat');
|
const defaultLongDateFormatTypeName = translateFn('default.longDateFormat');
|
||||||
return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longDate', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue);
|
return getDateTimeFormat(translateFn, LongDateFormat.all(), LongDateFormat.values(), 'format.longDate', defaultLongDateFormatTypeName, LongDateFormat.Default, formatTypeValue);
|
||||||
@@ -184,110 +122,6 @@ function getDateTimeFormat(translateFn, allFormatMap, allFormatArray, localeForm
|
|||||||
return translateFn(`${localeFormatPathPrefix}.${type.key}`);
|
return translateFn(`${localeFormatPathPrefix}.${type.key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllTimezones(includeSystemDefault, translateFn) {
|
|
||||||
const defaultTimezoneOffset = getBrowserTimezoneOffset();
|
|
||||||
const defaultTimezoneOffsetMinutes = getBrowserTimezoneOffsetMinutes();
|
|
||||||
const allTimezoneInfos = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < ALL_TIMEZONES.length; i++) {
|
|
||||||
const utcOffset = (ALL_TIMEZONES[i].timezoneName !== UTC_TIMEZONE.timezoneName ? getTimezoneOffset(ALL_TIMEZONES[i].timezoneName) : '');
|
|
||||||
const displayName = translateFn(`timezone.${ALL_TIMEZONES[i].displayName}`);
|
|
||||||
|
|
||||||
allTimezoneInfos.push({
|
|
||||||
name: ALL_TIMEZONES[i].timezoneName,
|
|
||||||
utcOffset: utcOffset,
|
|
||||||
utcOffsetMinutes: getTimezoneOffsetMinutes(ALL_TIMEZONES[i].timezoneName),
|
|
||||||
displayName: displayName,
|
|
||||||
displayNameWithUtcOffset: `(UTC${utcOffset}) ${displayName}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeSystemDefault) {
|
|
||||||
const defaultDisplayName = translateFn('System Default');
|
|
||||||
|
|
||||||
allTimezoneInfos.push({
|
|
||||||
name: '',
|
|
||||||
utcOffset: defaultTimezoneOffset,
|
|
||||||
utcOffsetMinutes: defaultTimezoneOffsetMinutes,
|
|
||||||
displayName: defaultDisplayName,
|
|
||||||
displayNameWithUtcOffset: `(UTC${defaultTimezoneOffset}) ${defaultDisplayName}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
allTimezoneInfos.sort(function(c1, c2) {
|
|
||||||
const utcOffset1 = parseInt(c1.utcOffset.replace(':', ''));
|
|
||||||
const utcOffset2 = parseInt(c2.utcOffset.replace(':', ''));
|
|
||||||
|
|
||||||
if (utcOffset1 !== utcOffset2) {
|
|
||||||
return utcOffset1 - utcOffset2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return c1.displayName.localeCompare(c2.displayName);
|
|
||||||
})
|
|
||||||
|
|
||||||
return allTimezoneInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTimezoneDifferenceDisplayText(utcOffset, translateFn) {
|
|
||||||
const defaultTimezoneOffset = getTimezoneOffsetMinutes();
|
|
||||||
const offsetTime = getTimeDifferenceHoursAndMinutes(utcOffset - defaultTimezoneOffset);
|
|
||||||
|
|
||||||
if (utcOffset > defaultTimezoneOffset) {
|
|
||||||
if (offsetTime.offsetMinutes) {
|
|
||||||
return translateFn('format.misc.hoursMinutesAheadOfDefaultTimezone', {
|
|
||||||
hours: offsetTime.offsetHours,
|
|
||||||
minutes: offsetTime.offsetMinutes
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return translateFn('format.misc.hoursAheadOfDefaultTimezone', {
|
|
||||||
hours: offsetTime.offsetHours
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (utcOffset < defaultTimezoneOffset) {
|
|
||||||
if (offsetTime.offsetMinutes) {
|
|
||||||
return translateFn('format.misc.hoursMinutesBehindDefaultTimezone', {
|
|
||||||
hours: offsetTime.offsetHours,
|
|
||||||
minutes: offsetTime.offsetMinutes
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return translateFn('format.misc.hoursBehindDefaultTimezone', {
|
|
||||||
hours: offsetTime.offsetHours
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return translateFn('Same time as default timezone');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllWeekDays(firstDayOfWeek, translateFn) {
|
|
||||||
const ret = [];
|
|
||||||
const allWeekDays = WeekDay.values();
|
|
||||||
|
|
||||||
if (!isNumber(firstDayOfWeek)) {
|
|
||||||
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = firstDayOfWeek; i < allWeekDays.length; i++) {
|
|
||||||
const weekDay = allWeekDays[i];
|
|
||||||
|
|
||||||
ret.push({
|
|
||||||
type: weekDay.type,
|
|
||||||
displayName: translateFn(`datetime.${weekDay.name}.long`)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < firstDayOfWeek; i++) {
|
|
||||||
const weekDay = allWeekDays[i];
|
|
||||||
|
|
||||||
ret.push({
|
|
||||||
type: weekDay.type,
|
|
||||||
displayName: translateFn(`datetime.${weekDay.name}.long`)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) {
|
function getAllDateRanges(scene, includeCustom, includeBillingCycle, translateFn) {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
const allDateRanges = DateRange.values();
|
const allDateRanges = DateRange.values();
|
||||||
@@ -460,11 +294,6 @@ function getNumberFormatOptions(translateFn, userStore, currencyCode) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFormattedAmount(value, translateFn, userStore, currencyCode) {
|
|
||||||
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore, currencyCode);
|
|
||||||
return formatAmount(value, numberFormatOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentCurrencyDisplayType(translateFn, userStore) {
|
function getCurrentCurrencyDisplayType(translateFn, userStore) {
|
||||||
let currencyDisplayType = CurrencyDisplayType.valueOf(userStore.currentUserCurrencyDisplayType);
|
let currencyDisplayType = CurrencyDisplayType.valueOf(userStore.currentUserCurrencyDisplayType);
|
||||||
|
|
||||||
@@ -525,96 +354,10 @@ function getFormattedAmountWithCurrency(value, currencyCode, translateFn, userSt
|
|||||||
return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural);
|
return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, translateFn, userStore) {
|
|
||||||
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
|
|
||||||
return getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, numberFormatOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllTransactionTagFilterTypes(translateFn) {
|
function getAllTransactionTagFilterTypes(translateFn) {
|
||||||
return getLocalizedDisplayNameAndType(TransactionTagFilterType.values(), translateFn);
|
return getLocalizedDisplayNameAndType(TransactionTagFilterType.values(), translateFn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, translateFn) {
|
|
||||||
const ret = [];
|
|
||||||
const allCategories = AccountCategory.values();
|
|
||||||
const categorizedAccounts = copyObjectTo(getCategorizedAccountsMap(allVisibleAccounts), {});
|
|
||||||
|
|
||||||
for (let i = 0; i < allCategories.length; i++) {
|
|
||||||
const category = allCategories[i];
|
|
||||||
|
|
||||||
if (!categorizedAccounts[category.type]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountCategory = categorizedAccounts[category.type];
|
|
||||||
|
|
||||||
if (accountCategory.accounts) {
|
|
||||||
for (let i = 0; i < accountCategory.accounts.length; i++) {
|
|
||||||
const account = accountCategory.accounts[i];
|
|
||||||
|
|
||||||
if (showAccountBalance && account.isAsset) {
|
|
||||||
account.displayBalance = getFormattedAmountWithCurrency(account.balance, account.currency, translateFn, userStore, settingsStore);
|
|
||||||
} else if (showAccountBalance && account.isLiability) {
|
|
||||||
account.displayBalance = getFormattedAmountWithCurrency(-account.balance, account.currency, translateFn, userStore, settingsStore);
|
|
||||||
} else {
|
|
||||||
account.displayBalance = '***';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showAccountBalance) {
|
|
||||||
const accountsBalance = getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category);
|
|
||||||
let totalBalance = 0;
|
|
||||||
let hasUnCalculatedAmount = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < accountsBalance.length; i++) {
|
|
||||||
if (accountsBalance[i].currency === defaultCurrency) {
|
|
||||||
if (accountsBalance[i].isAsset) {
|
|
||||||
totalBalance += accountsBalance[i].balance;
|
|
||||||
} else if (accountsBalance[i].isLiability) {
|
|
||||||
totalBalance -= accountsBalance[i].balance;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const balance = exchangeRatesStore.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, defaultCurrency);
|
|
||||||
|
|
||||||
if (!isNumber(balance)) {
|
|
||||||
hasUnCalculatedAmount = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountsBalance[i].isAsset) {
|
|
||||||
totalBalance += Math.floor(balance);
|
|
||||||
} else if (accountsBalance[i].isLiability) {
|
|
||||||
totalBalance -= Math.floor(balance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasUnCalculatedAmount) {
|
|
||||||
totalBalance = totalBalance + '+';
|
|
||||||
}
|
|
||||||
|
|
||||||
accountCategory.displayBalance = getFormattedAmountWithCurrency(totalBalance, defaultCurrency, translateFn, userStore, settingsStore);
|
|
||||||
} else {
|
|
||||||
accountCategory.displayBalance = '***';
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.push(accountCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function joinMultiText(textArray, translateFn) {
|
|
||||||
if (!textArray || !textArray.length) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const separator = translateFn('format.misc.multiTextJoinSeparator');
|
|
||||||
|
|
||||||
return textArray.join(separator);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLocalizedError(error) {
|
function getLocalizedError(error) {
|
||||||
if (error.errorCode === KnownErrorCode.ApiNotFound && SPECIFIED_API_NOT_FOUND_ERRORS[error.path]) {
|
if (error.errorCode === KnownErrorCode.ApiNotFound && SPECIFIED_API_NOT_FOUND_ERRORS[error.path]) {
|
||||||
return {
|
return {
|
||||||
@@ -685,23 +428,15 @@ export function i18nFunctions(i18nGlobal) {
|
|||||||
return {
|
return {
|
||||||
getWeekdayShortName: (weekDay) => getWeekdayShortName(weekDay, i18nGlobal.t),
|
getWeekdayShortName: (weekDay) => getWeekdayShortName(weekDay, i18nGlobal.t),
|
||||||
getWeekdayLongName: (weekDay) => getWeekdayLongName(weekDay, i18nGlobal.t),
|
getWeekdayLongName: (weekDay) => getWeekdayLongName(weekDay, i18nGlobal.t),
|
||||||
getMultiMonthdayShortNames: (monthdays) => getMultiMonthdayShortNames(monthdays, i18nGlobal.t),
|
|
||||||
getMultiWeekdayLongNames: (weekdayTypes, firstDayOfWeek) => getMultiWeekdayLongNames(weekdayTypes, firstDayOfWeek, i18nGlobal.t),
|
|
||||||
formatUnixTimeToLongDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
|
formatUnixTimeToLongDateTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat) + ' ' + getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
|
||||||
formatUnixTimeToLongDate: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
formatUnixTimeToLongDate: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongDateFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||||
formatUnixTimeToLongYear: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
formatUnixTimeToLongYear: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||||
formatUnixTimeToLongYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearMonthFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
formatUnixTimeToLongYearMonth: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongYearMonthFormat(i18nGlobal.t, userStore.currentUserLongDateFormat), utcOffset, currentUtcOffset),
|
||||||
formatUnixTimeToLongTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nLongTimeFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), utcOffset, currentUtcOffset),
|
|
||||||
formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset),
|
formatUnixTimeToShortTime: (userStore, unixTime, utcOffset, currentUtcOffset) => formatUnixTime(unixTime, getI18nShortTimeFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), utcOffset, currentUtcOffset),
|
||||||
getAllTimezones: (includeSystemDefault) => getAllTimezones(includeSystemDefault, i18nGlobal.t),
|
|
||||||
getTimezoneDifferenceDisplayText: (utcOffset) => getTimezoneDifferenceDisplayText(utcOffset, i18nGlobal.t),
|
|
||||||
getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t),
|
getAllDateRanges: (scene, includeCustom, includeBillingCycle) => getAllDateRanges(scene, includeCustom, includeBillingCycle, i18nGlobal.t),
|
||||||
getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t),
|
getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t),
|
||||||
getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t),
|
getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t),
|
||||||
formatAmount: (userStore, value, currencyCode) => getFormattedAmount(value, i18nGlobal.t, userStore, currencyCode),
|
|
||||||
formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore),
|
formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore),
|
||||||
getAdaptiveAmountRate: (userStore, amount1, amount2, fromExchangeRate, toExchangeRate) => getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, i18nGlobal.t, userStore),
|
getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t)
|
||||||
getAllTransactionTagFilterTypes: () => getAllTransactionTagFilterTypes(i18nGlobal.t),
|
|
||||||
getCategorizedAccountsWithDisplayBalance: (allVisibleAccounts, showAccountBalance, defaultCurrency, settingsStore, userStore, exchangeRatesStore) => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, i18nGlobal.t)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ import { getI18nOptions } from '@/locales/helpers.ts';
|
|||||||
import { i18nFunctions } from '@/locales/helper.js';
|
import { i18nFunctions } from '@/locales/helper.js';
|
||||||
import {
|
import {
|
||||||
showAlert,
|
showAlert,
|
||||||
showConfirm,
|
|
||||||
showToast,
|
showToast,
|
||||||
showLoading,
|
showLoading,
|
||||||
hideLoading,
|
hideLoading,
|
||||||
@@ -204,7 +203,6 @@ app.directive('TextareaAutoSize', TextareaAutoSize);
|
|||||||
app.config.globalProperties['$locale'] = i18nFunctions(i18n.global);
|
app.config.globalProperties['$locale'] = i18nFunctions(i18n.global);
|
||||||
|
|
||||||
app.config.globalProperties['$alert'] = (message: string, confirmCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showAlert(message, confirmCallback, i18n.global.t);
|
app.config.globalProperties['$alert'] = (message: string, confirmCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showAlert(message, confirmCallback, i18n.global.t);
|
||||||
app.config.globalProperties['$confirm'] = (message: string, confirmCallback: (dialog: Dialog.Dialog, e: Event) => void, cancelCallback: ((dialog: Dialog.Dialog, e: Event) => void) | undefined) => showConfirm(message, confirmCallback, cancelCallback, i18n.global.t);
|
|
||||||
app.config.globalProperties['$toast'] = (message: string, timeout: number | undefined) => showToast(message, timeout, i18n.global.t);
|
app.config.globalProperties['$toast'] = (message: string, timeout: number | undefined) => showToast(message, timeout, i18n.global.t);
|
||||||
app.config.globalProperties['$showLoading'] = showLoading;
|
app.config.globalProperties['$showLoading'] = showLoading;
|
||||||
app.config.globalProperties['$hideLoading'] = hideLoading;
|
app.config.globalProperties['$hideLoading'] = hideLoading;
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ export class TransactionTemplate extends Transaction implements TransactionTempl
|
|||||||
this.hidden = hidden;
|
this.hidden = hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public from(other: TransactionTemplate): void {
|
||||||
|
this.templateType = other.templateType;
|
||||||
|
this.name = other.name;
|
||||||
|
|
||||||
|
if (this.templateType === TemplateType.Schedule.type) {
|
||||||
|
this.scheduledFrequencyType = other.scheduledFrequencyType;
|
||||||
|
this.scheduledFrequency = other.scheduledFrequency;
|
||||||
|
this.utcOffset = other.utcOffset;
|
||||||
|
this.timeZone = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public toTemplateCreateRequest(clientSessionId: string): TransactionTemplateCreateRequest {
|
public toTemplateCreateRequest(clientSessionId: string): TransactionTemplateCreateRequest {
|
||||||
return {
|
return {
|
||||||
templateType: this.templateType,
|
templateType: this.templateType,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import {
|
|||||||
countSplitItems
|
countSplitItems
|
||||||
} from '@/lib/common.ts';
|
} from '@/lib/common.ts';
|
||||||
import {
|
import {
|
||||||
getCurrentUnixTime,
|
|
||||||
getTimezoneOffsetMinutes,
|
getTimezoneOffsetMinutes,
|
||||||
getBrowserTimezoneOffsetMinutes,
|
getBrowserTimezoneOffsetMinutes,
|
||||||
getActualUnixTimeForStore,
|
getActualUnixTimeForStore,
|
||||||
@@ -510,21 +509,6 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
clearUserTransactionDraft();
|
clearUserTransactionDraft();
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateNewTransactionModel(type: string): Transaction {
|
|
||||||
const now: number = getCurrentUnixTime();
|
|
||||||
const currentTimezone: string = settingsStore.appSettings.timeZone;
|
|
||||||
|
|
||||||
let defaultType: TransactionType = TransactionType.Expense;
|
|
||||||
|
|
||||||
if (type === TransactionType.Income.toString()) {
|
|
||||||
defaultType = TransactionType.Income;
|
|
||||||
} else if (type === TransactionType.Transfer.toString()) {
|
|
||||||
defaultType = TransactionType.Transfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(currentTimezone));
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTransactionSuitableDestinationAmount(transaction: Transaction, oldValue: number, newValue: number): void {
|
function setTransactionSuitableDestinationAmount(transaction: Transaction, oldValue: number, newValue: number): void {
|
||||||
if (transaction.type === TransactionType.Expense || transaction.type === TransactionType.Income) {
|
if (transaction.type === TransactionType.Expense || transaction.type === TransactionType.Income) {
|
||||||
transaction.destinationAmount = newValue;
|
transaction.destinationAmount = newValue;
|
||||||
@@ -1133,7 +1117,7 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId: string }): Promise<TransactionPictureInfoBasicResponse> {
|
function uploadTransactionPicture({ pictureFile, clientSessionId }: { pictureFile: File, clientSessionId?: string }): Promise<TransactionPictureInfoBasicResponse> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
|
services.uploadTransactionPicture({ pictureFile, clientSessionId }).then(response => {
|
||||||
const data = response.data;
|
const data = response.data;
|
||||||
@@ -1183,9 +1167,9 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null, disableBrowserCache?: boolean | string): string | null {
|
function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null, disableBrowserCache?: boolean | string): string | undefined {
|
||||||
if (!pictureInfo || !pictureInfo.originalUrl) {
|
if (!pictureInfo || !pictureInfo.originalUrl) {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return services.getTransactionPictureUrlWithToken(pictureInfo.originalUrl, disableBrowserCache);
|
return services.getTransactionPictureUrlWithToken(pictureInfo.originalUrl, disableBrowserCache);
|
||||||
@@ -1218,7 +1202,6 @@ export const useTransactionsStore = defineStore('transactions', () => {
|
|||||||
isTransactionDraftModified,
|
isTransactionDraftModified,
|
||||||
saveTransactionDraft,
|
saveTransactionDraft,
|
||||||
clearTransactionDraft,
|
clearTransactionDraft,
|
||||||
generateNewTransactionModel,
|
|
||||||
setTransactionSuitableDestinationAmount,
|
setTransactionSuitableDestinationAmount,
|
||||||
updateTransactionListInvalidState,
|
updateTransactionListInvalidState,
|
||||||
resetTransactions,
|
resetTransactions,
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
import { ref, computed, watch } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
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 { useTransactionTagsStore } from '@/stores/transactionTag.ts';
|
||||||
|
import { useTransactionsStore } from '@/stores/transaction.ts';
|
||||||
|
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||||
|
|
||||||
|
import type { LocalizedTimezoneInfo } from '@/core/timezone.ts';
|
||||||
|
import { CategoryType } from '@/core/category.ts';
|
||||||
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
|
import { TemplateType } from '@/core/template.ts';
|
||||||
|
import { TRANSACTION_MAX_PICTURE_COUNT } from '@/consts/transaction.ts';
|
||||||
|
|
||||||
|
import { Account, type CategorizedAccountWithDisplayBalance } from '@/models/account.ts';
|
||||||
|
import type { TransactionCategory } from '@/models/transaction_category.ts';
|
||||||
|
import type { TransactionTag } from '@/models/transaction_tag.ts';
|
||||||
|
import type { TransactionPictureInfoBasicResponse } from '@/models/transaction_picture_info.ts';
|
||||||
|
import { Transaction } from '@/models/transaction.ts';
|
||||||
|
import { TransactionTemplate } from '@/models/transaction_template.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
isArray
|
||||||
|
} from '@/lib/common.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getUtcOffsetByUtcOffsetMinutes,
|
||||||
|
getTimezoneOffsetMinutes,
|
||||||
|
getCurrentUnixTime
|
||||||
|
} from '@/lib/datetime.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getFirstAvailableCategoryId
|
||||||
|
} from '@/lib/category.ts';
|
||||||
|
|
||||||
|
export enum TransactionEditPageType {
|
||||||
|
Transaction = 'transaction',
|
||||||
|
Template = 'template'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TransactionEditPageMode {
|
||||||
|
Add = 'add',
|
||||||
|
Edit = 'edit',
|
||||||
|
View = 'view'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GeoLocationStatus {
|
||||||
|
Getting = 'getting',
|
||||||
|
Success = 'success',
|
||||||
|
Error = 'error'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTransactionEditPageBase(type: TransactionEditPageType, initMode?: TransactionEditPageMode, transactionDefaultType?: number) {
|
||||||
|
const {
|
||||||
|
tt,
|
||||||
|
getAllTimezones,
|
||||||
|
getTimezoneDifferenceDisplayText,
|
||||||
|
formatAmountWithCurrency,
|
||||||
|
getAdaptiveAmountRate,
|
||||||
|
getCategorizedAccountsWithDisplayBalance
|
||||||
|
} = useI18n();
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const accountsStore = useAccountsStore();
|
||||||
|
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||||
|
const transactionTagsStore = useTransactionTagsStore();
|
||||||
|
const transactionsStore = useTransactionsStore();
|
||||||
|
const exchangeRatesStore = useExchangeRatesStore();
|
||||||
|
|
||||||
|
const isSupportGeoLocation: boolean = !!navigator.geolocation;
|
||||||
|
|
||||||
|
const mode = ref<TransactionEditPageMode>(initMode ?? TransactionEditPageMode.Add);
|
||||||
|
const editId = ref<string | null>(null);
|
||||||
|
const addByTemplateId = ref<string | null>(null);
|
||||||
|
const duplicateFromId = ref<string | null>(null);
|
||||||
|
|
||||||
|
const clientSessionId = ref<string>('');
|
||||||
|
const loading = ref<boolean>(true);
|
||||||
|
const submitting = ref<boolean>(false);
|
||||||
|
const uploadingPicture = ref<boolean>(false);
|
||||||
|
const geoLocationStatus = ref<GeoLocationStatus | null>(null);
|
||||||
|
|
||||||
|
const transaction = ref<Transaction | TransactionTemplate>(createNewTransactionModel(transactionDefaultType));
|
||||||
|
|
||||||
|
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||||
|
const showAccountBalance = computed<boolean>(() => settingsStore.appSettings.showAccountBalance);
|
||||||
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
|
const defaultAccountId = computed<string>(() => userStore.currentUserDefaultAccountId);
|
||||||
|
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||||
|
|
||||||
|
const allTimezones = computed<LocalizedTimezoneInfo[]>(() => getAllTimezones(true));
|
||||||
|
const allAccounts = computed<Account[]>(() => accountsStore.allPlainAccounts);
|
||||||
|
const allVisibleAccounts = computed<Account[]>(() => accountsStore.allVisiblePlainAccounts);
|
||||||
|
const allAccountsMap = computed<Record<string, Account>>(() => accountsStore.allAccountsMap);
|
||||||
|
const allVisibleCategorizedAccounts = computed<CategorizedAccountWithDisplayBalance[]>(() => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts.value, showAccountBalance.value));
|
||||||
|
const allCategories = computed<Record<number, TransactionCategory[]>>(() => transactionCategoriesStore.allTransactionCategories);
|
||||||
|
const allCategoriesMap = computed<Record<string, TransactionCategory>>(() => transactionCategoriesStore.allTransactionCategoriesMap);
|
||||||
|
const allTags = computed<TransactionTag[]>(() => transactionTagsStore.allTransactionTags);
|
||||||
|
const allTagsMap = computed<Record<string, TransactionTag>>(() => transactionTagsStore.allTransactionTagsMap);
|
||||||
|
|
||||||
|
const canAddTransactionPicture = computed<boolean>(() => {
|
||||||
|
if (type !== TransactionEditPageType.Transaction || (mode.value !== TransactionEditPageMode.Add && mode.value !== TransactionEditPageMode.Edit)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isArray(transaction.value.pictures) || transaction.value.pictures.length < TRANSACTION_MAX_PICTURE_COUNT;
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = computed<string>(() => {
|
||||||
|
if (type === TransactionEditPageType.Transaction) {
|
||||||
|
if (mode.value === TransactionEditPageMode.Add) {
|
||||||
|
return 'Add Transaction';
|
||||||
|
} else if (mode.value === TransactionEditPageMode.Edit) {
|
||||||
|
return 'Edit Transaction';
|
||||||
|
} else {
|
||||||
|
return 'Transaction Detail';
|
||||||
|
}
|
||||||
|
} else if (type === TransactionEditPageType.Template && (transaction.value as TransactionTemplate).templateType === TemplateType.Normal.type) {
|
||||||
|
if (mode.value === TransactionEditPageMode.Add) {
|
||||||
|
return 'Add Transaction Template';
|
||||||
|
} else if (mode.value === TransactionEditPageMode.Edit) {
|
||||||
|
return 'Edit Transaction Template';
|
||||||
|
}
|
||||||
|
} else if (type === TransactionEditPageType.Template && (transaction.value as TransactionTemplate).templateType === TemplateType.Schedule.type) {
|
||||||
|
if (mode.value === TransactionEditPageMode.Add) {
|
||||||
|
return 'Add Scheduled Transaction';
|
||||||
|
} else if (mode.value === TransactionEditPageMode.Edit) {
|
||||||
|
return 'Edit Scheduled Transaction';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveButtonTitle = computed<string>(() => {
|
||||||
|
if (mode.value === TransactionEditPageMode.Add) {
|
||||||
|
return 'Add';
|
||||||
|
} else {
|
||||||
|
return 'Save';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cancelButtonTitle = computed<string>(() => {
|
||||||
|
if (mode.value === TransactionEditPageMode.View) {
|
||||||
|
return 'Close';
|
||||||
|
} else {
|
||||||
|
return 'Cancel';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceAmountName = computed<string>(() => {
|
||||||
|
if (transaction.value.type === TransactionType.Expense) {
|
||||||
|
return 'Expense Amount';
|
||||||
|
} else if (transaction.value.type === TransactionType.Income) {
|
||||||
|
return 'Income Amount';
|
||||||
|
} else if (transaction.value.type === TransactionType.Transfer) {
|
||||||
|
return 'Transfer Out Amount';
|
||||||
|
} else {
|
||||||
|
return 'Amount';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceAccountTitle = computed<string>(() => {
|
||||||
|
if (transaction.value.type === TransactionType.Expense || transaction.value.type === TransactionType.Income) {
|
||||||
|
return 'Account';
|
||||||
|
} else if (transaction.value.type === TransactionType.Transfer) {
|
||||||
|
return 'Source Account';
|
||||||
|
} else {
|
||||||
|
return 'Account';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const transferInAmountTitle = computed<string>(() => {
|
||||||
|
const sourceAccount = allAccountsMap.value[transaction.value.sourceAccountId];
|
||||||
|
const destinationAccount = allAccountsMap.value[transaction.value.destinationAccountId];
|
||||||
|
|
||||||
|
if (!sourceAccount || !destinationAccount || sourceAccount.currency === destinationAccount.currency) {
|
||||||
|
return tt('Transfer In Amount');
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromExchangeRate = exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
|
||||||
|
const toExchangeRate = exchangeRatesStore.latestExchangeRateMap[destinationAccount.currency];
|
||||||
|
const amountRate = getAdaptiveAmountRate(transaction.value.sourceAmount, transaction.value.destinationAmount, fromExchangeRate, toExchangeRate);
|
||||||
|
|
||||||
|
if (!amountRate) {
|
||||||
|
return tt('Transfer In Amount');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tt('Transfer In Amount') + ` (${amountRate})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasAvailableExpenseCategories = computed<boolean>(() => {
|
||||||
|
if (!allCategories.value || !allCategories.value[CategoryType.Expense] || !allCategories.value[CategoryType.Expense].length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Expense]);
|
||||||
|
return firstAvailableCategoryId !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasAvailableIncomeCategories = computed<boolean>(() => {
|
||||||
|
if (!allCategories.value || !allCategories.value[CategoryType.Income] || !allCategories.value[CategoryType.Income].length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Income]);
|
||||||
|
return firstAvailableCategoryId !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasAvailableTransferCategories = computed<boolean>(() => {
|
||||||
|
if (!allCategories.value || !allCategories.value[CategoryType.Transfer] || !allCategories.value[CategoryType.Transfer].length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstAvailableCategoryId = getFirstAvailableCategoryId(allCategories.value[CategoryType.Transfer]);
|
||||||
|
return firstAvailableCategoryId !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceAccountName = computed<string>(() => {
|
||||||
|
if (transaction.value.sourceAccountId) {
|
||||||
|
return Account.findAccountNameById(allAccounts.value, transaction.value.sourceAccountId) || '';
|
||||||
|
} else {
|
||||||
|
return tt('None');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const destinationAccountName = computed<string>(() => {
|
||||||
|
if (transaction.value.destinationAccountId) {
|
||||||
|
return Account.findAccountNameById(allAccounts.value, transaction.value.destinationAccountId) || '';
|
||||||
|
} else {
|
||||||
|
return tt('None');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceAccountCurrency = computed<string>(() => {
|
||||||
|
const sourceAccount = allAccountsMap.value[transaction.value.sourceAccountId];
|
||||||
|
|
||||||
|
if (sourceAccount) {
|
||||||
|
return sourceAccount.currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultCurrency.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const destinationAccountCurrency = computed<string>(() => {
|
||||||
|
const destinationAccount = allAccountsMap.value[transaction.value.destinationAccountId];
|
||||||
|
|
||||||
|
if (destinationAccount) {
|
||||||
|
return destinationAccount.currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultCurrency.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const transactionDisplayTimezone = computed<string>(() => {
|
||||||
|
return `UTC${getUtcOffsetByUtcOffsetMinutes(transaction.value.utcOffset)}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const transactionTimezoneTimeDifference = computed<string>(() => {
|
||||||
|
return getTimezoneDifferenceDisplayText(transaction.value.utcOffset);
|
||||||
|
});
|
||||||
|
|
||||||
|
const geoLocationStatusInfo = computed<string>(() => {
|
||||||
|
if (geoLocationStatus.value === GeoLocationStatus.Success) {
|
||||||
|
return '';
|
||||||
|
} else if (geoLocationStatus.value === GeoLocationStatus.Getting) {
|
||||||
|
return tt('Getting Location...');
|
||||||
|
} else {
|
||||||
|
return tt('No Location');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputEmptyProblemMessage = computed<string | null>(() => {
|
||||||
|
if (transaction.value.type === TransactionType.Expense) {
|
||||||
|
if (!transaction.value.expenseCategoryId || transaction.value.expenseCategoryId === '') {
|
||||||
|
return 'Transaction category cannot be blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
|
||||||
|
return 'Transaction account cannot be blank';
|
||||||
|
}
|
||||||
|
} else if (transaction.value.type === TransactionType.Income) {
|
||||||
|
if (!transaction.value.incomeCategoryId || transaction.value.incomeCategoryId === '') {
|
||||||
|
return 'Transaction category cannot be blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
|
||||||
|
return 'Transaction account cannot be blank';
|
||||||
|
}
|
||||||
|
} else if (transaction.value.type === TransactionType.Transfer) {
|
||||||
|
if (!transaction.value.transferCategoryId || transaction.value.transferCategoryId === '') {
|
||||||
|
return 'Transaction category cannot be blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transaction.value.sourceAccountId || transaction.value.sourceAccountId === '') {
|
||||||
|
return 'Source account cannot be blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transaction.value.destinationAccountId || transaction.value.destinationAccountId === '') {
|
||||||
|
return 'Destination account cannot be blank';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'template' && transaction.value instanceof TransactionTemplate) {
|
||||||
|
if (!transaction.value.name) {
|
||||||
|
return 'Template name cannot be blank';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputIsEmpty = computed<boolean>(() => {
|
||||||
|
return !!inputEmptyProblemMessage.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
function createNewTransactionModel(transactionType?: number): Transaction | TransactionTemplate {
|
||||||
|
const now: number = getCurrentUnixTime();
|
||||||
|
const currentTimezone: string = settingsStore.appSettings.timeZone;
|
||||||
|
|
||||||
|
let defaultType: TransactionType = TransactionType.Expense;
|
||||||
|
|
||||||
|
if (transactionType === TransactionType.Income) {
|
||||||
|
defaultType = TransactionType.Income;
|
||||||
|
} else if (transactionType === TransactionType.Transfer) {
|
||||||
|
defaultType = TransactionType.Transfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newTransaction: Transaction | TransactionTemplate = Transaction.createNewTransaction(defaultType, now, currentTimezone, getTimezoneOffsetMinutes(currentTimezone));
|
||||||
|
|
||||||
|
if (type === TransactionEditPageType.Template) {
|
||||||
|
newTransaction = TransactionTemplate.createNewTransactionTemplate(newTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapTransactionData(swapAccount: boolean, swapAmount: boolean): void {
|
||||||
|
if (swapAccount) {
|
||||||
|
const oldSourceAccountId = transaction.value.sourceAccountId;
|
||||||
|
transaction.value.sourceAccountId = transaction.value.destinationAccountId;
|
||||||
|
transaction.value.destinationAccountId = oldSourceAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapAmount) {
|
||||||
|
const oldSourceAmount = transaction.value.sourceAmount;
|
||||||
|
transaction.value.sourceAmount = transaction.value.destinationAmount;
|
||||||
|
transaction.value.destinationAmount = oldSourceAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplayAmount(amount: number | string, hideAmount: boolean, currencyCode: string): string {
|
||||||
|
if (hideAmount) {
|
||||||
|
return formatAmountWithCurrency('***', currencyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatAmountWithCurrency(amount, currencyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTransactionPictureUrl(pictureInfo?: TransactionPictureInfoBasicResponse | null): string | undefined {
|
||||||
|
return transactionsStore.getTransactionPictureUrl(pictureInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => transaction.value.sourceAmount, (newValue, oldValue) => {
|
||||||
|
if (mode.value === TransactionEditPageMode.View || loading.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionsStore.setTransactionSuitableDestinationAmount(transaction.value, oldValue, newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => transaction.value.destinationAmount, (newValue) => {
|
||||||
|
if (mode.value === TransactionEditPageMode.View || loading.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transaction.value.type === TransactionType.Expense || transaction.value.type === TransactionType.Income) {
|
||||||
|
transaction.value.sourceAmount = newValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => transaction.value.timeZone, (newValue) => {
|
||||||
|
for (let i = 0; i < allTimezones.value.length; i++) {
|
||||||
|
if (allTimezones.value[i].name === newValue) {
|
||||||
|
transaction.value.utcOffset = allTimezones.value[i].utcOffsetMinutes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// constants
|
||||||
|
isSupportGeoLocation,
|
||||||
|
// states
|
||||||
|
mode,
|
||||||
|
editId,
|
||||||
|
addByTemplateId,
|
||||||
|
duplicateFromId,
|
||||||
|
clientSessionId,
|
||||||
|
loading,
|
||||||
|
submitting,
|
||||||
|
uploadingPicture,
|
||||||
|
geoLocationStatus,
|
||||||
|
transaction,
|
||||||
|
// computed states
|
||||||
|
currentTimezoneOffsetMinutes,
|
||||||
|
showAccountBalance,
|
||||||
|
defaultCurrency,
|
||||||
|
firstDayOfWeek,
|
||||||
|
defaultAccountId,
|
||||||
|
allTimezones,
|
||||||
|
allAccounts,
|
||||||
|
allVisibleAccounts,
|
||||||
|
allAccountsMap,
|
||||||
|
allVisibleCategorizedAccounts,
|
||||||
|
allCategories,
|
||||||
|
allCategoriesMap,
|
||||||
|
allTags,
|
||||||
|
allTagsMap,
|
||||||
|
canAddTransactionPicture,
|
||||||
|
title,
|
||||||
|
saveButtonTitle,
|
||||||
|
cancelButtonTitle,
|
||||||
|
sourceAmountName,
|
||||||
|
sourceAccountTitle,
|
||||||
|
transferInAmountTitle,
|
||||||
|
hasAvailableExpenseCategories,
|
||||||
|
hasAvailableIncomeCategories,
|
||||||
|
hasAvailableTransferCategories,
|
||||||
|
sourceAccountName,
|
||||||
|
destinationAccountName,
|
||||||
|
sourceAccountCurrency,
|
||||||
|
destinationAccountCurrency,
|
||||||
|
transactionDisplayTimezone,
|
||||||
|
transactionTimezoneTimeDifference,
|
||||||
|
geoLocationStatusInfo,
|
||||||
|
inputEmptyProblemMessage,
|
||||||
|
inputIsEmpty,
|
||||||
|
// functions
|
||||||
|
createNewTransactionModel,
|
||||||
|
swapTransactionData,
|
||||||
|
getDisplayAmount,
|
||||||
|
getTransactionPictureUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<edit-dialog ref="editDialog" type="template" :persistent="true" />
|
<edit-dialog ref="editDialog" :type="TransactionEditPageType.Template" :persistent="true" />
|
||||||
|
|
||||||
<confirm-dialog ref="confirmDialog"/>
|
<confirm-dialog ref="confirmDialog"/>
|
||||||
<snack-bar ref="snackbar" />
|
<snack-bar ref="snackbar" />
|
||||||
@@ -149,6 +149,7 @@
|
|||||||
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
import ConfirmDialog from '@/components/desktop/ConfirmDialog.vue';
|
||||||
import SnackBar from '@/components/desktop/SnackBar.vue';
|
import SnackBar from '@/components/desktop/SnackBar.vue';
|
||||||
import EditDialog from '@/views/desktop/transactions/list/dialogs/EditDialog.vue';
|
import EditDialog from '@/views/desktop/transactions/list/dialogs/EditDialog.vue';
|
||||||
|
import { TransactionEditPageType } from '@/views/base/transactions/TransactionEditPageBase.ts';
|
||||||
|
|
||||||
import { ref, computed, useTemplateRef } from 'vue';
|
import { ref, computed, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user