[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:
committed by
Mathieu Duckerts-Antoine
parent
c5886e1109
commit
c28244e4b2
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user