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