[FIX] barcode_interface: when loading widget, picking_type_id was sometime not correc...
[odoo/odoo.git] / addons / stock / static / src / js / widgets.js
1 function openerp_picking_widgets(instance){
2
3     var module = instance.stock;
4     var _t     = instance.web._t;
5     var QWeb   = instance.web.qweb;
6
7     // This widget makes sure that the scaling is disabled on mobile devices.
8     // Widgets that want to display fullscreen on mobile phone need to extend this
9     // widget.
10
11     module.MobileWidget = instance.web.Widget.extend({
12         start: function(){
13             if(!$('#oe-mobilewidget-viewport').length){
14                 $('head').append('<meta id="oe-mobilewidget-viewport" name="viewport" content="initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">');
15             }
16             return this._super();
17         },
18         destroy: function(){
19             $('#oe-mobilewidget-viewport').remove();
20             return this._super();
21         },
22     });
23
24     module.PickingEditorWidget = instance.web.Widget.extend({
25         template: 'PickingEditorWidget',
26         init: function(parent,options){
27             this._super(parent,options);
28             var self = this;
29             this.rows = [];
30             this.search_filter = "";
31             jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
32                 return function( elem ) {
33                     return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
34                 };
35             });
36         },
37         get_header: function(){
38             return this.getParent().get_header();
39         },
40         get_location: function(){
41             var model = this.getParent();
42             var locations = [];
43             var self = this;
44             _.each(model.locations, function(loc){
45                 locations.push({name: loc.complete_name, id: loc.id,});
46             });
47             return locations;
48         },
49         get_logisticunit: function(){
50             var model = this.getParent();
51             var ul = [];
52             var self = this;
53             _.each(model.uls, function(ulog){
54                 ul.push({name: ulog.name, id: ulog.id,});
55             });
56             return ul;
57         },
58         get_rows: function(){
59             var model = this.getParent();
60             this.rows = [];
61             var self = this;
62             var pack_created = [];
63             _.each( model.packoplines, function(packopline){
64                     var pack = undefined;
65                     var color = "";
66                     if (packopline.product_id[1] !== undefined){ pack = packopline.package_id[1];}
67                     if (packopline.product_qty == packopline.qty_done){ color = "success "; }
68                     if (packopline.product_qty < packopline.qty_done){ color = "danger "; }
69                     //also check that we don't have a line already existing for that package
70                     if (packopline.result_package_id[1] !== undefined && $.inArray(packopline.result_package_id[0], pack_created) === -1){
71                         var myPackage = $.grep(model.packages, function(e){ return e.id == packopline.result_package_id[0]; })[0];
72                         self.rows.push({
73                             cols: { product: packopline.result_package_id[1],
74                                     qty: '',
75                                     rem: '',
76                                     uom: undefined,
77                                     lot: undefined,
78                                     pack: undefined,
79                                     container: packopline.result_package_id[1],
80                                     container_id: undefined,
81                                     loc: packopline.location_id[1],
82                                     dest: packopline.location_dest_id[1],
83                                     id: packopline.result_package_id[0],
84                                     product_id: undefined,
85                                     can_scan: false,
86                                     head_container: true,
87                                     processed: packopline.processed,
88                                     package_id: myPackage.id,
89                                     ul_id: myPackage.ul_id[0],
90                             },
91                             classes: ('success container_head ') + (packopline.processed === "true" ? 'processed hidden ':''),
92                         });
93                         pack_created.push(packopline.result_package_id[0]);
94                     }
95                     self.rows.push({
96                         cols: { product: packopline.product_id[1] || packopline.package_id[1],
97                                 qty: packopline.product_qty,
98                                 rem: packopline.qty_done,
99                                 uom: packopline.product_uom_id[1],
100                                 lot: packopline.lot_id[1],
101                                 pack: pack,
102                                 container: packopline.result_package_id[1],
103                                 container_id: packopline.result_package_id[0],
104                                 loc: packopline.location_id[1],
105                                 dest: packopline.location_dest_id[1],
106                                 id: packopline.id,
107                                 product_id: packopline.product_id[0],
108                                 can_scan: packopline.result_package_id[1] === undefined ? true : false,
109                                 head_container: false,
110                                 processed: packopline.processed,
111                                 package_id: undefined,
112                                 ul_id: -1,
113                         },
114                         classes: color + (packopline.result_package_id[1] !== undefined ? 'in_container_hidden ' : '') + (packopline.processed === "true" ? 'processed hidden ':''),
115                     });
116             });
117             //sort element by things to do, then things done, then grouped by packages
118             group_by_container = _.groupBy(self.rows, function(row){
119                 return row.cols.container;
120             });
121             var sorted_row = [];
122             if (group_by_container.undefined !== undefined){
123                 group_by_container.undefined.sort(function(a,b){return (b.classes === '') - (a.classes === '');});
124                 $.each(group_by_container.undefined, function(key, value){
125                     sorted_row.push(value);
126                 });
127             }
128
129             $.each(group_by_container, function(key, value){
130                 if (key !== 'undefined'){
131                     $.each(value, function(k,v){
132                         sorted_row.push(v);
133                     });
134                 }
135             });
136
137             return sorted_row;
138         },
139         renderElement: function(){
140             var self = this;
141             this._super();
142             this.check_content_screen();
143             this.$('.js_pick_done').click(function(){ self.getParent().done(); });
144             this.$('.js_pick_print').click(function(){ self.getParent().print_picking(); });
145             this.$('.oe_pick_app_header').text(self.get_header());
146             this.$('.oe_searchbox').keyup(function(event){
147                 self.on_searchbox($(this).val());
148             });
149             this.$('.js_putinpack').click(function(){ self.getParent().pack(); });
150             this.$('.js_drop_down').click(function(){ self.getParent().drop_down();});
151             this.$('.js_clear_search').click(function(){
152                 self.on_searchbox('');
153                 self.$('.oe_searchbox').val('');
154             });
155             this.$('.oe_searchbox').focus(function(){
156                 self.getParent().barcode_scanner.disconnect();
157             });
158             this.$('.oe_searchbox').blur(function(){
159                 self.getParent().barcode_scanner.connect(function(ean){
160                     self.get_Parent().scan(ean);
161                 });
162             })
163             this.$('#js_select').change(function(){
164                 var selection = self.$('#js_select option:selected').attr('value');
165                 if (selection === "ToDo"){
166                     self.getParent().$('.js_pick_pack').removeClass('hidden')
167                     self.getParent().$('.js_drop_down').removeClass('hidden')
168                     self.$('.js_pack_op_line.processed').addClass('hidden')
169                     self.$('.js_pack_op_line:not(.processed)').removeClass('hidden')
170                 }
171                 else{
172                     self.getParent().$('.js_pick_pack').addClass('hidden')
173                     self.getParent().$('.js_drop_down').addClass('hidden')
174                     self.$('.js_pack_op_line.processed').removeClass('hidden')
175                     self.$('.js_pack_op_line:not(.processed)').addClass('hidden')
176                 }
177                 self.on_searchbox(self.search_filter);
178             });
179             this.$('.js_plus').click(function(){
180                 var id = $(this).data('product-id');
181                 var op_id = $(this).parents("[data-id]:first").data('id');
182                 self.getParent().scan_product_id(id,true,op_id);
183             });
184             this.$('.js_minus').click(function(){
185                 var id = $(this).data('product-id');
186                 var op_id = $(this).parents("[data-id]:first").data('id');
187                 self.getParent().scan_product_id(id,false,op_id);
188             });
189             this.$('.js_unfold').click(function(){
190                 var op_id = $(this).parent().data('id');
191                 var line = $(this).parent();
192                 //select all js_pack_op_line with class in_container_hidden and correct container-id
193                 select = self.$('.js_pack_op_line.in_container_hidden[data-container-id='+op_id+']')
194                 if (select.length > 0){
195                     //we unfold
196                     line.addClass('warning');
197                     select.removeClass('in_container_hidden');
198                     select.addClass('in_container');
199                 }
200                 else{
201                     //we fold
202                     line.removeClass('warning');
203                     select = self.$('.js_pack_op_line.in_container[data-container-id='+op_id+']')
204                     select.removeClass('in_container');
205                     select.addClass('in_container_hidden');
206                 }
207             });
208             this.$('.js_create_lot').click(function(){
209                 var op_id = $(this).parents("[data-id]:first").data('id');
210                 var lot_name = false;
211                 self.$('.js_lot_scan').val('');
212                 var $lot_modal = self.$el.siblings('#js_LotChooseModal');
213                 //disconnect scanner to prevent scanning a product in the back while dialog is open
214                 self.getParent().barcode_scanner.disconnect();
215                 $lot_modal.modal()
216                 //focus input
217                 $lot_modal.on('shown.bs.modal', function(){
218                     self.$('.js_lot_scan').focus();
219                 })
220                 //reactivate scanner when dialog close
221                 $lot_modal.on('hidden.bs.modal', function(){
222                     self.getParent().barcode_scanner.connect(function(ean){
223                         self.getParent().scan(ean);
224                     });
225                 })
226                 self.$('.js_lot_scan').focus();
227                 //button action
228                 self.$('.js_validate_lot').click(function(){
229                     //get content of input
230                     var name = self.$('.js_lot_scan').val();
231                     if (name.length !== 0){
232                         lot_name = name;
233                     }
234                     $lot_modal.modal('hide');
235                     //we need this here since it is not sure the hide event
236                     //will be catch because we refresh the view after the create_lot call
237                     self.getParent().barcode_scanner.connect(function(ean){
238                         self.getParent().scan(ean);
239                     });
240                     self.getParent().create_lot(op_id, lot_name);
241                 });
242             });
243             this.$('.js_delete_pack').click(function(){
244                 var pack_id = $(this).parents("[data-id]:first").data('id');
245                 self.getParent().delete_package_op(pack_id);
246             });
247             this.$('.js_print_pack').click(function(){
248                 var pack_id = $(this).parents("[data-id]:first").data('id');
249                 // $(this).parents("[data-id]:first").data('id')
250                 self.getParent().print_package(pack_id);
251             });
252             this.$('.js_submit_value').submit(function(event){
253                 var op_id = $(this).parents("[data-id]:first").data('id');
254                 var value = parseFloat($("input", this).val());
255                 if (value>=0){
256                     self.getParent().set_operation_quantity(value, op_id);
257                 }
258                 $("input", this).val("");
259                 return false;
260             });
261             this.$('.js_qty').focus(function(){
262                 self.getParent().barcode_scanner.disconnect();
263             });
264             this.$('.js_qty').blur(function(){
265                 var op_id = $(this).parents("[data-id]:first").data('id');
266                 var value = parseFloat($(this).val());
267                 if (value>=0){
268                     self.getParent().set_operation_quantity(value, op_id);
269                 }
270                 
271                 self.getParent().barcode_scanner.connect(function(ean){
272                     self.getParent().scan(ean);
273                 });
274             });
275             this.$('.js_change_src').click(function(){
276                 var op_id = $(this).parents("[data-id]:first").data('id');//data('op_id');
277                 self.$('#js_loc_select').addClass('source');
278                 self.$('#js_loc_select').data('op-id',op_id);
279                 self.$el.siblings('#js_LocationChooseModal').modal();
280             });
281             this.$('.js_change_dst').click(function(){
282                 var op_id = $(this).parents("[data-id]:first").data('id');
283                 self.$('#js_loc_select').data('op-id',op_id);
284                 self.$el.siblings('#js_LocationChooseModal').modal();
285             });
286             this.$('.js_pack_change_dst').click(function(){
287                 var op_id = $(this).parents("[data-id]:first").data('id');
288                 self.$('#js_loc_select').addClass('pack');
289                 self.$('#js_loc_select').data('op-id',op_id);
290                 self.$el.siblings('#js_LocationChooseModal').modal();
291             });
292             this.$('.js_validate_location').click(function(){
293                 //get current selection
294                 var select_dom_element = self.$('#js_loc_select');
295                 var loc_id = self.$('#js_loc_select option:selected').data('loc-id');
296                 var src_dst = false;
297                 var op_id = select_dom_element.data('op-id');
298                 if (select_dom_element.hasClass('pack')){
299                     select_dom_element.removeClass('source');
300                     op_ids = [];
301                     self.$('.js_pack_op_line[data-container-id='+op_id+']').each(function(){
302                         op_ids.push($(this).data('id'));
303                     });
304                     op_id = op_ids;
305                 }
306                 else if (select_dom_element.hasClass('source')){
307                     src_dst = true;
308                     select_dom_element.removeClass('source');
309                 }
310                 if (loc_id === false){
311                     //close window
312                     self.$el.siblings('#js_LocationChooseModal').modal('hide');
313                 }
314                 else{
315                     self.$el.siblings('#js_LocationChooseModal').modal('hide');
316                     self.getParent().change_location(op_id, parseInt(loc_id), src_dst);
317
318                 }
319             });
320             this.$('.js_pack_configure').click(function(){
321                 var pack_id = $(this).parents(".js_pack_op_line:first").data('package-id');
322                 var ul_id = $(this).parents(".js_pack_op_line:first").data('ulid');
323                 self.$('#js_packconf_select').val(ul_id);
324                 self.$('#js_packconf_select').data('pack-id',pack_id);
325                 self.$el.siblings('#js_PackConfModal').modal();
326             });
327             this.$('.js_validate_pack').click(function(){
328                 //get current selection
329                 var select_dom_element = self.$('#js_packconf_select');
330                 var ul_id = self.$('#js_packconf_select option:selected').data('ul-id');
331                 var pack_id = select_dom_element.data('pack-id');
332                 self.$el.siblings('#js_PackConfModal').modal('hide');
333                 if (pack_id){
334                     self.getParent().set_package_pack(pack_id, ul_id);
335                     $('.container_head[data-package-id="'+pack_id+'"]').data('ulid', ul_id);
336                 }
337             });
338             
339             //remove navigtion bar from default openerp GUI
340             $('td.navbar').html('<div></div>');
341         },
342         on_searchbox: function(query){
343             //hide line that has no location matching the query and highlight location that match the query
344             this.search_filter = query;
345             var processed = ".processed";
346             if (this.$('#js_select option:selected').attr('value') == "ToDo"){
347                 processed = ":not(.processed)";
348             }
349             if (query !== '') {
350                 this.$('.js_loc:not(.js_loc:Contains('+query+'))').removeClass('info');
351                 this.$('.js_loc:Contains('+query+')').addClass('info');
352                 this.$('.js_pack_op_line'+processed+':not(.js_pack_op_line:has(.js_loc:Contains('+query+')))').addClass('hidden');
353                 this.$('.js_pack_op_line'+processed+':has(.js_loc:Contains('+query+'))').removeClass('hidden');
354             }
355             //if no query specified, then show everything
356             if (query === '') {
357                 this.$('.js_loc').removeClass('info');
358                 this.$('.js_pack_op_line'+processed+'.hidden').removeClass('hidden');
359             }
360             this.check_content_screen();
361         },
362         check_content_screen: function(){
363             //get all visible element and if none has positive qty, disable put in pack and process button
364             var self = this;
365             var processed = this.$('.js_pack_op_line.processed');
366             var qties = this.$('.js_pack_op_line:not(.processed):not(.hidden) .js_qty').map(function(){return $(this).val()});
367             var container = this.$('.js_pack_op_line.container_head:not(.processed):not(.hidden)')
368             var disabled = true;
369             $.each(qties,function(index, value){
370                 if (parseInt(value)>0){
371                     disabled = false;
372                 }
373             });
374
375             if (disabled){
376                 if (container.length===0){
377                     self.$('.js_drop_down').addClass('disabled');
378                 }
379                 else {
380                     self.$('.js_drop_down').removeClass('disabled');
381                 }
382                 self.$('.js_pick_pack').addClass('disabled');
383                 if (processed.length === 0){
384                     self.$('.js_pick_done').addClass('disabled');
385                 }
386                 else {
387                     self.$('.js_pick_done').removeClass('disabled');
388                 }
389             }
390             else{
391                 self.$('.js_drop_down').removeClass('disabled');
392                 self.$('.js_pick_pack').removeClass('disabled');
393                 self.$('.js_pick_done').removeClass('disabled');
394             }
395         },
396         get_current_op_selection: function(ignore_container){
397             //get ids of visible on the screen
398             pack_op_ids = []
399             this.$('.js_pack_op_line:not(.processed):not(.js_pack_op_line.hidden):not(.container_head)').each(function(){
400                 cur_id = $(this).data('id');
401                 pack_op_ids.push(parseInt(cur_id));
402             });
403             //get list of element in this.rows where rem > 0 and container is empty is specified
404             list = []
405             _.each(this.rows, function(row){
406                 if (row.cols.rem > 0 && (ignore_container || row.cols.container === undefined)){
407                     list.push(row.cols.id);
408                 }
409             });
410             //return only those visible with rem qty > 0 and container empty
411             return _.intersection(pack_op_ids, list);
412         },
413         remove_blink: function(){
414             this.$('.js_pack_op_line.blink_me').removeClass('blink_me');
415         },
416         blink: function(op_id){
417             this.$('.js_pack_op_line[data-id="'+op_id+'"]').addClass('blink_me');
418         },
419         check_done: function(){
420             var model = this.getParent();
421             var self = this;
422             var done = true;
423             _.each( model.packoplines, function(packopline){
424                 if (packopline.processed === "false"){
425                     done = false;
426                     return done;
427                 }
428             });
429             return done;
430         },
431         get_visible_ids: function(){
432             var self = this;
433             var visible_op_ids = []
434             var op_ids = this.$('.js_pack_op_line:not(.processed):not(.hidden):not(.container_head):not(.in_container):not(.in_container_hidden)').map(function(){
435                 return $(this).data('id');
436             });
437             $.each(op_ids, function(key, op_id){
438                 visible_op_ids.push(parseInt(op_id));
439             });
440             return visible_op_ids;
441         },
442     });
443
444     module.PickingMenuWidget = module.MobileWidget.extend({
445         template: 'PickingMenuWidget',
446         init: function(parent, params){
447             this._super(parent,params);
448             var self = this;
449             $(window).bind('hashchange', function(){
450                 var states = $.bbq.getState();
451                 if (states.action === "stock.ui"){
452                     self.do_action({
453                         type:   'ir.actions.client',
454                         tag:    'stock.ui',
455                         target: 'current',
456                     },{
457                         clear_breadcrumbs: true,
458                     });
459                 }
460             });
461             this.picking_types = [];
462             this.loaded = this.load();
463             this.scanning_type = 0;
464             this.barcode_scanner = new module.BarcodeScanner();
465             this.pickings_by_type = {};
466             this.pickings_by_id = {};
467             this.picking_search_string = "";
468         },
469         load: function(){
470             var self = this;
471             return new instance.web.Model('stock.picking.type').get_func('search_read')([],[])
472                 .then(function(types){
473                     self.picking_types = types;
474                     for(var i = 0; i < types.length; i++){
475                         self.pickings_by_type[types[i].id] = [];
476                     }
477                     self.pickings_by_type[0] = [];
478
479                     return new instance.web.Model('stock.picking').call('search_read',[ [['state','in', ['assigned', 'partially_available']]], [] ], {context: new instance.web.CompoundContext()});
480
481                 }).then(function(pickings){
482                     self.pickings = pickings;
483                     for(var i = 0; i < pickings.length; i++){
484                         var picking = pickings[i];
485                         self.pickings_by_type[picking.picking_type_id[0]].push(picking);
486                         self.pickings_by_id[picking.id] = picking;
487                         self.picking_search_string += '' + picking.id + ':' + picking.name.toUpperCase() + '\n'
488                     }
489
490                 });
491         },
492         renderElement: function(){
493             this._super();
494             var self = this;
495             this.$('.js_pick_quit').click(function(){ self.quit(); });
496             this.$('.js_pick_scan').click(function(){ self.scan_picking($(this).data('id')); });
497             this.$('.js_pick_last').click(function(){ self.goto_last_picking_of_type($(this).data('id')); });
498             this.$('.oe_searchbox').keyup(function(event){
499                 self.on_searchbox($(this).val());
500             });
501             //remove navigtion bar from default openerp GUI
502             $('td.navbar').html('<div></div>');
503         },
504         start: function(){
505             this._super();
506             var self = this;
507             instance.webclient.set_content_full_screen(true);
508             this.barcode_scanner.connect(function(barcode){
509                 self.on_scan(barcode);
510             });
511             this.loaded.then(function(){
512                 self.renderElement();
513             });
514         },
515         goto_picking: function(picking_id){
516             $.bbq.pushState('#action=stock.ui&picking_id='+picking_id);
517             $(window).trigger('hashchange');
518         },
519         goto_last_picking_of_type: function(type_id){
520             $.bbq.pushState('#action=stock.ui&picking_type_id='+type_id);
521             $(window).trigger('hashchange');
522         },
523         search_picking: function(barcode){
524             try {
525                 var re = RegExp("([0-9]+):.*?"+barcode.toUpperCase(),"gi");
526             }
527             catch(e) {
528                 //avoid crash if a not supported char is given (like '\' or ')')
529                 return [];
530             }
531
532             var results = [];
533             for(var i = 0; i < 100; i++){
534                 r = re.exec(this.picking_search_string);
535                 if(r){
536                     var picking = this.pickings_by_id[Number(r[1])];
537                     if(picking){
538                         results.push(picking);
539                     }
540                 }else{
541                     break;
542                 }
543             }
544             return results;
545         },
546         on_scan: function(barcode){
547             var self = this;
548             for(var i = 0, len = this.pickings.length; i < len; i++){
549                 var picking = this.pickings[i];
550                 if(picking.name.toUpperCase() === $.trim(barcode.toUpperCase())){
551                     this.goto_picking(picking.id);
552                     break;
553                 }
554             }
555             this.$('.js_picking_not_found').removeClass('hidden');
556
557             clearTimeout(this.picking_not_found_timeout);
558             this.picking_not_found_timeout = setTimeout(function(){
559                 self.$('.js_picking_not_found').addClass('hidden');
560             },2000);
561
562         },
563         on_searchbox: function(query){
564             var self = this;
565
566             clearTimeout(this.searchbox_timeout);
567             this.searchbox_timout = setTimeout(function(){
568                 if(query){
569                     self.$('.js_picking_not_found').addClass('hidden');
570                     self.$('.js_picking_categories').addClass('hidden');
571                     self.$('.js_picking_search_results').html(
572                         QWeb.render('PickingSearchResults',{results:self.search_picking(query)})
573                     );
574                     self.$('.js_picking_search_results .oe_picking').click(function(){
575                         self.goto_picking($(this).data('id'));
576                     });
577                     self.$('.js_picking_search_results').removeClass('hidden');
578                 }else{
579                     self.$('.js_title_label').removeClass('hidden');
580                     self.$('.js_picking_categories').removeClass('hidden');
581                     self.$('.js_picking_search_results').addClass('hidden');
582                 }
583             },100);
584         },
585         quit: function(){
586             return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_picking_type_form']], ['res_id']).pipe(function(res) {
587                     window.location = '/web#action=' + res[0]['res_id'];
588                 });
589         },
590         destroy: function(){
591             this._super();
592             this.barcode_scanner.disconnect();
593             instance.webclient.set_content_full_screen(false);
594         },
595     });
596     openerp.web.client_actions.add('stock.menu', 'instance.stock.PickingMenuWidget');
597
598     module.PickingMainWidget = module.MobileWidget.extend({
599         template: 'PickingMainWidget',
600         init: function(parent,params){
601             this._super(parent,params);
602             var self = this;
603             $(window).bind('hashchange', function(){
604                 var states = $.bbq.getState();
605                 if (states.action === "stock.menu"){
606                     self.do_action({
607                         type:   'ir.actions.client',
608                         tag:    'stock.menu',
609                         target: 'current',
610                     },{
611                         clear_breadcrumbs: true,
612                     });
613                 }
614             });
615             init_hash = $.bbq.getState();
616             this.picking_type_id = init_hash.picking_type_id ? init_hash.picking_type_id:0;
617             this.picking_id = init_hash.picking_id ? init_hash.picking_id:undefined;
618             this.picking = null;
619             this.pickings = [];
620             this.packoplines = null;
621             this.selected_operation = { id: null, picking_id: null};
622             this.packages = null;
623             this.barcode_scanner = new module.BarcodeScanner();
624             this.locations = [];
625             this.uls = [];
626             if(this.picking_id){
627                 this.loaded =  this.load(this.picking_id);
628             }else{
629                 this.loaded =  this.load();
630             }
631
632         },
633
634         // load the picking data from the server. If picking_id is undefined, it will take the first picking
635         // belonging to the category
636         load: function(picking_id){
637             var self = this;
638
639
640             function load_picking_list(type_id){
641                 var pickings = new $.Deferred();
642                 new instance.web.Model('stock.picking')
643                     .call('get_next_picking_for_ui',[{'default_picking_type_id':parseInt(type_id)}])
644                     .then(function(picking_ids){
645                         if(!picking_ids || picking_ids.length === 0){
646                             (new instance.web.Dialog(self,{
647                                 title: _t('No Picking Available'),
648                                 buttons: [{
649                                     text:_t('Ok'),
650                                     click: function(){
651                                         self.menu();
652                                     }
653                                 }]
654                             }, _t('<p>We could not find a picking to display.</p>'))).open();
655
656                             pickings.reject();
657                         }else{
658                             self.pickings = picking_ids;
659                             pickings.resolve(picking_ids);
660                         }
661                     });
662
663                 return pickings;
664             }
665
666             // if we have a specified picking id, we load that one, and we load the picking of the same type as the active list
667             if( picking_id ){
668                 var loaded_picking = new instance.web.Model('stock.picking')
669                     .call('read',[[parseInt(picking_id)], [], new instance.web.CompoundContext()])
670                     .then(function(picking){
671                         self.picking = picking[0];
672                         self.picking_type_id = picking[0].picking_type_id[0];
673                         return load_picking_list(self.picking.picking_type_id[0]);
674                     });
675             }else{
676                 // if we don't have a specified picking id, we load the pickings belong to the specified type, and then we take
677                 // the first one of that list as the active picking
678                 var loaded_picking = new $.Deferred();
679                 load_picking_list(self.picking_type_id)
680                     .then(function(){
681                         return new instance.web.Model('stock.picking').call('read',[self.pickings[0],[], new instance.web.CompoundContext()]);
682                     })
683                     .then(function(picking){
684                         self.picking = picking;
685                         self.picking_type_id = picking.picking_type_id[0];
686                         loaded_picking.resolve();
687                     });
688             }
689
690             return loaded_picking.then(function(){
691                     return new instance.web.Model('stock.location').call('search',[[['usage','=','internal']]]).then(function(locations_ids){
692                         return new instance.web.Model('stock.location').call('read',[locations_ids, []]).then(function(locations){
693                             self.locations = locations;
694                         });
695                     });
696                 }).then(function(){
697                     return new instance.web.Model('stock.picking').call('check_group_pack').then(function(result){
698                         return self.show_pack = result;
699                     });
700                 }).then(function(){
701                     return new instance.web.Model('stock.picking').call('check_group_lot').then(function(result){
702                         return self.show_lot = result;
703                     });
704                 }).then(function(){
705                     if (self.picking.pack_operation_exist === false){
706                         self.picking.recompute_pack_op = false;
707                         return new instance.web.Model('stock.picking').call('do_prepare_partial',[[self.picking.id]]);
708                     }
709                 }).then(function(){
710                         return new instance.web.Model('stock.pack.operation').call('search',[[['picking_id','=',self.picking.id]]])
711                 }).then(function(pack_op_ids){
712                         return new instance.web.Model('stock.pack.operation').call('read',[pack_op_ids, [], new instance.web.CompoundContext()])
713                 }).then(function(operations){
714                     self.packoplines = operations;
715                     var package_ids = [];
716
717                     for(var i = 0; i < operations.length; i++){
718                         if(!_.contains(package_ids,operations[i].result_package_id[0])){
719                             if (operations[i].result_package_id[0]){
720                                 package_ids.push(operations[i].result_package_id[0]);
721                             }
722                         }
723                     }
724                     return new instance.web.Model('stock.quant.package').call('read',[package_ids, [], new instance.web.CompoundContext()])
725                 }).then(function(packages){
726                     self.packages = packages;
727                 }).then(function(){
728                         return new instance.web.Model('product.ul').call('search',[[]])
729                 }).then(function(uls_ids){
730                         return new instance.web.Model('product.ul').call('read',[uls_ids, []])
731                 }).then(function(uls){
732                     self.uls = uls;
733                 });
734         },
735         start: function(){
736             this._super();
737             var self = this;
738             instance.webclient.set_content_full_screen(true);
739             this.barcode_scanner.connect(function(ean){
740                 self.scan(ean);
741             });
742
743             this.$('.js_pick_quit').click(function(){ self.quit(); });
744             this.$('.js_pick_prev').click(function(){ self.picking_prev(); });
745             this.$('.js_pick_next').click(function(){ self.picking_next(); });
746             this.$('.js_pick_menu').click(function(){ self.menu(); });
747             this.$('.js_reload_op').click(function(){ self.reload_pack_operation();});
748
749             $.when(this.loaded).done(function(){
750                 self.picking_editor = new module.PickingEditorWidget(self);
751                 self.picking_editor.replace(self.$('.oe_placeholder_picking_editor'));
752
753                 if( self.picking.id === self.pickings[0]){
754                     self.$('.js_pick_prev').addClass('disabled');
755                 }else{
756                     self.$('.js_pick_prev').removeClass('disabled');
757                 }
758
759                 if( self.picking.id === self.pickings[self.pickings.length-1] ){
760                     self.$('.js_pick_next').addClass('disabled');
761                 }else{
762                     self.$('.js_pick_next').removeClass('disabled');
763                 }
764                 if (self.picking.recompute_pack_op){
765                     self.$('.oe_reload_op').removeClass('hidden');
766                 }
767                 else {
768                     self.$('.oe_reload_op').addClass('hidden');
769                 }
770                 if (!self.show_pack){
771                     self.$('.js_pick_pack').addClass('hidden');
772                 }
773                 if (!self.show_lot){
774                     self.$('.js_create_lot').addClass('hidden');
775                 }
776
777             }).fail(function(error) {console.log(error);});
778
779         },
780         on_searchbox: function(query){
781             var self = this;
782             self.picking_editor.on_searchbox(query.toUpperCase());
783         },
784         // reloads the data from the provided picking and refresh the ui.
785         // (if no picking_id is provided, gets the first picking in the db)
786         refresh_ui: function(picking_id){
787             var self = this;
788             var remove_search_filter = "";
789             if (self.picking.id === picking_id){
790                 remove_search_filter = self.$('.oe_searchbox').val();
791             }
792             return this.load(picking_id)
793                 .then(function(){
794                     self.picking_editor.remove_blink();
795                     self.picking_editor.renderElement();
796                     if (!self.show_pack){
797                         self.$('.js_pick_pack').addClass('hidden');
798                     }
799                     if (!self.show_lot){
800                         self.$('.js_create_lot').addClass('hidden');
801                     }
802                     if (self.picking.recompute_pack_op){
803                         self.$('.oe_reload_op').removeClass('hidden');
804                     }
805                     else {
806                         self.$('.oe_reload_op').addClass('hidden');
807                     }
808
809                     if( self.picking.id === self.pickings[0]){
810                         self.$('.js_pick_prev').addClass('disabled');
811                     }else{
812                         self.$('.js_pick_prev').removeClass('disabled');
813                     }
814
815                     if( self.picking.id === self.pickings[self.pickings.length-1] ){
816                         self.$('.js_pick_next').addClass('disabled');
817                     }else{
818                         self.$('.js_pick_next').removeClass('disabled');
819                     }
820                     if (remove_search_filter === ""){
821                         self.$('.oe_searchbox').val('');
822                         self.on_searchbox('');
823                     }
824                     else{
825                         self.$('.oe_searchbox').val(remove_search_filter);
826                         self.on_searchbox(remove_search_filter);
827                     }
828                 });
829         },
830         get_header: function(){
831             if(this.picking){
832                 return this.picking.name;
833             }else{
834                 return '';
835             }
836         },
837         menu: function(){
838             $.bbq.pushState('#action=stock.menu');
839             $(window).trigger('hashchange');
840         },
841         scan: function(ean){ //scans a barcode, sends it to the server, then reload the ui
842             var self = this;
843             var product_visible_ids = this.picking_editor.get_visible_ids();
844             return new instance.web.Model('stock.picking')
845                 .call('process_barcode_from_ui', [self.picking.id, ean, product_visible_ids])
846                 .then(function(result){
847                     if (result.filter_loc !== false){
848                         //check if we have receive a location as answer
849                         if (result.filter_loc !== undefined){
850                             var modal_loc_hidden = self.$('#js_LocationChooseModal').attr('aria-hidden');
851                             if (modal_loc_hidden === "false"){
852                                 var line = self.$('#js_LocationChooseModal .js_loc_option[data-loc-id='+result.filter_loc_id+']').attr('selected','selected');
853                             }
854                             else{
855                                 self.$('.oe_searchbox').val(result.filter_loc);
856                                 self.on_searchbox(result.filter_loc);
857                             }
858                         }
859                     }
860                     if (result.operation_id !== false){
861                         self.refresh_ui(self.picking.id).then(function(){
862                             return self.picking_editor.blink(result.operation_id);
863                         });
864                     }
865                 });
866         },
867         scan_product_id: function(product_id,increment,op_id){ //performs the same operation as a scan, but with product id instead
868             var self = this;
869             return new instance.web.Model('stock.picking')
870                 .call('process_product_id_from_ui', [self.picking.id, product_id, op_id, increment])
871                 .then(function(result){
872                     return self.refresh_ui(self.picking.id);
873                 });
874         },
875         pack: function(){
876             var self = this;
877             var pack_op_ids = self.picking_editor.get_current_op_selection(false);
878             if (pack_op_ids.length !== 0){
879                 return new instance.web.Model('stock.picking')
880                     .call('action_pack',[[[self.picking.id]], pack_op_ids])
881                     .then(function(){
882                         instance.session.user_context.current_package_id = false;
883                         return self.refresh_ui(self.picking.id);
884                     });
885             }
886         },
887         drop_down: function(){
888             var self = this;
889             var pack_op_ids = self.picking_editor.get_current_op_selection(true);
890             if (pack_op_ids.length !== 0){
891                 return new instance.web.Model('stock.pack.operation')
892                     .call('action_drop_down', [pack_op_ids])
893                     .then(function(){
894                             return self.refresh_ui(self.picking.id).then(function(){
895                                 if (self.picking_editor.check_done()){
896                                     return self.done();
897                                 }
898                             });
899                     });
900             }
901         },
902         done: function(){
903             var self = this;
904             return new instance.web.Model('stock.picking')
905                 .call('action_done_from_ui',[self.picking.id, {'default_picking_type_id': self.picking_type_id}])
906                 .then(function(new_picking_ids){
907                     if (new_picking_ids){
908                         return self.refresh_ui(new_picking_ids[0]);
909                     }
910                     else {
911                         return 0;
912                     }
913                 });
914         },
915         create_lot: function(op_id, lot_name){
916             var self = this;
917             return new instance.web.Model('stock.pack.operation')
918                 .call('create_and_assign_lot',[parseInt(op_id), lot_name])
919                 .then(function(){
920                     return self.refresh_ui(self.picking.id);
921                 });
922         },
923         change_location: function(op_id, loc_id, is_src_dst){
924             var self = this;
925             var vals = {'location_dest_id': loc_id};
926             if (is_src_dst){
927                 vals = {'location_id': loc_id};
928             }
929             return new instance.web.Model('stock.pack.operation')
930                 .call('write',[op_id, vals])
931                 .then(function(){
932                     return self.refresh_ui(self.picking.id);
933                 });
934         },
935         print_package: function(package_id){
936             var self = this;
937             return new instance.web.Model('stock.quant.package')
938                 .call('action_print',[[package_id]])
939                 .then(function(action){
940                     return self.do_action(action);
941                 });
942         },
943         print_picking: function(){
944             var self = this;
945             return new instance.web.Model('stock.picking.type').call('read', [[self.picking_type_id], ['code'], new instance.web.CompoundContext()])
946                 .then(function(pick_type){
947                     return new instance.web.Model('stock.picking').call('do_print_picking',[[self.picking.id]])
948                            .then(function(action){
949                                 return self.do_action(action);
950                            });
951                 });
952         },
953         picking_next: function(){
954             for(var i = 0; i < this.pickings.length; i++){
955                 if(this.pickings[i] === this.picking.id){
956                     if(i < this.pickings.length -1){
957                         $.bbq.pushState('picking_id='+this.pickings[i+1]);
958                         this.refresh_ui(this.pickings[i+1]);
959                         return;
960                     }
961                 }
962             }
963         },
964         picking_prev: function(){
965             for(var i = 0; i < this.pickings.length; i++){
966                 if(this.pickings[i] === this.picking.id){
967                     if(i > 0){
968                         $.bbq.pushState('picking_id='+this.pickings[i-1]);
969                         this.refresh_ui(this.pickings[i-1]);
970                         return;
971                     }
972                 }
973             }
974         },
975         delete_package_op: function(pack_id){
976             var self = this;
977             return new instance.web.Model('stock.pack.operation').call('search', [[['result_package_id', '=', pack_id]]])
978                 .then(function(op_ids) {
979                     return new instance.web.Model('stock.pack.operation').call('write', [op_ids, {'result_package_id':false}])
980                         .then(function() {
981                             return self.refresh_ui(self.picking.id);
982                         });
983                 });
984         },
985         set_operation_quantity: function(quantity, op_id){
986             var self = this;
987             if(quantity >= 0){
988                 return new instance.web.Model('stock.pack.operation')
989                     .call('write',[[op_id],{'qty_done': quantity }])
990                     .then(function(){
991                         self.refresh_ui(self.picking.id);
992                     });
993             }
994
995         },
996         set_package_pack: function(package_id, pack){
997             var self = this;
998                 return new instance.web.Model('stock.quant.package')
999                     .call('write',[[package_id],{'ul_id': pack }]);
1000             return;
1001         },
1002         reload_pack_operation: function(){
1003             var self = this;
1004             return new instance.web.Model('stock.picking')
1005                 .call('do_prepare_partial',[[self.picking.id]])
1006                 .then(function(){
1007                     self.refresh_ui(self.picking.id);
1008                 });
1009         },
1010         quit: function(){
1011             this.destroy();
1012             return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_picking_type_form']], ['res_id']).pipe(function(res) {
1013                     window.location = '/web#action=' + res[0]['res_id'];
1014                 });
1015         },
1016         destroy: function(){
1017             this._super();
1018             // this.disconnect_numpad();
1019             this.barcode_scanner.disconnect();
1020             instance.webclient.set_content_full_screen(false);
1021         },
1022     });
1023     openerp.web.client_actions.add('stock.ui', 'instance.stock.PickingMainWidget');
1024
1025     module.BarcodeScanner = instance.web.Class.extend({
1026         connect: function(callback){
1027             var code = "";
1028             var timeStamp = 0;
1029             var timeout = null;
1030
1031             this.handler = function(e){
1032                 if(e.which === 13){ //ignore returns
1033                     return;
1034                 }
1035
1036                 if(timeStamp + 50 < new Date().getTime()){
1037                     code = "";
1038                 }
1039
1040                 timeStamp = new Date().getTime();
1041                 clearTimeout(timeout);
1042
1043                 code += String.fromCharCode(e.which);
1044
1045                 timeout = setTimeout(function(){
1046                     if(code.length >= 3){
1047                         callback(code);
1048                     }
1049                     code = "";
1050                 },100);
1051             };
1052
1053             $('body').on('keypress', this.handler);
1054
1055         },
1056         disconnect: function(){
1057             $('body').off('keypress', this.handler);
1058         },
1059     });
1060
1061 }
1062
1063 openerp.stock = function(openerp) {
1064     openerp.stock = openerp.stock || {};
1065     openerp_picking_widgets(openerp);
1066 }