diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_core.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_core.js index 74d32c74acc..08d0a9a5149 100644 --- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_core.js +++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_core.js @@ -23,51 +23,51 @@ var PartnerAutocompleteMixin = { * Get list of companies via Autocomplete API * * @param {string} value - * @returns {Deferred} + * @returns {Promise} * @private */ _autocomplete: function (value) { + var self = this; value = value.trim(); - var def = $.Deferred(), - isVAT = this._isVAT(value), - odooSuggestions = [], - clearbitSuggestions = []; + var isVAT = this._isVAT(value); + var odooSuggestions = []; + var clearbitSuggestions = []; + return new Promise(function (resolve, reject) { + var odooPromise = self._getOdooSuggestions(value, isVAT).then(function (suggestions){ + odooSuggestions = suggestions; + }); - var odooPromise = this._getOdooSuggestions(value, isVAT).then(function (suggestions){ - odooSuggestions = suggestions; + // Only get Clearbit suggestions if not a VAT number + var clearbitPromise = isVAT ? false : self._getClearbitSuggestions(value).then(function (suggestions){ + clearbitSuggestions = suggestions; + }); + + var concatResults = function () { + // Add Clearbit result with Odoo result (with unique domain) + if (clearbitSuggestions && clearbitSuggestions.length) { + var websites = odooSuggestions.map(function (suggestion) { + return suggestion.website; + }); + clearbitSuggestions.forEach(function (suggestion) { + if (websites.indexOf(suggestion.domain) < 0) { + websites.push(suggestion.domain); + odooSuggestions.push(suggestion); + } + }); + } + + odooSuggestions = _.filter(odooSuggestions, function (suggestion) { + return !suggestion.ignored; + }); + _.each(odooSuggestions, function(suggestion){ + delete suggestion.ignored; + }); + return resolve(odooSuggestions); + }; + + self._whenAll([odooPromise, clearbitPromise]).then(concatResults, concatResults); }); - // Only get Clearbit suggestions if not a VAT number - var clearbitPromise = isVAT ? false : this._getClearbitSuggestions(value).then(function (suggestions){ - clearbitSuggestions = suggestions; - }); - - var concatResults = function () { - // Add Clearbit result with Odoo result (with unique domain) - if (clearbitSuggestions && clearbitSuggestions.length) { - var websites = odooSuggestions.map(function (suggestion) { - return suggestion.website; - }); - clearbitSuggestions.forEach(function (suggestion) { - if (websites.indexOf(suggestion.domain) < 0) { - websites.push(suggestion.domain); - odooSuggestions.push(suggestion); - } - }); - } - - odooSuggestions = _.filter(odooSuggestions, function (suggestion) { - return !suggestion.ignored; - }); - _.each(odooSuggestions, function(suggestion){ - delete suggestion.ignored; - }); - return def.resolve(odooSuggestions); - }; - - this._whenAll([odooPromise, clearbitPromise]).then(concatResults, concatResults); - - return def; }, /** @@ -77,7 +77,7 @@ var PartnerAutocompleteMixin = { * @param {string} company.website * @param {string} company.partner_gid * @param {string} company.vat - * @returns {Deferred} + * @returns {Promise} * @private */ _enrichCompany: function (company) { @@ -92,7 +92,7 @@ var PartnerAutocompleteMixin = { * Get the company logo as Base 64 image from url * * @param {string} url - * @returns {Deferred} + * @returns {Promise} * @private */ _getCompanyLogo: function (url) { @@ -106,7 +106,7 @@ var PartnerAutocompleteMixin = { * Get enriched data + logo before populating partner form * * @param {Object} company - * @returns {Deferred} + * @returns {Promise} */ _getCreateData: function (company) { var self = this; @@ -123,40 +123,38 @@ var PartnerAutocompleteMixin = { }); }; - var def = $.Deferred(); + return new Promise(function (resolve) { + // Fetch additional company info via Autocomplete Enrichment API + var enrichPromise = self._enrichCompany(company); - // Fetch additional company info via Autocomplete Enrichment API - var enrichPromise = this._enrichCompany(company); + // Get logo + var logoPromise = company.logo ? self._getCompanyLogo(company.logo) : false; + self._whenAll([enrichPromise, logoPromise]).then(function (result) { + var company_data = result[0]; + var logo_data = result[1]; - // Get logo - var logoPromise = company.logo ? this._getCompanyLogo(company.logo) : false; + if (company_data.error && company_data.error_message === 'Insufficient Credit') { + self._notifyNoCredits(); + company_data = company; + } - this._whenAll([enrichPromise, logoPromise]).always(function (company_data, logo_data){ - if (Array.isArray(company_data)) company_data = company_data[0]; - else company_data = {}; + if (_.isEmpty(company_data)) { + company_data = company; + } - if (Array.isArray(logo_data)) logo_data = logo_data[0]; - else logo_data = ''; + // Delete attribute to avoid "Field_changed" errors + removeUselessFields(company_data); - if (company_data.error && company_data.error_message === 'Insufficient Credit'){ - self._notifyNoCredits(); - company_data = company; - } - - if (_.isEmpty(company_data)) company_data = company; - - // Delete attribute to avoid "Field_changed" errors - removeUselessFields(company_data); - - // Assign VAT coming from parent VIES VAT query - if (company.vat) company_data.vat = company.vat; - def.resolve({ - company: company_data, - logo: logo_data + // Assign VAT coming from parent VIES VAT query + if (company.vat) { + company_data.vat = company.vat; + } + resolve({ + company: company_data, + logo: logo_data + }); }); }); - - return def; }, /** @@ -186,35 +184,35 @@ var PartnerAutocompleteMixin = { //-------------------------------------------------------------------------- /** - * Returns a deferred which will be resolved with the base64 data of the + * Returns a promise which will be resolved with the base64 data of the * image fetched from the given url. * * @private * @param {string} url : the url where to find the image to fetch - * @returns {Deferred} + * @returns {Promise} */ _getBase64Image: function (url) { - var def = $.Deferred(); - var xhr = new XMLHttpRequest(); - xhr.onload = function () { - var reader = new FileReader(); - reader.onloadend = function () { - def.resolve(reader.result); + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.onload = function () { + var reader = new FileReader(); + reader.onloadend = function () { + resolve(reader.result); + }; + reader.readAsDataURL(xhr.response); }; - reader.readAsDataURL(xhr.response); - }; - xhr.open('GET', url); - xhr.responseType = 'blob'; - xhr.onerror = def.reject.bind(def); - xhr.send(); - return def; + xhr.open('GET', url); + xhr.responseType = 'blob'; + xhr.onerror = reject; + xhr.send(); + }); }, /** * Use Clearbit Autocomplete API to return suggestions * * @param {string} value - * @returns {Deferred} + * @returns {Promise} * @private */ _getClearbitSuggestions: function (value) { @@ -242,7 +240,7 @@ var PartnerAutocompleteMixin = { * * @param {string} value * @param {boolean} isVAT - * @returns {Deferred} + * @returns {Promise} * @private */ _getOdooSuggestions: function (value, isVAT) { @@ -298,72 +296,26 @@ var PartnerAutocompleteMixin = { /** * Utility to wait for multiple promises - * $.when will reject all promises whenever a promise is rejected + * Promise.all will reject all promises whenever a promise is rejected * This utility will continue - * Source : https://gist.github.com/fearphage/4341799 * - * @param array - * @returns {*} + * @param {Promise[]} promises + * @returns {Promise} * @private */ - _whenAll: function (array) { - var slice = [].slice; - - var - resolveValues = arguments.length === 1 && $.isArray(array) - ? array - : slice.call(arguments) - , length = resolveValues.length - , remaining = length - , deferred = $.Deferred() - , i = 0 - , failed = 0 - , rejectContexts = Array(length) - , rejectValues = Array(length) - , resolveContexts = Array(length) - , value - ; - - function updateFunc(index, contexts, values) { - return function () { - !(values === resolveValues) && failed++; - deferred.notifyWith( - contexts[index] = this - , values[index] = slice.call(arguments) - ); - if (!(--remaining)) { - deferred[(!failed ? 'resolve' : 'reject') + 'With'](contexts, values); - } - }; - } - - for (; i < length; i++) { - if ((value = resolveValues[i]) && $.isFunction(value.promise)) { - value.promise() - .done(updateFunc(i, resolveContexts, resolveValues)) - .fail(updateFunc(i, rejectContexts, rejectValues)) - ; - } - else { - deferred.notifyWith(this, value); - --remaining; - } - } - - if (!remaining) { - deferred.resolveWith(resolveContexts, resolveValues); - } - - return deferred.promise(); + _whenAll: function (promises) { + return Promise.all(promises.map(function (p) { + return Promise.resolve(p); + })); }, /** - * @private - * @returns {$.Promise} + * @private + * @returns {Promise} */ _notifyNoCredits: function () { var self = this; - this._rpc({ + return this._rpc({ model: 'iap.account', method: 'get_credits_url', args: ['partner_autocomplete'], diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js index 3a6374de5fc..efac26ca098 100644 --- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js +++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js @@ -135,14 +135,15 @@ var FieldAutocomplete = FieldChar.extend(AutocompleteMixin, { self.trigger_up('field_changed', { dataPointID: self.dataPointID, changes: data.company, + onSuccess: function () { + // update the input's value directly + if (self.onlyVAT) + self.$input.val(self._formatValue(company.vat)); + else + self.$input.val(self._formatValue(company.name)); + }, }); }); - - // update the input's value directly - if (this.onlyVAT) - this.$input.val(this._formatValue(company.vat)); - else - this.$input.val(this._formatValue(company.name)); this._removeDropdown(); }, diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js index 5a3334e8094..a594373f720 100644 --- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js +++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_many2one.js @@ -35,7 +35,7 @@ var PartnerField = FieldMany2One.extend(AutocompleteMixin, { * Action : create popup form with pre-filled values from Autocomplete * * @param {Object} company - * @returns {Deferred} + * @returns {Promise} * @private */ _createPartner: function (company) { @@ -78,7 +78,7 @@ var PartnerField = FieldMany2One.extend(AutocompleteMixin, { */ _modifyAutompleteRendering: function (){ var api = this.$input.data('ui-autocomplete'); - // FIXME: bugfix to prevent traceback in mobile apps due to override + // FIXME: bugfix to prevent traceback in mobile apps due to override // of Many2one widget with native implementation. if (!api) { return; @@ -114,40 +114,38 @@ var PartnerField = FieldMany2One.extend(AutocompleteMixin, { * * @override * @param search_val {string} - * @returns {Deferred} + * @returns {Promise} * @private */ _searchSuggestions: function (search_val) { - var def = $.Deferred(); + var self = this; + return new Promise(function (resolve, reject) { + if (self._isOnline()) { - if (this._isOnline()) { - var self = this; + self._autocomplete(search_val).then(function (suggestions) { + var choices = []; + if (suggestions && suggestions.length) { + _.each(suggestions, function (suggestion) { + var label = ' '; + label += _.str.sprintf('%s, %s', suggestion.label, suggestion.description); - self._autocomplete(search_val).then(function (suggestions) { - var choices = []; - if (suggestions && suggestions.length) { - _.each(suggestions, function (suggestion) { - var label = ' '; - label += _.str.sprintf('%s, %s', suggestion.label, suggestion.description); - - choices.push({ - label: label, - action: function () { - self._createPartner(suggestion); - }, - logo: suggestion.logo, - classname: 'o_partner_autocomplete_dropdown_item', + choices.push({ + label: label, + action: function () { + self._createPartner(suggestion); + }, + logo: suggestion.logo, + classname: 'o_partner_autocomplete_dropdown_item', + }); }); - }); - } + } - def.resolve(choices); - }); - } else { - def.resolve([]); - } - - return def; + resolve(choices); + }); + } else { + resolve([]); + } + }); }, }); diff --git a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js index bae95b395a5..0f5a7fb7d1c 100644 --- a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js +++ b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js @@ -8,7 +8,7 @@ odoo.define('partner_autocomplete.tests', function (require) { var PartnerField = require('partner.autocomplete.many2one'); var NotificationService = require('web.NotificationService'); - var createView = testUtils.createAsyncView; + var createView = testUtils.createView; function _compareResultFields(assert, form, fields, createData) { var type, formatted, $fieldInput; @@ -54,7 +54,7 @@ odoo.define('partner_autocomplete.tests', function (require) { _.each(fieldsToPatch, function (fieldToPatch) { testUtils.mock.patch(fieldToPatch, { _getBase64Image: function (url) { - return $.when(url === "odoo.com/logo.png" ? "odoobase64" : ""); + return Promise.resolve(url === "odoo.com/logo.png" ? "odoobase64" : ""); }, _isOnline: function () { return true; @@ -67,7 +67,7 @@ odoo.define('partner_autocomplete.tests', function (require) { return def; }, _enrichCompany: function (company) { - return $.when(enrichData); + return Promise.resolve(enrichData); }, _getOdooSuggestions: function (value, isVAT) { var results = _.filter(suggestions, function (suggestion) { @@ -75,7 +75,7 @@ odoo.define('partner_autocomplete.tests', function (require) { if (isVAT) return (suggestion.vat.toLowerCase().indexOf(value) >= 0); else return (suggestion.name.toLowerCase().indexOf(value) >= 0); }); - return $.when(results); + return Promise.resolve(results); }, _getClearbitSuggestions: function (value) { return this._getOdooSuggestions(value); @@ -185,11 +185,10 @@ odoo.define('partner_autocomplete.tests', function (require) { }); - QUnit.test("Partner autocomplete : Company type = Company / Name search", function (assert) { - assert.expect(21); - var done = assert.async(); + QUnit.test("Partner autocomplete : Company type = Company / Name search", async function (assert) { + assert.expect(18); var fields = this.data['res.partner'].fields; - createView({ + var form = await createView({ View: FormView, model: 'res.partner', data: this.data, @@ -209,34 +208,31 @@ odoo.define('partner_autocomplete.tests', function (require) { '' + '' + '', - mockRPC: function (route, args) { - if (args.method) { - assert.step(args.method); - } + mockRPC: function (route) { if (route === "/web/static/src/img/placeholder.png" || route === "odoo.com/logo.png" || route === "data:image/png;base64,odoobase64") { // land here as it is not valid base64 content - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, - }).then(function (form){ + }); // Set company type to Company var $company_type = form.$("select[name='company_type']"); - testUtils.fields.editSelect($company_type, '"company"'); + await testUtils.fields.editSelect($company_type, '"company"'); // Check input exists var $input = form.$(".o_field_partner_autocomplete > input:visible"); assert.strictEqual($input.length, 1, "there should be an for the field"); // Change input val and assert changes - testUtils.fields.editInput($input, "odoo") - + await testUtils.fields.editInput($input, "odoo"); + await testUtils.nextTick(); var $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 1, "there should be an opened dropdown"); - assert.strictEqual($dropdown.children().length, 1, "there should be only one proposition"); + assert.strictEqual($dropdown.children().length, 1, "there should be only ne proposition"); - testUtils.dom.click($dropdown.find("a").first()); + await testUtils.dom.click($dropdown.find("a").first()); $input = form.$(".o_field_partner_autocomplete > input"); assert.strictEqual($input.val(), "Odoo", "Input value should have been updated to \"Odoo\""); assert.strictEqual(form.$("input.o_field_widget").val(), "odoo.com", "website value should have been updated to \"odoo.com\""); @@ -244,31 +240,29 @@ odoo.define('partner_autocomplete.tests', function (require) { _compareResultFields(assert, form, fields, createData); // Try suggestion with bullshit query - testUtils.fields.editInput($input, "ZZZZZZZZZZZZZZZZZZZZZZ") + await testUtils.fields.editInput($input, "ZZZZZZZZZZZZZZZZZZZZZZ"); $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 0, "there should be no opened dropdown when no result"); // Try autocomplete again - testUtils.fields.editInput($input, "odoo") + await testUtils.fields.editInput($input, "odoo"); + await testUtils.nextTick(); $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 1, "there should be an opened dropdown when typing odoo letters again"); // Test if dropdown closes on focusout $input.trigger("focusout"); + await testUtils.nextTick(); $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 0, "unfocusing the input should close the dropdown"); form.destroy(); - - done(); - }); }); - QUnit.test("Partner autocomplete : Company type = Company / VAT search", function (assert) { - assert.expect(32); - var done = assert.async(); + QUnit.test("Partner autocomplete : Company type = Company / VAT search", async function (assert) { + assert.expect(29); var fields = this.data['res.partner'].fields; - createView({ + var form = await createView({ View: FormView, model: 'res.partner', data: this.data, @@ -288,21 +282,18 @@ odoo.define('partner_autocomplete.tests', function (require) { '' + '' + '', - mockRPC: function (route, args) { - if (args.method) { - assert.step(args.method); - } + mockRPC: function (route) { if (route === "/web/static/src/img/placeholder.png" || route === "odoo.com/logo.png" || route === "data:image/png;base64,odoobase64") { // land here as it is not valid base64 content - return $.when(); + return Promise.resolve(); } return this._super.apply(this, arguments); }, - }).then(function (form){ + }); // Set company type to Company var $company_type = form.$("select[name='company_type']"); - testUtils.fields.editSelect($company_type, '"company"'); + await testUtils.fields.editSelect($company_type, '"company"'); // Check input exists @@ -310,33 +301,33 @@ odoo.define('partner_autocomplete.tests', function (require) { assert.strictEqual($input.length, 1, "there should be an for the field"); // Set incomplete VAT and assert changes - testUtils.fields.editInput($input, "BE047747270") + await testUtils.fields.editInput($input, "BE047747270") var $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 0, "there should be no opened dropdown no results with incomplete VAT number"); // Set complete VAT and assert changes // First suggestion (only vat result) - testUtils.fields.editInput($input, "BE0477472701") + await testUtils.fields.editInput($input, "BE0477472701") $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 1, "there should be an opened dropdown"); assert.strictEqual($dropdown.children().length, 1, "there should be one proposition for complete VAT number"); - testUtils.dom.click($dropdown.find("a").first()); + await testUtils.dom.click($dropdown.find("a").first()); $input = form.$(".o_field_partner_autocomplete > input"); assert.strictEqual($input.val(), "Odoo", "Input value should have been updated to \"Odoo\""); _compareResultFields(assert, form, fields, createData); - + await testUtils.nextTick(); // Set complete VAT and assert changes // Second suggestion (only vat + clearbit result) - testUtils.fields.editInput($input, "BE0477472701") + await testUtils.fields.editInput($input, "BE0477472701") $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); assert.strictEqual($dropdown.length, 1, "there should be an opened dropdown"); assert.strictEqual($dropdown.children().length, 1, "there should be one proposition for complete VAT number"); - testUtils.dom.click($dropdown.find("a").first()); + await testUtils.dom.click($dropdown.find("a").first()); $input = form.$(".o_field_partner_autocomplete > input"); assert.strictEqual($input.val(), "Odoo", "Input value should have been updated to \"Odoo\""); @@ -350,8 +341,6 @@ odoo.define('partner_autocomplete.tests', function (require) { form.destroy(); - done(); - }); }); QUnit.test("Partner autocomplete : render Many2one", function (assert) { @@ -390,16 +379,15 @@ odoo.define('partner_autocomplete.tests', function (require) { }); }); - QUnit.test("Partner autocomplete : Notify not enough credits", function (assert) { + QUnit.test("Partner autocomplete : Notify not enough credits", async function (assert) { assert.expect(1); - var done = assert.async(); enrichData = { error: true, error_message: 'Insufficient Credit', }; - createView({ + var form = await createView({ View: FormView, model: 'res.partner', data: this.data, @@ -413,26 +401,24 @@ odoo.define('partner_autocomplete.tests', function (require) { }, mockRPC: function (route, args) { if (args.method === "get_credits_url"){ - return $.when('credits_url'); + return Promise.resolve('credits_url'); } return this._super.apply(this, arguments); }, - }).then(function (form){ + }); // Set company type to Company var $company_type = form.$("select[name='company_type']"); - testUtils.fields.editSelect($company_type, '"company"'); + await testUtils.fields.editSelect($company_type, '"company"'); var $input = form.$(".o_field_partner_autocomplete > input:visible"); - testUtils.fields.editInput($input, "BE0477472701"); + await testUtils.fields.editInput($input, "BE0477472701"); var $dropdown = form.$(".o_field_partner_autocomplete .dropdown-menu:visible"); - testUtils.dom.click($dropdown.find("a").first()); + await testUtils.dom.click($dropdown.find("a").first()); var $notify = $(".o_partner_autocomplete_test_notify"); assert.isVisible($notify, "there should be an 'Insufficient Credit' notification"); form.destroy(); - done(); - }); }); });