add online order management and POS synchronization features for dine360

This commit is contained in:
Alaguraj0361 2026-10-08 19:15:21 +05:30
parent c3a9798a41
commit 24b0e642a9
5 changed files with 123 additions and 28 deletions

View File

@ -262,10 +262,18 @@ def get_dashboard_context(active_page='dashboard'):
all_online = SaleOrder.search(domain)
online_orders_data['has_online_orders'] = bool(all_online)
if 'online_order_status' in SaleOrder._fields:
pending_domain = domain + [('online_order_status', '=', 'pending')]
confirmed_domain = domain + [('online_order_status', '=', 'confirmed')]
cooking_domain = domain + [('online_order_status', '=', 'cooking')]
else:
pending_domain = domain + [('state', 'in', ['draft', 'sent'])]
confirmed_domain = domain + [('state', 'in', ['sale', 'done'])]
cooking_domain = []
online_orders_data['pending_count'] = SaleOrder.search_count(pending_domain)
online_orders_data['confirmed_count'] = SaleOrder.search_count(confirmed_domain)
online_orders_data['cooking_count'] = SaleOrder.search_count(cooking_domain) if cooking_domain else 0
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
today_orders = SaleOrder.search(domain + [('date_order', '>=', today_start)])
@ -289,7 +297,7 @@ def get_dashboard_context(active_page='dashboard'):
'customer': o.partner_id.name or 'Guest',
'phone': o.partner_id.phone or o.partner_id.mobile or '',
'amount': f"{symbol}{o.amount_total:,.2f}",
'status': getattr(o, 'online_order_status', o.state) or 'confirmed',
'status': getattr(o, 'online_order_status', False) or ('confirmed' if o.state in ['sale', 'done'] else 'pending'),
'date': o.date_order.strftime('%d %b, %H:%M') if o.date_order else '',
'url': f"/web#id={o.id}&model=sale.order&view_type=form",
})
@ -695,10 +703,39 @@ class Dine360DashboardController(http.Controller):
if order_id and 'sale.order' in request.env:
order = request.env['sale.order'].sudo().browse(order_id)
if order.exists():
if hasattr(order, 'online_order_status'):
order.write({'online_order_status': status})
elif status == 'confirmed' and order.state == 'draft':
if status == 'confirmed':
if hasattr(order, 'action_accept_online_order'):
order.action_accept_online_order()
else:
if order.state in ['draft', 'sent']:
order.action_confirm()
order.write({'online_order_status': 'confirmed'})
elif status == 'cooking':
if hasattr(order, 'action_cook_online_order'):
order.action_cook_online_order()
else:
order.write({'online_order_status': 'cooking'})
if order.pos_order_id:
order.pos_order_id.write({'online_order_status': 'cooking'})
if hasattr(order.pos_order_id, 'lines'):
order.pos_order_id.lines.write({'preparation_status': 'preparing'})
elif status in ['delivered', 'ready']:
if hasattr(order, 'action_deliver_online_order'):
order.action_deliver_online_order()
else:
order.write({'online_order_status': 'delivered'})
if order.pos_order_id:
order.pos_order_id.write({'online_order_status': 'delivered'})
if hasattr(order.pos_order_id, 'lines'):
order.pos_order_id.lines.write({'preparation_status': 'ready'})
elif status == 'rejected':
if hasattr(order, 'action_reject_online_order'):
order.action_reject_online_order()
else:
order._action_cancel()
order.write({'online_order_status': 'rejected'})
else:
order.write({'online_order_status': status})
return request.make_response(
json.dumps({'success': True, 'order_id': order_id, 'status': status}),

View File

@ -204,6 +204,10 @@ document.addEventListener('DOMContentLoaded', function () {
const orderId = this.getAttribute('data-order-id');
const newStatus = this.getAttribute('data-status');
if (newStatus === 'rejected' && !confirm('Are you sure you want to cancel / reject this online order?')) {
return;
}
this.disabled = true;
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';

View File

@ -59,7 +59,7 @@
<span class="o_kpi_trend text-warning">Cooking</span>
</div>
<div class="o_kpi_label">In Kitchen</div>
<div class="o_kpi_number"><t t-esc="online_orders_data.get('today_count', 0)"/></div>
<div class="o_kpi_number"><t t-esc="online_orders_data.get('cooking_count', 0)"/></div>
<div class="o_kpi_footer">
<span class="o_kpi_sub">Preparing on station</span>
</div>
@ -156,28 +156,40 @@
<t t-elif="order['status'] == 'delivered'">
<span class="o_badge_status secondary"><span class="o_badge_dot"/> Delivered</span>
</t>
<t t-elif="order['status'] == 'rejected'">
<span class="o_badge_status danger"><span class="o_badge_dot"/> Rejected</span>
</t>
<t t-else="">
<span class="o_badge_status secondary"><t t-esc="order['status']"/></span>
</t>
</td>
<td class="text-end">
<div class="d-inline-flex gap-1">
<div class="d-inline-flex gap-1 align-items-center justify-content-end">
<t t-if="order['status'] == 'pending'">
<button type="button" class="btn btn-sm btn-success o_btn_update_order" t-att-data-order-id="order['id']" data-status="confirmed">
<button type="button" class="btn btn-sm btn-success o_btn_update_order" t-att-data-order-id="order['id']" data-status="confirmed" title="Accept Online Order">
<i class="fa fa-check"/> Accept
</button>
<button type="button" class="btn btn-sm btn-outline-danger o_btn_update_order" t-att-data-order-id="order['id']" data-status="rejected" title="Reject Order">
<i class="fa fa-times"/>
</button>
</t>
<t t-elif="order['status'] == 'confirmed'">
<button type="button" class="btn btn-sm btn-primary o_btn_update_order" t-att-data-order-id="order['id']" data-status="cooking">
<button type="button" class="btn btn-sm btn-warning text-dark fw-bold o_btn_update_order" t-att-data-order-id="order['id']" data-status="cooking" title="Send to Kitchen Station">
<i class="fa fa-fire"/> Cook
</button>
</t>
<t t-elif="order['status'] == 'cooking'">
<button type="button" class="btn btn-sm btn-outline-success o_btn_update_order" t-att-data-order-id="order['id']" data-status="delivered">
<i class="fa fa-motorcycle"/> Ready
<button type="button" class="btn btn-sm btn-outline-success fw-bold o_btn_update_order" t-att-data-order-id="order['id']" data-status="delivered" title="Mark as Ready">
<i class="fa fa-check-circle"/> Ready
</button>
</t>
<a t-att-href="order['url']" class="btn btn-sm btn-light border text-muted" title="View Order">
<t t-elif="order['status'] == 'delivered'">
<span class="badge bg-light text-success border py-1 px-2"><i class="fa fa-check me-1"/> Completed</span>
</t>
<t t-elif="order['status'] == 'rejected'">
<span class="badge bg-light text-danger border py-1 px-2"><i class="fa fa-times me-1"/> Cancelled</span>
</t>
<a t-att-href="order['url']" class="btn btn-sm btn-light border text-muted ms-1" title="View Order Details">
<i class="fa fa-external-link"/>
</a>
</div>

View File

@ -15,6 +15,8 @@ class PosOrder(models.Model):
online_order_status = fields.Selection([
('pending', 'Pending Kitchen'),
('confirmed', 'Confirmed'),
('cooking', 'Cooking'),
('delivered', 'Delivered'),
('rejected', 'Rejected'),
], string='Online Order Status', default='pending')
@ -88,17 +90,13 @@ class PosOrder(models.Model):
else:
_logger.warning("Could not find a suitable POS Payment Method for online order %s", self.name)
# Set all order lines to 'waiting' so KDS picks them up
for line in self.lines:
if line.product_id.is_kitchen_item and line.product_id.type != 'service':
line.write({
'preparation_status': 'waiting',
})
# Notify KDS
self.lines.filtered(
lambda l: l.product_id.is_kitchen_item and l.product_id.type != 'service'
)._notify_kds()
# Set kitchen order lines to 'waiting' so KDS picks them up
kitchen_lines = self.lines.filtered(
lambda l: (l.product_id.is_kitchen_item or l.product_id.type != 'service') and l.product_id.name not in ['Tips', 'Discount', 'Standard delivery']
)
if kitchen_lines:
kitchen_lines.write({'preparation_status': 'waiting'})
kitchen_lines._notify_kds()
# Notify POS that order was confirmed
if self.config_id:
@ -111,6 +109,32 @@ class PosOrder(models.Model):
_logger.info("Online order %s confirmed and sent to KDS", self.name)
return True
def action_cook_online_order(self):
"""Chef starts cooking the online order"""
self.ensure_one()
self.write({'online_order_status': 'cooking'})
kitchen_lines = self.lines.filtered(
lambda l: (l.product_id.is_kitchen_item or l.product_id.type != 'service') and l.product_id.name not in ['Tips', 'Discount', 'Standard delivery']
)
if kitchen_lines:
kitchen_lines.write({'preparation_status': 'preparing'})
kitchen_lines._notify_kds()
_logger.info("Online order %s marked cooking in KDS", self.name)
return True
def action_deliver_online_order(self):
"""Kitchen marks the online order ready / served"""
self.ensure_one()
self.write({'online_order_status': 'delivered'})
kitchen_lines = self.lines.filtered(
lambda l: (l.product_id.is_kitchen_item or l.product_id.type != 'service') and l.product_id.name not in ['Tips', 'Discount', 'Standard delivery']
)
if kitchen_lines:
kitchen_lines.write({'preparation_status': 'ready'})
kitchen_lines._notify_kds()
_logger.info("Online order %s marked ready/delivered in KDS", self.name)
return True
def action_reject_online_order(self):
"""Cashier rejects the online order"""
self.ensure_one()

View File

@ -42,25 +42,29 @@ class SaleOrderOnline(models.Model):
online_order_status = fields.Selection([
('pending', 'Pending Kitchen'),
('confirmed', 'Confirmed'),
('cooking', 'Cooking'),
('delivered', 'Delivered'),
('rejected', 'Rejected'),
], string='Online Order Status', compute='_compute_online_order_status', store=True)
], string='Online Order Status', compute='_compute_online_order_status', store=True, readonly=False)
is_online_order_accepted_manually = fields.Boolean(string='Manually Accepted', default=False)
@api.depends('pos_order_id.online_order_status', 'state', 'order_source', 'is_online_order_accepted_manually')
def _compute_online_order_status(self):
for order in self:
if order.pos_order_id:
if order.pos_order_id and order.pos_order_id.online_order_status:
order.online_order_status = order.pos_order_id.online_order_status
elif order.state == 'cancel':
order.online_order_status = 'rejected'
elif order.order_source == 'online' and order.state in ['draft', 'sent', 'sale']:
if order.is_online_order_accepted_manually:
if order.online_order_status in ['cooking', 'delivered']:
continue
elif order.is_online_order_accepted_manually:
order.online_order_status = 'confirmed'
else:
order.online_order_status = 'pending'
else:
order.online_order_status = False
order.online_order_status = order.online_order_status or False
def action_accept_online_order(self):
for order in self:
@ -82,6 +86,20 @@ class SaleOrderOnline(models.Model):
order._compute_online_order_status()
return {'type': 'ir.actions.client', 'tag': 'reload'}
def action_cook_online_order(self):
for order in self:
order.online_order_status = 'cooking'
if order.pos_order_id:
order.pos_order_id.action_cook_online_order()
return True
def action_deliver_online_order(self):
for order in self:
order.online_order_status = 'delivered'
if order.pos_order_id:
order.pos_order_id.action_deliver_online_order()
return True
def action_reject_online_order(self):
for order in self:
if order.state in ['draft', 'sent', 'sale']: