[IMP] restaurant: a module that extends the point of sale with restaurant features
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import restaurant
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
{
|
||||
'name': 'Restaurant',
|
||||
'version': '1.0',
|
||||
'category': 'Point of Sale',
|
||||
'sequence': 6,
|
||||
'summary': 'Restaurant extensions for the Point of Sale ',
|
||||
'description': """
|
||||
|
||||
=======================
|
||||
|
||||
This module adds several restaurant features to the Point of Sale:
|
||||
- Bill Printing: Allows you to print a receipt before the order is paid
|
||||
- Bill Splitting: Allows you to split an order into different orders
|
||||
- Kitchen Order Printing: allows you to print orders updates to kitchen or bar printers
|
||||
|
||||
""",
|
||||
'author': 'OpenERP SA',
|
||||
'depends': ['point_of_sale'],
|
||||
'data': [
|
||||
'restaurant_view.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/templates.xml',
|
||||
],
|
||||
'qweb':[
|
||||
'static/src/xml/multiprint.xml',
|
||||
'static/src/xml/splitbill.xml',
|
||||
'static/src/xml/printbill.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class restaurant_printer(osv.osv):
|
||||
_name = 'restaurant.printer'
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Printer Name', size=32, required=True, help='An internal identification of the printer'),
|
||||
'proxy_ip': fields.char('Proxy IP Address', size=32, help="The IP Address or hostname of the Printer's hardware proxy"),
|
||||
'product_categories_ids': fields.many2many('pos.category','printer_category_rel', 'printer_id','category_id',string='Printed Product Categories'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'name' : 'Printer',
|
||||
}
|
||||
|
||||
class pos_config(osv.osv):
|
||||
_inherit = 'pos.config'
|
||||
_columns = {
|
||||
'iface_splitbill': fields.boolean('Bill Splitting', help='Enables Bill Splitting in the Point of Sale'),
|
||||
'iface_printbill': fields.boolean('Bill Printing', help='Allows to print the Bill before payment'),
|
||||
'printer_ids': fields.many2many('restaurant.printer','pos_config_printer_rel', 'config_id','printer_id',string='Order Printers'),
|
||||
}
|
||||
_defaults = {
|
||||
'iface_splitbill': False,
|
||||
'iface_printbill': False,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="view_restaurant_printer_form">
|
||||
<field name="name">Order Printer</field>
|
||||
<field name="model">restaurant.printer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="POS Printer" version="7.0">
|
||||
<group col="2">
|
||||
<field name="name" />
|
||||
<field name="proxy_ip" />
|
||||
<field name="product_categories_ids" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_restaurant_printer_form">
|
||||
<field name="name">Order Printers</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">restaurant.printer</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to add a Restaurant Order Printer.
|
||||
</p><p>
|
||||
Order Printers are used by restaurants and bars to print the
|
||||
order updates in the kitchen/bar when the waiter updates the order.
|
||||
</p><p>
|
||||
Each Order Printer has an IP Address that defines the PosBox/Hardware
|
||||
Proxy where the printer can be found, and a list of product categories.
|
||||
An Order Printer will only print updates for prodcuts belonging to one of
|
||||
its categories.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_restaurant_printer">
|
||||
<field name="name">Order Printers</field>
|
||||
<field name="model">restaurant.printer</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Restaurant Order Printers">
|
||||
<field name="name" />
|
||||
<field name="proxy_ip" />
|
||||
<field name="product_categories_ids" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
parent="point_of_sale.menu_point_config_product"
|
||||
action="action_restaurant_printer_form"
|
||||
id="menu_restaurant_printer_all"
|
||||
sequence="30"
|
||||
groups="point_of_sale.group_pos_manager"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_pos_config_form">
|
||||
<field name="name">pos.config.form.view.inherit</field>
|
||||
<field name="model">pos.config</field>
|
||||
<field name="inherit_id" ref="point_of_sale.view_pos_config_form"></field>
|
||||
<field name="arch" type="xml">
|
||||
<sheet position='inside'>
|
||||
<group string="Bar & Restaurant" >
|
||||
<field name="iface_splitbill" />
|
||||
<field name="iface_printbill" />
|
||||
<field name="printer_ids" />
|
||||
</group>
|
||||
</sheet>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_restaurant_printer,restaurant.printer.user,model_restaurant_printer,point_of_sale.group_pos_user,1,0,0,0
|
||||
access_restaurant_printer_manager,restaurant.printer.manager,model_restaurant_printer,point_of_sale.group_pos_manager,1,0,0,0
|
||||
|
@@ -0,0 +1,2 @@
|
||||
/* --- Restaurant Specific CSS --- */
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
openerp.restaurant = function(instance){
|
||||
|
||||
var module = instance.point_of_sale;
|
||||
|
||||
openerp_restaurant_multiprint(instance,module);
|
||||
|
||||
openerp_restaurant_splitbill(instance,module);
|
||||
|
||||
openerp_restaurant_printbill(instance,module);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,215 @@
|
||||
function openerp_restaurant_multiprint(instance,module){
|
||||
var QWeb = instance.web.qweb;
|
||||
var _t = instance.web._t;
|
||||
|
||||
module.Printer = instance.web.Class.extend(openerp.PropertiesMixin,{
|
||||
init: function(parent,options){
|
||||
openerp.PropertiesMixin.init.call(this,parent);
|
||||
var self = this;
|
||||
options = options || {};
|
||||
var url = options.url || 'http://localhost:8069';
|
||||
this.connection = new instance.web.Session(undefined,url, { use_cors: true});
|
||||
this.host = url;
|
||||
this.receipt_queue = [];
|
||||
},
|
||||
print: function(receipt){
|
||||
var self = this;
|
||||
if(receipt){
|
||||
this.receipt_queue.push(receipt);
|
||||
}
|
||||
var aborted = false;
|
||||
function send_printing_job(){
|
||||
if(self.receipt_queue.length > 0){
|
||||
var r = self.receipt_queue.shift();
|
||||
self.connection.rpc('/hw_proxy/print_xml_receipt',{receipt: r},{timeout: 5000})
|
||||
.then(function(){
|
||||
send_printing_job();
|
||||
},function(){
|
||||
self.receipt_queue.unshift(r);
|
||||
});
|
||||
}
|
||||
}
|
||||
send_printing_job();
|
||||
},
|
||||
});
|
||||
|
||||
module.PosModel.prototype.models.push({
|
||||
model: 'restaurant.printer',
|
||||
fields: ['name','proxy_ip','product_categories_ids'],
|
||||
domain: null,
|
||||
loaded: function(self,printers){
|
||||
var active_printers = {};
|
||||
for (var i = 0; i < self.config.printer_ids.length; i++) {
|
||||
active_printers[self.config.printer_ids[i]] = true;
|
||||
}
|
||||
|
||||
self.printers = [];
|
||||
for(var i = 0; i < printers.length; i++){
|
||||
if(active_printers[printers[i].id]){
|
||||
var printer = new module.Printer(self,{url:'http://'+printers[i].proxy_ip+':8069'});
|
||||
printer.config = printers[i];
|
||||
self.printers.push(printer);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
return resume;
|
||||
},
|
||||
saveChanges: function(){
|
||||
this.old_resume = this.lineResume();
|
||||
},
|
||||
computeChanges: function(categories){
|
||||
var current = this.lineResume();
|
||||
var old = this.old_resume || {};
|
||||
var json = this.export_as_JSON();
|
||||
var add = [];
|
||||
var rem = [];
|
||||
|
||||
for( product in current){
|
||||
if (typeof old[product] === 'undefined'){
|
||||
add.push({
|
||||
'id': product,
|
||||
'name': this.pos.db.get_product_by_id(product).name,
|
||||
'quantity': current[product],
|
||||
});
|
||||
}else if( old[product] < current[product]){
|
||||
add.push({
|
||||
'id': product,
|
||||
'name': this.pos.db.get_product_by_id(product).name,
|
||||
'quantity': current[product] - old[product],
|
||||
});
|
||||
}else if( old[product] > current[product]){
|
||||
rem.push({
|
||||
'id': product,
|
||||
'name': this.pos.db.get_product_by_id(product).name,
|
||||
'quantity': old[product] - current[product],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for( product in old){
|
||||
if(typeof current[product] === 'undefined'){
|
||||
rem.push({
|
||||
'id': product,
|
||||
'name': this.pos.db.get_product_by_id(product).name,
|
||||
'quantity': old[product],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(categories && categories.length > 0){
|
||||
// filter the added and removed orders to only contains
|
||||
// products that belong to one of the categories supplied as a parameter
|
||||
|
||||
var self = this;
|
||||
function product_in_category(product_id){
|
||||
var cat = self.pos.db.get_product_by_id(product_id).pos_categ_id[0];
|
||||
while(cat){
|
||||
for(var i = 0; i < categories.length; i++){
|
||||
if(cat === categories[i]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cat = self.pos.db.get_category_parent_id(cat);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var _add = [];
|
||||
var _rem = [];
|
||||
|
||||
for(var i = 0; i < add.length; i++){
|
||||
if(product_in_category(add[i].id)){
|
||||
_add.push(add[i]);
|
||||
}
|
||||
}
|
||||
add = _add;
|
||||
|
||||
for(var i = 0; i < rem.length; i++){
|
||||
if(product_in_category(rem[i].id)){
|
||||
_rem.push(rem[i]);
|
||||
}
|
||||
}
|
||||
rem = _rem;
|
||||
}
|
||||
|
||||
return {
|
||||
'new': add,
|
||||
'cancelled': rem,
|
||||
'table': json.table || 'unknown table',
|
||||
'name': json.name || 'unknown order',
|
||||
};
|
||||
|
||||
},
|
||||
printChanges: function(){
|
||||
var printers = this.pos.printers;
|
||||
for(var i = 0; i < printers.length; i++){
|
||||
var changes = this.computeChanges(printers[i].config.product_categories_ids);
|
||||
if ( changes['new'].length > 0 || changes['cancelled'].length > 0){
|
||||
var receipt = QWeb.render('OrderChangeReceipt',{changes:changes, widget:this});
|
||||
printers[i].print(receipt);
|
||||
}
|
||||
}
|
||||
},
|
||||
hasChangesToPrint: function(){
|
||||
var printers = this.pos.printers;
|
||||
for(var i = 0; i < printers.length; i++){
|
||||
var changes = this.computeChanges(printers[i].config.product_categories_ids);
|
||||
if ( changes['new'].length > 0 || changes['cancelled'].length > 0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
module.PosWidget.include({
|
||||
build_widgets: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
if(this.pos.printers.length){
|
||||
var submitorder = $(QWeb.render('SubmitOrderButton'));
|
||||
|
||||
submitorder.click(function(){
|
||||
var order = self.pos.get('selectedOrder');
|
||||
if(order.hasChangesToPrint()){
|
||||
order.printChanges();
|
||||
order.saveChanges();
|
||||
self.pos_widget.order_widget.update_summary();
|
||||
}
|
||||
});
|
||||
|
||||
submitorder.appendTo(this.$('.control-buttons'));
|
||||
this.$('.control-buttons').removeClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.OrderWidget.include({
|
||||
update_summary: function(){
|
||||
this._super();
|
||||
var order = this.pos.get('selectedOrder');
|
||||
|
||||
if(order.hasChangesToPrint()){
|
||||
this.pos_widget.$('.order-submit').addClass('highlight');
|
||||
}else{
|
||||
this.pos_widget.$('.order-submit').removeClass('highlight');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
function openerp_restaurant_printbill(instance,module){
|
||||
var QWeb = instance.web.qweb;
|
||||
var _t = instance.web._t;
|
||||
|
||||
module.PosWidget.include({
|
||||
build_widgets: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
if(this.pos.config.iface_printbill){
|
||||
var printbill = $(QWeb.render('PrintBillButton'));
|
||||
|
||||
printbill.click(function(){
|
||||
var order = self.pos.get('selectedOrder');
|
||||
if(order.get('orderLines').models.length > 0){
|
||||
var receipt = order.export_for_printing();
|
||||
self.pos.proxy.print_receipt(QWeb.render('BillReceipt',{
|
||||
receipt: receipt, widget: self,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
printbill.appendTo(this.$('.control-buttons'));
|
||||
this.$('.control-buttons').removeClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
function openerp_restaurant_splitbill(instance, module){
|
||||
var QWeb = instance.web.qweb;
|
||||
var _t = instance.web._t;
|
||||
|
||||
module.SplitbillScreenWidget = module.ScreenWidget.extend({
|
||||
template: 'SplitbillScreenWidget',
|
||||
|
||||
show_leftpane: false,
|
||||
previous_screen: 'products',
|
||||
|
||||
renderElement: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
var order = this.pos.get('selectedOrder');
|
||||
if(!order){
|
||||
return;
|
||||
}
|
||||
var orderlines = order.get('orderLines').models;
|
||||
for(var i = 0; i < orderlines.length; i++){
|
||||
var line = orderlines[i];
|
||||
linewidget = $(QWeb.render('SplitOrderline',{
|
||||
widget:this,
|
||||
line:line,
|
||||
selected: false,
|
||||
quantity: 0,
|
||||
id: line.id,
|
||||
}));
|
||||
linewidget.data('id',line.id);
|
||||
this.$('.orderlines').append(linewidget);
|
||||
}
|
||||
this.$('.back').click(function(){
|
||||
self.pos_widget.screen_selector.set_current_screen(self.previous_screen);
|
||||
});
|
||||
},
|
||||
|
||||
lineselect: function($el,order,neworder,splitlines,line_id){
|
||||
var split = splitlines[line_id] || {'quantity': 0, line: null};
|
||||
var line = order.getOrderline(line_id);
|
||||
|
||||
if( !line.get_unit().groupable ){
|
||||
if( split.quantity !== line.get_quantity()){
|
||||
split.quantity = line.get_quantity();
|
||||
}else{
|
||||
split.quantity = 0;
|
||||
}
|
||||
}else{
|
||||
if( split.quantity < line.get_quantity()){
|
||||
split.quantity += line.get_unit().rounding;
|
||||
if(split.quantity > line.get_quantity()){
|
||||
split.quantity = line.get_quantity();
|
||||
}
|
||||
}else{
|
||||
split.quantity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( split.quantity ){
|
||||
if ( !split.line ){
|
||||
split.line = line.clone();
|
||||
neworder.addOrderline(split.line);
|
||||
}
|
||||
split.line.set_quantity(split.quantity);
|
||||
}else if( split.line ) {
|
||||
neworder.removeOrderline(split.line);
|
||||
split.line = null;
|
||||
}
|
||||
|
||||
splitlines[line_id] = split;
|
||||
$el.replaceWith($(QWeb.render('SplitOrderline',{
|
||||
widget: this,
|
||||
line: line,
|
||||
selected: split.quantity !== 0,
|
||||
quantity: split.quantity,
|
||||
id: line_id,
|
||||
})));
|
||||
this.$('.order-info .subtotal').text(this.format_currency(neworder.getSubtotal()));
|
||||
},
|
||||
|
||||
pay: function($el,order,neworder,splitlines,cashregister_id){
|
||||
var orderlines = order.get('orderLines').models;
|
||||
var empty = true;
|
||||
var full = true;
|
||||
|
||||
for(var i = 0; i < orderlines.length; i++){
|
||||
var id = orderlines[i].id;
|
||||
var split = splitlines[id];
|
||||
if(!split){
|
||||
full = false;
|
||||
}else{
|
||||
if(split.quantity){
|
||||
empty = false;
|
||||
if(split.quantity !== orderlines[i].get_quantity()){
|
||||
full = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty){
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i = 0; i < this.pos.cashregisters.length; i++){
|
||||
if(this.pos.cashregisters[i].id === cashregister_id){
|
||||
var cashregister = this.pos.cashregisters[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(full){
|
||||
order.addPaymentline(cashregister);
|
||||
this.pos_widget.screen_selector.set_current_screen('payment');
|
||||
}else{
|
||||
for(var id in splitlines){
|
||||
var split = splitlines[id];
|
||||
var line = order.getOrderline(parseInt(id));
|
||||
line.set_quantity(line.get_quantity() - split.quantity);
|
||||
if(Math.abs(line.get_quantity()) < 0.00001){
|
||||
order.removeOrderline(line);
|
||||
}
|
||||
delete splitlines[id];
|
||||
}
|
||||
neworder.addPaymentline(cashregister);
|
||||
neworder.set_screen_data('screen','payment');
|
||||
this.pos.get('orders').add(neworder);
|
||||
this.pos.set('selectedOrder',neworder);
|
||||
}
|
||||
},
|
||||
show: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
this.renderElement();
|
||||
|
||||
var order = this.pos.get('selectedOrder');
|
||||
var neworder = new module.Order({
|
||||
pos: this.pos,
|
||||
temporary: true,
|
||||
});
|
||||
neworder.set('client',order.get('client'));
|
||||
|
||||
var splitlines = {};
|
||||
|
||||
this.$('.orderlines').on('click','.orderline',function(){
|
||||
var id = parseInt($(this).data('id'));
|
||||
var $el = $(this);
|
||||
self.lineselect($el,order,neworder,splitlines,id);
|
||||
});
|
||||
|
||||
this.$('.paymentmethod').click(function(){
|
||||
var id = parseInt($(this).data('id'));
|
||||
var $el = $(this);
|
||||
self.pay($el,order,neworder,splitlines,id);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
module.PosWidget.include({
|
||||
build_widgets: function(){
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
if(this.pos.config.iface_splitbill){
|
||||
this.splitbill_screen = new module.SplitbillScreenWidget(this,{});
|
||||
this.splitbill_screen.appendTo(this.$('.screens'));
|
||||
this.screen_selector.add_screen('splitbill',this.splitbill_screen);
|
||||
|
||||
var splitbill = $(QWeb.render('SplitbillButton'));
|
||||
|
||||
splitbill.click(function(){
|
||||
if(self.pos.get('selectedOrder').get('orderLines').models.length > 0){
|
||||
self.pos_widget.screen_selector.set_current_screen('splitbill');
|
||||
}
|
||||
});
|
||||
|
||||
splitbill.appendTo(this.$('.control-buttons'));
|
||||
this.$('.control-buttons').removeClass('oe_hidden');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="SubmitOrderButton">
|
||||
<span class="control-button order-submit">
|
||||
<i class="fa fa-cutlery"></i>
|
||||
Order
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="OrderChangeReceipt">
|
||||
<receipt
|
||||
align='center'
|
||||
width='40'
|
||||
size='double-height'
|
||||
line-ratio='0.6'
|
||||
value-decimals='3'
|
||||
value-thousands-separator=''
|
||||
value-autoint='on'
|
||||
>
|
||||
<div><t t-esc="changes.name" /></div>
|
||||
<br />
|
||||
<br />
|
||||
<t t-if="changes.cancelled.length > 0">
|
||||
<div color='red'>
|
||||
<div bold='on' size='double'>CANCELLED</div>
|
||||
<br />
|
||||
<br />
|
||||
<t t-foreach="changes.cancelled" t-as="change">
|
||||
<line>
|
||||
<left><t t-esc="change.name" /></left>
|
||||
<right><value><t t-esc="change.quantity" /></value></right>
|
||||
</line>
|
||||
</t>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="changes.new.length > 0">
|
||||
<div bold='on' size='double'>NEW</div>
|
||||
<br />
|
||||
<br />
|
||||
<t t-foreach="changes.new" t-as="change">
|
||||
<line>
|
||||
<left><t t-esc="change.name" /></left>
|
||||
<right><value><t t-esc="change.quantity" /></value></right>
|
||||
</line>
|
||||
</t>
|
||||
<br />
|
||||
<br />
|
||||
</t>
|
||||
</receipt>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="PrintBillButton">
|
||||
<span class="control-button order-printbill">
|
||||
<i class="fa fa-print"></i>
|
||||
Bill
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="BillReceipt">
|
||||
<receipt align='center' width='40' value-thousands-separator='' >
|
||||
<t t-if='receipt.company.logo'>
|
||||
<img t-att-src='receipt.company.logo' />
|
||||
<br/>
|
||||
</t>
|
||||
<t t-if='!receipt.company.logo'>
|
||||
<h1><t t-esc='receipt.company.name' /></h1>
|
||||
<br/>
|
||||
</t>
|
||||
<div font='b'>
|
||||
<t t-if='receipt.shop.name'>
|
||||
<div><t t-esc='receipt.shop.name' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.contact_address'>
|
||||
<div><t t-esc='receipt.company.contact_address' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.phone'>
|
||||
<div>Tel:<t t-esc='receipt.company.phone' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.vat'>
|
||||
<div>VAT:<t t-esc='receipt.company.vat' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.email'>
|
||||
<div><t t-esc='receipt.company.email' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.company.website'>
|
||||
<div><t t-esc='receipt.company.website' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.header'>
|
||||
<div><t t-esc='receipt.header' /></div>
|
||||
</t>
|
||||
<t t-if='receipt.cashier'>
|
||||
<div>--------------------------------</div>
|
||||
<div>Served by <t t-esc='receipt.cashier' /></div>
|
||||
</t>
|
||||
</div>
|
||||
<br /><br />
|
||||
|
||||
<!-- Orderlines -->
|
||||
|
||||
<div line-ratio='0.6'>
|
||||
<t t-foreach='receipt.orderlines' t-as='line'>
|
||||
<t t-set='simple' t-value='line.discount === 0 and line.unit_name === "Unit(s)" and line.quantity === 1' />
|
||||
<t t-if='simple'>
|
||||
<line>
|
||||
<left><t t-esc='line.product_name' /></left>
|
||||
<right><value><t t-esc='line.price_display' /></value></right>
|
||||
</line>
|
||||
</t>
|
||||
<t t-if='!simple'>
|
||||
<line><left><t t-esc='line.product_name' /></left></line>
|
||||
<t t-if='line.discount !== 0'>
|
||||
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line>
|
||||
</t>
|
||||
<line indent='1'>
|
||||
<left>
|
||||
<value value-decimals='3' value-autoint='on'>
|
||||
<t t-esc='line.quantity' />
|
||||
</value>
|
||||
<t t-if='line.unit_name !== "Unit(s)"'>
|
||||
<t t-esc='line.unit_name' />
|
||||
</t>
|
||||
x
|
||||
<value value-decimals='2'>
|
||||
<t t-esc='line.price' />
|
||||
</value>
|
||||
</left>
|
||||
<right>
|
||||
<value><t t-esc='line.price_display' /></value>
|
||||
</right>
|
||||
</line>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<!-- Subtotal -->
|
||||
<t t-set='taxincluded' t-value='Math.abs(receipt.subtotal - receipt.total_with_tax) <= 0.000001' />
|
||||
<t t-if='!taxincluded'>
|
||||
<line><right>--------</right></line>
|
||||
<line><left>Subtotal</left><right> <value><t t-esc="receipt.subtotal" /></value></right></line>
|
||||
<t t-foreach='receipt.tax_details' t-as='tax'>
|
||||
<line>
|
||||
<left><t t-esc='tax.name' /></left>
|
||||
<right><value><t t-esc='tax.amount' /></value></right>
|
||||
</line>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Total -->
|
||||
|
||||
<line><right>--------</right></line>
|
||||
<line size='double-height'>
|
||||
<left><pre> TOTAL</pre></left>
|
||||
<right><value><t t-esc='receipt.total_with_tax' /></value></right>
|
||||
</line>
|
||||
<br/><br/>
|
||||
|
||||
<!-- Extra Payment Info -->
|
||||
|
||||
<t t-if='receipt.total_discount'>
|
||||
<line>
|
||||
<left>Discounts</left>
|
||||
<right><value><t t-esc='receipt.total_discount'/></value></right>
|
||||
</line>
|
||||
</t>
|
||||
<t t-if='taxincluded'>
|
||||
<t t-foreach='receipt.tax_details' t-as='tax'>
|
||||
<line>
|
||||
<left><t t-esc='tax.name' /></left>
|
||||
<right><value><t t-esc='tax.amount' /></value></right>
|
||||
</line>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Footer -->
|
||||
<t t-if='receipt.footer'>
|
||||
<br/>
|
||||
<pre><t t-esc='receipt.footer' /></pre>
|
||||
<br/>
|
||||
<br/>
|
||||
</t>
|
||||
|
||||
<br/>
|
||||
<div font='b'>
|
||||
<div><t t-esc='receipt.name' /></div>
|
||||
<div><t t-esc='receipt.date.localestring' /></div>
|
||||
</div>
|
||||
|
||||
</receipt>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="SplitbillButton">
|
||||
<span class="control-button order-split">
|
||||
<i class="fa fa-copy"></i>
|
||||
Split
|
||||
</span>
|
||||
</t>
|
||||
|
||||
<t t-name="SplitOrderline">
|
||||
|
||||
<li t-attf-class="orderline #{ selected ? 'selected' : ''} #{ quantity !== line.get_quantity() ? 'partially' : '' }"
|
||||
t-att-data-id="id">
|
||||
<span class="product-name">
|
||||
<t t-esc="line.get_product().name"/>
|
||||
</span>
|
||||
<span class="price">
|
||||
<t t-esc="widget.format_currency(line.get_display_price())"/>
|
||||
</span>
|
||||
<ul class="info-list">
|
||||
<t t-if="line.get_quantity_str() !== '1'">
|
||||
<li class="info">
|
||||
<t t-if='selected and line.get_unit().groupable'>
|
||||
<em class='big'>
|
||||
<t t-esc='quantity' />
|
||||
</em>
|
||||
/
|
||||
<t t-esc="line.get_quantity_str()" />
|
||||
</t>
|
||||
<t t-if='!(selected and line.get_unit().groupable)'>
|
||||
<em>
|
||||
<t t-esc="line.get_quantity_str()" />
|
||||
</em>
|
||||
</t>
|
||||
<t t-esc="line.get_unit().name" />
|
||||
at
|
||||
<t t-esc="widget.format_currency(line.get_unit_price())" />
|
||||
/
|
||||
<t t-esc="line.get_unit().name" />
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="line.get_discount_str() !== '0'">
|
||||
<li class="info">
|
||||
With a
|
||||
<em>
|
||||
<t t-esc="line.get_discount_str()" />%
|
||||
</em>
|
||||
discount
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="SplitbillScreenWidget">
|
||||
<div class='splitbill-screen screen'>
|
||||
<div class='screen-content'>
|
||||
<div class='top-content'>
|
||||
<span class='button back'>
|
||||
<i class='fa fa-angle-double-left'></i>
|
||||
Back
|
||||
</span>
|
||||
<h1>Bill Splitting</h1>
|
||||
</div>
|
||||
<div class='left-content touch-scrollable scrollable-y'>
|
||||
<div class='order'>
|
||||
<ul class='orderlines'>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='right-content touch-scrollable scrollable-y'>
|
||||
<div class='order-info'>
|
||||
<span class='subtotal'><t t-esc='widget.format_currency(0.0)'/></span>
|
||||
</div>
|
||||
<div class='paymentmethods'>
|
||||
<t t-foreach="widget.pos.cashregisters" t-as="cashregister">
|
||||
<div class='button paymentmethod' t-att-data-id="cashregister.id">
|
||||
<t t-esc='cashregister.journal.name' />
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- vim:fdn=3:
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<template id="index" inherit_id='point_of_sale.index' name="Restaurant Index"><!DOCTYPE html>
|
||||
<xpath expr="//link[@id='pos-stylesheet']" position="after">
|
||||
<link rel="stylesheet" href="/restaurant/static/src/css/restaurant.css" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_frontend" inherit_id="web.assets_common">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/restaurant/static/src/js/multiprint.js"></script>
|
||||
<script type="text/javascript" src="/restaurant/static/src/js/splitbill.js"></script>
|
||||
<script type="text/javascript" src="/restaurant/static/src/js/printbill.js"></script>
|
||||
<script type="text/javascript" src="/restaurant/static/src/js/main.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user