diff --git a/addons/project/static/src/views/project_task_kanban/project_task_kanban_record.js b/addons/project/static/src/views/project_task_kanban/project_task_kanban_record.js index 71c88065603..d1c13c41532 100644 --- a/addons/project/static/src/views/project_task_kanban/project_task_kanban_record.js +++ b/addons/project/static/src/views/project_task_kanban/project_task_kanban_record.js @@ -11,4 +11,14 @@ export class ProjectTaskRecord extends Record { } await super._applyChanges(changes); } + + get context() { + const context = super.context; + const value = context.default_personal_stage_type_ids; + if (value && Array.isArray(value)) { + context.default_personal_stage_type_id = value[0]; + delete context.default_personal_stage_type_ids; + } + return context; + } } diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index 0d3918a18e2..cc300ae1440 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -29,7 +29,7 @@ import { markRaw, markup, toRaw } from "@odoo/owl"; const preloadedDataRegistry = registry.category("preloadedData"); const { CREATE, UPDATE, DELETE, FORGET, LINK_TO, DELETE_ALL, REPLACE_WITH } = x2ManyCommands; -const QUICK_CREATE_FIELD_TYPES = ["char", "boolean", "many2one", "selection"]; +const QUICK_CREATE_FIELD_TYPES = ["char", "boolean", "many2one", "selection", "many2many"]; const AGGREGATABLE_FIELD_TYPES = ["float", "integer", "monetary"]; // types that can be aggregated in grouped views const DEFAULT_HANDLE_FIELD = "sequence"; const DEFAULT_QUICK_CREATE_FIELDS = { @@ -1245,7 +1245,11 @@ export class Record extends DataPoint { return; } - const { domain, value: changes, warning } = await this.model.orm.call( + const { + domain, + value: changes, + warning, + } = await this.model.orm.call( this.resModel, "onchange", [ @@ -2658,12 +2662,16 @@ export class Group extends DataPoint { getServerValue() { const { name, selection, type, granularity } = this.groupByField; + switch (type) { case "many2one": case "char": case "boolean": { return this.value || false; } + case "many2many": { + return this.value ? [this.value] : false; + } case "selection": { const descriptor = selection.find((opt) => opt[0] === this.value); return descriptor && descriptor[0]; diff --git a/addons/web/static/tests/views/kanban_view_tests.js b/addons/web/static/tests/views/kanban_view_tests.js index 9e7538b91bb..c9735637d1d 100644 --- a/addons/web/static/tests/views/kanban_view_tests.js +++ b/addons/web/static/tests/views/kanban_view_tests.js @@ -1866,6 +1866,258 @@ QUnit.module("Views", (hooks) => { ]); }); + QUnit.test("quick create record in grouped on m2m (no quick_create_view)", async (assert) => { + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + + + +
+
+
+
`, + groupBy: ["category_ids"], + async mockRPC(route, { method, args, kwargs }) { + assert.step(method || route); + if (method === "name_create") { + assert.strictEqual(args[0], "new partner"); + const { default_category_ids } = kwargs.context; + assert.deepEqual(default_category_ids, [6]); + } + }, + }); + + assert.containsOnce( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + "gold column should contain one records" + ); + + // click on 'Create', fill the quick create and validate + await quickCreateRecord(1); + await editQuickCreateInput("display_name", "new partner"); + await validateRecord(); + + assert.containsN( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + 2, + "gold column should contain two records" + ); + + assert.verifySteps([ + "get_views", + "web_read_group", // initial read_group + "web_search_read", // initial search_read (first column) + "web_search_read", // initial search_read (second column) + "read", // read display_name of categories + "onchange", // quick create + "name_create", // should perform a name_create to create the record + "read", + "read", // read the created record + "onchange", // reopen the quick create automatically + ]); + }); + + QUnit.test("quick create record in grouped on m2m in the None column", async (assert) => { + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + + + +
+
+
+
`, + groupBy: ["category_ids"], + async mockRPC(route, { method, args, kwargs }) { + assert.step(method || route); + if (method === "name_create") { + assert.strictEqual(args[0], "new partner"); + const { default_category_ids } = kwargs.context; + assert.deepEqual(default_category_ids, false); + } + }, + }); + + await click(target, ".o_kanban_group:nth-child(1)"); + assert.containsN( + target, + ".o_kanban_group:nth-child(1) .o_kanban_record", + 2, + "'None' column should contain two records" + ); + + // click on 'Create', fill the quick create and validate + await quickCreateRecord(0); + await editQuickCreateInput("display_name", "new partner"); + await validateRecord(); + + assert.containsN( + target, + ".o_kanban_group:nth-child(1) .o_kanban_record", + 3, + "'None' column should contain three records" + ); + + assert.verifySteps([ + "get_views", + "web_read_group", // initial read_group + "web_search_read", // initial search_read (first column) + "web_search_read", // initial search_read (second column) + "read", // read display_name of categories + "web_search_read", // read records when unfolding 'None' + "onchange", // quick create + "name_create", // should perform a name_create to create the record + "read", // read the created record + "onchange", // reopen the quick create automatically + ]); + }); + + QUnit.test("quick create record in grouped on m2m (field not in template)", async (assert) => { + serverData.views["partner,some_view_ref,form"] = ` +
+ + `; + + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + + +
+
+
+
`, + groupBy: ["category_ids"], + async mockRPC(route, { method, args, kwargs }) { + assert.step(method || route); + if (method === "create") { + assert.deepEqual(args[0], { + foo: "new partner", + }); + const { default_category_ids } = kwargs.context; + assert.deepEqual(default_category_ids, [6]); + } + }, + }); + + assert.containsOnce( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + "gold column should contain one records" + ); + + // click on 'Create', fill the quick create and validate + await quickCreateRecord(1); + await editQuickCreateInput("foo", "new partner"); + await validateRecord(); + + assert.containsN( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + 2, + "gold column should contain two records" + ); + + assert.verifySteps([ + "get_views", + "web_read_group", // initial read_group + "web_search_read", // initial search_read (first column) + "web_search_read", // initial search_read (second column) + "get_views", // get form view + "onchange", // quick create + "create", // should perform a name_create to create the record + "read", // read the created record + "onchange", // reopen the quick create automatically + ]); + }); + + QUnit.test("quick create record in grouped on m2m (field in the form view)", async (assert) => { + serverData.views["partner,some_view_ref,form"] = ` +
+ + + `; + + await makeView({ + type: "kanban", + resModel: "partner", + serverData, + arch: ` + + + +
+
+
+
`, + groupBy: ["category_ids"], + async mockRPC(route, { method, args, kwargs }) { + assert.step(method || route); + if (method === "create") { + assert.deepEqual(args[0], { + category_ids: [[6, false, [6]]], + foo: "new partner", + }); + const { default_category_ids } = kwargs.context; + assert.deepEqual(default_category_ids, [6]); + } + }, + }); + + assert.containsOnce( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + "gold column should contain one records" + ); + // click on 'Create', fill the quick create and validate + await quickCreateRecord(1); + + // verify that the quick create m2m field contains the column value + assert.containsOnce( + target, + ".o_tag_badge_text", + "quick create form should contain one tag in the m2m field" + ); + assert.strictEqual(target.querySelector(".o_tag_badge_text").textContent, "gold"); + + await editQuickCreateInput("foo", "new partner"); + await validateRecord(); + + assert.containsN( + target, + ".o_kanban_group:nth-child(2) .o_kanban_record", + 2, + "gold column should contain two records" + ); + + assert.verifySteps([ + "get_views", + "web_read_group", // initial read_group + "web_search_read", // initial search_read (first column) + "web_search_read", // initial search_read (second column) + "get_views", // get form view + "onchange", // quick create + "read", + "create", // should perform a create to create the record + "read", + "onchange", + "read", + ]); + }); + QUnit.test("quick create record validation: stays open when invalid", async (assert) => { await makeView({ type: "kanban",