[IMP] stock: use of barcodes nomenclatures to scan weighted products
This commit is contained in:
committed by
Frédéric van der Essen
parent
7464b3eab3
commit
3d483b14d4
@@ -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++) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user