diff --git a/packages/loot-core/src/shared/arithmetic.test.ts b/packages/loot-core/src/shared/arithmetic.test.ts index 8c14e3fe60..09d4dc11a2 100644 --- a/packages/loot-core/src/shared/arithmetic.test.ts +++ b/packages/loot-core/src/shared/arithmetic.test.ts @@ -48,4 +48,46 @@ describe('arithmetic', () => { setNumberFormat({ format: 'apostrophe-dot', hideFraction: false }); expect(evalArithmetic(`1\u2019222.45`)).toEqual(1222.45); }); + + test('handles apostrophe-dot format with keyboard apostrophe (U+0027)', () => { + setNumberFormat({ format: 'apostrophe-dot', hideFraction: false }); + + // Test with keyboard apostrophe (U+0027) - what users type + const keyboardApostrophe = '12\u0027345.67'; + expect(keyboardApostrophe.charCodeAt(2)).toBe(0x0027); // Verify it's U+0027 + expect(evalArithmetic(keyboardApostrophe)).toBe(12345.67); + + // More test cases with keyboard apostrophe + expect(evalArithmetic('1\u0027234.56')).toBe(1234.56); + expect(evalArithmetic('1\u0027000.33')).toBe(1000.33); + expect(evalArithmetic('100\u0027000.99')).toBe(100000.99); + expect(evalArithmetic('1\u0027000\u0027000.50')).toBe(1000000.5); + }); + + test('handles apostrophe-dot format with typographic apostrophe (U+2019)', () => { + setNumberFormat({ format: 'apostrophe-dot', hideFraction: false }); + + // Test with right single quotation mark (U+2019) - what Intl.NumberFormat outputs + const intlApostrophe = '12\u2019345.67'; + expect(intlApostrophe.charCodeAt(2)).toBe(0x2019); // Verify it's U+2019 + expect(evalArithmetic(intlApostrophe)).toBe(12345.67); + + // More test cases with typographic apostrophe + expect(evalArithmetic('1\u2019234.56')).toBe(1234.56); + expect(evalArithmetic('1\u2019000.33')).toBe(1000.33); + }); + + test('handles apostrophe-dot format in arithmetic expressions', () => { + setNumberFormat({ format: 'apostrophe-dot', hideFraction: false }); + + // Test arithmetic operations with keyboard apostrophe + expect(evalArithmetic('1\u0027000 + 2\u0027000')).toBe(3000); + expect(evalArithmetic('10\u0027000 - 2\u0027500')).toBe(7500); + expect(evalArithmetic('1\u0027000 * 2')).toBe(2000); + expect(evalArithmetic('4\u0027000 / 2')).toBe(2000); + + // Test arithmetic operations with typographic apostrophe + expect(evalArithmetic('1\u2019000 + 2\u2019000')).toBe(3000); + expect(evalArithmetic('10\u2019000 - 2\u2019500')).toBe(7500); + }); }); diff --git a/packages/loot-core/src/shared/arithmetic.ts b/packages/loot-core/src/shared/arithmetic.ts index fe791e0648..6a87efa4a4 100644 --- a/packages/loot-core/src/shared/arithmetic.ts +++ b/packages/loot-core/src/shared/arithmetic.ts @@ -38,7 +38,10 @@ function parsePrimary(state) { } let numberStr = ''; - while (char(state) && char(state).match(/[0-9,.’\u00A0\u202F ]|\p{Sc}/u)) { + while ( + char(state) && + char(state).match(/[0-9,.'\u2019\u00A0\u202F ]|\p{Sc}/u) + ) { numberStr += next(state); } diff --git a/packages/loot-core/src/shared/util.test.ts b/packages/loot-core/src/shared/util.test.ts index ab1ff7ab3d..c76ce8b320 100644 --- a/packages/loot-core/src/shared/util.test.ts +++ b/packages/loot-core/src/shared/util.test.ts @@ -184,6 +184,26 @@ describe('utility functions', () => { expect(currencyToAmount('3,000.')).toBe(3000); }); + test('currencyToAmount works with apostrophe-dot format', () => { + setNumberFormat({ format: 'apostrophe-dot', hideFraction: false }); + + // Test with regular apostrophe (U+0027) - what users type on keyboard + const keyboardApostrophe = '12\u0027345.67'; + expect(keyboardApostrophe.charCodeAt(2)).toBe(0x0027); // Verify it's U+0027 + expect(currencyToAmount(keyboardApostrophe)).toBe(12345.67); + expect(currencyToAmount('1\u0027234.56')).toBe(1234.56); + expect(currencyToAmount('1\u0027000.33')).toBe(1000.33); + expect(currencyToAmount('100\u0027000.99')).toBe(100000.99); + expect(currencyToAmount('1\u0027000\u0027000.50')).toBe(1000000.5); + + // Test with right single quotation mark (U+2019) - what Intl.NumberFormat outputs + const intlApostrophe = '12\u2019345.67'; + expect(intlApostrophe.charCodeAt(2)).toBe(0x2019); // Verify it's U+2019 + expect(currencyToAmount(intlApostrophe)).toBe(12345.67); + expect(currencyToAmount('1\u2019234.56')).toBe(1234.56); + expect(currencyToAmount('1\u2019000.33')).toBe(1000.33); + }); + test('currencyToAmount works with dot-comma', () => { setNumberFormat({ format: 'dot-comma', hideFraction: false }); expect(currencyToAmount('3,45')).toBe(3.45); diff --git a/upcoming-release-notes/6795.md b/upcoming-release-notes/6795.md new file mode 100644 index 0000000000..b939086a93 --- /dev/null +++ b/upcoming-release-notes/6795.md @@ -0,0 +1,6 @@ +--- +category: Bugfixes +authors: [Copilot] +--- + +Fix arithmetic parser to accept keyboard apostrophe in apostrophe-dot format.