[FIX] web: kanban progress bar: display correct aggregates

Before this commit, in a grouped kanban view with progressbars
and an aggregate field, after clicking on a bar to filter
records, the aggregate value was always 0 (at least when grouped
by a many2one or date(time) field).

This was due to a mismatch when trying to find the value of the
aggregate in the web_read_group result, as when grouped by a date
or datetime field, the key is `fieldname:granularity`, and we were
looking for the fieldname only. And for the many2one case, we were
comparing a pair [id, display_name] with an id.

This commit fixes the issue. It also fixes the mocked version of
read_progress_bar in the MockServer, s.t. we can correctly
reproduce the scenario in tests, as in the previous version, keys
in the returned object weren't computed the same way as in the real
read_progress_bar (e.g., "14,Mitchel", instead of "Mitchel"). A
similar fix has been done in [1]. This allows us to introduce a
test when grouped by many2one, which doesn't work as of 17.0.

[1] fd759f18d056844c486a68d0c394df5a03e789f0

closes odoo/odoo#155701

X-original-commit: 69b1809ed763d03560983bb0e4c996946dd694c5
Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Aaron Bohy
2024-02-28 18:30:16 +00:00
parent 2bcccbfb91
commit d111e66525
3 changed files with 143 additions and 17 deletions
@@ -30,6 +30,14 @@ function _createFilterDomain(fieldName, bars, value) {
return filterDomain;
}
function _groupsToAggregateValues(groups, groupBy, fields) {
const groupByFieldName = groupBy[0].split(":")[0];
return groups.map((g) => {
const groupInfo = extractInfoFromGroupData(g, groupBy, fields);
return Object.assign(groupInfo.aggregates, { [groupByFieldName]: groupInfo.serverValue });
});
}
class ProgressBarState {
constructor(progressAttributes, model, aggregateFields, activeBars = {}) {
this.progressAttributes = progressAttributes;
@@ -185,22 +193,19 @@ class ProgressBarState {
bars,
activeBar.value
);
const { context, groupBy, resModel } = this.model.root;
const { context, fields, groupBy, resModel } = this.model.root;
const kwargs = { context };
const fieldNames = this._aggregateFields.map((f) => f.name);
const fields = [...fieldNames, group.groupByField.name];
const fieldNames = [...this._aggregateFields.map((f) => f.name), group.groupByField.name];
const domain = filterDomain
? Domain.and([group.groupDomain, filterDomain]).toList()
: group.groupDomain;
return this.model.orm
.webReadGroup(resModel, domain, fields, groupBy, kwargs)
.webReadGroup(resModel, domain, fieldNames, groupBy, kwargs)
.then((res) => {
if (res.length) {
const resGroup = _findGroup(res.groups, group.groupByField, group.serverValue);
activeBar.aggregates = {
...resGroup,
[group.groupByField.name]: group.serverValue,
};
const groupByField = group.groupByField;
const aggrValues = _groupsToAggregateValues(res.groups, groupBy, fields);
activeBar.aggregates = _findGroup(aggrValues, groupByField, group.serverValue);
}
});
}
@@ -239,10 +244,7 @@ class ProgressBarState {
groupBy,
kwargs
);
this._aggregateValues = res.groups.map((r) => {
const groupInfo = extractInfoFromGroupData(r, groupBy, fields);
return { ...groupInfo.aggregates, [firstGroupByName]: groupInfo.serverValue };
});
this._aggregateValues = _groupsToAggregateValues(res.groups, groupBy, fields);
}
async _updateProgressBar() {
@@ -1327,6 +1327,9 @@ export class MockServer {
for (const group of groups) {
const records = this.getRecords(modelName, group.__domain || []);
let groupByValue = group[groupBy]; // always technical value here
if (Array.isArray(groupByValue)) {
groupByValue = groupByValue[1];
}
// special case for bool values: rpc call response with capitalized strings
if (!(groupByValue in data)) {
@@ -10417,6 +10417,123 @@ QUnit.module("Views", (hooks) => {
assert.verifySteps(["web_read_group", "read_progress_bar", "web_search_read"]);
});
QUnit.test(
"progress bar with aggregates: activate bars (grouped by boolean)",
async (assert) => {
serverData.models.partner.records = [
{ foo: "yop", bar: true, int_field: 1 },
{ foo: "yop", bar: true, int_field: 2 },
{ foo: "blip", bar: true, int_field: 4 },
{ foo: "gnap", bar: true, int_field: 8 },
];
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}' sum_field="int_field"/>
<templates><t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t></templates>
</kanban>`,
groupBy: ["bar"],
});
assert.deepEqual(getTooltips(target, 0), ["2 yop", "1 gnap", "1 blip"]);
assert.deepEqual(getCounters(target), ["15"]);
await click(getProgressBars(target, 0)[0]);
assert.deepEqual(getCounters(target), ["3"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["4"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["15"]);
}
);
QUnit.test(
"progress bar with aggregates: activate bars (grouped by many2one)",
async (assert) => {
serverData.models.partner.records = [
{ foo: "yop", product_id: 3, int_field: 1 },
{ foo: "yop", product_id: 3, int_field: 2 },
{ foo: "blip", product_id: 3, int_field: 4 },
{ foo: "gnap", product_id: 3, int_field: 8 },
];
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}' sum_field="int_field"/>
<templates><t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t></templates>
</kanban>`,
groupBy: ["product_id"],
});
assert.deepEqual(getTooltips(target, 0), ["2 yop", "1 gnap", "1 blip"]);
assert.deepEqual(getCounters(target), ["15"]);
await click(getProgressBars(target, 0)[0]);
assert.deepEqual(getCounters(target), ["3"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["4"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["15"]);
}
);
QUnit.test("progress bar with aggregates: activate bars (grouped by date)", async (assert) => {
serverData.models.partner.records = [
{ foo: "yop", date: "2023-10-08", int_field: 1 },
{ foo: "yop", date: "2023-10-08", int_field: 2 },
{ foo: "blip", date: "2023-10-08", int_field: 4 },
{ foo: "gnap", date: "2023-10-08", int_field: 8 },
];
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<progressbar field="foo" colors='{"yop": "success", "gnap": "warning", "blip": "danger"}' sum_field="int_field"/>
<templates><t t-name="kanban-box">
<div>
<field name="foo"/>
</div>
</t></templates>
</kanban>`,
groupBy: ["date:week"],
});
assert.deepEqual(getTooltips(target, 0), ["2 yop", "1 gnap", "1 blip"]);
assert.deepEqual(getCounters(target), ["15"]);
await click(getProgressBars(target, 0)[0]);
assert.deepEqual(getCounters(target), ["3"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["4"]);
await click(getProgressBars(target, 0)[2]);
assert.deepEqual(getCounters(target), ["15"]);
});
QUnit.test("load more should load correct records after drag&drop event", async (assert) => {
// Add a sequence number and initialize
serverData.models.partner.records.forEach((el, i) => (el.sequence = i));
@@ -13944,7 +14061,7 @@ QUnit.module("Views", (hooks) => {
});
QUnit.test("scroll on group unfold and progressbar click", async (assert) => {
assert.expect(7);
assert.expect(15);
await makeView({
type: "kanban",
@@ -13959,17 +14076,21 @@ QUnit.module("Views", (hooks) => {
</kanban>`,
groupBy: ["product_id"],
async mockRPC(route, args, performRPC) {
assert.step(args.method);
if (args.method === "web_read_group") {
const result = await performRPC(route, args);
if (result.groups.length) {
result.groups[0].__fold = false;
result.groups[1].__fold = true;
if (result.groups[1]) {
result.groups[1].__fold = true;
}
}
return result;
}
},
});
assert.verifySteps(["get_views", "web_read_group", "read_progress_bar", "web_search_read"]);
const content = target.querySelector(".o_content");
content.scrollTo = (params) => {
assert.step("scrolled");
@@ -13977,12 +14098,12 @@ QUnit.module("Views", (hooks) => {
};
await click(getProgressBars(target, 0)[0]);
assert.verifySteps(["scrolled"]);
assert.verifySteps(["web_read_group", "web_search_read", "scrolled"]);
const column1 = getColumn(target, 1);
assert.hasClass(column1, "o_column_folded");
await click(column1);
assert.verifySteps(["scrolled"]);
assert.verifySteps(["web_search_read", "scrolled"]);
});
QUnit.test(