[FIX] barcodes: form view barcode handler: concurrency

Suppose the following usecase: you scan the first product of the
picking. A server onchange is then triggered. You scan the same product
a second time, before the first onchange has returned. You expect that
the first onchange is completely executed before running the second one.
This was not the case. Moreover, some onchanges were not be able to
work with the invalid  state of the formview at some times, and crashed.

The previous implementation was wrong because id didn't run the main
barcode processing mechanism inside a Mutex. It was also wrong because
it didn't correctly wait for the onchange to execute (a previous  barcode
onchanges or some classic ones) before setting the barcode field.
This commit is contained in:
Simon Lejeune
2017-01-09 16:36:00 +01:00
parent f27e69eced
commit 17adc2e09e
@@ -27,6 +27,8 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
this.__quantity_listener = _.bind(this._set_quantity_listener, this);
BarcodeHandlerMixin.init.apply(this, arguments);
this.process_barcode_mutex = new utils.Mutex();
return this._super.apply(this, arguments);
},
@@ -136,25 +138,35 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
else if (this.form_view.get('actual_mode') === 'view')
this._display_no_edit_mode_warning();
else {
// Call hook method possibly implemented by subclass
this.pre_onchange_hook(barcode).then(function(proceed) {
if (proceed === true) {
// Wait for hypothetical ongoing onchange to finish
self.form_view.onchanges_mutex.exec(function() {
// A real onchange is triggered when a value actually changes (which can correspond
// to a widget's blur event per example). Commit the value of fields before
// programmatically triggering an onchange to be consistent with this.
var mutex_commit_value = new utils.Mutex();
_.each(self.form_view.fields, function(field) {
mutex_commit_value.exec(_.bind(field.commit_value, field));
});
return mutex_commit_value.def.then(function(){
// Trigger the barcode onchange
self.set_value(barcode);
});
});
var process_barcode = function () {
// this function can be passed to `Mutex.exec` in order to make sure
// that every ongoing onchanges in the form view are done
var form_onchanges_mutex = function () {
return self.form_view.onchanges_mutex.def;
}
});
// before setting the barcode field with the received barcode, we commit
// every fields of the form view and we wait for their hypothetical ongoing
// onchanges to finish
var commit_mutex = new utils.Mutex();
_.each(self.form_view.fields, function (field) {
commit_mutex.exec(function () {
return field.commit_value();
});
commit_mutex.exec(form_onchanges_mutex);
});
return commit_mutex.def.then(function () {
return self.pre_onchange_hook(barcode).then(function (proceed) {
if (proceed) {
self.set_value(barcode); // set the barcode field with the received one
return form_onchanges_mutex(); // wait for its onchange to finish
}
});
});
};
this.process_barcode_mutex.exec(process_barcode);
}
},