clinical_laboratory/test/test_cancel_cascade.py
Luis Ernesto Portillo Zaldivar 39318f9073 feat(#54): Cancelar automáticamente muestras y pruebas al cancelar orden
- Agregar estado 'cancelled' a stock.lot para muestras
- Implementar método action_cancel() en stock.lot
- Override action_cancel() en sale.order para:
  * Cancelar muestras en estados: pending_collection, collected, received, in_process
  * Cancelar pruebas asociadas que no estén validadas
  * Registrar mensajes en el chatter de cada elemento cancelado
  * Mostrar resumen de elementos cancelados en la orden
- Agregar tests unitarios completos para verificar:
  * Cancelación correcta de muestras y pruebas
  * No cancelación de elementos en estados finales
  * Generación de mensajes en chatter
  * Órdenes normales no afectadas

La funcionalidad asegura que no queden muestras o pruebas "huérfanas"
cuando se cancela una orden de laboratorio.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-15 18:53:19 -06:00

149 lines
5.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script para probar la funcionalidad de cancelación en cascada
"""
import odoo
import traceback
from datetime import datetime
def test_cancel_cascade(cr):
"""Probar la cancelación en cascada de órdenes"""
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
print("🧪 Probando cancelación en cascada de órdenes de laboratorio\n")
try:
# Buscar un paciente y doctor de prueba
patient = env['res.partner'].search([('is_patient', '=', True)], limit=1)
doctor = env['res.partner'].search([('is_doctor', '=', True)], limit=1)
if not patient or not doctor:
print("❌ No se encontraron pacientes o doctores de prueba")
return
# Buscar un análisis
analysis = env['product.product'].search([
('is_analysis', '=', True),
('required_sample_type_id', '!=', False)
], limit=1)
if not analysis:
print("❌ No se encontró un análisis con tipo de muestra requerido")
return
print(f"📋 Creando orden de laboratorio de prueba...")
print(f" Paciente: {patient.name}")
print(f" Doctor: {doctor.name}")
print(f" Análisis: {analysis.name}")
# Crear orden de laboratorio
order = env['sale.order'].create({
'partner_id': patient.id,
'doctor_id': doctor.id,
'is_lab_request': True,
'order_line': [(0, 0, {
'product_id': analysis.id,
'product_uom_qty': 1.0
})]
})
print(f"\n✓ Orden creada: {order.name}")
print(f" Estado inicial: {order.state}")
# Confirmar la orden
print("\n🔄 Confirmando orden...")
order.action_confirm()
print(f"✓ Orden confirmada")
print(f" Estado: {order.state}")
print(f" Muestras generadas: {len(order.generated_sample_ids)}")
# Mostrar muestras generadas
if order.generated_sample_ids:
print("\n📦 Muestras generadas:")
for sample in order.generated_sample_ids:
print(f" - {sample.name}: {sample.sample_type_product_id.name} (Estado: {sample.state})")
# Buscar pruebas generadas
tests = env['lims.test'].search([
('sale_order_line_id.order_id', '=', order.id)
])
print(f"\n🔬 Pruebas generadas: {len(tests)}")
if tests:
for test in tests:
print(f" - {test.name}: {test.product_id.name} (Estado: {test.state})")
# Iniciar proceso en una prueba para hacerlo más realista
if tests and order.generated_sample_ids:
print("\n🔄 Iniciando proceso en primera prueba...")
test = tests[0]
test.sample_id = order.generated_sample_ids[0]
test.action_start_process()
print(f" ✓ Prueba {test.name} en proceso")
# CANCELAR LA ORDEN
print("\n❌ Cancelando la orden de laboratorio...")
order.action_cancel()
print(f"\n✓ Orden cancelada")
print(f" Estado de la orden: {order.state}")
# Verificar estado de las muestras
print("\n📦 Estado final de las muestras:")
for sample in order.generated_sample_ids:
print(f" - {sample.name}: {sample.state}")
# Verificar si hay mensaje en el chatter
last_msg = sample.message_ids[0] if sample.message_ids else None
if last_msg and "cancelada automáticamente" in last_msg.body:
print(f" ✓ Mensaje de cancelación registrado")
# Verificar estado de las pruebas
print("\n🔬 Estado final de las pruebas:")
for test in tests:
test_updated = env['lims.test'].browse(test.id)
print(f" - {test_updated.name}: {test_updated.state}")
# Verificar mensaje
last_msg = test_updated.message_ids[0] if test_updated.message_ids else None
if last_msg and "cancelada automáticamente" in last_msg.body:
print(f" ✓ Mensaje de cancelación registrado")
# Verificar mensaje en la orden
print("\n📝 Mensajes en la orden:")
for msg in order.message_ids[:3]: # Últimos 3 mensajes
if "cancelaron automáticamente" in msg.body:
print(f" ✓ Mensaje de resumen de cancelación encontrado")
# Extraer números del mensaje
import re
samples_match = re.search(r'(\d+) muestras', msg.body)
tests_match = re.search(r'(\d+) pruebas', msg.body)
if samples_match:
print(f" - Muestras canceladas: {samples_match.group(1)}")
if tests_match:
print(f" - Pruebas canceladas: {tests_match.group(1)}")
break
print("\n✅ Prueba completada exitosamente!")
# Hacer rollback para no dejar datos de prueba
cr.rollback()
print("\n⚠️ Cambios revertidos (rollback)")
except Exception as e:
print(f"\n❌ Error durante la prueba: {str(e)}")
traceback.print_exc()
cr.rollback()
if __name__ == '__main__':
db_name = 'lims_demo'
try:
registry = odoo.modules.registry.Registry(db_name)
with registry.cursor() as cr:
test_cancel_cascade(cr)
except Exception as e:
print(f"Error general: {e}")
traceback.print_exc()