[IMP] stock_picking_ui: new menu to select the picking by type or barcode + correctly...
authorFrédéric van der Essen <fva@openerp.com>
Thu, 3 Oct 2013 09:49:04 +0000 (11:49 +0200)
committerFrédéric van der Essen <fva@openerp.com>
Thu, 3 Oct 2013 09:49:04 +0000 (11:49 +0200)
bzr revid: fva@openerp.com-20131003094904-65t4r9f0git7pl7q

addons/stock/static/src/css/picking.css
addons/stock/static/src/js/main.js [deleted file]
addons/stock/static/src/js/stock_picking_type.js
addons/stock/static/src/js/widgets.js
addons/stock/static/src/xml/picking.xml
addons/stock/stock.py

index 58cb3df..edf62fc 100644 (file)
@@ -48,7 +48,7 @@
 .oe_pick_widget .oe_pick_button{
     display: inline-block;
     min-width: 64px;
-    padding: 5px;
+    padding: 5px 10px;
     font-size: 18px;
     border-radius: 3px;
     margin: 10px 5px;
@@ -70,7 +70,8 @@
     box-shadow: 0px 1px 1px rgba(255, 255, 255, 0.32) inset;
 }
 
-.oe_pick_widget .oe_pick_button:not(.oe_disabled):active{
+.oe_pick_widget .oe_pick_button:not(.oe_disabled):active,
+.oe_pick_widget .oe_pick_button:not(.oe_disabled).oe_active{
     background: rgb(92, 84, 133);
     box-shadow: 0px 1px 0px 1px rgba(0,0,0,0.3) inset;
     margin: 11px 6px;
     color: rgb(168, 6, 6);
 }
 
+.oe_pick_widget .oe_pick_app_title{
+    font-size: 20px;
+    margin-top: 0px;
+}
+.oe_pick_widget .oe_pick_app_subtitle{
+    font-size: 16px;
+    font-weight: normal;
+}
+.oe_pick_widget .oe_pick_app_info{
+    margin-left: 4px;
+    opacity: 0.5;
+}
+
 /* ----------------------- *
  *      PICKING TABLES     *
  * ----------------------- */
diff --git a/addons/stock/static/src/js/main.js b/addons/stock/static/src/js/main.js
deleted file mode 100644 (file)
index 08e93d2..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-console.log('Executing picking module');
-
-openerp.stock = function(openerp){
-
-    console.log('Loading stock picking module');
-
-    openerp.stock = instance.stock || {};
-    openerp_picking_widgets(openerp);
-    openerp.web.client_actions.add('stock.ui', 'instance.stock.PickingMainWidget');
-};
-
-    
index 49886b8..5b87ba8 100644 (file)
@@ -65,6 +65,5 @@ openerp.stock = function(openerp) {
 
     openerp.stock = openerp.stock || {};
     openerp_picking_widgets(openerp);
-    openerp.web.client_actions.add('stock.ui', 'instance.stock.PickingMainWidget');
 
 };
index 517216f..8244124 100644 (file)
@@ -97,6 +97,104 @@ function openerp_picking_widgets(instance){
         },
     });
 
+    module.PickingMenuWidget = instance.web.Widget.extend({
+        template: 'PickingMenuWidget',
+        init: function(parent, params){
+            this._super(parent,params);
+            var self = this;
+
+            this.picking_types = [];
+            this.loaded = this.load();
+            this.scanning_type = 0;
+            this.barcode_scanner = new module.BarcodeScanner();
+            this.pickings_by_type = {};
+
+        },
+        load: function(){
+            var self = this;
+            return new instance.web.Model('stock.picking.type').get_func('search_read')([],[])
+                .then(function(types){
+                    self.picking_types = types;
+                    
+                    for(var i = 0; i < types.length; i++){
+                        self.pickings_by_type[types[i].id] = [];
+                    }
+                    self.pickings_by_type[0] = [];
+
+                    return new instance.web.Model('stock.picking').call('search_read',[ [['state','in',['confirmed','assigned']]], [] ], {context: new instance.web.CompoundContext()});
+                                                                  
+                }).then(function(pickings){
+                    self.pickings = pickings;
+
+                    for(var i = 0; i < pickings.length; i++){
+                        var picking = pickings[i];
+                        self.pickings_by_type[picking.picking_type_id[0]].push(picking);
+                    }
+                });
+        },
+        renderElement: function(){
+            this._super();
+            var self = this;
+            this.$('.js_pick_quit').click(function(){ self.quit(); });
+            this.$('.js_pick_scan').click(function(){ self.scan_picking($(this).data('id')); });
+            this.$('.js_pick_last').click(function(){ self.goto_picking($(this).data('id')); });
+        },
+        start: function(){
+            var self = this;
+            this.barcode_scanner.connect(function(barcode){
+                self.on_scan(barcode);
+            });
+            this.loaded.then(function(){
+                self.renderElement();
+            });
+        },
+        goto_picking: function(type_id){
+            this.do_action({
+                type:   'ir.actions.client',
+                tag:    'stock.ui',
+                target: 'current',
+                context: { active_id: type_id },
+            },{
+                clear_breadcrumbs: true,
+            });
+        },
+        scan_picking: function(id){
+            this.$('.js_pick_scan.oe_active').text(_t('Scan')).removeClass('oe_active');
+            if(id !== this.scanning_type){
+                this.$('.js_pick_scan[data-id='+id+']').text(_t('Please scan a barcode ...')).addClass('oe_active');
+                this.scanning_type = id;
+            }else{
+                this.scanning_type = 0;
+            }
+        },
+        on_scan: function(barcode){
+            for(var i = 0, len = this.pickings.length; i < len; i++){
+                var picking = this.pickings[i];
+                if(picking.picking_type_id[0] === this.scanning_type && picking.name.toUpperCase() === barcode.toUpperCase()){
+                    this.do_action({
+                        type:   'ir.actions.client',
+                        tag:    'stock.ui',
+                        target: 'current',
+                        context: { picking_id: picking.id },
+                    },{
+                        clear_breadcrumbs: true,
+                    });
+                }
+            }
+            this.$('.js_pick_scan.oe_active').text(_t('Scanned picking not found'));
+        },
+        quit: function(){
+            instance.webclient.set_content_full_screen(false);
+            window.location = '/'; // FIXME Ask niv how to do it correctly
+        },
+        destroy: function(){
+            this._super();
+            this.barcode_scanner.disconnect();
+            instance.webclient.set_content_full_screen(false);
+        },
+    });
+    openerp.web.client_actions.add('stock.menu', 'instance.stock.PickingMenuWidget');
+
     module.PickingMainWidget = instance.web.Widget.extend({
         template: 'PickingMainWidget',
         init: function(parent,params){
@@ -108,29 +206,29 @@ function openerp_picking_widgets(instance){
             this.movelines = null;
             this.operations = null;
             this.packages = null;
-            this.scan_timestamp = 0;
-            this.numpad_buffer  = [];
+            this.barcode_scanner = new module.BarcodeScanner();
+            this.picking_type_id = params.context.active_id || 0;
             
-            window.pickwidget = this;
-            
-            console.log('Action params:', params);
-            console.log('Session:',instance.session);
 
-            this.loaded =  this.load();
+            if(params.context.picking_id){
+                this.loaded =  this.load(params.context.picking_id);
+            }else{
+                this.loaded =  this.load();
+            }
         },
+
+        // load the picking data from the server. If picking_id is undefined, it will take the first picking
+        // belonging to the category
         load: function(picking_id){
             var self = this;
 
-            console.log('LOADING DATA FROM SERVER');
+       
+            function load_picking_list(type_id){
+                var pickings = new $.Deferred();
 
-            if(picking_id){
-                var picking = new instance.web.Model('stock.picking').call('read',[[picking_id], []]);
-            }else{ 
-                var picking  = new $.Deferred();
-                var pickings = new instance.web.Model('stock.picking')
-                    .call('get_picking_for_packing_ui')
+                new instance.web.Model('stock.picking')
+                    .call('get_picking_for_packing_ui',[{'default_picking_type_id':type_id}])
                     .then(function(picking_ids){
-                        console.log('Picking Ids',picking_ids);
                         if(!picking_ids || picking_ids.length === 0){
                             (new instance.web.Dialog(self,{
                                 title: _t('No Picking Available'),
@@ -142,32 +240,48 @@ function openerp_picking_widgets(instance){
                                 }]
                             }, _t('<p>We could not find a picking to display.</p>'))).open();
 
-                            picking.reject();
+                            pickings.reject();
                         }else{
                             self.pickings = picking_ids;
-                            new instance.web.Model('stock.picking').call('read',[[picking_ids[0]],[]])
-                                .then(function(pick){
-                                    picking.resolve(pick);
-                                });
+                            pickings.resolve(picking_ids);
                         }
                     });
+
+                return pickings;
+            }
+
+            // if we have a specified picking id, we load that one, and we load the picking of the same type as the active list
+            if( picking_id ){
+                var loaded_picking = new instance.web.Model('stock.picking')
+                    .call('read',[[picking_id], [], new instance.web.CompoundContext()])
+                    .then(function(picking){
+                        self.picking = picking[0];
+
+                        return load_picking_list(self.picking.picking_type_id[0]);
+                    });
+            }else{
+                // if we don't have a specified picking id, we load the pickings belong to the specified type, and then we take 
+                // the first one of that list as the active picking
+                var loaded_picking = new $.Deferred();
+                load_picking_list(self.picking_type_id)
+                    .then(function(){
+                        return new instance.web.Model('stock.picking').call('read',[self.pickings[0],[], new instance.web.CompoundContext()]);
+                    })
+                    .then(function(picking){
+                        self.picking = picking;
+                        loaded_picking.resolve();
+                    });
             }
 
-            var loaded = picking.then(function(picking){
-                    self.picking = picking instanceof Array ? picking[0] : picking;
-                    console.log('Picking:',self.picking);
-                    console.log('User Context:', instance.session.user_context);
-                    console.log('Context:', new instance.web.CompoundContext().eval());
+            var loaded = loaded_picking.then(function(){
 
                     return new instance.web.Model('stock.move').call('read',[self.picking.move_lines, [], new instance.web.CompoundContext()]);
                 }).then(function(movelines){
                     self.movelines = movelines;
-                    console.log('Move Lines:',movelines);
 
                     return new instance.web.Model('stock.pack.operation').call('read',[self.picking.pack_operation_ids, [], new instance.web.CompoundContext()]);
                 }).then(function(operations){
                     self.operations = operations;
-                    console.log('Operations:',self.operations);
                     
                     var package_ids = [];
 
@@ -177,26 +291,29 @@ function openerp_picking_widgets(instance){
                         }
                     }
 
-                    console.log('Package ids:',package_ids);
 
                     return new instance.web.Model('stock.quant.package').call('read',[package_ids, [], new instance.web.CompoundContext()]);
                 }).then(function(packages){
                     self.packages = packages;
-                    console.log('Packages:', self.packages);
                 });
 
             return loaded;
+
         },
         start: function(){
             var self = this;
             instance.webclient.set_content_full_screen(true);
-            this.connect_barcode_scanner_and_numpad();
+            this.connect_numpad();
+            this.barcode_scanner.connect(function(ean){
+                self.scan(ean);
+            });
 
             this.$('.js_pick_quit').click(function(){ self.quit(); });
             this.$('.js_pick_pack').click(function(){ self.pack(); });
             this.$('.js_pick_done').click(function(){ self.done(); });
             this.$('.js_pick_prev').click(function(){ self.picking_prev(); });
             this.$('.js_pick_next').click(function(){ self.picking_next(); });
+            this.$('.js_pick_menu').click(function(){ self.menu(); });
 
             $.when(this.loaded).done(function(){
                 self.picking_editor = new module.PickingEditorWidget(self);
@@ -231,7 +348,6 @@ function openerp_picking_widgets(instance){
             var self = this;
             return this.load(picking_id)
                 .then(function(){ 
-                    console.log('REFRESHING UI');
                     self.picking_editor.renderElement();
                     self.package_editor.renderElement();
                     self.package_selector.renderElement();
@@ -248,45 +364,47 @@ function openerp_picking_widgets(instance){
                     }
                 });
         },
+        menu: function(){
+            this.do_action({
+                type:   'ir.actions.client',
+                tag:    'stock.menu',
+                target: 'current',
+            },{
+                clear_breadcrumbs: true,
+            });
+
+        },
         scan: function(ean){
             var self = this;
-            console.log('Scan: ',ean);
             new instance.web.Model('stock.picking')
                 .call('get_barcode_and_return_todo_stuff', [self.picking.id, ean])
                 .then(function(){
                     return self.refresh_ui(self.picking.id);
                 });
-            this.scan_timestamp = new Date().getTime();
         },
         pack: function(){
             var self = this;
-            console.log('Pack');
             new instance.web.Model('stock.picking')
                 .call('action_pack',[[[self.picking.id]]])
                 .then(function(){
                     instance.session.user_context.current_package_id = false;
-                    console.log('Context Reset');
 
                     return self.refresh_ui(self.picking.id);
                 });
         },
         done: function(){
             var self = this;
-            console.log('Done');
             new instance.web.Model('stock.picking')
                 .call('action_done_from_packing_ui',[self.picking.id])
                 .then(function(new_picking_id){
-                    console.log('New picking id:',new_picking_id);
                     return self.refresh_ui(new_picking_id);
                 });
         },
         print_package: function(package_id){
             var self = this;
-            console.log('Print Package:',package_id);
             new instance.web.Model('stock.quant.package')
                 .call('action_print',[[package_id]])
                 .then(function(action){
-                    console.log('Print Package Repport Action:',action);
                     return self.do_action(action);
                 });
         },
@@ -312,7 +430,6 @@ function openerp_picking_widgets(instance){
         },
         copy_package: function(package_id){
             var self = this;
-            console.log('Copy Package:',package_id);
             new instance.web.Model('stock.quant.package')
                 .call('copy',[[package_id]])
                 .then(function(){
@@ -321,7 +438,6 @@ function openerp_picking_widgets(instance){
         },
         delete_package: function(package_id){
             var self = this;
-            console.log('Delete Package:',package_id);
             new instance.web.Model('stock.quant.package')
                 .call('unlink',[[package_id]])
                 .then(function(){
@@ -329,13 +445,11 @@ function openerp_picking_widgets(instance){
                 });
         },
         deselect_package: function(){
-            console.log('Deselect Package');
             instance.session.user_context.current_package_id = false;
             this.package_editor.renderElement();
             this.package_selector.renderElement();
         },
         select_package: function(package_id){
-            console.log('Select Package:',package_id);
             instance.session.user_context.current_package_id = package_id;
             this.package_editor.renderElement();
             this.package_selector.renderElement();
@@ -375,15 +489,12 @@ function openerp_picking_widgets(instance){
             var op = ops[ops.length-1];
 
             if(quantity === '++'){
-                console.log('Increase quantity!');
                 quantity = op.product_qty + 1;
             }else if(quantity === '--'){
-                console.log('Decrease quantity :(');
                 quantity = op.product_qty - 1;
             }
 
             if(typeof quantity === 'number' && quantity >= 0){
-                console.log('Set quantity: ',quantity);
                 new instance.web.Model('stock.pack.operation')
                     .call('write',[[op.id],{'product_qty': quantity }])
                     .then(function(){
@@ -392,27 +503,14 @@ function openerp_picking_widgets(instance){
             }
 
         },
-        connect_barcode_scanner_and_numpad: function(){
+        connect_numpad: function(){
             var self = this;
-            var numbers = [];
-            var timestamp = 0;
             var numpad = [];
             var numpad_timestamp;
-            // it is important to catch the keypress event and not keyup/keydown as keypress normalizes the input codes :) 
-            $('body').delegate('','keyup',function(e){ 
-                //console.log('Key:',e.keyCode);
-                if (e.keyCode >= 48 && e.keyCode < 58){
-                    if(timestamp + 30 < new Date().getTime()){
-                        numbers = [];
-                    }
-                    numbers.push(e.keyCode - 48);
-                    timestamp = new Date().getTime();
-                    if(numbers.length === 13){
-                        self.scan(numbers.join(''));
-                        numbers = [];
-                    }
-                }else{
-                    numbers = [];
+            
+            this.numpad_handler = function(e){ 
+                // upper row numbers are reserved for the barcode scanner
+                if( e.keyCode < 48 && e.keyCode >= 58){
                     if(numpad_timestamp + 1500 < new Date().getTime()){
                         numpad = [];
                     }
@@ -436,16 +534,59 @@ function openerp_picking_widgets(instance){
                     }
                     numpad_timestamp = new Date().getTime();
                 }
-            });
+            };
+            $('body').on('keypress', this.numpad_handler);
         },
-        disconnect_barcode_scanner_and_numpad: function(){
-            $('body').undelegate('', 'keyup')
+        disconnect_numpad: function(){
+            $('body').off('keypress', this.numpad_handler);
         },
         quit: function(){
-            console.log('Quit');
-            this.disconnect_barcode_scanner_and_numpad();
-            instance.webclient.set_content_full_screen(false);
+            this.destroy();
             window.location = '/'; // FIXME Ask niv how to do it correctly
         },
+        destroy: function(){
+            this._super();
+            this.disconnect_numpad();
+            this.barcode_scanner.disconnect();
+            instance.webclient.set_content_full_screen(false);
+        },
+    });
+    openerp.web.client_actions.add('stock.ui', 'instance.stock.PickingMainWidget');
+
+    module.BarcodeScanner = instance.web.Class.extend({
+        connect: function(callback){
+            var code = "";
+            var timeStamp = 0;
+            var timeout = null;
+
+            this.handler = function(e){
+                if(e.which === 13){ //ignore returns
+                    return;
+                }
+
+                if(timeStamp + 50 < new Date().getTime()){
+                    code = "";
+                }
+
+                timeStamp = new Date().getTime();
+                clearTimeout(timeout);
+
+                code += String.fromCharCode(e.which);
+
+                timeout = setTimeout(function(){
+                    if(code.length >= 3){
+                        callback(code);
+                    }
+                    code = "";
+                },100);
+            };
+
+            $('body').on('keypress', this.handler);
+
+        },
+        disconnect: function(){
+            $('body').off('keypress', this.handler);
+        },
     });
+
 }
index 6b15dbb..71c0b26 100644 (file)
         </div>
     </t>
 
-    <t t-name='PickingMenuWidget'>
+    <t t-name="PickingMenuWidget">
+        <div class='oe_pick_widget'>
+            <table class='oe_pick_layout'>
+                <tr class='oe_pick_header_row'>
+                    <td class='oe_pick_header'>
+
+                        <div class='oe_pick_right_toolbar'>
+                            <div class='oe_pick_button js_pick_quit'> Quit </div>
+                        </div>
+
+                    </td>
+                </tr>
+                <tr class='oe_pick_body_row'>
+                    <td class='oe_pick_body_cont'> 
+                        <div class='oe_pick_body'>
+                            <div class='oe_pick_app'>
+                                <h3 class='oe_pick_app_title'>Pickings</h3>
+
+                                <t t-foreach="widget.picking_types" t-as="type">
+                                    <h4 class='oe_pick_app_subtitle'> <t t-esc="type.name" /> </h4>
+                                    <t t-if="widget.pickings_by_type[type.id].length > 0">
+                                        <span class='oe_pick_button oe_medium js_pick_last' t-att-data-id="type.id">Last Picking</span>
+                                        <span class='oe_pick_button oe_medium js_pick_scan' t-att-data-id="type.id">Scan</span>
+                                    </t>
+                                    <t t-if="widget.pickings_by_type[type.id].length === 0">
+                                        <p class='oe_pick_app_info'>Nothing to process.</p>
+                                    </t>
+                                </t>
+                            </div>
+                        </div>
+                    </td>
+                </tr>
+            </table>
+        </div>
     </t>
 
     <t t-name="PickingMainWidget">
                         </div>
                         
                         <div class='oe_pick_toolbar'>
-                            <div class='oe_pick_button oe_small oe_left js_pick_prev'>&lt;</div>
+                            <div class='oe_pick_button oe_small oe_disabled oe_left js_pick_prev'>&lt;</div>
                             <div class='oe_pick_button js_pick_pack'> Pack </div>
-                            <!-- <div class='oe_pick_button js_pick_reset'> Reset </div> -->
                             <div class='oe_pick_button js_pick_done'> Done </div>
-                            <div class='oe_pick_button oe_small oe_right js_pick_next'>&gt;</div>
+                            <div class='oe_pick_button oe_small oe_disabled oe_right js_pick_next'>&gt;</div>
                         </div>
 
-
                     </td>
                 </tr>
                 <tr class='oe_pick_body_row'>
index 1fe5dba..91e8708 100644 (file)
@@ -1006,8 +1006,8 @@ class stock_picking(osv.osv):
 
     # Methods for the barcode UI
 
-    def get_picking_for_packing_ui(self, cr, uid, context=None):
-        return self.search(cr, uid, [('state', 'in', ('confirmed', 'assigned'))], context=context)
+    def get_picking_for_packing_ui(self, cr, uid, context={}):
+        return self.search(cr, uid, [('state', 'in', ('confirmed', 'assigned')),('picking_type_id','=', context.get('default_picking_type_id'))], context=context)
 
     def action_done_from_packing_ui(self, cr, uid, picking_id, only_split_lines=False, context=None):
         self.do_partial(cr, uid, picking_id, only_split_lines, context=context)