From 71ae60324bfa475ab071d783a0e6ea34c0c76ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 5 Sep 2013 12:43:57 +0200 Subject: [PATCH 1/6] [IMP] point_of_sale: sane multi order workflow + remove existing tooltips when we load the pos bzr revid: fva@openerp.com-20130905104357-xlf51euhnclymj7x --- addons/point_of_sale/static/src/css/pos.css | 11 +++++++-- addons/point_of_sale/static/src/js/models.js | 21 +++++++++++++---- addons/point_of_sale/static/src/js/widgets.js | 23 ++++++++++--------- addons/point_of_sale/static/src/xml/pos.xml | 3 ++- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index e328054aa28..2294513a389 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -175,10 +175,9 @@ background: linear-gradient(#b2b3d7, #7f82ac); } -.point-of-sale #rightheader button.neworder-button { +.point-of-sale #rightheader button.square{ width: 32px; margin-left:4px; - margin-right:4px; } .point-of-sale div#order-selector { @@ -186,12 +185,20 @@ } .point-of-sale ol#orders { display: inline; + margin-left: 8px; } .point-of-sale li.order-selector-button { display: inline; } .point-of-sale li.selected-order button { font-weight: 900; + background: #7174A8 !important; + color: rgb(236, 237, 255) !important; + text-shadow: 0px 1px rgba(0, 0, 0, 0.31); + -webkit-box-shadow: 0px 1px 2px rgb(63, 66, 139) inset; + -moz-box-shadow: 0px 1px 2px rgb(63, 66, 139) inset; + -ms-box-shadow: 0px 1px 2px rgb(63, 66, 139) inset; + box-shadow: 0px 1px 2px rgb(63, 66, 139) inset; } /* c) The session buttons */ diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index e4d69bca26f..cfaecf80a70 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -55,7 +55,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal 'selectedOrder': null, }); - this.get('orders').bind('remove', function(){ self.on_removed_order(); }); + this.get('orders').bind('remove', function(order,_unused_,options){ + self.on_removed_order(order,options.index,options.reason); + }); // We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched, // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. @@ -239,11 +241,14 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal // this is called when an order is removed from the order collection. It ensures that there is always an existing // order and a valid selected order - on_removed_order: function(removed_order){ - if( this.get('orders').isEmpty()){ - this.add_new_order(); + on_removed_order: function(removed_order,index,reason){ + if(reason === 'abandon' && this.get('orders').size() > 0){ + // when we intentionally remove an unfinished order, and there is another existing one + this.set({'selectedOrder' : this.get('orders').at(index) || this.get('orders').last()}); }else{ - this.set({ selectedOrder: this.get('orders').last() }); + // when the order was automatically removed after completion, + // or when we intentionally delete the only concurrent order + this.add_new_order(); } }, @@ -254,6 +259,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal this.set('selectedOrder', order); }, + //removes the current order + delete_current_order: function(){ + this.get('selectedOrder').destroy({'reason':'abandon'}); + console.log('coucou!'); + }, + // saves the order locally and try to send it to the backend. // it returns a deferred that succeeds after having tried to send the order and all the other pending orders. push_order: function(order) { diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index bf7cf4d13af..dc00c995480 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -362,28 +362,23 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.order = options.order; this.order.bind('destroy',function(){ self.destroy(); }); this.order.bind('change', function(){ self.renderElement(); }); - this.pos.bind('change:selectedOrder', _.bind( function(pos) { - var selectedOrder; - selectedOrder = pos.get('selectedOrder'); - if (this.order === selectedOrder) { - this.setButtonSelected(); - } - }, this)); + this.pos.bind('change:selectedOrder', function() { + self.renderElement(); + }, this); }, renderElement:function(){ this._super(); this.$('button.select-order').off('click').click(_.bind(this.selectOrder, this)); this.$('button.close-order').off('click').click(_.bind(this.closeOrder, this)); + if( this.order === this.pos.get('selectedOrder') ){ + this.$el.addClass('selected-order'); + } }, selectOrder: function(event) { this.pos.set({ selectedOrder: this.order }); }, - setButtonSelected: function() { - $('.selected-order').removeClass('selected-order'); - this.$el.addClass('selected-order'); - }, closeOrder: function(event) { this.order.destroy(); }, @@ -848,6 +843,8 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.leftpane_width = '440px'; this.cashier_controls_visible = true; this.image_cache = new module.ImageCache(); // for faster products image display + + $('.oe_tooltip').remove(); // remove tooltip from the start session button }, start: function() { @@ -859,6 +856,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa self.$('.neworder-button').click(function(){ self.pos.add_new_order(); }); + + self.$('.deleteorder-button').click(function(){ + self.pos.delete_current_order(); + }); //when a new order is created, add an order button widget self.pos.get('orders').bind('add', function(new_order){ diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index e28ea1c06b0..67df69879ee 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -12,7 +12,8 @@
- + +
    From 84cd290018016a068a67a087d5409ff2af8bda1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 5 Sep 2013 15:31:59 +0200 Subject: [PATCH 2/6] [IMP] point_of_sale: remove hover effects, and change button colors when pressed instead (use :active) + tooltip removal was not always effective bzr revid: fva@openerp.com-20130905133159-8pu2kd8ex5tdbirl --- addons/point_of_sale/static/src/css/pos.css | 34 +++++++------------ addons/point_of_sale/static/src/js/widgets.js | 11 +++--- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 2294513a389..d78276b2b97 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -222,7 +222,7 @@ .point-of-sale #rightheader .header-button:last-child{ border-left: 1px solid #3a3a3a; } -.point-of-sale #rightheader .header-button:hover{ +.point-of-sale #rightheader .header-button:active{ background: rgba(0,0,0,0.2); text-shadow: #000 0px 0px 3px; color:#EEE; @@ -314,18 +314,10 @@ border-top: 1px solid #efefef; font-size: 14px; } -.point-of-sale #paypad button, .point-of-sale #numpad button, .point-of-sale .popup button{ - position: relative; - top: 0; - -webkit-transition: top 150ms linear; - -moz-transition: top 150ms linear; - -ms-transition: top 150ms linear; - transition: top 150ms linear; -} -.point-of-sale #paypad button:active, .point-of-sale #numpad button:active, .point-of-sale .popup button:active{ - top:3px; -} -.point-of-sale #paypad button:hover, .point-of-sale #numpad button:hover, .point-of-sale #numpad .selected-mode, .point-of-sale .popup button:hover { +.point-of-sale #paypad button:active, +.point-of-sale #numpad button:active, +.point-of-sale #numpad .selected-mode, +.point-of-sale .popup button:active{ border: none; color: white; background: #7f82ac; @@ -525,7 +517,7 @@ -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); } -.point-of-sale .category-simple-button:hover { +.point-of-sale .category-simple-button:active{ color: white; background: #7f82ac; border: 1px solid #7f82ac; @@ -1056,13 +1048,13 @@ -moz-transition: background 250ms ease-in-out; transition: background 250ms ease-in-out; } -.point-of-sale .order .orderline:hover{ +.point-of-sale .order .orderline:active{ background: rgba(140,143,183,0.05); -webkit-transition: background 50ms ease-in-out; -moz-transition: background 50ms ease-in-out; transition: background 50ms ease-in-out; } -.point-of-sale .order .orderline.empty:hover{ +.point-of-sale .order .orderline.empty:active{ background: transparent; cursor: default; } @@ -1163,7 +1155,7 @@ .point-of-sale .pos-actionbar .button .icon{ margin-top: 10px; } -.point-of-sale .pos-actionbar .button:hover { +.point-of-sale .pos-actionbar .button:active{ color: white; background: #7f82ac; border: 1px solid #7f82ac; @@ -1180,7 +1172,7 @@ .point-of-sale .pos-actionbar .button.disabled *{ opacity: 0.5; } -.point-of-sale .pos-actionbar .button.disabled:hover{ +.point-of-sale .pos-actionbar .button.disabled:active{ border: 1px solid #cacaca; border-radius: 4px; color: #555; @@ -1249,7 +1241,7 @@ display: block; cursor:pointer; } -.point-of-sale .debug-widget .button:hover{ +.point-of-sale .debug-widget .button:active{ background: rgba(96,21,177,0.45); } .point-of-sale .debug-widget input{ @@ -1337,7 +1329,7 @@ -moz-box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); box-shadow: 0px 2px 2px rgba(0,0,0, 0.3); } -.point-of-sale .popup .button:hover { +.point-of-sale .popup .button:active{ color: white; background: #7f82ac; border: 1px solid #7f82ac; @@ -1405,7 +1397,7 @@ -moz-transition: all 250ms ease-in-out; transition: all 250ms ease-in-out; } -.point-of-sale .scrollbar .button:hover{ +.point-of-sale .scrollbar .button:active{ text-shadow: rgba(255,255,255,0.8) 0px 0px 15px; } .point-of-sale .scrollbar .button.disabled{ diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index dc00c995480..c67bb609a96 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -62,10 +62,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa start: function() { this.state.bind('change:mode', this.changedMode, this); this.changedMode(); - this.$el.find('button#numpad-backspace').click(_.bind(this.clickDeleteLastChar, this)); - this.$el.find('button#numpad-minus').click(_.bind(this.clickSwitchSign, this)); - this.$el.find('button.number-char').click(_.bind(this.clickAppendNewChar, this)); - this.$el.find('button.mode-button').click(_.bind(this.clickChangeMode, this)); + this.$el.find('.numpad-backspace').click(_.bind(this.clickDeleteLastChar, this)); + this.$el.find('.numpad-minus').click(_.bind(this.clickSwitchSign, this)); + this.$el.find('.number-char').click(_.bind(this.clickAppendNewChar, this)); + this.$el.find('.mode-button').click(_.bind(this.clickChangeMode, this)); }, clickDeleteLastChar: function() { return this.state.deleteLastChar(); @@ -844,12 +844,13 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.cashier_controls_visible = true; this.image_cache = new module.ImageCache(); // for faster products image display - $('.oe_tooltip').remove(); // remove tooltip from the start session button }, start: function() { var self = this; return self.pos.ready.done(function() { + $('.oe_tooltip').remove(); // remove tooltip from the start session button + self.build_currency_template(); self.renderElement(); From 3443f6cd9e01dd386e24607f70858dbd829c53a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 5 Sep 2013 15:38:31 +0200 Subject: [PATCH 3/6] [IMP] point_of_sale: forgot a few changes related to previous commit bzr revid: fva@openerp.com-20130905133831-5nwuy2njplvekd2y --- addons/point_of_sale/static/src/xml/pos.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 67df69879ee..9794b989b65 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -94,10 +94,10 @@
    - + -
    From 74a550dae851217e8f17fd99b29a7e150e28776f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Thu, 5 Sep 2013 15:42:17 +0200 Subject: [PATCH 4/6] [IMP] point_of_sale: this line doesn't make any sense and should never have been committed bzr revid: fva@openerp.com-20130905134217-2ij45szmu4tzsqiw --- addons/point_of_sale/static/src/js/widgets.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index c67bb609a96..8afd3df6255 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -189,8 +189,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa }else if( mode === 'price'){ order.getSelectedLine().set_unit_price(val); } - } else { - this.pos.get('selectedOrder').destroy(); } }, change_selected_order: function() { From 44967200e4a5a6b1748c25d4e6044e8492fbdb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Fri, 6 Sep 2013 17:34:48 +0200 Subject: [PATCH 5/6] [IMP] point_of_sale: removed the ScaleInvite Screen in cashier mode, + only update the weight value in ScaleScreen bzr revid: fva@openerp.com-20130906153448-daev4x2ea3rf4pug --- addons/point_of_sale/static/src/js/devices.js | 23 +++++++++++----- addons/point_of_sale/static/src/js/screens.js | 27 ++++++++++--------- addons/point_of_sale/static/src/xml/pos.xml | 5 ++-- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 9a74eec0c05..7e847abdc32 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -84,10 +84,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal weighting_start: function(){ if(!this.weighting){ this.weighting = true; - if(!this.bypass_proxy){ - this.weight = 0; - return this.message('weighting_start'); - } + this.weight = 0; + return this.message('weighting_start'); + }else{ + console.error('Weighting already started!!!'); + this.weight = 0; } }, @@ -96,6 +97,9 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // and a weighting_end() weighting_read_kg: function(){ var self = this; + if(!this.weighting){ + console.error('Weighting while not started!!!'); + } this.message('weighting_read_kg',{}) .done(function(weight){ if(self.weighting){ @@ -123,9 +127,14 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // the client has finished weighting products weighting_end: function(){ - this.weight = 0; - this.weighting = false; - this.message('weighting_end'); + if(this.weighting){ + this.weight = 0; + this.weighting = false; + this.message('weighting_end'); + }else{ + console.error('Weighting already ended !!!'); + this.weight = 0; + } }, // the pos asks the client to pay 'price' units diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 8c588adadf3..f8bf0df1590 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -271,19 +271,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa }); var self = this; - var cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier'; + this.cashier_mode = this.pos_widget.screen_selector.get_user_mode() === 'cashier'; - this.pos_widget.set_numpad_visible(this.show_numpad && cashier_mode); + this.pos_widget.set_numpad_visible(this.show_numpad && this.cashier_mode); this.pos_widget.set_leftpane_visible(this.show_leftpane); - this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !cashier_mode); - this.pos_widget.set_cashier_controls_visible(cashier_mode); + this.pos_widget.set_left_action_bar_visible(this.show_leftpane && !this.cashier_mode); + this.pos_widget.set_cashier_controls_visible(this.cashier_mode); - if(cashier_mode && this.pos.iface_self_checkout){ + if(this.cashier_mode && this.pos.iface_self_checkout){ this.pos_widget.client_button.show(); }else{ this.pos_widget.client_button.hide(); } - if(cashier_mode){ + if(this.cashier_mode){ this.pos_widget.close_button.show(); }else{ this.pos_widget.close_button.hide(); @@ -460,7 +460,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa clearInterval(this.intervalID); self.pos_widget.screen_selector.set_current_screen(self.next_screen); } - },100); + },50); this.add_action_button({ label: _t('Back'), @@ -512,9 +512,9 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa var weight = self.pos.proxy.weighting_read_kg(); if(weight != self.weight){ self.weight = weight; - self.renderElement(); + self.$('.js-weight').text(self.get_product_weight_string()); } - },100); + },50); }, renderElement: function(){ var self = this; @@ -544,8 +544,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa var product = this.get_product(); return (product ? product.get('price') : 0) || 0; }, - get_product_weight: function(){ - return this.weight || 0; + get_product_weight_string: function(){ + return (this.weight || 0).toFixed(3) + ' Kg'; }, close: function(){ this._super(); @@ -740,7 +740,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa module.ProductScreenWidget = module.ScreenWidget.extend({ template:'ProductScreenWidget', - scale_screen: 'scale_invite', + scale_screen: 'scale', + client_scale_screen : 'scale_invite', client_next_screen: 'client_payment', show_numpad: true, @@ -754,7 +755,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa this.product_list_widget = new module.ProductListWidget(this,{ click_product_action: function(product){ if(product.get('to_weight') && self.pos.iface_electronic_scale){ - self.pos_widget.screen_selector.set_current_screen(self.scale_screen, {product: product}); + self.pos_widget.screen_selector.set_current_screen( self.cashier_mode ? self.scale_screen : self.client_scale_screen, {product: product}); }else{ self.pos.get('selectedOrder').addProduct(product); } diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 9794b989b65..5c5faf3fc82 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -192,8 +192,9 @@

    - - Kg + + +

    From b38c27103158a751e408994c6d0e0cc03df1bf7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Wed, 11 Sep 2013 17:41:55 +0200 Subject: [PATCH 6/6] [IMP] point_of_sale: big reworking of the scale screens: - no welcome scale in cashier mode, as it was an unnecessary delay for people who use it all day - no more parallel calls to the proxy which could reorder or make the driver crash - auto throttling of the proxy calls -> max 20 reads per seconds, but can go slower as well. - faster and cleaner update of the weight in the scale screen bzr revid: fva@openerp.com-20130911154155-rs600cixvftvkhoi --- addons/point_of_sale/controllers/main.py | 39 ++++- addons/point_of_sale/static/src/js/devices.js | 138 +++++++++++++----- addons/point_of_sale/static/src/js/models.js | 1 + addons/point_of_sale/static/src/js/screens.js | 103 ++++++------- 4 files changed, 179 insertions(+), 102 deletions(-) diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py index db5d5a25018..e0bd19545a6 100644 --- a/addons/point_of_sale/controllers/main.py +++ b/addons/point_of_sale/controllers/main.py @@ -3,12 +3,17 @@ import logging import simplejson import os import openerp +import time +import random from openerp.addons.web import http from openerp.addons.web.http import request from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template class PointOfSaleController(http.Controller): + def __init__(self): + self.scale = 'closed' + self.scale_weight = 0.0 @http.route('/pos/app', type='http', auth='admin') def app(self): @@ -64,6 +69,10 @@ class PointOfSaleController(http.Controller): return m + @http.route('/pos/test_connection', type='json', auth='admin') + def test_connection(self): + return + @http.route('/pos/scan_item_success', type='json', auth='admin') def scan_item_success(self, ean): """ @@ -98,18 +107,38 @@ class PointOfSaleController(http.Controller): @http.route('/pos/weighting_start', type='json', auth='admin') def weighting_start(self): - print "weighting_start" + if self.scale == 'closed': + print "Opening (Fake) Connection to Scale..." + self.scale = 'open' + self.scale_weight = 0.0 + time.sleep(0.1) + print "... Scale Open." + else: + print "WARNING: Scale already Connected !!!" return @http.route('/pos/weighting_read_kg', type='json', auth='admin') def weighting_read_kg(self): - print "weighting_read_kg" - return 3.14 + if self.scale == 'open': + print "Reading Scale..." + time.sleep(0.025) + self.scale_weight += 0.01 + print "... Done." + return self.scale_weight + else: + print "WARNING: Reading closed scale !!!" + return 0.0 @http.route('/pos/weighting_end', type='json', auth='admin') def weighting_end(self): - print "weighting_end" - return + if self.scale == 'open': + print "Closing Connection to Scale ..." + self.scale = 'closed' + self.scale_weight = 0.0 + time.sleep(0.1) + print "... Scale Closed." + else: + print "WARNING: Scale already Closed !!!" @http.route('/pos/payment_request', type='json', auth='admin') def payment_request(self, price): diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 7e847abdc32..2be9cb74da2 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -1,6 +1,81 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sale + // the JobQueue schedules a sequence of 'jobs'. each job is + // a function returning a deferred. the queue waits for each job to finish + // before launching the next. Each job can also be scheduled with a delay. + // the is used to prevent parallel requests to the proxy. + + module.JobQueue = function(){ + var queue = []; + var running = false; + var scheduled_end_time = 0; + var end_of_queue = (new $.Deferred()).resolve(); + var stoprepeat = false; + + var run = function(){ + if(end_of_queue.state() === 'resolved'){ + end_of_queue = new $.Deferred(); + } + if(queue.length > 0){ + running = true; + var job = queue[0]; + if(!job.opts.repeat || stoprepeat){ + queue.shift(); + stoprepeat = false; + } + + // the time scheduled for this job + scheduled_end_time = (new Date()).getTime() + (job.opts.duration || 0); + + // we run the job and put in def when it finishes + var def = job.fun() || (new $.Deferred()).resolve(); + + // we don't care if a job fails ... + def.always(function(){ + // we run the next job after the scheduled_end_time, even if it finishes before + setTimeout(function(){ + run(); + }, Math.max(0, scheduled_end_time - (new Date()).getTime()) ); + }); + }else{ + running = false; + end_of_queue.resolve(); + } + }; + + // adds a job to the schedule. + // opts : { + // duration : the job is guaranteed to finish no quicker than this (milisec) + // repeat : if true, the job will be endlessly repeated + // important : if true, the scheduled job cannot be canceled by a queue.clear() + // } + this.schedule = function(fun, opts){ + queue.push({fun:fun, opts:opts || {}}); + if(!running){ + run(); + } + } + + // remove all jobs from the schedule (except the ones marked as important) + this.clear = function(){ + queue = _.filter(queue,function(job){job.opts.important === true}); + }; + + // end the repetition of the current job + this.stoprepeat = function(){ + stoprepeat = true; + }; + + // returns a deferred that resolves when all scheduled + // jobs have been run. + // ( jobs added after the call to this method are considered as well ) + this.finished = function(){ + return end_of_queue; + } + + }; + // this object interfaces with the local proxy to communicate to the various hardware devices // connected to the Point of Sale. As the communication only goes from the POS to the proxy, // methods are used both to signal an event, and to fetch information. @@ -10,7 +85,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal options = options || {}; url = options.url || 'http://localhost:8069'; - this.weight = 0; this.weighting = false; this.debug_weight = 0; this.use_debug_weight = false; @@ -35,18 +109,11 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal this.connection.destroy(); }, message : function(name,params){ - var ret = new $.Deferred(); var callbacks = this.notifications[name] || []; for(var i = 0; i < callbacks.length; i++){ callbacks[i](params); } - - this.connection.rpc('/pos/' + name, params || {}).done(function(result) { - ret.resolve(result); - }).fail(function(error) { - ret.reject(error); - }); - return ret; + return this.connection.rpc('/pos/' + name, params || {}); }, // this allows the client to be notified when a proxy call is made. The notification @@ -82,14 +149,32 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal //the client is starting to weight weighting_start: function(){ + var ret = new $.Deferred(); if(!this.weighting){ this.weighting = true; - this.weight = 0; - return this.message('weighting_start'); + this.message('weighting_start').always(function(){ + ret.resolve(); + }); }else{ console.error('Weighting already started!!!'); - this.weight = 0; + ret.resolve(); } + return ret; + }, + + // the client has finished weighting products + weighting_end: function(){ + var ret = new $.Deferred(); + if(this.weighting){ + this.weighting = false; + this.message('weighting_end').always(function(){ + ret.resolve(); + }); + }else{ + console.error('Weighting already ended !!!'); + ret.resolve(); + } + return ret; }, //returns the weight on the scale. @@ -97,20 +182,14 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal // and a weighting_end() weighting_read_kg: function(){ var self = this; - if(!this.weighting){ - console.error('Weighting while not started!!!'); - } + var ret = new $.Deferred(); this.message('weighting_read_kg',{}) - .done(function(weight){ - if(self.weighting){ - if(self.use_debug_weight){ - self.weight = self.debug_weight; - }else{ - self.weight = weight; - } - } + .then(function(weight){ + ret.resolve(self.use_debug_weight ? self.debug_weight : weight); + }, function(){ //failed to read weight + ret.resolve(self.use_debug_weight ? self.debug_weight : 0.0); }); - return this.weight; + return ret; }, // sets a custom weight, ignoring the proxy returned value. @@ -125,17 +204,6 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal this.debug_weight = 0; }, - // the client has finished weighting products - weighting_end: function(){ - if(this.weighting){ - this.weight = 0; - this.weighting = false; - this.message('weighting_end'); - }else{ - console.error('Weighting already ended !!!'); - this.weight = 0; - } - }, // the pos asks the client to pay 'price' units payment_request: function(price){ diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index cfaecf80a70..fbbb15af9c7 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -24,6 +24,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal this.barcode_reader = new module.BarcodeReader({'pos': this}); // used to read barcodes this.proxy = new module.ProxyDevice(); // used to communicate to the hardware devices via a local proxy + this.proxy_queue = new module.JobQueue(); // used to prevent parallels communications to the proxy this.db = new module.PosLS(); // a database used to store the products and categories this.db.clear('products','categories'); this.debug = jQuery.deparam(jQuery.param.querystring()).debug !== undefined; //debug mode diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index f8bf0df1590..854c1e1adec 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -441,7 +441,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa module.ErrorInvoiceTransferPopupWidget = module.ErrorPopupWidget.extend({ template: 'ErrorInvoiceTransferPopupWidget', }); - + module.ScaleInviteScreenWidget = module.ScreenWidget.extend({ template:'ScaleInviteScreenWidget', @@ -451,30 +451,35 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa show: function(){ this._super(); var self = this; + var queue = this.pos.proxy_queue; - self.pos.proxy.weighting_start(); - - this.intervalID = setInterval(function(){ - var weight = self.pos.proxy.weighting_read_kg(); - if(weight > 0.001){ - clearInterval(this.intervalID); - self.pos_widget.screen_selector.set_current_screen(self.next_screen); - } - },50); + queue.schedule(function(){ + return self.pos.proxy.weighting_start(); + },{ unclearable: true }); + + queue.schedule(function(){ + return self.pos.proxy.weighting_read_kg().then(function(weight){ + if(weight > 0.001){ + self.pos_widget.screen_selector.set_current_screen(self.next_screen); + } + }); + },{duration: 100, repeat: true}); this.add_action_button({ label: _t('Back'), icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png', click: function(){ - clearInterval(this.intervalID); self.pos_widget.screen_selector.set_current_screen(self.previous_screen); } }); }, close: function(){ this._super(); - clearInterval(this.intervalID); - this.pos.proxy.weighting_end(); + var self = this; + this.pos.proxy_queue.clear(); + this.pos.proxy_queue.schedule(function(){ + return self.pos.proxy.weighting_end(); + },{ unclearable: true }); }, }); @@ -486,9 +491,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa show: function(){ this._super(); - this.renderElement(); var self = this; + var queue = this.pos.proxy_queue; + this.set_weight(0); + this.renderElement(); this.add_action_button({ label: _t('Back'), @@ -507,14 +514,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa }, }); - this.pos.proxy.weighting_start(); - this.intervalID = setInterval(function(){ - var weight = self.pos.proxy.weighting_read_kg(); - if(weight != self.weight){ - self.weight = weight; - self.$('.js-weight').text(self.get_product_weight_string()); - } - },50); + queue.schedule(function(){ + return self.pos.proxy.weighting_start() + },{ unclearable: true }); + + queue.schedule(function(){ + return self.pos.proxy.weighting_read_kg().then(function(weight){ + self.set_weight(weight); + }); + },{duration:50, repeat: true}); + }, renderElement: function(){ var self = this; @@ -544,54 +553,24 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa var product = this.get_product(); return (product ? product.get('price') : 0) || 0; }, + set_weight: function(weight){ + this.weight = weight; + this.$('.js-weight').text(this.get_product_weight_string()); + }, get_product_weight_string: function(){ return (this.weight || 0).toFixed(3) + ' Kg'; }, close: function(){ + var self = this; this._super(); - clearInterval(this.intervalID); - this.pos.proxy.weighting_end(); + + this.pos.proxy_queue.clear(); + this.pos.proxy_queue.schedule(function(){ + self.pos.proxy.weighting_end(); + },{ unclearable: true }); }, }); - // the JobQueue schedules a sequence of 'jobs'. each job is - // a function returning a deferred. the queue waits for each job to finish - // before launching the next. Each job can also be scheduled with a delay. - // the queue jobqueue is used to prevent parallel requests to the payment terminal. - - module.JobQueue = function(){ - var queue = []; - var running = false; - var run = function(){ - if(queue.length > 0){ - running = true; - var job = queue.shift(); - setTimeout(function(){ - var def = job.fun(); - if(def){ - def.done(run); - }else{ - run(); - } - },job.delay || 0); - }else{ - running = false; - } - }; - - // adds a job to the schedule. - this.schedule = function(fun, delay){ - queue.push({fun:fun, delay:delay}); - if(!running){ - run(); - } - } - - // remove all jobs from the schedule - this.clear = function(){ - queue = []; - }; - }; module.ClientPaymentScreenWidget = module.ScreenWidget.extend({ template:'ClientPaymentScreenWidget',