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;