up
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* Helpers compartidos para los endpoints del módulo de laboratorio.
|
||||
* Incluido automáticamente por cada endpoint de /api/lab/.
|
||||
*/
|
||||
|
||||
// Capturar cualquier salida accidental (warnings, notices) antes del JSON
|
||||
ob_start();
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../classes/lab/ActividadAdmin.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Paciente.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Enfermera.php';
|
||||
require_once __DIR__ . '/../../classes/lab/OrdenMedica.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Domicilio.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Asignacion.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Formulario.php';
|
||||
|
||||
// Los endpoints API nunca deben mostrar errores PHP en HTML — enviar como JSON
|
||||
error_reporting(E_ERROR | E_PARSE);
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('html_errors', '0');
|
||||
|
||||
// Interceptar errores fatales y devolverlos como JSON en lugar de HTML
|
||||
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 del servidor: ' . $err['message'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
// Cabeceras JSON estándar
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
|
||||
requireAuthentication();
|
||||
|
||||
/**
|
||||
* ID del admin autenticado actualmente.
|
||||
*/
|
||||
function adminId(): ?int {
|
||||
return isset($_SESSION['admin_user']['id'])
|
||||
? (int)$_SESSION['admin_user']['id']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rol del usuario autenticado ('admin' | 'enfermero').
|
||||
*/
|
||||
function userRole(): string {
|
||||
return $_SESSION['admin_user']['role'] ?? 'admin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Detiene la ejecución con 403 JSON si el usuario no es admin (bloquea enfermeros).
|
||||
*/
|
||||
function requireAdmin(): void {
|
||||
if (userRole() !== 'admin') {
|
||||
jsonError('Acceso restringido a administradores', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detiene la ejecución si no se puede actuar como enfermero.
|
||||
* Acepta tanto admins (que pueden actuar en nombre de cualquier enfermera)
|
||||
* como el propio enfermero autenticado.
|
||||
*/
|
||||
function requireEnfermeroAccess(int $enfermeraIdSolicitado): void {
|
||||
if (userRole() === 'admin') return;
|
||||
$propio = enfermeraId();
|
||||
if (!$propio || $propio !== $enfermeraIdSolicitado) {
|
||||
jsonError('Solo puedes gestionar tus propios domicilios', 403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lee el body JSON del request.
|
||||
*/
|
||||
function inputJson(): array {
|
||||
static $parsed = null;
|
||||
if ($parsed === null) {
|
||||
$raw = file_get_contents('php://input');
|
||||
$parsed = json_decode($raw, true) ?? [];
|
||||
}
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve una respuesta de éxito.
|
||||
*/
|
||||
function jsonOk(array $payload = [], string $mensaje = ''): void {
|
||||
ob_clean();
|
||||
$resp = ['ok' => true, 'success' => true];
|
||||
if ($mensaje) {
|
||||
$resp['message'] = $mensaje;
|
||||
}
|
||||
echo json_encode(array_merge($resp, $payload), JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Devuelve un error JSON con código HTTP.
|
||||
*/
|
||||
function jsonError(string $mensaje, int $code = 400): void {
|
||||
ob_clean();
|
||||
http_response_code($code);
|
||||
echo json_encode(['ok' => false, 'success' => false, 'error' => $mensaje], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida que el método HTTP sea el esperado.
|
||||
*/
|
||||
function requireMethod(string $method): void {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== strtoupper($method)) {
|
||||
jsonError('Método no permitido', 405);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/autorizar_orden.php
|
||||
* Cambia el estado de una orden médica (flujo de revisión).
|
||||
* Body JSON:
|
||||
* { id, accion: 'en_revision'|'autorizada'|'rechazada'|'completada', comentario? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$om = new OrdenMedica();
|
||||
$admin = adminId();
|
||||
|
||||
if (empty($datos['id'])) {
|
||||
jsonError('El campo id es obligatorio');
|
||||
}
|
||||
if (empty($datos['accion'])) {
|
||||
jsonError('El campo accion es obligatorio');
|
||||
}
|
||||
|
||||
$id = (int)$datos['id'];
|
||||
$accion = $datos['accion'];
|
||||
$comentario = $datos['comentario'] ?? '';
|
||||
|
||||
if ($accion === 'rechazada' && empty($comentario)) {
|
||||
jsonError('El motivo de rechazo es obligatorio');
|
||||
}
|
||||
|
||||
$om->cambiarEstado($id, $accion, $admin, $comentario);
|
||||
|
||||
$mensajes = [
|
||||
'en_revision' => 'Orden marcada en revisión',
|
||||
'autorizada' => 'Orden autorizada correctamente',
|
||||
'rechazada' => 'Orden rechazada',
|
||||
'en_domicilio'=> 'Orden marcada como en domicilio',
|
||||
'completada' => 'Orden completada',
|
||||
];
|
||||
|
||||
jsonOk(['id' => $id], $mensajes[$accion] ?? 'Estado actualizado');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
jsonError($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* API: Crear orden médica desde conversación de WhatsApp
|
||||
*
|
||||
* GET ?solo_paciente=1&conversation_id=X → obtiene/crea paciente del contacto
|
||||
* POST { paciente_id, conversation_id, local_file, media_message_id, ... }
|
||||
* → crea lab_ordenes_medicas
|
||||
*/
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../classes/Database.php';
|
||||
require_once __DIR__ . '/../../classes/lab/ActividadAdmin.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Paciente.php';
|
||||
require_once __DIR__ . '/../../classes/lab/OrdenMedica.php';
|
||||
|
||||
// Helpers inline (similar a _helpers.php pero sin incluir todas las clases de nuevo)
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
requireAuthentication();
|
||||
|
||||
$adminId = (int)($_SESSION['admin_user']['id'] ?? 0);
|
||||
$db = Database::getInstance();
|
||||
|
||||
// ── GET: solo_paciente ─────────────────────────────────────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['solo_paciente'])) {
|
||||
$convId = (int)($_GET['conversation_id'] ?? 0);
|
||||
$userId = (int)($_GET['user_id'] ?? 0);
|
||||
|
||||
if (!$convId && !$userId) {
|
||||
echo json_encode(['success' => false, 'error' => 'conversation_id o user_id requerido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Obtener user_id y phone_number: por conversation_id o directamente por user_id
|
||||
if ($convId) {
|
||||
$conv = $db->fetch(
|
||||
'SELECT c.id, c.user_id, u.phone_number, u.name AS whatsapp_name
|
||||
FROM conversations c
|
||||
JOIN users u ON u.id = c.user_id
|
||||
WHERE c.id = ?',
|
||||
[$convId]
|
||||
);
|
||||
} else {
|
||||
$conv = $db->fetch(
|
||||
'SELECT u.id AS user_id, u.phone_number, u.name AS whatsapp_name
|
||||
FROM users u
|
||||
WHERE u.id = ?',
|
||||
[$userId]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$conv) {
|
||||
echo json_encode(['success' => false, 'error' => 'Conversación / usuario no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pacienteRepo = new Paciente();
|
||||
$pacienteId = $pacienteRepo->obtenerOCrearDesdeWhatsapp($conv['user_id']);
|
||||
$paciente = $pacienteRepo->obtener($pacienteId);
|
||||
echo json_encode(['success' => true, 'paciente' => $paciente]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── POST: crear orden ──────────────────────────────────────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Método no permitido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
if (!$input) {
|
||||
echo json_encode(['success' => false, 'error' => 'Datos inválidos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pacienteId = (int)($input['paciente_id'] ?? 0);
|
||||
if (!$pacienteId) {
|
||||
echo json_encode(['success' => false, 'error' => 'paciente_id requerido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Construir datos de la orden
|
||||
$datos = [
|
||||
'paciente_id' => $pacienteId,
|
||||
'conversation_id' => !empty($input['conversation_id']) ? (int)$input['conversation_id'] : null,
|
||||
'local_file' => $input['local_file'] ?? null,
|
||||
'media_message_id' => $input['media_message_id'] ?? null,
|
||||
'medico_nombre' => $input['medico_nombre'] ?? null,
|
||||
'fecha_orden' => !empty($input['fecha_orden']) ? $input['fecha_orden'] : null,
|
||||
'examenes_solicitados'=> $input['examenes_solicitados']?? null,
|
||||
'requiere_ayuno' => isset($input['requiere_ayuno']) ? (int)$input['requiere_ayuno'] : 0,
|
||||
'horas_ayuno' => !empty($input['horas_ayuno']) ? (int)$input['horas_ayuno'] : null,
|
||||
'notas_admin' => $input['notas_admin'] ?? null,
|
||||
'estado' => 'pendiente',
|
||||
];
|
||||
|
||||
try {
|
||||
$orden = new OrdenMedica();
|
||||
$ordenId = $orden->crear($datos, $adminId);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'orden_id' => $ordenId,
|
||||
'message' => "Orden médica #$ordenId creada correctamente",
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log('[lab/crear_desde_whatsapp] ' . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'error' => 'Error interno: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/create_enfermero_user.php
|
||||
* Crea o actualiza el usuario de acceso al sistema para una enfermera.
|
||||
* Solo administradores pueden llamar este endpoint.
|
||||
*
|
||||
* Body JSON:
|
||||
* { enfermera_id, username, password?, full_name? }
|
||||
* Si el usuario ya existe para esa enfermera_id, actualiza username/password.
|
||||
* Si password viene vacío en actualización, no cambia la contraseña.
|
||||
*
|
||||
* Returns:
|
||||
* { ok:true, message, user_id }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireAdmin();
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$enfermeraId = (int)($datos['enfermera_id'] ?? 0);
|
||||
$username = trim($datos['username'] ?? '');
|
||||
$password = $datos['password'] ?? '';
|
||||
$fullName = trim($datos['full_name'] ?? '');
|
||||
|
||||
if (!$enfermeraId) jsonError('enfermera_id es obligatorio');
|
||||
if (!$username) jsonError('username es obligatorio');
|
||||
if (strlen($username) < 3) jsonError('El usuario debe tener al menos 3 caracteres');
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Verificar que la enfermera existe
|
||||
$enf = $db->fetch('SELECT id, nombre_completo FROM lab_enfermeras WHERE id = ?', [$enfermeraId]);
|
||||
if (!$enf) jsonError('Enfermera no encontrada', 404);
|
||||
|
||||
if (empty($fullName)) $fullName = $enf['nombre_completo'];
|
||||
|
||||
// ¿Ya existe un usuario vinculado a esta enfermera?
|
||||
$existente = $db->fetch(
|
||||
'SELECT id, username FROM admin_users WHERE enfermera_id = ? LIMIT 1',
|
||||
[$enfermeraId]
|
||||
);
|
||||
|
||||
if ($existente) {
|
||||
// --- ACTUALIZAR ---
|
||||
// Verificar que el nuevo username no esté en uso por OTRO usuario
|
||||
$conflicto = $db->fetch(
|
||||
'SELECT id FROM admin_users WHERE username = ? AND id != ?',
|
||||
[$username, $existente['id']]
|
||||
);
|
||||
if ($conflicto) jsonError("El usuario '$username' ya está en uso por otra cuenta");
|
||||
|
||||
$sets = ['username = ?', 'full_name = ?', 'is_active = 1', 'updated_at = NOW()'];
|
||||
$vals = [$username, $fullName];
|
||||
|
||||
if (!empty($password)) {
|
||||
if (strlen($password) < 6) jsonError('La contraseña debe tener al menos 6 caracteres');
|
||||
$sets[] = 'password_hash = ?';
|
||||
$vals[] = password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
$vals[] = $existente['id'];
|
||||
$db->execute(
|
||||
'UPDATE admin_users SET ' . implode(', ', $sets) . ' WHERE id = ?',
|
||||
$vals
|
||||
);
|
||||
|
||||
jsonOk(['user_id' => $existente['id']], 'Acceso actualizado correctamente');
|
||||
|
||||
} else {
|
||||
// --- CREAR ---
|
||||
if (empty($password)) jsonError('La contraseña es obligatoria para crear el acceso');
|
||||
if (strlen($password) < 6) jsonError('La contraseña debe tener al menos 6 caracteres');
|
||||
|
||||
// Verificar username único
|
||||
$conflicto = $db->fetch('SELECT id FROM admin_users WHERE username = ?', [$username]);
|
||||
if ($conflicto) jsonError("El usuario '$username' ya está en uso");
|
||||
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$db->execute(
|
||||
"INSERT INTO admin_users (username, password_hash, full_name, role, enfermera_id, is_active, created_at)
|
||||
VALUES (?, ?, ?, 'enfermero', ?, 1, NOW())",
|
||||
[$username, $hash, $fullName, $enfermeraId]
|
||||
);
|
||||
$userId = $db->lastInsertId();
|
||||
|
||||
jsonOk(['user_id' => $userId], 'Acceso creado correctamente');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/delete_lab_user.php
|
||||
* Elimina un usuario. No permite auto-eliminarse.
|
||||
* Solo admins.
|
||||
*
|
||||
* Body JSON: { id }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin();
|
||||
|
||||
$body = inputJson();
|
||||
$id = (int)($body['id'] ?? 0);
|
||||
if (!$id) jsonError('ID de usuario requerido');
|
||||
|
||||
$selfId = adminId();
|
||||
if ($id === $selfId) jsonError('No puedes eliminar tu propio usuario');
|
||||
|
||||
$db = Database::getInstance();
|
||||
$user = $db->fetch("SELECT id, username FROM admin_users WHERE id = ?", [$id]);
|
||||
if (!$user) jsonError('Usuario no encontrado', 404);
|
||||
|
||||
$db->query("DELETE FROM admin_users WHERE id = ?", [$id]);
|
||||
|
||||
jsonOk([], "Usuario '{$user['username']}' eliminado correctamente");
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/delete_role.php
|
||||
* Elimina un rol no-sistema.
|
||||
* Solo admins.
|
||||
*
|
||||
* Body JSON: { id }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin();
|
||||
|
||||
$body = inputJson();
|
||||
$id = (int)($body['id'] ?? 0);
|
||||
if (!$id) jsonError('ID de rol requerido');
|
||||
|
||||
$db = Database::getInstance();
|
||||
$role = $db->fetch("SELECT id, is_system, slug FROM roles WHERE id = ?", [$id]);
|
||||
if (!$role) jsonError('Rol no encontrado', 404);
|
||||
if ($role['is_system']) jsonError('Los roles del sistema no se pueden eliminar');
|
||||
|
||||
// Reasignar usuarios que tengan este rol al rol admin (id=1) antes de borrar
|
||||
$db->query("UPDATE admin_users SET role_id = 1, role = 'admin' WHERE role_id = ?", [$id]);
|
||||
|
||||
// La FK ON DELETE CASCADE borra role_modules automáticamente
|
||||
$db->query("DELETE FROM roles WHERE id = ?", [$id]);
|
||||
|
||||
jsonOk([], 'Rol eliminado. Los usuarios afectados fueron reasignados al rol Administrador.');
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_actividad.php — Log de trazabilidad del módulo */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$log = new ActividadAdmin();
|
||||
|
||||
$modulo = $_GET['modulo'] ?? '';
|
||||
$adminFilt = (int)($_GET['admin_id'] ?? 0);
|
||||
$entidad = (int)($_GET['entidad_id'] ?? 0);
|
||||
$limit = max(1, min(200, (int)($_GET['limit'] ?? 50)));
|
||||
|
||||
if ($adminFilt) {
|
||||
$data = $log->porAdmin($adminFilt, $limit);
|
||||
} elseif ($entidad && $modulo) {
|
||||
$data = $log->porEntidad($modulo, $entidad);
|
||||
} else {
|
||||
$data = $log->reciente($limit, $modulo);
|
||||
}
|
||||
|
||||
jsonOk(['data' => $data, 'total' => count($data)]);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_asignaciones.php — Asignaciones del día / por enfermera */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$asig = new Asignacion();
|
||||
|
||||
$fecha = $_GET['fecha'] ?? date('Y-m-d');
|
||||
$data = $asig->porFecha($fecha);
|
||||
$carga = $asig->cargaHoy();
|
||||
|
||||
jsonOk([
|
||||
'data' => $data,
|
||||
'total' => count($data),
|
||||
'fecha' => $fecha,
|
||||
'carga_hoy' => $carga,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/get_config.php — config global del lab
|
||||
* GET /api/lab/get_config.php?forma=1 — config como objeto clave=>valor (para el builder)
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
$db = Database::getInstance();
|
||||
$rows = $db->fetchAll('SELECT clave, valor FROM lab_config ORDER BY clave');
|
||||
|
||||
$cfg = [];
|
||||
foreach ($rows as $r) {
|
||||
$cfg[$r['clave']] = $r['valor'];
|
||||
}
|
||||
|
||||
jsonOk(['config' => $cfg]);
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_domicilios.php — Lista de domicilios con filtros */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$dom = new Domicilio();
|
||||
|
||||
// Vista de un solo domicilio
|
||||
if (!empty($_GET['id'])) {
|
||||
$d = $dom->obtener((int)$_GET['id']);
|
||||
if (!$d) {
|
||||
jsonError('Domicilio no encontrado', 404);
|
||||
}
|
||||
jsonOk(['domicilio' => $d]);
|
||||
}
|
||||
|
||||
$filtros = array_filter([
|
||||
'fecha' => $_GET['fecha'] ?? '',
|
||||
'desde' => $_GET['desde'] ?? '',
|
||||
'hasta' => $_GET['hasta'] ?? '',
|
||||
'estado' => $_GET['estado'] ?? '',
|
||||
'enfermera_id' => (int)($_GET['enfermera_id'] ?? 0) ?: null,
|
||||
'paciente_id' => (int)($_GET['paciente_id'] ?? 0) ?: null,
|
||||
], fn($v) => $v !== null && $v !== '');
|
||||
|
||||
$pagina = max(1, (int)($_GET['page'] ?? 1));
|
||||
$por = max(1, min(200, (int)($_GET['limit'] ?? 30)));
|
||||
|
||||
$resultado = $dom->listar($filtros, $pagina, $por);
|
||||
|
||||
// Incluir resumen del día
|
||||
if (empty($_GET['no_stats'])) {
|
||||
$resultado['estadisticas_hoy'] = $dom->estadisticasHoy();
|
||||
$resultado['sin_asignar_hoy'] = count($dom->sinAsignar());
|
||||
}
|
||||
|
||||
jsonOk($resultado);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_enfermeras.php — Lista de enfermeras */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$enf = new Enfermera();
|
||||
|
||||
// Perfil + agenda de una sola enfermera
|
||||
if (!empty($_GET['id'])) {
|
||||
$id = (int)$_GET['id'];
|
||||
$e = $enf->obtener($id);
|
||||
if (!$e) {
|
||||
jsonError('Enfermera no encontrada', 404);
|
||||
}
|
||||
$fecha = $_GET['fecha'] ?? date('Y-m-d');
|
||||
$e['agenda'] = $enf->agenda($id, $fecha);
|
||||
|
||||
// Incluir info de acceso al sistema (si tiene usuario creado)
|
||||
$db = Database::getInstance();
|
||||
$u = $db->fetch(
|
||||
'SELECT id, username FROM admin_users WHERE enfermera_id = ? AND role = "enfermero" LIMIT 1',
|
||||
[$id]
|
||||
);
|
||||
$e['usuario_acceso'] = $u ?: null;
|
||||
|
||||
jsonOk(['enfermera' => $e]);
|
||||
}
|
||||
|
||||
$soloActivas = !isset($_GET['todas']) || $_GET['todas'] !== '1';
|
||||
$lista = $enf->listar($soloActivas);
|
||||
|
||||
// Enriquecer con username de acceso para cada enfermera
|
||||
$db = Database::getInstance();
|
||||
foreach ($lista as &$item) {
|
||||
$u = $db->fetch(
|
||||
'SELECT username FROM admin_users WHERE enfermera_id = ? AND is_active = 1 LIMIT 1',
|
||||
[$item['id']]
|
||||
);
|
||||
$item['username_acceso'] = $u['username'] ?? null;
|
||||
}
|
||||
unset($item);
|
||||
|
||||
jsonOk(['data' => $lista, 'total' => count($lista)]);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/get_formularios.php — Lista de plantillas
|
||||
* GET /api/lab/get_formularios.php?id=X — Plantilla específica
|
||||
* GET /api/lab/get_formularios.php?envios=1&formulario_id=X — Envíos
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$form = new Formulario();
|
||||
|
||||
// Lista de envíos
|
||||
if (!empty($_GET['envios'])) {
|
||||
$filtros = [];
|
||||
if (!empty($_GET['formulario_id'])) $filtros['formulario_id'] = (int)$_GET['formulario_id'];
|
||||
if (!empty($_GET['paciente_id'])) $filtros['paciente_id'] = (int)$_GET['paciente_id'];
|
||||
if (!empty($_GET['estado'])) $filtros['estado'] = $_GET['estado'];
|
||||
// Enfermero solo ve los suyos
|
||||
if (userRole() === 'enfermero') {
|
||||
$filtros['enviado_por'] = adminId();
|
||||
}
|
||||
jsonOk(['data' => $form->listarEnvios($filtros)]);
|
||||
}
|
||||
|
||||
// Plantilla específica
|
||||
if (!empty($_GET['id'])) {
|
||||
$f = $form->obtener((int)$_GET['id']);
|
||||
if (!$f) jsonError('Formulario no encontrado', 404);
|
||||
jsonOk(['formulario' => $f]);
|
||||
}
|
||||
|
||||
// Lista de plantillas
|
||||
$todas = isset($_GET['todas']) && userRole() === 'admin';
|
||||
jsonOk(['data' => $form->listar(!$todas)]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/get_lab_users.php
|
||||
* Devuelve todos los usuarios del sistema con información de rol.
|
||||
* Solo admins.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireAdmin();
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$users = $db->fetchAll("
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.full_name,
|
||||
u.email,
|
||||
u.is_active,
|
||||
u.role,
|
||||
u.role_id,
|
||||
u.enfermera_id,
|
||||
u.last_login,
|
||||
u.created_at,
|
||||
r.name AS role_name,
|
||||
r.color AS role_color,
|
||||
r.slug AS role_slug,
|
||||
e.nombre_completo AS enfermera_nombre
|
||||
FROM admin_users u
|
||||
LEFT JOIN roles r ON r.id = u.role_id
|
||||
LEFT JOIN lab_enfermeras e ON e.id = u.enfermera_id
|
||||
ORDER BY u.id ASC
|
||||
");
|
||||
|
||||
foreach ($users as &$u) {
|
||||
$u['is_active'] = (bool)$u['is_active'];
|
||||
$u['role_name'] = $u['role_name'] ?? ucfirst($u['role'] ?? 'admin');
|
||||
$u['role_color'] = $u['role_color'] ?? '#0d6efd';
|
||||
}
|
||||
unset($u);
|
||||
|
||||
jsonOk(['users' => $users]);
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_ordenes.php — Lista paginada de órdenes médicas con filtros */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$om = new OrdenMedica();
|
||||
|
||||
$filtros = array_filter([
|
||||
'estado' => $_GET['estado'] ?? '',
|
||||
'paciente_id' => (int)($_GET['paciente_id'] ?? 0) ?: null,
|
||||
'desde' => $_GET['desde'] ?? '',
|
||||
'hasta' => $_GET['hasta'] ?? '',
|
||||
'busqueda' => trim($_GET['busqueda'] ?? $_GET['search'] ?? ''),
|
||||
], fn($v) => $v !== null && $v !== '');
|
||||
|
||||
$pagina = max(1, (int)($_GET['page'] ?? 1));
|
||||
$por = max(1, min(100, (int)($_GET['limit'] ?? 30)));
|
||||
|
||||
// Vista de una sola orden
|
||||
if (!empty($_GET['id'])) {
|
||||
$orden = $om->obtener((int)$_GET['id']);
|
||||
if (!$orden) {
|
||||
jsonError('Orden no encontrada', 404);
|
||||
}
|
||||
jsonOk(['orden' => $orden]);
|
||||
}
|
||||
|
||||
$resultado = $om->listar($filtros, $pagina, $por);
|
||||
|
||||
// Incluir también el contador por estado para el dashboard
|
||||
if (!isset($_GET['no_counts'])) {
|
||||
$resultado['contadores'] = $om->contadorPorEstado();
|
||||
}
|
||||
|
||||
jsonOk($resultado);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/** GET /api/lab/get_pacientes.php — Lista paginada de pacientes */
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$pac = new Paciente();
|
||||
|
||||
$busqueda = trim($_GET['busqueda'] ?? $_GET['search'] ?? '');
|
||||
$pagina = max(1, (int)($_GET['page'] ?? $_GET['pagina'] ?? 1));
|
||||
$por = max(1, min(100, (int)($_GET['limit'] ?? $_GET['por_pagina'] ?? 30)));
|
||||
|
||||
jsonOk($pac->listar($busqueda, $pagina, $por));
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/get_roles.php
|
||||
* Devuelve todos los roles con sus módulos asignados.
|
||||
* Solo admins.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireAdmin();
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$roles = $db->fetchAll("SELECT id, name, slug, description, color, is_system, created_at FROM roles ORDER BY id ASC");
|
||||
|
||||
foreach ($roles as &$role) {
|
||||
$mods = $db->fetchAll(
|
||||
"SELECT module_slug FROM role_modules WHERE role_id = ? ORDER BY module_slug",
|
||||
[$role['id']]
|
||||
);
|
||||
$role['modules'] = array_column($mods, 'module_slug');
|
||||
$role['is_system'] = (bool)$role['is_system'];
|
||||
$role['user_count'] = (int)$db->fetch(
|
||||
"SELECT COUNT(*) AS c FROM admin_users WHERE role_id = ?",
|
||||
[$role['id']]
|
||||
)['c'];
|
||||
}
|
||||
unset($role);
|
||||
|
||||
jsonOk(['roles' => $roles]);
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/get_stats.php — Estadísticas del dashboard del módulo
|
||||
* Devuelve contadores, pendientes urgentes, agenda hoy y carga de enfermeras.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
$om = new OrdenMedica();
|
||||
$dom = new Domicilio();
|
||||
$asig = new Asignacion();
|
||||
$pac = new Paciente();
|
||||
$db = Database::getInstance();
|
||||
|
||||
// ── Órdenes ──────────────────────────────────────────────────────────────
|
||||
$estadosOrdenes = $om->contadorPorEstado();
|
||||
$pendientesViejos = $om->pendientesAntiguos(5);
|
||||
|
||||
// ── Domicilios hoy ───────────────────────────────────────────────────────
|
||||
$estDomHoy = $dom->estadisticasHoy();
|
||||
$sinAsignarHoy = $dom->sinAsignar();
|
||||
|
||||
// ── Carga de enfermeras hoy ──────────────────────────────────────────────
|
||||
$cargaEnfermeras = $asig->cargaHoy();
|
||||
|
||||
// ── Totales generales ────────────────────────────────────────────────────
|
||||
$totalPacientes = $db->fetch('SELECT COUNT(*) AS n FROM lab_pacientes WHERE is_active = 1')['n'] ?? 0;
|
||||
$totalEnfermeras = $db->fetch('SELECT COUNT(*) AS n FROM lab_enfermeras WHERE is_active = 1')['n'] ?? 0;
|
||||
|
||||
// ── Órdenes de hoy ───────────────────────────────────────────────────────
|
||||
$ordenesHoy = $db->fetch(
|
||||
'SELECT COUNT(*) AS n FROM lab_ordenes_medicas WHERE DATE(created_at) = CURDATE()'
|
||||
)['n'] ?? 0;
|
||||
|
||||
// ── Actividad reciente ───────────────────────────────────────────────────
|
||||
$actividadReciente = (new ActividadAdmin())->reciente(10);
|
||||
|
||||
jsonOk([
|
||||
'ordenes' => [
|
||||
'por_estado' => $estadosOrdenes,
|
||||
'pendientes_hoy' => (int)($estadosOrdenes['pendiente'] ?? 0),
|
||||
'en_revision_hoy' => (int)($estadosOrdenes['en_revision'] ?? 0),
|
||||
'pendientes_viejos'=> $pendientesViejos,
|
||||
'total_hoy' => (int)$ordenesHoy,
|
||||
],
|
||||
'domicilios' => [
|
||||
'por_estado' => $estDomHoy,
|
||||
'total_hoy' => array_sum($estDomHoy),
|
||||
'sin_asignar' => count($sinAsignarHoy),
|
||||
'lista_sin_asignar' => $sinAsignarHoy,
|
||||
],
|
||||
'enfermeras' => [
|
||||
'activas' => (int)$totalEnfermeras,
|
||||
'carga_hoy' => $cargaEnfermeras,
|
||||
],
|
||||
'generales' => [
|
||||
'total_pacientes' => (int)$totalPacientes,
|
||||
],
|
||||
'actividad_reciente' => $actividadReciente,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/my_agenda.php
|
||||
* Devuelve la agenda del enfermero autenticado (o de cualquier enfermera para admins).
|
||||
*
|
||||
* Query params:
|
||||
* enfermera_id (int) — Requerido para admins. Enfermeros usan automáticamente el suyo.
|
||||
* fecha (Y-m-d) — Por defecto: hoy.
|
||||
* rango_inicio (Y-m-d) — Para vista semanal/mensual.
|
||||
* rango_fin (Y-m-d)
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
try {
|
||||
// Determinar enfermera_id
|
||||
if (userRole() === 'enfermero') {
|
||||
$eid = enfermeraId();
|
||||
if (!$eid) {
|
||||
jsonError('Tu usuario no tiene una enfermera vinculada. Contacta al administrador.');
|
||||
}
|
||||
} else {
|
||||
$eid = (int)($_GET['enfermera_id'] ?? 0);
|
||||
if (!$eid) {
|
||||
jsonError('enfermera_id requerido');
|
||||
}
|
||||
}
|
||||
|
||||
$enf = new Enfermera();
|
||||
$fecha = $_GET['fecha'] ?? date('Y-m-d');
|
||||
|
||||
// Agenda del día con pleno detalle
|
||||
$agenda = $enf->agenda($eid, $fecha);
|
||||
|
||||
// Enriquecer con servicios extra y estado de asignación
|
||||
$db = Database::getInstance();
|
||||
foreach ($agenda as &$item) {
|
||||
$item['servicios_extra'] = $db->fetchAll(
|
||||
"SELECT * FROM lab_servicios_extra WHERE domicilio_id = ? ORDER BY created_at ASC",
|
||||
[(int)$item['domicilio_id']]
|
||||
);
|
||||
// Info completa del domicilio (notas, indicaciones)
|
||||
$extra = $db->fetch(
|
||||
"SELECT notas_admin, indicaciones_dir, barrio, ciudad FROM lab_domicilios WHERE id = ?",
|
||||
[(int)$item['domicilio_id']]
|
||||
);
|
||||
if ($extra) $item = array_merge($item, $extra);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
// Resumen rápido
|
||||
$totales = array_count_values(array_column($agenda, 'domicilio_estado'));
|
||||
|
||||
jsonOk([
|
||||
'enfermera_id' => $eid,
|
||||
'fecha' => $fecha,
|
||||
'agenda' => $agenda,
|
||||
'total' => count($agenda),
|
||||
'totales' => $totales,
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_asignacion.php
|
||||
* Asigna (o reasigna) una enfermera a un domicilio.
|
||||
* Body JSON:
|
||||
* { domicilio_id, enfermera_id, notas? }
|
||||
* -- Para liberar/completar:
|
||||
* { id, accion: 'liberar'|'completar', motivo? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$asig = new Asignacion();
|
||||
$admin = adminId();
|
||||
|
||||
// Liberar o completar
|
||||
if (!empty($datos['id']) && !empty($datos['accion'])) {
|
||||
$id = (int)$datos['id'];
|
||||
if ($datos['accion'] === 'liberar') {
|
||||
$asig->liberar($id, $admin, $datos['motivo'] ?? '');
|
||||
jsonOk(['id' => $id], 'Asignación liberada');
|
||||
}
|
||||
if ($datos['accion'] === 'completar') {
|
||||
$asig->completar($id, $admin);
|
||||
jsonOk(['id' => $id], 'Asignación completada');
|
||||
}
|
||||
jsonError('Acción no reconocida');
|
||||
}
|
||||
|
||||
// Asignar / reasignar
|
||||
foreach (['domicilio_id', 'enfermera_id'] as $req) {
|
||||
if (empty($datos[$req])) {
|
||||
jsonError("El campo $req es obligatorio");
|
||||
}
|
||||
}
|
||||
|
||||
$id = $asig->asignar(
|
||||
(int)$datos['domicilio_id'],
|
||||
(int)$datos['enfermera_id'],
|
||||
$admin,
|
||||
$datos['notas'] ?? ''
|
||||
);
|
||||
|
||||
jsonOk(['id' => $id], 'Enfermera asignada correctamente');
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_config.php — Guardar configuración global del lab
|
||||
* Solo admin.
|
||||
* Body JSON: { empresa_nombre, empresa_subtitulo, empresa_direccion, empresa_telefono,
|
||||
* empresa_email, empresa_ciudad, doc_color, doc_logo_base64, doc_pie_pagina }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin();
|
||||
|
||||
$datos = inputJson();
|
||||
$db = Database::getInstance();
|
||||
|
||||
$permitidas = [
|
||||
'empresa_nombre', 'empresa_subtitulo', 'empresa_direccion',
|
||||
'empresa_telefono', 'empresa_email', 'empresa_ciudad',
|
||||
'doc_color', 'doc_logo_base64', 'doc_pie_pagina',
|
||||
];
|
||||
|
||||
$guardadas = 0;
|
||||
foreach ($permitidas as $clave) {
|
||||
if (array_key_exists($clave, $datos)) {
|
||||
$db->query(
|
||||
'INSERT INTO lab_config(clave, valor) VALUES(?,?) ON DUPLICATE KEY UPDATE valor=?',
|
||||
[$clave, $datos[$clave], $datos[$clave]]
|
||||
);
|
||||
$guardadas++;
|
||||
}
|
||||
}
|
||||
|
||||
jsonOk(['guardadas' => $guardadas], 'Configuración guardada');
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_domicilio.php
|
||||
* Crea, actualiza o cambia estado de un domicilio.
|
||||
* Body JSON:
|
||||
* { id?, paciente_id, orden_id?, direccion, ciudad?, barrio?,
|
||||
* indicaciones_dir?, fecha_programada, hora_programada?,
|
||||
* tipo_servicio?, estado?,
|
||||
* -- Para cambio de estado solamente:
|
||||
* solo_estado?: true, nuevo_estado?, motivo_cancelacion?, ... }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$dom = new Domicilio();
|
||||
$admin = adminId();
|
||||
|
||||
// Solo cambio de estado
|
||||
if (!empty($datos['id']) && !empty($datos['solo_estado'])) {
|
||||
$id = (int)$datos['id'];
|
||||
$nuevoEstado = $datos['nuevo_estado'] ?? '';
|
||||
$extras = array_filter([
|
||||
'motivo_cancelacion' => $datos['motivo_cancelacion'] ?? null,
|
||||
'fecha_reprogramada' => $datos['fecha_reprogramada'] ?? null,
|
||||
'hora_llegada' => $datos['hora_llegada'] ?? null,
|
||||
'hora_salida' => $datos['hora_salida'] ?? null,
|
||||
'observaciones' => $datos['observaciones'] ?? null,
|
||||
'muestras_tomadas' => $datos['muestras_tomadas'] ?? null,
|
||||
]);
|
||||
$dom->cambiarEstado($id, $nuevoEstado, $admin, $extras);
|
||||
jsonOk(['id' => $id], 'Estado actualizado');
|
||||
}
|
||||
|
||||
if (!empty($datos['id'])) {
|
||||
// Actualización general
|
||||
$id = (int)$datos['id'];
|
||||
unset($datos['id'], $datos['solo_estado']);
|
||||
// Normalizar orden_id: vacío o 0 → null
|
||||
if (isset($datos['orden_id']) && ($datos['orden_id'] === '' || (int)$datos['orden_id'] === 0)) {
|
||||
$datos['orden_id'] = null;
|
||||
}
|
||||
$dom->actualizar($id, $datos, $admin);
|
||||
jsonOk(['id' => $id], 'Domicilio actualizado correctamente');
|
||||
} else {
|
||||
// Creación
|
||||
foreach (['paciente_id', 'direccion', 'fecha_programada'] as $req) {
|
||||
if (empty($datos[$req])) {
|
||||
jsonError("El campo $req es obligatorio");
|
||||
}
|
||||
}
|
||||
// Normalizar orden_id: vacío o 0 → null (evita FK constraint)
|
||||
if (isset($datos['orden_id']) && ($datos['orden_id'] === '' || (int)$datos['orden_id'] === 0)) {
|
||||
$datos['orden_id'] = null;
|
||||
}
|
||||
// Si el creador es un enfermero y no vino enfermera_id, auto-asignarlo desde la sesión
|
||||
if (empty($datos['enfermera_id']) && userRole() === 'enfermero') {
|
||||
$eid = enfermeraId();
|
||||
if ($eid) $datos['enfermera_id'] = $eid;
|
||||
}
|
||||
$id = $dom->crear($datos, $admin);
|
||||
|
||||
// Crear asignación automática si se indicó enfermera_id
|
||||
if (!empty($datos['enfermera_id'])) {
|
||||
$asig = new Asignacion();
|
||||
$asig->asignar($id, (int)$datos['enfermera_id'], $admin);
|
||||
}
|
||||
|
||||
jsonOk(['id' => $id], 'Domicilio creado correctamente');
|
||||
}
|
||||
} catch (InvalidArgumentException $e) {
|
||||
jsonError($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_enfermera.php
|
||||
* Crea o actualiza una enfermera.
|
||||
* Body JSON:
|
||||
* { id?, nombre_completo, numero_documento, tipo_documento?,
|
||||
* telefono, telefono_alt?, email?, zona?, notas?, is_active? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$enf = new Enfermera();
|
||||
$admin = adminId();
|
||||
|
||||
if (!empty($datos['id'])) {
|
||||
$id = (int)$datos['id'];
|
||||
unset($datos['id']);
|
||||
|
||||
// Desactivar
|
||||
if (isset($datos['is_active']) && $datos['is_active'] == 0) {
|
||||
$enf->desactivar($id, $admin);
|
||||
jsonOk(['id' => $id], 'Enfermera desactivada');
|
||||
}
|
||||
|
||||
$enf->actualizar($id, $datos, $admin);
|
||||
jsonOk(['id' => $id], 'Enfermera actualizada correctamente');
|
||||
} else {
|
||||
foreach (['nombre_completo', 'numero_documento', 'telefono'] as $req) {
|
||||
if (empty($datos[$req])) {
|
||||
jsonError("El campo $req es obligatorio");
|
||||
}
|
||||
}
|
||||
$id = $enf->crear($datos, $admin);
|
||||
jsonOk(['id' => $id], 'Enfermera registrada correctamente');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_formulario.php
|
||||
* Solo admins pueden crear/editar plantillas.
|
||||
*
|
||||
* Body JSON para crear: { nombre, descripcion?, categoria?, esquema, permite_firma?, requiere_firma? }
|
||||
* Body JSON para editar: { id, ...mismos campos... }
|
||||
* Body JSON para borrar: { id, borrar: true }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin(); // enfermeros NO pueden diseñar formularios
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$form = new Formulario();
|
||||
$admin = adminId();
|
||||
|
||||
// Borrar (soft delete)
|
||||
if (!empty($datos['id']) && !empty($datos['borrar'])) {
|
||||
$form->actualizar((int)$datos['id'], ['is_active' => 0], $admin);
|
||||
jsonOk(['id' => (int)$datos['id']], 'Formulario eliminado');
|
||||
}
|
||||
|
||||
// Editar
|
||||
if (!empty($datos['id'])) {
|
||||
$id = (int)$datos['id'];
|
||||
unset($datos['id']);
|
||||
$form->actualizar($id, $datos, $admin);
|
||||
jsonOk(['id' => $id], 'Formulario actualizado');
|
||||
}
|
||||
|
||||
// Crear
|
||||
if (empty($datos['nombre'])) jsonError('El nombre es obligatorio');
|
||||
if (empty($datos['esquema'])) jsonError('El esquema es obligatorio');
|
||||
|
||||
$id = $form->crear($datos, $admin);
|
||||
jsonOk(['id' => $id], 'Formulario creado correctamente');
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
jsonError($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_lab_user.php
|
||||
* Crea o actualiza un usuario del sistema.
|
||||
* Solo admins.
|
||||
*
|
||||
* Body JSON:
|
||||
* { id?, username, full_name, email?, password?, role_id, is_active?, enfermera_id? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin();
|
||||
|
||||
$body = inputJson();
|
||||
$id = isset($body['id']) ? (int)$body['id'] : null;
|
||||
$username = trim($body['username'] ?? '');
|
||||
$fullName = trim($body['full_name'] ?? '');
|
||||
$email = trim($body['email'] ?? '');
|
||||
$password = $body['password'] ?? '';
|
||||
$roleId = isset($body['role_id']) ? (int)$body['role_id'] : null;
|
||||
$isActive = isset($body['is_active']) ? (int)(bool)$body['is_active'] : 1;
|
||||
$enfermeraId = isset($body['enfermera_id']) && $body['enfermera_id'] !== '' && $body['enfermera_id'] !== null
|
||||
? (int)$body['enfermera_id'] : null;
|
||||
|
||||
if (!$username) jsonError('El nombre de usuario es obligatorio');
|
||||
if (!$fullName) jsonError('El nombre completo es obligatorio');
|
||||
if (!$roleId) jsonError('Debes seleccionar un rol');
|
||||
|
||||
// Validar username (solo alfanumérico + guión bajo)
|
||||
if (!preg_match('/^[a-zA-Z0-9_\.]{3,50}$/', $username)) {
|
||||
jsonError('El username solo puede tener letras, números, puntos y guiones bajos (3-50 caracteres)');
|
||||
}
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Obtener slug del rol
|
||||
$role = $db->fetch("SELECT id, slug FROM roles WHERE id = ?", [$roleId]);
|
||||
if (!$role) jsonError('Rol no encontrado', 404);
|
||||
$roleSlug = $role['slug'];
|
||||
|
||||
// Si el rol es enfermero y no hay enfermera_id, buscar si tiene ya uno vinculado
|
||||
// (se puede crear sin vincular y vincularlo después desde la pantalla de Enfermeras)
|
||||
|
||||
if ($id) {
|
||||
// ── Actualizar ──────────────────────────────────────────
|
||||
$existing = $db->fetch("SELECT id, username FROM admin_users WHERE id = ?", [$id]);
|
||||
if (!$existing) jsonError('Usuario no encontrado', 404);
|
||||
|
||||
// Verificar duplicado de username (excluir el propio)
|
||||
$dup = $db->fetch("SELECT id FROM admin_users WHERE username = ? AND id != ?", [$username, $id]);
|
||||
if ($dup) jsonError('Ese nombre de usuario ya está en uso');
|
||||
|
||||
$params = [$username, $fullName, $email ?: null, $roleId, $roleSlug, $isActive, $enfermeraId, $id];
|
||||
$sql = "UPDATE admin_users
|
||||
SET username=?, full_name=?, email=?, role_id=?, role=?, is_active=?, enfermera_id=?, updated_at=NOW()
|
||||
WHERE id=?";
|
||||
|
||||
if ($password) {
|
||||
if (strlen($password) < 6) jsonError('La contraseña debe tener al menos 6 caracteres');
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
$sql = "UPDATE admin_users
|
||||
SET username=?, full_name=?, email=?, role_id=?, role=?, is_active=?, enfermera_id=?, password_hash=?, updated_at=NOW()
|
||||
WHERE id=?";
|
||||
$params = [$username, $fullName, $email ?: null, $roleId, $roleSlug, $isActive, $enfermeraId, $hash, $id];
|
||||
}
|
||||
|
||||
$db->query($sql, $params);
|
||||
jsonOk(['id' => $id], 'Usuario actualizado correctamente');
|
||||
|
||||
} else {
|
||||
// ── Crear ────────────────────────────────────────────────
|
||||
if (!$password) jsonError('La contraseña es obligatoria al crear un usuario');
|
||||
if (strlen($password) < 6) jsonError('La contraseña debe tener al menos 6 caracteres');
|
||||
|
||||
$dup = $db->fetch("SELECT id FROM admin_users WHERE username = ?", [$username]);
|
||||
if ($dup) jsonError('Ese nombre de usuario ya está en uso');
|
||||
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
$db->query(
|
||||
"INSERT INTO admin_users (username, full_name, email, password_hash, role_id, role, is_active, enfermera_id)
|
||||
VALUES (?,?,?,?,?,?,?,?)",
|
||||
[$username, $fullName, $email ?: null, $hash, $roleId, $roleSlug, $isActive, $enfermeraId]
|
||||
);
|
||||
$newId = $db->lastInsertId();
|
||||
jsonOk(['id' => $newId], 'Usuario creado correctamente');
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_orden.php
|
||||
* Crea o actualiza datos de una orden médica (sin cambiar estado).
|
||||
* Body JSON:
|
||||
* { id?, paciente_id, conversation_id?, whatsapp_media_id?, local_file?,
|
||||
* medico_nombre?, medico_registro?, fecha_orden?, diagnostico?,
|
||||
* examenes_solicitados?, requiere_ayuno?, horas_ayuno?,
|
||||
* indicaciones?, notas_admin? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$om = new OrdenMedica();
|
||||
$admin = adminId();
|
||||
|
||||
if (!empty($datos['id'])) {
|
||||
$id = (int)$datos['id'];
|
||||
unset($datos['id']);
|
||||
$om->actualizar($id, $datos, $admin);
|
||||
jsonOk(['id' => $id], 'Orden actualizada correctamente');
|
||||
} else {
|
||||
if (empty($datos['paciente_id'])) {
|
||||
jsonError('El campo paciente_id es obligatorio');
|
||||
}
|
||||
$id = $om->crear($datos, $admin);
|
||||
jsonOk(['id' => $id], 'Orden creada correctamente');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_paciente.php
|
||||
* Crea o actualiza un paciente.
|
||||
* Body JSON:
|
||||
* { id?, nombre_completo, numero_documento?, tipo_documento?, telefono?,
|
||||
* email?, fecha_nacimiento?, genero?, direccion?, ciudad?, barrio?,
|
||||
* eps?, notas_admin?, user_id? }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$pac = new Paciente();
|
||||
$admin = adminId();
|
||||
|
||||
if (!empty($datos['id'])) {
|
||||
// Actualización
|
||||
$id = (int)$datos['id'];
|
||||
unset($datos['id']);
|
||||
$pac->actualizar($id, $datos, $admin);
|
||||
jsonOk(['id' => $id], 'Paciente actualizado correctamente');
|
||||
} else {
|
||||
// Creación — nombre obligatorio
|
||||
if (empty($datos['nombre_completo'])) {
|
||||
jsonError('El campo nombre_completo es obligatorio');
|
||||
}
|
||||
$id = $pac->crear($datos, $admin);
|
||||
jsonOk(['id' => $id], 'Paciente creado correctamente');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_role.php
|
||||
* Crea o actualiza un rol y sus módulos.
|
||||
* Solo admins.
|
||||
*
|
||||
* Body JSON:
|
||||
* { id?, name, slug, description?, color?, modules: [slug, ...] }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
requireAdmin();
|
||||
|
||||
$body = inputJson();
|
||||
|
||||
$id = isset($body['id']) ? (int)$body['id'] : null;
|
||||
$name = trim($body['name'] ?? '');
|
||||
$slug = trim($body['slug'] ?? '');
|
||||
$description = trim($body['description'] ?? '');
|
||||
$color = trim($body['color'] ?? '#6c757d');
|
||||
$modules = $body['modules'] ?? [];
|
||||
|
||||
if (!$name) jsonError('El nombre del rol es obligatorio');
|
||||
if (!$slug) jsonError('El slug del rol es obligatorio');
|
||||
if (!preg_match('/^[a-z0-9_\-]+$/', $slug)) jsonError('El slug solo puede contener letras minúsculas, números, guiones y guiones bajos');
|
||||
if (!is_array($modules)) jsonError('modules debe ser un arreglo');
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Verificar duplicado de slug
|
||||
$existing = $db->fetch("SELECT id, is_system FROM roles WHERE slug = ?", [$slug]);
|
||||
if ($existing && (!$id || $existing['id'] !== $id)) {
|
||||
jsonError('Ya existe un rol con ese slug');
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
// Actualizar
|
||||
$role = $db->fetch("SELECT id, is_system FROM roles WHERE id = ?", [$id]);
|
||||
if (!$role) jsonError('Rol no encontrado', 404);
|
||||
|
||||
$db->query(
|
||||
"UPDATE roles SET name=?, slug=?, description=?, color=?, updated_at=NOW() WHERE id=?",
|
||||
[$name, $slug, $description, $color, $id]
|
||||
);
|
||||
// Reconstruir módulos solo si no es sistema, o siempre (admin puede editar módulos)
|
||||
$db->query("DELETE FROM role_modules WHERE role_id = ?", [$id]);
|
||||
} else {
|
||||
// Crear
|
||||
$db->query(
|
||||
"INSERT INTO roles (name, slug, description, color) VALUES (?,?,?,?)",
|
||||
[$name, $slug, $description, $color]
|
||||
);
|
||||
$id = $db->lastInsertId();
|
||||
}
|
||||
|
||||
// Insertar módulos
|
||||
foreach ($modules as $mod) {
|
||||
$mod = trim((string)$mod);
|
||||
if ($mod) {
|
||||
$db->query(
|
||||
"INSERT IGNORE INTO role_modules (role_id, module_slug) VALUES (?,?)",
|
||||
[$id, $mod]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
jsonOk(['id' => $id], $id ? 'Rol actualizado' : 'Rol creado');
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/save_servicio_extra.php
|
||||
* El enfermero agrega (o actualiza/elimina) un servicio extra a un domicilio.
|
||||
*
|
||||
* Body JSON:
|
||||
* { domicilio_id, descripcion, tipo, notas?, requiere_pago?, valor? } -- crear
|
||||
* { id, descripcion?, tipo?, notas?, requiere_pago?, valor? } -- editar
|
||||
* { id, eliminar: true } -- borrar
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
$TIPOS_VALIDOS = ['inyeccion','cura','nebulizacion','toma_muestra',
|
||||
'tension_arterial','glucometria','otro'];
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$db = Database::getInstance();
|
||||
|
||||
// ── Eliminar ───────────────────────────────────────────────────────────
|
||||
if (!empty($datos['id']) && !empty($datos['eliminar'])) {
|
||||
$se = $db->fetch('SELECT * FROM lab_servicios_extra WHERE id = ?', [(int)$datos['id']]);
|
||||
if (!$se) jsonError('Servicio no encontrado', 404);
|
||||
if (userRole() === 'enfermero') {
|
||||
requireEnfermeroAccess((int)$se['realizado_por']);
|
||||
}
|
||||
$db->delete('lab_servicios_extra', 'id = ?', [(int)$datos['id']]);
|
||||
jsonOk(['id' => (int)$datos['id']], 'Servicio eliminado');
|
||||
}
|
||||
|
||||
// ── Editar ─────────────────────────────────────────────────────────────
|
||||
if (!empty($datos['id'])) {
|
||||
$id = (int)$datos['id'];
|
||||
$se = $db->fetch('SELECT * FROM lab_servicios_extra WHERE id = ?', [$id]);
|
||||
if (!$se) jsonError('Servicio no encontrado', 404);
|
||||
if (userRole() === 'enfermero') {
|
||||
requireEnfermeroAccess((int)$se['realizado_por']);
|
||||
}
|
||||
$permitidos = ['descripcion','tipo','notas','requiere_pago','valor'];
|
||||
$campos = array_intersect_key($datos, array_flip($permitidos));
|
||||
if (isset($campos['tipo']) && !in_array($campos['tipo'], $TIPOS_VALIDOS)) {
|
||||
jsonError('Tipo de servicio inválido');
|
||||
}
|
||||
$db->update('lab_servicios_extra', $campos, 'id = ?', [$id]);
|
||||
jsonOk(['id' => $id], 'Servicio actualizado');
|
||||
}
|
||||
|
||||
// ── Crear ──────────────────────────────────────────────────────────────
|
||||
foreach (['domicilio_id', 'descripcion', 'tipo'] as $req) {
|
||||
if (empty($datos[$req])) jsonError("El campo $req es obligatorio");
|
||||
}
|
||||
if (!in_array($datos['tipo'], $TIPOS_VALIDOS)) {
|
||||
jsonError('Tipo de servicio inválido');
|
||||
}
|
||||
|
||||
// Verificar que el domicilio existe y que el enfermero le pertenece
|
||||
$domId = (int)$datos['domicilio_id'];
|
||||
if (userRole() === 'enfermero') {
|
||||
$eid = enfermeraId();
|
||||
$asig = $db->fetch(
|
||||
'SELECT a.id FROM lab_asignaciones a WHERE a.domicilio_id = ? AND a.enfermera_id = ?',
|
||||
[$domId, $eid]
|
||||
);
|
||||
if (!$asig) jsonError('No tienes permiso para este domicilio', 403);
|
||||
}
|
||||
|
||||
$id = $db->insert('lab_servicios_extra', [
|
||||
'domicilio_id' => $domId,
|
||||
'descripcion' => trim($datos['descripcion']),
|
||||
'tipo' => $datos['tipo'],
|
||||
'notas' => $datos['notas'] ?? null,
|
||||
'requiere_pago' => !empty($datos['requiere_pago']) ? 1 : 0,
|
||||
'valor' => !empty($datos['valor']) ? (float)$datos['valor'] : null,
|
||||
'realizado_por' => enfermeraId() ?? (userRole() === 'admin' ? null : null),
|
||||
]);
|
||||
|
||||
jsonOk(['id' => $id], 'Servicio extra registrado');
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/send_formulario.php
|
||||
* Crea un envío (instancia) con datos pre-llenados y devuelve el link para WhatsApp.
|
||||
* Accesible por admin Y enfermero.
|
||||
*
|
||||
* Body JSON:
|
||||
* { formulario_id, paciente_id?, domicilio_id?, datos_prefilled:{...}, enviado_via? }
|
||||
*
|
||||
* Returns:
|
||||
* { id, token, url, whatsapp_url, mensaje_wa }
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
|
||||
if (empty($datos['formulario_id'])) jsonError('formulario_id es obligatorio');
|
||||
|
||||
// Verificar que la plantilla existe
|
||||
$form = new Formulario();
|
||||
$plantilla = $form->obtener((int)$datos['formulario_id']);
|
||||
if (!$plantilla) jsonError('Formulario no encontrado', 404);
|
||||
|
||||
// Pre-llenado: si viene paciente_id, auto-enriquecer con datos del paciente
|
||||
$prefilled = $datos['datos_prefilled'] ?? [];
|
||||
if (!empty($datos['paciente_id'])) {
|
||||
$pac = (new Paciente())->obtener((int)$datos['paciente_id']);
|
||||
if ($pac) {
|
||||
// Mapeo automático de campos estándar
|
||||
$prefilled['__paciente'] = [
|
||||
'id' => $pac['id'],
|
||||
'nombre_completo' => $pac['nombre_completo'],
|
||||
'numero_documento'=> $pac['numero_documento'],
|
||||
'tipo_documento' => $pac['tipo_documento'],
|
||||
'telefono' => $pac['telefono'],
|
||||
'email' => $pac['email'] ?? '',
|
||||
'fecha_nacimiento'=> $pac['fecha_nacimiento'] ?? '',
|
||||
'eps' => $pac['eps'] ?? '',
|
||||
'direccion' => $pac['direccion'] ?? '',
|
||||
'ciudad' => $pac['ciudad'] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$datos['datos_prefilled'] = json_encode($prefilled, JSON_UNESCAPED_UNICODE);
|
||||
$datos['enviado_via'] = $datos['enviado_via'] ?? 'whatsapp';
|
||||
|
||||
$envioId = $form->crearEnvio($datos, adminId());
|
||||
|
||||
// Construir URL pública
|
||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
||||
$base = $protocol . '://' . $host;
|
||||
// Subir un nivel desde /api/lab/
|
||||
$basePath = rtrim(dirname(dirname(dirname($_SERVER['SCRIPT_NAME']))), '/');
|
||||
|
||||
// Obtener el token del envío recién creado
|
||||
$db = Database::getInstance();
|
||||
$envio = $db->fetch('SELECT token FROM lab_form_envios WHERE id = ?', [$envioId]);
|
||||
$token = $envio['token'];
|
||||
$url = $base . $basePath . '/form_cliente.php?t=' . $token;
|
||||
|
||||
// Mensaje de WhatsApp preformateado
|
||||
$nomPac = $prefilled['__paciente']['nombre_completo'] ?? 'Estimado paciente';
|
||||
$nomForm = $plantilla['nombre'];
|
||||
$mensajeWa = "Hola $nomPac, le enviamos el formulario *\"$nomForm\"* para que lo complete y firme digitalmente.\n\n"
|
||||
. "👉 Accede aquí:\n$url\n\n"
|
||||
. "El enlace expira en 7 días. Si tiene dudas contáctenos.";
|
||||
|
||||
$telPac = $prefilled['__paciente']['telefono'] ?? '';
|
||||
$waUrl = 'https://wa.me/' . preg_replace('/\D/', '', $telPac)
|
||||
. '?text=' . rawurlencode($mensajeWa);
|
||||
|
||||
jsonOk([
|
||||
'id' => $envioId,
|
||||
'token' => $token,
|
||||
'url' => $url,
|
||||
'whatsapp_url'=> $waUrl,
|
||||
'mensaje_wa' => $mensajeWa,
|
||||
], 'Envío creado correctamente');
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/submit_formulario.php — Guardar respuesta del cliente (público, sin auth)
|
||||
* GET /api/lab/submit_formulario.php?t=TOKEN — Verificar estado del token (público)
|
||||
*
|
||||
* Body JSON (POST): { token, datos_cliente:{...}, firma_svg? }
|
||||
*/
|
||||
// No usar _helpers.php porque este endpoint es PÚBLICO (no requiere sesión)
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../classes/lab/Formulario.php';
|
||||
require_once __DIR__ . '/../../classes/Database.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }
|
||||
|
||||
function pubOk(array $data = [], string $msg = ''): void {
|
||||
$r = ['success' => true];
|
||||
if ($msg) $r['message'] = $msg;
|
||||
echo json_encode(array_merge($r, $data), JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
function pubErr(string $msg, int $code = 400): void {
|
||||
http_response_code($code);
|
||||
echo json_encode(['success' => false, 'error' => $msg], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$form = new Formulario();
|
||||
|
||||
// GET: cargar datos del formulario por token
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$token = $_GET['t'] ?? '';
|
||||
if (!$token) pubErr('Token requerido');
|
||||
$envio = $form->obtenerPorToken($token);
|
||||
if (!$envio) pubErr('Link inválido o expirado', 404);
|
||||
|
||||
// Config de diseño: primero override del formulario, luego config global
|
||||
$db2 = Database::getInstance();
|
||||
$cfgRows = $db2->fetchAll('SELECT clave, valor FROM lab_config ORDER BY clave');
|
||||
$gCfg = [];
|
||||
foreach ($cfgRows as $r) { $gCfg[$r['clave']] = $r['valor']; }
|
||||
$docConfig = [
|
||||
'doc_color' => $envio['doc_color'] ?: ($gCfg['doc_color'] ?? '#1565c0'),
|
||||
'doc_logo_base64' => $envio['doc_logo_base64'] ?: ($gCfg['doc_logo_base64'] ?? null),
|
||||
'doc_encabezado' => $envio['doc_encabezado'] ?: ($gCfg['empresa_nombre'] ?? null),
|
||||
'doc_subtitulo' => $envio['doc_subtitulo'] ?: ($gCfg['empresa_subtitulo'] ?? null),
|
||||
'doc_pie_pagina' => $envio['doc_pie_pagina'] ?: ($gCfg['doc_pie_pagina'] ?? null),
|
||||
];
|
||||
|
||||
// Estructura que espera form_cliente.php
|
||||
pubOk([
|
||||
'formulario' => [
|
||||
'nombre' => $envio['form_nombre'],
|
||||
'descripcion' => $envio['form_descripcion'] ?? '',
|
||||
'esquema_decoded'=> $envio['esquema_decoded'],
|
||||
'permite_firma' => (bool)($envio['permite_firma'] ?? false),
|
||||
'requiere_firma' => (bool)($envio['requiere_firma'] ?? false),
|
||||
],
|
||||
'envio' => [
|
||||
'id' => $envio['id'],
|
||||
'estado' => $envio['estado'],
|
||||
'firma_svg' => $envio['firma_svg'] ?? null,
|
||||
'hash_verificacion'=> $envio['hash_verificacion'] ?? null,
|
||||
'completado_en' => $envio['completado_en'] ?? null,
|
||||
],
|
||||
'prefilled' => $envio['prefilled_decoded'] ?? [],
|
||||
'config' => $docConfig,
|
||||
]);
|
||||
}
|
||||
|
||||
// POST: guardar respuesta
|
||||
$raw = file_get_contents('php://input');
|
||||
$datos = json_decode($raw, true) ?? [];
|
||||
|
||||
$token = $datos['token'] ?? '';
|
||||
if (!$token) pubErr('Token requerido');
|
||||
|
||||
$envio = $form->obtenerPorToken($token);
|
||||
if (!$envio) pubErr('Link inválido o expirado', 404);
|
||||
|
||||
if (in_array($envio['estado'], ['completado', 'firmado'])) {
|
||||
// El formulario ya fue guardado (posible reintento tras error de red).
|
||||
// Devolvemos éxito con los datos existentes para que el cliente no quede bloqueado.
|
||||
$db = Database::getInstance();
|
||||
$saved = $db->fetch('SELECT id, hash_verificacion, firma_svg FROM lab_form_envios WHERE token = ?', [$token]);
|
||||
pubOk([
|
||||
'firmado' => !empty($saved['firma_svg']),
|
||||
'hash' => $saved['hash_verificacion'] ?? null,
|
||||
'envio_id' => $saved['id'] ?? null,
|
||||
'ya_enviado'=> true,
|
||||
], 'Formulario enviado correctamente. ¡Gracias!');
|
||||
}
|
||||
|
||||
$datosCliente = $datos['datos_cliente'] ?? [];
|
||||
$firmaSvg = $datos['firma_svg'] ?? null;
|
||||
$firmaFoto = $datos['firma_foto'] ?? null;
|
||||
|
||||
// Guardar foto de firma dentro de datos_cliente para no requerir nueva columna
|
||||
if ($firmaFoto) {
|
||||
$datosCliente['__firma_foto'] = $firmaFoto;
|
||||
}
|
||||
|
||||
// Validar que si requiere firma, venga
|
||||
if ($envio['requiere_firma'] && !$firmaSvg) {
|
||||
pubErr('La firma es obligatoria para este formulario');
|
||||
}
|
||||
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
|
||||
$ok = $form->guardarRespuesta($token, $datosCliente, $firmaSvg, $ip, $ua);
|
||||
if (!$ok) pubErr('No se pudo guardar la respuesta', 500);
|
||||
|
||||
// Recuperar hash e id para la respuesta
|
||||
$db = Database::getInstance();
|
||||
$saved = $db->fetch('SELECT id, hash_verificacion FROM lab_form_envios WHERE token = ?', [$token]);
|
||||
|
||||
pubOk([
|
||||
'firmado' => (bool)$firmaSvg,
|
||||
'hash' => $saved['hash_verificacion'] ?? null,
|
||||
'envio_id' => $saved['id'] ?? null,
|
||||
], 'Formulario enviado correctamente. ¡Gracias!');
|
||||
|
||||
} catch (Exception $e) {
|
||||
pubErr($e->getMessage(), 500);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* POST /api/lab/update_domicilio_enfermero.php
|
||||
* El enfermero actualiza el estado de un domicilio asignado.
|
||||
*
|
||||
* Body JSON:
|
||||
* { domicilio_id, nuevo_estado, notas? }
|
||||
*
|
||||
* Transiciones permitidas al enfermero:
|
||||
* programado → confirmado
|
||||
* confirmado → en_camino
|
||||
* en_camino → en_domicilio
|
||||
* en_domicilio → completado
|
||||
* cualquiera → cancelado (con notas obligatorias)
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('POST');
|
||||
|
||||
// Flujo permitido enfermero: estado_actual => [estados_siguientes_posibles]
|
||||
const TRANSICIONES_ENFERMERO = [
|
||||
'programado' => ['confirmado'],
|
||||
'confirmado' => ['en_camino', 'cancelado'],
|
||||
'en_camino' => ['en_domicilio', 'cancelado'],
|
||||
'en_domicilio' => ['completado', 'cancelado'],
|
||||
];
|
||||
|
||||
try {
|
||||
$datos = inputJson();
|
||||
$domId = (int)($datos['domicilio_id'] ?? 0);
|
||||
$estado = trim($datos['nuevo_estado'] ?? '');
|
||||
$notas = trim($datos['notas'] ?? '');
|
||||
|
||||
if (!$domId) jsonError('domicilio_id requerido');
|
||||
if (!$estado) jsonError('nuevo_estado requerido');
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
// Verificar domicilio y obtener estado actual
|
||||
$dom = $db->fetch(
|
||||
'SELECT id, estado, paciente_id FROM lab_domicilios WHERE id = ?',
|
||||
[$domId]
|
||||
);
|
||||
if (!$dom) jsonError('Domicilio no encontrado', 404);
|
||||
|
||||
// Verificar que el enfermero tenga asignación activa
|
||||
if (userRole() === 'enfermero') {
|
||||
$eid = enfermeraId();
|
||||
$asig = $db->fetch(
|
||||
"SELECT a.id FROM lab_asignaciones a
|
||||
WHERE a.domicilio_id = ? AND a.enfermera_id = ?
|
||||
AND a.estado NOT IN ('liberada','completada')",
|
||||
[$domId, $eid]
|
||||
);
|
||||
if (!$asig) jsonError('No tienes asignación activa para este domicilio', 403);
|
||||
|
||||
// Validar transición
|
||||
$estadoActual = $dom['estado'];
|
||||
$permitidos = TRANSICIONES_ENFERMERO[$estadoActual] ?? [];
|
||||
if (!in_array($estado, $permitidos)) {
|
||||
jsonError("No puedes pasar de '$estadoActual' a '$estado'");
|
||||
}
|
||||
}
|
||||
|
||||
if ($estado === 'cancelado' && !$notas) {
|
||||
jsonError('Las notas son obligatorias al cancelar');
|
||||
}
|
||||
|
||||
// Actualizar domicilio
|
||||
$campos = ['estado' => $estado];
|
||||
if ($notas) $campos['notas_admin'] = $notas;
|
||||
if ($estado === 'completado') {
|
||||
$campos['hora_salida'] = date('H:i:s');
|
||||
}
|
||||
if ($estado === 'en_domicilio') {
|
||||
$campos['hora_llegada'] = date('H:i:s');
|
||||
}
|
||||
$db->update('lab_domicilios', $campos, 'id = ?', [$domId]);
|
||||
|
||||
// Actualizar asignación si completado/cancelado
|
||||
if (in_array($estado, ['completado', 'cancelado']) && userRole() === 'enfermero') {
|
||||
$estAsig = $estado === 'completado' ? 'completada' : 'liberada';
|
||||
$db->update(
|
||||
'lab_asignaciones',
|
||||
['estado' => $estAsig],
|
||||
'domicilio_id = ? AND enfermera_id = ?',
|
||||
[$domId, enfermeraId()]
|
||||
);
|
||||
}
|
||||
|
||||
jsonOk(['domicilio_id' => $domId, 'estado' => $estado], 'Estado actualizado correctamente');
|
||||
|
||||
} catch (Exception $e) {
|
||||
jsonError($e->getMessage(), 500);
|
||||
}
|
||||
Reference in New Issue
Block a user