diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index f9f6276..f15c95c 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -21,7 +21,9 @@
"WebFetch(domain:apps.odoo.com)",
"Bash(dir:*)",
"Bash(find:*)",
- "Bash(true)"
+ "Bash(true)",
+ "Bash(bash:*)",
+ "Bash(grep:*)"
],
"deny": []
}
diff --git a/init_odoo.py b/init_odoo.py
index fdff0c5..46e197f 100644
--- a/init_odoo.py
+++ b/init_odoo.py
@@ -36,6 +36,7 @@ odoo_command = [
"-d", DB_NAME,
"-i", MODULES_TO_INSTALL,
"--load-language", "es_ES",
+ "--without-demo=", # Forzar carga de datos demo
"--stop-after-init"
]
@@ -99,34 +100,62 @@ EOF
print("\nCreando datos de demostración de pruebas de laboratorio...")
sys.stdout.flush()
- if os.path.exists("/app/test/create_test_demo_data.py"):
- with open("/app/test/create_test_demo_data.py", "r") as f:
- test_script_content = f.read()
+ # Usar el nuevo script consolidado de datos demo
+ demo_script_path = "/app/test/create_demo_data.py"
+ if os.path.exists(demo_script_path):
+ with open(demo_script_path, "r") as f:
+ demo_script_content = f.read()
- create_tests_command = f"""
+ create_demo_command = f"""
odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF'
-{test_script_content}
+{demo_script_content}
EOF
"""
result = subprocess.run(
- create_tests_command,
+ create_demo_command,
shell=True,
capture_output=True,
text=True,
check=False
)
- print("--- Create Test Demo Data stdout ---")
+ print("--- Create Demo Data stdout ---")
print(result.stdout)
- print("--- Create Test Demo Data stderr ---")
+ print("--- Create Demo Data stderr ---")
print(result.stderr)
sys.stdout.flush()
if result.returncode == 0:
- print("Datos de demostración de pruebas creados exitosamente.")
+ print("Datos de demostración creados exitosamente.")
else:
- print(f"Advertencia: Fallo al crear datos de demostración de pruebas (código {result.returncode})")
+ print(f"Advertencia: Fallo al crear datos de demostración (código {result.returncode})")
+ else:
+ # Fallback al script anterior si existe
+ old_script_path = "/app/test/create_test_demo_data.py"
+ if os.path.exists(old_script_path):
+ print("Usando script de demostración anterior...")
+ with open(old_script_path, "r") as f:
+ test_script_content = f.read()
+
+ create_tests_command = f"""
+ odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF'
+{test_script_content}
+EOF
+ """
+
+ result = subprocess.run(
+ create_tests_command,
+ shell=True,
+ capture_output=True,
+ text=True,
+ check=False
+ )
+
+ if result.returncode == 0:
+ print("Datos de demostración de pruebas creados exitosamente.")
+ else:
+ print(f"Advertencia: Fallo al crear datos de demostración de pruebas (código {result.returncode})")
# --- Actualizar logo de la empresa ---
print("\nActualizando logo de la empresa...")
diff --git a/lims_management/__manifest__.py b/lims_management/__manifest__.py
index 27aafab..c7bf774 100644
--- a/lims_management/__manifest__.py
+++ b/lims_management/__manifest__.py
@@ -48,6 +48,9 @@
'demo/z_lims_demo.xml',
'demo/z_analysis_demo.xml',
'demo/z_sample_demo.xml',
+ 'demo/parameter_demo.xml',
+ 'demo/parameter_range_demo.xml',
+ 'demo/analysis_parameter_config_demo.xml',
'demo/z_automatic_generation_demo.xml',
],
'installable': True,
diff --git a/lims_management/demo/analysis_parameter_config_demo.xml b/lims_management/demo/analysis_parameter_config_demo.xml
new file mode 100644
index 0000000..b61b03a
--- /dev/null
+++ b/lims_management/demo/analysis_parameter_config_demo.xml
@@ -0,0 +1,363 @@
+
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+ 30
+ True
+
+
+
+
+
+ 40
+ True
+
+
+
+
+
+ 50
+ True
+
+
+
+
+
+ 60
+ True
+
+
+
+
+
+ 70
+ True
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+ 30
+ True
+
+
+
+
+
+ 40
+ True
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ False
+ Completar solo si el cultivo es positivo
+
+
+
+
+
+ 30
+ False
+ Completar solo si el cultivo es positivo. Formato: >100,000 UFC/mL
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ False
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ False
+
+
+
+
+
+
+ Química Sanguínea Básica
+ True
+ chemistry
+
+ service
+
+
+
+ 3.0
+
+ Panel básico de química sanguínea que incluye glucosa, creatinina, urea, ALT y AST.
+
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+ 30
+ True
+
+
+
+
+
+ 40
+ True
+
+
+
+
+
+ 50
+ True
+
+
+
+
+ Urianálisis Completo
+ True
+ other
+
+ service
+
+
+
+ 10.0
+
+ Examen completo de orina que incluye examen físico, químico y microscópico del sedimento.
+
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+ 30
+ True
+
+
+
+
+
+ 40
+ True
+
+
+
+
+
+ 50
+ True
+
+
+
+
+
+ 60
+ True
+
+
+
+
+
+ 70
+ True
+
+
+
+
+
+ 80
+ True
+
+
+
+
+
+ 90
+ True
+
+
+
+
+ Panel de Serología Básica
+ True
+ immunology
+
+ service
+
+
+
+ 5.0
+
+ Panel serológico que incluye HIV, Hepatitis B, Hepatitis C y VDRL.
+
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
+
+ 20
+ True
+
+
+
+
+
+ 30
+ True
+
+
+
+
+
+ 40
+ True
+
+
+
+
+ Prueba de Embarazo en Sangre
+ True
+ immunology
+
+ service
+
+
+
+ 1.0
+
+ Detección cualitativa de Beta-HCG en sangre.
+
+
+
+
+
+
+ 10
+ True
+
+
+
+
\ No newline at end of file
diff --git a/lims_management/demo/parameter_demo.xml b/lims_management/demo/parameter_demo.xml
new file mode 100644
index 0000000..585a565
--- /dev/null
+++ b/lims_management/demo/parameter_demo.xml
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+ HGB
+ Hemoglobina
+ numeric
+ g/dL
+ Concentración de hemoglobina en sangre
+
+
+
+
+ HCT
+ Hematocrito
+ numeric
+ %
+ Porcentaje del volumen de glóbulos rojos
+
+
+
+
+ RBC
+ Glóbulos Rojos
+ numeric
+ millones/µL
+ Recuento de eritrocitos
+
+
+
+
+ WBC
+ Glóbulos Blancos
+ numeric
+ mil/µL
+ Recuento de leucocitos
+
+
+
+
+ PLT
+ Plaquetas
+ numeric
+ mil/µL
+ Recuento de plaquetas
+
+
+
+
+ NEUT
+ Neutrófilos
+ numeric
+ %
+ Porcentaje de neutrófilos
+
+
+
+
+ LYMPH
+ Linfocitos
+ numeric
+ %
+ Porcentaje de linfocitos
+
+
+
+
+
+
+ GLU
+ Glucosa
+ numeric
+ mg/dL
+ Nivel de glucosa en sangre
+
+
+
+
+ CREA
+ Creatinina
+ numeric
+ mg/dL
+ Nivel de creatinina sérica
+
+
+
+
+ UREA
+ Urea
+ numeric
+ mg/dL
+ Nivel de urea en sangre
+
+
+
+
+ CHOL
+ Colesterol Total
+ numeric
+ mg/dL
+ Nivel de colesterol total
+
+
+
+
+ HDL
+ Colesterol HDL
+ numeric
+ mg/dL
+ Colesterol de alta densidad
+
+
+
+
+ LDL
+ Colesterol LDL
+ numeric
+ mg/dL
+ Colesterol de baja densidad
+
+
+
+
+ TRIG
+ Triglicéridos
+ numeric
+ mg/dL
+ Nivel de triglicéridos
+
+
+
+
+ ALT
+ Alanina Aminotransferasa (ALT)
+ numeric
+ U/L
+ Enzima hepática ALT
+
+
+
+
+ AST
+ Aspartato Aminotransferasa (AST)
+ numeric
+ U/L
+ Enzima hepática AST
+
+
+
+
+
+
+ U-COLOR
+ Color
+ selection
+ Amarillo claro,Amarillo,Amarillo oscuro,Ámbar,Rojizo,Marrón,Turbio
+ Color de la muestra de orina
+
+
+
+
+ U-ASP
+ Aspecto
+ selection
+ Transparente,Ligeramente turbio,Turbio,Muy turbio
+ Aspecto de la muestra de orina
+
+
+
+
+ U-PH
+ pH
+ numeric
+ unidades
+ pH de la orina
+
+
+
+
+ U-DENS
+ Densidad
+ numeric
+ g/mL
+ Densidad específica de la orina
+
+
+
+
+ U-PROT
+ Proteínas
+ selection
+ Negativo,Trazas,+,++,+++,++++
+ Presencia de proteínas en orina
+
+
+
+
+ U-GLU
+ Glucosa
+ selection
+ Negativo,Trazas,+,++,+++,++++
+ Presencia de glucosa en orina
+
+
+
+
+ U-SANG
+ Sangre
+ selection
+ Negativo,Trazas,+,++,+++
+ Presencia de sangre en orina
+
+
+
+
+ U-LEU
+ Leucocitos
+ numeric
+ por campo
+ Leucocitos en sedimento urinario
+
+
+
+
+ U-BACT
+ Bacterias
+ selection
+ Escasas,Moderadas,Abundantes
+ Presencia de bacterias en orina
+
+
+
+
+
+
+ CULT
+ Resultado del Cultivo
+ selection
+ Negativo,Positivo
+ Resultado del cultivo microbiológico
+
+
+
+
+ MICRO
+ Microorganismo Aislado
+ text
+ Identificación del microorganismo
+
+
+
+
+ UFC
+ Recuento de Colonias
+ text
+ UFC/mL (Unidades Formadoras de Colonias)
+
+
+
+
+
+
+ TP
+ Tiempo de Protrombina
+ numeric
+ segundos
+ Tiempo de coagulación PT
+
+
+
+
+ INR
+ INR
+ numeric
+ ratio
+ Índice Internacional Normalizado
+
+
+
+
+ TTP
+ Tiempo de Tromboplastina Parcial
+ numeric
+ segundos
+ Tiempo de coagulación PTT
+
+
+
+
+
+
+ HIV
+ HIV 1/2
+ selection
+ No Reactivo,Reactivo,Indeterminado
+ Anticuerpos anti-HIV
+
+
+
+
+ HBsAg
+ Antígeno de Superficie Hepatitis B
+ selection
+ No Reactivo,Reactivo,Indeterminado
+ HBsAg
+
+
+
+
+ HCV
+ Anticuerpos Hepatitis C
+ selection
+ No Reactivo,Reactivo,Indeterminado
+ Anti-HCV
+
+
+
+
+ VDRL
+ VDRL
+ selection
+ No Reactivo,Reactivo
+ Prueba de sífilis VDRL
+
+
+
+
+ HCG
+ Prueba de Embarazo
+ selection
+ Negativo,Positivo
+ Beta-HCG cualitativa
+
+
+
+
\ No newline at end of file
diff --git a/lims_management/demo/parameter_range_demo.xml b/lims_management/demo/parameter_range_demo.xml
new file mode 100644
index 0000000..816d9c5
--- /dev/null
+++ b/lims_management/demo/parameter_range_demo.xml
@@ -0,0 +1,374 @@
+
+
+
+
+
+
+ Hombre adulto
+ male
+ 18
+ 99
+ 13.5
+ 17.5
+ 7.0
+ 20.0
+
+
+
+
+ Mujer adulta
+ female
+ 18
+ 99
+ False
+ 12.0
+ 15.5
+ 7.0
+ 20.0
+
+
+
+
+ Mujer embarazada
+ female
+ 15
+ 50
+ True
+ 11.0
+ 14.0
+ 7.0
+ 20.0
+
+
+
+
+ Niños 2-12 años
+ both
+ 2
+ 12
+ 11.5
+ 14.5
+ 7.0
+ 20.0
+
+
+
+
+
+ Hombre adulto
+ male
+ 18
+ 99
+ 41
+ 53
+ 20
+ 60
+
+
+
+
+ Mujer adulta
+ female
+ 18
+ 99
+ 36
+ 46
+ 20
+ 60
+
+
+
+
+
+ Hombre adulto
+ male
+ 18
+ 99
+ 4.5
+ 5.9
+
+
+
+
+ Mujer adulta
+ female
+ 18
+ 99
+ 4.1
+ 5.1
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 4.5
+ 11.0
+ 2.0
+ 30.0
+
+
+
+
+ Niño
+ both
+ 2
+ 17
+ 5.0
+ 15.0
+ 2.0
+ 30.0
+
+
+
+
+
+ Todos
+ both
+ 0
+ 99
+ 150
+ 400
+ 50
+ 1000
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 45
+ 70
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 20
+ 45
+
+
+
+
+
+ Ayunas
+ both
+ 0
+ 99
+ 70
+ 100
+ 40
+ 500
+ Valores normales en ayunas. Prediabetes: 100-125 mg/dL. Diabetes: ≥126 mg/dL
+
+
+
+
+
+ Hombre adulto
+ male
+ 18
+ 99
+ 0.7
+ 1.3
+ 6.0
+
+
+
+
+ Mujer adulta
+ female
+ 18
+ 99
+ 0.6
+ 1.1
+ 6.0
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 15
+ 45
+ 100
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 0
+ 200
+ Deseable: <200 mg/dL. Límite alto: 200-239 mg/dL. Alto: ≥240 mg/dL
+
+
+
+
+
+ Hombre
+ male
+ 18
+ 99
+ 40
+ 100
+
+
+
+
+ Mujer
+ female
+ 18
+ 99
+ 50
+ 100
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 0
+ 100
+ Óptimo: <100 mg/dL. Casi óptimo: 100-129 mg/dL. Límite alto: 130-159 mg/dL. Alto: 160-189 mg/dL. Muy alto: ≥190 mg/dL
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 0
+ 150
+ 500
+ Normal: <150 mg/dL. Límite alto: 150-199 mg/dL. Alto: 200-499 mg/dL. Muy alto: ≥500 mg/dL
+
+
+
+
+
+ Hombre
+ male
+ 18
+ 99
+ 10
+ 40
+ 1000
+
+
+
+
+ Mujer
+ female
+ 18
+ 99
+ 10
+ 35
+ 1000
+
+
+
+
+
+ Adulto
+ both
+ 18
+ 99
+ 10
+ 40
+ 1000
+
+
+
+
+
+ Normal
+ both
+ 0
+ 99
+ 4.5
+ 8.0
+
+
+
+
+
+ Normal
+ both
+ 0
+ 99
+ 1.003
+ 1.030
+
+
+
+
+
+ Normal
+ both
+ 0
+ 99
+ 0
+ 5
+
+
+
+
+
+ Normal
+ both
+ 0
+ 99
+ 11
+ 13.5
+ 9
+ 30
+
+
+
+
+
+ Sin anticoagulación
+ both
+ 0
+ 99
+ 0.8
+ 1.2
+
+
+
+
+
+ Normal
+ both
+ 0
+ 99
+ 25
+ 35
+ 20
+ 70
+
+
+
+
\ No newline at end of file
diff --git a/test/create_demo_data.py b/test/create_demo_data.py
new file mode 100644
index 0000000..5c47913
--- /dev/null
+++ b/test/create_demo_data.py
@@ -0,0 +1,415 @@
+# -*- coding: utf-8 -*-
+"""
+Script para crear datos de demostración completos para el módulo LIMS.
+Incluye órdenes de laboratorio, muestras, pruebas y resultados.
+"""
+
+import odoo
+from datetime import datetime, timedelta
+import random
+import logging
+
+_logger = logging.getLogger(__name__)
+
+def create_demo_lab_data(cr):
+ """Crea datos completos de demostración para laboratorio"""
+ env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
+
+ print("\n=== INICIANDO CREACIÓN DE DATOS DE DEMOSTRACIÓN ===")
+
+ # Verificar que los parámetros y rangos se cargaron correctamente
+ param_count = env['lims.analysis.parameter'].search_count([])
+ range_count = env['lims.parameter.range'].search_count([])
+
+ print(f"Parámetros encontrados: {param_count}")
+ print(f"Rangos de referencia encontrados: {range_count}")
+
+ if param_count == 0 or range_count == 0:
+ print("⚠️ No se encontraron parámetros o rangos. Asegúrese de que los datos XML se cargaron.")
+ return
+
+ # Obtener pacientes de demostración
+ patients = []
+ patient_refs = [
+ 'lims_management.demo_patient_1',
+ 'lims_management.demo_patient_2',
+ 'lims_management.demo_patient_3',
+ 'lims_management.demo_patient_4'
+ ]
+
+ for ref in patient_refs:
+ patient = env.ref(ref, raise_if_not_found=False)
+ if patient:
+ patients.append(patient)
+
+ if not patients:
+ print("⚠️ No se encontraron pacientes de demostración")
+ return
+
+ print(f"Pacientes encontrados: {len(patients)}")
+
+ # Obtener doctores
+ doctors = []
+ doctor_refs = ['lims_management.demo_doctor_1', 'lims_management.demo_doctor_2']
+ for ref in doctor_refs:
+ doctor = env.ref(ref, raise_if_not_found=False)
+ if doctor:
+ doctors.append(doctor)
+
+ if not doctors:
+ # Crear un doctor de demo si no existe
+ doctors = [env['res.partner'].create({
+ 'name': 'Dr. Demo',
+ 'is_doctor': True
+ })]
+
+ # Obtener análisis disponibles
+ analyses = []
+ analysis_refs = [
+ 'lims_management.analysis_hemograma',
+ 'lims_management.analysis_perfil_lipidico',
+ 'lims_management.analysis_glucosa',
+ 'lims_management.analysis_quimica_sanguinea',
+ 'lims_management.analysis_urianalisis',
+ 'lims_management.analysis_serologia',
+ 'lims_management.analysis_urocultivo',
+ 'lims_management.analysis_tp',
+ 'lims_management.analysis_prueba_embarazo'
+ ]
+
+ for ref in analysis_refs:
+ analysis = env.ref(ref, raise_if_not_found=False)
+ if analysis:
+ analyses.append(analysis)
+
+ print(f"Análisis encontrados: {len(analyses)}")
+
+ if not analyses:
+ print("⚠️ No se encontraron análisis de demostración")
+ return
+
+ # Crear órdenes de laboratorio
+ orders_created = []
+
+ # Orden 1: Chequeo general para paciente adulto masculino
+ if len(patients) > 0 and len(analyses) >= 4:
+ order1 = env['sale.order'].create({
+ 'partner_id': patients[0].id,
+ 'doctor_id': doctors[0].id if doctors else False,
+ 'is_lab_request': True,
+ 'lab_request_priority': 'normal',
+ 'observations': 'Chequeo general anual - Control de salud preventivo',
+ 'order_line': [
+ (0, 0, {
+ 'product_id': analyses[0].product_variant_id.id, # Hemograma
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[1].product_variant_id.id, # Perfil Lipídico
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[2].product_variant_id.id, # Glucosa
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[3].product_variant_id.id, # Química Sanguínea
+ 'product_uom_qty': 1
+ })
+ ]
+ })
+ order1.action_confirm()
+ orders_created.append(order1)
+ print(f"✓ Orden {order1.name} creada para {order1.partner_id.name}")
+
+ # Orden 2: Control prenatal para paciente embarazada
+ if len(patients) > 1 and len(analyses) >= 5:
+ # Asegurarse de que la paciente esté marcada como embarazada
+ patients[1].is_pregnant = True
+
+ order2 = env['sale.order'].create({
+ 'partner_id': patients[1].id,
+ 'doctor_id': doctors[-1].id if doctors else False,
+ 'is_lab_request': True,
+ 'lab_request_priority': 'high',
+ 'observations': 'Control prenatal - 20 semanas de gestación',
+ 'order_line': [
+ (0, 0, {
+ 'product_id': analyses[0].product_variant_id.id, # Hemograma
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[2].product_variant_id.id, # Glucosa
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[4].product_variant_id.id if len(analyses) > 4 else analyses[0].product_variant_id.id, # Urianálisis
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[5].product_variant_id.id if len(analyses) > 5 else analyses[1].product_variant_id.id, # Serología
+ 'product_uom_qty': 1
+ })
+ ]
+ })
+ order2.action_confirm()
+ orders_created.append(order2)
+ print(f"✓ Orden {order2.name} creada para {order2.partner_id.name} (embarazada)")
+
+ # Orden 3: Urgencia - Sospecha de infección
+ if len(patients) > 2 and len(analyses) >= 3:
+ order3 = env['sale.order'].create({
+ 'partner_id': patients[2].id,
+ 'doctor_id': doctors[0].id if doctors else False,
+ 'is_lab_request': True,
+ 'lab_request_priority': 'urgent',
+ 'observations': 'Urgencia - Fiebre de 39°C, dolor lumbar, sospecha de infección urinaria',
+ 'order_line': [
+ (0, 0, {
+ 'product_id': analyses[4].product_variant_id.id if len(analyses) > 4 else analyses[0].product_variant_id.id, # Urianálisis
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[6].product_variant_id.id if len(analyses) > 6 else analyses[1].product_variant_id.id, # Urocultivo
+ 'product_uom_qty': 1
+ }),
+ (0, 0, {
+ 'product_id': analyses[0].product_variant_id.id, # Hemograma (para ver leucocitos)
+ 'product_uom_qty': 1
+ })
+ ]
+ })
+ order3.action_confirm()
+ orders_created.append(order3)
+ print(f"✓ Orden urgente {order3.name} creada para {order3.partner_id.name}")
+
+ # Orden 4: Control pediátrico
+ if len(patients) > 3:
+ order4 = env['sale.order'].create({
+ 'partner_id': patients[3].id,
+ 'doctor_id': doctors[-1].id if doctors else False,
+ 'is_lab_request': True,
+ 'lab_request_priority': 'normal',
+ 'observations': 'Control pediátrico - Evaluación de anemia, niña con palidez',
+ 'order_line': [
+ (0, 0, {
+ 'product_id': analyses[0].product_variant_id.id, # Hemograma completo
+ 'product_uom_qty': 1
+ })
+ ]
+ })
+ order4.action_confirm()
+ orders_created.append(order4)
+ print(f"✓ Orden pediátrica {order4.name} creada para {order4.partner_id.name}")
+
+ print(f"\n📋 Total de órdenes creadas: {len(orders_created)}")
+
+ # Procesar muestras y generar resultados
+ for idx, order in enumerate(orders_created):
+ print(f"\n--- Procesando orden {idx + 1}/{len(orders_created)}: {order.name} ---")
+
+ # Generar muestras si no existen
+ if not order.lab_sample_ids:
+ order.action_generate_samples()
+ print(f" ✓ Muestras generadas: {len(order.lab_sample_ids)}")
+
+ # Procesar cada muestra
+ for sample in order.lab_sample_ids:
+ # Marcar como recolectada
+ if sample.sample_state == 'pending_collection':
+ sample.action_collect()
+ print(f" ✓ Muestra {sample.name} recolectada")
+
+ # Procesar pruebas de esta muestra
+ for test in sample.test_ids:
+ print(f" - Procesando prueba: {test.product_id.name}")
+
+ # Iniciar proceso si está en borrador
+ if test.state == 'draft':
+ test.action_start_process()
+
+ # La generación automática de resultados ya debería haberse ejecutado
+ if test.result_ids:
+ print(f" ✓ Resultados generados automáticamente: {len(test.result_ids)}")
+
+ # Simular ingreso de valores en los resultados
+ simulate_test_results(env, test)
+
+ # Marcar como resultados ingresados
+ if test.state == 'in_process':
+ test.action_enter_results()
+ print(f" ✓ Resultados ingresados")
+
+ # Validar las primeras 2 órdenes completas y algunas pruebas de la tercera
+ should_validate = (idx < 2) or (idx == 2 and test == sample.test_ids[0])
+
+ if should_validate and test.state == 'result_entered':
+ test.action_validate()
+ print(f" ✓ Prueba validada")
+ else:
+ print(f" ⚠️ No se generaron resultados automáticamente")
+
+ # Resumen final
+ print("\n" + "="*60)
+ print("RESUMEN DE DATOS CREADOS")
+ print("="*60)
+
+ # Contar registros creados
+ total_samples = env['stock.lot'].search_count([('is_lab_sample', '=', True)])
+ total_tests = env['lims.test'].search_count([])
+ total_results = env['lims.result'].search_count([])
+
+ tests_by_state = {}
+ for state in ['draft', 'in_process', 'result_entered', 'validated', 'cancelled']:
+ count = env['lims.test'].search_count([('state', '=', state)])
+ if count > 0:
+ tests_by_state[state] = count
+
+ print(f"\n📊 Estadísticas:")
+ print(f" - Órdenes de laboratorio: {len(orders_created)}")
+ print(f" - Muestras totales: {total_samples}")
+ print(f" - Pruebas totales: {total_tests}")
+ print(f" - Resultados totales: {total_results}")
+ print(f"\n📈 Pruebas por estado:")
+ for state, count in tests_by_state.items():
+ print(f" - {state}: {count}")
+
+ # Verificar algunos resultados fuera de rango
+ out_of_range = env['lims.result'].search_count([('is_out_of_range', '=', True)])
+ critical = env['lims.result'].search_count([('is_critical', '=', True)])
+
+ if out_of_range or critical:
+ print(f"\n⚠️ Valores anormales:")
+ print(f" - Fuera de rango: {out_of_range}")
+ print(f" - Críticos: {critical}")
+
+ print("\n✅ Datos de demostración creados exitosamente")
+
+
+def simulate_test_results(env, test):
+ """Simular el ingreso de resultados realistas para una prueba"""
+
+ for result in test.result_ids:
+ param = result.parameter_id
+
+ if param.value_type == 'numeric':
+ # Generar valor numérico considerando el rango normal
+ if result.applicable_range_id:
+ range_obj = result.applicable_range_id
+
+ # Probabilidades: 75% normal, 20% anormal, 5% crítico
+ rand = random.random()
+
+ if rand < 0.75: # Valor normal
+ # Generar valor dentro del rango normal
+ value = random.uniform(range_obj.normal_min, range_obj.normal_max)
+
+ elif rand < 0.95: # Valor anormal pero no crítico
+ # Decidir si va por arriba o por abajo
+ if random.random() < 0.5 and range_obj.normal_min > 0:
+ # Por debajo del normal
+ value = random.uniform(range_obj.normal_min * 0.7, range_obj.normal_min * 0.95)
+ else:
+ # Por encima del normal
+ value = random.uniform(range_obj.normal_max * 1.05, range_obj.normal_max * 1.3)
+
+ else: # Valor crítico (5%)
+ if range_obj.critical_min and random.random() < 0.5:
+ # Crítico bajo
+ value = random.uniform(range_obj.critical_min * 0.5, range_obj.critical_min * 0.9)
+ elif range_obj.critical_max:
+ # Crítico alto
+ value = random.uniform(range_obj.critical_max * 1.1, range_obj.critical_max * 1.5)
+ else:
+ # Si no hay valores críticos definidos, usar un valor muy anormal
+ value = range_obj.normal_max * 2.0
+
+ # Redondear según el tipo de parámetro
+ if param.code in ['HGB', 'CREA', 'GLU', 'CHOL', 'HDL', 'LDL', 'TRIG']:
+ result.value_numeric = round(value, 1)
+ elif param.code in ['U-PH', 'U-DENS']:
+ result.value_numeric = round(value, 3)
+ else:
+ result.value_numeric = round(value, 2)
+
+ # Agregar notas para valores anormales en algunos casos
+ if result.is_out_of_range and random.random() < 0.3:
+ if param.code == 'GLU' and result.value_numeric > 126:
+ result.notes = "Hiperglucemia - Sugerir control de diabetes"
+ elif param.code == 'WBC' and result.value_numeric > 11:
+ result.notes = "Leucocitosis - Posible proceso infeccioso"
+ elif param.code == 'HGB' and result.value_numeric < range_obj.normal_min:
+ result.notes = "Anemia - Evaluar causa"
+
+ else:
+ # Sin rango definido, usar valores típicos
+ result.value_numeric = round(random.uniform(10, 100), 2)
+
+ elif param.value_type == 'selection':
+ # Seleccionar una opción con pesos realistas
+ if param.selection_values:
+ options = [opt.strip() for opt in param.selection_values.split(',')]
+
+ # Para cultivos, 70% negativo, 30% positivo
+ if param.code in ['CULT', 'HIV', 'HBsAg', 'HCV', 'VDRL']:
+ if 'Negativo' in options or 'No Reactivo' in options:
+ negative_option = 'Negativo' if 'Negativo' in options else 'No Reactivo'
+ positive_option = 'Positivo' if 'Positivo' in options else 'Reactivo'
+ result.value_selection = negative_option if random.random() < 0.7 else positive_option
+ else:
+ result.value_selection = random.choice(options)
+
+ # Para orina, distribución más realista
+ elif param.code == 'U-COLOR':
+ weights = [0.1, 0.6, 0.2, 0.05, 0.02, 0.02, 0.01] # Amarillo más común
+ result.value_selection = random.choices(options, weights=weights[:len(options)])[0]
+
+ elif param.code == 'U-ASP':
+ weights = [0.7, 0.2, 0.08, 0.02] # Transparente más común
+ result.value_selection = random.choices(options, weights=weights[:len(options)])[0]
+
+ else:
+ # Primera opción más probable (generalmente es la normal)
+ weights = [0.7] + [0.3/(len(options)-1)]*(len(options)-1)
+ result.value_selection = random.choices(options, weights=weights)[0]
+
+ elif param.value_type == 'boolean':
+ # Para pruebas de embarazo, considerar el género del paciente
+ if param.code == 'HCG' and test.patient_id.gender == 'female' and test.patient_id.is_pregnant:
+ result.value_boolean = True
+ else:
+ # 85% probabilidad de False (negativo) para la mayoría de pruebas
+ result.value_boolean = random.random() > 0.85
+
+ elif param.value_type == 'text':
+ # Generar texto según el parámetro
+ if param.code == 'MICRO':
+ # Solo si el cultivo es positivo
+ culture_result = test.result_ids.filtered(
+ lambda r: r.parameter_id.code == 'CULT'
+ )
+ if culture_result and culture_result.value_selection == 'Positivo':
+ organisms = ['E. coli', 'Klebsiella pneumoniae', 'Proteus mirabilis',
+ 'Enterococcus faecalis', 'Staphylococcus aureus',
+ 'Pseudomonas aeruginosa', 'Streptococcus agalactiae']
+ result.value_text = random.choice(organisms)
+ else:
+ result.value_text = "No se aisló microorganismo"
+
+ elif param.code == 'UFC':
+ # Solo si hay microorganismo
+ micro_result = test.result_ids.filtered(
+ lambda r: r.parameter_id.code == 'MICRO'
+ )
+ if micro_result and micro_result.value_text and micro_result.value_text != "No se aisló microorganismo":
+ counts = ['>100,000', '>50,000', '>10,000', '<10,000']
+ weights = [0.5, 0.3, 0.15, 0.05]
+ result.value_text = random.choices(counts, weights=weights)[0] + " UFC/mL"
+
+
+if __name__ == '__main__':
+ db_name = 'lims_demo'
+ registry = odoo.registry(db_name)
+ with registry.cursor() as cr:
+ create_demo_lab_data(cr)
+ cr.commit()
\ No newline at end of file
diff --git a/test/create_test_demo_data.py b/test/create_test_demo_data.py
deleted file mode 100644
index 4952c24..0000000
--- a/test/create_test_demo_data.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-import odoo
-from datetime import datetime, timedelta
-
-def create_test_demo_data(cr):
- """Crea datos de demostración para lims.test y lims.result"""
- env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
-
- # Buscar algunos pacientes y análisis existentes
- patients = env['res.partner'].search([('is_patient', '=', True)], limit=3)
- if not patients:
- print("No se encontraron pacientes para crear pruebas de demostración")
- return
-
- # Buscar análisis disponibles
- hemograma = env.ref('lims_management.analysis_hemograma', raise_if_not_found=False)
- glucosa = env.ref('lims_management.analysis_glucosa', raise_if_not_found=False)
-
- if not hemograma or not glucosa:
- print("No se encontraron análisis de demostración")
- return
-
- # Buscar o crear una orden de laboratorio simple
- lab_order = env['sale.order'].search([
- ('is_lab_request', '=', True),
- ('state', '=', 'sale')
- ], limit=1)
-
- if not lab_order:
- # Crear una orden básica si no existe
- lab_order = env['sale.order'].create({
- 'partner_id': patients[0].id,
- 'is_lab_request': True,
- 'order_line': [(0, 0, {
- 'product_id': hemograma.product_variant_id.id,
- 'product_uom_qty': 1
- }), (0, 0, {
- 'product_id': glucosa.product_variant_id.id,
- 'product_uom_qty': 1
- })]
- })
- lab_order.action_confirm()
-
- # Obtener las líneas de orden
- order_lines = lab_order.order_line
- if not order_lines:
- print("No se encontraron líneas de orden")
- return
-
- # Buscar muestras existentes
- samples = env['stock.lot'].search([
- ('is_lab_sample', '=', True),
- ('patient_id', '=', lab_order.partner_id.id)
- ], limit=2)
-
- if not samples:
- print("No se encontraron muestras de laboratorio")
- return
-
- # Crear prueba 1: Hemograma en proceso
- test1 = env['lims.test'].create({
- 'sale_order_line_id': order_lines[0].id,
- 'sample_id': samples[0].id,
- 'state': 'draft'
- })
-
- # Iniciar proceso
- test1.action_start_process()
-
- # Crear resultados para hemograma
- results_data = [
- {
- 'test_id': test1.id,
- 'parameter_name': 'Glóbulos Rojos',
- 'sequence': 10,
- 'value_numeric': 4.5,
- 'unit': '10^6/µL',
- 'normal_min': 4.2,
- 'normal_max': 5.4
- },
- {
- 'test_id': test1.id,
- 'parameter_name': 'Glóbulos Blancos',
- 'sequence': 20,
- 'value_numeric': 12.5, # Fuera de rango
- 'unit': '10^3/µL',
- 'normal_min': 4.5,
- 'normal_max': 11.0,
- 'notes': 'Valor elevado - posible infección'
- },
- {
- 'test_id': test1.id,
- 'parameter_name': 'Hemoglobina',
- 'sequence': 30,
- 'value_numeric': 14.2,
- 'unit': 'g/dL',
- 'normal_min': 12.0,
- 'normal_max': 16.0
- },
- {
- 'test_id': test1.id,
- 'parameter_name': 'Plaquetas',
- 'sequence': 40,
- 'value_numeric': 250,
- 'unit': '10^3/µL',
- 'normal_min': 150,
- 'normal_max': 400
- }
- ]
-
- for result_data in results_data:
- env['lims.result'].create(result_data)
-
- print(f"Creada prueba {test1.name} con 4 resultados")
-
- # Crear prueba 2: Glucosa con resultado ingresado
- if len(order_lines) > 1:
- test2 = env['lims.test'].create({
- 'sale_order_line_id': order_lines[1].id,
- 'sample_id': samples[0].id,
- 'state': 'draft'
- })
-
- test2.action_start_process()
-
- # Crear resultado de glucosa
- env['lims.result'].create({
- 'test_id': test2.id,
- 'parameter_name': 'Glucosa en Ayunas',
- 'sequence': 10,
- 'value_numeric': 125, # Fuera de rango
- 'unit': 'mg/dL',
- 'normal_min': 70,
- 'normal_max': 110,
- 'notes': 'Valor elevado - prediabetes'
- })
-
- # Marcar resultados como ingresados
- test2.action_enter_results()
-
- print(f"Creada prueba {test2.name} con resultado ingresado")
-
- # Crear prueba 3: Uroanálisis con valores mixtos (si hay más pacientes)
- if len(patients) > 1 and len(samples) > 1:
- # Crear una orden adicional
- urine_analysis = env['product.template'].search([
- ('is_analysis', '=', True),
- ('name', 'ilike', 'orina')
- ], limit=1)
-
- if urine_analysis:
- lab_order2 = env['sale.order'].create({
- 'partner_id': patients[1].id,
- 'is_lab_request': True,
- 'order_line': [(0, 0, {
- 'product_id': urine_analysis.product_variant_id.id,
- 'product_uom_qty': 1
- })]
- })
- lab_order2.action_confirm()
-
- test3 = env['lims.test'].create({
- 'sale_order_line_id': lab_order2.order_line[0].id,
- 'sample_id': samples[1].id,
- 'state': 'draft'
- })
-
- test3.action_start_process()
-
- # Crear resultados mixtos
- urine_results = [
- {
- 'test_id': test3.id,
- 'parameter_name': 'Color',
- 'sequence': 10,
- 'value_text': 'Amarillo claro'
- },
- {
- 'test_id': test3.id,
- 'parameter_name': 'pH',
- 'sequence': 20,
- 'value_numeric': 6.5,
- 'normal_min': 4.6,
- 'normal_max': 8.0
- },
- {
- 'test_id': test3.id,
- 'parameter_name': 'Densidad',
- 'sequence': 30,
- 'value_numeric': 1.020,
- 'normal_min': 1.005,
- 'normal_max': 1.030
- },
- {
- 'test_id': test3.id,
- 'parameter_name': 'Proteínas',
- 'sequence': 40,
- 'value_text': 'Negativo'
- },
- {
- 'test_id': test3.id,
- 'parameter_name': 'Glucosa',
- 'sequence': 50,
- 'value_text': 'Negativo'
- }
- ]
-
- for result_data in urine_results:
- env['lims.result'].create(result_data)
-
- # Ingresar y validar resultados
- test3.action_enter_results()
- if test3.state == 'result_entered':
- test3.action_validate()
-
- print(f"Creada prueba {test3.name} validada con resultados mixtos")
-
- print("\nDatos de demostración de pruebas creados exitosamente")
-
-if __name__ == '__main__':
- db_name = 'lims_demo'
- registry = odoo.registry(db_name)
- with registry.cursor() as cr:
- create_test_demo_data(cr)
- cr.commit()
\ No newline at end of file
diff --git a/test/verify_demo_data.py b/test/verify_demo_data.py
new file mode 100644
index 0000000..1fc8779
--- /dev/null
+++ b/test/verify_demo_data.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Script para verificar los datos de demostración cargados.
+"""
+
+import odoo
+import json
+
+def verify_demo_data(cr):
+ """Verificar datos de demostración"""
+
+ # Verificar parámetros
+ cr.execute("""
+ SELECT COUNT(*) as total,
+ COUNT(DISTINCT value_type) as tipos
+ FROM lims_analysis_parameter
+ """)
+ params = cr.fetchone()
+
+ # Verificar rangos
+ cr.execute("""
+ SELECT COUNT(*) as total,
+ COUNT(DISTINCT parameter_id) as parametros_con_rangos
+ FROM lims_parameter_range
+ """)
+ ranges = cr.fetchone()
+
+ # Verificar configuración de parámetros en análisis
+ cr.execute("""
+ SELECT pt.name as analisis,
+ COUNT(ptp.id) as parametros_configurados
+ FROM product_template pt
+ LEFT JOIN product_template_parameter ptp ON ptp.product_tmpl_id = pt.id
+ WHERE pt.is_analysis = true
+ GROUP BY pt.id, pt.name
+ ORDER BY pt.name
+ """)
+ analysis_config = cr.fetchall()
+
+ # Verificar órdenes de laboratorio
+ cr.execute("""
+ SELECT COUNT(*) as total_ordenes,
+ COUNT(DISTINCT partner_id) as pacientes_distintos,
+ COUNT(CASE WHEN state = 'sale' THEN 1 END) as confirmadas
+ FROM sale_order
+ WHERE is_lab_request = true
+ """)
+ orders = cr.fetchone()
+
+ # Verificar muestras
+ cr.execute("""
+ SELECT COUNT(*) as total_muestras,
+ COUNT(DISTINCT sample_state) as estados_distintos
+ FROM stock_lot
+ WHERE is_lab_sample = true
+ """)
+ samples = cr.fetchone()
+
+ # Verificar pruebas
+ cr.execute("""
+ SELECT COUNT(*) as total_pruebas,
+ COUNT(CASE WHEN state = 'validated' THEN 1 END) as validadas,
+ COUNT(CASE WHEN state = 'result_entered' THEN 1 END) as con_resultados
+ FROM lims_test
+ """)
+ tests = cr.fetchone()
+
+ # Verificar resultados
+ cr.execute("""
+ SELECT COUNT(*) as total_resultados,
+ COUNT(CASE WHEN is_out_of_range = true THEN 1 END) as fuera_rango,
+ COUNT(CASE WHEN is_critical = true THEN 1 END) as criticos
+ FROM lims_result
+ """)
+ results = cr.fetchone()
+
+ return {
+ 'parametros': {
+ 'total': params[0],
+ 'tipos_distintos': params[1]
+ },
+ 'rangos': {
+ 'total': ranges[0],
+ 'parametros_con_rangos': ranges[1]
+ },
+ 'analisis_configurados': [
+ {'analisis': row[0], 'parametros': row[1]}
+ for row in analysis_config
+ ],
+ 'ordenes': {
+ 'total': orders[0],
+ 'pacientes_distintos': orders[1],
+ 'confirmadas': orders[2]
+ },
+ 'muestras': {
+ 'total': samples[0],
+ 'estados_distintos': samples[1]
+ },
+ 'pruebas': {
+ 'total': tests[0],
+ 'validadas': tests[1],
+ 'con_resultados': tests[2]
+ },
+ 'resultados': {
+ 'total': results[0],
+ 'fuera_rango': results[1],
+ 'criticos': results[2]
+ }
+ }
+
+
+if __name__ == '__main__':
+ import sys
+ sys.path.insert(0, '/usr/lib/python3/dist-packages')
+
+ db_name = 'lims_demo'
+ registry = odoo.registry(db_name)
+
+ with registry.cursor() as cr:
+ data = verify_demo_data(cr)
+
+ print("\n" + "="*60)
+ print("VERIFICACIÓN DE DATOS DE DEMOSTRACIÓN")
+ print("="*60)
+
+ print(f"\n📊 PARÁMETROS DE ANÁLISIS:")
+ print(f" - Total: {data['parametros']['total']}")
+ print(f" - Tipos distintos: {data['parametros']['tipos_distintos']}")
+
+ print(f"\n📏 RANGOS DE REFERENCIA:")
+ print(f" - Total: {data['rangos']['total']}")
+ print(f" - Parámetros con rangos: {data['rangos']['parametros_con_rangos']}")
+
+ print(f"\n🧪 ANÁLISIS CONFIGURADOS:")
+ for item in data['analisis_configurados']:
+ if item['parametros'] > 0:
+ print(f" - {item['analisis']}: {item['parametros']} parámetros")
+
+ print(f"\n📋 ÓRDENES DE LABORATORIO:")
+ print(f" - Total: {data['ordenes']['total']}")
+ print(f" - Pacientes distintos: {data['ordenes']['pacientes_distintos']}")
+ print(f" - Confirmadas: {data['ordenes']['confirmadas']}")
+
+ print(f"\n🧪 MUESTRAS:")
+ print(f" - Total: {data['muestras']['total']}")
+ print(f" - Estados distintos: {data['muestras']['estados_distintos']}")
+
+ print(f"\n🔬 PRUEBAS:")
+ print(f" - Total: {data['pruebas']['total']}")
+ print(f" - Validadas: {data['pruebas']['validadas']}")
+ print(f" - Con resultados: {data['pruebas']['con_resultados']}")
+
+ print(f"\n📊 RESULTADOS:")
+ print(f" - Total: {data['resultados']['total']}")
+ print(f" - Fuera de rango: {data['resultados']['fuera_rango']}")
+ print(f" - Críticos: {data['resultados']['criticos']}")
+
+ print("\n" + "="*60)
\ No newline at end of file