From e660cd94a84ff23507efe4b8d06283a97660432e Mon Sep 17 00:00:00 2001 From: Hubert Van De Walle Date: Thu, 7 Dec 2023 14:27:58 +0000 Subject: [PATCH] [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) Signed-off-by: Antoine Demany (ande) --- .../many2many_tags/many2many_tags_field.js | 5 +- .../views/fields/many2one/many2one_field.js | 7 +-- addons/web/static/tests/mock_server_tests.js | 49 +++++++++++++++++++ .../views/fields/many2one_field_tests.js | 25 ++++++++++ .../views/fields/reference_field_tests.js | 22 +++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js index 9d3a2a798c7..601d6843322 100644 --- a/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js +++ b/addons/web/static/src/views/fields/many2many_tags/many2many_tags_field.js @@ -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 { diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.js b/addons/web/static/src/views/fields/many2one/many2one_field.js index 5dfd143c011..97f4609d252 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.js +++ b/addons/web/static/src/views/fields/many2one/many2one_field.js @@ -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, diff --git a/addons/web/static/tests/mock_server_tests.js b/addons/web/static/tests/mock_server_tests.js index e7744a2d142..87dfcd28ab4 100644 --- a/addons/web/static/tests/mock_server_tests.js +++ b/addons/web/static/tests/mock_server_tests.js @@ -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": `
+ + `, + "bar,10001,search": ``, + }; + + const expectedForm = `
+ + `; + 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": `
+ + `, + "bar,10001,search": ``, + }; + + const expectedForm = `
+ + `; + const mockServer = new MockServer(data); + const { views } = mockServer.mockGetViews("bar", { + views: [[10001, "form"]], + options: {}, + }); + assert.deepEqual(views.form.arch, expectedForm); + } + ); }); diff --git a/addons/web/static/tests/views/fields/many2one_field_tests.js b/addons/web/static/tests/views/fields/many2one_field_tests.js index e9ba4b51088..e6a69015de9 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -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: ` +
+ + + +
`, + }); + 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", diff --git a/addons/web/static/tests/views/fields/reference_field_tests.js b/addons/web/static/tests/views/fields/reference_field_tests.js index 4a14d3d757b..a1ee0c7b9cc 100644 --- a/addons/web/static/tests/views/fields/reference_field_tests.js +++ b/addons/web/static/tests/views/fields/reference_field_tests.js @@ -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: `
`, + }); + + 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;