diff --git a/documents/plans/ISSUE32_PLAN.md b/documents/plans/ISSUE32_PLAN.md index 6d744b7..db5c1ca 100644 --- a/documents/plans/ISSUE32_PLAN.md +++ b/documents/plans/ISSUE32_PLAN.md @@ -70,12 +70,12 @@ Automatizar la generación de muestras cuando se confirman órdenes de laborator - [x] Agregar validación de duplicados - [x] Considerar prefijos por tipo de muestra -### 4. Actualizar vistas de sale.order +### 4. Actualizar vistas de sale.order ✅ **Archivo:** `lims_management/views/sale_order_views.xml` -- [ ] Agregar pestaña "Muestras Generadas" en formulario de orden -- [ ] Mostrar campo `generated_sample_ids` con vista de lista embebida -- [ ] Agregar botón para regenerar muestras (si es necesario) -- [ ] Incluir indicadores visuales del estado de generación +- [x] Agregar pestaña "Muestras Generadas" en formulario de orden +- [x] Mostrar campo `generated_sample_ids` con vista de lista embebida +- [x] Agregar botón para regenerar muestras (si es necesario) +- [x] Incluir indicadores visuales del estado de generación ### 5. Crear wizard de configuración (opcional) **Archivos:** @@ -85,13 +85,13 @@ Automatizar la generación de muestras cuando se confirman órdenes de laborator - [ ] Permitir ajustes manuales de agrupación si es necesario - [ ] Opción para excluir ciertos análisis de la generación automática -### 6. Notificaciones y alertas +### 6. Notificaciones y alertas ✅ **Archivo:** `lims_management/models/sale_order.py` -- [ ] Implementar sistema de notificaciones: +- [x] Implementar sistema de notificaciones: - Análisis sin tipo de muestra definido - Muestras generadas exitosamente - Errores en la generación -- [ ] Usar el sistema de mensajería de Odoo (`mail.thread`) +- [x] Usar el sistema de mensajería de Odoo (`mail.thread`) ### 7. Pruebas y validación **Archivo:** `verify_automatic_sample_generation.py` diff --git a/lims_management/models/__pycache__/sale_order.cpython-312.pyc b/lims_management/models/__pycache__/sale_order.cpython-312.pyc index 7ad7754..560f98c 100644 Binary files a/lims_management/models/__pycache__/sale_order.cpython-312.pyc and b/lims_management/models/__pycache__/sale_order.cpython-312.pyc differ diff --git a/verify_automatic_sample_generation.py b/verify_automatic_sample_generation.py new file mode 100644 index 0000000..dedd067 --- /dev/null +++ b/verify_automatic_sample_generation.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +""" +Verification script for automatic sample generation in LIMS +This script tests the automatic generation of samples when lab orders are confirmed. +""" + +import odoo +import json +from datetime import datetime + +def verify_automatic_sample_generation(cr): + """Verify automatic sample generation functionality""" + env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) + + print("\n" + "="*60) + print("AUTOMATIC SAMPLE GENERATION VERIFICATION") + print("="*60) + print(f"Execution time: {datetime.now()}") + print("="*60 + "\n") + + # 1. Check existing lab orders with generated samples + print("1. EXISTING LAB ORDERS WITH GENERATED SAMPLES:") + print("-" * 60) + + lab_orders = env['sale.order'].search([ + ('is_lab_request', '=', True), + ('state', '=', 'sale') + ]) + + if not lab_orders: + print("No confirmed lab orders found.") + else: + for order in lab_orders: + print(f"\nOrder: {order.name}") + print(f" Patient: {order.partner_id.name}") + print(f" Doctor: {order.doctor_id.name if order.doctor_id else 'None'}") + print(f" Generated Samples: {len(order.generated_sample_ids)}") + + if order.generated_sample_ids: + for sample in order.generated_sample_ids: + print(f" - {sample.name} | Barcode: {sample.barcode}") + print(f" Type: {sample.sample_type_product_id.name if sample.sample_type_product_id else 'None'}") + print(f" Volume: {sample.volume_ml} ml") + print(f" Analyses: {sample.analysis_names}") + print(f" State: {sample.state}") + + # 2. Test sample generation with a new order + print("\n2. TESTING NEW SAMPLE GENERATION:") + print("-" * 60) + + try: + # Get test data + patient = env.ref('lims_management.demo_patient_1', raise_if_not_found=False) + doctor = env.ref('lims_management.demo_doctor_1', raise_if_not_found=False) + + if not patient: + print("ERROR: Demo patient not found. Cannot proceed with test.") + return + + # Get some analyses with different sample types + analyses = env['product.template'].search([ + ('is_analysis', '=', True), + ('required_sample_type_id', '!=', False) + ], limit=5) + + if not analyses: + print("ERROR: No analyses with sample types found. Cannot proceed with test.") + return + + print(f"Creating test order with {len(analyses)} analyses...") + + # Create order lines + order_lines = [] + for analysis in analyses: + order_lines.append((0, 0, { + 'product_id': analysis.product_variant_id.id, + 'product_uom_qty': 1 + })) + + # Create test order + test_order = env['sale.order'].create({ + 'partner_id': patient.id, + 'doctor_id': doctor.id if doctor else False, + 'is_lab_request': True, + 'order_line': order_lines + }) + + print(f"Created order: {test_order.name}") + print("Confirming order...") + + # Confirm order (this should trigger sample generation) + test_order.action_confirm() + + print(f"\nOrder confirmed. Generated samples: {len(test_order.generated_sample_ids)}") + + # Check generated samples + if test_order.generated_sample_ids: + print("\nGenerated samples details:") + + # Group by sample type to verify grouping logic + sample_types = {} + for sample in test_order.generated_sample_ids: + sample_type_name = sample.sample_type_product_id.name if sample.sample_type_product_id else 'Unknown' + if sample_type_name not in sample_types: + sample_types[sample_type_name] = [] + sample_types[sample_type_name].append(sample) + + for sample_type, samples in sample_types.items(): + print(f"\n Sample Type: {sample_type}") + for sample in samples: + print(f" - {sample.name}") + print(f" Barcode: {sample.barcode}") + print(f" Volume: {sample.volume_ml} ml") + print(f" Analyses: {sample.analysis_names}") + print(f" State: {sample.state}") + else: + print("WARNING: No samples were generated!") + + # Check for messages/notifications + print("\nChecking notifications...") + messages = test_order.message_ids.filtered(lambda m: m.body) + for msg in messages[:5]: # Show last 5 messages + print(f" - {msg.body[:100]}...") + + except Exception as e: + print(f"ERROR during test: {str(e)}") + import traceback + traceback.print_exc() + + # 3. Verify barcode uniqueness + print("\n3. BARCODE UNIQUENESS CHECK:") + print("-" * 60) + + all_samples = env['stock.lot'].search([ + ('is_lab_sample', '=', True), + ('barcode', '!=', False) + ]) + + barcodes = {} + duplicates = [] + + for sample in all_samples: + if sample.barcode in barcodes: + duplicates.append((sample.barcode, sample.name, barcodes[sample.barcode])) + else: + barcodes[sample.barcode] = sample.name + + print(f"Total samples with barcodes: {len(all_samples)}") + print(f"Unique barcodes: {len(barcodes)}") + print(f"Duplicates found: {len(duplicates)}") + + if duplicates: + print("\nDUPLICATE BARCODES FOUND:") + for barcode, name1, name2 in duplicates: + print(f" Barcode {barcode}: {name1} and {name2}") + + # 4. Check analyses without sample types + print("\n4. ANALYSES WITHOUT SAMPLE TYPES:") + print("-" * 60) + + analyses_without_type = env['product.template'].search([ + ('is_analysis', '=', True), + ('required_sample_type_id', '=', False) + ]) + + print(f"Found {len(analyses_without_type)} analyses without sample types:") + for analysis in analyses_without_type[:10]: # Show first 10 + print(f" - {analysis.name}") + + if len(analyses_without_type) > 10: + print(f" ... and {len(analyses_without_type) - 10} more") + + # 5. Summary + print("\n" + "="*60) + print("SUMMARY:") + print("="*60) + + issues = [] + + if duplicates: + issues.append(f"{len(duplicates)} duplicate barcodes found") + + if analyses_without_type: + issues.append(f"{len(analyses_without_type)} analyses without sample types") + + if issues: + print("\n⚠️ ISSUES FOUND:") + for issue in issues: + print(f" - {issue}") + else: + print("\n✅ All checks passed successfully!") + + print("\n" + "="*60) + +if __name__ == '__main__': + db_name = 'lims_demo' + try: + registry = odoo.registry(db_name) + with registry.cursor() as cr: + verify_automatic_sample_generation(cr) + except Exception as e: + print(f"ERROR: {str(e)}") + print("\nMake sure:") + print("1. The Odoo instance is running") + print("2. The database 'lims_demo' exists") + print("3. The LIMS module is installed") \ No newline at end of file