From c28244e4b256c3858ea9fefa3a536907876737fa Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Fri, 21 Feb 2020 10:32:14 +0000 Subject: [PATCH] [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) --- .../js/control_panel/control_panel_model.js | 48 ++++++++++++------- .../static/src/js/views/kanban/kanban_view.js | 2 +- .../control_panel/control_panel_tests.js | 22 +++++++++ addons/web/static/tests/views/kanban_tests.js | 44 +++++++++++++++++ addons/web/static/tests/views/list_tests.js | 40 ++++++++++++++++ 5 files changed, 139 insertions(+), 17 deletions(-) diff --git a/addons/web/static/src/js/control_panel/control_panel_model.js b/addons/web/static/src/js/control_panel/control_panel_model.js index 93c9d225d9b..034c4cd22c7 100644 --- a/addons/web/static/src/js/control_panel/control_panel_model.js +++ b/addons/web/static/src/js/control_panel/control_panel_model.js @@ -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 || {}; diff --git a/addons/web/static/src/js/views/kanban/kanban_view.js b/addons/web/static/src/js/views/kanban/kanban_view.js index d49b4a73c4e..1add9169244 100644 --- a/addons/web/static/src/js/views/kanban/kanban_view.js +++ b/addons/web/static/src/js/views/kanban/kanban_view.js @@ -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]; } }, diff --git a/addons/web/static/tests/control_panel/control_panel_tests.js b/addons/web/static/tests/control_panel/control_panel_tests.js index 560063cefb2..af6704da7af 100644 --- a/addons/web/static/tests/control_panel/control_panel_tests.js +++ b/addons/web/static/tests/control_panel/control_panel_tests.js @@ -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 = ``; + 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(); + }); + + }); }); diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js index 0b261317446..22c29168585 100644 --- a/addons/web/static/tests/views/kanban_tests.js +++ b/addons/web/static/tests/views/kanban_tests.js @@ -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: ` + + + + +
+
+
+
+ `, + archs: { + 'partner,false,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); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 7f60f1e0bd9..0025421c45a 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -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: ` + + + + + `, + archs: { + 'foo,false,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);