[FIX] web: do not use groupbys in views not groupable

Before this commit: A problem occurs with kanban views that are not
groupable (i.e. for which searchMenuTypes does not contain 'groupBy'):
if the action has a default groupby (set in Studio for instance) then
it is still applied and causes crashes. That happens in the documents
kanban view for instance.
More generally, if a view is not groupable, some information on groupbys
can appear in the interface (in a facet) while not used/useful
for the view.

After this commit:
    - if a kanban view is not groupable, a default groupby will never be
     applied.

    - if a view is not groupable, we ensures that
        - the search query generated in the control panel has
          groupBy = [],
        - groupbys cannot be represented in a facet.

closes odoo/odoo#51041

X-original-commit: 91c80291e90127792623476d8997fd2a67569170
Signed-off-by: Damien Bouvy (dbo) <dbo@odoo.com>
This commit is contained in:
Mohammed Shekha
2020-05-11 13:40:10 +00:00
committed by Mathieu Duckerts-Antoine
parent c5886e1109
commit c28244e4b2
5 changed files with 139 additions and 17 deletions
@@ -346,22 +346,33 @@ odoo.define('web.ControlPanelModel', function (require) {
* @returns {Object[]}
*/
getFacets() {
const groups = this._getGroups();
return groups.map(group => {
const { activities, type, id } = group;
const facet = {
groupId: id,
separator: type === 'groupBy' ? ">" : this.env._t("or"),
values: this._getFacetDescriptions(activities, type),
};
const icon = FACET_ICONS[type];
if (icon) {
facet.icon = icon;
} else {
facet.title = activities[0].filter.description;
const accept = type => {
if (type === 'groupBy' && !this.searchMenuTypes.includes('groupBy')) {
return false;
}
return facet;
});
return true;
};
const groups = this._getGroups();
const facets = [];
for (const group of groups) {
const { activities, type, id } = group;
if (accept(type)) {
const facet = {
groupId: id,
separator: type === 'groupBy' ? ">" : this.env._t("or"),
values: this._getFacetDescriptions(activities, type),
};
const icon = FACET_ICONS[type];
if (icon) {
facet.icon = icon;
} else {
facet.title = activities[0].filter.description;
}
facets.push(facet);
}
}
return facets;
}
/**
@@ -400,9 +411,14 @@ odoo.define('web.ControlPanelModel', function (require) {
const query = {
context: this._getContext(groups),
domain: this._getDomain(groups, requireEvaluation),
groupBy: this._getGroupBy(groups),
orderedBy: this._getOrderedBy(groups)
};
if (this.searchMenuTypes.includes('groupBy')) {
const groupBy = this._getGroupBy(groups);
query.groupBy = groupBy;
} else {
query.groupBy = [];
}
if (this.searchMenuTypes.includes('timeRange')) {
const timeRanges = this._getTimeRanges(requireEvaluation);
query.timeRanges = timeRanges || {};
@@ -108,7 +108,7 @@ var KanbanView = BasicView.extend({
*/
_updateMVCParams: function () {
this._super.apply(this, arguments);
if (!this.noDefaultGroupby && this.arch.attrs.default_group_by) {
if (this.searchMenuTypes.includes('groupBy') && !this.noDefaultGroupby && this.arch.attrs.default_group_by) {
this.loadParams.groupBy = [this.arch.attrs.default_group_by];
}
},
@@ -125,5 +125,27 @@ odoo.define('web.control_panel_tests', function (require) {
controlPanel.destroy();
});
QUnit.test('groupby menu is not rendered if searchMenuTypes does not have groupBy', async function (assert) {
assert.expect(2);
const arch = `<search/>`;
const searchMenuTypes = ['filter'];
const params = {
cpStoreConfig: {
viewInfo: { arch, fields: this.fields },
searchMenuTypes
},
cpProps: { fields: this.fields, searchMenuTypes },
};
const controlPanel = await createControlPanel(params);
assert.containsOnce(controlPanel.el, 'div.o_search_options div.o_filter_menu');
assert.containsNone(controlPanel.el, 'div.o_search_options div.o_group_by_menu');
controlPanel.destroy();
});
});
});
@@ -2909,6 +2909,50 @@ QUnit.module('Views', {
kanban.destroy();
});
QUnit.test('kanban view not groupable', async function (assert) {
assert.expect(3);
const searchMenuTypesOriginal = KanbanView.prototype.searchMenuTypes;
KanbanView.prototype.searchMenuTypes = ['filter', 'favorite'];
const kanban = await createView({
View: KanbanView,
model: 'partner',
data: this.data,
arch: `
<kanban class="o_kanban_test" default_group_by="bar">
<field name="bar"/>
<templates>
<t t-name="kanban-box">
<div><field name="foo"/></div>
</t>
</templates>
</kanban>
`,
archs: {
'partner,false,search': `
<search>
<filter string="candle" name="itsName" context="{'group_by': 'foo'}"/>
</search>
`,
},
mockRPC: function (route, args) {
if (args.method === 'read_group') {
throw new Error("Should not do a read_group RPC");
}
return this._super.apply(this, arguments);
},
context: { search_default_itsName: 1, },
});
assert.doesNotHaveClass(kanban.$('.o_kanban_view'), 'o_kanban_grouped');
assert.containsNone(kanban, '.o_control_panel div.o_search_options div.o_group_by_menu');
assert.deepEqual(cpHelpers.getFacetTexts(kanban), []);
kanban.destroy();
KanbanView.prototype.searchMenuTypes = searchMenuTypesOriginal;
});
QUnit.test('kanban view with create=False', async function (assert) {
assert.expect(1);
@@ -1283,6 +1283,46 @@ QUnit.module('Views', {
actionManager.destroy();
});
QUnit.test('list view not groupable', async function (assert) {
assert.expect(2);
const searchMenuTypesOriginal = ListView.prototype.searchMenuTypes;
ListView.prototype.searchMenuTypes = ['filter', 'favorite'];
const list = await createView({
View: ListView,
model: 'foo',
data: this.data,
arch: `
<tree editable="top">
<field name="display_name"/>
<field name="foo"/>
</tree>
`,
archs: {
'foo,false,search': `
<search>
<filter context="{'group_by': 'foo'}" name="foo"/>
</search>
`,
},
mockRPC: function (route, args) {
if (args.method === 'read_group') {
throw new Error("Should not do a read_group RPC");
}
return this._super.apply(this, arguments);
},
context: { search_default_foo: 1, },
});
assert.containsNone(list, '.o_control_panel div.o_search_options div.o_group_by_menu',
"there should not be groupby menu");
assert.deepEqual(cpHelpers.getFacetTexts(list), []);
list.destroy();
ListView.prototype.searchMenuTypes = searchMenuTypesOriginal;
});
QUnit.test('selection changes are triggered correctly', async function (assert) {
assert.expect(8);