Implement a new Odoo module for restaurant table reservations with a public web form.
This commit is contained in:
parent
4621645621
commit
d0ecc73453
2
addons/dine360_reservation/__init__.py
Normal file
2
addons/dine360_reservation/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
27
addons/dine360_reservation/__manifest__.py
Normal file
27
addons/dine360_reservation/__manifest__.py
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
'name': 'Dine360 Table Reservation',
|
||||
'version': '1.0',
|
||||
'category': 'Sales/Restaurant',
|
||||
'summary': 'Advanced Table Reservation System with Time Slots and Overlap Prevention',
|
||||
'description': """
|
||||
Advanced Table Reservation System:
|
||||
- Time Slot based reservations
|
||||
- Overlap prevention
|
||||
- Auto-completion of past reservations
|
||||
- WhatsApp/SMS notification hooks
|
||||
""",
|
||||
'author': 'Dine360',
|
||||
'depends': ['base', 'website', 'pos_restaurant'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/reservation_sequence.xml',
|
||||
'data/reservation_cron.xml',
|
||||
'data/website_menu.xml',
|
||||
'views/reservation_views.xml',
|
||||
'views/reservation_templates.xml',
|
||||
'views/menu_items.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
BIN
addons/dine360_reservation/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
addons/dine360_reservation/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
1
addons/dine360_reservation/controllers/__init__.py
Normal file
1
addons/dine360_reservation/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import main
|
||||
Binary file not shown.
Binary file not shown.
54
addons/dine360_reservation/controllers/main.py
Normal file
54
addons/dine360_reservation/controllers/main.py
Normal file
@ -0,0 +1,54 @@
|
||||
from odoo import http, _
|
||||
from odoo.http import request
|
||||
import datetime
|
||||
|
||||
class TableReservationController(http.Controller):
|
||||
|
||||
@http.route(['/reservation'], type='http', auth="public", website=True)
|
||||
def reservation_form(self, **post):
|
||||
floors = request.env['restaurant.floor'].sudo().search([])
|
||||
tables = request.env['restaurant.table'].sudo().search([])
|
||||
return request.render("dine360_reservation.reservation_page_template", {
|
||||
'floors': floors,
|
||||
'tables': tables,
|
||||
})
|
||||
|
||||
@http.route(['/reservation/submit'], type='http', auth="public", website=True, methods=['POST'], csrf=True)
|
||||
def reservation_submit(self, **post):
|
||||
# Extract data from post
|
||||
customer_name = post.get('customer_name')
|
||||
phone = post.get('phone')
|
||||
email = post.get('email')
|
||||
floor_id = int(post.get('floor_id'))
|
||||
table_id = int(post.get('table_id'))
|
||||
start_time_str = post.get('start_time') # Format: 2024-05-20T18:00
|
||||
num_people = int(post.get('num_people', 1))
|
||||
|
||||
# Convert start_time to datetime object
|
||||
start_time = datetime.datetime.strptime(start_time_str, '%Y-%m-%dT%H:%M')
|
||||
# Standard duration of 1 hour for now
|
||||
end_time = start_time + datetime.timedelta(hours=1)
|
||||
|
||||
# Create reservation
|
||||
try:
|
||||
reservation = request.env['restaurant.reservation'].sudo().create({
|
||||
'customer_name': customer_name,
|
||||
'phone': phone,
|
||||
'email': email,
|
||||
'floor_id': floor_id,
|
||||
'table_id': table_id,
|
||||
'start_time': start_time,
|
||||
'end_time': end_time,
|
||||
'num_people': num_people,
|
||||
'state': 'draft'
|
||||
})
|
||||
return request.render("dine360_reservation.reservation_success_template", {
|
||||
'reservation': reservation,
|
||||
})
|
||||
except Exception as e:
|
||||
return request.render("dine360_reservation.reservation_page_template", {
|
||||
'error': str(e),
|
||||
'floors': request.env['restaurant.floor'].sudo().search([]),
|
||||
'tables': request.env['restaurant.table'].sudo().search([]),
|
||||
'post': post,
|
||||
})
|
||||
16
addons/dine360_reservation/data/reservation_cron.xml
Normal file
16
addons/dine360_reservation/data/reservation_cron.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="ir_cron_auto_complete_reservations" model="ir.cron">
|
||||
<field name="name">Restaurant: Auto-complete Past Reservations</field>
|
||||
<field name="model_id" ref="model_restaurant_reservation"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._auto_complete_reservations()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
12
addons/dine360_reservation/data/reservation_sequence.xml
Normal file
12
addons/dine360_reservation/data/reservation_sequence.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="seq_restaurant_reservation" model="ir.sequence">
|
||||
<field name="name">Restaurant Reservation</field>
|
||||
<field name="code">restaurant.reservation</field>
|
||||
<field name="prefix">RES/%(year)s/</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
12
addons/dine360_reservation/data/website_menu.xml
Normal file
12
addons/dine360_reservation/data/website_menu.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="menu_reservation" model="website.menu">
|
||||
<field name="name">Table Reservation</field>
|
||||
<field name="url">/reservation</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">40</field>
|
||||
<field name="website_id" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
1
addons/dine360_reservation/models/__init__.py
Normal file
1
addons/dine360_reservation/models/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import restaurant_reservation
|
||||
Binary file not shown.
Binary file not shown.
89
addons/dine360_reservation/models/restaurant_reservation.py
Normal file
89
addons/dine360_reservation/models/restaurant_reservation.py
Normal file
@ -0,0 +1,89 @@
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from datetime import timedelta
|
||||
|
||||
class RestaurantReservation(models.Model):
|
||||
_name = 'restaurant.reservation'
|
||||
_description = 'Restaurant Table Reservation'
|
||||
_order = 'start_time desc'
|
||||
|
||||
name = fields.Char(string='Reservation Reference', required=True, copy=False, readonly=True, default=lambda self: _('New'))
|
||||
customer_name = fields.Char(string='Customer Name', required=True)
|
||||
phone = fields.Char(string='Phone Number', required=True)
|
||||
email = fields.Char(string='Email')
|
||||
num_people = fields.Integer(string='Number of People', default=1)
|
||||
|
||||
floor_id = fields.Many2one('restaurant.floor', string='Floor', required=True)
|
||||
table_id = fields.Many2one('restaurant.table', string='Table', required=True)
|
||||
|
||||
start_time = fields.Datetime(string='Start Time', required=True)
|
||||
end_time = fields.Datetime(string='End Time', required=True)
|
||||
|
||||
whatsapp_url = fields.Char(compute='_compute_whatsapp_url')
|
||||
|
||||
def _compute_whatsapp_url(self):
|
||||
for rec in self:
|
||||
if rec.phone:
|
||||
msg = f"Hello {rec.customer_name}, your reservation {rec.name} for {rec.start_time.strftime('%I:%M %p')} is confirmed!"
|
||||
rec.whatsapp_url = f"https://wa.me/{rec.phone}?text={msg.replace(' ', '%20')}"
|
||||
else:
|
||||
rec.whatsapp_url = False
|
||||
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
('confirmed', 'Confirmed'),
|
||||
('completed', 'Completed'),
|
||||
('cancelled', 'Cancelled')
|
||||
], string='Status', default='draft', tracking=True)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if vals.get('name', _('New')) == _('New'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('restaurant.reservation') or _('New')
|
||||
return super(RestaurantReservation, self).create(vals)
|
||||
|
||||
@api.constrains('table_id', 'start_time', 'end_time', 'state')
|
||||
def _check_overlap(self):
|
||||
for rec in self:
|
||||
if rec.state in ['confirmed', 'completed']:
|
||||
overlap = self.search([
|
||||
('id', '!=', rec.id),
|
||||
('table_id', '=', rec.table_id.id),
|
||||
('state', '=', 'confirmed'),
|
||||
('start_time', '<', rec.end_time),
|
||||
('end_time', '>', rec.start_time),
|
||||
])
|
||||
if overlap:
|
||||
raise ValidationError(_('This table is already reserved for the selected time slot.'))
|
||||
|
||||
@api.onchange('start_time')
|
||||
def _onchange_start_time(self):
|
||||
if self.start_time:
|
||||
self.end_time = self.start_time + timedelta(hours=1)
|
||||
|
||||
def action_confirm(self):
|
||||
self.write({'state': 'confirmed'})
|
||||
self._send_confirmation_notification()
|
||||
|
||||
def action_complete(self):
|
||||
self.write({'state': 'completed'})
|
||||
|
||||
def action_cancel(self):
|
||||
self.write({'state': 'cancelled'})
|
||||
|
||||
def _send_confirmation_notification(self):
|
||||
""" Placeholder for WhatsApp/SMS logic """
|
||||
for rec in self:
|
||||
# Logic for WhatsApp/SMS can be added here
|
||||
# e.g., self.env['sms.api']._send_sms(rec.phone, "Your table is confirmed!")
|
||||
pass
|
||||
|
||||
@api.model
|
||||
def _auto_complete_reservations(self):
|
||||
""" Scheduled action to mark past reservations as completed """
|
||||
now = fields.Datetime.now()
|
||||
past_reservations = self.search([
|
||||
('state', '=', 'confirmed'),
|
||||
('end_time', '<', now)
|
||||
])
|
||||
past_reservations.write({'state': 'completed'})
|
||||
2
addons/dine360_reservation/security/ir.model.access.csv
Normal file
2
addons/dine360_reservation/security/ir.model.access.csv
Normal file
@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_restaurant_reservation_user,restaurant.reservation,model_restaurant_reservation,base.group_user,1,1,1,1
|
||||
|
15
addons/dine360_reservation/views/menu_items.xml
Normal file
15
addons/dine360_reservation/views/menu_items.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Main Menu -->
|
||||
<menuitem id="menu_restaurant_reservation_root"
|
||||
name="Reservations"
|
||||
sequence="20"
|
||||
web_icon="dine360_reservation,static/description/icon.png"/>
|
||||
|
||||
<!-- Sub Menu -->
|
||||
<menuitem id="menu_restaurant_reservation_list"
|
||||
name="Table Reservations"
|
||||
parent="menu_restaurant_reservation_root"
|
||||
action="action_restaurant_reservation"
|
||||
sequence="10"/>
|
||||
</odoo>
|
||||
130
addons/dine360_reservation/views/reservation_templates.xml
Normal file
130
addons/dine360_reservation/views/reservation_templates.xml
Normal file
@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="reservation_page_template" name="Table Reservation Form">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure oe_empty">
|
||||
<section class="s_reservation_form pt64 pb64" style="background: linear-gradient(rgba(0,0,0,0.6), rgba(0,0,0,0.6)), url('/dine360_theme_chennora/static/src/img/bg_reservation.jpg') center/cover no-repeat;">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-7">
|
||||
<div class="card border-0 shadow-lg" style="background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); border-radius: 20px;">
|
||||
<div class="card-body p-5">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="display-4 fw-bold mb-2" style="color: #fecd4f;">Table Reservation</h2>
|
||||
<p class="text-muted">Book your spot for an authentic South Indian dining experience.</p>
|
||||
</div>
|
||||
|
||||
<t t-if="error">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<t t-esc="error"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<form action="/reservation/submit" method="post" class="s_website_form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Full Name</label>
|
||||
<input type="text" name="customer_name" class="form-control form-control-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" placeholder="John Doe" required="1" t-att-value="post.get('customer_name') if post else ''"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Phone Number</label>
|
||||
<input type="tel" name="phone" class="form-control form-control-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" placeholder="+1 (647) 000-0000" required="1" t-att-value="post.get('phone') if post else ''"/>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-bold">Email (Optional)</label>
|
||||
<input type="email" name="email" class="form-control form-control-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" placeholder="john@example.com" t-att-value="post.get('email') if post else ''"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Select Floor</label>
|
||||
<select name="floor_id" id="floor_id" class="form-select form-select-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" required="1">
|
||||
<option value="">Choose a Floor...</option>
|
||||
<t t-foreach="floors" t-as="floor">
|
||||
<option t-att-value="floor.id"><t t-esc="floor.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Select Table</label>
|
||||
<select name="table_id" id="table_id" class="form-select form-select-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" required="1">
|
||||
<option value="">Choose a Table...</option>
|
||||
<t t-foreach="tables" t-as="table">
|
||||
<option t-att-value="table.id" t-att-data-floor="table.floor_id.id"><t t-esc="table.name"/> (Cap: <t t-esc="table.seats"/>)</option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Number of Guests</label>
|
||||
<input type="number" name="num_people" class="form-control form-control-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" value="2" min="1" required="1" t-att-value="post.get('num_people') if post else 2"/>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label fw-bold">Reservation Date & Time</label>
|
||||
<input type="datetime-local" name="start_time" class="form-control form-control-lg border-0 shadow-sm" style="background: #f8f9fa; border-radius: 12px; color: #171422 !important; border: 1px solid #fecd4f !important;" required="1" t-att-value="post.get('start_time') if post else ''"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<button type="submit" class="btn btn-lg w-100 shadow-sm transition-all text-dark" style="background: #fecd4f; border-radius: 12px; height: 60px; font-weight: 700; font-size: 1.1rem;">
|
||||
CONFIRM RESERVATION
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('floor_id').addEventListener('change', function() {
|
||||
var floorId = this.value;
|
||||
var tableSelect = document.getElementById('table_id');
|
||||
var tableOptions = tableSelect.querySelectorAll('option');
|
||||
|
||||
tableSelect.value = "";
|
||||
tableOptions.forEach(function(option) {
|
||||
if (option.value === "") {
|
||||
option.style.display = "block";
|
||||
} else if (option.getAttribute('data-floor') === floorId) {
|
||||
option.style.display = "block";
|
||||
} else {
|
||||
option.style.display = "none";
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="reservation_success_template" name="Reservation Success">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure">
|
||||
<section class="pt64 pb64 text-center">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="bg-success-light p-5 rounded-circle d-inline-block mb-4" style="background: rgba(43, 177, 165, 0.1);">
|
||||
<i class="fa fa-check-circle text-success display-1"></i>
|
||||
</div>
|
||||
<h1 class="display-3 fw-bold mb-3">Thank You!</h1>
|
||||
<p class="lead text-muted mb-5">Your reservation request <strong><t t-esc="reservation.name"/></strong> has been received. We will contact you shortly to confirm.</p>
|
||||
<div class="card border-0 shadow-sm p-4 mb-5" style="border-radius: 15px; background: #f8f9fa;">
|
||||
<div class="row text-start g-3">
|
||||
<div class="col-6"><strong>Floor:</strong> <t t-esc="reservation.floor_id.name"/></div>
|
||||
<div class="col-6"><strong>Table:</strong> <t t-esc="reservation.table_id.name"/></div>
|
||||
<div class="col-6"><strong>Guests:</strong> <t t-esc="reservation.num_people"/></div>
|
||||
<div class="col-12"><strong>Time:</strong> <t t-esc="reservation.start_time.strftime('%B %d, %Y at %I:%M %p')"/></div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/" class="btn btn-lg text-dark" style="background: #fecd4f; border-radius: 12px; padding-left: 40px; padding-right: 40px; font-weight: 600;">BACK TO HOME</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
98
addons/dine360_reservation/views/reservation_views.xml
Normal file
98
addons/dine360_reservation/views/reservation_views.xml
Normal file
@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Tree View -->
|
||||
<record id="view_restaurant_reservation_tree" model="ir.ui.view">
|
||||
<field name="name">restaurant.reservation.tree</field>
|
||||
<field name="model">restaurant.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Reservations" decoration-info="state == 'draft'" decoration-success="state == 'confirmed'" decoration-muted="state == 'completed'">
|
||||
<field name="name"/>
|
||||
<field name="customer_name"/>
|
||||
<field name="phone"/>
|
||||
<field name="floor_id"/>
|
||||
<field name="table_id"/>
|
||||
<field name="start_time"/>
|
||||
<field name="end_time"/>
|
||||
<field name="num_people"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'draft'" decoration-success="state == 'confirmed'"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Form View -->
|
||||
<record id="view_restaurant_reservation_form" model="ir.ui.view">
|
||||
<field name="name">restaurant.reservation.form</field>
|
||||
<field name="model">restaurant.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Reservation">
|
||||
<header>
|
||||
<button name="action_confirm" string="Confirm" type="object" class="oe_highlight" invisible="state != 'draft'"/>
|
||||
<button name="action_complete" string="Mark Completed" type="object" invisible="state != 'confirmed'"/>
|
||||
<button name="action_cancel" string="Cancel" type="object" invisible="state not in ['draft', 'confirmed']"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,completed"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="whatsapp_url" invisible="1"/>
|
||||
<button name="whatsapp_url" type="url" class="oe_stat_button" icon="fa-whatsapp" string="WhatsApp" invisible="not whatsapp_url"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Customer Information">
|
||||
<field name="customer_name"/>
|
||||
<field name="phone"/>
|
||||
<field name="email"/>
|
||||
<field name="num_people"/>
|
||||
</group>
|
||||
<group string="Reservation Details">
|
||||
<field name="floor_id"/>
|
||||
<field name="table_id" domain="[('floor_id', '=', floor_id)]"/>
|
||||
<field name="start_time"/>
|
||||
<field name="end_time"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search View -->
|
||||
<record id="view_restaurant_reservation_search" model="ir.ui.view">
|
||||
<field name="name">restaurant.reservation.search</field>
|
||||
<field name="model">restaurant.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="name"/>
|
||||
<field name="customer_name"/>
|
||||
<field name="phone"/>
|
||||
<field name="table_id"/>
|
||||
<filter string="Draft" name="draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter string="Confirmed" name="confirmed" domain="[('state', '=', 'confirmed')]"/>
|
||||
<filter string="Completed" name="completed" domain="[('state', '=', 'completed')]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Table" name="group_by_table" context="{'group_by': 'table_id'}"/>
|
||||
<filter string="Status" name="group_by_status" context="{'group_by': 'state'}"/>
|
||||
<filter string="Date" name="group_by_date" context="{'group_by': 'start_time'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action -->
|
||||
<record id="action_restaurant_reservation" model="ir.actions.act_window">
|
||||
<field name="name">Table Reservations</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">restaurant.reservation</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_restaurant_reservation_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create your first table reservation!
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
x
Reference in New Issue
Block a user