diff --git a/addons/web/static/src/views/kanban/kanban_view.js b/addons/web/static/src/views/kanban/kanban_view.js
index 0cb59157b78..a82f5607218 100644
--- a/addons/web/static/src/views/kanban/kanban_view.js
+++ b/addons/web/static/src/views/kanban/kanban_view.js
@@ -12,7 +12,6 @@ export const kanbanView = {
display_name: "Kanban",
icon: "oi oi-view-kanban",
- isMobileFriendly: true,
multiRecord: true,
ArchParser: KanbanArchParser,
diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js
index 2ae1993c2b2..de0dff5eaa3 100644
--- a/addons/web/static/src/webclient/actions/action_service.js
+++ b/addons/web/static/src/webclient/actions/action_service.js
@@ -943,11 +943,10 @@ function makeActionManager(env) {
}
if (env.isSmall) {
- if (!view.isMobileFriendly) {
- view = _findMobileView(views, view.multiRecord) || view;
- }
- if (lazyView && !lazyView.isMobileFriendly) {
- lazyView = _findMobileView(views, lazyView.multiRecord) || lazyView;
+ view = _findView(views, view.multiRecord, action.mobile_view_mode) || view;
+ if (lazyView) {
+ lazyView =
+ _findView(views, lazyView.multiRecord, action.mobile_view_mode) || lazyView;
}
}
@@ -984,15 +983,14 @@ function makeActionManager(env) {
}
/**
- * Helper function to find the first mobile-friendly view, if any.
- *
* @private
* @param {Array} views an array of views
* @param {boolean} multiRecord true if we search for a multiRecord view
- * @returns {Object|undefined} first mobile-friendly view found
+ * @param {string} viewType type of the view to search
+ * @returns {Object|undefined} the requested view if it could be found
*/
- function _findMobileView(views, multiRecord) {
- return views.find((view) => view.isMobileFriendly && view.multiRecord === multiRecord);
+ function _findView(views, multiRecord, viewType) {
+ return views.find((v) => v.type === viewType && v.multiRecord == multiRecord);
}
// ---------------------------------------------------------------------------
diff --git a/addons/web/static/tests/mobile/webclient/window_action_tests.js b/addons/web/static/tests/mobile/webclient/window_action_tests.js
new file mode 100644
index 00000000000..d809e5c3194
--- /dev/null
+++ b/addons/web/static/tests/mobile/webclient/window_action_tests.js
@@ -0,0 +1,67 @@
+/** @odoo-module **/
+
+import { getFixture } from "@web/../tests/helpers/utils";
+import { setupViewRegistries } from "@web/../tests/views/helpers";
+import { createWebClient, doAction } from "@web/../tests/webclient/helpers";
+
+let serverData, target;
+
+QUnit.module("ActionManager", (hooks) => {
+ hooks.beforeEach(() => {
+ serverData = {
+ models: {
+ project: {
+ fields: {
+ foo: { string: "Foo", type: "boolean" },
+ },
+ records: [
+ {
+ id: 1,
+ foo: true,
+ },
+ {
+ id: 2,
+ foo: false,
+ },
+ ],
+ },
+ },
+ views: {
+ "project,false,list": '
',
+ "project,false,kanban": `
+
+
+
+
+
+
+
+
+
+ `,
+ "project,false,search": "",
+ },
+ };
+ target = getFixture();
+ setupViewRegistries();
+ });
+
+ QUnit.module("Window Actions");
+
+ QUnit.test("execute a window action with mobile_view_mode", async (assert) => {
+ const webClient = await createWebClient({ serverData });
+ await doAction(webClient, {
+ xml_id: "project.action",
+ name: "Project Action",
+ res_model: "project",
+ type: "ir.actions.act_window",
+ view_mode: "list,kanban",
+ mobile_view_mode: "list",
+ views: [
+ [false, "kanban"],
+ [false, "list"],
+ ],
+ });
+ assert.containsOnce(target, ".o_list_view");
+ });
+});
diff --git a/addons/web/static/tests/webclient/helpers.js b/addons/web/static/tests/webclient/helpers.js
index 5ebcc47a6bf..b93f8889a7d 100644
--- a/addons/web/static/tests/webclient/helpers.js
+++ b/addons/web/static/tests/webclient/helpers.js
@@ -174,6 +174,7 @@ export function getActionManagerServerData() {
xml_id: "action_3",
name: "Partners",
res_model: "partner",
+ mobile_view_mode: "kanban",
type: "ir.actions.act_window",
views: [
[false, "list"],
diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py
index 388b8fa5d42..bbe6cec211a 100644
--- a/odoo/addons/base/models/ir_actions.py
+++ b/odoo/addons/base/models/ir_actions.py
@@ -272,6 +272,7 @@ class IrActionsActWindow(models.Model):
target = fields.Selection([('current', 'Current Window'), ('new', 'New Window'), ('inline', 'Inline Edit'), ('fullscreen', 'Full Screen'), ('main', 'Main action of Current Window')], default="current", string='Target Window')
view_mode = fields.Char(required=True, default='tree,form',
help="Comma-separated list of allowed view modes, such as 'form', 'tree', 'calendar', etc. (Default: tree,form)")
+ mobile_view_mode = fields.Char(default="kanban", help="First view mode in mobile and small screen environments (default='kanban'). If it can't be found among available view modes, the same mode as for wider screens is used)")
usage = fields.Char(string='Action Usage',
help="Used to filter menu and home actions from the user form.")
view_ids = fields.One2many('ir.actions.act_window.view', 'act_window_id', string='No of Views')
@@ -327,9 +328,8 @@ class IrActionsActWindow(models.Model):
def _get_readable_fields(self):
return super()._get_readable_fields() | {
- "context", "domain", "filter", "groups_id", "limit", "res_id",
- "res_model", "search_view_id", "target", "view_id",
- "view_mode", "views",
+ "context", "mobile_view_mode", "domain", "filter", "groups_id", "limit",
+ "res_id", "res_model", "search_view_id", "target", "view_id", "view_mode", "views",
# `flags` is not a real field of ir.actions.act_window but is used
# to give the parameters to generate the action
"flags"
diff --git a/odoo/addons/base/views/ir_actions_views.xml b/odoo/addons/base/views/ir_actions_views.xml
index a92e825344d..eca3223fda1 100644
--- a/odoo/addons/base/views/ir_actions_views.xml
+++ b/odoo/addons/base/views/ir_actions_views.xml
@@ -215,6 +215,7 @@
+