support setting decimal separator and digit grouping symbol

This commit is contained in:
MaysWind
2024-06-29 17:12:22 +08:00
parent d9c8142c51
commit 399413a270
51 changed files with 1280 additions and 582 deletions
+3
View File
@@ -618,6 +618,9 @@ func printUserInfo(user *models.User) {
fmt.Printf("[ShortDateFormat] %s (%d)\n", user.ShortDateFormat, user.ShortDateFormat)
fmt.Printf("[LongTimeFormat] %s (%d)\n", user.LongTimeFormat, user.LongTimeFormat)
fmt.Printf("[ShortTimeFormat] %s (%d)\n", user.ShortTimeFormat, user.ShortTimeFormat)
fmt.Printf("[DecimalSeparator] %s (%d)\n", user.DecimalSeparator, user.DecimalSeparator)
fmt.Printf("[DigitGroupingSymbol] %s (%d)\n", user.DigitGroupingSymbol, user.DigitGroupingSymbol)
fmt.Printf("[DigitGrouping] %s (%d)\n", user.DigitGrouping, user.DigitGrouping)
fmt.Printf("[Deleted] %t\n", user.Deleted)
fmt.Printf("[EmailVerified] %t\n", user.EmailVerified)
fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime)
+52
View File
@@ -8,6 +8,7 @@ import (
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/locales"
"github.com/mayswind/ezbookkeeping/pkg/log"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/services"
@@ -336,6 +337,57 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (any, *errs.Error)
userNew.ShortTimeFormat = models.SHORT_TIME_FORMAT_INVALID
}
if userUpdateReq.DecimalSeparator != nil && *userUpdateReq.DecimalSeparator != user.DecimalSeparator {
user.DecimalSeparator = *userUpdateReq.DecimalSeparator
userNew.DecimalSeparator = *userUpdateReq.DecimalSeparator
anythingUpdate = true
} else {
userNew.DecimalSeparator = models.DECIMAL_SEPARATOR_INVALID
}
if userUpdateReq.DigitGroupingSymbol != nil && *userUpdateReq.DigitGroupingSymbol != user.DigitGroupingSymbol {
user.DigitGroupingSymbol = *userUpdateReq.DigitGroupingSymbol
userNew.DigitGroupingSymbol = *userUpdateReq.DigitGroupingSymbol
anythingUpdate = true
} else {
userNew.DigitGroupingSymbol = models.DIGIT_GROUPING_SYMBOL_INVALID
}
if userUpdateReq.DigitGrouping != nil && *userUpdateReq.DigitGrouping != user.DigitGrouping {
user.DigitGrouping = *userUpdateReq.DigitGrouping
userNew.DigitGrouping = *userUpdateReq.DigitGrouping
anythingUpdate = true
} else {
userNew.DigitGrouping = models.DIGIT_GROUPING_TYPE_INVALID
}
if modifyUserLanguage || userNew.DecimalSeparator != models.DECIMAL_SEPARATOR_INVALID || userNew.DigitGroupingSymbol != models.DIGIT_GROUPING_SYMBOL_INVALID {
decimalSeparator := userNew.DecimalSeparator
digitGroupingSymbol := userNew.DigitGroupingSymbol
if userNew.DecimalSeparator == models.DECIMAL_SEPARATOR_INVALID {
decimalSeparator = user.DecimalSeparator
}
if userNew.DigitGroupingSymbol == models.DIGIT_GROUPING_SYMBOL_INVALID {
digitGroupingSymbol = user.DigitGroupingSymbol
}
locale := user.Language
if modifyUserLanguage {
locale = userNew.Language
}
if locale == "" {
locale = c.GetClientLocale()
}
if locales.IsDecimalSeparatorEqualsDigitGroupingSymbol(decimalSeparator, digitGroupingSymbol, locale) {
return nil, errs.ErrDecimalSeparatorAndDigitGroupingSymbolCannotBeEqual
}
}
if !anythingUpdate {
return nil, errs.ErrNothingWillBeUpdated
}
+24 -23
View File
@@ -6,27 +6,28 @@ import (
// Error codes related to users
var (
ErrLoginNameInvalid = NewNormalError(NormalSubcategoryUser, 0, http.StatusUnauthorized, "login name is invalid")
ErrLoginNameOrPasswordInvalid = NewNormalError(NormalSubcategoryUser, 1, http.StatusUnauthorized, "login name or password is invalid")
ErrLoginNameOrPasswordWrong = NewNormalError(NormalSubcategoryUser, 2, http.StatusUnauthorized, "login name or password is wrong")
ErrUserIdInvalid = NewNormalError(NormalSubcategoryUser, 3, http.StatusBadRequest, "user id is invalid")
ErrUsernameIsEmpty = NewNormalError(NormalSubcategoryUser, 4, http.StatusBadRequest, "username is empty")
ErrEmailIsEmpty = NewNormalError(NormalSubcategoryUser, 5, http.StatusBadRequest, "email is empty")
ErrNicknameIsEmpty = NewNormalError(NormalSubcategoryUser, 6, http.StatusBadRequest, "nickname is empty")
ErrPasswordIsEmpty = NewNormalError(NormalSubcategoryUser, 7, http.StatusBadRequest, "password is empty")
ErrUserDefaultCurrencyIsEmpty = NewNormalError(NormalSubcategoryUser, 8, http.StatusBadRequest, "user default currency is empty")
ErrUserDefaultCurrencyIsInvalid = NewNormalError(NormalSubcategoryUser, 9, http.StatusBadRequest, "user default currency is invalid")
ErrUserNotFound = NewNormalError(NormalSubcategoryUser, 10, http.StatusBadRequest, "user not found")
ErrUserPasswordWrong = NewNormalError(NormalSubcategoryUser, 11, http.StatusBadRequest, "password is wrong")
ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists")
ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists")
ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed")
ErrUserDefaultAccountIsInvalid = NewNormalError(NormalSubcategoryUser, 15, http.StatusBadRequest, "user default account is invalid")
ErrUserIsDisabled = NewNormalError(NormalSubcategoryUser, 16, http.StatusBadRequest, "user is disabled")
ErrEmptyIsInvalid = NewNormalError(NormalSubcategoryUser, 17, http.StatusBadRequest, "email is invalid")
ErrEmailIsEmptyOrInvalid = NewNormalError(NormalSubcategoryUser, 18, http.StatusBadRequest, "email is empty or invalid")
ErrNewPasswordEqualsOldInvalid = NewNormalError(NormalSubcategoryUser, 19, http.StatusBadRequest, "new password equals old password")
ErrEmailIsNotVerified = NewNormalError(NormalSubcategoryUser, 20, http.StatusBadRequest, "email is not verified")
ErrEmailIsVerified = NewNormalError(NormalSubcategoryUser, 21, http.StatusBadRequest, "email is verified")
ErrEmailValidationNotAllowed = NewNormalError(NormalSubcategoryUser, 22, http.StatusBadRequest, "email validation not allowed")
ErrLoginNameInvalid = NewNormalError(NormalSubcategoryUser, 0, http.StatusUnauthorized, "login name is invalid")
ErrLoginNameOrPasswordInvalid = NewNormalError(NormalSubcategoryUser, 1, http.StatusUnauthorized, "login name or password is invalid")
ErrLoginNameOrPasswordWrong = NewNormalError(NormalSubcategoryUser, 2, http.StatusUnauthorized, "login name or password is wrong")
ErrUserIdInvalid = NewNormalError(NormalSubcategoryUser, 3, http.StatusBadRequest, "user id is invalid")
ErrUsernameIsEmpty = NewNormalError(NormalSubcategoryUser, 4, http.StatusBadRequest, "username is empty")
ErrEmailIsEmpty = NewNormalError(NormalSubcategoryUser, 5, http.StatusBadRequest, "email is empty")
ErrNicknameIsEmpty = NewNormalError(NormalSubcategoryUser, 6, http.StatusBadRequest, "nickname is empty")
ErrPasswordIsEmpty = NewNormalError(NormalSubcategoryUser, 7, http.StatusBadRequest, "password is empty")
ErrUserDefaultCurrencyIsEmpty = NewNormalError(NormalSubcategoryUser, 8, http.StatusBadRequest, "user default currency is empty")
ErrUserDefaultCurrencyIsInvalid = NewNormalError(NormalSubcategoryUser, 9, http.StatusBadRequest, "user default currency is invalid")
ErrUserNotFound = NewNormalError(NormalSubcategoryUser, 10, http.StatusBadRequest, "user not found")
ErrUserPasswordWrong = NewNormalError(NormalSubcategoryUser, 11, http.StatusBadRequest, "password is wrong")
ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists")
ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists")
ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed")
ErrUserDefaultAccountIsInvalid = NewNormalError(NormalSubcategoryUser, 15, http.StatusBadRequest, "user default account is invalid")
ErrUserIsDisabled = NewNormalError(NormalSubcategoryUser, 16, http.StatusBadRequest, "user is disabled")
ErrEmptyIsInvalid = NewNormalError(NormalSubcategoryUser, 17, http.StatusBadRequest, "email is invalid")
ErrEmailIsEmptyOrInvalid = NewNormalError(NormalSubcategoryUser, 18, http.StatusBadRequest, "email is empty or invalid")
ErrNewPasswordEqualsOldInvalid = NewNormalError(NormalSubcategoryUser, 19, http.StatusBadRequest, "new password equals old password")
ErrEmailIsNotVerified = NewNormalError(NormalSubcategoryUser, 20, http.StatusBadRequest, "email is not verified")
ErrEmailIsVerified = NewNormalError(NormalSubcategoryUser, 21, http.StatusBadRequest, "email is verified")
ErrEmailValidationNotAllowed = NewNormalError(NormalSubcategoryUser, 22, http.StatusBadRequest, "email validation not allowed")
ErrDecimalSeparatorAndDigitGroupingSymbolCannotBeEqual = NewNormalError(NormalSubcategoryUser, 23, http.StatusBadRequest, "decimal separator and digit grouping symbol cannot be equal")
)
+24
View File
@@ -1,5 +1,7 @@
package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
// DefaultLanguage represents the default language
var DefaultLanguage = en
@@ -22,3 +24,25 @@ func GetLocaleTextItems(locale string) *LocaleTextItems {
return DefaultLanguage
}
func IsDecimalSeparatorEqualsDigitGroupingSymbol(decimalSeparator models.DecimalSeparator, digitGroupingSymbol models.DigitGroupingSymbol, locale string) bool {
if decimalSeparator == models.DECIMAL_SEPARATOR_DEFAULT && digitGroupingSymbol == models.DIGIT_GROUPING_SYMBOL_DEFAULT {
return false
}
if byte(decimalSeparator) == byte(digitGroupingSymbol) {
return true
}
localeTextItems := GetLocaleTextItems(locale)
if decimalSeparator == models.DECIMAL_SEPARATOR_DEFAULT {
decimalSeparator = localeTextItems.DefaultTypes.DecimalSeparator
}
if digitGroupingSymbol == models.DIGIT_GROUPING_SYMBOL_DEFAULT {
digitGroupingSymbol = localeTextItems.DefaultTypes.DigitGroupingSymbol
}
return byte(decimalSeparator) == byte(digitGroupingSymbol)
}
+8
View File
@@ -1,11 +1,19 @@
package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
// LocaleTextItems represents all text items need to be translated
type LocaleTextItems struct {
DefaultTypes *DefaultTypes
VerifyEmailTextItems *VerifyEmailTextItems
ForgetPasswordMailTextItems *ForgetPasswordMailTextItems
}
type DefaultTypes struct {
DecimalSeparator models.DecimalSeparator
DigitGroupingSymbol models.DigitGroupingSymbol
}
// VerifyEmailTextItems represents text items need to be translated in verify mail
type VerifyEmailTextItems struct {
Title string
+6
View File
@@ -1,6 +1,12 @@
package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
var en = &LocaleTextItems{
DefaultTypes: &DefaultTypes{
DecimalSeparator: models.DECIMAL_SEPARATOR_DOT,
DigitGroupingSymbol: models.DIGIT_GROUPING_SYMBOL_COMMA,
},
VerifyEmailTextItems: &VerifyEmailTextItems{
Title: "Verify Email",
SalutationFormat: "Hi %s,",
+6
View File
@@ -1,6 +1,12 @@
package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
var zhHans = &LocaleTextItems{
DefaultTypes: &DefaultTypes{
DecimalSeparator: models.DECIMAL_SEPARATOR_DOT,
DigitGroupingSymbol: models.DIGIT_GROUPING_SYMBOL_COMMA,
},
VerifyEmailTextItems: &VerifyEmailTextItems{
Title: "验证邮箱",
SalutationFormat: "%s 您好,",
+95
View File
@@ -0,0 +1,95 @@
package models
import (
"fmt"
)
// DecimalSeparator represents the type of decimal separator
type DecimalSeparator byte
// Decimal Separator
const (
DECIMAL_SEPARATOR_DEFAULT DecimalSeparator = 0
DECIMAL_SEPARATOR_DOT DecimalSeparator = 1
DECIMAL_SEPARATOR_COMMA DecimalSeparator = 2
DECIMAL_SEPARATOR_SPACE DecimalSeparator = 3
DECIMAL_SEPARATOR_INVALID DecimalSeparator = 255
)
// String returns a textual representation of the decimal separator enum
func (f DecimalSeparator) String() string {
switch f {
case DECIMAL_SEPARATOR_DEFAULT:
return "Default"
case DECIMAL_SEPARATOR_DOT:
return "Dot"
case DECIMAL_SEPARATOR_COMMA:
return "Comma"
case DECIMAL_SEPARATOR_SPACE:
return "Space"
case DECIMAL_SEPARATOR_INVALID:
return "Invalid"
default:
return fmt.Sprintf("Invalid(%d)", int(f))
}
}
// DigitGroupingSymbol represents the digit grouping symbol
type DigitGroupingSymbol byte
// Digit Grouping Symbol
const (
DIGIT_GROUPING_SYMBOL_DEFAULT DigitGroupingSymbol = 0
DIGIT_GROUPING_SYMBOL_DOT DigitGroupingSymbol = 1
DIGIT_GROUPING_SYMBOL_COMMA DigitGroupingSymbol = 2
DIGIT_GROUPING_SYMBOL_SPACE DigitGroupingSymbol = 3
DIGIT_GROUPING_SYMBOL_APOSTROPHE DigitGroupingSymbol = 4
DIGIT_GROUPING_SYMBOL_INVALID DigitGroupingSymbol = 255
)
// String returns a textual representation of the digit grouping symbol enum
func (f DigitGroupingSymbol) String() string {
switch f {
case DIGIT_GROUPING_SYMBOL_DEFAULT:
return "Default"
case DIGIT_GROUPING_SYMBOL_DOT:
return "Dot"
case DIGIT_GROUPING_SYMBOL_COMMA:
return "Comma"
case DIGIT_GROUPING_SYMBOL_SPACE:
return "Space"
case DIGIT_GROUPING_SYMBOL_APOSTROPHE:
return "Apostrophe"
case DIGIT_GROUPING_SYMBOL_INVALID:
return "Invalid"
default:
return fmt.Sprintf("Invalid(%d)", int(f))
}
}
// DigitGroupingType represents digit grouping type
type DigitGroupingType byte
// Digit Grouping Type
const (
DIGIT_GROUPING_TYPE_DEFAULT DigitGroupingType = 0
DIGIT_GROUPING_TYPE_NONE DigitGroupingType = 1
DIGIT_GROUPING_TYPE_THOUSANDS_SEPARATOR DigitGroupingType = 2
DIGIT_GROUPING_TYPE_INVALID DigitGroupingType = 255
)
// String returns a textual representation of the digit grouping type enum
func (d DigitGroupingType) String() string {
switch d {
case DIGIT_GROUPING_TYPE_DEFAULT:
return "Default"
case DIGIT_GROUPING_TYPE_NONE:
return "None"
case DIGIT_GROUPING_TYPE_THOUSANDS_SEPARATOR:
return "Thousands Separator"
case DIGIT_GROUPING_TYPE_INVALID:
return "Invalid"
default:
return fmt.Sprintf("Invalid(%d)", int(d))
}
}
+18
View File
@@ -64,6 +64,9 @@ type User struct {
ShortDateFormat ShortDateFormat `xorm:"TINYINT"`
LongTimeFormat LongTimeFormat `xorm:"TINYINT"`
ShortTimeFormat ShortTimeFormat `xorm:"TINYINT"`
DecimalSeparator DecimalSeparator `xorm:"TINYINT"`
DigitGroupingSymbol DigitGroupingSymbol `xorm:"TINYINT"`
DigitGrouping DigitGroupingType `xorm:"TINYINT"`
Disabled bool
Deleted bool `xorm:"NOT NULL"`
EmailVerified bool `xorm:"NOT NULL"`
@@ -89,6 +92,9 @@ type UserBasicInfo struct {
ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
DecimalSeparator DecimalSeparator `json:"decimalSeparator"`
DigitGroupingSymbol DigitGroupingSymbol `json:"digitGroupingSymbol"`
DigitGrouping DigitGroupingType `json:"digitGrouping"`
EmailVerified bool `json:"emailVerified"`
}
@@ -142,6 +148,9 @@ type UserProfileUpdateRequest struct {
ShortDateFormat *ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"`
LongTimeFormat *LongTimeFormat `json:"longTimeFormat" binding:"omitempty,min=0,max=3"`
ShortTimeFormat *ShortTimeFormat `json:"shortTimeFormat" binding:"omitempty,min=0,max=3"`
DecimalSeparator *DecimalSeparator `json:"decimalSeparator" binding:"omitempty,min=0,max=3"`
DigitGroupingSymbol *DigitGroupingSymbol `json:"digitGroupingSymbol" binding:"omitempty,min=0,max=4"`
DigitGrouping *DigitGroupingType `json:"digitGrouping" binding:"omitempty,min=0,max=2"`
}
// UserProfileUpdateResponse represents the data returns to frontend after updating profile
@@ -166,6 +175,9 @@ type UserProfileResponse struct {
ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
DecimalSeparator DecimalSeparator `json:"decimalSeparator"`
DigitGroupingSymbol DigitGroupingSymbol `json:"digitGroupingSymbol"`
DigitGrouping DigitGroupingType `json:"digitGrouping"`
EmailVerified bool `json:"emailVerified"`
LastLoginAt int64 `json:"lastLoginAt"`
}
@@ -229,6 +241,9 @@ func (u *User) ToUserBasicInfo() *UserBasicInfo {
ShortDateFormat: u.ShortDateFormat,
LongTimeFormat: u.LongTimeFormat,
ShortTimeFormat: u.ShortTimeFormat,
DecimalSeparator: u.DecimalSeparator,
DigitGroupingSymbol: u.DigitGroupingSymbol,
DigitGrouping: u.DigitGrouping,
EmailVerified: u.EmailVerified,
}
}
@@ -250,6 +265,9 @@ func (u *User) ToUserProfileResponse() *UserProfileResponse {
ShortDateFormat: u.ShortDateFormat,
LongTimeFormat: u.LongTimeFormat,
ShortTimeFormat: u.ShortTimeFormat,
DecimalSeparator: u.DecimalSeparator,
DigitGroupingSymbol: u.DigitGroupingSymbol,
DigitGrouping: u.DigitGrouping,
EmailVerified: u.EmailVerified,
LastLoginAt: u.LastLoginUnixTime,
}
+12
View File
@@ -248,6 +248,18 @@ func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserL
updateCols = append(updateCols, "short_time_format")
}
if models.DECIMAL_SEPARATOR_DEFAULT <= user.DecimalSeparator && user.DecimalSeparator <= models.DECIMAL_SEPARATOR_SPACE {
updateCols = append(updateCols, "decimal_separator")
}
if models.DIGIT_GROUPING_SYMBOL_DEFAULT <= user.DigitGroupingSymbol && user.DigitGroupingSymbol <= models.DIGIT_GROUPING_SYMBOL_APOSTROPHE {
updateCols = append(updateCols, "digit_grouping_symbol")
}
if models.DIGIT_GROUPING_TYPE_DEFAULT <= user.DigitGrouping && user.DigitGrouping <= models.DIGIT_GROUPING_TYPE_THOUSANDS_SEPARATOR {
updateCols = append(updateCols, "digit_grouping")
}
user.UpdatedUnixTime = now
updateCols = append(updateCols, "updated_unix_time")
+141 -35
View File
@@ -1,12 +1,12 @@
<template>
<v-text-field class="text-field-with-colored-label"
:type="hide ? 'password' : 'number'" :class="extraClass"
:type="hide ? 'password' : 'text'" :class="extraClass"
:color="color" :base-color="color"
:density="density" :readonly="!!readonly" :disabled="!!disabled"
:label="label" :placeholder="placeholder"
:persistent-placeholder="!!persistentPlaceholder"
:rules="enableRules ? rules : []" v-model="currentValue"
@keydown="onKeyUpDown" @keyup="onKeyUpDown">
@keydown="onKeyUpDown" @keyup="onKeyUpDown" @paste="onPaste">
<template #prepend-inner v-if="currency && prependText">
<div>{{ prependText }}</div>
</template>
@@ -19,9 +19,10 @@
<script>
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import transactionConstants from '@/consts/transaction.js';
import { numericCurrencyToString, stringCurrencyToNumeric } from '@/lib/currency.js';
import { removeAll } from '@/lib/common.js';
export default {
props: [
@@ -43,9 +44,10 @@ export default {
],
data() {
const self = this;
const userStore = useUserStore();
return {
currentValue: numericCurrencyToString(self.modelValue),
currentValue: self.getFormattedValue(userStore, self.modelValue),
rules: [
(v) => {
if (v === '') {
@@ -53,8 +55,13 @@ export default {
}
try {
const val = parseFloat(v);
return (val >= transactionConstants.minAmount && val <= transactionConstants.maxAmount) || self.$t('Amount value exceeds limitation');
const val = self.$locale.parseAmount(userStore, v);
if (Number.isNaN(val) || !Number.isFinite(val)) {
return self.$t('Amount value is not number');
}
return (val >= transactionConstants.minAmountNumber && val <= transactionConstants.maxAmountNumber) || self.$t('Amount value exceeds limitation');
} catch (e) {
return self.$t('Amount value is not number');
}
@@ -63,7 +70,7 @@ export default {
}
},
computed: {
...mapStores(useSettingsStore),
...mapStores(useSettingsStore, useUserStore),
extraClass() {
let finalClass = this.class;
@@ -106,10 +113,10 @@ export default {
},
watch: {
'modelValue': function (newValue) {
const numericCurrentValue = stringCurrencyToNumeric(this.currentValue);
const numericCurrentValue = this.$locale.parseAmount(this.userStore, this.currentValue);
if (newValue !== numericCurrentValue) {
const newStringValue = numericCurrencyToString(newValue, false, true);
const newStringValue = this.getFormattedValue(this.userStore, newValue);
if (!(newStringValue === '0' && this.currentValue === '')) {
this.currentValue = newStringValue;
@@ -117,57 +124,156 @@ export default {
}
},
'currentValue': function (newValue) {
this.$emit('update:modelValue', stringCurrencyToNumeric(newValue));
this.$emit('update:modelValue', this.$locale.parseAmount(this.userStore, newValue));
}
},
methods: {
onKeyUpDown(e) {
if (e.target.value === '' || e.target.value === 0) {
if (e.altKey || e.ctrlKey || e.metaKey || (e.code.indexOf('F') === 0 && (e.code.length === 2 || e.code.length === 3))
|| e.code === 'ArrowLeft' || e.code === 'ArrowRight'
|| e.code === 'Home' || e.code === 'End' || e.code === 'Tab'
|| e.code === 'Backspace' || e.code === 'Delete' || e.code === 'Del') {
return;
}
const digitGroupingSymbol = this.$locale.getCurrentDigitGroupingSymbol(this.userStore);
const decimalSeparator = this.$locale.getCurrentDecimalSeparator(this.userStore);
if (e.code.indexOf('Digit') !== 0 && e.code !== 'Minus' && e.key !== decimalSeparator) {
e.preventDefault();
return;
}
let str = e.target.value;
if (str.indexOf(digitGroupingSymbol) >= 0) {
str = removeAll(str, digitGroupingSymbol);
}
if (e.code === 'Minus' && str.lastIndexOf('-') > 0) {
const lastMinusPos = str.lastIndexOf('-');
e.target.value = str.substring(0, lastMinusPos) + str.substring(lastMinusPos + 1, str.length);
this.currentValue = e.target.value;
e.preventDefault();
return;
}
if (e.key === decimalSeparator && str.indexOf(decimalSeparator) !== str.lastIndexOf(decimalSeparator)) {
const lastDecimalSeparatorPos = str.lastIndexOf(decimalSeparator);
e.target.value = str.substring(0, lastDecimalSeparatorPos) + str.substring(lastDecimalSeparatorPos + 1, str.length);
this.currentValue = e.target.value;
e.preventDefault();
return;
}
if (e.key === decimalSeparator && (str.indexOf(decimalSeparator) === 0 || (str.indexOf(decimalSeparator) === 1 && str.charAt(0) === '-'))) {
const negative = str.charAt(0) === '-';
if (negative) {
str = str.substring(1);
}
str = (negative ? '-0' : '0') + str;
e.target.value = str;
this.currentValue = e.target.value;
e.preventDefault();
return;
}
let decimalLength = 0;
let decimalIndex = e.target.value.indexOf('.');
let decimalIndex = str.indexOf(decimalSeparator);
if (decimalIndex >= 0) {
decimalLength = e.target.value.length - e.target.value.indexOf('.') - 1;
}
decimalLength = str.length - str.indexOf(decimalSeparator) - 1;
} else if ((str.startsWith('0') && str.length >= 2) || (str.startsWith('-0') && str.length >= 3)) {
const negative = str.charAt(0) === '-';
if (negative) {
str = str.substring(1);
}
while (str.charAt(0) === '0' && (str.length >= 2 || e.code !== 'Digit0')) {
str = str.substring(1);
}
e.target.value = (negative ? '-' : '') + str;
this.currentValue = e.target.value;
e.preventDefault();
return;
}
if (decimalLength > 2) {
e.target.value = e.target.value.substring(0, Math.min(decimalIndex + 3, e.target.value.length - 1));
e.target.value = str.substring(0, Math.min(decimalIndex + 3, str.length - 1));
this.currentValue = e.target.value;
e.preventDefault();
return;
}
try {
const val = this.$locale.parseAmount(this.userStore, str);
const finalValue = this.getValidFormattedValue(val, str, decimalIndex >= 0);
const val = parseFloat(e.target.value);
let maxLength = transactionConstants.maxAmount.toString().length;
if (val < 0) {
maxLength = transactionConstants.minAmount.toString().length;
}
if (val < transactionConstants.minAmount) {
e.target.value = transactionConstants.minAmount;
this.currentValue = e.target.value;
e.preventDefault();
} else if (val > transactionConstants.maxAmount) {
e.target.value = transactionConstants.maxAmount;
this.currentValue = e.target.value;
e.preventDefault();
} else if (e.target.value.length > maxLength) {
e.target.value = e.target.value.substring(0, maxLength);
this.currentValue = e.target.value;
if (finalValue !== str) {
e.target.value = finalValue;
this.currentValue = finalValue;
e.preventDefault();
}
} catch (e) {
e.target.value = 0;
e.target.value = '0';
}
},
onPaste(e) {
if (!e.clipboardData) {
e.preventDefault();
return;
}
const text = e.clipboardData.getData('Text');
if (!text) {
e.preventDefault();
return;
}
const value = this.$locale.parseAmount(this.userStore, text);
const textualValue = this.getFormattedValue(this.userStore, value);
const decimalSeparator = this.$locale.getCurrentDecimalSeparator(this.userStore);
const hasDecimalSeparator = text.indexOf(decimalSeparator) >= 0;
this.currentValue = this.getValidFormattedValue(value, textualValue, hasDecimalSeparator);
e.preventDefault();
},
getValidFormattedValue(value, textualValue, hasDecimalSeparator) {
let maxLength = transactionConstants.maxAmountNumber.toString().length;
if (value < 0) {
maxLength = transactionConstants.minAmountNumber.toString().length;
}
if (value < transactionConstants.minAmountNumber) {
return this.getFormattedValue(this.userStore, transactionConstants.minAmountNumber);
} else if (value > transactionConstants.maxAmountNumber) {
return this.getFormattedValue(this.userStore, transactionConstants.maxAmountNumber);
}
if (!hasDecimalSeparator && textualValue.length > maxLength) {
return textualValue.substring(0, maxLength);
} else if (hasDecimalSeparator && textualValue.length > maxLength + 1) {
return textualValue.substring(0, maxLength + 1);
}
return textualValue;
},
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 '0';
},
getDisplayCurrencyPrependAndAppendText() {
return this.$locale.getDisplayCurrencyPrependAndAppendText(this.currency, this.settingsStore.appSettings.currencyDisplayMode);
return this.$locale.getAmountPrependAndAppendText(this.settingsStore, this.userStore, this.currency);
}
}
}
+3 -5
View File
@@ -8,6 +8,7 @@ import { useTheme } from 'vuetify';
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import colorConstants from '@/consts/color.js';
import { formatPercent } from '@/lib/numeral.js';
@@ -37,7 +38,7 @@ export default {
};
},
computed: {
...mapStores(useSettingsStore),
...mapStores(useSettingsStore, useUserStore),
isDarkMode() {
return this.globalTheme.global.name.value === 'dark';
},
@@ -289,10 +290,7 @@ export default {
return color;
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
}
}
}
+1 -4
View File
@@ -353,10 +353,7 @@ export default {
this.selectedLegends = e.selected;
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
}
}
}
+43 -47
View File
@@ -43,8 +43,8 @@
<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="inputDot()">
<span class="numpad-button-text numpad-button-text-normal">.</span>
<f7-button class="numpad-button numpad-button-num" @click="inputDecimalSeparator()">
<span class="numpad-button-text numpad-button-text-normal">{{ decimalSeparator }}</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>
@@ -64,11 +64,9 @@
<script>
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { isString } from '@/lib/common.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { numericCurrencyToString, stringCurrencyToNumeric } from '@/lib/currency.js';
import { isNumber, removeAll } from '@/lib/common.js';
export default {
props: [
@@ -83,21 +81,22 @@ export default {
],
data() {
const self = this;
const userStore = useUserStore();
return {
previousValue: '',
currentSymbol: '',
currentValue: self.getStringValue(self.modelValue)
currentValue: self.getStringValue(userStore, self.modelValue)
}
},
computed: {
...mapStores(useSettingsStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
...mapStores(useUserStore),
decimalSeparator() {
return this.$locale.getCurrentDecimalSeparator(this.userStore);
},
currentDisplay() {
const previousValue = appendThousandsSeparator(this.previousValue, this.isEnableThousandsSeparator);
const currentValue = appendThousandsSeparator(this.currentValue, this.isEnableThousandsSeparator);
const previousValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.previousValue);
const currentValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.currentValue);
if (this.currentSymbol) {
return `${previousValue} ${this.currentSymbol} ${currentValue}`;
@@ -125,16 +124,19 @@ export default {
}
},
methods: {
getStringValue(value) {
let str = numericCurrencyToString(value, this.isEnableThousandsSeparator);
getStringValue(userStore, value) {
let str = this.$locale.formatAmount(userStore, value);
if (str.indexOf(',')) {
str = str.replaceAll(/,/g, '');
const digitGroupingSymbol = this.$locale.getCurrentDigitGroupingSymbol(userStore);
if (str.indexOf(digitGroupingSymbol) >= 0) {
str = removeAll(str, digitGroupingSymbol);
}
const dotPos = str.indexOf('.');
const decimalSeparator = this.$locale.getCurrentDecimalSeparator(userStore);
const decimalSeparatorPos = str.indexOf(decimalSeparator);
if (dotPos < 0) {
if (decimalSeparatorPos < 0) {
if (str === '0') {
return '';
}
@@ -142,8 +144,8 @@ export default {
return str;
}
let integer = str.substring(0, dotPos);
let decimals = str.substring(dotPos + 1, str.length);
let integer = str.substring(0, decimalSeparatorPos);
let decimals = str.substring(decimalSeparatorPos + 1, str.length);
let newDecimals = '';
for (let i = decimals.length - 1; i >= 0; i--) {
@@ -160,7 +162,7 @@ export default {
return integer;
}
return `${integer}.${newDecimals}`;
return `${integer}${decimalSeparator}${newDecimals}`;
},
inputNum(num) {
if (!this.previousValue && this.currentSymbol === '') {
@@ -176,36 +178,34 @@ export default {
return;
}
const dotPos = this.currentValue.indexOf('.');
const decimalSeparatorPos = this.currentValue.indexOf(this.decimalSeparator);
if (dotPos >= 0 && this.currentValue.substring(dotPos + 1, this.currentValue.length).length >= 2) {
if (decimalSeparatorPos >= 0 && this.currentValue.substring(decimalSeparatorPos + 1, this.currentValue.length).length >= 2) {
return;
}
const newValue = this.currentValue + num.toString();
if (isString(this.minValue) && this.minValue !== '') {
const min = stringCurrencyToNumeric(this.minValue);
const current = stringCurrencyToNumeric(newValue);
if (isNumber(this.minValue)) {
const current = this.$locale.parseAmount(this.userStore, newValue);
if (current < min) {
if (current < this.minValue) {
return;
}
}
if (isString(this.maxValue) && this.maxValue !== '') {
const max = stringCurrencyToNumeric(this.maxValue);
const current = stringCurrencyToNumeric(newValue);
if (isNumber(this.maxValue)) {
const current = this.$locale.parseAmount(this.userStore, newValue);
if (current > max) {
if (current > this.maxValue) {
return;
}
}
this.currentValue = newValue;
},
inputDot() {
if (this.currentValue.indexOf('.') >= 0) {
inputDecimalSeparator() {
if (this.currentValue.indexOf(this.decimalSeparator) >= 0) {
return;
}
@@ -220,7 +220,7 @@ export default {
this.currentValue = '-0';
}
this.currentValue = this.currentValue + '.';
this.currentValue = this.currentValue + this.decimalSeparator;
},
setSymbol(symbol) {
if (this.currentValue) {
@@ -258,8 +258,8 @@ export default {
},
confirm() {
if (this.currentSymbol && this.currentValue.length >= 1) {
const previousValue = stringCurrencyToNumeric(this.previousValue);
const currentValue = stringCurrencyToNumeric(this.currentValue);
const previousValue = this.$locale.parseAmount(this.userStore, this.previousValue);
const currentValue = this.$locale.parseAmount(this.userStore, this.currentValue);
let finalValue = 0;
switch (this.currentSymbol) {
@@ -276,25 +276,21 @@ export default {
finalValue = previousValue;
}
if (isString(this.minValue) && this.minValue !== '') {
const min = stringCurrencyToNumeric(this.minValue);
if (finalValue < min) {
if (isNumber(this.minValue)) {
if (finalValue < this.minValue) {
this.$toast('Numeric Overflow');
return false;
}
}
if (isString(this.maxValue) && this.maxValue !== '') {
const max = stringCurrencyToNumeric(this.maxValue);
if (finalValue > max) {
if (isNumber(this.maxValue)) {
if (finalValue > this.maxValue) {
this.$toast('Numeric Overflow');
return false;
}
}
this.currentValue = this.getStringValue(finalValue);
this.currentValue = this.getStringValue(this.userStore, finalValue);
this.previousValue = '';
this.currentSymbol = '';
@@ -306,7 +302,7 @@ export default {
return true;
} else {
const value = stringCurrencyToNumeric(this.currentValue);
const value = this.$locale.parseAmount(this.userStore, this.currentValue);
this.$emit('update:modelValue', value);
this.close();
@@ -318,7 +314,7 @@ export default {
this.$emit('update:show', false);
},
onSheetOpen() {
this.currentValue = this.getStringValue(this.modelValue);
this.currentValue = this.getStringValue(this.userStore, this.modelValue);
},
onSheetClosed() {
this.close();
+3 -5
View File
@@ -79,6 +79,7 @@
<script>
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import colorConstants from '@/consts/color.js';
import { formatPercent } from '@/lib/numeral.js';
@@ -113,7 +114,7 @@ export default {
}
},
computed: {
...mapStores(useSettingsStore),
...mapStores(useSettingsStore, useUserStore),
validItems: function () {
let totalValidValue = 0;
@@ -272,10 +273,7 @@ export default {
return this.circumference - allPreviousLength + offset;
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
}
}
}
+108
View File
@@ -0,0 +1,108 @@
const allDecimalSeparator = {
Dot: {
type: 1,
name: 'Dot',
symbol: '.'
},
Comma: {
type: 2,
name: 'Comma',
symbol: ','
},
Space: {
type: 3,
name: 'Space',
symbol: ' '
}
};
const allDecimalSeparatorArray = [
allDecimalSeparator.Dot,
allDecimalSeparator.Comma,
allDecimalSeparator.Space
];
const allDecimalSeparatorMap = {
[allDecimalSeparator.Dot.type]: allDecimalSeparator.Dot,
[allDecimalSeparator.Comma.type]: allDecimalSeparator.Comma,
[allDecimalSeparator.Space.type]: allDecimalSeparator.Space
};
const allDigitGroupingSymbol = {
Dot: {
type: 1,
name: 'Dot',
symbol: '.'
},
Comma: {
type: 2,
name: 'Comma',
symbol: ','
},
Space: {
type: 3,
name: 'Space',
symbol: ' '
},
Apostrophe: {
type: 4,
name: 'Apostrophe',
symbol: '\''
}
};
const allDigitGroupingSymbolArray = [
allDigitGroupingSymbol.Dot,
allDigitGroupingSymbol.Comma,
allDigitGroupingSymbol.Space,
allDigitGroupingSymbol.Apostrophe
];
const allDigitGroupingSymbolMap = {
[allDigitGroupingSymbol.Dot.type]: allDigitGroupingSymbol.Dot,
[allDigitGroupingSymbol.Comma.type]: allDigitGroupingSymbol.Comma,
[allDigitGroupingSymbol.Space.type]: allDigitGroupingSymbol.Space,
[allDigitGroupingSymbol.Apostrophe.type]: allDigitGroupingSymbol.Apostrophe
};
const allDigitGroupingType = {
None: {
type: 1,
name: 'None'
},
ThousandsSeparator: {
type: 2,
name: 'Thousands Separator'
}
};
const allDigitGroupingTypeArray = [
allDigitGroupingType.None,
allDigitGroupingType.ThousandsSeparator
];
const allDigitGroupingTypeMap = {
[allDigitGroupingType.None.type]: allDigitGroupingType.None,
[allDigitGroupingType.ThousandsSeparator.type]: allDigitGroupingType.ThousandsSeparator
};
const defaultDecimalSeparator = allDecimalSeparator.Dot;
const defaultDigitGroupingSymbol = allDigitGroupingSymbol.Comma;
const defaultDigitGroupingType = allDigitGroupingType.ThousandsSeparator;
const defaultValue = 0;
export default {
allDecimalSeparator: allDecimalSeparator,
allDecimalSeparatorArray: allDecimalSeparatorArray,
allDecimalSeparatorMap: allDecimalSeparatorMap,
allDigitGroupingSymbol: allDigitGroupingSymbol,
allDigitGroupingSymbolArray: allDigitGroupingSymbolArray,
allDigitGroupingSymbolMap: allDigitGroupingSymbolMap,
allDigitGroupingType: allDigitGroupingType,
allDigitGroupingTypeArray: allDigitGroupingTypeArray,
allDigitGroupingTypeMap: allDigitGroupingTypeMap,
defaultDecimalSeparator: defaultDecimalSeparator,
defaultDigitGroupingSymbol: defaultDigitGroupingSymbol,
defaultDigitGroupingType: defaultDigitGroupingType,
defaultValue: defaultValue,
};
+4 -4
View File
@@ -5,11 +5,11 @@ const allTransactionTypes = {
Transfer: 4
};
const minAmount = '-999999999.99';
const maxAmount = '999999999.99';
const minAmountNumber = -99999999999; // -999999999.99
const maxAmountNumber = 99999999999; // 999999999.99
export default {
allTransactionTypes: allTransactionTypes,
minAmount: minAmount,
maxAmount: maxAmount,
minAmountNumber: minAmountNumber,
maxAmountNumber: maxAmountNumber,
};
+8
View File
@@ -109,6 +109,14 @@ export function getObjectOwnFieldCount(object) {
return count;
}
export function replaceAll(value, originalValue, targetValue) {
return value.replaceAll(new RegExp(originalValue, 'g'), targetValue);
}
export function removeAll(value, originalValue) {
return replaceAll(value, originalValue, '');
}
export function limitText(value, maxLength) {
let length = 0;
-153
View File
@@ -1,153 +0,0 @@
import { isNumber } from './common.js';
import { appendThousandsSeparator } from './numeral.js';
export function numericCurrencyToString(num, enableThousandsSeparator, trimTailZero) {
let str = num.toString();
const negative = str.charAt(0) === '-';
if (negative) {
str = str.substring(1);
}
let integer = '0';
let decimals = '00';
if (str.length > 2) {
integer = str.substring(0, str.length - 2);
decimals = str.substring(str.length - 2);
} else if (str.length === 2) {
decimals = str;
} else if (str.length === 1) {
decimals = '0' + str;
}
if (trimTailZero) {
if (decimals.charAt(0) === '0' && decimals.charAt(1) === '0') {
decimals = '';
} else if (decimals.charAt(0) !== '0' && decimals.charAt(1) === '0') {
decimals = decimals.charAt(0);
}
}
integer = appendThousandsSeparator(integer, enableThousandsSeparator);
if (decimals !== '') {
str = `${integer}.${decimals}`;
} else {
str = integer;
}
if (negative) {
str = `-${str}`;
}
return str;
}
export function stringCurrencyToNumeric(str) {
if (!str || str.length < 1) {
return 0;
}
const negative = str.charAt(0) === '-';
if (negative) {
str = str.substring(1);
}
if (!str || str.length < 1) {
return 0;
}
const sign = negative ? -1 : 1;
if (str.indexOf(',')) {
str = str.replaceAll(/,/g, '');
}
let dotPos = str.indexOf('.');
if (dotPos < 0) {
return sign * parseInt(str) * 100;
} else if (dotPos === 0) {
str = '0' + str;
dotPos++;
}
const integer = str.substring(0, dotPos);
const decimals = str.substring(dotPos + 1, str.length);
if (decimals.length < 1) {
return sign * parseInt(integer) * 100;
} else if (decimals.length === 1) {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals) * 10;
} else if (decimals.length === 2) {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals);
} else {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals.substring(0, 2));
}
}
export function getExchangedAmount(amount, fromRate, toRate) {
const exchangeRate = parseFloat(toRate) / parseFloat(fromRate);
if (!isNumber(exchangeRate)) {
return null;
}
return amount * exchangeRate;
}
export function getConvertedAmount(baseAmount, fromExchangeRate, toExchangeRate) {
if (!fromExchangeRate || !toExchangeRate) {
return '';
}
if (baseAmount === '') {
return 0;
}
return getExchangedAmount(baseAmount, fromExchangeRate.rate, toExchangeRate.rate);
}
export function getDisplayExchangeRateAmount(rateStr, isEnableThousandsSeparator) {
if (rateStr.indexOf('.') < 0) {
return appendThousandsSeparator(rateStr, isEnableThousandsSeparator);
} else {
let firstNonZeroPos = 0;
for (let i = 0; i < rateStr.length; i++) {
if (rateStr.charAt(i) !== '.' && rateStr.charAt(i) !== '0') {
firstNonZeroPos = Math.min(i + 4, rateStr.length);
break;
}
}
const trimmedRateStr = rateStr.substring(0, Math.max(6, Math.max(firstNonZeroPos, rateStr.indexOf('.') + 2)));
return appendThousandsSeparator(trimmedRateStr, isEnableThousandsSeparator);
}
}
export function getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, isEnableThousandsSeparator) {
if (!amount1 || !amount2 || amount1 === amount2) {
if (!fromExchangeRate || !fromExchangeRate.rate || !toExchangeRate || !toExchangeRate.rate) {
return null;
}
amount1 = fromExchangeRate.rate;
amount2 = toExchangeRate.rate;
}
amount1 = parseFloat(amount1);
amount2 = parseFloat(amount2);
if (amount1 > amount2) {
const rateStr = (amount1 / amount2).toString();
const displayRateStr = getDisplayExchangeRateAmount(rateStr, isEnableThousandsSeparator);
return `${displayRateStr} : 1`;
} else {
const rateStr = (amount2 / amount1).toString();
const displayRateStr = getDisplayExchangeRateAmount(rateStr, isEnableThousandsSeparator);
return `1 : ${displayRateStr}`;
}
}
+251 -93
View File
@@ -1,6 +1,7 @@
import moment from 'moment-timezone';
import { defaultLanguage, allLanguages } from '@/locales/index.js';
import numeral from '@/consts/numeral.js';
import datetime from '@/consts/datetime.js';
import timezone from '@/consts/timezone.js';
import currency from '@/consts/currency.js';
@@ -11,6 +12,7 @@ import statistics from '@/consts/statistics.js';
import {
isString,
isNumber,
isBoolean,
getTextBefore,
getTextAfter,
copyObjectTo,
@@ -36,8 +38,12 @@ import {
} from './datetime.js';
import {
numericCurrencyToString
} from './currency.js';
appendDigitGroupingSymbol,
parseAmount,
formatAmount,
formatExchangeRateAmount,
getAdaptiveDisplayAmountRate
} from './numeral.js';
import {
getCategorizedAccounts,
@@ -781,6 +787,231 @@ function getAllTimezoneTypesUsedForStatistics(currentTimezone, translateFn) {
];
}
function getAllDecimalSeparators(translateFn) {
const defaultDecimalSeparatorTypeName = translateFn('default.decimalSeparator');
return getNumeralSeparatorFormats(translateFn, numeral.allDecimalSeparator, numeral.allDecimalSeparatorArray, defaultDecimalSeparatorTypeName, numeral.defaultDecimalSeparator);
}
function getAllDigitGroupingSymbols(translateFn) {
const defaultDigitGroupingSymbolTypeName = translateFn('default.digitGroupingSymbol');
return getNumeralSeparatorFormats(translateFn, numeral.allDigitGroupingSymbol, numeral.allDigitGroupingSymbolArray, defaultDigitGroupingSymbolTypeName, numeral.defaultDigitGroupingSymbol);
}
function getNumeralSeparatorFormats(translateFn, allSeparatorMap, allSeparatorArray, localeDefaultTypeName, systemDefaultType) {
let defaultSeparatorType = allSeparatorMap[localeDefaultTypeName];
if (!defaultSeparatorType) {
defaultSeparatorType = systemDefaultType;
}
const ret = [];
ret.push({
type: numeral.defaultValue,
symbol: defaultSeparatorType.symbol,
displayName: `${translateFn('Language Default')} (${defaultSeparatorType.symbol})`
});
for (let i = 0; i < allSeparatorArray.length; i++) {
const type = allSeparatorArray[i];
ret.push({
type: type.type,
symbol: type.symbol,
displayName: `${translateFn('numeral.' + type.name)} (${type.symbol})`
});
}
return ret;
}
function getAllDigitGroupingTypes(translateFn) {
const defaultDigitGroupingTypeName = translateFn('default.digitGrouping');
let defaultDigitGroupingType = numeral.allDigitGroupingType[defaultDigitGroupingTypeName];
if (!defaultDigitGroupingType) {
defaultDigitGroupingType = numeral.defaultDigitGroupingType;
}
const ret = [];
ret.push({
type: numeral.defaultValue,
displayName: `${translateFn('Language Default')} (${translateFn('numeral.' + defaultDigitGroupingType.name)})`
});
for (let i = 0; i < numeral.allDigitGroupingTypeArray.length; i++) {
const type = numeral.allDigitGroupingTypeArray[i];
ret.push({
type: type.type,
displayName: translateFn('numeral.' + type.name)
});
}
return ret;
}
function getCurrentDecimalSeparator(translateFn, decimalSeparator) {
let decimalSeparatorType = numeral.allDecimalSeparatorMap[decimalSeparator];
if (!decimalSeparatorType) {
const defaultDecimalSeparatorTypeName = translateFn('default.decimalSeparator');
decimalSeparatorType = numeral.allDecimalSeparator[defaultDecimalSeparatorTypeName];
if (!decimalSeparatorType) {
decimalSeparatorType = numeral.defaultDecimalSeparator;
}
}
return decimalSeparatorType.symbol;
}
function getCurrentDigitGroupingSymbol(translateFn, digitGroupingSymbol) {
let digitGroupingSymbolType = numeral.allDigitGroupingSymbolMap[digitGroupingSymbol];
if (!digitGroupingSymbolType) {
const defaultDigitGroupingSymbolTypeName = translateFn('default.digitGroupingSymbol');
digitGroupingSymbolType = numeral.allDigitGroupingSymbol[defaultDigitGroupingSymbolTypeName];
if (!digitGroupingSymbolType) {
digitGroupingSymbolType = numeral.defaultDigitGroupingSymbol;
}
}
return digitGroupingSymbolType.symbol;
}
function getCurrentDigitGroupingType(translateFn, digitGrouping) {
let digitGroupingType = numeral.allDigitGroupingTypeMap[digitGrouping];
if (!digitGroupingType) {
const defaultDigitGroupingTypeName = translateFn('default.digitGrouping');
digitGroupingType = numeral.allDigitGroupingType[defaultDigitGroupingTypeName];
if (!digitGroupingType) {
digitGroupingType = numeral.defaultDigitGroupingType;
}
}
return digitGroupingType.type;
}
function getNumberFormatOptions(translateFn, userStore) {
return {
decimalSeparator: getCurrentDecimalSeparator(translateFn, userStore.currentUserDecimalSeparator),
digitGroupingSymbol: getCurrentDigitGroupingSymbol(translateFn, userStore.currentUserDigitGroupingSymbol),
digitGrouping: getCurrentDigitGroupingType(translateFn, userStore.currentUserDigitGrouping),
};
}
function getNumberWithDigitGroupingSymbol(value, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return appendDigitGroupingSymbol(value, numberFormatOptions);
}
function getParsedAmountNumber(value, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return parseAmount(value, numberFormatOptions);
}
function getFormatedAmount(value, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return formatAmount(value, numberFormatOptions);
}
function getFormatedAmountWithCurrency(value, currencyCode, translateFn, userStore, settingsStore, notConvertValue) {
if (!isNumber(value) && !isString(value)) {
return value;
}
if (isNumber(value)) {
value = value.toString();
}
if (!notConvertValue) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
const hasIncompleteFlag = isString(value) && value.charAt(value.length - 1) === '+';
if (hasIncompleteFlag) {
value = value.substring(0, value.length - 1);
}
value = formatAmount(value, numberFormatOptions);
if (hasIncompleteFlag) {
value = value + '+';
}
}
if (!isBoolean(currencyCode) && !currencyCode) {
currencyCode = userStore.currentUserDefaultCurrency;
} else if (isBoolean(currencyCode) && !currencyCode) {
currencyCode = '';
}
const currencyDisplayMode = settingsStore.appSettings.currencyDisplayMode;
if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Symbol) {
const currencyInfo = currency.all[currencyCode];
let currencySymbol = currency.defaultCurrencySymbol;
if (currencyInfo && currencyInfo.symbol) {
currencySymbol = currencyInfo.symbol;
} else if (currencyInfo && currencyInfo.code) {
currencySymbol = currencyInfo.code;
}
return translateFn('format.currency.symbol', {
amount: value,
symbol: currencySymbol
});
} else if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Code) {
return `${value} ${currencyCode}`;
} else if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Name) {
const currencyName = getCurrencyName(currencyCode, translateFn);
return `${value} ${currencyName}`;
} else {
return value;
}
}
function getFormatedExchangeRateAmount(value, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return formatExchangeRateAmount(value, numberFormatOptions);
}
function getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, translateFn, userStore) {
const numberFormatOptions = getNumberFormatOptions(translateFn, userStore);
return getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, numberFormatOptions);
}
function getAmountPrependAndAppendText(currencyCode, translateFn, userStore, settingsStore) {
const placeholder = '***';
const finalText = getFormatedAmountWithCurrency(placeholder, currencyCode, translateFn, userStore, settingsStore, true);
if (!finalText) {
return null;
}
let prependText = getTextBefore(finalText, placeholder);
if (prependText) {
prependText = prependText.trim();
}
let appendText = getTextAfter(finalText, placeholder);
if (appendText) {
appendText = appendText.trim();
}
return {
prependText: prependText,
appendText: appendText
};
}
function getAllAccountCategories(translateFn) {
const allAccountCategories = [];
@@ -996,91 +1227,7 @@ function getEnableDisableOptions(translateFn) {
}];
}
function getDisplayCurrency(value, currencyCode, options, translateFn) {
if (!isNumber(value) && !isString(value)) {
return value;
}
if (isNumber(value)) {
value = value.toString();
}
if (!options) {
options = {};
}
if (!options.notConvertValue) {
const hasIncompleteFlag = isString(value) && value.charAt(value.length - 1) === '+';
if (hasIncompleteFlag) {
value = value.substring(0, value.length - 1);
}
value = numericCurrencyToString(value, options.enableThousandsSeparator);
if (hasIncompleteFlag) {
value = value + '+';
}
}
const currencyDisplayMode = options.currencyDisplayMode;
if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Symbol) {
const currencyInfo = currency.all[currencyCode];
let currencySymbol = currency.defaultCurrencySymbol;
if (currencyInfo && currencyInfo.symbol) {
currencySymbol = currencyInfo.symbol;
} else if (currencyInfo && currencyInfo.code) {
currencySymbol = currencyInfo.code;
}
return translateFn('format.currency.symbol', {
amount: value,
symbol: currencySymbol
});
} else if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Code) {
return `${value} ${currencyCode}`;
} else if (currencyCode && currencyDisplayMode === currency.allCurrencyDisplayModes.Name) {
const currencyName = getCurrencyName(currencyCode, translateFn);
return `${value} ${currencyName}`;
} else {
return value;
}
}
function getDisplayCurrencyPrependAndAppendText(currencyCode, currencyDisplayMode, translateFn) {
const options = {
currencyDisplayMode: currencyDisplayMode,
notConvertValue: true
};
const placeholder = '***';
const finalText = getDisplayCurrency(placeholder, currencyCode, options, translateFn);
if (!finalText) {
return null;
}
let prependText = getTextBefore(finalText, placeholder);
if (prependText) {
prependText = prependText.trim();
}
let appendText = getTextAfter(finalText, placeholder);
if (appendText) {
appendText = appendText.trim();
}
return {
prependText: prependText,
appendText: appendText
};
}
function getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisibleAccounts, showAccountBalance, defaultCurrency, options, translateFn) {
function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, translateFn) {
const categorizedAccounts = copyObjectTo(getCategorizedAccounts(allVisibleAccounts), {});
for (let category in categorizedAccounts) {
@@ -1095,9 +1242,9 @@ function getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisible
const account = accountCategory.accounts[i];
if (showAccountBalance && account.isAsset) {
account.displayBalance = getDisplayCurrency(account.balance, account.currency, options, translateFn);
account.displayBalance = getFormatedAmountWithCurrency(account.balance, account.currency, translateFn, userStore, settingsStore);
} else if (showAccountBalance && account.isLiability) {
account.displayBalance = getDisplayCurrency(-account.balance, account.currency, options, translateFn);
account.displayBalance = getFormatedAmountWithCurrency(-account.balance, account.currency, translateFn, userStore, settingsStore);
} else {
account.displayBalance = '***';
}
@@ -1136,7 +1283,7 @@ function getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisible
totalBalance = totalBalance + '+';
}
accountCategory.displayBalance = getDisplayCurrency(totalBalance, defaultCurrency, options, translateFn);
accountCategory.displayBalance = getFormatedAmountWithCurrency(totalBalance, defaultCurrency, translateFn, userStore, settingsStore);
} else {
accountCategory.displayBalance = '***';
}
@@ -1384,6 +1531,19 @@ export function i18nFunctions(i18nGlobal) {
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),
getAllDecimalSeparators: () => getAllDecimalSeparators(i18nGlobal.t),
getAllDigitGroupingSymbols: () => getAllDigitGroupingSymbols(i18nGlobal.t),
getAllDigitGroupingTypes: () => getAllDigitGroupingTypes(i18nGlobal.t),
getCurrentDecimalSeparator: (userStore) => getCurrentDecimalSeparator(i18nGlobal.t, userStore.currentUserDecimalSeparator),
getCurrentDigitGroupingSymbol: (userStore) => getCurrentDigitGroupingSymbol(i18nGlobal.t, userStore.currentUserDigitGroupingSymbol),
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) => getFormatedAmount(value, i18nGlobal.t, userStore),
formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormatedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore),
formatExchangeRateAmount: (userStore, value) => getFormatedExchangeRateAmount(value, i18nGlobal.t, userStore),
getAdaptiveAmountRate: (userStore, amount1, amount2, fromExchangeRate, toExchangeRate) => getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, i18nGlobal.t, userStore),
getAmountPrependAndAppendText: (settingsStore, userStore, currencyCode) => getAmountPrependAndAppendText(currencyCode, i18nGlobal.t, userStore, settingsStore),
getAllAccountCategories: () => getAllAccountCategories(i18nGlobal.t),
getAllAccountTypes: () => getAllAccountTypes(i18nGlobal.t),
getAllCategoricalChartTypes: () => getAllCategoricalChartTypes(i18nGlobal.t),
@@ -1394,9 +1554,7 @@ export function i18nFunctions(i18nGlobal) {
getAllTransactionDefaultCategories: (categoryType, locale) => getAllTransactionDefaultCategories(categoryType, locale, i18nGlobal.t),
getAllDisplayExchangeRates: (exchangeRatesData) => getAllDisplayExchangeRates(exchangeRatesData, i18nGlobal.t),
getEnableDisableOptions: () => getEnableDisableOptions(i18nGlobal.t),
getDisplayCurrency: (value, currencyCode, options) => getDisplayCurrency(value, currencyCode, options, i18nGlobal.t),
getDisplayCurrencyPrependAndAppendText: (currencyCode, currencyDisplayMode) => getDisplayCurrencyPrependAndAppendText(currencyCode, currencyDisplayMode, i18nGlobal.t),
getCategorizedAccountsWithDisplayBalance: (exchangeRatesStore, allVisibleAccounts, showAccountBalance, defaultCurrency, options) => getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisibleAccounts, showAccountBalance, defaultCurrency, options, i18nGlobal.t),
getCategorizedAccountsWithDisplayBalance: (allVisibleAccounts, showAccountBalance, defaultCurrency, settingsStore, userStore, exchangeRatesStore) => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, i18nGlobal.t),
joinMultiText: (textArray) => joinMultiText(textArray, i18nGlobal.t),
setLanguage: (locale, force) => setLanguage(i18nGlobal, locale, force),
setTimeZone: (timezone) => setTimeZone(timezone),
+231 -16
View File
@@ -1,5 +1,25 @@
export function appendThousandsSeparator(value, enable) {
if (!enable || value.length <= 3) {
import numeralConstants from '@/consts/numeral.js';
import { isString, isNumber, removeAll } from './common.js';
export function appendDigitGroupingSymbol(value, options) {
if (isNumber(value)) {
value = value.toString();
}
if (!isString(value)) {
return value;
}
if (!options) {
options = {};
}
if (!isNumber(options.digitGrouping) || options.digitGrouping === numeralConstants.allDigitGroupingType.None.type) {
return value;
}
if (value.length <= 3) {
return value;
}
@@ -9,35 +29,159 @@ export function appendThousandsSeparator(value, enable) {
value = value.substring(1);
}
const dotPos = value.indexOf('.');
const integer = dotPos < 0 ? value : value.substring(0, dotPos);
const decimals = dotPos < 0 ? '' : value.substring(dotPos + 1, value.length);
const digitGroupingSymbol = options.digitGroupingSymbol || numeralConstants.defaultDigitGroupingSymbol.symbol;
const decimalSeparator = options.decimalSeparator || numeralConstants.defaultDecimalSeparator.symbol;
const finalChars = [];
let integerChars = [];
let currentDecimalSeparator = '';
let decimals = '';
for (let i = 0; i < integer.length; i++) {
if (i % 3 === 0 && i > 0) {
finalChars.push(',');
for (let i = 0; i < value.length; i++) {
const ch = value.charAt(i);
if ('0' <= ch && ch <= '9') {
integerChars.push(ch);
} else {
currentDecimalSeparator = ch;
decimals = value.substring(i + 1);
break;
}
finalChars.push(integer.charAt(integer.length - 1 - i));
}
finalChars.reverse();
let newInteger = '';
let newInteger = finalChars.join('');
if (options.digitGrouping === numeralConstants.allDigitGroupingType.ThousandsSeparator.type) {
for (let i = integerChars.length - 1, j = 0; i >= 0; i--, j++) {
if (j % 3 === 0 && j > 0) {
newInteger = digitGroupingSymbol + newInteger;
}
newInteger = integerChars[i] + newInteger;
}
}
if (negative) {
newInteger = `-${newInteger}`;
}
if (dotPos < 0) {
return newInteger;
if (currentDecimalSeparator) {
return `${newInteger}${decimalSeparator}${decimals}`;
} else {
return `${newInteger}.${decimals}`;
return newInteger;
}
}
export function parseAmount(str, options) {
if (!isString(str)) {
return str;
}
if (!options) {
options = {};
}
if (!str || str.length < 1) {
return 0;
}
const negative = str.charAt(0) === '-';
if (negative) {
str = str.substring(1);
}
if (!str || str.length < 1) {
return 0;
}
const sign = negative ? -1 : 1;
const decimalSeparator = options.decimalSeparator || numeralConstants.defaultDecimalSeparator.symbol;
const digitGroupingSymbol = options.digitGroupingSymbol || numeralConstants.defaultDigitGroupingSymbol.symbol;
if (str.indexOf(digitGroupingSymbol) >= 0) {
str = removeAll(str, digitGroupingSymbol);
}
let decimalSeparatorPos = str.indexOf(decimalSeparator);
if (decimalSeparatorPos < 0) {
return sign * parseInt(str) * 100;
} else if (decimalSeparatorPos === 0) {
str = '0' + str;
decimalSeparatorPos++;
}
const integer = str.substring(0, decimalSeparatorPos);
const decimals = str.substring(decimalSeparatorPos + 1, str.length);
if (decimals.length < 1) {
return sign * parseInt(integer) * 100;
} else if (decimals.length === 1) {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals) * 10;
} else if (decimals.length === 2) {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals);
} else {
return sign * parseInt(integer) * 100 + sign * parseInt(decimals.substring(0, 2));
}
}
export function formatAmount(value, options) {
if (isNumber(value)) {
value = value.toString();
}
if (!isString(value)) {
return value;
}
if (!options) {
options = {};
}
const negative = value.charAt(0) === '-';
if (negative) {
value = value.substring(1);
}
const decimalSeparator = options.decimalSeparator || numeralConstants.defaultDecimalSeparator.symbol;
let integer = '0';
let decimals = '00';
if (value.length > 2) {
integer = value.substring(0, value.length - 2);
decimals = value.substring(value.length - 2);
} else if (value.length === 2) {
decimals = value;
} else if (value.length === 1) {
decimals = '0' + value;
}
if (options.trimTailZero) {
if (decimals.charAt(0) === '0' && decimals.charAt(1) === '0') {
decimals = '';
} else if (decimals.charAt(0) !== '0' && decimals.charAt(1) === '0') {
decimals = decimals.charAt(0);
}
}
integer = appendDigitGroupingSymbol(integer, options);
if (decimals !== '') {
value = `${integer}${decimalSeparator}${decimals}`;
} else {
value = integer;
}
if (negative) {
value = `-${value}`;
}
return value;
}
export function formatPercent(value, precision, lowPrecisionValue) {
const ratio = Math.pow(10, precision);
const normalizedValue = Math.floor(value * ratio);
@@ -49,3 +193,74 @@ export function formatPercent(value, precision, lowPrecisionValue) {
const result = normalizedValue / ratio;
return result + '%';
}
export function formatExchangeRateAmount(exchangeRateAmount, options) {
if (!options) {
options = {};
}
const rateStr = exchangeRateAmount.toString();
const decimalSeparator = numeralConstants.allDecimalSeparator.Dot.symbol;
if (rateStr.indexOf(decimalSeparator) < 0) {
return appendDigitGroupingSymbol(rateStr, options);
} else {
let firstNonZeroPos = 0;
for (let i = 0; i < rateStr.length; i++) {
if (rateStr.charAt(i) !== decimalSeparator && rateStr.charAt(i) !== '0') {
firstNonZeroPos = Math.min(i + 4, rateStr.length);
break;
}
}
const trimmedRateStr = rateStr.substring(0, Math.max(6, Math.max(firstNonZeroPos, rateStr.indexOf(decimalSeparator) + 2)));
return appendDigitGroupingSymbol(trimmedRateStr, options);
}
}
export function getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, options) {
if (!amount1 || !amount2 || amount1 === amount2) {
if (!fromExchangeRate || !fromExchangeRate.rate || !toExchangeRate || !toExchangeRate.rate) {
return null;
}
amount1 = fromExchangeRate.rate;
amount2 = toExchangeRate.rate;
}
amount1 = parseFloat(amount1);
amount2 = parseFloat(amount2);
if (amount1 > amount2) {
const rateStr = (amount1 / amount2).toString();
const displayRateStr = formatExchangeRateAmount(rateStr, options);
return `${displayRateStr} : 1`;
} else {
const rateStr = (amount2 / amount1).toString();
const displayRateStr = formatExchangeRateAmount(rateStr, options);
return `1 : ${displayRateStr}`;
}
}
export function getExchangedAmount(amount, fromRate, toRate) {
const exchangeRate = parseFloat(toRate) / parseFloat(fromRate);
if (!isNumber(exchangeRate)) {
return null;
}
return amount * exchangeRate;
}
export function getConvertedAmount(baseAmount, fromExchangeRate, toExchangeRate) {
if (!fromExchangeRate || !toExchangeRate) {
return '';
}
if (baseAmount === '') {
return 0;
}
return getExchangedAmount(baseAmount, fromExchangeRate.rate, toExchangeRate.rate);
}
+5 -2
View File
@@ -169,7 +169,7 @@ export default {
getProfile: () => {
return axios.get('v1/users/profile/get.json');
},
updateProfile: ({ email, nickname, password, oldPassword, defaultAccountId, transactionEditScope, language, defaultCurrency, firstDayOfWeek, longDateFormat, shortDateFormat, longTimeFormat, shortTimeFormat }) => {
updateProfile: ({ email, nickname, password, oldPassword, defaultAccountId, transactionEditScope, language, defaultCurrency, firstDayOfWeek, longDateFormat, shortDateFormat, longTimeFormat, shortTimeFormat, decimalSeparator, digitGroupingSymbol, digitGrouping }) => {
return axios.post('v1/users/profile/update.json', {
email,
nickname,
@@ -183,7 +183,10 @@ export default {
longDateFormat,
shortDateFormat,
longTimeFormat,
shortTimeFormat
shortTimeFormat,
decimalSeparator,
digitGroupingSymbol,
digitGrouping
});
},
resendVerifyEmailByLoginedUser: () => {
-9
View File
@@ -13,7 +13,6 @@ const defaultSettings = {
applicationLockWebAuthn: false,
autoUpdateExchangeRatesData: true,
autoGetCurrentGeoLocation: false,
thousandsSeparator: true,
currencyDisplayMode: currencyConstants.defaultCurrencyDisplayMode,
showAmountInHomePage: true,
timezoneUsedForStatisticsInHomePage: timezoneConstants.defaultTimezoneTypesUsedForStatistics,
@@ -167,14 +166,6 @@ export function setAutoGetCurrentGeoLocation(value) {
setOption('autoGetCurrentGeoLocation', value);
}
export function isEnableThousandsSeparator() {
return getOption('thousandsSeparator');
}
export function setEnableThousandsSeparator(value) {
setOption('thousandsSeparator', value);
}
export function getCurrencyDisplayMode() {
return getOption('currencyDisplayMode');
}
+16 -2
View File
@@ -10,7 +10,10 @@ export default {
'longDateFormat': 'MMDDYYYY',
'shortDateFormat': 'MMDDYYYY',
'longTimeFormat': 'HHMMSSA',
'shortTimeFormat': 'HHMMA'
'shortTimeFormat': 'HHMMA',
'decimalSeparator': 'Dot',
'digitGroupingSymbol': 'Comma',
'digitGrouping': 'ThousandsSeparator'
},
'format': { // The type of date or time format is moment format, ref: https://momentjs.com/docs/#/displaying/
'longDate': {
@@ -172,6 +175,14 @@ export default {
'long': 'December'
}
},
'numeral': {
'Dot': 'Dot',
'Comma': 'Comma',
'Space': 'Space',
'Apostrophe': 'Apostrophe',
'None': 'None',
'Thousands Separator': 'Thousands Separator',
},
'timezone': {
'International Date Line West': 'International Date Line West',
'Coordinated Universal Time-11': 'Coordinated Universal Time-11',
@@ -604,6 +615,7 @@ export default {
'email is not verified': 'Email is not verified',
'email is verified': 'Email is verified',
'email validation not allowed': 'Email validation is not allowed',
'decimal separator and digit grouping symbol cannot be equal': 'Decimal separator and digit grouping symbol cannot be equal',
'unauthorized access': 'Unauthorized access',
'current token is invalid': 'Current token is invalid',
'current token is expired': 'Current token is expired',
@@ -844,6 +856,9 @@ export default {
'Short Date Format': 'Short Date Format',
'Long Time Format': 'Long Time Format',
'Short Time Format': 'Short Time Format',
'Decimal Separator': 'Decimal Separator',
'Digit Grouping Symbol': 'Digit Grouping Symbol',
'Digit Grouping': 'Digit Grouping',
'Editable Transaction Range': 'Editable Transaction Range',
'Today or later': 'Today or later',
'Recent 24 hours or later': 'Recent 24 hours or later',
@@ -1071,7 +1086,6 @@ export default {
'System Default': 'System Default',
'Language Default': 'Language Default',
'Auto-update Exchange Rates Data': 'Auto-update Exchange Rates Data',
'Enable Thousands Separator': 'Enable Thousands Separator',
'Currency Display Mode': 'Currency Display Mode',
'Currency Code': 'Currency Code',
'Currency Name': 'Currency Name',
+16 -2
View File
@@ -10,7 +10,10 @@ export default {
'longDateFormat': 'YYYYMMDD',
'shortDateFormat': 'YYYYMMDD',
'longTimeFormat': 'HHMMSS',
'shortTimeFormat': 'HHMM'
'shortTimeFormat': 'HHMM',
'decimalSeparator': 'Dot',
'digitGroupingSymbol': 'Comma',
'digitGrouping': 'ThousandsSeparator'
},
'format': {
'longDate': {
@@ -172,6 +175,14 @@ export default {
'long': '十二月'
}
},
'numeral': {
'Dot': '句点',
'Comma': '逗号',
'Space': '空格',
'Apostrophe': '撇号',
'None': '无',
'Thousands Separator': '千位分隔符',
},
'timezone': {
'International Date Line West': '国际日期变更线西',
'Coordinated Universal Time-11': '协调世界时-11',
@@ -604,6 +615,7 @@ export default {
'email is not verified': '邮箱还未验证通过',
'email is verified': '邮箱已经验证过',
'email validation not allowed': '不允许邮箱验证',
'decimal separator and digit grouping symbol cannot be equal': '小数点和数字分组符号不能相同',
'unauthorized access': '未授权的登录',
'current token is invalid': '当前认证令牌无效',
'current token is expired': '当前认证令牌已过期',
@@ -844,6 +856,9 @@ export default {
'Short Date Format': '短日期格式',
'Long Time Format': '长时间格式',
'Short Time Format': '短时间格式',
'Decimal Separator': '小数点',
'Digit Grouping Symbol': '数字分组符号',
'Digit Grouping': '数字分组',
'Editable Transaction Range': '可编辑交易范围',
'Today or later': '今天或更晚',
'Recent 24 hours or later': '最近24小时或更晚',
@@ -1071,7 +1086,6 @@ export default {
'System Default': '系统默认',
'Language Default': '语言默认',
'Auto-update Exchange Rates Data': '自动更新汇率数据',
'Enable Thousands Separator': '启用千位分隔符',
'Currency Display Mode': '货币显示模式',
'Currency Code': '货币代码',
'Currency Name': '货币名称',
+1 -1
View File
@@ -4,7 +4,7 @@ import services from '@/lib/services.js';
import logger from '@/lib/logger.js';
import { isEquals } from '@/lib/common.js';
import { getCurrentUnixTime, formatUnixTime } from '@/lib/datetime.js';
import { getExchangedAmount } from '@/lib/currency.js';
import { getExchangedAmount } from '@/lib/numeral.js';
const exchangeRatesLocalStorageKey = 'ebk_app_exchange_rates';
+4 -1
View File
@@ -380,7 +380,10 @@ export const useRootStore = defineStore('root', {
longDateFormat: profile.longDateFormat,
shortDateFormat: profile.shortDateFormat,
longTimeFormat: profile.longTimeFormat,
shortTimeFormat: profile.shortTimeFormat
shortTimeFormat: profile.shortTimeFormat,
decimalSeparator: profile.decimalSeparator,
digitGroupingSymbol: profile.digitGroupingSymbol,
digitGrouping: profile.digitGrouping
}).then(response => {
const data = response.data;
-5
View File
@@ -14,7 +14,6 @@ export const useSettingsStore = defineStore('settings', {
applicationLockWebAuthn: settings.isEnableApplicationLockWebAuthn(),
autoUpdateExchangeRatesData: settings.isAutoUpdateExchangeRatesData(),
autoGetCurrentGeoLocation: settings.isAutoGetCurrentGeoLocation(),
thousandsSeparator: settings.isEnableThousandsSeparator(),
currencyDisplayMode: settings.getCurrencyDisplayMode(),
showAmountInHomePage: settings.isShowAmountInHomePage(),
timezoneUsedForStatisticsInHomePage: settings.getTimezoneUsedForStatisticsInHomePage(),
@@ -68,10 +67,6 @@ export const useSettingsStore = defineStore('settings', {
settings.setAutoGetCurrentGeoLocation(value);
this.appSettings.autoGetCurrentGeoLocation = value;
},
setEnableThousandsSeparator(value) {
settings.setEnableThousandsSeparator(value);
this.appSettings.thousandsSeparator = value;
},
setCurrencyDisplayMode(value) {
settings.setCurrencyDisplayMode(value);
this.appSettings.currencyDisplayMode = value;
+1 -5
View File
@@ -25,9 +25,6 @@ import {
getDay,
getDayOfWeekName
} from '@/lib/datetime.js';
import {
stringCurrencyToNumeric
} from '@/lib/currency.js';
const emptyTransactionResult = {
items: [],
@@ -353,8 +350,7 @@ export const useTransactionsStore = defineStore('transactions', {
}
if ((!sourceAccount || !destinationAccount || transaction.destinationAmount === oldValue || transaction.destinationAmount === 0) &&
(stringCurrencyToNumeric(transactionConstants.minAmount) <= newValue &&
newValue <= stringCurrencyToNumeric(transactionConstants.maxAmount))) {
(transactionConstants.minAmountNumber <= newValue && newValue <= transactionConstants.maxAmountNumber)) {
transaction.destinationAmount = newValue;
}
}
+12
View File
@@ -58,6 +58,18 @@ export const useUserStore = defineStore('user', {
const settingsStore = useSettingsStore();
const userInfo = state.currentUserInfo || {};
return isNumber(userInfo.shortTimeFormat) ? userInfo.shortTimeFormat : settingsStore.shortTimeFormat;
},
currentUserDecimalSeparator(state) {
const userInfo = state.currentUserInfo || {};
return userInfo.decimalSeparator;
},
currentUserDigitGroupingSymbol(state) {
const userInfo = state.currentUserInfo || {};
return userInfo.digitGroupingSymbol;
},
currentUserDigitGrouping(state) {
const userInfo = state.currentUserInfo || {};
return userInfo.digitGrouping;
}
},
actions: {
+11 -22
View File
@@ -114,7 +114,7 @@
@click="setAsBaseline(exchangeRate.currencyCode, getConvertedAmount(exchangeRate))">
{{ $t('Set as Base') }}
</v-btn>
<span>{{ getDisplayConvertedAmount(exchangeRate, isEnableThousandsSeparator) }}</span>
<span>{{ getConvertedAmount(exchangeRate) }}</span>
</div>
</td>
</tr>
@@ -140,12 +140,8 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import { isNumber } from '@/lib/common.js';
import {
stringCurrencyToNumeric,
getConvertedAmount,
getDisplayExchangeRateAmount
} from '@/lib/currency.js';
import logger from '@/lib/logger.js';
import { getConvertedAmount } from '@/lib/numeral.js';
import {
mdiRefresh,
@@ -172,9 +168,6 @@ export default {
},
computed: {
...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
exchangeRatesData() {
return this.exchangeRatesStore.latestExchangeRates.data;
},
@@ -248,24 +241,20 @@ export default {
}
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency];
let exchangeRateAmount = 0;
try {
return getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
exchangeRateAmount = getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
} catch (e) {
return 0;
}
},
getDisplayConvertedAmount(toExchangeRate, isEnableThousandsSeparator) {
const rateStr = this.getConvertedAmount(toExchangeRate).toString();
return getDisplayExchangeRateAmount(rateStr, isEnableThousandsSeparator);
},
setAsBaseline(currency, amount) {
if (!isNumber(amount)) {
amount = '';
exchangeRateAmount = 0;
logger.warn('failed to convert amount by exchange rates, original base amount is ' + this.baseAmount)
}
return this.$locale.formatExchangeRateAmount(this.userStore, exchangeRateAmount);
},
setAsBaseline(currency, amount) {
this.baseCurrency = currency;
this.baseAmount = stringCurrencyToNumeric(amount.toString());
this.baseAmount = this.$locale.parseAmount(this.userStore, amount);
}
}
}
+1 -4
View File
@@ -391,10 +391,7 @@ export default {
this.$router.push(`/transaction/list?type=${type}&dateType=${datetimeConstants.allDateRanges.Custom.type}&maxTime=${maxTime}&minTime=${minTime}`);
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) {
+1 -4
View File
@@ -594,10 +594,7 @@ export default {
});
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
}
}
}
@@ -47,18 +47,6 @@
/>
</v-col>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
item-value="value"
persistent-placeholder
:label="$t('Enable Thousands Separator')"
:placeholder="$t('Enable Thousands Separator')"
:items="enableDisableOptions"
v-model="isEnableThousandsSeparator"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
@@ -248,14 +236,6 @@ export default {
this.settingsStore.setAutoUpdateExchangeRatesData(value);
}
},
isEnableThousandsSeparator: {
get: function () {
return this.settingsStore.appSettings.thousandsSeparator;
},
set: function (value) {
this.settingsStore.setEnableThousandsSeparator(value);
}
},
currencyDisplayMode: {
get: function () {
return this.settingsStore.appSettings.currencyDisplayMode;
@@ -277,10 +277,7 @@ export default {
}
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) {
@@ -827,10 +827,7 @@ export default {
return amount;
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getDisplayPercent(value, precision, lowPrecisionValue) {
return formatPercent(value, precision, lowPrecisionValue);
+1 -4
View File
@@ -960,10 +960,7 @@ export default {
return symbol + displayAmount + (incomplete ? '+' : '');
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getLongDate(transaction) {
const transactionTime = getUnixTime(parseDateFromUnixTime(transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes));
@@ -338,9 +338,6 @@ import {
getTimezoneOffsetMinutes,
getCurrentUnixTime
} from '@/lib/datetime.js';
import {
getAdaptiveDisplayAmountRate
} from '@/lib/currency.js';
import {
getFirstAvailableCategoryId
} from '@/lib/category.js';
@@ -445,7 +442,7 @@ export default {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
const toExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[destinationAccount.currency];
const amountRate = getAdaptiveDisplayAmountRate(this.transaction.sourceAmount, this.transaction.destinationAmount, fromExchangeRate, toExchangeRate, this.settingsStore.appSettings.thousandsSeparator);
const amountRate = this.$locale.getAdaptiveAmountRate(this.userStore, this.transaction.sourceAmount, this.transaction.destinationAmount, fromExchangeRate, toExchangeRate);
if (!amountRate) {
return this.$t('Transfer In Amount');
@@ -475,10 +472,7 @@ export default {
return this.accountsStore.allVisiblePlainAccounts;
},
categorizedAccounts() {
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.exchangeRatesStore, this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, this.settingsStore, this.userStore, this.exchangeRatesStore);
},
allAccountsMap() {
return this.accountsStore.allAccountsMap;
@@ -204,6 +204,47 @@
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
item-value="type"
persistent-placeholder
:disabled="loading || saving"
:label="$t('Decimal Separator')"
:placeholder="$t('Decimal Separator')"
:items="allDecimalSeparators"
v-model="newProfile.decimalSeparator"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
item-value="type"
persistent-placeholder
:disabled="loading || saving"
:label="$t('Digit Grouping Symbol')"
:placeholder="$t('Digit Grouping Symbol')"
:items="allDigitGroupingSymbols"
v-model="newProfile.digitGroupingSymbol"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
item-title="displayName"
item-value="type"
persistent-placeholder
:disabled="loading || saving"
:label="$t('Digit Grouping')"
:placeholder="$t('Digit Grouping')"
:items="allDigitGroupingTypes"
v-model="newProfile.digitGrouping"
/>
</v-col>
</v-row>
</v-card-text>
<v-card-text class="d-flex flex-wrap gap-4">
@@ -259,7 +300,10 @@ export default {
longDateFormat: 0,
shortDateFormat: 0,
longTimeFormat: 0,
shortTimeFormat: 0
shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
},
oldProfile: {
email: '',
@@ -272,7 +316,10 @@ export default {
longDateFormat: 0,
shortDateFormat: 0,
longTimeFormat: 0,
shortTimeFormat: 0
shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
},
emailVerified: false,
loading: true,
@@ -315,6 +362,15 @@ export default {
allShortTimeFormats() {
return this.$locale.getAllShortTimeFormats();
},
allDecimalSeparators() {
return this.$locale.getAllDecimalSeparators();
},
allDigitGroupingSymbols() {
return this.$locale.getAllDigitGroupingSymbols();
},
allDigitGroupingTypes() {
return this.$locale.getAllDigitGroupingTypes();
},
allTransactionEditScopeTypes() {
return this.$locale.getAllTransactionEditScopeTypes();
},
@@ -346,7 +402,10 @@ export default {
this.newProfile.longDateFormat === this.oldProfile.longDateFormat &&
this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat &&
this.newProfile.longTimeFormat === this.oldProfile.longTimeFormat &&
this.newProfile.shortTimeFormat === this.oldProfile.shortTimeFormat) {
this.newProfile.shortTimeFormat === this.oldProfile.shortTimeFormat &&
this.newProfile.decimalSeparator === this.oldProfile.decimalSeparator &&
this.newProfile.digitGroupingSymbol === this.oldProfile.digitGroupingSymbol &&
this.newProfile.digitGrouping === this.oldProfile.digitGrouping) {
return 'Nothing has been modified';
} else {
return null;
@@ -473,6 +532,9 @@ export default {
this.oldProfile.shortDateFormat = profile.shortDateFormat;
this.oldProfile.longTimeFormat = profile.longTimeFormat;
this.oldProfile.shortTimeFormat = profile.shortTimeFormat;
this.oldProfile.decimalSeparator = profile.decimalSeparator;
this.oldProfile.digitGroupingSymbol = profile.digitGroupingSymbol;
this.oldProfile.digitGrouping = profile.digitGrouping;
this.newProfile.email = this.oldProfile.email
this.newProfile.nickname = this.oldProfile.nickname;
@@ -485,6 +547,9 @@ export default {
this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat;
this.newProfile.longTimeFormat = this.oldProfile.longTimeFormat;
this.newProfile.shortTimeFormat = this.oldProfile.shortTimeFormat;
this.newProfile.decimalSeparator = this.oldProfile.decimalSeparator;
this.newProfile.digitGroupingSymbol = this.oldProfile.digitGroupingSymbol;
this.newProfile.digitGrouping = this.oldProfile.digitGrouping;
}
}
};
@@ -167,7 +167,6 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { isEquals } from '@/lib/common.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { isDataExportingEnabled } from '@/lib/server_settings.js';
import { startDownloadFile } from '@/lib/ui.js';
@@ -205,9 +204,6 @@ export default {
},
computed: {
...mapStores(useRootStore, useSettingsStore, useUserStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
displayDataStatistics() {
const self = this;
@@ -216,10 +212,10 @@ export default {
}
return {
totalAccountCount: appendThousandsSeparator(self.dataStatistics.totalAccountCount, self.isEnableThousandsSeparator),
totalTransactionCategoryCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCategoryCount, self.isEnableThousandsSeparator),
totalTransactionTagCount: appendThousandsSeparator(self.dataStatistics.totalTransactionTagCount, self.isEnableThousandsSeparator),
totalTransactionCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCount, self.isEnableThousandsSeparator)
totalAccountCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalAccountCount),
totalTransactionCategoryCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCategoryCount),
totalTransactionTagCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTagCount),
totalTransactionCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCount)
};
},
isDataExportingEnabled() {
+8 -23
View File
@@ -48,7 +48,7 @@
<f7-list strong inset dividers class="margin-vertical" v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length">
<f7-list-item swipeout
:after="getDisplayConvertedAmount(exchangeRate)"
:after="getConvertedAmount(exchangeRate)"
:key="exchangeRate.currencyCode" v-for="exchangeRate in availableExchangeRates">
<template #title>
<div class="no-padding no-margin">
@@ -97,12 +97,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import transactionConstants from '@/consts/transaction.js';
import { isNumber } from '@/lib/common.js';
import {
numericCurrencyToString,
stringCurrencyToNumeric,
getConvertedAmount,
getDisplayExchangeRateAmount
} from '@/lib/currency.js';
import { getConvertedAmount } from '@/lib/numeral.js';
export default {
data() {
@@ -118,9 +113,6 @@ export default {
},
computed: {
...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
exchangeRatesData() {
return this.exchangeRatesStore.latestExchangeRates.data;
},
@@ -132,7 +124,7 @@ export default {
return this.$locale.getAllDisplayExchangeRates(this.exchangeRatesData);
},
displayBaseAmount() {
return numericCurrencyToString(this.baseAmount, this.isEnableThousandsSeparator);
return this.$locale.formatAmount(this.userStore, this.baseAmount);
},
baseAmountFontSizeClass() {
if (this.baseAmount >= 100000000 || this.baseAmount <= -100000000) {
@@ -144,10 +136,10 @@ export default {
}
},
allowedMinAmount() {
return transactionConstants.minAmount;
return transactionConstants.minAmountNumber;
},
allowedMaxAmount() {
return transactionConstants.maxAmount;
return transactionConstants.maxAmountNumber;
}
},
created() {
@@ -212,19 +204,12 @@ export default {
},
getConvertedAmount(toExchangeRate) {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency];
return getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
},
getDisplayConvertedAmount(toExchangeRate) {
const rateStr = this.getConvertedAmount(toExchangeRate).toString();
return getDisplayExchangeRateAmount(rateStr, this.isEnableThousandsSeparator);
const exchangeRateAmount = getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
return this.$locale.formatExchangeRateAmount(this.userStore, exchangeRateAmount);
},
setAsBaseline(currency, amount) {
if (!isNumber(amount)) {
amount = '';
}
this.baseCurrency = currency;
this.baseAmount = stringCurrencyToNumeric(amount.toString());
this.baseAmount = this.$locale.parseAmount(this.userStore, amount);
}
}
}
+1 -4
View File
@@ -293,10 +293,7 @@ export default {
});
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) {
-13
View File
@@ -47,11 +47,6 @@
<f7-toggle :checked="isAutoUpdateExchangeRatesData" @toggle:change="isAutoUpdateExchangeRatesData = $event"></f7-toggle>
</f7-list-item>
<f7-list-item>
<span>{{ $t('Enable Thousands Separator') }}</span>
<f7-toggle :checked="isEnableThousandsSeparator" @toggle:change="isEnableThousandsSeparator = $event"></f7-toggle>
</f7-list-item>
<f7-list-item
:key="currentLocale + '_currency_display'"
:title="$t('Currency Display Mode')"
@@ -165,14 +160,6 @@ export default {
isEnableApplicationLock() {
return this.settingsStore.appSettings.applicationLock;
},
isEnableThousandsSeparator: {
get: function () {
return this.settingsStore.appSettings.thousandsSeparator;
},
set: function (value) {
this.settingsStore.setEnableThousandsSeparator(value);
}
},
currencyDisplayMode: {
get: function () {
return this.settingsStore.appSettings.currencyDisplayMode;
+5 -7
View File
@@ -420,6 +420,7 @@
<script>
import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { useAccountsStore } from '@/stores/account.js';
import accountConstants from '@/consts/account.js';
@@ -459,7 +460,7 @@ export default {
};
},
computed: {
...mapStores(useSettingsStore, useAccountsStore),
...mapStores(useSettingsStore, useUserStore, useAccountsStore),
title() {
if (!this.editAccountId) {
return 'Add Account';
@@ -493,10 +494,10 @@ export default {
return this.$locale.getAllCurrencies();
},
allowedMinAmount() {
return transactionConstants.minAmount;
return transactionConstants.minAmountNumber;
},
allowedMaxAmount() {
return transactionConstants.maxAmount;
return transactionConstants.maxAmountNumber;
}
},
watch: {
@@ -647,10 +648,7 @@ export default {
return this.getDisplayCurrency(account.balance, account.currency);
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
chooseSuitableIcon(oldCategory, newCategory) {
setAccountSuitableIcon(this.account, oldCategory, newCategory);
+1 -4
View File
@@ -438,10 +438,7 @@ export default {
});
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getAccountDomId(account) {
return 'account_' + account.id;
@@ -164,10 +164,7 @@ export default {
return '';
},
getDisplayAmount(value) {
return this.$locale.getDisplayCurrency(value, this.userStore.currentUserDefaultCurrency, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value);
}
}
}
@@ -562,10 +562,7 @@ export default {
return amount;
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getDisplayPercent(value, precision, lowPrecisionValue) {
return formatPercent(value, precision, lowPrecisionValue);
+6 -15
View File
@@ -367,9 +367,6 @@ import {
getUtcOffsetByUtcOffsetMinutes,
getActualUnixTimeForStore
} from '@/lib/datetime.js';
import {
getAdaptiveDisplayAmountRate
} from '@/lib/currency.js';
import {
getTransactionPrimaryCategoryName,
getTransactionSecondaryCategoryName,
@@ -458,7 +455,7 @@ export default {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
const toExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[destinationAccount.currency];
const amountRate = getAdaptiveDisplayAmountRate(this.transaction.sourceAmount, this.transaction.destinationAmount, fromExchangeRate, toExchangeRate, this.settingsStore.appSettings.thousandsSeparator);
const amountRate = this.$locale.getAdaptiveAmountRate(this.userStore, this.transaction.sourceAmount, this.transaction.destinationAmount, fromExchangeRate, toExchangeRate);
if (!amountRate) {
return this.$t('Transfer In Amount');
@@ -494,10 +491,7 @@ export default {
return this.accountsStore.allAccountsMap;
},
categorizedAccounts() {
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.exchangeRatesStore, this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, this.settingsStore, this.userStore, this.exchangeRatesStore);
},
allCategories() {
return this.transactionCategoriesStore.allTransactionCategories;
@@ -593,10 +587,10 @@ export default {
}
},
allowedMinAmount() {
return transactionConstants.minAmount;
return transactionConstants.minAmountNumber;
},
allowedMaxAmount() {
return transactionConstants.maxAmount;
return transactionConstants.maxAmountNumber;
},
showAccountBalance() {
return this.settingsStore.appSettings.showAccountBalance;
@@ -833,11 +827,8 @@ export default {
return this.getDisplayCurrency(amount);
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
getDisplayCurrency(value) {
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, false);
},
getPrimaryCategoryName(categoryId, allCategories) {
return getTransactionPrimaryCategoryName(categoryId, allCategories);
+1 -4
View File
@@ -870,10 +870,7 @@ export default {
return symbol + displayAmount + (incomplete ? '+' : '');
},
getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, {
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
},
getWeekdayShortName(transaction) {
return this.$locale.getWeekdayShortName(transaction.dayOfWeek);
@@ -73,7 +73,6 @@ import { useRootStore } from '@/stores/index.js';
import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { isDataExportingEnabled } from '@/lib/server_settings.js';
export default {
@@ -96,9 +95,6 @@ export default {
},
computed: {
...mapStores(useRootStore, useSettingsStore, useUserStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
displayDataStatistics() {
const self = this;
@@ -107,10 +103,10 @@ export default {
}
return {
totalAccountCount: appendThousandsSeparator(self.dataStatistics.totalAccountCount, self.isEnableThousandsSeparator),
totalTransactionCategoryCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCategoryCount, self.isEnableThousandsSeparator),
totalTransactionTagCount: appendThousandsSeparator(self.dataStatistics.totalTransactionTagCount, self.isEnableThousandsSeparator),
totalTransactionCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCount, self.isEnableThousandsSeparator)
totalAccountCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalAccountCount),
totalTransactionCategoryCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCategoryCount),
totalTransactionTagCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTagCount),
totalTransactionCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCount)
};
},
isDataExportingEnabled() {
+66 -3
View File
@@ -206,6 +206,45 @@
</select>
</f7-list-item>
<f7-list-item
class="list-item-with-header-and-title list-item-no-item-after"
:header="$t('Decimal Separator')"
:title="getNameByKeyValue(allDecimalSeparators, newProfile.decimalSeparator, 'type', 'displayName')"
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Decimal Separator'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Decimal Separator'), popupCloseLinkText: $t('Done') }"
>
<select v-model="newProfile.decimalSeparator">
<option :value="format.type"
:key="format.type"
v-for="format in allDecimalSeparators">{{ format.displayName }}</option>
</select>
</f7-list-item>
<f7-list-item
class="list-item-with-header-and-title list-item-no-item-after"
:header="$t('Digit Grouping Symbol')"
:title="getNameByKeyValue(allDigitGroupingSymbols, newProfile.digitGroupingSymbol, 'type', 'displayName')"
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Digit Grouping Symbol'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Digit Grouping Symbol'), popupCloseLinkText: $t('Done') }"
>
<select v-model="newProfile.digitGroupingSymbol">
<option :value="format.type"
:key="format.type"
v-for="format in allDigitGroupingSymbols">{{ format.displayName }}</option>
</select>
</f7-list-item>
<f7-list-item
class="list-item-with-header-and-title list-item-no-item-after"
:header="$t('Digit Grouping')"
:title="getNameByKeyValue(allDigitGroupingTypes, newProfile.digitGrouping, 'type', 'displayName')"
smart-select :smart-select-params="{ openIn: 'popup', popupPush: true, closeOnSelect: true, scrollToSelectedItem: true, searchbar: true, searchbarPlaceholder: $t('Digit Grouping'), searchbarDisableText: $t('Cancel'), appendSearchbarNotFound: $t('No results'), pageTitle: $t('Digit Grouping'), popupCloseLinkText: $t('Done') }"
>
<select v-model="newProfile.digitGrouping">
<option :value="format.type"
:key="format.type"
v-for="format in allDigitGroupingTypes">{{ format.displayName }}</option>
</select>
</f7-list-item>
<f7-list-item class="ebk-list-item-error-info" v-if="langAndRegionInputIsInvalid" :footer="$t(langAndRegionInputInvalidProblemMessage)"></f7-list-item>
</f7-list>
@@ -261,7 +300,10 @@ export default {
longDateFormat: 0,
shortDateFormat: 0,
longTimeFormat: 0,
shortTimeFormat: 0
shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
},
oldProfile: {
email: '',
@@ -274,7 +316,10 @@ export default {
longDateFormat: 0,
shortDateFormat: 0,
longTimeFormat: 0,
shortTimeFormat: 0
shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
},
emailVerified: false,
currentPassword: '',
@@ -319,6 +364,15 @@ export default {
allShortTimeFormats() {
return this.$locale.getAllShortTimeFormats();
},
allDecimalSeparators() {
return this.$locale.getAllDecimalSeparators();
},
allDigitGroupingSymbols() {
return this.$locale.getAllDigitGroupingSymbols();
},
allDigitGroupingTypes() {
return this.$locale.getAllDigitGroupingTypes();
},
allTransactionEditScopeTypes() {
return this.$locale.getAllTransactionEditScopeTypes();
},
@@ -363,7 +417,10 @@ export default {
this.newProfile.longDateFormat === this.oldProfile.longDateFormat &&
this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat &&
this.newProfile.longTimeFormat === this.oldProfile.longTimeFormat &&
this.newProfile.shortTimeFormat === this.oldProfile.shortTimeFormat) {
this.newProfile.shortTimeFormat === this.oldProfile.shortTimeFormat &&
this.newProfile.decimalSeparator === this.oldProfile.decimalSeparator &&
this.newProfile.digitGroupingSymbol === this.oldProfile.digitGroupingSymbol &&
this.newProfile.digitGrouping === this.oldProfile.digitGrouping) {
return 'Nothing has been modified';
} else if (!this.newProfile.password && this.newProfile.confirmPassword) {
return 'Password cannot be blank';
@@ -512,6 +569,9 @@ export default {
this.oldProfile.shortDateFormat = profile.shortDateFormat;
this.oldProfile.longTimeFormat = profile.longTimeFormat;
this.oldProfile.shortTimeFormat = profile.shortTimeFormat;
this.oldProfile.decimalSeparator = profile.decimalSeparator;
this.oldProfile.digitGroupingSymbol = profile.digitGroupingSymbol;
this.oldProfile.digitGrouping = profile.digitGrouping;
this.newProfile.email = this.oldProfile.email
this.newProfile.nickname = this.oldProfile.nickname;
@@ -524,6 +584,9 @@ export default {
this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat;
this.newProfile.longTimeFormat = this.oldProfile.longTimeFormat;
this.newProfile.shortTimeFormat = this.oldProfile.shortTimeFormat;
this.newProfile.decimalSeparator = this.oldProfile.decimalSeparator;
this.newProfile.digitGroupingSymbol = this.oldProfile.digitGroupingSymbol;
this.newProfile.digitGrouping = this.oldProfile.digitGrouping;
}
}
};