from odoo import api, models class QualityCheckWizard(models.TransientModel): _inherit = 'quality.check.wizard' # The AttributeError fix is now handled by the compatibility field in models/mrp_production.py @api.onchange('qty_failed') def onchange_qty_failed(self): """ Disable enterprise quality_mrp behavior that resets qty_failed to qty_line """ pass def do_fail(self): """ Ensure move_line_id is populated for MO checks with Quantity control. This is required for quality_control's _move_to_failure_location to split the stock move correctly. """ check = self.current_check_id # We mimic the enterprise/quality_mrp behavior but in our patch if check.production_id and check.point_id.measure_on == 'move_line' and not check.move_line_id: # Try to find a finished move line for this product move_line = check.production_id.finished_move_line_ids.filtered( lambda ml: ml.product_id == (check.product_id or self.product_id) )[:1] if move_line: check.move_line_id = move_line # Also set lot if available if not check.lot_line_id and check.production_id.lot_producing_ids: check.lot_line_id = check.production_id.lot_producing_ids[0] return super().do_fail() def confirm_fail(self): """ Override to capture the quantity from the failure wizard popup """ import logging _logger = logging.getLogger(__name__) _logger.info(f"DEBUG: confirm_fail called. qty_failed from wizard: {self.qty_failed}") if self.qty_failed > 0: self.current_check_id.qty_failed_manual = self.qty_failed _logger.info(f"DEBUG: Saved qty_failed_manual: {self.current_check_id.qty_failed_manual} to check {self.current_check_id.id}") # Odoo 18 Wizard logic: # self.current_check_id.do_fail() # if self.measure_on == 'move_line': # self.current_check_id._move_line_to_failure_location(self.failure_location_id.id, self.qty_failed) # We replicate this but with our relaxed condition: self.current_check_id.do_fail() # Check if we should split the move line. # We allow it if strictly defined as move_line check OR if a manual fail qty is provided (relaxed logic) if self.current_check_id.point_id.measure_on == 'move_line' or self.qty_failed > 0: _logger.info(f"DEBUG: Triggering _move_line_to_failure_location for check {self.current_check_id.id}") # Use our ported method self.current_check_id._move_line_to_failure_location(self.failure_location_id.id, self.qty_failed) else: _logger.info(f"DEBUG: Skipping _move_line_to_failure_location. measure_on={self.current_check_id.point_id.measure_on}, qty_failed={self.qty_failed}") return self.action_generate_next_window() def show_failure_message(self): """ Initialize qty_failed with tested quantity or line quantity """ res = super().show_failure_message() # If the user has already tested a specific quantity, that's likely the one they found # failed if it's a pass/fail check on a quantity. if self.qty_tested and self.qty_tested < self.qty_line: self.qty_failed = self.qty_tested return res