139 lines
5.8 KiB
Python
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)
|