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