add dine360 dashboard and restaurant management modules with customized UI components

This commit is contained in:
Alaguraj0361 2026-10-12 11:30:48 +05:30
parent 24b0e642a9
commit 1929e81878
16 changed files with 449 additions and 102 deletions

View File

@ -477,8 +477,13 @@ class Dine360DashboardController(http.Controller):
if kitchen_action and kitchen_action.res_id:
return request.redirect(f'/web#action={kitchen_action.res_id}')
# 3. Fallback to POS register
return request.redirect('/pos/web')
# 3. Fallback to Dashboard
return request.redirect('/dashboard')
@http.route(['/pos/web', '/pos/ui', '/pos/web/'], type='http', auth='user')
def redirect_pos_to_dashboard(self, **kwargs):
"""Redirect direct POS cashier access to Dine360 restaurant dashboard."""
return request.redirect('/dashboard')
# -------------------------------------------------------------
# APIs: Interactive Operations & Form Handlers
@ -495,10 +500,7 @@ class Dine360DashboardController(http.Controller):
if table_id and 'restaurant.table' in request.env:
table = request.env['restaurant.table'].sudo().browse(table_id)
if table.exists():
if hasattr(table, 'is_occupied'):
table.write({'is_occupied': is_occupied})
elif hasattr(table, 'state'):
table.write({'state': 'occupied' if is_occupied else 'available'})
table.write({'is_occupied': bool(is_occupied)})
return request.make_response(
json.dumps({'success': True, 'table_id': table_id, 'is_occupied': is_occupied}),
@ -653,10 +655,20 @@ class Dine360DashboardController(http.Controller):
rec_id = False
if 'restaurant.table' in request.env:
if not floor_id and 'restaurant.floor' in request.env:
first_floor = request.env['restaurant.floor'].sudo().search([], limit=1)
if first_floor:
floor_id = first_floor.id
import uuid
vals = {
'name': name,
'name': name or f"Table {request.env['restaurant.table'].sudo().search_count([]) + 1}",
'seats': seats,
'shape': shape,
'is_occupied': False,
'is_reservation_enabled': True,
'reservation_slot_duration': '1.0',
'identifier': str(uuid.uuid4())[:8],
}
if floor_id:
vals['floor_id'] = floor_id

View File

@ -1,3 +1,29 @@
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css');
/* Direct FontAwesome font-face declaration with reliable paths */
@font-face {
font-family: 'FontAwesome';
src: url('/web/static/src/libs/fontawesome/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),
url('/web/static/src/libs/fontawesome/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),
url('/web/static/src/libs/fontawesome/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Enforce FontAwesome font rendering across all dashboard components */
.fa, .fas, .far, .fal, .fad, .fab, [class*=" fa-"], [class^="fa-"] {
font-family: 'FontAwesome' !important;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Main background */
body.o_home_dashboard,
#wrapwrap.o_home_dashboard,
@ -542,6 +568,73 @@ body.o_restaurant_body #wrap {
overflow-x: hidden !important;
}
/* Suppress website frontend header/footer & backend quick bar inside dashboard */
html.o_dine_dashboard_root #oe_main_menu_navbar,
html.o_dine_dashboard_root .o_main_navbar,
html.o_dine_dashboard_root .o_frontend_to_backend_nav,
html.o_dine_dashboard_root header:not(.o_saas_topbar),
html.o_dine_dashboard_root #top,
html.o_dine_dashboard_root .o_header_standard,
html.o_dine_dashboard_root #wrapwrap > header,
html.o_dine_dashboard_root footer,
html.o_dine_dashboard_root #footer,
html.o_dine_dashboard_root #wrapwrap > footer,
body:has(.o_saas_layout) #oe_main_menu_navbar,
body:has(.o_saas_layout) .o_main_navbar,
body:has(.o_saas_layout) .o_frontend_to_backend_nav,
body:has(.o_saas_layout) header:not(.o_saas_topbar),
body:has(.o_saas_layout) #top,
body:has(.o_saas_layout) .o_header_standard,
body:has(.o_saas_layout) #wrapwrap > header,
body:has(.o_saas_layout) footer,
body:has(.o_saas_layout) #footer,
body:has(.o_saas_layout) #wrapwrap > footer,
body.o_restaurant_body #oe_main_menu_navbar,
body.o_restaurant_body .o_main_navbar,
body.o_restaurant_body .o_frontend_to_backend_nav,
body.o_restaurant_body header:not(.o_saas_topbar),
body.o_restaurant_body #top,
body.o_restaurant_body .o_header_standard,
body.o_restaurant_body #wrapwrap > header,
body.o_restaurant_body footer,
body.o_restaurant_body #footer,
body.o_restaurant_body #wrapwrap > footer,
body.o_saas_admin_body #oe_main_menu_navbar,
body.o_saas_admin_body .o_main_navbar,
body.o_saas_admin_body .o_frontend_to_backend_nav,
body.o_saas_admin_body header:not(.o_saas_topbar),
body.o_saas_admin_body #top,
body.o_saas_admin_body .o_header_standard,
body.o_saas_admin_body #wrapwrap > header,
body.o_saas_admin_body footer,
body.o_saas_admin_body #footer,
body.o_saas_admin_body #wrapwrap > footer {
display: none !important;
}
/* Dine360 High-Visibility Title & Heading Typography Overrides */
html.o_dine_dashboard_root h1, html.o_dine_dashboard_root h2, html.o_dine_dashboard_root h3,
html.o_dine_dashboard_root h4, html.o_dine_dashboard_root h5, html.o_dine_dashboard_root h6,
body:has(.o_saas_layout) h1, body:has(.o_saas_layout) h2, body:has(.o_saas_layout) h3,
body:has(.o_saas_layout) h4, body:has(.o_saas_layout) h5, body:has(.o_saas_layout) h6,
body.o_restaurant_body h1, body.o_restaurant_body h2, body.o_restaurant_body h3,
body.o_restaurant_body h4, body.o_restaurant_body h5, body.o_restaurant_body h6,
body.o_saas_admin_body h1, body.o_saas_admin_body h2, body.o_saas_admin_body h3,
body.o_saas_admin_body h4, body.o_saas_admin_body h5, body.o_saas_admin_body h6,
.o_saas_layout h1, .o_saas_layout h2, .o_saas_layout h3,
.o_saas_layout h4, .o_saas_layout h5, .o_saas_layout h6,
.o_heading_title,
.o_card_title,
.o_settings_title,
.o_modal_title,
.o_table_num,
.o_app_card_name,
.o_kpi_label {
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
letter-spacing: -0.015em !important;
text-transform: none !important;
}
/* SaaS Admin Flex Layout */
.o_saas_layout {
display: flex;
@ -550,20 +643,34 @@ body.o_restaurant_body #wrap {
width: 100%;
}
/* Sidebar */
/* Sidebar - Fixed on Left so it never scrolls when right-side content scrolls */
.o_saas_sidebar {
width: 260px;
min-width: 260px;
background: #ffffff;
border-right: 1px solid #f1f5f9;
padding: 24px 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
position: sticky;
top: 0;
z-index: 100;
width: 260px !important;
min-width: 260px !important;
max-width: 260px !important;
background: #ffffff !important;
border-right: 1px solid #e2e8f0 !important;
padding: 24px 20px !important;
display: flex !important;
flex-direction: column !important;
justify-content: space-between !important;
height: 100vh !important;
position: fixed !important;
top: 0 !important;
left: 0 !important;
bottom: 0 !important;
z-index: 1000 !important;
overflow-y: auto !important;
overflow-x: hidden !important;
box-sizing: border-box !important;
}
.o_saas_sidebar::-webkit-scrollbar {
width: 4px;
}
.o_saas_sidebar::-webkit-scrollbar-thumb {
background: #e2e8f0;
border-radius: 4px;
}
.o_sidebar_brand {
@ -640,10 +747,13 @@ body.o_restaurant_body #wrap {
}
.o_nav_icon {
font-size: 15px;
width: 18px;
text-align: center;
color: inherit;
font-size: 16px !important;
width: 20px !important;
text-align: center !important;
color: inherit !important;
display: inline-block !important;
flex-shrink: 0 !important;
line-height: 1 !important;
}
/* Promo Card at bottom of sidebar */
@ -700,12 +810,16 @@ body.o_restaurant_body #wrap {
color: #dc2626 !important;
}
/* Main Content Area */
/* Main Content Area - Scroller with offset for fixed sidebar */
.o_saas_main {
flex: 1;
padding: 24px 32px 48px 32px;
width: calc(100% - 260px);
min-width: 0;
flex: 1 !important;
margin-left: 260px !important;
padding: 24px 32px 48px 32px !important;
width: calc(100% - 260px) !important;
max-width: calc(100% - 260px) !important;
min-width: 0 !important;
min-height: 100vh !important;
box-sizing: border-box !important;
}
/* Top Header Bar */
@ -832,11 +946,14 @@ body.o_restaurant_body #wrap {
}
.o_heading_title {
font-size: 26px;
font-weight: 800;
color: #0f172a;
margin: 0 0 4px 0;
letter-spacing: -0.5px;
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
font-size: 26px !important;
font-weight: 800 !important;
color: #0f172a !important;
margin: 0 0 4px 0 !important;
letter-spacing: -0.025em !important;
line-height: 1.25 !important;
text-transform: none !important;
}
.o_heading_subtitle {
@ -2064,6 +2181,63 @@ body.o_restaurant_body #wrap {
color: #ffffff;
}
/* Apps Marketplace Grid - Strictly 4 Cards Per Row on Desktop, 1 on Mobile */
.o_apps_grid {
display: grid !important;
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
gap: 20px !important;
margin-bottom: 28px !important;
width: 100% !important;
}
.o_app_col {
display: flex !important;
flex-direction: column !important;
min-width: 0 !important;
width: 100% !important;
}
.o_app_col .o_settings_card {
height: 100% !important;
display: flex !important;
flex-direction: column !important;
justify-content: space-between !important;
padding: 22px 20px !important;
border-radius: 16px !important;
background: #ffffff !important;
border: 1px solid #e2e8f0 !important;
box-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.06) !important;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.o_app_col .o_settings_card:hover {
transform: translateY(-4px) !important;
box-shadow: 0 16px 32px -4px rgba(15, 23, 42, 0.12) !important;
border-color: #cbd5e1 !important;
}
.o_app_col .o_settings_title {
font-size: 15px !important;
font-weight: 700 !important;
line-height: 1.3 !important;
margin-bottom: 8px !important;
}
.o_app_col .o_settings_desc {
font-size: 12px !important;
color: #64748b !important;
line-height: 1.45 !important;
margin-bottom: 16px !important;
}
.o_app_col .o_settings_icon {
width: 40px !important;
height: 40px !important;
border-radius: 10px !important;
font-size: 16px !important;
flex-shrink: 0 !important;
}
/* Settings Category Grid */
.o_settings_grid {
display: grid;
@ -2102,10 +2276,13 @@ body.o_restaurant_body #wrap {
}
.o_settings_title {
font-size: 16px;
font-weight: 700;
color: #0f172a;
margin: 0 0 2px 0;
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
font-size: 16.5px !important;
font-weight: 700 !important;
color: #0f172a !important;
margin: 0 0 3px 0 !important;
letter-spacing: -0.015em !important;
text-transform: none !important;
}
.o_settings_desc {
@ -2267,4 +2444,138 @@ body.o_restaurant_body #wrap {
overflow: hidden !important;
}
/* -------------------------------------------------------------
Icon Box Render Enforcements
------------------------------------------------------------- */
.o_kpi_icon_box i,
.o_substat_icon_box i,
.o_act_icon_box i,
.o_icon_btn i,
.o_settings_icon i {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
font-size: inherit;
line-height: 1;
}
.o_kpi_trend i,
.o_substat_pct i {
font-size: 11px;
margin-right: 2px;
}
/* -------------------------------------------------------------
Responsive Mobile & Tablet Viewport Adaptations
------------------------------------------------------------- */
@media (max-width: 1200px) {
.o_apps_grid {
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
gap: 18px !important;
}
}
@media (max-width: 991px) {
.o_saas_layout {
flex-direction: column !important;
width: 100% !important;
overflow-x: hidden !important;
}
.o_saas_sidebar {
position: static !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
height: auto !important;
padding: 14px 16px !important;
border-right: none !important;
border-bottom: 1px solid #e2e8f0 !important;
box-sizing: border-box !important;
}
.o_sidebar_brand {
margin-bottom: 12px !important;
}
.o_sidebar_nav {
flex-direction: row !important;
overflow-x: auto !important;
flex-wrap: nowrap !important;
gap: 6px !important;
-webkit-overflow-scrolling: touch !important;
padding-bottom: 4px !important;
}
.o_nav_link {
white-space: nowrap !important;
padding: 6px 12px !important;
font-size: 13px !important;
flex-shrink: 0 !important;
}
.o_nav_link.active::before {
display: none !important;
}
.o_sidebar_promo {
display: none !important;
}
.o_saas_main {
margin-left: 0 !important;
width: 100% !important;
max-width: 100% !important;
padding: 16px 14px !important;
box-sizing: border-box !important;
}
.o_kpi_grid {
grid-template-columns: repeat(2, 1fr) !important;
}
}
@media (max-width: 767px) {
.o_saas_topbar {
flex-direction: column-reverse !important;
align-items: stretch !important;
gap: 12px !important;
}
.o_search_container {
width: 100% !important;
}
.o_topbar_actions {
justify-content: flex-end !important;
width: 100% !important;
}
.o_page_header {
flex-direction: column !important;
align-items: flex-start !important;
gap: 12px !important;
}
.o_header_controls {
flex-wrap: wrap !important;
width: 100% !important;
}
.o_page_toolbar {
flex-direction: column !important;
align-items: stretch !important;
gap: 12px !important;
}
.o_toolbar_search {
width: 100% !important;
}
.o_filter_pills {
overflow-x: auto !important;
flex-wrap: nowrap !important;
padding-bottom: 6px !important;
-webkit-overflow-scrolling: touch !important;
}
.o_filter_pill {
white-space: nowrap !important;
flex-shrink: 0 !important;
}
.o_kpi_grid {
grid-template-columns: 1fr !important;
gap: 14px !important;
}
.o_apps_grid {
grid-template-columns: 1fr !important;
gap: 16px !important;
}
}

View File

@ -3,6 +3,31 @@
* Handles live search, status filter pills, modals, AJAX operations, and toast notifications.
*/
document.addEventListener('DOMContentLoaded', function () {
// -------------------------------------------------------------
// Dashboard Layout Isolation
// -------------------------------------------------------------
if (document.querySelector('.o_saas_layout')) {
document.documentElement.classList.add('o_dine_dashboard_root');
document.body.classList.add('o_restaurant_body', 'o_dine_dashboard');
const straySelectors = [
'#oe_main_menu_navbar',
'.o_main_navbar',
'.o_frontend_to_backend_nav',
'#wrapwrap > header',
'header:not(.o_saas_topbar)',
'#wrapwrap > footer',
'footer'
];
straySelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(el => {
if (!el.closest('.o_saas_layout')) {
el.style.setProperty('display', 'none', 'important');
}
});
});
}
// -------------------------------------------------------------
// Toast Notification Utility
// -------------------------------------------------------------

View File

@ -112,9 +112,10 @@
</div>
<!-- Row 3: Apps Grid -->
<div class="row g-4 mb-4">
<!-- Row 3: Apps Grid (4 Cards Per Row on Desktop, 1 on Mobile) -->
<div class="o_apps_grid mb-4">
<!-- App 1: SaaS Master -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="saas">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="saas">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -135,7 +136,7 @@
<!-- App 2: Table Reservations (Tenant Restaurants Only) -->
<t t-if="not is_saas_master">
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -155,7 +156,7 @@
</div>
<!-- App 3: Table & Floor Manager (Tenant Restaurants Only) -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -175,28 +176,8 @@
</div>
</t>
<!-- App 4: Point of Sale -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
<div class="o_settings_icon" style="background:#dcfce7; color:#16a34a;">
<i class="fa fa-desktop"/>
</div>
<span class="o_badge_status success"><span class="o_badge_dot"/> Online</span>
</div>
<h4 class="o_settings_title fs-5 mb-2">Point of Sale (POS)</h4>
<p class="o_settings_desc mb-3">Ultra-fast touch screen cashier and waiter terminal, offline mode resilience, split billing, table transfers, and payment gateways.</p>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 border-top">
<span class="badge bg-light text-muted border">Terminal</span>
<a href="/pos/web" target="_blank" class="o_link_red fw-bold small">Launch POS →</a>
</div>
</div>
</div>
<!-- App 5: Kitchen Display System (KDS) -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -216,7 +197,7 @@
</div>
<!-- App 6: Online Orders & Storefront -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="delivery">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="delivery">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -236,7 +217,7 @@
</div>
<!-- App 7: Uber Eats & Direct -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="delivery">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="delivery">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -256,7 +237,7 @@
</div>
<!-- App 8: Recipe & Ingredient Control -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">
@ -276,7 +257,7 @@
</div>
<!-- App 9: QZ Thermal Network Printer -->
<div class="col-lg-4 col-md-6" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_app_col" data-search-item="true" data-filter-item="true" data-status="operations">
<div class="o_settings_card h-100 d-flex flex-column justify-content-between">
<div>
<div class="d-flex align-items-start justify-content-between mb-3">

View File

@ -54,10 +54,6 @@
<i class="fa fa-shopping-bag o_nav_icon"/>
<span>Online Orders</span>
</a>
<a href="/pos/web" target="_blank" class="o_nav_link">
<i class="fa fa-desktop o_nav_icon"/>
<span>Point of Sale</span>
</a>
<a href="/dashboard/tables" t-att-class="'o_nav_link ' + ('active' if active_page == 'tables' else '')">
<i class="fa fa-th o_nav_icon"/>
<span>Tables</span>
@ -152,8 +148,19 @@
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.2"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.2"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.6"/>
<script type="text/javascript">
document.documentElement.classList.add('o_dine_dashboard_root');
if (document.body) {
document.body.classList.add('o_restaurant_body', 'o_dine_dashboard');
} else {
window.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('o_restaurant_body', 'o_dine_dashboard');
});
}
</script>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.6"/>
<div class="o_saas_layout">
<!-- Shared Dine360 Sidebar -->

View File

@ -8,8 +8,9 @@
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous"/>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.2"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.2"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
<link rel="stylesheet" type="text/css" href="/dine360_dashboard/static/src/css/home_menu.css?v=6.4"/>
<script type="text/javascript" src="/dine360_dashboard/static/src/js/dashboard_pages.js?v=6.4"/>
<!-- ============================================================== -->
<!-- 1. ADMIN RESTAURANT ONBOARD MASTER DB DASHBOARD -->
@ -521,8 +522,8 @@
<a href="/shop" target="_blank" class="o_btn_red_action">
<i class="fa fa-shopping-bag me-1"/> Storefront
</a>
<a href="/pos/web" target="_blank" class="o_btn_red_action" style="background: #111827;">
<i class="fa fa-desktop me-1"/> POS
<a href="/dashboard/kds" class="o_btn_red_action" style="background: #111827;">
<i class="fa fa-cutlery me-1"/> Kitchen (KDS)
</a>
</div>
</section>
@ -727,13 +728,13 @@
</div>
</a>
<a href="/pos/web" target="_blank" class="o_app_card">
<div class="o_app_card_icon bg-green-light text-success">
<i class="fa fa-desktop"/>
<a href="/dashboard/kds" class="o_app_card">
<div class="o_app_card_icon" style="background:#fce7f3; color:#db2777;">
<i class="fa fa-cutlery"/>
</div>
<div>
<div class="o_app_card_name">POS Terminal</div>
<div class="o_app_card_badge text-success">Registers</div>
<div class="o_app_card_name">Kitchen (KDS)</div>
<div class="o_app_card_badge" style="color:#db2777;">Live Station</div>
</div>
</a>

View File

@ -81,7 +81,7 @@
Elevate Your Dining<br/>
<span class="o_title_accent">Experience</span>
</h1>
<p class="o_hero_subtitle">Unified Point of Sale, Kitchen Display System (KDS), Interactive Tables, and Online Orders — all in one modern platform.</p>
<p class="o_hero_subtitle">Unified Kitchen Display System (KDS), Interactive Tables, Reservations, and Online Orders — all in one modern platform.</p>
<div class="o_hero_features">
<div class="o_hero_feature_item">

View File

@ -18,9 +18,6 @@
<button type="button" class="o_btn_red_action" data-open-modal="modalAddTable">
<i class="fa fa-plus me-1"/> Add Table
</button>
<a href="/pos/web" target="_blank" class="o_btn_white_small py-2 px-3">
<i class="fa fa-desktop me-1"/> Launch POS
</a>
</div>
</section>

View File

@ -59,10 +59,10 @@
</field>
</record>
<!-- Menuitem (Inside Dine360 Dashboard or Restaurant) -->
<!-- Menuitem (Inside Inventory / Stock) -->
<menuitem id="menu_dine360_recipe_root"
name="Recipes"
parent="point_of_sale.menu_point_root"
parent="stock.menu_stock_root"
sequence="25"/>
<menuitem id="menu_dine360_recipe"

View File

@ -3,6 +3,7 @@ from odoo import models, fields, api
class RestaurantTable(models.Model):
_inherit = 'restaurant.table'
is_occupied = fields.Boolean(string='Is Occupied', default=False, tracking=True)
is_reservation_enabled = fields.Boolean(string='Enabled for Reservation', default=True)
min_party_size = fields.Integer(string='Min Party Size', default=1)
max_party_size = fields.Integer(string='Max Party Size', default=1)
@ -27,3 +28,8 @@ class RestaurantTable(models.Model):
else:
record.min_party_size = 1
record.max_party_size = record.seats or 1
def action_toggle_occupied(self):
for table in self:
table.is_occupied = not table.is_occupied
return True

View File

@ -17,6 +17,7 @@
</xpath>
<xpath expr="//sheet/group[1]" position="after">
<group string="Reservation Settings" name="reservation_settings">
<field name="is_occupied" widget="boolean_toggle"/>
<field name="is_reservation_enabled"/>
<field name="reservation_slot_duration"/>
<field name="min_party_size"/>
@ -36,6 +37,7 @@
<field name="floor_id"/>
<field name="name" string="Table Name"/>
<field name="seats" string="Seat Count"/>
<field name="is_occupied" widget="boolean_toggle"/>
<field name="is_reservation_enabled"/>
<field name="reservation_slot_duration"/>
<field name="min_party_size"/>

View File

@ -21,8 +21,7 @@ class ResUsers(models.Model):
role_mapping = {
'waiter': [
'dine360_restaurant.group_restaurant_waiter',
'base.group_user',
'point_of_sale.group_pos_user'
'base.group_user'
],
'chef': [
'dine360_restaurant.group_restaurant_kitchen',
@ -30,19 +29,16 @@ class ResUsers(models.Model):
],
'cashier': [
'dine360_restaurant.group_restaurant_cashier',
'base.group_user',
'point_of_sale.group_pos_user'
'base.group_user'
],
'manager': [
'dine360_restaurant.group_restaurant_manager',
'base.group_user',
'point_of_sale.group_pos_manager',
'stock.group_stock_user'
],
'admin': [
'dine360_restaurant.group_restaurant_admin',
'base.group_user',
'point_of_sale.group_pos_manager',
'stock.group_stock_manager',
'base.group_system',
'base.group_erp_manager'

View File

@ -32,6 +32,7 @@
<field name="name">Kitchen Display</field>
<field name="parent_id" ref="point_of_sale.menu_point_root"/>
<field name="action" ref="action_restaurant_kitchen_orders"/>
<field name="active" eval="False"/>
<field name="sequence">30</field>
</record>
</odoo>

View File

@ -4,8 +4,9 @@
<field name="groups_id" eval="[(4, ref('group_restaurant_manager')), (4, ref('group_restaurant_store_keeper'))]"/>
</record>
<!-- Deactivate and hide Point of Sale app menu from backend for all users -->
<record id="point_of_sale.menu_point_root" model="ir.ui.menu">
<field name="groups_id" eval="[(4, ref('group_restaurant_manager')), (4, ref('group_restaurant_waiter')), (4, ref('group_restaurant_cashier'))]"/>
<field name="active" eval="False"/>
</record>
<!-- Redundant: Consolidated into dine360_kds

View File

@ -78,6 +78,10 @@ def _async_provisioning_worker(parent_db, saas_res_id, db_name, modules, company
'client_secret': 'SANDBOX_CLIENT_SECRET',
'environment': 'sandbox',
})
# Ensure Point of Sale backend menu is hidden in tenant restaurant
pos_menu = env.ref('point_of_sale.menu_point_root', raise_if_not_found=False)
if pos_menu:
pos_menu.write({'active': False})
# Force Odoo registry signaling update
env.registry.registry_invalidated = True

View File

@ -1,10 +1,13 @@
/* Global Font Family Overrides */
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
/* Global Font Family Overrides (Website Frontend Only, excluded from Dine360 Portal & Dashboards) */
body:not(.o_restaurant_body):not(.o_saas_admin_body):not(.o_dine_dashboard):not(.o_home_dashboard) {
h1:not([class*="o_"]), h2:not([class*="o_"]), h3:not([class*="o_"]),
h4:not([class*="o_"]), h5:not([class*="o_"]), h6:not([class*="o_"]) {
font-family: 'Bebas Neue', sans-serif !important;
}
p, li, .lead, .hero-desc, .blog-excerpt {
p:not([class*="o_"]), li:not([class*="o_"]), .lead, .hero-desc, .blog-excerpt {
font-family: 'Roboto', sans-serif !important;
}
}
/* Shivasakthi Theme Custom Styles */