diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 150b97ff0d8..35435a3e500 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -4323,8 +4323,11 @@ var BasicModel = AbstractModel.extend({ self.localData[newGroup.id] = newGroup; } else if (!newGroup.openGroupByDefault || openGroupCount >= self.OPEN_GROUP_LIMIT) { newGroup.isOpen = false; + } else if ('__fold' in group) { + newGroup.isOpen = !group.__fold; } else { - newGroup.isOpen = '__fold' in group ? !group.__fold : true; + // open the group iff it is a first level group + newGroup.isOpen = !self.localData[newGroup.parentID].parentID; } list.data.push(newGroup.id); list.count += newGroup.count; diff --git a/addons/web/static/src/js/views/list/list_view.js b/addons/web/static/src/js/views/list/list_view.js index 60f0bc32cf8..163ac850817 100644 --- a/addons/web/static/src/js/views/list/list_view.js +++ b/addons/web/static/src/js/views/list/list_view.js @@ -54,6 +54,7 @@ var ListView = BasicView.extend({ this.rendererParams.addCreateLineInGroups = this.rendererParams.editable && this.controllerParams.activeActions.create; this.loadParams.limit = this.loadParams.limit || 80; + this.loadParams.openGroupByDefault = !!JSON.parse(this.arch.attrs.expand || "0"); this.loadParams.type = 'list'; }, diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 48e4c975127..66201d3c38b 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -4065,6 +4065,60 @@ QUnit.module('Views', { delete widgetRegistry.map.asyncWidget; }); + QUnit.test('grouped list with expand attribute', async function (assert) { + assert.expect(6); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '', + groupBy: ['bar'], + mockRPC: function (route, args) { + assert.step(args.method || route); + return this._super.apply(this, arguments); + } + }); + + assert.containsN(list, '.o_group_header', 2); + assert.containsN(list, '.o_data_row', 4); + + assert.verifySteps([ + 'read_group', + '/web/dataset/search_read', + '/web/dataset/search_read', + ]); + + list.destroy(); + }); + + QUnit.test('grouped list (two levels) with expand attribute', async function (assert) { + // the expand attribute only opens the first level groups + assert.expect(5); + + var list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '', + groupBy: ['bar', 'int_field'], + mockRPC: function (route, args) { + assert.step(args.method || route); + return this._super.apply(this, arguments); + } + }); + + assert.containsN(list, '.o_group_header', 6); + + assert.verifySteps([ + 'read_group', // global + 'read_group', // first group + 'read_group', // second group + ]); + + list.destroy(); + }); + QUnit.test('editable grouped lists', async function (assert) { assert.expect(4); diff --git a/doc/reference/views.rst b/doc/reference/views.rst index 68d024e47db..0e2e30cf602 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -221,6 +221,9 @@ root can have the following attributes: corresponding attribute to ``false`` ``limit`` the default size of a page. It should be a positive integer +``expand`` + when the list view is grouped, automatically open the first level of groups + if set to true (default: false) Possible children elements of the list view are: diff --git a/odoo/addons/base/rng/tree_view.rng b/odoo/addons/base/rng/tree_view.rng index 3fd0cb6f754..8fc5bae7f9f 100644 --- a/odoo/addons/base/rng/tree_view.rng +++ b/odoo/addons/base/rng/tree_view.rng @@ -42,6 +42,14 @@ + + + + 1 + 0 + + +