diff --git a/migrations/20260616_turnero_lugar_grupo.sql b/migrations/20260616_turnero_lugar_grupo.sql new file mode 100644 index 0000000..7f13f57 --- /dev/null +++ b/migrations/20260616_turnero_lugar_grupo.sql @@ -0,0 +1,7 @@ +-- Agrupa lugares que comparten cola (ej: todas las estaciones de Toma de Muestras) +ALTER TABLE turnero_lugares + ADD COLUMN IF NOT EXISTS grupo_id INT NULL DEFAULT NULL + COMMENT 'Grupo de lugares que comparten cola. NULL = cola individual.'; + +-- Agrupar todos los puestos de Toma de Muestras en grupo 1 +UPDATE turnero_lugares SET grupo_id = 1 WHERE nombre LIKE 'Toma de Muestras%'; diff --git a/modules/turnero/api/_helpers.php b/modules/turnero/api/_helpers.php index 1c38e28..fbc39a2 100644 --- a/modules/turnero/api/_helpers.php +++ b/modules/turnero/api/_helpers.php @@ -193,27 +193,34 @@ function siguienteNumeroPorPrioridad(int $sesionId, int $prioridadId): int /** * Selecciona el siguiente turno a llamar según el motor de prioridades. * - * @param string $estado Estado que deben tener los turnos en cola - * @param int|null $lugarId Si no es null, filtra por lugar_destino_id - * @return array|null Fila de turnero_turnos o null si cola vacía + * @param string $estado Estado que deben tener los turnos en cola + * @param int|null $lugarId Si no es null, filtra por lugar_destino_id (o su grupo) + * @param array|null $lugarIds Lista explícita de lugar_ids (grupo completo) + * @return array|null Fila de turnero_turnos o null si cola vacía */ -function siguienteTurnoEnCola(string $estado, ?int $lugarId = null): ?array +function siguienteTurnoEnCola(string $estado, ?int $lugarId = null, ?array $lugarIds = null): ?array { $pdo = db(); - if ($lugarId !== null) { - $sql = ' + // Resolver grupo si se pasó un lugarId sin lista explícita + if ($lugarId !== null && $lugarIds === null) { + $lugarIds = lugarIdsDeGrupo($lugarId); + } + + if (!empty($lugarIds)) { + $in = implode(',', array_map('intval', $lugarIds)); + $sql = " SELECT t.*, p.orden_peso, p.codigo AS prioridad_codigo, p.color AS prioridad_color FROM turnero_turnos t JOIN turnero_prioridades p ON p.id = t.prioridad_id WHERE t.estado = ? - AND t.lugar_destino_id = ? + AND t.lugar_destino_id IN ($in) ORDER BY p.orden_peso ASC, t.creado_at ASC LIMIT 1 FOR UPDATE - '; + "; $stmt = $pdo->prepare($sql); - $stmt->execute([$estado, $lugarId]); + $stmt->execute([$estado]); } else { $sql = ' SELECT t.*, p.orden_peso, p.codigo AS prioridad_codigo, p.color AS prioridad_color @@ -232,6 +239,28 @@ function siguienteTurnoEnCola(string $estado, ?int $lugarId = null): ?array return $row ?: null; } +/** + * Devuelve todos los lugar_ids que comparten grupo con $lugarId. + * Si no tiene grupo, devuelve [$lugarId]. + */ +function lugarIdsDeGrupo(int $lugarId): array +{ + $pdo = db(); + $stmt = $pdo->prepare("SELECT grupo_id FROM turnero_lugares WHERE id = ?"); + $stmt->execute([$lugarId]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$row || $row['grupo_id'] === null) { + return [$lugarId]; + } + + $grupoId = (int)$row['grupo_id']; + $stmt2 = $pdo->prepare("SELECT id FROM turnero_lugares WHERE grupo_id = ? AND activo = 1"); + $stmt2->execute([$grupoId]); + $ids = $stmt2->fetchAll(PDO::FETCH_COLUMN); + return $ids ?: [$lugarId]; +} + /** * Emite un evento SSE a todos los clientes suscritos escribiendo * un flag en la tabla turnero_sesiones (campo sse_ping_at). diff --git a/modules/turnero/api/get_cola.php b/modules/turnero/api/get_cola.php index 3b6f523..e913e29 100644 --- a/modules/turnero/api/get_cola.php +++ b/modules/turnero/api/get_cola.php @@ -49,13 +49,16 @@ if ($sesionCerrada) { // ── Cola según área ─────────────────────────────────────────── if ($area === 'recepcion') { - $estadosCola = ["'espera'", "'en_recepcion'"]; - $filtroLugar = ''; - $bindsCola = []; + $estadosCola = ["'espera'", "'en_recepcion'"]; + $filtroLugar = ''; + $bindsCola = [$sesionId]; } else { - $estadosCola = ["'en_espera_lugar'", "'en_servicio'"]; - $filtroLugar = 'AND t.lugar_destino_id = ?'; - $bindsCola = [$lugarId]; + $estadosCola = ["'en_espera_lugar'", "'en_servicio'"]; + // Expandir grupo de lugares + $grupoIds = lugarIdsDeGrupo($lugarId); + $inLugares = implode(',', array_map('intval', $grupoIds)); + $filtroLugar = "AND t.lugar_destino_id IN ($inLugares)"; + $bindsCola = [$sesionId]; } $inEstados = implode(', ', $estadosCola); @@ -81,15 +84,19 @@ $stmt = $pdo->prepare( {$filtroLugar} ORDER BY p.orden_peso ASC, t.creado_at ASC" ); -array_unshift($bindsCola, $sesionId); $stmt->execute($bindsCola); $cola = $stmt->fetchAll(PDO::FETCH_ASSOC); // ── Turno actualmente en pantalla (último llamado) ──────────── $campoLlamado = $area === 'recepcion' ? 'llamado_recepcion_at' : 'llamado_lugar_at'; $estadoActivo = $area === 'recepcion' ? "'en_recepcion'" : "'en_servicio'"; -$bindActivo = $area === 'lugar' ? [$sesionId, $lugarId] : [$sesionId]; -$filtroActivo = $area === 'lugar' ? 'AND t.lugar_destino_id = ?' : ''; +if ($area === 'lugar') { + $filtroActivo = "AND t.lugar_destino_id IN ($inLugares)"; + $bindActivo = [$sesionId]; +} else { + $filtroActivo = ''; + $bindActivo = [$sesionId]; +} $stmt = $pdo->prepare( "SELECT t.id, diff --git a/run_migration_lugar_grupo.php b/run_migration_lugar_grupo.php new file mode 100644 index 0000000..1685d28 --- /dev/null +++ b/run_migration_lugar_grupo.php @@ -0,0 +1,47 @@ +getConnection(); + $sql = file_get_contents($sqlFile); + + $statements = array_filter(array_map('trim', explode(';', $sql))); + + echo "
🚀 Ejecutando migración: 20260616_turnero_lugar_grupo\n\n";
+
+ foreach ($statements as $stmt) {
+ $clean = preg_replace('/--[^\n]*/', '', $stmt);
+ $clean = trim($clean);
+ if (empty($clean)) continue;
+ try {
+ $pdo->exec($clean);
+ echo "✓ " . substr(preg_replace('/\s+/', ' ', $clean), 0, 120) . "\n";
+ } catch (PDOException $e) {
+ $msg = $e->getMessage();
+ if (str_contains($msg, 'Duplicate column') || str_contains($msg, 'errno: 1060')) {
+ echo "ℹ️ (columna ya existe)\n";
+ } else {
+ throw $e;
+ }
+ }
+ }
+
+ echo "\n✅ Migración completada.\n\n";
+ echo "📋 Grupos configurados:\n";
+ $rows = $pdo->query("SELECT id, nombre, grupo_id FROM turnero_lugares ORDER BY grupo_id, sort_order")->fetchAll(PDO::FETCH_ASSOC);
+ foreach ($rows as $r) {
+ $g = $r['grupo_id'] ? "grupo {$r['grupo_id']}" : 'individual';
+ echo " [{$r['id']}] {$r['nombre']} — $g\n";
+ }
+ echo "";
+
+} catch (Throwable $e) {
+ echo "❌ Error: " . htmlspecialchars($e->getMessage()) . "\n"; +} diff --git a/ver_formulario_enviado.php b/ver_formulario_enviado.php index 124a984..14adb76 100644 --- a/ver_formulario_enviado.php +++ b/ver_formulario_enviado.php @@ -96,8 +96,8 @@ if ($modoTurnero) { 'fecha_nacimiento' => $tcRow['fecha_nacimiento'], 'paciente_telefono' => $tcRow['paciente_telefono'], 'eps' => $tcRow['eps'], - 'enviado_por_nombre'=> 'Sistema Turnero', - 'enviado_por_email' => null, + 'enviado_por_nombre'=> $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['username'] ?? 'Turnero', + 'enviado_por_email' => $_SESSION['admin_user']['email'] ?? null, 'firma_profesional_svg' => $tcRow['firma_profesional_svg'] ?? null, 'datos_cliente' => '{}', 'datos_prefilled' => json_encode(['__paciente' => [ @@ -107,7 +107,7 @@ if ($modoTurnero) { 'telefono' => $tcRow['paciente_telefono'] ?? '', 'eps' => $tcRow['eps'] ?? '', ]]), - 'created_at' => $tcRow['enviado_at'], + 'created_at' => $tcRow['enviado_at'] ?? date('Y-m-d H:i:s'), 'completado_en' => $tcRow['firmado_at'], 'ip_cliente' => $tcRow['ip_firma'], 'hash_verificacion' => null, @@ -538,8 +538,8 @@ function esc2(mixed $v): string { - - + +