Story AB-006 : Configuration des paramètres d'absence¶
Module : absence-management Slice : VS-AB-017 from architecture Brand context : [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority : 6 Depends on : AB-002 Can develop concurrently with : AB-007, AB-008 Merge order : N'importe quel ordre avec AB-007, AB-008 Estimated complexity : S PRD User Stories : N/A - Technical Story
Objectif¶
Permettre aux responsables RH et DG de configurer les paramètres d'absence au niveau tenant (unité de décompte, type de période de référence, politique de report) via une API backend et une page de paramétrage frontend. Les paramètres sont initialisés avec les valeurs par défaut légales CI à l'activation du module.
Périmètre Backend¶
Entités & Records¶
AbsenceSettings (de AB-002, tenant DB) :
| Field | Type | Constraints |
|---|---|---|
| id | UUID v7 | PK, server-generated |
| tenant_id | UUID | NOT NULL |
| deduction_unit | DeductionUnit enum | NOT NULL, default JOURS_CALENDAIRES |
| reference_period_type | ReferencePeriodType enum | NOT NULL, default CALENDAR_YEAR |
| carry_over_policy | CarryOverPolicy enum | NOT NULL, default STRICT_EXPIRY |
| partial_carry_over_max_days | BigDecimal | Nullable, utilisé quand PARTIAL_CARRY_OVER |
| created_at | Instant | NOT NULL |
| updated_at | Instant | NOT NULL |
Enums :
public enum DeductionUnit { JOURS_CALENDAIRES, JOURS_OUVRES, JOURS_OUVRABLES }
public enum ReferencePeriodType { CALENDAR_YEAR, ANNIVERSARY }
public enum CarryOverPolicy { STRICT_EXPIRY, PARTIAL_CARRY_OVER, UNLIMITED }
DTOs :
public record AbsenceSettingsResponse(
UUID id,
String deductionUnit,
String referencePeriodType,
String carryOverPolicy,
BigDecimal partialCarryOverMaxDays
) {}
public record UpdateAbsenceSettingsRequest(
@Nullable String deductionUnit,
@Nullable String referencePeriodType,
@Nullable String carryOverPolicy,
@Nullable BigDecimal partialCarryOverMaxDays
) {}
Couche Repository¶
AbsenceSettingsRepository.java:findByTenantId(UUID tenantId) → Optional<AbsenceSettings>
Couche Service¶
AbsenceSettingsService.java :
- getSettings(UUID tenantId) → AbsenceSettings — Retourne les paramètres actuels ou initialise les valeurs par défaut si aucun n'existe
- updateSettings(UUID tenantId, UpdateAbsenceSettingsRequest request) → AbsenceSettings — Mise à jour partielle, publie un événement AbsenceSettingsUpdated
Endpoints API¶
| Method | Path | Request Body | Response | Auth |
|---|---|---|---|---|
| GET | /api/v1/absence/config/settings | — | AbsenceSettingsResponse | HR_MANAGER, DG |
| PATCH | /api/v1/absence/config/settings | UpdateAbsenceSettingsRequest | AbsenceSettingsResponse | HR_MANAGER, DG |
Règles de validation¶
- Si
carryOverPolicy=PARTIAL_CARRY_OVER, alorspartialCarryOverMaxDaysdoit être > 0 - Si
carryOverPolicy!=PARTIAL_CARRY_OVER,partialCarryOverMaxDaysest ignoré (mis à null) deductionUnitdoit être l'un de :JOURS_CALENDAIRES,JOURS_OUVRES,JOURS_OUVRABLESreferencePeriodTypedoit être l'un de :CALENDAR_YEAR,ANNIVERSARYcarryOverPolicydoit être l'un de :STRICT_EXPIRY,PARTIAL_CARRY_OVER,UNLIMITED- Changer
deductionUnitN'altère PAS les valeurs jours ouvrables stockées (conversion d'affichage uniquement)
Considérations multi-tenant¶
- AbsenceSettings est stocké dans la base tenant, scopé par
tenant_id - Toutes les requêtes doivent inclure le filtre
tenant_id - Une ligne par tenant — sémantique upsert à l'initialisation
Périmètre Frontend¶
Pages & Routes¶
/absences/parametres/types— Page de paramétrage (S11 dans la spec de design, section politique générale)
Composants¶
AbsenceSettingsPage.tsx— Page conteneur pour tous les paramètres d'absence- Composant Select pour
deductionUnit(3 options) - Composant Select pour
referencePeriodType(2 options) - Composant Select pour
carryOverPolicy(3 options) - Input (
type=number) pourpartialCarryOverMaxDays— affiché conditionnellement quandcarryOverPolicy=PARTIAL_CARRY_OVER - Button (
variant="primary") pour sauvegarder
États UI (TOUS REQUIS)¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | Skeleton shimmer pour 3 champs select | — |
| Empty | N/A (paramètres toujours initialisés) | — |
| Error | Toast erreur à l'échec de la sauvegarde | "Impossible d'enregistrer les paramètres. Réessayez." |
| Offline | Paramètres lus depuis le cache, sauvegarde bloquée | "Cette action nécessite une connexion internet" |
| Success | Toast à la sauvegarde | "Paramètres enregistrés" |
Comportement responsive¶
- 360px (mobile) : Colonne unique, selects pleine largeur, bouton de sauvegarde pleine largeur
- 768px (tablet) : Même mise en page, marges légèrement plus larges
- 1024px+ (desktop) : Conteneur carte dans la mise en page avec sidebar, max-width 600px
Interactions¶
- Titre de page : "Paramètres des congés"
- Label unité de décompte : "Unité de décompte"
- Options : "Jours calendaires", "Jours ouvrés", "Jours ouvrables"
- Label période de référence : "Période de référence"
- Options : "Année civile (jan-déc)", "Anniversaire d'embauche"
- Label report : "Report de congés"
- Options : "Expiration stricte (loi)", "Report partiel", "Report illimité"
- Label nombre max de jours : "Nombre maximum de jours reportables" (affiché uniquement quand report = "Report partiel")
- Bouton de sauvegarde : "Enregistrer"
Critères d'acceptation¶
AC-017: GET returns current settings
Given the ABSMGT module is active for a CI tenant
When HR calls GET /api/v1/absence/config/settings
Then the response contains current settings (reference period, carry-over, deduction unit) with CI defaults if never modified
AC-018: PATCH updates settings
Given HR is logged in with HR_MANAGER role
When they PATCH /api/v1/absence/config/settings with { deductionUnit: "JOURS_OUVRES" }
Then the setting is updated and the response reflects the new value
AC-019: CI defaults seeded at module activation
Given a new CI tenant activates the ABSMGT module
When the module initializes
Then AbsenceSettings is created with deductionUnit=JOURS_CALENDAIRES, referencePeriodType=CALENDAR_YEAR, carryOverPolicy=STRICT_EXPIRY
AC-020: Changing deduction unit does not alter stored values
Given employee balances are stored in jours ouvrables (source of truth)
When HR changes the deduction unit from JOURS_CALENDAIRES to JOURS_OUVRES
Then the stored annual_balance_ouvrables values remain unchanged (only display conversion changes)
Règles OHADA & réglementaires¶
- FR-ABS-004 : Le tenant configure la préférence d'unité de décompte. Le stockage interne est TOUJOURS en jours ouvrables.
- FR-ABS-020 : Période de référence : CALENDAR_YEAR (par défaut) ou ANNIVERSARY.
- FR-ABS-022 : Politiques de report : STRICT_EXPIRY (par défaut selon la loi CI), PARTIAL_CARRY_OVER, UNLIMITED.
- FR-ABS-023 : Le paiement compensatoire au lieu de congés non pris est INTERDIT pendant l'emploi (Art. 25.12 CT 2015).
Standards & conventions¶
docs/standards/java-spring-guidelines.md— §Entities, §Service layer, §Enumsdocs/standards/database-guidelines.md— §Multi-tenant repository pattern, §Tenant isolationdocs/standards/api-guidelines.md— §PATCH semantics, §Standard envelopedocs/standards/react-typescript-guidelines.md— §Form components, §Select patterndocs/standards/design-system.md— §Select, §Button, §Toast components
Exigences de test¶
Tests unitaires¶
- AbsenceSettingsService :
getSettings()initialise les valeurs par défaut quand aucune n'existe - AbsenceSettingsService :
updateSettings()valide que PARTIAL_CARRY_OVER nécessite max days > 0 - AbsenceSettingsService :
updateSettings()ignorepartialCarryOverMaxDaysquand la politique n'est pas PARTIAL_CARRY_OVER - AbsenceSettingsService :
updateSettings()rejette les valeurs d'enum invalides
Tests d'intégration¶
- GET /api/v1/absence/config/settings — retourne les valeurs par défaut initialisées pour un nouveau tenant
- PATCH /api/v1/absence/config/settings — met à jour et retourne les nouvelles valeurs
- PATCH avec carryOverPolicy invalide + partialCarryOverMaxDays manquant → 422
- Isolation tenant : les paramètres du tenant A ne sont pas visibles pour le tenant B
Ce que la QA validera¶
- La page de paramétrage s'affiche correctement sur tous les breakpoints (360px, 768px, 1024px)
- Les 3 selects sont remplis avec les options françaises correctes
- L'input partialCarryOverMaxDays s'affiche/se masque en fonction de la sélection de report
- Le bouton sauvegarder déclenche un toast en cas de succès et d'erreur
- L'état offline bloque la sauvegarde avec le message approprié
Hors périmètre¶
- Traitement du report en fin de période (V2)
- Recalcul des soldes de congé quand l'unité de décompte change
- Paramètres par type de congé (AB-007 gère la configuration des types de congé)
- UI de journal d'audit pour les changements de paramètres (V2)
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) — sauvegarde bloquée 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