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";