up
This commit is contained in:
+237
-8
@@ -1,13 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* ver_formulario_enviado.php — Ver respuesta de un formulario (imprimible / PDF)
|
||||
* Acceso admin/enfermero: ver_formulario_enviado.php?id=ENVIO_ID (requiere sesión)
|
||||
* Acceso público cliente: ver_formulario_enviado.php?t=TOKEN (sin sesión, solo firmado/completado)
|
||||
* Acceso admin/enfermero: ver_formulario_enviado.php?id=ENVIO_ID (requiere sesión)
|
||||
* Acceso público cliente: ver_formulario_enviado.php?t=TOKEN (sin sesión, solo firmado/completado)
|
||||
* Acceso turnero (consent): ver_formulario_enviado.php?token=UUID (firma de consentimiento turnero)
|
||||
*/
|
||||
require_once 'config/config.php';
|
||||
|
||||
// ── TURNERO: Consentimiento por token UUID (detección previa al flujo normal) ──
|
||||
$tokenTurnero = trim($_GET['token'] ?? '');
|
||||
$modoTurnero = ($tokenTurnero !== '');
|
||||
|
||||
if ($modoTurnero && !preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $tokenTurnero)) {
|
||||
http_response_code(400); die('Token de consentimiento inválido');
|
||||
}
|
||||
|
||||
$tokenPublico = trim($_GET['t'] ?? '');
|
||||
$modoPublico = ($tokenPublico !== '');
|
||||
$modoPublico = ($modoTurnero || $tokenPublico !== '');
|
||||
|
||||
if (!$modoPublico && !isUserLoggedIn()) {
|
||||
header('Location: login.php'); exit;
|
||||
@@ -16,7 +25,93 @@ if (!$modoPublico && !isUserLoggedIn()) {
|
||||
// ── Cargar datos ──────────────────────────────────────────────────────
|
||||
$db = Database::getInstance();
|
||||
|
||||
if ($modoPublico) {
|
||||
if ($modoTurnero) {
|
||||
// ── Turnero: cargar desde turnero_consentimientos ──────────────────
|
||||
$tcRow = $db->fetch(
|
||||
"SELECT tc.id, tc.turno_id, tc.formulario_id, tc.token, tc.estado,
|
||||
tc.enviado_at, tc.firmado_at, tc.ip_firma, tc.ua_firma, tc.firma_svg,
|
||||
f.nombre AS form_nombre, f.categoria, f.descripcion AS form_descripcion,
|
||||
f.esquema, f.doc_encabezado, f.doc_subtitulo, f.doc_logo_base64, f.doc_color, f.doc_pie_pagina,
|
||||
p.nombre_completo AS paciente_nombre, p.numero_documento, p.tipo_documento,
|
||||
p.fecha_nacimiento, p.telefono AS paciente_telefono, p.eps,
|
||||
t.sesion_id
|
||||
FROM turnero_consentimientos tc
|
||||
JOIN lab_formularios f ON f.id = tc.formulario_id
|
||||
JOIN turnero_turnos t ON t.id = tc.turno_id
|
||||
JOIN turnero_solicitudes ts ON ts.turno_id = tc.turno_id
|
||||
LEFT JOIN lab_pacientes p ON p.id = ts.paciente_id
|
||||
WHERE tc.token = ?",
|
||||
[$tokenTurnero]
|
||||
);
|
||||
if (!$tcRow) { http_response_code(404); die('Consentimiento no encontrado'); }
|
||||
|
||||
// ── POST: guardar firma y marcar como firmado ──────────────────────
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
if ($tcRow['estado'] === 'firmado') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Este consentimiento ya fue firmado']); exit;
|
||||
}
|
||||
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$firmaSvg = trim($input['firma_svg'] ?? '');
|
||||
if (strlen($firmaSvg) < 100) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Firma requerida']); exit;
|
||||
}
|
||||
$ip = filter_var(
|
||||
$_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '',
|
||||
FILTER_VALIDATE_IP
|
||||
) ?: ($_SERVER['REMOTE_ADDR'] ?? '');
|
||||
$ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 500);
|
||||
$pdo = $db->getConnection();
|
||||
$stmt = $pdo->prepare(
|
||||
"UPDATE turnero_consentimientos
|
||||
SET estado='firmado', firmado_at=NOW(), firma_svg=?,
|
||||
ip_firma=?, ua_firma=?, version_formulario=?
|
||||
WHERE token=?"
|
||||
);
|
||||
$stmt->execute([$firmaSvg, $ip, $ua, (int)$tcRow['formulario_id'], $tokenTurnero]);
|
||||
require_once __DIR__ . '/modules/turnero/api/_helpers.php';
|
||||
notificarSSE((int)$tcRow['sesion_id']);
|
||||
echo json_encode(['ok' => true, 'message' => 'Consentimiento firmado correctamente']); exit;
|
||||
}
|
||||
|
||||
// ── GET: construir $envio compatible con la plantilla ─────────────
|
||||
$envio = [
|
||||
'id' => (int)$tcRow['id'],
|
||||
'estado' => $tcRow['estado'],
|
||||
'form_nombre' => $tcRow['form_nombre'],
|
||||
'form_descripcion' => $tcRow['form_descripcion'],
|
||||
'categoria' => $tcRow['categoria'],
|
||||
'esquema' => $tcRow['esquema'],
|
||||
'firma_svg' => $tcRow['firma_svg'],
|
||||
'doc_color' => $tcRow['doc_color'],
|
||||
'doc_encabezado' => $tcRow['doc_encabezado'],
|
||||
'doc_subtitulo' => $tcRow['doc_subtitulo'],
|
||||
'doc_logo_base64' => $tcRow['doc_logo_base64'],
|
||||
'doc_pie_pagina' => $tcRow['doc_pie_pagina'],
|
||||
'paciente_nombre' => $tcRow['paciente_nombre'],
|
||||
'numero_documento' => $tcRow['numero_documento'],
|
||||
'tipo_documento' => $tcRow['tipo_documento'],
|
||||
'fecha_nacimiento' => $tcRow['fecha_nacimiento'],
|
||||
'paciente_telefono' => $tcRow['paciente_telefono'],
|
||||
'eps' => $tcRow['eps'],
|
||||
'enviado_por_nombre'=> 'Sistema Turnero',
|
||||
'enviado_por_email' => null,
|
||||
'datos_cliente' => '{}',
|
||||
'datos_prefilled' => json_encode(['__paciente' => [
|
||||
'nombre_completo' => $tcRow['paciente_nombre'] ?? '',
|
||||
'numero_documento' => $tcRow['numero_documento'] ?? '',
|
||||
'tipo_documento' => $tcRow['tipo_documento'] ?? '',
|
||||
'telefono' => $tcRow['paciente_telefono'] ?? '',
|
||||
'eps' => $tcRow['eps'] ?? '',
|
||||
]]),
|
||||
'created_at' => $tcRow['enviado_at'],
|
||||
'completado_en' => $tcRow['firmado_at'],
|
||||
'ip_cliente' => $tcRow['ip_firma'],
|
||||
'hash_verificacion' => null,
|
||||
'permite_firma' => 1,
|
||||
'requiere_firma' => 1,
|
||||
];
|
||||
} elseif ($modoPublico) {
|
||||
// Acceso por token: solo si el formulario ya fue firmado/completado
|
||||
if (!preg_match('/^[a-f0-9]{64}$/i', $tokenPublico)) {
|
||||
http_response_code(400); die('Token inválido');
|
||||
@@ -217,15 +312,30 @@ function esc2(mixed $v): string {
|
||||
|
||||
<!-- ── Barra de acciones ──────────────────────────────────────────── -->
|
||||
<div class="action-bar no-print">
|
||||
<?php if (!$modoPublico): ?>
|
||||
<a href="lab_formularios.php#envios"><i class="fas fa-arrow-left me-1"></i>Volver</a>
|
||||
<?php else: ?>
|
||||
<span style="font-size:13px;opacity:.85"><i class="fas fa-file-alt me-1"></i>Documento firmado</span>
|
||||
<?php if ($modoTurnero): ?>
|
||||
<span style="font-size:13px;opacity:.85">
|
||||
<i class="fas fa-file-signature me-1"></i>
|
||||
<?= $envio['estado'] === 'firmado' ? 'Consentimiento firmado ✓' : 'Lea el documento y firme al final' ?>
|
||||
</span>
|
||||
<div style="flex:1"></div>
|
||||
<?php if ($envio['estado'] === 'firmado'): ?>
|
||||
<button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
|
||||
<i class="fas fa-file-pdf me-2"></i>Imprimir
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php elseif (!$modoPublico): ?>
|
||||
<a href="lab_formularios.php#envios"><i class="fas fa-arrow-left me-1"></i>Volver</a>
|
||||
<div style="flex:1"></div>
|
||||
<button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
|
||||
<i class="fas fa-file-pdf me-2"></i>Descargar / Imprimir PDF
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<span style="font-size:13px;opacity:.85"><i class="fas fa-file-alt me-1"></i>Documento firmado</span>
|
||||
<div style="flex:1"></div>
|
||||
<button onclick="window.print()" class="btn btn-warning btn-sm fw-semibold">
|
||||
<i class="fas fa-file-pdf me-2"></i>Descargar / Imprimir PDF
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- ── Documento ─────────────────────────────────────────────────── -->
|
||||
@@ -466,6 +576,40 @@ function esc2(mixed $v): string {
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ── Widget firma turnero (solo modo consentimiento) ───────── -->
|
||||
<?php if ($modoTurnero && $envio['estado'] !== 'firmado'): ?>
|
||||
<div class="mt-4 no-print" id="turneroFirmaWidget">
|
||||
<div class="section-title" style="color:#1565c0">
|
||||
<i class="fas fa-pen me-1"></i>Firma del paciente / responsable
|
||||
</div>
|
||||
<p class="text-muted small mb-2">
|
||||
He leído y comprendido el contenido de este documento.
|
||||
Por favor dibuje su firma en el recuadro:
|
||||
</p>
|
||||
<canvas id="turneroCv" width="500" height="160"
|
||||
style="border:2px solid #1565c0;border-radius:8px;background:#f0f4ff;
|
||||
cursor:crosshair;display:block;max-width:100%;touch-action:none">
|
||||
</canvas>
|
||||
<div class="mt-2 d-flex gap-2 flex-wrap">
|
||||
<button id="turneroLimpiar" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="fas fa-eraser me-1"></i>Limpiar
|
||||
</button>
|
||||
<button id="turneroFirmar" class="btn btn-success fw-semibold">
|
||||
<i class="fas fa-check-circle me-1"></i>Confirmar y firmar consentimiento
|
||||
</button>
|
||||
</div>
|
||||
<div id="turneroMsg" class="mt-2 small"></div>
|
||||
</div>
|
||||
<?php elseif ($modoTurnero && $envio['estado'] === 'firmado'): ?>
|
||||
<div class="alert alert-success mt-4 d-flex align-items-center gap-2 no-print">
|
||||
<i class="fas fa-check-circle fs-4"></i>
|
||||
<div>
|
||||
<strong>Consentimiento firmado</strong><br>
|
||||
Puede cerrar esta ventana. El personal de salud ha sido notificado.
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- /doc-body -->
|
||||
|
||||
<div class="doc-footer">
|
||||
@@ -575,6 +719,91 @@ function esc2(mixed $v): string {
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
/* ── Firma de consentimiento turnero ────────────────────────────────── */
|
||||
(function () {
|
||||
var widget = document.getElementById('turneroFirmaWidget');
|
||||
if (!widget) return;
|
||||
|
||||
var canvas = document.getElementById('turneroCv');
|
||||
var ctx = canvas.getContext('2d');
|
||||
var btnLimp = document.getElementById('turneroLimpiar');
|
||||
var btnFirm = document.getElementById('turneroFirmar');
|
||||
var msgEl = document.getElementById('turneroMsg');
|
||||
var drawing = false;
|
||||
|
||||
(function scaleCanvas() {
|
||||
var ratio = window.devicePixelRatio || 1;
|
||||
var w = canvas.offsetWidth || canvas.width;
|
||||
var h = canvas.offsetHeight || canvas.height;
|
||||
canvas.width = w * ratio;
|
||||
canvas.height = h * ratio;
|
||||
canvas.style.width = w + 'px';
|
||||
canvas.style.height = h + 'px';
|
||||
ctx.scale(ratio, ratio);
|
||||
ctx.strokeStyle = '#1565c0';
|
||||
ctx.lineWidth = 2.5;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.lineJoin = 'round';
|
||||
})();
|
||||
|
||||
function getPos(e) {
|
||||
var r = canvas.getBoundingClientRect();
|
||||
var src = e.touches ? e.touches[0] : e;
|
||||
return { x: src.clientX - r.left, y: src.clientY - r.top };
|
||||
}
|
||||
|
||||
canvas.addEventListener('mousedown', function(e){ drawing=true; ctx.beginPath(); var p=getPos(e); ctx.moveTo(p.x,p.y); });
|
||||
canvas.addEventListener('mousemove', function(e){ if(!drawing) return; var p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); });
|
||||
canvas.addEventListener('mouseup', function(){ drawing=false; });
|
||||
canvas.addEventListener('mouseleave', function(){ drawing=false; });
|
||||
canvas.addEventListener('touchstart', function(e){ e.preventDefault(); drawing=true; ctx.beginPath(); var p=getPos(e); ctx.moveTo(p.x,p.y); }, {passive:false});
|
||||
canvas.addEventListener('touchmove', function(e){ e.preventDefault(); if(!drawing) return; var p=getPos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); }, {passive:false});
|
||||
canvas.addEventListener('touchend', function(){ drawing=false; });
|
||||
|
||||
btnLimp.addEventListener('click', function() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
msgEl.textContent = '';
|
||||
});
|
||||
|
||||
btnFirm.addEventListener('click', function() {
|
||||
var png = canvas.toDataURL('image/png');
|
||||
if (png.length < 1500) {
|
||||
msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-triangle me-1"></i>Por favor dibuje su firma antes de confirmar.</span>';
|
||||
return;
|
||||
}
|
||||
btnFirm.disabled = true;
|
||||
btnFirm.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
|
||||
msgEl.textContent = '';
|
||||
|
||||
fetch(window.location.href, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ firma_svg: png })
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.ok) {
|
||||
widget.outerHTML =
|
||||
'<div class="alert alert-success mt-4 d-flex align-items-center gap-2 no-print">' +
|
||||
'<i class="fas fa-check-circle fs-4"></i>' +
|
||||
'<div><strong>Consentimiento firmado correctamente.</strong><br>' +
|
||||
'Puede cerrar esta ventana.</div></div>';
|
||||
// Notificar ventana padre si está en iframe/modal
|
||||
try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
|
||||
} else {
|
||||
msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>' + (data.error || 'Error al guardar') + '</span>';
|
||||
btnFirm.disabled = false;
|
||||
btnFirm.innerHTML = '<i class="fas fa-check-circle me-1"></i>Confirmar y firmar consentimiento';
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
msgEl.innerHTML = '<span class="text-danger"><i class="fas fa-times me-1"></i>Error de conexión.</span>';
|
||||
btnFirm.disabled = false;
|
||||
btnFirm.innerHTML = '<i class="fas fa-check-circle me-1"></i>Confirmar y firmar consentimiento';
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user