[FIX] web: no_quick_create option ignored

Steps to reproduce
==================

1. Install event
2. Go to Events
3. Configuration > Event Templates
4. Click on any event
5. Click on Communication
6. Add a line, change type to SMS
7. Type anything in Template
8. Click on Create "..."
9. The operation cannot be completed

Cause of the issue
==================

For many2one and many2many editable fields, the server sets the
can_create and can_write attributes on the field nodes. [1][2]

In some cases [3], those attributes are missing even though the fields
are actually editable. They are also absent for reference fields.

The `canQuickCreate` props was set to the following value:
`canCreate && !noQuickCreate`. When canCreate was absent, this was
evaluated to undefined, thus ignoring the noQuickCreate option.

Solution
========

When the permission attributes are missing, we have to assume that the
user has the appropriate permissions.

We also have to cast the extracted props to booleans as if you have an
props with a value of undefined, it will fall back to the default value.

In the test `no_quick_create option on a many2one when can_create is absent`,
we set the field as readonly and then cancel the readonly on the view.
This reproduces one of the bug of [3] which causes the permission
attributes to be missing.

-----

[1]: https://github.com/odoo/odoo/blob/fa6da3d63a3a4cce4e8ca788a49772925d318764/odoo/addons/base/models/ir_ui_view.py#L1296-L1297
[2]: https://github.com/odoo/odoo/blob/fa6da3d63a3a4cce4e8ca788a49772925d318764/odoo/addons/base/models/ir_ui_view.py#L1080-L1086
[3]: https://github.com/odoo/odoo/pull/138032

opw-3609799

closes odoo/odoo#148163

X-original-commit: efcb2a820bb57f1fae809725e799b3145fbbe599
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Antoine Demany (ande) <ande@odoo.com>
This commit is contained in:
Hubert Van De Walle
2024-01-11 15:16:22 +00:00
parent 04943d400b
commit e660cd94a8
5 changed files with 102 additions and 6 deletions
@@ -222,10 +222,9 @@ export const many2ManyTagsField = {
return relatedFields;
},
extractProps({ attrs, options, string }, dynamicInfo) {
const hasCreatePermission = attrs.can_create ? evaluateBooleanExpr(attrs.can_create) : true;
const noCreate = Boolean(options.no_create);
const canCreate = noCreate
? false
: attrs.can_create && evaluateBooleanExpr(attrs.can_create);
const canCreate = noCreate ? false : hasCreatePermission;
const noQuickCreate = Boolean(options.no_quick_create);
const noCreateEdit = Boolean(options.no_create_edit);
return {
@@ -366,13 +366,14 @@ export const many2OneField = {
],
supportedTypes: ["many2one"],
extractProps({ attrs, context, decorations, options, string }, dynamicInfo) {
const canCreate =
options.no_create ? false : attrs.can_create && evaluateBooleanExpr(attrs.can_create);
const hasCreatePermission = attrs.can_create ? evaluateBooleanExpr(attrs.can_create) : true;
const hasWritePermission = attrs.can_write ? evaluateBooleanExpr(attrs.can_write) : true;
const canCreate = options.no_create ? false : hasCreatePermission
return {
placeholder: attrs.placeholder,
canOpen: !options.no_open,
canCreate,
canWrite: attrs.can_write && evaluateBooleanExpr(attrs.can_write),
canWrite: hasWritePermission,
canQuickCreate: canCreate && !options.no_quick_create,
canCreateEdit: canCreate && !options.no_create_edit,
context: context,
@@ -1457,4 +1457,53 @@ QUnit.module("MockServer", (hooks) => {
});
assert.deepEqual(result.value, { foo: 0 });
});
QUnit.test(
"access rights attributes are present on an editable many2one field",
async function (assert) {
data.views = {
"bar,10001,form": `<form>
<field name="partner_id"/>
</form>`,
"bar,10001,search": `<search></search>`,
};
const expectedForm = `<form>
<field name="partner_id" can_create="true" can_write="true"/>
</form>`;
const mockServer = new MockServer(data);
const { views } = mockServer.mockGetViews("bar", {
views: [[10001, "form"]],
options: {},
});
assert.deepEqual(views.form.arch, expectedForm);
}
);
QUnit.test(
"access rights attributes are missing on an editable many2one field",
async function (assert) {
// The access rights attributes should be present,
// but are actually missing when a field definition is readonly and readonly=0 is on the view.
// @see the commit description for more details.
data.models.bar.fields.partner_id.readonly = true;
data.views = {
"bar,10001,form": `<form>
<field name="partner_id" readonly="0"/>
</form>`,
"bar,10001,search": `<search></search>`,
};
const expectedForm = `<form>
<field name="partner_id" readonly="0"/>
</form>`;
const mockServer = new MockServer(data);
const { views } = mockServer.mockGetViews("bar", {
views: [[10001, "form"]],
options: {},
});
assert.deepEqual(views.form.arch, expectedForm);
}
);
});
@@ -3239,6 +3239,31 @@ QUnit.module("Fields", (hooks) => {
);
});
QUnit.test("no_quick_create option on a many2one when can_create is absent", async function (assert) {
serverData.models.partner.fields.product_id.readonly = true;
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: `
<form>
<sheet>
<field name="product_id" options="{'no_quick_create': 1}" readonly="0" />
</sheet>
</form>`,
});
await editInput(target, ".o_field_many2one input", "new partner");
assert.containsOnce(
target,
".ui-autocomplete .o_m2o_dropdown_option",
"Dropdown should be opened and have only one item"
);
assert.hasClass(
target.querySelector(".ui-autocomplete .o_m2o_dropdown_option"),
"o_m2o_dropdown_option_create_edit"
);
});
QUnit.test("can_create and can_write option on a many2one", async function (assert) {
serverData.models.product.options = {
can_create: "false",
@@ -258,6 +258,28 @@ QUnit.module("Fields", (hooks) => {
);
});
QUnit.test("ReferenceField respects no_quick_create", async function (assert) {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: `<form><field name="reference" options="{'no_quick_create': 1}" /></form>`,
});
await editSelect(target, "select", "partner");
await click(target, ".o_field_widget[name='reference'] input");
await editInput(target, ".o_field_widget[name='reference'] input", "new partner");
assert.containsOnce(
target,
".ui-autocomplete .o_m2o_dropdown_option",
"Dropdown should be opened and have only one item"
);
assert.hasClass(
target.querySelector(".ui-autocomplete .o_m2o_dropdown_option"),
"o_m2o_dropdown_option_create_edit"
);
});
QUnit.test("ReferenceField in modal readonly mode", async function (assert) {
serverData.models.partner.records[0].p = [2];
serverData.models.partner.records[1].trululu = 1;