diff --git a/addons/web/static/src/views/kanban/kanban_renderer.js b/addons/web/static/src/views/kanban/kanban_renderer.js index 60fd55151ff..67dc8ccf344 100644 --- a/addons/web/static/src/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/views/kanban/kanban_renderer.js @@ -16,6 +16,7 @@ import { KanbanRecord } from "./kanban_record"; import { KanbanRecordQuickCreate } from "./kanban_record_quick_create"; import { Component, onPatched, onWillDestroy, onWillPatch, useRef, useState } from "@odoo/owl"; +import { evaluateExpr } from "@web/core/py_js/py"; const DRAGGABLE_GROUP_TYPES = ["many2one"]; const MOVABLE_RECORD_TYPES = ["char", "boolean", "integer", "selection", "many2one"]; @@ -224,12 +225,17 @@ export class KanbanRenderer extends Component { (fieldNode) => fieldNode.name === groupByField.name ); let isReadonly = this.props.list.fields[groupByField.name].readonly; - if ( - !isReadonly && - fieldNodes.length && - fieldNodes.some((fieldNode) => "readonly" in fieldNode) - ) { - isReadonly = fieldNodes.every((fieldNode) => fieldNode.readonly === "True"); + if (!isReadonly && fieldNodes.length) { + isReadonly = fieldNodes.every((fieldNode) => { + if (!fieldNode.readonly) { + return false; + } + try { + return evaluateExpr(fieldNode.readonly, this.props.list.evalContext); + } catch { + return false; + } + }); } return !isReadonly && this.isMovableField(groupByField); } diff --git a/addons/web/static/tests/views/kanban/kanban_view_tests.js b/addons/web/static/tests/views/kanban/kanban_view_tests.js index 66734632841..70e219bf5ed 100644 --- a/addons/web/static/tests/views/kanban/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban/kanban_view_tests.js @@ -14224,4 +14224,74 @@ QUnit.module("Views", (hooks) => { assert.containsOnce(target, ".o_kanban_record:not(.o_kanban_ghost)"); assert.containsNone(target, ".my_kanban_compiler"); }); + + QUnit.test( + "Kanban grouped on field with readonly expression depending on context", + async (assert) => { + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + +
+ +
+
+
+
`, + groupBy: ["product_id"], + context: { abc: true }, + }); + + assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 2); + assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 2); + + await dragAndDrop( + ".o_kanban_group:first-child .o_kanban_record", + ".o_kanban_group:nth-child(2)" + ); + + assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 2); + assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 2); + } + ); + + QUnit.test( + "Kanban grouped on field with readonly expression depending on fields", + async (assert) => { + // Fields are not available in the current context as the drag and drop must be enabled globally + // for the view, it's not a per record thing. + // So if the readonly expression contains fields, it will resolve to readonly === false and + // the drag and drop will be enabled. + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + +
+ + +
+
+
+
`, + groupBy: ["product_id"], + }); + + assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 2); + assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 2); + + await dragAndDrop( + ".o_kanban_group:first-child .o_kanban_record", + ".o_kanban_group:nth-child(2)" + ); + + assert.containsN(target, ".o_kanban_group:first-child .o_kanban_record", 1); + assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 3); + } + ); });