123 lines
5.6 KiB
Python
123 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Fix for KDS (Kitchen Display System) / Preparation Display bug:
|
|
|
|
Problem:
|
|
When Table X has N items that are already in the "Completed" stage on the KDS,
|
|
and the customer adds a new item, Odoo creates a new pos.prep.order for the
|
|
same pos.order. The KDS frontend receives a LOAD_ORDERS notification and reloads
|
|
all open prep states for that pos.order via get_preparation_display_order().
|
|
|
|
The _get_open_orderlines_in_display() filter excludes ONLY states where:
|
|
todo=False AND stage=last_stage
|
|
|
|
Items that were advanced to the last stage via the stage-advance button have:
|
|
todo=True, stage=last_stage <-- these ARE returned and shown as "To Prepare"
|
|
|
|
So the previously completed items (stage-advanced to last stage, todo=True) reappear
|
|
on the KDS card alongside the new item, effectively resetting their visual status.
|
|
|
|
Additional complication:
|
|
The custom_preparation_display module ALSO overrides _process_preparation_changes
|
|
and runs AFTER pos_kds_fix in the MRO (since it calls super() which reaches here).
|
|
After super() returns, custom_preparation_display re-searches ALL prep orders
|
|
(including old ones) and syncs parent combo states. This can revert the todo=False
|
|
we set on old last-stage states back to todo=True, defeating the fix.
|
|
|
|
Fix:
|
|
1. Capture existing prep orders BEFORE super() creates a new one.
|
|
2. After all supers() have run (including custom_preparation_display's combo sync),
|
|
do a FINAL enforcement pass that forces all old last-stage states to todo=False.
|
|
This runs last in the call chain so no subsequent override can undo it.
|
|
3. Only target states from genuinely OLD prep orders (pre-existing before this call).
|
|
"""
|
|
import logging
|
|
|
|
from odoo import models
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PosOrder(models.Model):
|
|
_inherit = 'pos.order'
|
|
|
|
def _process_preparation_changes(self, options):
|
|
"""
|
|
Override to fix KDS stage reset bug.
|
|
|
|
We capture existing prep orders BEFORE super() (which may include
|
|
custom_preparation_display's combo sync and pos_enterprise's core logic).
|
|
After the entire super() chain returns, we do a final enforcement pass
|
|
to ensure all old last-stage states remain todo=False regardless of
|
|
what any intermediate override may have set them to.
|
|
"""
|
|
self.ensure_one()
|
|
|
|
# Step 1: Capture existing prep orders BEFORE super() creates a new one.
|
|
# These are the "old" prep orders whose states should not be reset.
|
|
existing_prep_order_ids = self.env['pos.prep.order'].search([
|
|
('pos_order_id', '=', self.id)
|
|
]).ids
|
|
|
|
# Step 2: Run the full super() chain.
|
|
# This includes:
|
|
# - custom_preparation_display._process_preparation_changes (combo sync)
|
|
# - pos_enterprise._process_preparation_changes (new prep order creation)
|
|
result = super()._process_preparation_changes(options)
|
|
|
|
# Step 3: Only act if new items were added (a new prep order was created).
|
|
if not result.get('order_added') or not existing_prep_order_ids:
|
|
return result
|
|
|
|
# Step 4: Fetch all prep displays relevant to this order's POS config.
|
|
prep_displays = self.env['pos.prep.display'].search([
|
|
'|',
|
|
('pos_config_ids', '=', False),
|
|
('pos_config_ids', 'in', self.config_id.id),
|
|
])
|
|
|
|
for prep_display in prep_displays:
|
|
stage_ids = prep_display.stage_ids.ids
|
|
if not stage_ids:
|
|
continue
|
|
last_stage_id = stage_ids[-1]
|
|
|
|
# Step 5: Find ALL states from OLD prep orders that are at the last stage.
|
|
# This covers both:
|
|
# (a) States with todo=True — kitchen staff used stage-advance button
|
|
# (b) States with todo=False — already correctly excluded by the filter
|
|
# but may have been re-set to True by custom_preparation_display's
|
|
# combo-parent sync logic.
|
|
# We unconditionally force them all to todo=False so the filter
|
|
# in _get_open_orderlines_in_display() correctly excludes them.
|
|
old_prep_lines = self.env['pos.prep.line'].search([
|
|
('prep_order_id', 'in', existing_prep_order_ids),
|
|
])
|
|
if not old_prep_lines:
|
|
continue
|
|
|
|
states_to_seal = self.env['pos.prep.state'].search([
|
|
('prep_line_id', 'in', old_prep_lines.ids),
|
|
('stage_id', '=', last_stage_id),
|
|
# Include both todo=True AND todo=False — if custom_preparation_display
|
|
# reverted a False back to True, we need to catch it.
|
|
# Using a simple search with no todo filter is intentional.
|
|
])
|
|
|
|
# Separate the ones that still need updating to avoid unnecessary writes.
|
|
states_needing_fix = states_to_seal.filtered(lambda s: s.todo)
|
|
if states_needing_fix:
|
|
_logger.info(
|
|
"pos_kds_fix: Sealing %d old last-stage states as todo=False "
|
|
"for pos.order %s (prep orders: %s)",
|
|
len(states_needing_fix),
|
|
self.name or self.id,
|
|
existing_prep_order_ids,
|
|
)
|
|
# Use direct ORM write — no websocket notification is needed here.
|
|
# The frontend will get a LOAD_ORDERS notification from process_order()
|
|
# anyway, and get_preparation_display_order() will re-read from DB.
|
|
states_needing_fix.write({'todo': False})
|
|
|
|
return result
|