up
This commit is contained in:
@@ -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%';
|
||||
@@ -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).
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Runner — Migración 20260616: grupo_id en turnero_lugares
|
||||
* Ejecutar una sola vez desde el navegador o CLI.
|
||||
*/
|
||||
require_once __DIR__ . '/config/config.php';
|
||||
|
||||
$sqlFile = __DIR__ . '/migrations/20260616_turnero_lugar_grupo.sql';
|
||||
if (!file_exists($sqlFile)) die("❌ Archivo no encontrado: $sqlFile\n");
|
||||
|
||||
try {
|
||||
$pdo = Database::getInstance()->getConnection();
|
||||
$sql = file_get_contents($sqlFile);
|
||||
|
||||
$statements = array_filter(array_map('trim', explode(';', $sql)));
|
||||
|
||||
echo "<pre>🚀 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 "</pre>";
|
||||
|
||||
} catch (Throwable $e) {
|
||||
echo "<pre>❌ Error: " . htmlspecialchars($e->getMessage()) . "\n</pre>";
|
||||
}
|
||||
@@ -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 {
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- Firma digital global (dibujo) -->
|
||||
<?php if ($envio['firma_svg']): ?>
|
||||
<!-- Firma digital global (solo si el esquema no tiene campo firma propio) -->
|
||||
<?php if ($envio['firma_svg'] && !$_renderedFirmaPaciente): ?>
|
||||
<div class="section-title mt-4"><i class="fas fa-signature me-1"></i>Firma digital (dibujo)</div>
|
||||
<div class="firma-box">
|
||||
<img src="<?= htmlspecialchars($envio['firma_svg']) ?>" alt="Firma digital">
|
||||
|
||||
Reference in New Issue
Block a user