add online order management and POS synchronization features for dine360
This commit is contained in:
parent
c3a9798a41
commit
24b0e642a9
@ -262,10 +262,18 @@ def get_dashboard_context(active_page='dashboard'):
|
|||||||
all_online = SaleOrder.search(domain)
|
all_online = SaleOrder.search(domain)
|
||||||
online_orders_data['has_online_orders'] = bool(all_online)
|
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'])]
|
pending_domain = domain + [('state', 'in', ['draft', 'sent'])]
|
||||||
confirmed_domain = domain + [('state', 'in', ['sale', 'done'])]
|
confirmed_domain = domain + [('state', 'in', ['sale', 'done'])]
|
||||||
|
cooking_domain = []
|
||||||
|
|
||||||
online_orders_data['pending_count'] = SaleOrder.search_count(pending_domain)
|
online_orders_data['pending_count'] = SaleOrder.search_count(pending_domain)
|
||||||
online_orders_data['confirmed_count'] = SaleOrder.search_count(confirmed_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_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
today_orders = SaleOrder.search(domain + [('date_order', '>=', today_start)])
|
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',
|
'customer': o.partner_id.name or 'Guest',
|
||||||
'phone': o.partner_id.phone or o.partner_id.mobile or '',
|
'phone': o.partner_id.phone or o.partner_id.mobile or '',
|
||||||
'amount': f"{symbol}{o.amount_total:,.2f}",
|
'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 '',
|
'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",
|
'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:
|
if order_id and 'sale.order' in request.env:
|
||||||
order = request.env['sale.order'].sudo().browse(order_id)
|
order = request.env['sale.order'].sudo().browse(order_id)
|
||||||
if order.exists():
|
if order.exists():
|
||||||
if hasattr(order, 'online_order_status'):
|
if status == 'confirmed':
|
||||||
order.write({'online_order_status': status})
|
if hasattr(order, 'action_accept_online_order'):
|
||||||
elif status == 'confirmed' and order.state == 'draft':
|
order.action_accept_online_order()
|
||||||
|
else:
|
||||||
|
if order.state in ['draft', 'sent']:
|
||||||
order.action_confirm()
|
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(
|
return request.make_response(
|
||||||
json.dumps({'success': True, 'order_id': order_id, 'status': status}),
|
json.dumps({'success': True, 'order_id': order_id, 'status': status}),
|
||||||
|
|||||||
@ -204,6 +204,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const orderId = this.getAttribute('data-order-id');
|
const orderId = this.getAttribute('data-order-id');
|
||||||
const newStatus = this.getAttribute('data-status');
|
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.disabled = true;
|
||||||
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
|
this.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
<span class="o_kpi_trend text-warning">Cooking</span>
|
<span class="o_kpi_trend text-warning">Cooking</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="o_kpi_label">In Kitchen</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">
|
<div class="o_kpi_footer">
|
||||||
<span class="o_kpi_sub">Preparing on station</span>
|
<span class="o_kpi_sub">Preparing on station</span>
|
||||||
</div>
|
</div>
|
||||||
@ -156,28 +156,40 @@
|
|||||||
<t t-elif="order['status'] == 'delivered'">
|
<t t-elif="order['status'] == 'delivered'">
|
||||||
<span class="o_badge_status secondary"><span class="o_badge_dot"/> Delivered</span>
|
<span class="o_badge_status secondary"><span class="o_badge_dot"/> Delivered</span>
|
||||||
</t>
|
</t>
|
||||||
|
<t t-elif="order['status'] == 'rejected'">
|
||||||
|
<span class="o_badge_status danger"><span class="o_badge_dot"/> Rejected</span>
|
||||||
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<span class="o_badge_status secondary"><t t-esc="order['status']"/></span>
|
<span class="o_badge_status secondary"><t t-esc="order['status']"/></span>
|
||||||
</t>
|
</t>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<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'">
|
<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
|
<i class="fa fa-check"/> Accept
|
||||||
</button>
|
</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 t-elif="order['status'] == 'confirmed'">
|
<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
|
<i class="fa fa-fire"/> Cook
|
||||||
</button>
|
</button>
|
||||||
</t>
|
</t>
|
||||||
<t t-elif="order['status'] == 'cooking'">
|
<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">
|
<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-motorcycle"/> Ready
|
<i class="fa fa-check-circle"/> Ready
|
||||||
</button>
|
</button>
|
||||||
</t>
|
</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"/>
|
<i class="fa fa-external-link"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -15,6 +15,8 @@ class PosOrder(models.Model):
|
|||||||
online_order_status = fields.Selection([
|
online_order_status = fields.Selection([
|
||||||
('pending', 'Pending Kitchen'),
|
('pending', 'Pending Kitchen'),
|
||||||
('confirmed', 'Confirmed'),
|
('confirmed', 'Confirmed'),
|
||||||
|
('cooking', 'Cooking'),
|
||||||
|
('delivered', 'Delivered'),
|
||||||
('rejected', 'Rejected'),
|
('rejected', 'Rejected'),
|
||||||
], string='Online Order Status', default='pending')
|
], string='Online Order Status', default='pending')
|
||||||
|
|
||||||
@ -88,17 +90,13 @@ class PosOrder(models.Model):
|
|||||||
else:
|
else:
|
||||||
_logger.warning("Could not find a suitable POS Payment Method for online order %s", self.name)
|
_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
|
# Set kitchen order lines to 'waiting' so KDS picks them up
|
||||||
for line in self.lines:
|
kitchen_lines = self.lines.filtered(
|
||||||
if line.product_id.is_kitchen_item and line.product_id.type != 'service':
|
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']
|
||||||
line.write({
|
)
|
||||||
'preparation_status': 'waiting',
|
if kitchen_lines:
|
||||||
})
|
kitchen_lines.write({'preparation_status': 'waiting'})
|
||||||
|
kitchen_lines._notify_kds()
|
||||||
# Notify KDS
|
|
||||||
self.lines.filtered(
|
|
||||||
lambda l: l.product_id.is_kitchen_item and l.product_id.type != 'service'
|
|
||||||
)._notify_kds()
|
|
||||||
|
|
||||||
# Notify POS that order was confirmed
|
# Notify POS that order was confirmed
|
||||||
if self.config_id:
|
if self.config_id:
|
||||||
@ -111,6 +109,32 @@ class PosOrder(models.Model):
|
|||||||
_logger.info("Online order %s confirmed and sent to KDS", self.name)
|
_logger.info("Online order %s confirmed and sent to KDS", self.name)
|
||||||
return True
|
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):
|
def action_reject_online_order(self):
|
||||||
"""Cashier rejects the online order"""
|
"""Cashier rejects the online order"""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|||||||
@ -42,25 +42,29 @@ class SaleOrderOnline(models.Model):
|
|||||||
online_order_status = fields.Selection([
|
online_order_status = fields.Selection([
|
||||||
('pending', 'Pending Kitchen'),
|
('pending', 'Pending Kitchen'),
|
||||||
('confirmed', 'Confirmed'),
|
('confirmed', 'Confirmed'),
|
||||||
|
('cooking', 'Cooking'),
|
||||||
|
('delivered', 'Delivered'),
|
||||||
('rejected', 'Rejected'),
|
('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)
|
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')
|
@api.depends('pos_order_id.online_order_status', 'state', 'order_source', 'is_online_order_accepted_manually')
|
||||||
def _compute_online_order_status(self):
|
def _compute_online_order_status(self):
|
||||||
for order in 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
|
order.online_order_status = order.pos_order_id.online_order_status
|
||||||
elif order.state == 'cancel':
|
elif order.state == 'cancel':
|
||||||
order.online_order_status = 'rejected'
|
order.online_order_status = 'rejected'
|
||||||
elif order.order_source == 'online' and order.state in ['draft', 'sent', 'sale']:
|
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'
|
order.online_order_status = 'confirmed'
|
||||||
else:
|
else:
|
||||||
order.online_order_status = 'pending'
|
order.online_order_status = 'pending'
|
||||||
else:
|
else:
|
||||||
order.online_order_status = False
|
order.online_order_status = order.online_order_status or False
|
||||||
|
|
||||||
def action_accept_online_order(self):
|
def action_accept_online_order(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
@ -82,6 +86,20 @@ class SaleOrderOnline(models.Model):
|
|||||||
order._compute_online_order_status()
|
order._compute_online_order_status()
|
||||||
return {'type': 'ir.actions.client', 'tag': 'reload'}
|
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):
|
def action_reject_online_order(self):
|
||||||
for order in self:
|
for order in self:
|
||||||
if order.state in ['draft', 'sent', 'sale']:
|
if order.state in ['draft', 'sent', 'sale']:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user