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 @@
+
+