[WIP] point_of_sale: improved performance of the product screen & order widget + ongoing cleanup
bzr revid: fva@openerp.com-20131202162144-6se3s0wjwz0jdfih
This commit is contained in:
@@ -56,7 +56,6 @@ class PosController(http.Controller):
|
||||
@http.route('/pos/web', type='http', auth='none')
|
||||
def a(self, debug=False, **k):
|
||||
|
||||
print '\nDEBUG',debug,'\n'
|
||||
|
||||
js_list = manifest_list('js',db=request.db, debug=debug)
|
||||
css_list = manifest_list('css',db=request.db, debug=debug)
|
||||
@@ -64,8 +63,6 @@ class PosController(http.Controller):
|
||||
print css_list
|
||||
print js_list
|
||||
|
||||
js_list = [ js for js in js_list if 'select2' not in js ]
|
||||
|
||||
js = "\n".join('<script type="text/javascript" src="%s"></script>' % i for i in js_list)
|
||||
css = "\n".join('<link rel="stylesheet" href="%s">' % i for i in css_list)
|
||||
r = html_template % {
|
||||
@@ -73,15 +70,18 @@ class PosController(http.Controller):
|
||||
'css': css,
|
||||
'modules': simplejson.dumps(module_boot(request.db)),
|
||||
'init': """
|
||||
window.navigator.standalone = true;
|
||||
console.log('LOADED');
|
||||
var wc = new s.web.WebClient();
|
||||
wc.appendTo($(document.body));
|
||||
wc.show_application = function(){
|
||||
console.log('SHOW APPLICATION');
|
||||
wc.action_manager.do_action("pos.ui");
|
||||
};
|
||||
wc.show_login = function(){
|
||||
console.log('SHOW LOGIN');
|
||||
window.location.href = '/';
|
||||
}
|
||||
wc.appendTo($(document.body));
|
||||
console.log('APPENDED');
|
||||
"""
|
||||
}
|
||||
return r
|
||||
|
||||
@@ -1163,7 +1163,8 @@
|
||||
background: rgba(0,0,0,0.82);
|
||||
color: white;
|
||||
padding-bottom: 10px;
|
||||
cursor:move;
|
||||
cursor: move;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
}
|
||||
.pos .debug-widget .toggle{
|
||||
position: absolute;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,63 +0,0 @@
|
||||
v Affichage Catégories doivent respecter le poids
|
||||
v Affichage Catégories par poids doivent avoir une hiérarchie plate
|
||||
v Le Onscreen keyboard ne fonctionne plus
|
||||
v Scrolling dans la sélection produit et dans
|
||||
la liste de courses
|
||||
v Réductions
|
||||
v Redesign liste de courses
|
||||
v bugs scans produits par prix par poids
|
||||
v si pas photo photo par defaut
|
||||
v WebSQL
|
||||
v Redesign header, bouton retour aux backend en mode caissière
|
||||
v bouton exit en mode caissière doit retourner au backend.
|
||||
v posting orders
|
||||
v bug ajout ligne payment à zero + curseur dedans + suppression + design plus grand
|
||||
v si case print via proxy cochée, alors on skip l'ecran receipt
|
||||
v bouton exit self checkout
|
||||
v demarrage en mode self-checkout si self-checkout
|
||||
v produits poid code barre non reconnus
|
||||
v activer popup client aux écrans self-checkout
|
||||
v discount pas plus grand que 100
|
||||
v numpad dans l'écran payment pour pouvoir entrer le montant
|
||||
v différence de total. Methode d'arrondis
|
||||
|
||||
- login alternatif
|
||||
- user preferences : désactiver la balance etc.
|
||||
- numpad state parfois sans state
|
||||
- numpad state en mode pesée ?? le cacher ?
|
||||
- Redesign du receipt
|
||||
- retirer le receipt screen
|
||||
- le numpad en mode paymenet marche pas
|
||||
- l'écran de payement est hyper laid
|
||||
- l'écran de pesée est hyper laid
|
||||
x générer les données de printing
|
||||
x popups d'erreur certainement buggés
|
||||
|
||||
TODO AUG 20
|
||||
-----------
|
||||
|
||||
* GREEN STATUS
|
||||
- make it green !
|
||||
|
||||
* TRUNK
|
||||
- Connection status tooltip
|
||||
v Remove receipt screen
|
||||
- Finish the receipt JSON generation
|
||||
- Modifie le widget de liste course
|
||||
v supprimer l'écran
|
||||
v bloquer sur l'impression
|
||||
v L'impression est foireuse
|
||||
- description_sales
|
||||
|
||||
* CLIENT
|
||||
v create a new branch
|
||||
- Self-checkout welcome screen
|
||||
~ removal of products for the root category
|
||||
- Terminal de payement
|
||||
- Code à barres
|
||||
- Vidanges
|
||||
- Design
|
||||
- Backport
|
||||
- CSS pour écran résistif
|
||||
- Test sur matos
|
||||
|
||||
@@ -43,7 +43,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
|
||||
'orders': new module.OrderCollection(),
|
||||
//this is the product list as seen by the product list widgets, it will change based on the category filters
|
||||
'products': new module.ProductCollection(),
|
||||
'cashRegisters': null,
|
||||
|
||||
'bank_statements': null,
|
||||
@@ -444,11 +443,11 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
}
|
||||
|
||||
if(parsed_code.type === 'price'){
|
||||
selectedOrder.addProduct(new module.Product(product), {price:parsed_code.value});
|
||||
selectedOrder.addProduct(product, {price:parsed_code.value});
|
||||
}else if(parsed_code.type === 'weight'){
|
||||
selectedOrder.addProduct(new module.Product(product), {quantity:parsed_code.value, merge:false});
|
||||
selectedOrder.addProduct(product, {quantity:parsed_code.value, merge:false});
|
||||
}else{
|
||||
selectedOrder.addProduct(new module.Product(product));
|
||||
selectedOrder.addProduct(product);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@@ -461,16 +460,6 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
model: module.CashRegister,
|
||||
});
|
||||
|
||||
module.Product = Backbone.Model.extend({
|
||||
get_image_url: function(){
|
||||
return instance.session.url('/web/binary/image', {model: 'product.product', field: 'image_medium', id: this.get('id')});
|
||||
},
|
||||
});
|
||||
|
||||
module.ProductCollection = Backbone.Collection.extend({
|
||||
model: module.Product,
|
||||
});
|
||||
|
||||
// An orderline represent one element of the content of a client's shopping cart.
|
||||
// An orderline contains a product, its quantity, its price, discount. etc.
|
||||
// An Order contains zero or more Orderlines.
|
||||
@@ -479,7 +468,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
this.pos = options.pos;
|
||||
this.order = options.order;
|
||||
this.product = options.product;
|
||||
this.price = options.product.get('price');
|
||||
this.price = options.product.price;
|
||||
this.quantity = 1;
|
||||
this.quantityStr = '1';
|
||||
this.discount = 0;
|
||||
@@ -492,7 +481,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
var disc = Math.min(Math.max(parseFloat(discount) || 0, 0),100);
|
||||
this.discount = disc;
|
||||
this.discountStr = '' + disc;
|
||||
this.trigger('change');
|
||||
this.trigger('change',this);
|
||||
},
|
||||
// returns the discount [0,100]%
|
||||
get_discount: function(){
|
||||
@@ -522,7 +511,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
this.quantityStr = '' + this.quantity;
|
||||
}
|
||||
}
|
||||
this.trigger('change');
|
||||
this.trigger('change',this);
|
||||
},
|
||||
// return the quantity of product
|
||||
get_quantity: function(){
|
||||
@@ -541,7 +530,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
},
|
||||
// return the unit of measure of the product
|
||||
get_unit: function(){
|
||||
var unit_id = (this.product.get('uos_id') || this.product.get('uom_id'));
|
||||
var unit_id = (this.product.uos_id || this.product.uom_id);
|
||||
if(!unit_id){
|
||||
return undefined;
|
||||
}
|
||||
@@ -558,7 +547,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
// selects or deselects this orderline
|
||||
set_selected: function(selected){
|
||||
this.selected = selected;
|
||||
this.trigger('change');
|
||||
this.trigger('change',this);
|
||||
},
|
||||
// returns true if this orderline is selected
|
||||
is_selected: function(){
|
||||
@@ -567,7 +556,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
// when we add an new orderline we want to merge it with the last line to see reduce the number of items
|
||||
// in the orderline. This returns true if it makes sense to merge the two
|
||||
can_be_merged_with: function(orderline){
|
||||
if( this.get_product().get('id') !== orderline.get_product().get('id')){ //only orderline of the same product can be merged
|
||||
if( this.get_product().id !== orderline.get_product().id){ //only orderline of the same product can be merged
|
||||
return false;
|
||||
}else if(this.get_product_type() !== orderline.get_product_type()){
|
||||
return false;
|
||||
@@ -587,7 +576,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
qty: this.get_quantity(),
|
||||
price_unit: this.get_unit_price(),
|
||||
discount: this.get_discount(),
|
||||
product_id: this.get_product().get('id'),
|
||||
product_id: this.get_product().id,
|
||||
};
|
||||
},
|
||||
//used to create a json of the ticket, to be sent to the printer
|
||||
@@ -597,19 +586,19 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
unit_name: this.get_unit().name,
|
||||
price: this.get_unit_price(),
|
||||
discount: this.get_discount(),
|
||||
product_name: this.get_product().get('name'),
|
||||
product_name: this.get_product().name,
|
||||
price_display : this.get_display_price(),
|
||||
price_with_tax : this.get_price_with_tax(),
|
||||
price_without_tax: this.get_price_without_tax(),
|
||||
tax: this.get_tax(),
|
||||
product_description: this.get_product().get('description'),
|
||||
product_description_sale: this.get_product().get('description_sale'),
|
||||
product_description: this.get_product().description,
|
||||
product_description_sale: this.get_product().description_sale,
|
||||
};
|
||||
},
|
||||
// changes the base price of the product for this orderline
|
||||
set_unit_price: function(price){
|
||||
this.price = round_di(parseFloat(price) || 0, 2);
|
||||
this.trigger('change');
|
||||
this.trigger('change',this);
|
||||
},
|
||||
get_unit_price: function(){
|
||||
var rounding = this.pos.get('currency').rounding;
|
||||
@@ -637,8 +626,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
|
||||
var product_list = this.pos.get('product_list');
|
||||
var product = this.get_product();
|
||||
var taxes_ids = product.get('taxes_id');;
|
||||
var taxes = self.pos.get('taxes');
|
||||
var taxes_ids = product.taxes_id;
|
||||
var taxes = self.pos.taxes;
|
||||
var taxtotal = 0;
|
||||
_.each(taxes_ids, function(el) {
|
||||
var tax = _.detect(taxes, function(t) {return t.id === el;});
|
||||
@@ -749,7 +738,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
},
|
||||
addProduct: function(product, options){
|
||||
options = options || {};
|
||||
var attr = product.toJSON();
|
||||
var attr = JSON.parse(JSON.stringify(product));
|
||||
attr.pos = this.pos;
|
||||
attr.order = this;
|
||||
var line = new module.Orderline({}, {pos: this.pos, order: this, product: product});
|
||||
@@ -951,6 +940,12 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
|
||||
this.selected_orderline = undefined;
|
||||
}
|
||||
},
|
||||
deselectLine: function(){
|
||||
if(this.selected_orderline){
|
||||
this.selected_orderline.set_selected(false);
|
||||
this.selected_orderline = undefined;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.OrderCollection = Backbone.Collection.extend({
|
||||
|
||||
@@ -634,7 +634,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
}
|
||||
|
||||
var cashregister = selfCheckoutRegisters[0] || self.pos.get('cashRegisters').models[0];
|
||||
currentOrder.addPaymentLine(cashregister);
|
||||
currentOrder.add_payment_line(cashregister);
|
||||
self.pos.push_order(currentOrder)
|
||||
currentOrder.destroy();
|
||||
self.pos.proxy.transaction_end();
|
||||
@@ -745,19 +745,23 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
start: function(){ //FIXME this should work as renderElement... but then the categories aren't properly set. explore why
|
||||
var self = this;
|
||||
this.product_categories_widget = new module.ProductCategoriesWidget(this,{});
|
||||
this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
|
||||
|
||||
this.product_list_widget = new module.ProductListWidget(this,{
|
||||
click_product_action: function(product){
|
||||
if(product.get('to_weight') && self.pos.iface_electronic_scale){
|
||||
if(product.to_weight && self.pos.iface_electronic_scale){
|
||||
self.pos_widget.screen_selector.set_current_screen( self.cashier_mode ? self.scale_screen : self.client_scale_screen, {product: product});
|
||||
}else{
|
||||
self.pos.get('selectedOrder').addProduct(product);
|
||||
}
|
||||
},
|
||||
product_list: this.pos.db.get_product_by_category(0)
|
||||
});
|
||||
this.product_list_widget.replace($('.placeholder-ProductListWidget'));
|
||||
|
||||
this.product_categories_widget = new module.ProductCategoriesWidget(this,{
|
||||
product_list_widget: this.product_list_widget,
|
||||
});
|
||||
this.product_categories_widget.replace($('.placeholder-ProductCategoriesWidget'));
|
||||
},
|
||||
|
||||
show: function(){
|
||||
@@ -766,7 +770,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
this.product_categories_widget.reset_category();
|
||||
|
||||
this.pos_widget.order_widget.set_numpad_state(this.pos_widget.numpad.state);
|
||||
this.pos_widget.order_widget.set_editable(true);
|
||||
|
||||
if(this.pos_widget.screen_selector.current_mode === 'client'){
|
||||
this.add_action_button({
|
||||
@@ -781,13 +785,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
close: function(){
|
||||
this._super();
|
||||
this.pos_widget.order_widget.set_numpad_state(null);
|
||||
this.pos_widget.payment_screen.set_numpad_state(null);
|
||||
|
||||
this.pos_widget.order_widget.set_editable(false);
|
||||
|
||||
if(this.pos.iface_vkeyboard && this.pos_widget.onscreen_keyboard){
|
||||
this.pos_widget.onscreen_keyboard.hide();
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.ReceiptScreenWidget = module.ScreenWidget.extend({
|
||||
@@ -812,8 +816,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this._super();
|
||||
var self = this;
|
||||
|
||||
this.pos_widget.set_numpad_visible(false);
|
||||
|
||||
var print_button = this.add_action_button({
|
||||
label: _t('Print'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/printer.png',
|
||||
@@ -876,7 +878,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
},
|
||||
close: function(){
|
||||
this._super();
|
||||
this.pos_widget.set_numpad_visible(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -888,7 +889,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this._super(parent,options);
|
||||
this.model = options.model;
|
||||
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
|
||||
this.bindPaymentLineEvents();
|
||||
this.bind_payment_line_events();
|
||||
this.bind_orderline_events();
|
||||
this.paymentlinewidgets = [];
|
||||
this.focusedLine = null;
|
||||
@@ -899,7 +900,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
this.hotkey_handler = function(event){
|
||||
if(event.which === 13){
|
||||
self.validateCurrentOrder();
|
||||
self.validate();
|
||||
}else if(event.which === 27){
|
||||
self.back();
|
||||
}
|
||||
@@ -912,8 +913,8 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this.pos.proxy.open_cashbox();
|
||||
}
|
||||
|
||||
this.set_numpad_state(this.pos_widget.numpad.state);
|
||||
|
||||
this.enable_numpad();
|
||||
|
||||
this.add_action_button({
|
||||
label: _t('Back'),
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/go-previous.png',
|
||||
@@ -927,7 +928,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
name: 'validation',
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/validate.png',
|
||||
click: function(){
|
||||
self.validateCurrentOrder();
|
||||
self.validate();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -937,7 +938,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
name: 'invoice',
|
||||
icon: '/point_of_sale/static/src/img/icons/png48/invoice.png',
|
||||
click: function(){
|
||||
self.validateCurrentOrder({invoice: true});
|
||||
self.validate({invoice: true});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -953,13 +954,12 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
});
|
||||
}
|
||||
|
||||
this.updatePaymentSummary();
|
||||
this.update_payment_summary();
|
||||
this.line_refocus();
|
||||
},
|
||||
close: function(){
|
||||
this._super();
|
||||
this.pos_widget.order_widget.set_numpad_state(null);
|
||||
this.pos_widget.payment_screen.set_numpad_state(null);
|
||||
this.disable_numpad();
|
||||
$('body').off('keyup',this.hotkey_handler);
|
||||
},
|
||||
back: function() {
|
||||
@@ -970,13 +970,13 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
});
|
||||
this.pos_widget.screen_selector.set_current_screen(this.back_screen);
|
||||
},
|
||||
validateCurrentOrder: function(options) {
|
||||
validate: function(options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
var currentOrder = this.pos.get('selectedOrder');
|
||||
|
||||
if(!this.isPaymentPaid()){
|
||||
if(!this.is_paid()){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1018,19 +1018,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
$("input").blur();
|
||||
},250);
|
||||
},
|
||||
bindPaymentLineEvents: function() {
|
||||
bind_payment_line_events: function() {
|
||||
this.currentPaymentLines = (this.pos.get('selectedOrder')).get('paymentLines');
|
||||
this.currentPaymentLines.bind('add', this.addPaymentLine, this);
|
||||
this.currentPaymentLines.bind('add', this.add_payment_line, this);
|
||||
this.currentPaymentLines.bind('remove', this.renderElement, this);
|
||||
this.currentPaymentLines.bind('all', this.updatePaymentSummary, this);
|
||||
this.currentPaymentLines.bind('all', this.update_payment_summary, this);
|
||||
},
|
||||
bind_orderline_events: function() {
|
||||
this.currentOrderLines = (this.pos.get('selectedOrder')).get('orderLines');
|
||||
this.currentOrderLines.bind('all', this.updatePaymentSummary, this);
|
||||
this.currentOrderLines.bind('all', this.update_payment_summary, this);
|
||||
},
|
||||
change_selected_order: function() {
|
||||
this.currentPaymentLines.unbind();
|
||||
this.bindPaymentLineEvents();
|
||||
this.bind_payment_line_events();
|
||||
this.currentOrderLines.unbind();
|
||||
this.bind_orderline_events();
|
||||
this.renderElement();
|
||||
@@ -1045,7 +1045,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this.focusedLine.focus();
|
||||
}
|
||||
},
|
||||
addPaymentLine: function(newPaymentLine) {
|
||||
add_payment_line: function(newPaymentLine) {
|
||||
var self = this;
|
||||
var l = new module.PaymentlineWidget(this, {
|
||||
payment_line: newPaymentLine,
|
||||
@@ -1069,21 +1069,21 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
this.paymentlinewidgets = [];
|
||||
|
||||
this.currentPaymentLines.each(_.bind( function(paymentLine) {
|
||||
this.addPaymentLine(paymentLine);
|
||||
this.add_payment_line(paymentLine);
|
||||
}, this));
|
||||
this.updatePaymentSummary();
|
||||
this.update_payment_summary();
|
||||
},
|
||||
deleteLine: function(lineWidget) {
|
||||
this.currentPaymentLines.remove([lineWidget.payment_line]);
|
||||
lineWidget.destroy();
|
||||
},
|
||||
isPaymentPaid: function(){
|
||||
is_paid: function(){
|
||||
var currentOrder = this.pos.get('selectedOrder');
|
||||
return (currentOrder.getTotalTaxIncluded() >= 0.000001
|
||||
&& currentOrder.getPaidTotal() + 0.000001 >= currentOrder.getTotalTaxIncluded());
|
||||
|
||||
},
|
||||
updatePaymentSummary: function() {
|
||||
update_payment_summary: function() {
|
||||
var currentOrder = this.pos.get('selectedOrder');
|
||||
var paidTotal = currentOrder.getPaidTotal();
|
||||
var dueTotal = currentOrder.getTotalTaxIncluded();
|
||||
@@ -1099,25 +1099,29 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
|
||||
}
|
||||
|
||||
if(this.pos_widget.action_bar){
|
||||
this.pos_widget.action_bar.set_button_disabled('validation', !this.isPaymentPaid());
|
||||
this.pos_widget.action_bar.set_button_disabled('invoice', !this.isPaymentPaid());
|
||||
this.pos_widget.action_bar.set_button_disabled('validation', !this.is_paid());
|
||||
this.pos_widget.action_bar.set_button_disabled('invoice', !this.is_paid());
|
||||
}
|
||||
},
|
||||
set_numpad_state: function(numpadState) {
|
||||
if (this.numpadState) {
|
||||
this.numpadState.unbind('set_value', this.set_value);
|
||||
this.numpadState.unbind('change:mode', this.setNumpadMode);
|
||||
}
|
||||
this.numpadState = numpadState;
|
||||
if (this.numpadState) {
|
||||
this.numpadState.bind('set_value', this.set_value, this);
|
||||
this.numpadState.bind('change:mode', this.setNumpadMode, this);
|
||||
this.numpadState.reset();
|
||||
this.setNumpadMode();
|
||||
}
|
||||
enable_numpad: function(){
|
||||
this.disable_numpad(); //ensure we don't register the callbacks twice
|
||||
this.numpad_state = this.pos_widget.numpad.state;
|
||||
if(this.numpad_state){
|
||||
this.numpad_state.reset();
|
||||
this.numpad_state.changeMode('payment');
|
||||
this.numpad_state.bind('set_value', this.set_value, this);
|
||||
this.numpad_state.bind('change:mode', this.set_mode_back_to_payment, this);
|
||||
}
|
||||
|
||||
},
|
||||
setNumpadMode: function() {
|
||||
this.numpadState.set({mode: 'payment'});
|
||||
disable_numpad: function(){
|
||||
if(this.numpad_state){
|
||||
this.numpad_state.unbind('set_value', this.set_value);
|
||||
this.numpad_state.unbind('change:mode',this.set_mode_back_to_payment);
|
||||
}
|
||||
},
|
||||
set_mode_back_to_payment: function() {
|
||||
this.numpad_state.set({mode: 'payment'});
|
||||
},
|
||||
set_value: function(val) {
|
||||
this.currentPaymentLines.last().set_amount(val);
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* This Widget provides a javascript scrollbar that is suitable to use with resistive
|
||||
* tactile screens.
|
||||
*
|
||||
* Options:
|
||||
* target_widget : the widget that will be scrolled.
|
||||
* target_selector : if you don't want to scroll the root element of the the widget, you can provide a
|
||||
* jquery selector string that will match on the widget's dom element. If there is no widget provided,
|
||||
* it will match on the document
|
||||
* step: on each click, the target will be scrolled by it's deplayed size multiplied by this value.
|
||||
* duration: this is the duration of the scrolling animation
|
||||
* wheel_step: the target will be scrolled by wheel_step pixels on each mouse scroll.
|
||||
* track_bottom: the target will be kept on bottom when it's on the bottom and the size has changed
|
||||
* on_show: this function will be called with the scrollbar as sole argument when the scrollbar is shown
|
||||
* on_hide: this function will be called with the scrollbar as sole argument when the scrollbar is hidden
|
||||
*/
|
||||
function openerp_pos_scrollbar(instance, module){ //module is instance.point_of_sale
|
||||
|
||||
module.ScrollbarWidget = instance.web.Widget.extend({
|
||||
template:'ScrollbarWidget',
|
||||
|
||||
init: function(parent,options){
|
||||
var self = this;
|
||||
options = options || {};
|
||||
this._super(parent,options);
|
||||
this.target_widget = options.target_widget;
|
||||
this.target_selector = options.target_selector;
|
||||
this.scroll_target = this.target().scrollTop();
|
||||
this.scroll_step = options.step || 0.8;
|
||||
this.scroll_duration = options.duration || 250;
|
||||
this.wheel_step = options.wheel_step || 80;
|
||||
this.name = options.name || 'unnamed';
|
||||
this.bottom = false; // true if the scroller cannot be scrolled further
|
||||
this.track_bottom = options.track_bottom || false;
|
||||
this.on_show = options.on_show || function(){};
|
||||
this.on_hide = options.on_hide || function(){};
|
||||
|
||||
// these handlers are declared once for the object's lifetime so that we can bind and unbind them.
|
||||
this.resize_handler = function(){
|
||||
setTimeout(function(){
|
||||
if(self.bottom && self.track_bottom){
|
||||
self.set_position(Number.MAX_VALUE);
|
||||
}
|
||||
self.update_scroller_dimensions();
|
||||
self.update_button_status();
|
||||
self.auto_hide(false);
|
||||
},0);
|
||||
};
|
||||
this.target_mousewheel_handler = function(event,delta){
|
||||
self.scroll(delta*self.wheel_step);
|
||||
}
|
||||
},
|
||||
|
||||
renderElement: function(){
|
||||
this._super();
|
||||
var self = this;
|
||||
this.$('.up-button').off('click').click(function(){
|
||||
self.page_up();
|
||||
});
|
||||
this.$('.down-button').off('click').click(function(){
|
||||
self.page_down();
|
||||
});
|
||||
this.update_scroller_dimensions(false);
|
||||
this.update_button_status();
|
||||
this.auto_hide(false);
|
||||
this.$el.bind('mousewheel',function(event,delta){
|
||||
self.scroll(delta*self.wheel_step);
|
||||
return false;
|
||||
});
|
||||
this.$el.bind('click',function(event){
|
||||
var vpos = event.pageY - self.$el.offset().top;
|
||||
var spos = self.scroller_dimensions();
|
||||
if(vpos > spos.bar_pos && vpos < spos.pos){
|
||||
self.page_up();
|
||||
}else if( (vpos < spos.bar_pos + spos.bar_height) &&
|
||||
(vpos > spos.pos + spos.height) ){
|
||||
self.page_down();
|
||||
}
|
||||
});
|
||||
// FIXME: use the event bus to handle window resize events
|
||||
$(window).unbind('resize',this.resize_handler);
|
||||
$(window).bind('resize',this.resize_handler);
|
||||
|
||||
this.target().unbind('mousewheel',this.target_mousewheel_handler);
|
||||
this.target().bind('mousewheel',this.target_mousewheel_handler);
|
||||
|
||||
// because the rendering is asynchronous we must wait for the next javascript update
|
||||
// for good dimensions values
|
||||
setTimeout(function(){
|
||||
self.update_scroller_dimensions(false);
|
||||
self.update_button_status();
|
||||
self.auto_hide(false);
|
||||
},0);
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
$(window).unbind('resize',this.resize_handler);
|
||||
this.target().unbind('mousewheel',this.target_mousewheel_handler);
|
||||
this._super();
|
||||
},
|
||||
|
||||
// shows the scrollbar. if animated is true, it will do it in an animated fashion
|
||||
show: function(animated){ //FIXME: animated show and hide don't work ... ?
|
||||
if(animated){
|
||||
this.$el.removeClass('oe_hidden').animate({'width':'48px'}, 500, 'swing');
|
||||
}else{
|
||||
this.$el.removeClass('oe_hidden').css('width','48px');
|
||||
}
|
||||
this.on_show(this);
|
||||
},
|
||||
|
||||
// hides the scrollbar. if animated is true, it will do it in a animated fashion
|
||||
hide: function(animated){
|
||||
var self = this;
|
||||
if(animated){
|
||||
this.$el.animate({'width':'0px'}, 500, 'swing', function(){ self.$el.addClass('oe_hidden');});
|
||||
}else{
|
||||
this.$el.addClass('oe_hidden').css('width','0px');
|
||||
}
|
||||
this.on_hide(this);
|
||||
},
|
||||
|
||||
// returns the scroller position and other information as a dictionnary with the following fields:
|
||||
// pos: the position in pixels of the top of the scroller starting from the top of the scrollbar
|
||||
// height: the height of the scroller in pixels
|
||||
// bar_pos: the position of the top of the scrollbar's inner region, starting from the top
|
||||
// bar_height: the height of the scrollbar's inner region
|
||||
scroller_dimensions: function(){
|
||||
var target = this.target()[0];
|
||||
var scroller_height = target.clientHeight / target.scrollHeight || 0;
|
||||
var scroller_pos = this.scroll_target / target.scrollHeight || 0;
|
||||
var button_up_height = this.$('.up-button')[0].offsetHeight || 48;
|
||||
var button_down_height = this.$('.down-button')[0].offsetHeight || 48;
|
||||
|
||||
var bar_height = this.$el[0].offsetHeight || 96;
|
||||
var scrollbar_height = bar_height - button_up_height - button_down_height;
|
||||
|
||||
scroller_pos = scroller_pos * scrollbar_height + button_up_height;
|
||||
scroller_height = scroller_height * scrollbar_height;
|
||||
|
||||
return { pos: Math.round(scroller_pos),
|
||||
height: Math.round(scroller_height),
|
||||
bar_pos: button_up_height,
|
||||
bar_height: scrollbar_height };
|
||||
},
|
||||
|
||||
|
||||
//checks if it should show or hide the scrollbar based on the target content and then show or hide it
|
||||
// if animated is true, then the scrollbar will be shown or hidden with an animation
|
||||
auto_hide: function(animated){
|
||||
var target = this.target()[0];
|
||||
if(target.clientHeight && (target.clientHeight === target.scrollHeight)){
|
||||
this.hide(animated);
|
||||
}else{
|
||||
this.show(animated);
|
||||
}
|
||||
},
|
||||
|
||||
//returns the pageup/down scrolling distance in pixels
|
||||
get_scroll_step: function(){
|
||||
var target = this.target()[0];
|
||||
var step = target.clientHeight * this.scroll_step;
|
||||
var c = target.scrollHeight / step;
|
||||
var c = Math.max(1,Math.ceil(c));
|
||||
return target.scrollHeight / c;
|
||||
},
|
||||
|
||||
//sets the scroller to the correct size and position based on the target scrolling status
|
||||
//if animated is true, the scroller will move smoothly to its destination
|
||||
update_scroller_dimensions: function(animated){
|
||||
var dim = this.scroller_dimensions();
|
||||
var target = this.target()[0];
|
||||
if(animated){
|
||||
this.$('.scroller').animate({'top':dim.pos+'px', 'height': dim.height+'px'},this.scroll_duration);
|
||||
}else{
|
||||
this.$('.scroller').css({'top':dim.pos+'px', 'height': dim.height+'px'});
|
||||
}
|
||||
if(this.scroll_target + target.clientHeight >= target.scrollHeight){
|
||||
this.bottom = true;
|
||||
}else{
|
||||
this.bottom = false;
|
||||
}
|
||||
},
|
||||
|
||||
//disable or enable the up/down buttons according to the scrolled position
|
||||
update_button_status: function(){
|
||||
var target = this.target()[0];
|
||||
this.$('.up-button').removeClass('disabled');
|
||||
this.$('.down-button').removeClass('disabled');
|
||||
if(this.scroll_target === 0){
|
||||
this.$('.up-button').addClass('disabled');
|
||||
}
|
||||
if(this.scroll_target + target.clientHeight >= target.scrollHeight){
|
||||
this.$('.down-button').addClass('disabled');
|
||||
}
|
||||
},
|
||||
|
||||
//returns the jquery object of the scrolling target
|
||||
target: function(){
|
||||
if(this.target_widget){
|
||||
if(this.target_selector){
|
||||
return this.target_widget.$(this.target_selector);
|
||||
}else{
|
||||
return this.target_widget.$el;
|
||||
}
|
||||
}else if(this.target_selector){
|
||||
return $(this.target_selector);
|
||||
}else{
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
//scroll one page up
|
||||
page_up: function(){
|
||||
return this.set_position(this.scroll_target - this.get_scroll_step(), true);
|
||||
},
|
||||
|
||||
//scroll one page down
|
||||
page_down: function(){
|
||||
return this.set_position(this.scroll_target + this.get_scroll_step(), true);
|
||||
},
|
||||
|
||||
//scrolls up or down by pixels
|
||||
scroll: function(pixels){
|
||||
return this.set_position(this.scroll_target - pixels, false);
|
||||
},
|
||||
|
||||
//scroll to a specific position (in pixels).
|
||||
//if animated is true, it will do this in an animated fashion with a duration equal to scroll_duration
|
||||
set_position: function(position,animated){
|
||||
var self = this;
|
||||
var target = this.target()[0];
|
||||
var bottom = target.scrollHeight-target.clientHeight;
|
||||
this.scroll_target = Math.max(0,Math.min(bottom,position));
|
||||
if(this.scroll_target === 0){
|
||||
this.position = 'top';
|
||||
}else if(this.scroll_target === 'bottom'){
|
||||
this.position = 'bottom';
|
||||
}else{
|
||||
this.position = 'center';
|
||||
}
|
||||
if(animated){
|
||||
this.target().animate({'scrollTop':this.scroll_target},this.scroll_duration);
|
||||
this.update_button_status();
|
||||
this.update_scroller_dimensions(true);
|
||||
}else{
|
||||
this.target().scrollTop(this.scroll_target);
|
||||
this.update_scroller_dimensions(false);
|
||||
this.update_button_status();
|
||||
}
|
||||
return this.scroll_target;
|
||||
},
|
||||
|
||||
//returns the current position of the scrollbar
|
||||
get_position: function(){
|
||||
return this.scroll_target;
|
||||
},
|
||||
|
||||
//returns true if it cannot be scrolled further down
|
||||
is_at_bottom: function(){
|
||||
return this.bottom;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
@@ -58,6 +58,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
init: function(parent, options) {
|
||||
this._super(parent);
|
||||
this.state = new module.NumpadState();
|
||||
window.numpadstate = this.state;
|
||||
var self = this;
|
||||
this.state.bind('change:buffer',function(){
|
||||
console.log('BUFFER:',self.state.get('buffer'));
|
||||
})
|
||||
},
|
||||
start: function() {
|
||||
this.state.bind('change:mode', this.changedMode, this);
|
||||
@@ -129,61 +134,50 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
},
|
||||
});
|
||||
|
||||
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', this.refresh, this);
|
||||
},
|
||||
renderElement: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
this.$el.click(function(){
|
||||
self.order.selectLine(self.model);
|
||||
self.trigger('order_line_selected');
|
||||
});
|
||||
if(this.model.is_selected()){
|
||||
this.$el.addClass('selected');
|
||||
}
|
||||
},
|
||||
refresh: function(){
|
||||
this.renderElement();
|
||||
this.trigger('order_line_refreshed');
|
||||
},
|
||||
destroy: function(){
|
||||
this.model.unbind('change',this.refresh,this);
|
||||
this._super();
|
||||
},
|
||||
});
|
||||
|
||||
module.OrderWidget = module.PosBaseWidget.extend({
|
||||
template:'OrderWidget',
|
||||
init: function(parent, options) {
|
||||
var self = this;
|
||||
this._super(parent,options);
|
||||
this.display_mode = options.display_mode || 'numpad'; // 'maximized' | 'actionbar' | 'numpad'
|
||||
this.set_numpad_state(options.numpadState);
|
||||
this.editable = false;
|
||||
this.pos.bind('change:selectedOrder', this.change_selected_order, this);
|
||||
this.bind_orderline_events();
|
||||
this.orderlinewidgets = [];
|
||||
this.line_click_handler = function(event){
|
||||
if(!self.editable){
|
||||
return;
|
||||
}
|
||||
self.pos.get('selectedOrder').selectLine(this.orderline);
|
||||
self.pos_widget.numpad.state.reset();
|
||||
};
|
||||
},
|
||||
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();
|
||||
}
|
||||
enable_numpad: function(){
|
||||
this.disable_numpad(); //ensure we don't register the callbacks twice
|
||||
this.numpad_state = this.pos_widget.numpad.state;
|
||||
if(this.numpad_state){
|
||||
this.numpad_state.reset();
|
||||
this.numpad_state.bind('set_value', this.set_value, this);
|
||||
}
|
||||
|
||||
},
|
||||
disable_numpad: function(){
|
||||
if(this.numpad_state){
|
||||
this.numpad_state.unbind('set_value', this.set_value);
|
||||
this.numpad_state.reset();
|
||||
}
|
||||
},
|
||||
set_editable: function(editable){
|
||||
this.editable = editable;
|
||||
if(editable){
|
||||
this.enable_numpad();
|
||||
}else{
|
||||
this.disable_numpad();
|
||||
this.pos.get('selectedOrder').deselectLine();
|
||||
}
|
||||
},
|
||||
set_value: function(val) {
|
||||
var order = this.pos.get('selectedOrder');
|
||||
if (order.get('orderLines').length !== 0) {
|
||||
var mode = this.numpadState.get('mode');
|
||||
if (this.editable && order.getSelectedLine()) {
|
||||
var mode = this.numpad_state.get('mode');
|
||||
if( mode === 'quantity'){
|
||||
order.getSelectedLine().set_quantity(val);
|
||||
}else if( mode === 'discount'){
|
||||
@@ -194,81 +188,96 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
}
|
||||
},
|
||||
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', function(){ this.renderElement(true);}, this);
|
||||
this.currentOrderLines.bind('remove', this.renderElement, this);
|
||||
var lines = this.pos.get('selectedOrder').get('orderLines');
|
||||
lines.unbind();
|
||||
lines.bind('add', function(){
|
||||
this.numpad_state.reset();
|
||||
this.renderElement(true);
|
||||
},this);
|
||||
lines.bind('remove', function(line){
|
||||
this.remove_orderline(line);
|
||||
this.numpad_state.reset();
|
||||
this.update_summary();
|
||||
},this);
|
||||
lines.bind('change', function(line){
|
||||
this.rerender_orderline(line);
|
||||
this.update_summary();
|
||||
},this);
|
||||
},
|
||||
update_numpad: function() {
|
||||
this.selected_line = this.pos.get('selectedOrder').getSelectedLine();
|
||||
if (this.numpadState)
|
||||
this.numpadState.reset();
|
||||
},
|
||||
renderElement: function(goto_bottom) {
|
||||
var self = this;
|
||||
var scroller = this.$('.order-scroller')[0];
|
||||
var scrollbottom = true;
|
||||
var scrollTop = 0;
|
||||
if(scroller){
|
||||
var overflow_bottom = scroller.scrollHeight - scroller.clientHeight;
|
||||
scrollTop = scroller.scrollTop;
|
||||
if( !goto_bottom && scrollTop < 0.9 * overflow_bottom){
|
||||
scrollbottom = false;
|
||||
}
|
||||
}
|
||||
this._super();
|
||||
render_orderline: function(orderline){
|
||||
var el_str = openerp.qweb.render('Orderline',{widget:this, line:orderline});
|
||||
var el_node = document.createElement('div');
|
||||
el_node.innerHTML = _.str.trim(el_str);
|
||||
el_node = el_node.childNodes[0];
|
||||
el_node.orderline = orderline;
|
||||
el_node.addEventListener('click',this.line_click_handler);
|
||||
|
||||
// freeing subwidgets
|
||||
|
||||
for(var i = 0, len = this.orderlinewidgets.length; i < len; i++){
|
||||
this.orderlinewidgets[i].destroy();
|
||||
orderline.node = el_node;
|
||||
return el_node;
|
||||
},
|
||||
remove_orderline: function(order_line){
|
||||
if(this.pos.get('selectedOrder').get('orderLines').length === 0){
|
||||
this.renderElement();
|
||||
}else{
|
||||
order_line.node.parentNode.removeChild(order_line.node);
|
||||
}
|
||||
this.orderlinewidgets = [];
|
||||
},
|
||||
rerender_orderline: function(order_line){
|
||||
var node = order_line.node;
|
||||
var replacement_line = this.render_orderline(order_line);
|
||||
node.parentNode.replaceChild(replacement_line,node);
|
||||
},
|
||||
replace: function($target){
|
||||
this.renderElement();
|
||||
var target = $target[0];
|
||||
target.parentNode.replaceChild(this.el,target);
|
||||
},
|
||||
renderElement: function(scrollbottom){
|
||||
this.pos_widget.numpad.state.reset();
|
||||
|
||||
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();
|
||||
var order = this.pos.get('selectedOrder');
|
||||
var orderlines = order.get('orderLines').models;
|
||||
|
||||
var el_str = openerp.qweb.render('OrderWidget',{widget:this, order:order, orderlines:orderlines});
|
||||
var el_node = document.createElement('div');
|
||||
el_node.innerHTML = _.str.trim(el_str);
|
||||
el_node = el_node.childNodes[0];
|
||||
|
||||
var list_container = el_node.querySelector('.orderlines');
|
||||
for(var i = 0, len = orderlines.length; i < len; i++){
|
||||
var orderline = this.render_orderline(orderlines[i]);
|
||||
list_container.appendChild(orderline);
|
||||
}
|
||||
|
||||
if(this.el && this.el.parentNode){
|
||||
this.el.parentNode.replaceChild(el_node,this.el);
|
||||
}
|
||||
this.el = el_node;
|
||||
this.update_summary();
|
||||
|
||||
scroller = this.$('.order-scroller')[0];
|
||||
|
||||
if(scroller){
|
||||
if(scrollbottom){
|
||||
scroller.scrollTop = scroller.scrollHeight - scroller.clientHeight;
|
||||
}else{
|
||||
scroller.scrollTop = scrollTop;
|
||||
}
|
||||
if(scrollbottom){
|
||||
this.el.querySelector('.order-scroller').scrollTop = 100 * orderlines.length;
|
||||
}
|
||||
},
|
||||
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();
|
||||
}
|
||||
|
||||
this.el.querySelector('.summary .total > .value').innerText = this.format_currency(total);
|
||||
this.el.querySelector('.summary .total .subentry .value').innerText = this.format_currency(taxes);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
module.PaymentlineWidget = module.PosBaseWidget.extend({
|
||||
template: 'PaymentlineWidget',
|
||||
init: function(parent, options) {
|
||||
@@ -439,8 +448,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
},
|
||||
});
|
||||
|
||||
module.CategoryButton = module.PosBaseWidget.extend({
|
||||
});
|
||||
module.ProductCategoriesWidget = module.PosBaseWidget.extend({
|
||||
template: 'ProductCategoriesWidget',
|
||||
init: function(parent, options){
|
||||
@@ -451,7 +458,13 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
this.category = this.pos.root_category;
|
||||
this.breadcrumb = [];
|
||||
this.subcategories = [];
|
||||
this.product_list_widget = options.product_list_widget || null;
|
||||
this.category_cache = {};
|
||||
this.set_category();
|
||||
this.switch_category_handler = function(event){
|
||||
self.set_category(self.pos.db.get_category_by_id(Number(this.dataset['categoryId'])));
|
||||
self.renderElement();
|
||||
};
|
||||
},
|
||||
|
||||
// changes the category. if undefined, sets to root category
|
||||
@@ -474,46 +487,77 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
},
|
||||
|
||||
get_image_url: function(category){
|
||||
return instance.session.url('/web/binary/image', {model: 'pos.category', field: 'image_medium', id: category.id});
|
||||
return window.location.origin + '/web/binary/image?model=pos.category&field=image_medium&id='+category.id;
|
||||
},
|
||||
|
||||
render_category: function( category, with_image ){
|
||||
if(!this.category_cache[category.id]){
|
||||
|
||||
if(with_image){
|
||||
var image_url = this.get_image_url(category);
|
||||
var category_html = QWeb.render('CategoryButton',{
|
||||
widget: this,
|
||||
category: category,
|
||||
});
|
||||
category_html = _.str.trim(category_html);
|
||||
var category_node = document.createElement('div');
|
||||
category_node.innerHTML = category_html;
|
||||
category_node = category_node.childNodes[0];
|
||||
var img = category_node.querySelector('img');
|
||||
img.parentNode.replaceChild(this.pos_widget.image_cache.get_image(image_url),img);
|
||||
}else{
|
||||
var category_html = QWeb.render('CategorySimpleButton',{
|
||||
widget: this,
|
||||
category: category,
|
||||
});
|
||||
category_html = _.str.trim(category_html);
|
||||
var category_node = document.createElement('div');
|
||||
category_node.innerHTML = category_html;
|
||||
category_node = category_node.childNodes[0];
|
||||
}
|
||||
this.category_cache[category.id] = category_node;
|
||||
}
|
||||
return this.category_cache[category.id];
|
||||
},
|
||||
|
||||
replace: function($target){
|
||||
this.renderElement();
|
||||
var target = $target[0];
|
||||
target.parentNode.replaceChild(this.el,target);
|
||||
},
|
||||
|
||||
renderElement: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
var el_str = openerp.qweb.render(this.template, {widget: this});
|
||||
var el_node = document.createElement('div');
|
||||
el_node.innerHTML = el_str;
|
||||
el_node = el_node.childNodes[1];
|
||||
|
||||
if(this.el && this.el.parentNode){
|
||||
this.el.parentNode.replaceChild(el_node,this.el);
|
||||
}
|
||||
|
||||
this.el = el_node;
|
||||
|
||||
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){
|
||||
for(var i = 0; i < this.subcategories.length; i++){
|
||||
if(this.subcategories[i].image){
|
||||
hasimages = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_.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);
|
||||
}
|
||||
var list_container = el_node.querySelector('.category-list');
|
||||
for(var i = 0, len = this.subcategories.length; i < len; i++){
|
||||
list_container.appendChild(this.render_category(this.subcategories[i],hasimages));
|
||||
};
|
||||
|
||||
var buttons = el_node.querySelectorAll('.js-category-switch');
|
||||
for(var i = 0; i < buttons.length; i++){
|
||||
buttons[i].addEventListener('click',this.switch_category_handler);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -522,11 +566,6 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
}
|
||||
},
|
||||
|
||||
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();
|
||||
@@ -536,7 +575,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
// 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.product_list_widget.set_product_list(products);
|
||||
this.$('.searchbox input').val('').focus();
|
||||
this.$('.search-clear').fadeOut();
|
||||
},
|
||||
@@ -547,9 +586,10 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
|
||||
// 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);
|
||||
this.product_list_widget.set_product_list(products);
|
||||
|
||||
|
||||
/*
|
||||
var searchtimeout = null;
|
||||
// filter the products according to the search string
|
||||
this.$('.searchbox input').keyup(function(event){
|
||||
@@ -562,17 +602,17 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
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.pos.get('selectedOrder').addProduct(self.pos.get('products').at(0)); // FIXME
|
||||
self.clear_search();
|
||||
}
|
||||
}else{
|
||||
var products = self.pos.db.search_product_in_category(self.category.id, query);
|
||||
self.pos.get('products').reset(products);
|
||||
this.product_list_widget.set_product_list(products);
|
||||
self.$('.search-clear').fadeIn();
|
||||
}
|
||||
}else{
|
||||
var products = self.pos.db.get_product_by_category(self.category.id);
|
||||
self.pos.get('products').reset(products);
|
||||
this.product_list_widget.set_product_list(products);
|
||||
self.$('.search-clear').fadeOut();
|
||||
}
|
||||
},200);
|
||||
@@ -581,11 +621,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
//reset the search when clicking on reset
|
||||
this.$('.search-clear').click(function(){
|
||||
self.clear_search();
|
||||
});
|
||||
});*/
|
||||
},
|
||||
});
|
||||
|
||||
module.ProductListWidget = module.ScreenWidget.extend({
|
||||
module.ProductListWidget = module.PosBaseWidget.extend({
|
||||
template:'ProductListWidget',
|
||||
init: function(parent, options) {
|
||||
var self = this;
|
||||
@@ -595,38 +635,65 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
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();
|
||||
});
|
||||
this.click_product_handler = function(event){
|
||||
var product = self.pos.db.get_product_by_id(this.dataset['productId']);
|
||||
options.click_product_action(product);
|
||||
};
|
||||
|
||||
this.product_list = options.product_list || [];
|
||||
this.product_cache = {};
|
||||
},
|
||||
set_product_list: function(product_list){
|
||||
this.product_list = product_list;
|
||||
this.renderElement();
|
||||
},
|
||||
get_product_image_url: function(product){
|
||||
return window.location.origin + '/web/binary/image?model=product.product&field=image_medium&id='+product.id;
|
||||
},
|
||||
replace: function($target){
|
||||
this.renderElement();
|
||||
var target = $target[0];
|
||||
target.parentNode.replaceChild(this.el,target);
|
||||
},
|
||||
|
||||
render_product: function(product){
|
||||
if(!this.product_cache[product.id]){
|
||||
var image_url = this.get_product_image_url(product);
|
||||
var product_html = QWeb.render('Product',{
|
||||
widget: this,
|
||||
product: product,
|
||||
});
|
||||
var product_node = document.createElement('div');
|
||||
product_node.innerHTML = product_html;
|
||||
product_node = product_node.childNodes[1];
|
||||
var img = product_node.querySelector('img');
|
||||
img.parentNode.replaceChild(this.pos_widget.image_cache.get_image(image_url),img);
|
||||
this.product_cache[product.id] = product_node;
|
||||
}
|
||||
return this.product_cache[product.id];
|
||||
},
|
||||
|
||||
renderElement: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
var products = this.pos.get('products').models || [];
|
||||
// this._super()
|
||||
var el_str = openerp.qweb.render(this.template, {widget: this});
|
||||
var el_node = document.createElement('div');
|
||||
el_node.innerHTML = el_str;
|
||||
el_node = el_node.childNodes[1];
|
||||
|
||||
_.each(products,function(product,i){
|
||||
var $product = $(QWeb.render('Product',{ widget:self, product: products[i] }));
|
||||
$product.find('img').replaceWith(self.pos_widget.image_cache.get_image(products[i].get_image_url()));
|
||||
$product.appendTo(self.$('.product-list'));
|
||||
});
|
||||
|
||||
this.$el.delegate('.product','touchstart',function TOUCHSTART(){
|
||||
console.log('Touchstart',(new Date()).getTime());
|
||||
});
|
||||
|
||||
this.$el.delegate('.product','touchend',function TOUCHEND(){
|
||||
console.log('Touchend',(new Date()).getTime());
|
||||
});
|
||||
|
||||
this.$el.delegate('.product','click',function TOUCHCLICK(){
|
||||
console.log('ClickStart',(new Date()).getTime());
|
||||
self.click_product_action(new module.Product(self.pos.db.get_product_by_id(+$(this).data('product-id'))));
|
||||
console.log('ClickEnd',(new Date()).getTime());
|
||||
});
|
||||
if(this.el && this.el.parentNode){
|
||||
this.el.parentNode.replaceChild(el_node,this.el);
|
||||
}
|
||||
this.el = el_node;
|
||||
|
||||
var list_container = el_node.querySelector('.product-list');
|
||||
for(var i = 0, len = this.product_list.length; i < len; i++){
|
||||
var product_node = this.render_product(this.product_list[i]);
|
||||
product_node.addEventListener('click',this.click_product_handler);
|
||||
list_container.appendChild(product_node);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1046,13 +1113,9 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1062,20 +1125,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
set_fullscreen: function(){
|
||||
if(this.el.webkitRequestFullscreen){
|
||||
this.el.webkitRequestFullscreen();
|
||||
}
|
||||
},
|
||||
//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){
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="CategoryButton">
|
||||
<li class='category-button'>
|
||||
<li class='category-button js-category-switch' t-att-data-category-id="category.id" >
|
||||
<div class="category-img">
|
||||
<img src="" />
|
||||
</div>
|
||||
@@ -148,23 +148,24 @@
|
||||
</t>
|
||||
|
||||
<t t-name="CategorySimpleButton">
|
||||
<li class='category-simple-button'>
|
||||
<li class='category-simple-button js-category-switch' t-att-data-category-id="category.id" >
|
||||
<t t-esc="category.name"/>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="ProductCategoriesWidget">
|
||||
<div>
|
||||
<header>
|
||||
<ol class="breadcrumb">
|
||||
<li class="oe-pos-categories-list">
|
||||
<a href="javascript:void(0)">
|
||||
<a href="javascript:void(0)" class="js-category-switch">
|
||||
<img src="/point_of_sale/static/src/img/home.png" class="homeimg" />
|
||||
</a>
|
||||
</li>
|
||||
<t t-foreach="widget.breadcrumb" t-as="category">
|
||||
<li class="oe-pos-categories-list">
|
||||
<img src="/point_of_sale/static/src/img/bc-arrow.png" class="bc-arrow" />
|
||||
<a href="javascript:void(0)" t-att-data-category-id="category.id">
|
||||
<a href="javascript:void(0)" class="js-category-switch" t-att-data-category-id="category.id">
|
||||
<t t-esc="category.name"/>
|
||||
</a>
|
||||
</li>
|
||||
@@ -181,6 +182,7 @@
|
||||
</ol>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="ProductListWidget">
|
||||
@@ -425,52 +427,36 @@
|
||||
<li class='product' t-att-data-product-id="product.id">
|
||||
<div class="product-img">
|
||||
<img src='' /> <!-- the product thumbnail -->
|
||||
<t t-if="!product.get('to_weight')">
|
||||
<t t-if="!product.to_weight">
|
||||
<span class="price-tag">
|
||||
<t t-esc="widget.format_currency(product.get('price'))"/>
|
||||
<t t-esc="widget.format_currency(product.price)"/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="product.get('to_weight')">
|
||||
<t t-if="product.to_weight">
|
||||
<span class="price-tag">
|
||||
<t t-esc="widget.format_currency(product.get('price'))+'/Kg'"/>
|
||||
<t t-esc="widget.format_currency(product.price)+'/Kg'"/>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="product-name">
|
||||
<t t-esc="product.get('name')"/>
|
||||
<t t-esc="product.name"/>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="ScrollbarWidget">
|
||||
<div class='scrollbar'>
|
||||
<div class='up-button button oe_e'>
|
||||
&atilde;
|
||||
</div>
|
||||
|
||||
<div class='scroller'>
|
||||
</div>
|
||||
|
||||
<div class='down-button button oe_e'>
|
||||
&auml;
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="OrderWidget">
|
||||
<div class="order-container">
|
||||
<div class="order-scroller">
|
||||
<div class="order">
|
||||
<ul class="orderlines">
|
||||
<t t-if="widget.pos.get('selectedOrder').get('orderLines').length === 0">
|
||||
<t t-if="orderlines.length === 0">
|
||||
<li class="orderline empty">
|
||||
Your shopping cart is empty
|
||||
</li>
|
||||
</t>
|
||||
|
||||
</ul>
|
||||
<div class="summary">
|
||||
<div t-attf-class="line #{widget.pos.get('selectedOrder').get('orderLines').length === 0 ? 'empty' : ''}">
|
||||
<div t-attf-class="line #{orderlines.length === 0 ? 'empty' : ''}">
|
||||
<div class='entry total'>
|
||||
<span class="label">Total: </span> <span class="value">0.00 €</span>
|
||||
<div class='subentry'>Taxes: <span class="value">0.00€</span></div>
|
||||
@@ -480,8 +466,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shadow-top"></div>
|
||||
<span class="placeholder-ScrollbarWidget"></span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -538,32 +522,32 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="OrderlineWidget">
|
||||
<li class="orderline">
|
||||
<t t-name="Orderline">
|
||||
<li t-attf-class="orderline #{ line.selected ? 'selected' : '' }">
|
||||
<span class="product-name">
|
||||
<t t-esc="widget.model.get_product().get('name')"/>
|
||||
<t t-esc="line.get_product().name"/>
|
||||
</span>
|
||||
<span class="price">
|
||||
<t t-esc="widget.format_currency(widget.model.get_display_price())"/>
|
||||
<t t-esc="widget.format_currency(line.get_display_price())"/>
|
||||
</span>
|
||||
<ul class="info-list">
|
||||
<t t-if="widget.model.get_quantity_str() !== '1'">
|
||||
<t t-if="line.get_quantity_str() !== '1' || line.selected ">
|
||||
<li class="info">
|
||||
<em>
|
||||
<t t-esc="widget.model.get_quantity_str()" />
|
||||
<t t-esc="line.get_quantity_str()" />
|
||||
</em>
|
||||
<t t-esc="widget.model.get_unit().name" />
|
||||
<t t-esc="line.get_unit().name" />
|
||||
at
|
||||
<t t-esc="widget.format_currency(widget.model.get_unit_price())" />
|
||||
<t t-esc="widget.format_currency(line.get_unit_price())" />
|
||||
/
|
||||
<t t-esc="widget.model.get_unit().name" />
|
||||
<t t-esc="line.get_unit().name" />
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="widget.model.get_discount_str() !== '0'">
|
||||
<t t-if="line.get_discount_str() !== '0'">
|
||||
<li class="info">
|
||||
With a
|
||||
<em>
|
||||
<t t-esc="widget.model.get_discount_str()" />%
|
||||
<t t-esc="line.get_discount_str()" />%
|
||||
</em>
|
||||
discount
|
||||
</li>
|
||||
@@ -621,7 +605,7 @@
|
||||
</colgroup>
|
||||
<tr t-foreach="widget.currentOrderLines.toArray()" t-as="orderline">
|
||||
<td>
|
||||
<t t-esc="orderline.get_product().get('name')"/>
|
||||
<t t-esc="orderline.get_product().name"/>
|
||||
<t t-if="orderline.get_discount() > 0">
|
||||
<div class="pos-disc-font">
|
||||
With a <t t-esc="orderline.get_discount()"/>% discount
|
||||
|
||||
Reference in New Issue
Block a user