[FIX] web/mail: many2many_tags_email widget returns erratic values (opw: 693067)

The many2many_tags_email's internal value change logic fails to process
all its values. It is not concurrency proof either.

The internal value change logic is now replaced by a mutex and deferreds
are used for the partner's form view popups in order to allow concurrent
events.
This commit is contained in:
Fabien Meghazi 2016-12-21 11:09:15 +01:00
parent 0b529cf3af
commit 874c7aa2dd
1 changed files with 35 additions and 33 deletions

View File

@ -9,51 +9,49 @@ var _t = instance.web._t;
instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags.extend({ instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags.extend({
start: function() { start: function() {
this.values = []; this.mutex = new openerp.Mutex();
this.values_checking = [];
// This widget will indirectly trigger a change:value to it's parent widget
// when setting the value of valid partners. For this reason we have to keep an
// internal state of the last value in order to compute the effective value changes.
this.last_processed_value = [];
this.on("change:value", this, this.on_change_value_check); this.on("change:value", this, this.on_change_value_check);
this.trigger("change:value");
this._super.apply(this, arguments); this._super.apply(this, arguments);
}, },
on_change_value_check : function () { on_change_value_check : function () {
this.values = _.uniq(this.values); var self = this;
var values = this.get('value').slice(0); // Clone the array
// filter for removed values // We only validate partners emails in case the value is not empty
var values_removed = _.difference(this.values, this.get('value')); // and is different from the last processed value
if (values_removed.length) { var effective_change = _.difference(values, self.last_processed_value).length;
this.values = _.difference(this.values, values_removed); if (values.length && effective_change) {
this.set({'value': this.values}); this.mutex.exec(function() {
return false; return self._check_email_popup(values);
} });
// find not checked values that are not currently on checking
var not_checked = _.difference(this.get('value'), this.values, this.values_checking);
if (not_checked.length) {
// remember values on checking for cheked only one time
this.values_checking = this.values_checking.concat(not_checked);
// check values
this._check_email_popup(not_checked);
} }
}, },
_check_email_popup: function (ids) { _check_email_popup: function (ids) {
var self = this; var self = this;
var valid_partners;
new instance.web.Model('res.partner').call("search", [[ new instance.web.Model('res.partner').call("search", [[
["id", "in", ids], ["id", "in", ids],
["email", "=", false], ["email", "=", false],
["notify_email", "=", 'always'] ]], ["notify_email", "=", 'always'] ]],
{context: this.build_context()}) {context: this.build_context()})
.then(function (record_ids) { .then(function (record_ids) {
// valid partner var popups_deferreds = [];
var valid_partner = _.difference(ids, record_ids); self.valid_partners = _.difference(ids, record_ids);
self.values = self.values.concat(valid_partner);
self.values_checking = _.difference(self.values_checking, valid_partner);
// unvalid partner // Propose the user to correct invalid partners
_.each(record_ids, function (id) { _.each(record_ids, function (id) {
var popup_def = $.Deferred();
popups_deferreds.push(popup_def);
var pop = new instance.web.form.FormOpenPopup(self); var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element( pop.show_element(
'res.partner', 'res.partner',
@ -64,15 +62,19 @@ instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags
} }
); );
pop.on('write_completed', self, function () { pop.on('write_completed', self, function () {
this.values.push(id); self.valid_partners.push(id);
this.values_checking = _.without(this.values_checking, id);
this.set({'value': this.values});
}); });
pop.on('closed', self, function () { pop.on('closed', self, function () {
this.values_checking = _.without(this.values_checking, id); popup_def.resolve();
this.set({'value': this.values});
}); });
}); });
return $.when.apply($, popups_deferreds).then(function() {
// All popups have been processed for the given ids
// It is now time to set the final value with valid partners ids.
var filtered_value = _.uniq(self.valid_partners);
self.last_processed_value = filtered_value;
self.set({'value': filtered_value});
});
}); });
}, },
}); });