From ccf07028cfb17649912e34c87509f2be8b794d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Mon, 30 Jul 2012 17:41:43 +0200 Subject: [PATCH] [IMP] point_of_sale: various fixes and improvements bzr revid: fva@openerp.com-20120730154143-kz57aldzo1yalfx9 --- addons/point_of_sale/static/src/css/pos.css | 47 +++++++++++++++ addons/point_of_sale/static/src/js/TODO.txt | 45 +++++++-------- .../static/src/js/pos_devices.js | 26 ++++++--- .../point_of_sale/static/src/js/pos_models.js | 11 +--- .../static/src/js/pos_screens.js | 44 ++++++++++---- .../static/src/js/pos_widgets.js | 57 ++++++++++++++----- addons/point_of_sale/static/src/xml/pos.xml | 5 +- 7 files changed, 168 insertions(+), 67 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index b03633e181c..07838e0f209 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -658,8 +658,16 @@ display: inline-block; font-size: 1.5em; } +.point-of-sale .greyed-out{ + color: #AAA; +} +.point-of-sale .pos-step-container input{ + font-size: 1em; +} + .point-of-sale .pos-payment-container { text-align: left; + min-width: 500px; } .point-of-sale .pos-payment-container .left-block{ display: inline-block; @@ -821,6 +829,28 @@ font-family: "Inconsolata"; } +/* e) The Welcome Screen */ +.point-of-sale .goodbye-message{ + position: absolute; + left:50%; + top:30%; + width:500px; + height:400px; + margin-left: -250px; + margin-top: -200px; + padding:10px; + padding-top:20px; + text-align:center; + font-size:20px; + font-weight:bold; + background-color: #F0EEEE; + border: 1px solid #E0DDDD; + -webkit-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -moz-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + -ms-box-shadow: 0px 10px 20px rgba(0,0,0, 0.3); + z-index:1150; +} + /* ********* The OrderWidget ********* */ .point-of-sale .order-container{ @@ -1017,6 +1047,23 @@ -webkit-transition-timing-function: ease-out; } +.point-of-sale .pos-actionbar .button.disabled{ + color:#AAA; +} +.point-of-sale .pos-actionbar .button.disabled:hover{ + border: 1px solid #cacaca; + border-radius: 4px; + + background: #e2e2e2; + background: -webkit-linear-gradient(#f0f0f0, #e2e2e2); + background: -moz-linear-gradient(#f0f0f0, #e2e2e2); + background: -ms-linear-gradient(#f0f0f0, #e2e2e2); + background: linear-gradient(#f0f0f0, #e2e2e2); + -webkit-box-shadow: 0px 2px 2px rgba(0,0,0, 0.1); + -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 .pos-actionbar .button.rightalign{ float:right; } diff --git a/addons/point_of_sale/static/src/js/TODO.txt b/addons/point_of_sale/static/src/js/TODO.txt index 2742a0ffa49..74148ce4e80 100644 --- a/addons/point_of_sale/static/src/js/TODO.txt +++ b/addons/point_of_sale/static/src/js/TODO.txt @@ -1,35 +1,30 @@ -- Affichage Catégories doivent respecter le poids -- Affichage Catégories par poids doivent avoir une hiérarchie plate -- Le Onscreen keyboard ne fonctionne plus -- Scrolling dans la sélection produit et dans +v Affichage Catégories doivent respecter le poids +v Affichage Catégories par poids doivent avoir une hiérarchie plate +v Le Onscreen keyboard ne fonctionne plus +v Scrolling dans la sélection produit et dans la liste de courses -- Réductions -- Redesign liste de courses +v Réductions +v Redesign liste de courses - Redesign du receipt -- générer les données de printing -- popups d'erreur certainement buggés -- bugs scans produits par prix par poids -- si pas photo photo par defaut +x générer les données de printing +x popups d'erreur certainement buggés +v bugs scans produits par prix par poids +v si pas photo photo par defaut - WebSQL -- Redesign header, bouton retour aux backend en mode caissière -- bouton exit en mode caissière doit retourner au backend. +v Redesign header, bouton retour aux backend en mode caissière +v bouton exit en mode caissière doit retourner au backend. - user preferences : désactiver la balance etc. - posting orders - login alternatif -- bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand -- si case print via proxy cochée, alors on skip l'ecran receipt -- bouton exit self checkout -- demarrage en mode self-checkout si self-checkout -- produits poid code barre non reconnus -- activer popup client aux écrans self-checkout -- discount pas plus grand que 100 +v bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand +v si case print via proxy cochée, alors on skip l'ecran receipt +v bouton exit self checkout +v demarrage en mode self-checkout si self-checkout +v produits poid code barre non reconnus +v activer popup client aux écrans self-checkout +v discount pas plus grand que 100 - numpad state parfois sans state - numpad state en mode pesée ?? le cacher ? -- numpad dans l'écran payment pour pouvoir entrer le montant +v numpad dans l'écran payment pour pouvoir entrer le montant - différence de total. Methode d'arrondis -Bellevue Kriek -Chaufontaine pétillante 33 -Boon Framboise -Chaufontaine pétillante 50 avec discount 50% -- mettre toutes les lignes de payement dans l'order diff --git a/addons/point_of_sale/static/src/js/pos_devices.js b/addons/point_of_sale/static/src/js/pos_devices.js index 39a977fdfea..c7d96eccffc 100644 --- a/addons/point_of_sale/static/src/js/pos_devices.js +++ b/addons/point_of_sale/static/src/js/pos_devices.js @@ -17,7 +17,9 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal delay_payment: function(){ this.activate(); this.payment_status = 'waiting_for_payment'; }, }))(); - //window.debug_devices = debug_devices; + if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ + window.debug_devices = debug_devices; + } // 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, @@ -42,21 +44,26 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal success_callback = success_callback || function(){}; error_callback = error_callback || function(){}; - if(debug_devices && debug_devices.active){ + + if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ console.log('PROXY:',name,params); - }else{ + } + + if(!(debug_devices && debug_devices.active)){ this.connection.rpc('/pos/'+name, params || {}, success_callback, error_callback); } }, //a product has been scanned and recognized with success - scan_item_success: function(){ - this.message('scan_item_success'); + // ean is a parsed ean object + scan_item_success: function(ean){ + this.message('scan_item_success',ean); }, - //a product has been scanned but not recognized - scan_item_error_unrecognized: function(){ - this.message('scan_item_error_unrecognized'); + // a product has been scanned but not recognized + // ean is a parsed ean object + scan_item_error_unrecognized: function(ean){ + this.message('scan_item_error_unrecognized',ean); }, //the client is asking for help @@ -331,6 +338,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal value: 0, unit: 'none', }; + console.log('ean',ean); function match_prefix(prefix_set, type){ for(prefix in prefix_set){ @@ -402,7 +410,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal var parse_result = self.parse_ean(codeNumbers.join('')); if (parse_result.type === 'error') { //most likely a checksum error, raise warning - console.error('ERROR: barcode checksum error:',parse_result); + console.warn('WARNING: barcode checksum error:',parse_result); }else if(parse_result.type in {'unit':'', 'weight':'', 'price':''}){ //ean is associated to a product if(self.action_callback['product']){ self.action_callback['product'](parse_result); diff --git a/addons/point_of_sale/static/src/js/pos_models.js b/addons/point_of_sale/static/src/js/pos_models.js index fd1e3fb384f..548b6b749f9 100644 --- a/addons/point_of_sale/static/src/js/pos_models.js +++ b/addons/point_of_sale/static/src/js/pos_models.js @@ -608,15 +608,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal return false; }else if(this.get_discount() > 0){ // we don't merge discounted orderlines return false; - }else if(this.get_product_type() === 'unit'){ - return true; - }else if(this.get_product_type() === 'weight'){ - return true; - }else if(this.get_product_type() === 'price'){ - return this.get_product().get('list_price') === orderline.get_product().get('list_price'); - }else{ - console.error('point_of_sale/pos_models.js/Orderline.can_be_merged_with() : unknown product type:',this.get('product_type')); + }else if(this.price !== orderline.price){ return false; + }else{ + return true; } }, merge: function(orderline){ diff --git a/addons/point_of_sale/static/src/js/pos_screens.js b/addons/point_of_sale/static/src/js/pos_screens.js index 93e25ab21dc..f79b60052c0 100644 --- a/addons/point_of_sale/static/src/js/pos_screens.js +++ b/addons/point_of_sale/static/src/js/pos_screens.js @@ -160,12 +160,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa // there's an error. barcode_product_action: function(ean){ if(this.pos_widget.scan_product(ean)){ - this.pos.proxy.scan_item_success(); + this.pos.proxy.scan_item_success(ean); if(this.barcode_product_screen){ this.pos_widget.screen_selector.set_current_screen(this.barcode_product_screen); } }else{ - if(this.barcode_product_error_popup){ + this.pos.proxy.scan_item_error_unrecognized(ean); + if(this.barcode_product_error_popup && this.pos_widget.screen_selector.get_user_mode() !== 'cashier'){ this.pos_widget.screen_selector.show_popup(this.barcode_product_error_popup); } } @@ -186,6 +187,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa return true; } } + this.pos.proxy.scan_item_unrecognized(ean); return false; }, @@ -199,9 +201,11 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa if(users[i].ean13 === ean.ean){ this.pos.get('selectedOrder').set_client(users[i]); this.pos_widget.username.refresh(); + this.pos.proxy.scan_item_success(ean); return true; } } + this.pos.proxy.scan_item_unrecognized(ean); return false; //TODO start the transaction }, @@ -209,6 +213,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa // what happens when a discount barcode is scanned : the default behavior // is to set the discount on the last order. barcode_discount_action: function(ean){ + this.pos.proxy.scan_item_success(ean); var last_orderline = this.pos.get('selectedOrder').getLastOrderline(); if(last_orderline){ last_orderline.set_discount(ean.value) @@ -241,6 +246,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa // this method shows the screen and sets up all the widget related to this screen. Extend this method // if you want to alter the behavior of the screen. show: function(){ + var self = this; + this.hidden = false; if(this.$element){ this.$element.show(); @@ -251,14 +258,22 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa }else{ this.hide_action_bar(); } + + // we add the help button by default. we do this because the buttons are cleared on each refresh so that + // the button stay local to each screen + this.pos_widget.left_action_bar.add_new_button({ + label: 'help', + icon: '/point_of_sale/static/src/img/icons/png48/help.png', + click: function(){ self.help_button_action(); }, + }); var self = this; var 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_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.action_bar.set_element_visible('help-button', !cashier_mode, function(){ self.help_button_action(); });*/ if(cashier_mode && this.pos.use_selfcheckout){ this.pos_widget.client_button.show(); @@ -287,9 +302,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa if(this.pos.barcode_reader){ this.pos.barcode_reader.reset_action_callbacks(); } - if(this.pos_widget.action_bar){ - this.pos_widget.action_bar.destroy_buttons(); - } + this.pos_widget.action_bar.destroy_buttons(); + this.pos_widget.left_action_bar.destroy_buttons(); }, // this methods hides the screen. It's not a good place to put your cleanup stuff as it is called on the @@ -559,12 +573,17 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa barcode_client_action: function(ean){ this._super(ean); - this.pos_widget.screen_selector.set_current_screen(self.next_screen); + this.pos_widget.screen_selector.set_current_screen(this.next_screen); }, show: function(){ this._super(); var self = this; + $('.goodbye-message').css({opacity:1}).show(); + setTimeout(function(){ + console.log('kill'); + $('.goodbye-message').animate({opacity:0},500,'swing',function(){$('.goodbye-message').hide();}); + },3000); }, }); @@ -731,18 +750,23 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa validateCurrentOrder: function() { var self = this; var currentOrder = this.pos.get('selectedOrder'); - - this.validate_button.$element.attr('disabled','disabled'); //FIXME is the css actually using this attr ? + if(this.busy){ + return; + }else{ + this.busy = true; + } + this.validate_button.$element.addClass('disabled'); this.pos.push_order(currentOrder.exportAsJSON()) .then(function() { - self.validate_button.$element.removeAttr('disabled'); if(self.pos.use_proxy_printer){ self.pos.proxy.print_receipt(currentOrder.export_for_printing()); self.pos.get('selectedOrder').destroy(); //finish order and go back to scan screen }else{ self.pos_widget.screen_selector.set_current_screen(self.next_screen); } + self.validate_button.$element.removeClass('disabled'); + self.busy = false; }); }, bindPaymentLineEvents: function() { diff --git a/addons/point_of_sale/static/src/js/pos_widgets.js b/addons/point_of_sale/static/src/js/pos_widgets.js index a8948437f2c..b41ccbfcc9a 100644 --- a/addons/point_of_sale/static/src/js/pos_widgets.js +++ b/addons/point_of_sale/static/src/js/pos_widgets.js @@ -112,7 +112,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa template:'OrderWidget', init: function(parent, options) { this._super(parent,options); - this.compact = options.compact !== undefined ? options.compact : true; + this.display_mode = options.display_mode || 'numpad'; // 'maximized' | 'actionbar' | 'numpad' this.set_numpad_state(options.numpadState); this.pos.bind('change:selectedOrder', this.change_selected_order, this); this.bind_orderline_events(); @@ -165,9 +165,12 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa var self = this; this._super(); - if(!this.compact){ - console.log('not compact'); + if(this.display_mode === 'maximized'){ $('.point-of-sale .order-container').css({'bottom':'0px'}); + }else if(this.display_mode === 'actionbar'){ + $('.point-of-sale .order-container').css({'bottom':'105px'}); + }else if(this.display_mode !== 'numpad'){ + console.error('ERROR: OrderWidget renderElement(): wrong display_mode:',this.display_mode); } var $content = this.$('.orderlines'); @@ -212,10 +215,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa var total = order ? order.getTotal() : 0; this.$('.summary .value.total').html(this.format_currency(total)); }, - set_compact: function(compact){ - console.log('set_compact',compact); - if(this.compact !== compact){ - this.compact = compact; + set_display_mode: function(mode){ + if(this.display_mode !== mode){ + this.display_mode = mode; this.renderElement(); } }, @@ -368,7 +370,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa add_new_button: function(button_options){ var button = new module.ActionButtonWidget(this,button_options); this.button_list.push(button); - button.appendTo($('.pos-actionbar-button-list')); + button.appendTo(this.$('.pos-actionbar-button-list')); return button; }, show:function(){ @@ -671,14 +673,15 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically this.numpad_visible = true; + this.left_action_bar_visible = true; this.leftpane_visible = true; this.leftpane_width = '440px'; this.cashier_controls_visible = true; - var degree = 0; + /* //Epileptic mode setInterval(function(){ - $('body').css({'-webkit-filter':'sepia('+Math.round(Math.random())+') hue-rotate('+Math.random()*360+'deg) blur('+Math.random()*5+'px)' }); + $('body').css({'-webkit-filter':'hue-rotate('+Math.random()*360+'deg)' }); },100); */ @@ -718,9 +721,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session'); } - self.$('.loader').animate({opacity:0},3000,'swing',function(){$('.loader').hide();}); + self.$('.loader').animate({opacity:0},3000,'swing',function(){self.$('.loader').hide();}); self.$('.loader img').hide(); + if(jQuery.deparam(jQuery.param.querystring()).debug !== undefined){ + window.pos = self.pos; + window.pos_widget = self.pos_widget; + } + },function(){ // error when loading models data from the backend self.$('.loader img').hide(); return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id']) @@ -733,7 +741,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa }, self)); }); }, - // This method instantiates all the screens, widgets, etc. If you want to add new screens change the // startup screen, etc, override this method. @@ -788,6 +795,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa this.action_bar = new module.ActionBarWidget(this); this.action_bar.appendTo($(".point-of-sale #rightpane")); + this.left_action_bar = new module.ActionBarWidget(this); + this.left_action_bar.appendTo($(".point-of-sale #leftpane")); + this.paypad = new module.PaypadWidget(this, {}); this.paypad.replace($('#placeholder-PaypadWidget')); @@ -895,16 +905,35 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa if(visible !== this.numpad_visible){ this.numpad_visible = visible; if(visible){ + this.set_left_action_bar_visible(false); this.numpad.show(); this.paypad.show(); - this.order_widget.set_compact(true); + this.order_widget.set_display_mode('numpad'); }else{ this.numpad.hide(); this.paypad.hide(); - this.order_widget.set_compact(false); + if(this.order_widget.display_mode === 'numpad'){ + this.order_widget.set_display_mode('maximized'); + } } } }, + set_left_action_bar_visible: function(visible){ + if(visible !== this.left_action_bar_visible){ + this.left_action_bar_visible = visible; + if(visible){ + this.set_numpad_visible(false); + this.left_action_bar.show(); + this.order_widget.set_display_mode('actionbar'); + }else{ + this.left_action_bar.hide(); + if(this.order_widget.display_mode === 'actionbar'){ + this.order_widget.set_display_mode('maximized'); + } + } + } + }, + //shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.) set_leftpane_visible: function(visible){ if(visible !== this.leftpane_visible){ diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index b690f6ebe6b..30875eb0fe5 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -237,7 +237,7 @@ -
+
Change: @@ -269,6 +269,9 @@

Please scan an item or your member card

+
+

Thank you for shopping with us.

+