Files
whatsapp/modules/turnero/module.php
T
Lizandro GuarnizoandClaude Opus 5 3a7b267f65 Rol de Calidad, y restricción por rol dentro del turnero
Rol nuevo para consultar turnos y tiempos de atención, de solo lectura y con
acceso únicamente a dashboard e historial. Se le permite exportar el historial,
que es la forma de analizar tiempos fuera del sistema.

Al crearlo salió que las vistas del turnero no verificaban nada por su cuenta:
el control del ERP es por módulo, así que cualquiera con acceso al turnero podía
abrir Configuración —lugares, dispositivos, plantillas— escribiendo la URL,
aunque el menú no se la mostrara. Aplicaba a todos los roles, no solo al nuevo.

Se agrega _acceso.php con una verificación de rol que usan las tres pantallas
que operan sobre la atención o cambian configuración. Verificado que cada rol
conserva lo que ya usaba: recepcionista entra a recepción, bacteriólogo a las
estaciones, supervisor y administradores a todo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 10:50:24 -05:00

182 lines
8.2 KiB
PHP

<?php
/**
* Descriptor del módulo Turnero — Oleada 1
* Los links visibles se filtran según el rol del usuario.
*/
$_trRole = $_SESSION['admin_user']['role'] ?? 'admin';
$_trAdminRoles = ['superadmin', 'admin', 'supervisor'];
$_trIsAdmin = in_array($_trRole, $_trAdminRoles, true);
$_trIsRecep = in_array($_trRole, ['recepcionista', 'lab_recepcion'], true);
$_trIsBacte = $_trRole === 'bacteriologo';
$_trIsCalidad = $_trRole === 'calidad';
$_trClientIp = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '')[0]);
try {
$_trPdo = Database::getInstance()->getConnection();
$_trDesks = $_trPdo->query(
"SELECT id, nombre FROM turnero_lugares WHERE activo=1 AND tipo='recepcion' ORDER BY sort_order"
)->fetchAll(PDO::FETCH_ASSOC);
$_trMuestras = $_trPdo->query(
"SELECT id, nombre, acceso_libre FROM turnero_lugares WHERE activo=1 AND tipo='muestras' ORDER BY sort_order"
)->fetchAll(PDO::FETCH_ASSOC);
} catch (\Throwable $_) {
$_trDesks = [];
$_trMuestras = [];
}
$_trLinks = [];
// ── Calidad: solo consulta de turnos y tiempos ────────────────
if ($_trIsCalidad) {
$_trLinks[] = ['name' => 'Dashboard', 'icon' => 'fas fa-tachometer-alt', 'route' => '/erp.php?m=turnero&v=dashboard'];
$_trLinks[] = ['name' => 'Historial', 'icon' => 'fas fa-history', 'route' => '/erp.php?m=turnero&v=historial'];
// ── Recepcionista: pantallas comunes + desks según IP ─────────
} elseif ($_trIsRecep) {
$_trLinks[] = ['name' => 'Chat Turnero', 'icon' => 'fab fa-whatsapp', 'route' => '/erp.php?m=turnero&v=chat'];
$_trLinks[] = ['name' => 'Verificar Paciente', 'icon' => 'fas fa-id-card', 'route' => '/erp.php?m=turnero&v=verificar_paciente'];
$_trLinks[] = ['name' => 'Pantalla TV', 'icon' => 'fas fa-tv', 'route' => '/erp.php?m=turnero&v=display_global'];
// Detectar escritorio de recepción: primero por token, luego por IP
$_trRecepIpDesk = null;
try {
$_trDevToken = trim($_COOKIE['turnero_token'] ?? '');
if ($_trDevToken) {
$_trTokRow = $_trPdo->prepare(
"SELECT d.lugar_id FROM turnero_dispositivos d
JOIN turnero_lugares l ON l.id = d.lugar_id
WHERE d.token = ? AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
);
$_trTokRow->execute([$_trDevToken]);
$_trRecepIpDesk = $_trTokRow->fetchColumn() ?: null;
}
if (!$_trRecepIpDesk) {
$_trRecepIpRow = $_trPdo->prepare(
"SELECT d.lugar_id FROM turnero_dispositivos d
JOIN turnero_lugares l ON l.id = d.lugar_id
WHERE d.ip = ? AND d.token IS NULL AND d.activo = 1 AND l.tipo = 'recepcion' LIMIT 1"
);
$_trRecepIpRow->execute([$_trClientIp]);
$_trRecepIpDesk = $_trRecepIpRow->fetchColumn() ?: null;
}
} catch (\Throwable $_) {}
if ($_trRecepIpDesk) {
// IP registrada: solo su desk
foreach ($_trDesks as $_d) {
if ((int)$_d['id'] === (int)$_trRecepIpDesk) {
$_trLinks[] = [
'name' => $_d['nombre'],
'icon' => 'fas fa-concierge-bell',
'route' => '/erp.php?m=turnero&v=recepcion&desk_id=' . (int)$_d['id'],
];
break;
}
}
} elseif (!empty($_trDesks)) {
// IP libre: todos los desks
foreach ($_trDesks as $_d) {
$_trLinks[] = [
'name' => $_d['nombre'],
'icon' => 'fas fa-concierge-bell',
'route' => '/erp.php?m=turnero&v=recepcion&desk_id=' . (int)$_d['id'],
];
}
} else {
$_trLinks[] = ['name' => 'Recepción', 'icon' => 'fas fa-concierge-bell', 'route' => '/erp.php?m=turnero&v=recepcion'];
}
// ── Bacteriólogo: sin dashboard ni historial; vista según IP ──
} elseif ($_trIsBacte) {
// Detectar lugar de muestras: primero por token, luego por IP
$_trIpLugar = null;
try {
$_trDevToken = trim($_COOKIE['turnero_token'] ?? '');
if ($_trDevToken) {
$_trTokRow2 = $_trPdo->prepare(
"SELECT lugar_id FROM turnero_dispositivos WHERE token = ? AND activo = 1 LIMIT 1"
);
$_trTokRow2->execute([$_trDevToken]);
$_trIpLugar = $_trTokRow2->fetchColumn() ?: null;
}
if (!$_trIpLugar) {
$_trIpRow = $_trPdo->prepare(
"SELECT lugar_id FROM turnero_dispositivos WHERE ip = ? AND token IS NULL AND activo = 1 LIMIT 1"
);
$_trIpRow->execute([$_trClientIp]);
$_trIpLugar = $_trIpRow->fetchColumn() ?: null;
}
} catch (\Throwable $_) {}
$_trLinks[] = ['name' => 'Bandeja del día', 'icon' => 'fas fa-layer-group', 'route' => '/erp.php?m=turnero&v=bandeja'];
if ($_trIpLugar) {
// IP registrada: su estación, más las marcadas como de acceso libre
// (Pediatría, Ginecología), que se atienden desde cualquier puesto.
foreach ($_trMuestras as $_m) {
$_esSuyo = (int)$_m['id'] === (int)$_trIpLugar;
if (!$_esSuyo && empty($_m['acceso_libre'])) continue;
$_trLinks[] = [
'name' => $_m['nombre'],
'icon' => $_esSuyo ? 'fas fa-flask' : 'fas fa-share-square',
'route' => '/erp.php?m=turnero&v=lugar&lugar_id=' . (int)$_m['id'],
];
}
} else {
// IP no registrada: TV + todos los lugares muestras
$_trLinks[] = ['name' => 'Pantalla TV', 'icon' => 'fas fa-tv', 'route' => '/erp.php?m=turnero&v=display_global'];
foreach ($_trMuestras as $_m) {
$_trLinks[] = [
'name' => $_m['nombre'],
'icon' => 'fas fa-flask',
'route' => '/erp.php?m=turnero&v=lugar&lugar_id=' . (int)$_m['id'],
];
}
}
// ── Admin / Supervisor / otros: acceso completo ───────────────
} else {
$_trLinks[] = ['name' => 'Dashboard', 'icon' => 'fas fa-tachometer-alt', 'route' => '/erp.php?m=turnero&v=dashboard'];
$_trLinks[] = ['name' => 'Historial', 'icon' => 'fas fa-history', 'route' => '/erp.php?m=turnero&v=historial'];
$_trLinks[] = ['name' => 'Bandeja del día', 'icon' => 'fas fa-layer-group', 'route' => '/erp.php?m=turnero&v=bandeja'];
$_trLinks[] = ['name' => 'Chat Turnero', 'icon' => 'fab fa-whatsapp', 'route' => '/erp.php?m=turnero&v=chat'];
$_trLinks[] = ['name' => 'Verificar Paciente', 'icon' => 'fas fa-id-card', 'route' => '/erp.php?m=turnero&v=verificar_paciente'];
$_trLinks[] = ['name' => 'Configuración', 'icon' => 'fas fa-sliders-h', 'route' => '/erp.php?m=turnero&v=configuracion'];
$_trLinks[] = ['name' => 'Kiosko', 'icon' => 'fas fa-desktop', 'route' => '/erp.php?m=turnero&v=kiosko'];
$_trLinks[] = ['name' => 'Pantalla TV Global', 'icon' => 'fas fa-th-large', 'route' => '/erp.php?m=turnero&v=display_global'];
if (!empty($_trDesks)) {
foreach ($_trDesks as $_d) {
$_trLinks[] = [
'name' => $_d['nombre'],
'icon' => 'fas fa-concierge-bell',
'route' => '/erp.php?m=turnero&v=recepcion&desk_id=' . (int)$_d['id'],
];
}
} else {
$_trLinks[] = ['name' => 'Recepción', 'icon' => 'fas fa-concierge-bell', 'route' => '/erp.php?m=turnero&v=recepcion'];
}
foreach ($_trMuestras as $_m) {
$_trLinks[] = [
'name' => $_m['nombre'],
'icon' => 'fas fa-flask',
'route' => '/erp.php?m=turnero&v=lugar&lugar_id=' . (int)$_m['id'],
];
}
}
return [
'slug' => 'turnero',
'name' => 'Turnero',
'icon' => 'fas fa-ticket-alt',
'category' => 'turnero',
'route' => '/erp.php?m=turnero&v=dashboard',
'is_active' => true,
'sort_order' => 40,
'oleada' => 1,
'description' => 'Sistema de turnos presenciales en recepción con prioridades, consentimientos y pantallas TV',
'links' => $_trLinks,
];