Files
whatsapp/scripts/etl_examenes.php
T
Lizandro GuarnizoandClaude Sonnet 4.6 fb0e77523c feat: módulo lab_examenes — CRUD catálogo de exámenes, items y tarifas
- Nuevo módulo lab_examenes con lista paginada, buscador y filtro por categoría
- Vista de detalle/edición con campos completos de exam_tipos (CUPS, protocolo,
  tipo muestra, nivel, abreviatura, seremite, ayuno, instrucciones)
- Sub-tabla inline de items de resultado (lab_items_resultado) con edición por fila
- Sub-tabla de precios por tarifa (lab_tarifas) con modal de edición
- APIs: list, get, save, save_item, save_tarifa, get_tarifas
- Script ETL Firebird→MySQL (scripts/etl_examenes.php) para la migración inicial

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 23:06:54 -05:00

304 lines
12 KiB
PHP

<?php
/**
* ETL: Firebird staging → MySQL producción
*
* PASO PREVIO (una sola vez):
* php scripts/etl_examenes.php --load # carga staging y ejecuta ETL
* php scripts/etl_examenes.php # solo ETL (staging ya cargado)
* php scripts/etl_examenes.php --clean # elimina tablas staging
*/
require_once __DIR__ . '/../config/config.php';
$pdo = Database::getInstance()->getConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// MariaDB defaultea utf8mb4 a uca1400_ai_ci; forzamos unicode_ci para que
// los literales de string en queries no colisionen con columnas de producción
$pdo->exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");
$doLoad = in_array('--load', $argv);
$doClean = in_array('--clean', $argv);
// ── helpers ──────────────────────────────────────────────────
function run(PDO $pdo, string $sql, string $label): void {
$t = microtime(true);
$rows = $pdo->exec($sql);
printf(" %-45s %6d filas %.2fs\n", $label, $rows, microtime(true) - $t);
}
function step(string $msg): void {
echo "\n── $msg\n";
}
// ── limpiar staging si se pide ────────────────────────────────
if ($doClean) {
$staging = ['SECCION','PROTOCOLO','MUESTRA','EXAMEN','ITEM',
'PERFIL','PERFIL_EXA','TARIFAID','TARIFA','LAB_REFER',
'stg_empresa','stg_examen_emp'];
$pdo->exec('SET FOREIGN_KEY_CHECKS=0');
foreach ($staging as $t) {
$pdo->exec("DROP TABLE IF EXISTS `$t`");
echo " DROP $t\n";
}
$pdo->exec('SET FOREIGN_KEY_CHECKS=1');
echo "Staging eliminado.\n";
exit(0);
}
// ── cargar staging desde examenes_completo.sql ────────────────
if ($doLoad) {
$sqlFile = realpath(__DIR__ . '/../database/examenes_completo.sql');
if (!$sqlFile) { echo "ERROR: No se encuentra examenes_completo.sql\n"; exit(1); }
$tmpFile = sys_get_temp_dir() . '/examenes_staging_' . getmypid() . '.sql';
echo "Preparando staging SQL...\n";
$sql = file_get_contents($sqlFile);
$sql = preg_replace('/\blab_examenes_empresa\b/', 'stg_examen_emp', $sql);
$sql = preg_replace('/\blab_empresas\b/', 'stg_empresa', $sql);
$sql = preg_replace('/\bINSERT INTO\b/', 'INSERT IGNORE INTO', $sql);
file_put_contents($tmpFile, $sql);
unset($sql);
$host = DB_HOST; $port = DB_PORT; $user = DB_USER; $pass = DB_PASS; $db = DB_NAME;
echo "Cargando staging vía mysql CLI (puede tardar 2-3 min)...\n";
$cmd = "mysql -h " . escapeshellarg($host)
. " -P " . escapeshellarg($port)
. " -u " . escapeshellarg($user)
. " -p" . escapeshellarg($pass)
. " --default-character-set=utf8mb4"
. " " . escapeshellarg($db)
. " < " . escapeshellarg($tmpFile)
. " 2>&1";
passthru($cmd, $ret);
unlink($tmpFile);
if ($ret !== 0) { echo "ERROR cargando staging.\n"; exit(1); }
echo "Staging cargado.\n";
}
// ── verificar staging ─────────────────────────────────────────
try {
$pdo->query("SELECT 1 FROM EXAMEN LIMIT 1");
} catch (\Throwable $e) {
echo "ERROR: Tablas staging no encontradas. Ejecuta: php etl_examenes.php --load\n";
exit(1);
}
echo "ETL Firebird → MySQL\n";
echo str_repeat('─', 60) . "\n";
// convertir staging a misma collation que producción (MariaDB usa uca1400 por defecto)
$pdo->exec('SET FOREIGN_KEY_CHECKS = 0');
foreach (['SECCION','PROTOCOLO','MUESTRA','EXAMEN','ITEM',
'PERFIL','PERFIL_EXA','TARIFAID','TARIFA','LAB_REFER',
'stg_empresa','stg_examen_emp'] as $_t) {
try {
$pdo->exec("ALTER TABLE `$_t` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
} catch (\Throwable $_e) { /* tabla no existe aún, ok */ }
}
$pdo->exec('SET UNIQUE_CHECKS = 0');
// ── 1. SECCION → lab_secciones ───────────────────────────────
step('1. SECCION → lab_secciones');
run($pdo, "
INSERT IGNORE INTO lab_secciones (codigo, nombre)
SELECT CODIGO, NOMBRE FROM SECCION
", 'lab_secciones');
// ── 2. PROTOCOLO → lab_protocolos ────────────────────────────
step('2. PROTOCOLO → lab_protocolos');
run($pdo, "
INSERT IGNORE INTO lab_protocolos (codigo, nombre, cod_seccion)
SELECT CODIGO, NOMBRE, COD_SECCION FROM PROTOCOLO
", 'lab_protocolos');
// ── 3. MUESTRA → lab_tipos_muestra ───────────────────────────
step('3. MUESTRA → lab_tipos_muestra');
run($pdo, "
INSERT IGNORE INTO lab_tipos_muestra (codigo, nombre)
SELECT CODIGO, NOMBRE FROM MUESTRA
", 'lab_tipos_muestra');
// ── 4. EXAMEN → exam_tipos ───────────────────────────────────
step('4. EXAMEN → exam_tipos');
// codigo = EXAMEN.CODIGO (unique en Firebird, usamos como código interno también)
// categoria = nombre de la sección del protocolo
run($pdo, "
INSERT IGNORE INTO exam_tipos
(codigo, nombre, categoria, activo,
codigo_legacy, cups, cod_protocolo, tipo_muestra,
nivel, abreviatura, seremite, serecibe)
SELECT
e.CODIGO,
e.NOMBRE,
s.NOMBRE,
1,
e.CODIGO,
NULLIF(e.CUPS, ''),
NULLIF(e.COD_PROTOCOLO, ''),
NULLIF(e.TIPOMUESTRA, ''),
e.NIVEL,
NULLIF(e.ABREVIATURA, ''),
IF(e.SEREMITE = 'T', 1, 0),
NULLIF(e.SERECIBE, '')
FROM EXAMEN e
LEFT JOIN PROTOCOLO p ON p.CODIGO = e.COD_PROTOCOLO
LEFT JOIN SECCION s ON s.CODIGO = p.COD_SECCION
", 'exam_tipos');
// ── 5. ITEM → lab_items_resultado ────────────────────────────
step('5. ITEM → lab_items_resultado');
// ponytail: truncate antes de insertar — ITEM no tiene UK en MySQL
$pdo->exec('DELETE FROM lab_items_resultado');
run($pdo, "
INSERT INTO lab_items_resultado
(cod_protocolo, nombre, tipo_sexo, tipo,
medida, abreviatura, vmin_ref, vmax_ref,
orden, formula, cups_detalle)
SELECT
i.COD_PROTOCOLO,
i.NOM_ITEM,
CASE i.TIPO_SEXO WHEN 'M' THEN 'M' WHEN 'F' THEN 'F' ELSE NULL END,
CASE WHEN i.TIPO = '2' THEN 'N' ELSE 'T' END,
NULLIF(i.MEDIDA, ''),
NULLIF(i.ABREVIATURA, ''),
NULLIF(i.VMINREF, 0),
NULLIF(i.VMAXREF, 0),
COALESCE(i.ORDEN, 0),
NULLIF(i.FORMULA, ''),
NULLIF(i.CUPS_DETALLE, '')
FROM ITEM i
WHERE EXISTS (SELECT 1 FROM lab_protocolos p WHERE p.codigo = i.COD_PROTOCOLO)
", 'lab_items_resultado');
// ── 6. PERFIL → lab_perfiles ─────────────────────────────────
step('6. PERFIL → lab_perfiles');
run($pdo, "
INSERT IGNORE INTO lab_perfiles (id, nombre, activo)
SELECT COD_PERFIL, NOM_PERFIL, 1 FROM PERFIL
", 'lab_perfiles');
// ── 7. PERFIL_EXA → lab_perfil_examenes ──────────────────────
step('7. PERFIL_EXA → lab_perfil_examenes');
run($pdo, "
INSERT IGNORE INTO lab_perfil_examenes (perfil_id, exam_tipo_id)
SELECT pe.COD_PERFIL, et.id
FROM PERFIL_EXA pe
JOIN exam_tipos et ON et.codigo_legacy = pe.COD_EXA
", 'lab_perfil_examenes');
// ── 8. TARIFAID → lab_tarifas_id ─────────────────────────────
step('8. TARIFAID → lab_tarifas_id');
run($pdo, "
INSERT IGNORE INTO lab_tarifas_id (id, nombre, tarifa_origen, porcentaje)
SELECT
COD_TARIFA,
NOMBRE,
NULLIF(TARIFAORIGEN, 0),
PORCENTAJE
FROM TARIFAID
", 'lab_tarifas_id');
// ── 9. TARIFA → lab_tarifas (104 788 filas) ──────────────────
step('9. TARIFA → lab_tarifas [puede tardar ~30s]');
run($pdo, "
INSERT IGNORE INTO lab_tarifas
(cod_examen_legacy, exam_tipo_id, tarifa_id,
valor, recargo_urg, recargo_fes, recargo_esp)
SELECT
t.COD_EXAMEN,
et.id,
t.TARIFA,
COALESCE(t.VALOR, 0),
COALESCE(t.RECARGO_URG, 0),
COALESCE(t.RECARGO_FES, 0),
COALESCE(t.RECARGO_ESP, 0)
FROM TARIFA t
LEFT JOIN exam_tipos et ON et.codigo_legacy = t.COD_EXAMEN
", 'lab_tarifas');
// ── 10. LAB_REFER → lab_laboratorios_externos ────────────────
step('10. LAB_REFER → lab_laboratorios_externos');
$pdo->exec("
CREATE TABLE IF NOT EXISTS lab_laboratorios_externos (
codigo VARCHAR(20) NOT NULL,
nombre VARCHAR(100),
nit_lab VARCHAR(30),
direccion VARCHAR(200),
telefonos VARCHAR(50),
email VARCHAR(100),
activo TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (codigo)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Laboratorios externos de referencia — replica LAB_REFER Firebird'
");
run($pdo, "
INSERT IGNORE INTO lab_laboratorios_externos
(codigo, nombre, nit_lab, direccion, telefonos, email, activo)
SELECT
CODIGO, NOMBRE, NIT_LAB, DIRECCION, TELEFONOS, EMAIL,
IF(ACTIVADO = 'T', 1, 0)
FROM LAB_REFER
", 'lab_laboratorios_externos');
// ── 11. EMPRESA (staging) → lab_empresas ─────────────────────
step('11. EMPRESA → lab_empresas');
// ponytail: staging usa columnas Firebird, producción tiene columnas en snake_case
run($pdo, "
INSERT IGNORE INTO lab_empresas
(nit, nombre, razon_social, tarifa_id, descuento_pct,
codigo_eps, tipo_usuario, tipo_usuario_sispro,
cod_contrato, cod_tercero, centro_costo,
req_autoriza, activa)
SELECT
NIT,
NOMBRE,
NULLIF(RAZONSOCIAL, ''),
NULLIF(TARIFA, 0),
COALESCE(DSCTO, 0),
NULLIF(CODIGOEPS, ''),
NULLIF(TIPOUSUARIO, ''),
NULLIF(TIPOUSUARIOSISPRO, ''),
NULLIF(CODCONTRATO, ''),
NULLIF(CODTERCERO, ''),
NULLIF(CENTROCOSTO, ''),
IF(REQAUTORIZA = 'T', 1, 0),
IF(ACTIVADA = 'T', 1, 0)
FROM stg_empresa
", 'lab_empresas');
// ── 12. EXAMEN_EMP (staging) → lab_examenes_empresa ──────────
step('12. EXAMEN_EMP → lab_examenes_empresa');
run($pdo, "
INSERT IGNORE INTO lab_examenes_empresa
(nit_empresa, cod_examen_legacy, exam_tipo_id, codigo_empresa)
SELECT
ee.NIT_EMP,
ee.COD_EXA,
et.id,
NULLIF(ee.EXA_EMP, '')
FROM stg_examen_emp ee
LEFT JOIN exam_tipos et ON et.codigo_legacy = ee.COD_EXA
", 'lab_examenes_empresa');
// ── fin ───────────────────────────────────────────────────────
$pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
$pdo->exec('SET UNIQUE_CHECKS = 1');
echo "\n" . str_repeat('─', 60) . "\n";
echo "ETL completado.\n\n";
// resumen rápido
$tablas = [
'lab_secciones', 'lab_protocolos', 'lab_tipos_muestra', 'exam_tipos',
'lab_items_resultado', 'lab_perfiles', 'lab_perfil_examenes',
'lab_tarifas_id', 'lab_tarifas', 'lab_laboratorios_externos',
'lab_empresas', 'lab_examenes_empresa',
];
echo "Totales en producción:\n";
foreach ($tablas as $t) {
$n = $pdo->query("SELECT COUNT(*) FROM `$t`")->fetchColumn();
printf(" %-35s %7d\n", $t, $n);
}
echo "\nListo.\n";