diff --git a/migrations/20260709_dispositivos_token.sql b/migrations/20260709_dispositivos_token.sql
new file mode 100644
index 0000000..1a42c67
--- /dev/null
+++ b/migrations/20260709_dispositivos_token.sql
@@ -0,0 +1,5 @@
+-- Token único por navegador/dispositivo para restricción sin depender de IP pública.
+-- NULL = dispositivo registrado solo por IP (compatibilidad hacia atrás).
+ALTER TABLE turnero_dispositivos
+ ADD COLUMN IF NOT EXISTS token VARCHAR(64) DEFAULT NULL,
+ ADD UNIQUE KEY IF NOT EXISTS uq_dispositivo_token (token);
diff --git a/modules/turnero/api/save_dispositivo.php b/modules/turnero/api/save_dispositivo.php
index 46997de..54fcdfd 100644
--- a/modules/turnero/api/save_dispositivo.php
+++ b/modules/turnero/api/save_dispositivo.php
@@ -23,22 +23,23 @@ $ip = trim($input['ip'] ?? '');
$nombre = trim($input['nombre'] ?? '');
$lugarId = (int)($input['lugar_id'] ?? 0);
$activo = (int)($input['activo'] ?? 1);
+$token = trim($input['token'] ?? '') ?: null;
-if ($ip === '') jsonError('La IP es requerida');
if ($nombre === '') jsonError('El nombre es requerido');
if ($lugarId <= 0) jsonError('Debes asignar un lugar');
+if ($ip === '' && $token === null) jsonError('Se requiere IP o token de dispositivo');
if ($id) {
$stmt = db()->prepare(
- 'UPDATE turnero_dispositivos SET ip=?, nombre=?, lugar_id=?, activo=? WHERE id=?'
+ 'UPDATE turnero_dispositivos SET ip=?, nombre=?, lugar_id=?, activo=?, token=? WHERE id=?'
);
- $stmt->execute([$ip, $nombre, $lugarId, $activo, $id]);
+ $stmt->execute([$ip, $nombre, $lugarId, $activo, $token, $id]);
jsonOk(['id' => $id], 'Dispositivo actualizado');
} else {
$stmt = db()->prepare(
- 'INSERT INTO turnero_dispositivos (ip, nombre, lugar_id, activo) VALUES (?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE nombre=VALUES(nombre), lugar_id=VALUES(lugar_id), activo=VALUES(activo)'
+ 'INSERT INTO turnero_dispositivos (ip, nombre, lugar_id, activo, token) VALUES (?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE nombre=VALUES(nombre), lugar_id=VALUES(lugar_id), activo=VALUES(activo), token=VALUES(token)'
);
- $stmt->execute([$ip, $nombre, $lugarId, $activo]);
+ $stmt->execute([$ip, $nombre, $lugarId, $activo, $token]);
jsonOk(['id' => (int)db()->lastInsertId()], 'Dispositivo creado');
}
diff --git a/modules/turnero/views/configuracion.php b/modules/turnero/views/configuracion.php
index 35c150c..1ced4ed 100644
--- a/modules/turnero/views/configuracion.php
+++ b/modules/turnero/views/configuracion.php
@@ -482,15 +482,15 @@ $tab = $_GET['tab'] ?? 'lugares';
= count($dispositivos) ?>
- Cada tablet queda bloqueada a su lugar asignado según la IP de red.
- Si la IP no aparece aquí, el dispositivo verá todos los lugares disponibles.
+ Cada equipo queda bloqueado a su lugar asignado por token de navegador.
+ Si el equipo no aparece aquí, verá todos los lugares disponibles.