From 1d4d2a68315dcc07acf225d449b8d2e1fd3fb5a8 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 1 Dec 2020 12:06:07 +0000 Subject: [PATCH] [IMP] web: FieldMany2One: improve focusout case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit, when the user entered some text in a many2one and then focused out (e.g. clicked outside), a dialog was shown to ask him if he wanted to create such a value, no matter what he wrote in the input. After this commit, if the search triggered by the text he entered returned records, the first one is automatically selected when the focus is lost. However, if it matches no record, the dialog still opens. The text in the dialog has been slightly reworded as well. Task 2372480 Closes #61455 closes odoo/odoo#62677 Signed-off-by: Géry Debongnie (ged) --- .../static/src/js/fields/relational_fields.js | 36 +++++-------- addons/web/static/src/xml/base.xml | 2 +- .../relational_fields/field_many2one_tests.js | 51 +++++++++++++++---- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index b42cfa0741f..02cb6356658 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -54,17 +54,7 @@ var M2ODialog = Dialog.extend({ this.trigger_up('quick_create', { value: this.value }); }, }, { - text: _t('Create and edit'), - classes: 'btn-primary', - close: true, - click: function () { - this.trigger_up('search_create_popup', { - view_type: 'form', - value: this.value, - }); - }, - }, { - text: _t('Cancel'), + text: _t('Discard'), close: true, }], }); @@ -96,7 +86,6 @@ var FieldMany2One = AbstractField.extend({ 'closed_unset': '_onDialogClosedUnset', 'field_changed': '_onFieldChanged', 'quick_create': '_onQuickCreate', - 'search_create_popup': '_onSearchCreatePopup', }), events: _.extend({}, AbstractField.prototype.events, { 'click input': '_onInputClick', @@ -146,6 +135,9 @@ var FieldMany2One = AbstractField.extend({ // Add default search method for M20 (name_search) this._addAutocompleteSource(this._search, {placeholder: _t('Loading...'), order: 1}); + // list of last autocomplete suggestions + this.suggestions = []; + // use a DropPrevious to properly handle related record quick creations, // and store a createDef to be able to notify the environment that there // is pending quick create operation @@ -260,6 +252,7 @@ var FieldMany2One = AbstractField.extend({ } this.$input.autocomplete({ source: function (req, resp) { + self.suggestions = []; _.each(self._autocompleteSources, function (source) { // Resets the results for this source source.results = []; @@ -274,7 +267,8 @@ var FieldMany2One = AbstractField.extend({ Promise.resolve(source.method.call(self, search)).then(function (results) { source.results = results; source.loading = false; - resp(self._concatenateAutocompleteResults()); + self.suggestions = self._concatenateAutocompleteResults(); + resp(self.suggestions); }); } }); @@ -771,7 +765,13 @@ var FieldMany2One = AbstractField.extend({ * @private */ _onInputFocusout: function () { - if (this.can_create && this.floating) { + if (!this.floating) { + return; + } + const firstValue = this.suggestions.find(s => s.id); + if (firstValue) { + this.reinitialize(firstValue.id); + } else if (this.can_create) { new M2ODialog(this, this.string, this.$input.val()).open(); } }, @@ -827,14 +827,6 @@ var FieldMany2One = AbstractField.extend({ _onQuickCreate: function (event) { this._quickCreate(event.data.value); }, - /** - * @private - * @param {OdooEvent} event - */ - _onSearchCreatePopup: function (event) { - var data = event.data; - this._searchCreatePopup(data.view_type, false, this._createContext(data.value)); - }, }); var Many2oneBarcode = FieldMany2One.extend({ diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 7b4ae80f146..92f360a0abf 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1652,7 +1652,7 @@
- Do you want to create as a new ? + Create as a new ?
diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index 80f5dd89304..1c9dd615c9d 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -2265,7 +2265,7 @@ QUnit.module('fields', {}, function () { await testUtils.fields.editAndTrigger(form.$('.o_field_many2one input'), 'new partner', ['keyup', 'blur']); await testUtils.dom.click($('.modal .modal-footer .btn-primary').first()); - assert.strictEqual($('.modal .modal-body').text().trim(), "Do you want to create new partner as a new Product?"); + assert.strictEqual($('.modal .modal-body').text().trim(), "Create new partner as a new Product?"); form.destroy(); }); @@ -2365,19 +2365,23 @@ QUnit.module('fields', {}, function () { }, }); - // cancel the many2one creation with Cancel button - form.$('.o_field_many2one input').focus().val('new product').trigger('keyup').trigger('blur'); + // cancel the many2one creation with Discard button + form.$('.o_field_many2one input').focus().val('new product').trigger('input').trigger('keyup'); + await testUtils.nextTick(); + form.$('.o_field_many2one input').trigger('blur'); await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "there should be one opened modal"); - await testUtils.dom.click($('.modal .modal-footer .btn:contains(Cancel)')); + await testUtils.dom.click($('.modal .modal-footer .btn:contains(Discard)')); assert.strictEqual($('.modal').length, 0, "the modal should be closed"); assert.strictEqual(form.$('.o_field_many2one input').val(), "", 'the many2one should not set a value as its creation has been cancelled (with Cancel button)'); // cancel the many2one creation with Close button - await testUtils.fields.editAndTrigger(form.$('.o_field_many2one input'), - 'new product', ['keyup', 'blur']); + form.$('.o_field_many2one input').focus().val('new product').trigger('input').trigger('keyup'); + await testUtils.nextTick(); + form.$('.o_field_many2one input').trigger('blur'); + await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "there should be one opened modal"); await testUtils.dom.click($('.modal .modal-header button')); assert.strictEqual(form.$('.o_field_many2one input').val(), "", @@ -2389,21 +2393,24 @@ QUnit.module('fields', {}, function () { await testUtils.fields.many2one.clickItem('product_id','o'); assert.strictEqual(form.$('.o_field_many2one input').val(), "xphone", "should have selected xphone"); - form.$('.o_field_many2one input').focus().val('new product').trigger('keyup').trigger('blur'); + form.$('.o_field_many2one input').focus().val('new product').trigger('input').trigger('keyup'); + await testUtils.nextTick(); + form.$('.o_field_many2one input').trigger('blur'); await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "there should be one opened modal"); - await testUtils.dom.click($('.modal .modal-footer .btn:contains(Cancel)')); + await testUtils.dom.click($('.modal .modal-footer .btn:contains(Discard)')); assert.strictEqual(form.$('.o_field_many2one input').val(), "xphone", 'should have restored the many2one with its previous selected value (xphone)'); // confirm the many2one creation - form.$('.o_field_many2one input').focus().val('new partner').trigger('keyup').trigger('blur'); + form.$('.o_field_many2one input').focus().val('new product').trigger('input').trigger('keyup'); + await testUtils.nextTick(); + form.$('.o_field_many2one input').trigger('blur'); await testUtils.nextTick(); assert.strictEqual($('.modal').length, 1, "there should be one opened modal"); - await testUtils.dom.click($('.modal .modal-footer .btn-primary:contains(Create and edit)')); - await testUtils.nextTick(); + await testUtils.dom.click($('.modal .modal-footer .btn-primary:contains(Create)')); assert.strictEqual($('.modal .o_form_view').length, 1, 'a new modal should be opened and contain a form view'); @@ -2412,6 +2419,28 @@ QUnit.module('fields', {}, function () { form.destroy(); }); + QUnit.test("select a many2one value by focusing out", async function (assert) { + assert.expect(3); + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: `
`, + }); + + form.$('.o_field_many2one input').focus().val('xph').trigger('input').trigger('keyup'); + await testUtils.nextTick(); + form.$('.o_field_many2one input').trigger('blur'); + await testUtils.nextTick(); + + assert.containsNone(document.body, '.modal'); + assert.strictEqual(form.$('.o_field_many2one input').val(), 'xphone'); + assert.containsOnce(form, '.o_external_button'); + + form.destroy(); + }); + QUnit.test('no_create option on a many2one', async function (assert) { assert.expect(1);