[WIP] point_of_sale, pos_restaurant: access to orders from the floor plan. TODO:...
authorFrederic van der Essen <fva@openerp.com / fvdessen+o@gmail.com>
Thu, 25 Sep 2014 23:07:43 +0000 (01:07 +0200)
committerFrédéric van der Essen <fvdessen@gmail.com>
Tue, 25 Nov 2014 17:11:12 +0000 (18:11 +0100)
Conflicts:
addons/point_of_sale/static/src/js/screens.js
addons/point_of_sale/static/src/js/widgets.js

addons/point_of_sale/static/src/js/models.js
addons/point_of_sale/static/src/js/screens.js
addons/point_of_sale/static/src/js/widgets.js
addons/pos_restaurant/restaurant.py
addons/pos_restaurant/static/src/js/floors.js

index 3629e26..e7b401e 100644 (file)
@@ -446,9 +446,10 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
         // 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
@@ -461,12 +462,24 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
             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'});
@@ -621,7 +634,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
 
         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'){
index 0faf117..6219969 100644 (file)
@@ -45,7 +45,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 this.popup_set[popup_name].hide();
             }
 
-            this.pos.get('selectedOrder').set_screen_data({
+            this.pos.get_order().set_screen_data({
                 'screen': this.default_screen,
             });
 
@@ -70,11 +70,12 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                 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');
             
         },
@@ -96,17 +97,19 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
             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){
@@ -119,16 +122,16 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             }
         },
         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(){
@@ -194,7 +197,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
         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;
             }
@@ -205,7 +208,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
         // 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)
             }
@@ -479,7 +482,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             }
         },
         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();
@@ -536,7 +539,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
                     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)
@@ -587,7 +590,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
 
             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(){
@@ -689,7 +692,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
         },
         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(){
@@ -1299,7 +1302,6 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa
             }
 
             if (order.isPaidWithCash() && this.pos.config.iface_cashdrawer) { 
-            
                     this.pos.proxy.open_cashbox();
             }
 
index b74014c..4f2c069 100644 (file)
@@ -109,7 +109,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                 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){
@@ -138,11 +138,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                 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'){
@@ -155,12 +155,14 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                }
         },
         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);
 
@@ -192,7 +194,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             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);
@@ -212,7 +214,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
         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});
@@ -239,7 +241,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             }
         },
         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;
 
@@ -261,11 +263,11 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             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();
@@ -281,9 +283,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             }
         },
         selectOrder: function(event) {
-            this.pos.set({
-                selectedOrder: this.order
-            });
+            this.pos.set_order(this.order);
         },
         destroy: function(){
             this.order.unbind('destroy', this.destroy, this);
@@ -456,7 +456,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             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);
@@ -561,7 +561,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
             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;
@@ -869,7 +869,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
                 });
 
                 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'),
index 6b51fcf..25bde45 100644 (file)
@@ -49,6 +49,7 @@ class restaurant_table(osv.osv):
         '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 = {
@@ -105,3 +106,9 @@ class pos_config(osv.osv):
         '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'),
+    }
+
index 0443ddd..f661de5 100644 (file)
@@ -1,6 +1,7 @@
 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'],
@@ -15,6 +16,8 @@ function openerp_restaurant_floors(instance,module){
         },
     });
 
+    // 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'],
@@ -28,6 +31,7 @@ function openerp_restaurant_floors(instance,module){
         },
     });
 
+    // The Table GUI element, should always be a child of the FloorScreenWidget
     module.TableWidget = module.PosBaseWidget.extend({
         template: 'TableWidget',
         init: function(parent, options){
@@ -39,6 +43,8 @@ function openerp_restaurant_floors(instance,module){
             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};
@@ -46,11 +52,13 @@ function openerp_restaurant_floors(instance,module){
                 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;
@@ -61,17 +69,23 @@ function openerp_restaurant_floors(instance,module){
                         }
                     } 
                 },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;
@@ -86,6 +100,7 @@ function openerp_restaurant_floors(instance,module){
                 $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;
@@ -107,9 +122,9 @@ function openerp_restaurant_floors(instance,module){
 
                 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;
@@ -146,6 +161,8 @@ function openerp_restaurant_floors(instance,module){
                 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'; }
@@ -169,6 +186,7 @@ function openerp_restaurant_floors(instance,module){
 
             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 = "";
@@ -177,15 +195,18 @@ function openerp_restaurant_floors(instance,module){
             }
             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');
@@ -200,12 +221,14 @@ function openerp_restaurant_floors(instance,module){
                 });
             });
         },
+        // 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++) {
@@ -246,6 +269,8 @@ function openerp_restaurant_floors(instance,module){
         },
     });
 
+    // 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,
@@ -495,6 +520,7 @@ function openerp_restaurant_floors(instance,module){
         },
     });
 
+    // Add the FloorScreen to the GUI, and set it as the default screen
     module.PosWidget.include({
         build_widgets: function(){
             var self = this;
@@ -508,4 +534,87 @@ function openerp_restaurant_floors(instance,module){
         },
     });
 
+    // 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);
+            }
+        },
+    });
 }