diff --git a/addons/web/static/src/views/graph/graph_model.js b/addons/web/static/src/views/graph/graph_model.js
index 9f9f7744667..a5103051f1e 100644
--- a/addons/web/static/src/views/graph/graph_model.js
+++ b/addons/web/static/src/views/graph/graph_model.js
@@ -431,7 +431,7 @@ export class GraphModel extends Model {
label = `${val}`; // toUpperCase?
} else if (val === false) {
label = this.env._t("Undefined");
- } else if (type === "many2one") {
+ } else if (["many2many", "many2one"].includes(type)) {
const [id, name] = val;
const key = JSON.stringify([fieldName, name]);
if (!numbering[key]) {
@@ -501,9 +501,10 @@ export class GraphModel extends Model {
const processedGroupBy = [];
for (const gb of groupBy) {
const { fieldName, interval } = gb;
- const { sortable, type } = fields[fieldName];
+ const { sortable, type, store } = fields[fieldName];
if (
- !sortable ||
+ // many2many is groupable precisely when it is stored (cf. groupable in odoo/fields.py)
+ (type === "many2many" ? !store : !sortable) ||
["id", "__count"].includes(fieldName) ||
!GROUPABLE_TYPES.includes(type)
) {
diff --git a/addons/web/static/tests/views/graph_view_tests.js b/addons/web/static/tests/views/graph_view_tests.js
index 74862fc4df2..400540d08c2 100644
--- a/addons/web/static/tests/views/graph_view_tests.js
+++ b/addons/web/static/tests/views/graph_view_tests.js
@@ -193,6 +193,12 @@ QUnit.module("Views", (hooks) => {
group_operator: "sum",
sortable: true,
},
+ color_ids: {
+ string: "Colors",
+ type: "many2many",
+ relation: "color",
+ store: true,
+ },
},
records: [
{
@@ -202,6 +208,7 @@ QUnit.module("Views", (hooks) => {
product_id: 37,
date: "2016-01-01",
revenue: 1,
+ color_ids: [7],
},
{
id: 2,
@@ -211,6 +218,7 @@ QUnit.module("Views", (hooks) => {
color_id: 7,
date: "2016-01-03",
revenue: 2,
+ color_ids: [14],
},
{
id: 3,
@@ -219,6 +227,7 @@ QUnit.module("Views", (hooks) => {
product_id: 37,
date: "2016-03-04",
revenue: 3,
+ color_ids: [7, 14],
},
{
id: 4,
@@ -227,6 +236,7 @@ QUnit.module("Views", (hooks) => {
product_id: 37,
date: "2016-03-07",
revenue: 4,
+ color_ids: [7],
},
{
id: 5,
@@ -235,6 +245,7 @@ QUnit.module("Views", (hooks) => {
product_id: 41,
date: "2016-05-01",
revenue: 5,
+ color_ids: [7, 14],
},
{ id: 6, foo: 63, bar: false, product_id: 41 },
{ id: 7, foo: 42, bar: false, product_id: 41 },
@@ -550,6 +561,96 @@ QUnit.module("Views", (hooks) => {
);
});
+ QUnit.test("bar chart many2many groupBy", async function (assert) {
+ assert.expect(16);
+ const graph = await makeView({
+ serverData,
+ type: "graph",
+ resModel: "foo",
+ arch: `
+
+
+
+ `,
+ });
+ assert.containsOnce(target, "div.o_graph_canvas_container canvas");
+ checkLabels(assert, graph, ["Undefined", "black", "red"]);
+ checkDatasets(assert, graph, ["backgroundColor", "borderColor", "data", "label"], {
+ backgroundColor: "#1f77b4",
+ borderColor: undefined,
+ data: [8, 10, 13],
+ label: "Revenue",
+ });
+ checkLegend(assert, graph, "Revenue");
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "Undefined", value: "8" }], title: "Revenue" },
+ 0
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "black", value: "10" }], title: "Revenue" },
+ 1
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "red", value: "13" }], title: "Revenue" },
+ 2
+ );
+ });
+
+ QUnit.test("differentiate many2many values with same label", async function (assert) {
+ assert.expect(19);
+ serverData.models.color.records.push({ id: 21, display_name: "red" });
+ serverData.models.foo.records.push({ id: 30, color_ids: [21], revenue: 14 });
+ const graph = await makeView({
+ serverData,
+ type: "graph",
+ resModel: "foo",
+ arch: `
+
+
+
+ `,
+ });
+ assert.containsOnce(target, "div.o_graph_canvas_container canvas");
+ checkLabels(assert, graph, ["Undefined", "black", "red", "red (2)"]);
+ checkDatasets(assert, graph, ["backgroundColor", "borderColor", "data", "label"], {
+ backgroundColor: "#1f77b4",
+ borderColor: undefined,
+ data: [8, 10, 14, 13],
+ label: "Revenue",
+ });
+
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "Undefined", value: "8" }], title: "Revenue" },
+ 0
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "black", value: "10" }], title: "Revenue" },
+ 1
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "red", value: "14" }], title: "Revenue" },
+ 2
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "red (2)", value: "13" }], title: "Revenue" },
+ 3
+ );
+ });
+
QUnit.test(
"bar chart rendering (one groupBy, several domains with date identification)",
async function (assert) {
@@ -887,6 +988,42 @@ QUnit.module("Views", (hooks) => {
);
});
+ QUnit.test("line chart many2many groupBy", async function (assert) {
+ assert.expect(12);
+ const graph = await makeView({
+ serverData,
+ type: "graph",
+ resModel: "foo",
+ arch: `
+
+
+
+
+ `,
+ });
+ assert.containsOnce(target, "div.o_graph_canvas_container canvas");
+ checkLabels(assert, graph, ["black", "red"]);
+ checkDatasets(assert, graph, ["backgroundColor", "borderColor", "data", "label"], {
+ backgroundColor: "rgba(31,119,180,0.4)",
+ borderColor: "#1f77b4",
+ data: [10, 13],
+ label: "Revenue",
+ });
+ checkLegend(assert, graph, "Revenue");
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "black", value: "10" }], title: "Revenue" },
+ 0
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "red", value: "13" }], title: "Revenue" },
+ 1
+ );
+ });
+
QUnit.test("Stacked button visible in the line chart", async function (assert) {
const graph = await makeView({
serverData,
@@ -1506,6 +1643,47 @@ QUnit.module("Views", (hooks) => {
checkTooltip(assert, graph, { lines: [{ label: "true", value: "3 (37.50%)" }] }, 1);
});
+ QUnit.test("pie chart many2many groupby", async function (assert) {
+ assert.expect(16);
+ const graph = await makeView({
+ serverData,
+ type: "graph",
+ resModel: "foo",
+ arch: `
+
+
+
+
+ `,
+ });
+ assert.containsOnce(target, "div.o_graph_canvas_container canvas");
+ checkLabels(assert, graph, ["Undefined", "black", "red"]);
+ checkDatasets(assert, graph, ["backgroundColor", "borderColor", "data"], {
+ backgroundColor: ["#1f77b4", "#ff7f0e", "#aec7e8"],
+ borderColor: BORDER_WHITE,
+ data: [8, 10, 13],
+ });
+ checkLegend(assert, graph, ["Undefined", "black", "red"]);
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "Undefined", value: "8 (25.81%)" }], title: "Revenue" },
+ 0
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "black", value: "10 (32.26%)" }], title: "Revenue" },
+ 1
+ );
+ checkTooltip(
+ assert,
+ graph,
+ { lines: [{ label: "red", value: "13 (41.94%)" }], title: "Revenue" },
+ 2
+ );
+ });
+
QUnit.test("pie chart rendering (two groupBy)", async function (assert) {
assert.expect(16);
const graph = await makeView({