Aller au contenu

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, alors partialCarryOverMaxDays doit être > 0
  • Si carryOverPolicy != PARTIAL_CARRY_OVER, partialCarryOverMaxDays est ignoré (mis à null)
  • deductionUnit doit être l'un de : JOURS_CALENDAIRES, JOURS_OUVRES, JOURS_OUVRABLES
  • referencePeriodType doit être l'un de : CALENDAR_YEAR, ANNIVERSARY
  • carryOverPolicy doit être l'un de : STRICT_EXPIRY, PARTIAL_CARRY_OVER, UNLIMITED
  • Changer deductionUnit N'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) pour partialCarryOverMaxDays — affiché conditionnellement quand carryOverPolicy = 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, §Enums
  • docs/standards/database-guidelines.md — §Multi-tenant repository pattern, §Tenant isolation
  • docs/standards/api-guidelines.md — §PATCH semantics, §Standard envelope
  • docs/standards/react-typescript-guidelines.md — §Form components, §Select pattern
  • docs/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() ignore partialCarryOverMaxDays quand 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