Controle de horas por contrato no GLPI com Contract Hours

Guia técnico para configurar o Contract Hours no GLPI: cronômetro na timeline, contratos por entidade com resolução grupo E categoria, arredondamento faturável, saldo em minutos e o cron externo que evita travar o faturamento.

Vender pacote de horas sem um saldo confiável é operar no escuro: o cliente questiona a fatura, o técnico não sabe quanto ainda resta e o fechamento do mês vira arqueologia de planilha. O módulo Contract Hours do NexTool resolve isso dentro do próprio GLPI, cronometrando o tempo na timeline do chamado e debitando do contrato certo. Este guia mostra como configurar, quais campos mais confundem e o erro de operação que trava o faturamento inteiro.

O que o GLPI nativo não entrega

O campo Duração da tarefa (armazenado em segundos na coluna actiontime) registra tempo, mas não conhece contrato, quota nem saldo. Você digita os minutos na mão, sem cronômetro, sem arredondamento faturável e sem responder à única pergunta que o cliente faz todo mês: "quanto já usei e quanto ainda sobra?". O Contract Hours adiciona essa camada por cima do campo nativo, sem substituí-lo.

Como o tempo é capturado

O técnico controla um cronômetro direto na timeline do ticket:

  • Start inicia a contagem; um badge flutuante segue visível em todas as páginas do GLPI enquanto houver medição ativa.
  • Pause / Resume descontam as pausas (reunião, almoço) sem perder o tempo acumulado.
  • Stop arredonda o tempo, cria uma TicketTask privada e debita o contrato vinculado.

Ao resolver ou fechar o chamado, um hook para o timer automaticamente (auto-stop). É o detalhe que evita o timer esquecido rodando a noite toda e inflando o consumo.

Passo a passo: configurar um contrato

  1. Instale e ative o módulo em NexTool -> Módulos -> Contract Hours. A instalação cria as tabelas, a VIEW de resumo e registra três CronTasks (billing, cleanup, reconcile).
  2. Defina o mínimo faturável nas Configurações (padrão 15 min). O arredondamento é em dois passos: primeiro para o múltiplo do mínimo, depois para múltiplo de 5 (compatibilidade com o actiontime do GLPI).
  3. Crie o contrato na aba Contratos de Horas: nome, entidade (relação 1:1), Total de horas, dia de faturamento, periodicidade e limiar de alerta.
  4. Configure os critérios na aba Condições: os grupos e/ou as categorias ITIL que fazem um chamado "cair" nesse contrato.
  5. Ajuste a visibilidade na aba Visibilidade: usuários, perfis e grupos que enxergam o contrato no dashboard do cliente. Lista vazia significa visível para todos com permissão de leitura.

O erro comum aqui: o campo "Total de horas" é armazenado em MINUTOS. Quem digita "40" pensando em 40 horas cria, na verdade, um contrato de 40 minutos e vê o saldo estourar já no primeiro chamado. Para 40 horas, informe 2400.

A regra de resolução é E, não OU

Decidir quando um chamado pertence a um contrato é onde mais vemos configuração errada. A lógica combina a entidade (sempre obrigatória, 1:1) com critérios opcionais de grupo e categoria:

Critérios no contratoRegra aplicadaO chamado entra no contrato quando
NenhumSó a entidadeSempre (basta ser da entidade do contrato)
Só gruposPertinência no tipoUm grupo atribuído está na lista do contrato
Só categoriasPertinência no tipoA categoria ITIL está nas regras do contrato
Grupos E categoriasAND entre os tiposGrupo na lista E categoria nas regras (os dois)

Ou seja: com grupo E categoria preenchidos, o ticket só entra no contrato se ambos baterem. Configurar os dois esperando comportamento "OU" é o que joga metade dos chamados para o card "Sem Contrato".

actiontime é a fonte de verdade

A partir da versão 3.4.2 o módulo trata o actiontime da TicketTask como fonte primária. O billable_time do timer é mantido em sincronia por três camadas: o hook onTicketTaskUpdated (tempo real, quando você edita a tarefa pelo GLPI nativo), o cron reconcile (a cada 4h) e o botão "Recalcular Contratos". Na prática, editar a duração da tarefa direto no GLPI reflete no saldo do contrato - desde que o cron esteja rodando.

O cron precisa estar em modo externo

Aqui está o erro de operação que trava tudo. O Contract Hours depende de três CronTasks: billing (fecha o ciclo e reseta o consumo), cleanup (fecha timers órfãos) e reconcile (sincroniza e re-resolve). Elas só rodam se as ações automáticas do GLPI estiverem em modo externo (CLI). No modo GLPI (disparado por acesso web), um ambiente de pouco tráfego pode passar dias sem fechar o ciclo de faturamento, e timers esquecidos ficam somando horas fantasma.

Garanta o cron do sistema apontando para o executor de ações automáticas do GLPI:

# /etc/cron.d/glpi - dispara as acoes automaticas do GLPI a cada minuto (modo externo)
# Ajuste o caminho conforme a instalacao (em GLPI 11 o document root e public/)
* * * * * www-data /usr/bin/php /var/www/glpi/front/cron.php >/dev/null 2>&1

Depois confirme que as tarefas do módulo estão agendadas, em modo externo e com execução recente:

-- Estado das 3 CronTasks do Contract Hours
SELECT name, frequency, mode, state, lastrun
FROM glpi_crontasks
WHERE itemtype = 'PluginNextoolContracthoursCron';
-- mode=2 (externo/CLI), state=1 (agendada) e lastrun recente = saudavel.

Diagnóstico de saldo divergente

Na sustentação de ambientes de clientes, o padrão se repete: o contrato é criado certo, os primeiros chamados debitam direito e, três semanas depois, o saldo "trava". Em quase todos os casos a causa é a mesma - as ações automáticas estavam no modo web, não no CLI externo. Sem o cron externo, o billing nunca fecha o ciclo, o reconcile nunca sincroniza as edições feitas na tarefa e o cleanup nunca mata os timers órfãos de quem esqueceu o Stop na sexta. Por isso a primeira query que rodamos num diagnóstico de Contract Hours é a de glpi_crontasks, não a de saldo: se o lastrun das três tarefas não é recente, o problema não é o módulo, é a infraestrutura do GLPI.

Confirmado o cron, o segundo suspeito é o billable_time dessincronizado do actiontime após uma edição retroativa. Meça a divergência antes de sair recalculando às cegas:

-- Timers cujo tempo faturado divergiu do actiontime da tarefa (fonte de verdade)
SELECT t.id, t.tickets_id,
       t.billable_time/60 AS min_faturado,
       tt.actiontime/60   AS min_tarefa,
       (t.billable_time - tt.actiontime) AS diff_segundos
FROM glpi_plugin_nextool_contracthours_timers t
JOIN glpi_tickettasks tt ON tt.id = t.tickettasks_id
WHERE t.status = 'stopped'
  AND t.billable_time != tt.actiontime
LIMIT 20;

Havendo divergências, o botão "Recalcular Contratos" (aba Dados) roda o sync e recalcula o consumo. Para conferir o saldo, lembrando que os campos estão em minutos:

-- Saldo por contrato ativo (valores em minutos, convertidos para horas)
SELECT name, entities_id,
       total_hours/60 AS quota_h,
       used_hours/60  AS consumido_h,
       (total_hours - used_hours)/60 AS saldo_h
FROM glpi_plugin_nextool_contracthours_contracts
WHERE is_active = 1
ORDER BY saldo_h;

Auditoria, alertas e cortesia

Toda edição retroativa de tempo é registrada - quem editou, quando e qual era o valor anterior - na aba Logs. É a rastreabilidade que você agradece quando o cliente contesta uma hora lançada. Os alertas disparam por percentual de consumo (limiar padrão 80%) e por duas condições de contrato: expirado (data final no passado) e horas excedidas. Cada condição pode ser configurada como "não fazer nada", "alerta" (popup amarelo dismissível) ou "bloqueio". Vale a regra fina: o bloqueio vermelho só atua na criação de chamado pelo helpdesk (ServiceCatalog); um chamado já aberto nunca é bloqueado, apenas recebe o alerta. Desde a versão 3.6 existe ainda o toggle "não faturável" (cortesia): o tempo é contabilizado mas não debita o saldo - útil para o atendimento que você quer registrar sem descontar a quota do cliente.

Transformar horas de chamado em faturamento defensável - com saldo, extrato e um dashboard que o cliente entende - é exatamente o que o módulo Contract Hours do plugin NexTool resolve. Se você sustenta GLPI e ainda fecha o mês na planilha, fale com a gente.


Este conteúdo foi produzido com auxílio de inteligência artificial e revisado pela equipe Nextool Solutions.

Perguntas Frequentes

Em minutos. Para 40 horas, informe 2400. É o erro de configuração mais comum: digitar "40" cria um contrato de 40 minutos e o saldo estoura logo no primeiro chamado.

Quase sempre porque as ações automáticas do GLPI não estão em modo externo (CLI). As CronTasks billing, cleanup e reconcile não rodam, o ciclo não fecha e as edições de tarefa não sincronizam. Verifique o lastrun em glpi_crontasks e configure o cron do sistema apontando para front/cron.php.

Pela entidade (relação 1:1, obrigatória) combinada com critérios opcionais de grupo e categoria. Com os dois preenchidos a lógica é E (AND): o grupo atribuído e a categoria precisam bater. Se só um estiver configurado, basta ele. Nenhum critério: qualquer chamado da entidade entra.

Sim. O actiontime da tarefa é a fonte de verdade; o hook onTicketTaskUpdated sincroniza o billable_time em tempo real e o cron reconcile corrige qualquer divergência a cada 4h. Se não refletiu, rode "Recalcular Contratos" na aba Dados.

Não. O módulo é GLPI 11 apenas. Em GLPI 10 você mede tempo com o campo nativo de duração ou com o ActualTime, mas o controle de saldo por contrato exige a migração para o GLPI 11.

Sim, desde a versão 3.6. O toggle "não faturável" por tarefa contabiliza o tempo mas não debita o saldo do contrato - útil para atendimentos de cortesia que você quer registrar sem descontar a quota.

Precisa de ajuda?