[REF] partner_autocomplete: adapt code after jQuery update

Part of task 1896658

Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Christophe Matthieu <chm@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
Co-authored-by: David Monjoie <dmo@odoo.com>
Co-authored-by: Martin Geubelle <mge@odoo.com>
Co-authored-by: svs-odoo <svs@odoo.com>
Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
This commit is contained in:
Vincent Schippefilt
2019-03-06 20:07:17 +01:00
committed by Aaron Bohy
co-authored by Aaron Bohy Christophe Matthieu Mathieu Duckerts-Antoine David Monjoie Martin Geubelle svs-odoo
parent bfed574255
commit 5547aa2487
4 changed files with 166 additions and 229 deletions
@@ -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'],
@@ -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();
},
@@ -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 = '<i class="fa fa-magic text-muted"/> ';
label += _.str.sprintf('%s, <span class="text-muted">%s</span>', suggestion.label, suggestion.description);
self._autocomplete(search_val).then(function (suggestions) {
var choices = [];
if (suggestions && suggestions.length) {
_.each(suggestions, function (suggestion) {
var label = '<i class="fa fa-magic text-muted"/> ';
label += _.str.sprintf('%s, <span class="text-muted">%s</span>', 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([]);
}
});
},
});
@@ -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) {
'<field name="comment"/>' +
'<field name="vat"/>' +
'</form>',
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 <input/> 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) {
'<field name="comment"/>' +
'<field name="vat"/>' +
'</form>',
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 <input/> 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();
});
});
});