Files
whatsapp/classes/lab/Formulario.php
T
2026-03-16 20:29:21 -05:00

206 lines
9.9 KiB
PHP

<?php
/**
* Formulario — Gestión de plantillas y envíos de formularios clínicos.
*/
require_once __DIR__ . '/../../classes/Database.php';
require_once __DIR__ . '/ActividadAdmin.php';
class Formulario {
private Database $db;
private ActividadAdmin $log;
public function __construct() {
$this->db = Database::getInstance();
$this->log = new ActividadAdmin();
}
// ─────────────────────────────────────────────────────────────────────────
// PLANTILLAS (admin)
// ─────────────────────────────────────────────────────────────────────────
public function listar(bool $soloActivos = true): array {
$w = $soloActivos ? 'WHERE f.is_active = 1' : '';
return $this->db->fetchAll("
SELECT f.*, u.full_name AS creado_por_nombre,
(SELECT COUNT(*) FROM lab_form_envios e WHERE e.formulario_id = f.id) AS total_envios,
(SELECT COUNT(*) FROM lab_form_envios e WHERE e.formulario_id = f.id AND e.estado IN ('completado','firmado')) AS total_completados
FROM lab_formularios f
LEFT JOIN admin_users u ON u.id = f.creado_por
$w
ORDER BY f.created_at DESC
");
}
public function obtener(int $id): ?array {
$row = $this->db->fetch(
"SELECT f.*, u.full_name AS creado_por_nombre
FROM lab_formularios f
LEFT JOIN admin_users u ON u.id = f.creado_por
WHERE f.id = ?",
[$id]
);
if ($row) {
$row['esquema_decoded'] = json_decode($row['esquema'], true) ?? [];
}
return $row ?: null;
}
public function crear(array $datos, int $adminId): int {
$this->validarEsquema($datos['esquema'] ?? '[]');
$id = $this->db->insert('lab_formularios', [
'nombre' => trim($datos['nombre']),
'descripcion' => $datos['descripcion'] ?? null,
'categoria' => $datos['categoria'] ?? 'otro',
'esquema' => $datos['esquema'] ?? '[]',
'permite_firma' => !empty($datos['permite_firma']) ? 1 : 0,
'requiere_firma' => !empty($datos['requiere_firma']) ? 1 : 0,
'doc_encabezado' => $datos['doc_encabezado'] ?? null,
'doc_subtitulo' => $datos['doc_subtitulo'] ?? null,
'doc_logo_base64' => $datos['doc_logo_base64'] ?? null,
'doc_color' => $datos['doc_color'] ?? null,
'doc_pie_pagina' => $datos['doc_pie_pagina'] ?? null,
'creado_por' => $adminId,
]);
$this->log->registrar($adminId, 'formularios', 'crear', $id, ['nombre' => $datos['nombre']]);
return $id;
}
public function actualizar(int $id, array $datos, int $adminId): bool {
$permitidos = [
'nombre','descripcion','categoria','esquema',
'permite_firma','requiere_firma','is_active',
'doc_encabezado','doc_subtitulo','doc_logo_base64','doc_color','doc_pie_pagina',
];
$campos = array_intersect_key($datos, array_flip($permitidos));
if (isset($campos['esquema'])) {
$this->validarEsquema($campos['esquema']);
$row = $this->db->fetch('SELECT version FROM lab_formularios WHERE id = ?', [$id]);
$campos['version'] = (int)($row['version'] ?? 0) + 1;
}
$ok = $this->db->update('lab_formularios', $campos, 'id = ?', [$id]);
$this->log->registrar($adminId, 'formularios', 'editar', $id, $campos);
return $ok > 0;
}
// ─────────────────────────────────────────────────────────────────────────
// ENVÍOS
// ─────────────────────────────────────────────────────────────────────────
/**
* Crear un envío con datos pre-llenados, genera token único.
*/
public function crearEnvio(array $datos, int $enviadoPor): int {
$token = bin2hex(random_bytes(32));
$expira = date('Y-m-d H:i:s', strtotime('+7 days'));
$id = $this->db->insert('lab_form_envios', [
'formulario_id' => (int)$datos['formulario_id'],
'paciente_id' => !empty($datos['paciente_id']) ? (int)$datos['paciente_id'] : null,
'domicilio_id' => !empty($datos['domicilio_id']) ? (int)$datos['domicilio_id'] : null,
'token' => $token,
'datos_prefilled' => $datos['datos_prefilled'] ?? '{}',
'estado' => 'pendiente',
'enviado_por' => $enviadoPor,
'enviado_via' => $datos['enviado_via'] ?? 'whatsapp',
'expira_en' => $expira,
]);
$this->log->registrar($enviadoPor, 'formularios', 'enviar', $id, [
'formulario_id' => $datos['formulario_id'],
'via' => $datos['enviado_via'] ?? 'whatsapp',
]);
return $id;
}
/**
* Obtener envío por token (uso público del cliente).
*/
public function obtenerPorToken(string $token): ?array {
$envio = $this->db->fetch(
"SELECT e.*, f.nombre AS form_nombre, f.esquema, f.permite_firma, f.requiere_firma, f.descripcion AS form_descripcion,
f.doc_color, f.doc_logo_base64, f.doc_encabezado, f.doc_subtitulo, f.doc_pie_pagina,
p.nombre_completo AS paciente_nombre, p.numero_documento, p.telefono AS paciente_telefono
FROM lab_form_envios e
JOIN lab_formularios f ON f.id = e.formulario_id
LEFT JOIN lab_pacientes p ON p.id = e.paciente_id
WHERE e.token = ?",
[$token]
);
if (!$envio) return null;
if ($envio['estado'] === 'expirado') return null;
if ($envio['expira_en'] && strtotime($envio['expira_en']) < time()) {
$this->db->update('lab_form_envios', ['estado' => 'expirado'], 'id = ?', [$envio['id']]);
return null;
}
$envio['esquema_decoded'] = json_decode($envio['esquema'], true) ?? [];
$envio['prefilled_decoded'] = json_decode($envio['datos_prefilled'], true) ?? [];
return $envio;
}
/**
* Guardar respuesta del cliente (firma + datos).
*/
public function guardarRespuesta(string $token, array $datosCliente, ?string $firmaSvg, string $ip, string $ua): bool {
$envio = $this->db->fetch('SELECT id, estado FROM lab_form_envios WHERE token = ?', [$token]);
if (!$envio || $envio['estado'] === 'expirado') return false;
$tieneCanvas = !empty($firmaSvg);
$tieneFoto = !empty($datosCliente['__firma_foto']);
$estado = ($tieneCanvas || $tieneFoto) ? 'firmado' : 'completado';
// Hash de verificación SHA-256 (vincula contenido + firma + timestamp)
$payloadHash = json_encode($datosCliente, JSON_UNESCAPED_UNICODE)
. ($firmaSvg ?? '')
. $envio['id']
. $envio['token']
. date('Y-m-d H:i:s');
$hash = hash('sha256', $payloadHash);
$this->db->update('lab_form_envios', [
'datos_cliente' => json_encode($datosCliente, JSON_UNESCAPED_UNICODE),
'firma_svg' => $firmaSvg,
'hash_verificacion' => $hash,
'ip_cliente' => $ip,
'user_agent' => substr($ua, 0, 512),
'estado' => $estado,
'completado_en' => date('Y-m-d H:i:s'),
], 'token = ?', [$token]);
return true;
}
/**
* Listar envíos (admin/enfermero).
*/
public function listarEnvios(array $filtros = []): array {
$where = ['1=1'];
$params = [];
if (!empty($filtros['formulario_id'])) { $where[] = 'e.formulario_id = ?'; $params[] = $filtros['formulario_id']; }
if (!empty($filtros['paciente_id'])) { $where[] = 'e.paciente_id = ?'; $params[] = $filtros['paciente_id']; }
if (!empty($filtros['enviado_por'])) { $where[] = 'e.enviado_por = ?'; $params[] = $filtros['enviado_por']; }
if (!empty($filtros['estado'])) { $where[] = 'e.estado = ?'; $params[] = $filtros['estado']; }
$w = implode(' AND ', $where);
return $this->db->fetchAll("
SELECT e.*, f.nombre AS form_nombre, f.categoria, f.esquema,
p.nombre_completo AS paciente_nombre,
u.full_name AS enviado_por_nombre
FROM lab_form_envios e
JOIN lab_formularios f ON f.id = e.formulario_id
LEFT JOIN lab_pacientes p ON p.id = e.paciente_id
LEFT JOIN admin_users u ON u.id = e.enviado_por
WHERE $w
ORDER BY e.created_at DESC
LIMIT 200
", $params);
}
// ─────────────────────────────────────────────────────────────────────────
// Helper
// ─────────────────────────────────────────────────────────────────────────
private function validarEsquema(string $json): void {
$decoded = json_decode($json, true);
if (!is_array($decoded)) {
throw new InvalidArgumentException('El esquema del formulario no es un JSON válido');
}
}
}