[IMP] web: graph: add group by many2many

The purpose of this commit is to enable the "group by" functionality of
many2many in graph views.
Some other views also implement it (e.g. Gannt odoo/enterprise#20192,
List and Kanban odoo/odoo#74985), it is therefore a logical step to also support group
by many2many in graph views.

Previous behavior:
It is possible to select a many2many in the group by but it has no effect in the graph.

Expected behavior with this commit:
It is possible to select a many2many in the group by and it has an impact in the graph.

task-2721035

closes odoo/odoo#98009

Related: odoo/enterprise#30272
Signed-off-by: Mathieu Duckerts-Antoine <dam@odoo.com>
This commit is contained in:
Dardenne Florent (dafl)
2022-08-23 16:29:02 +02:00
parent 771fdcc67c
commit 87effdf514
2 changed files with 182 additions and 3 deletions
@@ -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)
) {
@@ -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: `
<graph>
<field name="revenue" type="measure"/>
<field name="color_ids"/>
</graph>`,
});
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: `
<graph>
<field name="revenue" type="measure"/>
<field name="color_ids"/>
</graph>`,
});
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: `
<graph type="line">
<field name="revenue" type="measure"/>
<field name="color_ids"/>
</graph>
`,
});
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: `
<graph type="pie">
<field name="revenue" type="measure"/>
<field name="color_ids"/>
</graph>
`,
});
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({