En GLPI estándar no existe registro de lectura: el historial del ticket guarda lo que cambió, nunca quién solo lo abrió y lo leyó. En el mantenimiento de parques GLPI de clientes, esto se convierte en el clásico punto muerto de "el técnico ni miró el ticket" contra "sí lo leí" - y sin un dato objetivo es la palabra de uno contra la del otro. El módulo gratuito Ticket Tracker cierra esa brecha registrando cada visualización de ticket. Esta guía muestra cómo almacena los datos, cómo configurarlo sin inflar contadores y tres consultas SQL que responden "quién leyó qué" al instante.
Por qué GLPI no sabe quién leyó un ticket
El historial nativo (glpi_logs) solo registra cambios: creación, cambio de estado, nuevo seguimiento, reasignación de técnico. Abrir el formulario y leerlo no modifica ningún campo, así que no genera fila alguna. En el núcleo de GLPI no hay un evento de "visualización". Ticket Tracker lo resuelve enganchando en el momento en que se renderiza el formulario del ticket (el hook postShowItem de Ticket): cada vez que una sesión autenticada muestra un ticket, el módulo registra la visualización. Sin acción manual, sin campo nuevo en el formulario.
Las dos capas de registro: resumen y log
El módulo escribe en dos tablas con propósitos distintos. La de resumen responde "cuántas veces y cuándo"; la de log responde "cada acceso, uno a uno":
-- Resumen por (ticket, usuario): una fila, actualizada en cada acceso.
CREATE TABLE `glpi_plugin_nextool_tickettracker_views` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`tickets_id` int unsigned NOT NULL,
`users_id` int unsigned NOT NULL,
`view_count` int unsigned NOT NULL DEFAULT 1,
`first_view` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_view` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_ticket_user` (`tickets_id`, `users_id`)
);
-- Log detallado: una fila por acceso individual.
CREATE TABLE `glpi_plugin_nextool_tickettracker_viewlog` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`tickets_id` int unsigned NOT NULL,
`users_id` int unsigned NOT NULL,
`view_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
La clave UNIQUE (tickets_id, users_id) es el corazón de la tabla de resumen: cada par ticket/usuario tiene exactamente una fila. La escritura usa INSERT ... ON DUPLICATE KEY UPDATE view_count = view_count + 1, un upsert atómico - dos accesos simultáneos del mismo usuario ni crean fila duplicada ni pierden conteo. La pestaña "Visualizaciones" en el ticket lee el resumen (tabla con todos los visualizadores, conteo, primera y última lectura) y ofrece una cascada expandible que extrae del log cada acceso individual, con fecha y hora.
Activación y checklist de configuración
El módulo es gratuito y se activa desde el catálogo NexTool. Una vez habilitado, la configuración está en Módulos > Ticket Tracker, pestaña Funcionalidades:
- Módulo activo - el mismo interruptor de la página de Módulos. Apagado, no se rastrea nada.
- Tracking activo (tracking_enabled) - activa el registro de visualizaciones. Puede quedar activo con la pestaña oculta si quieres recolectar sin exponer.
- Mostrar pestaña en el ticket (show_tab_on_ticket) - muestra u oculta la pestaña "Visualizaciones" en el formulario del ticket.
- Intervalo mínimo (s) (min_interval_seconds, por defecto 60) - ventana de debounce: los accesos del mismo usuario al mismo ticket dentro de ese intervalo cuentan como uno.
- Ignorados (pestaña Permisos adicionales) - usuarios, perfiles o grupos que visualizan sin ser rastreados. Todo en blanco = todos los accesos cuentan.
El error de campo que infla (o pone a cero) los contadores
El error común es poner a cero el intervalo mínimo pensando que "cuanto más granular, mejor". El hook se dispara en cada render del formulario - incluidas recargas, navegación hacia atrás y reaperturas de la pestaña. Con min_interval_seconds = 0, un técnico que deja el ticket abierto y pulsa actualizar tres veces se convierte en "tres lecturas", y view_count pierde su sentido de "cuántas veces volvió de verdad al asunto". El valor por defecto de 60 segundos existe justamente para colapsar la ráfaga de recargas en una sola lectura. Usa 0 solo en un diagnóstico puntual y vuelve al valor por defecto después.
El segundo error es más sutil y lo topamos en un cliente: hasta la versión 1.2.0 del módulo, el registro dependía del permiso de ver la pestaña de Visualizaciones - es decir, solo se contaban los perfiles administrativos. El solicitante y los observadores abrían el ticket y no se registraba nada, así que la base subnotificaba justo a quien más importa en una disputa de SLA. Desacoplamos el registro del permiso de ver la pestaña: hoy cualquier sesión autenticada que renderiza el ticket se contabiliza - solicitante, observador o técnico - sin importar el perfil. Si operas una versión anterior, actualiza antes de confiar en los números.
Matriz de decisión: intervalo e ignorados por escenario
El ajuste correcto depende de qué quieres demostrar. La matriz que usamos en el diseño:
| Escenario de operación | Tracking | Intervalo mínimo (s) | Quién poner en "ignorados" |
|---|---|---|---|
| Auditoría de lectura / SLA (por defecto) | Activado | 60 | Nadie |
| Parque con auto-refresco o panel de pared | Activado | 180 a 300 | Cuentas de integración / bots |
| Supervisión que no debe inflar el contador | Activado | 60 | Perfil de gestores / auditores |
| Diagnóstico puntual (contar cada acceso) | Activado | 0 (temporal) | Nadie |
Tres consultas SQL que responden al instante
La pestaña resuelve el caso a caso; para una vista de parque, ve directo a la base de datos. Tickets abiertos asignados que el técnico responsable aún no ha abierto (type = 2 en glpi_tickets_users es el técnico asignado):
-- Tickets abiertos asignados que el técnico responsable NUNCA visualizó.
SELECT t.id AS ticket, t.name AS titulo, u.name AS tecnico, t.date AS abierto_en
FROM glpi_tickets t
JOIN glpi_tickets_users tu ON tu.tickets_id = t.id AND tu.type = 2
JOIN glpi_users u ON u.id = tu.users_id
LEFT JOIN glpi_plugin_nextool_tickettracker_views v
ON v.tickets_id = t.id AND v.users_id = tu.users_id
WHERE t.is_deleted = 0
AND t.status IN (1, 2, 3, 4) -- nuevo, asignado, planificado, pendiente
AND v.id IS NULL -- ninguna lectura registrada por el técnico
ORDER BY t.date ASC;
Latencia de lectura - cuántos minutos el ticket estuvo abierto hasta la primera lectura del técnico asignado. Es la métrica que antecede a la primera respuesta y suele explicar un SLA incumplido:
-- Minutos entre la apertura y la 1a lectura por el técnico asignado.
SELECT t.id AS ticket,
TIMESTAMPDIFF(MINUTE, t.date, v.first_view) AS minutos_hasta_1a_lectura
FROM glpi_tickets t
JOIN glpi_tickets_users tu ON tu.tickets_id = t.id AND tu.type = 2
JOIN glpi_plugin_nextool_tickettracker_views v
ON v.tickets_id = t.id AND v.users_id = tu.users_id
WHERE t.is_deleted = 0
ORDER BY minutos_hasta_1a_lectura DESC;
Los visualizadores más activos de un ticket concreto, desde el resumen, para saber quién de verdad lo sigue (cambia 12345 por el ID del ticket):
-- Quién abrió más el ticket 12345, del más recurrente al menos.
SELECT u.name AS usuario, v.view_count, v.first_view, v.last_view
FROM glpi_plugin_nextool_tickettracker_views v
JOIN glpi_users u ON u.id = v.users_id
WHERE v.tickets_id = 12345
ORDER BY v.view_count DESC;
Leer no es responder: qué demuestra el contador
El dato es potente, pero tiene un límite honesto: demuestra que el formulario se renderizó para ese usuario, no que la persona lo leyó con atención ni que actuó. Un view_count alto sin seguimiento es señal amarilla - alguien abre y cierra sin resolver. Un ticket con cero visualizaciones del responsable mientras el SLA corre es señal roja - nadie lo miró siquiera. En el mantenimiento, el valor real no es vigilar al técnico, es cerrar la brecha probatoria: cuando el cliente pregunta "¿por qué este ticket estuvo parado tres días?", la respuesta deja de ser opinión y pasa a tener sello de fecha y hora.
¿Necesitas visibilidad real sobre la atención en tu GLPI, con los módulos adecuados activados y ajustados a tu flujo? Conoce el servicio de mantenimiento de GLPI de NexTool.
Este contenido fue producido con ayuda de inteligencia artificial y revisado por el equipo de Nextool Solutions.