[IMP] point_of_sale, pos_restaurant: Table seats

This commit is contained in:
Frédéric van der Essen
2015-02-02 16:29:32 +01:00
parent 386f5f99d6
commit 3335a79e6b
11 changed files with 247 additions and 42 deletions
+1 -1
View File
@@ -22,7 +22,7 @@
<field name="state" widget="statusbar" statusbar_visible="draft,paid,done" statusbar_colors='{"cancel":"red"}'/>
</header>
<sheet>
<group col="4" colspan="4">
<group col="4" colspan="4" name="order_fields">
<field name="name"/>
<field name="date_order"/>
<field name="session_id" required="1"/>
@@ -565,6 +565,20 @@ td {
.pos .control-button .fa{
margin-right: 4px;
}
.pos .control-button .control-button-number {
color: rgb(226, 226, 226);
background: rgb(85, 85, 85);
display: inline-block;
height: 28px;
vertical-align: middle;
font-weight: bold;
line-height: 28px;
width: 28px;
border-radius: 50%;
text-align: center;
margin-left: -16px;
margin-right: 4px;
}
.pos .control-button.highlight,
.pos .button.highlight {
@@ -2117,6 +2131,9 @@ td {
box-sizing: border-box;
width: 80%;
}
.pos .popup-number .popup-input {
text-align: center;
}
.pos .popup input:focus,
.pos .popup-input.active {
outline: none;
+1 -1
View File
@@ -194,7 +194,7 @@ openerp.point_of_sale.load_popups = function load_popups(instance, module) {
var self = this;
this._super(options);
this.inputbuffer = options.value || '';
this.inputbuffer = '' + options.value || '';
this.renderElement();
this.firstinput = true;
},
+21 -6
View File
@@ -666,6 +666,9 @@
<div>Served by <t t-esc='receipt.cashier' /></div>
</div>
</t>
<t t-if='!receipt.customer_count'>
<div>Guests: <t t-esc='receipt.customer_count' /></div>
</t>
</div>
<br /><br />
@@ -981,17 +984,29 @@
<button class="input-button number-char" data-action='1'>1</button>
<button class="input-button number-char" data-action='2'>2</button>
<button class="input-button number-char" data-action='3'>3</button>
<button class="mode-button add" data-action='+10'>+10</button>
<t t-if="widget.options.cheap">
<button class="mode-button add" data-action='+1'>+1</button>
</t><t t-if="!widget.options.cheap">
<button class="mode-button add" data-action='+10'>+10</button>
</t>
<br />
<button class="input-button number-char" data-action='4'>4</button>
<button class="input-button number-char" data-action='5'>5</button>
<button class="input-button number-char" data-action='6'>6</button>
<button class="mode-button add" data-action='+20'>+20</button>
<t t-if="widget.options.cheap">
<button class="mode-button add" data-action='+2'>+2</button>
</t><t t-if="!widget.options.cheap">
<button class="mode-button add" data-action='+20'>+20</button>
</t>
<br />
<button class="input-button number-char" data-action='7'>7</button>
<button class="input-button number-char" data-action='8'>8</button>
<button class="input-button number-char" data-action='9'>9</button>
<button class="mode-button add" data-action='+50'>+50</button>
<t t-if="widget.options.cheap">
<button class="mode-button add" data-action='+5'>+5</button>
</t><t t-if="!widget.options.cheap">
<button class="mode-button add" data-action='+50'>+50</button>
</t>
<br />
<button class="input-button numpad-char" data-action='CLEAR' >C</button>
<button class="input-button number-char" data-action='0'>0</button>
@@ -1002,12 +1017,12 @@
<br />
</div>
<div class="footer">
<div class="button confirm">
Ok
</div>
<div class="button cancel">
Cancel
</div>
<div class="button confirm">
Ok
</div>
</div>
</div>
</div>
+14 -5
View File
@@ -53,17 +53,19 @@ class restaurant_table(osv.osv):
'name': fields.char('Table Name', size=32, required=True, help='An internal identification of a table'),
'floor_id': fields.many2one('restaurant.floor','Floor'),
'shape': fields.selection([('square','Square'),('round','Round')],'Shape', required=True),
'position_h': fields.float('Horizontal Position', help="The table's horizontal position from the left side to the table's center, in percentage of the floor's width"),
'position_v': fields.float('Vertical Position', help="The table's vertical position from the top to the table's center, in percentage of the floor's height"),
'width': fields.float('Width', help="The table's width in percentage of the floor's width"),
'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"),
'position_h': fields.float('Horizontal Position', help="The table's horizontal position from the left side to the table's center, in pixels"),
'position_v': fields.float('Vertical Position', help="The table's vertical position from the top to the table's center, in pixels"),
'width': fields.float('Width', help="The table's width in pixels"),
'height': fields.float('Height', help="The table's height in pixels"),
'seats': fields.integer('Seats', help="The default number of customer served at this table."),
'color': fields.char('Color', help="The table's color, expressed as a valid 'background' CSS property value"),
'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 = {
'shape': 'square',
'seats': 1,
'position_h': 10,
'position_v': 10,
'height': 50,
@@ -121,4 +123,11 @@ class pos_order(osv.osv):
_inherit = 'pos.order'
_columns = {
'table_id': fields.many2one('restaurant.table','Table', help='The table where this order was served'),
'customer_count' : fields.integer('Guests', help='The amount of customers that have been served by this order.'),
}
def _order_fields(self, cr, uid, ui_order, context=None):
fields = super(pos_order,self)._order_fields(cr,uid,ui_order,context)
fields['table_id'] = ui_order.get('table_id',0)
fields['customer_count'] = ui_order.get('customer_count',0)
return fields
+20 -2
View File
@@ -2,6 +2,21 @@
<openerp>
<data>
<!-- POS ORDERS -->
<record model="ir.ui.view" id="view_pos_pos_form">
<field name="name">pos.order.form.view.inherit</field>
<field name="model">pos.order</field>
<field name="inherit_id" ref="point_of_sale.view_pos_pos_form"></field>
<field name="arch" type="xml">
<xpath expr="//group[@name='order_fields']" position="inside">
<field name="table_id"/>
<field name="customer_count"/>
</xpath>
</field>
</record>
<!-- RESTAURANTS FLOORS -->
<record model="ir.ui.view" id="view_restaurant_floor_form">
@@ -19,6 +34,7 @@
<field name="table_ids">
<tree string='Tables'>
<field name="name" />
<field name="seats" />
<field name="shape" />
</tree>
</field>
@@ -62,10 +78,12 @@
<form string="Restaurant Table">
<group col="2">
<field name="name" />
<field name="floor_id" />
<field name="seats" />
</group>
<group col="4" string="Appearance">
<field name="shape" />
<field name="color" />
</group>
<group col="4">
<field name="position_h" />
<field name="position_v" />
<field name="width" />
@@ -49,6 +49,7 @@
background-repeat: no-repeat;
overflow: hidden;
background-size: cover;
transition: all 300ms ease-in-out;
}
@media screen and (min-width: 1024px) {
@@ -80,11 +81,42 @@
border-radius: 3px;
cursor: pointer;
box-shadow: 0px 3px rgba(0,0,0,0.07);
transition: background, background-color 300ms ease-in-out;
}
.floor-map .table .table-cover {
display: block;
position: absolute;
left: 0; right: 0; bottom: 0;
border-radius: 0px 0px 3px 3px;
background: rgba(0,0,0,0.2);
}
.floor-map .table .table-cover.full {
border-radius: 3px 3px 3px 3px;
}
.floor-map .table .table-seats {
position: absolute;
display: inline-block;
bottom: 0;
left: 50%;
height: 20px;
width: 20px;
line-height: 20px;
font-size: 16px;
border-radius: 50%;
margin-left: -10px;
margin-bottom: 4px;
background: black;
color: white;
opacity: 0.2;
z-index: 3;
}
.floor-map .table .label {
display: block;
max-height: 100%;
overflow: hidden;
position: relative;
bottom: 5px;
z-index: 5;
}
.floor-map .table.selected {
outline: solid rgba(255,255,255,0.3);
@@ -96,7 +128,7 @@
top: 0;
right: 0;
font-size: 20px;
margin: 4px;
margin: 8px;
line-height: 32px;
width: 32px;
text-align: center;
@@ -106,20 +138,20 @@
}
.floor-map .edit-button.editing.active {
background: #444;
border-color: transparent;
color: white;
}
.floor-map .edit-bar {
position: absolute;
top: 0;
right: 40px;
margin: 4px;
line-height: 32px;
margin: 8px;
line-height: 34px;
text-align: center;
border-radius: 5px;
cursor: pointer;
font-size: 20px;
border: solid 1px rgba(0,0,0,0.2);
background: #F0EEEE;
background: rgba(255,255,255,0.5);
}
.floor-map .edit-bar .edit-button {
position: relative;
@@ -127,14 +159,15 @@
display: inline-block;
cursor: pointer;
margin-right: -4px;
border-right: solid 1px rgba(0,0,0,0.1);
border-right: solid 1px rgba(0,0,0,0.2);
transition: all 150ms linear;
}
.floor-map .edit-bar .edit-button.disabled {
opacity: 0.5;
border-right: solid 1px rgba(0,0,0,0.2);
cursor: default;
}
.floor-map .edit-bar .edit-button.disabled > * {
opacity: 0.5;
}
.floor-map .edit-bar .color-picker {
position: absolute;
left: -106px;
@@ -192,6 +225,7 @@
box-shadow: 0px 2px 3px rgba(0,0,0,0.2);
cursor: grab;
transition: all 150ms linear;
z-index: 100;
}
.floor-map .table.selected .table-handle:hover {
width: 20px;
@@ -217,6 +251,7 @@
line-height: 20px;
border-radius: 10px;
font-size: 16px;
z-index: 10;
}
.floor-map .table .order-count.notify-printing {
background: red;
+108 -12
View File
@@ -27,7 +27,7 @@ openerp.pos_restaurant.load_floors = function(instance,module){
// them with their floor.
module.load_models({
model: 'restaurant.table',
fields: ['name','width','height','position_h','position_v','shape','floor_id','color'],
fields: ['name','width','height','position_h','position_v','shape','floor_id','color','seats'],
loaded: function(self,tables){
self.tables_by_id = {};
for (var i = 0; i < tables.length; i++) {
@@ -161,8 +161,8 @@ openerp.pos_restaurant.load_floors = function(instance,module){
}
},
set_table_color: function(color){
this.table.color = color;
this.renderElement();
this.table.color = _.escape(color);
this.$el.css({'background': this.table.color});
},
set_table_name: function(name){
if (name) {
@@ -170,6 +170,12 @@ openerp.pos_restaurant.load_floors = function(instance,module){
this.renderElement();
}
},
set_table_seats: function(seats){
if (seats) {
this.table.seats = Number(seats);
this.renderElement();
}
},
// The table's positioning is handled via css absolute positioning,
// which is handled here.
table_style: function(){
@@ -187,7 +193,7 @@ openerp.pos_restaurant.load_floors = function(instance,module){
unit(Math.max(table.width,table.height)/2) : '3px',
};
if (table.color) {
style['background-color'] = table.color;
style['background'] = table.color;
}
if (table.height >= 150 && table.width >= 150) {
style['font-size'] = '32px';
@@ -290,8 +296,10 @@ openerp.pos_restaurant.load_floors = function(instance,module){
},
renderElement: function(){
var self = this;
this.order_count = this.pos.get_table_orders(this.table).length;
this.notifications = this.get_notifications();
this.order_count = this.pos.get_table_orders(this.table).length;
this.customer_count = this.pos.get_customer_count(this.table);
this.fill = Math.min(1,Math.max(0,this.customer_count / this.table.seats));
this.notifications = this.get_notifications();
this._super();
this.$el.on('mouseup', function(event){ self.click_handler(event,$(this)); });
@@ -375,7 +383,7 @@ openerp.pos_restaurant.load_floors = function(instance,module){
event.stopPropagation();
event.preventDefault();
});
this.renderElement();
this.$('.floor-map').css({"background-color": _.escape(background)});
},
deselect_tables: function(){
for (var i = 0; i < this.table_widgets.length; i++) {
@@ -437,6 +445,19 @@ openerp.pos_restaurant.load_floors = function(instance,module){
});
}
},
tool_change_seats: function(){
var self = this;
if (this.selected_table) {
this.gui.show_popup('number',{
'title':_t('Number of Seats ?'),
'cheap': true,
'value': this.selected_table.table.seats,
'confirm': function(value) {
self.selected_table.set_table_seats(value);
},
});
}
},
tool_duplicate_table: function(){
if (this.selected_table) {
var tw = this.create_table(this.selected_table.table);
@@ -448,12 +469,13 @@ openerp.pos_restaurant.load_floors = function(instance,module){
},
tool_new_table: function(){
var tw = this.create_table({
'position_v': 50,
'position_h': 50,
'width': 50,
'height': 50,
'position_v': 100,
'position_h': 100,
'width': 75,
'height': 75,
'name': 'T1',
'shape': 'square',
'seats': 1,
});
this.select_table(tw);
},
@@ -562,6 +584,10 @@ openerp.pos_restaurant.load_floors = function(instance,module){
self.tool_rename_table();
});
this.$('.edit-button.seats').click(function(event){
self.tool_change_seats();
});
this.$('.edit-button.trash').click(function(event){
self.tool_trash_table();
});
@@ -573,7 +599,6 @@ openerp.pos_restaurant.load_floors = function(instance,module){
this.$('.color-picker .color').click(function(event){
self.tool_colorpicker_pick(event,$(this));
self.tool_colorpicker_close();
event.stopPropagation();
});
@@ -624,6 +649,13 @@ openerp.pos_restaurant.load_floors = function(instance,module){
if (!this.table) {
this.table = this.pos.table;
}
if (!this.customer_count) {
if (this.table) {
this.customer_count = this.table.seats;
} else {
this.customer_count = 1;
}
}
this.save_to_db();
},
export_as_JSON: function() {
@@ -632,19 +664,29 @@ openerp.pos_restaurant.load_floors = function(instance,module){
json.table_id = this.table ? this.table.id : false;
json.floor = this.table ? this.table.floor.name : false;
json.floor_id = this.table ? this.table.floor.id : false;
json.customer_count = this.customer_count;
return json;
},
init_from_JSON: function(json) {
_super_order.init_from_JSON.apply(this,arguments);
this.table = this.pos.tables_by_id[json.table_id];
this.floor = this.table ? this.pos.floors_by_id[json.floor_id] : undefined;
this.customer_count = json.customer_count || 1;
},
export_for_printing: function() {
var json = _super_order.export_for_printing.apply(this,arguments);
json.table = this.table ? this.table.name : undefined;
json.floor = this.table ? this.table.floor.name : undefined;
json.customer_count = this.get_customer_count();
return json;
},
get_customer_count: function(){
return this.customer_count;
},
set_customer_count: function(count) {
this.customer_count = Math.max(count,0);
this.trigger('change');
},
});
// We need to modify the OrderSelector to hide itself when we're on
@@ -759,6 +801,16 @@ openerp.pos_restaurant.load_floors = function(instance,module){
return t_orders;
},
// get customer count at table
get_customer_count: function(table) {
var orders = this.get_table_orders(table);
var count = 0;
for (var i = 0; i < orders.length; i++) {
count += orders[i].get_customer_count();
}
return count;
},
// 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.
@@ -775,6 +827,50 @@ openerp.pos_restaurant.load_floors = function(instance,module){
_super_posmodel.on_removed_order.apply(this,arguments);
}
},
});
module.TableGuestsButton = module.ActionButtonWidget.extend({
template: 'TableGuestsButton',
guests: function() {
if (this.pos.get_order()) {
return this.pos.get_order().customer_count;
} else {
return 0;
}
},
button_click: function() {
var self = this;
this.gui.show_popup('number', {
'title': _t('Guests ?'),
'cheap': true,
'value': this.pos.get_order().customer_count,
'confirm': function(value) {
value = Math.max(1,Number(value));
self.pos.get_order().set_customer_count(value);
self.renderElement();
},
});
},
});
module.OrderWidget.include({
update_summary: function(){
this._super();
if (this.getParent().action_buttons &&
this.getParent().action_buttons.guests) {
this.getParent().action_buttons.guests.renderElement();
}
},
});
module.define_action_button({
'name': 'guests',
'widget': module.TableGuestsButton,
'condition': function(){
return this.pos.config.iface_floorplan;
},
});
};
@@ -229,13 +229,6 @@ openerp.pos_restaurant.load_multiprint = function(instance,module){
order.saveChanges();
}
},
hilight: function(hi){
if (hi) {
this.$el.addClass('highlight');
} else {
this.$el.removeClass('highlight');
}
},
});
module.define_action_button({
@@ -125,6 +125,9 @@ openerp.pos_restaurant.load_splitbill = function(instance, module){
neworder.saveChanges();
}
neworder.set_customer_count(1);
order.set_customer_count(order.get_customer_count() - 1);
this.pos.get('orders').add(neworder);
this.pos.set('selectedOrder',neworder);
}
@@ -17,15 +17,30 @@
</t>
</t>
<t t-name="TableGuestsButton">
<div class='control-button'>
<span class='control-button-number'>
<t t-esc="widget.guests()" />
</span>
Guests
</div>
</t>
<t t-name="TableWidget">
<t t-if='!widget.selected'>
<div class='table' t-att-style='widget.table_style_str()'>
<span
t-if="widget.table.shape !== 'round'"
t-att-class='"table-cover " + (widget.fill >= 1 ? "full" : "")'
t-att-style='"height: " + Math.ceil(widget.fill * 100) + "%;"'
></span>
<t t-if='widget.order_count'>
<span t-att-class='"order-count " + (widget.notifications.printing ? "notify-printing":"")'><t t-esc='widget.order_count'/></span>
</t>
<span class='label'>
<t t-esc='widget.table.name' />
</span>
<span class="table-seats"><t t-esc="widget.table.seats" /></span>
</div>
</t>
<t t-if='widget.selected'>
@@ -33,6 +48,7 @@
<span class='label'>
<t t-esc='widget.table.name' />
</span>
<span class="table-seats"><t t-esc="widget.table.seats" /></span>
<span class='table-handle top'></span>
<span class='table-handle bottom'></span>
<span class='table-handle left'></span>
@@ -82,6 +98,9 @@
<span class='edit-button rename needs-selection'>
<i class='fa fa-font'></i>
</span>
<span class='edit-button seats needs-selection'>
<i class='fa fa-user'></i>
</span>
<span class='edit-button shape needs-selection'>
<span class='button-option square'><i class='fa fa-square-o'></i></span>
<span class='button-option round oe_hidden'><i class='fa fa-circle-o'></i></span>