No GLPI padrão não existe registo de leitura: o histórico do pedido guarda o que mudou, nunca quem apenas o abriu e leu. Na manutenção de parques GLPI de clientes, isto torna-se o clássico impasse do "o técnico nem olhou para o pedido" contra "eu li, sim" - e sem um dado objetivo é a palavra de um contra a do outro. O módulo gratuito Ticket Tracker fecha essa lacuna registando cada visualização de ticket. Este guia mostra como grava os dados, como o configurar sem inflacionar contadores e três consultas SQL que respondem "quem leu o quê" na hora.
Porque é que o GLPI não sabe quem leu um pedido
O histórico nativo (glpi_logs) só regista alterações: criação, mudança de estado, novo seguimento, troca de técnico. Abrir o formulário e ler não muda nenhum campo, portanto não gera qualquer linha. Não existe, no núcleo do GLPI, um evento de "visualização". O Ticket Tracker resolve isto ligando-se ao momento em que o formulário do ticket é renderizado (o hook postShowItem do Ticket): sempre que uma sessão autenticada exibe um pedido, o módulo grava a visualização. Sem ação manual, sem campo novo no formulário.
As duas camadas de registo: resumo e log
O módulo grava em duas tabelas com propósitos distintos. A de resumo responde "quantas vezes e quando"; a de log responde "cada acesso, um a um":
-- Resumo por (ticket, utilizador): uma linha, atualizada a cada acesso.
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 detalhado: uma linha por acesso 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`)
);
A chave UNIQUE (tickets_id, users_id) é o coração da tabela de resumo: cada par ticket/utilizador tem exatamente uma linha. A gravação usa INSERT ... ON DUPLICATE KEY UPDATE view_count = view_count + 1, um upsert atómico - dois acessos simultâneos do mesmo utilizador não criam linha duplicada nem perdem contagem. O separador "Visualizações" no ticket lê o resumo (tabela com todos os visualizadores, contagem, primeira e última leitura) e oferece uma cascata expansível que puxa do log cada acesso individual, com data e hora.
Ativação e checklist de configuração
O módulo é gratuito e liga-se no catálogo NexTool. Depois de ativado, a configuração fica em Módulos > Ticket Tracker, separador Funcionalidades:
- Módulo ativo - o mesmo interruptor da página de Módulos. Desligado, nada é rastreado.
- Rastreamento ativo (tracking_enabled) - liga a gravação das visualizações. Pode ficar ligado com o separador escondido, se quiser recolher sem expor.
- Mostrar separador no ticket (show_tab_on_ticket) - mostra ou oculta o separador "Visualizações" no formulário do pedido.
- Intervalo mínimo (s) (min_interval_seconds, predefinição 60) - uma janela de debounce: acessos do mesmo utilizador ao mesmo ticket dentro desse intervalo contam como um só.
- Ignorados (separador Permissões adicionais) - utilizadores, perfis ou grupos que visualizam sem serem rastreados. Tudo em branco = todos os acessos contam.
O erro de campo que inflaciona (ou zera) os contadores
O erro comum é pôr o intervalo mínimo a zero achando que "quanto mais granular, melhor". O hook dispara a cada render do formulário - incluindo recarregamentos, navegação para trás e reaberturas do separador. Com min_interval_seconds = 0, um técnico que deixa o pedido aberto e faz três atualizações passa a "três leituras", e o view_count perde o sentido de "quantas vezes voltou de facto ao assunto". A predefinição de 60 segundos existe precisamente para colapsar a rajada de recarregamentos numa única leitura. Use 0 apenas em diagnóstico pontual e volte à predefinição depois.
O segundo erro é mais subtil e demos com ele num cliente: até à versão 1.2.0 do módulo, a gravação dependia da permissão de ver o separador de Visualizações - ou seja, só os perfis administrativos eram contados. O requerente e os observadores abriam o pedido e nada era registado, portanto a base subnotificava exatamente quem mais importa numa disputa de SLA. Desacoplámos o registo da permissão de ver o separador: hoje qualquer sessão autenticada que renderiza o ticket é contabilizada - requerente, observador ou técnico - independentemente do perfil. Se opera uma versão anterior, atualize antes de confiar nos números.
Matriz de decisão: intervalo e ignorados por cenário
O ajuste certo depende do que quer provar. A matriz que usamos no desenho:
| Cenário de operação | Rastreamento | Intervalo mínimo (s) | Quem colocar em "ignorados" |
|---|---|---|---|
| Auditoria de leitura / SLA (predefinição) | Ligado | 60 | Ninguém |
| Parque com atualização automática ou painel de parede | Ligado | 180 a 300 | Contas de integração / robôs |
| Supervisão que não deve inflacionar o contador | Ligado | 60 | Perfil de gestores / auditores |
| Diagnóstico pontual (contar cada acesso) | Ligado | 0 (temporário) | Ninguém |
Três consultas SQL que respondem na hora
O separador resolve o caso a caso; para uma visão de parque, vá direto à base de dados. Pedidos abertos atribuídos que o técnico responsável ainda não abriu (type = 2 em glpi_tickets_users é o técnico atribuído):
-- Pedidos abertos atribuídos que o técnico responsável NUNCA visualizou.
SELECT t.id AS pedido, t.name AS titulo, u.name AS tecnico, t.date AS aberto_em
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) -- novo, atribuído, planeado, pendente
AND v.id IS NULL -- nenhuma leitura registada pelo técnico
ORDER BY t.date ASC;
Latência de leitura - quantos minutos o pedido esteve aberto até à primeira leitura do técnico atribuído. É a métrica que antecede a primeira resposta e costuma explicar um SLA ultrapassado:
-- Minutos entre a abertura e a 1a leitura pelo técnico atribuído.
SELECT t.id AS pedido,
TIMESTAMPDIFF(MINUTE, t.date, v.first_view) AS minutos_ate_1a_leitura
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_ate_1a_leitura DESC;
Os visualizadores mais ativos de um pedido específico, a partir do resumo, para saber quem realmente o acompanha (troque 12345 pelo ID do ticket):
-- Quem mais abriu o pedido 12345, do mais recorrente ao menos.
SELECT u.name AS utilizador, 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;
Ler não é responder: o que o contador prova
O dado é poderoso, mas tem um limite honesto: prova que o formulário foi renderizado para aquele utilizador, não que a pessoa o leu com atenção nem que agiu. Um view_count alto sem seguimento é sinal amarelo - alguém está a abrir e fechar sem resolver. Um pedido com zero visualizações do responsável enquanto o SLA corre é sinal vermelho - ninguém sequer olhou. Na manutenção, o valor real não é vigiar o técnico, é fechar a lacuna probatória: quando o cliente pergunta "porque é que este pedido ficou parado três dias?", a resposta deixa de ser opinião e passa a ter carimbo de data e hora.
Precisa de visibilidade real sobre o atendimento no seu GLPI, com os módulos certos ativados e ajustados ao seu fluxo? Conheça o serviço de manutenção de GLPI da NexTool.
Este conteúdo foi produzido com o auxílio de inteligência artificial e revisto pela equipa da Nextool Solutions.