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",