From 5e3600224b28ca38c105d7ccda8d22ef7d418a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Sun, 21 Dec 2014 01:05:18 +0100 Subject: [PATCH] [WIP] point_of_sale: split screens / popups / gui into own files --- addons/point_of_sale/static/src/js/chrome.js | 24 +- addons/point_of_sale/static/src/js/gui.js | 326 +++++++++++ addons/point_of_sale/static/src/js/main.js | 4 + addons/point_of_sale/static/src/js/popups.js | 236 ++++++++ addons/point_of_sale/static/src/js/screens.js | 548 +----------------- addons/point_of_sale/views/point_of_sale.xml | 2 + 6 files changed, 592 insertions(+), 548 deletions(-) create mode 100644 addons/point_of_sale/static/src/js/gui.js create mode 100644 addons/point_of_sale/static/src/js/popups.js diff --git a/addons/point_of_sale/static/src/js/chrome.js b/addons/point_of_sale/static/src/js/chrome.js index 52caec2fffd..74f9d96fce4 100644 --- a/addons/point_of_sale/static/src/js/chrome.js +++ b/addons/point_of_sale/static/src/js/chrome.js @@ -949,7 +949,7 @@ openerp.point_of_sale.load_chrome = function load_chrome(instance, module){ //mo })); popup.find('.button').click(function(){ - self.close(); + self.gui.close(); }); popup.css({ zindex: 9001 }); @@ -1040,7 +1040,7 @@ openerp.point_of_sale.load_chrome = function load_chrome(instance, module){ //mo },2000); } else { clearTimeout(this.confirmed); - this.chrome.close(); + this.gui.close(); } }, } @@ -1137,26 +1137,6 @@ openerp.point_of_sale.load_chrome = function load_chrome(instance, module){ //mo } } }, - close: function() { - var self = this; - self.loading_show(); - self.loading_message(_t('Closing ...')); - - self.pos.push_order().then(function(){ - return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']) - .pipe(function(res) { - window.location = '/web#action=' + res[0]['res_id']; - },function(err,event) { - event.preventDefault(); - self.gui.show_popup('error',{ - 'title': _t('Could not close the point of sale.'), - 'body': _t('Your internet connection is probably down.'), - }); - self.close_button.renderElement(); - }); - }); - - }, destroy: function() { this.pos.destroy(); instance.webclient.set_content_full_screen(false); diff --git a/addons/point_of_sale/static/src/js/gui.js b/addons/point_of_sale/static/src/js/gui.js new file mode 100644 index 00000000000..b2e335179a8 --- /dev/null +++ b/addons/point_of_sale/static/src/js/gui.js @@ -0,0 +1,326 @@ + +// this file contains the Gui, which is the pos 'controller'. +// It contains high level methods to manipulate the interface +// such as changing between screens, creating popups, etc. +// +// it is available to all pos objects trough the '.gui' field. + +openerp.point_of_sale.load_gui = function load_gui(instance, module) { + "use strict"; + + var QWeb = instance.web.qweb; + var _t = instance.web._t; + + module.Gui = instance.web.Class.extend({ + init: function(options){ + 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_screen = null; + + 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()); + }); + }); + }, + + /* ---- 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; + }, + + // 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(); + 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); + } + }, + + // 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) { + console.error("ERROR: show_screen("+screen_name+") : screen not found"); + } + + this.close_popup(); + + var order = this.pos.get_order(); + if (order) { + var old_screen_name = order.get_screen_data('screen'); + + order.set_screen_data('screen',screen_name); + + if(params){ + order.set_screen_data('params',params); + } + + if( screen_name !== old_screen_name ){ + order.set_screen_data('previous-screen',old_screen_name); + } + } + + if (refresh || screen !== this.current_screen) { + if (this.current_screen) { + this.current_screen.close(); + this.current_screen.hide(); + } + this.current_screen = screen; + this.current_screen.show(); + } + }, + + // returns the current screen. + get_current_screen: function() { + return this.pos.get_order().get_screen_data('screen') || this.default_screen; + }, + + // 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) { + this.show_screen(previous); + } + }, + + // 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. + // - title: 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',{ + 'title': options.title || _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',{ + 'title': _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, + title: _t('Login as a Manager'), + }) + } + }, + + /* ---- Gui: CLOSING THE POINT OF SALE ---- */ + + close: function() { + var self = this; + this.chrome.loading_show(); + this.chrome.loading_message(_t('Closing ...')); + + this.pos.push_order().then(function(){ + return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']) + .pipe(function(res) { + window.location = '/web#action=' + res[0]['res_id']; + },function(err,event) { + event.preventDefault(); + self.show_popup('error',{ + 'title': _t('Could not close the point of sale.'), + 'body': _t('Your internet connection is probably down.'), + }); + self.chrome.widget.close_button.renderElement(); + }); + }); + }, + + /* ---- 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; + }, + }); +}; + diff --git a/addons/point_of_sale/static/src/js/main.js b/addons/point_of_sale/static/src/js/main.js index 27fe7f1b433..3b3064fa957 100644 --- a/addons/point_of_sale/static/src/js/main.js +++ b/addons/point_of_sale/static/src/js/main.js @@ -12,6 +12,10 @@ openerp.point_of_sale = function(openerp) { openerp.point_of_sale.load_keyboard(openerp,module); + openerp.point_of_sale.load_gui(openerp,module); + + openerp.point_of_sale.load_popups(openerp,module); + openerp.point_of_sale.load_screens(openerp,module); openerp.point_of_sale.load_devices(openerp,module); diff --git a/addons/point_of_sale/static/src/js/popups.js b/addons/point_of_sale/static/src/js/popups.js new file mode 100644 index 00000000000..0fc90eb8638 --- /dev/null +++ b/addons/point_of_sale/static/src/js/popups.js @@ -0,0 +1,236 @@ + +// This file contains the Popups. +// Popups must be loaded and named in chrome.js. +// They are instanciated / destroyed with the .gui.show_popup() +// and .gui.close_popup() methods. + +openerp.point_of_sale.load_popups = function load_popups(instance, module) { + "use strict"; + + module.PopupWidget = module.PosBaseWidget.extend({ + 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. + // 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 + hide: function(){ + if (this.$el) { + this.$el.addClass('oe_hidden'); + } + }, + + // 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); + } + }, + + // 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){ + this._super(options); + + $('body').append(''); + + }, + }); + + module.ErrorTracebackPopupWidget = module.ErrorPopupWidget.extend({ + template:'ErrorTracebackPopupWidget', + }); + + module.ErrorBarcodePopupWidget = module.ErrorPopupWidget.extend({ + template:'ErrorBarcodePopupWidget', + show: function(barcode){ + this._super({barcode: barcode}); + }, + }); + + module.ConfirmPopupWidget = module.PopupWidget.extend({ + template: 'ConfirmPopupWidget', + }); + + /** + * A popup that allows the user to select one item from a list. + * + * show_popup('selection',{ + * title: "Popup Title", + * list: [ + * { label: 'foobar', item: 45 }, + * { label: 'bar foo', item: 'stuff' }, + * ], + * confirm: function(item) { + * // get the item selected by the user. + * }, + * cancel: function(){ + * // user chose nothing + * } + * }); + */ + + module.SelectionPopupWidget = module.PopupWidget.extend({ + template: 'SelectionPopupWidget', + show: function(options){ + options = options || {}; + var self = this; + this._super(); + + this.list = options.list || []; + this.renderElement(); + + this.$('.selection-item').click(function(){ + if (options.confirm) { + var item = self.list[parseInt($(this).data('item-index'))]; + item = item ? item.item : item; + options.confirm.call(self,item); + } + }); + }, + 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({ + template: 'TextInputPopupWidget', + show: function(options){ + options = options || {}; + var self = this; + this._super(); + + this.renderElement(); + this.$('input,textarea').focus(); + + this.$('.button.confirm').click(function(){ + }); + }, + 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({ + template: 'TextAreaPopupWidget', + }); + + module.NumberPopupWidget = module.PopupWidget.extend({ + template: 'NumberPopupWidget', + show: function(options){ + options = options || {}; + var self = this; + this._super(); + + this.inputbuffer = options.value || ''; + this.renderElement(); + this.firstinput = true; + }, + click_numpad: function(event){ + var newbuf = this.gui.numpad_input( + this.inputbuffer, + $(event.target).data('action'), + {'firstinput': this.firstinput}); + + 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'); // WHY NOT ?! + }, + }); + + module.ErrorNoClientPopupWidget = module.ErrorPopupWidget.extend({ + template: 'ErrorNoClientPopupWidget', + }); + + module.ErrorInvoiceTransferPopupWidget = module.ErrorPopupWidget.extend({ + template: 'ErrorInvoiceTransferPopupWidget', + }); + + module.UnsentOrdersPopupWidget = module.ConfirmPopupWidget.extend({ + template: 'UnsentOrdersPopupWidget', + }); + + module.UnpaidOrdersPopupWidget = module.ConfirmPopupWidget.extend({ + template: 'UnpaidOrdersPopupWidget', + }); +}; + diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index f79d6a72152..32794c66bf7 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -1,318 +1,39 @@ -// this file contains the screens definitions. Screens are the +// 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 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 gui, -// which is responsible of hiding and showing the screens, as well as maintaining -// the state of the screens between different orders. +// Screens must be defined and named in chrome.js before use. // -// all screens inherit from ScreenWidget. the only addition from the base widgets +// Screens transitions are controlled by the Gui. +// gui.set_startup_screen() sets the screen displayed at startup +// gui.set_default_screen() sets the screen displayed for new orders +// gui.show_screen() shows a screen +// gui.back() goes to the previous screen +// +// Screen state is saved in the order. When a new order is selected, +// a screen is displayed based on the state previously saved in the order. +// this is also done in the Gui with: +// gui.show_saved_screen() +// +// 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 gui guarantees // that only one screen is shown at the same time and that show() is called after all // hide()s +// +// Each Screens must be independant from each other, and should have no +// persistent state outside the models. Screen state variables are reset at +// each screen display. A screen can be called with parameters, which are +// to be used for the duration of the screen only. openerp.point_of_sale.load_screens = function load_screens(instance, module){ //module is instance.point_of_sale "use strict"; - var QWeb = instance.web.qweb, - _t = instance.web._t; + var QWeb = instance.web.qweb; + var _t = instance.web._t; var round_pr = instance.web.round_precision - module.Gui = instance.web.Class.extend({ - init: function(options){ - 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_screen = null; - - 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()); - }); - }); - }, - - /* ---- 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; - }, - - // 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(); - 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); - } - }, - - // 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) { - console.error("ERROR: show_screen("+screen_name+") : screen not found"); - } - - this.close_popup(); - - var order = this.pos.get_order(); - if (order) { - var old_screen_name = order.get_screen_data('screen'); - - order.set_screen_data('screen',screen_name); - - if(params){ - order.set_screen_data('params',params); - } - - if( screen_name !== old_screen_name ){ - order.set_screen_data('previous-screen',old_screen_name); - } - } - - if (refresh || screen !== this.current_screen) { - if (this.current_screen) { - this.current_screen.close(); - this.current_screen.hide(); - } - this.current_screen = screen; - this.current_screen.show(); - } - }, - - // returns the current screen. - get_current_screen: function() { - return this.pos.get_order().get_screen_data('screen') || this.default_screen; - }, - - // 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) { - this.show_screen(previous); - } - }, - - // 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. - // - title: 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',{ - 'title': options.title || _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',{ - 'title': _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, - 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({ show_numpad: true, @@ -437,231 +158,6 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }, }); - module.PopUpWidget = module.PosBaseWidget.extend({ - 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. - // 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 - hide: function(){ - if (this.$el) { - this.$el.addClass('oe_hidden'); - } - }, - - // 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); - } - }, - - // 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){ - this._super(options); - - $('body').append(''); - - }, - }); - - module.ErrorTracebackPopupWidget = module.ErrorPopupWidget.extend({ - template:'ErrorTracebackPopupWidget', - }); - - module.ErrorBarcodePopupWidget = module.ErrorPopupWidget.extend({ - template:'ErrorBarcodePopupWidget', - show: function(barcode){ - this._super({barcode: barcode}); - }, - }); - - module.ConfirmPopupWidget = module.PopUpWidget.extend({ - template: 'ConfirmPopupWidget', - }); - - /** - * A popup that allows the user to select one item from a list. - * - * show_popup('selection',{ - * title: "Popup Title", - * list: [ - * { label: 'foobar', item: 45 }, - * { label: 'bar foo', item: 'stuff' }, - * ], - * confirm: function(item) { - * // get the item selected by the user. - * }, - * cancel: function(){ - * // user chose nothing - * } - * }); - */ - - module.SelectionPopupWidget = module.PopUpWidget.extend({ - template: 'SelectionPopupWidget', - show: function(options){ - options = options || {}; - var self = this; - this._super(); - - this.list = options.list || []; - this.renderElement(); - - this.$('.selection-item').click(function(){ - if (options.confirm) { - var item = self.list[parseInt($(this).data('item-index'))]; - item = item ? item.item : item; - options.confirm.call(self,item); - } - }); - }, - 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({ - template: 'TextInputPopupWidget', - show: function(options){ - options = options || {}; - var self = this; - this._super(); - - this.renderElement(); - this.$('input,textarea').focus(); - - this.$('.button.confirm').click(function(){ - }); - }, - 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({ - template: 'TextAreaPopupWidget', - }); - - module.NumberPopupWidget = module.PopUpWidget.extend({ - template: 'NumberPopupWidget', - show: function(options){ - options = options || {}; - var self = this; - this._super(); - - this.inputbuffer = options.value || ''; - this.renderElement(); - this.firstinput = true; - }, - click_numpad: function(event){ - var newbuf = this.gui.numpad_input( - this.inputbuffer, - $(event.target).data('action'), - {'firstinput': this.firstinput}); - - 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'); // WHY NOT ?! - }, - }); - - module.ErrorNoClientPopupWidget = module.ErrorPopupWidget.extend({ - template: 'ErrorNoClientPopupWidget', - }); - - module.ErrorInvoiceTransferPopupWidget = module.ErrorPopupWidget.extend({ - template: 'ErrorInvoiceTransferPopupWidget', - }); - - module.UnsentOrdersPopupWidget = module.ConfirmPopupWidget.extend({ - template: 'UnsentOrdersPopupWidget', - }); - - module.UnpaidOrdersPopupWidget = module.ConfirmPopupWidget.extend({ - template: 'UnpaidOrdersPopupWidget', - }); module.ScaleScreenWidget = module.ScreenWidget.extend({ template:'ScaleScreenWidget', @@ -1572,5 +1068,5 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ // }, }); -} +}; diff --git a/addons/point_of_sale/views/point_of_sale.xml b/addons/point_of_sale/views/point_of_sale.xml index d9918f8dc12..665f5faf889 100644 --- a/addons/point_of_sale/views/point_of_sale.xml +++ b/addons/point_of_sale/views/point_of_sale.xml @@ -14,6 +14,8 @@ + +