feat: cierre anticipado de tomas prolongadas con justificación
- DB: añade estado 'cerrado_anticipado' al enum de turnero_consentimientos - ver_formulario_enviado.php: POST handler mp_cerrar_anticipado guarda motivo en _cierre_anticipado y cambia estado; botón "Cerrar anticipadamente" con modal textarea; bloque display para estado cerrado_anticipado mostrando el motivo; modoEditar y check de estado actualizados para excluir cerrado_anticipado - get_consentimientos.php: expone cierre_anticipado en respuesta; mueve parse de datos_respuestas fuera del if(esTomaProg) para ambos loops - lugar.php: CONSENT_IC/LBL incluye cerrado_anticipado; ya[] lo incluye; hayPendientes lo excluye; fila muestra motivo en banner amarillo - recepcion.php: CONSENT_META incluye cerrado_anticipado; ya[] lo incluye; motivo visible en la tarjeta del consentimiento Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
915fa3ba5c
commit
c9ea76f782
+103
-3
@@ -56,10 +56,23 @@ if ($modoTurnero) {
|
||||
// ── 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;
|
||||
if (in_array($tcRow['estado'], ['firmado', 'cerrado_anticipado'])) {
|
||||
echo json_encode(['ok' => false, 'error' => 'Este consentimiento ya fue finalizado']); exit;
|
||||
}
|
||||
$input = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
// Cierre anticipado de toma progresiva con justificación
|
||||
if (isset($input['mp_cerrar_anticipado'])) {
|
||||
$motivo = trim($input['motivo'] ?? '');
|
||||
if (!$motivo) { echo json_encode(['ok' => false, 'error' => 'Motivo requerido']); exit; }
|
||||
$existing = json_decode($tcRow['datos_respuestas'] ?? '{}', true) ?: [];
|
||||
$existing['_cierre_anticipado'] = ['motivo' => $motivo, 'cerrado_at' => date('Y-m-d H:i:s')];
|
||||
$db->getConnection()
|
||||
->prepare("UPDATE turnero_consentimientos SET estado='cerrado_anticipado', firmado_at=NOW(), datos_respuestas=? WHERE token=?")
|
||||
->execute([json_encode($existing, JSON_UNESCAPED_UNICODE), $tokenTurnero]);
|
||||
require_once __DIR__ . '/modules/turnero/api/_helpers.php';
|
||||
notificarSSE((int)$tcRow['sesion_id']);
|
||||
echo json_encode(['ok' => true, 'mp_cerrado' => true]); exit;
|
||||
}
|
||||
// Borrador: guardar campos sin firma (auto-save desde el formulario)
|
||||
// Muestras Prolongadas: marcar completo sin SVG global
|
||||
if (isset($input['mp_completar'])) {
|
||||
@@ -226,7 +239,7 @@ $esquema = json_decode($envio['esquema'], true) ?? [];
|
||||
$datosCliente = json_decode($envio['datos_cliente'] ?? '{}', true) ?? [];
|
||||
$datosPrefilled = json_decode($envio['datos_prefilled'] ?? '{}', true) ?? [];
|
||||
$todos = array_merge($datosPrefilled, $datosCliente);
|
||||
$modoEditar = $modoTurnero && $envio['estado'] !== 'firmado';
|
||||
$modoEditar = $modoTurnero && !in_array($envio['estado'], ['firmado', 'cerrado_anticipado']);
|
||||
$embebido = isset($_GET['embed']) && $_GET['embed'] === '1';
|
||||
$compact = $embebido && isset($_GET['compact']);
|
||||
$autoprint = isset($_GET['autoprint']) && $_GET['autoprint'] === '1';
|
||||
@@ -1359,6 +1372,22 @@ function _addExamWizardHtml(string $cid): string {
|
||||
</div>
|
||||
<div class="turnero-msg mt-2 small"></div>
|
||||
</div>
|
||||
<?php elseif ($modoTurnero && $envio['estado'] === 'cerrado_anticipado'): ?>
|
||||
<?php $_ca = $datosCliente['_cierre_anticipado'] ?? null; ?>
|
||||
<div class="alert alert-warning mt-4 d-flex align-items-start gap-3 no-print" style="border-left:4px solid #f59e0b">
|
||||
<i class="fas fa-exclamation-triangle fs-4 flex-shrink-0 mt-1 text-warning"></i>
|
||||
<div>
|
||||
<strong>Toma cerrada anticipadamente</strong><br>
|
||||
<?php if (!empty($_ca['motivo'])): ?>
|
||||
<div class="mt-1 text-secondary" style="font-size:.9rem">
|
||||
<i class="fas fa-comment-alt me-1"></i><?= esc2($_ca['motivo']) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<button onclick="window.close()" class="btn btn-outline-secondary btn-sm fw-semibold mt-2">
|
||||
<i class="fas fa-times me-1"></i>Cerrar ventana
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif ($modoTurnero && $envio['estado'] === 'firmado'): ?>
|
||||
<div class="alert alert-success mt-4 d-flex align-items-start gap-3 no-print">
|
||||
<i class="fas fa-check-circle fs-4 flex-shrink-0 mt-1"></i>
|
||||
@@ -1377,6 +1406,29 @@ function _addExamWizardHtml(string $cid): string {
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($modoTurnero && $embebido && !empty($_mpMap) && !in_array($envio['estado'], ['firmado','cerrado_anticipado'])): ?>
|
||||
<div class="mt-3 no-print text-end">
|
||||
<button class="btn btn-outline-danger btn-sm" id="btn-cerrar-anticip">
|
||||
<i class="fas fa-times-circle me-1"></i>Cerrar anticipadamente
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal cierre anticipado -->
|
||||
<div id="modal-cerrar-anticip" class="d-none" style="position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.5);display:none;align-items:center;justify-content:center">
|
||||
<div style="background:#fff;border-radius:12px;padding:1.5rem;max-width:380px;width:94%;box-shadow:0 8px 32px rgba(0,0,0,.25)">
|
||||
<h6 class="fw-bold mb-2"><i class="fas fa-exclamation-triangle text-warning me-2"></i>Cerrar anticipadamente</h6>
|
||||
<p class="text-muted small mb-3">Explique por qué no se completaron todas las tomas. Este mensaje quedará visible en toma de muestras y recepción.</p>
|
||||
<textarea id="mca-motivo" rows="3" class="form-control mb-2" placeholder="Ej: El paciente no toleró más extracciones…"></textarea>
|
||||
<div id="mca-error" class="text-danger small mb-2 d-none"></div>
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
<button class="btn btn-outline-secondary btn-sm" id="mca-cancelar">Cancelar</button>
|
||||
<button class="btn btn-danger btn-sm fw-semibold" id="mca-confirmar">
|
||||
<i class="fas fa-times-circle me-1"></i>Confirmar cierre
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- /doc-body -->
|
||||
|
||||
<div class="doc-footer">
|
||||
@@ -2586,6 +2638,54 @@ document.querySelectorAll('.turnero-firma-item').forEach(function(widget) {
|
||||
});
|
||||
})();
|
||||
<?php endif; ?>
|
||||
<?php if ($modoTurnero && $embebido && !empty($_mpMap) && !in_array($envio['estado'], ['firmado','cerrado_anticipado'])): ?>
|
||||
(function() {
|
||||
var btn = document.getElementById('btn-cerrar-anticip');
|
||||
var modal = document.getElementById('modal-cerrar-anticip');
|
||||
if (!btn || !modal) return;
|
||||
btn.addEventListener('click', function() {
|
||||
document.getElementById('mca-motivo').value = '';
|
||||
document.getElementById('mca-error').classList.add('d-none');
|
||||
modal.style.display = 'flex';
|
||||
setTimeout(function() { document.getElementById('mca-motivo').focus(); }, 50);
|
||||
});
|
||||
document.getElementById('mca-cancelar').addEventListener('click', function() {
|
||||
modal.style.display = 'none';
|
||||
});
|
||||
document.getElementById('mca-confirmar').addEventListener('click', function() {
|
||||
var motivo = document.getElementById('mca-motivo').value.trim();
|
||||
var errEl = document.getElementById('mca-error');
|
||||
if (!motivo) { errEl.textContent = 'El motivo es obligatorio.'; errEl.classList.remove('d-none'); return; }
|
||||
errEl.classList.add('d-none');
|
||||
var confirmBtn = this;
|
||||
confirmBtn.disabled = true;
|
||||
confirmBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Guardando...';
|
||||
fetch(window.location.href, {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mp_cerrar_anticipado: true, motivo: motivo })
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (d.ok) {
|
||||
modal.style.display = 'none';
|
||||
try { window.parent.postMessage({ type: 'turneroFirmado' }, '*'); } catch(e) {}
|
||||
location.reload();
|
||||
} else {
|
||||
errEl.textContent = d.error || 'Error al guardar.';
|
||||
errEl.classList.remove('d-none');
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.innerHTML = '<i class="fas fa-times-circle me-1"></i>Confirmar cierre';
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
errEl.textContent = 'Error de conexión.';
|
||||
errEl.classList.remove('d-none');
|
||||
confirmBtn.disabled = false;
|
||||
confirmBtn.innerHTML = '<i class="fas fa-times-circle me-1"></i>Confirmar cierre';
|
||||
});
|
||||
});
|
||||
})();
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
|
||||
<!-- ── Topaz SigWeb overlay ───────────────────────────────────────── -->
|
||||
|
||||
Reference in New Issue
Block a user