From f4da864db7522e0c308d64981b358fc9642ba024 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 8 Jul 2026 21:16:07 -0500 Subject: [PATCH] feat(turnero): toma prolongada secuencial + bloqueo muestras/cierre MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ver_formulario_enviado: auto-fill médico (nombres/especialidad/código) en datos_prefilled desde solicitud - ver_formulario_enviado: canvases de toma progresiva bloqueados secuencialmente — solo el turno activo es firmable, los siguientes aparecen al expirar el countdown - ver_formulario_enviado: countdown prominente con reloj grande, color dinámico, sonido (Web Audio) y vibración al llegar la hora - lugar: bloquear cierre del modal durante toma progresiva activa (botón X deshabilitado hasta mp_completar) - lugar: bloquear Finalizar atención si hay muestras pendientes sin decisión (recibida/rechazada) - lugar: _syncBtnFinalizar combina consentimientos + muestras para el aviso y estado del botón - lugar: restricción de usuario a lugar específico via turnero_lugar_id (admin_users) + bloqueo JS en confirmarLugar() - migrations: turnero_lugar_id en admin_users para restringir bacteriólogos por usuario Co-Authored-By: Claude Sonnet 4.6 --- api/lab/save_domicilio.php | 13 +++ classes/lab/Domicilio.php | 2 +- lab_domicilios.php | 8 ++ .../20260703_numero_orden_domicilio.sql | 7 ++ .../20260708_admin_users_turnero_lugar.sql | 6 ++ modules/turnero/api/get_consecutivo.php | 39 +++++--- modules/turnero/views/lugar.php | 99 ++++++++++++++----- ver_formulario_enviado.php | 89 +++++++++++++---- 8 files changed, 208 insertions(+), 55 deletions(-) create mode 100644 migrations/20260703_numero_orden_domicilio.sql create mode 100644 migrations/20260708_admin_users_turnero_lugar.sql diff --git a/api/lab/save_domicilio.php b/api/lab/save_domicilio.php index 9bb55ee..7344e41 100644 --- a/api/lab/save_domicilio.php +++ b/api/lab/save_domicilio.php @@ -91,6 +91,19 @@ try { $eid = enfermeraId(); if ($eid) $datos['enfermera_id'] = $eid; } + // Asignar número de orden D-YYYYMMDD-NNN si no viene uno + if (empty($datos['numero_orden'])) { + $pdo = db(); + $prefix = 'D-' . date('Ymd') . '-'; + $st = $pdo->prepare( + "SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden, '-', -1) AS UNSIGNED)) AS ultimo + FROM lab_domicilios WHERE numero_orden LIKE ?" + ); + $st->execute([$prefix . '%']); + $ultimo = (int)($st->fetch(\PDO::FETCH_ASSOC)['ultimo'] ?? 0); + $datos['numero_orden'] = $prefix . str_pad($ultimo + 1, 3, '0', STR_PAD_LEFT); + } + $id = $dom->crear($datos, $admin); // Crear asignación automática si se indicó enfermera_id diff --git a/classes/lab/Domicilio.php b/classes/lab/Domicilio.php index b13e6a8..4a03c77 100644 --- a/classes/lab/Domicilio.php +++ b/classes/lab/Domicilio.php @@ -364,7 +364,7 @@ class Domicilio { private function filtrarCampos(array $datos): array { $permitidos = [ - 'orden_id', 'paciente_id', 'direccion', 'ciudad', 'barrio', + 'orden_id', 'paciente_id', 'numero_orden', 'direccion', 'ciudad', 'barrio', 'indicaciones_dir', 'fecha_programada', 'hora_programada', 'tipo_servicio', 'tipo_cliente', 'examenes_solicitados', 'estado', 'motivo_cancelacion', diff --git a/lab_domicilios.php b/lab_domicilios.php index b87fcb3..ddfb3c0 100644 --- a/lab_domicilios.php +++ b/lab_domicilios.php @@ -681,6 +681,7 @@ async function cargarLista(pag = 1) {
${esc(dom.paciente_nombre)}
${esc(dom.barrio||'')} + ${dom.numero_orden ? `
#${esc(dom.numero_orden)}
` : ''} ${dom.enfermera_nombre ? `${esc(dom.enfermera_nombre)}` : 'Sin asignar'} @@ -799,6 +800,13 @@ async function verDomicilio(id) {
+ ${dom.numero_orden ? `
+
+ + ${esc(dom.numero_orden)} + +
` : ''}

${esc(dom.estado)} diff --git a/migrations/20260703_numero_orden_domicilio.sql b/migrations/20260703_numero_orden_domicilio.sql new file mode 100644 index 0000000..feb40c8 --- /dev/null +++ b/migrations/20260703_numero_orden_domicilio.sql @@ -0,0 +1,7 @@ +-- Agrega número de orden visible a domicilios (formato D-YYYYMMDD-NNN) +ALTER TABLE lab_domicilios + ADD COLUMN numero_orden VARCHAR(20) NULL DEFAULT NULL + COMMENT 'Número de orden del domicilio. Formato D-YYYYMMDD-NNN' + AFTER paciente_id; + +CREATE INDEX idx_lab_domicilios_numero_orden ON lab_domicilios (numero_orden); diff --git a/migrations/20260708_admin_users_turnero_lugar.sql b/migrations/20260708_admin_users_turnero_lugar.sql new file mode 100644 index 0000000..e8f2bc8 --- /dev/null +++ b/migrations/20260708_admin_users_turnero_lugar.sql @@ -0,0 +1,6 @@ +-- Restricción de usuario a un lugar específico del turnero. +-- NULL = acceso libre (admin, recepcionista, etc.) +-- Valor = solo puede operar en ese lugar (ej. bacteriólogo asignado a Toma 1) +ALTER TABLE admin_users + ADD COLUMN IF NOT EXISTS turnero_lugar_id INT UNSIGNED DEFAULT NULL + COMMENT 'FK turnero_lugares.id — si != NULL el usuario solo opera en ese lugar'; diff --git a/modules/turnero/api/get_consecutivo.php b/modules/turnero/api/get_consecutivo.php index c3abeb6..55c43f1 100644 --- a/modules/turnero/api/get_consecutivo.php +++ b/modules/turnero/api/get_consecutivo.php @@ -1,23 +1,40 @@ prepare( - "SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden,'-',-1) AS UNSIGNED)) AS ultimo - FROM turnero_solicitudes - WHERE numero_orden LIKE ?" -); -$row->execute([$prefix . '%']); -$ultimo = (int)($row->fetch(PDO::FETCH_ASSOC)['ultimo'] ?? 0); +$pdo = db(); +$prefix = $tipo . '-' . date('Ymd') . '-'; + +if ($tipo === 'D') { + $stmt = $pdo->prepare( + "SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden, '-', -1) AS UNSIGNED)) AS ultimo + FROM lab_domicilios + WHERE numero_orden LIKE ?" + ); +} else { + $stmt = $pdo->prepare( + "SELECT MAX(CAST(SUBSTRING_INDEX(numero_orden, '-', -1) AS UNSIGNED)) AS ultimo + FROM turnero_solicitudes + WHERE numero_orden LIKE ?" + ); +} +$stmt->execute([$prefix . '%']); +$ultimo = (int)($stmt->fetch(PDO::FETCH_ASSOC)['ultimo'] ?? 0); $siguiente = $prefix . str_pad($ultimo + 1, 3, '0', STR_PAD_LEFT); - jsonOk(['consecutivo' => $siguiente]); diff --git a/modules/turnero/views/lugar.php b/modules/turnero/views/lugar.php index b773f91..4e78851 100644 --- a/modules/turnero/views/lugar.php +++ b/modules/turnero/views/lugar.php @@ -10,6 +10,7 @@ if (!isUserLoggedIn()) { } // Tablet asignada → forzar su lugar, bloquear cualquier otro +$lugarForzado = 0; if (!empty($_SESSION['turnero_dispositivo'])) { $_d = $_SESSION['turnero_dispositivo']; $_forzado = (int)$_d['lugar_id']; @@ -19,6 +20,17 @@ if (!empty($_SESSION['turnero_dispositivo'])) { if ((int)($_GET['lugar_id'] ?? 0) !== $_forzado) { header('Location: ' . BASE_URL . 'erp.php?m=turnero&v=lugar&lugar_id=' . $_forzado); exit; } + $lugarForzado = $_forzado; +} +// Restricción a nivel de usuario (turnero_lugar_id en admin_users) +if (!$lugarForzado) { + $_userLugar = (int)($_SESSION['admin_user']['turnero_lugar_id'] ?? 0); + if ($_userLugar) { + if ((int)($_GET['lugar_id'] ?? 0) !== $_userLugar) { + header('Location: ' . BASE_URL . 'erp.php?m=turnero&v=lugar&lugar_id=' . $_userLugar); exit; + } + $lugarForzado = $_userLugar; + } } try { @@ -495,9 +507,11 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php'; onclick="abrirModalMiFirma()" title=""> + +
@@ -751,8 +765,9 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php'; Formulario de consentimiento - @@ -769,6 +784,7 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php'; +
+ data-formulario="" + >