import logging
import time
+import uuid
from openerp import tools
from openerp.osv import fields, osv
'proxy_ip': fields.char('IP Address', help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty', size=45),
'state' : fields.selection(POS_CONFIG_STATE, 'Status', required=True, readonly=True, copy=False),
+ 'uuid' : fields.char('uuid', readonly=True, help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data'),
'sequence_id' : fields.many2one('ir.sequence', 'Order IDs Sequence', readonly=True,
help="This sequence is automatically created by Odoo but you can change it "\
"to customize the reference numbers of your orders.", copy=False),
session = record.session_ids[0]
result.append((record.id, record.name + ' ('+session.user_id.name+')')) #, '+states[session.state]+')'))
return result
+ def _generate_uuid(self, cr, uid, context=None):
+ return str(uuid.uuid1())
def _default_sale_journal(self, cr, uid, context=None):
company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
return company_id
_defaults = {
+ 'uuid' : _generate_uuid,
'state' : POS_CONFIG_STATE[0][0],
'journal_id': _default_sale_journal,
'group_by' : True,
options = options || {};
this.name = options.name || this.name;
this.limit = options.limit || this.limit;
+
+ if (options.uuid) {
+ this.name = this.name + '_' + options.uuid;
+ }
//cache the data in memory to avoid roundtrips to the localstorage
this.cache = {};
this.packagings_by_product_tmpl_id = {};
this.packagings_by_ean13 = {};
},
+
+ /*
+ * sets an uuid to prevent conflict in locally stored data between multiple databases running
+ * in the same browser at the same origin (Doing this is not advised !)
+ */
+ set_uuid: function(uuid){
+ this.name = this.name + '_' + uuid;
+ },
+
/* returns the category object from its id. If you pass a list of id as parameters, you get
* a list of category objects.
*/
}
var results = [];
for(var i = 0; i < this.limit; i++){
- r = re.exec(this.partner_search_string);
+ var r = re.exec(this.partner_search_string);
if(r){
var id = Number(r[1]);
results.push(this.get_partner_by_id(id));
}
return results;
},
+
+ /* paid orders */
add_order: function(order){
var order_id = order.uid;
var orders = this.load('orders',[]);
}
return undefined;
},
+
+ /* working orders */
+ save_unpaid_order: function(order){
+ var order_id = order.uid;
+ var orders = this.load('unpaid_orders',[]);
+ var serialized = order.export_as_JSON();
+
+ for (var i = 0; i < orders.length; i++) {
+ if (orders[i].id === order_id){
+ orders[i].data = serialized;
+ this.save('unpaid_orders',orders);
+ return order_id;
+ }
+ }
+
+ orders.push({id: order_id, data: serialized});
+ this.save('unpaid_orders',orders);
+ return order_id;
+ },
+ remove_unpaid_order: function(order){
+ var orders = this.load('unpaid_orders',[]);
+ orders = _.filter(orders, function(o){
+ return o.id !== order.uid;
+ });
+ this.save('unpaid_orders',orders);
+ },
+ remove_all_unpaid_orders: function(){
+ this.save('unpaid_orders',[]);
+ },
+ get_unpaid_orders: function(){
+ var saved = this.load('unpaid_orders',[]);
+ var orders = [];
+ for (var i = 0; i < saved.length; i++) {
+ orders.push(saved[i].data);
+ }
+ return orders;
+ },
});
}
scale_read: function(){
var self = this;
var ret = new $.Deferred();
- console.log('scale_read');
this.message('scale_read',{})
.then(function(weight){
- console.log(weight)
ret.resolve(self.use_debug_weight ? self.debug_weight : weight);
}, function(){ //failed to read weight
ret.resolve(self.use_debug_weight ? self.debug_weight : {weight:0.0, unit:'Kg', info:'ok'});
domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
loaded: function(self,pos_sessions){
self.pos_session = pos_sessions[0];
-
- var orders = self.db.get_orders();
- for (var i = 0; i < orders.length; i++) {
- self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
- }
},
},{
model: 'pos.config',
'price': self.config.barcode_price,
});
+
if (self.config.company_id[0] !== self.user.company_id[0]) {
throw new Error(_t("Error: The Point of Sale User must belong to the same company as the Point of Sale. You are probably trying to load the point of sale as an administrator in a multi-company setup, with the administrator account set to the wrong company."));
}
+
+ self.db.set_uuid(self.config.uuid);
+
+ var orders = self.db.get_orders();
+ for (var i = 0; i < orders.length; i++) {
+ self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
+ }
},
},{
model: 'stock.location',
//creates a new empty order and sets it as the current order
add_new_order: function(){
- var order = new module.Order({pos:this});
+ var order = new module.Order({},{pos:this});
this.get('orders').add(order);
this.set('selectedOrder', order);
return order;
},
+ // load the locally saved unpaid orders for this session.
+ load_orders: function(){
+ var jsons = this.db.get_unpaid_orders();
+ var orders = [];
+ var not_loaded_count = 0;
+
+ for (var i = 0; i < jsons.length; i++) {
+ var json = jsons[i];
+ if (json.pos_session_id === this.pos_session.id) {
+ orders.push(new module.Order({},{
+ pos: this,
+ json: json,
+ }));
+ } else {
+ not_loaded_count += 1;
+ }
+ }
+
+ if (not_loaded_count) {
+ console.info('There are '+not_loaded_count+' locally saved unpaid orders belonging to another session');
+ }
+
+ orders = orders.sort(function(a,b){
+ return a.sequence_number - b.sequence_number;
+ });
+
+ if (orders.length) {
+ this.get('orders').add(orders);
+ }
+ },
+
+ set_start_order: function(){
+ var orders = this.get('orders').models;
+
+ if (orders.length && !this.get('selectedOrder')) {
+ this.set('selectedOrder',orders[0]);
+ } else {
+ this.add_new_order();
+ }
+ },
// return the current order
get_order: function(){
}
if(parsed_code.type === 'price'){
- selectedOrder.addProduct(product, {price:parsed_code.value});
+ selectedOrder.add_product(product, {price:parsed_code.value});
}else if(parsed_code.type === 'weight'){
- selectedOrder.addProduct(product, {quantity:parsed_code.value, merge:false});
+ selectedOrder.add_product(product, {quantity:parsed_code.value, merge:false});
}else if(parsed_code.type === 'discount'){
- selectedOrder.addProduct(product, {discount:parsed_code.value, merge:false});
+ selectedOrder.add_product(product, {discount:parsed_code.value, merge:false});
}else{
- selectedOrder.addProduct(product);
+ selectedOrder.add_product(product);
}
return true;
},
// rounded to zero
set_quantity: function(quantity){
if(quantity === 'remove'){
- this.order.removeOrderline(this);
+ this.order.remove_orderline(this);
return;
}else{
var quant = parseFloat(quantity) || 0;
module.PaymentlineCollection = Backbone.Collection.extend({
model: module.Paymentline,
});
-
// An order more or less represents the content of a client's shopping cart (the OrderLines)
// plus the associated payment information (the Paymentlines)
initialize: function(attributes,options){
Backbone.Model.prototype.initialize.apply(this, arguments);
options = options || {};
- this.pos = attributes.pos;
+
+ this.init_locked = true;
+ this.pos = options.pos;
+ this.selected_orderline = undefined;
+ this.selected_paymentline = undefined;
+ this.screen_data = {}; // see ScreenSelector
+ this.temporary = options.temporary || false;
+ this.creation_date = new Date();
+ this.to_invoice = false;
+ this.orderlines = new module.OrderlineCollection();
+ this.paymentlines = new module.PaymentlineCollection();
+ this.pos_session_id = this.pos.pos_session.id;
+
+ this.set({ client: null });
+
if (options.json) {
this.init_from_JSON(options.json);
- return this;
+ } else {
+ this.sequence_number = this.pos.pos_session.sequence_number++;
+ this.uid = this.generate_unique_id();
+ this.name = _t("Order ") + this.uid;
}
- this.sequence_number = this.pos.pos_session.sequence_number++;
- this.uid = this.generateUniqueId();
- this.set({
- creationDate: new Date(),
- orderLines: new module.OrderlineCollection(),
- paymentLines: new module.PaymentlineCollection(),
- name: _t("Order ") + this.uid,
- client: null,
- });
- this.selected_orderline = undefined;
- this.selected_paymentline = undefined;
- this.screen_data = {}; // see ScreenSelector
- this.temporary = attributes.temporary || false;
- this.to_invoice = false;
+
+ this.on('change', this.save_to_db, this);
+ this.orderlines.on('change', this.save_to_db, this);
+ this.paymentlines.on('change', this.save_to_db, this);
+
+ this.init_locked = false;
+ this.save_to_db();
+
return this;
},
+ save_to_db: function(){
+ if (!this.init_locked) {
+ this.pos.db.save_unpaid_order(this);
+ }
+ },
init_from_JSON: function(json) {
this.sequence_number = json.sequence_number;
+ this.pos.pos_session.sequence_number = Math.max(this.sequence_number+1,this.pos.pos_session.sequence_number);
+ this.session_id = json.pos_session_id;
this.uid = json.uid;
+ this.name = _t("Order ") + this.uid;
if (json.partner_id) {
var client = this.pos.db.get_partner_by_id(json.partner_id);
if (!client) {
} else {
var client = null;
}
- this.set({
- creationDate: new Date(),
- orderLines: new module.OrderlineCollection(),
- paymentLines: new module.PaymentlineCollection(),
- name: _t("Order ") + this.uid,
- client: client,
- });
- this.selected_orderline = undefined;
- this.selected_paymentline = undefined;
- this.screen_data = {};
+ this.set_client(client);
+
this.temporary = false; // FIXME
this.to_invoice = false; // FIXME
var orderlines = json.lines;
for (var i = 0; i < orderlines.length; i++) {
var orderline = orderlines[i][2];
- this.addOrderline(new module.Orderline({}, {pos: this.pos, order: this, json: orderline}));
+ this.add_orderline(new module.Orderline({}, {pos: this.pos, order: this, json: orderline}));
}
var paymentlines = json.statement_ids;
for (var i = 0; i < paymentlines.length; i++) {
var paymentline = paymentlines[i][2];
- this.get('paymentLines').add(new module.Paymentline({},{pos: this.pos, json: paymentline}));
+ this.paymentlines.add(new module.Paymentline({},{pos: this.pos, json: paymentline}));
}
},
+ export_as_JSON: function() {
+ var orderLines, paymentLines;
+ orderLines = [];
+ this.orderlines.each(_.bind( function(item) {
+ return orderLines.push([0, 0, item.export_as_JSON()]);
+ }, this));
+ paymentLines = [];
+ this.paymentlines.each(_.bind( function(item) {
+ return paymentLines.push([0, 0, item.export_as_JSON()]);
+ }, this));
+ return {
+ name: this.get_name(),
+ amount_paid: this.get_total_paid(),
+ amount_total: this.get_total_with_tax(),
+ amount_tax: this.get_total_tax(),
+ amount_return: this.get_change(),
+ lines: orderLines,
+ statement_ids: paymentLines,
+ pos_session_id: this.pos_session_id,
+ partner_id: this.get_client() ? this.get_client().id : false,
+ user_id: this.pos.cashier ? this.pos.cashier.id : this.pos.user.id,
+ uid: this.uid,
+ sequence_number: this.sequence_number,
+ };
+ },
+ export_for_printing: function(){
+ var orderlines = [];
+ var self = this;
+
+ this.orderlines.each(function(orderline){
+ orderlines.push(orderline.export_for_printing());
+ });
+
+ var paymentlines = [];
+ this.paymentlines.each(function(paymentline){
+ paymentlines.push(paymentline.export_for_printing());
+ });
+ var client = this.get('client');
+ var cashier = this.pos.cashier || this.pos.user;
+ var company = this.pos.company;
+ var shop = this.pos.shop;
+ var date = new Date();
+
+ function is_xml(subreceipt){
+ return subreceipt ? (subreceipt.split('\n')[0].indexOf('<!DOCTYPE QWEB') >= 0) : false;
+ }
+
+ function render_xml(subreceipt){
+ if (!is_xml(subreceipt)) {
+ return subreceipt;
+ } else {
+ subreceipt = subreceipt.split('\n').slice(1).join('\n');
+ var qweb = new QWeb2.Engine();
+ qweb.debug = instance.session.debug;
+ qweb.default_dict = _.clone(QWeb.default_dict);
+ qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>');
+
+ return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.pos_widget,'order':self, 'receipt': receipt}) ;
+ }
+ }
+
+ var receipt = {
+ orderlines: orderlines,
+ paymentlines: paymentlines,
+ subtotal: this.get_subtotal(),
+ total_with_tax: this.get_total_with_tax(),
+ total_without_tax: this.get_total_without_tax(),
+ total_tax: this.get_total_tax(),
+ total_paid: this.get_total_paid(),
+ total_discount: this.get_total_discount(),
+ tax_details: this.get_tax_details(),
+ change: this.get_change(),
+ name : this.get_name(),
+ client: client ? client.name : null ,
+ invoice_id: null, //TODO
+ cashier: cashier ? cashier.name : null,
+ header: this.pos.config.receipt_header || '',
+ footer: this.pos.config.receipt_footer || '',
+ precision: {
+ price: 2,
+ money: 2,
+ quantity: 3,
+ },
+ date: {
+ year: date.getFullYear(),
+ month: date.getMonth(),
+ date: date.getDate(), // day of the month
+ day: date.getDay(), // day of the week
+ hour: date.getHours(),
+ minute: date.getMinutes() ,
+ isostring: date.toISOString(),
+ localestring: date.toLocaleString(),
+ },
+ company:{
+ email: company.email,
+ website: company.website,
+ company_registry: company.company_registry,
+ contact_address: company.partner_id[1],
+ vat: company.vat,
+ name: company.name,
+ phone: company.phone,
+ logo: this.pos.company_logo_base64,
+ },
+ shop:{
+ name: shop.name,
+ },
+ currency: this.pos.currency,
+ };
+
+ if (is_xml(this.pos.config.receipt_header)){
+ receipt.header_xml = render_xml(this.pos.config.receipt_header);
+ }
+
+ if (is_xml(this.pos.config.receipt_footer)){
+ receipt.footer_xml = render_xml(this.pos.config.receipt_footer);
+ }
+
+ return receipt;
+ },
is_empty: function(){
- return (this.get('orderLines').models.length === 0);
+ return this.orderlines.models.length === 0;
},
+ generate_unique_id: function() {
+ // Generates a public identification number for the order.
+ // The generated number must be unique and sequential. They are made 12 digit long
+ // to fit into EAN-13 barcodes, should it be needed
- // Generates a public identification number for the order.
- // The generated number must be unique and sequential. They are made 12 digit long
- // to fit into EAN-13 barcodes, should it be needed
- generateUniqueId: function() {
function zero_pad(num,size){
var s = ""+num;
while (s.length < size) {
zero_pad(this.pos.pos_session.login_number,3) +'-'+
zero_pad(this.sequence_number,4);
},
- addOrderline: function(line){
+ get_name: function() {
+ return this.name;
+ },
+ /* ---- Order Lines --- */
+ add_orderline: function(line){
if(line.order){
- line.order.removeOrderline(line);
+ line.order.remove_orderline(line);
}
line.order = this;
- this.get('orderLines').add(line);
- this.selectLine(this.getLastOrderline());
+ this.orderlines.add(line);
+ this.select_orderline(this.get_last_orderline());
},
- addProduct: function(product, options){
+ get_orderline: function(id){
+ var orderlines = this.orderlines.models;
+ for(var i = 0; i < orderlines.length; i++){
+ if(orderlines[i].id === id){
+ return orderlines[i];
+ }
+ }
+ return null;
+ },
+ get_orderlines: function(){
+ return this.orderlines.models;
+ },
+ get_last_orderline: function(){
+ return this.orderlines.at(this.orderlines.length -1);
+ },
+ remove_orderline: function( line ){
+ this.orderlines.remove(line);
+ this.select_orderline(this.get_last_orderline());
+ },
+ add_product: function(product, options){
options = options || {};
var attr = JSON.parse(JSON.stringify(product));
attr.pos = this.pos;
}
}
- var last_orderline = this.getLastOrderline();
+ var last_orderline = this.get_last_orderline();
if( last_orderline && last_orderline.can_be_merged_with(line) && options.merge !== false){
last_orderline.merge(line);
}else{
- this.get('orderLines').add(line);
+ this.orderlines.add(line);
}
- this.selectLine(this.getLastOrderline());
+ this.select_orderline(this.get_last_orderline());
},
- removeOrderline: function( line ){
- this.get('orderLines').remove(line);
- this.selectLine(this.getLastOrderline());
+ get_selected_orderline: function(){
+ return this.selected_orderline;
},
- getOrderline: function(id){
- var orderlines = this.get('orderLines').models;
- for(var i = 0; i < orderlines.length; i++){
- if(orderlines[i].id === id){
- return orderlines[i];
+ select_orderline: function(line){
+ if(line){
+ if(line !== this.selected_orderline){
+ if(this.selected_orderline){
+ this.selected_orderline.set_selected(false);
+ }
+ this.selected_orderline = line;
+ this.selected_orderline.set_selected(true);
}
+ }else{
+ this.selected_orderline = undefined;
}
- return null;
},
- getLastOrderline: function(){
- return this.get('orderLines').at(this.get('orderLines').length -1);
+ deselect_orderline: function(){
+ if(this.selected_orderline){
+ this.selected_orderline.set_selected(false);
+ this.selected_orderline = undefined;
+ }
},
- addPaymentline: function(cashregister) {
- var paymentLines = this.get('paymentLines');
- var newPaymentline = new module.Paymentline({},{cashregister:cashregister, pos:this.pos});
+ /* ---- Payment Lines --- */
+ add_paymentline: function(cashregister) {
+ var newPaymentline = new module.Paymentline({},{cashregister:cashregister, pos: this.pos});
if(cashregister.journal.type !== 'cash' || this.pos.config.iface_precompute_cash){
- newPaymentline.set_amount( Math.max(this.getDueLeft(),0) );
+ newPaymentline.set_amount( Math.max(this.get_due(),0) );
}
- paymentLines.add(newPaymentline);
- this.selectPaymentline(newPaymentline);
+ this.paymentlines.add(newPaymentline);
+ this.select_paymentline(newPaymentline);
},
- removePaymentline: function(line){
+ get_paymentlines: function(){
+ return this.paymentlines.models;
+ },
+ remove_paymentline: function(line){
if(this.selected_paymentline === line){
- this.selectPaymentline(undefined);
+ this.select_paymentline(undefined);
}
- this.get('paymentLines').remove(line);
+ this.paymentlines.remove(line);
},
- getName: function() {
- return this.get('name');
+ clean_empty_paymentlines: function() {
+ var lines = this.paymentlines.models;
+ var empty = [];
+ for ( var i = 0; i < lines.length; i++) {
+ if (!lines[i].get_amount()) {
+ empty.push(lines[i]);
+ }
+ }
+ for ( var i = 0; i < empty.length; i++) {
+ this.remove_paymentline(empty[i]);
+ }
},
- getSubtotal : function(){
- return (this.get('orderLines')).reduce((function(sum, orderLine){
+ select_paymentline: function(line){
+ if(line !== this.selected_paymentline){
+ if(this.selected_paymentline){
+ this.selected_paymentline.set_selected(false);
+ }
+ this.selected_paymentline = line;
+ if(this.selected_paymentline){
+ this.selected_paymentline.set_selected(true);
+ }
+ this.trigger('change:selected_paymentline',this.selected_paymentline);
+ }
+ },
+ /* ---- Payment Status --- */
+ get_subtotal : function(){
+ return this.orderlines.reduce((function(sum, orderLine){
return sum + orderLine.get_display_price();
}), 0);
},
- getTotalTaxIncluded: function() {
- return (this.get('orderLines')).reduce((function(sum, orderLine) {
+ get_total_with_tax: function() {
+ return this.orderlines.reduce((function(sum, orderLine) {
return sum + orderLine.get_price_with_tax();
}), 0);
},
- getDiscountTotal: function() {
- return (this.get('orderLines')).reduce((function(sum, orderLine) {
- return sum + (orderLine.get_unit_price() * (orderLine.get_discount()/100) * orderLine.get_quantity());
+ get_total_without_tax: function() {
+ return this.orderlines.reduce((function(sum, orderLine) {
+ return sum + orderLine.get_price_without_tax();
}), 0);
},
- getTotalTaxExcluded: function() {
- return (this.get('orderLines')).reduce((function(sum, orderLine) {
- return sum + orderLine.get_price_without_tax();
+ get_total_discount: function() {
+ return this.orderlines.reduce((function(sum, orderLine) {
+ return sum + (orderLine.get_unit_price() * (orderLine.get_discount()/100) * orderLine.get_quantity());
}), 0);
},
- getTax: function() {
- return (this.get('orderLines')).reduce((function(sum, orderLine) {
+ get_total_tax: function() {
+ return this.orderlines.reduce((function(sum, orderLine) {
return sum + orderLine.get_tax();
}), 0);
},
- getTaxDetails: function(){
+ get_total_paid: function() {
+ return this.paymentlines.reduce((function(sum, paymentLine) {
+ return sum + paymentLine.get_amount();
+ }), 0);
+ },
+ get_tax_details: function(){
var details = {};
var fulldetails = [];
var taxes_by_id = {};
taxes_by_id[this.pos.taxes[i].id] = this.pos.taxes[i];
}
- this.get('orderLines').each(function(line){
+ this.orderlines.each(function(line){
var ldetails = line.get_tax_details();
for(var id in ldetails){
if(ldetails.hasOwnProperty(id)){
return fulldetails;
},
- getPaidTotal: function() {
- return (this.get('paymentLines')).reduce((function(sum, paymentLine) {
- return sum + paymentLine.get_amount();
- }), 0);
- },
- getChange: function(paymentline) {
+ get_change: function(paymentline) {
if (!paymentline) {
- var change = this.getPaidTotal() - this.getTotalTaxIncluded();
+ var change = this.get_total_paid() - this.get_total_with_tax();
} else {
- var change = -this.getTotalTaxIncluded();
- var lines = this.get('paymentLines').models;
+ var change = -this.get_total_with_tax();
+ var lines = this.paymentlines.models;
for (var i = 0; i < lines.length; i++) {
change += lines[i].get_amount();
if (lines[i] === paymentline) {
}
return round_pr(Math.max(0,change), this.pos.currency.rounding);
},
- getDueLeft: function(paymentline) {
+ get_due: function(paymentline) {
if (!paymentline) {
- var due = this.getTotalTaxIncluded() - this.getPaidTotal();
+ var due = this.get_total_with_tax() - this.get_total_paid();
} else {
- var due = this.getTotalTaxIncluded();
- var lines = this.get('paymentLines').models;
+ var due = this.get_total_with_tax();
+ var lines = this.paymentlines.models;
for (var i = 0; i < lines.length; i++) {
if (lines[i] === paymentline) {
break;
}
return round_pr(Math.max(0,due), this.pos.currency.rounding);
},
- isPaid: function(){
- return this.getDueLeft() === 0;
+ is_paid: function(){
+ return this.get_due() === 0;
},
- isPaidWithCash: function(){
- return !!this.get('paymentLines').find( function(pl){
+ is_paid_with_cash: function(){
+ return !!this.paymentlines.find( function(pl){
return pl.cashregister.journal.type === 'cash';
});
},
finalize: function(){
this.destroy();
},
+ destroy: function(args){
+ Backbone.Model.prototype.destroy.apply(this,arguments);
+ this.pos.db.remove_unpaid_order(this);
+ },
+ /* ---- Invoice --- */
+ set_to_invoice: function(to_invoice) {
+ this.to_invoice = to_invoice;
+ },
+ is_to_invoice: function(){
+ return this.to_invoice;
+ },
+ /* ---- Client / Customer --- */
// the client related to the current order.
set_client: function(client){
this.set('client',client);
var client = this.get('client');
return client ? client.name : "";
},
+ /* ---- Screen Status --- */
// the order also stores the screen status, as the PoS supports
// different active screens per order. This method is used to
// store the screen status.
}
}
},
- set_to_invoice: function(to_invoice) {
- this.to_invoice = to_invoice;
- },
- is_to_invoice: function(){
- return this.to_invoice;
- },
- // remove all the paymentlines with zero money in it
- clean_empty_paymentlines: function() {
- var lines = this.get('paymentLines').models;
- var empty = [];
- for ( var i = 0; i < lines.length; i++) {
- if (!lines[i].get_amount()) {
- empty.push(lines[i]);
- }
- }
- for ( var i = 0; i < empty.length; i++) {
- this.removePaymentline(empty[i]);
- }
- },
//see set_screen_data
get_screen_data: function(key){
return this.screen_data[key];
},
- // exports a JSON for receipt printing
- export_for_printing: function(){
- var orderlines = [];
- var self = this;
-
- this.get('orderLines').each(function(orderline){
- orderlines.push(orderline.export_for_printing());
- });
-
- var paymentlines = [];
- this.get('paymentLines').each(function(paymentline){
- paymentlines.push(paymentline.export_for_printing());
- });
- var client = this.get('client');
- var cashier = this.pos.cashier || this.pos.user;
- var company = this.pos.company;
- var shop = this.pos.shop;
- var date = new Date();
-
- function is_xml(subreceipt){
- return subreceipt ? (subreceipt.split('\n')[0].indexOf('<!DOCTYPE QWEB') >= 0) : false;
- }
-
- function render_xml(subreceipt){
- if (!is_xml(subreceipt)) {
- return subreceipt;
- } else {
- subreceipt = subreceipt.split('\n').slice(1).join('\n');
- var qweb = new QWeb2.Engine();
- qweb.debug = instance.session.debug;
- qweb.default_dict = _.clone(QWeb.default_dict);
- qweb.add_template('<templates><t t-name="subreceipt">'+subreceipt+'</t></templates>');
-
- return qweb.render('subreceipt',{'pos':self.pos,'widget':self.pos.pos_widget,'order':self, 'receipt': receipt}) ;
- }
- }
-
- var receipt = {
- orderlines: orderlines,
- paymentlines: paymentlines,
- subtotal: this.getSubtotal(),
- total_with_tax: this.getTotalTaxIncluded(),
- total_without_tax: this.getTotalTaxExcluded(),
- total_tax: this.getTax(),
- total_paid: this.getPaidTotal(),
- total_discount: this.getDiscountTotal(),
- tax_details: this.getTaxDetails(),
- change: this.getChange(),
- name : this.getName(),
- client: client ? client.name : null ,
- invoice_id: null, //TODO
- cashier: cashier ? cashier.name : null,
- header: this.pos.config.receipt_header || '',
- footer: this.pos.config.receipt_footer || '',
- precision: {
- price: 2,
- money: 2,
- quantity: 3,
- },
- date: {
- year: date.getFullYear(),
- month: date.getMonth(),
- date: date.getDate(), // day of the month
- day: date.getDay(), // day of the week
- hour: date.getHours(),
- minute: date.getMinutes() ,
- isostring: date.toISOString(),
- localestring: date.toLocaleString(),
- },
- company:{
- email: company.email,
- website: company.website,
- company_registry: company.company_registry,
- contact_address: company.partner_id[1],
- vat: company.vat,
- name: company.name,
- phone: company.phone,
- logo: this.pos.company_logo_base64,
- },
- shop:{
- name: shop.name,
- },
- currency: this.pos.currency,
- };
-
- if (is_xml(this.pos.config.receipt_header)){
- receipt.header_xml = render_xml(this.pos.config.receipt_header);
- }
-
- if (is_xml(this.pos.config.receipt_footer)){
- receipt.footer_xml = render_xml(this.pos.config.receipt_footer);
- }
-
- return receipt;
- },
- export_as_JSON: function() {
- var orderLines, paymentLines;
- orderLines = [];
- (this.get('orderLines')).each(_.bind( function(item) {
- return orderLines.push([0, 0, item.export_as_JSON()]);
- }, this));
- paymentLines = [];
- (this.get('paymentLines')).each(_.bind( function(item) {
- return paymentLines.push([0, 0, item.export_as_JSON()]);
- }, this));
- return {
- name: this.getName(),
- amount_paid: this.getPaidTotal(),
- amount_total: this.getTotalTaxIncluded(),
- amount_tax: this.getTax(),
- amount_return: this.getChange(),
- lines: orderLines,
- statement_ids: paymentLines,
- pos_session_id: this.pos.pos_session.id,
- partner_id: this.get_client() ? this.get_client().id : false,
- user_id: this.pos.cashier ? this.pos.cashier.id : this.pos.user.id,
- uid: this.uid,
- sequence_number: this.sequence_number,
- };
- },
- getSelectedLine: function(){
- return this.selected_orderline;
- },
- selectLine: function(line){
- if(line){
- if(line !== this.selected_orderline){
- if(this.selected_orderline){
- this.selected_orderline.set_selected(false);
- }
- this.selected_orderline = line;
- this.selected_orderline.set_selected(true);
- }
- }else{
- this.selected_orderline = undefined;
- }
- },
- deselectLine: function(){
- if(this.selected_orderline){
- this.selected_orderline.set_selected(false);
- this.selected_orderline = undefined;
- }
- },
- selectPaymentline: function(line){
- if(line !== this.selected_paymentline){
- if(this.selected_paymentline){
- this.selected_paymentline.set_selected(false);
- }
- this.selected_paymentline = line;
- if(this.selected_paymentline){
- this.selected_paymentline.set_selected(true);
- }
- this.trigger('change:selected_paymentline',this.selected_paymentline);
- }
- },
});
module.OrderCollection = Backbone.Collection.extend({
// what happens when a discount barcode is scanned : the default behavior
// is to set the discount on the last order.
barcode_discount_action: function(code){
- var last_orderline = this.pos.get_order().getLastOrderline();
+ var last_orderline = this.pos.get_order().get_last_orderline();
if(last_orderline){
last_orderline.set_discount(code.value)
}
module.ConfirmPopupWidget = module.PopUpWidget.extend({
template: 'ConfirmPopupWidget',
show: function(options){
+ options = options || {};
var self = this;
this._super();
template: 'ErrorInvoiceTransferPopupWidget',
});
- module.UnsentOrdersPopupWidget = module.PopUpWidget.extend({
+ module.UnsentOrdersPopupWidget = module.ConfirmPopupWidget.extend({
template: 'UnsentOrdersPopupWidget',
- show: function(options){
- var self = this;
- this._super(options);
- this.renderElement();
- this.$('.button.confirm').click(function(){
- self.pos_widget.screen_selector.close_popup();
- });
- },
+ });
+
+ module.UnpaidOrdersPopupWidget = module.ConfirmPopupWidget.extend({
+ template: 'UnpaidOrdersPopupWidget',
});
module.ScaleScreenWidget = module.ScreenWidget.extend({
}
},
order_product: function(){
- this.pos.get_order().addProduct(this.get_product(),{ quantity: this.weight });
+ this.pos.get_order().add_product(this.get_product(),{ quantity: this.weight });
},
get_product_name: function(){
var product = this.get_product();
if(product.to_weight && self.pos.config.iface_electronic_scale){
self.pos_widget.screen_selector.set_current_screen('scale',{product: product});
}else{
- self.pos.get_order().addProduct(product);
+ self.pos.get_order().add_product(product);
}
},
product_list: this.pos.db.get_product_by_category(0)
widget:this,
order: order,
receipt: order.export_for_printing(),
- orderlines: order.get('orderLines').models,
- paymentlines: order.get('paymentLines').models,
+ orderlines: order.get_orderlines(),
+ paymentlines: order.get_paymentlines(),
}));
},
});
return numpad;
},
click_delete_paymentline: function(cid){
- var lines = this.pos.get_order().get('paymentLines').models;
+ var lines = this.pos.get_order().get_paymentlines();
for ( var i = 0; i < lines.length; i++ ) {
if (lines[i].cid === cid) {
- this.pos.get_order().removePaymentline(lines[i]);
+ this.pos.get_order().remove_paymentline(lines[i]);
this.reset_input();
this.render_paymentlines();
return;
}
},
click_paymentline: function(cid){
- var lines = this.pos.get_order().get('paymentLines').models;
+ var lines = this.pos.get_order().get_paymentlines();
for ( var i = 0; i < lines.length; i++ ) {
if (lines[i].cid === cid) {
- this.pos.get_order().selectPaymentline(lines[i]);
+ this.pos.get_order().select_paymentline(lines[i]);
this.reset_input();
this.render_paymentlines();
return;
return;
}
- var lines = order.get('paymentLines').models;
+ var lines = order.get_paymentlines();
this.$('.paymentlines-container').empty();
var lines = $(QWeb.render('PaymentScreen-Paymentlines', {
break;
}
}
- this.pos.get_order().addPaymentline( cashregister );
+ this.pos.get_order().add_paymentline( cashregister );
this.reset_input();
this.render_paymentlines();
},
var order = this.pos.get_order();
if (!order) {
return;
- } else if (order.isPaid()) {
+ } else if (order.is_paid()) {
self.$('.next').addClass('highlight');
}else{
self.$('.next').removeClass('highlight');
// FIXME: this check is there because the backend is unable to
// process empty orders. This is not the right place to fix it.
- if(order.get_orderlines().length === 0){
+ if (order.get_orderlines().length === 0) {
this.pos_widget.screen_selector.show_popup('error',{
'message': _t('Empty Order'),
'comment': _t('There must be at least one product in your order before it can be validated'),
return;
}
- if (!order.isPaid() || this.invoicing) {
+ if (!order.is_paid() || this.invoicing) {
return;
}
// The exact amount must be paid if there is no cash payment method defined.
- if (Math.abs(order.getTotalTaxIncluded() - order.getPaidTotal()) > 0.00001) {
+ if (Math.abs(order.get_total_with_tax() - order.get_total_paid()) > 0.00001) {
var cash = false;
for (var i = 0; i < this.pos.cashregisters.length; i++) {
cash = cash || (this.pos.cashregisters[i].journal.type === 'cash');
}
}
- if (order.isPaidWithCash() && this.pos.config.iface_cashdrawer) {
+ if (order.is_paid_with_cash() && this.pos.config.iface_cashdrawer) {
+
this.pos.proxy.open_cashbox();
}
if(!self.editable){
return;
}
- self.pos.get_order().selectLine(this.orderline);
+ self.pos.get_order().select_orderline(this.orderline);
self.pos_widget.numpad.state.reset();
};
this.client_change_handler = function(event){
this.enable_numpad();
}else{
this.disable_numpad();
- order.deselectLine();
+ order.deselect_orderline();
}
}
},
set_value: function(val) {
var order = this.pos.get_order();
- if (this.editable && order.getSelectedLine()) {
+ if (this.editable && order.get_selected_orderline()) {
var mode = this.numpad_state.get('mode');
if( mode === 'quantity'){
- order.getSelectedLine().set_quantity(val);
+ order.get_selected_orderline().set_quantity(val);
}else if( mode === 'discount'){
- order.getSelectedLine().set_discount(val);
+ order.get_selected_orderline().set_discount(val);
}else if( mode === 'price'){
- order.getSelectedLine().set_unit_price(val);
+ order.get_selected_orderline().set_unit_price(val);
}
}
},
this.renderElement();
}
},
+ orderline_add: function(){
+ this.numpad_state.reset();
+ this.renderElement('and_scroll_to_bottom');
+ },
+ orderline_remove: function(line){
+ this.remove_orderline(line);
+ this.numpad_state.reset();
+ this.update_summary();
+ },
+ orderline_change: function(line){
+ this.rerender_orderline(line);
+ this.update_summary();
+ },
bind_order_events: function() {
-
var order = this.pos.get_order();
order.unbind('change:client', this.client_change_handler);
order.bind('change:client', this.client_change_handler);
- var lines = order.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);
+ var lines = order.orderlines;
+ lines.unbind('add', this.orderline_add, this);
+ lines.bind('add', this.orderline_add, this);
+ lines.unbind('remove', this.orderline_remove, this);
+ lines.bind('remove', this.orderline_remove, this);
+ lines.unbind('change', this.orderline_change, this);
+ lines.bind('change', this.orderline_change, this);
+
},
render_orderline: function(orderline){
var el_str = openerp.qweb.render('Orderline',{widget:this, line:orderline});
return el_node;
},
remove_orderline: function(order_line){
- if(this.pos.get_order().get('orderLines').length === 0){
+ if(this.pos.get_order().get_orderlines().length === 0){
this.renderElement();
}else{
order_line.node.parentNode.removeChild(order_line.node);
if (!order) {
return;
}
- var orderlines = order.get('orderLines').models;
+ var orderlines = order.get_orderlines();
var el_str = openerp.qweb.render('OrderWidget',{widget:this, order:order, orderlines:orderlines});
},
update_summary: function(){
var order = this.pos.get_order();
- var total = order ? order.getTotalTaxIncluded() : 0;
- var taxes = order ? total - order.getTotalTaxExcluded() : 0;
+ var total = order ? order.get_total_with_tax() : 0;
+ var taxes = order ? total - order.get_total_without_tax() : 0;
this.el.querySelector('.summary .total > .value').textContent = this.format_currency(total);
this.el.querySelector('.summary .total .subentry .value').textContent = this.format_currency(taxes);
if(query){
var products = this.pos.db.search_product_in_category(category.id,query)
if(buy_result && products.length === 1){
- this.pos.get_order().addProduct(products[0]);
+ this.pos.get_order().add_product(products[0]);
this.clear_search();
}else{
this.product_list_widget.set_product_list(products);
},
});
});
+ this.$('.button.show_unpaid_orders').click(function(){
+ self.pos.pos_widget.screen_selector.show_popup('unpaid-orders');
+ });
+ this.$('.button.delete_unpaid_orders').click(function(){
+ self.pos.pos_widget.screen_selector.show_popup('confirm',{
+ message: _t('Delete Unpaid Orders ?'),
+ comment: _t('This operation will permanently destroy all unpaid orders from all sessions that have been put in the local storage. You will lose all the data and exit the point of sale. This operation cannot be undone.'),
+ confirm: function(){
+ self.pos.db.remove_all_unpaid_orders();
+ window.location = '/';
+ },
+ });
+ });
_.each(this.eans, function(ean, name){
self.$('.button.'+name).click(function(){
self.$('input.ean').val(ean);
self.renderElement();
- self.pos.add_new_order();
+ self.pos.load_orders();
+ self.pos.set_start_order();
self.build_widgets();
this.unsent_orders_popup = new module.UnsentOrdersPopupWidget(this,{});
this.unsent_orders_popup.appendTo(this.$el);
+ this.unpaid_orders_popup = new module.UnpaidOrdersPopupWidget(this,{});
+ this.unpaid_orders_popup.appendTo(this.$el);
+
// -------- Misc ---------
this.order_selector = new module.OrderSelectorWidget(this,{});
'fullscreen': this.fullscreen_popup,
'selection': this.selection_popup,
'unsent-orders': this.unsent_orders_popup,
+ 'unpaid-orders': this.unpaid_orders_popup,
},
default_screen: 'products',
default_mode: 'cashier',
}
var draft_order = _.find( self.pos.get('orders').models, function(order){
- return order.get('orderLines').length !== 0 && order.get('paymentLines').length === 0;
+ return order.get_orderlines().length !== 0 && order.get_paymentlines().length === 0;
});
if(draft_order){
if (confirm(_t("Pending orders will be lost.\nAre you sure you want to leave this session?"))) {
<t t-if="!paymentlines.length">
<div class='paymentlines-empty'>
<div class='total'>
- <t t-esc="widget.format_currency(order.getTotalTaxIncluded())"/>
+ <t t-esc="widget.format_currency(order.get_total_with_tax())"/>
</div>
<div class='message'>
Please select a payment method.
<t t-foreach='paymentlines' t-as='line'>
<t t-if='line.selected'>
<tr class='paymentline selected'>
- <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
+ <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.get_due(line))' /> </td>
<td class='col-tendered edit'>
<t t-esc='widget.inputbuffer' />
<!-- <t t-esc='line.get_amount()' /> -->
</td>
- <t t-if='order.getChange(line)'>
+ <t t-if='order.get_change(line)'>
<td class='col-change highlight' >
- <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' />
+ <t t-esc='widget.format_currency_no_symbol(order.get_change(line))' />
</td>
</t>
- <t t-if='!order.getChange(line)'>
+ <t t-if='!order.get_change(line)'>
<td class='col-change' ></td>
</t>
</t>
<t t-if='!line.selected'>
<tr class='paymentline' t-att-data-cid='line.cid'>
- <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.getDueLeft(line))' /> </td>
+ <td class='col-due'> <t t-esc='widget.format_currency_no_symbol(order.get_due(line))' /> </td>
<td class='col-tendered'> <t t-esc='widget.format_currency_no_symbol(line.get_amount())' /> </td>
<td class='col-change'>
- <t t-if='order.getChange(line)'>
- <t t-esc='widget.format_currency_no_symbol(order.getChange(line))' />
+ <t t-if='order.get_change(line)'>
+ <t t-esc='widget.format_currency_no_symbol(order.get_change(line))' />
</t>
</td>
<td class='col-name'> <t t-esc='line.name' /> </td>
</div>
</t>
+ <t t-name="UnpaidOrdersPopupWidget">
+ <div class="modal-dialog">
+ <div class="popup popup-unpaid-orders">
+ <p class="message">Unpaid Orders</p>
+ <t t-if='widget.pos.db.get_unpaid_orders().length === 0'>
+ <p class='comment'>
+ There are no unpaid orders
+ </p>
+ </t>
+ <t t-if='widget.pos.db.get_unpaid_orders().length > 0'>
+ <p class='comment traceback'>
+ <t t-esc='JSON.stringify(widget.pos.db.get_unpaid_orders(),null,2)' />
+ </p>
+ </t>
+ <div class="footer">
+ <div class="button confirm">
+ Ok
+ </div>
+ </div>
+ </div>
+ </div>
+ </t>
+
<t t-name="SelectionPopupWidget">
<div class="modal-dialog">
<div class="popup popup-selection">
<li class="button show_orders">Show All Unsent Orders</li>
<li class="button delete_orders">Delete All Unsent Orders</li>
</ul>
+ <p class="category">Unpaid Orders</p>
+ <ul>
+ <li class="button show_unpaid_orders">Show All Unpaid Orders</li>
+ <li class="button delete_unpaid_orders">Delete All Unpaid Orders</li>
+ </ul>
<p class="category">Hardware Status</p>
<ul>
<t t-if="order.get_client()">
<i class='fa fa-user'/>
</t>
- <t t-esc="(order.get_client() ? order.get_client_name()+' : ':'') + order.get('creationDate').toString('t')"/>
+ <t t-esc="(order.get_client() ? order.get_client_name()+' : ':'') + order.creation_date.toString('t')"/>
</span>
</t>
<t t-if='order !== widget.pos.get_order()'>
<t t-name="PosTicket">
<div class="pos-sale-ticket">
- <div class="pos-center-align"><t t-esc="moment().format('L LT')"/> <t t-esc="order.get('name')"/></div>
+ <div class="pos-center-align"><t t-esc="moment().format('L LT')"/> <t t-esc="order.name"/></div>
<br />
<t t-esc="widget.pos.company.name"/><br />
Phone: <t t-esc="widget.pos.company.phone || ''"/><br />
<tr>
<td>Subtotal:</td>
<td class="pos-right-align">
- <t t-esc="widget.format_currency(order.getSubtotal())"/>
+ <t t-esc="widget.format_currency(order.get_subtotal())"/>
</td>
</tr>
- <t t-foreach="order.getTaxDetails()" t-as="taxdetail">
+ <t t-foreach="order.get_tax_details()" t-as="taxdetail">
<tr>
<td><t t-esc="taxdetail.name" /></td>
<td class="pos-right-align">
<tr>
<td>Discount:</td>
<td class="pos-right-align">
- <t t-esc="widget.format_currency(order.getDiscountTotal())"/>
+ <t t-esc="widget.format_currency(order.get_total_discount())"/>
</td>
</tr>
<tr class="emph">
<td>Total:</td>
<td class="pos-right-align">
- <t t-esc="widget.format_currency(order.getTotalTaxIncluded())"/>
+ <t t-esc="widget.format_currency(order.get_total_with_tax())"/>
</td>
</tr>
</table>
<br />
<table>
<tr><td>Change:</td><td class="pos-right-align">
- <t t-esc="widget.format_currency(order.getChange())"/>
+ <t t-esc="widget.format_currency(order.get_change())"/>
</td></tr>
</table>
<t t-if="receipt.footer">
var discount = $(QWeb.render('DiscountButton'));
discount.click(function(){
- var order = self.pos.get('selectedOrder');
+ var order = self.pos.get_order();
var product = self.pos.db.get_product_by_id(self.pos.config.discount_product_id[0]);
- var discount = - self.pos.config.discount_pc/ 100.0 * order.getTotalTaxIncluded();
+ var discount = - self.pos.config.discount_pc/ 100.0 * order.get_total_tax_included();
if( discount < 0 ){
- order.addProduct(product, { price: discount });
+ order.add_product(product, { price: discount });
}
});
domain: function(self){ return [['loyalty_program_id','=',self.loyalty.id]]; },
loaded: function(self,rewards){
self.loyalty.rewards = rewards;
+ self.loyalty.rewards_by_id = {};
+ for (var i = 0; i < rewards.length;i++) {
+ self.loyalty.rewards_by_id[rewards[i].id] = rewards[i];
+ }
},
});
}
}
+ var _super_orderline = module.Orderline;
+ module.Orderline = module.Orderline.extend({
+ get_reward: function(){
+ return this.pos.loyalty.rewards_by_id[this.reward_id];
+ },
+ set_reward: function(reward){
+ this.reward_id = reward.id;
+ },
+ export_as_JSON: function(){
+ var json = _super_orderline.prototype.export_as_JSON.apply(this,arguments);
+ json.reward_id = this.reward_id;
+ return json;
+ },
+ init_from_JSON: function(json){
+ _super_orderline.prototype.init_from_JSON.apply(this,arguments);
+ this.reward_id = json.reward_id;
+ },
+ });
+
var _super = module.Order;
module.Order = module.Order.extend({
/* The total of points won, excluding the points spent on rewards */
get_won_points: function(){
- if (!this.pos.loyalty || !this.get('client')) {
+ if (!this.pos.loyalty || !this.get_client()) {
return 0;
}
- var orderLines = this.get('orderLines').models;
+ var orderLines = this.get_orderlines();
var rounding = this.pos.loyalty.rounding;
var product_sold = 0;
var rules = this.pos.loyalty.rules_by_product_id[product.id] || [];
var overriden = false;
- if (line.reward) { // Reward products are ignored
+ if (line.get_reward()) { // Reward products are ignored
continue;
}
break;
}
}
+ var _category = category;
category = this.pos.db.get_category_by_id(this.pos.db.get_category_parent_id(category.id));
+ if (_category === category) {
+ break;
+ }
}
}
/* The total number of points spent on rewards */
get_spent_points: function() {
- if (!this.pos.loyalty || !this.get('client')) {
+ if (!this.pos.loyalty || !this.get_client()) {
return 0;
} else {
- var lines = this.get('orderLines').models;
+ var lines = this.get_orderlines();
var rounding = this.pos.loyalty.rounding;
var points = 0;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
- if (line.reward) {
- if (line.reward.type === 'gift') {
- points += round_pr(line.get_quantity() * line.reward.point_cost, rounding);
- } else if (line.reward.type === 'discount') {
- points += round_pr(-line.get_price_with_tax() * line.reward.point_cost, rounding);
+ var reward = line.get_reward();
+ if (reward) {
+ if (reward.type === 'gift') {
+ points += round_pr(line.get_quantity() * reward.point_cost, rounding);
+ } else if (reward.type === 'discount') {
+ points += round_pr(-line.get_price_with_tax() * reward.point_cost, rounding);
}
}
}
/* The total number of points lost or won after the order is validated */
get_new_points: function() {
- if (!this.pos.loyalty || !this.get('client')) {
+ if (!this.pos.loyalty || !this.get_client()) {
return 0;
} else {
return round_pr(this.get_won_points() - this.get_spent_points(), this.pos.loyalty.rounding);
/* The total number of points that the customer will have after this order is validated */
get_new_total_points: function() {
- if (!this.pos.loyalty || !this.get('client')) {
+ if (!this.pos.loyalty || !this.get_client()) {
return 0;
} else {
- return round_pr(this.get('client').loyalty_points + this.get_new_points(), this.pos.loyalty.rounding);
+ return round_pr(this.get_client().loyalty_points + this.get_new_points(), this.pos.loyalty.rounding);
}
},
/* The number of loyalty points currently owned by the customer */
get_current_points: function(){
- return this.get('client') ? this.get('client').loyalty_points : 0;
+ return this.get_client() ? this.get_client().loyalty_points : 0;
},
/* The total number of points spendable on rewards */
get_spendable_points: function(){
- if (!this.pos.loyalty || !this.get('client')) {
+ if (!this.pos.loyalty || !this.get_client()) {
return 0;
} else {
- return round_pr(this.get('client').loyalty_points - this.get_spent_points(), this.pos.loyalty.rounding);
+ return round_pr(this.get_client().loyalty_points - this.get_spent_points(), this.pos.loyalty.rounding);
}
},
/* The list of rewards that the current customer can get */
get_available_rewards: function(){
- var client = this.get('client');
+ var client = this.get_client();
if (!client) {
return [];
}
},
apply_reward: function(reward){
- var client = this.get('client');
+ var client = this.get_client();
if (!client) {
return;
} else if (reward.type === 'gift') {
return;
}
- var line = this.addProduct(product, {
+ var line = this.add_product(product, {
price: 0,
quantity: 1,
merge: false,
- extras: { reward: reward },
+ extras: { reward_id: reward.id },
});
} else if (reward.type === 'discount') {
var lrounding = this.pos.loyalty.rounding;
var crounding = this.pos.currency.rounding;
var spendable = this.get_spendable_points();
- var order_total = this.getTotalTaxIncluded();
+ var order_total = this.get_total_with_tax();
var discount = round_pr(order_total * reward.discount,crounding);
if ( round_pr(discount * reward.point_cost,lrounding) > spendable ) {
return;
}
- var line = this.addProduct(product, {
+ var line = this.add_product(product, {
price: -discount,
quantity: 1,
merge: false,
- extras: { reward: reward },
+ extras: { reward_id: reward.id },
});
}
},
validate: function(){
- var client = this.get('client');
+ var client = this.get_client();
if ( client ) {
client.loyalty_points = this.get_new_total_points();
}
module.PosWidget.include({
loyalty_reward_click: function(){
var self = this;
- var order = this.pos.get('selectedOrder');
- var client = order.get('client');
+ var order = this.pos.get_order();
+ var client = order.get_client();
if (!client) {
this.screen_selector.set_current_screen('clientlist');
return;
self.floors_by_id[floors[i].id] = floors[i];
}
// Ignore floorplan features if no floor specified, or feature deactivated
- self.config.iface_floorplan = self.config.iface_floorplan && self.floors.length;
+ self.config.iface_floorplan = self.config.iface_floorplan && !!self.floors.length;
},
});
model: 'restaurant.table',
fields: ['name','width','height','position_h','position_v','shape','floor_id','color'],
loaded: function(self,tables){
+ self.tables_by_id = {};
for (var i = 0; i < tables.length; i++) {
+ self.tables_by_id[tables[i].id] = tables[i];
var floor = self.floors_by_id[tables[i].floor_id[0]];
if (floor) {
floor.tables.push(tables[i]);
if (this.editing) {
this.toggle_editing();
}
+ this.pos_widget.order_selector.show();
},
show: function(){
this._super();
+ this.pos_widget.order_selector.hide();
for (var i = 0; i < this.table_widgets.length; i++) {
this.table_widgets[i].renderElement();
}
var _super_order = module.Order.prototype;
module.Order = module.Order.extend({
initialize: function(attr) {
- _super_order.initialize.call(this,attr);
- this.table = this.pos.table;
+ _super_order.initialize.apply(this,arguments);
+ if (!this.table) {
+ this.table = this.pos.table;
+ }
+ this.save_to_db();
},
export_as_JSON: function() {
var json = _super_order.export_as_JSON.apply(this,arguments);
- json.table = this.table.name;
- json.floor = this.table ? this.table.floor.name : false;
+ json.table = this.table ? this.table.name : undefined;
+ json.table_id = this.table ? this.table.id : false;
+ json.floor = this.table ? this.table.floor.name : false;
+ json.floor_id = this.table ? this.table.floor.id : false;
return json;
},
+ init_from_JSON: function(json) {
+ _super_order.init_from_JSON.apply(this,arguments);
+ this.table = this.pos.tables_by_id[json.table_id];
+ this.floor = this.table ? this.pos.floors_by_id[json.floor_id] : undefined;
+ },
});
// We need to modify the OrderSelector to hide itself when we're on
floor_button_click_handler: function(){
this.pos.set_table(null);
},
+ hide: function(){
+ this.$el.addClass('oe_invisible');
+ },
+ show: function(){
+ this.$el.removeClass('oe_invisible');
+ },
renderElement: function(){
var self = this;
this._super();
}
},
+ // if we have tables, we do not load a default order, as the default order will be
+ // set when the user selects a table.
+ set_start_order: function() {
+ if (!this.config.iface_floorplan) {
+ _super_posmodel.set_start_order.apply(this,arguments);
+ }
+ },
+
// we need to prevent the creation of orders when there is no
// table selected.
add_new_order: function() {
}
},
+
// get the list of unpaid orders (associated to the current table)
get_order_list: function() {
var orders = _super_posmodel.get_order_list.call(this);
- if (!this.table || !this.config.iface_floorplan) {
+ if (!this.config.iface_floorplan) {
return orders;
+ } else if (!this.table) {
+ return [];
} else {
var t_orders = [];
for (var i = 0; i < orders.length; i++) {
},
});
+ module.Orderline = module.Orderline.extend({
+ get_line_diff_hash: function(){
+ if (this.get_note()) {
+ return this.get_product().id + '|' + this.get_note();
+ } else {
+ return '' + this.get_product().id;
+ }
+ },
+ });
+
+ var _super_order = module.Order.prototype;
module.Order = module.Order.extend({
lineResume: function(){
var resume = {};
- this.get('orderLines').each(function(item){
- var line = item.export_as_JSON();
- if( typeof resume[line.product_id] === 'undefined'){
- resume[line.product_id] = line.qty;
- }else{
- resume[line.product_id] += line.qty;
+
+ this.orderlines.each(function(line){
+ var line_hash = line.get_line_diff_hash();
+ var qty = Number(line.get_quantity());
+ var note = line.get_note();
+ var product_id = line.get_product().id;
+
+ if (typeof resume[line_hash] === 'undefined') {
+ resume[line_hash] = { qty: qty, note: note, product_id: product_id };
+ } else {
+ resume[line_hash].qty += qty;
}
});
return resume;
},
saveChanges: function(){
- this.old_resume = this.lineResume();
+ this.saved_resume = this.build_line_resume();
+ this.trigger('change',this);
},
computeChanges: function(categories){
- var current = this.lineResume();
- var old = this.old_resume || {};
- var json = this.export_as_JSON();
+ var current_res = this.build_line_resume();
+ var old_res = this.saved_resume || {};
+ var json = this.export_as_JSON();
var add = [];
var rem = [];
}
return false;
},
+ export_as_JSON: function(){
+ var json = _super_order.export_as_JSON.apply(this,arguments);
+ json.multiprint_resume = this.saved_resume;
+ return json;
+ },
+ init_from_JSON: function(json){
+ _super_order.init_from_JSON.apply(this,arguments);
+ this.saved_resume = json.multiprint_resume;
+ },
});
module.PosWidget.include({
--- /dev/null
+function openerp_restaurant_notes(instance,module){
+ "use strict";
+
+ var QWeb = instance.web.qweb;
+ var _t = instance.web._t;
+
+ var _super_orderline = module.Orderline.prototype;
+
+ module.Orderline = module.Orderline.extend({
+ initialize: function(attr, options) {
+ _super_orderline.initialize.call(this,attr,options);
+ this.note = this.note || "";
+ },
+ set_note: function(note){
+ this.note = note;
+ this.trigger('change',this);
+ },
+ get_note: function(note){
+ return this.note;
+ },
+ can_be_merged_with: function(orderline) {
+ if (orderline.get_note() !== this.get_note()) {
+ return false;
+ } else {
+ return _super_orderline.can_be_merged_with.call(this,orderline);
+ }
+ },
+ clone: function(){
+ var orderline = _super_orderline.clone.call(this);
+ orderline.note = this.note;
+ return orderline;
+ },
+ export_as_JSON: function(){
+ var json = _super_orderline.export_as_JSON.call(this);
+ json.note = this.note;
+ return json;
+ },
+ init_from_JSON: function(json){
+ _super_orderline.init_from_JSON.apply(this,arguments);
+ this.note = json.note;
+ },
+ });
+
+ module.PosWidget.include({
+ orderline_note_click: function(){
+ var self = this;
+ var line = this.pos.get_order().get_selected_orderline();
+
+ if (line) {
+ this.screen_selector.show_popup('textarea',{
+ message: _t('Orderline Note'),
+ value: line.get_note(),
+ confirm: function(note) {
+ line.set_note(note);
+ },
+ });
+ }
+ },
+ build_widgets: function(){
+ var self = this;
+ this._super();
+
+ if (this.pos.config.iface_orderline_notes) {
+ var button = $(QWeb.render('OrderlineNoteButton'));
+ button.click(function(){ self.orderline_note_click(); });
+ button.appendTo(this.$('.control-buttons'));
+ this.$('.control-buttons').removeClass('oe_hidden');
+ }
+ },
+ });
+}
renderElement: function(){
var self = this;
this._super();
- var order = this.pos.get('selectedOrder');
+ var order = this.pos.get_order();
if(!order){
return;
}
- var orderlines = order.get('orderLines').models;
+ var orderlines = order.get_orderlines();
for(var i = 0; i < orderlines.length; i++){
var line = orderlines[i];
linewidget = $(QWeb.render('SplitOrderline',{
lineselect: function($el,order,neworder,splitlines,line_id){
var split = splitlines[line_id] || {'quantity': 0, line: null};
- var line = order.getOrderline(line_id);
+ var line = order.get_orderline(line_id);
if( !line.get_unit().groupable ){
if( split.quantity !== line.get_quantity()){
if( split.quantity ){
if ( !split.line ){
split.line = line.clone();
- neworder.addOrderline(split.line);
+ neworder.add_orderline(split.line);
}
split.line.set_quantity(split.quantity);
}else if( split.line ) {
- neworder.removeOrderline(split.line);
+ neworder.remove_orderline(split.line);
split.line = null;
}
quantity: split.quantity,
id: line_id,
})));
- this.$('.order-info .subtotal').text(this.format_currency(neworder.getSubtotal()));
+ this.$('.order-info .subtotal').text(this.format_currency(neworder.get_subtotal()));
},
pay: function(order,neworder,splitlines){
- var orderlines = order.get('orderLines').models;
+ var orderlines = order.get_orderlines();
var empty = true;
var full = true;
}else{
for(var id in splitlines){
var split = splitlines[id];
- var line = order.getOrderline(parseInt(id));
+ var line = order.get_orderline(parseInt(id));
line.set_quantity(line.get_quantity() - split.quantity);
if(Math.abs(line.get_quantity()) < 0.00001){
- order.removeOrderline(line);
+ order.remove_orderline(line);
}
delete splitlines[id];
}
this._super();
this.renderElement();
- var order = this.pos.get('selectedOrder');
- var neworder = new module.Order({
+ var order = this.pos.get_order();
+ var neworder = new module.Order({},{
pos: this.pos,
temporary: true,
});
var splitbill = $(QWeb.render('SplitbillButton'));
splitbill.click(function(){
- if(self.pos.get('selectedOrder').get('orderLines').models.length > 0){
+ if(self.pos.get_order().get_orderlines().length > 0){
self.pos_widget.screen_selector.set_current_screen('splitbill');
}
});