Aller au contenu

Story CP-018: Employee Offline + Manager Hierarchy + PRE_HIRE Transition

Module: control-plane Slice: Slice 5 from architecture (split from CP-005) Brand context: Papillon HR Suite (mobile-first, offline-first, warm UX) Priority: 5 Depends on: CP-017 Estimated complexity: M

⚠️ Pre-existing event stub (2026-04-22) — The record class ManagerChanged was created ahead of this story on branch feat/control-plane-employee-events to unblock consumers (AB-003 approval chain, QR-002 attendance cache). This story still owns publishing it — wiring into the manager-hierarchy service, @TransactionalEventListener contract, and integration tests for old→new manager transitions. Do not re-create the record.


Objective

Complete the employee experience with offline-first creation (IndexedDB queue + sync), manager hierarchy historization, offline employee list, and the scheduled PRE_HIRE-to-ACTIVE automatic transition. This story makes the employee module work without connectivity and ensures manager changes are auditable with date ranges.


Backend Scope

Entities & Records

ManagerHistory entity (tenant DB):

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

Repository Layer

  • ManagerHistoryRepository.java — Tenant-aware:
  • findByEmployeeId(UUID) → List<ManagerHistory>
  • findByEmployeeIdAndDate(UUID, LocalDate) → Optional<ManagerHistory> — Find active manager at a given date (end_date is null or >= date)

Service Layer

EmployeeService.java (additions to CP-017): - update(UUID, UpdateEmployeeRequest) — Enhanced: If manager changed, creates ManagerHistory entry (closes previous with end_date=today, creates new with start_date=today). Publishes ManagerChanged event in addition to EmployeeUpdated.

PreHireTransitionJob.java (@Scheduled): - Runs daily at 00:01 tenant local time (per FR-CP-051) - Queries: SELECT * FROM employees WHERE status = 'PRE_HIRE' AND hire_date <= CURRENT_DATE AND tenant_id = ? - For each matching employee: updates status to ACTIVE, sets status_effective_date = hire_date - Publishes EmployeeStatusChanged event for each transition - Logs: "Transitioned {count} employees from PRE_HIRE to ACTIVE for tenant {tenantId}" - Must run for ALL tenants (iterate tenant list)

Flyway Migrations (Tenant DB)

  • V006__create_manager_history.sqlmanager_history table

Multi-Tenant Considerations

  • ManagerHistory is in the tenant DB and tenant-scoped
  • PRE_HIRE transition job must iterate all active tenants and run within each tenant's context

Frontend Scope

Offline Employee Creation

Employee creation (offline): 1. Same form as CP-017. UUID v7 generated client-side. 2. Submit --> saved to IndexedDB sync queue. Matricule shows "Numero en attente". 3. Toast: "Employe enregistre hors ligne" 4. On reconnect --> sync queue sends POST /api/v1/employees --> server assigns matricule --> IndexedDB updated --> matricule displayed

Sync queue behavior: - Uses the offline sync queue pattern from docs/standards/offline-sync-guidelines.md - Employee creation requests queued in IndexedDB with syncStatus: 'pending' - On connectivity restored: queue processed FIFO - On sync success: local record updated with server response (matricule, etc.) - On sync failure (409 conflict): mark as syncStatus: 'conflict' for manual resolution

Employee List Offline Mode

Offline employee list: - Employee list reads from IndexedDB when offline - Orange header badge: "Hors ligne -- donnees locales" - All filtering and search works locally against IndexedDB data - Pending-sync employees show orange left border + small sync icon

UI States (additions to 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"

Employee card states (addition to CP-017):

State Visual
Pending sync Orange left border + small sync icon

Components (additions)

No new page components. Enhancements to existing CP-017 components: - EmployeeListPage.tsx — Add offline data source, orange badge, sync status indicators - EmployeeCard.tsx — Add pending-sync visual state - EmployeeFormPage.tsx — Add offline save path (IndexedDB queue)


Acceptance Criteria

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 & Regulatory Rules

  • Code du Travail Art. 92.3 (Registre d'employeur): Manager history supports the traceability requirement for organizational reporting.
  • Employee data is personal data (Loi 2013-450): Offline-stored employee data in IndexedDB is PII. The PWA must not persist data after logout. IndexedDB is cleared on user sign-out.
  • AUDCIF Art. 24: Manager changes are historized (ManagerHistory) and produce audit entries, satisfying the traceability requirement.

Standards & Conventions

  • docs/standards/java-spring-guidelines.md — §Application Events, §Scheduled jobs
  • docs/standards/database-guidelines.md — §Self-referencing FKs
  • docs/standards/offline-sync-guidelines.md — §IndexedDB sync queue, §UUID v7 client generation, §Conflict detection, §"Numero en attente" pattern
  • docs/standards/react-typescript-guidelines.md — §Offline indicators, §Sync status badges

Testing Requirements

Unit Tests

  • ManagerHistory: correct start/end date management on manager change
  • ManagerHistory: first manager assignment creates entry with no previous to close
  • PreHireTransitionJob: transitions matching employees to ACTIVE
  • PreHireTransitionJob: skips employees with future hire_date
  • PreHireTransitionJob: publishes EmployeeStatusChanged event

Integration Tests

  • PATCH /api/v1/employees/{id} with manager change → ManagerHistory entries created correctly
  • PreHireTransitionJob: end-to-end with real DB
  • Offline sync: client-generated UUID v7 accepted by server
  • Offline sync: conflict detection on stale version

What QA Will Validate

  • Employee creation form works offline (saved to IndexedDB)
  • Matricule shows "Numero en attente" offline, real number after sync
  • Employee list works offline with orange badge
  • Pending-sync employees show orange border + sync icon
  • After reconnect, employees sync and matricule appears
  • Manager change creates history entries (verify via audit trail)
  • PRE_HIRE employees transition to ACTIVE on hire_date

Out of Scope

  • Sync conflict resolution UI (ConflictResolver component) — described in design §16.2, implement when needed. For now, conflicts are flagged but not auto-resolved.
  • Full employee dossier (photo, emergency contacts, bank info, documents) — HR Core module
  • Bulk employee import — V2

Definition of Done

  • [ ] Offline employee creation saves to IndexedDB and syncs on reconnect
  • [ ] ManagerHistory entries created correctly on manager change
  • [ ] PRE_HIRE→ACTIVE scheduled job runs daily and transitions matching employees
  • [ ] Employee list works offline with IndexedDB data source
  • [ ] All acceptance criteria pass manually
  • [ ] Unit tests written and passing
  • [ ] Integration tests written and passing
  • [ ] Multi-tenant isolation verified (no cross-tenant data leaks)
  • [ ] IndexedDB cleared on sign-out (no PII persistence)
  • [ ] No TypeScript any — all types explicit
  • [ ] No Java raw types — all generics explicit
  • [ ] Audit trail entries created for manager changes and status transitions