From 23752f1da92ebaaf39f85f81404bbce311375484 Mon Sep 17 00:00:00 2001 From: Antoine Taillard Date: Thu, 20 Feb 2025 00:31:48 +0100 Subject: [PATCH] Fix currencyToAmount incorrectly converting input (#4383) * fix: ensure currencyToAmount works regardless of the configured number format * chore: linting * docs: add release note * test: ensure correct amount is entered for debit when adding split transactions * chore: rename variable thousandsSep to thousandsSeparator Co-authored-by: Joel Jeremy Marquez * chore: rename variable decimalSep to decimalSeparator Co-authored-by: Joel Jeremy Marquez * chore: rename decimalSep and thousandsSep variables to decimalSeparator and thousandsSeparator --------- Co-authored-by: Joel Jeremy Marquez --- .../e2e/page-models/account-page.ts | 3 +- packages/loot-core/src/shared/util.ts | 63 +++++++++---------- upcoming-release-notes/4383.md | 6 ++ 3 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 upcoming-release-notes/4383.md diff --git a/packages/desktop-client/e2e/page-models/account-page.ts b/packages/desktop-client/e2e/page-models/account-page.ts index ebbee3f006..32fd607222 100644 --- a/packages/desktop-client/e2e/page-models/account-page.ts +++ b/packages/desktop-client/e2e/page-models/account-page.ts @@ -194,7 +194,8 @@ export class AccountPage { transaction: TransactionEntry, ) { if (transaction.debit) { - await transactionRow.getByTestId('debit').click(); + // double click to ensure the content is selected when adding split transactions + await transactionRow.getByTestId('debit').dblclick(); await this.page.keyboard.type(transaction.debit); await this.page.keyboard.press('Tab'); } diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts index 9c603384b5..9ce69619c2 100644 --- a/packages/loot-core/src/shared/util.ts +++ b/packages/loot-core/src/shared/util.ts @@ -210,7 +210,7 @@ export function appendDecimals( amountText: string, hideDecimals = false, ): string { - const { separator } = getNumberFormat(); + const { decimalSeparator: separator } = getNumberFormat(); let result = amountText; if (result.slice(-1) === separator) { result = result.slice(0, -1); @@ -283,47 +283,44 @@ export function getNumberFormat({ format?: NumberFormats; hideFraction: boolean; } = numberFormatConfig) { - let locale, regex, separator, separatorRegex; + let locale, thousandsSeparator, decimalSeparator; switch (format) { case 'space-comma': locale = 'en-SE'; - regex = /[^-0-9,.]/g; - separator = ','; - separatorRegex = /[,.]/g; + thousandsSeparator = '\xa0'; + decimalSeparator = ','; break; case 'dot-comma': locale = 'de-DE'; - regex = /[^-0-9,]/g; - separator = ','; + thousandsSeparator = '.'; + decimalSeparator = ','; break; case 'apostrophe-dot': locale = 'de-CH'; - regex = /[^-0-9,.]/g; - separator = '.'; - separatorRegex = /[,.]/g; + thousandsSeparator = '’'; + decimalSeparator = '.'; break; case 'comma-dot-in': locale = 'en-IN'; - regex = /[^-0-9.]/g; - separator = '.'; + thousandsSeparator = ','; + decimalSeparator = '.'; break; case 'comma-dot': default: locale = 'en-US'; - regex = /[^-0-9.]/g; - separator = '.'; + thousandsSeparator = ','; + decimalSeparator = '.'; } return { value: format, - separator, + thousandsSeparator, + decimalSeparator, formatter: new Intl.NumberFormat(locale, { minimumFractionDigits: hideFraction ? 0 : 2, maximumFractionDigits: hideFraction ? 0 : 2, }), - regex, - separatorRegex, }; } @@ -390,23 +387,25 @@ export function amountToCurrencyNoDecimal(amount: Amount): CurrencyAmount { }).formatter.format(amount); } -export function currencyToAmount( - currencyAmount: CurrencyAmount, -): Amount | null { - let amount; - if (getNumberFormat().separatorRegex) { - amount = parseFloat( - currencyAmount - .replace(getNumberFormat().regex, '') - .replace(getNumberFormat().separatorRegex, '.'), - ); +export function currencyToAmount(currencyAmount: string): Amount | null { + let integer, fraction; + + // match the last dot or comma in the string + const match = currencyAmount.match(/[,.](?=[^.,]*$)/); + + if ( + !match || + (match[0] === getNumberFormat().thousandsSeparator && + match.index + 4 === currencyAmount.length) + ) { + fraction = null; + integer = currencyAmount.replace(/\D/g, ''); } else { - amount = parseFloat( - currencyAmount - .replace(getNumberFormat().regex, '') - .replace(getNumberFormat().separator, '.'), - ); + integer = currencyAmount.slice(0, match.index).replace(/\D/g, ''); + fraction = currencyAmount.slice(match.index + 1); } + + const amount = parseFloat(integer + '.' + fraction); return isNaN(amount) ? null : amount; } diff --git a/upcoming-release-notes/4383.md b/upcoming-release-notes/4383.md new file mode 100644 index 0000000000..8736e49385 --- /dev/null +++ b/upcoming-release-notes/4383.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [AntoineTA] +--- + +Ensure decimal separator is recognized independantly of the configured number format. \ No newline at end of file