[IMP] barcode nomenclature used in WMS, also for lots, packages and locations
authorAaron Bohy <aab@odoo.com>
Wed, 5 Nov 2014 10:42:13 +0000 (11:42 +0100)
committerFrédéric van der Essen <fvdessen@gmail.com>
Wed, 26 Nov 2014 10:46:18 +0000 (11:46 +0100)
addons/point_of_sale/static/src/js/devices.js
addons/pos_barcodes/barcodes.py
addons/pos_barcodes/data/barcodes_data.xml
addons/stock/static/src/js/widgets.js
addons/stock/static/src/xml/picking.xml
addons/stock/stock.py

index ec4a50a..6676690 100644 (file)
@@ -501,7 +501,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
             }
         },
 
-        // returns the checksum of the ean, or -1 if the ean has not the correct length, ean must be a string
+        // returns the checksum of the ean13, or -1 if the ean has not the correct length, ean must be a string
         ean_checksum: function(ean){
             var code = ean.split('');
             if(code.length !== 13){
@@ -542,6 +542,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
         // - base_code : the barcode with all the encoding parts set to zero; the one put on
         //               the product in the backend
         parse_barcode: function(barcode){
+            console.log("Parse barcode");
             var self = this;
             var parsed_result = {
                 encoding: '',
@@ -552,6 +553,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
             };
             
             if (!this.pos.nomenclature) {
+                console.log("no nomenclature");
                 return parsed_result;
             }
 
@@ -645,9 +647,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
                     parsed_result.type      = rules[i].type;
                     parsed_result.value     = get_value(barcode,rules[i].pattern);
                     parsed_result.base_code = get_basecode(barcode,rules[i].pattern,parsed_result.encoding);
+                    console.log(parsed_result);
                     return parsed_result;
                 }
             }
+            console.log(parsed_result);
             return parsed_result;
         },
         
index 6de89d5..4618605 100644 (file)
@@ -37,7 +37,7 @@ 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
+    # returns the checksum of the ean13, 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:
@@ -53,82 +53,106 @@ class barcode_nomenclature(osv.osv):
         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.
+    # Attempts to interpret an barcode (string encoding a barcode)
+    # It will return an object containing various information about the barcode.
     # most importantly : 
-    #  - code    : the ean
-    #  - type   : the type of the ean: 
-    #     'price' |  'weight' | 'product' | 'cashier' | 'client' | 'discount' | 'error'
+    #  - code    : the barcode
+    #  - type   : the type of the barcode: 
+    #      'price' |  'weight' | 'product' | 'cashier' | 'client' | 'discount' | 'lot' | 
+    #            'package' | 'location' | '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
+    #  - base_code : the barcode 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})
+    def parse_ean(self, barcode):
+        parsed_result = {
+            'encoding': '', 
+            'type': 'error', 
+            'code': barcode, 
+            'base_code': barcode, 
+            'value': 0}
         
-        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):
+        def match_pattern(barcode,pattern):
+            if(len(barcode) < len(pattern.replace('{','').replace('}',''))):
+                return False # match of this pattern is impossible
+            numerical_content  = False
+            j=0
             for i in range(len(pattern)):
                 p = pattern[i]
-                e = ean[i]
-                if is_number(p) and p != e:
+                if p == '{' or p == '}':
+                    numerical_content = not numerical_content
+                    continue
+
+                if not numerical_content and p != '*' and p != barcode[j]:
                     return False
+                j+=1
             return True
 
-        def get_value(ean,pattern):
+        def get_value(barcode,pattern):
             value = 0
             decimals = 0
+            numerical_content = False
+            j = 0
             for i in range(len(pattern)):
                 p = pattern[i]
-                v = int(ean[i])
+                if not numerical_content and p != "{":
+                    j+=1
+                    continue
+                elif p == "{":
+                    numerical_content = True
+                    continue
+                elif p == "}":
+                    break;
+
+                v = int(barcode[j])
                 if p == 'N':
                     value *= 10
                     value += v
                 elif p == 'D':  #FIXME precision ....
                     decimals += 1
                     value += v * pow(10,-decimals)
+                j+=1
             return value
 
-        def get_basecode(ean,pattern):
+        def get_basecode(barcode,pattern,encoding):
             base = ''
+            numerical_content = False
+            j = 0
             for i in range(len(pattern)):
                 p = pattern[i]
-                v = ean[i]
-                if p == '*' or is_number(p):
-                    base += v
-                else:
+                if p == '{' or p == '}':
+                    numerical_content = not numerical_content
+                    continue
+
+                if numerical_content:
                     base += '0'
-            return self.sanitize_ean(base)
+                else:
+                    base += barcode[j]
+                j+=1
 
-        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
+            for i in range(j, len(barcode)): # Read the rest of the barcode
+                base += barcode[i]
+            if encoding == "ean13":
+                base = self.sanitize_ean(base)
+            return base
 
-        return parse_result
+        rules = []
+        for rule in self.rule_ids:
+            rules.append({'type': rule.type, 'encoding': rule.encoding, 'sequence': rule.sequence, 'pattern': rule.pattern})
+
+        for rule in rules:
+            if match_pattern(barcode, rule['pattern']):
+                parsed_result['encoding'] = rule['encoding']
+                parsed_result['type'] = rule['type']
+                parsed_result['value'] = get_value(barcode, rule['pattern'])
+                parsed_result['base_code'] = get_basecode(barcode, rule['pattern'], parsed_result['encoding'])
+                return parsed_result
+        return parsed_result
 
 class barcode_rule(osv.osv):
     _name = 'barcode.rule'
index cee8498..0620c5d 100644 (file)
@@ -2,7 +2,8 @@
 <openerp>
     <data>
        <record id="default_barcode_nomenclature" model="barcode.nomenclature">
-            <field name="name">Default Nomenclature</field>
+            <field name="name">Default Nomenc04201
+                lature</field>
         </record>
 
         <record id="barcode_rule_product" model="barcode.rule">
index f9b1b48..58af047 100644 (file)
@@ -278,6 +278,16 @@ function openerp_picking_widgets(instance){
                 self.$('#js_loc_select').data('op-id',op_id);
                 self.$el.siblings('#js_LocationChooseModal').modal();
             });
+            /*this.$('.js_send_to_scrap').click(function(){
+                var op_id = $(this).parents("[data-id]:first").data('id');
+                var scrap_location = new instance.web.Model("stock.location")
+                    .query(["id"])
+                    .filter([['usage','!=','view'],['scrap_location','=',true]])
+                    .first()
+                    .then(function(result) {
+                        self.getParent().change_location(op_id, result.id, false);
+                    })
+            });*/
             this.$('.js_pack_change_dst').click(function(){
                 var op_id = $(this).parents("[data-id]:first").data('id');
                 self.$('#js_loc_select').addClass('pack');
@@ -362,7 +372,7 @@ function openerp_picking_widgets(instance){
             var container = this.$('.js_pack_op_line.container_head:not(.processed):not(.hidden)')
             var disabled = true;
             $.each(qties,function(index, value){
-                if (parseInt(value)>0){
+                if(value>0) {
                     disabled = false;
                 }
             });
index e95cfeb..d2db763 100644 (file)
                                         <t t-if="!row.cols.head_container &amp;&amp; !row.cols.container">
                                             <li><a class="js_change_src" href="#">Change source location</a></li>
                                             <li><a class="js_change_dst" href="#">Change destination location</a></li>
+                                            <!--<li><a class="js_send_to_scrap" href="#">Move to scrap</a></li>-->
                                         </t>
                                         <t t-if="row.cols.head_container">
                                             <li><a class="js_pack_change_dst" href="#">Change destination location</a></li>
index cae63e1..6007714 100644 (file)
@@ -1447,11 +1447,7 @@ class stock_picking(osv.osv):
 
     def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, visible_op_ids, context=None):
         '''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')
-        product_obj = self.pool.get('product.product')
         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
@@ -1461,45 +1457,52 @@ class stock_picking(osv.osv):
             return answer # TODO: return specific error?
         else:
             barcode_nom = rec.barcode_nomenclature_id
-        parse_result = barcode_nom.parse_ean(barcode_str)
+        parsed_result = barcode_nom.parse_ean(barcode_str)
+
+        #check if the barcode is a weighted barcode or simply a product
+        if parsed_result['type'] in ['weight', 'product', 'package']:
+            weight=-1
+            if parsed_result['type'] == 'weight':
+                domain = ['|', ('ean13', '=', parsed_result['base_code']), ('default_code', '=', barcode_str)]
+                weight=parsed_result['value']
+                obj = self.pool.get('product.product')
+                id_in_operation = 'product_id'
+            elif parsed_result['type'] == 'product':
+                domain = ['|', ('ean13', '=', barcode_str), ('default_code', '=', barcode_str)]
+                obj = self.pool.get('product.product')
+                id_in_operation = 'product_id'
+            else:
+                domain = [('name', '=', barcode_str)]
+                obj = self.pool.get('stock.quant.package')
+                id_in_operation = 'package_id'
 
-        #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)
+            matching_product_ids = obj.search(cr, uid, domain, 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)
+                op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [(id_in_operation, '=', matching_product_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, weight=weight, 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:
-            #if we have a location, return immediatly with the location name
-            location = stock_location_obj.browse(cr, uid, matching_location_ids[0], context=None)
-            answer['filter_loc'] = stock_location_obj._name_get(cr, uid, location, context=None)
-            answer['filter_loc_id'] = matching_location_ids[0]
-            return answer
-        #check if the barcode correspond to a product
-        matching_product_ids = product_obj.search(cr, uid, ['|', ('ean13', '=', barcode_str), ('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, increment=True, context=context)
-            answer['operation_id'] = op_id
-            return answer
+                  
         #check if the barcode correspond to a lot
-        matching_lot_ids = lot_obj.search(cr, uid, [('name', '=', barcode_str)], context=context)
-        if matching_lot_ids:
-            lot = lot_obj.browse(cr, uid, matching_lot_ids[0], context=context)
-            op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
-            answer['operation_id'] = op_id
-            return answer
-        #check if the barcode correspond to a package
-        matching_package_ids = package_obj.search(cr, uid, [('name', '=', barcode_str)], context=context)
-        if matching_package_ids:
-            op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('package_id', '=', matching_package_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
-            answer['operation_id'] = op_id
-            return answer
+        elif parsed_result['type'] == 'lot':
+            lot_obj = self.pool.get('stock.production.lot')
+            matching_lot_ids = lot_obj.search(cr, uid, [('name', '=', barcode_str)], context=context)
+            if matching_lot_ids:
+                lot = lot_obj.browse(cr, uid, matching_lot_ids[0], context=context)
+                op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], filter_visible=True, visible_op_ids=visible_op_ids, increment=True, context=context)
+                answer['operation_id'] = op_id
+                return answer
+            
+        #check if the barcode correspond to a location
+        elif parsed_result['type'] == 'location':
+            stock_location_obj = self.pool.get('stock.location')
+            matching_location_ids = stock_location_obj.search(cr, uid, [('loc_barcode', '=', barcode_str)], context=context)
+            if matching_location_ids:
+                #if we have a location, return immediatly with the location name
+                location = stock_location_obj.browse(cr, uid, matching_location_ids[0], context=None)
+                answer['filter_loc'] = stock_location_obj._name_get(cr, uid, location, context=None)
+                answer['filter_loc_id'] = matching_location_ids[0]
+                return answer
+            
         return answer
 
 
@@ -2282,7 +2285,6 @@ class stock_move(osv.osv):
     def action_done(self, cr, uid, ids, context=None):
         """ Process completely the moves given as ids and if all moves are done, it will finish the picking.
         """
-        print "Action done"
         context = context or {}
         picking_obj = self.pool.get("stock.picking")
         quant_obj = self.pool.get("stock.quant")
@@ -3927,6 +3929,7 @@ class stock_pack_operation(osv.osv):
                 op = self.copy(cr, uid, pack_op.id, {'product_qty': pack_op.qty_done, 'qty_done': pack_op.qty_done}, context=context)
                 self.write(cr, uid, [pack_op.id], {'product_qty': pack_op.product_qty - pack_op.qty_done, 'qty_done': 0, 'lot_id': False}, context=context)
             processed_ids.append(op)
+        print "Nb processed: " + str(len(processed_ids))
         self.write(cr, uid, processed_ids, {'processed': 'true'}, context=context)
 
     def create_and_assign_lot(self, cr, uid, id, name, context=None):
@@ -3972,8 +3975,8 @@ class stock_pack_operation(osv.osv):
             op_obj = self.browse(cr, uid, operation_id, context=context)
             qty = op_obj.qty_done
             if increment:
-                if weight == -1: # button + pressed
-                    if op_obj.product_qty != qty:
+                if weight == -1: # increment by 1
+                    if qty < op_obj.product_qty:
                         qty = min(qty+1, op_obj.product_qty);
                     else:
                         qty += 1
@@ -3996,7 +3999,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': weight,
+                'qty_done': weight if weight != -1 else 1,
                 }
             for key in domain:
                 var_name, dummy, value = key