Aller au contenu

Story AB-007 : Configuration des types de congé

Module : absence-management Slice : VS-AB-018 from architecture Brand context : [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority : 7 Depends on : AB-002 Can develop concurrently with : AB-006, AB-008 Merge order : N'importe quel ordre avec AB-006, AB-008 Estimated complexity : S PRD User Stories : N/A - Technical Story


Objectif

Permettre aux responsables RH de visualiser et gérer les configurations de types de congé (activer/désactiver des types, consulter les propriétés). Les types système (ANNUAL, SICK, MATERNITY, etc.) sont immuables ; les tenants peuvent basculer actif/inactif pour les types non-système. La configuration est mise en cache dans Redis.


Périmètre Backend

Entités & Records

LeaveTypeConfig (de AB-002, tenant DB) :

Field Type Constraints
id UUID v7 PK, server-generated
tenant_id UUID NOT NULL
code String NOT NULL, unique par tenant, max 50
name_fr String NOT NULL, max 100
is_paid boolean NOT NULL
requires_document boolean NOT NULL
requires_approval boolean NOT NULL
accrual_based boolean NOT NULL
max_days_per_year BigDecimal Nullable
is_system boolean NOT NULL, default true
is_active boolean NOT NULL, default true
created_at Instant NOT NULL
updated_at Instant NOT NULL

7 types de congé par défaut CI (initialisés par tenant) :

Code name_fr is_paid requires_document requires_approval accrual_based max_days_per_year is_system Désactivable ?
ANNUAL Congé annuel true false true true 26 true Non
SICK Congé maladie true true true false null true Non
MATERNITY Congé maternité true true true false 98 true Non
PATERNITY Congé paternité true false true false 2 true Oui
EXCEPTIONAL Permission exceptionnelle true true true false 10 true Oui
UNPAID Congé sans solde false false true false null true Oui
UNJUSTIFIED Absence injustifiée false false false false null true Oui

DTOs :

public record LeaveTypeConfigResponse(
    UUID id,
    String code,
    String nameFr,
    boolean isPaid,
    boolean requiresDocument,
    boolean requiresApproval,
    boolean accrualBased,
    BigDecimal maxDaysPerYear,
    boolean isSystem,
    boolean isActive
) {}

public record UpdateLeaveTypeConfigRequest(
    @Nullable String nameFr,
    @Nullable Boolean isActive
) {}

Couche Repository

  • LeaveTypeConfigRepository.java :
  • findByTenantId(UUID tenantId) → List<LeaveTypeConfig>
  • findByTenantIdAndCode(UUID tenantId, String code) → Optional<LeaveTypeConfig>

Couche Service

LeaveTypeConfigService.java : - getLeaveTypes(UUID tenantId) → List<LeaveTypeConfig> — Lecture depuis le cache Redis (absence:leave-types:{tenantId}), fallback sur la base - updateLeaveType(UUID tenantId, String code, UpdateLeaveTypeConfigRequest request) → LeaveTypeConfig — Met à jour les champs non-système, invalide le cache Redis - seedDefaults(UUID tenantId) → void — Crée les 7 types de congé CI ; appelé à l'activation du module

Stratégie de cache : - Clé Redis : absence:leave-types:{tenantId} - TTL : 1 heure - Invalidé à chaque PATCH sur les types de congé - Pattern cache-aside : lire depuis le cache → miss → interroger la base → peupler le cache

Endpoints API

Method Path Request Body Response Auth
GET /api/v1/absence/config/leave-types LeaveTypeConfigResponse[] EMPLOYEE (actifs uniquement), HR_MANAGER (tous)
PATCH /api/v1/absence/config/leave-types/:code UpdateLeaveTypeConfigRequest LeaveTypeConfigResponse HR_MANAGER, DG

Règles de validation

  • Types système (is_system=true) : impossible de changer is_paid, requires_document, requires_approval, accrual_based, max_days_per_year
  • Types système : seuls is_active (pour masquer dans l'UI employé) et name_fr peuvent être modifiés
  • ANNUAL, SICK, MATERNITY ne peuvent pas être désactivés (toujours is_active=true)
  • Si tentative de modifier des champs protégés sur un type système → 422 avec "Les types de congé système ne peuvent pas être modifiés"
  • Si tentative de désactiver ANNUAL, SICK ou MATERNITY → 422 avec "Ce type de congé ne peut pas être désactivé"
  • Le paramètre de chemin code doit correspondre à un type de congé existant pour le tenant

Considérations multi-tenant

  • LeaveTypeConfig est stocké dans la base tenant, scopé par tenant_id
  • La clé de cache Redis inclut le tenantId pour l'isolation
  • Le rôle EMPLOYEE ne voit que les types is_active=true ; HR_MANAGER voit tous
  • Chaque tenant a sa propre copie des 7 types par défaut (configuration indépendante)

Périmètre Frontend

Pages & Routes

  • /absences/parametres/types — Section types de congé dans la page de paramétrage (S11 dans la spec de design)

Composants

  • LeaveTypeList.tsx — Liste de cartes/lignes de types de congé
  • Chaque ligne affiche :
  • Icône (icône du type de congé)
  • name_fr (nom du type de congé)
  • Badge is_paid ("Payé" / "Non payé")
  • Indicateur requires_document
  • Indicateur requires_approval
  • Toggle is_active (composant Switch)
  • Les types système affichent une icône cadenas — toggle désactivé pour ANNUAL, SICK, MATERNITY

États UI (TOUS REQUIS)

State Behavior French Copy
Loading Liste skeleton (7 lignes)
Empty N/A (toujours initialisé avec 7 types)
Error Toast à l'échec de la sauvegarde "Impossible de mettre à jour le type de congé."
Offline Lecture depuis le cache, toggle bloqué "Cette action nécessite une connexion internet"
Success Toast au toggle "Paramètres enregistrés"

Comportement responsive

  • 360px (mobile) : Liste de cartes, chaque carte affiche icône + nom + badge payé + toggle. Propriétés repliées.
  • 768px (tablet) : Même mise en page en cartes mais plus large, propriétés visibles en ligne.
  • 1024px+ (desktop) : Vue tableau dans la mise en page avec sidebar : Nom, Payé, Justificatif, Approbation, Actif.

Interactions

  • Titre de section : "Types de congé"
  • Tooltip icône cadenas sur les types système : "Type système — non modifiable"
  • Label du toggle actif/inactif : "Actif" / "Inactif"
  • Badge payé : "Payé" (vert) / "Non payé" (neutre)
  • Indicateur justificatif requis : "Justificatif requis"
  • Indicateur approbation requise : "Approbation requise"

Critères d'acceptation

AC-021: HR can view all leave types
Given the ABSMGT module is active
When HR calls GET /api/v1/absence/config/leave-types
Then all 7 CI leave types are returned with their properties (name, is_paid, requires_document, etc.)

AC-022: Cannot modify system type properties
Given leave type ANNUAL has is_system=true
When HR tries to PATCH with { isPaid: false }
Then the API returns 422 with error "Les types de congé système ne peuvent pas être modifiés"

AC-023: Custom types can be toggled active/inactive
Given a non-system leave type exists
When HR PATCHes with { isActive: false }
Then the type is deactivated and no longer shown to employees

AC-024: Config is cached in Redis
Given leave types have been loaded from the database
When the same GET request is made again
Then the response is served from Redis cache (no DB query)

Règles OHADA & réglementaires

  • 7 types de congé CI selon le catalogue B.1 :
  • ANNUAL : payé, basé sur l'accumulation, 2,2 jours ouvrables/mois
  • SICK : payé selon le barème CCI (100%/75%/50% par tranche d'ancienneté)
  • MATERNITY : payé 100%, 14 semaines (98 jours)
  • PATERNITY : payé, 2 jours
  • EXCEPTIONAL : payé, plafond de 10 jours/an (permission exceptionnelle)
  • UNPAID : non payé, pas d'accumulation
  • UNJUSTIFIED : non payé, implications disciplinaires
  • ANNUAL, SICK, MATERNITY sont légalement obligatoires — ne peuvent pas être désactivés

Standards & conventions

  • docs/standards/java-spring-guidelines.md — §Entities, §Service layer, §Caching
  • docs/standards/database-guidelines.md — §Multi-tenant repository pattern, §Tenant isolation
  • docs/standards/api-guidelines.md — §PATCH semantics, §List endpoints, §Standard envelope
  • docs/standards/react-typescript-guidelines.md — §List components, §Toggle/Switch pattern
  • docs/standards/design-system.md — §Switch, §Card, §Toast components

Exigences de test

Tests unitaires

  • LeaveTypeConfigService : seedDefaults() crée exactement 7 types avec les propriétés correctes
  • LeaveTypeConfigService : updateLeaveType() rejette la modification des champs protégés des types système
  • LeaveTypeConfigService : updateLeaveType() rejette la désactivation de ANNUAL, SICK, MATERNITY
  • LeaveTypeConfigService : getLeaveTypes() lit depuis le cache Redis en cas de cache hit
  • LeaveTypeConfigService : updateLeaveType() invalide le cache Redis après la mise à jour

Tests d'intégration

  • GET /api/v1/absence/config/leave-types — retourne les 7 types initialisés
  • GET en tant qu'EMPLOYEE — retourne uniquement les types is_active=true
  • PATCH /api/v1/absence/config/leave-types/ANNUAL avec { isPaid: false } → 422
  • PATCH /api/v1/absence/config/leave-types/UNPAID avec { isActive: false } → 200
  • Isolation tenant : les types du tenant A ne sont pas visibles pour le tenant B

Ce que la QA validera

  • La liste des types de congé s'affiche correctement sur tous les breakpoints (360px, 768px, 1024px)
  • Les types système affichent l'icône cadenas et le toggle désactivé pour ANNUAL, SICK, MATERNITY
  • Le toggle des types non-système fonctionne et déclenche un toast
  • Les badges payé/non payé s'affichent correctement
  • L'état offline bloque le toggle avec le message approprié

Hors périmètre

  • Création de types de congé personnalisés (V2)
  • Modification des propriétés des types système (is_paid, requires_document, etc.)
  • Flux d'approbation spécifiques par type de congé
  • Personnalisation des icônes/couleurs des types de congé
  • Configuration du taux d'accumulation par type de congé (utilise le taux global de AB-002)

Définition de Done

  • [ ] Tous les endpoints backend implémentés et retournant les réponses correctes
  • [ ] Toutes les pages frontend s'affichent correctement à 360px, 768px, 1024px
  • [ ] Les 5 états UI implémentés (loading, empty, error, offline, success)
  • [ ] Le micro-copy français correspond exactement à la spec de design
  • [ ] 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 (pas de fuite de données inter-tenant)
  • [ ] L'écriture + synchronisation offline fonctionne (Papillon) — toggle bloqué offline, lecture depuis le cache
  • [ ] Pas de TypeScript any — tous les types explicites
  • [ ] Pas de Java raw types — tous les generics explicites
  • [ ] Les réponses API suivent le format d'enveloppe standard
  • [ ] Entrées de piste d'audit créées pour chaque mutation de données
  • [ ] Cache Redis testé (hit, miss, invalidation)