147 lines
6.6 KiB
Python
147 lines
6.6 KiB
Python
from odoo import models, fields, api, _
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
class PosOrderLine(models.Model):
|
|
_inherit = 'pos.order.line'
|
|
|
|
preparation_status = fields.Selection([
|
|
('waiting', 'Waiting'),
|
|
('preparing', 'Preparing'),
|
|
('ready', 'Ready'),
|
|
('served', 'Served'),
|
|
('cancelled', 'Cancelled')
|
|
], string='Preparation Status', default='waiting', tracking=True, group_expand='_read_group_preparation_status')
|
|
|
|
@api.model
|
|
def _read_group_preparation_status(self, stages, domain, order):
|
|
return ['waiting', 'preparing', 'ready', 'served']
|
|
|
|
color = fields.Integer(string='Color', default=0)
|
|
preparation_time_start = fields.Datetime(string='Start Time')
|
|
preparation_time_end = fields.Datetime(string='Ready Time')
|
|
cooking_time = fields.Integer(string='Cooking Time (min)', compute='_compute_cooking_time', store=True)
|
|
|
|
table_id = fields.Many2one('restaurant.table', related='order_id.table_id', string='Table', store=True)
|
|
floor_id = fields.Many2one('restaurant.floor', related='order_id.table_id.floor_id', string='Floor', store=True)
|
|
|
|
@api.depends('preparation_time_start', 'preparation_time_end')
|
|
def _compute_cooking_time(self):
|
|
for line in self:
|
|
if line.preparation_time_start and line.preparation_time_end:
|
|
diff = line.preparation_time_end - line.preparation_time_start
|
|
line.cooking_time = int(diff.total_seconds() / 60)
|
|
else:
|
|
line.cooking_time = 0
|
|
|
|
def _notify_pos(self):
|
|
"""Send notification to POS when order line status changes"""
|
|
_logger.info("=== _notify_pos called for %s lines ===" % len(self))
|
|
for line in self:
|
|
_logger.info(f"Processing line {line.id}, order: {line.order_id.name}, config: {line.order_id.config_id}")
|
|
if line.order_id.config_id:
|
|
channel_name = "pos_config_Channel_%s" % line.order_id.config_id.id
|
|
payload = {
|
|
'line_id': line.id,
|
|
'order_id': line.order_id.id,
|
|
'status': line.preparation_status,
|
|
'status_label': dict(self._fields['preparation_status']._description_selection(self.env)).get(line.preparation_status),
|
|
'order_uid': line.order_id.pos_reference,
|
|
'product_id': line.product_id.id,
|
|
'qty': line.qty,
|
|
}
|
|
_logger.info(f"KDS NOTIFICATION: Sending update for Line {line.id} Status {line.preparation_status} to {channel_name}")
|
|
self.env['bus.bus']._sendone(channel_name, 'kds_update', payload)
|
|
else:
|
|
_logger.warning(f"Line {line.id} has no config_id - cannot send notification")
|
|
|
|
def _notify_kds(self):
|
|
"""Send notification to KDS backend when new order lines are created"""
|
|
_logger.info("=== _notify_kds called for %s lines ===" % len(self))
|
|
for line in self:
|
|
# Only notify for kitchen items
|
|
if line.product_id.is_kitchen_item and line.product_id.name != 'Water':
|
|
# Send to global KDS channel
|
|
kds_channel = "kds_channel"
|
|
payload = {
|
|
'line_id': line.id,
|
|
'order_id': line.order_id.id,
|
|
'product_name': line.product_id.name,
|
|
'qty': line.qty,
|
|
'table_name': line.table_id.name if line.table_id else '',
|
|
'floor_name': line.floor_id.name if line.floor_id else '',
|
|
'customer_note': line.customer_note or '',
|
|
'preparation_status': line.preparation_status,
|
|
'create_date': line.create_date.isoformat() if line.create_date else '',
|
|
}
|
|
_logger.info(f"KDS BACKEND NOTIFICATION: New order line {line.id} for {line.product_id.name}")
|
|
self.env['bus.bus']._sendone(kds_channel, 'kds_new_order', payload)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
"""Override create to send notifications to KDS when new orders are added"""
|
|
lines = super(PosOrderLine, self).create(vals_list)
|
|
# Send notification to KDS backend only for new items (waiting status)
|
|
waiting_lines = lines.filtered(lambda l: l.preparation_status == 'waiting')
|
|
if waiting_lines:
|
|
waiting_lines._notify_kds()
|
|
return lines
|
|
|
|
def write(self, vals):
|
|
"""Prevent resetting 'served' or 'ready' status back to 'waiting' during POS sync"""
|
|
if 'preparation_status' in vals and vals['preparation_status'] == 'waiting':
|
|
for line in self:
|
|
if line.preparation_status in ['served', 'ready', 'preparing']:
|
|
# Keep the current status if it's already being processed or served
|
|
actual_vals = vals.copy()
|
|
actual_vals['preparation_status'] = line.preparation_status
|
|
super(PosOrderLine, line).write(actual_vals)
|
|
|
|
# Handle lines that are actually allowed to be updated to waiting
|
|
remaining_lines = self.filtered(lambda l: l.preparation_status not in ['served', 'ready', 'preparing'])
|
|
if remaining_lines:
|
|
res = super(PosOrderLine, remaining_lines).write(vals)
|
|
# If quantity changed or status is waiting, notify KDS to refresh
|
|
remaining_lines._notify_kds()
|
|
return res
|
|
return True
|
|
|
|
res = super(PosOrderLine, self).write(vals)
|
|
# Notify KDS for quantity changes on active items
|
|
if 'qty' in vals:
|
|
active_lines = self.filtered(lambda l: l.preparation_status in ['waiting', 'preparing'])
|
|
if active_lines:
|
|
active_lines._notify_kds()
|
|
return res
|
|
|
|
def action_start_preparing(self):
|
|
self.write({
|
|
'preparation_status': 'preparing',
|
|
'preparation_time_start': fields.Datetime.now()
|
|
})
|
|
self._notify_pos()
|
|
|
|
def action_mark_ready(self):
|
|
self.write({
|
|
'preparation_status': 'ready',
|
|
'preparation_time_end': fields.Datetime.now()
|
|
})
|
|
self._notify_pos()
|
|
|
|
def action_mark_served(self):
|
|
self.write({
|
|
'preparation_status': 'served'
|
|
})
|
|
self._notify_pos()
|
|
|
|
class PosOrder(models.Model):
|
|
_inherit = 'pos.order'
|
|
|
|
@api.model
|
|
def _prepare_order_line_vals(self, line, session_id=None):
|
|
res = super()._prepare_order_line_vals(line, session_id)
|
|
if 'preparation_status' in line:
|
|
res['preparation_status'] = line['preparation_status']
|
|
return res
|