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 changeris_paid,requires_document,requires_approval,accrual_based,max_days_per_year - Types système : seuls
is_active(pour masquer dans l'UI employé) etname_frpeuvent ê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
codedoit 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
tenantIdpour 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, §Cachingdocs/standards/database-guidelines.md— §Multi-tenant repository pattern, §Tenant isolationdocs/standards/api-guidelines.md— §PATCH semantics, §List endpoints, §Standard envelopedocs/standards/react-typescript-guidelines.md— §List components, §Toggle/Switch patterndocs/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)