659ee09c2326e557ef0b66cd5942cc0373204d8c
[odoo/odoo.git] / addons / point_of_sale / static / src / js / pos_screens.js
1
2 // this file contains the screens definitions. Screens are the
3 // content of the right pane of the pos, containing the main functionalities. 
4 // screens are contained in the PosWidget, in pos_widget.js
5 // all screens are present in the dom at all time, but only one is shown at the
6 // same time. 
7 //
8 // transition between screens is made possible by the use of the screen_selector,
9 // which is responsible of hiding and showing the screens, as well as maintaining
10 // the state of the screens between different orders.
11 //
12 // all screens inherit from ScreenWidget. the only addition from the base widgets
13 // are show() and hide() which shows and hides the screen but are also used to 
14 // bind and unbind actions on widgets and devices. The screen_selector guarantees
15 // that only one screen is shown at the same time and that show() is called after all
16 // hide()s
17
18 function openerp_pos_screens(instance, module){ //module is instance.point_of_sale
19     var QWeb = instance.web.qweb;
20
21     module.ScreenSelector = instance.web.Class.extend({
22         init: function(options){
23             this.pos = options.pos;
24
25             this.screen_set = options.screen_set || {};
26
27             this.popup_set = options.popup_set || {};
28
29             this.default_client_screen = options.default_client_screen;
30             this.default_cashier_screen = options.default_cashier_screen;
31
32             this.current_popup = null;
33
34             this.current_mode = options.default_mode || 'client';
35
36             this.current_screen = null; 
37
38             for(screen_name in this.screen_set){
39                 this.screen_set[screen_name].hide();
40             }
41             
42             for(popup_name in this.popup_set){
43                 this.popup_set[popup_name].hide();
44             }
45
46             this.selected_order = this.pos.get('selectedOrder');
47             this.selected_order.set_screen_data({
48                 client_screen: this.default_client_screen,
49                 cashier_screen: this.default_cashier_screen,
50             });
51
52             this.pos.bind('change:selectedOrder', this.load_saved_screen, this);
53         },
54         add_screen: function(screen_name, screen){
55             screen.hide();
56             this.screen_set[screen_name] = screen;
57             return this;
58         },
59         show_popup: function(name){
60             if(this.current_popup){
61                 this.close_popup();
62             }
63             this.current_popup = this.popup_set[name];
64             this.current_popup.show();
65         },
66         close_popup: function(){
67             if(this.current_popup){
68                 this.current_popup.hide();
69                 this.current_popup = null;
70             }
71         },
72         load_saved_screen:  function(){
73             this.close_popup();
74
75             var selectedOrder = this.pos.get('selectedOrder');
76             
77             if(this.current_mode === 'client'){
78                 this.set_current_screen(selectedOrder.get_screen_data('client_screen') || this.default_client_screen,null,'refresh');
79             }else if(this.current_mode === 'cashier'){
80                 this.set_current_screen(selectedOrder.get_screen_data('cashier_screen') || this.default_cashier_screen,null,'refresh');
81             }
82             this.selected_order = selectedOrder;
83         },
84         set_user_mode: function(user_mode){
85             if(user_mode !== this.current_mode){
86                 this.close_popup();
87                 this.current_mode = user_mode;
88                 this.load_saved_screen();
89             }
90         },
91         get_user_mode: function(){
92             return this.current_mode;
93         },
94         set_current_screen: function(screen_name,params,refresh){
95             var screen = this.screen_set[screen_name];
96             if(!screen){
97                 console.error("ERROR: set_current_screen("+screen_name+") : screen not found");
98             }
99
100             this.close_popup();
101             var selectedOrder = this.pos.get('selectedOrder');
102             if(this.current_mode === 'client'){
103                 selectedOrder.set_screen_data('client_screen',screen_name);
104                 if(params){ 
105                     selectedOrder.set_screen_data('client_screen_params',params); 
106                 }
107             }else{
108                 selectedOrder.set_screen_data('cashier_screen',screen_name);
109                 if(params){
110                     selectedOrder.set_screen_data('cashier_screen_params',params);
111                 }
112             }
113
114             if(screen && (refresh || screen !== this.current_screen)){
115                 if(this.current_screen){
116                     this.current_screen.close();
117                     this.current_screen.hide();
118                 }
119                 this.current_screen = screen;
120                 this.current_screen.show();
121             }
122         },
123         get_current_screen_param: function(param){
124             var selected_order = this.pos.get('selectedOrder');
125             if(this.current_mode === 'client'){
126                 var params = selected_order.get_screen_data('client_screen_params');
127             }else{
128                 var params = selected_order.get_screen_data('cashier_screen_params');
129             }
130             if(params){
131                 return params[param];
132             }else{
133                 return undefined;
134             }
135         },
136         set_default_screen: function(){
137             this.set_current_screen(this.current_mode === 'client' ? this.default_client_screen : this.default_cashier_screen);
138         },
139     });
140
141     module.ScreenWidget = module.PosBaseWidget.extend({
142
143         show_numpad:     true,  
144         show_leftpane:   true,
145
146         init: function(parent,options){
147             this._super(parent,options);
148             this.hidden = false;
149         },
150
151         help_button_action: function(){
152             this.pos_widget.screen_selector.show_popup('help');
153         },
154
155         barcode_product_screen:         'products',     //if defined, this screen will be loaded when a product is scanned
156         barcode_product_error_popup:    'error',    //if defined, this popup will be loaded when there's an error in the popup
157
158         // what happens when a product is scanned : 
159         // it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if 
160         // there's an error.
161         barcode_product_action: function(ean){
162             var self = this;
163             if(pos_widget.scan_product(ean)){
164                 self.pos.proxy.scan_item_success(ean);
165                 if(self.barcode_product_screen){ 
166                     self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
167                 }
168             }else{
169                 self.pos.proxy.scan_item_error_unrecognized(ean);
170                 if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
171                     self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
172                 }
173             }
174         },
175         
176         // what happens when a cashier id barcode is scanned.
177         // the default behavior is the following : 
178         // - if there's a user with a matching ean, put it as the active 'cashier', go to cashier mode, and return true
179         // - else : do nothing and return false. You probably want to extend this to show and appropriate error popup... 
180         barcode_cashier_action: function(ean){
181             var users = this.pos.get('user_list');
182             for(var i = 0, len = users.length; i < len; i++){
183                 if(users[i].ean13 === ean.ean){
184                     this.pos.set('cashier',users[i]);
185                     this.pos_widget.username.refresh();
186                     this.pos.proxy.cashier_mode_activated();
187                     this.pos_widget.screen_selector.set_user_mode('cashier');
188                     return true;
189                 }
190             }
191             this.pos.proxy.scan_item_unrecognized(ean);
192             return false;
193         },
194         
195         // what happens when a client id barcode is scanned.
196         // the default behavior is the following : 
197         // - if there's a user with a matching ean, put it as the active 'client' and return true
198         // - else : return false. 
199         barcode_client_action: function(ean){
200             var users = this.pos.get('user_list');
201             for(var i = 0, len = users.length; i < len; i++){
202                 if(users[i].ean13 === ean.ean){
203                     this.pos.get('selectedOrder').set_client(users[i]);
204                     this.pos_widget.username.refresh();
205                     this.pos.proxy.scan_item_success(ean);
206                     return true;
207                 }
208             }
209             this.pos.proxy.scan_item_unrecognized(ean);
210             return false;
211             //TODO start the transaction
212         },
213         
214         // what happens when a discount barcode is scanned : the default behavior
215         // is to set the discount on the last order.
216         barcode_discount_action: function(ean){
217             this.pos.proxy.scan_item_success(ean);
218             var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
219             if(last_orderline){
220                 last_orderline.set_discount(ean.value)
221             }
222         },
223
224         // shows an action bar on the screen. The actionbar is automatically shown when you add a button
225         // with add_action_button()
226         show_action_bar: function(){
227             this.pos_widget.action_bar.show();
228             this.$element.css({'bottom':'105px'});
229         },
230
231         // hides the action bar. The actionbar is automatically hidden when it is empty
232         hide_action_bar: function(){
233             this.pos_widget.action_bar.hide();
234             this.$element.css({'bottom':'0px'});
235         },
236
237         // adds a new button to the action bar. The button definition takes three parameters, all optional :
238         // - label: the text below the button
239         // - icon:  a small icon that will be shown
240         // - click: a callback that will be executed when the button is clicked.
241         // the method returns a reference to the button widget, and automatically show the actionbar.
242         add_action_button: function(button_def){
243             this.show_action_bar();
244             return this.pos_widget.action_bar.add_new_button(button_def);
245         },
246
247         // this method shows the screen and sets up all the widget related to this screen. Extend this method
248         // if you want to alter the behavior of the screen.
249         show: function(){
250             var self = this;
251
252             this.hidden = false;
253             if(this.$element){
254                 this.$element.show();
255             }
256
257             if(this.pos_widget.action_bar.get_button_count() > 0){
258                 this.show_action_bar();
259             }else{
260                 this.hide_action_bar();
261             }
262             
263             // we add the help button by default. we do this because the buttons are cleared on each refresh so that
264             // the button stay local to each screen
265             this.pos_widget.left_action_bar.add_new_button({
266                     label: 'help',
267                     icon: '/point_of_sale/static/src/img/icons/png48/help.png',
268                     click: function(){ self.help_button_action(); },
269                 });
270
271             var self = this;
272             var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
273
274             this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
275             this.pos_widget.set_leftpane_visible(this.show_leftpane);
276             this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode);
277             this.pos_widget.set_cashier_controls_visible(cashier_mode);
278
279             if(cashier_mode && this.pos.use_selfcheckout){
280                 this.pos_widget.client_button.show();
281             }else{
282                 this.pos_widget.client_button.hide();
283             }
284             if(cashier_mode){
285                 this.pos_widget.close_button.show();
286             }else{
287                 this.pos_widget.close_button.hide();
288             }
289             
290             this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
291
292             this.pos.barcode_reader.set_action_callback({
293                 'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined ,
294                 'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined ,
295                 'client' : self.barcode_client_action ?  function(ean){ self.barcode_client_action(ean);  } : undefined ,
296                 'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined,
297             });
298         },
299
300         // this method is called when the screen is closed to make place for a new screen. this is a good place
301         // to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close()
302         close: function(){
303             if(this.pos.barcode_reader){
304                 this.pos.barcode_reader.reset_action_callbacks();
305             }
306             this.pos_widget.action_bar.destroy_buttons();
307             this.pos_widget.left_action_bar.destroy_buttons();
308         },
309
310         // this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
311         // POS initialization.
312         hide: function(){
313             this.hidden = true;
314             if(this.$element){
315                 this.$element.hide();
316             }
317         },
318
319         // we need this because some screens re-render themselves when they are hidden
320         // (due to some events, or magic, or both...)  we must make sure they remain hidden.
321         // the good solution would probably be to make them not re-render themselves when they
322         // are hidden. 
323         renderElement: function(){
324             this._super();
325             if(this.hidden){
326                 if(this.$element){
327                     this.$element.hide();
328                 }
329             }
330         },
331     });
332
333     module.PopUpWidget = module.PosBaseWidget.extend({
334         show: function(){
335             if(this.$element){
336                 this.$element.show();
337             }
338         },
339         hide: function(){
340             if(this.$element){
341                 this.$element.hide();
342             }
343         },
344     });
345
346     module.HelpPopupWidget = module.PopUpWidget.extend({
347         template:'HelpPopupWidget',
348         show: function(){
349             this._super();
350             this.pos.proxy.help_needed();
351             var self = this;
352             
353             this.$element.find('.button').off('click').click(function(){
354                 self.pos_widget.screen_selector.close_popup();
355                 self.pos.proxy.help_canceled();
356             });
357         },
358     });
359
360     module.ErrorPopupWidget = module.PopUpWidget.extend({
361         template:'ErrorPopupWidget',
362         show: function(){
363             var self = this;
364             this._super();
365             this.pos.proxy.help_needed();
366             this.pos.proxy.scan_item_error_unrecognized();
367
368             this.pos.barcode_reader.save_callbacks();
369             this.pos.barcode_reader.reset_action_callbacks();
370             this.pos.barcode_reader.set_action_callback({
371                 'cashier': function(ean){
372                     clearInterval(this.intervalID);
373                     self.pos.proxy.cashier_mode_activated();
374                     self.pos_widget.screen_selector.set_user_mode('cashier');
375                 },
376             });
377         },
378         close:function(){
379             this._super();
380             this.pos.proxy.help_canceled();
381             this.pos.barcode_reader.restore_callbacks();
382         },
383     });
384
385     module.ErrorProductNotRecognizedPopupWidget = module.ErrorPopupWidget.extend({
386         template:'ErrorProductNotRecognizedPopupWidget',
387     });
388
389     module.ErrorNoSessionPopupWidget = module.ErrorPopupWidget.extend({
390         template:'ErrorNoSessionPopupWidget',
391     });
392
393     module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
394         template:'ScaleInviteScreenWidget',
395
396         next_screen:'scale',
397         previous_screen:'products',
398
399         show: function(){
400             this._super();
401             var self = this;
402
403             self.pos.proxy.weighting_start();
404
405             this.intervalID = setInterval(function(){
406                 var weight = self.pos.proxy.weighting_read_kg();
407                 if(weight > 0.001){
408                     clearInterval(this.intervalID);
409                     self.pos_widget.screen_selector.set_current_screen(self.next_screen);
410                 }
411             },500);
412
413             this.add_action_button({
414                     label: 'back',
415                     icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
416                     click: function(){  
417                         clearInterval(this.intervalID);
418                         self.pos.proxy.weighting_end();
419                         self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
420                     }
421                 });
422         },
423         close: function(){
424             this._super();
425             clearInterval(this.intervalID);
426         },
427     });
428
429     module.ScaleScreenWidget = module.ScreenWidget.extend({
430         template:'ScaleScreenWidget',
431
432         next_screen: 'products',
433         previous_screen: 'products',
434
435         show: function(){
436             this._super();
437             this.renderElement();
438             var self = this;
439
440
441             this.add_action_button({
442                     label: 'back',
443                     icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
444                     click: function(){
445                         self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
446                     }
447                 });
448
449             this.validate_button = this.add_action_button({
450                     label: 'Validate',
451                     icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
452                     click: function(){
453                         self.order_product();
454                         self.pos_widget.screen_selector.set_current_screen(self.next_screen);
455                     },
456                 });
457             
458             this.pos.proxy.weighting_start();
459             this.intervalID = setInterval(function(){
460                 var weight = self.pos.proxy.weighting_read_kg();
461                 if(weight != self.weight){
462                     self.weight = weight;
463                     self.renderElement();
464                 }
465             },200);
466         },
467         renderElement: function(){
468             var self = this;
469             this._super();
470             this.$('.product-picture').click(function(){
471                 self.order_product();
472                 self.pos_widget.screen_selector.set_current_screen(self.next_screen);
473             });
474         },
475         get_product: function(){
476             var ss = this.pos_widget.screen_selector;
477             if(ss){
478                 return ss.get_current_screen_param('product');
479             }else{
480                 return undefined;
481             }
482         },
483         order_product: function(){
484             var weight = this.pos.proxy.weighting_read_kg();
485             this.pos.get('selectedOrder').addProduct(this.get_product(),{ quantity:weight });
486         },
487         get_product_name: function(){
488             var product = this.get_product();
489             return (product ? product.get('name') : undefined) || 'Unnamed Product';
490         },
491         get_product_price: function(){
492             var product = this.get_product();
493             return (product ? product.get('list_price') : 0) || 0;
494         },
495         get_product_image: function(){
496             var product = this.get_product();
497             return product ? product.get('image') : undefined;
498         },
499         get_product_weight: function(){
500             return this.weight || 0;
501         },
502         close: function(){
503             this._super();
504             clearInterval(this.intervalID);
505             this.pos.proxy.weighting_end();
506         },
507     });
508
509     module.ClientPaymentScreenWidget =  module.ScreenWidget.extend({
510         template:'ClientPaymentScreenWidget',
511
512         next_screen: 'welcome',
513         previous_screen: 'products',
514
515         show: function(){
516             this._super();
517             var self = this;
518
519             this.pos.proxy.payment_request(this.pos.get('selectedOrder').getDueLeft(),'card','info');    //TODO TOTAL
520
521             this.intervalID = setInterval(function(){
522                 var payment = self.pos.proxy.is_payment_accepted();
523                 if(payment === 'payment_accepted'){
524                     clearInterval(this.intervalID);
525
526                     var currentOrder = self.pos.get('selectedOrder');
527                     
528                     //we get the first cashregister marked as self-checkout
529                     var selfCheckoutRegisters = [];
530                     for(var i = 0; i < this.pos.get('cashRegisters').models.length; i++){
531                         var cashregister = this.pos.get('cashRegisters').models[i];
532                         if(cashregister.self_checkout_payment_method){
533                             selfCheckoutRegisters.push(cashregister);
534                         }
535                     }
536
537                     var cashregister = selfCheckoutRegisters[0] || this.pos.get('cashRegisters').models[0];
538                     currentOrder.addPaymentLine(cashregister);
539
540                     self.pos.push_order(currentOrder.exportAsJSON()).then(function() {
541                         currentOrder.destroy();
542                         self.pos.proxy.transaction_end();
543                         self.pos_widget.screen_selector.set_current_screen(self.next_screen);
544                     });
545                 }else if(payment === 'payment_rejected'){
546                     clearInterval(this.intervalID);
547                     //TODO show a tryagain thingie ? 
548                 }
549             },500);
550
551             this.add_action_button({
552                     label: 'back',
553                     icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
554                     click: function(){  
555                         clearInterval(this.intervalID);
556                         self.pos.proxy.payment_canceled();
557                         self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
558                     }
559                 });
560         },
561         close: function(){
562             this._super();
563             clearInterval(this.intervalID);
564         },
565     });
566
567     module.WelcomeScreenWidget = module.ScreenWidget.extend({
568         template:'WelcomeScreenWidget',
569
570         next_screen: 'products',
571
572         show_numpad:     false,
573         show_leftpane:   false,
574
575         barcode_client_action: function(ean){
576             this._super(ean);
577             this.pos_widget.screen_selector.set_current_screen(this.next_screen);
578         },
579         
580         show: function(){
581             this._super();
582             var self = this;
583             $('.goodbye-message').css({opacity:1}).show();
584             setTimeout(function(){
585                 $('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').hide();});
586             },3000);
587         },
588     });
589     
590     module.ProductScreenWidget = module.ScreenWidget.extend({
591         template:'ProductScreenWidget',
592
593         scale_screen: 'scale_invite',
594         client_next_screen:  'client_payment',
595
596         show_numpad:     true,
597         show_leftpane:   true,
598
599         start: function(){ //FIXME this should work as renderElement... but then the categories aren't properly set. explore why
600             var self = this;
601             this.product_categories_widget = new module.ProductCategoriesWidget(this,{});
602             this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
603
604             this.product_list_widget = new module.ProductListWidget(this,{
605                 click_product_action: function(product){
606                     if(product.get('to_weight') && self.pos.use_scale){
607                         self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product});
608                     }else{
609                         self.pos.get('selectedOrder').addProduct(product);
610                     }
611                 },
612             });
613             this.product_list_widget.replace($('.placeholder-ProductListWidget'));
614         },
615
616         show: function(){
617             this._super();
618             var self = this;
619
620             this.product_categories_widget.reset_category();
621
622             this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
623             if(this.pos.use_virtual_keyboard){
624                 this.pos_widget.onscreen_keyboard.connect();
625             }
626
627             if(this.pos_widget.screen_selector.current_mode === 'client'){ 
628                 this.add_action_button({
629                         label: 'pay',
630                         icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
631                         click: function(){  
632                             self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
633                         }
634                     });
635             }
636         },
637
638         close: function(){
639             this._super();
640             this.pos_widget.order_widget.set_numpad_state(null);
641             this.pos_widget.payment_screen.set_numpad_state(null);
642         },
643
644     });
645
646     module.ReceiptScreenWidget = module.ScreenWidget.extend({
647         template: 'ReceiptScreenWidget',
648
649         show_numpad:     true,
650         show_leftpane:   true,
651
652         init: function(parent, options) {
653             this._super(parent,options);
654             this.model = options.model;
655             this.user = this.pos.get('user');
656             this.company = this.pos.get('company');
657             this.shop_obj = this.pos.get('shop');
658         },
659         renderElement: function() {
660             this._super();
661             this.pos.bind('change:selectedOrder', this.change_selected_order, this);
662             this.change_selected_order();
663         },
664         show: function(){
665             this._super();
666             var self = this;
667
668             this.add_action_button({
669                     label: 'Print',
670                     icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
671                     click: function(){ self.print(); },
672                 });
673
674             this.add_action_button({
675                     label: 'Next Order',
676                     icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
677                     click: function() { self.finishOrder(); },
678                 });
679         },
680         print: function() {
681             window.print();
682         },
683         finishOrder: function() {
684             this.pos.get('selectedOrder').destroy();
685         },
686         change_selected_order: function() {
687             if (this.currentOrderLines)
688                 this.currentOrderLines.unbind();
689             this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
690             this.currentOrderLines.bind('add', this.refresh, this);
691             this.currentOrderLines.bind('change', this.refresh, this);
692             this.currentOrderLines.bind('remove', this.refresh, this);
693             if (this.currentPaymentLines)
694                 this.currentPaymentLines.unbind();
695             this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
696             this.currentPaymentLines.bind('all', this.refresh, this);
697             this.refresh();
698         },
699         refresh: function() {
700             this.currentOrder = this.pos.get('selectedOrder');
701             $('.pos-receipt-container', this.$element).html(QWeb.render('PosTicket',{widget:this}));
702         },
703     });
704
705     module.PaymentScreenWidget = module.ScreenWidget.extend({
706         template: 'PaymentScreenWidget',
707         back_screen: 'products',
708         next_screen: 'receipt',
709         init: function(parent, options) {
710             this._super(parent,options);
711             this.model = options.model;
712             this.pos.bind('change:selectedOrder', this.change_selected_order, this);
713             this.bindPaymentLineEvents();
714             this.bind_orderline_events();
715         },
716         show: function(){
717             this._super();
718             var self = this;
719
720             if(this.pos.use_cashbox){
721                 this.pos.proxy.open_cashbox();
722             }
723
724             this.set_numpad_state(this.pos_widget.numpad.state);
725             
726             this.back_button = this.add_action_button({
727                     label: 'Back',
728                     icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
729                     click: function(){  
730                         self.pos_widget.screen_selector.set_current_screen(self.back_screen);
731                     },
732                 });
733             
734             this.validate_button = this.add_action_button({
735                     label: 'Validate',
736                     icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
737                     click: function(){
738                         self.validateCurrentOrder();
739                     },
740                 });
741         },
742         close: function(){
743             this._super();
744             this.pos_widget.order_widget.set_numpad_state(null);
745             this.pos_widget.payment_screen.set_numpad_state(null);
746         },
747         back: function() {
748             this.pos_widget.screen_selector.set_current_screen(self.back_screen);
749         },
750         validateCurrentOrder: function() {
751             var self = this;
752             var currentOrder = this.pos.get('selectedOrder');
753             if(this.busy){
754                 return;
755             }else{
756                 this.busy = true;
757             }
758             this.validate_button.$element.addClass('disabled');
759
760             this.pos.push_order(currentOrder.exportAsJSON()) 
761                 .then(function() {
762                     if(self.pos.use_proxy_printer){
763                         self.pos.proxy.print_receipt(currentOrder.export_for_printing());
764                         self.pos.get('selectedOrder').destroy();    //finish order and go back to scan screen
765                     }else{
766                         self.pos_widget.screen_selector.set_current_screen(self.next_screen);
767                     }
768                     self.validate_button.$element.removeClass('disabled');
769                     self.busy = false;
770                 });
771         },
772         bindPaymentLineEvents: function() {
773             this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
774             this.currentPaymentLines.bind('add', this.addPaymentLine, this);
775             this.currentPaymentLines.bind('remove', this.renderElement, this);
776             this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
777         },
778         bind_orderline_events: function() {
779             this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
780             this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
781         },
782         change_selected_order: function() {
783             this.currentPaymentLines.unbind();
784             this.bindPaymentLineEvents();
785             this.currentOrderLines.unbind();
786             this.bind_orderline_events();
787             this.renderElement();
788         },
789         addPaymentLine: function(newPaymentLine) {
790             var x = new module.PaymentlineWidget(null, {
791                     payment_line: newPaymentLine
792                 });
793             x.on_delete.add(_.bind(this.deleteLine, this, x));
794             x.appendTo(this.$('#paymentlines'));
795         },
796         renderElement: function() {
797             this._super();
798             this.$('#paymentlines').empty();
799             this.currentPaymentLines.each(_.bind( function(paymentLine) {
800                 this.addPaymentLine(paymentLine);
801             }, this));
802             this.updatePaymentSummary();
803         },
804         deleteLine: function(lineWidget) {
805                 this.currentPaymentLines.remove([lineWidget.payment_line]);
806         },
807         updatePaymentSummary: function() {
808             var currentOrder = this.pos.get('selectedOrder');
809             var paidTotal = currentOrder.getPaidTotal();
810             var dueTotal = currentOrder.getTotal();
811             var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0;
812             var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0;
813
814             this.$('#payment-due-total').html(dueTotal.toFixed(2));
815             this.$('#payment-paid-total').html(paidTotal.toFixed(2));
816             this.$('#payment-remaining').html(remaining.toFixed(2));
817             this.$('#payment-change').html(change.toFixed(2));
818         },
819         set_numpad_state: function(numpadState) {
820                 if (this.numpadState) {
821                         this.numpadState.unbind('set_value', this.set_value);
822                         this.numpadState.unbind('change:mode', this.setNumpadMode);
823                 }
824                 this.numpadState = numpadState;
825                 if (this.numpadState) {
826                         this.numpadState.bind('set_value', this.set_value, this);
827                         this.numpadState.bind('change:mode', this.setNumpadMode, this);
828                         this.numpadState.reset();
829                         this.setNumpadMode();
830                 }
831         },
832         setNumpadMode: function() {
833                 this.numpadState.set({mode: 'payment'});
834         },
835         set_value: function(val) {
836                 this.currentPaymentLines.last().set({amount: val});
837         },
838     });
839
840 }