import logging from odoo import models, fields, api from odoo.addons.pos_enterprise.utils.date_utils import compute_seconds_since _logger = logging.getLogger(__name__) class PosPreparationState(models.Model): _inherit = 'pos.prep.state' kds_accumulated_time = fields.Integer(string="KDS Accumulated Time", default=0) @api.model def _load_pos_preparation_data_fields(self): fields_list = super()._load_pos_preparation_data_fields() if fields_list: fields_list.append('kds_accumulated_time') return fields_list def _update_kds_report(self, pdis_state, old_last_stage_change=None): """ Synchronous KDS report update. Optimized to minimize database calls and avoid redundant aggregations. """ if not pdis_state.prep_line_id: return order_line = pdis_state.prep_line_id.pos_order_line_id if not order_line: return order = order_line.order_id stage = pdis_state.stage_id if not stage: return display = stage.prep_display_id if not display: return stage_ids = display.stage_ids.ids if not stage_ids: return first_stage_id = stage_ids[0] last_stage_id = stage_ids[-1] second_last_stage_id = stage_ids[-2] if len(stage_ids) > 1 else False # Determine if this is a completion or a reset is_completed = False if len(stage_ids) > 1: if stage.id == last_stage_id: is_completed = True elif second_last_stage_id and stage.id == second_last_stage_id and not pdis_state.todo: is_completed = True else: if not pdis_state.todo: is_completed = True is_reset = (stage.id == first_stage_id and pdis_state.todo) if not (is_completed or is_reset): return display_id = display.id order_id = order.id order_name = order.name or order.pos_reference or "Order" KdsLineReport = self.env['pos.kds.report.line'].sudo() KdsOrderReport = self.env['pos.kds.report.order'].sudo() try: if is_reset: _logger.info("KDS Reset: Order %s on display %s", order_name, display.name) line_report = KdsLineReport.search([ ('pos_order_line_id', '=', order_line.id), ('prep_display_id', '=', display_id) ], limit=1) if line_report: line_report.write({'state': 'in_prep'}) pdis_state.kds_accumulated_time = line_report.preparation_time order_report = KdsOrderReport.search([ ('pos_order_id', '=', order_id), ('prep_display_id', '=', display_id) ], limit=1) if order_report: order_report.write({'state': 'in_prep'}) return # --- Completed --- prep_time = order_line.preparation_time svc_time = order_line.service_time # Fallback timing logic if prep_time == -1: if old_last_stage_change: prep_time = int(compute_seconds_since(old_last_stage_change)) else: prep_time = int(compute_seconds_since(pdis_state.last_stage_change)) if svc_time == -1: if is_completed and stage.id == last_stage_id and old_last_stage_change: svc_time = int(compute_seconds_since(old_last_stage_change)) else: svc_time = 0 prep_time = max(0, prep_time) svc_time = max(0, svc_time) stage_count = self.env['pos.prep.stage'].search_count([('prep_display_id', '=', display_id)]) if stage_count <= 2: comp_time = prep_time svc_time = prep_time else: comp_time = prep_time + svc_time line_report = KdsLineReport.search([ ('pos_order_line_id', '=', order_line.id), ('prep_display_id', '=', display_id) ], limit=1) vals = { 'pos_order_id': order_id, 'pos_order_line_id': order_line.id, 'product_id': order_line.product_id.id, 'prep_display_id': display_id, 'preparation_time': prep_time, 'preparation_time_avg': prep_time, 'service_time': svc_time, 'service_time_avg': svc_time, 'completion_time': comp_time, 'completion_time_avg': comp_time, 'completion_datetime': fields.Datetime.now(), 'state': 'done', } if line_report: # Accumulate if it was already existing (e.g. from a previous completion before reset) # Note: We only accumulate if the current session's time is valid. new_prep = line_report.preparation_time + prep_time new_svc = line_report.service_time + svc_time if stage_count <= 2: new_comp = new_prep else: new_comp = new_prep + new_svc _logger.info("KDS Re-Completed (Accumulating): Order %s, Line %s: %ss + %ss = %ss", order_name, order_line.product_id.name, line_report.completion_time, comp_time, new_comp) line_report.write({ 'preparation_time': new_prep, 'preparation_time_avg': new_prep, 'service_time': new_svc, 'service_time_avg': new_svc, 'completion_time': new_comp, 'completion_time_avg': new_comp, 'completion_datetime': vals['completion_datetime'], 'state': 'done', }) # Update local comp_time for order report update below comp_time = new_comp else: _logger.info("KDS Completed: Order %s, Line %s: Prep=%ss, Svc=%ss, Total=%ss", order_name, order_line.product_id.name, prep_time, svc_time, comp_time) KdsLineReport.create(vals) # Update Order Report order_report = KdsOrderReport.search([ ('pos_order_id', '=', order_id), ('prep_display_id', '=', display_id) ], limit=1) if order_report: # Update state and accumulate/max time # We use the max completion time across all lines of this order for this display. # If we just updated a line to 219s, and order report was 204s, it becomes 219s. if comp_time > order_report.completion_time or order_report.state != 'done': new_comp_time = max(comp_time, order_report.completion_time) order_report.write({ 'completion_time': new_comp_time, 'completion_time_avg': new_comp_time, 'state': 'done', 'completion_datetime': vals['completion_datetime'], }) else: KdsOrderReport.create({ 'pos_order_id': order_id, 'prep_display_id': display_id, 'preparation_time': prep_time, # Simplified: will be updated by other lines if needed 'preparation_time_avg': prep_time, 'service_time': svc_time, 'service_time_avg': svc_time, 'completion_time': comp_time, 'completion_time_avg': comp_time, 'state': 'done', 'completion_datetime': vals['completion_datetime'], }) except Exception as e: _logger.error("KDS reporting failed for order %s: %s", order_name, e, exc_info=True) 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, old_last_stage_change=old_last_stage_change)