[WIP] point_of_sale: architecture cleanup : first pass

- This is the first pass of a big cleanup. This breaks
   all backward compatibility of pos .js extensions.

 - PosWidget has been renamed to 'Chrome'. The Chrome is both
   the root widget but also a reference to all chrome subwidgets.
   It is the V of MVC.

 - ScreenSelector has been renamed to 'Gui'. The Gui contains
   the method to manipulate the user interface by changing
   screens and instanciating popups. It is the C of MVC

 - PosModel is still the root model, the M of MVC

 - All these objects are available to all Pos objects trough the
   .pos .chrome and .gui references. This is very practical, but
   usually you should not need to refer to the chrome or to the
   gui in your models. And you should also minimize references
   to .chrome, which should be manipulated via .gui

 - The Popups, Screens, and Widgets are now declared declaratively
   in the Chrome prototype for easier extension by external modules

 - You can hook yourself into the various loading steps trough
   deferreds:
    - pos.ready      -> All models have been loaded
    - chrome.started -> The chrome dom is inserted but incomplete
    - chrome.ready   -> The pos is fully rendered and ready to use

 - The Chrome subwidgets are available trough .widget
   ex: pos_widget.numpad -> chrome.widget.numpad

 - The GUI api has been changed a bit :
    - set_current_screen -> .show_screen
This commit is contained in:
Frédéric van der Essen
2014-12-31 15:34:30 +01:00
parent f15d84094d
commit 02e829bf50
7 changed files with 308 additions and 362 deletions
@@ -416,7 +416,7 @@ openerp.point_of_sale.load_devices = function load_devices(instance,module){ //m
send_printing_job();
},function(error){
if (error) {
self.pos.pos_widget.screen_selector.show_popup('error-traceback',{
self.pos.chrome.screen_selector.show_popup('error-traceback',{
'message': _t('Printing Error: ') + error.data.message,
'comment': error.data.debug,
});
+1 -1
View File
@@ -18,7 +18,7 @@ openerp.point_of_sale = function(openerp) {
openerp.point_of_sale.load_widgets(openerp,module); // import pos_widgets.js
openerp.web.client_actions.add('pos.ui', 'openerp.point_of_sale.PosWidget');
openerp.web.client_actions.add('pos.ui', 'openerp.point_of_sale.Chrome');
};
+29 -19
View File
@@ -3,7 +3,6 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
var QWeb = instance.web.qweb;
var _t = instance.web._t;
var barcode_parser_module = instance.barcodes;
var round_di = instance.web.round_decimals;
var round_pr = instance.web.round_precision
@@ -24,10 +23,14 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
var self = this;
this.session = session;
this.flush_mutex = new $.Mutex(); // used to make sure the orders are sent to the server once at time
this.pos_widget = attributes.pos_widget;
this.chrome = attributes.chrome;
this.gui = attributes.gui;
this.proxy = new module.ProxyDevice(this); // used to communicate to the hardware devices via a local proxy
this.barcode_reader = new module.BarcodeReader({'pos': this, proxy:this.proxy});
this.chrome.ready.then(function(){
self.barcode_reader.connect();
});
this.proxy_queue = new module.JobQueue(); // used to prevent parallels communications to the proxy
this.db = new module.PosDB(); // a local database used to search trough products and categories & store pending orders
@@ -73,16 +76,23 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
this.get('orders').bind('remove', function(order,_unused_,options){
self.on_removed_order(order,options.index,options.reason);
});
// We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched,
// Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched.
// when all the data has loaded, we compute some stuff, and declare the Pos ready to be used.
this.ready = this.load_server_data()
.then(function(){
if(self.config.use_proxy){
return self.connect_to_proxy();
}
}); // used to read barcodes);
this.ready = this.load_server_data();
},
on_chrome_started: function(){
this.barcode_reader.connect();
this.load_orders();
this.set_start_order();
},
on_chrome_ready: function(){
if(this.config.use_proxy){
return this.connect_to_proxy();
}
this.push_order();
},
// releases ressources holds by the model at the end of life of the posmodel
@@ -97,14 +107,14 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
var self = this;
var done = new $.Deferred();
this.barcode_reader.disconnect_from_proxy();
this.pos_widget.loading_message(_t('Connecting to the PosBox'),0);
this.pos_widget.loading_skip(function(){
this.chrome.loading_message(_t('Connecting to the PosBox'),0);
this.chrome.loading_skip(function(){
self.proxy.stop_searching();
});
this.proxy.autoconnect({
force_ip: self.config.proxy_ip || undefined,
progress: function(prog){
self.pos_widget.loading_progress(prog);
self.chrome.loading_progress(prog);
},
}).then(function(){
if(self.config.iface_scan_via_proxy){
@@ -119,7 +129,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
// helper function to load data from the server. Obsolete use the models loader below.
fetch: function(model, fields, domain, ctx){
this._load_progress = (this._load_progress || 0) + 0.05;
this.pos_widget.loading_message(_t('Loading')+' '+model,this._load_progress);
this.chrome.loading_message(_t('Loading')+' '+model,this._load_progress);
return new instance.web.Model(model).query(fields).filter(domain).context(ctx).all()
},
@@ -397,7 +407,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
}, {
label: 'barcodes',
loaded: function(self) {
var barcode_parser = new barcode_parser_module.BarcodeParser({'nomenclature_id': self.config.barcode_nomenclature_id});
var barcode_parser = new instance.barcodes.BarcodeParser({'nomenclature_id': self.config.barcode_nomenclature_id});
self.barcode_reader.set_barcode_parser(barcode_parser);
return barcode_parser.is_loaded();
},
@@ -417,7 +427,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
loaded.resolve();
}else{
var model = self.models[index];
self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
self.chrome.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
var cond = typeof model.condition === 'function' ? model.condition(self,tmp) : true;
if (!cond) {
@@ -649,7 +659,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
transfer.pipe(function(order_server_id){
// generate the pdf and download it
self.pos_widget.do_action('point_of_sale.pos_invoice_report',{additional_context:{
self.chrome.do_action('point_of_sale.pos_invoice_report',{additional_context:{
active_ids:order_server_id,
}});
@@ -722,7 +732,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
if (error.data.exception_type == 'warning') {
delete error.data.debug;
}
self.pos_widget.screen_selector.show_popup('error-traceback',{
self.gui.show_popup('error-traceback',{
message: error.data.message,
comment: error.data.debug
});
@@ -1107,7 +1117,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
this.pos = options.pos;
this.selected_orderline = undefined;
this.selected_paymentline = undefined;
this.screen_data = {}; // see ScreenSelector
this.screen_data = {}; // see Gui
this.temporary = options.temporary || false;
this.creation_date = new Date();
this.to_invoice = false;
@@ -1236,7 +1246,7 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
qweb.default_dict = _.clone(QWeb.default_dict);
qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>');
return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.pos_widget,'order':self, 'receipt': receipt}) ;
return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.chrome,'order':self, 'receipt': receipt}) ;
}
}
+95 -110
View File
@@ -1,17 +1,17 @@
// this file contains the screens definitions. Screens are the
// content of the right pane of the pos, containing the main functionalities.
// screens are contained in the PosWidget, in pos_widget.js
// screens are contained in the PosWidget, in chrome.js
// all screens are present in the dom at all time, but only one is shown at the
// same time.
//
// transition between screens is made possible by the use of the screen_selector,
// transition between screens is made possible by the use of the gui,
// which is responsible of hiding and showing the screens, as well as maintaining
// the state of the screens between different orders.
//
// all screens inherit from ScreenWidget. the only addition from the base widgets
// are show() and hide() which shows and hides the screen but are also used to
// bind and unbind actions on widgets and devices. The screen_selector guarantees
// bind and unbind actions on widgets and devices. The gui guarantees
// that only one screen is shown at the same time and that show() is called after all
// hide()s
@@ -23,37 +23,44 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
var round_pr = instance.web.round_precision
module.ScreenSelector = instance.web.Class.extend({
module.Gui = instance.web.Class.extend({
init: function(options){
this.pos = options.pos;
this.screen_set = options.screen_set || {};
this.popup_set = options.popup_set || {};
this.default_screen = options.default_screen;
this.startup_screen = options.startup_screen;
var self = this;
this.pos = options.pos;
this.chrome = options.chrome;
this.screen_set = {};
this.popup_set = {};
this.default_screen = null;
this.startup_screen = null;
this.current_popup = null;
this.current_mode = options.default_mode || 'cashier';
this.current_screen = null;
for (var screen_name in this.screen_set) {
this.screen_set[screen_name].hide();
}
for (var popup_name in this.popup_set) {
this.popup_set[popup_name].hide();
}
if (this.pos.get_order()) {
this.pos.get_order().set_screen_data({
'screen': this.default_screen,
this.chrome.ready.then(function(){
var order = self.pos.get_order();
if (order) {
self.show_saved_screen(order);
} else {
self.show_screen(self.startup_screen);
}
self.pos.bind('change:selectedOrder', function(){
self.show_saved_screen(self.pos.get_order());
});
}
this.pos.bind('change:selectedOrder', this.load_saved_screen, this);
});
},
add_screen: function(screen_name, screen){
add_screen: function(name, screen){
screen.hide();
this.screen_set[screen_name] = screen;
return this;
this.screen_set[name] = screen;
},
set_default_screen: function(name){
this.default_screen = name;
},
set_startup_screen: function(name) {
this.startup_screen = name;
},
add_popup: function(name, popup) {
popup.hide();
this.popup_set[name] = popup;
},
show_popup: function(name,options){
if(this.current_popup){
@@ -69,29 +76,25 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.current_popup = null;
}
},
load_saved_screen: function(options){
show_saved_screen: function(order,options) {
options = options || {};
this.close_popup();
var selectedOrder = this.pos.get_order();
// FIXME : this changing screen behaviour is sometimes confusing ...
this.set_current_screen(selectedOrder.get_screen_data('screen') || options.default_screen || this.default_screen,null,'refresh');
//this.set_current_screen(this.default_screen,null,'refresh');
},
set_user_mode: function(user_mode){
if(user_mode !== this.current_mode){
this.close_popup();
this.current_mode = user_mode;
this.load_saved_screen();
if (order) {
this.show_screen(order.get_screen_data('screen') ||
options.default_screen ||
this.default_screen,
null,'refresh');
} else {
this.show_screen(this.startup_screen);
}
},
get_user_mode: function(){
return this.current_mode;
},
set_current_screen: function(screen_name,params,refresh){
show_screen: function(screen_name,params,refresh){
var screen = this.screen_set[screen_name];
if(!screen){
console.error("ERROR: set_current_screen("+screen_name+") : screen not found");
console.error("ERROR: show_screen("+screen_name+") : screen not found");
debugger;
}
this.close_popup();
@@ -126,19 +129,13 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
back: function(){
var previous = this.pos.get_order().get_screen_data('previous-screen');
if(previous){
this.set_current_screen(previous);
this.show_screen(previous);
}
},
get_current_screen_param: function(param){
var params = this.pos.get_order().get_screen_data('params');
return params ? params[param] : undefined;
},
set_default_screen: function(){
this.set_current_screen(this.default_screen);
},
change_default_screen: function(screen){
this.default_screen = screen;
},
});
module.ScreenWidget = module.PosBaseWidget.extend({
@@ -152,7 +149,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
},
help_button_action: function(){
this.pos_widget.screen_selector.show_popup('help');
this.gui.show_popup('help');
},
barcode_product_screen: 'products', //if defined, this screen will be loaded when a product is scanned
@@ -165,10 +162,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
var self = this;
if(self.pos.scan_product(code)){
if(self.barcode_product_screen){
self.pos_widget.screen_selector.set_current_screen(self.barcode_product_screen);
self.gui.show_screen(self.barcode_product_screen);
}
}else{
self.pos_widget.screen_selector.show_popup('error-barcode',code.code);
self.gui.show_popup('error-barcode',code.code);
}
},
@@ -181,11 +178,11 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
for(var i = 0, len = users.length; i < len; i++){
if(users[i].barcode === code.code){
this.pos.set_cashier(users[i]);
this.pos_widget.username.renderElement();
this.chrome.widget.username.renderElement();
return true;
}
}
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
this.gui.show_popup('error-barcode',code.code);
return false;
},
@@ -199,7 +196,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.pos.get_order().set_client(partner);
return true;
}
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
this.gui.show_popup('error-barcode',code.code);
return false;
},
@@ -213,7 +210,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
},
// What happens when an invalid barcode is scanned : shows an error popup.
barcode_error_action: function(code){
this.pos_widget.screen_selector.show_popup('error-barcode',code.code);
this.gui.show_popup('error-barcode',code.code);
},
// this method shows the screen and sets up all the widget related to this screen. Extend this method
@@ -228,10 +225,9 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
var self = this;
this.pos_widget.set_numpad_visible(this.show_numpad);
this.pos_widget.set_leftpane_visible(this.show_leftpane);
this.chrome.set_numpad_visible(this.show_numpad);
this.chrome.set_leftpane_visible(this.show_leftpane);
this.pos_widget.username.set_user_mode(this.pos_widget.screen_selector.get_user_mode());
this.pos.barcode_reader.set_action_callback({
'cashier': self.barcode_cashier_action ? function(code){ self.barcode_cashier_action(code); } : undefined ,
'product': self.barcode_product_action ? function(code){ self.barcode_product_action(code); } : undefined ,
@@ -298,10 +294,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.renderElement();
this.$('.button.fullscreen').off('click').click(function(){
window.document.body.webkitRequestFullscreen();
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
});
this.$('.button.cancel').off('click').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
});
},
ismobile: function(){
@@ -328,7 +324,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.pos.barcode_reader.reset_action_callbacks();
this.$('.footer .button').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if ( options.confirm ) {
options.confirm.call(self);
}
@@ -364,14 +360,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.renderElement();
this.$('.button.cancel').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if( options.cancel ){
options.cancel.call(self);
}
});
this.$('.button.confirm').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if( options.confirm ){
options.confirm.call(self);
}
@@ -410,14 +406,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.renderElement();
this.$('.button.cancel').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if (options.cancel){
options.cancel.call(self);
}
});
this.$('.selection-item').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if (options.confirm) {
var item = self.list[parseInt($(this).data('item-index'))];
item = item ? item.item : item;
@@ -441,14 +437,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.$('input,textarea').focus();
this.$('.button.cancel').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if( options.cancel ){
options.cancel.call(self);
}
});
this.$('.button.confirm').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
var value = self.$('input,textarea').val();
if( options.confirm ){
options.confirm.call(self,value);
@@ -522,14 +518,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
self.click_numpad_button($(this),event);
});
this.$('.button.cancel').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if( options.cancel ){
options.cancel.call(self);
}
});
this.$('.button.confirm').click(function(){
self.pos_widget.screen_selector.close_popup();
self.gui.close_popup();
if( options.confirm ){
options.confirm.call(self,self.inputbuffer);
}
@@ -579,21 +575,21 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.hotkey_handler = function(event){
if(event.which === 13){
self.order_product();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
self.gui.show_screen(self.next_screen);
}else if(event.which === 27){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
self.gui.show_screen(self.previous_screen);
}
};
$('body').on('keyup',this.hotkey_handler);
this.$('.back').click(function(){
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
self.gui.show_screen(self.previous_screen);
});
this.$('.next,.buy-product').click(function(){
self.order_product();
self.pos_widget.screen_selector.set_current_screen(self.next_screen);
self.gui.show_screen(self.next_screen);
});
queue.schedule(function(){
@@ -604,12 +600,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
},
get_product: function(){
var ss = this.pos_widget.screen_selector;
if(ss){
return ss.get_current_screen_param('product');
}else{
return undefined;
}
return this.gui.get_current_screen_param('product');
},
order_product: function(){
this.pos.get_order().add_product(this.get_product(),{ quantity: this.weight });
@@ -667,7 +658,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.product_list_widget = new module.ProductListWidget(this,{
click_product_action: function(product){
if(product.to_weight && self.pos.config.iface_electronic_scale){
self.pos_widget.screen_selector.set_current_screen('scale',{product: product});
self.gui.show_screen('scale',{product: product});
}else{
self.pos.get_order().add_product(product);
}
@@ -684,20 +675,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
show: function(){
this._super();
var self = this;
this.product_categories_widget.reset_category();
this.pos_widget.order_widget.set_editable(true);
this.chrome.widget.order.enable_numpad();
},
close: function(){
this._super();
this.pos_widget.order_widget.set_editable(false);
if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
this.pos_widget.onscreen_keyboard.hide();
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){
this.chrome.widget.keyboard.hide();
}
},
});
@@ -724,12 +709,12 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.new_client = this.old_client;
this.$('.back').click(function(){
self.pos_widget.screen_selector.back();
self.gui.back();
});
this.$('.next').click(function(){
this.$('.next').click(function(){
self.save_changes();
self.pos_widget.screen_selector.back();
self.gui.back(); // FIXME HUH ?
});
this.$('.new-customer').click(function(){
@@ -753,8 +738,8 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
var search_timeout = null;
if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
this.pos_widget.onscreen_keyboard.connect(this.$('.searchbox input'));
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){
this.chrome.widget.keyboard.connect(this.$('.searchbox input'));
}
this.$('.searchbox input').on('keyup',function(event){
@@ -785,7 +770,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
if ( associate_result && customers.length === 1){
this.new_client = customers[0];
this.save_changes();
this.pos_widget.screen_selector.back();
this.gui.back();
}
this.render_list(customers);
}else{
@@ -896,7 +881,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
});
if (!fields.name) {
this.pos_widget.screen_selector.show_popup('error',{
this.gui.show_popup('error',{
message: _t('A Customer Name Is Required'),
});
return;
@@ -914,7 +899,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
self.saved_client_details(partner_id);
},function(err,event){
event.preventDefault();
self.pos_widget.screen_selector.show_popup('error',{
self.gui.show_popup('error',{
'message':_t('Error: Could not Save Changes'),
'comment':_t('Your Internet connection is probably down.'),
});
@@ -971,7 +956,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
load_image_file: function(file, callback){
var self = this;
if (!file.type.match(/image.*/)) {
this.pos_widget.screen_selector.show_popup('error',{
this.gui.show_popup('error',{
message:_t('Unsupported File Format'),
comment:_t('Only web-compatible Image formats such as .png or .jpeg are supported'),
});
@@ -986,7 +971,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
self.resize_image_to_dataurl(img,800,600,callback);
}
reader.onerror = function(){
self.pos_widget.screen_selector.show_popup('error',{
self.gui.show_popup('error',{
message:_t('Could Not Read Image'),
comment:_t('The provided file could not be read due to an unknown error'),
});
@@ -1349,10 +1334,10 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
}
},
click_set_customer: function(){
this.pos_widget.screen_selector.set_current_screen('clientlist');
this.gui.show_screen('clientlist');
},
click_back: function(){
this.pos_widget.screen_selector.set_current_screen('products');
this.gui.show_screen('products');
},
renderElement: function() {
var self = this;
@@ -1443,7 +1428,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
// FIXME: this check is there because the backend is unable to
// process empty orders. This is not the right place to fix it.
if (order.get_orderlines().length === 0) {
this.pos_widget.screen_selector.show_popup('error',{
this.gui.show_popup('error',{
'message': _t('Empty Order'),
'comment': _t('There must be at least one product in your order before it can be validated'),
});
@@ -1461,7 +1446,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
cash = cash || (this.pos.cashregisters[i].journal.type === 'cash');
}
if (!cash) {
this.pos_widget.screen_selector.show_popup('error',{
this.gui.show_popup('error',{
message: _t('Cannot return change without a cash payment method'),
comment: _t('There is no cash payment method available in this point of sale to handle the change.\n\n Please pay the exact amount or add a cash payment method in the point of sale configuration'),
});
@@ -1481,15 +1466,15 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
invoiced.fail(function(error){
self.invoicing = false;
if (error === 'error-no-client') {
self.pos_widget.screen_selector.show_popup('confirm',{
self.gui.show_popup('confirm',{
message: _t('Please select the Customer'),
comment: _t('You need to select the customer before you can invoice an order.'),
confirm: function(){
self.pos_widget.screen_selector.set_current_screen('clientlist');
self.gui.show_screen('clientlist');
},
});
} else {
self.pos_widget.screen_selector.show_popup('error',{
self.gui.show_popup('error',{
message: _t('The order could not be sent'),
comment: _t('Check your internet connection and try again.'),
});
@@ -1506,7 +1491,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
this.print_escpos_receipt();
order.finalize(); //finish order and go back to scan screen
} else {
this.pos_widget.screen_selector.set_current_screen(this.next_screen);
this.gui.show_screen(this.next_screen);
}
}
},
@@ -17,8 +17,9 @@ openerp.point_of_sale.load_basewidget = function load_basewidget(instance, modul
init:function(parent,options){
this._super(parent);
options = options || {};
this.pos = options.pos || (parent ? parent.pos : undefined);
this.pos_widget = options.pos_widget || (parent ? parent.pos_widget : undefined);
this.pos = options.pos || (parent ? parent.pos : undefined);
this.chrome = options.chrome || (parent ? parent.chrome : undefined);
this.gui = options.gui || (parent ? parent.gui : undefined);
},
format_currency: function(amount,precision){
var currency = (this.pos && this.pos.currency) ? this.pos.currency : {symbol:'$', position: 'after', rounding: 0.01, decimals: 2};
+178 -228
View File
@@ -92,10 +92,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
var self = this;
this._super();
this.$('.pay').click(function(){
self.pos.pos_widget.screen_selector.set_current_screen('payment');
self.gui.show_screen('payment');
});
this.$('.set-customer').click(function(){
self.pos.pos_widget.screen_selector.set_current_screen('clientlist');
self.gui.show_screen('clientlist');
});
}
});
@@ -112,7 +112,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
return;
}
self.pos.get_order().select_orderline(this.orderline);
self.pos_widget.numpad.state.reset();
self.chrome.widget.numpad.state.reset();
};
this.client_change_handler = function(event){
self.update_summary();
@@ -123,7 +123,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
},
enable_numpad: function(){
this.disable_numpad(); //ensure we don't register the callbacks twice
this.numpad_state = this.pos_widget.numpad.state;
this.numpad_state = this.chrome.widget.numpad.state;
if(this.numpad_state){
this.numpad_state.reset();
this.numpad_state.bind('set_value', this.set_value, this);
@@ -136,18 +136,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
this.numpad_state.reset();
}
},
set_editable: function(editable){
var order = this.pos.get_order();
if (order) {
this.editable = editable;
if (editable) {
this.enable_numpad();
}else{
this.disable_numpad();
order.deselect_orderline();
}
}
},
set_value: function(val) {
var order = this.pos.get_order();
if (this.editable && order.get_selected_orderline()) {
@@ -224,7 +212,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
target.parentNode.replaceChild(this.el,target);
},
renderElement: function(scrollbottom){
this.pos_widget.numpad.state.reset();
this.chrome.widget.numpad.state.reset(); //FIXME WTF
var order = this.pos.get_order();
if (!order) {
@@ -297,7 +285,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
if (!order) {
return;
} else if ( !order.is_empty() ){
this.pos_widget.screen_selector.show_popup('confirm',{
this.gui.show_popup('confirm',{
message: _t('Destroy Current Order ?'),
comment: _t('You will lose any data associated with the current order'),
confirm: function(){
@@ -463,8 +451,8 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
this.el.querySelector('.search-clear').addEventListener('click',this.clear_search_handler);
if(this.pos.config.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
this.pos_widget.onscreen_keyboard.connect($(this.el.querySelector('.searchbox input')));
if(this.pos.config.iface_vkeyboard && this.chrome.widget.keyboard){
this.chrome.widget.keyboard.connect($(this.el.querySelector('.searchbox input')));
}
},
@@ -578,10 +566,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
var options = options || {};
this._super(parent,options);
},
set_user_mode: function(mode){
this.mode = mode;
this.renderElement();
},
renderElement: function(){
var self = this;
this._super();
@@ -592,7 +576,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
},
click_username: function(){
var self = this;
this.pos_widget.select_user({
this.chrome.select_user({
'security': true,
'current_user': this.pos.get_cashier(),
'message': _t('Change Cashier'),
@@ -736,10 +720,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
self.pos.barcode_reader.scan(self.$('input.ean').val());
});
this.$('.button.show_orders').click(function(){
self.pos.pos_widget.screen_selector.show_popup('unsent-orders');
self.gui.show_popup('unsent-orders');
});
this.$('.button.delete_orders').click(function(){
self.pos.pos_widget.screen_selector.show_popup('confirm',{
self.gui.show_popup('confirm',{
message: _t('Delete Unsent Orders ?'),
comment: _t('This operation will permanently destroy all unsent orders from the local storage. You will lose all the data. This operation cannot be undone.'),
confirm: function(){
@@ -749,10 +733,10 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
});
});
this.$('.button.show_unpaid_orders').click(function(){
self.pos.pos_widget.screen_selector.show_popup('unpaid-orders');
self.gui.show_popup('unpaid-orders');
});
this.$('.button.delete_unpaid_orders').click(function(){
self.pos.pos_widget.screen_selector.show_popup('confirm',{
self.gui.show_popup('confirm',{
message: _t('Delete Unpaid Orders ?'),
comment: _t('This operation will permanently destroy all unpaid orders from all sessions that have been put in the local storage. You will lose all the data and exit the point of sale. This operation cannot be undone.'),
confirm: function(){
@@ -862,30 +846,69 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
});
// The PosWidget is the main widget that contains all other widgets in the PointOfSale.
// The Chrome is the main widget that contains all other widgets in the PointOfSale.
// It is mainly composed of :
// - a header, containing the list of orders
// - a leftpane, containing the list of bought products (orderlines)
// - a rightpane, containing the screens (see pos_screens.js)
// - popups
// - an onscreen keyboard
// a screen_selector which controls the switching between screens and the showing/closing of popups
// a gui which controls the switching between screens and the showing/closing of popups
module.PosWidget = module.PosBaseWidget.extend({
template: 'PosWidget',
module.Chrome = module.PosBaseWidget.extend({
template: 'Chrome',
init: function() {
var self = this;
this._super(arguments[0],{});
this.pos = new module.PosModel(this.session,{pos_widget:this});
this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically
this.started = new $.Deferred(); // resolves when DOM is onlyne
this.ready = new $.Deferred(); // resolves when the whole GUI has been loaded
this.pos = new module.PosModel(this.session,{chrome:this});
this.gui = new module.Gui({pos: this.pos, chrome: this});
this.chrome = this; // So that chrome's childs have chrome set automatically
this.widget = {}; // contains references to subwidgets instances
this.numpad_visible = true;
this.leftpane_visible = true;
this.leftpane_width = '440px';
this.cashier_controls_visible = true;
this.pos.ready.done(function(){
console.log('yeah');
self.build_chrome();
self.pos.on_chrome_started();
self.started.resolve();
self.build_widgets();
self.pos.on_chrome_ready();
self.ready.resolve();
self.disable_rubberbanding();
self.loading_hide();
}).fail(function(err){ // error when loading models data from the backend
self.loading_error(err);
});
},
build_chrome: function() {
// remove default webclient handlers that induce click delay
$(document).off();
$(window).off();
$('html').off();
$('body').off();
$(self.$el).parent().off();
$('document').off();
$('.oe_web_client').off();
$('.openerp_webclient_container').off();
FastClick.attach(document.body);
instance.webclient.set_content_full_screen(true);
this.renderElement();
if(this.pos.config.iface_big_scrollbars){
this.$el.addClass('big-scrollbars');
}
},
disable_rubberbanding: function(){
@@ -902,49 +925,6 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
});
},
start: function() {
var self = this;
return self.pos.ready.done(function() {
// remove default webclient handlers that induce click delay
$(document).off();
$(window).off();
$('html').off();
$('body').off();
$(self.$el).parent().off();
$('document').off();
$('.oe_web_client').off();
$('.openerp_webclient_container').off();
self.renderElement();
self.pos.load_orders();
self.pos.set_start_order();
self.build_widgets();
if(self.pos.config.iface_big_scrollbars){
self.$el.addClass('big-scrollbars');
}
self.screen_selector.set_default_screen();
self.pos.barcode_reader.connect();
instance.webclient.set_content_full_screen(true);
if(self.pos.config.iface_fullscreen && document.body.webkitRequestFullscreen && (
window.screen.availWidth > window.innerWidth ||
window.screen.availHeight > window.innerHeight )){
self.screen_selector.show_popup('fullscreen');
}
self.loading_hide();
self.pos.push_order();
}).fail(function(err){ // error when loading models data from the backend
self.loading_error(err);
});
},
loading_error: function(err){
var self = this;
@@ -1029,7 +1009,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
}
}
this.pos_widget.screen_selector.show_popup('selection',{
this.gui.show_popup('selection',{
'message': options.message || _t('Select User'),
list: list,
confirm: function(user){ def.resolve(user); },
@@ -1040,11 +1020,11 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
if (options.security && user !== options.current_user && user.pos_security_pin) {
var ret = new $.Deferred();
self.pos_widget.screen_selector.show_popup('password',{
self.gui.show_popup('password',{
'message': _t('Password'),
confirm: function(password) {
if (password !== user.pos_security_pin) {
this.pos_widget.screen_selector.show_popup('error',{
this.gui.show_popup('error',{
'message':_t('Password Incorrect'),
});
ret.reject();
@@ -1082,171 +1062,141 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
}
},
// This method instantiates all the screens, widgets, etc. If you want to add new screens change the
// startup screen, etc, override this method.
screens: {
'products': module.ProductScreenWidget,
'receipt': module.ReceiptScreenWidget,
'payment': module.PaymentScreenWidget,
'clientlist': module.ClientListScreenWidget,
'scale': module.ScaleScreenWidget,
},
popups: {
'error': module.ErrorPopupWidget,
'error-barcode': module.ErrorBarcodePopupWidget,
'error-traceback': module.ErrorTracebackPopupWidget,
'textinput': module.TextInputPopupWidget,
'textarea': module.TextAreaPopupWidget,
'number': module.NumberPopupWidget,
'password': module.PasswordPopupWidget,
'confirm': module.ConfirmPopupWidget,
'selection': module.SelectionPopupWidget,
'unsent-orders': module.UnsentOrdersPopupWidget,
'unpaid-orders': module.UnpaidOrdersPopupWidget,
},
widgets: [
{
'name': 'order_selector',
'widget': module.OrderSelectorWidget,
'replace': '.placeholder-OrderSelectorWidget',
},{
'name': 'proxy_status',
'widget': module.ProxyStatusWidget,
'append': '.pos-rightheader',
'condition': function(self){ return self.pos.config.use_proxy },
},{
'name': 'notification',
'widget': module.SynchNotificationWidget,
'append': '.pos-rightheader',
},{
'name': 'close_button',
'widget': module.HeaderButtonWidget,
'append': '.pos-rightheader',
'args': {
label: _t('Close'),
action: function(){
var self = this;
if (!this.confirmed) {
this.$el.addClass('confirm');
this.$el.text(_t('Confirm'));
this.confirmed = setTimeout(function(){
self.$el.removeClass('confirm');
self.$el.text(_t('Close'));
self.confirmed = false;
},2000);
} else {
clearTimeout(this.confirmed);
this.chrome.close();
}
},
}
},{
'name': 'username',
'widget': module.UsernameWidget,
'replace': '.placeholder-UsernameWidget',
},{
'name': 'actionpad',
'widget': module.ActionpadWidget,
'replace': '.placeholder-ActionpadWidget',
},{
'name': 'numpad',
'widget': module.NumpadWidget,
'replace': '.placeholder-NumpadWidget',
},{
'name': 'order',
'widget': module.OrderWidget,
'replace': '.placeholder-OrderWidget',
},{
'name': 'keyboard',
'widget': module.OnscreenKeyboardWidget,
'replace': '.placeholder-OnscreenKeyboardWidget',
},{
'name': 'debug',
'widget': module.DebugWidget,
'append': '.pos-content',
'condition': function(self){ return self.pos.debug },
},
],
// This method instantiates all the screens, widgets, etc.
build_widgets: function() {
var self = this;
// -------- Screens ---------
this.product_screen = new module.ProductScreenWidget(this,{});
this.product_screen.appendTo(this.$('.screens'));
this.receipt_screen = new module.ReceiptScreenWidget(this, {});
this.receipt_screen.appendTo(this.$('.screens'));
this.payment_screen = new module.PaymentScreenWidget(this, {});
this.payment_screen.appendTo(this.$('.screens'));
this.clientlist_screen = new module.ClientListScreenWidget(this, {});
this.clientlist_screen.appendTo(this.$('.screens'));
this.scale_screen = new module.ScaleScreenWidget(this,{});
this.scale_screen.appendTo(this.$('.screens'));
// -------- Popups ---------
this.error_popup = new module.ErrorPopupWidget(this, {});
this.error_popup.appendTo(this.$el);
this.error_barcode_popup = new module.ErrorBarcodePopupWidget(this, {});
this.error_barcode_popup.appendTo(this.$el);
this.error_traceback_popup = new module.ErrorTracebackPopupWidget(this,{});
this.error_traceback_popup.appendTo(this.$el);
this.confirm_popup = new module.ConfirmPopupWidget(this,{});
this.confirm_popup.appendTo(this.$el);
this.fullscreen_popup = new module.FullscreenPopup(this,{});
this.fullscreen_popup.appendTo(this.$el);
this.selection_popup = new module.SelectionPopupWidget(this,{});
this.selection_popup.appendTo(this.$el);
this.textinput_popup = new module.TextInputPopupWidget(this,{});
this.textinput_popup.appendTo(this.$el);
this.textarea_popup = new module.TextAreaPopupWidget(this,{});
this.textarea_popup.appendTo(this.$el);
this.number_popup = new module.NumberPopupWidget(this,{});
this.number_popup.appendTo(this.$el);
this.password_popup = new module.PasswordPopupWidget(this,{});
this.password_popup.appendTo(this.$el);
this.unsent_orders_popup = new module.UnsentOrdersPopupWidget(this,{});
this.unsent_orders_popup.appendTo(this.$el);
this.unpaid_orders_popup = new module.UnpaidOrdersPopupWidget(this,{});
this.unpaid_orders_popup.appendTo(this.$el);
// -------- Misc ---------
this.order_selector = new module.OrderSelectorWidget(this,{});
this.order_selector.replace(this.$('.placeholder-OrderSelectorWidget'));
if(this.pos.config.use_proxy){
this.proxy_status = new module.ProxyStatusWidget(this,{});
this.proxy_status.appendTo(this.$('.pos-rightheader'));
for (var name in this.screens) {
var screen = new this.screens[name](this,{});
screen.appendTo(this.$('.screens'));
this.gui.add_screen(name, screen);
}
this.notification = new module.SynchNotificationWidget(this,{});
this.notification.appendTo(this.$('.pos-rightheader'));
for (var name in this.popups) {
var popup = new this.popups[name](this,{});
popup.appendTo(this.$el); // FIXME .popups
this.gui.add_popup(name, popup);
}
this.close_button = new module.HeaderButtonWidget(this,{
label: _t('Close'),
action: function(){
var self = this;
if (!this.confirmed) {
this.$el.addClass('confirm');
this.$el.text(_t('Confirm'));
this.confirmed = setTimeout(function(){
self.$el.removeClass('confirm');
self.$el.text(_t('Close'));
self.confirmed = false;
},2000);
for (var i = 0; i < this.widgets.length; i++) {
var def = this.widgets[i];
if ( !def.condition || def.condition(this) ) {
var args = typeof def.args === 'function' ? def.args(this) : def.args;
var w = new def.widget(this, args || {});
if (def.replace) {
w.replace(this.$(def.replace));
} else if (def.append) {
w.appendTo(this.$(def.append));
} else if (def.prepend) {
w.prependTo(this.$(def.prepend));
} else {
clearTimeout(this.confirmed);
this.pos_widget.close();
w.appendTo(this.$el);
}
},
});
this.close_button.appendTo(this.$('.pos-rightheader'));
this.username = new module.UsernameWidget(this,{});
this.username.replace(this.$('.placeholder-UsernameWidget'));
this.actionpad = new module.ActionpadWidget(this, {});
this.actionpad.replace(this.$('.placeholder-ActionpadWidget'));
this.numpad = new module.NumpadWidget(this);
this.numpad.replace(this.$('.placeholder-NumpadWidget'));
this.order_widget = new module.OrderWidget(this, {});
this.order_widget.replace(this.$('.placeholder-OrderWidget'));
this.onscreen_keyboard = new module.OnscreenKeyboardWidget(this, {
'keyboard_model': 'simple'
});
this.onscreen_keyboard.replace(this.$('.placeholder-OnscreenKeyboardWidget'));
// -------- Screen Selector ---------
this.screen_selector = new module.ScreenSelector({
pos: this.pos,
screen_set:{
'products': this.product_screen,
'payment' : this.payment_screen,
'scale': this.scale_screen,
'receipt' : this.receipt_screen,
'clientlist': this.clientlist_screen,
},
popup_set:{
'error': this.error_popup,
'error-barcode': this.error_barcode_popup,
'error-traceback': this.error_traceback_popup,
'textinput': this.textinput_popup,
'textarea': this.textarea_popup,
'number': this.number_popup,
'password': this.password_popup,
'confirm': this.confirm_popup,
'fullscreen': this.fullscreen_popup,
'selection': this.selection_popup,
'unsent-orders': this.unsent_orders_popup,
'unpaid-orders': this.unpaid_orders_popup,
},
default_screen: 'products',
default_mode: 'cashier',
});
if(this.pos.debug){
this.debug_widget = new module.DebugWidget(this);
this.debug_widget.appendTo(this.$('.pos-content'));
this.widget[def.name] = w;
}
}
this.disable_rubberbanding();
this.gui.set_startup_screen('products');
this.gui.set_default_screen('products');
},
changed_pending_operations: function () {
var self = this;
this.synch_notification.on_change_nbr_pending(self.pos.get('nbr_pending_operations').length);
},
// shows or hide the numpad and related controls like the paypad.
set_numpad_visible: function(visible){
if(visible !== this.numpad_visible){
this.numpad_visible = visible;
if(visible){
this.numpad.show();
this.actionpad.show();
this.widget.numpad.show();
this.widget.actionpad.show();
}else{
this.numpad.hide();
this.actionpad.hide();
this.widget.numpad.hide();
this.widget.actionpad.hide();
}
}
},
@@ -1274,7 +1224,7 @@ openerp.point_of_sale.load_widgets = function load_widgets(instance, module){ //
window.location = '/web#action=' + res[0]['res_id'];
},function(err,event) {
event.preventDefault();
self.screen_selector.show_popup('error',{
self.gui.show_popup('error',{
'message': _t('Could not close the point of sale.'),
'comment': _t('Your internet connection is probably down.'),
});
+1 -1
View File
@@ -3,7 +3,7 @@
-->
<templates id="template" xml:space="preserve">
<t t-name="PosWidget">
<t t-name="Chrome">
<div class="pos">
<div class="pos-topheader">
<div class="pos-branding">