Suivi des heures par contrat dans GLPI avec Contract Hours

Guide technique pour configurer Contract Hours dans GLPI : chronomètre sur la timeline, contrats par entité avec résolution groupe ET catégorie, arrondi facturable, solde en minutes et le cron externe qui évite le blocage de la facturation.

Vendre des forfaits d'heures sans un solde fiable, c'est piloter à l'aveugle : le client conteste la facture, le technicien ne sait pas combien il reste et la clôture du mois vire à l'archéologie de tableur. Le module Contract Hours de NexTool résout cela directement dans GLPI, en chronométrant le temps sur la timeline du ticket et en le débitant du bon contrat. Ce guide montre comment le configurer, quels champs prêtent le plus à confusion et l'erreur d'exploitation qui bloque toute la facturation.

Ce que GLPI natif n'offre pas

Le champ Durée de la tâche (stocké en secondes dans la colonne actiontime) enregistre du temps, mais ne connaît ni contrat, ni quota, ni solde. Vous saisissez les minutes à la main, sans chronomètre, sans arrondi facturable et sans répondre à la seule question que le client pose chaque mois : « combien ai-je consommé et combien reste-t-il ? ». Contract Hours ajoute cette couche par-dessus le champ natif, sans le remplacer.

Comment le temps est capturé

Le technicien contrôle un chronomètre directement sur la timeline du ticket :

  • Start lance le décompte ; un badge flottant reste visible sur toutes les pages de GLPI tant qu'une mesure est active.
  • Pause / Resume décomptent les pauses (réunion, déjeuner) sans perdre le temps accumulé.
  • Stop arrondit le temps, crée une TicketTask privée et débite le contrat lié.

À la résolution ou à la clôture du ticket, un hook arrête le chronomètre automatiquement (auto-stop). C'est le détail qui évite qu'un chronomètre oublié tourne toute la nuit et gonfle la consommation.

Pas à pas : configurer un contrat

  1. Installez et activez le module dans NexTool -> Modules -> Contract Hours. L'installation crée les tables, la VIEW de synthèse et enregistre trois CronTasks (billing, cleanup, reconcile).
  2. Définissez le minimum facturable dans les Paramètres (par défaut 15 min). L'arrondi se fait en deux temps : d'abord au multiple du minimum, puis à un multiple de 5 (compatibilité avec l'actiontime de GLPI).
  3. Créez le contrat dans l'onglet Contrats d'Heures : nom, entité (relation 1:1), Total d'heures, jour de facturation, périodicité et seuil d'alerte.
  4. Configurez les critères dans l'onglet Conditions : les groupes et/ou catégories ITIL qui font qu'un ticket « tombe » dans ce contrat.
  5. Ajustez la visibilité dans l'onglet Visibilité : utilisateurs, profils et groupes qui voient le contrat sur le tableau de bord client. Une liste vide signifie visible pour tous ceux qui ont le droit de lecture.

L'erreur courante ici : le champ « Total d'heures » est stocké en MINUTES. Saisir « 40 » en pensant à 40 heures crée en réalité un contrat de 40 minutes, et le solde explose dès le premier ticket. Pour 40 heures, indiquez 2400.

La règle de résolution est ET, pas OU

Décider quand un ticket appartient à un contrat est l'endroit où l'on voit le plus de mauvaises configurations. La logique combine l'entité (toujours obligatoire, 1:1) avec des critères optionnels de groupe et de catégorie :

Critères sur le contratRègle appliquéeLe ticket rejoint le contrat quand
AucunEntité seuleToujours (il suffit d'appartenir à l'entité du contrat)
Groupes seulsAppartenance dans le typeUn groupe attribué figure dans la liste du contrat
Catégories seulesAppartenance dans le typeLa catégorie ITIL figure dans les règles du contrat
Groupes ET catégoriesAND entre les typesGroupe dans la liste ET catégorie dans les règles (les deux)

Autrement dit : avec groupe ET catégorie renseignés, le ticket ne rejoint le contrat que si les deux correspondent. Configurer les deux en attendant un comportement « OU », c'est ce qui envoie la moitié des tickets dans la carte « Sans Contrat ».

actiontime est la source de vérité

Depuis la version 3.4.2, le module traite l'actiontime de la TicketTask comme source primaire. Le billable_time du chronomètre est maintenu synchronisé par trois couches : le hook onTicketTaskUpdated (temps réel, quand vous éditez la tâche via GLPI natif), le cron reconcile (toutes les 4h) et le bouton « Recalculer les Contrats ». En pratique, éditer la durée de la tâche directement dans GLPI se répercute sur le solde du contrat - à condition que le cron tourne.

Le cron doit être en mode externe

Voici l'erreur d'exploitation qui bloque tout. Contract Hours dépend de trois CronTasks : billing (clôture le cycle et remet à zéro la consommation), cleanup (ferme les chronomètres orphelins) et reconcile (synchronise et re-résout). Elles ne s'exécutent que si les actions automatiques de GLPI sont en mode externe (CLI). En mode GLPI (déclenché par l'accès web), un environnement à faible trafic peut passer des jours sans clôturer le cycle de facturation, tandis que les chronomètres oubliés accumulent des heures fantômes.

Assurez le cron système pointant vers l'exécuteur d'actions automatiques de GLPI :

# /etc/cron.d/glpi - declenche les actions automatiques de GLPI chaque minute (mode externe)
# Adaptez le chemin a votre installation (sous GLPI 11 le document root est public/)
* * * * * www-data /usr/bin/php /var/www/glpi/front/cron.php >/dev/null 2>&1

Confirmez ensuite que les tâches du module sont planifiées, en mode externe et avec une exécution récente :

-- Etat des 3 CronTasks de Contract Hours
SELECT name, frequency, mode, state, lastrun
FROM glpi_crontasks
WHERE itemtype = 'PluginNextoolContracthoursCron';
-- mode=2 (externe/CLI), state=1 (planifiee) et un lastrun recent = sain.

Diagnostiquer un solde divergent

Dans la maintenance des environnements clients, le schéma se répète : le contrat est bien créé, les premiers tickets débitent correctement et, trois semaines plus tard, le solde « se fige ». Dans presque tous les cas, la cause est la même : les actions automatiques étaient en mode web, pas en CLI externe. Sans le cron externe, billing ne clôture jamais le cycle, reconcile ne synchronise jamais les modifications faites sur la tâche et cleanup ne supprime jamais les chronomètres orphelins de celui qui a oublié le Stop le vendredi. C'est pourquoi la première requête que nous lançons dans un diagnostic Contract Hours porte sur glpi_crontasks, pas sur le solde : si le lastrun des trois tâches n'est pas récent, le problème n'est pas le module, c'est l'infrastructure GLPI.

Le cron confirmé, le second suspect est le billable_time désynchronisé de l'actiontime après une édition rétroactive. Mesurez l'écart avant de recalculer à l'aveugle :

-- Chronometres dont le temps facture a devie de l'actiontime de la tache (source de verite)
SELECT t.id, t.tickets_id,
       t.billable_time/60 AS min_facture,
       tt.actiontime/60   AS min_tache,
       (t.billable_time - tt.actiontime) AS diff_secondes
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;

En cas d'écarts, le bouton « Recalculer les Contrats » (onglet Données) exécute la synchronisation et recalcule la consommation. Pour vérifier le solde, en rappelant que les champs sont en minutes :

-- Solde par contrat actif (valeurs en minutes, converties en heures)
SELECT name, entities_id,
       total_hours/60 AS quota_h,
       used_hours/60  AS consomme_h,
       (total_hours - used_hours)/60 AS solde_h
FROM glpi_plugin_nextool_contracthours_contracts
WHERE is_active = 1
ORDER BY solde_h;

Audit, alertes et courtoisie

Chaque édition rétroactive de temps est enregistrée - qui a édité, quand et quelle était la valeur précédente - dans l'onglet Journaux. C'est la traçabilité dont vous êtes reconnaissant quand un client conteste une heure imputée. Les alertes se déclenchent par pourcentage de consommation (seuil par défaut 80 %) et par deux conditions de contrat : expiré (date de fin dans le passé) et heures dépassées. Chaque condition peut être réglée sur « ne rien faire », « alerte » (popup jaune que l'on peut fermer) ou « blocage ». Une règle fine s'applique : le blocage rouge n'agit qu'à la création de ticket par le centre d'assistance (Service Catalog) ; un ticket déjà ouvert n'est jamais bloqué, il reçoit seulement l'alerte. Depuis la version 3.6 il existe aussi le bouton « non facturable » (courtoisie) : le temps est comptabilisé mais ne débite pas le solde - pratique pour une intervention que vous voulez enregistrer sans entamer le quota du client.

Transformer des heures de ticket en facturation défendable - avec un solde, un relevé et un tableau de bord que le client comprend - est exactement ce que résout le module Contract Hours du plugin NexTool. Si vous maintenez GLPI et clôturez encore le mois dans un tableur, parlez-nous.


Ce contenu a été produit avec l'aide de l'intelligence artificielle et révisé par l'équipe Nextool Solutions.

Questions fréquentes

En minutes. Pour 40 heures, indiquez 2400. C'est l'erreur de configuration la plus courante : saisir « 40 » crée un contrat de 40 minutes et le solde explose dès le premier ticket.

Presque toujours parce que les actions automatiques de GLPI ne sont pas en mode externe (CLI). Les CronTasks billing, cleanup et reconcile ne tournent pas, le cycle ne se clôture pas et les éditions de tâche ne se synchronisent pas. Vérifiez lastrun dans glpi_crontasks et configurez le cron système pointant vers front/cron.php.

Par entité (relation 1:1 obligatoire) combinée à des critères optionnels de groupe et de catégorie. Avec les deux renseignés, la logique est ET (AND) : le groupe attribué et la catégorie doivent tous deux correspondre. Si un seul est configuré, il suffit. Sans critère, tout ticket de l'entité entre.

Oui. L'actiontime de la tâche est la source de vérité ; le hook onTicketTaskUpdated synchronise billable_time en temps réel et le cron reconcile corrige tout écart toutes les 4h. S'il ne l'a pas reflété, lancez « Recalculer les Contrats » dans l'onglet Données.

Non. Le module est GLPI 11 uniquement. Sous GLPI 10, vous mesurez le temps avec le champ natif de durée ou avec ActualTime, mais le contrôle du solde par contrat exige la migration vers GLPI 11.

Oui, depuis la version 3.6. Le bouton « non facturable » par tâche comptabilise le temps sans débiter le solde du contrat - pratique pour une intervention de courtoisie que vous voulez enregistrer sans entamer le quota.

Besoin d'aide ?