recepcion: fix solo_muestras, reorder fields, duplicar visita, kiosko label
- create_solicitud.php: evitar SQL `IN ()` vacío cuando solo_muestras=1 sin exámenes - recepcion.php: mover embarazada+médico después del historial del paciente - recepcion.php: actualizar label kiosko con nombre real al vincular paciente - recepcion.php: botón "Duplicar" en historial para copiar exámenes y pago de última visita - recepcion.php: omitir validación de consentimientos pendientes cuando es solo_muestras - recepcion.php: agregar alias esc() = escHtml() usado en buscarMedico Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
d3826972ad
commit
c511da18bd
@@ -94,13 +94,15 @@ try {
|
||||
jsonError('Lugar destino no encontrado o inactivo.', 404);
|
||||
}
|
||||
|
||||
// Verificar que todos los exam_tipo_ids existen
|
||||
$in = implode(',', array_fill(0, count($examIds), '?'));
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM exam_tipos WHERE id IN ({$in}) AND activo = 1");
|
||||
$stmt->execute($examIds);
|
||||
if ((int)$stmt->fetchColumn() !== count($examIds)) {
|
||||
$pdo->rollBack();
|
||||
jsonError('Uno o más exámenes no existen o están inactivos.', 422);
|
||||
// Verificar que todos los exam_tipo_ids existen (se omite si solo_muestras y sin exámenes)
|
||||
if (!empty($examIds)) {
|
||||
$in = implode(',', array_fill(0, count($examIds), '?'));
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM exam_tipos WHERE id IN ({$in}) AND activo = 1");
|
||||
$stmt->execute($examIds);
|
||||
if ((int)$stmt->fetchColumn() !== count($examIds)) {
|
||||
$pdo->rollBack();
|
||||
jsonError('Uno o más exámenes no existen o están inactivos.', 422);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Insertar o reemplazar solicitud (DELETE + INSERT para poder re-guardar) ──
|
||||
|
||||
@@ -404,7 +404,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
<!-- Si el kiosko capturó un nombre -->
|
||||
<div id="bloque-nombre-kiosko" class="d-none mb-2">
|
||||
<small class="text-muted">Nombre del kiosko:</small>
|
||||
<small class="text-muted" id="lbl-kiosko-label">Kiosko:</small>
|
||||
<div class="fw-semibold" id="lbl-nombre-kiosko"></div>
|
||||
</div>
|
||||
|
||||
@@ -438,6 +438,29 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Historial colapsable del paciente -->
|
||||
<div id="sec-historial-pac" class="d-none">
|
||||
<button class="hist-toggle-btn" id="btn-hist-toggle" onclick="toggleHistorialPaciente()">
|
||||
<i class="fas fa-history" style="color:#6366f1"></i>
|
||||
Historial del paciente
|
||||
<span id="btn-duplicar-wrap" class="ms-auto d-none">
|
||||
<span class="btn btn-outline-indigo btn-sm py-0 px-2"
|
||||
style="font-size:.7rem;border-color:#6366f1;color:#6366f1;background:#fff;border-radius:6px"
|
||||
onmousedown="event.stopPropagation();duplicarUltimaVisita()">
|
||||
<i class="fas fa-copy me-1"></i>Duplicar
|
||||
</span>
|
||||
</span>
|
||||
<i class="fas fa-chevron-down hist-chev"></i>
|
||||
</button>
|
||||
<div id="hist-pac-body" class="hist-pac-body d-none">
|
||||
<div id="hist-pac-content" class="pac-timeline">
|
||||
<div class="text-center text-muted py-2 small">
|
||||
<i class="fas fa-spinner fa-spin me-1"></i>Cargando…
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Embarazo -->
|
||||
<div class="mt-2">
|
||||
<div class="form-check">
|
||||
@@ -468,22 +491,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
<input type="hidden" id="inp-medico-id">
|
||||
</div>
|
||||
|
||||
<!-- Historial colapsable del paciente -->
|
||||
<div id="sec-historial-pac" class="d-none">
|
||||
<button class="hist-toggle-btn" id="btn-hist-toggle" onclick="toggleHistorialPaciente()">
|
||||
<i class="fas fa-history" style="color:#6366f1"></i>
|
||||
Historial del paciente
|
||||
<i class="fas fa-chevron-down hist-chev"></i>
|
||||
</button>
|
||||
<div id="hist-pac-body" class="hist-pac-body d-none">
|
||||
<div id="hist-pac-content" class="pac-timeline">
|
||||
<div class="text-center text-muted py-2 small">
|
||||
<i class="fas fa-spinner fa-spin me-1"></i>Cargando…
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1038,10 +1045,11 @@ function abrirFicha(turno) {
|
||||
document.getElementById('badge-turno-activo').classList.remove('d-none');
|
||||
document.getElementById('badge-codigo').textContent = turno.codigo;
|
||||
|
||||
// Nombre del kiosko
|
||||
// Nombre del kiosko (valor capturado en kiosko; se actualiza al vincular paciente)
|
||||
if (turno.paciente_nombre) {
|
||||
document.getElementById('bloque-nombre-kiosko').classList.remove('d-none');
|
||||
document.getElementById('lbl-nombre-kiosko').textContent = turno.paciente_nombre;
|
||||
document.getElementById('lbl-kiosko-label').textContent = 'Kiosko:';
|
||||
document.getElementById('inp-buscar-pac').value = turno.paciente_nombre;
|
||||
}
|
||||
|
||||
@@ -1154,13 +1162,19 @@ function seleccionarPaciente(pac) {
|
||||
}
|
||||
document.getElementById('bloque-pac-no-vinculado').classList.add('d-none');
|
||||
document.getElementById('bloque-pac-seleccionado').classList.remove('d-none');
|
||||
document.getElementById('lbl-pac-nombre').textContent =
|
||||
(pac.full_name || (pac.nombre||'') + ' ' + (pac.apellido||'')).trim();
|
||||
const pacNombre = (pac.full_name || pac.nombre_completo || (pac.nombre||'') + ' ' + (pac.apellido||'')).trim();
|
||||
document.getElementById('lbl-pac-nombre').textContent = pacNombre;
|
||||
document.getElementById('lbl-pac-doc').textContent =
|
||||
(pac.tipo_documento||'') + ' ' + (pac.documento||pac.numero_documento||'');
|
||||
document.getElementById('lbl-pac-cel').textContent =
|
||||
pac.telefono || pac.celular || '';
|
||||
document.getElementById('lista-pacientes-res').innerHTML = '';
|
||||
// Actualizar etiqueta kiosko con el nombre real del paciente vinculado
|
||||
const lblKiosko = document.getElementById('lbl-nombre-kiosko');
|
||||
if (lblKiosko && document.getElementById('bloque-nombre-kiosko').classList.contains('d-none') === false) {
|
||||
lblKiosko.textContent = pacNombre;
|
||||
document.getElementById('lbl-kiosko-label').textContent = 'Cliente:';
|
||||
}
|
||||
|
||||
// Mostrar historial del paciente
|
||||
const secHist = document.getElementById('sec-historial-pac');
|
||||
@@ -1168,6 +1182,7 @@ function seleccionarPaciente(pac) {
|
||||
// Resetear estado colapsado
|
||||
document.getElementById('hist-pac-body').classList.add('d-none');
|
||||
document.getElementById('btn-hist-toggle').classList.remove('open');
|
||||
document.getElementById('btn-duplicar-wrap').classList.remove('d-none');
|
||||
// Precargar en background (usuario decide si abre)
|
||||
_historialPacienteCargado = false;
|
||||
_historialPacienteId = pac.id;
|
||||
@@ -1203,6 +1218,7 @@ function desvincularPaciente() {
|
||||
document.getElementById('sec-historial-pac').classList.add('d-none');
|
||||
document.getElementById('hist-pac-body').classList.add('d-none');
|
||||
document.getElementById('btn-hist-toggle').classList.remove('open');
|
||||
document.getElementById('btn-duplicar-wrap').classList.add('d-none');
|
||||
document.getElementById('lista-pacientes-res').innerHTML = '';
|
||||
document.getElementById('inp-buscar-pac').value = '';
|
||||
document.getElementById('chk-embarazada').checked = false;
|
||||
@@ -1314,14 +1330,16 @@ async function guardarSolicitud() {
|
||||
const lugarId = parseInt(document.getElementById('sel-lugar').value);
|
||||
if (!lugarId) { mostrarError('Seleccione el lugar destino.'); return; }
|
||||
|
||||
// ✅ VALIDACIÓN: verificar que los consentimientos del lugar estén firmados
|
||||
const consentPendientes = Array.from(document.querySelectorAll('.consent-item.pendiente, .consent-item.enviado, .consent-item.visto')).length;
|
||||
if (consentPendientes > 0) {
|
||||
mostrarError('⚠️ El paciente debe firmar los consentimientos del lugar antes de guardar la solicitud.');
|
||||
return;
|
||||
}
|
||||
|
||||
const soloMuestras = document.getElementById('chk-solo-muestras').checked;
|
||||
|
||||
// ✅ VALIDACIÓN: verificar que los consentimientos del lugar estén firmados (omitir en solo muestras)
|
||||
if (!soloMuestras) {
|
||||
const consentPendientes = Array.from(document.querySelectorAll('.consent-item.pendiente, .consent-item.enviado, .consent-item.visto')).length;
|
||||
if (consentPendientes > 0) {
|
||||
mostrarError('⚠️ El paciente debe firmar los consentimientos del lugar antes de guardar la solicitud.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const examIds = Array.from(document.querySelectorAll('.exam-chk:checked')).map(c => parseInt(c.value));
|
||||
if (!soloMuestras && !examIds.length) { mostrarError('Seleccione al menos un examen.'); return; }
|
||||
|
||||
@@ -1739,6 +1757,45 @@ function resetMedico() {
|
||||
quitarMedico();
|
||||
}
|
||||
|
||||
async function duplicarUltimaVisita() {
|
||||
if (!_historialPacienteId) { mostrarError('No hay paciente seleccionado.'); return; }
|
||||
mostrarToast('Buscando última visita…', 'info', 1500);
|
||||
try {
|
||||
// 1. Obtener el turno más reciente del paciente
|
||||
const rH = await fetch(`${API}get_historial.php?paciente_id=${_historialPacienteId}&per_page=1&page=1`);
|
||||
const jH = await rH.json();
|
||||
if (!jH.ok || !jH.turnos?.length) { mostrarError('Sin visitas anteriores para duplicar.'); return; }
|
||||
const ultimoTurnoId = jH.turnos[0].id;
|
||||
|
||||
// 2. Obtener solicitud + exámenes de esa visita
|
||||
const rS = await fetch(`${API}get_consentimientos.php?turno_id=${ultimoTurnoId}&incluir_solicitud=1`);
|
||||
const jS = await rS.json();
|
||||
if (!jS.ok || !jS.examenes?.length) { mostrarError('La última visita no tiene exámenes registrados.'); return; }
|
||||
|
||||
// 3. Pre-marcar checkboxes con los exam_tipo_ids
|
||||
const ids = new Set(jS.examenes.map(e => String(e.id)));
|
||||
let marcados = 0;
|
||||
document.querySelectorAll('.exam-chk').forEach(c => {
|
||||
const match = ids.has(c.value);
|
||||
c.checked = match;
|
||||
if (match) marcados++;
|
||||
});
|
||||
|
||||
// 4. Pre-llenar pago si viene
|
||||
if (jS.solicitud?.metodo_pago) {
|
||||
document.getElementById('sel-pago').value = jS.solicitud.metodo_pago;
|
||||
togglePagoCombinado();
|
||||
}
|
||||
if (jS.solicitud?.total_cobrado) {
|
||||
document.getElementById('inp-total').value = jS.solicitud.total_cobrado;
|
||||
}
|
||||
|
||||
mostrarToast(`Duplicado: ${marcados} exámen${marcados !== 1 ? 'es' : ''} copiado${marcados !== 1 ? 's' : ''}.`, 'success', 3000);
|
||||
} catch (e) {
|
||||
mostrarError('Error al duplicar: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function escJs(s) { return String(s||'').replace(/\\/g,'\\\\').replace(/'/g,"\\'"); }
|
||||
|
||||
function toggleSoloMuestras() {
|
||||
@@ -1766,6 +1823,7 @@ function escHtml(str) {
|
||||
d.appendChild(document.createTextNode(String(str)));
|
||||
return d.innerHTML;
|
||||
}
|
||||
const esc = escHtml;
|
||||
|
||||
function resetPlaceholder() {
|
||||
const ph = document.getElementById('ficha-placeholder');
|
||||
|
||||
Reference in New Issue
Block a user