0e1e0253f547ea769af03d9f3805b17c3b2d8531
[odoo/odoo.git] / addons / pos_restaurant / static / src / js / floors.js
1 function openerp_restaurant_floors(instance,module){
2     var QWeb = instance.web.qweb;
3     var _t = instance.web._t;
4
5     // At POS Startup, load the floors, and add them to the pos model
6     module.PosModel.prototype.models.push({
7         model: 'restaurant.floor',
8         fields: ['name','background_image','table_ids'],
9         domain: function(self){ return [['pos_config_id','=',self.config.id]] },
10         loaded: function(self,floors){
11             self.floors = floors;
12             self.floors_by_id = {};
13             for (var i = 0; i < floors.length; i++) {
14                 floors[i].tables = [];
15                 self.floors_by_id[floors[i].id] = floors[i];
16             }
17             // Ignore floorplan features if no floor specified, or feature deactivated
18             self.config.iface_floorplan = self.config.iface_floorplan && !!self.floors.length;
19         },
20     });
21
22     // At POS Startup, after the floors are loaded, load the tables, and associate
23     // them with their floor. 
24     module.PosModel.prototype.models.push({
25         model: 'restaurant.table',
26         fields: ['name','width','height','position_h','position_v','shape','floor_id','color'],
27         loaded: function(self,tables){
28             self.tables_by_id = {};
29             for (var i = 0; i < tables.length; i++) {
30                 self.tables_by_id[tables[i].id] = tables[i];
31                 var floor = self.floors_by_id[tables[i].floor_id[0]];
32                 if (floor) {
33                     floor.tables.push(tables[i]);
34                     tables[i].floor = floor;
35                 }
36             }
37         },
38     });
39
40     // The Table GUI element, should always be a child of the FloorScreenWidget
41     module.TableWidget = module.PosBaseWidget.extend({
42         template: 'TableWidget',
43         init: function(parent, options){
44             this._super(parent, options)
45             this.table    = options.table;
46             this.selected = false;
47             this.moved    = false;
48             this.dragpos  = {x:0, y:0};
49             this.handle_dragging = false;
50             this.handle   = null;
51         },
52         // computes the absolute position of a DOM mouse event, used
53         // when resizing tables
54         event_position: function(event){
55             if(event.touches && event.touches[0]){
56                 return {x: event.touches[0].screenX, y: event.touches[0].screenY};
57             }else{
58                 return {x: event.screenX, y: event.screenY};
59             }
60         },
61         // when a table is clicked, go to the table's orders
62         // but if we're editing, we select/deselect it.
63         click_handler: function(){
64             var self = this;
65             var floorplan = this.getParent();
66             if (floorplan.editing) {
67                 setTimeout(function(){  // in a setTimeout to debounce with drag&drop start
68                     if (!self.dragging) {
69                         if (self.moved) {
70                             self.moved = false;
71                         } else if (!self.selected) {
72                             self.getParent().select_table(self);
73                         } else {
74                             self.getParent().deselect_tables();
75                         }
76                     } 
77                 },50);
78             } else {
79                 floorplan.pos.set_table(this.table);
80             }
81         },
82         // drag and drop for moving the table, at drag start
83         dragstart_handler: function(event,$el,drag){
84             if (this.selected && !this.handle_dragging) {
85                 this.dragging = true;
86                 this.dragpos  = { x: drag.offsetX, y: drag.offsetY };
87             }
88         },
89         // drag and drop for moving the table, at drag end
90         dragend_handler:   function(event,$el){
91             this.dragging = false;
92         },
93         // drag and drop for moving the table, at every drop movement.
94         dragmove_handler: function(event,$el,drag){
95             if (this.dragging) {
96                 var dx   = drag.offsetX - this.dragpos.x;
97                 var dy   = drag.offsetY - this.dragpos.y;
98
99                 this.dragpos = { x: drag.offsetX, y: drag.offsetY };
100                 this.moved   = true;
101
102                 this.table.position_v += dy;
103                 this.table.position_h += dx;
104
105                 $el.css(this.table_style());
106             } 
107         },
108         // drag and dropping the resizing handles
109         handle_dragstart_handler: function(event, $el, drag) {
110             if (this.selected && !this.dragging) {
111                 this.handle_dragging = true;
112                 this.handle_dragpos  = this.event_position(event);
113                 this.handle          = drag.target;
114             } 
115         },
116         handle_dragend_handler: function(event, $el, drag) {
117             this.handle_dragging = false;
118         },
119         handle_dragmove_handler: function(event, $el, drag) {
120             if (this.handle_dragging) {
121                 var pos  = this.event_position(event);
122                 var dx   = pos.x - this.handle_dragpos.x;
123                 var dy   = pos.y - this.handle_dragpos.y;
124
125                 this.handle_dragpos = pos;
126                 this.moved   = true;
127
128                 var cl     = this.handle.classList;
129
130                 var MIN_SIZE = 40;  // smaller than this, and it becomes impossible to edit.
131
132                 var tw = Math.max(MIN_SIZE, this.table.width);  
133                 var th = Math.max(MIN_SIZE, this.table.height);
134                 var tx = this.table.position_h;
135                 var ty = this.table.position_v;
136
137                 if (cl.contains('left') && tw - dx >= MIN_SIZE) {
138                     tw -= dx;
139                     tx += dx;
140                 } else if (cl.contains('right') && tw + dx >= MIN_SIZE) {
141                     tw += dx;
142                 }
143
144                 if (cl.contains('top') && th - dy >= MIN_SIZE) {
145                     th -= dy;
146                     ty += dy;
147                 } else if (cl.contains('bottom') && th + dy >= MIN_SIZE) {
148                     th += dy;
149                 }
150
151                 this.table.width  = tw;
152                 this.table.height = th;
153                 this.table.position_h = tx;
154                 this.table.position_v = ty;
155
156                 this.$el.css(this.table_style());
157             }
158         },
159         set_table_color: function(color){
160             this.table.color = color;
161             this.renderElement();
162         },
163         set_table_name: function(name){
164             if (name) {
165                 this.table.name = name;
166                 this.renderElement();
167             }
168         },
169         // The table's positioning is handled via css absolute positioning,
170         // which is handled here.
171         table_style: function(){
172             var table = this.table;
173             function unit(val){ return '' + val + 'px'; }
174             var style = {
175                 'width':        unit(table.width),
176                 'height':       unit(table.height),
177                 'line-height':  unit(table.height),
178                 'margin-left':  unit(-table.width/2),
179                 'margin-top':   unit(-table.height/2),
180                 'top':          unit(table.position_v + table.height/2),
181                 'left':         unit(table.position_h + table.width/2),
182                 'border-radius': table.shape === 'round' ? 
183                         unit(Math.max(table.width,table.height)/2) : '3px',
184             };
185             if (table.color) {
186                 style['background-color'] = table.color;
187             }
188             if (table.height >= 150 && table.width >= 150) {
189                 style['font-size'] = '32px';
190             } 
191
192             return style;
193         },
194         // convert the style dictionary to a ; separated string for inclusion in templates
195         table_style_str: function(){
196             var style = this.table_style();
197             var str = "";
198             for (s in style) {
199                 str += s + ":" + style[s] + "; ";
200             }
201             return str;
202         },
203         // select the table (should be called via the floorplan)
204         select: function() {
205             this.selected = true;
206             this.renderElement();
207         },
208         // deselect the table (should be called via the floorplan)
209         deselect: function() {
210             this.selected = false;
211             this.renderElement();
212             this.save_changes();
213         },
214         // sends the table's modification to the server
215         save_changes: function(){
216             var self   = this;
217             var model  = new instance.web.Model('restaurant.table');
218             var fields = _.find(this.pos.models,function(model){ return model.model === 'restaurant.table'; }).fields;
219
220             // we need a serializable copy of the table, containing only the fields defined on the server
221             var serializable_table = {};
222             for (var i = 0; i < fields.length; i++) {
223                 if (typeof this.table[fields[i]] !== 'undefined') {
224                     serializable_table[fields[i]] = this.table[fields[i]];
225                 }
226             }
227             // and the id ...
228             serializable_table.id = this.table.id
229
230             model.call('create_from_ui',[serializable_table]).then(function(table_id){
231                 model.query(fields).filter([['id','=',table_id]]).first().then(function(table){
232                     for (field in table) {
233                         self.table[field] = table[field];
234                     }
235                     self.renderElement();
236                 });
237             });
238         },
239         // destroy the table.  We do not really destroy it, we set it 
240         // to inactive so that it doesn't show up anymore, but it still
241         // available on the database for the orders that depend on it.
242         trash: function(){
243             var self  = this;
244             var model = new instance.web.Model('restaurant.table');
245             return model.call('create_from_ui',[{'active':false,'id':this.table.id}]).then(function(table_id){
246                 // Removing all references from the table and the table_widget in in the UI ... 
247                 for (var i = 0; i < self.pos.floors.length; i++) {
248                     var floor = self.pos.floors[i];
249                     for (var j = 0; j < floor.tables.length; j++) {
250                         if (floor.tables[j].id === table_id) {
251                             floor.tables.splice(j,1);
252                             break;
253                         }
254                     }
255                 }
256                 var floorplan = self.getParent();
257                 for (var i = 0; i < floorplan.table_widgets.length; i++) {
258                     if (floorplan.table_widgets[i] === self) {
259                         floorplan.table_widgets.splice(i,1);
260                     }
261                 }
262                 if (floorplan.selected_table === self) {
263                     floorplan.selected_table = null;
264                 }
265                 floorplan.update_toolbar();
266                 self.destroy();
267             });
268         },
269         get_notifications: function(){  //FIXME : Make this faster
270             var orders = this.pos.get_table_orders(this.table);
271             var notifications = {};
272             for (var i = 0; i < orders.length; i++) {
273                 if (orders[i].hasChangesToPrint()) {
274                     notifications['printing'] = true;
275                     break;
276                 }
277             }
278             return notifications
279         },
280         renderElement: function(){
281             var self = this;
282             this.order_count = this.pos.get_table_orders(this.table).length;
283             this.notifications = this.get_notifications();
284             this._super();
285
286             this.$el.on('mouseup',      function(event){ self.click_handler(event,$(this)); });
287             this.$el.on('touchend',     function(event){ self.click_handler(event,$(this)); });
288             this.$el.on('touchcancel',  function(event){ self.click_handler(event,$(this)); });
289             this.$el.on('dragstart', function(event,drag){ self.dragstart_handler(event,$(this),drag); });
290             this.$el.on('drag',      function(event,drag){ self.dragmove_handler(event,$(this),drag); });
291             this.$el.on('dragend',   function(event,drag){ self.dragend_handler(event,$(this),drag); });
292             
293             var handles = this.$el.find('.table-handle');
294             handles.on('dragstart',  function(event,drag){ self.handle_dragstart_handler(event,$(this),drag); });
295             handles.on('drag',       function(event,drag){ self.handle_dragmove_handler(event,$(this),drag); });
296             handles.on('dragend',    function(event,drag){ self.handle_dragend_handler(event,$(this),drag); });
297         },
298     });
299
300     // The screen that allows you to select the floor, see and select the table,
301     // as well as edit them.
302     module.FloorScreenWidget = module.ScreenWidget.extend({
303         template: 'FloorScreenWidget',
304         show_leftpane: false,
305
306         // Ignore products, discounts, and client barcodes
307         barcode_product_action: function(code){},
308         barcode_discount_action: function(code){},
309         barcode_client_action: function(code){},
310
311         init: function(parent, options) {
312             this._super(parent, options);
313             this.floor = this.pos.floors[0];
314             this.table_widgets = [];
315             this.selected_table = null;
316             this.editing = false;
317         },
318         hide: function(){
319             this._super();
320             if (this.editing) { 
321                 this.toggle_editing();
322             }
323             this.pos_widget.order_selector.show();
324         },
325         show: function(){
326             this._super();
327             this.pos_widget.order_selector.hide();
328             for (var i = 0; i < this.table_widgets.length; i++) { 
329                 this.table_widgets[i].renderElement();
330             }
331         },
332         click_floor_button: function(event,$el){
333             var floor = this.pos.floors_by_id[$el.data('id')];
334             if (floor !== this.floor) {
335                 if (this.editing) {
336                     this.toggle_editing();
337                 }
338                 this.floor = floor;
339                 this.selected_table = null;
340                 this.renderElement();
341             }
342         },
343         background_image_url: function(floor) { 
344             return '/web/binary/image?model=restaurant.floor&id='+floor.id+'&field=background_image';
345         },
346         deselect_tables: function(){
347             for (var i = 0; i < this.table_widgets.length; i++) {
348                 var table = this.table_widgets[i];
349                 if (table.selected) {
350                     table.deselect();
351                 }
352             }
353             this.selected_table = null;
354             this.update_toolbar();
355         },
356         select_table: function(table_widget){
357             if (!table_widget.selected) {
358                 this.deselect_tables();
359                 table_widget.select();
360                 this.selected_table = table_widget;
361                 this.update_toolbar();
362             }
363         },
364         tool_shape_action: function(){
365             if (this.selected_table) {
366                 var table = this.selected_table.table;
367                 if (table.shape === 'square') {
368                     table.shape = 'round';
369                 } else {
370                     table.shape = 'square';
371                 }
372                 this.selected_table.renderElement();
373                 this.update_toolbar();
374             }
375         },
376         tool_colorpicker_open: function(){
377             if (this.selected_table) {
378                 this.$('.color-picker').removeClass('oe_hidden');
379             }
380         },
381         tool_colorpicker_pick: function(event,$el){
382             if (this.selected_table) {
383                 this.selected_table.set_table_color($el[0].style['background-color']);
384             }
385         },
386         tool_colorpicker_close: function(){
387             this.$('.color-picker').addClass('oe_hidden');
388         },
389         tool_rename_table: function(){
390             var self = this;
391             if (this.selected_table) {
392                 this.pos_widget.screen_selector.show_popup('textinput',{
393                     'message':_t('Table Name ?'),
394                     'value': this.selected_table.table.name,
395                     'confirm': function(value) {
396                         self.selected_table.set_table_name(value);
397                     },
398                 });
399             }
400         },
401         tool_duplicate_table: function(){
402             if (this.selected_table) {
403                 var tw = this.create_table(this.selected_table.table);
404                 tw.table.position_h += 10;
405                 tw.table.position_v += 10;
406                 tw.save_changes();
407                 this.select_table(tw);
408             }
409         },
410         tool_new_table: function(){
411             var tw = this.create_table({
412                 'position_v': 50,
413                 'position_h': 50,
414                 'width': 50,
415                 'height': 50,
416                 'name': 'T1',
417                 'shape': 'square',
418             });
419             this.select_table(tw);
420         },
421         create_table: function(params) {
422             var table = {};
423             for (var p in params) {
424                 table[p] = params[p];
425             }
426
427             delete table['id']; 
428             table.floor_id = [this.floor.id,''];
429             table.floor = this.floor;
430             
431             this.floor.tables.push(table);
432             var tw = new module.TableWidget(this,{table: table});
433                 tw.appendTo('.floor-map');
434             this.table_widgets.push(tw);
435             return tw;
436         },
437         tool_trash_table: function(){
438             var self = this;
439             if (this.selected_table) {
440                 this.pos_widget.screen_selector.show_popup('confirm',{
441                     'message':_t('Are you sure ?'),
442                     'comment':_t('Removing a table cannot be undone'),
443                     'confirm': function(){
444                         self.selected_table.trash();
445                     },
446                 });
447             }
448         },
449         toggle_editing: function(){
450             this.editing = !this.editing;
451             this.update_toolbar();
452
453             if (!this.editing) {
454                 this.deselect_tables();
455             }
456         },
457         update_toolbar: function(){
458             
459             if (this.editing) {
460                 this.$('.edit-bar').removeClass('oe_hidden');
461                 this.$('.edit-button.editing').addClass('active');
462             } else {
463                 this.$('.edit-bar').addClass('oe_hidden');
464                 this.$('.edit-button.editing').removeClass('active');
465             }
466
467             if (this.selected_table) {
468                 this.$('.needs-selection').removeClass('disabled');
469                 var table = this.selected_table.table;
470                 if (table.shape === 'square') {
471                     this.$('.button-option.square').addClass('oe_hidden');
472                     this.$('.button-option.round').removeClass('oe_hidden');
473                 } else {
474                     this.$('.button-option.square').removeClass('oe_hidden');
475                     this.$('.button-option.round').addClass('oe_hidden');
476                 }
477             } else {
478                 this.$('.needs-selection').addClass('disabled');
479             }
480             this.tool_colorpicker_close();
481         },
482         renderElement: function(){
483             var self = this;
484
485             // cleanup table widgets from previous renders
486             for (var i = 0; i < this.table_widgets.length; i++) { 
487                 this.table_widgets[i].destroy();
488             }
489
490             this.table_widgets = [];
491
492             this._super();
493
494             for (var i = 0; i < this.floor.tables.length; i++) {
495                 var tw = new module.TableWidget(this,{
496                     table: this.floor.tables[i],
497                 });
498                 tw.appendTo(this.$('.floor-map'));
499                 this.table_widgets.push(tw);
500             }
501
502             this.$('.floor-selector .button').click(function(event){
503                 self.click_floor_button(event,$(this));
504             });
505
506             this.$('.edit-button.shape').click(function(event){
507                 self.tool_shape_action();
508             });
509
510             this.$('.edit-button.color').click(function(event){
511                 self.tool_colorpicker_open();
512             });
513
514             this.$('.edit-button.dup-table').click(function(event){
515                 self.tool_duplicate_table();
516             });
517
518             this.$('.edit-button.new-table').click(function(event){
519                 self.tool_new_table();
520             });
521
522             this.$('.edit-button.rename').click(function(event){
523                 self.tool_rename_table();
524             });
525
526             this.$('.edit-button.trash').click(function(event){
527                 self.tool_trash_table();
528             });
529             
530             this.$('.color-picker .close-picker').click(function(event){
531                 self.tool_colorpicker_close();
532                 event.stopPropagation();
533             });
534
535             this.$('.color-picker .color').click(function(event){
536                 self.tool_colorpicker_pick(event,$(this));
537                 self.tool_colorpicker_close();
538                 event.stopPropagation();
539             });
540
541             this.$('.edit-button.editing').click(function(){
542                 self.toggle_editing();
543             });
544
545             this.$('.floor-map').click(function(event){
546                 if (event.target === self.$('.floor-map')[0]) {
547                     self.deselect_tables();
548                 }
549             });
550
551             this.$('.color-picker .close-picker').click(function(event){
552                 self.tool_colorpicker_close();
553                 event.stopPropagation();
554             });
555
556             this.update_toolbar();
557
558         },
559     });
560
561     // Add the FloorScreen to the GUI, and set it as the default screen
562     module.PosWidget.include({
563         build_widgets: function(){
564             var self = this;
565             this._super();
566             if (this.pos.config.iface_floorplan) {
567                 this.floors_screen = new module.FloorScreenWidget(this,{});
568                 this.floors_screen.appendTo(this.$('.screens'));
569                 this.screen_selector.add_screen('floors',this.floors_screen);
570                 this.screen_selector.change_default_screen('floors');
571             }
572         },
573     });
574
575     // when the floor plan is activated, we need to go back to the floor plan
576     // when an order is completed. Usually on order completion, a new order is
577     // set as the current one. Now, we set the new order to null. 
578     // load_saved_screen() is called whenever the current order is changed, and 
579     // will detect this, and set the current screen to the default_screen, 
580     // which is the floor plan.
581     module.ScreenSelector.include({
582         load_saved_screen: function(){
583             if (this.pos.config.iface_floorplan) {
584                 if (!this.pos.get_order()) {
585                     this.set_current_screen(this.default_screen,null,'refresh');
586                 } else {
587                     this._super({default_screen:'products'});
588                 }
589             } else {
590                 this._super.apply(this,arguments);
591             }
592         },
593     });
594
595     // New orders are now associated with the current table, if any.
596     var _super_order = module.Order.prototype;
597     module.Order = module.Order.extend({
598         initialize: function(attr) {
599             _super_order.initialize.apply(this,arguments);
600             if (!this.table) {
601                 this.table = this.pos.table;
602             }
603             this.save_to_db();
604         },
605         export_as_JSON: function() {
606             var json = _super_order.export_as_JSON.apply(this,arguments);
607             json.table     = this.table ? this.table.name : undefined;
608             json.table_id  = this.table ? this.table.id : false;
609             json.floor     = this.table ? this.table.floor.name : false; 
610             json.floor_id  = this.table ? this.table.floor.id : false;
611             return json;
612         },
613         init_from_JSON: function(json) {
614             _super_order.init_from_JSON.apply(this,arguments);
615             this.table = this.pos.tables_by_id[json.table_id];
616             this.floor = this.table ? this.pos.floors_by_id[json.floor_id] : undefined;
617         },
618         export_for_printing: function() {
619             var json = _super_order.export_for_printing.apply(this,arguments);
620             json.table = this.table ? this.table.name : undefined;
621             json.floor = this.table ? this.table.floor.name : undefined;
622             return json;
623         },
624     });
625
626     // We need to modify the OrderSelector to hide itself when we're on
627     // the floor plan
628     module.OrderSelectorWidget.include({
629         floor_button_click_handler: function(){
630             this.pos.set_table(null);
631         },
632         hide: function(){
633             this.$el.addClass('oe_invisible');
634         },
635         show: function(){
636             this.$el.removeClass('oe_invisible');
637         },
638         renderElement: function(){
639             var self = this;
640             this._super();
641             if (this.pos.config.iface_floorplan) {
642                 if (this.pos.get_order()) {
643                     if (this.pos.table && this.pos.table.floor) {
644                         this.$('.orders').prepend(QWeb.render('BackToFloorButton',{table: this.pos.table, floor:this.pos.table.floor}));
645                         this.$('.floor-button').click(function(){
646                             self.floor_button_click_handler();
647                         });
648                     }
649                     this.$el.removeClass('oe_invisible');
650                 } else {
651                     this.$el.addClass('oe_invisible');
652                 }
653             }
654         },
655     });
656
657     // We need to change the way the regular UI sees the orders, it
658     // needs to only see the orders associated with the current table,
659     // and when an order is validated, it needs to go back to the floor map.
660     //
661     // And when we change the table, we must create an order for that table
662     // if there is none. 
663     var _super_posmodel = module.PosModel.prototype;
664     module.PosModel = module.PosModel.extend({
665         initialize: function(session, attributes) {
666             this.table = null;
667             return _super_posmodel.initialize.call(this,session,attributes);
668         },
669
670         // changes the current table. 
671         set_table: function(table) {
672             if (!table) { // no table ? go back to the floor plan, see ScreenSelector
673                 this.set_order(null);   
674             } else {     // table ? load the associated orders  ...
675                 this.table = table;
676                 var orders = this.get_order_list();
677                 if (orders.length) {   
678                     this.set_order(orders[0]); // and go to the first one ...
679                 } else { 
680                     this.add_new_order();  // or create a new order with the current table
681                 }
682             }
683         },
684
685         // if we have tables, we do not load a default order, as the default order will be
686         // set when the user selects a table.
687         set_start_order: function() {
688             if (!this.config.iface_floorplan) {
689                 _super_posmodel.set_start_order.apply(this,arguments);
690             }
691         },
692
693         // we need to prevent the creation of orders when there is no
694         // table selected.
695         add_new_order: function() {
696             if (this.config.iface_floorplan) {
697                 if (this.table) {
698                     _super_posmodel.add_new_order.call(this);
699                 } else {
700                     console.warn("WARNING: orders cannot be created when there is no active table in restaurant mode");
701                 }
702             } else {
703                 _super_posmodel.add_new_order.apply(this,arguments);
704             }
705         },
706
707
708         // get the list of unpaid orders (associated to the current table)
709         get_order_list: function() {    
710             var orders = _super_posmodel.get_order_list.call(this);  
711             if (!this.config.iface_floorplan) {
712                 return orders;
713             } else if (!this.table) {
714                 return [];
715             } else {
716                 var t_orders = [];
717                 for (var i = 0; i < orders.length; i++) {
718                     if ( orders[i].table === this.table) {
719                         t_orders.push(orders[i]);
720                     }
721                 }
722                 return t_orders;
723             }
724         },
725
726         // get the list of orders associated to a table. FIXME: should be O(1)
727         get_table_orders: function(table) {
728             var orders   = _super_posmodel.get_order_list.call(this);
729             var t_orders = [];
730             for (var i = 0; i < orders.length; i++) {
731                 if (orders[i].table === table) {
732                     t_orders.push(orders[i]);
733                 }
734             }
735             return t_orders;
736         },
737
738         // When we validate an order we go back to the floor plan. 
739         // When we cancel an order and there is multiple orders 
740         // on the table, stay on the table.
741         on_removed_order: function(removed_order,index,reason){
742             if (this.config.iface_floorplan) {
743                 var order_list = this.get_order_list();
744                 if( (reason === 'abandon' || removed_order.temporary) && order_list.length > 0){
745                     this.set_order(order_list[index] || order_list[order_list.length -1]);
746                 }else{
747                     // back to the floor plan
748                     this.set_table(null);
749                 }
750             } else {
751                 _super_posmodel.on_removed_order.apply(this,arguments);
752             }
753         },
754     });
755 }