Scénario CP-018 : Employé hors ligne + Hiérarchie des responsables +¶
Module
de transition PRE_HIRE : plan de contrôle Tranche
: Tranche 5 de l'architecture (dissociée de CP-005) Contexte de marque
: Papillon HR Suite (priorité au mobile, priorité au hors ligne, expérience utilisateur conviviale) Priorité
: 5 Dépend de
: CP-017 Complexité estimée
: M
Objectif¶
Compléter l'expérience employé avec la création « offline-first » (file d'attente IndexedDB + synchronisation), l'historisation de la hiérarchie des managers, la liste des employés hors ligne et la transition automatique planifiée de PRE_HIRE vers ACTIVE. Cette story permet au module employé de fonctionner sans connectivité et garantit que les changements de manager sont vérifiables avec des plages de dates.
Portée du backend¶
Entités et enregistrements¶
Entité ManagerHistory (base de données du tenant) :
| Field | Type | Constraints |
|---|---|---|
| id | UUID v7 | PK |
| tenant_id | UUID | NOT NULL |
| employee_id | UUID | NOT NULL, FK to Employee |
| manager_id | UUID | NOT NULL, FK to Employee |
| start_date | LocalDate | NOT NULL |
| end_date | LocalDate | Nullable |
| ### Couche de référentiel |
ManagerHistoryRepository.java— Prise en compte du tenant :findByEmployeeId(UUID) → List<MANAGERHISTORY>findByEmployeeIdAndDate(UUID, LocalDate) → Optional<MANAGERHISTORY>— Recherche du responsable en poste à une date donnée (end_date est null ou >= date)
Couche de service¶
EmployeeService.java (ajouts à CP-017) :
- update(UUID, UpdateEmployeeRequest) — Amélioré : si le responsable a changé, crée une entrée ManagerHistory (ferme l'ancienne avec end_date=today, en crée une nouvelle avec start_date=today). Publie l'événement ManagerChanged en plus de EmployeeUpdated.
PreHireTransitionJob.java (@Scheduled) :
- S'exécute quotidiennement à 00 h 01, heure locale du tenant (conformément à FR-CP-051)
- Requêtes :
SELECT * FROM employees WHERE status = 'PRE_HIRE' AND hire_date <= CURRENT_DATE AND tenant_id = ? - Pour chaque employé correspondant : met à jour le statut sur ACTIVE, définit status_effective_date = hire_date
- Publie l'événement
EmployeeStatusChangedpour chaque transition - Journaux : « Transition de {count} employés de PRE_HIRE à ACTIVE pour le tenant {tenantId} »
- Doit s'exécuter pour TOUS les tenants (itérer la liste des tenants)
Migrations Flyway (base de données du tenant)¶
V006__create_manager_history.sql— tablemanager_history
Considérations multi-tenant¶
- ManagerHistory se trouve dans la base de données des locataires et est propre à chaque locataire
- La tâche de transition PRE_HIRE doit parcourir tous les locataires actifs et s'exécuter dans le contexte de chaque locataire
Portée du frontend¶
Création d'employés hors ligne¶
Création d'employés (hors ligne) : 1. Même formulaire que CP-017. UUID v7 généré côté client. 2. Soumettre --> enregistré dans la file d'attente de synchronisation IndexedDB. Le matricule affiche « Numéro en attente ». 3. Notification : « Employé enregistré hors ligne » 4. À la reconnexion --> la file d'attente de synchronisation envoie une requête POST /api/v1/employees --> le serveur attribue un matricule --> IndexedDB mis à jour --> matricule affiché
Comportement de la file d'attente de synchronisation :
**
- Utilise le modèle de file d'attente de synchronisation hors ligne de docs/standards/offline-sync-guidelines.md
- Les demandes de création d'employés sont mises en file d'attente dans IndexedDB avec syncStatus: 'pending'
- Une fois la connexion rétablie : la file d'attente est traitée selon le principe FIFO
- En cas de synchronisation réussie : l'enregistrement local est mis à jour avec la réponse du serveur (matricule, etc.)
- En cas d'échec de la synchronisation (conflit 409) : marquer comme syncStatus: 'conflict' pour une résolution manuelle
Mode hors ligne de la liste des employés¶
Liste des employés hors ligne :**
- La liste des employés est lue à partir d'IndexedDB en mode hors ligne - Badge orange dans l'en-tête : « Hors ligne -- données locales » - Tous les filtres et recherches s'effectuent localement sur les données d'IndexedDB - Les employés en attente de synchronisation sont signalés par une bordure gauche orange + une petite icône de synchronisation
États de l'interface utilisateur (ajouts à CP-017)¶
| State | Behavior | French Copy |
|---|---|---|
| Offline (list) | Full list from IndexedDB, orange header badge | "Hors ligne -- donnees locales" |
| Success (create offline) | Orange toast | "Employe enregistre hors ligne" |
| Pending sync | Employee card: orange left border + sync icon | — |
| Sync success | Green toast | "Employe synchronise" |
| Sync conflict | Red badge on employee card | "Conflit de synchronisation" |
Mentions figurant sur la carte d'employé (en complément du formulaire CP-017) :
| State | Visual |
|---|---|
| Pending sync | Orange left border + small sync icon |
| ### Composants (ajouts) |
Aucun nouveau composant de page. Améliorations apportées aux composants CP-017 existants :
- EmployeeListPage.tsx — Ajout d'une source de données hors ligne, d'un badge orange et d'indicateurs d'état de synchronisation
- EmployeeCard.tsx — Ajout d'un état visuel « synchronisation en attente »
- EmployeeFormPage.tsx — Ajout d'un chemin d'enregistrement hors ligne (file d'attente IndexedDB)
Critères d'acceptation¶
AC-035: Create employee offline
Given I am RH Manager and offline
When I submit the employee form with valid data
Then the employee is saved to IndexedDB with UUID v7
And the matricule shows "Numero en attente"
And I see the toast "Employe enregistre hors ligne"
And when connectivity returns, the employee syncs and receives a server-assigned matricule
AC-039: Manager hierarchy historization
Given employee "Jean" has manager "Moussa"
When I change Jean's manager to "Aminata"
Then ManagerHistory for Moussa gets end_date=today
And a new ManagerHistory for Aminata is created with start_date=today
And a ManagerChanged event is published
AC-044: Employee list works offline
Given I am offline
When I navigate to the employee list
Then I see employees loaded from IndexedDB
And I see an orange "Hors ligne" indicator
AC-045: PRE_HIRE automatic transition
Given employee "Jean" has status=PRE_HIRE and hire_date=2026-03-15
When the daily batch job runs on 2026-03-15
Then Jean's status transitions to ACTIVE automatically
OHADA et dispositions réglementaires¶
- Code du travail, art. 92.3 (Registre de l'employeur) : l'historique des responsables répond à l'exigence de traçabilité pour les rapports organisationnels.
- Les données des employés sont des données à caractère personnel (Loi 2013-450) : les données des employés stockées hors ligne dans IndexedDB constituent des informations à caractère personnel. L'application web progressive (PWA) ne doit pas conserver ces données après la déconnexion. IndexedDB est effacé lorsque l'utilisateur se déconnecte.
- AUDCIF, art. 24 : les changements de responsable sont archivés (ManagerHistory) et génèrent des entrées d'audit, satisfaisant ainsi à l'exigence de traçabilité.
Normes et conventions¶
docs/standards/java-Spring-guidelines.md— §Événements d'application, §Tâches planifiéesdocs/standards/database-guidelines.md— §Clés étrangères autoréférencéesdocs/standards/offline-sync-guidelines.md— §File d'attente de synchronisation IndexedDB, §Génération d'UUID v7 par le client, §Détection des conflits, §Modèle « Numéro en attente »docs/standards/react-typescript-guidelines.md— §Indicateurs hors ligne, §Badges d'état de synchronisation
Exigences de test¶
Tests unitaires¶
- ManagerHistory : gestion correcte des dates de début/fin lors d'un changement de responsable
- ManagerHistory : la première affectation de responsable crée une entrée sans entrée précédente à fermer
- PreHireTransitionJob : fait passer les employés concernés à l'état ACTIVE
- PreHireTransitionJob : ignore les employés dont la date d'embauche est future
- PreHireTransitionJob : publie l'événement EmployeeStatusChanged
Tests d'intégration¶
- PATCH /api/v1/employees/{id} avec changement de responsable → entrées ManagerHistory créées correctement
- PreHireTransitionJob : test de bout en bout avec une base de données réelle
- Synchronisation hors ligne : UUID v7 généré par le client accepté par le serveur
- Synchronisation hors ligne : détection des conflits sur une version obsolète
Ce que l'assurance qualité va valider¶
- Le formulaire de création d'employé fonctionne hors ligne (enregistré dans IndexedDB)
- Le matricule affiche « Numéro en attente » hors ligne, le numéro réel après la synchronisation
- La liste des employés fonctionne hors ligne avec un badge orange
- Les employés en attente de synchronisation affichent une bordure orange + l'icône de synchronisation
- Après la reconnexion, les employés sont synchronisés et le matricule apparaît
- Le changement de responsable crée des entrées d'historique (vérifier via la piste d'audit)
- Les employés PRE_HIRE passent à ACTIVE à la date d'embauche
Hors du périmètre¶
- Interface utilisateur de résolution des conflits de synchronisation (composant ConflictResolver) — décrite dans la conception §16.2, à implémenter si nécessaire. Pour l'instant, les conflits sont signalés mais ne sont pas résolus automatiquement.
- Dossier complet de l'employé (photo, contacts d'urgence, coordonnées bancaires, documents) — module HR Core
- Importation groupée d'employés — V2
Définition de « terminé »¶
- [ ] La création hors ligne d'un employé est enregistrée dans IndexedDB et synchronisée lors de la reconnexion
- [ ] Les entrées ManagerHistory sont créées correctement lors d'un changement de responsable
- [ ] La tâche planifiée PRE_HIRE→ACTIVE s'exécute quotidiennement et fait passer les employés concernés du statut PRE_HIRE au statut ACTIVE
- [ ] La liste des employés fonctionne hors ligne avec la source de données IndexedDB
- [ ] Tous les critères d'acceptation sont satisfaits manuellement
- [ ] Les tests unitaires sont écrits et réussis
- [ ] Les tests d'intégration sont écrits et réussis
- [ ] L'isolation multi-tenant est vérifiée (aucune fuite de données entre tenants)
- [ ] IndexedDB est effacé à la déconnexion (aucune persistance des données à caractère personnel)
- [ ] Pas de
anyen TypeScript — tous les types sont explicites - [ ] Pas de types bruts en Java — tous les génériques sont explicites
- [ ] Des entrées de piste d'audit sont créées pour les changements de responsable et les transitions de statut