Cas QR-004 : Configuration du module + Tranches d'heures supplémentaires + Intégration de la conformité¶
@assignee: @angeisaac-ui
Module : qr-attendance.
Segment : QR-T1-003 + QR-T5-006.
Contexte de la marque : [LES DEUX] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite.
Priorité : 4.
Dépend de : QR-001, QR-004b (garantit l'existence d'AttendanceModuleConfig via le listener d'activation du module).
Peut être développé en parallèle avec : QR-002, QR-003, QR-005.
Ordre de fusion : Avant QR-014 (le télétravail nécessite une configuration), QR-018 (les heures supplémentaires nécessitent des tranches).
Complexité estimée : M.
PRD User Stories : US-QR-013.
Objectif¶
Fournir aux RH une interface de configuration pour le module QR Attendance : paramètres généraux (tolérance de retard, jour de début de la semaine pour les heures supplémentaires, heure de fermeture automatique, limites du télétravail), configuration des tranches d'heures supplémentaires CI (avec des valeurs par défaut imposées par la loi), dérogations aux tolérances par service, et un assistant d'intégration de conformité qui enregistre l'obligation de consultation des délégués du personnel. L'assistant d'intégration est un flux d'activation en 3 étapes : Conformité -> Créer un site -> Créer un planning.
Portée du backend¶
Migrations Flyway¶
⚠️ Les tables de référence Platform DB (
country_attendance_config_defaults,country_overtime_band_defaults) sont créées par CP-001bis — pas dans ce story.
V001_006__attendance_overtime_config.sql :
CREATE TABLE attendance_overtime_config (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
band_code VARCHAR(20) NOT NULL,
weekly_threshold_start DECIMAL,
weekly_threshold_end DECIMAL,
night_start_time TIME,
night_end_time TIME,
country_code VARCHAR(3) NOT NULL DEFAULT 'CI',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, band_code, country_code)
);
V001_007__configuration_service_présence.sql
:
CREATE TABLE attendance_department_config (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
department_id UUID NOT NULL,
late_tolerance_minutes INTEGER,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, department_id)
);
V001_008__attendance_overtime_ci_defaults.sql (reservé pour commentaires, pas de seeding statique) :
-- CI overtime defaults are seeded at runtime by AttendanceModuleActivationListener
-- when it receives ModuleActivated(moduleCode="QRCONTR").
-- Source: country_attendance_config_defaults (platform DB) for tenant's countryCode.
-- See ADR-26 and QR-004b.
-- No static INSERT here — tenant isolation requires runtime copy, not shared rows.
```### Entités et enregistrements
**OvertimeConfig**
(base de données du tenant) :
| Field | Type | Constraints |
|-------|------|-------------|
| id | UUID | PK |
| tenantId | UUID | NOT NULL |
| bandCode | String | NOT NULL (BAND_1, BAND_2, BAND_3, NIGHT, SUNDAY_HOLIDAY) |
| weeklyThresholdStart | BigDecimal | Nullable |
| weeklyThresholdEnd | BigDecimal | Nullable |
| nightStartTime | LocalTime | Nullable |
| nightEndTime | LocalTime | Nullable |
| countryCode | String | NOT NULL, default "CI" |
| createdAt | OffsetDateTime | NOT NULL |
| updatedAt | OffsetDateTime | NOT NULL |
**DepartmentAttendanceConfig**
(base de données du tenant) :
| Field | Type | Constraints |
|----------------------|----------------|--------------------------------|
| id | UUID | PK |
| tenantId | UUID | NOT NULL |
| departmentId | UUID | NOT NULL, UNIQUE with tenantId |
| lateToleranceMinutes | Integer | Nullable |
| createdAt | OffsetDateTime | NOT NULL |
| updatedAt | OffsetDateTime | NOT NULL |
**Tranches horaires supplémentaires par défaut de CI :**
| Band Code | weeklyThresholdStart | weeklyThresholdEnd | nightStartTime | nightEndTime |
|----------------|----------------------|--------------------|----------------|--------------|
| BAND_1 | 40 | 46 | — | — |
| BAND_2 | 46 | 48 | — | — |
| BAND_3 | 48 | null (unlimited) | — | — |
| NIGHT | — | — | 21:00 | 05:00 |
| SUNDAY_HOLIDAY | — | — | — | — |
**DTO :**
```java
public record ModuleConfigResponse(
UUID id,
int defaultLateToleranceMinutes,
short overtimeWeekStartDay,
int minRemoteWorkAdvanceDays,
Integer maxRemoteWorkPerWeek,
Integer maxRemoteWorkPerMonth,
LocalTime autoCloseTime,
boolean deleguesConsultationDone,
LocalDate deleguesConsultationDate,
UUID deleguesPvDocumentId
) {}
public record UpdateModuleConfigRequest(
Integer defaultLateToleranceMinutes,
Short overtimeWeekStartDay,
Integer minRemoteWorkAdvanceDays,
Integer maxRemoteWorkPerWeek,
Integer maxRemoteWorkPerMonth,
LocalTime autoCloseTime
) {}
public record OvertimeConfigResponse(
List<OvertimeBandResponse> bands
) {}
public record OvertimeBandResponse(
String bandCode,
BigDecimal weeklyThresholdStart,
BigDecimal weeklyThresholdEnd,
LocalTime nightStartTime,
LocalTime nightEndTime,
String countryCode
) {}
public record UpdateOvertimeConfigRequest(
@NotEmpty List<OvertimeBandUpdate> bands
) {}
public record OvertimeBandUpdate(
@NotBlank String bandCode,
BigDecimal weeklyThresholdStart,
BigDecimal weeklyThresholdEnd,
LocalTime nightStartTime,
LocalTime nightEndTime
) {}
public record DepartmentConfigResponse(
UUID departmentId,
Integer lateToleranceMinutes
) {}
public record UpdateDepartmentConfigRequest(
Integer lateToleranceMinutes
) {}
public record ComplianceAcknowledgementRequest(
@NotNull boolean consultationDone,
LocalDate consultationDate,
UUID pvDocumentId
) {}
public record ComplianceStatusResponse(
boolean consultationDone,
LocalDate consultationDate,
UUID pvDocumentId,
boolean reminderShown,
int employeeCount
) {}
Couche de référentiel¶
OvertimeConfigRepository.java:findByTenantIdAndCountryCode(UUID tenantId, String countryCode) -> List<OVERTIMECONFIG>findByTenantIdAndBandCodeAndCountryCode(UUID tenantId, String bandCode, String countryCode) -> Optional<OVERTIMECONFIG>DepartmentAttendanceConfigRepository.java:findByTenantIdAndDepartmentId(UUID tenantId, UUID departmentId) -> Optional<DepartmentAttendanceConfig>
Couche de service¶
ConfigService.java:
getModuleConfig(UUID tenantId) -> ModuleConfigResponse— Renvoie la configuration du module. La configuration est garantie d'exister (créée parAttendanceModuleActivationListenerlors de l'activation du module — voir QR-004b). LèveModuleNotInitializedExceptionsi absente (état incohérent — devrait toujours exister si le module est activé).updateModuleConfig(UUID tenantId, UpdateModuleConfigRequest) -> ModuleConfigResponse— Mise à jour partielle des paramètres du modulegetOvertimeConfig(UUID tenantId) -> OvertimeConfigResponse— Renvoie toutes les plages d'heures supplémentaires pour le tenantupdateOvertimeConfig(UUID tenantId, UpdateOvertimeConfigRequest) -> OvertimeConfigResponse— Met à jour les seuils/horaires des plages d'heures supplémentairesgetDepartmentConfig(UUID tenantId, UUID departmentId) -> DepartmentConfigResponse— Renvoie la configuration spécifique au départementupdateDepartmentConfig(UUID tenantId, UUID departmentId, UpdateDepartmentConfigRequest) -> DepartmentConfigResponse— Ajoute ou met à jour la dérogation de tolérance du service
ComplianceService.java:
acknowledgeConsultation(UUID tenantId, ComplianceAcknowledgementRequest) -> ComplianceStatusResponse— Enregistre l'accusé de réception de la consultation du personnel délégué. Définitdelegues_consultation_done, stocke éventuellement la date et la référence du document PV.getComplianceStatus(UUID tenantId) -> ComplianceStatusResponse— Renvoie l'état de conformité actuel, y compris le nombre d'employés (pour les notifications de seuil à partir de 11).
Endpoints API¶
| Method | Path | Request Body | Response | Auth |
|---|---|---|---|---|
| GET | /api/v1/qrcontr/config |
— | ApiResponse |
HR (P2) |
| PUT | /api/v1/qrcontr/config |
UpdateModuleConfigRequest | ApiResponse |
HR (P2) |
| GET | /api/v1/qrcontr/config/overtime |
— | ApiResponse |
HR (P2) |
| PUT | /api/v1/qrcontr/config/overtime |
UpdateOvertimeConfigRequest | ApiResponse |
HR (P2) |
| GET | /api/v1/qrcontr/config/departments/{deptId} |
— | ApiResponse |
HR (P2) |
| PUT | /api/v1/qrcontr/config/departments/{deptId} |
UpdateDepartmentConfigRequest | ApiResponse |
HR (P2) |
| POST | /api/v1/qrcontr/compliance/delegues |
ComplianceAcknowledgementRequest | ApiResponse |
HR (P2) |
| GET | /api/v1/qrcontr/compliance/delegues |
— | ApiResponse |
HR (P2) |
| ### Règles de validation |
defaultLateToleranceMinutesdoit être >= 0overtimeWeekStartDaydoit être compris entre 1 et 7autoCloseTimedoit correspondre à une heure valide- Seuils des plages d'heures supplémentaires :
weeklyThresholdStartdoit être <weeklyThresholdEndlorsque les deux sont définis - Valeurs par défaut CI : BAND_1 (40, 46), BAND_2 (46, 48), BAND_3 (48, null), NIGHT (21h00, 05h00)
Considérations relatives au multi-tenant¶
- Toutes les tables incluent la colonne
tenant_id - Toutes les requêtes DOIVENT filtrer par
tenant_id - Les valeurs par défaut de CI sont définies par tenant (non partagées) afin que chaque tenant puisse les personnaliser indépendamment
- La configuration du service est facultative — si elle n'est pas définie, la valeur par défaut au niveau du module s'applique
Composants¶
Les composants suivent le wireframe de spécifications de conception 3.12 :
ModuleConfigPage.tsx— Onglets : Général, Heures supplémentaires, ServicesGeneralConfigPanel.tsx— Tolérance de retard, jour de début de semaine, heure de fermeture automatique, limites de travail à distanceOvertimeConfigPanel.tsx— Configuration des plages horaires avec note sur les valeurs par défaut de CIDepartmentConfigPanel.tsx— Remplacement de la tolérance par serviceOnboardingWizard.tsx— 3 étapes : Conformité -> Créer un site -> Créer un planningComplianceStep.tsx— Mentions légales, case à cocher « Oui, consultation effectuée », champ de date, espace réservé pour le téléchargement du PV
États de l'interface utilisateur (TOUS OBLIGATOIRES)¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | Skeleton for config panels | — |
| Empty | Config with CI defaults pre-filled | "Configuration par defaut (Cote d'Ivoire)" |
| Error | Field-level validation | "La valeur doit etre un nombre positif." |
| Offline | View cached config, edit disabled | "Disponible en ligne uniquement" |
| Success | Toast confirmation | "Configuration enregistree." |
Comportement adaptatif¶
- 360px (mobile) : sections de configuration empilées, volet inférieur pour la configuration des services
- 768px (tablette) : mise en page à onglets
- 1024px+ (ordinateur de bureau) : onglets dans la barre latérale, édition en ligne
Interactions¶
- Assistant d'intégration : l'étape 1 (Conformité) affiche une mention légale faisant référence à l'art. 16.1. Case à cocher + date facultative + téléchargement facultatif du PV. Le module s'active indépendamment de l'acceptation (ce n'est pas un blocage). Étape 2 : « Créer votre premier site » (lien vers la création du site). Étape 3 : « Définir un horaire » (lien vers la création de l'horaire).
- Seuil de 11 employés et plus : si le tenant compte moins de 11 employés, afficher la note : « Cette obligation concerne les entreprises de 11 salariés et plus. »
- Tranches d'heures supplémentaires : libellés en lecture seule pour les codes de tranche avec des champs de seuil modifiables. Note ci-dessous : « Valeurs par défaut basées sur le Code du travail ivoirien. »
- Configuration du service : menu déroulant pour sélectionner le service, puis champ de tolérance. Si vide, affiche « Tolérance par défaut du module : {n} minutes »
Critères d'acceptation¶
AC-001: Compliance notice on module activation
Given I am HR activating the QR Attendance module
When the activation screen loads
Then I see a compliance notice referencing Art. 16.1 Code du Travail and the delegues du personnel consultation requirement
AC-002: Compliance acknowledgement with optional details
Given I see the compliance notice
When I acknowledge it
Then I can check "Oui, consultation effectuee", optionally enter the consultation date, and optionally upload the PV (proces-verbal)
AC-003: Module activation without compliance acknowledgement
Given I do NOT check the consultation box
When I continue
Then the module activates anyway (not a blocker) but the system records that the reminder was shown and not acknowledged
OHADA et dispositions réglementaires¶
- Art. 16.1 du Code du travail de la Côte d'Ivoire : le règlement intérieur doit être soumis pour avis aux délégués du personnel.
- Obligation relative aux délégués du personnel : entreprises comptant 11 salariés ou plus en Côte d'Ivoire.
- FR-QR-121 : si l'entreprise compte moins de 11 salariés, ajouter la mention « Cette obligation concerne les entreprises de 11 salariés et plus. »
- Valeurs par défaut pour les heures supplémentaires en Côte d'Ivoire (TV-QR-002) : BAND_1 40-46 h, BAND_2 46-48 h, BAND_3 > 48 h, NUIT 21 h 00-05 h 00
Normes et conventions¶
docs/standards/java-Spring-guidelines.md— Enregistrements d'entités, couche de service, validationdocs/standards/api-guidelines.md— Enveloppe ApiResponse, sémantique PUTdocs/standards/database-guidelines.md— Conception de tables multi-tenant, BigDecimal pour les seuilsdocs/standards/react-typescript-guidelines.md— Structure des composants, modèles de formulaires, onglets
Exigences de test¶
Tests unitaires¶
- Création de bandes par défaut en CI : 5 bandes initialisées avec les seuils corrects
- Suivi du statut de conformité : confirmé vs non confirmé
- Validation de la configuration : lateToleranceMinutes >= 0, overtimeWeekStartDay 1-7
- Validation des plages d'heures supplémentaires : thresholdStart < thresholdEnd
Tests d'intégration¶
- GET /config -> renvoie la configuration du module (créée automatiquement lors du premier accès)
- PUT /config -> met à jour les champs de configuration
- GET /config/overtime -> renvoie 5 plages par défaut de CI
- PUT /config/overtime -> met à jour les seuils des plages
- GET /config/departments/{deptId} -> renvoie la configuration du service
- PUT /config/departments/{deptId} -> met à jour ou ajoute la configuration du service
- POST /compliance/delegues -> enregistre l'accusé de réception
- GET /compliance/delegues -> renvoie le statut de conformité
- Isolation des locataires : le locataire A ne peut pas voir la configuration du locataire B
Ce que l'assurance qualité va valider¶
- Le flux en 3 étapes de l'assistant d'intégration fonctionne de bout en bout
- L'avis de conformité affiche la référence à l'art. 16.1
- Le module s'active indépendamment de l'accusé de réception de conformité
- La note relative au seuil de 11 employés ou plus s'affiche le cas échéant
- Les onglets de la page de configuration fonctionnent (Général, Heures supplémentaires, Départements)
- Les valeurs par défaut des heures supplémentaires CI sont préremplies
- La modification de la configuration du service est enregistrée correctement
- Tous les points de rupture (360px, 768px, 1024px) s'affichent correctement
Hors du périmètre¶
- Logique de calcul des heures supplémentaires (QR-018)
- Détection des retards à l'aide d'une tolérance (QR-009)
- Application des limites de télétravail (QR-014)
- Téléchargement de fichiers pour les documents PV (utiliser un espace réservé — dépend de la gestion des documents)
Définition de « Terminé »¶
- [ ] Tous les endpoints backend sont implémentés et renvoient des réponses correctes
- [ ] Toutes les pages frontend s'affichent correctement à 360px, 768px, 1024px
- [ ] Les 5 états de l'interface utilisateur sont implémentés (chargement, vide, erreur, hors ligne, succès)
- [ ] Les micro-textes en français correspondent exactement aux spécifications de conception
- [ ] Tous les critères d'acceptation sont validés manuellement
- [ ] Les tests unitaires sont écrits et réussis
- [ ] Les tests d'intégration sont écrits et réussis
- [ ] Isolation multi-tenant vérifiée
- [ ] Écriture et synchronisation hors ligne : N/A (la configuration est en ligne uniquement)
- [ ] Pas de
anyen TypeScript, pas de types bruts en Java - [ ] Les réponses API respectent le format d'enveloppe standard
- [ ] Entrées de piste d'audit pour les modifications de configuration, accusé de réception de conformité