[IMP] web,project: kanban: quick create when grouped by many2many
This commit adds the ability to quick create records from the kanban view when it is grouped by a many2many field. This feature improve the user experience for multiple views, here are some examples of new possible workflows: - Project -> My tasks (this view is grouped by m2m) -> quick create - Project -> Select a project -> group by Assignees -> quick create - Multiples module -> group by tags -> quick create The "My Tasks" view is grouped by the m2m `personal_stage_type_ids` field which is a complicated computed field on which we cannot write. A custom behaviour in their `ProjectTaskRecord` was necessary in order to support this feature. Previously, they also implemented a custom behaviour for this field to be able to move records between groups. closes odoo/odoo#108150 Task-id: 2960497 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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: `
|
||||
<kanban on_create="quick_create">
|
||||
<field name="category_ids"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div><field name="foo"/></div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
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: `
|
||||
<kanban on_create="quick_create">
|
||||
<field name="category_ids"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div><field name="foo"/></div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
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"] = `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
</form>`;
|
||||
|
||||
await makeView({
|
||||
type: "kanban",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<kanban on_create="quick_create" quick_create_view="some_view_ref">
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div><field name="foo"/></div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
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"] = `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
<field name="category_ids" widget="many2many_tags"/>
|
||||
</form>`;
|
||||
|
||||
await makeView({
|
||||
type: "kanban",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<kanban on_create="quick_create" quick_create_view="some_view_ref">
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div><field name="foo"/></div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>`,
|
||||
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",
|
||||
|
||||
Reference in New Issue
Block a user