[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:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user