feat(lab): endpoint server-to-server ingest_paciente para sync desde RIPS
Agrega /api/lab/ingest_paciente.php con autenticación por X-Lab-Key (LAB_SYNC_KEY en config). Hace UPSERT por numero_documento sin requerir sesión de admin — permite que RIPS Manager sincronice pacientes directo a lab_pacientes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
1f45f58205
commit
9651f14302
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/ingest_paciente.php
|
||||
* Endpoint server-to-server para ingesta de pacientes desde RIPS Manager.
|
||||
* Autenticación: header X-Lab-Key: <LAB_SYNC_KEY> (sin sesión de usuario).
|
||||
*
|
||||
* Body JSON (todos opcionales excepto nombre_completo en creación):
|
||||
* { numero_documento, tipo_documento, nombre_completo, telefono, email,
|
||||
* fecha_nacimiento, genero, direccion, ciudad, eps, origen }
|
||||
*
|
||||
* Respuesta:
|
||||
* { ok, action: "created"|"updated"|"skipped", id, message }
|
||||
*/
|
||||
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Paciente.php';
|
||||
|
||||
error_reporting(E_ERROR | E_PARSE);
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('html_errors', '0');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
$err = error_get_last();
|
||||
if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
|
||||
ob_clean();
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['ok' => false, 'error' => 'Error interno: ' . $err['message']]);
|
||||
}
|
||||
});
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['ok' => false, 'error' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Autenticación por API key ────────────────────────────────────────────────
|
||||
$keyHeader = $_SERVER['HTTP_X_LAB_KEY']
|
||||
?? $_SERVER['HTTP_X_LAB_SYNC_KEY']
|
||||
?? '';
|
||||
|
||||
if (!$keyHeader || !hash_equals(LAB_SYNC_KEY, $keyHeader)) {
|
||||
http_response_code(401);
|
||||
ob_clean();
|
||||
echo json_encode(['ok' => false, 'error' => 'API key inválida']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Leer body ────────────────────────────────────────────────────────────────
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = json_decode($raw, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
http_response_code(400);
|
||||
ob_clean();
|
||||
echo json_encode(['ok' => false, 'error' => 'Body JSON inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Normalizar campos ────────────────────────────────────────────────────────
|
||||
$campos = [];
|
||||
|
||||
if (!empty($data['nombre_completo'])) {
|
||||
$campos['nombre_completo'] = mb_strtoupper(trim($data['nombre_completo']));
|
||||
}
|
||||
if (!empty($data['numero_documento'])) {
|
||||
$campos['numero_documento'] = trim($data['numero_documento']);
|
||||
}
|
||||
if (!empty($data['tipo_documento'])) {
|
||||
$tiposValidos = ['CC', 'CE', 'TI', 'PA', 'NIT', 'RC', 'MS'];
|
||||
$t = strtoupper(trim($data['tipo_documento']));
|
||||
$campos['tipo_documento'] = in_array($t, $tiposValidos) ? $t : 'CC';
|
||||
}
|
||||
if (!empty($data['telefono'])) {
|
||||
$tel = preg_replace('/[^0-9+]/', '', $data['telefono']);
|
||||
if (strlen($tel) >= 6) {
|
||||
$campos['telefono'] = $tel;
|
||||
}
|
||||
}
|
||||
if (!empty($data['email']) && filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$e = strtolower(trim($data['email']));
|
||||
// Ignorar emails de relleno generados por RIPS
|
||||
if (!str_contains($e, '@sinregistro.co') && !str_contains($e, 'sinregistro')) {
|
||||
$campos['email'] = $e;
|
||||
}
|
||||
}
|
||||
if (!empty($data['fecha_nacimiento'])) {
|
||||
$fn = trim($data['fecha_nacimiento']);
|
||||
// Acepta dd/mm/yyyy o yyyy-mm-dd
|
||||
if (preg_match('/^(\d{2})\/(\d{2})\/(\d{4})$/', $fn, $m)) {
|
||||
$campos['fecha_nacimiento'] = "{$m[3]}-{$m[2]}-{$m[1]}";
|
||||
} elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $fn)) {
|
||||
$campos['fecha_nacimiento'] = $fn;
|
||||
}
|
||||
}
|
||||
if (!empty($data['genero'])) {
|
||||
$g = strtoupper(trim($data['genero']));
|
||||
if (in_array($g, ['M', 'F', 'O'])) {
|
||||
$campos['genero'] = $g;
|
||||
}
|
||||
}
|
||||
if (!empty($data['direccion'])) {
|
||||
$campos['direccion'] = trim($data['direccion']);
|
||||
}
|
||||
if (!empty($data['ciudad'])) {
|
||||
$campos['ciudad'] = trim($data['ciudad']);
|
||||
}
|
||||
if (!empty($data['eps'])) {
|
||||
$campos['eps'] = trim($data['eps']);
|
||||
}
|
||||
|
||||
// ── UPSERT ───────────────────────────────────────────────────────────────────
|
||||
try {
|
||||
$pac = new Paciente();
|
||||
|
||||
$existente = null;
|
||||
if (!empty($campos['numero_documento'])) {
|
||||
$existente = $pac->porDocumento($campos['numero_documento']);
|
||||
}
|
||||
|
||||
ob_clean();
|
||||
|
||||
if ($existente) {
|
||||
$pac->actualizar($existente['id'], $campos, null);
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'action' => 'updated',
|
||||
'id' => $existente['id'],
|
||||
'message' => 'Paciente actualizado',
|
||||
]);
|
||||
} elseif (!empty($campos['nombre_completo'])) {
|
||||
$id = $pac->crear($campos, null);
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'action' => 'created',
|
||||
'id' => $id,
|
||||
'message' => 'Paciente creado',
|
||||
]);
|
||||
} else {
|
||||
http_response_code(422);
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'action' => 'skipped',
|
||||
'error' => 'Sin número de documento ni nombre: registro omitido',
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
ob_clean();
|
||||
http_response_code(500);
|
||||
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -402,6 +402,10 @@ if (!defined('BASE_URL')) {
|
||||
if (!defined('MIGRATION_TOKEN')) {
|
||||
define('MIGRATION_TOKEN', 'lab2026migrate');
|
||||
}
|
||||
// API key server-to-server para ingesta de pacientes desde RIPS Manager
|
||||
if (!defined('LAB_SYNC_KEY')) {
|
||||
define('LAB_SYNC_KEY', getenv('LAB_SYNC_KEY') ?: 'rips-lab-sync-2026');
|
||||
}
|
||||
define('TIMEZONE', 'America/Bogota');
|
||||
|
||||
// Información del desarrollador
|
||||
|
||||
Reference in New Issue
Block a user