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