diff --git a/addons/web/static/src/views/kanban/kanban_model.js b/addons/web/static/src/views/kanban/kanban_model.js index 7680b9a6142..166e9b48749 100644 --- a/addons/web/static/src/views/kanban/kanban_model.js +++ b/addons/web/static/src/views/kanban/kanban_model.js @@ -439,12 +439,20 @@ export class KanbanDynamicGroupList extends DynamicGroupList { targetGroup.addRecord(sourceGroup.removeRecord(record), refIndex + 1); const value = isRelational(this.groupByField) ? [targetGroup.value] : targetGroup.value; - try { - await record.update({ [this.groupByField.name]: value }); - await record.save({ noReload: true }); - } catch (err) { + const abort = () => { this.model.transaction.abort(dataRecordId); this.model.notify(); + }; + + try { + await record.update({ [this.groupByField.name]: value }); + const saved = await record.save({ noReload: true }); + if (!saved) { + abort(); + return; + } + } catch (err) { + abort(); throw err; } diff --git a/addons/web/static/tests/views/kanban_view_tests.js b/addons/web/static/tests/views/kanban_view_tests.js index d5a67d21ac4..eef1049d9c5 100644 --- a/addons/web/static/tests/views/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban_view_tests.js @@ -10507,4 +10507,95 @@ QUnit.module("Views", (hooks) => { ); assert.verifySteps(["resequence"]); }); + + QUnit.test("dragged record cannot be saved", async (assert) => { + let def; + serverData.models.partner.records = [ + { id: 1, bar: false, state: "abc" }, + { id: 2, bar: true, state: "def", foo: "blip" }, + ]; + serverData.models.partner.onchanges = { + bar() {}, + }; + + const kanban = await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + + + +
+ + +
+
+
+
+ `, + groupBy: ["bar"], + async mockRPC(route, args) { + if (args.method === "onchange") { + assert.step("onchange"); + await Promise.resolve(def); + } + }, + }); + + patchWithCleanup(kanban.env.services.notification, { + add(message, options) { + assert.step("notification"); + assert.strictEqual(options.title, "Invalid fields: "); + assert.strictEqual(`${message}`, ""); + }, + }); + + def = makeDeferred(); + + assert.containsOnce(target, ".o_kanban_group:nth-child(1) .o_kanban_record"); + assert.strictEqual( + target.querySelector(".o_kanban_group:nth-child(1) .o_kanban_record").innerText, + "ABC" + ); + assert.containsOnce(target, ".o_kanban_group:nth-child(2) .o_kanban_record"); + assert.strictEqual( + target.querySelector(".o_kanban_group:nth-child(2) .o_kanban_record").innerText, + "blipDEF" + ); + + assert.verifySteps([]); + + // move "yop" from first to second column + await dragAndDrop( + ".o_kanban_group:nth-child(1) .o_kanban_record", + ".o_kanban_group:nth-child(2)" + ); + + assert.containsNone(target, ".o_kanban_group:nth-child(1) .o_kanban_record"); + assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 2); + assert.deepEqual( + [...target.querySelectorAll(".o_kanban_group:nth-child(2) .o_kanban_record")].map( + (el) => el.innerText + ), + ["blipDEF", "ABC"] + ); + assert.verifySteps(["onchange"]); + + def.resolve(); + await nextTick(); + + assert.containsOnce(target, ".o_kanban_group:nth-child(1) .o_kanban_record"); + assert.strictEqual( + target.querySelector(".o_kanban_group:nth-child(1) .o_kanban_record").innerText, + "ABC" + ); + assert.containsOnce(target, ".o_kanban_group:nth-child(2) .o_kanban_record"); + assert.strictEqual( + target.querySelector(".o_kanban_group:nth-child(2) .o_kanban_record").innerText, + "blipDEF" + ); + assert.verifySteps(["notification"]); + }); });