Aller au contenu

Story AB-022 : Gestionnaire de Départ Employé (Backend)

Module : absence-management Tranche : VS-AB-016 de l'architecture Contexte de marque : [LES DEUX] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priorité : 22 Dépend de : AB-009, AB-010 Parallèle avec : AB-020, AB-021 Ordre de fusion : Ordre quelconque Complexité estimée : M PRD User Stories : US-ABS-010


Objectif

Écouter les événements EmployeeTerminated et EmployeeOffboarded du module Gestion des Employés / Control Plane, annuler automatiquement toutes les demandes de congé en attente, préserver les congés approuvés dans la période de préavis, calculer le solde de congé final et publier un LeaveBalanceSettlement à la Paie pour le calcul de l'indemnité compensatrice de congés payés. Cela garantit le règlement approprié du solde de congé lors du départ d'un employé, comme l'exige le droit du travail ivoirien.


Périmètre Backend

Entités et Enregistrements

Aucune nouvelle entité. Cette story ajoute un nouveau type de transaction de grand livre et des événements inter-modules.

Type de transaction d'entrée de grand livre (étendre l'enum) :

Valeur Description
SETTLEMENT Solde final à zéro lors du départ de l'employé — montant négatif égal au solde restant

Événements consommés :

// Depuis le module Gestion des Employés / Control Plane
public record EmployeeTerminated(
    UUID employeeId,
    UUID tenantId,
    LocalDate terminationDate,
    LocalDate noticeEndDate
) {}

public record EmployeeOffboarded(
    UUID employeeId,
    UUID tenantId,
    LocalDate effectiveDate
) {}

Événements publiés :

public record LeaveBalanceSettlement(
    UUID employeeId,
    UUID tenantId,
    BigDecimal remainingBalance,     // jours calendaires
    LocalDate lastAccrualDate,
    LocalDate terminationDate
) {}

DTOs :

public record LeaveBalanceSettlement(
    UUID employeeId,
    UUID tenantId,
    BigDecimal remainingBalanceCalendaires,
    LocalDate lastAccrualDate,
    LocalDate terminationDate,
    int cancelledPendingCount,
    int cancelledFutureApprovedCount,
    int preservedApprovedCount
) {}

Couche Repository

Aucun nouveau repository. Utilise les repositories existants d'AB-009 et AB-010 : - Repository LeaveEmployeeProfile (requêtes de solde) - Repository LeaveRequest (mises à jour de statut pour les annulations) - Repository de grand livre (création d'entrée SETTLEMENT)

Couche Service

EmployeeTerminationListener.java — @ApplicationModuleListener : - À la réception de EmployeeTerminated : 1. Annuler toutes les demandes de congé PENDING_APPROVAL et PENDING_DOCUMENT de l'employé avec motif "Depart de l'employe" et statut CANCELLED_BY_HR 2. Pour les congés APPROVED futurs : préserver ceux où start_date <= noticeEndDate ; annuler ceux où start_date > noticeEndDate avec motif "Depart de l'employe" 3. Déléguer à TerminationSettlementService pour le calcul du solde - À la réception de EmployeeOffboarded : 1. Même logique que EmployeeTerminated mais utilise effectiveDate comme date limite (pas de distinction de période de préavis — tous les congés futurs après effectiveDate sont annulés) 2. Déléguer à TerminationSettlementService pour le calcul du solde

TerminationSettlementService.java : - computeSettlement(UUID tenantId, UUID employeeId, LocalDate terminationDate) -> LeaveBalanceSettlement : 1. Calculer le solde annuel restant depuis la SUM du grand livre (jours ouvrables) 2. Convertir le solde restant en jours calendaires pour l'événement de règlement 3. Créer une entrée de grand livre SETTLEMENT avec un montant négatif égal au solde restant (remet le solde à zéro) 4. Mettre à jour le cache annual_balance_ouvrables à 0 5. Publier LeaveBalanceSettlement au module Paie via Application Event 6. Publier l'événement AuditEntryRequested avec les détails du règlement 7. Retourner le résumé du règlement

Points de terminaison API

Aucun nouveau endpoint. Cette story est entièrement pilotée par les événements (communication inter-modules via Spring Modulith Application Events).

Règles de validation

  • Les événements doivent porter des tenant_id et employee_id valides
  • L'employé doit exister dans le LeaveEmployeeProfile du module absence pour le tenant donné
  • Le montant du règlement doit être calculé en utilisant BigDecimal (jamais double/float)
  • Le montant de l'entrée de grand livre SETTLEMENT doit exactement remettre le solde restant à zéro
  • Idempotence : si un règlement a déjà été traité pour cet employé (une entrée SETTLEMENT existe), ignorer le re-traitement et journaliser un avertissement

Considérations Multi-Tenant

  • Les événements portent tenant_id — toutes les opérations limitées au tenant de l'employé partant
  • Les requêtes d'annulation de demande de congé filtrées par tenant_id + employee_id
  • Les opérations de grand livre utilisent le pattern de verrouillage pessimiste limité au tenant d'AB-012
  • Aucun accès aux données inter-tenant : le règlement n'affecte que les données de l'employé dans son tenant

Migrations Flyway

Aucune nouvelle migration. Le type de transaction SETTLEMENT fait déjà partie de l'enum depuis la configuration du grand livre d'AB-012.


Périmètre Frontend

Pages et Routes

N/A — Tranche backend uniquement. Les résultats du règlement sont visibles dans le détail du solde employé RH (AB-021) si l'enregistrement de l'employé est consulté avant le nettoyage complet lors de l'offboarding.

Composants

N/A

États UI (TOUS REQUIS)

N/A — Tranche backend uniquement.

Comportement Responsive

N/A

Interactions

N/A


Critères d'Acceptation

AC-070 : Les demandes en attente sont automatiquement annulées lors du départ
Étant donné qu'un employé a 2 demandes PENDING_APPROVAL et 1 demande PENDING_DOCUMENT
Quand un EmployeeTerminated est reçu pour cet employé
Alors les 3 demandes sont passées à CANCELLED_BY_HR avec motif "Depart de l'employe"
Et un événement AuditEntryRequested est publié pour chaque annulation

AC-071 : Les congés approuvés futurs sont traités correctement
Étant donné qu'un employé est licencié avec terminationDate=2026-04-30 et noticeEndDate=2026-05-15
Et l'employé a des congés APPROVED : un débutant le 2026-05-10 (dans le préavis) et un débutant le 2026-06-01 (après le préavis)
Quand le EmployeeTerminated est reçu
Alors le congé du 2026-05-10 est préservé (reste APPROVED)
Et le congé du 2026-06-01 est passé à CANCELLED_BY_HR avec motif "Depart de l'employe"

AC-072 : L'événement de règlement est publié à la Paie
Étant donné qu'un employé est licencié et a un solde annuel restant de 12.5 jours ouvrables
Quand le règlement est calculé
Alors une entrée de grand livre SETTLEMENT est créée avec amount=-12.5 (remettant le solde à zéro)
Et un LeaveBalanceSettlement est publié avec remainingBalance en jours calendaires
Et le cache annual_balance_ouvrables est mis à 0

Règles OHADA et Réglementaires

  • FR-ABS-024 : À la rupture du contrat, le solde de congé restant DOIT être payé en tant qu'indemnité compensatrice de congés payés. Ceci est obligatoire en vertu du droit du travail ivoirien — l'employeur ne peut pas confisquer les jours de congé non utilisés.
  • Art. 25.9 CT 2015 : Le paiement compensatoire est dû pour toute forme de rupture du contrat (démission, licenciement, accord mutuel, fin de contrat à durée déterminée).
  • FR-ABS-111 : Spécification de gestion de l'événement EmployeeTerminated — le module absence doit réagir aux événements du cycle de vie du module Gestion des Employés.
  • FR-ABS-120 : SETTLEMENT est un type de transaction de grand livre valide.
  • FR-ABS-123 : Grand livre immuable — l'entrée SETTLEMENT ne peut pas être modifiée après création.

Standards et Conventions

  • docs/standards/java-spring-guidelines.md — @ApplicationModuleListener, publication d'événements, frontières Spring Modulith
  • docs/standards/database-guidelines.md — BigDecimal pour les calculs de solde, isolation tenant, pattern grand livre en ajout uniquement
  • docs/standards/api-guidelines.md — N/A (aucun endpoint dans cette story)
  • docs/standards/multi-datasource-patterns.md — Accès aux données limité au tenant, verrouillage pessimiste pour la mutation du solde
  • docs/standards/offline-sync-guidelines.md — N/A (backend uniquement)

Exigences de Test

Tests Unitaires

  • EmployeeTerminationListener : annuler toutes les demandes PENDING_APPROVAL lors du départ
  • EmployeeTerminationListener : annuler toutes les demandes PENDING_DOCUMENT lors du départ
  • EmployeeTerminationListener : préserver les congés APPROVED dans la période de préavis
  • EmployeeTerminationListener : annuler les congés APPROVED après la date de fin du préavis
  • EmployeeTerminationListener : gérer EmployeeOffboarded (pas de distinction de période de préavis)
  • TerminationSettlementService : calculer le solde restant correct depuis le grand livre
  • TerminationSettlementService : créer l'entrée SETTLEMENT qui remet le solde à zéro
  • TerminationSettlementService : convertir les ouvrables en calendaires pour l'événement de règlement
  • TerminationSettlementService : idempotence — ignorer si SETTLEMENT existe déjà

Tests d'Intégration

  • Flux complet : publier EmployeeTerminated, vérifier les demandes en attente annulées, règlement créé, événement publié
  • Flux complet : publier EmployeeOffboarded, vérifier le même comportement avec la date limite effectiveDate
  • Isolation tenant : l'événement de départ pour le tenant A n'affecte pas les employés du tenant B
  • Idempotence : traiter le même événement de départ deux fois ne crée pas d'entrées SETTLEMENT en double
  • Vérifier que le payload de LeaveBalanceSettlement contient le remainingBalance correct en jours calendaires

Ce que QA Validera

  • Après l'événement de départ : vérifier dans AB-021 (détail du solde RH) que le solde est à 0 et que l'entrée SETTLEMENT est visible dans le grand livre
  • Vérifier que les demandes annulées affichent le motif "Depart de l'employe" dans l'historique des demandes
  • Vérifier que les congés préservés (dans la période de préavis) restent visibles et APPROVED

Hors Périmètre

  • Calcul de la valeur monétaire du règlement — responsabilité du module Paie (consomme LeaveBalanceSettlement)
  • Procédures de licenciement pour inaptitude — Gestion des Employés V2
  • Gestion du retour anticipé pour les congés en cours lors du départ — V2 (le MVP actuel suppose que le congé doit se terminer avant la date effective du départ ou est géré manuellement)
  • Règlement partiel (proratisation pour un départ en milieu de mois) — V2
  • Réintégration de l'employé (inversion d'un règlement) — V2
  • Notification frontend du règlement de départ — les RH voient les résultats dans AB-021 détail du solde

Définition de Done

  • [ ] Tous les endpoints backend implémentés et retournant les réponses correctes — N/A (piloté par événements, aucun endpoint)
  • [ ] Toutes les pages frontend s'affichent correctement à 360px, 768px, 1024px — N/A (backend uniquement)
  • [ ] Les 5 états UI implémentés — N/A (backend uniquement)
  • [ ] La micro-copie française correspond exactement à la spécification design — N/A (backend uniquement, le motif d'annulation est une chaîne française codée en dur)
  • [ ] Tous les critères d'acceptation passent manuellement
  • [ ] Tests unitaires écrits et passants
  • [ ] Tests d'intégration écrits et passants
  • [ ] Isolation multi-tenant vérifiée (aucune fuite de données inter-tenant)
  • [ ] Synchronisation hors ligne (Papillon) — N/A (backend uniquement)
  • [ ] Pas de any TypeScript — N/A (backend uniquement)
  • [ ] Pas de types bruts Java — tous les génériques explicites
  • [ ] Les réponses API suivent le format d'enveloppe standard — N/A (aucun endpoint)
  • [ ] Entrées de piste d'audit créées pour chaque mutation de données