diff --git a/addons/web/static/src/views/list/list_arch_parser.js b/addons/web/static/src/views/list/list_arch_parser.js
index 6884358f558..192532bf165 100644
--- a/addons/web/static/src/views/list/list_arch_parser.js
+++ b/addons/web/static/src/views/list/list_arch_parser.js
@@ -68,7 +68,6 @@ export class ListArchParser extends XMLParser {
const groupListArchParser = new GroupListArchParser();
let buttonGroup;
let handleField = null;
- let defaultOrder = stringToOrderBy(xmlDoc.getAttribute("default_order") || null);
const treeAttr = {};
let nextId = 0;
const activeFields = {};
@@ -207,6 +206,9 @@ export class ListArchParser extends XMLParser {
treeAttr.expand = archParseBoolean(xmlDoc.getAttribute("expand") || "");
treeAttr.decorations = getDecoration(xmlDoc);
+ treeAttr.defaultGroupBy = xmlDoc.getAttribute("default_group_by");
+ treeAttr.defaultOrder = stringToOrderBy(xmlDoc.getAttribute("default_order") || null);
+
// custom open action when clicking on record row
const action = xmlDoc.getAttribute("action");
const type = xmlDoc.getAttribute("type");
@@ -214,8 +216,8 @@ export class ListArchParser extends XMLParser {
}
});
- if (!defaultOrder.length && handleField) {
- defaultOrder = stringToOrderBy(handleField);
+ if (!treeAttr.defaultOrder.length && handleField) {
+ treeAttr.defaultOrder = stringToOrderBy(handleField);
}
for (const [key, field] of Object.entries(fieldNodes)) {
@@ -230,7 +232,6 @@ export class ListArchParser extends XMLParser {
activeFields,
columns,
groupBy,
- defaultOrder,
__rawArch: arch,
...treeAttr,
};
diff --git a/addons/web/static/src/views/list/list_controller.js b/addons/web/static/src/views/list/list_controller.js
index 06bd880e714..15a90fb1194 100644
--- a/addons/web/static/src/views/list/list_controller.js
+++ b/addons/web/static/src/views/list/list_controller.js
@@ -70,6 +70,7 @@ export class ListController extends Component {
groupByInfo: this.archInfo.groupBy.fields,
limit: this.archInfo.limit || this.props.limit,
defaultOrder: this.archInfo.defaultOrder,
+ defaultGroupBy: this.props.searchMenuTypes.includes("groupBy") && this.props.archInfo.defaultGroupBy,
expand: this.archInfo.expand,
groupsLimit: this.archInfo.groupsLimit,
multiEdit: this.multiEdit,
diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js
index 8d8c2ed3efb..fb01f8d6879 100644
--- a/addons/web/static/tests/views/list_view_tests.js
+++ b/addons/web/static/tests/views/list_view_tests.js
@@ -16634,4 +16634,43 @@ QUnit.module("Views", (hooks) => {
await click(target, ".o_field_many2one_selection .o-autocomplete--input");
assert.verifySteps(["name_search"]);
});
+
+ QUnit.test("list view with default_group_by", async (assert) => {
+ serverData.models.foo.fields.m2m.store = true;
+
+ let readGroupCount = 0;
+ await makeView({
+ type: "list",
+ resModel: "foo",
+ serverData,
+ arch: `
+
+
+
+ `,
+ async mockRPC(route, { kwargs }) {
+ if (route === "/web/dataset/call_kw/partner/web_read_group") {
+ readGroupCount++;
+ switch (readGroupCount) {
+ case 1:
+ return assert.deepEqual(kwargs.groupby, ["bar"]);
+ case 2:
+ return assert.deepEqual(kwargs.groupby, ["m2m"]);
+ case 3:
+ return assert.deepEqual(kwargs.groupby, ["bar"]);
+ }
+ }
+ },
+ });
+
+ assert.hasClass(target.querySelector(".o_list_renderer table"), "o_list_table_grouped");
+ assert.containsN(target, ".o_group_header", 2);
+
+ await groupByMenu(target, "m2m");
+ assert.containsN(target, ".o_group_header", 4);
+
+ await toggleMenuItem(target, "M2M field");
+ assert.containsN(target, ".o_group_header", 2);
+ });
+
});
diff --git a/odoo/addons/base/rng/tree_view.rng b/odoo/addons/base/rng/tree_view.rng
index ed194bbda6e..a02c3223503 100644
--- a/odoo/addons/base/rng/tree_view.rng
+++ b/odoo/addons/base/rng/tree_view.rng
@@ -36,6 +36,7 @@
+