quality_check_lot_preserve/models/stock_move_line.py
2025-11-27 10:00:38 +07:00

139 lines
5.8 KiB
Python

# -*- 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)