pos_kds_report/models/pos_prep_state.py

165 lines
6.6 KiB
Python

import threading
import logging
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
def _threaded_report_update(registry, uid, context, pdis_state_id, is_reset, is_completed):
"""Background worker to update KDS reports without blocking the POS transaction."""
try:
with registry.cursor() as new_cr:
new_env = api.Environment(new_cr, uid, context)
pdis_state = new_env['pos.prep.state'].browse(pdis_state_id)
if not pdis_state.exists():
return
order_line = pdis_state.prep_line_id.pos_order_line_id
stage = pdis_state.stage_id
display_id = stage.prep_display_id.id
KdsLineReport = new_env['pos.kds.report.line'].sudo()
KdsOrderReport = new_env['pos.kds.report.order'].sudo()
if is_reset:
line_report = KdsLineReport.search([
('pos_order_line_id', '=', order_line.id),
('prep_display_id', '=', display_id)
], limit=1)
if line_report:
line_report.unlink()
order_report = KdsOrderReport.search([
('pos_order_id', '=', order_line.order_id.id),
('prep_display_id', '=', display_id)
], limit=1)
if order_report:
res = KdsLineReport._read_group(
[('pos_order_id', '=', order_line.order_id.id), ('prep_display_id', '=', display_id)],
aggregates=['completion_time:max']
)
max_comp_time = res[0][0] if res else 0
if max_comp_time == 0:
order_report.unlink()
else:
order_report.write({'completion_time': max_comp_time})
new_cr.commit()
return
# If completed
prep_time = max(0, order_line.preparation_time)
svc_time = max(0, order_line.service_time)
comp_time = prep_time + svc_time
vals = {
'pos_order_id': order_line.order_id.id,
'pos_order_line_id': order_line.id,
'product_id': order_line.product_id.id,
'prep_display_id': display_id,
'preparation_time': prep_time,
'service_time': svc_time,
'completion_time': comp_time,
'completion_datetime': fields.Datetime.now(),
}
line_report = KdsLineReport.search([
('pos_order_line_id', '=', order_line.id),
('prep_display_id', '=', display_id)
], limit=1)
if line_report:
line_report.write(vals)
else:
KdsLineReport.create(vals)
# Update order-level report
order_report = KdsOrderReport.search([
('pos_order_id', '=', order_line.order_id.id),
('prep_display_id', '=', display_id)
], limit=1)
res = KdsLineReport._read_group(
[('pos_order_id', '=', order_line.order_id.id), ('prep_display_id', '=', display_id)],
aggregates=['completion_time:max']
)
max_comp_time = res[0][0] if res else comp_time
order_vals = {
'pos_order_id': order_line.order_id.id,
'prep_display_id': display_id,
'completion_time': max_comp_time,
'completion_datetime': fields.Datetime.now(),
}
if order_report:
order_report.write(order_vals)
else:
KdsOrderReport.create(order_vals)
new_cr.commit()
except Exception as e:
_logger.error("Background KDS reporting failed: %s", e)
class PosPreparationState(models.Model):
_inherit = 'pos.prep.state'
def _update_kds_report(self, pdis_state):
if not pdis_state.prep_line_id or not pdis_state.prep_line_id.pos_order_line_id:
return
stage = pdis_state.stage_id
if not stage:
return
# Determine status synchronously to decide if we need a thread
if not hasattr(self.env, '_kds_display_cache'):
self.env._kds_display_cache = {}
display_id = stage.prep_display_id.id
if display_id not in self.env._kds_display_cache:
prep_display = stage.prep_display_id
stage_ids = prep_display.stage_ids.ids
self.env._kds_display_cache[display_id] = {
'num_stages': len(stage_ids),
'first_stage_id': stage_ids[0] if stage_ids else False,
'last_stage_id': stage_ids[-1] if stage_ids else False,
'second_last_stage_id': stage_ids[-2] if len(stage_ids) > 1 else False,
}
display_info = self.env._kds_display_cache[display_id]
num_stages = display_info['num_stages']
is_completed = False
if num_stages > 1:
if stage.id == display_info['last_stage_id']:
is_completed = True
elif display_info['second_last_stage_id'] and stage.id == display_info['second_last_stage_id'] and not pdis_state.todo:
is_completed = True
elif num_stages == 1:
if not pdis_state.todo:
is_completed = True
is_reset = False
if stage.id == display_info['first_stage_id'] and pdis_state.todo:
is_reset = True
if not (is_completed or is_reset):
return
# Start background thread to handle the reporting DB operations
# This makes the main POS sync call nearly instant
thread = threading.Thread(
target=_threaded_report_update,
args=(self.env.registry, self.env.uid, self.env.context, pdis_state.id, is_reset, is_completed)
)
thread.daemon = True
thread.start()
def _record_status_change_prep_time(self, pdis_state):
super()._record_status_change_prep_time(pdis_state)
self._update_kds_report(pdis_state)
def _record_stage_change_prep_time(self, pdis_state, old_last_stage_change, prep_order_completion_time):
super()._record_stage_change_prep_time(pdis_state, old_last_stage_change, prep_order_completion_time)
self._update_kds_report(pdis_state)