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" + ); } );