[IMP] stock: use of barcodes nomenclatures to scan weighted products

This commit is contained in:
Aaron Bohy
2014-11-26 11:46:15 +01:00
committed by Frédéric van der Essen
parent 7464b3eab3
commit 3d483b14d4
4 changed files with 134 additions and 14 deletions
+3 -3
View File
@@ -169,10 +169,10 @@ class pos_config(osv.osv):
return company_id
def _get_default_nomenclature(self, cr, uid, context=None):
default_nom_obj = self.pool.get('barcode.nomenclature')
res = default_nom_obj.search(cr, uid, [('id', '=', 1)], limit=1, context=context)
nom_obj = self.pool.get('barcode.nomenclature')
res = nom_obj.search(cr, uid, [], limit=1, context=context)
if res and res[0]:
return default_nom_obj.browse(cr, uid, res[0], context=context).id
return nom_obj.browse(cr, uid, res[0], context=context).id
return False
_defaults = {
@@ -311,7 +311,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
fields: ['name','rule_ids'],
domain: function(self){ return [] },
loaded: function(self,nomenclatures){
console.log("PUSH");
if (self.config.barcode_nomenclature_id) {
for (var i = 0; i < nomenclatures.length; i++) {
if (nomenclatures[i].id === self.config.barcode_nomenclature_id[0]) {
@@ -320,8 +319,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}
}
self.nomenclature = self.nomenclature || null;
console.log(self.nomenclature);
console.log(self.attributes);
},
}, {
model: 'barcode.rule',
@@ -329,7 +326,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
domain: function(self){ return [['barcode_nomenclature_id','=',self.nomenclature ? self.nomenclature.id : 0]]; },
loaded: function(self,rules){
if (self.nomenclature) {
console.log("PUSH2");
rules = rules.sort(function(a,b){ return a.sequence - b.sequence; });
self.nomenclature.rules = rules;
for (var i = 0; i < rules.length; i++) {
+94
View File
@@ -20,6 +20,7 @@
##############################################################################
import logging
import re
import openerp
from openerp import tools
@@ -36,6 +37,99 @@ class barcode_nomenclature(osv.osv):
'rule_ids': fields.one2many('barcode.rule','barcode_nomenclature_id','Rules', help='The list of barcode rules'),
}
# returns the checksum of the ean, or -1 if the ean has not the correct length, ean must be a string
def ean_checksum(self, ean):
code = list(ean)
if len(code) != 13:
return -1
oddsum = evensum = total = 0
code = code[:-1] # Remove checksum
for i in range(len(code)):
if i % 2 == 0:
evensum += int(code[i])
else:
oddsum += int(code[i])
total = oddsum * 3 + evensum
return int((10 - total % 10) % 10)
# Returns true if the ean is a valid EAN codebar number by checking the control digit.
# ean must be a string
def check_ean(self, ean):
return re.match(re.compile('\d+$'), ean) and (self.ean_checksum(ean) == int(ean[len(ean)-1]))
# Returns a valid zero padded ean13 from an ean prefix. the ean prefix must be a string.
def sanitize_ean(self, ean):
ean = ean[0:13]
ean = ean + (13-len(ean))*'0'
return ean[0:12] + str(self.ean_checksum(ean))
# Attempts to interpret an ean (string encoding an ean)
# It will check its validity then return an object containing various
# information about the ean.
# most importantly :
# - code : the ean
# - type : the type of the ean:
# 'price' | 'weight' | 'product' | 'cashier' | 'client' | 'discount' | 'error'
# - value : if the id encodes a numerical value, it will be put there
# - base_code : the ean code with all the encoding parts set to zero; the one put on
# the product in the backend
def parse_ean(self, ean):
parse_result = {'encoding': 'ean13', 'type': 'error', 'code': ean, 'base_code': ean, 'value': 0}
rules = []
for rule in self.rule_ids:
rules.append({'type': rule.type, 'sequence': rule.sequence, 'pattern': rule.pattern})
if not self.check_ean(ean):
return parse_result
def is_number(char):
n = ord(char)
return n >= 48 and n <= 57
def match_pattern(ean,pattern):
for i in range(len(pattern)):
p = pattern[i]
e = ean[i]
if is_number(p) and p != e:
return False
return True
def get_value(ean,pattern):
value = 0
decimals = 0
for i in range(len(pattern)):
p = pattern[i]
v = int(ean[i])
if p == 'N':
value *= 10
value += v
elif p == 'D': #FIXME precision ....
decimals += 1
value += v * pow(10,-decimals)
return value
def get_basecode(ean,pattern):
base = ''
for i in range(len(pattern)):
p = pattern[i]
v = ean[i]
if p == '*' or is_number(p):
base += v
else:
base += '0'
return self.sanitize_ean(base)
for rule in rules:
if match_pattern(ean, rule['pattern']):
parse_result['type'] = rule['type']
parse_result['value'] = get_value(ean, rule['pattern'])
parse_result['base_code'] = get_basecode(ean, rule['pattern'])
return parse_result
return parse_result
class barcode_rule(osv.osv):
_name = 'barcode.rule'
_order = 'sequence asc'
+37 -7
View File
@@ -818,8 +818,16 @@ class stock_picking(osv.osv):
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10),
'stock.move': (_get_pickings, ['group_id', 'picking_id'], 10),
}),
'barcode_nomenclature_id': fields.many2one('barcode.nomenclature','Barcode Nomenclature', help='A barcode nomenclature', required="True"),
}
def _get_default_nomenclature(self, cr, uid, context=None):
nom_obj = self.pool.get('barcode.nomenclature')
res = nom_obj.search(cr, uid, [], limit=1, context=context)
if res and res[0]:
return nom_obj.browse(cr, uid, res[0], context=context).id
return False
_defaults = {
'name': '/',
'state': 'draft',
@@ -828,6 +836,7 @@ class stock_picking(osv.osv):
'date': fields.datetime.now,
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.picking', context=c),
'recompute_pack_op': True,
'barcode_nomenclature_id': _get_default_nomenclature,
}
_sql_constraints = [
('name_uniq', 'unique(name, company_id)', 'Reference must be unique per company!'),
@@ -1437,7 +1446,6 @@ class stock_picking(osv.osv):
return self.pool.get('stock.pack.operation')._search_and_increment(cr, uid, picking_id, [('product_id', '=', product_id),('id', '=', op_id)], increment=increment, context=context)
def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, visible_op_ids, context=None):
print barcode_str
'''This function is called each time there barcode scanner reads an input'''
lot_obj = self.pool.get('stock.production.lot')
package_obj = self.pool.get('stock.quant.package')
@@ -1445,6 +1453,26 @@ class stock_picking(osv.osv):
stock_operation_obj = self.pool.get('stock.pack.operation')
stock_location_obj = self.pool.get('stock.location')
answer = {'filter_loc': False, 'operation_id': False}
# Barcode Nomenclatures
this_picking = self.search(cr, uid, [('id', '=', picking_id)], context=context)
rec = self.browse(cr, uid, this_picking, context=context)
if not rec:
return answer # TODO: return specific error?
else:
barcode_nom = rec.barcode_nomenclature_id
parse_result = barcode_nom.parse_ean(barcode_str)
#check if the barcode is a weighted barcode
if parse_result['type'] == 'weight':
matching_product_ids = product_obj.search(cr, uid, ['|', ('ean13', '=', parse_result['base_code']), ('default_code', '=', barcode_str)], context=context)
if matching_product_ids:
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', matching_product_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, weight=parse_result['value'], increment=True, context=context)
answer['operation_id'] = op_id
return answer
else:
print "Weighted barcode but product not found" #TODO: what to do here?
#check if the barcode correspond to a location
matching_location_ids = stock_location_obj.search(cr, uid, [('loc_barcode', '=', barcode_str)], context=context)
if matching_location_ids:
@@ -3917,11 +3945,12 @@ class stock_pack_operation(osv.osv):
new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context)
self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context)
def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False, visible_op_ids=False, increment=True, context=None):
'''Search for an operation with given 'domain' in a picking, if it exists increment the qty (+1) otherwise create it
def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False, visible_op_ids=False, weight=1, increment=True, context=None):
'''Search for an operation with given 'domain' in a picking, if it exists increment the qty (+weight) otherwise create it
:param domain: list of tuple directly reusable as a domain
context can receive a key 'current_package_id' with the package to consider for this operation
weight is used for weighted products for which quantity != 1 (not per unit but e.g. per kg)
returns True
'''
if context is None:
@@ -3942,10 +3971,11 @@ class stock_pack_operation(osv.osv):
op_obj = self.browse(cr, uid, operation_id, context=context)
qty = op_obj.qty_done
if increment:
qty += 1
qty += weight
else:
qty -= 1 if qty >= 1 else 0
if qty == 0 and op_obj.product_qty == 0:
qty -= 1 #if qty >= 1 else 0
#if qty == 0 and op_obj.product_qty == 0:
if qty < 0 and op_obj.product_qty == 0:
#we have a line with 0 qty set, so delete it
self.unlink(cr, uid, [operation_id], context=context)
return False
@@ -3959,7 +3989,7 @@ class stock_pack_operation(osv.osv):
'product_qty': 0,
'location_id': picking.location_id.id,
'location_dest_id': picking.location_dest_id.id,
'qty_done': 1,
'qty_done': weight,
}
for key in domain:
var_name, dummy, value = key