Aller au contenu

Moteur de workflow

Ce que fait le moteur de workflow

De nombreux processus dans Jigi ont besoin d'un circuit de validation à plusieurs étapes : une demande de congé validée par le manager, une demande de personnel validée en plusieurs paliers, une lettre d'offre nécessitant une double validation, etc. Plutôt que de réimplémenter cette logique dans chaque module, Jigi utilise un seul moteur de workflow générique et partagé, indépendant du type d'entité concerné.

Un module métier ne modélise donc jamais ses propres colonnes d'approbation (pas de PENDING_L1/L2/L3 ni de table *_approvals propre) : il garde simplement une référence (workflow_instance_id) vers une exécution du moteur, et laisse celui-ci piloter tout le cycle de vie de l'approbation.

Le modèle en 4 tables

Table Rôle
workflow_definitions Un circuit d'approbation nommé et réutilisable, rattaché à un type d'entité (entity_type). Modèle global (tenant_id NULL) ou personnalisé par tenant.
workflow_step_definitions Les étapes ordonnées d'un circuit. Chaque étape déclare qui doit approuver via un approver_type.
workflow_instances Une exécution concrète d'un circuit pour une entité précise, identifiée par (entity_type, entity_id).
workflow_step_instances L'état de chaque étape d'une exécution donnée.

Ces quatre tables vivent dans un schéma PostgreSQL dédié (workflow), pensé pour pouvoir être extrait plus tard en service indépendant si besoin.

Comment un module utilise le moteur

  1. Définir un circuit d'approbation une fois (une définition + ses étapes). Par exemple pour les congés, une politique de congé génère la configuration d'approbation sous forme de workflow.
  2. Démarrer : quand une demande est soumise, le module appelle le moteur avec (type d'entité, id de l'entité, définition, initiateur). Le moteur crée une instance (statut PENDING) et toutes les étapes associées, puis assigne la première étape à son approbateur.
  3. Référencer : le module stocke l'workflow_instance_id retourné sur sa propre ligne de données. Le statut métier du module (DRAFT/PENDING/APPROVED/REJECTED/CANCELLED) reste simple et reflète le statut de l'instance de workflow.
  4. Agir : les approbateurs valident ou rejettent une étape. Le moteur fait avancer current_step, applique la règle définie en cas de rejet, respecte les étapes optionnelles et les délais (due_at → passage en TIMED_OUT), puis fixe le statut final de l'instance.
  5. Réagir : à la clôture, le moteur déclenche un événement que le module d'origine consomme pour finaliser sa propre donnée (ex. créditer/débiter un solde de congé).

Qui approuve quoi : la résolution d'approbateur

Une étape ne référence jamais un utilisateur précis codé en dur (sauf cas explicite) : elle référence un rôle ou une relation organisationnelle, résolue au moment de l'exécution. Cela rend les circuits portables d'une société à l'autre.

Type d'approbateur Résolu en pratique vers
DIRECT_MANAGER Le manager direct de l'initiateur (organigramme)
DEPARTMENT_HEAD Le responsable du département de l'initiateur
HR_MANAGER Le(s) responsable(s) RH du tenant
ROLE N'importe quel utilisateur détenant le rôle indiqué
SPECIFIC_USER Un utilisateur précis (exception au principe de portabilité)

Règles métier appliquées par le moteur

  • Validation séquentielle stricte : seule l'étape actuellement active peut être approuvée ou rejetée — impossible de « sauter » une étape.
  • Anti-auto-approbation : l'initiateur d'une demande ne peut jamais approuver ou rejeter sa propre demande — il doit annuler la demande directement s'il change d'avis.
  • En cas de rejet, trois comportements possibles selon la configuration de l'étape :
    • STOP — le workflow s'arrête, la demande est rejetée définitivement ;
    • RETURN_TO_SUBMITTER — la demande repasse en brouillon pour être corrigée et resoumise ;
    • (sinon) l'étape est simplement ignorée et le workflow continue.
  • Une seule instance ouverte à la fois par entité : impossible de soumettre une nouvelle demande tant qu'une instance PENDING/IN_PROGRESS existe déjà pour cette même entité.
  • Escalade automatique : une tâche planifiée détecte les étapes dépassant leur délai (due_at) et les marque TIMED_OUT — une étape optionnelle en retard fait avancer le workflow, une étape obligatoire en retard le rejette.
  • Délégation : une étape en attente peut être réassignée à un autre approbateur (ex. absence du manager).
  • Étapes de notification pure : une étape peut être une simple notification (sans action attendue), automatiquement marquée NOTIFIED dès qu'elle est atteinte.
Détails techniques
  • Package : app.jigi.workflow (domain/ pour les entités et enums, engine/ pour la machine à états : WorkflowEngine, ApproverResolver, WorkflowPathResolver).
  • Enums clés : ApproverType (DIRECT_MANAGER, HR_MANAGER, DEPARTMENT_HEAD, ROLE, SPECIFIC_USER), StepStatus (PENDING, APPROVED, REJECTED, SKIPPED, TIMED_OUT, NOTIFIED), WorkflowStatus (PENDING, IN_PROGRESS, APPROVED, REJECTED, CANCELLED, RETURNED), OnRejection (STOP, RETURN_TO_SUBMITTER, autre = passer à l'étape suivante).
  • Un index unique partiel garantit au plus une instance ouverte (PENDING/IN_PROGRESS) par (entity_type, entity_id).
  • Le moteur ne gère aucun canal de notification lui-même : il publie des événements (assignation d'étape, clôture de workflow) que le module notification transforme en emails / notifications in-app — voir Notifications.
  • Exemples de entity_type réels : LEAVE/LEAVE_REQUEST, JOB_REQUISITION, OBJECTIVE, EMPLOYEE_ONBOARDING/OFFBOARDING, PURCHASE_REQUEST/SUPPLY_REQUEST, RESOURCE_BOOKING/BOOKING, OFFER_LETTER.
  • Administration des circuits : WorkflowAdminController / WorkflowAdminService — voir Workflows d'approbation pour l'usage fonctionnel côté administrateur RH.

FAQ

Un même type de demande peut-il avoir des circuits d'approbation différents selon la société ? Oui — une définition de workflow peut être globale (partagée) ou personnalisée par tenant.

Que se passe-t-il si l'approbateur désigné (ex. le manager direct) quitte l'entreprise en cours de validation ? L'étape en attente peut être déléguée à un autre utilisateur par un administrateur, sans perdre l'historique de la demande.

Un même circuit peut-il mélanger des étapes obligatoires et optionnelles ? Oui, chaque étape porte son propre indicateur is_optional, qui détermine le comportement en cas de délai dépassé.

Où voit-on l'historique complet d'une demande passée par le workflow ? Chaque étape (workflow_step_instance) conserve qui a agi, quand, et un commentaire éventuel — cet historique est visible depuis l'écran de suivi de la demande dans le module concerné.