[IMP] point_of_sale: support for non ean13 barcodes using the Internal Reference field

bzr revid: fva@openerp.com-20130923145139-iu3ds9tcfzy1ffcf
This commit is contained in:
Frédéric van der Essen 2013-09-23 16:51:39 +02:00
parent 13f6d59043
commit e03f3aa288
7 changed files with 101 additions and 72 deletions

View File

@ -82,7 +82,7 @@ class PointOfSaleController(http.Controller):
""" """
print 'scan_item_success: ' + str(ean) print 'scan_item_success: ' + str(ean)
@http.route('/pos/scan_item_error_unrecognized') @http.route('/pos/scan_item_error_unrecognized', type='json', auth='admin')
def scan_item_error_unrecognized(self, ean): def scan_item_error_unrecognized(self, ean):
""" """
A product has been scanned without success A product has been scanned without success

View File

@ -43,6 +43,7 @@ function openerp_pos_db(instance, module){
this.product_by_id = {}; this.product_by_id = {};
this.product_by_ean13 = {}; this.product_by_ean13 = {};
this.product_by_category_id = {}; this.product_by_category_id = {};
this.product_by_reference = {};
this.category_by_id = {}; this.category_by_id = {};
this.root_category_id = 0; this.root_category_id = 0;
@ -197,6 +198,9 @@ function openerp_pos_db(instance, module){
if(product.ean13){ if(product.ean13){
this.product_by_ean13[product.ean13] = product; this.product_by_ean13[product.ean13] = product;
} }
if(product.default_code){
this.product_by_reference[product.default_code] = product;
}
} }
}, },
add_packagings: function(packagings){ add_packagings: function(packagings){
@ -241,6 +245,9 @@ function openerp_pos_db(instance, module){
} }
return undefined; return undefined;
}, },
get_product_by_reference: function(ref){
return this.product_by_reference[ref];
},
get_product_by_category: function(category_id){ get_product_by_category: function(category_id){
var product_ids = this.product_by_category_id[category_id]; var product_ids = this.product_by_category_id[category_id];
var list = []; var list = [];

View File

@ -450,7 +450,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
// it will check its validity then return an object containing various // it will check its validity then return an object containing various
// information about the ean. // information about the ean.
// most importantly : // most importantly :
// - ean : the ean // - code : the ean
// - type : the type of the ean: // - type : the type of the ean:
// 'price' | 'weight' | 'unit' | 'cashier' | 'client' | 'discount' | 'error' // 'price' | 'weight' | 'unit' | 'cashier' | 'client' | 'discount' | 'error'
// //
@ -460,13 +460,16 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
// - unit : if the encoded value has a unit, it will be put there. // - unit : if the encoded value has a unit, it will be put there.
// not to be confused with the 'unit' type, which represent an unit of a // not to be confused with the 'unit' type, which represent an unit of a
// unique product // unique product
// - base_code : the ean code with all the encoding parts set to zero; the one put on
// the product in the backend
parse_ean: function(ean){ parse_ean: function(ean){
var parse_result = { var parse_result = {
type:'unknown', // encoding: 'ean13',
type:'unknown',
prefix:'', prefix:'',
ean:ean, code:ean,
base_ean: ean, base_code: ean,
id:'', id:'',
value: 0, value: 0,
unit: 'none', unit: 'none',
@ -487,13 +490,13 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
parse_result.type = 'error'; parse_result.type = 'error';
} else if( match_prefix(this.price_prefix_set,'price')){ } else if( match_prefix(this.price_prefix_set,'price')){
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7)); parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0; parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = 'euro'; parse_result.unit = 'euro';
} else if( match_prefix(this.weight_prefix_set,'weight')){ } else if( match_prefix(this.weight_prefix_set,'weight')){
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.value = Number(ean.substring(7,12))/1000.0; parse_result.value = Number(ean.substring(7,12))/1000.0;
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7)); parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.unit = 'Kg'; parse_result.unit = 'Kg';
} else if( match_prefix(this.client_prefix_set,'client')){ } else if( match_prefix(this.client_prefix_set,'client')){
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
@ -502,7 +505,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
} else if( match_prefix(this.discount_prefix_set,'discount')){ } else if( match_prefix(this.discount_prefix_set,'discount')){
parse_result.id = ean.substring(0,7); parse_result.id = ean.substring(0,7);
parse_result.base_ean = this.sanitize_ean(ean.substring(0,7)); parse_result.base_code = this.sanitize_ean(ean.substring(0,7));
parse_result.value = Number(ean.substring(7,12))/100.0; parse_result.value = Number(ean.substring(7,12))/100.0;
parse_result.unit = '%'; parse_result.unit = '%';
} else { } else {
@ -512,9 +515,19 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
} }
return parse_result; return parse_result;
}, },
on_ean: function(ean){ scan: function(type,code){
var parse_result = this.parse_ean(ean); console.log('scan',type,code);
if (type === 'ean13'){
var parse_result = this.parse_ean(code);
}else if(type === 'reference'){
var parse_result = {
encoding: 'reference',
type: 'unit',
code: code,
prefix: '',
};
}
if (parse_result.type === 'error') { //most likely a checksum error, raise warning if (parse_result.type === 'error') { //most likely a checksum error, raise warning
console.warn('WARNING: barcode checksum error:',parse_result); console.warn('WARNING: barcode checksum error:',parse_result);
@ -522,7 +535,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
if(this.action_callback['product']){ if(this.action_callback['product']){
this.action_callback['product'](parse_result); this.action_callback['product'](parse_result);
} }
//this.trigger("codebar",parse_result );
}else{ }else{
if(this.action_callback[parse_result.type]){ if(this.action_callback[parse_result.type]){
this.action_callback[parse_result.type](parse_result); this.action_callback[parse_result.type](parse_result);
@ -530,41 +542,43 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
} }
}, },
on_reference: function(code){
if(this.action_callback['reference']){
this.action_callback['reference'](code);
}
},
// starts catching keyboard events and tries to interpret codebar // starts catching keyboard events and tries to interpret codebar
// calling the callbacks when needed. // calling the callbacks when needed.
connect: function(){ connect: function(){
var self = this; var self = this;
var codeNumbers = []; var code = "";
var timeStamp = 0; var timeStamp = 0;
var lastTimeStamp = 0; var onlynumbers = true;
// The barcode readers acts as a keyboard, we catch all keyup events and try to find a
// barcode sequence in the typed keys, then act accordingly.
this.handler = function(e){ this.handler = function(e){
//We only care about numbers if(timeStamp + 50 < new Date().getTime()){
if (e.which >= 48 && e.which < 58){ code = "";
onlynumbers = true;
}
// The barcode reader sends keystrokes with a specific interval. timeStamp = new Date().getTime();
// We look if the typed keys fit in the interval.
if (codeNumbers.length === 0) { if( e.which < 48 || e.which >= 58 ){ // not a number
timeStamp = new Date().getTime(); onlynumbers = false;
} else { }
if (lastTimeStamp + 30 < new Date().getTime()) {
// not a barcode reader code += String.fromCharCode(e.which);
codeNumbers = [];
timeStamp = new Date().getTime(); if(code.length >= 2 && self.pos.db.get_product_by_reference(code)){
} self.scan('reference',code);
} code = "";
codeNumbers.push(e.which - 48); onlynumbers = true;
lastTimeStamp = new Date().getTime(); }else if(code.length === 13 && onlynumbers){
if (codeNumbers.length === 13) { self.scan('ean13',code);
//We have found what seems to be a valid codebar code = "";
self.on_ean(codeNumbers.join('')); onlynumbers = true;
codeNumbers = [];
}
} else {
// NaN
codeNumbers = [];
} }
}; };
$('body').on('keypress', this.handler); $('body').on('keypress', this.handler);

View File

@ -428,19 +428,23 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return tried_all; return tried_all;
}, },
scan_product: function(parsed_ean){ scan_product: function(parsed_code){
var self = this; var self = this;
var product = this.db.get_product_by_ean13(parsed_ean.base_ean);
var selectedOrder = this.get('selectedOrder'); var selectedOrder = this.get('selectedOrder');
if(parsed_code.encoding === 'ean13'){
var product = this.db.get_product_by_ean13(parsed_code.base_code);
}else if(parsed_code.encoding === 'reference'){
var product = this.db.get_product_by_reference(parsed_code.code);
}
if(!product){ if(!product){
return false; return false;
} }
if(parsed_ean.type === 'price'){ if(parsed_code.type === 'price'){
selectedOrder.addProduct(new module.Product(product), {price:parsed_ean.value}); selectedOrder.addProduct(new module.Product(product), {price:parsed_code.value});
}else if(parsed_ean.type === 'weight'){ }else if(parsed_code.type === 'weight'){
selectedOrder.addProduct(new module.Product(product), {quantity:parsed_ean.value, merge:false}); selectedOrder.addProduct(new module.Product(product), {quantity:parsed_code.value, merge:false});
}else{ }else{
selectedOrder.addProduct(new module.Product(product)); selectedOrder.addProduct(new module.Product(product));
} }

View File

@ -160,29 +160,29 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// what happens when a product is scanned : // what happens when a product is scanned :
// it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if // it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if
// there's an error. // there's an error.
barcode_product_action: function(ean){ barcode_product_action: function(code){
var self = this; var self = this;
if(self.pos.scan_product(ean)){ if(self.pos.scan_product(code)){
self.pos.proxy.scan_item_success(ean); self.pos.proxy.scan_item_success(code);
if(self.barcode_product_screen){ if(self.barcode_product_screen){
self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen); self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
} }
}else{ }else{
self.pos.proxy.scan_item_error_unrecognized(ean); self.pos.proxy.scan_item_error_unrecognized(code);
if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){ if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup); self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
} }
} }
}, },
// what happens when a cashier id barcode is scanned. // what happens when a cashier id barcode is scanned.
// the default behavior is the following : // the default behavior is the following :
// - if there's a user with a matching ean, put it as the active 'cashier', go to cashier mode, and return true // - if there's a user with a matching ean, put it as the active 'cashier', go to cashier mode, and return true
// - else : do nothing and return false. You probably want to extend this to show and appropriate error popup... // - else : do nothing and return false. You probably want to extend this to show and appropriate error popup...
barcode_cashier_action: function(ean){ barcode_cashier_action: function(code){
var users = this.pos.get('user_list'); var users = this.pos.get('user_list');
for(var i = 0, len = users.length; i < len; i++){ for(var i = 0, len = users.length; i < len; i++){
if(users[i].ean13 === ean.ean){ if(users[i].ean13 === code.code){
this.pos.set('cashier',users[i]); this.pos.set('cashier',users[i]);
this.pos_widget.username.refresh(); this.pos_widget.username.refresh();
this.pos.proxy.cashier_mode_activated(); this.pos.proxy.cashier_mode_activated();
@ -190,7 +190,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
return true; return true;
} }
} }
this.pos.proxy.scan_item_error_unrecognized(ean); this.pos.proxy.scan_item_error_unrecognized(code);
return false; return false;
}, },
@ -198,28 +198,28 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
// the default behavior is the following : // the default behavior is the following :
// - if there's a user with a matching ean, put it as the active 'client' and return true // - if there's a user with a matching ean, put it as the active 'client' and return true
// - else : return false. // - else : return false.
barcode_client_action: function(ean){ barcode_client_action: function(code){
var partners = this.pos.get('partner_list'); var partners = this.pos.get('partner_list');
for(var i = 0, len = partners.length; i < len; i++){ for(var i = 0, len = partners.length; i < len; i++){
if(partners[i].ean13 === ean.ean){ if(partners[i].ean13 === code.code){
this.pos.get('selectedOrder').set_client(partners[i]); this.pos.get('selectedOrder').set_client(partners[i]);
this.pos_widget.username.refresh(); this.pos_widget.username.refresh();
this.pos.proxy.scan_item_success(ean); this.pos.proxy.scan_item_success(code);
return true; return true;
} }
} }
this.pos.proxy.scan_item_error_unrecognized(ean); this.pos.proxy.scan_item_error_unrecognized(code);
return false; return false;
//TODO start the transaction //TODO start the transaction
}, },
// what happens when a discount barcode is scanned : the default behavior // what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order. // is to set the discount on the last order.
barcode_discount_action: function(ean){ barcode_discount_action: function(code){
this.pos.proxy.scan_item_success(ean); this.pos.proxy.scan_item_success(code);
var last_orderline = this.pos.get('selectedOrder').getLastOrderline(); var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
if(last_orderline){ if(last_orderline){
last_orderline.set_discount(ean.value) last_orderline.set_discount(code.value)
} }
}, },
@ -292,10 +292,10 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode()); this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
this.pos.barcode_reader.set_action_callback({ this.pos.barcode_reader.set_action_callback({
'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined , 'cashier': self.barcode_cashier_action ? function(code){ self.barcode_cashier_action(code); } : undefined ,
'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined , 'product': self.barcode_product_action ? function(code){ self.barcode_product_action(code); } : undefined ,
'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined , 'client' : self.barcode_client_action ? function(code){ self.barcode_client_action(code); } : undefined ,
'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined, 'discount': self.barcode_discount_action ? function(code){ self.barcode_discount_action(code); } : undefined,
}); });
}, },
@ -405,7 +405,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
this.pos.barcode_reader.save_callbacks(); this.pos.barcode_reader.save_callbacks();
this.pos.barcode_reader.reset_action_callbacks(); this.pos.barcode_reader.reset_action_callbacks();
this.pos.barcode_reader.set_action_callback({ this.pos.barcode_reader.set_action_callback({
'cashier': function(ean){ 'cashier': function(code){
clearInterval(this.intervalID); clearInterval(this.intervalID);
self.pos.proxy.cashier_mode_activated(); self.pos.proxy.cashier_mode_activated();
self.pos_widget.screen_selector.set_user_mode('cashier'); self.pos_widget.screen_selector.set_user_mode('cashier');
@ -683,14 +683,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
show_numpad: false, show_numpad: false,
show_leftpane: false, show_leftpane: false,
barcode_product_action: function(ean){ barcode_product_action: function(code){
this.pos.proxy.transaction_start(); this.pos.proxy.transaction_start();
this._super(ean); this._super(code);
}, },
barcode_client_action: function(ean){ barcode_client_action: function(code){
this.pos.proxy.transaction_start(); this.pos.proxy.transaction_start();
this._super(ean); this._super(code);
$('.goodbye-message').addClass('oe_hidden'); $('.goodbye-message').addClass('oe_hidden');
this.pos_widget.screen_selector.show_popup('choose-receipt'); this.pos_widget.screen_selector.show_popup('choose-receipt');
}, },

View File

@ -753,12 +753,15 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
this.$('.button.custom_ean').click(function(){ this.$('.button.custom_ean').click(function(){
var ean = self.pos.barcode_reader.sanitize_ean(self.$('input.ean').val() || '0'); var ean = self.pos.barcode_reader.sanitize_ean(self.$('input.ean').val() || '0');
self.$('input.ean').val(ean); self.$('input.ean').val(ean);
self.pos.barcode_reader.on_ean(ean); self.pos.barcode_reader.scan('ean13',ean);
});
this.$('.button.reference').click(function(){
self.pos.barcode_reader.scan('reference',self.$('input.ean').val());
}); });
_.each(this.eans, function(ean, name){ _.each(this.eans, function(ean, name){
self.$('.button.'+name).click(function(){ self.$('.button.'+name).click(function(){
self.$('input.ean').val(ean); self.$('input.ean').val(ean);
self.pos.barcode_reader.on_ean(ean); self.pos.barcode_reader.scan('ean13',ean);
}); });
}); });
_.each(this.events, function(name){ _.each(this.events, function(name){

View File

@ -494,6 +494,7 @@
<li class="button lemon_price">1.54€ Lemon</li> <li class="button lemon_price">1.54€ Lemon</li>
<li class="button unknown_product">Unknown Product</li> <li class="button unknown_product">Unknown Product</li>
<li class="button invalid_ean">Invalid Ean</li> <li class="button invalid_ean">Invalid Ean</li>
<li class="button reference">Reference</li>
</ul> </ul>
<p class="category">Hardware Status</p> <p class="category">Hardware Status</p>