From 6371565e6933d6ede88fa85992f4acbc2cb5327b Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Tue, 28 Mar 2023 06:55:06 +0000 Subject: [PATCH] [FIX] web: many2x fail for a good reason Before this commit, when the "Create" button of a m2x is clicked and an error is returned by the server, a form view dialog is opened to create the record. We want to know if the error is caused by a missing required field but not by any other type of error. So we open the form view dialog only if the error is of type ValidationError otherwise we display an error dialog. closes odoo/odoo#116954 X-original-commit: 49bae69c602d1587fe159719667ff8dd0fb23984 Signed-off-by: Aaron Bohy (aab) --- .../src/views/fields/relational_utils.js | 13 +- .../fields/many2many_tags_field_tests.js | 4 +- .../views/fields/many2one_field_tests.js | 162 +++++++++++------- 3 files changed, 113 insertions(+), 66 deletions(-) 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": ` `, - "product,false,form": '
', - }; + "product,false,form": '
', + }; - await makeView({ - type: "form", - resModel: "partner", - serverData, - arch: '
', - mockRPC(route, { args, method }) { - if (method === "name_create") { - throw new RPCError("Something went wrong"); - } - if (method === "create") { - assert.deepEqual(args[0], { name: "xyz" }); - } - }, - }); + 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 click(target, ".o_field_x2many_list_row_add a"); - await editInput(target, ".o_field_widget[name='product_id'] input", "abcd"); - await click(target.querySelector(".o_field_widget[name='product_id'] .dropdown-item")); + await click(target, ".o_field_x2many_list_row_add a"); + await editInput(target, ".o_field_widget[name='product_id'] input", "abcd"); + await click(target.querySelector(".o_field_widget[name='product_id'] .dropdown-item")); - assert.containsOnce(target, ".modal .o_form_view"); - assert.strictEqual( - target.querySelector(".modal .o_field_widget[name='name'] input").value, - "abcd" - ); + 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 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("slow create on a many2one", async function (assert) { serverData.views = {