forked from alaguraj/odoo-testing-addons
Implement online order management with service mode selection, KDS integration, and dedicated POS order fields.
This commit is contained in:
parent
d8db1f9334
commit
216c627369
@ -25,8 +25,8 @@
|
||||
</strong>
|
||||
</div>
|
||||
<div class="ms-auto h5 mb-0">
|
||||
<span class="badge rounded-pill bg-light text-dark border">
|
||||
<i class="fa fa-cutlery me-1"/> <field name="table_id"/>
|
||||
<span t-if="record.table_id.raw_value" class="badge rounded-pill bg-light text-dark border">
|
||||
<i class="fa fa-map-marker me-1"/> <field name="table_id"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
'security/ir.model.access.csv',
|
||||
'views/pos_order_views.xml',
|
||||
'views/kds_override_views.xml',
|
||||
'views/pos_config_views.xml',
|
||||
'views/website_sale_templates.xml',
|
||||
],
|
||||
'assets': {
|
||||
'point_of_sale._assets_pos': [
|
||||
@ -24,6 +26,9 @@
|
||||
'dine360_online_orders/static/src/js/online_orders_navbar.js',
|
||||
'dine360_online_orders/static/src/xml/online_orders_screen.xml',
|
||||
],
|
||||
'web.assets_frontend': [
|
||||
'dine360_online_orders/static/src/js/service_mode.js',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
|
||||
1
addons/dine360_online_orders/controllers/__init__.py
Normal file
1
addons/dine360_online_orders/controllers/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import main
|
||||
14
addons/dine360_online_orders/controllers/main.py
Normal file
14
addons/dine360_online_orders/controllers/main.py
Normal file
@ -0,0 +1,14 @@
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
class Dine360OnlineOrders(http.Controller):
|
||||
|
||||
@http.route('/shop/update_service_mode', type='json', auth="public", website=True)
|
||||
def update_service_mode(self, service_mode, **post):
|
||||
order = request.website.sale_get_order()
|
||||
if order and service_mode in ['pickup', 'delivery', 'dine_in']:
|
||||
order.sudo().write({
|
||||
'dine360_service_mode': service_mode,
|
||||
'dine360_order_source': 'web'
|
||||
})
|
||||
return True
|
||||
@ -1,2 +1,5 @@
|
||||
from . import pos_order
|
||||
from . import sale_order
|
||||
from . import pos_config
|
||||
from . import res_config_settings
|
||||
from . import pos_order_line
|
||||
|
||||
10
addons/dine360_online_orders/models/pos_config.py
Normal file
10
addons/dine360_online_orders/models/pos_config.py
Normal file
@ -0,0 +1,10 @@
|
||||
from odoo import models, fields
|
||||
|
||||
class PosConfig(models.Model):
|
||||
_inherit = 'pos.config'
|
||||
|
||||
is_kiosk = fields.Boolean(string='Is Self-Order Kiosk', default=False)
|
||||
kiosk_service_mode = fields.Selection([
|
||||
('pickup', 'Pickup'),
|
||||
('dine_in', 'Dine-In')
|
||||
], string='Default Kiosk Service Mode', default='dine_in')
|
||||
@ -31,6 +31,18 @@ class PosOrder(models.Model):
|
||||
default=fields.Datetime.now
|
||||
)
|
||||
|
||||
dine360_order_source = fields.Selection([
|
||||
('web', 'Customer Self (Web)'),
|
||||
('kiosk', 'Store Self (Kiosk)'),
|
||||
('pos', 'Standard POS')
|
||||
], string='Order Source', default='pos')
|
||||
|
||||
dine360_service_mode = fields.Selection([
|
||||
('pickup', 'Pickup'),
|
||||
('delivery', 'Delivery'),
|
||||
('dine_in', 'Dine-In')
|
||||
], string='Service Mode', default='dine_in')
|
||||
|
||||
@api.depends('partner_id', 'partner_id.name')
|
||||
def _compute_online_customer_name(self):
|
||||
for order in self:
|
||||
@ -80,6 +92,16 @@ class PosOrder(models.Model):
|
||||
_logger.info("Online order %s rejected", self.name)
|
||||
return True
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if 'session_id' in vals:
|
||||
session = self.env['pos.session'].browse(vals['session_id'])
|
||||
if session.config_id.is_kiosk:
|
||||
vals['dine360_order_source'] = 'kiosk'
|
||||
vals['dine360_service_mode'] = session.config_id.kiosk_service_mode
|
||||
return super().create(vals_list)
|
||||
|
||||
@api.model
|
||||
def get_online_orders(self, config_id):
|
||||
"""Fetch pending online orders for a specific POS config"""
|
||||
@ -112,6 +134,8 @@ class PosOrder(models.Model):
|
||||
'amount_total': order.amount_total,
|
||||
'date_order': order.date_order.isoformat() if order.date_order else '',
|
||||
'sale_order_name': order.sale_order_id.name if order.sale_order_id else '',
|
||||
'service_mode': order.dine360_service_mode,
|
||||
'order_source': order.dine360_order_source,
|
||||
'note': order.note or '',
|
||||
'lines': lines,
|
||||
})
|
||||
|
||||
15
addons/dine360_online_orders/models/pos_order_line.py
Normal file
15
addons/dine360_online_orders/models/pos_order_line.py
Normal file
@ -0,0 +1,15 @@
|
||||
from odoo import models, fields, api
|
||||
|
||||
class PosOrderLine(models.Model):
|
||||
_inherit = 'pos.order.line'
|
||||
|
||||
dine360_order_source = fields.Selection(
|
||||
related='order_id.dine360_order_source',
|
||||
string='Order Source',
|
||||
store=True
|
||||
)
|
||||
dine360_service_mode = fields.Selection(
|
||||
related='order_id.dine360_service_mode',
|
||||
string='Service Mode',
|
||||
store=True
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
from odoo import models, fields
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
is_kiosk = fields.Boolean(related='pos_config_id.is_kiosk', readonly=False)
|
||||
kiosk_service_mode = fields.Selection(related='pos_config_id.kiosk_service_mode', readonly=False)
|
||||
@ -12,6 +12,18 @@ class SaleOrderOnline(models.Model):
|
||||
help='The POS order created from this website sale order'
|
||||
)
|
||||
|
||||
dine360_order_source = fields.Selection([
|
||||
('web', 'Customer Self (Web)'),
|
||||
('kiosk', 'Store Self (Kiosk)'),
|
||||
('pos', 'Standard POS')
|
||||
], string='Order Source', default='web')
|
||||
|
||||
dine360_service_mode = fields.Selection([
|
||||
('pickup', 'Pickup'),
|
||||
('delivery', 'Delivery'),
|
||||
('dine_in', 'Dine-In')
|
||||
], string='Service Mode', default='pickup')
|
||||
|
||||
def _create_pos_order_for_kds(self, sale_order):
|
||||
"""
|
||||
Override from dine360_kds to also mark the POS order as an online order.
|
||||
@ -35,6 +47,8 @@ class SaleOrderOnline(models.Model):
|
||||
'online_order_status': 'pending',
|
||||
'sale_order_id': sale_order.id,
|
||||
'online_order_date': fields.Datetime.now(),
|
||||
'dine360_order_source': sale_order.dine360_order_source,
|
||||
'dine360_service_mode': sale_order.dine360_service_mode,
|
||||
})
|
||||
|
||||
# Link back to sale order
|
||||
|
||||
62
addons/dine360_online_orders/static/src/js/service_mode.js
Normal file
62
addons/dine360_online_orders/static/src/js/service_mode.js
Normal file
@ -0,0 +1,62 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
import { jsonrpc } from "@web/core/network/rpc_service";
|
||||
|
||||
publicWidget.registry.ServiceModeSelector = publicWidget.Widget.extend({
|
||||
selector: '#service_mode_selector',
|
||||
events: {
|
||||
'change input[name="dine360_service_mode"]': '_onChangeServiceMode',
|
||||
},
|
||||
|
||||
start: function () {
|
||||
// Init visual selection
|
||||
this.$('input[name="dine360_service_mode"]:checked').closest('.service-option').find('.service-card')
|
||||
.css({ 'border-color': '#FECD4F', 'background-color': '#fffdf6', 'box-shadow': '0 4px 10px rgba(254, 205, 79, 0.2)' });
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
_onChangeServiceMode: function (ev) {
|
||||
var $input = $(ev.currentTarget);
|
||||
var mode = $input.val();
|
||||
|
||||
// Reset styles
|
||||
this.$('.service-card').css({ 'border-color': '', 'background-color': '', 'box-shadow': '' });
|
||||
// Apply active styles
|
||||
$input.closest('.service-option').find('.service-card')
|
||||
.css({ 'border-color': '#FECD4F', 'background-color': '#fffdf6', 'box-shadow': '0 4px 10px rgba(254, 205, 79, 0.2)' });
|
||||
|
||||
// Hide error if present
|
||||
this.$('#service_mode_error').addClass('d-none');
|
||||
|
||||
// RPC Call to update order
|
||||
jsonrpc('/shop/update_service_mode', {
|
||||
service_mode: mode
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Intercept checkout to ensure a service mode is selected
|
||||
publicWidget.registry.CartCheckoutValidation = publicWidget.Widget.extend({
|
||||
selector: '.oe_cart',
|
||||
events: {
|
||||
'click a[href="/shop/checkout"]': '_onCheckoutClicked',
|
||||
},
|
||||
|
||||
_onCheckoutClicked: function (ev) {
|
||||
// If there's a selector on the page
|
||||
if (this.$('#service_mode_selector').length > 0) {
|
||||
var selectedMode = this.$('input[name="dine360_service_mode"]:checked').val();
|
||||
if (!selectedMode) {
|
||||
ev.preventDefault();
|
||||
this.$('#service_mode_error').removeClass('d-none');
|
||||
|
||||
// Highlight the box
|
||||
this.$('#service_mode_selector').css('border', '1px solid #dc3545').addClass('shake-animation');
|
||||
setTimeout(() => {
|
||||
this.$('#service_mode_selector').removeClass('shake-animation');
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -10,4 +10,31 @@
|
||||
'|', ('order_id.is_online_order', '=', False), ('order_id.online_order_status', '!=', 'pending')
|
||||
]</field>
|
||||
</record>
|
||||
|
||||
<!-- Extend KDS Kanban to show Service Mode -->
|
||||
<record id="view_pos_order_line_kds_kanban_inherit" model="ir.ui.view">
|
||||
<field name="name">pos.order.line.kds.kanban.inherit</field>
|
||||
<field name="model">pos.order.line</field>
|
||||
<field name="inherit_id" ref="dine360_kds.view_pos_order_line_kds_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='create_date']" position="after">
|
||||
<field name="dine360_order_source"/>
|
||||
<field name="dine360_service_mode"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('ms-auto')]" position="replace">
|
||||
<div class="ms-auto h5 mb-0 d-flex flex-column align-items-end">
|
||||
<span t-if="record.dine360_service_mode.raw_value" class="badge rounded-pill mb-1"
|
||||
t-attf-class="{{record.dine360_service_mode.raw_value == 'pickup' ? 'bg-info' : (record.dine360_service_mode.raw_value == 'delivery' ? 'bg-primary' : 'bg-secondary')}} text-white">
|
||||
<i t-if="record.dine360_service_mode.raw_value == 'pickup'" class="fa fa-shopping-basket me-1" title="Pickup"/>
|
||||
<i t-if="record.dine360_service_mode.raw_value == 'delivery'" class="fa fa-truck me-1" title="Delivery"/>
|
||||
<i t-if="record.dine360_service_mode.raw_value == 'dine_in'" class="fa fa-cutlery me-1" title="Dine-In"/>
|
||||
<field name="dine360_service_mode"/>
|
||||
</span>
|
||||
<span t-if="record.table_id.raw_value" class="badge rounded-pill bg-light text-dark border">
|
||||
<i class="fa fa-map-marker me-1" title="Table"/> <field name="table_id"/>
|
||||
</span>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
22
addons/dine360_online_orders/views/pos_config_views.xml
Normal file
22
addons/dine360_online_orders/views/pos_config_views.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form_inherit_dine360" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.dine360</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="priority" eval="95"/>
|
||||
<field name="inherit_id" ref="point_of_sale.res_config_settings_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//block[@id='pos_interface_section']" position="inside">
|
||||
<setting string="Self-Order Kiosk" help="Enable this POS as a self-order kiosk">
|
||||
<field name="is_kiosk"/>
|
||||
<div class="content-group" invisible="not is_kiosk">
|
||||
<div class="mt16">
|
||||
<label string="Default Service Mode" for="kiosk_service_mode" class="col-lg-3 o_light_label"/>
|
||||
<field name="kiosk_service_mode"/>
|
||||
</div>
|
||||
</div>
|
||||
</setting>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@ -15,6 +15,11 @@
|
||||
decoration-success="online_order_status=='confirmed'"
|
||||
decoration-danger="online_order_status=='rejected'"/>
|
||||
<field name="sale_order_id"/>
|
||||
<field name="dine360_order_source"/>
|
||||
<field name="dine360_service_mode" widget="badge"
|
||||
decoration-info="dine360_service_mode=='pickup'"
|
||||
decoration-primary="dine360_service_mode=='delivery'"
|
||||
decoration-muted="dine360_service_mode=='dine_in'"/>
|
||||
<field name="config_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
@ -33,6 +38,8 @@
|
||||
decoration-success="online_order_status=='confirmed'"
|
||||
decoration-danger="online_order_status=='rejected'"/>
|
||||
<field name="sale_order_id" invisible="not is_online_order"/>
|
||||
<field name="dine360_order_source" string="Source"/>
|
||||
<field name="dine360_service_mode" string="Service"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="cart_service_mode" inherit_id="website_sale.cart" name="Service Mode Selector">
|
||||
<xpath expr="//t[@t-call='website_sale.cart_lines']" position="before">
|
||||
<div id="service_mode_selector" class="mb-4 bg-white p-4 rounded-4 shadow-sm border" style="border-left: 5px solid #FECD4F !important;">
|
||||
<h4 class="mb-3 fw-bold">How would you like your order?</h4>
|
||||
<div class="d-flex gap-3">
|
||||
<label class="service-option position-relative flex-fill cursor-pointer">
|
||||
<input type="radio" name="dine360_service_mode" value="pickup" class="d-none" t-att-checked="'checked' if website_sale_order.dine360_service_mode == 'pickup' else None"/>
|
||||
<div class="service-card p-3 rounded-3 border text-center transition-all">
|
||||
<i class="fa fa-shopping-basket fs-3 mb-2 text-primary"></i>
|
||||
<h6 class="mb-1 fw-bold">Pickup</h6>
|
||||
<small class="text-muted">Pick up at store</small>
|
||||
</div>
|
||||
</label>
|
||||
<label class="service-option position-relative flex-fill cursor-pointer">
|
||||
<input type="radio" name="dine360_service_mode" value="delivery" class="d-none" t-att-checked="'checked' if website_sale_order.dine360_service_mode == 'delivery' else None"/>
|
||||
<div class="service-card p-3 rounded-3 border text-center transition-all">
|
||||
<i class="fa fa-truck fs-3 mb-2 text-info"></i>
|
||||
<h6 class="mb-1 fw-bold">Delivery</h6>
|
||||
<small class="text-muted">Delivered to you</small>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-3 small text-danger d-none" id="service_mode_error">Please select Pickup or Delivery to continue.</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
Loading…
x
Reference in New Issue
Block a user