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("[ShortDateFormat] %s (%d)\n", user.ShortDateFormat, user.ShortDateFormat)
fmt.Printf("[LongTimeFormat] %s (%d)\n", user.LongTimeFormat, user.LongTimeFormat) fmt.Printf("[LongTimeFormat] %s (%d)\n", user.LongTimeFormat, user.LongTimeFormat)
fmt.Printf("[ShortTimeFormat] %s (%d)\n", user.ShortTimeFormat, user.ShortTimeFormat) 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("[Deleted] %t\n", user.Deleted)
fmt.Printf("[EmailVerified] %t\n", user.EmailVerified) fmt.Printf("[EmailVerified] %t\n", user.EmailVerified)
fmt.Printf("[CreatedAt] %s (%d)\n", utils.FormatUnixTimeToLongDateTimeInServerTimezone(user.CreatedUnixTime), user.CreatedUnixTime) 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/core"
"github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/locales"
"github.com/mayswind/ezbookkeeping/pkg/log" "github.com/mayswind/ezbookkeeping/pkg/log"
"github.com/mayswind/ezbookkeeping/pkg/models" "github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/services" "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 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 { if !anythingUpdate {
return nil, errs.ErrNothingWillBeUpdated return nil, errs.ErrNothingWillBeUpdated
} }
+24 -23
View File
@@ -6,27 +6,28 @@ import (
// Error codes related to users // Error codes related to users
var ( var (
ErrLoginNameInvalid = NewNormalError(NormalSubcategoryUser, 0, http.StatusUnauthorized, "login name is invalid") ErrLoginNameInvalid = NewNormalError(NormalSubcategoryUser, 0, http.StatusUnauthorized, "login name is invalid")
ErrLoginNameOrPasswordInvalid = NewNormalError(NormalSubcategoryUser, 1, http.StatusUnauthorized, "login name or password 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") ErrLoginNameOrPasswordWrong = NewNormalError(NormalSubcategoryUser, 2, http.StatusUnauthorized, "login name or password is wrong")
ErrUserIdInvalid = NewNormalError(NormalSubcategoryUser, 3, http.StatusBadRequest, "user id is invalid") ErrUserIdInvalid = NewNormalError(NormalSubcategoryUser, 3, http.StatusBadRequest, "user id is invalid")
ErrUsernameIsEmpty = NewNormalError(NormalSubcategoryUser, 4, http.StatusBadRequest, "username is empty") ErrUsernameIsEmpty = NewNormalError(NormalSubcategoryUser, 4, http.StatusBadRequest, "username is empty")
ErrEmailIsEmpty = NewNormalError(NormalSubcategoryUser, 5, http.StatusBadRequest, "email is empty") ErrEmailIsEmpty = NewNormalError(NormalSubcategoryUser, 5, http.StatusBadRequest, "email is empty")
ErrNicknameIsEmpty = NewNormalError(NormalSubcategoryUser, 6, http.StatusBadRequest, "nickname is empty") ErrNicknameIsEmpty = NewNormalError(NormalSubcategoryUser, 6, http.StatusBadRequest, "nickname is empty")
ErrPasswordIsEmpty = NewNormalError(NormalSubcategoryUser, 7, http.StatusBadRequest, "password is empty") ErrPasswordIsEmpty = NewNormalError(NormalSubcategoryUser, 7, http.StatusBadRequest, "password is empty")
ErrUserDefaultCurrencyIsEmpty = NewNormalError(NormalSubcategoryUser, 8, http.StatusBadRequest, "user default currency is empty") ErrUserDefaultCurrencyIsEmpty = NewNormalError(NormalSubcategoryUser, 8, http.StatusBadRequest, "user default currency is empty")
ErrUserDefaultCurrencyIsInvalid = NewNormalError(NormalSubcategoryUser, 9, http.StatusBadRequest, "user default currency is invalid") ErrUserDefaultCurrencyIsInvalid = NewNormalError(NormalSubcategoryUser, 9, http.StatusBadRequest, "user default currency is invalid")
ErrUserNotFound = NewNormalError(NormalSubcategoryUser, 10, http.StatusBadRequest, "user not found") ErrUserNotFound = NewNormalError(NormalSubcategoryUser, 10, http.StatusBadRequest, "user not found")
ErrUserPasswordWrong = NewNormalError(NormalSubcategoryUser, 11, http.StatusBadRequest, "password is wrong") ErrUserPasswordWrong = NewNormalError(NormalSubcategoryUser, 11, http.StatusBadRequest, "password is wrong")
ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists") ErrUsernameAlreadyExists = NewNormalError(NormalSubcategoryUser, 12, http.StatusBadRequest, "username already exists")
ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists") ErrUserEmailAlreadyExists = NewNormalError(NormalSubcategoryUser, 13, http.StatusBadRequest, "email already exists")
ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed") ErrUserRegistrationNotAllowed = NewNormalError(NormalSubcategoryUser, 14, http.StatusBadRequest, "user registration not allowed")
ErrUserDefaultAccountIsInvalid = NewNormalError(NormalSubcategoryUser, 15, http.StatusBadRequest, "user default account is invalid") ErrUserDefaultAccountIsInvalid = NewNormalError(NormalSubcategoryUser, 15, http.StatusBadRequest, "user default account is invalid")
ErrUserIsDisabled = NewNormalError(NormalSubcategoryUser, 16, http.StatusBadRequest, "user is disabled") ErrUserIsDisabled = NewNormalError(NormalSubcategoryUser, 16, http.StatusBadRequest, "user is disabled")
ErrEmptyIsInvalid = NewNormalError(NormalSubcategoryUser, 17, http.StatusBadRequest, "email is invalid") ErrEmptyIsInvalid = NewNormalError(NormalSubcategoryUser, 17, http.StatusBadRequest, "email is invalid")
ErrEmailIsEmptyOrInvalid = NewNormalError(NormalSubcategoryUser, 18, http.StatusBadRequest, "email is empty or invalid") ErrEmailIsEmptyOrInvalid = NewNormalError(NormalSubcategoryUser, 18, http.StatusBadRequest, "email is empty or invalid")
ErrNewPasswordEqualsOldInvalid = NewNormalError(NormalSubcategoryUser, 19, http.StatusBadRequest, "new password equals old password") ErrNewPasswordEqualsOldInvalid = NewNormalError(NormalSubcategoryUser, 19, http.StatusBadRequest, "new password equals old password")
ErrEmailIsNotVerified = NewNormalError(NormalSubcategoryUser, 20, http.StatusBadRequest, "email is not verified") ErrEmailIsNotVerified = NewNormalError(NormalSubcategoryUser, 20, http.StatusBadRequest, "email is not verified")
ErrEmailIsVerified = NewNormalError(NormalSubcategoryUser, 21, http.StatusBadRequest, "email is verified") ErrEmailIsVerified = NewNormalError(NormalSubcategoryUser, 21, http.StatusBadRequest, "email is verified")
ErrEmailValidationNotAllowed = NewNormalError(NormalSubcategoryUser, 22, http.StatusBadRequest, "email validation not allowed") 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 package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
// DefaultLanguage represents the default language // DefaultLanguage represents the default language
var DefaultLanguage = en var DefaultLanguage = en
@@ -22,3 +24,25 @@ func GetLocaleTextItems(locale string) *LocaleTextItems {
return DefaultLanguage 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 package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
// LocaleTextItems represents all text items need to be translated // LocaleTextItems represents all text items need to be translated
type LocaleTextItems struct { type LocaleTextItems struct {
DefaultTypes *DefaultTypes
VerifyEmailTextItems *VerifyEmailTextItems VerifyEmailTextItems *VerifyEmailTextItems
ForgetPasswordMailTextItems *ForgetPasswordMailTextItems ForgetPasswordMailTextItems *ForgetPasswordMailTextItems
} }
type DefaultTypes struct {
DecimalSeparator models.DecimalSeparator
DigitGroupingSymbol models.DigitGroupingSymbol
}
// VerifyEmailTextItems represents text items need to be translated in verify mail // VerifyEmailTextItems represents text items need to be translated in verify mail
type VerifyEmailTextItems struct { type VerifyEmailTextItems struct {
Title string Title string
+6
View File
@@ -1,6 +1,12 @@
package locales package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
var en = &LocaleTextItems{ var en = &LocaleTextItems{
DefaultTypes: &DefaultTypes{
DecimalSeparator: models.DECIMAL_SEPARATOR_DOT,
DigitGroupingSymbol: models.DIGIT_GROUPING_SYMBOL_COMMA,
},
VerifyEmailTextItems: &VerifyEmailTextItems{ VerifyEmailTextItems: &VerifyEmailTextItems{
Title: "Verify Email", Title: "Verify Email",
SalutationFormat: "Hi %s,", SalutationFormat: "Hi %s,",
+6
View File
@@ -1,6 +1,12 @@
package locales package locales
import "github.com/mayswind/ezbookkeeping/pkg/models"
var zhHans = &LocaleTextItems{ var zhHans = &LocaleTextItems{
DefaultTypes: &DefaultTypes{
DecimalSeparator: models.DECIMAL_SEPARATOR_DOT,
DigitGroupingSymbol: models.DIGIT_GROUPING_SYMBOL_COMMA,
},
VerifyEmailTextItems: &VerifyEmailTextItems{ VerifyEmailTextItems: &VerifyEmailTextItems{
Title: "验证邮箱", Title: "验证邮箱",
SalutationFormat: "%s 您好,", 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"` ShortDateFormat ShortDateFormat `xorm:"TINYINT"`
LongTimeFormat LongTimeFormat `xorm:"TINYINT"` LongTimeFormat LongTimeFormat `xorm:"TINYINT"`
ShortTimeFormat ShortTimeFormat `xorm:"TINYINT"` ShortTimeFormat ShortTimeFormat `xorm:"TINYINT"`
DecimalSeparator DecimalSeparator `xorm:"TINYINT"`
DigitGroupingSymbol DigitGroupingSymbol `xorm:"TINYINT"`
DigitGrouping DigitGroupingType `xorm:"TINYINT"`
Disabled bool Disabled bool
Deleted bool `xorm:"NOT NULL"` Deleted bool `xorm:"NOT NULL"`
EmailVerified bool `xorm:"NOT NULL"` EmailVerified bool `xorm:"NOT NULL"`
@@ -89,6 +92,9 @@ type UserBasicInfo struct {
ShortDateFormat ShortDateFormat `json:"shortDateFormat"` ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
LongTimeFormat LongTimeFormat `json:"longTimeFormat"` LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"` ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
DecimalSeparator DecimalSeparator `json:"decimalSeparator"`
DigitGroupingSymbol DigitGroupingSymbol `json:"digitGroupingSymbol"`
DigitGrouping DigitGroupingType `json:"digitGrouping"`
EmailVerified bool `json:"emailVerified"` EmailVerified bool `json:"emailVerified"`
} }
@@ -142,6 +148,9 @@ type UserProfileUpdateRequest struct {
ShortDateFormat *ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"` ShortDateFormat *ShortDateFormat `json:"shortDateFormat" binding:"omitempty,min=0,max=3"`
LongTimeFormat *LongTimeFormat `json:"longTimeFormat" 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"` 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 // UserProfileUpdateResponse represents the data returns to frontend after updating profile
@@ -166,6 +175,9 @@ type UserProfileResponse struct {
ShortDateFormat ShortDateFormat `json:"shortDateFormat"` ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
LongTimeFormat LongTimeFormat `json:"longTimeFormat"` LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"` ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
DecimalSeparator DecimalSeparator `json:"decimalSeparator"`
DigitGroupingSymbol DigitGroupingSymbol `json:"digitGroupingSymbol"`
DigitGrouping DigitGroupingType `json:"digitGrouping"`
EmailVerified bool `json:"emailVerified"` EmailVerified bool `json:"emailVerified"`
LastLoginAt int64 `json:"lastLoginAt"` LastLoginAt int64 `json:"lastLoginAt"`
} }
@@ -229,6 +241,9 @@ func (u *User) ToUserBasicInfo() *UserBasicInfo {
ShortDateFormat: u.ShortDateFormat, ShortDateFormat: u.ShortDateFormat,
LongTimeFormat: u.LongTimeFormat, LongTimeFormat: u.LongTimeFormat,
ShortTimeFormat: u.ShortTimeFormat, ShortTimeFormat: u.ShortTimeFormat,
DecimalSeparator: u.DecimalSeparator,
DigitGroupingSymbol: u.DigitGroupingSymbol,
DigitGrouping: u.DigitGrouping,
EmailVerified: u.EmailVerified, EmailVerified: u.EmailVerified,
} }
} }
@@ -250,6 +265,9 @@ func (u *User) ToUserProfileResponse() *UserProfileResponse {
ShortDateFormat: u.ShortDateFormat, ShortDateFormat: u.ShortDateFormat,
LongTimeFormat: u.LongTimeFormat, LongTimeFormat: u.LongTimeFormat,
ShortTimeFormat: u.ShortTimeFormat, ShortTimeFormat: u.ShortTimeFormat,
DecimalSeparator: u.DecimalSeparator,
DigitGroupingSymbol: u.DigitGroupingSymbol,
DigitGrouping: u.DigitGrouping,
EmailVerified: u.EmailVerified, EmailVerified: u.EmailVerified,
LastLoginAt: u.LastLoginUnixTime, 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") 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 user.UpdatedUnixTime = now
updateCols = append(updateCols, "updated_unix_time") updateCols = append(updateCols, "updated_unix_time")
+141 -35
View File
@@ -1,12 +1,12 @@
<template> <template>
<v-text-field class="text-field-with-colored-label" <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" :color="color" :base-color="color"
:density="density" :readonly="!!readonly" :disabled="!!disabled" :density="density" :readonly="!!readonly" :disabled="!!disabled"
:label="label" :placeholder="placeholder" :label="label" :placeholder="placeholder"
:persistent-placeholder="!!persistentPlaceholder" :persistent-placeholder="!!persistentPlaceholder"
:rules="enableRules ? rules : []" v-model="currentValue" :rules="enableRules ? rules : []" v-model="currentValue"
@keydown="onKeyUpDown" @keyup="onKeyUpDown"> @keydown="onKeyUpDown" @keyup="onKeyUpDown" @paste="onPaste">
<template #prepend-inner v-if="currency && prependText"> <template #prepend-inner v-if="currency && prependText">
<div>{{ prependText }}</div> <div>{{ prependText }}</div>
</template> </template>
@@ -19,9 +19,10 @@
<script> <script>
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js'; import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import transactionConstants from '@/consts/transaction.js'; import transactionConstants from '@/consts/transaction.js';
import { numericCurrencyToString, stringCurrencyToNumeric } from '@/lib/currency.js'; import { removeAll } from '@/lib/common.js';
export default { export default {
props: [ props: [
@@ -43,9 +44,10 @@ export default {
], ],
data() { data() {
const self = this; const self = this;
const userStore = useUserStore();
return { return {
currentValue: numericCurrencyToString(self.modelValue), currentValue: self.getFormattedValue(userStore, self.modelValue),
rules: [ rules: [
(v) => { (v) => {
if (v === '') { if (v === '') {
@@ -53,8 +55,13 @@ export default {
} }
try { try {
const val = parseFloat(v); const val = self.$locale.parseAmount(userStore, v);
return (val >= transactionConstants.minAmount && val <= transactionConstants.maxAmount) || self.$t('Amount value exceeds limitation');
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) { } catch (e) {
return self.$t('Amount value is not number'); return self.$t('Amount value is not number');
} }
@@ -63,7 +70,7 @@ export default {
} }
}, },
computed: { computed: {
...mapStores(useSettingsStore), ...mapStores(useSettingsStore, useUserStore),
extraClass() { extraClass() {
let finalClass = this.class; let finalClass = this.class;
@@ -106,10 +113,10 @@ export default {
}, },
watch: { watch: {
'modelValue': function (newValue) { 'modelValue': function (newValue) {
const numericCurrentValue = stringCurrencyToNumeric(this.currentValue); const numericCurrentValue = this.$locale.parseAmount(this.userStore, this.currentValue);
if (newValue !== numericCurrentValue) { if (newValue !== numericCurrentValue) {
const newStringValue = numericCurrencyToString(newValue, false, true); const newStringValue = this.getFormattedValue(this.userStore, newValue);
if (!(newStringValue === '0' && this.currentValue === '')) { if (!(newStringValue === '0' && this.currentValue === '')) {
this.currentValue = newStringValue; this.currentValue = newStringValue;
@@ -117,57 +124,156 @@ export default {
} }
}, },
'currentValue': function (newValue) { 'currentValue': function (newValue) {
this.$emit('update:modelValue', stringCurrencyToNumeric(newValue)); this.$emit('update:modelValue', this.$locale.parseAmount(this.userStore, newValue));
} }
}, },
methods: { methods: {
onKeyUpDown(e) { 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; return;
} }
let decimalLength = 0; let decimalLength = 0;
let decimalIndex = e.target.value.indexOf('.'); let decimalIndex = str.indexOf(decimalSeparator);
if (decimalIndex >= 0) { 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) { 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; this.currentValue = e.target.value;
e.preventDefault(); e.preventDefault();
return; return;
} }
try { try {
const val = this.$locale.parseAmount(this.userStore, str);
const finalValue = this.getValidFormattedValue(val, str, decimalIndex >= 0);
const val = parseFloat(e.target.value); if (finalValue !== str) {
let maxLength = transactionConstants.maxAmount.toString().length; e.target.value = finalValue;
this.currentValue = finalValue;
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;
e.preventDefault(); e.preventDefault();
} }
} catch (e) { } 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() { 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 { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js'; import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import colorConstants from '@/consts/color.js'; import colorConstants from '@/consts/color.js';
import { formatPercent } from '@/lib/numeral.js'; import { formatPercent } from '@/lib/numeral.js';
@@ -37,7 +38,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapStores(useSettingsStore), ...mapStores(useSettingsStore, useUserStore),
isDarkMode() { isDarkMode() {
return this.globalTheme.global.name.value === 'dark'; return this.globalTheme.global.name.value === 'dark';
}, },
@@ -289,10 +290,7 @@ export default {
return color; return color;
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
} }
} }
} }
+1 -4
View File
@@ -353,10 +353,7 @@ export default {
this.selectedLegends = e.selected; this.selectedLegends = e.selected;
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
} }
} }
} }
+43 -47
View File
@@ -43,8 +43,8 @@
<f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('+')"> <f7-button class="numpad-button numpad-button-function no-right-border" @click="setSymbol('+')">
<span class="numpad-button-text numpad-button-text-normal">&plus;</span> <span class="numpad-button-text numpad-button-text-normal">&plus;</span>
</f7-button> </f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputDot()"> <f7-button class="numpad-button numpad-button-num" @click="inputDecimalSeparator()">
<span class="numpad-button-text numpad-button-text-normal">.</span> <span class="numpad-button-text numpad-button-text-normal">{{ decimalSeparator }}</span>
</f7-button> </f7-button>
<f7-button class="numpad-button numpad-button-num" @click="inputNum(0)"> <f7-button class="numpad-button numpad-button-num" @click="inputNum(0)">
<span class="numpad-button-text numpad-button-text-normal">0</span> <span class="numpad-button-text numpad-button-text-normal">0</span>
@@ -64,11 +64,9 @@
<script> <script>
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js'; import { useUserStore } from '@/stores/user.js';
import { isString } from '@/lib/common.js'; import { isNumber, removeAll } from '@/lib/common.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { numericCurrencyToString, stringCurrencyToNumeric } from '@/lib/currency.js';
export default { export default {
props: [ props: [
@@ -83,21 +81,22 @@ export default {
], ],
data() { data() {
const self = this; const self = this;
const userStore = useUserStore();
return { return {
previousValue: '', previousValue: '',
currentSymbol: '', currentSymbol: '',
currentValue: self.getStringValue(self.modelValue) currentValue: self.getStringValue(userStore, self.modelValue)
} }
}, },
computed: { computed: {
...mapStores(useSettingsStore), ...mapStores(useUserStore),
isEnableThousandsSeparator() { decimalSeparator() {
return this.settingsStore.appSettings.thousandsSeparator; return this.$locale.getCurrentDecimalSeparator(this.userStore);
}, },
currentDisplay() { currentDisplay() {
const previousValue = appendThousandsSeparator(this.previousValue, this.isEnableThousandsSeparator); const previousValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.previousValue);
const currentValue = appendThousandsSeparator(this.currentValue, this.isEnableThousandsSeparator); const currentValue = this.$locale.appendDigitGroupingSymbol(this.userStore, this.currentValue);
if (this.currentSymbol) { if (this.currentSymbol) {
return `${previousValue} ${this.currentSymbol} ${currentValue}`; return `${previousValue} ${this.currentSymbol} ${currentValue}`;
@@ -125,16 +124,19 @@ export default {
} }
}, },
methods: { methods: {
getStringValue(value) { getStringValue(userStore, value) {
let str = numericCurrencyToString(value, this.isEnableThousandsSeparator); let str = this.$locale.formatAmount(userStore, value);
if (str.indexOf(',')) { const digitGroupingSymbol = this.$locale.getCurrentDigitGroupingSymbol(userStore);
str = str.replaceAll(/,/g, '');
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') { if (str === '0') {
return ''; return '';
} }
@@ -142,8 +144,8 @@ export default {
return str; return str;
} }
let integer = str.substring(0, dotPos); let integer = str.substring(0, decimalSeparatorPos);
let decimals = str.substring(dotPos + 1, str.length); let decimals = str.substring(decimalSeparatorPos + 1, str.length);
let newDecimals = ''; let newDecimals = '';
for (let i = decimals.length - 1; i >= 0; i--) { for (let i = decimals.length - 1; i >= 0; i--) {
@@ -160,7 +162,7 @@ export default {
return integer; return integer;
} }
return `${integer}.${newDecimals}`; return `${integer}${decimalSeparator}${newDecimals}`;
}, },
inputNum(num) { inputNum(num) {
if (!this.previousValue && this.currentSymbol === '') { if (!this.previousValue && this.currentSymbol === '') {
@@ -176,36 +178,34 @@ export default {
return; 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; return;
} }
const newValue = this.currentValue + num.toString(); const newValue = this.currentValue + num.toString();
if (isString(this.minValue) && this.minValue !== '') { if (isNumber(this.minValue)) {
const min = stringCurrencyToNumeric(this.minValue); const current = this.$locale.parseAmount(this.userStore, newValue);
const current = stringCurrencyToNumeric(newValue);
if (current < min) { if (current < this.minValue) {
return; return;
} }
} }
if (isString(this.maxValue) && this.maxValue !== '') { if (isNumber(this.maxValue)) {
const max = stringCurrencyToNumeric(this.maxValue); const current = this.$locale.parseAmount(this.userStore, newValue);
const current = stringCurrencyToNumeric(newValue);
if (current > max) { if (current > this.maxValue) {
return; return;
} }
} }
this.currentValue = newValue; this.currentValue = newValue;
}, },
inputDot() { inputDecimalSeparator() {
if (this.currentValue.indexOf('.') >= 0) { if (this.currentValue.indexOf(this.decimalSeparator) >= 0) {
return; return;
} }
@@ -220,7 +220,7 @@ export default {
this.currentValue = '-0'; this.currentValue = '-0';
} }
this.currentValue = this.currentValue + '.'; this.currentValue = this.currentValue + this.decimalSeparator;
}, },
setSymbol(symbol) { setSymbol(symbol) {
if (this.currentValue) { if (this.currentValue) {
@@ -258,8 +258,8 @@ export default {
}, },
confirm() { confirm() {
if (this.currentSymbol && this.currentValue.length >= 1) { if (this.currentSymbol && this.currentValue.length >= 1) {
const previousValue = stringCurrencyToNumeric(this.previousValue); const previousValue = this.$locale.parseAmount(this.userStore, this.previousValue);
const currentValue = stringCurrencyToNumeric(this.currentValue); const currentValue = this.$locale.parseAmount(this.userStore, this.currentValue);
let finalValue = 0; let finalValue = 0;
switch (this.currentSymbol) { switch (this.currentSymbol) {
@@ -276,25 +276,21 @@ export default {
finalValue = previousValue; finalValue = previousValue;
} }
if (isString(this.minValue) && this.minValue !== '') { if (isNumber(this.minValue)) {
const min = stringCurrencyToNumeric(this.minValue); if (finalValue < this.minValue) {
if (finalValue < min) {
this.$toast('Numeric Overflow'); this.$toast('Numeric Overflow');
return false; return false;
} }
} }
if (isString(this.maxValue) && this.maxValue !== '') { if (isNumber(this.maxValue)) {
const max = stringCurrencyToNumeric(this.maxValue); if (finalValue > this.maxValue) {
if (finalValue > max) {
this.$toast('Numeric Overflow'); this.$toast('Numeric Overflow');
return false; return false;
} }
} }
this.currentValue = this.getStringValue(finalValue); this.currentValue = this.getStringValue(this.userStore, finalValue);
this.previousValue = ''; this.previousValue = '';
this.currentSymbol = ''; this.currentSymbol = '';
@@ -306,7 +302,7 @@ export default {
return true; return true;
} else { } else {
const value = stringCurrencyToNumeric(this.currentValue); const value = this.$locale.parseAmount(this.userStore, this.currentValue);
this.$emit('update:modelValue', value); this.$emit('update:modelValue', value);
this.close(); this.close();
@@ -318,7 +314,7 @@ export default {
this.$emit('update:show', false); this.$emit('update:show', false);
}, },
onSheetOpen() { onSheetOpen() {
this.currentValue = this.getStringValue(this.modelValue); this.currentValue = this.getStringValue(this.userStore, this.modelValue);
}, },
onSheetClosed() { onSheetClosed() {
this.close(); this.close();
+3 -5
View File
@@ -79,6 +79,7 @@
<script> <script>
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js'; import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import colorConstants from '@/consts/color.js'; import colorConstants from '@/consts/color.js';
import { formatPercent } from '@/lib/numeral.js'; import { formatPercent } from '@/lib/numeral.js';
@@ -113,7 +114,7 @@ export default {
} }
}, },
computed: { computed: {
...mapStores(useSettingsStore), ...mapStores(useSettingsStore, useUserStore),
validItems: function () { validItems: function () {
let totalValidValue = 0; let totalValidValue = 0;
@@ -272,10 +273,7 @@ export default {
return this.circumference - allPreviousLength + offset; return this.circumference - allPreviousLength + offset;
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
} }
} }
} }
+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 Transfer: 4
}; };
const minAmount = '-999999999.99'; const minAmountNumber = -99999999999; // -999999999.99
const maxAmount = '999999999.99'; const maxAmountNumber = 99999999999; // 999999999.99
export default { export default {
allTransactionTypes: allTransactionTypes, allTransactionTypes: allTransactionTypes,
minAmount: minAmount, minAmountNumber: minAmountNumber,
maxAmount: maxAmount, maxAmountNumber: maxAmountNumber,
}; };
+8
View File
@@ -109,6 +109,14 @@ export function getObjectOwnFieldCount(object) {
return count; 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) { export function limitText(value, maxLength) {
let length = 0; 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 moment from 'moment-timezone';
import { defaultLanguage, allLanguages } from '@/locales/index.js'; import { defaultLanguage, allLanguages } from '@/locales/index.js';
import numeral from '@/consts/numeral.js';
import datetime from '@/consts/datetime.js'; import datetime from '@/consts/datetime.js';
import timezone from '@/consts/timezone.js'; import timezone from '@/consts/timezone.js';
import currency from '@/consts/currency.js'; import currency from '@/consts/currency.js';
@@ -11,6 +12,7 @@ import statistics from '@/consts/statistics.js';
import { import {
isString, isString,
isNumber, isNumber,
isBoolean,
getTextBefore, getTextBefore,
getTextAfter, getTextAfter,
copyObjectTo, copyObjectTo,
@@ -36,8 +38,12 @@ import {
} from './datetime.js'; } from './datetime.js';
import { import {
numericCurrencyToString appendDigitGroupingSymbol,
} from './currency.js'; parseAmount,
formatAmount,
formatExchangeRateAmount,
getAdaptiveDisplayAmountRate
} from './numeral.js';
import { import {
getCategorizedAccounts, 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) { function getAllAccountCategories(translateFn) {
const allAccountCategories = []; const allAccountCategories = [];
@@ -996,91 +1227,7 @@ function getEnableDisableOptions(translateFn) {
}]; }];
} }
function getDisplayCurrency(value, currencyCode, options, translateFn) { function getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, 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) {
const categorizedAccounts = copyObjectTo(getCategorizedAccounts(allVisibleAccounts), {}); const categorizedAccounts = copyObjectTo(getCategorizedAccounts(allVisibleAccounts), {});
for (let category in categorizedAccounts) { for (let category in categorizedAccounts) {
@@ -1095,9 +1242,9 @@ function getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisible
const account = accountCategory.accounts[i]; const account = accountCategory.accounts[i];
if (showAccountBalance && account.isAsset) { 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) { } 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 { } else {
account.displayBalance = '***'; account.displayBalance = '***';
} }
@@ -1136,7 +1283,7 @@ function getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisible
totalBalance = totalBalance + '+'; totalBalance = totalBalance + '+';
} }
accountCategory.displayBalance = getDisplayCurrency(totalBalance, defaultCurrency, options, translateFn); accountCategory.displayBalance = getFormatedAmountWithCurrency(totalBalance, defaultCurrency, translateFn, userStore, settingsStore);
} else { } else {
accountCategory.displayBalance = '***'; accountCategory.displayBalance = '***';
} }
@@ -1384,6 +1531,19 @@ export function i18nFunctions(i18nGlobal) {
getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t), getAllRecentMonthDateRanges: (userStore, includeAll, includeCustom) => getAllRecentMonthDateRanges(userStore, includeAll, includeCustom, i18nGlobal.t),
getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t), getDateRangeDisplayName: (userStore, dateType, startTime, endTime) => getDateRangeDisplayName(userStore, dateType, startTime, endTime, i18nGlobal.t),
getAllTimezoneTypesUsedForStatistics: (currentTimezone) => getAllTimezoneTypesUsedForStatistics(currentTimezone, 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), getAllAccountCategories: () => getAllAccountCategories(i18nGlobal.t),
getAllAccountTypes: () => getAllAccountTypes(i18nGlobal.t), getAllAccountTypes: () => getAllAccountTypes(i18nGlobal.t),
getAllCategoricalChartTypes: () => getAllCategoricalChartTypes(i18nGlobal.t), getAllCategoricalChartTypes: () => getAllCategoricalChartTypes(i18nGlobal.t),
@@ -1394,9 +1554,7 @@ export function i18nFunctions(i18nGlobal) {
getAllTransactionDefaultCategories: (categoryType, locale) => getAllTransactionDefaultCategories(categoryType, locale, i18nGlobal.t), getAllTransactionDefaultCategories: (categoryType, locale) => getAllTransactionDefaultCategories(categoryType, locale, i18nGlobal.t),
getAllDisplayExchangeRates: (exchangeRatesData) => getAllDisplayExchangeRates(exchangeRatesData, i18nGlobal.t), getAllDisplayExchangeRates: (exchangeRatesData) => getAllDisplayExchangeRates(exchangeRatesData, i18nGlobal.t),
getEnableDisableOptions: () => getEnableDisableOptions(i18nGlobal.t), getEnableDisableOptions: () => getEnableDisableOptions(i18nGlobal.t),
getDisplayCurrency: (value, currencyCode, options) => getDisplayCurrency(value, currencyCode, options, i18nGlobal.t), getCategorizedAccountsWithDisplayBalance: (allVisibleAccounts, showAccountBalance, defaultCurrency, settingsStore, userStore, exchangeRatesStore) => getCategorizedAccountsWithDisplayBalance(allVisibleAccounts, showAccountBalance, defaultCurrency, userStore, settingsStore, exchangeRatesStore, i18nGlobal.t),
getDisplayCurrencyPrependAndAppendText: (currencyCode, currencyDisplayMode) => getDisplayCurrencyPrependAndAppendText(currencyCode, currencyDisplayMode, i18nGlobal.t),
getCategorizedAccountsWithDisplayBalance: (exchangeRatesStore, allVisibleAccounts, showAccountBalance, defaultCurrency, options) => getCategorizedAccountsWithDisplayBalance(exchangeRatesStore, allVisibleAccounts, showAccountBalance, defaultCurrency, options, i18nGlobal.t),
joinMultiText: (textArray) => joinMultiText(textArray, i18nGlobal.t), joinMultiText: (textArray) => joinMultiText(textArray, i18nGlobal.t),
setLanguage: (locale, force) => setLanguage(i18nGlobal, locale, force), setLanguage: (locale, force) => setLanguage(i18nGlobal, locale, force),
setTimeZone: (timezone) => setTimeZone(timezone), setTimeZone: (timezone) => setTimeZone(timezone),
+231 -16
View File
@@ -1,5 +1,25 @@
export function appendThousandsSeparator(value, enable) { import numeralConstants from '@/consts/numeral.js';
if (!enable || value.length <= 3) {
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; return value;
} }
@@ -9,35 +29,159 @@ export function appendThousandsSeparator(value, enable) {
value = value.substring(1); value = value.substring(1);
} }
const dotPos = value.indexOf('.'); const digitGroupingSymbol = options.digitGroupingSymbol || numeralConstants.defaultDigitGroupingSymbol.symbol;
const integer = dotPos < 0 ? value : value.substring(0, dotPos); const decimalSeparator = options.decimalSeparator || numeralConstants.defaultDecimalSeparator.symbol;
const decimals = dotPos < 0 ? '' : value.substring(dotPos + 1, value.length);
const finalChars = []; let integerChars = [];
let currentDecimalSeparator = '';
let decimals = '';
for (let i = 0; i < integer.length; i++) { for (let i = 0; i < value.length; i++) {
if (i % 3 === 0 && i > 0) { const ch = value.charAt(i);
finalChars.push(',');
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) { if (negative) {
newInteger = `-${newInteger}`; newInteger = `-${newInteger}`;
} }
if (dotPos < 0) { if (currentDecimalSeparator) {
return newInteger; return `${newInteger}${decimalSeparator}${decimals}`;
} else { } 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) { export function formatPercent(value, precision, lowPrecisionValue) {
const ratio = Math.pow(10, precision); const ratio = Math.pow(10, precision);
const normalizedValue = Math.floor(value * ratio); const normalizedValue = Math.floor(value * ratio);
@@ -49,3 +193,74 @@ export function formatPercent(value, precision, lowPrecisionValue) {
const result = normalizedValue / ratio; const result = normalizedValue / ratio;
return result + '%'; 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: () => { getProfile: () => {
return axios.get('v1/users/profile/get.json'); 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', { return axios.post('v1/users/profile/update.json', {
email, email,
nickname, nickname,
@@ -183,7 +183,10 @@ export default {
longDateFormat, longDateFormat,
shortDateFormat, shortDateFormat,
longTimeFormat, longTimeFormat,
shortTimeFormat shortTimeFormat,
decimalSeparator,
digitGroupingSymbol,
digitGrouping
}); });
}, },
resendVerifyEmailByLoginedUser: () => { resendVerifyEmailByLoginedUser: () => {
-9
View File
@@ -13,7 +13,6 @@ const defaultSettings = {
applicationLockWebAuthn: false, applicationLockWebAuthn: false,
autoUpdateExchangeRatesData: true, autoUpdateExchangeRatesData: true,
autoGetCurrentGeoLocation: false, autoGetCurrentGeoLocation: false,
thousandsSeparator: true,
currencyDisplayMode: currencyConstants.defaultCurrencyDisplayMode, currencyDisplayMode: currencyConstants.defaultCurrencyDisplayMode,
showAmountInHomePage: true, showAmountInHomePage: true,
timezoneUsedForStatisticsInHomePage: timezoneConstants.defaultTimezoneTypesUsedForStatistics, timezoneUsedForStatisticsInHomePage: timezoneConstants.defaultTimezoneTypesUsedForStatistics,
@@ -167,14 +166,6 @@ export function setAutoGetCurrentGeoLocation(value) {
setOption('autoGetCurrentGeoLocation', value); setOption('autoGetCurrentGeoLocation', value);
} }
export function isEnableThousandsSeparator() {
return getOption('thousandsSeparator');
}
export function setEnableThousandsSeparator(value) {
setOption('thousandsSeparator', value);
}
export function getCurrencyDisplayMode() { export function getCurrencyDisplayMode() {
return getOption('currencyDisplayMode'); return getOption('currencyDisplayMode');
} }
+16 -2
View File
@@ -10,7 +10,10 @@ export default {
'longDateFormat': 'MMDDYYYY', 'longDateFormat': 'MMDDYYYY',
'shortDateFormat': 'MMDDYYYY', 'shortDateFormat': 'MMDDYYYY',
'longTimeFormat': 'HHMMSSA', '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/ 'format': { // The type of date or time format is moment format, ref: https://momentjs.com/docs/#/displaying/
'longDate': { 'longDate': {
@@ -172,6 +175,14 @@ export default {
'long': 'December' 'long': 'December'
} }
}, },
'numeral': {
'Dot': 'Dot',
'Comma': 'Comma',
'Space': 'Space',
'Apostrophe': 'Apostrophe',
'None': 'None',
'Thousands Separator': 'Thousands Separator',
},
'timezone': { 'timezone': {
'International Date Line West': 'International Date Line West', 'International Date Line West': 'International Date Line West',
'Coordinated Universal Time-11': 'Coordinated Universal Time-11', 'Coordinated Universal Time-11': 'Coordinated Universal Time-11',
@@ -604,6 +615,7 @@ export default {
'email is not verified': 'Email is not verified', 'email is not verified': 'Email is not verified',
'email is verified': 'Email is verified', 'email is verified': 'Email is verified',
'email validation not allowed': 'Email validation is not allowed', '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', 'unauthorized access': 'Unauthorized access',
'current token is invalid': 'Current token is invalid', 'current token is invalid': 'Current token is invalid',
'current token is expired': 'Current token is expired', 'current token is expired': 'Current token is expired',
@@ -844,6 +856,9 @@ export default {
'Short Date Format': 'Short Date Format', 'Short Date Format': 'Short Date Format',
'Long Time Format': 'Long Time Format', 'Long Time Format': 'Long Time Format',
'Short Time Format': 'Short 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', 'Editable Transaction Range': 'Editable Transaction Range',
'Today or later': 'Today or later', 'Today or later': 'Today or later',
'Recent 24 hours or later': 'Recent 24 hours or later', 'Recent 24 hours or later': 'Recent 24 hours or later',
@@ -1071,7 +1086,6 @@ export default {
'System Default': 'System Default', 'System Default': 'System Default',
'Language Default': 'Language Default', 'Language Default': 'Language Default',
'Auto-update Exchange Rates Data': 'Auto-update Exchange Rates Data', 'Auto-update Exchange Rates Data': 'Auto-update Exchange Rates Data',
'Enable Thousands Separator': 'Enable Thousands Separator',
'Currency Display Mode': 'Currency Display Mode', 'Currency Display Mode': 'Currency Display Mode',
'Currency Code': 'Currency Code', 'Currency Code': 'Currency Code',
'Currency Name': 'Currency Name', 'Currency Name': 'Currency Name',
+16 -2
View File
@@ -10,7 +10,10 @@ export default {
'longDateFormat': 'YYYYMMDD', 'longDateFormat': 'YYYYMMDD',
'shortDateFormat': 'YYYYMMDD', 'shortDateFormat': 'YYYYMMDD',
'longTimeFormat': 'HHMMSS', 'longTimeFormat': 'HHMMSS',
'shortTimeFormat': 'HHMM' 'shortTimeFormat': 'HHMM',
'decimalSeparator': 'Dot',
'digitGroupingSymbol': 'Comma',
'digitGrouping': 'ThousandsSeparator'
}, },
'format': { 'format': {
'longDate': { 'longDate': {
@@ -172,6 +175,14 @@ export default {
'long': '十二月' 'long': '十二月'
} }
}, },
'numeral': {
'Dot': '句点',
'Comma': '逗号',
'Space': '空格',
'Apostrophe': '撇号',
'None': '无',
'Thousands Separator': '千位分隔符',
},
'timezone': { 'timezone': {
'International Date Line West': '国际日期变更线西', 'International Date Line West': '国际日期变更线西',
'Coordinated Universal Time-11': '协调世界时-11', 'Coordinated Universal Time-11': '协调世界时-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 not allowed': '不允许邮箱验证',
'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': '小数点',
'Digit Grouping Symbol': '数字分组符号',
'Digit Grouping': '数字分组',
'Editable Transaction Range': '可编辑交易范围', 'Editable Transaction Range': '可编辑交易范围',
'Today or later': '今天或更晚', 'Today or later': '今天或更晚',
'Recent 24 hours or later': '最近24小时或更晚', 'Recent 24 hours or later': '最近24小时或更晚',
@@ -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': '启用千位分隔符',
'Currency Display Mode': '货币显示模式', 'Currency Display Mode': '货币显示模式',
'Currency Code': '货币代码', 'Currency Code': '货币代码',
'Currency Name': '货币名称', 'Currency Name': '货币名称',
+1 -1
View File
@@ -4,7 +4,7 @@ import services from '@/lib/services.js';
import logger from '@/lib/logger.js'; import logger from '@/lib/logger.js';
import { isEquals } from '@/lib/common.js'; import { isEquals } from '@/lib/common.js';
import { getCurrentUnixTime, formatUnixTime } from '@/lib/datetime.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'; const exchangeRatesLocalStorageKey = 'ebk_app_exchange_rates';
+4 -1
View File
@@ -380,7 +380,10 @@ export const useRootStore = defineStore('root', {
longDateFormat: profile.longDateFormat, longDateFormat: profile.longDateFormat,
shortDateFormat: profile.shortDateFormat, shortDateFormat: profile.shortDateFormat,
longTimeFormat: profile.longTimeFormat, longTimeFormat: profile.longTimeFormat,
shortTimeFormat: profile.shortTimeFormat shortTimeFormat: profile.shortTimeFormat,
decimalSeparator: profile.decimalSeparator,
digitGroupingSymbol: profile.digitGroupingSymbol,
digitGrouping: profile.digitGrouping
}).then(response => { }).then(response => {
const data = response.data; const data = response.data;
-5
View File
@@ -14,7 +14,6 @@ export const useSettingsStore = defineStore('settings', {
applicationLockWebAuthn: settings.isEnableApplicationLockWebAuthn(), applicationLockWebAuthn: settings.isEnableApplicationLockWebAuthn(),
autoUpdateExchangeRatesData: settings.isAutoUpdateExchangeRatesData(), autoUpdateExchangeRatesData: settings.isAutoUpdateExchangeRatesData(),
autoGetCurrentGeoLocation: settings.isAutoGetCurrentGeoLocation(), autoGetCurrentGeoLocation: settings.isAutoGetCurrentGeoLocation(),
thousandsSeparator: settings.isEnableThousandsSeparator(),
currencyDisplayMode: settings.getCurrencyDisplayMode(), currencyDisplayMode: settings.getCurrencyDisplayMode(),
showAmountInHomePage: settings.isShowAmountInHomePage(), showAmountInHomePage: settings.isShowAmountInHomePage(),
timezoneUsedForStatisticsInHomePage: settings.getTimezoneUsedForStatisticsInHomePage(), timezoneUsedForStatisticsInHomePage: settings.getTimezoneUsedForStatisticsInHomePage(),
@@ -68,10 +67,6 @@ export const useSettingsStore = defineStore('settings', {
settings.setAutoGetCurrentGeoLocation(value); settings.setAutoGetCurrentGeoLocation(value);
this.appSettings.autoGetCurrentGeoLocation = value; this.appSettings.autoGetCurrentGeoLocation = value;
}, },
setEnableThousandsSeparator(value) {
settings.setEnableThousandsSeparator(value);
this.appSettings.thousandsSeparator = value;
},
setCurrencyDisplayMode(value) { setCurrencyDisplayMode(value) {
settings.setCurrencyDisplayMode(value); settings.setCurrencyDisplayMode(value);
this.appSettings.currencyDisplayMode = value; this.appSettings.currencyDisplayMode = value;
+1 -5
View File
@@ -25,9 +25,6 @@ import {
getDay, getDay,
getDayOfWeekName getDayOfWeekName
} from '@/lib/datetime.js'; } from '@/lib/datetime.js';
import {
stringCurrencyToNumeric
} from '@/lib/currency.js';
const emptyTransactionResult = { const emptyTransactionResult = {
items: [], items: [],
@@ -353,8 +350,7 @@ export const useTransactionsStore = defineStore('transactions', {
} }
if ((!sourceAccount || !destinationAccount || transaction.destinationAmount === oldValue || transaction.destinationAmount === 0) && if ((!sourceAccount || !destinationAccount || transaction.destinationAmount === oldValue || transaction.destinationAmount === 0) &&
(stringCurrencyToNumeric(transactionConstants.minAmount) <= newValue && (transactionConstants.minAmountNumber <= newValue && newValue <= transactionConstants.maxAmountNumber)) {
newValue <= stringCurrencyToNumeric(transactionConstants.maxAmount))) {
transaction.destinationAmount = newValue; transaction.destinationAmount = newValue;
} }
} }
+12
View File
@@ -58,6 +58,18 @@ export const useUserStore = defineStore('user', {
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const userInfo = state.currentUserInfo || {}; const userInfo = state.currentUserInfo || {};
return isNumber(userInfo.shortTimeFormat) ? userInfo.shortTimeFormat : settingsStore.shortTimeFormat; 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: { actions: {
+11 -22
View File
@@ -114,7 +114,7 @@
@click="setAsBaseline(exchangeRate.currencyCode, getConvertedAmount(exchangeRate))"> @click="setAsBaseline(exchangeRate.currencyCode, getConvertedAmount(exchangeRate))">
{{ $t('Set as Base') }} {{ $t('Set as Base') }}
</v-btn> </v-btn>
<span>{{ getDisplayConvertedAmount(exchangeRate, isEnableThousandsSeparator) }}</span> <span>{{ getConvertedAmount(exchangeRate) }}</span>
</div> </div>
</td> </td>
</tr> </tr>
@@ -140,12 +140,8 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js'; import { useUserStore } from '@/stores/user.js';
import { useExchangeRatesStore } from '@/stores/exchangeRates.js'; import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import { isNumber } from '@/lib/common.js'; import logger from '@/lib/logger.js';
import { import { getConvertedAmount } from '@/lib/numeral.js';
stringCurrencyToNumeric,
getConvertedAmount,
getDisplayExchangeRateAmount
} from '@/lib/currency.js';
import { import {
mdiRefresh, mdiRefresh,
@@ -172,9 +168,6 @@ export default {
}, },
computed: { computed: {
...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore), ...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
exchangeRatesData() { exchangeRatesData() {
return this.exchangeRatesStore.latestExchangeRates.data; return this.exchangeRatesStore.latestExchangeRates.data;
}, },
@@ -248,24 +241,20 @@ export default {
} }
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency]; const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency];
let exchangeRateAmount = 0;
try { try {
return getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate); exchangeRateAmount = getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
} catch (e) { } catch (e) {
return 0; exchangeRateAmount = 0;
} logger.warn('failed to convert amount by exchange rates, original base amount is ' + this.baseAmount)
},
getDisplayConvertedAmount(toExchangeRate, isEnableThousandsSeparator) {
const rateStr = this.getConvertedAmount(toExchangeRate).toString();
return getDisplayExchangeRateAmount(rateStr, isEnableThousandsSeparator);
},
setAsBaseline(currency, amount) {
if (!isNumber(amount)) {
amount = '';
} }
return this.$locale.formatExchangeRateAmount(this.userStore, exchangeRateAmount);
},
setAsBaseline(currency, amount) {
this.baseCurrency = currency; 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}`); this.$router.push(`/transaction/list?type=${type}&dateType=${datetimeConstants.allDateRanges.Custom.type}&maxTime=${maxTime}&minTime=${minTime}`);
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getDisplayAmount(amount, incomplete) { getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) { if (!this.showAmountInHomePage) {
+1 -4
View File
@@ -594,10 +594,7 @@ export default {
}); });
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
} }
} }
} }
@@ -47,18 +47,6 @@
/> />
</v-col> </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-col cols="12" md="6">
<v-select <v-select
item-title="displayName" item-title="displayName"
@@ -248,14 +236,6 @@ export default {
this.settingsStore.setAutoUpdateExchangeRatesData(value); this.settingsStore.setAutoUpdateExchangeRatesData(value);
} }
}, },
isEnableThousandsSeparator: {
get: function () {
return this.settingsStore.appSettings.thousandsSeparator;
},
set: function (value) {
this.settingsStore.setEnableThousandsSeparator(value);
}
},
currencyDisplayMode: { currencyDisplayMode: {
get: function () { get: function () {
return this.settingsStore.appSettings.currencyDisplayMode; return this.settingsStore.appSettings.currencyDisplayMode;
@@ -277,10 +277,7 @@ export default {
} }
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getDisplayAmount(amount, incomplete) { getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) { if (!this.showAmountInHomePage) {
@@ -827,10 +827,7 @@ export default {
return amount; return amount;
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getDisplayPercent(value, precision, lowPrecisionValue) { getDisplayPercent(value, precision, lowPrecisionValue) {
return formatPercent(value, precision, lowPrecisionValue); return formatPercent(value, precision, lowPrecisionValue);
+1 -4
View File
@@ -960,10 +960,7 @@ export default {
return symbol + displayAmount + (incomplete ? '+' : ''); return symbol + displayAmount + (incomplete ? '+' : '');
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getLongDate(transaction) { getLongDate(transaction) {
const transactionTime = getUnixTime(parseDateFromUnixTime(transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes)); const transactionTime = getUnixTime(parseDateFromUnixTime(transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes));
@@ -338,9 +338,6 @@ import {
getTimezoneOffsetMinutes, getTimezoneOffsetMinutes,
getCurrentUnixTime getCurrentUnixTime
} from '@/lib/datetime.js'; } from '@/lib/datetime.js';
import {
getAdaptiveDisplayAmountRate
} from '@/lib/currency.js';
import { import {
getFirstAvailableCategoryId getFirstAvailableCategoryId
} from '@/lib/category.js'; } from '@/lib/category.js';
@@ -445,7 +442,7 @@ export default {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency]; const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
const toExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[destinationAccount.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) { if (!amountRate) {
return this.$t('Transfer In Amount'); return this.$t('Transfer In Amount');
@@ -475,10 +472,7 @@ export default {
return this.accountsStore.allVisiblePlainAccounts; return this.accountsStore.allVisiblePlainAccounts;
}, },
categorizedAccounts() { categorizedAccounts() {
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.exchangeRatesStore, this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, { return this.$locale.getCategorizedAccountsWithDisplayBalance(this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, this.settingsStore, this.userStore, this.exchangeRatesStore);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
allAccountsMap() { allAccountsMap() {
return this.accountsStore.allAccountsMap; return this.accountsStore.allAccountsMap;
@@ -204,6 +204,47 @@
/> />
</v-col> </v-col>
</v-row> </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>
<v-card-text class="d-flex flex-wrap gap-4"> <v-card-text class="d-flex flex-wrap gap-4">
@@ -259,7 +300,10 @@ export default {
longDateFormat: 0, longDateFormat: 0,
shortDateFormat: 0, shortDateFormat: 0,
longTimeFormat: 0, longTimeFormat: 0,
shortTimeFormat: 0 shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
}, },
oldProfile: { oldProfile: {
email: '', email: '',
@@ -272,7 +316,10 @@ export default {
longDateFormat: 0, longDateFormat: 0,
shortDateFormat: 0, shortDateFormat: 0,
longTimeFormat: 0, longTimeFormat: 0,
shortTimeFormat: 0 shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
}, },
emailVerified: false, emailVerified: false,
loading: true, loading: true,
@@ -315,6 +362,15 @@ export default {
allShortTimeFormats() { allShortTimeFormats() {
return this.$locale.getAllShortTimeFormats(); return this.$locale.getAllShortTimeFormats();
}, },
allDecimalSeparators() {
return this.$locale.getAllDecimalSeparators();
},
allDigitGroupingSymbols() {
return this.$locale.getAllDigitGroupingSymbols();
},
allDigitGroupingTypes() {
return this.$locale.getAllDigitGroupingTypes();
},
allTransactionEditScopeTypes() { allTransactionEditScopeTypes() {
return this.$locale.getAllTransactionEditScopeTypes(); return this.$locale.getAllTransactionEditScopeTypes();
}, },
@@ -346,7 +402,10 @@ export default {
this.newProfile.longDateFormat === this.oldProfile.longDateFormat && this.newProfile.longDateFormat === this.oldProfile.longDateFormat &&
this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat && this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat &&
this.newProfile.longTimeFormat === this.oldProfile.longTimeFormat && 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'; return 'Nothing has been modified';
} else { } else {
return null; return null;
@@ -473,6 +532,9 @@ export default {
this.oldProfile.shortDateFormat = profile.shortDateFormat; this.oldProfile.shortDateFormat = profile.shortDateFormat;
this.oldProfile.longTimeFormat = profile.longTimeFormat; this.oldProfile.longTimeFormat = profile.longTimeFormat;
this.oldProfile.shortTimeFormat = profile.shortTimeFormat; 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.email = this.oldProfile.email
this.newProfile.nickname = this.oldProfile.nickname; this.newProfile.nickname = this.oldProfile.nickname;
@@ -485,6 +547,9 @@ export default {
this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat; this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat;
this.newProfile.longTimeFormat = this.oldProfile.longTimeFormat; 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;
} }
} }
}; };
@@ -167,7 +167,6 @@ import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js'; import { useUserStore } from '@/stores/user.js';
import { isEquals } from '@/lib/common.js'; import { isEquals } from '@/lib/common.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { isDataExportingEnabled } from '@/lib/server_settings.js'; import { isDataExportingEnabled } from '@/lib/server_settings.js';
import { startDownloadFile } from '@/lib/ui.js'; import { startDownloadFile } from '@/lib/ui.js';
@@ -205,9 +204,6 @@ export default {
}, },
computed: { computed: {
...mapStores(useRootStore, useSettingsStore, useUserStore), ...mapStores(useRootStore, useSettingsStore, useUserStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
displayDataStatistics() { displayDataStatistics() {
const self = this; const self = this;
@@ -216,10 +212,10 @@ export default {
} }
return { return {
totalAccountCount: appendThousandsSeparator(self.dataStatistics.totalAccountCount, self.isEnableThousandsSeparator), totalAccountCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalAccountCount),
totalTransactionCategoryCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCategoryCount, self.isEnableThousandsSeparator), totalTransactionCategoryCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCategoryCount),
totalTransactionTagCount: appendThousandsSeparator(self.dataStatistics.totalTransactionTagCount, self.isEnableThousandsSeparator), totalTransactionTagCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTagCount),
totalTransactionCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCount, self.isEnableThousandsSeparator) totalTransactionCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCount)
}; };
}, },
isDataExportingEnabled() { 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 strong inset dividers class="margin-vertical" v-if="exchangeRatesData && exchangeRatesData.exchangeRates && exchangeRatesData.exchangeRates.length">
<f7-list-item swipeout <f7-list-item swipeout
:after="getDisplayConvertedAmount(exchangeRate)" :after="getConvertedAmount(exchangeRate)"
:key="exchangeRate.currencyCode" v-for="exchangeRate in availableExchangeRates"> :key="exchangeRate.currencyCode" v-for="exchangeRate in availableExchangeRates">
<template #title> <template #title>
<div class="no-padding no-margin"> <div class="no-padding no-margin">
@@ -97,12 +97,7 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.js';
import transactionConstants from '@/consts/transaction.js'; import transactionConstants from '@/consts/transaction.js';
import { isNumber } from '@/lib/common.js'; import { isNumber } from '@/lib/common.js';
import { import { getConvertedAmount } from '@/lib/numeral.js';
numericCurrencyToString,
stringCurrencyToNumeric,
getConvertedAmount,
getDisplayExchangeRateAmount
} from '@/lib/currency.js';
export default { export default {
data() { data() {
@@ -118,9 +113,6 @@ export default {
}, },
computed: { computed: {
...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore), ...mapStores(useSettingsStore, useUserStore, useExchangeRatesStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
exchangeRatesData() { exchangeRatesData() {
return this.exchangeRatesStore.latestExchangeRates.data; return this.exchangeRatesStore.latestExchangeRates.data;
}, },
@@ -132,7 +124,7 @@ export default {
return this.$locale.getAllDisplayExchangeRates(this.exchangeRatesData); return this.$locale.getAllDisplayExchangeRates(this.exchangeRatesData);
}, },
displayBaseAmount() { displayBaseAmount() {
return numericCurrencyToString(this.baseAmount, this.isEnableThousandsSeparator); return this.$locale.formatAmount(this.userStore, this.baseAmount);
}, },
baseAmountFontSizeClass() { baseAmountFontSizeClass() {
if (this.baseAmount >= 100000000 || this.baseAmount <= -100000000) { if (this.baseAmount >= 100000000 || this.baseAmount <= -100000000) {
@@ -144,10 +136,10 @@ export default {
} }
}, },
allowedMinAmount() { allowedMinAmount() {
return transactionConstants.minAmount; return transactionConstants.minAmountNumber;
}, },
allowedMaxAmount() { allowedMaxAmount() {
return transactionConstants.maxAmount; return transactionConstants.maxAmountNumber;
} }
}, },
created() { created() {
@@ -212,19 +204,12 @@ export default {
}, },
getConvertedAmount(toExchangeRate) { getConvertedAmount(toExchangeRate) {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency]; const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[this.baseCurrency];
return getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate); const exchangeRateAmount = getConvertedAmount(this.baseAmount / 100, fromExchangeRate, toExchangeRate);
}, return this.$locale.formatExchangeRateAmount(this.userStore, exchangeRateAmount);
getDisplayConvertedAmount(toExchangeRate) {
const rateStr = this.getConvertedAmount(toExchangeRate).toString();
return getDisplayExchangeRateAmount(rateStr, this.isEnableThousandsSeparator);
}, },
setAsBaseline(currency, amount) { setAsBaseline(currency, amount) {
if (!isNumber(amount)) {
amount = '';
}
this.baseCurrency = currency; 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) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getDisplayAmount(amount, incomplete) { getDisplayAmount(amount, incomplete) {
if (!this.showAmountInHomePage) { if (!this.showAmountInHomePage) {
-13
View File
@@ -47,11 +47,6 @@
<f7-toggle :checked="isAutoUpdateExchangeRatesData" @toggle:change="isAutoUpdateExchangeRatesData = $event"></f7-toggle> <f7-toggle :checked="isAutoUpdateExchangeRatesData" @toggle:change="isAutoUpdateExchangeRatesData = $event"></f7-toggle>
</f7-list-item> </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 <f7-list-item
:key="currentLocale + '_currency_display'" :key="currentLocale + '_currency_display'"
:title="$t('Currency Display Mode')" :title="$t('Currency Display Mode')"
@@ -165,14 +160,6 @@ export default {
isEnableApplicationLock() { isEnableApplicationLock() {
return this.settingsStore.appSettings.applicationLock; return this.settingsStore.appSettings.applicationLock;
}, },
isEnableThousandsSeparator: {
get: function () {
return this.settingsStore.appSettings.thousandsSeparator;
},
set: function (value) {
this.settingsStore.setEnableThousandsSeparator(value);
}
},
currencyDisplayMode: { currencyDisplayMode: {
get: function () { get: function () {
return this.settingsStore.appSettings.currencyDisplayMode; return this.settingsStore.appSettings.currencyDisplayMode;
+5 -7
View File
@@ -420,6 +420,7 @@
<script> <script>
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
import { useSettingsStore } from '@/stores/setting.js'; import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js';
import { useAccountsStore } from '@/stores/account.js'; import { useAccountsStore } from '@/stores/account.js';
import accountConstants from '@/consts/account.js'; import accountConstants from '@/consts/account.js';
@@ -459,7 +460,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapStores(useSettingsStore, useAccountsStore), ...mapStores(useSettingsStore, useUserStore, useAccountsStore),
title() { title() {
if (!this.editAccountId) { if (!this.editAccountId) {
return 'Add Account'; return 'Add Account';
@@ -493,10 +494,10 @@ export default {
return this.$locale.getAllCurrencies(); return this.$locale.getAllCurrencies();
}, },
allowedMinAmount() { allowedMinAmount() {
return transactionConstants.minAmount; return transactionConstants.minAmountNumber;
}, },
allowedMaxAmount() { allowedMaxAmount() {
return transactionConstants.maxAmount; return transactionConstants.maxAmountNumber;
} }
}, },
watch: { watch: {
@@ -647,10 +648,7 @@ export default {
return this.getDisplayCurrency(account.balance, account.currency); return this.getDisplayCurrency(account.balance, account.currency);
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
chooseSuitableIcon(oldCategory, newCategory) { chooseSuitableIcon(oldCategory, newCategory) {
setAccountSuitableIcon(this.account, oldCategory, newCategory); setAccountSuitableIcon(this.account, oldCategory, newCategory);
+1 -4
View File
@@ -438,10 +438,7 @@ export default {
}); });
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getAccountDomId(account) { getAccountDomId(account) {
return 'account_' + account.id; return 'account_' + account.id;
@@ -164,10 +164,7 @@ export default {
return ''; return '';
}, },
getDisplayAmount(value) { getDisplayAmount(value) {
return this.$locale.getDisplayCurrency(value, this.userStore.currentUserDefaultCurrency, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
} }
} }
} }
@@ -562,10 +562,7 @@ export default {
return amount; return amount;
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getDisplayPercent(value, precision, lowPrecisionValue) { getDisplayPercent(value, precision, lowPrecisionValue) {
return formatPercent(value, precision, lowPrecisionValue); return formatPercent(value, precision, lowPrecisionValue);
+6 -15
View File
@@ -367,9 +367,6 @@ import {
getUtcOffsetByUtcOffsetMinutes, getUtcOffsetByUtcOffsetMinutes,
getActualUnixTimeForStore getActualUnixTimeForStore
} from '@/lib/datetime.js'; } from '@/lib/datetime.js';
import {
getAdaptiveDisplayAmountRate
} from '@/lib/currency.js';
import { import {
getTransactionPrimaryCategoryName, getTransactionPrimaryCategoryName,
getTransactionSecondaryCategoryName, getTransactionSecondaryCategoryName,
@@ -458,7 +455,7 @@ export default {
const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency]; const fromExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[sourceAccount.currency];
const toExchangeRate = this.exchangeRatesStore.latestExchangeRateMap[destinationAccount.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) { if (!amountRate) {
return this.$t('Transfer In Amount'); return this.$t('Transfer In Amount');
@@ -494,10 +491,7 @@ export default {
return this.accountsStore.allAccountsMap; return this.accountsStore.allAccountsMap;
}, },
categorizedAccounts() { categorizedAccounts() {
return this.$locale.getCategorizedAccountsWithDisplayBalance(this.exchangeRatesStore, this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, { return this.$locale.getCategorizedAccountsWithDisplayBalance(this.allVisibleAccounts, this.showAccountBalance, this.defaultCurrency, this.settingsStore, this.userStore, this.exchangeRatesStore);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
allCategories() { allCategories() {
return this.transactionCategoriesStore.allTransactionCategories; return this.transactionCategoriesStore.allTransactionCategories;
@@ -593,10 +587,10 @@ export default {
} }
}, },
allowedMinAmount() { allowedMinAmount() {
return transactionConstants.minAmount; return transactionConstants.minAmountNumber;
}, },
allowedMaxAmount() { allowedMaxAmount() {
return transactionConstants.maxAmount; return transactionConstants.maxAmountNumber;
}, },
showAccountBalance() { showAccountBalance() {
return this.settingsStore.appSettings.showAccountBalance; return this.settingsStore.appSettings.showAccountBalance;
@@ -833,11 +827,8 @@ export default {
return this.getDisplayCurrency(amount); return this.getDisplayCurrency(amount);
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, false);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getPrimaryCategoryName(categoryId, allCategories) { getPrimaryCategoryName(categoryId, allCategories) {
return getTransactionPrimaryCategoryName(categoryId, allCategories); return getTransactionPrimaryCategoryName(categoryId, allCategories);
+1 -4
View File
@@ -870,10 +870,7 @@ export default {
return symbol + displayAmount + (incomplete ? '+' : ''); return symbol + displayAmount + (incomplete ? '+' : '');
}, },
getDisplayCurrency(value, currencyCode) { getDisplayCurrency(value, currencyCode) {
return this.$locale.getDisplayCurrency(value, currencyCode, { return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
currencyDisplayMode: this.settingsStore.appSettings.currencyDisplayMode,
enableThousandsSeparator: this.settingsStore.appSettings.thousandsSeparator
});
}, },
getWeekdayShortName(transaction) { getWeekdayShortName(transaction) {
return this.$locale.getWeekdayShortName(transaction.dayOfWeek); return this.$locale.getWeekdayShortName(transaction.dayOfWeek);
@@ -73,7 +73,6 @@ import { useRootStore } from '@/stores/index.js';
import { useSettingsStore } from '@/stores/setting.js'; import { useSettingsStore } from '@/stores/setting.js';
import { useUserStore } from '@/stores/user.js'; import { useUserStore } from '@/stores/user.js';
import { appendThousandsSeparator } from '@/lib/numeral.js';
import { isDataExportingEnabled } from '@/lib/server_settings.js'; import { isDataExportingEnabled } from '@/lib/server_settings.js';
export default { export default {
@@ -96,9 +95,6 @@ export default {
}, },
computed: { computed: {
...mapStores(useRootStore, useSettingsStore, useUserStore), ...mapStores(useRootStore, useSettingsStore, useUserStore),
isEnableThousandsSeparator() {
return this.settingsStore.appSettings.thousandsSeparator;
},
displayDataStatistics() { displayDataStatistics() {
const self = this; const self = this;
@@ -107,10 +103,10 @@ export default {
} }
return { return {
totalAccountCount: appendThousandsSeparator(self.dataStatistics.totalAccountCount, self.isEnableThousandsSeparator), totalAccountCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalAccountCount),
totalTransactionCategoryCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCategoryCount, self.isEnableThousandsSeparator), totalTransactionCategoryCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCategoryCount),
totalTransactionTagCount: appendThousandsSeparator(self.dataStatistics.totalTransactionTagCount, self.isEnableThousandsSeparator), totalTransactionTagCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionTagCount),
totalTransactionCount: appendThousandsSeparator(self.dataStatistics.totalTransactionCount, self.isEnableThousandsSeparator) totalTransactionCount: self.$locale.appendDigitGroupingSymbol(self.userStore, self.dataStatistics.totalTransactionCount)
}; };
}, },
isDataExportingEnabled() { isDataExportingEnabled() {
+66 -3
View File
@@ -206,6 +206,45 @@
</select> </select>
</f7-list-item> </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-item class="ebk-list-item-error-info" v-if="langAndRegionInputIsInvalid" :footer="$t(langAndRegionInputInvalidProblemMessage)"></f7-list-item>
</f7-list> </f7-list>
@@ -261,7 +300,10 @@ export default {
longDateFormat: 0, longDateFormat: 0,
shortDateFormat: 0, shortDateFormat: 0,
longTimeFormat: 0, longTimeFormat: 0,
shortTimeFormat: 0 shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
}, },
oldProfile: { oldProfile: {
email: '', email: '',
@@ -274,7 +316,10 @@ export default {
longDateFormat: 0, longDateFormat: 0,
shortDateFormat: 0, shortDateFormat: 0,
longTimeFormat: 0, longTimeFormat: 0,
shortTimeFormat: 0 shortTimeFormat: 0,
decimalSeparator: 0,
digitGroupingSymbol: 0,
digitGrouping: 0
}, },
emailVerified: false, emailVerified: false,
currentPassword: '', currentPassword: '',
@@ -319,6 +364,15 @@ export default {
allShortTimeFormats() { allShortTimeFormats() {
return this.$locale.getAllShortTimeFormats(); return this.$locale.getAllShortTimeFormats();
}, },
allDecimalSeparators() {
return this.$locale.getAllDecimalSeparators();
},
allDigitGroupingSymbols() {
return this.$locale.getAllDigitGroupingSymbols();
},
allDigitGroupingTypes() {
return this.$locale.getAllDigitGroupingTypes();
},
allTransactionEditScopeTypes() { allTransactionEditScopeTypes() {
return this.$locale.getAllTransactionEditScopeTypes(); return this.$locale.getAllTransactionEditScopeTypes();
}, },
@@ -363,7 +417,10 @@ export default {
this.newProfile.longDateFormat === this.oldProfile.longDateFormat && this.newProfile.longDateFormat === this.oldProfile.longDateFormat &&
this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat && this.newProfile.shortDateFormat === this.oldProfile.shortDateFormat &&
this.newProfile.longTimeFormat === this.oldProfile.longTimeFormat && 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'; return 'Nothing has been modified';
} else if (!this.newProfile.password && this.newProfile.confirmPassword) { } else if (!this.newProfile.password && this.newProfile.confirmPassword) {
return 'Password cannot be blank'; return 'Password cannot be blank';
@@ -512,6 +569,9 @@ export default {
this.oldProfile.shortDateFormat = profile.shortDateFormat; this.oldProfile.shortDateFormat = profile.shortDateFormat;
this.oldProfile.longTimeFormat = profile.longTimeFormat; this.oldProfile.longTimeFormat = profile.longTimeFormat;
this.oldProfile.shortTimeFormat = profile.shortTimeFormat; 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.email = this.oldProfile.email
this.newProfile.nickname = this.oldProfile.nickname; this.newProfile.nickname = this.oldProfile.nickname;
@@ -524,6 +584,9 @@ export default {
this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat; this.newProfile.shortDateFormat = this.oldProfile.shortDateFormat;
this.newProfile.longTimeFormat = this.oldProfile.longTimeFormat; 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;
} }
} }
}; };