// this is called when an order is removed from the order collection. It ensures that there is always an existing
// order and a valid selected order
on_removed_order: function(removed_order,index,reason){
- if( (reason === 'abandon' || removed_order.temporary) && this.get('orders').size() > 0){
+ var order_list = this.get_order_list();
+ if( (reason === 'abandon' || removed_order.temporary) && this.order_list.length > 0){
// when we intentionally remove an unfinished order, and there is another existing one
- this.set({'selectedOrder' : this.get('orders').at(index) || this.get('orders').last()});
+ this.set_order(order_list[index] || order_list[order_list.length -1]);
}else{
// when the order was automatically removed after completion,
// or when we intentionally delete the only concurrent order
var order = new module.Order({pos:this});
this.get('orders').add(order);
this.set('selectedOrder', order);
+ return order;
},
+ // return the current order
get_order: function(){
return this.get('selectedOrder');
},
+ // change the current order
+ set_order: function(order){
+ this.set({ selectedOrder: order });
+ },
+
+ // return the list of unpaid orders
+ get_order_list: function(){
+ return this.get('orders').models;
+ },
+
//removes the current order
delete_current_order: function(){
this.get('selectedOrder').destroy({'reason':'abandon'});
scan_product: function(parsed_code){
var self = this;
- var selectedOrder = this.get('selectedOrder');
+ var selectedOrder = this.get_order();
if(parsed_code.encoding === 'ean13'){
var product = this.db.get_product_by_ean13(parsed_code.base_code);
}else if(parsed_code.encoding === 'reference'){
this.popup_set[popup_name].hide();
}
- this.pos.get('selectedOrder').set_screen_data({
+ this.pos.get_order().set_screen_data({
'screen': this.default_screen,
});
this.current_popup = null;
}
},
- load_saved_screen: function(){
+ load_saved_screen: function(options){
+ options = options || {};
this.close_popup();
- var selectedOrder = this.pos.get('selectedOrder');
+ var selectedOrder = this.pos.get_order();
// FIXME : this changing screen behaviour is sometimes confusing ...
- this.set_current_screen(selectedOrder.get_screen_data('screen') || this.default_screen,null,'refresh');
+ this.set_current_screen(selectedOrder.get_screen_data('screen') || options.default_screen || this.default_screen,null,'refresh');
//this.set_current_screen(this.default_screen,null,'refresh');
},
this.close_popup();
- var order = this.pos.get('selectedOrder');
- var old_screen_name = order.get_screen_data('screen');
+ var order = this.pos.get_order();
+ if (order) {
+ var old_screen_name = order.get_screen_data('screen');
- order.set_screen_data('screen',screen_name);
+ order.set_screen_data('screen',screen_name);
- if(params){
- order.set_screen_data('params',params);
- }
+ if(params){
+ order.set_screen_data('params',params);
+ }
- if( screen_name !== old_screen_name ){
- order.set_screen_data('previous-screen',old_screen_name);
+ if( screen_name !== old_screen_name ){
+ order.set_screen_data('previous-screen',old_screen_name);
+ }
}
if ( refresh || screen !== this.current_screen){
}
},
get_current_screen: function(){
- return this.pos.get('selectedOrder').get_screen_data('screen') || this.default_screen;
+ return this.pos.get_order().get_screen_data('screen') || this.default_screen;
},
back: function(){
- var previous = this.pos.get('selectedOrder').get_screen_data('previous-screen');
+ var previous = this.pos.get_order().get_screen_data('previous-screen');
if(previous){
this.set_current_screen(previous);
}
},
get_current_screen_param: function(param){
- var params = this.pos.get('selectedOrder').get_screen_data('params');
+ var params = this.pos.get_order().get_screen_data('params');
return params ? params[param] : undefined;
},
set_default_screen: function(){
barcode_client_action: function(code){
var partner = this.pos.db.get_partner_by_ean13(code.code);
if(partner){
- this.pos.get('selectedOrder').set_client(partner);
+ this.pos.get_order().set_client(partner);
this.pos_widget.username.refresh();
return true;
}
// 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('selectedOrder').getLastOrderline();
+ var last_orderline = this.pos.get_order().getLastOrderline();
if(last_orderline){
last_orderline.set_discount(code.value)
}
}
},
order_product: function(){
- this.pos.get('selectedOrder').addProduct(this.get_product(),{ quantity: this.weight });
+ this.pos.get_order().addProduct(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('selectedOrder').addProduct(product);
+ self.pos.get_order().addProduct(product);
}
},
product_list: this.pos.db.get_product_by_category(0)
this.renderElement();
this.details_visible = false;
- this.old_client = this.pos.get('selectedOrder').get('client');
+ this.old_client = this.pos.get_order().get_client()
this.new_client = this.old_client;
this.$('.back').click(function(){
},
save_changes: function(){
if( this.has_client_changed() ){
- this.pos.get('selectedOrder').set_client(this.new_client);
+ this.pos.get_order().set_client(this.new_client);
}
},
has_client_changed: function(){
}
if (order.isPaidWithCash() && this.pos.config.iface_cashdrawer) {
-
this.pos.proxy.open_cashbox();
}
if(!self.editable){
return;
}
- self.pos.get('selectedOrder').selectLine(this.orderline);
+ self.pos.get_order().selectLine(this.orderline);
self.pos_widget.numpad.state.reset();
};
this.client_change_handler = function(event){
this.enable_numpad();
}else{
this.disable_numpad();
- this.pos.get('selectedOrder').deselectLine();
+ this.pos.get_order().deselectLine();
}
},
set_value: function(val) {
- var order = this.pos.get('selectedOrder');
+ var order = this.pos.get_order();
if (this.editable && order.getSelectedLine()) {
var mode = this.numpad_state.get('mode');
if( mode === 'quantity'){
}
},
change_selected_order: function() {
- this.bind_order_events();
- this.renderElement();
+ if (this.pos.get_order()) {
+ this.bind_order_events();
+ this.renderElement();
+ }
},
bind_order_events: function() {
- var order = this.pos.get('selectedOrder');
+ var order = this.pos.get_order();
order.unbind('change:client', this.client_change_handler);
order.bind('change:client', this.client_change_handler);
return el_node;
},
remove_orderline: function(order_line){
- if(this.pos.get('selectedOrder').get('orderLines').length === 0){
+ if(this.pos.get_order().get('orderLines').length === 0){
this.renderElement();
}else{
order_line.node.parentNode.removeChild(order_line.node);
renderElement: function(scrollbottom){
this.pos_widget.numpad.state.reset();
- var order = this.pos.get('selectedOrder');
+ var order = this.pos.get_order();
var orderlines = order.get('orderLines').models;
var el_str = openerp.qweb.render('OrderWidget',{widget:this, order:order, orderlines:orderlines});
}
},
update_summary: function(){
- var order = this.pos.get('selectedOrder');
+ var order = this.pos.get_order();
var total = order ? order.getTotalTaxIncluded() : 0;
var taxes = order ? total - order.getTotalTaxExcluded() : 0;
this.pos.bind('change:selectedOrder', this.renderElement,this );
},
renderElement:function(){
- this.selected = ( this.pos.get('selectedOrder') === this.order )
+ this.selected = ( this.pos.get_order() === this.order )
this._super();
var self = this;
this.$el.click(function(){
- if( self.pos.get('selectedOrder') === self.order ){
+ if( self.pos.get_order() === self.order ){
var ss = self.pos.pos_widget.screen_selector;
if(ss.get_current_screen() === 'clientlist'){
ss.back();
}
},
selectOrder: function(event) {
- this.pos.set({
- selectedOrder: this.order
- });
+ this.pos.set_order(this.order);
},
destroy: function(){
this.order.unbind('destroy', this.destroy, this);
if(query){
var products = this.pos.db.search_product_in_category(category.id,query)
if(buy_result && products.length === 1){
- this.pos.get('selectedOrder').addProduct(products[0]);
+ this.pos.get_order().addProduct(products[0]);
this.clear_search();
}else{
this.product_list_widget.set_product_list(products);
if(this.mode === 'cashier'){
user = this.pos.cashier || this.pos.user;
}else{
- user = this.pos.get('selectedOrder').get_client() || this.pos.user;
+ user = this.pos.get_order().get_client() || this.pos.user;
}
if(user){
return user.name;
});
self.$('.deleteorder-button').click(function(){
- if( !self.pos.get('selectedOrder').is_empty() ){
+ if( !self.pos.get_order().is_empty() ){
self.screen_selector.show_popup('confirm',{
message: _t('Destroy Current Order ?'),
comment: _t('You will lose any data associated with the current order'),
'height': fields.float('Height', help="The table's height in percentage of the floor's height"),
'color': fields.char('Color', size=32, help="The table's color"),
'active': fields.boolean('Active',help='If false, the table is deactivated and will not be available in the point of sale'),
+ 'pos_order_ids':fields.one2many('pos.order','table_id','Pos Orders', help='The orders served at this table'),
}
_defaults = {
'iface_printbill': False,
}
+class pos_order(osv.osv):
+ _inherit = 'pos.order'
+ _columns = {
+ 'table_id': fields.many2one('restaurant.table','Table', help='The table where this order was served'),
+ }
+
function openerp_restaurant_floors(instance,module){
var _t = instance.web._t;
+ // At POS Startup, load the floors, and add them to the pos model
module.PosModel.prototype.models.push({
model: 'restaurant.floor',
fields: ['name','background_image','table_ids'],
},
});
+ // At POS Startup, after the floors are loaded, load the tables, and associate
+ // them with their floor.
module.PosModel.prototype.models.push({
model: 'restaurant.table',
fields: ['name','width','height','position_h','position_v','shape','floor_id','color'],
},
});
+ // The Table GUI element, should always be a child of the FloorScreenWidget
module.TableWidget = module.PosBaseWidget.extend({
template: 'TableWidget',
init: function(parent, options){
this.handle_dragging = false;
this.handle = null;
},
+ // computes the absolute position of a DOM mouse event, used
+ // when resizing tables
event_position: function(event){
if(event.touches && event.touches[0]){
return {x: event.touches[0].screenX, y: event.touches[0].screenY};
return {x: event.screenX, y: event.screenY};
}
},
+ // when a table is clicked, go to the table's orders
+ // but if we're editing, we select/deselect it.
click_handler: function(){
var self = this;
var floorplan = this.getParent();
if (floorplan.editing) {
- setTimeout(function(){ // in a setTimeout to debounce with drag&drop
+ setTimeout(function(){ // in a setTimeout to debounce with drag&drop start
if (!self.dragging) {
if (self.moved) {
self.moved = false;
}
}
},50);
+ } else {
+ console.log('set table',this.table);
+ floorplan.pos.set_table(this.table);
}
},
+ // drag and drop for moving the table, at drag start
dragstart_handler: function(event,$el,drag){
if (this.selected && !this.handle_dragging) {
this.dragging = true;
this.dragpos = { x: drag.offsetX, y: drag.offsetY };
}
},
+ // drag and drop for moving the table, at drag end
dragend_handler: function(event,$el){
this.dragging = false;
},
+ // drag and drop for moving the table, at every drop movement.
dragmove_handler: function(event,$el,drag){
if (this.dragging) {
var dx = drag.offsetX - this.dragpos.x;
$el.css(this.table_style());
}
},
+ // drag and dropping the resizing handles
handle_dragstart_handler: function(event, $el, drag) {
if (this.selected && !this.dragging) {
this.handle_dragging = true;
var cl = this.handle.classList;
- var MIN_SIZE = 40;
+ var MIN_SIZE = 40; // smaller than this, and it becomes impossible to edit.
- var tw = Math.max(MIN_SIZE, this.table.width);
+ var tw = Math.max(MIN_SIZE, this.table.width);
var th = Math.max(MIN_SIZE, this.table.height);
var tx = this.table.position_h;
var ty = this.table.position_v;
this.renderElement();
}
},
+ // The table's positioning is handled via css absolute positioning,
+ // which is handled here.
table_style: function(){
var table = this.table;
function unit(val){ return '' + val + 'px'; }
return style;
},
+ // convert the style dictionary to a ; separated string for inclusion in templates
table_style_str: function(){
var style = this.table_style();
var str = "";
}
return str;
},
+ // select the table (should be called via the floorplan)
select: function() {
this.selected = true;
this.renderElement();
},
+ // deselect the table (should be called via the floorplan)
deselect: function() {
this.selected = false;
this.renderElement();
this.save_changes();
},
+ // sends the table's modification to the server
save_changes: function(){
var self = this;
var model = new instance.web.Model('restaurant.table');
});
});
},
+ // destroy the table. We do not really destroy it, we set it
+ // to inactive so that it doesn't show up anymore, but it still
+ // available on the database for the orders that depend on it.
trash: function(){
var self = this;
var model = new instance.web.Model('restaurant.table');
return model.call('create_from_ui',[{'active':false,'id':this.table.id}]).then(function(table_id){
// Removing all references from the table and the table_widget in in the UI ...
- // This should probably be cleaned.
for (var i = 0; i < self.pos.floors.length; i++) {
var floor = self.pos.floors[i];
for (var j = 0; j < floor.tables.length; j++) {
},
});
+ // The screen that allows you to select the floor, see and select the table,
+ // as well as edit them.
module.FloorScreenWidget = module.ScreenWidget.extend({
template: 'FloorScreenWidget',
show_leftpane: false,
},
});
+ // Add the FloorScreen to the GUI, and set it as the default screen
module.PosWidget.include({
build_widgets: function(){
var self = this;
},
});
+ // when the floor plan is activated, we need to go back to the floor plan
+ // when an order is completed. Usually on order completion, a new order is
+ // set as the current one. Now, we set the new order to null.
+ // load_saved_screen() is called whenever the current order is changed, and
+ // will detect this, and set the current screen to the default_screen,
+ // which is the floor plan.
+ module.ScreenSelector.include({
+ load_saved_screen: function(){
+ if (!this.pos.get_order()) {
+ this.set_current_screen(this.default_screen,null,'refresh');
+ } else {
+ this._super({default_screen:'products'});
+ }
+ },
+ });
+
+ // New orders are now associated with the current table, if any.
+ 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;
+ }
+ });
+
+ // We need to change the way the regular UI sees the orders, it
+ // needs to only see the orders associated with the current table,
+ // and when an order is validated, it needs to go back to the floor map.
+ //
+ // And when we change the table, we must create an order for that table
+ // if there is none.
+
+ var _super_posmodel = module.PosModel.prototype;
+ module.PosModel = module.PosModel.extend({
+ initialize: function(session, attributes) {
+ this.table = null;
+ return _super_posmodel.initialize.call(this,session,attributes);
+ },
+
+ // changes the current table.
+ set_table: function(table) {
+ if (!table) { // no table ? go back to the floor plan, see ScreenSelector
+ this.set_order(null);
+ } else { // table ? load the associated orders ...
+ this.table = table;
+ var orders = this.get_order_list();
+ if (orders.length) {
+ this.set_order(orders[0]); // and go to the first one ...
+ } else {
+ this.add_new_order(); // or create a new order with the current table
+ }
+ }
+ },
+
+ // 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) {
+ return orders;
+ } else {
+ var t_orders = [];
+ for (var i = 0; i < orders.length; i++) {
+ if ( orders[i].table === this.table) {
+ t_orders.push(orders[i]);
+ }
+ }
+ return t_orders;
+ }
+ },
+
+ // When we validate an order we go back to the floor plan.
+ // When we cancel an order and there is multiple orders
+ // on the table, stay on the table.
+ on_removed_order: function(removed_order,index,reason){
+ var order_list = this.get_order_list();
+ if( (reason === 'abandon' || removed_order.temporary) && this.order_list.length > 0){
+ this.set_order(order_list[index] || order_list[order_list.length -1]);
+ }else{
+ // back to the floor plan
+ this.set_table(null);
+ }
+ },
+ });
}