From 17adc2e09ec67ea4a2325b1cc5bfaf93e7d968c1 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Tue, 15 Nov 2016 22:22:31 +0100 Subject: [PATCH] [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. --- .../src/js/form_view_barcode_handler.js | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js index ffc6dfc7383..fd0a84166f5 100644 --- a/addons/barcodes/static/src/js/form_view_barcode_handler.js +++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js @@ -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); } },