From ea8e42cdba5433e1bd11281efd2537f2c5b487e4 Mon Sep 17 00:00:00 2001 From: "Hubert Van de Walle (huvw)" Date: Mon, 8 May 2023 22:48:37 +0000 Subject: [PATCH] [FIX] web: parseMonetary of decimal value without leading 0 Steps to reproduce ================== - Open a product form view - Enter .5 in the Cost field It is parsed as 5 but it should be 0.5 Cause of the issue ================== When trying to strip the currency symbol from the input, the leading decimal separator was also removed Solution ======== The decimalPoint can have multiple characters. This means that we can't simply add the decimal separator inside the negated character class. Instead, what we can do is skip everything until we find a interesting substring. (more details in the comment) We then remove everything that is not a digit at the end. Finally, we can pass that to `parseFloat` closes odoo/odoo#123358 X-original-commit: fa466d75da3b30ac1e512a95436075ef1461a95c Signed-off-by: Lucas Perais (lpe) Signed-off-by: Hubert Van De Walle --- addons/web/static/src/views/fields/parsers.js | 20 +++++++++------ .../tests/views/fields/parsers_tests.js | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/addons/web/static/src/views/fields/parsers.js b/addons/web/static/src/views/fields/parsers.js index 9655b0c31b5..8f4187fa8c1 100644 --- a/addons/web/static/src/views/fields/parsers.js +++ b/addons/web/static/src/views/fields/parsers.js @@ -159,20 +159,26 @@ export function parsePercentage(value) { /** * Try to extract a monetary value from a string. The localization is considered in the process. - * This is a very lenient function such that it just strips non-numeric characters at the - * beginning and end of the string, and then tries to parse the remaining string as a float. + * This is a very lenient function such that it ignores everything before we encounter a substring consisting of either + * - a sign (- or +) + * - an equals sign (signaling the start of a mathematical expression) + * - a decimal point + * - a number + * We then remove any non-numeric characters at the end + * * * @param {string} value * @returns {number} */ export function parseMonetary(value) { value = value.trim(); - const regex = new RegExp(`^[^\\d\\-+=]*(?.*?)[^\\d]*$`); - const match = value.match(regex); - if (!match) { - throw new InvalidNumberError(`"${value}" is not a valid number.`); + const startMatch = value.match( + new RegExp(`[\\d\\-+=]|${escapeRegExp(localization.decimalPoint)}`) + ); + if (startMatch) { + value = value.substring(startMatch.index); } - value = match.groups.strToParse; + value = value.replace(/\D*$/, ""); return parseFloat(value); } diff --git a/addons/web/static/tests/views/fields/parsers_tests.js b/addons/web/static/tests/views/fields/parsers_tests.js index d1a3036643a..6964b062536 100644 --- a/addons/web/static/tests/views/fields/parsers_tests.js +++ b/addons/web/static/tests/views/fields/parsers_tests.js @@ -129,6 +129,7 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(parseMonetary("100.00\u00a0€"), 100); assert.strictEqual(parseMonetary("-100.00"), -100); assert.strictEqual(parseMonetary("1,000.00"), 1000); + assert.strictEqual(parseMonetary(".1"), 0.1); assert.strictEqual(parseMonetary("1,000,000.00"), 1000000); assert.strictEqual(parseMonetary("$\u00a0125.00"), 125); assert.strictEqual(parseMonetary("1,000.00\u00a0€"), 1000); @@ -165,6 +166,30 @@ QUnit.module("Fields", (hooks) => { assert.strictEqual(parseMonetary("1\u00a0000,14DOLLARS"), 1000.14); assert.strictEqual(parseMonetary("DOLLARS+1\u00a0000,15"), 1000.15); assert.strictEqual(parseMonetary("EURO-1\u00a0000,16DOGE"), -1000.16); + + // comma as decimal point and dot as thousands separator + patchWithCleanup(localization, { thousandsSep: ".", decimalPoint: "," }); + assert.strictEqual(parseMonetary("10,08"), 10.08); + assert.strictEqual(parseMonetary(""), 0); + assert.strictEqual(parseMonetary("0"), 0); + assert.strictEqual(parseMonetary("100,12\u00a0€"), 100.12); + assert.strictEqual(parseMonetary("-100,12"), -100.12); + assert.strictEqual(parseMonetary("1.000,12"), 1000.12); + assert.strictEqual(parseMonetary(",1"), 0.1); + assert.strictEqual(parseMonetary("1.000.000,12"), 1000000.12); + assert.strictEqual(parseMonetary("$\u00a0125,12"), 125.12); + assert.strictEqual(parseMonetary("1.000,00\u00a0€"), 1000); + assert.strictEqual(parseMonetary(","), 0); + assert.strictEqual(parseMonetary("1\u00a0"), 1); + assert.strictEqual(parseMonetary("\u00a01"), 1); + assert.strictEqual(parseMonetary("12,34 €"), 12.34); + assert.strictEqual(parseMonetary("$ 12,34"), 12.34); + + // Can evaluate expression + assert.strictEqual(parseMonetary("=1.000,1 + 2.000,2"), 3000.3); + assert.strictEqual(parseMonetary("=1.000,00 + 11.121,00"), 12121); + assert.strictEqual(parseMonetary("=1000,00 + 11122,00"), 12122); + assert.strictEqual(parseMonetary("=1000 + 11123"), 12123); }); QUnit.test("parsers fallback on english localisation", function (assert) {