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);
+ }
+ );
});