Controlo de horas por contrato no GLPI com Contract Hours

Guia técnico para configurar o Contract Hours no GLPI: cronómetro na linha de tempo, contratos por entidade com resolução grupo E categoria, arredondamento faturável, saldo em minutos e o cron externo que evita bloquear a faturação.

Vender pacotes de horas sem um saldo fiável é operar às escuras: o cliente questiona a fatura, o técnico não sabe quanto ainda resta e o fecho do mês vira arqueologia de folha de cálculo. O módulo Contract Hours do NexTool resolve isto dentro do próprio GLPI, cronometrando o tempo na linha de tempo do chamado e debitando do contrato certo. Este guia mostra como configurar, que campos mais confundem e o erro de operação que bloqueia a faturação inteira.

O que o GLPI nativo não entrega

O campo Duração da tarefa (armazenado em segundos na coluna actiontime) regista tempo, mas não conhece contrato, quota nem saldo. Introduz os minutos à mão, sem cronómetro, sem arredondamento faturável e sem responder à única pergunta que o cliente faz todos os meses: "quanto já usei e quanto ainda sobra?". O Contract Hours acrescenta essa camada por cima do campo nativo, sem o substituir.

Como o tempo é capturado

O técnico controla um cronómetro diretamente na linha de tempo do ticket:

  • Start inicia a contagem; um crachá flutuante mantém-se visível em todas as páginas do GLPI enquanto houver uma 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 associado.

Ao resolver ou fechar o chamado, um hook para o cronómetro automaticamente (auto-stop). É o detalhe que evita o cronómetro esquecido a correr a noite toda e a inflacionar 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 regista três CronTasks (billing, cleanup, reconcile).
  2. Defina o mínimo faturável nas Configurações (predefiniçã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 no separador Contratos de Horas: nome, entidade (relação 1:1), Total de horas, dia de faturação, periodicidade e limiar de alerta.
  4. Configure os critérios no separador Condições: os grupos e/ou as categorias ITIL que fazem um chamado "cair" nesse contrato.
  5. Ajuste a visibilidade no separador Visibilidade: utilizadores, perfis e grupos que veem o contrato no painel do cliente. Uma 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 introduz "40" a pensar em 40 horas cria, na verdade, um contrato de 40 minutos e vê o saldo rebentar logo no primeiro chamado. Para 40 horas, indique 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ó gruposPertença no tipoUm grupo atribuído está na lista do contrato
Só categoriasPertença 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 corresponderem. Configurar os dois à espera de comportamento "OU" é o que atira metade dos chamados para o cartão "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 cronómetro é mantido sincronizado por três camadas: o hook onTicketTaskUpdated (em tempo real, quando 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 diretamente no GLPI reflete-se no saldo do contrato - desde que o cron esteja a correr.

O cron tem de estar em modo externo

Aqui está o erro de operação que bloqueia tudo. O Contract Hours depende de três CronTasks: billing (fecha o ciclo e repõe o consumo), cleanup (fecha cronómetros órfãos) e reconcile (sincroniza e volta a resolver). Só correm se as ações automáticas do GLPI estiverem em modo externo (CLI). No modo GLPI (despoletado por acesso web), um ambiente com pouco tráfego pode passar dias sem fechar o ciclo de faturação, enquanto cronómetros esquecidos vão somando horas fantasma.

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

# /etc/cron.d/glpi - despoleta 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 repete-se: o contrato é criado certo, os primeiros chamados debitam bem e, três semanas depois, o saldo "bloqueia". 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 elimina os cronómetros órfãos de quem se esqueceu do Stop à sexta-feira. Por isso a primeira consulta que corremos num diagnóstico de Contract Hours é a de glpi_crontasks, não a do 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 recalcular às cegas:

-- Cronometros 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" (separador Dados) corre o sync e recalcula o consumo. Para confirmar 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 a edição retroativa de tempo é registada - quem editou, quando e qual era o valor anterior - no separador Registos. É a rastreabilidade que agradece quando o cliente contesta uma hora lançada. Os alertas disparam por percentagem de consumo (limiar predefinido 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" (janela amarela que se pode fechar) ou "bloqueio". Aplica-se uma regra fina: o bloqueio vermelho só atua na criação de chamado pelo serviço de apoio (Service Catalog); um chamado já aberto nunca é bloqueado, apenas recebe o alerta. Desde a versão 3.6 existe ainda o interruptor "não faturável" (cortesia): o tempo é contabilizado mas não debita o saldo - útil para o atendimento que quer registar sem descontar a quota do cliente.

Transformar horas de chamado em faturação defensável - com saldo, extrato e um painel que o cliente entende - é exatamente o que o módulo Contract Hours do plugin NexTool resolve. Se sustenta GLPI e ainda fecha o mês na folha de cálculo, fale connosco.


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

Perguntas Frequentes

Em minutos. Para 40 horas, indique 2400. É o erro de configuração mais comum: introduzir "40" cria um contrato de 40 minutos e o saldo rebenta 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 correm, 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 a apontar 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 têm de corresponder. Se só um estiver configurado, basta esse. Sem critérios, 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, corra "Recalcular Contratos" no separador Dados.

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

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

Precisa de ajuda?