[MERGE] web/barcodes: add backend barcode scanning tools

This commit is contained in:
Arthur Maniet
2015-09-01 13:30:08 +02:00
10 changed files with 197 additions and 6 deletions
+4
View File
@@ -1 +1,5 @@
# -*- coding: utf-8 -*-
import barcodes
import models
+3
View File
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import barcode_events_mixin
@@ -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 `<field name="_barcode_scanned" widget="barcode_handler"/>`.
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")
@@ -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);
}
},
@@ -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.');
},
};
});
@@ -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;
});
@@ -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);
});
+3
View File
@@ -6,6 +6,9 @@
<xpath expr="." position="inside">
<script type="text/javascript" src="/barcodes/static/src/js/barcode_parser.js"></script>
<script type="text/javascript" src="/barcodes/static/src/js/barcode_events.js"></script>
<script type="text/javascript" src="/barcodes/static/src/js/barcode_handler_mixin.js"></script>
<script type="text/javascript" src="/barcodes/static/src/js/form_view_barcode_handler.js"></script>
<script type="text/javascript" src="/barcodes/static/src/js/inherited_form_widget_button.js"></script>
</xpath>
</template>
@@ -1663,7 +1663,8 @@ core.form_tag_registry.add('button', WidgetButton);
return {
FieldChar: FieldChar,
FieldMonetary: FieldMonetary
FieldMonetary: FieldMonetary,
WidgetButton: WidgetButton
};
});
+2
View File
@@ -666,6 +666,7 @@
<span t-att-class="'oe_form_field '+widget.widget_class" t-att-style="widget.node.attrs.style">
<t t-if="!widget.get('effective_readonly')">
<input t-att-type="widget.password ? 'password' : 'text'"
t-att-barcode_events="widget.options.barcode_events"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
@@ -820,6 +821,7 @@
class="oe_m2o_cm_button fa fa-external-link" draggable="false"/>
<div>
<input type="text"
t-att-barcode_events="widget.options.barcode_events"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"