function openerp_pos_widgets(instance, module){ //module is instance.point_of_sale var QWeb = instance.web.qweb, _t = instance.web._t; // The ImageCache is used to hide the latency of the application cache on-disk access in chrome // that causes annoying flickering on product pictures. Why the hell a simple access to // the application cache involves such latency is beyond me, hopefully one day this can be // removed. module.ImageCache = instance.web.Class.extend({ init: function(options){ options = options || {}; this.max_size = options.max_size || 500; this.cache = {}; this.access_time = {}; this.size = 0; }, get_image_uncached: function(url){ var img = new Image(); img.src = url; return img; }, // returns a DOM Image object from an url, and cache the last 500 (by default) results get_image: function(url){ var cached = this.cache[url]; if(cached){ this.access_time[url] = (new Date()).getTime(); return cached; }else{ var img = new Image(); img.src = url; while(this.size >= this.max_size){ var oldestUrl = null; var oldestTime = (new Date()).getTime(); for(var url in this.cache){ var time = this.access_time[url]; if(time <= oldestTime){ oldestTime = time; oldestUrl = url; } } if(oldestUrl){ delete this.cache[oldestUrl]; delete this.access_time[oldestUrl]; } this.size--; } this.cache[url] = img; this.access_time[url] = (new Date()).getTime(); this.size++; return img; } }, }); module.NumpadWidget = module.PosBaseWidget.extend({ template:'NumpadWidget', init: function(parent, options) { this._super(parent); this.state = new module.NumpadState(); }, start: function() { this.state.bind('change:mode', this.changedMode, this); this.changedMode(); this.$el.find('button#numpad-backspace').click(_.bind(this.clickDeleteLastChar, this)); this.$el.find('button#numpad-minus').click(_.bind(this.clickSwitchSign, this)); this.$el.find('button.number-char').click(_.bind(this.clickAppendNewChar, this)); this.$el.find('button.mode-button').click(_.bind(this.clickChangeMode, this)); }, clickDeleteLastChar: function() { return this.state.deleteLastChar(); }, clickSwitchSign: function() { return this.state.switchSign(); }, clickAppendNewChar: function(event) { var newChar; newChar = event.currentTarget.innerText || event.currentTarget.textContent; return this.state.appendNewChar(newChar); }, clickChangeMode: function(event) { var newMode = event.currentTarget.attributes['data-mode'].nodeValue; return this.state.changeMode(newMode); }, changedMode: function() { var mode = this.state.get('mode'); $('.selected-mode').removeClass('selected-mode'); $(_.str.sprintf('.mode-button[data-mode="%s"]', mode), this.$el).addClass('selected-mode'); }, }); // The paypad allows to select the payment method (cashRegisters) // used to pay the order. module.PaypadWidget = module.PosBaseWidget.extend({ template: 'PaypadWidget', renderElement: function() { var self = this; this._super(); this.pos.get('cashRegisters').each(function(cashRegister) { var button = new module.PaypadButtonWidget(self,{ pos: self.pos, pos_widget : self.pos_widget, cashRegister: cashRegister, }); button.appendTo(self.$el); }); } }); module.PaypadButtonWidget = module.PosBaseWidget.extend({ template: 'PaypadButtonWidget', init: function(parent, options){ this._super(parent, options); this.cashRegister = options.cashRegister; }, renderElement: function() { var self = this; this._super(); this.$el.click(function(){ if (self.pos.get('selectedOrder').get('screen') === 'receipt'){ //TODO Why ? console.warn('TODO should not get there...?'); return; } self.pos.get('selectedOrder').addPaymentLine(self.cashRegister); self.pos_widget.screen_selector.set_current_screen('payment'); }); }, }); module.OrderlineWidget = module.PosBaseWidget.extend({ template: 'OrderlineWidget', init: function(parent, options) { this._super(parent,options); this.model = options.model; this.order = options.order; this.model.bind('change', _.bind( function() { this.refresh(); }, this)); }, click_handler: function() { this.order.selectLine(this.model); this.trigger('order_line_selected'); }, renderElement: function() { this._super(); this.$el.click(_.bind(this.click_handler, this)); if(this.model.is_selected()){ this.$el.addClass('selected'); } }, refresh: function(){ this.renderElement(); this.trigger('order_line_refreshed'); }, }); module.OrderWidget = module.PosBaseWidget.extend({ template:'OrderWidget', init: function(parent, options) { this._super(parent,options); this.display_mode = options.display_mode || 'numpad'; // 'maximized' | 'actionbar' | 'numpad' this.set_numpad_state(options.numpadState); this.pos.bind('change:selectedOrder', this.change_selected_order, this); this.bind_orderline_events(); this.orderlinewidgets = []; }, set_numpad_state: function(numpadState) { if (this.numpadState) { this.numpadState.unbind('set_value', this.set_value); } this.numpadState = numpadState; if (this.numpadState) { this.numpadState.bind('set_value', this.set_value, this); this.numpadState.reset(); } }, set_value: function(val) { var order = this.pos.get('selectedOrder'); if (order.get('orderLines').length !== 0) { var mode = this.numpadState.get('mode'); if( mode === 'quantity'){ order.getSelectedLine().set_quantity(val); }else if( mode === 'discount'){ order.getSelectedLine().set_discount(val); }else if( mode === 'price'){ order.getSelectedLine().set_unit_price(val); } } else { this.pos.get('selectedOrder').destroy(); } }, change_selected_order: function() { this.currentOrderLines.unbind(); this.bind_orderline_events(); this.renderElement(); }, bind_orderline_events: function() { this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines'); this.currentOrderLines.bind('add', this.renderElement, this); this.currentOrderLines.bind('remove', this.renderElement, this); }, update_numpad: function() { this.selected_line = this.pos.get('selectedOrder').getSelectedLine(); if (this.numpadState) this.numpadState.reset(); }, renderElement: function() { var self = this; this._super(); // freeing subwidgets if(this.scrollbar){ this.scrollbar.destroy(); } for(var i = 0, len = this.orderlinewidgets.length; i < len; i++){ this.orderlinewidgets[i].destroy(); } this.orderlinewidgets = []; if(this.display_mode === 'maximized'){ $('.point-of-sale .order-container').css({'bottom':'0px'}); }else if(this.display_mode === 'actionbar'){ $('.point-of-sale .order-container').css({'bottom':'105px'}); }else if(this.display_mode !== 'numpad'){ console.error('ERROR: OrderWidget renderElement(): wrong display_mode:',this.display_mode); } var $content = this.$('.orderlines'); this.currentOrderLines.each(_.bind( function(orderLine) { var line = new module.OrderlineWidget(this, { model: orderLine, order: this.pos.get('selectedOrder'), }); line.on('order_line_selected', self, self.update_numpad); line.on('order_line_refreshed', self, self.update_summary); line.appendTo($content); self.orderlinewidgets.push(line); }, this)); this.update_numpad(); this.update_summary(); var position = this.scrollbar ? this.scrollbar.get_position() : 0; var at_bottom = this.scrollbar ? this.scrollbar.is_at_bottom() : false; this.scrollbar = new module.ScrollbarWidget(this,{ target_widget: this, target_selector: '.order-scroller', name: 'order', track_bottom: true, on_show: function(){ self.$('.order-scroller').css({'width':'89%'},100); }, on_hide: function(){ self.$('.order-scroller').css({'width':'100%'},100); }, }); this.scrollbar.replace(this.$('.placeholder-ScrollbarWidget')); this.scrollbar.set_position(position); if( at_bottom ){ this.scrollbar.set_position(Number.MAX_VALUE, false); } }, update_summary: function(){ var order = this.pos.get('selectedOrder'); var total = order ? order.getTotalTaxIncluded() : 0; var taxes = order ? total - order.getTotalTaxExcluded() : 0; this.$('.summary .total > .value').html(this.format_currency(total)); this.$('.summary .total .subentry .value').html(this.format_currency(taxes)); }, set_display_mode: function(mode){ if(this.display_mode !== mode){ this.display_mode = mode; this.renderElement(); } }, }); module.ProductWidget = module.PosBaseWidget.extend({ template: 'ProductWidget', init: function(parent, options) { this._super(parent,options); this.model = options.model; this.model.attributes.weight = options.weight; this.next_screen = options.next_screen; //when a product is clicked, this screen is set this.click_product_action = options.click_product_action; }, // returns the url of the product thumbnail renderElement: function() { this._super(); this.$('img').replaceWith(this.pos_widget.image_cache.get_image(this.model.get_image_url())); var self = this; $("a", this.$el).click(function(e){ if(self.click_product_action){ self.click_product_action(self.model); } }); }, }); module.PaymentlineWidget = module.PosBaseWidget.extend({ template: 'PaymentlineWidget', init: function(parent, options) { this._super(parent,options); this.payment_line = options.payment_line; this.payment_line.bind('change', this.changedAmount, this); }, changeAmount: function(event) { var newAmount = event.currentTarget.value; var amount = parseFloat(newAmount); if(!isNaN(amount)){ this.amount = amount; this.payment_line.set_amount(amount); } }, changedAmount: function() { if (this.amount !== this.payment_line.get_amount()){ this.renderElement(); } }, renderElement: function() { var self = this; this.name = this.payment_line.get_cashregister().get('journal_id')[1]; this._super(); this.$('input').keyup(function(event){ self.changeAmount(event); }); this.$('.delete-payment-line').click(function() { self.trigger('delete_payment_line', self); }); }, focus: function(){ var val = this.$('input')[0].value; this.$('input')[0].focus(); this.$('input')[0].value = val; this.$('input')[0].select(); }, }); module.OrderButtonWidget = module.PosBaseWidget.extend({ template:'OrderButtonWidget', init: function(parent, options) { this._super(parent,options); var self = this; this.order = options.order; this.order.bind('destroy',function(){ self.destroy(); }); this.order.bind('change', function(){ self.renderElement(); }); this.pos.bind('change:selectedOrder', _.bind( function(pos) { var selectedOrder; selectedOrder = pos.get('selectedOrder'); if (this.order === selectedOrder) { this.setButtonSelected(); } }, this)); }, renderElement:function(){ this._super(); this.$('button.select-order').off('click').click(_.bind(this.selectOrder, this)); this.$('button.close-order').off('click').click(_.bind(this.closeOrder, this)); }, selectOrder: function(event) { this.pos.set({ selectedOrder: this.order }); }, setButtonSelected: function() { $('.selected-order').removeClass('selected-order'); this.$el.addClass('selected-order'); }, closeOrder: function(event) { this.order.destroy(); }, }); module.ActionButtonWidget = instance.web.Widget.extend({ template:'ActionButtonWidget', icon_template:'ActionButtonWidgetWithIcon', init: function(parent, options){ this._super(parent, options); this.label = options.label || 'button'; this.rightalign = options.rightalign || false; this.click_action = options.click; this.disabled = options.disabled || false; if(options.icon){ this.icon = options.icon; this.template = this.icon_template; } }, set_disabled: function(disabled){ if(this.disabled != disabled){ this.disabled = !!disabled; this.renderElement(); } }, renderElement: function(){ this._super(); if(this.click_action && !this.disabled){ this.$el.click(_.bind(this.click_action, this)); } }, }); module.ActionBarWidget = instance.web.Widget.extend({ template:'ActionBarWidget', init: function(parent, options){ this._super(parent,options); this.button_list = []; this.buttons = {}; this.visibility = {}; }, set_element_visible: function(element, visible, action){ if(visible != this.visibility[element]){ this.visibility[element] = !!visible; if(visible){ this.$('.'+element).show(); }else{ this.$('.'+element).hide(); } } if(visible && action){ this.action[element] = action; this.$('.'+element).off('click').click(action); } }, set_button_disabled: function(name, disabled){ var b = this.buttons[name]; if(b){ b.set_disabled(disabled); } }, destroy_buttons:function(){ for(var i = 0; i < this.button_list.length; i++){ this.button_list[i].destroy(); } this.button_list = []; this.buttons = {}; return this; }, get_button_count: function(){ return this.button_list.length; }, add_new_button: function(button_options){ var button = new module.ActionButtonWidget(this,button_options); this.button_list.push(button); if(button_options.name){ this.buttons[button_options.name] = button; } button.appendTo(this.$('.pos-actionbar-button-list')); return button; }, show:function(){ this.$el.show(); }, hide:function(){ this.$el.hide(); }, }); module.CategoryButton = module.PosBaseWidget.extend({ }); module.ProductCategoriesWidget = module.PosBaseWidget.extend({ template: 'ProductCategoriesWidget', init: function(parent, options){ var self = this; this._super(parent,options); this.product_type = options.product_type || 'all'; // 'all' | 'weightable' this.onlyWeightable = options.onlyWeightable || false; this.category = this.pos.root_category; this.breadcrumb = []; this.subcategories = []; this.set_category(); }, // changes the category. if undefined, sets to root category set_category : function(category){ var db = this.pos.db; if(!category){ this.category = db.get_category_by_id(db.root_category_id); }else{ this.category = category; } this.breadcrumb = []; var ancestors_ids = db.get_category_ancestors_ids(this.category.id); for(var i = 1; i < ancestors_ids.length; i++){ this.breadcrumb.push(db.get_category_by_id(ancestors_ids[i])); } if(this.category.id !== db.root_category_id){ this.breadcrumb.push(this.category); } this.subcategories = db.get_category_by_id(db.get_category_childs_ids(this.category.id)); }, get_image_url: function(category){ return instance.session.url('/web/binary/image', {model: 'pos.category', field: 'image', id: category.id}); }, renderElement: function(){ var self = this; this._super(); var hasimages = false; //if none of the subcategories have images, we don't display buttons with icons _.each(this.subcategories, function(category){ if(category.image){ hasimages = true; } }); _.each(this.subcategories, function(category){ if(hasimages){ var button = QWeb.render('CategoryButton',{category:category}); var button = _.str.trim(button); var button = $(button); button.find('img').replaceWith(self.pos_widget.image_cache.get_image(self.get_image_url(category))); }else{ var button = QWeb.render('CategorySimpleButton',{category:category}); button = _.str.trim(button); // we remove whitespace between buttons to fix spacing var button = $(button); } button.appendTo(this.$('.category-list')).click(function(event){ var id = category.id; var cat = self.pos.db.get_category_by_id(id); self.set_category(cat); self.renderElement(); }); }); // breadcrumb click actions this.$(".oe-pos-categories-list a").click(function(event){ var id = $(event.target).data("category-id"); var category = self.pos.db.get_category_by_id(id); self.set_category(category); self.renderElement(); }); this.search_and_categories(); if(this.pos.iface_vkeyboard && this.pos_widget.onscreen_keyboard){ this.pos_widget.onscreen_keyboard.connect(this.$('.searchbox input')); } }, set_product_type: function(type){ // 'all' | 'weightable' this.product_type = type; this.reset_category(); }, // resets the current category to the root category reset_category: function(){ this.set_category(); this.renderElement(); }, // empties the content of the search box clear_search: function(){ var products = this.pos.db.get_product_by_category(this.category.id); this.pos.get('products').reset(products); this.$('.searchbox input').val('').focus(); this.$('.search-clear').fadeOut(); }, // filters the products, and sets up the search callbacks search_and_categories: function(category){ var self = this; // find all products belonging to the current category var products = this.pos.db.get_product_by_category(this.category.id); self.pos.get('products').reset(products); // filter the products according to the search string this.$('.searchbox input').keyup(function(event){ console.log('event',event); query = $(this).val().toLowerCase(); if(query){ if(event.which === 13){ if( self.pos.get('products').size() === 1 ){ self.pos.get('selectedOrder').addProduct(self.pos.get('products').at(0)); self.clear_search(); } }else{ var products = self.pos.db.search_product_in_category(self.category.id, query); self.pos.get('products').reset(products); self.$('.search-clear').fadeIn(); } }else{ var products = self.pos.db.get_product_by_category(self.category.id); self.pos.get('products').reset(products); self.$('.search-clear').fadeOut(); } }); //reset the search when clicking on reset this.$('.search-clear').click(function(){ self.clear_search(); }); }, }); module.ProductListWidget = module.ScreenWidget.extend({ template:'ProductListWidget', init: function(parent, options) { var self = this; this._super(parent,options); this.model = options.model; this.productwidgets = []; this.weight = options.weight || 0; this.show_scale = options.show_scale || false; this.next_screen = options.next_screen || false; this.click_product_action = options.click_product_action; this.pos.get('products').bind('reset', function(){ self.renderElement(); }); }, renderElement: function() { var self = this; this._super(); // free subwidgets memory from previous renders for(var i = 0, len = this.productwidgets.length; i < len; i++){ this.productwidgets[i].destroy(); } this.productwidgets = []; if(this.scrollbar){ this.scrollbar.destroy(); } var products = this.pos.get('products').models || []; for(var i = 0, len = products.length; i < len; i++){ var product = new module.ProductWidget(self, { model: products[i], click_product_action: this.click_product_action, }); this.productwidgets.push(product); product.appendTo(this.$('.product-list')); } this.scrollbar = new module.ScrollbarWidget(this,{ target_widget: this, target_selector: '.product-list-scroller', on_show: function(){ self.$('.product-list-scroller').css({'padding-right':'62px'},100); }, on_hide: function(){ self.$('.product-list-scroller').css({'padding-right':'0px'},100); }, }); this.scrollbar.replace(this.$('.placeholder-ScrollbarWidget')); }, }); module.UsernameWidget = module.PosBaseWidget.extend({ template: 'UsernameWidget', init: function(parent, options){ var options = options || {}; this._super(parent,options); this.mode = options.mode || 'cashier'; }, set_user_mode: function(mode){ this.mode = mode; this.refresh(); }, refresh: function(){ this.renderElement(); }, get_name: function(){ var user; if(this.mode === 'cashier'){ user = this.pos.get('cashier') || this.pos.get('user'); }else{ user = this.pos.get('selectedOrder').get_client() || this.pos.get('user'); } if(user){ return user.name; }else{ return ""; } }, }); module.HeaderButtonWidget = module.PosBaseWidget.extend({ template: 'HeaderButtonWidget', init: function(parent, options){ options = options || {}; this._super(parent, options); this.action = options.action; this.label = options.label; }, renderElement: function(){ var self = this; this._super(); if(this.action){ this.$el.click(function(){ self.action(); }); } }, show: function(){ this.$el.show(); }, hide: function(){ this.$el.hide(); }, }); // The debug widget lets the user control and monitor the hardware and software status // without the use of the proxy module.DebugWidget = module.PosBaseWidget.extend({ template: "DebugWidget", eans:{ admin_badge: '0410100000006', client_badge: '0420200000004', invalid_ean: '1232456', soda_33cl: '5449000000996', oranges_kg: '2100002031410', lemon_price: '2301000001560', unknown_product: '9900000000004', }, events:[ 'scan_item_success', 'scan_item_error_unrecognized', 'payment_request', 'open_cashbox', 'print_receipt', 'print_pdf_invoice', 'weighting_read_kg', 'payment_status', ], minimized: false, start: function(){ var self = this; this.$el.draggable(); this.$('.toggle').click(function(){ var content = self.$('.content'); var bg = self.$el; if(!self.minimized){ content.animate({'height':'0'},200); }else{ content.css({'height':'auto'}); } self.minimized = !self.minimized; }); this.$('.button.accept_payment').click(function(){ self.pos.proxy.debug_accept_payment(); }); this.$('.button.reject_payment').click(function(){ self.pos.proxy.debug_reject_payment(); }); this.$('.button.set_weight').click(function(){ var kg = Number(self.$('input.weight').val()); if(!isNaN(kg)){ self.pos.proxy.debug_set_weight(kg); } }); this.$('.button.reset_weight').click(function(){ self.$('input.weight').val(''); self.pos.proxy.debug_reset_weight(); }); this.$('.button.custom_ean').click(function(){ var ean = self.pos.barcode_reader.sanitize_ean(self.$('input.ean').val() || '0'); self.$('input.ean').val(ean); self.pos.barcode_reader.on_ean(ean); }); _.each(this.eans, function(ean, name){ self.$('.button.'+name).click(function(){ self.$('input.ean').val(ean); self.pos.barcode_reader.on_ean(ean); }); }); _.each(this.events, function(name){ self.pos.proxy.add_notification(name,function(){ self.$('.event.'+name).stop().clearQueue().css({'background-color':'#6CD11D'}); self.$('.event.'+name).animate({'background-color':'#1E1E1E'},2000); }); }); self.pos.proxy.add_notification('help_needed',function(){ self.$('.status.help_needed').addClass('on'); }); self.pos.proxy.add_notification('help_canceled',function(){ self.$('.status.help_needed').removeClass('on'); }); self.pos.proxy.add_notification('transaction_start',function(){ self.$('.status.transaction').addClass('on'); }); self.pos.proxy.add_notification('transaction_end',function(){ self.$('.status.transaction').removeClass('on'); }); self.pos.proxy.add_notification('weighting_start',function(){ self.$('.status.weighting').addClass('on'); }); self.pos.proxy.add_notification('weighting_end',function(){ self.$('.status.weighting').removeClass('on'); }); }, }); // ---------- Main Point of Sale Widget ---------- // this is used to notify the user that data is being synchronized on the network module.SynchNotificationWidget = module.PosBaseWidget.extend({ template: "SynchNotificationWidget", init: function(parent, options){ options = options || {}; this._super(parent, options); }, renderElement: function() { var self = this; this._super(); this.$el.click(function(){ self.pos.flush(); }); }, start: function(){ var self = this; this.pos.bind('change:nbr_pending_operations', function(){ self.renderElement(); }); }, get_nbr_pending: function(){ return this.pos.get('nbr_pending_operations'); }, }); // The PosWidget 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) // - an actionbar on the bottom, containing various action buttons // - popups // - an onscreen keyboard // a screen_selector which controls the switching between screens and the showing/closing of popups module.PosWidget = module.PosBaseWidget.extend({ template: 'PosWidget', init: function() { this._super(arguments[0],{}); instance.web.blockUI(); this.pos = new module.PosModel(this.session); this.pos_widget = this; //So that pos_widget's childs have pos_widget set automatically this.numpad_visible = true; this.left_action_bar_visible = true; this.leftpane_visible = true; this.leftpane_width = '440px'; this.cashier_controls_visible = true; this.image_cache = new module.ImageCache(); // for faster products image display }, start: function() { var self = this; return self.pos.ready.done(function() { self.build_currency_template(); self.renderElement(); self.$('.neworder-button').click(function(){ self.pos.add_new_order(); }); //when a new order is created, add an order button widget self.pos.get('orders').bind('add', function(new_order){ var new_order_button = new module.OrderButtonWidget(null, { order: new_order, pos: self.pos }); new_order_button.appendTo($('#orders')); new_order_button.selectOrder(); }, self); self.pos.get('orders').add(new module.Order({ pos: self.pos })); self.build_widgets(); self.screen_selector.set_default_screen(); window.screen_selector = self.screen_selector; self.pos.barcode_reader.connect(); instance.webclient.set_content_full_screen(true); if (!self.pos.get('pos_session')) { self.screen_selector.show_popup('error', 'Sorry, we could not create a user session'); }else if(!self.pos.get('pos_config')){ self.screen_selector.show_popup('error', 'Sorry, we could not find any PoS Configuration for this session'); } instance.web.unblockUI(); self.$('.loader').animate({opacity:0},1500,'swing',function(){self.$('.loader').hide();}); self.pos.flush(); }).fail(function(){ // error when loading models data from the backend instance.web.unblockUI(); return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_session_opening']], ['res_id']) .pipe( _.bind(function(res){ return instance.session.rpc('/web/action/load', {'action_id': res[0]['res_id']}) .pipe(_.bind(function(result){ var action = result.result; this.do_action(action); }, this)); }, self)); }); }, // This method instantiates all the screens, widgets, etc. If you want to add new screens change the // startup screen, etc, override this method. build_widgets: function() { var self = this; // -------- Screens --------- this.product_screen = new module.ProductScreenWidget(this,{}); this.product_screen.appendTo($('#rightpane')); this.receipt_screen = new module.ReceiptScreenWidget(this, {}); this.receipt_screen.appendTo($('#rightpane')); this.payment_screen = new module.PaymentScreenWidget(this, {}); this.payment_screen.appendTo($('#rightpane')); this.welcome_screen = new module.WelcomeScreenWidget(this,{}); this.welcome_screen.appendTo($('#rightpane')); this.client_payment_screen = new module.ClientPaymentScreenWidget(this, {}); this.client_payment_screen.appendTo($('#rightpane')); this.scale_invite_screen = new module.ScaleInviteScreenWidget(this, {}); this.scale_invite_screen.appendTo($('#rightpane')); this.scale_screen = new module.ScaleScreenWidget(this,{}); this.scale_screen.appendTo($('#rightpane')); // -------- Popups --------- this.help_popup = new module.HelpPopupWidget(this, {}); this.help_popup.appendTo($('.point-of-sale')); this.error_popup = new module.ErrorPopupWidget(this, {}); this.error_popup.appendTo($('.point-of-sale')); this.error_product_popup = new module.ProductErrorPopupWidget(this, {}); this.error_product_popup.appendTo($('.point-of-sale')); this.error_session_popup = new module.ErrorSessionPopupWidget(this, {}); this.error_session_popup.appendTo($('.point-of-sale')); this.choose_receipt_popup = new module.ChooseReceiptPopupWidget(this, {}); this.choose_receipt_popup.appendTo($('.point-of-sale')); this.error_negative_price_popup = new module.ErrorNegativePricePopupWidget(this, {}); this.error_negative_price_popup.appendTo($('.point-of-sale')); // -------- Misc --------- this.notification = new module.SynchNotificationWidget(this,{}); this.notification.appendTo(this.$('#rightheader')); this.username = new module.UsernameWidget(this,{}); this.username.replace(this.$('.placeholder-UsernameWidget')); this.action_bar = new module.ActionBarWidget(this); this.action_bar.appendTo($(".point-of-sale #rightpane")); this.left_action_bar = new module.ActionBarWidget(this); this.left_action_bar.appendTo($(".point-of-sale #leftpane")); this.paypad = new module.PaypadWidget(this, {}); this.paypad.replace($('#placeholder-PaypadWidget')); this.numpad = new module.NumpadWidget(this); this.numpad.replace($('#placeholder-NumpadWidget')); this.order_widget = new module.OrderWidget(this, {}); this.order_widget.replace($('#placeholder-OrderWidget')); this.onscreen_keyboard = new module.OnscreenKeyboardWidget(this, { 'keyboard_model': 'simple' }); this.onscreen_keyboard.appendTo($(".point-of-sale #content")); this.close_button = new module.HeaderButtonWidget(this,{ label: _t('Close'), action: function(){ self.try_close(); }, }); this.close_button.appendTo(this.$('#rightheader')); this.client_button = new module.HeaderButtonWidget(this,{ label: _t('Self-Checkout'), action: function(){ self.screen_selector.set_user_mode('client'); }, }); this.client_button.appendTo(this.$('#rightheader')); // -------- Screen Selector --------- this.screen_selector = new module.ScreenSelector({ pos: this.pos, screen_set:{ 'products': this.product_screen, 'payment' : this.payment_screen, 'client_payment' : this.client_payment_screen, 'scale_invite' : this.scale_invite_screen, 'scale': this.scale_screen, 'receipt' : this.receipt_screen, 'welcome' : this.welcome_screen, }, popup_set:{ 'help': this.help_popup, 'error': this.error_popup, 'error-product': this.error_product_popup, 'error-session': this.error_session_popup, 'error-negative-price': this.error_negative_price_popup, 'choose-receipt': this.choose_receipt_popup, }, default_client_screen: 'welcome', default_cashier_screen: 'products', default_mode: this.pos.iface_self_checkout ? 'client' : 'cashier', }); if(this.pos.debug){ this.debug_widget = new module.DebugWidget(this); this.debug_widget.appendTo(this.$('#content')); } }, 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.set_left_action_bar_visible(false); this.numpad.show(); this.paypad.show(); this.order_widget.set_display_mode('numpad'); }else{ this.numpad.hide(); this.paypad.hide(); if(this.order_widget.display_mode === 'numpad'){ this.order_widget.set_display_mode('maximized'); } } } }, set_left_action_bar_visible: function(visible){ if(visible !== this.left_action_bar_visible){ this.left_action_bar_visible = visible; if(visible){ this.set_numpad_visible(false); this.left_action_bar.show(); this.order_widget.set_display_mode('actionbar'); }else{ this.left_action_bar.hide(); if(this.order_widget.display_mode === 'actionbar'){ this.order_widget.set_display_mode('maximized'); } } } }, //shows or hide the leftpane (contains the list of orderlines, the numpad, the paypad, etc.) set_leftpane_visible: function(visible){ if(visible !== this.leftpane_visible){ this.leftpane_visible = visible; if(visible){ $('#leftpane').show().animate({'width':this.leftpane_width},500,'swing'); $('#rightpane').animate({'left':this.leftpane_width},500,'swing'); }else{ var leftpane = $('#leftpane'); $('#leftpane').animate({'width':'0px'},500,'swing', function(){ leftpane.hide(); }); $('#rightpane').animate({'left':'0px'},500,'swing'); } } }, //shows or hide the controls in the PosWidget that are specific to the cashier ( Orders, close button, etc. ) set_cashier_controls_visible: function(visible){ if(visible !== this.cashier_controls_visible){ this.cashier_controls_visible = visible; if(visible){ $('#loggedas').show(); $('#rightheader').show(); }else{ $('#loggedas').hide(); $('#rightheader').hide(); } } }, try_close: function() { var self = this; //TODO : do the close after the flush... self.pos.flush() self.close(); }, close: function() { var self = this; this.pos.barcode_reader.disconnect(); return new instance.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_client_pos_menu']], ['res_id']).pipe( _.bind(function(res) { return this.rpc('/web/action/load', {'action_id': res[0]['res_id']}).pipe(_.bind(function(result) { var action = result; action.context = _.extend(action.context || {}, {'cancel_action': {type: 'ir.actions.client', tag: 'reload'}}); //self.destroy(); this.do_action(action); }, this)); }, this)); }, destroy: function() { instance.webclient.set_content_full_screen(false); self.pos = undefined; this._super(); } }); }