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