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
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.
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
18 function openerp_pos_screens(instance, module){ //module is instance.point_of_sale
19 var QWeb = instance.web.qweb;
21 module.ScreenSelector = instance.web.Class.extend({
22 init: function(options){
23 this.pos = options.pos;
25 this.screen_set = options.screen_set || {};
27 this.popup_set = options.popup_set || {};
29 this.default_client_screen = options.default_client_screen;
30 this.default_cashier_screen = options.default_cashier_screen;
32 this.current_popup = null;
34 this.current_mode = options.default_mode || 'client';
36 this.current_screen = null;
38 for(screen_name in this.screen_set){
39 this.screen_set[screen_name].hide();
42 for(popup_name in this.popup_set){
43 this.popup_set[popup_name].hide();
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,
52 this.pos.bind('change:selectedOrder', this.load_saved_screen, this);
54 add_screen: function(screen_name, screen){
56 this.screen_set[screen_name] = screen;
59 show_popup: function(name){
60 if(this.current_popup){
63 this.current_popup = this.popup_set[name];
64 this.current_popup.show();
66 close_popup: function(){
67 if(this.current_popup){
68 this.current_popup.close();
69 this.current_popup.hide();
70 this.current_popup = null;
73 load_saved_screen: function(){
76 var selectedOrder = this.pos.get('selectedOrder');
78 if(this.current_mode === 'client'){
79 this.set_current_screen(selectedOrder.get_screen_data('client_screen') || this.default_client_screen,null,'refresh');
80 }else if(this.current_mode === 'cashier'){
81 this.set_current_screen(selectedOrder.get_screen_data('cashier_screen') || this.default_cashier_screen,null,'refresh');
83 this.selected_order = selectedOrder;
85 set_user_mode: function(user_mode){
86 if(user_mode !== this.current_mode){
88 this.current_mode = user_mode;
89 this.load_saved_screen();
92 get_user_mode: function(){
93 return this.current_mode;
95 set_current_screen: function(screen_name,params,refresh){
96 var screen = this.screen_set[screen_name];
98 console.error("ERROR: set_current_screen("+screen_name+") : screen not found");
102 var selectedOrder = this.pos.get('selectedOrder');
103 if(this.current_mode === 'client'){
104 selectedOrder.set_screen_data('client_screen',screen_name);
106 selectedOrder.set_screen_data('client_screen_params',params);
109 selectedOrder.set_screen_data('cashier_screen',screen_name);
111 selectedOrder.set_screen_data('cashier_screen_params',params);
115 if(screen && (refresh || screen !== this.current_screen)){
116 if(this.current_screen){
117 this.current_screen.close();
118 this.current_screen.hide();
120 this.current_screen = screen;
121 this.current_screen.show();
124 get_current_screen_param: function(param){
125 var selected_order = this.pos.get('selectedOrder');
126 if(this.current_mode === 'client'){
127 var params = selected_order.get_screen_data('client_screen_params');
129 var params = selected_order.get_screen_data('cashier_screen_params');
132 return params[param];
137 set_default_screen: function(){
138 this.set_current_screen(this.current_mode === 'client' ? this.default_client_screen : this.default_cashier_screen);
142 module.ScreenWidget = module.PosBaseWidget.extend({
147 init: function(parent,options){
148 this._super(parent,options);
152 help_button_action: function(){
153 this.pos_widget.screen_selector.show_popup('help');
156 barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned
157 barcode_product_error_popup: 'error-product', //if defined, this popup will be loaded when there's an error in the popup
159 // what happens when a product is scanned :
160 // it will add the product to the order and go to barcode_product_screen. Or show barcode_product_error_popup if
162 barcode_product_action: function(ean){
164 if(self.pos.scan_product(ean)){
165 self.pos.proxy.scan_item_success(ean);
166 if(self.barcode_product_screen){
167 self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
170 self.pos.proxy.scan_item_error_unrecognized(ean);
171 if(self.barcode_product_error_popup && self.pos_widget.screen_selector.get_user_mode() !== 'cashier'){
172 self.pos_widget.screen_selector.show_popup(self.barcode_product_error_popup);
177 // what happens when a cashier id barcode is scanned.
178 // the default behavior is the following :
179 // - if there's a user with a matching ean, put it as the active 'cashier', go to cashier mode, and return true
180 // - else : do nothing and return false. You probably want to extend this to show and appropriate error popup...
181 barcode_cashier_action: function(ean){
182 var users = this.pos.get('user_list');
183 for(var i = 0, len = users.length; i < len; i++){
184 if(users[i].ean13 === ean.ean){
185 this.pos.set('cashier',users[i]);
186 this.pos_widget.username.refresh();
187 this.pos.proxy.cashier_mode_activated();
188 this.pos_widget.screen_selector.set_user_mode('cashier');
192 this.pos.proxy.scan_item_error_unrecognized(ean);
196 // what happens when a client id barcode is scanned.
197 // the default behavior is the following :
198 // - if there's a user with a matching ean, put it as the active 'client' and return true
199 // - else : return false.
200 barcode_client_action: function(ean){
201 var partners = this.pos.get('partner_list');
202 for(var i = 0, len = partners.length; i < len; i++){
203 if(partners[i].ean13 === ean.ean){
204 this.pos.get('selectedOrder').set_client(partners[i]);
205 this.pos_widget.username.refresh();
206 this.pos.proxy.scan_item_success(ean);
210 this.pos.proxy.scan_item_error_unrecognized(ean);
212 //TODO start the transaction
215 // what happens when a discount barcode is scanned : the default behavior
216 // is to set the discount on the last order.
217 barcode_discount_action: function(ean){
218 this.pos.proxy.scan_item_success(ean);
219 var last_orderline = this.pos.get('selectedOrder').getLastOrderline();
221 last_orderline.set_discount(ean.value)
225 // shows an action bar on the screen. The actionbar is automatically shown when you add a button
226 // with add_action_button()
227 show_action_bar: function(){
228 this.pos_widget.action_bar.show();
229 this.$el.css({'bottom':'105px'});
232 // hides the action bar. The actionbar is automatically hidden when it is empty
233 hide_action_bar: function(){
234 this.pos_widget.action_bar.hide();
235 this.$el.css({'bottom':'0px'});
238 // adds a new button to the action bar. The button definition takes three parameters, all optional :
239 // - label: the text below the button
240 // - icon: a small icon that will be shown
241 // - click: a callback that will be executed when the button is clicked.
242 // the method returns a reference to the button widget, and automatically show the actionbar.
243 add_action_button: function(button_def){
244 this.show_action_bar();
245 return this.pos_widget.action_bar.add_new_button(button_def);
248 // this method shows the screen and sets up all the widget related to this screen. Extend this method
249 // if you want to alter the behavior of the screen.
258 if(this.pos_widget.action_bar.get_button_count() > 0){
259 this.show_action_bar();
261 this.hide_action_bar();
264 // we add the help button by default. we do this because the buttons are cleared on each refresh so that
265 // the button stay local to each screen
266 this.pos_widget.left_action_bar.add_new_button({
268 icon: '/point_of_sale/static/src/img/icons/png48/help.png',
269 click: function(){ self.help_button_action(); },
273 var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier';
275 this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode);
276 this.pos_widget.set_leftpane_visible(this.show_leftpane);
277 this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode);
278 this.pos_widget.set_cashier_controls_visible(cashier_mode);
280 if(cashier_mode && this.pos.iface_self_checkout){
281 this.pos_widget.client_button.show();
283 this.pos_widget.client_button.hide();
286 this.pos_widget.close_button.show();
288 this.pos_widget.close_button.hide();
291 this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
293 this.pos.barcode_reader.set_action_callback({
294 'cashier': self.barcode_cashier_action ? function(ean){ self.barcode_cashier_action(ean); } : undefined ,
295 'product': self.barcode_product_action ? function(ean){ self.barcode_product_action(ean); } : undefined ,
296 'client' : self.barcode_client_action ? function(ean){ self.barcode_client_action(ean); } : undefined ,
297 'discount': self.barcode_discount_action ? function(ean){ self.barcode_discount_action(ean); } : undefined,
301 // this method is called when the screen is closed to make place for a new screen. this is a good place
302 // to put your cleanup stuff as it is guaranteed that for each show() there is one and only one close()
304 if(this.pos.barcode_reader){
305 this.pos.barcode_reader.reset_action_callbacks();
307 this.pos_widget.action_bar.destroy_buttons();
308 this.pos_widget.left_action_bar.destroy_buttons();
311 // this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the
312 // POS initialization.
320 // we need this because some screens re-render themselves when they are hidden
321 // (due to some events, or magic, or both...) we must make sure they remain hidden.
322 // the good solution would probably be to make them not re-render themselves when they
324 renderElement: function(){
334 module.PopUpWidget = module.PosBaseWidget.extend({
340 /* called before hide, when a popup is closed */
343 /* hides the popup. keep in mind that this is called in the initialization pass of the
344 * pos instantiation, so you don't want to do anything fancy in here */
352 module.HelpPopupWidget = module.PopUpWidget.extend({
353 template:'HelpPopupWidget',
356 this.pos.proxy.help_needed();
359 this.$el.find('.button').off('click').click(function(){
360 self.pos_widget.screen_selector.close_popup();
364 this.pos.proxy.help_canceled();
368 module.ChooseReceiptPopupWidget = module.PopUpWidget.extend({
369 template:'ChooseReceiptPopupWidget',
372 this.renderElement();
374 var currentOrder = self.pos.get('selectedOrder');
376 this.$('.button.receipt').off('click').click(function(){
377 currentOrder.set_receipt_type('receipt');
378 self.pos_widget.screen_selector.set_current_screen('products');
381 this.$('.button.invoice').off('click').click(function(){
382 currentOrder.set_receipt_type('invoice');
383 self.pos_widget.screen_selector.set_current_screen('products');
386 get_client_name: function(){
387 var client = this.pos.get('selectedOrder').get_client();
396 module.ErrorPopupWidget = module.PopUpWidget.extend({
397 template:'ErrorPopupWidget',
401 this.pos.proxy.help_needed();
402 this.pos.proxy.scan_item_error_unrecognized();
404 this.pos.barcode_reader.save_callbacks();
405 this.pos.barcode_reader.reset_action_callbacks();
406 this.pos.barcode_reader.set_action_callback({
407 'cashier': function(ean){
408 clearInterval(this.intervalID);
409 self.pos.proxy.cashier_mode_activated();
410 self.pos_widget.screen_selector.set_user_mode('cashier');
413 this.$('.footer .button').off('click').click(function(){
414 self.pos_widget.screen_selector.close_popup();
419 this.pos.proxy.help_canceled();
420 this.pos.barcode_reader.restore_callbacks();
424 module.ProductErrorPopupWidget = module.ErrorPopupWidget.extend({
425 template:'ProductErrorPopupWidget',
428 module.ErrorSessionPopupWidget = module.ErrorPopupWidget.extend({
429 template:'ErrorSessionPopupWidget',
432 module.ErrorNegativePricePopupWidget = module.ErrorPopupWidget.extend({
433 template:'ErrorNegativePricePopupWidget',
436 module.ScaleInviteScreenWidget = module.ScreenWidget.extend({
437 template:'ScaleInviteScreenWidget',
440 previous_screen:'products',
446 self.pos.proxy.weighting_start();
448 this.intervalID = setInterval(function(){
449 var weight = self.pos.proxy.weighting_read_kg();
451 clearInterval(this.intervalID);
452 self.pos_widget.screen_selector.set_current_screen(self.next_screen);
456 this.add_action_button({
458 icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
460 clearInterval(this.intervalID);
461 self.pos.proxy.weighting_end();
462 self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
468 clearInterval(this.intervalID);
469 this.pos.proxy.weighting_end();
473 module.ScaleScreenWidget = module.ScreenWidget.extend({
474 template:'ScaleScreenWidget',
476 next_screen: 'products',
477 previous_screen: 'products',
481 this.renderElement();
485 this.add_action_button({
487 icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
489 self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
493 this.validate_button = this.add_action_button({
495 icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
497 self.order_product();
498 self.pos_widget.screen_selector.set_current_screen(self.next_screen);
502 this.pos.proxy.weighting_start();
503 this.intervalID = setInterval(function(){
504 var weight = self.pos.proxy.weighting_read_kg();
505 if(weight != self.weight){
506 self.weight = weight;
507 self.renderElement();
511 renderElement: function(){
514 this.$('.product-picture').click(function(){
515 self.order_product();
516 self.pos_widget.screen_selector.set_current_screen(self.next_screen);
519 get_product: function(){
520 var ss = this.pos_widget.screen_selector;
522 return ss.get_current_screen_param('product');
527 order_product: function(){
528 var weight = this.pos.proxy.weighting_read_kg();
529 this.pos.get('selectedOrder').addProduct(this.get_product(),{ quantity:weight });
531 get_product_name: function(){
532 var product = this.get_product();
533 return (product ? product.get('name') : undefined) || 'Unnamed Product';
535 get_product_price: function(){
536 var product = this.get_product();
537 return (product ? product.get('price') : 0) || 0;
539 get_product_weight: function(){
540 return this.weight || 0;
544 clearInterval(this.intervalID);
545 this.pos.proxy.weighting_end();
549 // the JobQueue schedules a sequence of 'jobs'. each job is
550 // a function returning a deferred. the queue waits for each job to finish
551 // before launching the next. Each job can also be scheduled with a delay.
552 // the queue jobqueue is used to prevent parallel requests to the payment terminal.
554 module.JobQueue = function(){
557 var run = function(){
558 if(queue.length > 0){
560 var job = queue.shift();
561 setTimeout(function(){
574 // adds a job to the schedule.
575 this.schedule = function(fun, delay){
576 queue.push({fun:fun, delay:delay});
582 // remove all jobs from the schedule
583 this.clear = function(){
588 module.ClientPaymentScreenWidget = module.ScreenWidget.extend({
589 template:'ClientPaymentScreenWidget',
591 next_screen: 'welcome',
592 previous_screen: 'products',
598 this.queue = new module.JobQueue();
599 this.canceled = false;
602 // initiates the connection to the payment terminal and starts the update requests
603 this.start = function(){
604 var def = new $.Deferred();
605 self.pos.proxy.payment_request(self.pos.get('selectedOrder').getDueLeft())
608 self.queue.schedule(self.update);
609 }else if(ack.indexOf('error') === 0){
610 console.error('cannot make payment. TODO');
612 console.error('unknown payment request return value:',ack);
619 // gets updated status from the payment terminal and performs the appropriate consequences
620 this.update = function(){
621 var def = new $.Deferred();
623 return def.resolve();
625 self.pos.proxy.payment_status()
626 .done(function(status){
627 if(status.status === 'paid'){
629 var currentOrder = self.pos.get('selectedOrder');
631 //we get the first cashregister marked as self-checkout
632 var selfCheckoutRegisters = [];
633 for(var i = 0; i < self.pos.get('cashRegisters').models.length; i++){
634 var cashregister = self.pos.get('cashRegisters').models[i];
635 if(cashregister.self_checkout_payment_method){
636 selfCheckoutRegisters.push(cashregister);
640 var cashregister = selfCheckoutRegisters[0] || self.pos.get('cashRegisters').models[0];
641 currentOrder.addPaymentLine(cashregister);
642 self.pos.push_order(currentOrder.exportAsJSON())
643 currentOrder.destroy();
644 self.pos.proxy.transaction_end();
645 self.pos_widget.screen_selector.set_current_screen(self.next_screen);
647 }else if(status.status.indexOf('error') === 0){
648 console.error('error in payment request. TODO');
649 }else if(status.status === 'waiting'){
650 self.queue.schedule(self.update,200);
652 console.error('unknown status value:',status.status);
659 // cancels a payment.
660 this.cancel = function(){
661 if(!self.paid && !self.canceled){
662 self.canceled = true;
663 self.pos.proxy.payment_cancel();
664 self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
667 return (new $.Deferred()).resolve();
670 if(this.pos.get('selectedOrder').getDueLeft() <= 0){
671 this.pos_widget.screen_selector.show_popup('error-negative-price');
673 this.queue.schedule(this.start);
676 this.add_action_button({
678 icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
680 self.queue.schedule(self.cancel);
686 this.queue.schedule(this.cancel);
693 module.WelcomeScreenWidget = module.ScreenWidget.extend({
694 template:'WelcomeScreenWidget',
696 next_screen: 'products',
699 show_leftpane: false,
700 barcode_product_action: function(ean){
701 this.pos.proxy.transaction_start();
705 barcode_client_action: function(ean){
706 this.pos.proxy.transaction_start();
708 $('.goodbye-message').hide();
709 this.pos_widget.screen_selector.show_popup('choose-receipt');
716 this.add_action_button({
718 icon: '/point_of_sale/static/src/img/icons/png48/help.png',
720 $('.goodbye-message').css({opacity:1}).hide();
721 self.help_button_action();
725 $('.goodbye-message').css({opacity:1}).show();
726 setTimeout(function(){
727 $('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').hide();});
732 module.ProductScreenWidget = module.ScreenWidget.extend({
733 template:'ProductScreenWidget',
735 scale_screen: 'scale_invite',
736 client_next_screen: 'client_payment',
741 start: function(){ //FIXME this should work as renderElement... but then the categories aren't properly set. explore why
743 this.product_categories_widget = new module.ProductCategoriesWidget(this,{});
744 this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
746 this.product_list_widget = new module.ProductListWidget(this,{
747 click_product_action: function(product){
748 if(product.get('to_weight') && self.pos.iface_electronic_scale){
749 self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product});
751 self.pos.get('selectedOrder').addProduct(product);
755 this.product_list_widget.replace($('.placeholder-ProductListWidget'));
762 this.product_categories_widget.reset_category();
764 this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
765 if(this.pos.iface_vkeyboard){
766 this.pos_widget.onscreen_keyboard.connect();
769 if(this.pos_widget.screen_selector.current_mode === 'client'){
770 this.add_action_button({
772 icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
774 self.pos_widget.screen_selector.set_current_screen(self.client_next_screen);
782 this.pos_widget.order_widget.set_numpad_state(null);
783 this.pos_widget.payment_screen.set_numpad_state(null);
788 module.ReceiptScreenWidget = module.ScreenWidget.extend({
789 template: 'ReceiptScreenWidget',
794 init: function(parent, options) {
795 this._super(parent,options);
796 this.model = options.model;
797 this.user = this.pos.get('user');
798 this.company = this.pos.get('company');
799 this.shop_obj = this.pos.get('shop');
801 renderElement: function() {
803 this.pos.bind('change:selectedOrder', this.change_selected_order, this);
804 this.change_selected_order();
810 this.add_action_button({
812 icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
813 click: function(){ self.print(); },
816 this.add_action_button({
818 icon: '/point_of_sale/static/src/img/icons/png48/go-next.png',
819 click: function() { self.finishOrder(); },
827 finishOrder: function() {
828 this.pos.get('selectedOrder').destroy();
830 change_selected_order: function() {
831 if (this.currentOrderLines)
832 this.currentOrderLines.unbind();
833 this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
834 this.currentOrderLines.bind('add', this.refresh, this);
835 this.currentOrderLines.bind('change', this.refresh, this);
836 this.currentOrderLines.bind('remove', this.refresh, this);
837 if (this.currentPaymentLines)
838 this.currentPaymentLines.unbind();
839 this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
840 this.currentPaymentLines.bind('all', this.refresh, this);
843 refresh: function() {
844 this.currentOrder = this.pos.get('selectedOrder');
845 $('.pos-receipt-container', this.$el).html(QWeb.render('PosTicket',{widget:this}));
849 module.PaymentScreenWidget = module.ScreenWidget.extend({
850 template: 'PaymentScreenWidget',
851 back_screen: 'products',
852 next_screen: 'receipt',
853 init: function(parent, options) {
854 this._super(parent,options);
855 this.model = options.model;
856 this.pos.bind('change:selectedOrder', this.change_selected_order, this);
857 this.bindPaymentLineEvents();
858 this.bind_orderline_events();
859 this.paymentlinewidgets = [];
865 if(this.pos.iface_cashdrawer){
866 this.pos.proxy.open_cashbox();
869 this.set_numpad_state(this.pos_widget.numpad.state);
871 this.back_button = this.add_action_button({
873 icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
875 self.pos_widget.screen_selector.set_current_screen(self.back_screen);
879 this.validate_button = this.add_action_button({
882 icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
884 self.validateCurrentOrder();
888 this.updatePaymentSummary();
892 this.pos_widget.order_widget.set_numpad_state(null);
893 this.pos_widget.payment_screen.set_numpad_state(null);
896 this.pos_widget.screen_selector.set_current_screen(self.back_screen);
898 validateCurrentOrder: function() {
899 var currentOrder = this.pos.get('selectedOrder');
901 this.pos.push_order(currentOrder.exportAsJSON())
902 if(this.pos.iface_print_via_proxy){
903 this.pos.proxy.print_receipt(currentOrder.export_for_printing());
904 this.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen
906 this.pos_widget.screen_selector.set_current_screen(this.next_screen);
909 bindPaymentLineEvents: function() {
910 this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
911 this.currentPaymentLines.bind('add', this.addPaymentLine, this);
912 this.currentPaymentLines.bind('remove', this.renderElement, this);
913 this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
915 bind_orderline_events: function() {
916 this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
917 this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
919 change_selected_order: function() {
920 this.currentPaymentLines.unbind();
921 this.bindPaymentLineEvents();
922 this.currentOrderLines.unbind();
923 this.bind_orderline_events();
924 this.renderElement();
926 addPaymentLine: function(newPaymentLine) {
928 var l = new module.PaymentlineWidget(null, {
929 payment_line: newPaymentLine
931 l.on('delete_payment_line', self, function(r) {
934 l.appendTo(this.$('#paymentlines'));
935 this.paymentlinewidgets.push(l);
936 if(this.numpadState){
937 this.numpadState.resetValue();
940 renderElement: function() {
942 this.$('#paymentlines').empty();
943 for(var i = 0, len = this.paymentlinewidgets.length; i < len; i++){
944 this.paymentlinewidgets[i].destroy();
946 this.paymentlinewidgets = [];
948 this.currentPaymentLines.each(_.bind( function(paymentLine) {
949 this.addPaymentLine(paymentLine);
951 this.updatePaymentSummary();
953 deleteLine: function(lineWidget) {
954 this.currentPaymentLines.remove([lineWidget.payment_line]);
956 updatePaymentSummary: function() {
957 var currentOrder = this.pos.get('selectedOrder');
958 var paidTotal = currentOrder.getPaidTotal();
959 var dueTotal = currentOrder.getTotalTaxIncluded();
960 var remaining = dueTotal > paidTotal ? dueTotal - paidTotal : 0;
961 var change = paidTotal > dueTotal ? paidTotal - dueTotal : 0;
963 this.$('#payment-due-total').html(this.format_currency(dueTotal));
964 this.$('#payment-paid-total').html(this.format_currency(paidTotal));
965 this.$('#payment-remaining').html(this.format_currency(remaining));
966 this.$('#payment-change').html(this.format_currency(change));
967 if(currentOrder.selected_orderline === undefined){
968 remaining = 1; // What is this ?
971 if(this.pos_widget.action_bar){
972 this.pos_widget.action_bar.set_button_disabled('validation', remaining > 0);
975 set_numpad_state: function(numpadState) {
976 if (this.numpadState) {
977 this.numpadState.unbind('set_value', this.set_value);
978 this.numpadState.unbind('change:mode', this.setNumpadMode);
980 this.numpadState = numpadState;
981 if (this.numpadState) {
982 this.numpadState.bind('set_value', this.set_value, this);
983 this.numpadState.bind('change:mode', this.setNumpadMode, this);
984 this.numpadState.reset();
985 this.setNumpadMode();
988 setNumpadMode: function() {
989 this.numpadState.set({mode: 'payment'});
991 set_value: function(val) {
992 this.currentPaymentLines.last().set_amount(val);