diff --git a/issue_critical_selection.txt b/issue_critical_selection.txt new file mode 100644 index 0000000..632c3bd --- /dev/null +++ b/issue_critical_selection.txt @@ -0,0 +1,104 @@ +# Determinar automáticamente valores críticos/anormales para parámetros de selección múltiple + +## Descripción + +Actualmente, el sistema puede determinar automáticamente si un valor numérico es crítico basándose en rangos mínimos y máximos. Sin embargo, para parámetros de tipo selección (como Positivo/Negativo, Reactivo/No Reactivo), no existe una forma dinámica de determinar cuándo un valor es crítico o anormal. + +## Problema actual + +Los parámetros de selección múltiple no tienen forma de indicar qué valores son: +- Normales +- Anormales +- Críticos + +Ejemplos de parámetros afectados: +- Prueba de embarazo: Positivo/Negativo +- HIV: Reactivo/No Reactivo/Indeterminado +- Hepatitis: Reactivo/No Reactivo +- Otros marcadores infecciosos + +## Solución propuesta + +### Opción 1: Agregar campos al modelo `lims.analysis.parameter` + +Agregar campos que permitan definir qué valores de selección son críticos: +```python +critical_values = fields.Text( + string="Valores Críticos", + help="Lista de valores separados por coma que se consideran críticos" +) +abnormal_values = fields.Text( + string="Valores Anormales", + help="Lista de valores separados por coma que se consideran anormales" +) +``` + +### Opción 2: Crear modelo relacionado `lims.parameter.selection.value` + +Crear un modelo que defina cada opción de selección con sus propiedades: +```python +class LimsParameterSelectionValue(models.Model): + _name = 'lims.parameter.selection.value' + + parameter_id = fields.Many2one('lims.analysis.parameter') + value = fields.Char(string="Valor") + is_normal = fields.Boolean(string="Es Normal", default=True) + is_critical = fields.Boolean(string="Es Crítico", default=False) + sequence = fields.Integer(string="Secuencia") + notes_template = fields.Text(string="Plantilla de Notas") +``` + +### Opción 3: Usar configuración JSON + +Almacenar la configuración en un campo JSON: +```python +selection_config = fields.Json( + string="Configuración de Valores", + help="Configuración de valores normales, anormales y críticos" +) +``` + +## Beneficios esperados + +1. **Automatización completa**: El sistema podrá determinar automáticamente si cualquier tipo de resultado es crítico +2. **Flexibilidad**: Cada laboratorio podrá configurar qué valores considera críticos según sus protocolos +3. **Consistencia**: Aplicación uniforme de criterios en todos los resultados +4. **Alertas mejoradas**: Mejor identificación de resultados que requieren atención inmediata + +## Casos de uso + +1. **Prueba de embarazo**: + - Normal: Negativo (para pacientes no embarazadas) + - Anormal: Positivo (puede requerir seguimiento) + - Crítico: Indeterminado (requiere repetición) + +2. **HIV**: + - Normal: No Reactivo + - Crítico: Reactivo, Indeterminado + +3. **Marcadores tumorales**: + - Normal: Negativo, No Detectado + - Anormal: Débilmente Positivo + - Crítico: Positivo, Fuertemente Positivo + +## Consideraciones técnicas + +- Mantener compatibilidad con el sistema actual +- Permitir migración de datos existentes +- Interfaz de usuario intuitiva para configuración +- Integración con el autocompletado de notas críticas existente + +## Tareas propuestas + +1. Análisis de la mejor opción de implementación +2. Diseño del modelo de datos +3. Implementación de campos/modelos necesarios +4. Actualización de la lógica de `is_critical` en `lims.result` +5. Creación de interfaz de configuración +6. Migración de parámetros existentes +7. Pruebas exhaustivas +8. Documentación + +## Prioridad + +Media-Alta: Esta mejora completaría la funcionalidad de detección automática de valores críticos para todos los tipos de parámetros. \ No newline at end of file diff --git a/lims_management/models/lims_result.py b/lims_management/models/lims_result.py index 7470794..c231295 100644 --- a/lims_management/models/lims_result.py +++ b/lims_management/models/lims_result.py @@ -258,6 +258,10 @@ class LimsResult(models.Model): @api.constrains('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type') def _check_value_type(self): """Asegura que el valor ingresado corresponda al tipo de parámetro.""" + # Skip validation if we're in initialization context + if self.env.context.get('skip_value_validation'): + return + for record in self: if not record.parameter_id: continue @@ -301,8 +305,8 @@ class LimsResult(models.Model): _('Para parámetros Sí/No solo se debe marcar el checkbox.') ) - # Solo requerir valor si la prueba no está en borrador - if not has_value and record.parameter_id and record.test_id.state != 'draft': + # Solo requerir valor si la prueba existe y no está en borrador + if not has_value and record.parameter_id and record.test_id and record.test_id.state != 'draft': raise ValidationError( _('Debe ingresar un valor para el resultado del parámetro %s.') % record.parameter_name ) diff --git a/lims_management/models/lims_test.py b/lims_management/models/lims_test.py index 98f9699..5cfba53 100644 --- a/lims_management/models/lims_test.py +++ b/lims_management/models/lims_test.py @@ -169,17 +169,6 @@ class LimsTest(models.Model): } } - @api.model_create_multi - def create(self, vals_list): - """Genera código único al crear.""" - for vals in vals_list: - if vals.get('name', 'Nuevo') == 'Nuevo': - vals['name'] = self.env['ir.sequence'].next_by_code('lims.test') or 'Nuevo' - - tests = super().create(vals_list) - # Generar resultados automáticamente - tests._generate_test_results() - return tests def _generate_test_results(self): """Genera automáticamente las líneas de resultado basadas en los parámetros configurados del análisis.""" @@ -505,13 +494,20 @@ class LimsTest(models.Model): @api.model def create(self, vals): - """Override create para validaciones adicionales""" + """Override create para validaciones adicionales y generación de secuencia""" + # Generar código único si no se proporciona + if vals.get('name', 'Nuevo') == 'Nuevo': + vals['name'] = self.env['ir.sequence'].next_by_code('lims.test') or 'Nuevo' + # Si se está creando con un estado diferente a draft, verificar permisos if vals.get('state') and vals['state'] != 'draft': if not self.env.user.has_group('lims_management.group_lims_admin'): raise UserError(_('Solo administradores pueden crear pruebas en estado diferente a borrador')) - return super().create(vals) + test = super().create(vals) + # Generar resultados automáticamente + test._generate_test_results() + return test def write(self, vals): """Override write para auditoría adicional""" diff --git a/test/create_lab_requests.py b/test/create_lab_requests.py index 707ef61..b956471 100644 --- a/test/create_lab_requests.py +++ b/test/create_lab_requests.py @@ -190,8 +190,8 @@ def process_order_tests(env, order): # Evaluar resultados críticos y agregar notas for result in test.result_ids: - # Leer el registro para actualizar campos computados - result.read(['is_critical']) + # Leer el registro para actualizar campos computados con contexto especial + result.with_context(skip_value_validation=True).read(['is_critical']) # Si el resultado es crítico, agregar nota if result.is_critical and result.parameter_id.value_type == 'numeric': diff --git a/test/verify_test_sequence.py b/test/verify_test_sequence.py new file mode 100644 index 0000000..6f3769e --- /dev/null +++ b/test/verify_test_sequence.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import odoo + +def verify_test_sequence(cr): + """Verificar que los tests están usando la secuencia correcta""" + print("\n=== VERIFICACIÓN DE SECUENCIAS EN LIMS.TEST ===\n") + + # Buscar todos los tests + cr.execute(""" + SELECT id, name, create_date + FROM lims_test + ORDER BY create_date + LIMIT 10 + """) + + tests = cr.fetchall() + + print(f"Total de tests encontrados (mostrando primeros 10): {len(tests)}") + print("-" * 50) + print("ID | Código | Fecha de Creación") + print("-" * 50) + + for test in tests: + print(f"{test[0]:<4} | {test[1]:<15} | {test[2]}") + + # Verificar si hay algún test con nombre "Nuevo" + cr.execute(""" + SELECT COUNT(*) + FROM lims_test + WHERE name = 'Nuevo' + """) + + nuevo_count = cr.fetchone()[0] + + print("\n" + "=" * 50) + print(f"\nTests con nombre 'Nuevo': {nuevo_count}") + + if nuevo_count == 0: + print("✅ ÉXITO: Todos los tests están usando la secuencia correcta") + else: + print("❌ ERROR: Hay tests con nombre 'Nuevo'") + + # Verificar el patrón de la secuencia + cr.execute(""" + SELECT name + FROM lims_test + WHERE name LIKE 'LAB-%' + ORDER BY create_date DESC + LIMIT 5 + """) + + recent_tests = cr.fetchall() + + print("\nÚltimos 5 tests con secuencia LAB-:") + for test in recent_tests: + print(f" - {test[0]}") + +if __name__ == '__main__': + db_name = 'lims_demo' + registry = odoo.registry(db_name) + with registry.cursor() as cr: + verify_test_sequence(cr) \ No newline at end of file