Estación de muestras: corregir la ficha del paciente desde el turno

Los campos del formulario F-LAB-08 son vinculados: leen de la ficha del
paciente. Si el dato está mal, no había forma de arreglarlo desde la estación
—el modal era de solo lectura— y corregirlo únicamente en el documento habría
dejado la ficha equivocada, reapareciendo en el próximo turno, orden o
consentimiento. Por eso se corrige la ficha y no el documento: el dato queda
bien en todo el sistema y el formulario, al ser automático, lo toma solo.

La edición tiene dos niveles de fricción. Teléfono, dirección y EPS se editan
directo: cambian seguido y el error es de bajo riesgo. Nombre, tipo y número
de documento y fecha de nacimiento quedan tras un botón aparte con
confirmación, porque identifican al paciente en toda su historia clínica.

Si el turno ya tiene documentos firmados se advierte que corregir la ficha no
los modifica y que hay que emitirlos de nuevo, para que nadie espere que un
PDF ya firmado se actualice solo.

Paciente::actualizar registraba en la bitácora solo el valor nuevo, lo que
impedía reconstruir el anterior si la corrección resultaba equivocada —justo
cuando hace falta consultarlo. Ahora guarda antes y después, y únicamente de
los campos que realmente cambiaron.

La validación del servidor ya cubría nombre, celular, documento y correo, y la
cédula tiene índice único, así que no hizo falta agregar validación.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-11 09:05:48 -05:00
co-authored by Claude Opus 5
parent fd46a86007
commit 57818b180a
2 changed files with 194 additions and 1 deletions
+15 -1
View File
@@ -173,8 +173,22 @@ class Paciente {
}
}
// Registrar antes y después de lo que realmente cambia. Guardar solo el
// valor nuevo impide reconstruir el dato anterior si la corrección
// resultó equivocada, que es justo cuando hace falta consultarlo.
$antes = $this->db->fetch('SELECT * FROM lab_pacientes WHERE id = ?', [$id]) ?: [];
$cambios = [];
foreach ($campos as $campo => $nuevo) {
$previo = $antes[$campo] ?? null;
if ((string)$previo !== (string)$nuevo) {
$cambios[$campo] = ['antes' => $previo, 'despues' => $nuevo];
}
}
$ok = $this->db->update('lab_pacientes', $campos, 'id = ?', [$id]);
$this->log->registrar($adminId, 'pacientes', 'editar', $id, $campos);
if ($cambios) {
$this->log->registrar($adminId, 'pacientes', 'editar', $id, $cambios);
}
return $ok > 0;
}
+179
View File
@@ -520,6 +520,15 @@ try {
}
/* Historial timeline */
.mpac-timeline { padding: 0; }
.mpac-lbl { font-size:.7rem; color:#64748b; font-weight:600; display:block; margin-bottom:2px; }
.mpac-aviso { font-size:.74rem; color:#1e40af; background:#eff6ff; border:1px solid #bfdbfe;
border-radius:8px; padding:7px 10px; margin-bottom:10px; }
.mpac-lock { background:#fffbeb; border:1px solid #fde68a; border-radius:8px;
padding:9px 11px; margin:4px 0 10px; }
.mpac-lock-txt { font-size:.72rem; color:#92400e; margin-bottom:7px; line-height:1.45; }
.mpac-aviso-firmados { font-size:.74rem; color:#7f1d1d; background:#fef2f2;
border:1px solid #fecaca; border-radius:8px;
padding:8px 11px; margin-bottom:10px; line-height:1.45; }
.mtl-item {
display:flex; gap:10px; align-items:flex-start;
padding-bottom:12px; position:relative;
@@ -949,6 +958,79 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php';
<div id="mpac-badge-emb" class="d-none mt-1">
<span class="badge text-bg-danger"><i class="fas fa-baby me-1"></i>Paciente embarazada</span>
</div>
<div class="mt-2">
<button class="btn btn-sm btn-outline-secondary" onclick="mpacEditar(true)"
id="mpac-btn-editar" style="font-size:.74rem">
<i class="fas fa-pen me-1"></i>Corregir datos
</button>
</div>
</div>
<!-- ── Edición de la ficha ── -->
<div id="mpac-form" class="d-none">
<div class="mpac-aviso">
<i class="fas fa-info-circle me-1"></i>
Lo que corrija aquí cambia la ficha del paciente <b>en todo el sistema</b>.
</div>
<label class="mpac-lbl">Celular</label>
<input type="text" class="form-control form-control-sm mb-2" id="mpac-in-tel" placeholder="3001234567">
<label class="mpac-lbl">Dirección</label>
<input type="text" class="form-control form-control-sm mb-2" id="mpac-in-dir">
<label class="mpac-lbl">EPS</label>
<input type="text" class="form-control form-control-sm mb-2" id="mpac-in-eps">
<!-- Datos de identidad: requieren un paso deliberado -->
<div id="mpac-identidad-lock" class="mpac-lock">
<div class="mpac-lock-txt">
<i class="fas fa-lock me-1"></i>
Nombre, documento y fecha de nacimiento identifican al paciente en toda su
historia clínica.
</div>
<button class="btn btn-sm btn-outline-warning w-100" onclick="mpacDesbloquearIdentidad()"
style="font-size:.74rem">
<i class="fas fa-unlock me-1"></i>Corregir también estos datos
</button>
</div>
<div id="mpac-identidad" class="d-none">
<label class="mpac-lbl">Nombre completo</label>
<input type="text" class="form-control form-control-sm mb-2" id="mpac-in-nombre">
<div class="d-flex gap-2 mb-2">
<div style="width:90px">
<label class="mpac-lbl">Tipo</label>
<select class="form-select form-select-sm" id="mpac-in-tipodoc">
<option>CC</option><option>TI</option><option>RC</option>
<option>CE</option><option>PA</option><option>NIT</option>
</select>
</div>
<div class="flex-grow-1">
<label class="mpac-lbl">Nº documento</label>
<input type="text" class="form-control form-control-sm" id="mpac-in-doc">
</div>
</div>
<label class="mpac-lbl">Fecha de nacimiento</label>
<input type="date" class="form-control form-control-sm mb-2" id="mpac-in-fnac">
</div>
<div id="mpac-aviso-firmados" class="mpac-aviso-firmados d-none">
<i class="fas fa-exclamation-triangle me-1"></i>
Este turno ya tiene documentos firmados. Corregir la ficha
<b>no modifica lo que ya se firmó</b>: si un documento salió con datos
equivocados, hay que emitirlo de nuevo.
</div>
<div id="mpac-msg" class="small mb-2"></div>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-primary flex-grow-1" onclick="mpacGuardarFicha()" id="mpac-btn-guardar">
<i class="fas fa-save me-1"></i>Guardar
</button>
<button class="btn btn-sm btn-outline-secondary" onclick="mpacEditar(false)">Cancelar</button>
</div>
</div>
<!-- Historial -->
<div class="mpac-sec-title"><i class="fas fa-history" style="color:#6366f1"></i>Historial de visitas</div>
@@ -2386,6 +2468,103 @@ function abrirModalPaciente() {
function cerrarModalPaciente() {
document.getElementById('modal-pac-lugar').classList.remove('show');
mpacEditar(false);
}
/* ── Corrección de la ficha del paciente desde la estación ──
Los campos del formulario son vinculados: leen de la ficha. Por eso se
corrige la ficha y no el documento — así el dato queda bien en el turnero,
los domicilios y las órdenes, no solo en la hoja que se está llenando. */
function mpacEditar(abrir) {
document.getElementById('mpac-form').classList.toggle('d-none', !abrir);
document.getElementById('mpac-datos').classList.toggle('d-none', abrir);
document.getElementById('mpac-msg').textContent = '';
if (!abrir) { mpacBloquearIdentidad(); return; }
const p = _pacienteActivo || {};
document.getElementById('mpac-in-tel').value = p.telefono || p.celular || '';
document.getElementById('mpac-in-dir').value = p.direccion || '';
document.getElementById('mpac-in-eps').value = p.eps || '';
document.getElementById('mpac-in-nombre').value = p.nombre_completo || '';
document.getElementById('mpac-in-tipodoc').value = p.tipo_documento || 'CC';
document.getElementById('mpac-in-doc').value = p.numero_documento || p.documento || '';
document.getElementById('mpac-in-fnac').value = (p.fecha_nacimiento || '').slice(0, 10);
mpacBloquearIdentidad();
// Si ya hay documentos firmados, advertir que corregir no los modifica.
// El botón "Ver firmado" solo se renderiza para consentimientos ya firmados.
const firmados = document.querySelectorAll('#lista-consent button[title="Ver firmado"]').length > 0;
document.getElementById('mpac-aviso-firmados').classList.toggle('d-none', !firmados);
}
function mpacBloquearIdentidad() {
document.getElementById('mpac-identidad').classList.add('d-none');
document.getElementById('mpac-identidad-lock').classList.remove('d-none');
}
function mpacDesbloquearIdentidad() {
if (!confirm('Nombre, documento y fecha de nacimiento identifican al paciente en toda su '
+ 'historia clínica.\n\n¿Confirma que quiere corregirlos?')) return;
document.getElementById('mpac-identidad').classList.remove('d-none');
document.getElementById('mpac-identidad-lock').classList.add('d-none');
}
async function mpacGuardarFicha() {
if (!_pacienteActivo?.id) return;
const msg = document.getElementById('mpac-msg');
const btn = document.getElementById('mpac-btn-guardar');
const val = id => document.getElementById(id).value.trim();
const cuerpo = {
id: _pacienteActivo.id,
telefono: val('mpac-in-tel'),
direccion: val('mpac-in-dir'),
eps: val('mpac-in-eps'),
};
// Los datos de identidad solo se envían si se desbloquearon
if (!document.getElementById('mpac-identidad').classList.contains('d-none')) {
cuerpo.nombre_completo = val('mpac-in-nombre');
cuerpo.tipo_documento = val('mpac-in-tipodoc');
cuerpo.numero_documento = val('mpac-in-doc');
cuerpo.fecha_nacimiento = val('mpac-in-fnac') || null;
}
btn.disabled = true;
msg.className = 'small mb-2 text-muted';
msg.textContent = 'Guardando…';
try {
const r = await fetch(`${BASE_WA}api/lab/save_paciente.php`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(cuerpo),
});
const d = await r.json();
if (!d.ok) {
msg.className = 'small mb-2 text-danger';
msg.textContent = d.error || 'No se pudo guardar.';
btn.disabled = false;
return;
}
// Reflejar el cambio sin recargar: ficha del modal, tarjeta del turno
// y el formulario abierto, cuyos campos vinculados leen de la ficha.
Object.assign(_pacienteActivo, cuerpo);
msg.className = 'small mb-2 text-success';
msg.textContent = 'Ficha actualizada.';
setTimeout(() => {
mpacEditar(false);
abrirModalPaciente();
if (turnoActivo?.id) actualizarConsentimientos(turnoActivo.id);
const marco = document.querySelector('#modal-consent iframe');
if (marco && marco.src) marco.src = marco.src;
}, 700);
} catch (_) {
msg.className = 'small mb-2 text-danger';
msg.textContent = 'Error de conexión.';
btn.disabled = false;
}
}
async function cargarHistorialModal(pacienteId) {