fix: Corregir generación de secuencias en lims.test
- Eliminar método create duplicado que sobrescribía la lógica de secuencias - Consolidar la generación de secuencias en un único método create - Agregar contexto especial para evitar validaciones durante la inicialización - Ahora todos los tests se crean con códigos secuenciales (LAB-YYYY-NNNNN) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
64f44b9d2c
commit
754c5f5572
104
issue_critical_selection.txt
Normal file
104
issue_critical_selection.txt
Normal file
|
@ -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.
|
|
@ -258,6 +258,10 @@ class LimsResult(models.Model):
|
||||||
@api.constrains('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
|
@api.constrains('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
|
||||||
def _check_value_type(self):
|
def _check_value_type(self):
|
||||||
"""Asegura que el valor ingresado corresponda al tipo de parámetro."""
|
"""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:
|
for record in self:
|
||||||
if not record.parameter_id:
|
if not record.parameter_id:
|
||||||
continue
|
continue
|
||||||
|
@ -301,8 +305,8 @@ class LimsResult(models.Model):
|
||||||
_('Para parámetros Sí/No solo se debe marcar el checkbox.')
|
_('Para parámetros Sí/No solo se debe marcar el checkbox.')
|
||||||
)
|
)
|
||||||
|
|
||||||
# Solo requerir valor si la prueba no está en borrador
|
# Solo requerir valor si la prueba existe y no está en borrador
|
||||||
if not has_value and record.parameter_id and record.test_id.state != 'draft':
|
if not has_value and record.parameter_id and record.test_id and record.test_id.state != 'draft':
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_('Debe ingresar un valor para el resultado del parámetro %s.') % record.parameter_name
|
_('Debe ingresar un valor para el resultado del parámetro %s.') % record.parameter_name
|
||||||
)
|
)
|
||||||
|
|
|
@ -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):
|
def _generate_test_results(self):
|
||||||
"""Genera automáticamente las líneas de resultado basadas en los parámetros configurados del análisis."""
|
"""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
|
@api.model
|
||||||
def create(self, vals):
|
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
|
# Si se está creando con un estado diferente a draft, verificar permisos
|
||||||
if vals.get('state') and vals['state'] != 'draft':
|
if vals.get('state') and vals['state'] != 'draft':
|
||||||
if not self.env.user.has_group('lims_management.group_lims_admin'):
|
if not self.env.user.has_group('lims_management.group_lims_admin'):
|
||||||
raise UserError(_('Solo administradores pueden crear pruebas en estado diferente a borrador'))
|
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):
|
def write(self, vals):
|
||||||
"""Override write para auditoría adicional"""
|
"""Override write para auditoría adicional"""
|
||||||
|
|
|
@ -190,8 +190,8 @@ def process_order_tests(env, order):
|
||||||
|
|
||||||
# Evaluar resultados críticos y agregar notas
|
# Evaluar resultados críticos y agregar notas
|
||||||
for result in test.result_ids:
|
for result in test.result_ids:
|
||||||
# Leer el registro para actualizar campos computados
|
# Leer el registro para actualizar campos computados con contexto especial
|
||||||
result.read(['is_critical'])
|
result.with_context(skip_value_validation=True).read(['is_critical'])
|
||||||
|
|
||||||
# Si el resultado es crítico, agregar nota
|
# Si el resultado es crítico, agregar nota
|
||||||
if result.is_critical and result.parameter_id.value_type == 'numeric':
|
if result.is_critical and result.parameter_id.value_type == 'numeric':
|
||||||
|
|
64
test/verify_test_sequence.py
Normal file
64
test/verify_test_sequence.py
Normal file
|
@ -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)
|
Loading…
Reference in New Issue
Block a user