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