From 02e829bf5077f7127a1adbef8e3bba5b05542e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sat, 20 Dec 2014 17:09:30 +0100 Subject: [PATCH 01/21] [WIP] point_of_sale: architecture cleanup : first pass - This is the first pass of a big cleanup. This breaks all backward compatibility of pos .js extensions. - PosWidget has been renamed to 'Chrome'. The Chrome is both the root widget but also a reference to all chrome subwidgets. It is the V of MVC. - ScreenSelector has been renamed to 'Gui'. The Gui contains the method to manipulate the user interface by changing screens and instanciating popups. It is the C of MVC - PosModel is still the root model, the M of MVC - All these objects are available to all Pos objects trough the .pos .chrome and .gui references. This is very practical, but usually you should not need to refer to the chrome or to the gui in your models. And you should also minimize references to .chrome, which should be manipulated via .gui - The Popups, Screens, and Widgets are now declared declaratively in the Chrome prototype for easier extension by external modules - You can hook yourself into the various loading steps trough deferreds: - pos.ready -> All models have been loaded - chrome.started -> The chrome dom is inserted but incomplete - chrome.ready -> The pos is fully rendered and ready to use - The Chrome subwidgets are available trough .widget ex: pos_widget.numpad -> chrome.widget.numpad - The GUI api has been changed a bit : - set_current_screen -> .show_screen --- addons/point_of_sale/static/src/js/devices.js | 2 +- addons/point_of_sale/static/src/js/main.js | 2 +- addons/point_of_sale/static/src/js/models.js | 48 ++- addons/point_of_sale/static/src/js/screens.js | 205 ++++----- .../static/src/js/widget_base.js | 5 +- addons/point_of_sale/static/src/js/widgets.js | 406 ++++++++---------- addons/point_of_sale/static/src/xml/pos.xml | 2 +- 7 files changed, 308 insertions(+), 362 deletions(-) diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index cc44f6f9ce1..3765e1d1ab8 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -416,7 +416,7 @@ openerp.point_of_sale.load_devices = function load_devices(instance,module){ //m send_printing_job(); },function(error){ if (error) { - self.pos.pos_widget.screen_selector.show_popup('error-traceback',{ + self.pos.chrome.screen_selector.show_popup('error-traceback',{ 'message': _t('Printing Error: ') + error.data.message, 'comment': error.data.debug, }); diff --git a/addons/point_of_sale/static/src/js/main.js b/addons/point_of_sale/static/src/js/main.js index fd0e9bd773e..5c18b9af38e 100644 --- a/addons/point_of_sale/static/src/js/main.js +++ b/addons/point_of_sale/static/src/js/main.js @@ -18,7 +18,7 @@ openerp.point_of_sale = function(openerp) { openerp.point_of_sale.load_widgets(openerp,module); // import pos_widgets.js - openerp.web.client_actions.add('pos.ui', 'openerp.point_of_sale.PosWidget'); + openerp.web.client_actions.add('pos.ui', 'openerp.point_of_sale.Chrome'); }; diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 946c7f16988..81f94dcea4d 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -3,7 +3,6 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo var QWeb = instance.web.qweb; var _t = instance.web._t; - var barcode_parser_module = instance.barcodes; var round_di = instance.web.round_decimals; var round_pr = instance.web.round_precision @@ -24,10 +23,14 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo var self = this; this.session = session; this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time - this.pos_widget = attributes.pos_widget; + this.chrome = attributes.chrome; + this.gui = attributes.gui; this.proxy = new module.ProxyDevice(this); // used to communicate to the hardware devices via a local proxy this.barcode_reader = new module.BarcodeReader({'pos': this, proxy:this.proxy}); + this.chrome.ready.then(function(){ + self.barcode_reader.connect(); + }); this.proxy_queue = new module.JobQueue(); // used to prevent parallels communications to the proxy this.db = new module.PosDB(); // a local database used to search trough products and categories & store pending orders @@ -73,16 +76,23 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo 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. // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. - this.ready = this.load_server_data() - .then(function(){ - if(self.config.use_proxy){ - return self.connect_to_proxy(); - } - }); // used to read barcodes); + this.ready = this.load_server_data(); + + }, + on_chrome_started: function(){ + this.barcode_reader.connect(); + this.load_orders(); + this.set_start_order(); + }, + on_chrome_ready: function(){ + if(this.config.use_proxy){ + return this.connect_to_proxy(); + } + this.push_order(); }, // releases ressources holds by the model at the end of life of the posmodel @@ -97,14 +107,14 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo var self = this; var done = new $.Deferred(); this.barcode_reader.disconnect_from_proxy(); - this.pos_widget.loading_message(_t('Connecting to the PosBox'),0); - this.pos_widget.loading_skip(function(){ + this.chrome.loading_message(_t('Connecting to the PosBox'),0); + this.chrome.loading_skip(function(){ self.proxy.stop_searching(); }); this.proxy.autoconnect({ force_ip: self.config.proxy_ip || undefined, progress: function(prog){ - self.pos_widget.loading_progress(prog); + self.chrome.loading_progress(prog); }, }).then(function(){ if(self.config.iface_scan_via_proxy){ @@ -119,7 +129,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo // helper function to load data from the server. Obsolete use the models loader below. fetch: function(model, fields, domain, ctx){ this._load_progress = (this._load_progress || 0) + 0.05; - this.pos_widget.loading_message(_t('Loading')+' '+model,this._load_progress); + this.chrome.loading_message(_t('Loading')+' '+model,this._load_progress); return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all() }, @@ -397,7 +407,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo }, { label: 'barcodes', loaded: function(self) { - var barcode_parser = new barcode_parser_module.BarcodeParser({'nomenclature_id': self.config.barcode_nomenclature_id}); + var barcode_parser = new instance.barcodes.BarcodeParser({'nomenclature_id': self.config.barcode_nomenclature_id}); self.barcode_reader.set_barcode_parser(barcode_parser); return barcode_parser.is_loaded(); }, @@ -417,7 +427,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo loaded.resolve(); }else{ var model = self.models[index]; - self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress); + self.chrome.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress); var cond = typeof model.condition === 'function' ? model.condition(self,tmp) : true; if (!cond) { @@ -649,7 +659,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo transfer.pipe(function(order_server_id){ // generate the pdf and download it - self.pos_widget.do_action('point_of_sale.pos_invoice_report',{additional_context:{ + self.chrome.do_action('point_of_sale.pos_invoice_report',{additional_context:{ active_ids:order_server_id, }}); @@ -722,7 +732,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo if (error.data.exception_type == 'warning') { delete error.data.debug; } - self.pos_widget.screen_selector.show_popup('error-traceback',{ + self.gui.show_popup('error-traceback',{ message: error.data.message, comment: error.data.debug }); @@ -1107,7 +1117,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo this.pos = options.pos; this.selected_orderline = undefined; this.selected_paymentline = undefined; - this.screen_data = {}; // see ScreenSelector + this.screen_data = {}; // see Gui this.temporary = options.temporary || false; this.creation_date = new Date(); this.to_invoice = false; @@ -1236,7 +1246,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo qweb.default_dict = _.clone(QWeb.default_dict); qweb.add_template(''+subreceipt+''); - return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.pos_widget,'order':self, 'receipt': receipt}) ; + return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.chrome,'order':self, 'receipt': receipt}) ; } } diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 092c85a0d47..3666a0022d8 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -1,17 +1,17 @@ // this file contains the screens definitions. Screens are the // content of the right pane of the pos, containing the main functionalities. -// screens are contained in the PosWidget, in pos_widget.js +// screens are contained in the PosWidget, in chrome.js // all screens are present in the dom at all time, but only one is shown at the // same time. // -// transition between screens is made possible by the use of the screen_selector, +// transition between screens is made possible by the use of the gui, // which is responsible of hiding and showing the screens, as well as maintaining // the state of the screens between different orders. // // all screens inherit from ScreenWidget. the only addition from the base widgets // are show() and hide() which shows and hides the screen but are also used to -// bind and unbind actions on widgets and devices. The screen_selector guarantees +// bind and unbind actions on widgets and devices. The gui guarantees // that only one screen is shown at the same time and that show() is called after all // hide()s @@ -23,37 +23,44 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var round_pr = instance.web.round_precision - module.ScreenSelector = instance.web.Class.extend({ + module.Gui = instance.web.Class.extend({ init: function(options){ - this.pos = options.pos; - this.screen_set = options.screen_set || {}; - this.popup_set = options.popup_set || {}; - this.default_screen = options.default_screen; - this.startup_screen = options.startup_screen; + var self = this; + this.pos = options.pos; + this.chrome = options.chrome; + this.screen_set = {}; + this.popup_set = {}; + this.default_screen = null; + this.startup_screen = null; this.current_popup = null; - this.current_mode = options.default_mode || 'cashier'; this.current_screen = null; - for (var screen_name in this.screen_set) { - this.screen_set[screen_name].hide(); - } - - for (var popup_name in this.popup_set) { - this.popup_set[popup_name].hide(); - } - - if (this.pos.get_order()) { - this.pos.get_order().set_screen_data({ - 'screen': this.default_screen, + this.chrome.ready.then(function(){ + var order = self.pos.get_order(); + if (order) { + self.show_saved_screen(order); + } else { + self.show_screen(self.startup_screen); + } + self.pos.bind('change:selectedOrder', function(){ + self.show_saved_screen(self.pos.get_order()); }); - } - - this.pos.bind('change:selectedOrder', this.load_saved_screen, this); + }); }, - add_screen: function(screen_name, screen){ + + add_screen: function(name, screen){ screen.hide(); - this.screen_set[screen_name] = screen; - return this; + this.screen_set[name] = screen; + }, + set_default_screen: function(name){ + this.default_screen = name; + }, + set_startup_screen: function(name) { + this.startup_screen = name; + }, + add_popup: function(name, popup) { + popup.hide(); + this.popup_set[name] = popup; }, show_popup: function(name,options){ if(this.current_popup){ @@ -69,29 +76,25 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.current_popup = null; } }, - load_saved_screen: function(options){ + + show_saved_screen: function(order,options) { options = options || {}; this.close_popup(); - var selectedOrder = this.pos.get_order(); - // FIXME : this changing screen behaviour is sometimes confusing ... - this.set_current_screen(selectedOrder.get_screen_data('screen') || options.default_screen || this.default_screen,null,'refresh'); - //this.set_current_screen(this.default_screen,null,'refresh'); - - }, - set_user_mode: function(user_mode){ - if(user_mode !== this.current_mode){ - this.close_popup(); - this.current_mode = user_mode; - this.load_saved_screen(); + if (order) { + this.show_screen(order.get_screen_data('screen') || + options.default_screen || + this.default_screen, + null,'refresh'); + } else { + this.show_screen(this.startup_screen); } }, - get_user_mode: function(){ - return this.current_mode; - }, - set_current_screen: function(screen_name,params,refresh){ + + show_screen: function(screen_name,params,refresh){ var screen = this.screen_set[screen_name]; if(!screen){ - console.error("ERROR: set_current_screen("+screen_name+") : screen not found"); + console.error("ERROR: show_screen("+screen_name+") : screen not found"); + debugger; } this.close_popup(); @@ -126,19 +129,13 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // back: function(){ var previous = this.pos.get_order().get_screen_data('previous-screen'); if(previous){ - this.set_current_screen(previous); + this.show_screen(previous); } }, get_current_screen_param: function(param){ var params = this.pos.get_order().get_screen_data('params'); return params ? params[param] : undefined; }, - set_default_screen: function(){ - this.set_current_screen(this.default_screen); - }, - change_default_screen: function(screen){ - this.default_screen = screen; - }, }); module.ScreenWidget = module.PosBaseWidget.extend({ @@ -152,7 +149,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }, help_button_action: function(){ - this.pos_widget.screen_selector.show_popup('help'); + this.gui.show_popup('help'); }, barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned @@ -165,10 +162,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var self = this; if(self.pos.scan_product(code)){ if(self.barcode_product_screen){ - self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen); + self.gui.show_screen(self.barcode_product_screen); } }else{ - self.pos_widget.screen_selector.show_popup('error-barcode',code.code); + self.gui.show_popup('error-barcode',code.code); } }, @@ -181,11 +178,11 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // for(var i = 0, len = users.length; i < len; i++){ if(users[i].barcode === code.code){ this.pos.set_cashier(users[i]); - this.pos_widget.username.renderElement(); + this.chrome.widget.username.renderElement(); return true; } } - this.pos_widget.screen_selector.show_popup('error-barcode',code.code); + this.gui.show_popup('error-barcode',code.code); return false; }, @@ -199,7 +196,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.pos.get_order().set_client(partner); return true; } - this.pos_widget.screen_selector.show_popup('error-barcode',code.code); + this.gui.show_popup('error-barcode',code.code); return false; }, @@ -213,7 +210,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }, // What happens when an invalid barcode is scanned : shows an error popup. barcode_error_action: function(code){ - this.pos_widget.screen_selector.show_popup('error-barcode',code.code); + this.gui.show_popup('error-barcode',code.code); }, // this method shows the screen and sets up all the widget related to this screen. Extend this method @@ -228,10 +225,9 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var self = this; - this.pos_widget.set_numpad_visible(this.show_numpad); - this.pos_widget.set_leftpane_visible(this.show_leftpane); + this.chrome.set_numpad_visible(this.show_numpad); + this.chrome.set_leftpane_visible(this.show_leftpane); - this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode()); this.pos.barcode_reader.set_action_callback({ 'cashier': self.barcode_cashier_action ? function(code){ self.barcode_cashier_action(code); } : undefined , 'product': self.barcode_product_action ? function(code){ self.barcode_product_action(code); } : undefined , @@ -298,10 +294,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.renderElement(); this.$('.button.fullscreen').off('click').click(function(){ window.document.body.webkitRequestFullscreen(); - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); }); this.$('.button.cancel').off('click').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); }); }, ismobile: function(){ @@ -328,7 +324,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.pos.barcode_reader.reset_action_callbacks(); this.$('.footer .button').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if ( options.confirm ) { options.confirm.call(self); } @@ -364,14 +360,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.renderElement(); this.$('.button.cancel').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if( options.cancel ){ options.cancel.call(self); } }); this.$('.button.confirm').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if( options.confirm ){ options.confirm.call(self); } @@ -410,14 +406,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.renderElement(); this.$('.button.cancel').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if (options.cancel){ options.cancel.call(self); } }); this.$('.selection-item').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if (options.confirm) { var item = self.list[parseInt($(this).data('item-index'))]; item = item ? item.item : item; @@ -441,14 +437,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.$('input,textarea').focus(); this.$('.button.cancel').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if( options.cancel ){ options.cancel.call(self); } }); this.$('.button.confirm').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); var value = self.$('input,textarea').val(); if( options.confirm ){ options.confirm.call(self,value); @@ -522,14 +518,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // self.click_numpad_button($(this),event); }); this.$('.button.cancel').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if( options.cancel ){ options.cancel.call(self); } }); this.$('.button.confirm').click(function(){ - self.pos_widget.screen_selector.close_popup(); + self.gui.close_popup(); if( options.confirm ){ options.confirm.call(self,self.inputbuffer); } @@ -579,21 +575,21 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.hotkey_handler = function(event){ if(event.which === 13){ self.order_product(); - self.pos_widget.screen_selector.set_current_screen(self.next_screen); + self.gui.show_screen(self.next_screen); }else if(event.which === 27){ - self.pos_widget.screen_selector.set_current_screen(self.previous_screen); + self.gui.show_screen(self.previous_screen); } }; $('body').on('keyup',this.hotkey_handler); this.$('.back').click(function(){ - self.pos_widget.screen_selector.set_current_screen(self.previous_screen); + self.gui.show_screen(self.previous_screen); }); this.$('.next,.buy-product').click(function(){ self.order_product(); - self.pos_widget.screen_selector.set_current_screen(self.next_screen); + self.gui.show_screen(self.next_screen); }); queue.schedule(function(){ @@ -604,12 +600,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }, get_product: function(){ - var ss = this.pos_widget.screen_selector; - if(ss){ - return ss.get_current_screen_param('product'); - }else{ - return undefined; - } + return this.gui.get_current_screen_param('product'); }, order_product: function(){ this.pos.get_order().add_product(this.get_product(),{ quantity: this.weight }); @@ -667,7 +658,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.product_list_widget = new module.ProductListWidget(this,{ click_product_action: function(product){ if(product.to_weight && self.pos.config.iface_electronic_scale){ - self.pos_widget.screen_selector.set_current_screen('scale',{product: product}); + self.gui.show_screen('scale',{product: product}); }else{ self.pos.get_order().add_product(product); } @@ -684,20 +675,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // show: function(){ this._super(); - var self = this; - this.product_categories_widget.reset_category(); - - this.pos_widget.order_widget.set_editable(true); + this.chrome.widget.order.enable_numpad(); }, close: function(){ this._super(); - - this.pos_widget.order_widget.set_editable(false); - - if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){ - this.pos_widget.onscreen_keyboard.hide(); + if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ + this.chrome.widget.keyboard.hide(); } }, }); @@ -724,12 +709,12 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.new_client = this.old_client; this.$('.back').click(function(){ - self.pos_widget.screen_selector.back(); + self.gui.back(); }); - this.$('.next').click(function(){ + this.$('.next').click(function(){ self.save_changes(); - self.pos_widget.screen_selector.back(); + self.gui.back(); // FIXME HUH ? }); this.$('.new-customer').click(function(){ @@ -753,8 +738,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var search_timeout = null; - if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){ - this.pos_widget.onscreen_keyboard.connect(this.$('.searchbox input')); + if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ + this.chrome.widget.keyboard.connect(this.$('.searchbox input')); } this.$('.searchbox input').on('keyup',function(event){ @@ -785,7 +770,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // if ( associate_result && customers.length === 1){ this.new_client = customers[0]; this.save_changes(); - this.pos_widget.screen_selector.back(); + this.gui.back(); } this.render_list(customers); }else{ @@ -896,7 +881,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }); if (!fields.name) { - this.pos_widget.screen_selector.show_popup('error',{ + this.gui.show_popup('error',{ message: _t('A Customer Name Is Required'), }); return; @@ -914,7 +899,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // self.saved_client_details(partner_id); },function(err,event){ event.preventDefault(); - self.pos_widget.screen_selector.show_popup('error',{ + self.gui.show_popup('error',{ 'message':_t('Error: Could not Save Changes'), 'comment':_t('Your Internet connection is probably down.'), }); @@ -971,7 +956,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // load_image_file: function(file, callback){ var self = this; if (!file.type.match(/image.*/)) { - this.pos_widget.screen_selector.show_popup('error',{ + this.gui.show_popup('error',{ message:_t('Unsupported File Format'), comment:_t('Only web-compatible Image formats such as .png or .jpeg are supported'), }); @@ -986,7 +971,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // self.resize_image_to_dataurl(img,800,600,callback); } reader.onerror = function(){ - self.pos_widget.screen_selector.show_popup('error',{ + self.gui.show_popup('error',{ message:_t('Could Not Read Image'), comment:_t('The provided file could not be read due to an unknown error'), }); @@ -1349,10 +1334,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } }, click_set_customer: function(){ - this.pos_widget.screen_selector.set_current_screen('clientlist'); + this.gui.show_screen('clientlist'); }, click_back: function(){ - this.pos_widget.screen_selector.set_current_screen('products'); + this.gui.show_screen('products'); }, renderElement: function() { var self = this; @@ -1443,7 +1428,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // // FIXME: this check is there because the backend is unable to // process empty orders. This is not the right place to fix it. if (order.get_orderlines().length === 0) { - this.pos_widget.screen_selector.show_popup('error',{ + this.gui.show_popup('error',{ 'message': _t('Empty Order'), 'comment': _t('There must be at least one product in your order before it can be validated'), }); @@ -1461,7 +1446,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // cash = cash || (this.pos.cashregisters[i].journal.type === 'cash'); } if (!cash) { - this.pos_widget.screen_selector.show_popup('error',{ + this.gui.show_popup('error',{ message: _t('Cannot return change without a cash payment method'), comment: _t('There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration'), }); @@ -1481,15 +1466,15 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // invoiced.fail(function(error){ self.invoicing = false; if (error === 'error-no-client') { - self.pos_widget.screen_selector.show_popup('confirm',{ + self.gui.show_popup('confirm',{ message: _t('Please select the Customer'), comment: _t('You need to select the customer before you can invoice an order.'), confirm: function(){ - self.pos_widget.screen_selector.set_current_screen('clientlist'); + self.gui.show_screen('clientlist'); }, }); } else { - self.pos_widget.screen_selector.show_popup('error',{ + self.gui.show_popup('error',{ message: _t('The order could not be sent'), comment: _t('Check your internet connection and try again.'), }); @@ -1506,7 +1491,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.print_escpos_receipt(); order.finalize(); //finish order and go back to scan screen } else { - this.pos_widget.screen_selector.set_current_screen(this.next_screen); + this.gui.show_screen(this.next_screen); } } }, diff --git a/addons/point_of_sale/static/src/js/widget_base.js b/addons/point_of_sale/static/src/js/widget_base.js index 9f44effebb4..a391b3d19d3 100644 --- a/addons/point_of_sale/static/src/js/widget_base.js +++ b/addons/point_of_sale/static/src/js/widget_base.js @@ -17,8 +17,9 @@ openerp.point_of_sale.load_basewidget = function load_basewidget(instance, modul init:function(parent,options){ this._super(parent); options = options || {}; - this.pos = options.pos || (parent ? parent.pos : undefined); - this.pos_widget = options.pos_widget || (parent ? parent.pos_widget : undefined); + this.pos = options.pos || (parent ? parent.pos : undefined); + this.chrome = options.chrome || (parent ? parent.chrome : undefined); + this.gui = options.gui || (parent ? parent.gui : undefined); }, format_currency: function(amount,precision){ var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2}; diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index 51f7d196998..39ad2d5d6b0 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -92,10 +92,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // var self = this; this._super(); this.$('.pay').click(function(){ - self.pos.pos_widget.screen_selector.set_current_screen('payment'); + self.gui.show_screen('payment'); }); this.$('.set-customer').click(function(){ - self.pos.pos_widget.screen_selector.set_current_screen('clientlist'); + self.gui.show_screen('clientlist'); }); } }); @@ -112,7 +112,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // return; } self.pos.get_order().select_orderline(this.orderline); - self.pos_widget.numpad.state.reset(); + self.chrome.widget.numpad.state.reset(); }; this.client_change_handler = function(event){ self.update_summary(); @@ -123,7 +123,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }, enable_numpad: function(){ this.disable_numpad(); //ensure we don't register the callbacks twice - this.numpad_state = this.pos_widget.numpad.state; + this.numpad_state = this.chrome.widget.numpad.state; if(this.numpad_state){ this.numpad_state.reset(); this.numpad_state.bind('set_value', this.set_value, this); @@ -136,18 +136,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.numpad_state.reset(); } }, - set_editable: function(editable){ - var order = this.pos.get_order(); - if (order) { - this.editable = editable; - if (editable) { - this.enable_numpad(); - }else{ - this.disable_numpad(); - order.deselect_orderline(); - } - } - }, set_value: function(val) { var order = this.pos.get_order(); if (this.editable && order.get_selected_orderline()) { @@ -224,7 +212,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // target.parentNode.replaceChild(this.el,target); }, renderElement: function(scrollbottom){ - this.pos_widget.numpad.state.reset(); + this.chrome.widget.numpad.state.reset(); //FIXME WTF var order = this.pos.get_order(); if (!order) { @@ -297,7 +285,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // if (!order) { return; } else if ( !order.is_empty() ){ - this.pos_widget.screen_selector.show_popup('confirm',{ + this.gui.show_popup('confirm',{ message: _t('Destroy Current Order ?'), comment: _t('You will lose any data associated with the current order'), confirm: function(){ @@ -463,8 +451,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.el.querySelector('.search-clear').addEventListener('click',this.clear_search_handler); - if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){ - this.pos_widget.onscreen_keyboard.connect($(this.el.querySelector('.searchbox input'))); + if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){ + this.chrome.widget.keyboard.connect($(this.el.querySelector('.searchbox input'))); } }, @@ -578,10 +566,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // var options = options || {}; this._super(parent,options); }, - set_user_mode: function(mode){ - this.mode = mode; - this.renderElement(); - }, renderElement: function(){ var self = this; this._super(); @@ -592,7 +576,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }, click_username: function(){ var self = this; - this.pos_widget.select_user({ + this.chrome.select_user({ 'security': true, 'current_user': this.pos.get_cashier(), 'message': _t('Change Cashier'), @@ -736,10 +720,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // self.pos.barcode_reader.scan(self.$('input.ean').val()); }); this.$('.button.show_orders').click(function(){ - self.pos.pos_widget.screen_selector.show_popup('unsent-orders'); + self.gui.show_popup('unsent-orders'); }); this.$('.button.delete_orders').click(function(){ - self.pos.pos_widget.screen_selector.show_popup('confirm',{ + self.gui.show_popup('confirm',{ message: _t('Delete Unsent Orders ?'), comment: _t('This operation will permanently destroy all unsent orders from the local storage. You will lose all the data. This operation cannot be undone.'), confirm: function(){ @@ -749,10 +733,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }); }); this.$('.button.show_unpaid_orders').click(function(){ - self.pos.pos_widget.screen_selector.show_popup('unpaid-orders'); + self.gui.show_popup('unpaid-orders'); }); this.$('.button.delete_unpaid_orders').click(function(){ - self.pos.pos_widget.screen_selector.show_popup('confirm',{ + self.gui.show_popup('confirm',{ message: _t('Delete Unpaid Orders ?'), comment: _t('This operation will permanently destroy all unpaid orders from all sessions that have been put in the local storage. You will lose all the data and exit the point of sale. This operation cannot be undone.'), confirm: function(){ @@ -862,30 +846,69 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }); - // The PosWidget is the main widget that contains all other widgets in the PointOfSale. + // The Chrome is the main widget that contains all other widgets in the PointOfSale. // It is mainly composed of : // - a header, containing the list of orders // - a leftpane, containing the list of bought products (orderlines) // - a rightpane, containing the screens (see pos_screens.js) // - popups // - an onscreen keyboard - // a screen_selector which controls the switching between screens and the showing/closing of popups + // a gui which controls the switching between screens and the showing/closing of popups - module.PosWidget = module.PosBaseWidget.extend({ - template: 'PosWidget', + module.Chrome = module.PosBaseWidget.extend({ + template: 'Chrome', init: function() { + var self = this; this._super(arguments[0],{}); - this.pos = new module.PosModel(this.session,{pos_widget:this}); - this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically + this.started = new $.Deferred(); // resolves when DOM is onlyne + this.ready = new $.Deferred(); // resolves when the whole GUI has been loaded + + this.pos = new module.PosModel(this.session,{chrome:this}); + this.gui = new module.Gui({pos: this.pos, chrome: this}); + this.chrome = this; // So that chrome's childs have chrome set automatically + this.widget = {}; // contains references to subwidgets instances this.numpad_visible = true; this.leftpane_visible = true; this.leftpane_width = '440px'; this.cashier_controls_visible = true; + this.pos.ready.done(function(){ + console.log('yeah'); + self.build_chrome(); + self.pos.on_chrome_started(); + self.started.resolve(); + self.build_widgets(); + self.pos.on_chrome_ready(); + self.ready.resolve(); + self.disable_rubberbanding(); + self.loading_hide(); + }).fail(function(err){ // error when loading models data from the backend + self.loading_error(err); + }); + }, + + build_chrome: function() { + // remove default webclient handlers that induce click delay + $(document).off(); + $(window).off(); + $('html').off(); + $('body').off(); + $(self.$el).parent().off(); + $('document').off(); + $('.oe_web_client').off(); + $('.openerp_webclient_container').off(); + FastClick.attach(document.body); + instance.webclient.set_content_full_screen(true); + + this.renderElement(); + + if(this.pos.config.iface_big_scrollbars){ + this.$el.addClass('big-scrollbars'); + } }, disable_rubberbanding: function(){ @@ -902,49 +925,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }); }, - start: function() { - var self = this; - return self.pos.ready.done(function() { - // remove default webclient handlers that induce click delay - $(document).off(); - $(window).off(); - $('html').off(); - $('body').off(); - $(self.$el).parent().off(); - $('document').off(); - $('.oe_web_client').off(); - $('.openerp_webclient_container').off(); - - self.renderElement(); - - self.pos.load_orders(); - self.pos.set_start_order(); - - self.build_widgets(); - - if(self.pos.config.iface_big_scrollbars){ - self.$el.addClass('big-scrollbars'); - } - - self.screen_selector.set_default_screen(); - - self.pos.barcode_reader.connect(); - - instance.webclient.set_content_full_screen(true); - - if(self.pos.config.iface_fullscreen && document.body.webkitRequestFullscreen && ( - window.screen.availWidth > window.innerWidth || - window.screen.availHeight > window.innerHeight )){ - self.screen_selector.show_popup('fullscreen'); - } - self.loading_hide(); - - self.pos.push_order(); - - }).fail(function(err){ // error when loading models data from the backend - self.loading_error(err); - }); - }, loading_error: function(err){ var self = this; @@ -1029,7 +1009,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // } } - this.pos_widget.screen_selector.show_popup('selection',{ + this.gui.show_popup('selection',{ 'message': options.message || _t('Select User'), list: list, confirm: function(user){ def.resolve(user); }, @@ -1040,11 +1020,11 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // if (options.security && user !== options.current_user && user.pos_security_pin) { var ret = new $.Deferred(); - self.pos_widget.screen_selector.show_popup('password',{ + self.gui.show_popup('password',{ 'message': _t('Password'), confirm: function(password) { if (password !== user.pos_security_pin) { - this.pos_widget.screen_selector.show_popup('error',{ + this.gui.show_popup('error',{ 'message':_t('Password Incorrect'), }); ret.reject(); @@ -1082,171 +1062,141 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // } }, - // This method instantiates all the screens, widgets, etc. If you want to add new screens change the - // startup screen, etc, override this method. + screens: { + 'products': module.ProductScreenWidget, + 'receipt': module.ReceiptScreenWidget, + 'payment': module.PaymentScreenWidget, + 'clientlist': module.ClientListScreenWidget, + 'scale': module.ScaleScreenWidget, + }, + + popups: { + 'error': module.ErrorPopupWidget, + 'error-barcode': module.ErrorBarcodePopupWidget, + 'error-traceback': module.ErrorTracebackPopupWidget, + 'textinput': module.TextInputPopupWidget, + 'textarea': module.TextAreaPopupWidget, + 'number': module.NumberPopupWidget, + 'password': module.PasswordPopupWidget, + 'confirm': module.ConfirmPopupWidget, + 'selection': module.SelectionPopupWidget, + 'unsent-orders': module.UnsentOrdersPopupWidget, + 'unpaid-orders': module.UnpaidOrdersPopupWidget, + }, + + widgets: [ + { + 'name': 'order_selector', + 'widget': module.OrderSelectorWidget, + 'replace': '.placeholder-OrderSelectorWidget', + },{ + 'name': 'proxy_status', + 'widget': module.ProxyStatusWidget, + 'append': '.pos-rightheader', + 'condition': function(self){ return self.pos.config.use_proxy }, + },{ + 'name': 'notification', + 'widget': module.SynchNotificationWidget, + 'append': '.pos-rightheader', + },{ + 'name': 'close_button', + 'widget': module.HeaderButtonWidget, + 'append': '.pos-rightheader', + 'args': { + label: _t('Close'), + action: function(){ + var self = this; + if (!this.confirmed) { + this.$el.addClass('confirm'); + this.$el.text(_t('Confirm')); + this.confirmed = setTimeout(function(){ + self.$el.removeClass('confirm'); + self.$el.text(_t('Close')); + self.confirmed = false; + },2000); + } else { + clearTimeout(this.confirmed); + this.chrome.close(); + } + }, + } + },{ + 'name': 'username', + 'widget': module.UsernameWidget, + 'replace': '.placeholder-UsernameWidget', + },{ + 'name': 'actionpad', + 'widget': module.ActionpadWidget, + 'replace': '.placeholder-ActionpadWidget', + },{ + 'name': 'numpad', + 'widget': module.NumpadWidget, + 'replace': '.placeholder-NumpadWidget', + },{ + 'name': 'order', + 'widget': module.OrderWidget, + 'replace': '.placeholder-OrderWidget', + },{ + 'name': 'keyboard', + 'widget': module.OnscreenKeyboardWidget, + 'replace': '.placeholder-OnscreenKeyboardWidget', + },{ + 'name': 'debug', + 'widget': module.DebugWidget, + 'append': '.pos-content', + 'condition': function(self){ return self.pos.debug }, + }, + ], + + // This method instantiates all the screens, widgets, etc. build_widgets: function() { var self = this; - // -------- Screens --------- - - this.product_screen = new module.ProductScreenWidget(this,{}); - this.product_screen.appendTo(this.$('.screens')); - - this.receipt_screen = new module.ReceiptScreenWidget(this, {}); - this.receipt_screen.appendTo(this.$('.screens')); - - this.payment_screen = new module.PaymentScreenWidget(this, {}); - this.payment_screen.appendTo(this.$('.screens')); - - this.clientlist_screen = new module.ClientListScreenWidget(this, {}); - this.clientlist_screen.appendTo(this.$('.screens')); - - this.scale_screen = new module.ScaleScreenWidget(this,{}); - this.scale_screen.appendTo(this.$('.screens')); - - - // -------- Popups --------- - - this.error_popup = new module.ErrorPopupWidget(this, {}); - this.error_popup.appendTo(this.$el); - - this.error_barcode_popup = new module.ErrorBarcodePopupWidget(this, {}); - this.error_barcode_popup.appendTo(this.$el); - - this.error_traceback_popup = new module.ErrorTracebackPopupWidget(this,{}); - this.error_traceback_popup.appendTo(this.$el); - - this.confirm_popup = new module.ConfirmPopupWidget(this,{}); - this.confirm_popup.appendTo(this.$el); - - this.fullscreen_popup = new module.FullscreenPopup(this,{}); - this.fullscreen_popup.appendTo(this.$el); - - this.selection_popup = new module.SelectionPopupWidget(this,{}); - this.selection_popup.appendTo(this.$el); - - this.textinput_popup = new module.TextInputPopupWidget(this,{}); - this.textinput_popup.appendTo(this.$el); - - this.textarea_popup = new module.TextAreaPopupWidget(this,{}); - this.textarea_popup.appendTo(this.$el); - - this.number_popup = new module.NumberPopupWidget(this,{}); - this.number_popup.appendTo(this.$el); - - this.password_popup = new module.PasswordPopupWidget(this,{}); - this.password_popup.appendTo(this.$el); - - this.unsent_orders_popup = new module.UnsentOrdersPopupWidget(this,{}); - this.unsent_orders_popup.appendTo(this.$el); - - this.unpaid_orders_popup = new module.UnpaidOrdersPopupWidget(this,{}); - this.unpaid_orders_popup.appendTo(this.$el); - - // -------- Misc --------- - - this.order_selector = new module.OrderSelectorWidget(this,{}); - this.order_selector.replace(this.$('.placeholder-OrderSelectorWidget')); - - if(this.pos.config.use_proxy){ - this.proxy_status = new module.ProxyStatusWidget(this,{}); - this.proxy_status.appendTo(this.$('.pos-rightheader')); + for (var name in this.screens) { + var screen = new this.screens[name](this,{}); + screen.appendTo(this.$('.screens')); + this.gui.add_screen(name, screen); } - this.notification = new module.SynchNotificationWidget(this,{}); - this.notification.appendTo(this.$('.pos-rightheader')); + for (var name in this.popups) { + var popup = new this.popups[name](this,{}); + popup.appendTo(this.$el); // FIXME .popups + this.gui.add_popup(name, popup); + } - this.close_button = new module.HeaderButtonWidget(this,{ - label: _t('Close'), - action: function(){ - var self = this; - if (!this.confirmed) { - this.$el.addClass('confirm'); - this.$el.text(_t('Confirm')); - this.confirmed = setTimeout(function(){ - self.$el.removeClass('confirm'); - self.$el.text(_t('Close')); - self.confirmed = false; - },2000); + for (var i = 0; i < this.widgets.length; i++) { + var def = this.widgets[i]; + if ( !def.condition || def.condition(this) ) { + var args = typeof def.args === 'function' ? def.args(this) : def.args; + var w = new def.widget(this, args || {}); + if (def.replace) { + w.replace(this.$(def.replace)); + } else if (def.append) { + w.appendTo(this.$(def.append)); + } else if (def.prepend) { + w.prependTo(this.$(def.prepend)); } else { - clearTimeout(this.confirmed); - this.pos_widget.close(); + w.appendTo(this.$el); } - }, - }); - this.close_button.appendTo(this.$('.pos-rightheader')); - - - - this.username = new module.UsernameWidget(this,{}); - this.username.replace(this.$('.placeholder-UsernameWidget')); - - this.actionpad = new module.ActionpadWidget(this, {}); - this.actionpad.replace(this.$('.placeholder-ActionpadWidget')); - - this.numpad = new module.NumpadWidget(this); - this.numpad.replace(this.$('.placeholder-NumpadWidget')); - - this.order_widget = new module.OrderWidget(this, {}); - this.order_widget.replace(this.$('.placeholder-OrderWidget')); - - this.onscreen_keyboard = new module.OnscreenKeyboardWidget(this, { - 'keyboard_model': 'simple' - }); - this.onscreen_keyboard.replace(this.$('.placeholder-OnscreenKeyboardWidget')); - - // -------- Screen Selector --------- - - this.screen_selector = new module.ScreenSelector({ - pos: this.pos, - screen_set:{ - 'products': this.product_screen, - 'payment' : this.payment_screen, - 'scale': this.scale_screen, - 'receipt' : this.receipt_screen, - 'clientlist': this.clientlist_screen, - }, - popup_set:{ - 'error': this.error_popup, - 'error-barcode': this.error_barcode_popup, - 'error-traceback': this.error_traceback_popup, - 'textinput': this.textinput_popup, - 'textarea': this.textarea_popup, - 'number': this.number_popup, - 'password': this.password_popup, - 'confirm': this.confirm_popup, - 'fullscreen': this.fullscreen_popup, - 'selection': this.selection_popup, - 'unsent-orders': this.unsent_orders_popup, - 'unpaid-orders': this.unpaid_orders_popup, - }, - default_screen: 'products', - default_mode: 'cashier', - }); - - if(this.pos.debug){ - this.debug_widget = new module.DebugWidget(this); - this.debug_widget.appendTo(this.$('.pos-content')); + this.widget[def.name] = w; + } } - this.disable_rubberbanding(); + this.gui.set_startup_screen('products'); + this.gui.set_default_screen('products'); }, - changed_pending_operations: function () { - var self = this; - this.synch_notification.on_change_nbr_pending(self.pos.get('nbr_pending_operations').length); - }, // shows or hide the numpad and related controls like the paypad. set_numpad_visible: function(visible){ if(visible !== this.numpad_visible){ this.numpad_visible = visible; if(visible){ - this.numpad.show(); - this.actionpad.show(); + this.widget.numpad.show(); + this.widget.actionpad.show(); }else{ - this.numpad.hide(); - this.actionpad.hide(); + this.widget.numpad.hide(); + this.widget.actionpad.hide(); } } }, @@ -1274,7 +1224,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // window.location = '/web#action=' + res[0]['res_id']; },function(err,event) { event.preventDefault(); - self.screen_selector.show_popup('error',{ + self.gui.show_popup('error',{ 'message': _t('Could not close the point of sale.'), 'comment': _t('Your internet connection is probably down.'), }); diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 87f6302932f..301517669e5 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -3,7 +3,7 @@ --> - +
From dec547d1f89c72e87411cbb2ce5b8c1f8f0cf2de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sat, 20 Dec 2014 18:03:31 +0100 Subject: [PATCH 02/21] [WIP] point_of_sale: cleanup Gui, move acess control to Gui --- addons/point_of_sale/static/src/js/screens.js | 180 +++++++++++++++--- addons/point_of_sale/static/src/js/widgets.js | 75 -------- 2 files changed, 154 insertions(+), 101 deletions(-) diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 3666a0022d8..4cdcb7506bc 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -48,35 +48,31 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }); }, + /* ---- Gui: SCREEN MANIPULATION ---- */ + + // register a screen widget to the gui, + // it must have been inserted into the dom. add_screen: function(name, screen){ screen.hide(); this.screen_set[name] = screen; }, + + // sets the screen that will be displayed + // for new orders set_default_screen: function(name){ this.default_screen = name; }, + + // sets the screen that will be displayed + // when no orders are present set_startup_screen: function(name) { this.startup_screen = name; }, - add_popup: function(name, popup) { - popup.hide(); - this.popup_set[name] = popup; - }, - show_popup: function(name,options){ - if(this.current_popup){ - this.close_popup(); - } - this.current_popup = this.popup_set[name]; - this.current_popup.show(options); - }, - close_popup: function(){ - if(this.current_popup){ - this.current_popup.close(); - this.current_popup.hide(); - this.current_popup = null; - } - }, + // display the screen saved in an order, + // called when the user changes the current order + // no screen saved ? -> display default_screen + // no order ? -> display startup_screen show_saved_screen: function(order,options) { options = options || {}; this.close_popup(); @@ -90,9 +86,15 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } }, - show_screen: function(screen_name,params,refresh){ + // display a screen. + // If there is an order, the screen will be saved in the order + // - params: used to load a screen with parameters, for + // example loading a 'product_details' screen for a specific product. + // - refresh: if you want the screen to cycle trough show / hide even + // if you are already on the same screen. + show_screen: function(screen_name,params,refresh) { var screen = this.screen_set[screen_name]; - if(!screen){ + if (!screen) { console.error("ERROR: show_screen("+screen_name+") : screen not found"); debugger; } @@ -114,8 +116,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } } - if ( refresh || screen !== this.current_screen){ - if(this.current_screen){ + if (refresh || screen !== this.current_screen) { + if (this.current_screen) { this.current_screen.close(); this.current_screen.hide(); } @@ -123,19 +125,145 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.current_screen.show(); } }, - get_current_screen: function(){ + + // returns the current screen. + get_current_screen: function() { return this.pos.get_order().get_screen_data('screen') || this.default_screen; }, - back: function(){ + + // goes to the previous screen (as specified in the order). The history only + // goes 1 deep ... + back: function() { var previous = this.pos.get_order().get_screen_data('previous-screen'); - if(previous){ + if (previous) { this.show_screen(previous); } }, - get_current_screen_param: function(param){ + + // returns the parameter specified when this screen was displayed + get_current_screen_param: function(param) { var params = this.pos.get_order().get_screen_data('params'); return params ? params[param] : undefined; }, + + /* ---- Gui: POPUP MANIPULATION ---- */ + + // registers a new popup in the GUI. + // the popup must have been previously inserted + // into the dom. + add_popup: function(name, popup) { + popup.hide(); + this.popup_set[name] = popup; + }, + + // displays a popup. Popup do not stack, + // are not remembered by the order, and are + // closed by screen changes or new popups. + show_popup: function(name,options) { + if (this.current_popup) { + this.close_popup(); + } + this.current_popup = this.popup_set[name]; + this.current_popup.show(options); + }, + + // close the current popup. + close_popup: function() { + if (this.current_popup) { + this.current_popup.close(); + this.current_popup.hide(); + this.current_popup = null; + } + }, + + /* ---- Gui: ACCESS CONTROL ---- */ + + // A Generic UI that allow to select a user from a list. + // It returns a deferred that resolves with the selected user + // upon success. Several options are available : + // - security: passwords will be asked + // - only_managers: restricts the list to managers + // - current_user: password will not be asked if this + // user is selected. + // - message: The title of the user selection list. + select_user: function(options){ + options = options || {}; + var self = this; + var def = new $.Deferred(); + + var list = []; + for (var i = 0; i < this.pos.users.length; i++) { + var user = this.pos.users[i]; + if (!options.only_managers || user.role === 'manager') { + list.push({ + 'label': user.name, + 'item': user, + }); + } + } + + this.show_popup('selection',{ + 'message': options.message || _t('Select User'), + list: list, + confirm: function(user){ def.resolve(user); }, + cancel: function(){ def.reject(); }, + }); + + return def.then(function(user){ + if (options.security && user !== options.current_user && user.pos_security_pin) { + return self.ask_password(user.pos_security_pin).then(function(){ + return user; + }); + } else { + return user; + } + }); + }, + + // Ask for a password, and checks if it this + // the same as specified by the function call. + // returns a deferred that resolves on success, + // fails on failure. + ask_password: function(password) { + var self = this; + var ret = new $.Deferred(); + if (password) { + this.gui.show_popup('password',{ + 'message': _t('Password ?'), + confirm: function(pw) { + if (pw !== password) { + self.show_popup('error',_t('Incorrect Password')); + ret.reject(); + } else { + ret.resolve(); + } + }, + }); + } else { + ret.resolve(); + } + return ret; + }, + + // checks if the current user (or the user provided) has manager + // access rights. If not, a popup is shown allowing the user to + // temporarily login as an administrator. + // This method returns a deferred, that succeeds with the + // manager user when the login is successfull. + sudo: function(user){ + var def = new $.Deferred(); + user = user || this.pos.get_cashier(); + + if (user.role === 'manager') { + return new $.Deferred().resolve(user); + } else { + return this.select_user({ + security: true, + only_managers: true, + message: _t('Login as a Manager'), + }) + } + }, }); module.ScreenWidget = module.PosBaseWidget.extend({ diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index 39ad2d5d6b0..a93042c8e5a 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -985,82 +985,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.$('.loader').removeClass('oe_hidden').animate({opacity:1},150,'swing'); }, - // A Generic UI that allow to select a user from a list. - // It returns a deferred that resolves with the selected user - // upon success. Several options are available : - // - security: passwords will be asked - // - only_managers: restricts the list to managers - // - current_user: password will not be asked if this - // user is selected. - // - message: The title of the user selection list. - select_user: function(options){ - options = options || {}; - var self = this; - var def = new $.Deferred(); - var list = []; - for (var i = 0; i < this.pos.users.length; i++) { - var user = this.pos.users[i]; - if (!options.only_managers || user.role === 'manager') { - list.push({ - 'label': user.name, - 'item': user, - }); - } - } - - this.gui.show_popup('selection',{ - 'message': options.message || _t('Select User'), - list: list, - confirm: function(user){ def.resolve(user); }, - cancel: function(){ def.reject(); }, - }); - - return def.then(function(user){ - if (options.security && user !== options.current_user && user.pos_security_pin) { - var ret = new $.Deferred(); - - self.gui.show_popup('password',{ - 'message': _t('Password'), - confirm: function(password) { - if (password !== user.pos_security_pin) { - this.gui.show_popup('error',{ - 'message':_t('Password Incorrect'), - }); - ret.reject(); - } else { - ret.resolve(user); - } - }, - cancel: function(){ ret.reject(); }, - }); - - return ret; - } else { - return user; - } - }); - }, - - // checks if the current user (or the user provided) has manager - // access rights. If not, a popup is shown allowing the user to - // temporarily login as an administrator. - // This method returns a defferred, that succeeds with the - // manager user when the login is successfull. - sudo: function(user){ - var def = new $.Deferred(); - user = user || this.pos.get_cashier(); - - if (user.role === 'manager') { - return new $.Deferred().resolve(user); - } else { - return this.select_user({ - security: true, - only_managers: true, - message: _t('Login as a Manager'), - }) - } - }, screens: { 'products': module.ProductScreenWidget, From d536eb4df481ecda045d2f46ba7dbe96c8dc1df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sat, 20 Dec 2014 18:05:37 +0100 Subject: [PATCH 03/21] [FIX] point_of_sale: changed by refactor --- addons/point_of_sale/static/src/js/widgets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index a93042c8e5a..f8acd19f8db 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -576,7 +576,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }, click_username: function(){ var self = this; - this.chrome.select_user({ + this.gui.select_user({ 'security': true, 'current_user': this.pos.get_cashier(), 'message': _t('Change Cashier'), From f5abed280c1f9e2cd28db3ab50dbea1e42254096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sat, 20 Dec 2014 18:12:31 +0100 Subject: [PATCH 04/21] [IMP] point_of_sale: remove obsolete code --- addons/point_of_sale/static/src/js/screens.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 4cdcb7506bc..23fb308cc90 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -276,14 +276,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // this.hidden = false; }, - help_button_action: function(){ - this.gui.show_popup('help'); - }, - barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned - hotkeys_handlers: {}, - // what happens when a product is scanned : // it will add the product to the order and go to barcode_product_screen. barcode_product_action: function(code){ From 7eebdbb677fdee1163c16ec2bb967f37e1e4b69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sat, 20 Dec 2014 23:53:34 +0100 Subject: [PATCH 05/21] [WIP] point_of_sale: refactored popups: - message and comment have been renamed to title and body - refactored the popups to remove duplicated code - a few css bugfixes for retina displays. (but css is still a mess) - (unrelated) set a background on body for less white on startup --- addons/point_of_sale/static/src/css/pos.css | 47 ++- addons/point_of_sale/static/src/js/devices.js | 4 +- addons/point_of_sale/static/src/js/models.js | 4 +- addons/point_of_sale/static/src/js/screens.js | 383 ++++++++---------- addons/point_of_sale/static/src/js/widgets.js | 41 +- addons/point_of_sale/static/src/xml/pos.xml | 111 ++--- addons/point_of_sale/views/templates.xml | 2 + 7 files changed, 256 insertions(+), 336 deletions(-) diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index 49b31d9181d..fa2428448a5 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -44,6 +44,7 @@ html { -webkit-tap-highlight-color: rgba(0, 0, 0, 0); font-family: sans-serif; } + table { border-spacing: 0px; border-collapse: collapse; @@ -1222,6 +1223,7 @@ td { } body { margin: 0; + color: white !important; } .oe_leftbar, .pos .pos-topheader, @@ -1861,12 +1863,12 @@ td { box-shadow: 0px 10px 20px rgba(0,0,0,0.4); z-index:1200; } -.pos .popup .comment{ +.pos .popup .body { font-weight: normal; font-size: 18px; margin: 0px 16px 16px 16px; } -.pos .popup .comment.traceback { +.pos .popup .body.traceback { height: 264px; overflow: auto; font-size: 14px; @@ -1876,17 +1878,6 @@ td { -moz-user-select: text; user-select: text; } -.pos .popup .comment.traceback { - height: 264px; - overflow: auto; - font-size: 12px; - text-align: left; - white-space: pre-wrap; - font-family: 'Inconsolata'; - -webkit-user-select: text; - -moz-user-select: text; - user-select: text; -} .pos .popup .footer{ position:absolute; bottom:0; @@ -1964,7 +1955,7 @@ td { outline: none; box-shadow: 0px 0px 0px 3px #6EC89B; } -.pos .popup.popup-selection .message { +.pos .popup.popup-selection .title { margin-top: 0; } .pos .popup.popup-selection .selection { @@ -1986,12 +1977,23 @@ td { .pos .popup.popup-selection .selection-item:nth-child(even) { background: rgb(247,247,247); } +.pos .popup.popup-number { + width: 300px; + margin-left: -150px; +} +.pos .popup-number .footer{ + text-align: center; +} +.pos .popup-number .footer .button{ + float: none; + display: inline-block; +} .pos .popup-numpad { margin: 12px auto; text-align: left; - width: 244px; + width: 250px; } -.pos .popup-number .message { +.pos .popup-number .title { margin-top: 0; } .pos .popup-numpad .input-button, @@ -2013,20 +2015,27 @@ td { } .pos .popup.popup-password { - width: 238px; - margin-left: -119px; + width: 250px; + margin-left: -125; } .pos .popup-password .mode-button.add, .pos .popup-password .input-button.dot { display: none; } .pos .popup-password .popup-numpad { - width: 182px; + width: 190px; } .pos .popup-password .popup-input { width: 70%; -webkit-text-security: disc; } +.pos .popup-password .footer{ + text-align: center; +} +.pos .popup-password .footer .button{ + float: none; + display: inline-block; +} /* ********* The Webkit Scrollbar ********* */ diff --git a/addons/point_of_sale/static/src/js/devices.js b/addons/point_of_sale/static/src/js/devices.js index 3765e1d1ab8..2ec7f9f61ed 100644 --- a/addons/point_of_sale/static/src/js/devices.js +++ b/addons/point_of_sale/static/src/js/devices.js @@ -417,8 +417,8 @@ openerp.point_of_sale.load_devices = function load_devices(instance,module){ //m },function(error){ if (error) { self.pos.chrome.screen_selector.show_popup('error-traceback',{ - 'message': _t('Printing Error: ') + error.data.message, - 'comment': error.data.debug, + 'title': _t('Printing Error: ') + error.data.message, + 'body': error.data.debug, }); return; } diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 81f94dcea4d..28c1a5be910 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -733,8 +733,8 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo delete error.data.debug; } self.gui.show_popup('error-traceback',{ - message: error.data.message, - comment: error.data.debug + 'title': error.data.message, + 'body': error.data.debug }); } // prevent an error popup creation by the rpc failure diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 23fb308cc90..f79d6a72152 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -96,7 +96,6 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var screen = this.screen_set[screen_name]; if (!screen) { console.error("ERROR: show_screen("+screen_name+") : screen not found"); - debugger; } this.close_popup(); @@ -185,7 +184,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // // - only_managers: restricts the list to managers // - current_user: password will not be asked if this // user is selected. - // - message: The title of the user selection list. + // - title: The title of the user selection list. select_user: function(options){ options = options || {}; var self = this; @@ -203,7 +202,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } this.show_popup('selection',{ - 'message': options.message || _t('Select User'), + 'title': options.title || _t('Select User'), list: list, confirm: function(user){ def.resolve(user); }, cancel: function(){ def.reject(); }, @@ -229,7 +228,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var ret = new $.Deferred(); if (password) { this.gui.show_popup('password',{ - 'message': _t('Password ?'), + 'title': _t('Password ?'), confirm: function(pw) { if (pw !== password) { self.show_popup('error',_t('Incorrect Password')); @@ -260,10 +259,58 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // return this.select_user({ security: true, only_managers: true, - message: _t('Login as a Manager'), + title: _t('Login as a Manager'), }) } }, + + /* ---- Gui: KEYBOARD INPUT ---- */ + + // This is a helper to handle numpad keyboard input. + // - buffer: an empty or number string + // - input: '[0-9],'+','-','.','CLEAR','BACKSPACE' + // - options: 'firstinput' -> will clear buffer if + // input is '[0-9]' or '.' + // returns the new buffer containing the modifications + // (the original is not touched) + numpad_input: function(buffer, input, options) { + var options = options || {}; + var newbuf = buffer.slice(0); + + if (input === '.') { + if (options.firstinput) { + newbuf = "0."; + }else if (!newbuf.length || newbuf === '-') { + newbuf += "0."; + } else if (newbuf.indexOf('.') < 0){ + newbuf = newbuf + '.'; + } + } else if (input === 'CLEAR') { + newbuf = ""; + } else if (input === 'BACKSPACE') { + newbuf = newbuf.substring(0,newbuf.length - 1); + } else if (input === '+') { + if ( newbuf[0] === '-' ) { + newbuf = newbuf.substring(1,newbuf.length); + } + } else if (input === '-') { + if ( newbuf[0] === '-' ) { + newbuf = newbuf.substring(1,newbuf.length); + } else { + newbuf = '-' + newbuf; + } + } else if (input[0] === '+' && !isNaN(parseFloat(input))) { + newbuf = '' + ((parseFloat(newbuf) || 0) + parseFloat(input)); + } else if (!isNaN(parseInt(input))) { + if (options.firstinput) { + newbuf = '' + input; + } else { + newbuf += input; + } + } + + return newbuf; + }, }); module.ScreenWidget = module.PosBaseWidget.extend({ @@ -391,70 +438,87 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }); module.PopUpWidget = module.PosBaseWidget.extend({ - show: function(){ + init: function(parent, args) { + this._super(parent, args); + this.options = {}; + }, + events: { + 'click .button.cancel': 'click_cancel', + 'click .button.confirm': 'click_confirm', + 'click .selection-item': 'click_item', + 'click .input-button': 'click_numpad', + 'click .mode-button': 'click_numpad', + }, + + // show the popup ! + show: function(options){ if(this.$el){ this.$el.removeClass('oe_hidden'); } + + if (typeof options === 'string') { + this.options = {title: options}; + } else { + this.options = options || {}; + } + + this.renderElement(); + + // popups block the barcode reader ... + if (this.pos.barcode_reader) { + this.pos.barcode_reader.save_callbacks(); + this.pos.barcode_reader.reset_action_callbacks(); + } }, - /* called before hide, when a popup is closed */ + + // called before hide, when a popup is closed. + // extend this if you want a custom action when the + // popup is closed. close: function(){ + if (this.pos.barcode_reader) { + this.pos.barcode_reader.restore_callbacks(); + } }, - /* hides the popup. keep in mind that this is called in the initialization pass of the - * pos instantiation, so you don't want to do anything fancy in here */ + + // hides the popup. keep in mind that this is called in + // the initialization pass of the pos instantiation, + // so you don't want to do anything fancy in here hide: function(){ - if(this.$el){ + if (this.$el) { this.$el.addClass('oe_hidden'); } }, - }); - module.FullscreenPopup = module.PopUpWidget.extend({ - template:'FullscreenPopupWidget', - show: function(){ - var self = this; - this._super(); - this.renderElement(); - this.$('.button.fullscreen').off('click').click(function(){ - window.document.body.webkitRequestFullscreen(); - self.gui.close_popup(); - }); - this.$('.button.cancel').off('click').click(function(){ - self.gui.close_popup(); - }); + // what happens when we click cancel + // ( it should close the popup and do nothing ) + click_cancel: function(){ + this.gui.close_popup(); + if (this.options.cancel) { + this.options.cancel.call(this); + } }, - ismobile: function(){ - return typeof window.orientation !== 'undefined'; - } - }); + // what happens when we confirm the action + click_confirm: function(){ + this.gui.close_popup(); + if (this.options.confirm) { + this.options.confirm.call(this); + } + }, + + // Since Widget does not support extending the events declaration + // we declared them all in the top class. + click_item: function(){}, + click_numad: function(){}, + }); module.ErrorPopupWidget = module.PopUpWidget.extend({ template:'ErrorPopupWidget', show: function(options){ - options = options || {}; - var self = this; - this._super(); + this._super(options); $('body').append(''); - this.message = options.message || _t('Error'); - this.comment = options.comment || ''; - - this.renderElement(); - - this.pos.barcode_reader.save_callbacks(); - this.pos.barcode_reader.reset_action_callbacks(); - - this.$('.footer .button').click(function(){ - self.gui.close_popup(); - if ( options.confirm ) { - options.confirm.call(self); - } - }); - }, - close:function(){ - this._super(); - this.pos.barcode_reader.restore_callbacks(); }, }); @@ -465,44 +529,19 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // module.ErrorBarcodePopupWidget = module.ErrorPopupWidget.extend({ template:'ErrorBarcodePopupWidget', show: function(barcode){ - this.barcode = barcode; - this._super(); + this._super({barcode: barcode}); }, }); module.ConfirmPopupWidget = module.PopUpWidget.extend({ template: 'ConfirmPopupWidget', - show: function(options){ - options = options || {}; - var self = this; - this._super(); - - this.message = options.message || ''; - this.comment = options.comment || ''; - this.renderElement(); - - this.$('.button.cancel').click(function(){ - self.gui.close_popup(); - if( options.cancel ){ - options.cancel.call(self); - } - }); - - this.$('.button.confirm').click(function(){ - self.gui.close_popup(); - if( options.confirm ){ - options.confirm.call(self); - } - }); - }, }); /** * A popup that allows the user to select one item from a list. * * show_popup('selection',{ - * message: 'Pick an Option', - * message: "Popup Title", + * title: "Popup Title", * list: [ * { label: 'foobar', item: 45 }, * { label: 'bar foo', item: 'stuff' }, @@ -523,19 +562,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var self = this; this._super(); - this.message = options.message || ''; this.list = options.list || []; this.renderElement(); - this.$('.button.cancel').click(function(){ - self.gui.close_popup(); - if (options.cancel){ - options.cancel.call(self); - } - }); - this.$('.selection-item').click(function(){ - self.gui.close_popup(); if (options.confirm) { var item = self.list[parseInt($(this).data('item-index'))]; item = item ? item.item : item; @@ -543,6 +573,13 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } }); }, + click_item : function(event) { + this.gui.close_popup(); + if (this.options.confirm) { + var item = self.list[parseInt($(event.target).data('item-index'))]; + item = item ? item.item : item; + } + } }); module.TextInputPopupWidget = module.PopUpWidget.extend({ @@ -552,27 +589,19 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var self = this; this._super(); - this.message = options.message || ''; - this.comment = options.comment || ''; - this.value = options.value || ''; this.renderElement(); this.$('input,textarea').focus(); - this.$('.button.cancel').click(function(){ - self.gui.close_popup(); - if( options.cancel ){ - options.cancel.call(self); - } - }); - this.$('.button.confirm').click(function(){ - self.gui.close_popup(); - var value = self.$('input,textarea').val(); - if( options.confirm ){ - options.confirm.call(self,value); - } }); }, + click_confirm: function(){ + var value = self.$('input,textarea').val(); + self.gui.close_popup(); + if( this.options.confirm ){ + this.options.confirm.call(self,value); + } + }, }); module.TextAreaPopupWidget = module.TextInputPopupWidget.extend({ @@ -581,84 +610,40 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // module.NumberPopupWidget = module.PopUpWidget.extend({ template: 'NumberPopupWidget', - click_numpad_button: function($el,event){ - this.numpad_input($el.data('action')); - }, - numpad_input: function(input) { //FIXME -> Deduplicate code - var oldbuf = this.inputbuffer.slice(0); - - if (input === '.') { - if (this.firstinput) { - this.inputbuffer = "0."; - }else if (!this.inputbuffer.length || this.inputbuffer === '-') { - this.inputbuffer += "0."; - } else if (this.inputbuffer.indexOf('.') < 0){ - this.inputbuffer = this.inputbuffer + '.'; - } - } else if (input === 'CLEAR') { - this.inputbuffer = ""; - } else if (input === 'BACKSPACE') { - this.inputbuffer = this.inputbuffer.substring(0,this.inputbuffer.length - 1); - } else if (input === '+') { - if ( this.inputbuffer[0] === '-' ) { - this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length); - } - } else if (input === '-') { - if ( this.inputbuffer[0] === '-' ) { - this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length); - } else { - this.inputbuffer = '-' + this.inputbuffer; - } - } else if (input[0] === '+' && !isNaN(parseFloat(input))) { - this.inputbuffer = '' + ((parseFloat(this.inputbuffer) || 0) + parseFloat(input)); - } else if (!isNaN(parseInt(input))) { - if (this.firstinput) { - this.inputbuffer = '' + input; - } else { - this.inputbuffer += input; - } - } - - this.firstinput = this.inputbuffer.length === 0; - - if (this.inputbuffer !== oldbuf) { - this.$('.value').text(this.inputbuffer); - } - }, show: function(options){ options = options || {}; var self = this; this._super(); - this.message = options.message || ''; - this.comment = options.comment || ''; this.inputbuffer = options.value || ''; this.renderElement(); this.firstinput = true; - - this.$('.input-button,.mode-button').click(function(event){ - self.click_numpad_button($(this),event); - }); - this.$('.button.cancel').click(function(){ - self.gui.close_popup(); - if( options.cancel ){ - options.cancel.call(self); - } - }); + }, + click_numpad: function(event){ + var newbuf = this.gui.numpad_input( + this.inputbuffer, + $(event.target).data('action'), + {'firstinput': this.firstinput}); - this.$('.button.confirm').click(function(){ - self.gui.close_popup(); - if( options.confirm ){ - options.confirm.call(self,self.inputbuffer); - } - }); + this.firstinput = (newbuf.length === 0); + + if (newbuf !== this.inputbuffer) { + this.inputbuffer = newbuf; + this.$('.value').text(this.inputbuffer); + } + }, + click_confirm: function(event){ + this.gui.close_popup(); + if( this.options.confirm ){ + this.options.confirm.call(this,this.inputbuffer); + } }, }); module.PasswordPopupWidget = module.NumberPopupWidget.extend({ renderElement: function(){ this._super(); - this.$('.popup').addClass('popup-password'); // HELLO HACK ! + this.$('.popup').addClass('popup-password'); // WHY NOT ?! }, }); @@ -1003,9 +988,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }); if (!fields.name) { - this.gui.show_popup('error',{ - message: _t('A Customer Name Is Required'), - }); + this.gui.show_popup('error',_t('A Customer Name Is Required')); return; } @@ -1022,8 +1005,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // },function(err,event){ event.preventDefault(); self.gui.show_popup('error',{ - 'message':_t('Error: Could not Save Changes'), - 'comment':_t('Your Internet connection is probably down.'), + 'title': _t('Error: Could not Save Changes'), + 'body': _t('Your Internet connection is probably down.'), }); }); }, @@ -1079,8 +1062,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // var self = this; if (!file.type.match(/image.*/)) { this.gui.show_popup('error',{ - message:_t('Unsupported File Format'), - comment:_t('Only web-compatible Image formats such as .png or .jpeg are supported'), + title: _t('Unsupported File Format'), + body: _t('Only web-compatible Image formats such as .png or .jpeg are supported'), }); return; } @@ -1094,8 +1077,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } reader.onerror = function(){ self.gui.show_popup('error',{ - message:_t('Could Not Read Image'), - comment:_t('The provided file could not be read due to an unknown error'), + title :_t('Could Not Read Image'), + body :_t('The provided file could not be read due to an unknown error'), }); }; reader.readAsDataURL(file); @@ -1322,42 +1305,12 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // // a string that represents the key pressed. payment_input: function(input) { var oldbuf = this.inputbuffer.slice(0); + var newbuf = this.gui.numpad_input(this.inputbuffer, input, {'firstinput': this.firstinput}); - if (input === '.') { - if (this.firstinput) { - this.inputbuffer = "0."; - }else if (!this.inputbuffer.length || this.inputbuffer === '-') { - this.inputbuffer += "0."; - } else if (this.inputbuffer.indexOf('.') < 0){ - this.inputbuffer = this.inputbuffer + '.'; - } - } else if (input === 'CLEAR') { - this.inputbuffer = ""; - } else if (input === 'BACKSPACE') { - this.inputbuffer = this.inputbuffer.substring(0,this.inputbuffer.length - 1); - } else if (input === '+') { - if ( this.inputbuffer[0] === '-' ) { - this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length); - } - } else if (input === '-') { - if ( this.inputbuffer[0] === '-' ) { - this.inputbuffer = this.inputbuffer.substring(1,this.inputbuffer.length); - } else { - this.inputbuffer = '-' + this.inputbuffer; - } - } else if (input[0] === '+' && !isNaN(parseFloat(input))) { - this.inputbuffer = '' + ((parseFloat(this.inputbuffer) || 0) + parseFloat(input)); - } else if (!isNaN(parseInt(input))) { - if (this.firstinput) { - this.inputbuffer = '' + input; - } else { - this.inputbuffer += input; - } - } - - this.firstinput = this.inputbuffer.length === 0; - - if (this.inputbuffer !== oldbuf) { + this.firstinput = (newbuf.length === 0); + + if (newbuf !== this.inputbuffer) { + this.inputbuffer = newbuf; var order = this.pos.get_order(); if (order.selected_paymentline) { order.selected_paymentline.set_amount(parseFloat(this.inputbuffer)); @@ -1551,8 +1504,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // // process empty orders. This is not the right place to fix it. if (order.get_orderlines().length === 0) { this.gui.show_popup('error',{ - 'message': _t('Empty Order'), - 'comment': _t('There must be at least one product in your order before it can be validated'), + 'title': _t('Empty Order'), + 'body': _t('There must be at least one product in your order before it can be validated'), }); return; } @@ -1569,8 +1522,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // } if (!cash) { this.gui.show_popup('error',{ - message: _t('Cannot return change without a cash payment method'), - comment: _t('There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration'), + title: _t('Cannot return change without a cash payment method'), + body: _t('There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration'), }); return; } @@ -1589,16 +1542,16 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // self.invoicing = false; if (error === 'error-no-client') { self.gui.show_popup('confirm',{ - message: _t('Please select the Customer'), - comment: _t('You need to select the customer before you can invoice an order.'), + 'title': _t('Please select the Customer'), + 'body': _t('You need to select the customer before you can invoice an order.'), confirm: function(){ self.gui.show_screen('clientlist'); }, }); } else { self.gui.show_popup('error',{ - message: _t('The order could not be sent'), - comment: _t('Check your internet connection and try again.'), + 'title': _t('The order could not be sent'), + 'body': _t('Check your internet connection and try again.'), }); } }); diff --git a/addons/point_of_sale/static/src/js/widgets.js b/addons/point_of_sale/static/src/js/widgets.js index f8acd19f8db..660ebd76fec 100644 --- a/addons/point_of_sale/static/src/js/widgets.js +++ b/addons/point_of_sale/static/src/js/widgets.js @@ -286,8 +286,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // return; } else if ( !order.is_empty() ){ this.gui.show_popup('confirm',{ - message: _t('Destroy Current Order ?'), - comment: _t('You will lose any data associated with the current order'), + 'title': _t('Destroy Current Order ?'), + 'body': _t('You will lose any data associated with the current order'), confirm: function(){ self.pos.delete_current_order(); }, @@ -579,7 +579,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.gui.select_user({ 'security': true, 'current_user': this.pos.get_cashier(), - 'message': _t('Change Cashier'), + 'title': _t('Change Cashier'), }).then(function(user){ self.pos.set_cashier(user); self.renderElement(); @@ -724,8 +724,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }); this.$('.button.delete_orders').click(function(){ self.gui.show_popup('confirm',{ - message: _t('Delete Unsent Orders ?'), - comment: _t('This operation will permanently destroy all unsent orders from the local storage. You will lose all the data. This operation cannot be undone.'), + 'title': _t('Delete Unsent Orders ?'), + 'body': _t('This operation will permanently destroy all unsent orders from the local storage. You will lose all the data. This operation cannot be undone.'), confirm: function(){ self.pos.db.remove_all_orders(); self.pos.set({synch: { state:'connected', pending: 0 }}); @@ -737,8 +737,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // }); this.$('.button.delete_unpaid_orders').click(function(){ self.gui.show_popup('confirm',{ - message: _t('Delete Unpaid Orders ?'), - comment: _t('This operation will permanently destroy all unpaid orders from all sessions that have been put in the local storage. You will lose all the data and exit the point of sale. This operation cannot be undone.'), + 'title': _t('Delete Unpaid Orders ?'), + 'body': _t('This operation will permanently destroy all unpaid orders from all sessions that have been put in the local storage. You will lose all the data and exit the point of sale. This operation cannot be undone.'), confirm: function(){ self.pos.db.remove_all_unpaid_orders(); window.location = '/'; @@ -867,6 +867,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.pos = new module.PosModel(this.session,{chrome:this}); this.gui = new module.Gui({pos: this.pos, chrome: this}); this.chrome = this; // So that chrome's childs have chrome set automatically + this.pos.gui = this.gui; + this.widget = {}; // contains references to subwidgets instances this.numpad_visible = true; @@ -875,7 +877,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.cashier_controls_visible = true; this.pos.ready.done(function(){ - console.log('yeah'); self.build_chrome(); self.pos.on_chrome_started(); self.started.resolve(); @@ -928,23 +929,23 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // loading_error: function(err){ var self = this; - var message = err.message; - var comment = err.stack; + var title = err.message; + var body = err.stack; if(err.message === 'XmlHttpRequestError '){ - message = 'Network Failure (XmlHttpRequestError)'; - comment = 'The Point of Sale could not be loaded due to a network problem.\n Please check your internet connection.'; + title = 'Network Failure (XmlHttpRequestError)'; + body = 'The Point of Sale could not be loaded due to a network problem.\n Please check your internet connection.'; }else if(err.message === 'OpenERP Server Error'){ - message = err.data.message; - comment = err.data.debug; + title = err.data.message; + body = err.data.debug; } - if( typeof comment !== 'string' ){ - comment = 'Traceback not available.'; + if( typeof body !== 'string' ){ + body = 'Traceback not available.'; } var popup = $(QWeb.render('ErrorTracebackPopupWidget',{ - widget: { message: message, comment: comment }, + widget: { title: title , body: body }, })); popup.find('.button').click(function(){ @@ -985,8 +986,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // this.$('.loader').removeClass('oe_hidden').animate({opacity:1},150,'swing'); }, - - screens: { 'products': module.ProductScreenWidget, 'receipt': module.ReceiptScreenWidget, @@ -1150,8 +1149,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ // },function(err,event) { event.preventDefault(); self.gui.show_popup('error',{ - 'message': _t('Could not close the point of sale.'), - 'comment': _t('Your internet connection is probably down.'), + 'title': _t('Could not close the point of sale.'), + 'body': _t('Your internet connection is probably down.'), }); self.close_button.renderElement(); }); diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 301517669e5..a688f096fde 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -157,7 +157,7 @@
@@ -797,58 +797,15 @@ - - - -