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

198 lines
8.4 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, api, fields
class QualityCheck(models.Model):
_inherit = 'quality.check'
# Add a context flag to track when we're preserving state
_preserve_state_context_key = 'quality_check_preserve_state'
def _is_receipt_operation(self):
"""
Check if the current quality check 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_line_id if picking_id is not available
if self.move_line_id:
if self.move_line_id.picking_id and self.move_line_id.picking_id.picking_type_id:
return self.move_line_id.picking_id.picking_type_id.code == 'incoming'
# Fallback 2: check through move_id.picking_type_id
if self.move_line_id.move_id and self.move_line_id.move_id.picking_type_id:
return self.move_line_id.move_id.picking_type_id.code == 'incoming'
# Fallback 3: check through move_id if available
if hasattr(self, 'move_id') and self.move_id and self.move_id.picking_type_id:
return self.move_id.picking_type_id.code == 'incoming'
# Default: not a receipt operation if we can't determine the type
return False
def _should_preserve_state(self):
"""
Determine if the quality check state should be preserved during lot assignment.
State preservation applies only to receipt operations.
Returns:
bool: True if state should be preserved, False otherwise
"""
self.ensure_one()
return self._is_receipt_operation()
def write(self, vals):
"""
Override write to prevent state reset when lot_id is updated on receipt operations.
"""
# Store current states before write for receipt operations
state_data = {}
for record in self:
if record._should_preserve_state() and record.quality_state != 'none':
# Only preserve if we're not explicitly changing the quality_state
if 'quality_state' not in vals:
state_data[record.id] = {
'quality_state': record.quality_state,
'user_id': record.user_id.id if record.user_id else False,
'control_date': record.control_date,
}
# Perform the write
result = super(QualityCheck, self).write(vals)
# Restore states if they were changed
if state_data:
for record in self:
if record.id in state_data:
stored = state_data[record.id]
if record.quality_state != stored['quality_state']:
# Use SQL to restore state without triggering write again
self.env.cr.execute(
"""
UPDATE quality_check
SET quality_state = %s, user_id = %s, control_date = %s
WHERE id = %s
""",
(stored['quality_state'], stored['user_id'],
stored['control_date'], record.id)
)
record.invalidate_recordset(['quality_state', 'user_id', 'control_date'])
return result
@api.depends('move_line_id.lot_id')
def _compute_lot_line_id(self):
"""
Override the compute method to preserve quality check state during lot updates.
This prevents the state from being reset when lot numbers are assigned.
"""
for qc in self:
# Always update lot_line_id
qc.lot_line_id = qc.move_line_id.lot_id
# Check if we should update lot_id
if qc.lot_line_id and qc._update_lot_from_lot_line():
# For receipt operations, preserve the state
if qc._should_preserve_state():
# Store current state before updating lot_id
current_state = qc.quality_state
# Directly update lot_id field in database without triggering ORM
if current_state != 'none':
# Use SQL to update lot_id while preserving state
self.env.cr.execute(
"""
UPDATE quality_check
SET lot_id = %s
WHERE id = %s AND quality_state = %s
""",
(qc.lot_line_id.id if qc.lot_line_id else None, qc.id, current_state)
)
# Invalidate cache for lot_id only
qc.invalidate_recordset(['lot_id'])
else:
# If state is 'none', use normal assignment
qc.lot_id = qc.lot_line_id
else:
# For non-receipt operations, use standard behavior
qc.lot_id = qc.lot_line_id
def _update_lot_from_lot_line(self):
"""
Override the standard method from quality_control module.
This method is called by _compute_lot_line_id to determine if the lot_id
should be automatically updated from the move_line_id.lot_id.
For receipt operations, we return True to allow the update, but we handle
state preservation in the overridden _compute_lot_line_id method.
For non-receipt operations, we preserve standard Odoo behavior.
Returns:
bool: True to allow lot update (state preservation handled separately)
"""
self.ensure_one()
# Always return True to allow lot updates
# State preservation is handled in _compute_lot_line_id
return super(QualityCheck, self)._update_lot_from_lot_line()
def _check_to_unlink(self):
"""
Override to prevent deletion of completed quality checks on receipt operations.
"""
self.ensure_one()
# For receipt operations with completed quality checks, prevent deletion
if self._should_preserve_state() and self.quality_state != 'none':
return False
# For other cases, use standard behavior
return super(QualityCheck, self)._check_to_unlink()
def _update_lot_from_move_line_manual(self, lot_id):
"""
Manually update the lot_id field while preserving the current quality check state.
This method is called from stock.move.line when a lot number is assigned or changed
on receipt operations.
Args:
lot_id: The ID of the lot to assign (or False to clear)
"""
self.ensure_one()
# Store the current quality state before updating lot_id
current_state = self.quality_state
current_user = self.user_id
current_control_date = self.control_date
# Use context flag to preserve state during write
self.with_context(quality_check_preserve_state=True).sudo().write({'lot_id': lot_id})
# Force restore the quality state if it was changed during the write
if current_state != 'none':
# Use direct SQL update to avoid triggering any compute methods
self.env.cr.execute(
"""
UPDATE quality_check
SET quality_state = %s, user_id = %s, control_date = %s
WHERE id = %s
""",
(current_state, current_user.id if current_user else None,
current_control_date, self.id)
)
# Invalidate cache to ensure the updated values are reflected
self.invalidate_recordset(['quality_state', 'user_id', 'control_date'])