- Migration: columna 'origen' (manual|rips|whatsapp) en lab_pacientes - ingest_paciente.php: guarda origen='rips' en cada paciente importado - Paciente::listar(): filtro por origen + orden por created_at DESC - Paciente::stats(): conteo total/importados/con_wa/manuales - get_pacientes.php: expone ?origen= y ?stats=1 - lab_pacientes.php: cards de stats clicables, badge origen por fila, columna ciudad, columna fecha registro, dropdown filtro por origen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
183 lines
6.3 KiB
PHP
183 lines
6.3 KiB
PHP
<?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');
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type, X-Lab-Key, X-Lab-Sync-Key');
|
|
header('Access-Control-Max-Age: 86400');
|
|
|
|
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']);
|
|
}
|
|
$origenValidos = ['manual', 'rips', 'whatsapp'];
|
|
$campos['origen'] = in_array($data['origen'] ?? '', $origenValidos, true)
|
|
? $data['origen'] : 'rips';
|
|
|
|
// ── Modo: "insertar" (solo nuevos) | "upsert" (crea o actualiza) ─────────────
|
|
$modo = trim($data['modo'] ?? 'upsert');
|
|
if (!in_array($modo, ['insertar', 'upsert'], true)) {
|
|
$modo = 'upsert';
|
|
}
|
|
|
|
// ── UPSERT / INSERT-ONLY ─────────────────────────────────────────────────────
|
|
try {
|
|
$pac = new Paciente();
|
|
|
|
$existente = null;
|
|
if (!empty($campos['numero_documento'])) {
|
|
$existente = $pac->porDocumento($campos['numero_documento']);
|
|
}
|
|
|
|
ob_clean();
|
|
|
|
if ($existente) {
|
|
if ($modo === 'insertar') {
|
|
echo json_encode([
|
|
'ok' => true,
|
|
'action' => 'skipped',
|
|
'id' => $existente['id'],
|
|
'message' => 'Paciente ya existe',
|
|
]);
|
|
} else {
|
|
$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()]);
|
|
}
|