display amounts according to currency decimals number count

This commit is contained in:
MaysWind
2024-12-06 23:55:19 +08:00
parent e2f2b325a6
commit e549779164
15 changed files with 313 additions and 23 deletions
+11 -3
View File
@@ -44,6 +44,7 @@ export default {
'color',
'density',
'currency',
'showCurrency',
'label',
'placeholder',
'persistentPlaceholder',
@@ -100,7 +101,7 @@ export default {
return finalClass;
},
prependText() {
if (!this.currency) {
if (!this.currency || !this.showCurrency) {
return '';
}
@@ -113,7 +114,7 @@ export default {
return texts.prependText;
},
appendText() {
if (!this.currency) {
if (!this.currency || !this.showCurrency) {
return '';
}
@@ -127,6 +128,13 @@ export default {
}
},
watch: {
'currency': function () {
const newStringValue = this.getFormattedValue(this.userStore, this.modelValue);
if (!(newStringValue === '0' && this.currentValue === '')) {
this.currentValue = newStringValue;
}
},
'modelValue': function (newValue) {
const numericCurrentValue = this.$locale.parseAmount(this.userStore, this.currentValue);
@@ -300,7 +308,7 @@ export default {
getFormattedValue(userStore, value) {
if (!Number.isNaN(value) && Number.isFinite(value)) {
const digitGroupingSymbol = this.$locale.getCurrentDigitGroupingSymbol(userStore);
return removeAll(this.$locale.formatAmount(userStore, value), digitGroupingSymbol);
return removeAll(this.$locale.formatAmount(userStore, value, this.currency), digitGroupingSymbol);
}
return '0';
+18 -2
View File
@@ -43,9 +43,12 @@
<f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('+')">
<span class="numpad-button-text numpad-button-text-normal">&plus;</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputDecimalSeparator()">
<f7-button class="numpad-button numpad-button-num" v-if="supportDecimalSeparator" @click="inputDecimalSeparator()">
<span class="numpad-button-text numpad-button-text-normal">{{ decimalSeparator }}</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" v-if="!supportDecimalSeparator" @click="inputDoubleNum(0)">
<span class="numpad-button-text numpad-button-text-normal">00</span>
</f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(0)">
<span class="numpad-button-text numpad-button-text-normal">0</span>
</f7-button>
@@ -66,6 +69,7 @@
import { mapStores } from 'pinia';
import { useUserStore } from '@/stores/user.js';
import currencyConstants from '@/consts/currency.js';
import { isString, isNumber, removeAll } from '@/lib/common.js';
export default {
@@ -73,6 +77,7 @@ export default {
'modelValue',
'minValue',
'maxValue',
'currency',
'show'
],
emits: [
@@ -94,6 +99,13 @@ export default {
decimalSeparator() {
return this.$locale.getCurrentDecimalSeparator(this.userStore);
},
supportDecimalSeparator() {
if (!this.currency || !currencyConstants.all[this.currency] || !isNumber(currencyConstants.all[this.currency].fraction)) {
return true;
}
return currencyConstants.all[this.currency].fraction > 0;
},
currentDisplay() {
const previousValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.previousValue);
const currentValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.currentValue);
@@ -129,7 +141,7 @@ export default {
return '';
}
let str = this.$locale.formatAmount(userStore, value);
let str = this.$locale.formatAmount(userStore, value, this.currency);
const digitGroupingSymbol = this.$locale.getCurrentDigitGroupingSymbol(userStore);
@@ -208,6 +220,10 @@ export default {
this.currentValue = newValue;
},
inputDoubleNum(num) {
this.inputNum(num);
this.inputNum(num);
},
inputDecimalSeparator() {
if (this.currentValue.indexOf(this.decimalSeparator) >= 0) {
return;
File diff suppressed because it is too large Load Diff
+4
View File
@@ -140,6 +140,8 @@ const allAmountFilterTypeMap = {
};
const defaultDecimalSeparator = allDecimalSeparator.Dot;
const defaultDecimalNumberCount = 2;
const maxSupportedDecimalNumberCount = 2;
const defaultDigitGroupingSymbol = allDigitGroupingSymbol.Comma;
const defaultDigitGroupingType = allDigitGroupingType.ThousandsSeparator;
const defaultValue = 0;
@@ -158,6 +160,8 @@ export default {
allAmountFilterTypeArray: allAmountFilterTypeArray,
allAmountFilterTypeMap: allAmountFilterTypeMap,
defaultDecimalSeparator: defaultDecimalSeparator,
defaultDecimalNumberCount: defaultDecimalNumberCount,
maxSupportedDecimalNumberCount: maxSupportedDecimalNumberCount,
defaultDigitGroupingSymbol: defaultDigitGroupingSymbol,
defaultDigitGroupingType: defaultDigitGroupingType,
defaultValue: defaultValue,
+10
View File
@@ -2,6 +2,16 @@ import currencyConstants from '@/consts/currency.js';
import { isString, isNumber } from './common.js';
export function getCurrencyFraction(currencyCode) {
const currencyInfo = currencyConstants.all[currencyCode];
if (currencyInfo) {
return currencyInfo.fraction;
}
return null;
}
export function appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural) {
if (isNumber(value)) {
value = value.toString();
+7 -5
View File
@@ -50,6 +50,7 @@ import {
} from './numeral.js';
import {
getCurrencyFraction,
appendCurrencySymbol,
getAmountPrependAndAppendCurrencySymbol
} from './currency.js';
@@ -936,9 +937,10 @@ function getCurrentDigitGroupingType(translateFn, digitGrouping) {
return digitGroupingType.type;
}
function getNumberFormatOptions(translateFn, userStore) {
function getNumberFormatOptions(translateFn, userStore, currencyCode) {
return {
decimalSeparator: getCurrentDecimalSeparator(translateFn, userStore.currentUserDecimalSeparator),
decimalNumberCount: getCurrencyFraction(currencyCode),
digitGroupingSymbol: getCurrentDigitGroupingSymbol(translateFn, userStore.currentUserDigitGroupingSymbol),
digitGrouping: getCurrentDigitGroupingType(translateFn, userStore.currentUserDigitGrouping),
};
@@ -954,8 +956,8 @@ function getParsedAmountNumber(value, translateFn, userStore) {
return parseAmount(value, numberFormatOptions);
}
function getFormattedAmount(value, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
function getFormattedAmount(value, translateFn, userStore, currencyCode) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore, currencyCode);
return formatAmount(value, numberFormatOptions);
}
@@ -986,7 +988,7 @@ function getFormattedAmountWithCurrency(value, currencyCode, translateFn, userSt
const isPlural = value !== '100' && value !== '-100';
if (!notConvertValue) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore, currencyCode);
const hasIncompleteFlag = isString(value) && value.charAt(value.length - 1) === '+';
if (hasIncompleteFlag) {
@@ -1739,7 +1741,7 @@ export function i18nFunctions(i18nGlobal) {
getCurrentDigitGroupingType: (userStore) => getCurrentDigitGroupingType(i18nGlobal.t, userStore.currentUserDigitGrouping),
appendDigitGroupingSymbol: (userStore, value) => getNumberWithDigitGroupingSymbol(value, i18nGlobal.t, userStore),
parseAmount: (userStore, value) => getParsedAmountNumber(value, i18nGlobal.t, userStore),
formatAmount: (userStore, value) => getFormattedAmount(value, i18nGlobal.t, userStore),
formatAmount: (userStore, value, currencyCode) => getFormattedAmount(value, i18nGlobal.t, userStore, currencyCode),
formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore),
formatExchangeRateAmount: (userStore, value) => getFormattedExchangeRateAmount(value, i18nGlobal.t, userStore),
getAdaptiveAmountRate: (userStore, amount1, amount2, fromExchangeRate, toExchangeRate) => getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, i18nGlobal.t, userStore),
+27
View File
@@ -146,6 +146,11 @@ export function formatAmount(value, options) {
}
const decimalSeparator = options.decimalSeparator || numeralConstants.defaultDecimalSeparator.symbol;
let decimalNumberCount = options.decimalNumberCount;
if (!isNumber(decimalNumberCount) || decimalNumberCount > numeralConstants.maxSupportedDecimalNumberCount) {
decimalNumberCount = numeralConstants.defaultDecimalNumberCount;
}
let integer = '0';
let decimals = '00';
@@ -159,6 +164,18 @@ export function formatAmount(value, options) {
decimals = '0' + value;
}
if (decimalNumberCount === 0) {
if (decimals === '00') {
decimals = '';
} else if (decimals.charAt(1) === '0') {
decimals = decimals.charAt(0);
}
} else if (decimalNumberCount === 1) {
if (decimals.charAt(1) === '0') {
decimals = decimals.charAt(0);
}
}
if (options.trimTailZero) {
if (decimals.charAt(0) === '0' && decimals.charAt(1) === '0') {
decimals = '';
@@ -194,6 +211,16 @@ export function formatPercent(value, precision, lowPrecisionValue) {
return result + '%';
}
export function getAmountWithDecimalNumberCount(amount, decimalNumberCount) {
if (decimalNumberCount === 0) {
return Math.floor(amount / 100) * 100;
} else if (decimalNumberCount === 1) {
return Math.floor(amount / 10) * 10;
}
return amount;
}
export function formatExchangeRateAmount(exchangeRateAmount, options) {
if (!options) {
options = {};
+6 -1
View File
@@ -32,6 +32,8 @@ import {
getDay,
getDayOfWeekName
} from '@/lib/datetime.js';
import { getAmountWithDecimalNumberCount } from '@/lib/numeral.js';
import { getCurrencyFraction } from '@/lib/currency.js';
import { getFirstAvailableCategoryId } from '@/lib/category.js';
const emptyTransactionResult = {
@@ -624,7 +626,7 @@ export const useTransactionsStore = defineStore('transactions', {
geoLocation: null
};
},
setTransactionSuitableDestinationAmount(transaction, oldValue, newValue) {
setTransactionSuitableDestinationAmount(transaction, oldValue, newValue, destinationAccountCurrency) {
const accountsStore = useAccountsStore();
const exchangeRatesStore = useExchangeRatesStore();
@@ -635,15 +637,18 @@ export const useTransactionsStore = defineStore('transactions', {
const destinationAccount = accountsStore.allAccountsMap[transaction.destinationAccountId];
if (sourceAccount && destinationAccount && sourceAccount.currency !== destinationAccount.currency) {
const decimalNumberCount = getCurrencyFraction(destinationAccountCurrency);
const exchangedOldValue = exchangeRatesStore.getExchangedAmount(oldValue, sourceAccount.currency, destinationAccount.currency);
const exchangedNewValue = exchangeRatesStore.getExchangedAmount(newValue, sourceAccount.currency, destinationAccount.currency);
if (isNumber(exchangedOldValue)) {
oldValue = Math.floor(exchangedOldValue);
oldValue = getAmountWithDecimalNumberCount(oldValue, decimalNumberCount);
}
if (isNumber(exchangedNewValue)) {
newValue = Math.floor(exchangedNewValue);
newValue = getAmountWithDecimalNumberCount(newValue, decimalNumberCount);
}
}
+1
View File
@@ -26,6 +26,7 @@
<div class="mx-6 mt-4">
<span class="text-subtitle-2">{{ $t('Base Amount') }}</span>
<amount-input class="mt-2" density="compact"
:currency="baseCurrency"
:disabled="loading || !exchangeRatesData || !exchangeRatesData.exchangeRates || !exchangeRatesData.exchangeRates.length"
v-model="baseAmount"/>
</div>
@@ -131,6 +131,7 @@
<amount-input :disabled="loading || submitting || !!editAccountId"
:persistent-placeholder="true"
:currency="selectedAccount.currency"
:show-currency="true"
:label="currentAccountIndex < 0 ? $t('Account Balance') : $t('Sub-account Balance')"
:placeholder="currentAccountIndex < 0 ? $t('Account Balance') : $t('Sub-account Balance')"
v-model="selectedAccount.balance"/>
+6 -2
View File
@@ -256,10 +256,14 @@
<div class="d-flex align-center">
<span class="text-sm ml-3">{{ $t(filterType.name) }}</span>
<span class="text-sm ml-4" v-if="query.amountFilter && query.amountFilter.startsWith(`${filterType.type}:`) && currentAmountFilterType !== filterType.type">{{ queryAmount }}</span>
<amount-input class="transaction-amount-filter-value ml-4" density="compact" v-model="currentAmountFilterValue1"
<amount-input class="transaction-amount-filter-value ml-4" density="compact"
:currency="defaultCurrency"
v-model="currentAmountFilterValue1"
v-if="currentAmountFilterType === filterType.type"/>
<span class="ml-2 mr-2" v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2">~</span>
<amount-input class="transaction-amount-filter-value" density="compact" v-model="currentAmountFilterValue2"
<amount-input class="transaction-amount-filter-value" density="compact"
:currency="defaultCurrency"
v-model="currentAmountFilterValue2"
v-if="currentAmountFilterType === filterType.type && filterType.paramCount === 2"/>
<v-btn class="ml-2" density="compact" color="primary" variant="tonal"
@click="changeAmountFilter(filterType.type)"
@@ -85,6 +85,7 @@
<v-col cols="12" :md="transaction.type === allTransactionTypes.Transfer ? 6 : 12">
<amount-input class="transaction-edit-amount font-weight-bold"
:color="sourceAmountColor"
:currency="sourceAccountCurrency"
:readonly="mode === 'view'"
:disabled="loading || submitting"
:persistent-placeholder="true"
@@ -95,6 +96,7 @@
</v-col>
<v-col cols="12" :md="6" v-if="transaction.type === allTransactionTypes.Transfer">
<amount-input class="transaction-edit-amount font-weight-bold" color="primary"
:currency="destinationAccountCurrency"
:readonly="mode === 'view'"
:disabled="loading || submitting"
:persistent-placeholder="true"
@@ -637,6 +639,24 @@ export default {
return this.$t('None');
}
},
sourceAccountCurrency() {
const sourceAccount = this.allAccountsMap[this.transaction.sourceAccountId];
if (sourceAccount) {
return sourceAccount.currency;
}
return this.defaultCurrency;
},
destinationAccountCurrency() {
const destinationAccount = this.allAccountsMap[this.transaction.destinationAccountId];
if (destinationAccount) {
return destinationAccount.currency;
}
return this.defaultCurrency;
},
transactionDisplayTimezone() {
return `UTC${getUtcOffsetByUtcOffsetMinutes(this.transaction.utcOffset)}`;
},
@@ -737,7 +757,7 @@ export default {
return;
}
this.transactionsStore.setTransactionSuitableDestinationAmount(this.transaction, oldValue, newValue);
this.transactionsStore.setTransactionSuitableDestinationAmount(this.transaction, oldValue, newValue, this.destinationAccountCurrency);
},
'transaction.destinationAmount': function (newValue) {
if (this.mode === 'view' || this.loading) {
+2 -1
View File
@@ -36,6 +36,7 @@
>
<number-pad-sheet :min-value="allowedMinAmount"
:max-value="allowedMaxAmount"
:currency="baseCurrency"
v-model:show="showBaseAmountSheet"
v-model="baseAmount"
></number-pad-sheet>
@@ -123,7 +124,7 @@ export default {
return this.$locale.getAllDisplayExchangeRates(this.settingsStore, this.exchangeRatesData);
},
displayBaseAmount() {
return this.$locale.formatAmount(this.userStore, this.baseAmount);
return this.$locale.formatAmount(this.userStore, this.baseAmount, this.baseCurrency);
},
baseAmountFontSizeClass() {
if (this.baseAmount >= 100000000 || this.baseAmount <= -100000000) {
+2
View File
@@ -183,6 +183,7 @@
>
<number-pad-sheet :min-value="allowedMinAmount"
:max-value="allowedMaxAmount"
:currency="account.currency"
v-model:show="account.showBalanceSheet"
v-model="account.balance"
></number-pad-sheet>
@@ -396,6 +397,7 @@
>
<number-pad-sheet :min-value="allowedMinAmount"
:max-value="allowedMaxAmount"
:currency="subAccount.currency"
v-model:show="subAccount.showBalanceSheet"
v-model="subAccount.balance"
></number-pad-sheet>
+28 -8
View File
@@ -65,11 +65,12 @@
link="#" no-chevron
:class="sourceAmountClass"
:header="$t(sourceAmountName)"
:title="getDisplayAmount(transaction.sourceAmount, transaction.hideAmount)"
:title="getDisplayAmount(transaction.sourceAmount, transaction.hideAmount, sourceAccountCurrency)"
@click="showSourceAmountSheet = true"
>
<number-pad-sheet :min-value="allowedMinAmount"
:max-value="allowedMaxAmount"
:currency="sourceAccountCurrency"
v-model:show="showSourceAmountSheet"
v-model="transaction.sourceAmount"
></number-pad-sheet>
@@ -80,12 +81,13 @@
link="#" no-chevron
:class="destinationAmountClass"
:header="transferInAmountTitle"
:title="getDisplayAmount(transaction.destinationAmount, transaction.hideAmount)"
:title="getDisplayAmount(transaction.destinationAmount, transaction.hideAmount, destinationAccountCurrency)"
@click="showDestinationAmountSheet = true"
v-if="transaction.type === allTransactionTypes.Transfer"
>
<number-pad-sheet :min-value="allowedMinAmount"
:max-value="allowedMaxAmount"
:currency="destinationAccountCurrency"
v-model:show="showDestinationAmountSheet"
v-model="transaction.destinationAmount"
></number-pad-sheet>
@@ -672,6 +674,24 @@ export default {
return this.$t('None');
}
},
sourceAccountCurrency() {
const sourceAccount = this.allAccountsMap[this.transaction.sourceAccountId];
if (sourceAccount) {
return sourceAccount.currency;
}
return this.defaultCurrency;
},
destinationAccountCurrency() {
const destinationAccount = this.allAccountsMap[this.transaction.destinationAccountId];
if (destinationAccount) {
return destinationAccount.currency;
}
return this.defaultCurrency;
},
transactionDisplayDate() {
if (this.mode !== 'view' || !this.showTimeInDefaultTimezone) {
return this.$locale.formatUnixTimeToLongDate(this.userStore, getActualUnixTimeForStore(this.transaction.time, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
@@ -862,7 +882,7 @@ export default {
return;
}
this.transactionsStore.setTransactionSuitableDestinationAmount(this.transaction, oldValue, newValue);
this.transactionsStore.setTransactionSuitableDestinationAmount(this.transaction, oldValue, newValue, this.destinationAccountCurrency);
},
'transaction.destinationAmount': function (newValue) {
if (this.mode === 'view' || this.loading) {
@@ -1333,15 +1353,15 @@ export default {
return 'ebk-large-amount';
}
},
getDisplayAmount(amount, hideAmount) {
getDisplayAmount(amount, hideAmount, currencyCode) {
if (hideAmount) {
return this.getDisplayCurrency('***');
return this.getDisplayCurrency('***', currencyCode);
}
return this.getDisplayCurrency(amount);
return this.getDisplayCurrency(amount, currencyCode);
},
getDisplayCurrency(value) {
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, false);
getDisplayCurrency(value, currencyCode) {
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getPrimaryCategoryName(categoryId, allCategories) {
return getTransactionPrimaryCategoryName(categoryId, allCategories);