[FIX] web: formatter: monetary with human number

HumanNumber now correctly rounds the number according to decimals (bug with value < 1000 eg: 0.02)
It also correctly keeps `decimals` significant figures whatever powers of 10^3 it should keep

The monetary formatter now passes the currency's digits to human number

Part-of: odoo/odoo#73311
This commit is contained in:
Lucas Perais (lpe)
2021-09-07 10:04:34 +00:00
committed by Aaron Bohy
parent 0008067ee5
commit a19ad0fe24
2 changed files with 40 additions and 13 deletions
+15 -4
View File
@@ -36,7 +36,9 @@ function insertThousandsSep(number, thousandsSep = ",", grouping = [3, 0]) {
* @private
* @param {number} number to format
* @param {Object} [options] Options to format
* @param {number} [options.decimals=0] maximum number of decimals to use
* @param {number} [options.decimals=0] number of decimals to use
* if minDigits > 1 is used and effective on the number then decimals
* will be shrunk to zero, to avoid displaying irrelevant figures ( 0.01 compared to 1000 )
* @param {number} [options.minDigits=1]
* the minimum number of digits to preserve when switching to another
* level of thousands (e.g. with a value of '2', 4321 will still be
@@ -44,11 +46,11 @@ function insertThousandsSep(number, thousandsSep = ",", grouping = [3, 0]) {
* @returns {string}
*/
function humanNumber(number, options = { decimals: 0, minDigits: 1 }) {
number = Math.round(number);
const decimals = options.decimals || 0;
const minDigits = options.minDigits || 1;
const d2 = Math.pow(10, decimals);
const numberMagnitude = +number.toExponential().split("e+")[1];
number = Math.round(number * d2) / d2;
// the case numberMagnitude >= 21 corresponds to a number
// better expressed in the scientific format.
if (numberMagnitude >= 21) {
@@ -72,7 +74,10 @@ function humanNumber(number, options = { decimals: 0, minDigits: 1 }) {
}
}
const { decimalPoint, grouping, thousandsSep } = l10n;
const [integerPart, decimalPart] = String(number).split(".");
// determine if we should keep the decimals (we don't want to display 1,020.02k for 1020020)
const decimalsToKeep = number >= 1000 ? 0 : decimals;
const [integerPart, decimalPart] = number.toFixed(decimalsToKeep).split(".");
const int = insertThousandsSep(sign * Number(integerPart), thousandsSep, grouping);
if (!decimalPart) {
return int + symbol;
@@ -282,7 +287,13 @@ export function formatMonetary(value, options = {}) {
const currency = session.currencies[currencyId];
const digits = (currency && currency.digits) || options.digits;
const formatted = options.humanReadable ? humanNumber(value) : formatFloat(value, { digits });
let formatted;
if (options.humanReadable) {
formatted = humanNumber(value, { decimals: digits ? digits[1] : 2 });
} else {
formatted = formatFloat(value, { digits });
}
if (!currency || options.noSymbol) {
return formatted;
}
@@ -61,7 +61,7 @@ QUnit.module("Fields", (hooks) => {
);
assert.strictEqual(
formatFloat(10200000, { humanReadable: true, decimals: 2, minDigits: 2 }),
"10.2M"
"10.20M"
);
assert.strictEqual(
formatFloat(1020, { humanReadable: true, decimals: 2, minDigits: 1 }),
@@ -69,15 +69,15 @@ QUnit.module("Fields", (hooks) => {
);
assert.strictEqual(
formatFloat(1002, { humanReadable: true, decimals: 2, minDigits: 1 }),
"1k"
"1.00k"
);
assert.strictEqual(
formatFloat(101, { humanReadable: true, decimals: 2, minDigits: 1 }),
"101"
"101.00"
);
assert.strictEqual(
formatFloat(64.2, { humanReadable: true, decimals: 2, minDigits: 1 }),
"64"
"64.20"
);
assert.strictEqual(formatFloat(1e18, { humanReadable: true }), "1E");
assert.strictEqual(
@@ -110,7 +110,7 @@ QUnit.module("Fields", (hooks) => {
);
assert.strictEqual(
formatFloat(-10200000, { humanReadable: true, decimals: 2, minDigits: 2 }),
"-10.2M"
"-10.20M"
);
assert.strictEqual(
formatFloat(-1020, { humanReadable: true, decimals: 2, minDigits: 1 }),
@@ -118,15 +118,15 @@ QUnit.module("Fields", (hooks) => {
);
assert.strictEqual(
formatFloat(-1002, { humanReadable: true, decimals: 2, minDigits: 1 }),
"-1k"
"-1.00k"
);
assert.strictEqual(
formatFloat(-101, { humanReadable: true, decimals: 2, minDigits: 1 }),
"-101"
"-101.00"
);
assert.strictEqual(
formatFloat(-64.2, { humanReadable: true, decimals: 2, minDigits: 1 }),
"-64"
"-64.20"
);
assert.strictEqual(formatFloat(-1e18, { humanReadable: true }), "-1E");
assert.strictEqual(
@@ -235,9 +235,14 @@ QUnit.module("Fields", (hooks) => {
formatMonetary(1234567.654, { currencyId: 10, noSymbol: true }),
"1,234,567.65"
);
assert.deepEqual(formatMonetary(8.0, { currencyId: 10, humanReadable: true }), "8.00 €");
assert.deepEqual(
formatMonetary(1234567.654, { currencyId: 10, humanReadable: true }),
"1M €"
"1.23M €"
);
assert.deepEqual(
formatMonetary(1990000.001, { currencyId: 10, humanReadable: true }),
"1.99M €"
);
assert.deepEqual(
formatMonetary(1234567.654, { currencyId: 44, digits: [69, 1] }),
@@ -269,6 +274,17 @@ QUnit.module("Fields", (hooks) => {
assert.strictEqual(formatMonetary(200, { field: floatField, data }), "$ 200.00");
});
QUnit.test("formatMonetary without currency", function (assert) {
patchWithCleanup(session, {
currencies: {},
});
assert.deepEqual(
formatMonetary(1234567.654, { currencyId: 10, humanReadable: true }),
"1.23M"
);
assert.deepEqual(formatMonetary(1234567.654, { currencyId: 10 }), "1,234,567.65");
});
QUnit.test("formatPercentage", function (assert) {
assert.strictEqual(formatPercentage(false), "0%");
assert.strictEqual(formatPercentage(0), "0%");