diff --git a/addons/web/static/src/views/fields/relational_utils.js b/addons/web/static/src/views/fields/relational_utils.js index 0c74bff3b4b..fc66daa7daa 100644 --- a/addons/web/static/src/views/fields/relational_utils.js +++ b/addons/web/static/src/views/fields/relational_utils.js @@ -255,12 +255,21 @@ export class Many2XAutocomplete extends Component { try { await this.props.quickCreate(request, params); } catch (e) { - if (e && e.name === "RPC_ERROR") { + if ( + e && + e.name === "RPC_ERROR" && + e.exceptionName === "odoo.exceptions.ValidationError" + ) { const context = this.getCreationContext(request); return this.openMany2X({ context }); } // Compatibility with legacy code - if (e && e.message && e.message.name === "RPC_ERROR") { + if ( + e && + e.message && + e.message.name === "RPC_ERROR" && + e.message.exceptionName === "odoo.exceptions.ValidationError" + ) { // The event.preventDefault() is necessary because we still use the legacy e.event.preventDefault(); const context = this.getCreationContext(request); diff --git a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js index a5cd81985c4..55c29168685 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_field_tests.js @@ -1480,7 +1480,9 @@ QUnit.module("Fields", (hooks) => { arch: '
', mockRPC(route, args) { if (args.method === "name_create") { - throw new RPCError("Something went wrong"); + const error = new RPCError("Something went wrong"); + error.exceptionName = "odoo.exceptions.ValidationError"; + throw error; } if (args.method === "create") { assert.deepEqual(args.args[0], { 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 2e843444412..4563e0014af 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -2967,19 +2967,64 @@ QUnit.module("Fields", (hooks) => { await click(target, ".modal .modal-footer .btn-primary"); }); + QUnit.test( + "failing quick create on a many2one because ValidationError", + async function (assert) { + assert.expect(5); + + registry.category("services").add("error", errorService); + + // remove the override in qunit.js that swallows unhandledrejection errors + // s.t. we let the error service handle them + const originalOnUnhandledRejection = window.onunhandledrejection; + window.onunhandledrejection = () => {}; + registerCleanup(() => { + window.onunhandledrejection = originalOnUnhandledRejection; + }); + + serverData.views = { + "product,false,form": '', + }; + + await makeView({ + type: "form", + resModel: "partner", + serverData, + arch: '', + mockRPC(route, { args, method }) { + if (method === "name_create") { + const error = new RPCError("Something went wrong"); + error.exceptionName = "odoo.exceptions.ValidationError"; + throw error; + } + if (method === "create") { + assert.deepEqual(args[0], { name: "xyz" }); + } + }, + }); + + await editInput(target, ".o_field_widget[name='product_id'] input", "abcd"); + await click(target.querySelector(".o_field_widget[name='product_id'] .dropdown-item")); + await nextTick(); // wait for the error service to ensure that there's no error dialog + assert.containsNone(target, ".o_error_dialog"); + assert.containsOnce(target, ".modal .o_form_view"); + assert.strictEqual( + target.querySelector(".modal .o_field_widget[name='name'] input").value, + "abcd" + ); + + await editInput(target, ".modal .o_field_widget[name='name'] input", "xyz"); + await click(target, ".modal .o_form_button_save"); + assert.strictEqual( + target.querySelector(".o_field_widget[name='product_id'] input").value, + "xyz" + ); + } + ); + QUnit.test("failing quick create on a many2one", async function (assert) { - assert.expect(5); - registry.category("services").add("error", errorService); - // remove the override in qunit.js that swallows unhandledrejection errors - // s.t. we let the error service handle them - const originalOnUnhandledRejection = window.onunhandledrejection; - window.onunhandledrejection = () => {}; - registerCleanup(() => { - window.onunhandledrejection = originalOnUnhandledRejection; - }); - serverData.views = { "product,false,form": '', }; @@ -2991,75 +3036,66 @@ QUnit.module("Fields", (hooks) => { arch: '', mockRPC(route, { args, method }) { if (method === "name_create") { - throw new RPCError("Something went wrong"); - } - if (method === "create") { - assert.deepEqual(args[0], { name: "xyz" }); + return new RPCError("Something went wrong"); } }, }); await editInput(target, ".o_field_widget[name='product_id'] input", "abcd"); await click(target.querySelector(".o_field_widget[name='product_id'] .dropdown-item")); - await nextTick(); // wait for the error service to ensure that there's no error dialog - assert.containsNone(target, ".o_error_dialog"); - assert.containsOnce(target, ".modal .o_form_view"); - assert.strictEqual( - target.querySelector(".modal .o_field_widget[name='name'] input").value, - "abcd" - ); - - await editInput(target, ".modal .o_field_widget[name='name'] input", "xyz"); - await click(target, ".modal .o_form_button_save"); - assert.strictEqual( - target.querySelector(".o_field_widget[name='product_id'] input").value, - "xyz" - ); + await nextTick(); // wait for the error service + assert.containsOnce(target, ".o_error_dialog"); + assert.containsNone(target, ".modal .o_form_view"); }); - QUnit.test("failing quick create on a many2one inside a one2many", async function (assert) { - assert.expect(4); + QUnit.test( + "failing quick create on a many2one inside a one2many because ValidationError", + async function (assert) { + assert.expect(4); - serverData.views = { - "partner,false,list": ` + serverData.views = { + "partner,false,list": `