[WIP] point_of_sale: split screens / popups / gui into own files
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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('<audio src="/point_of_sale/static/src/sounds/error.wav" autoplay="true"></audio>');
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
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',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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('<audio src="/point_of_sale/static/src/sounds/error.wav" autoplay="true"></audio>');
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
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){ //
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/keyboard.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/chrome.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/devices.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/gui.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/popups.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/screens.js"></script>
|
||||
<script type="text/javascript" src="/point_of_sale/static/src/js/tests.js"></script>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user