diff --git a/addons/barcodes/__init__.py b/addons/barcodes/__init__.py
index 5bea30ad78f..42a4cc552ac 100644
--- a/addons/barcodes/__init__.py
+++ b/addons/barcodes/__init__.py
@@ -1 +1,5 @@
+# -*- coding: utf-8 -*-
+
import barcodes
+
+import models
diff --git a/addons/barcodes/models/__init__.py b/addons/barcodes/models/__init__.py
new file mode 100644
index 00000000000..5c31ffd4a98
--- /dev/null
+++ b/addons/barcodes/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+import barcode_events_mixin
diff --git a/addons/barcodes/models/barcode_events_mixin.py b/addons/barcodes/models/barcode_events_mixin.py
new file mode 100644
index 00000000000..383e622c0a1
--- /dev/null
+++ b/addons/barcodes/models/barcode_events_mixin.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+from openerp import models, fields, api
+
+class BarcodeEventsMixin(models.AbstractModel):
+ """ Mixin class for objects reacting when a barcode is scanned in their form views
+ which contains ``.
+ Models using this mixin must implement the method on_barcode_scanned. It works
+ like an onchange and receives the scanned barcode in parameter.
+ """
+
+ _name = 'barcodes.barcode_events_mixin'
+
+ _barcode_scanned = fields.Char("Barcode Scanned", help="Value of the last barcode scanned.", store=False)
+
+ @api.onchange('_barcode_scanned')
+ def _on_barcode_scanned(self):
+ barcode = self._barcode_scanned
+ if barcode:
+ self._barcode_scanned = ""
+ return self.on_barcode_scanned(barcode)
+
+ def on_barcode_scanned(self, barcode):
+ raise NotImplementedError("In order to use barcodes.barcode_events_mixin, method on_barcode_scanned must be implemented")
diff --git a/addons/barcodes/static/src/js/barcode_events.js b/addons/barcodes/static/src/js/barcode_events.js
index 11df512ccea..7d0789f76b6 100644
--- a/addons/barcodes/static/src/js/barcode_events.js
+++ b/addons/barcodes/static/src/js/barcode_events.js
@@ -21,12 +21,12 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
init: function() {
mixins.PropertiesMixin.init.call(this);
// Keep a reference of the handler functions to use when adding and removing event listeners
- this.__keydown_handler = this.keydown_handler.bind(this);
- this.__keyup_handler = this.keyup_handler.bind(this);
- this.__handler = this.handler.bind(this);
+ this.__keydown_handler = _.bind(this.keydown_handler, this);
+ this.__keyup_handler = _.bind(this.keyup_handler, this);
+ this.__handler = _.bind(this.handler, this);
// Bind event handler once the DOM is loaded
// TODO: find a way to be active only when there are listeners on the bus
- $(this.start.bind(this, false));
+ $(_.bind(this.start, this, false));
},
handle_buffered_keys: function() {
@@ -145,7 +145,7 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
if (String.fromCharCode(e.which).match(this.suffix)) {
this.handle_buffered_keys();
} else {
- this.timeout = setTimeout(this.handle_buffered_keys.bind(this), this.max_time_between_keys_in_ms);
+ this.timeout = setTimeout(_.bind(this.handle_buffered_keys, this), this.max_time_between_keys_in_ms);
}
},
diff --git a/addons/barcodes/static/src/js/barcode_handler_mixin.js b/addons/barcodes/static/src/js/barcode_handler_mixin.js
new file mode 100644
index 00000000000..5b01516788f
--- /dev/null
+++ b/addons/barcodes/static/src/js/barcode_handler_mixin.js
@@ -0,0 +1,47 @@
+odoo.define('barcodes.BarcodeHandlerMixin', function(require) {
+"use strict";
+
+var core = require('web.core');
+var View = require('web.View');
+
+// Mixin implementing the common basis for barcode handlers.
+// The object on which this mixin is applied must also include ParentedMixin. Example :
+// Widget.extend(BarcodeHandlerMixin, { ... });
+// Class.extend(PropertiesMixin, BarcodeHandlerMixin, { ... });
+
+return {
+ init: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ this.__on_barcode_scanned = function () {
+ self.on_barcode_scanned.apply(self, arguments);
+ };
+ this.start_listening();
+ // Handlers inside a View managed by a ViewManager only listen to barcode events while their view is displayed
+ var view = this.findAncestor(function(ancestor) { return ancestor instanceof View });
+ if (view) {
+ view.on('attached', this, this.start_listening);
+ view.on('detached', this, this.stop_listening);
+ }
+ },
+
+ start_listening: function() {
+ if (! this.is_listening) {
+ core.bus.on('barcode_scanned', this, this.__on_barcode_scanned);
+ this.is_listening = true;
+ }
+ },
+
+ stop_listening: function() {
+ if (this.is_listening) {
+ core.bus.off('barcode_scanned', this, this.__on_barcode_scanned);
+ this.is_listening = false;
+ }
+ },
+
+ on_barcode_scanned: function(barcode) {
+ console.error('A class implementing BarcodeHandlerMixin must redefine method on_barcode_scanned.');
+ },
+};
+
+});
diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js
new file mode 100644
index 00000000000..ab2f6514bf0
--- /dev/null
+++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js
@@ -0,0 +1,78 @@
+odoo.define('barcodes.FormViewBarcodeHandler', function(require) {
+"use strict";
+
+var core = require('web.core');
+var utils = require('web.utils');
+var common = require('web.form_common');
+var BarcodeHandlerMixin = require('barcodes.BarcodeHandlerMixin');
+
+var _t = core._t;
+
+var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
+ start: function() {
+ this._super();
+ this.form_view = this.field_manager;
+ // Hardcoded barcode actions
+ this.map_barcode_method = {
+ 'O-CMD.NEW': _.bind(this.form_view.on_button_new, this.form_view),
+ 'O-CMD.CANCEL': _.bind(this.form_view.on_button_cancel, this.form_view),
+ // FIXME: on_button_save shouldn't mix view and model concerns (it expects to be used as onclick handler)
+ 'O-CMD.SAVE': _.bind(this.form_view.on_button_save, this.form_view, {target: $('.o_cp_buttons .o_form_button_save')}),
+ };
+ // Old design pager actions
+ if (this.form_view.execute_pager_action) {
+ this.map_barcode_method['O-CMD.PAGER-PREV'] = _.bind(this.form_view.execute_pager_action, this.form_view, 'previous');
+ this.map_barcode_method['O-CMD.PAGER-NEXT'] = _.bind(this.form_view.execute_pager_action, this.form_view, 'next');
+ // New design pager actions
+ } else if (this.form_view.pager) {
+ this.map_barcode_method['O-CMD.PAGER-PREV'] = _.bind(this.form_view.pager.previous, this.form_view.pager);
+ this.map_barcode_method['O-CMD.PAGER-NEXT'] = _.bind(this.form_view.pager.next, this.form_view.pager);
+ }
+ },
+
+ // Let subclasses add custom behaviour before onchange
+ // Return false in order to proceed with the onchange, anything else to prevent it.
+ pre_onchange_hook: function(barcode) {
+ return false;
+ },
+
+ on_barcode_scanned: function(barcode) {
+ var self = this;
+ // Execute a harcoded action
+ var action = this.map_barcode_method[barcode];
+ if (typeof action === "function")
+ return $.when(action());
+ // Warn the user if form view is not editable
+ else if (this.form_view.get('actual_mode') === 'view')
+ this.do_warn(_t('Error : Document not editable'), _t('To modify this document, please first start edition.'));
+ else {
+ // Call hook method possibly implemented by subclass
+ $.when(this.pre_onchange_hook(barcode)).then(function(result) {
+ if (result !== false) {
+ return result;
+ } else {
+ // 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);
+ });
+ });
+ }
+ });
+ }
+ },
+});
+
+core.form_widget_registry.add('barcode_handler', FormViewBarcodeHandler);
+
+return FormViewBarcodeHandler;
+
+});
diff --git a/addons/barcodes/static/src/js/inherited_form_widget_button.js b/addons/barcodes/static/src/js/inherited_form_widget_button.js
new file mode 100644
index 00000000000..6c85b9a92a8
--- /dev/null
+++ b/addons/barcodes/static/src/js/inherited_form_widget_button.js
@@ -0,0 +1,29 @@
+odoo.define('barcode.InheritedFormWidgetButton', function (require) {
+"use strict";
+
+var widgets = require('web.form_widgets');
+var BarcodeHandlerMixin = require('barcodes.BarcodeHandlerMixin');
+
+// If the button has a barcode_trigger attribute, dynamically inherit
+// BarcodeHandlerMixin and redefine on_barcode_scanned
+
+var ButtonBarcodeHandlerMixin = _.extend({}, BarcodeHandlerMixin, {
+ init: function(field_manager, node) {
+ if (node.attrs.barcode_trigger) {
+ BarcodeHandlerMixin.init.call(this, field_manager, node);
+ var self = this;
+ this.on_barcode_scanned = function(barcode) {
+ var match = barcode.match(/O-BTN\.(.+)/);
+ if (match && match[1] === self.node.attrs.barcode_trigger && self.$el.is(':visible')) {
+ self.on_click();
+ }
+ };
+ } else {
+ this._super(field_manager, node);
+ }
+ },
+});
+
+widgets.WidgetButton.include(ButtonBarcodeHandlerMixin);
+
+});
diff --git a/addons/barcodes/views/templates.xml b/addons/barcodes/views/templates.xml
index f0f92579f49..2fcab2ea6db 100644
--- a/addons/barcodes/views/templates.xml
+++ b/addons/barcodes/views/templates.xml
@@ -6,6 +6,9 @@
+
+
+
diff --git a/addons/web/static/src/js/views/form_widgets.js b/addons/web/static/src/js/views/form_widgets.js
index 8a51e8aa480..54c57c6fd8c 100644
--- a/addons/web/static/src/js/views/form_widgets.js
+++ b/addons/web/static/src/js/views/form_widgets.js
@@ -1663,7 +1663,8 @@ core.form_tag_registry.add('button', WidgetButton);
return {
FieldChar: FieldChar,
- FieldMonetary: FieldMonetary
+ FieldMonetary: FieldMonetary,
+ WidgetButton: WidgetButton
};
});
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index c79d9fc1f86..428a12db511 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -666,6 +666,7 @@