From e96566e11d6084c6f510361ff1e5cb243eb3a84e Mon Sep 17 00:00:00 2001 From: "Julien Carion (juca)" Date: Fri, 24 Mar 2023 13:34:29 +0000 Subject: [PATCH] [FIX] web: Fix computation of aggregates in list_renderer This commit fixes how monetary fields are handled when doing aggregates in list view: aggregates should not be computed when all values are not in the same currency and the currency should be displayed with the aggregate when this is not the case. closes odoo/odoo#116556 Signed-off-by: Aaron Bohy (aab) --- .../static/src/views/list/list_renderer.js | 31 ++++++ .../web/static/tests/views/list_view_tests.js | 105 +++++++++++++++++- 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 1070114dcc0..58afbc64c3e 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -30,6 +30,7 @@ import { useRef, useState, } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; const formatters = registry.category("formatters"); @@ -604,6 +605,33 @@ export class ListRenderer extends Component { if (type !== "integer" && type !== "float" && type !== "monetary") { continue; } + let currencyId; + if (type === "monetary") { + const currencyField = + this.props.list.activeFields[column.name].options.currency_field || + this.fields[column.name].currency_field || + "currency_id"; + if (!(currencyField in this.props.list.activeFields)) { + aggregates[fieldName] = { + help: _t("No currency provided"), + value: "—", + }; + continue; + } + currencyId = values[0][currencyField]?.[0]; + if (currencyId) { + const sameCurrency = values.every( + (value) => currencyId === value[currencyField][0] + ); + if (!sameCurrency) { + aggregates[fieldName] = { + help: _t("Different currencies cannot be aggregated"), + value: "—", + }; + continue; + } + } + } const { attrs, widget } = column; const func = (attrs.sum && "sum") || @@ -628,6 +656,9 @@ export class ListRenderer extends Component { digits: attrs.digits ? JSON.parse(attrs.digits) : undefined, escape: true, }; + if (currencyId) { + formatOptions.currencyId = currencyId; + } aggregates[fieldName] = { help: attrs[func], value: formatter ? formatter(aggregateValue, formatOptions) : aggregateValue, diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 745d3dfa269..819cdbfe209 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -124,6 +124,12 @@ QUnit.module("Views", (hooks) => { relation: "res_currency", default: 1, }, + currency_test: { + string: "Currency", + type: "many2one", + relation: "res_currency", + default: 1, + }, company_currency_id: { string: "Company Currency", type: "many2one", @@ -3592,11 +3598,101 @@ QUnit.module("Views", (hooks) => { ); assert.strictEqual( target.querySelectorAll("tfoot td")[1].textContent, - "2000.000", - "aggregates monetary use digits attribute if available" + "—", + "aggregates monetary should never work if no currency field is present" ); }); + QUnit.test("aggregates monetary (same currency)", async function (assert) { + serverData.models.foo.records[0].currency_id = 1; + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + `, + }); + + assert.deepEqual(getNodesTextContent(target.querySelectorAll("tbody .o_monetary_cell")), [ + "$\u00a01200.00", + "$\u00a0500.00", + "$\u00a0300.00", + "$\u00a00.00", + ]); + + assert.strictEqual(target.querySelectorAll("tfoot td")[1].textContent, "$\u00a02000.00"); + }); + + QUnit.test("aggregates monetary (different currencies)", async function (assert) { + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + `, + }); + + assert.deepEqual(getNodesTextContent(target.querySelectorAll("tbody .o_monetary_cell")), [ + "1200.00\u00a0€", + "$\u00a0500.00", + "$\u00a0300.00", + "$\u00a00.00", + ]); + + assert.strictEqual(target.querySelectorAll("tfoot td")[1].textContent, "—"); + }); + + QUnit.test("aggregates monetary (currency field not in view)", async function (assert) { + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + `, + }); + + assert.deepEqual(getNodesTextContent(target.querySelectorAll("tbody .o_monetary_cell")), [ + "1200.00", + "500.00", + "300.00", + "0.00", + ]); + + assert.strictEqual(target.querySelectorAll("tfoot td")[1].textContent, "—"); + }); + + QUnit.test("aggregates monetary (currency field in view)", async function (assert) { + serverData.models.foo.fields.amount.currency_field = "currency_test"; + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + `, + }); + + assert.deepEqual(getNodesTextContent(target.querySelectorAll("tbody .o_monetary_cell")), [ + "$\u00a01200.00", + "$\u00a0500.00", + "$\u00a0300.00", + "$\u00a00.00", + ]); + + assert.strictEqual(target.querySelectorAll("tfoot td")[1].textContent, "$\u00a02000.00"); + }); + QUnit.test( "currency_field is taken into account when formatting monetary values", async (assert) => { @@ -3623,6 +3719,11 @@ QUnit.module("Views", (hooks) => { "$\u00a01100.00", "field should be formatted based on company_currency_id" ); + assert.strictEqual( + target.querySelectorAll("tfoot td")[1].textContent, + "—", + "aggregates monetary should never work if different currencies are used" + ); } );