- 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>
304 lines
12 KiB
PHP
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";
|