**Issue:** - Install CRM. - Change language to French. - From CRM, open a lead that has `Revenu espéré` thousands as value, e.g. 10 000,00. - Try changing `Revenu espéré` by deleting one of the thousands digit. E.g. 10 000,00 -> 1 000,00 - BUG: The modified value is not understood by the system. **Solution:** This is because the thousands separator from the French language is nbsp. Coincidentally, the parsing logic assumes that the separator between the currency symbol and value is also nbsp. So when inputting `1 000,00`, the parsers thinks either `1` or `000,00` is the currency symbol. We concluded that it's better to be less restrictive when parsing monetary values. So, instead of making sure that the current input's currency is compatible with the currency of the the field, we just strip the non-numeric characters from beginning and end of the input without validating the currency symbol, then parse the remaining string as float. This leads to a more user-friendly monetary input fields such that inputting "DOGE 1,000,000.01" in a "$" monetary field will be parsed as 1000000.01. NOTE that *no* currency conversion is performed. **Minor breaking change:** The second parameter `options` of the `parseMonetary` function is now removed. Any existing use of that parameter is just ignored. closes odoo/odoo#107862 X-original-commit: dc27f1f4c039bdbf84c55a4fb5364c6398c45949 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
180 lines
7.7 KiB
JavaScript
180 lines
7.7 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { localization } from "@web/core/l10n/localization";
|
|
import {
|
|
parseFloat,
|
|
parseFloatTime,
|
|
parseInteger,
|
|
parsePercentage,
|
|
parseMonetary,
|
|
} from "@web/views/fields/parsers";
|
|
import { session } from "@web/session";
|
|
import { defaultLocalization } from "@web/../tests/helpers/mock_services";
|
|
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
|
import { nbsp } from "@web/core/utils/strings";
|
|
|
|
function expectInvalidNumberError(assert, func, value, options) {
|
|
let message = `${func.name} fails on value: "${value}"`;
|
|
if (options) {
|
|
message += ` with options: ${JSON.stringify(options)}`;
|
|
}
|
|
assert.throws(() => func(value, options), message);
|
|
}
|
|
|
|
QUnit.module("Fields", (hooks) => {
|
|
hooks.beforeEach(() => {
|
|
patchWithCleanup(localization, defaultLocalization);
|
|
});
|
|
|
|
QUnit.module("Parsers");
|
|
|
|
QUnit.test("parseFloat", async (assert) => {
|
|
assert.strictEqual(parseFloat(""), 0);
|
|
assert.strictEqual(parseFloat("0"), 0);
|
|
assert.strictEqual(parseFloat("100.00"), 100);
|
|
assert.strictEqual(parseFloat("-100.00"), -100);
|
|
assert.strictEqual(parseFloat("1,000.00"), 1000);
|
|
assert.strictEqual(parseFloat("1,000,000.00"), 1000000);
|
|
assert.strictEqual(parseFloat("1,234.567"), 1234.567);
|
|
expectInvalidNumberError(assert, parseFloat, "1.000.000");
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: "." });
|
|
assert.strictEqual(parseFloat("1.234,567"), 1234.567);
|
|
|
|
// Can evaluate expression from locale with decimal point different from ".".
|
|
assert.strictEqual(parseFloat("=1.000,1 + 2.000,2"), 3000.3);
|
|
assert.strictEqual(parseFloat("=1.000,00 + 11.121,00"), 12121);
|
|
assert.strictEqual(parseFloat("=1000,00 + 11122,00"), 12122);
|
|
assert.strictEqual(parseFloat("=1000 + 11123"), 12123);
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: false });
|
|
assert.strictEqual(parseFloat("1234,567"), 1234.567);
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: nbsp });
|
|
assert.strictEqual(parseFloat("9 876,543"), 9876.543);
|
|
assert.strictEqual(parseFloat("1 234 567,89"), 1234567.89);
|
|
assert.strictEqual(parseFloat(`98${nbsp}765 432,1`), 98765432.1);
|
|
});
|
|
|
|
QUnit.test("parseFloatTime", function (assert) {
|
|
assert.strictEqual(parseFloatTime("0"), 0);
|
|
assert.strictEqual(parseFloatTime("100"), 100);
|
|
assert.strictEqual(parseFloatTime("100.00"), 100);
|
|
assert.strictEqual(parseFloatTime("7:15"), 7.25);
|
|
assert.strictEqual(parseFloatTime("-4:30"), -4.5);
|
|
assert.strictEqual(parseFloatTime(":"), 0);
|
|
assert.strictEqual(parseFloatTime("1:"), 1);
|
|
assert.strictEqual(parseFloatTime(":12"), 0.2);
|
|
|
|
expectInvalidNumberError(assert, parseFloatTime, "a:1");
|
|
expectInvalidNumberError(assert, parseFloatTime, "1:a");
|
|
expectInvalidNumberError(assert, parseFloatTime, "1:1:");
|
|
expectInvalidNumberError(assert, parseFloatTime, ":1:1");
|
|
});
|
|
|
|
QUnit.test("parseInteger", function (assert) {
|
|
assert.strictEqual(parseInteger(""), 0);
|
|
assert.strictEqual(parseInteger("0"), 0);
|
|
assert.strictEqual(parseInteger("100"), 100);
|
|
assert.strictEqual(parseInteger("-100"), -100);
|
|
assert.strictEqual(parseInteger("1,000"), 1000);
|
|
assert.strictEqual(parseInteger("1,000,000"), 1000000);
|
|
expectInvalidNumberError(assert, parseInteger, "1.000.000");
|
|
expectInvalidNumberError(assert, parseInteger, "1,234.567");
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: "." });
|
|
|
|
assert.strictEqual(parseInteger("1.000.000"), 1000000);
|
|
expectInvalidNumberError(assert, parseInteger, "1.234,567");
|
|
// fallback to en localization
|
|
assert.strictEqual(parseInteger("1,000,000"), 1000000);
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: false });
|
|
assert.strictEqual(parseInteger("1000000"), 1000000);
|
|
});
|
|
|
|
QUnit.test("parsePercentage", function (assert) {
|
|
assert.strictEqual(parsePercentage(""), 0);
|
|
assert.strictEqual(parsePercentage("0"), 0);
|
|
assert.strictEqual(parsePercentage("0.5"), 0.005);
|
|
assert.strictEqual(parsePercentage("1"), 0.01);
|
|
assert.strictEqual(parsePercentage("100"), 1);
|
|
assert.strictEqual(parsePercentage("50%"), 0.5);
|
|
expectInvalidNumberError(assert, parsePercentage, "50%40");
|
|
|
|
patchWithCleanup(localization, { decimalPoint: ",", thousandsSep: "." });
|
|
|
|
assert.strictEqual(parsePercentage("1.234,56"), 12.3456);
|
|
assert.strictEqual(parsePercentage("6,02"), 0.0602);
|
|
});
|
|
|
|
QUnit.test("parseMonetary", function (assert) {
|
|
patchWithCleanup(session, {
|
|
currencies: {
|
|
1: {
|
|
digits: [69, 2],
|
|
position: "after",
|
|
symbol: "€",
|
|
},
|
|
3: {
|
|
digits: [69, 2],
|
|
position: "before",
|
|
symbol: "$",
|
|
},
|
|
},
|
|
});
|
|
|
|
assert.strictEqual(parseMonetary(""), 0);
|
|
assert.strictEqual(parseMonetary("0"), 0);
|
|
assert.strictEqual(parseMonetary("100.00\u00a0€"), 100);
|
|
assert.strictEqual(parseMonetary("-100.00"), -100);
|
|
assert.strictEqual(parseMonetary("1,000.00"), 1000);
|
|
assert.strictEqual(parseMonetary("1,000,000.00"), 1000000);
|
|
assert.strictEqual(parseMonetary("$\u00a0125.00"), 125);
|
|
assert.strictEqual(parseMonetary("1,000.00\u00a0€"), 1000);
|
|
|
|
assert.strictEqual(parseMonetary("\u00a0"), 0);
|
|
assert.strictEqual(parseMonetary("1\u00a0"), 1);
|
|
assert.strictEqual(parseMonetary("\u00a01"), 1);
|
|
|
|
assert.strictEqual(parseMonetary("12.00 €"), 12);
|
|
assert.strictEqual(parseMonetary("$ 12.00"), 12);
|
|
assert.strictEqual(parseMonetary("1\u00a0$"), 1);
|
|
assert.strictEqual(parseMonetary("$\u00a01"), 1);
|
|
|
|
assert.throws(() => parseMonetary("1$\u00a01"));
|
|
assert.throws(() => parseMonetary("$\u00a012.00\u00a034"));
|
|
|
|
// nbsp as thousands separator
|
|
patchWithCleanup(localization, { thousandsSep: "\u00a0", decimalPoint: "," });
|
|
assert.strictEqual(parseMonetary("1\u00a0000,06\u00a0€"), 1000.06);
|
|
assert.strictEqual(parseMonetary("$\u00a01\u00a0000,07"), 1000.07);
|
|
assert.strictEqual(parseMonetary("1000000,08"), 1000000.08);
|
|
assert.strictEqual(parseMonetary("$ -1\u00a0000,09"), -1000.09);
|
|
|
|
// symbol not separated from the value
|
|
assert.strictEqual(parseMonetary("1\u00a0000,08€"), 1000.08);
|
|
assert.strictEqual(parseMonetary("€1\u00a0000,09"), 1000.09);
|
|
assert.strictEqual(parseMonetary("$1\u00a0000,10"), 1000.1);
|
|
assert.strictEqual(parseMonetary("$-1\u00a0000,11"), -1000.11);
|
|
|
|
// any symbol
|
|
assert.strictEqual(parseMonetary("1\u00a0000,11EUROS"), 1000.11);
|
|
assert.strictEqual(parseMonetary("EUR1\u00a0000,12"), 1000.12);
|
|
assert.strictEqual(parseMonetary("DOL1\u00a0000,13"), 1000.13);
|
|
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);
|
|
});
|
|
|
|
QUnit.test("parsers fallback on english localisation", function (assert) {
|
|
patchWithCleanup(localization, {
|
|
decimalPoint: ",",
|
|
thousandsSep: ".",
|
|
});
|
|
|
|
assert.strictEqual(parseInteger("1,000,000"), 1000000);
|
|
assert.strictEqual(parseFloat("1,000,000.50"), 1000000.5);
|
|
});
|
|
});
|