# -*- coding: utf-8 -*- from odoo import models, fields, api class StockMoveLine(models.Model): _inherit = 'stock.move.line' def _create_check(self): """ Override to prevent creating duplicate quality checks when move lines are recreated. For receipt operations with existing completed quality checks, relink them instead. """ # Check each move line move_lines_to_process = self.env['stock.move.line'] for ml in self: if ml._is_receipt_operation() and ml.picking_id: # Search for existing quality checks (including orphaned ones) existing_checks = self.env['quality.check'].search([ ('picking_id', '=', ml.picking_id.id), ('product_id', '=', ml.product_id.id), ('quality_state', '!=', 'none'), '|', ('move_line_id', '=', False), ('move_line_id', '=', ml.id) ]) # If there are completed quality checks, relink them instead of creating new ones if existing_checks: # Relink to this move line existing_checks.write({'move_line_id': ml.id}) # Skip creating new checks for this move line continue # Add to list for standard processing move_lines_to_process |= ml # Call parent method only for move lines that need new checks if move_lines_to_process: return super(StockMoveLine, move_lines_to_process)._create_check() return self.env['quality.check'] def write(self, vals): """ Override write method to detect lot_id and lot_name changes. When a lot number is assigned or changed on a receipt operation, update related quality checks with the new lot number. """ # Call parent write first to ensure the changes are saved result = super(StockMoveLine, self).write(vals) # Check if lot_id or lot_name was changed if 'lot_id' in vals or 'lot_name' in vals: # Process each move line that was updated for move_line in self: # Check if this is a receipt operation if move_line._is_receipt_operation(): # Get the lot_id to propagate to quality checks lot_id = move_line.lot_id.id if move_line.lot_id else False # Update related quality checks move_line._update_quality_check_lot(lot_id) return result def _is_receipt_operation(self): """ Check if the current move line is part of a receipt operation. Receipt operations are identified by picking_type_code == 'incoming'. This method implements robust operation type identification with multiple fallback paths to ensure reliable detection across different scenarios. Returns: bool: True if this is a receipt operation, False otherwise """ self.ensure_one() # Primary check: picking_type_code through the picking if self.picking_id and self.picking_id.picking_type_id: return self.picking_id.picking_type_id.code == 'incoming' # Fallback 1: check through move_id.picking_type_id if self.move_id and self.move_id.picking_type_id: return self.move_id.picking_type_id.code == 'incoming' # Fallback 2: check through move_id.picking_id (in case picking_type_id is not directly on move) if self.move_id and self.move_id.picking_id and self.move_id.picking_id.picking_type_id: return self.move_id.picking_id.picking_type_id.code == 'incoming' # Default: not a receipt operation if we can't determine the type return False def _update_quality_check_lot(self, lot_id): """ Update related quality checks with the new lot number. This method searches for quality checks linked to this move line and updates their lot_id field without triggering state reset. Also handles orphaned quality checks (where move_line_id was cleared). Args: lot_id: The ID of the lot to assign to quality checks (or False) """ self.ensure_one() QualityCheck = self.env['quality.check'] # Search for quality checks related to this move line quality_checks = QualityCheck.search([ ('move_line_id', '=', self.id) ]) # Also search for orphaned quality checks from the same picking # that might have been unlinked from their move_line if self.picking_id: orphaned_checks = QualityCheck.search([ ('picking_id', '=', self.picking_id.id), ('move_line_id', '=', False), ('product_id', '=', self.product_id.id), ('quality_state', '!=', 'none') ]) # Relink orphaned checks to this move line if orphaned_checks: orphaned_checks.write({'move_line_id': self.id}) quality_checks |= orphaned_checks # Filter to ensure we only update quality checks for receipt operations receipt_quality_checks = quality_checks.filtered( lambda qc: qc._is_receipt_operation() ) # Update the lot_id on each quality check without triggering state reset for quality_check in receipt_quality_checks: # Use the quality check's own method to update lot without state reset quality_check._update_lot_from_move_line_manual(lot_id)