Story QR-004: Module Configuration + Overtime Bands + Compliance Onboarding¶
@assignee: @angeisaac-ui
Module: qr-attendance Slice: QR-T1-003 + QR-T5-006 Brand context: [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority: 4 Depends on: QR-001 Can develop concurrently with: QR-002, QR-003, QR-005 Merge order: Before QR-014 (teletravail needs config), QR-018 (overtime needs bands) Estimated complexity: M PRD User Stories: US-QR-013
Objective¶
Provide HR with a configuration interface for the QR Attendance module: general settings (late tolerance, overtime week start day, auto-close time, remote work limits), CI overtime band configuration (with legally-mandated defaults), per-department tolerance overrides, and a compliance onboarding wizard that records the "délégués du personnel" consultation requirement. The onboarding wizard is a 3-step activation flow: Compliance -> Create Site -> Create Schedule.
Backend Scope¶
Flyway Migrations¶
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__attendance_department_config.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 (seed data):
-- CI overtime defaults (temporarily validated -- TV-QR-002)
-- These are tenant-configurable; inserting as defaults for new tenants
-- Actual insertion happens in ConfigService when module is activated
Entities & Records¶
OvertimeConfig (tenant DB):
| 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 (tenant DB):
| 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 |
CI Default Overtime Bands:
| 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 | — | — | — | — |
DTOs:
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
) {}
Repository Layer¶
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>
Service Layer¶
ConfigService.java:
- getModuleConfig(UUID tenantId) -> ModuleConfigResponse — Returns module config. On first access, creates default config with auto-generated 256-bit hex HMAC secret and seeds CI overtime defaults.
- updateModuleConfig(UUID tenantId, UpdateModuleConfigRequest) -> ModuleConfigResponse — Partial update of module settings
- getOvertimeConfig(UUID tenantId) -> OvertimeConfigResponse — Returns all overtime bands for tenant
- updateOvertimeConfig(UUID tenantId, UpdateOvertimeConfigRequest) -> OvertimeConfigResponse — Updates overtime band thresholds/times
- getDepartmentConfig(UUID tenantId, UUID departmentId) -> DepartmentConfigResponse — Returns department-specific config
- updateDepartmentConfig(UUID tenantId, UUID departmentId, UpdateDepartmentConfigRequest) -> DepartmentConfigResponse — Upserts department tolerance override
ComplianceService.java:
- acknowledgeConsultation(UUID tenantId, ComplianceAcknowledgementRequest) -> ComplianceStatusResponse — Records delegues du personnel consultation acknowledgement. Sets delegues_consultation_done, optionally stores date and PV document reference.
- getComplianceStatus(UUID tenantId) -> ComplianceStatusResponse — Returns current compliance status including employee count (for 11+ threshold notice).
API Endpoints¶
| 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) |
Validation Rules¶
defaultLateToleranceMinutesmust be >= 0overtimeWeekStartDaymust be 1-7autoCloseTimemust be a valid time- Overtime band thresholds:
weeklyThresholdStartmust be <weeklyThresholdEndwhen both present - CI defaults: BAND_1 (40, 46), BAND_2 (46, 48), BAND_3 (48, null), NIGHT (21:00, 05:00)
Multi-Tenant Considerations¶
- All tables include
tenant_idcolumn - All queries MUST filter by
tenant_id - CI defaults are seeded per-tenant (not shared) so each tenant can customize independently
- Department config is optional — if not set, module-level default applies
Frontend Scope¶
Pages & Routes¶
/qrcontr/config— Module configuration page (HR)/qrcontr/onboarding— 3-step activation wizard
Components¶
Components follow design spec wireframe 3.12:
ModuleConfigPage.tsx— Tabs: General, Overtime, DepartmentsGeneralConfigPanel.tsx— Late tolerance, week start day, auto-close time, remote work limitsOvertimeConfigPanel.tsx— Band configuration with CI defaults noteDepartmentConfigPanel.tsx— Per-department tolerance overrideOnboardingWizard.tsx— 3 steps: Compliance -> Create Site -> Create ScheduleComplianceStep.tsx— Legal notice, checkbox "Oui, consultation effectuee", date field, PV upload placeholder
UI States (ALL REQUIRED)¶
| 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." |
Responsive Behavior¶
- 360px (mobile): Stacked config sections, bottom sheet for department config
- 768px (tablet): Tabbed layout
- 1024px+ (desktop): Sidebar tabs, inline editing
Interactions¶
- Onboarding wizard: Step 1 (Compliance) shows legal notice referencing Art. 16.1. Checkbox + optional date + optional PV upload. Module activates regardless of acknowledgement (not a blocker). Step 2: "Creer votre premier site" (links to site creation). Step 3: "Definir un horaire" (links to schedule creation).
- 11+ employee threshold: If tenant has fewer than 11 employees, show note: "Cette obligation concerne les entreprises de 11 salaries et plus."
- Overtime bands: Read-only labels for band codes with editable threshold fields. Note below: "Valeurs par defaut basees sur le Code du Travail ivoirien."
- Department config: Dropdown to select department, then tolerance field. If empty, displays "Tolerance par defaut du module : {n} minutes"
Acceptance Criteria¶
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 & Regulatory Rules¶
- Art. 16.1 Code du Travail CI: Reglement interieur must be submitted for opinion to delegues du personnel.
- Delegues du personnel obligation: enterprises with 11+ employees in CI.
- FR-QR-121: If tenant < 11 employees, show note "Cette obligation concerne les entreprises de 11 salaries et plus."
- CI overtime defaults (TV-QR-002): BAND_1 40-46h, BAND_2 46-48h, BAND_3 >48h, NIGHT 21:00-05:00
Standards & Conventions¶
docs/standards/java-spring-guidelines.md— Entity records, service layer, validationdocs/standards/api-guidelines.md— ApiResponse envelope, PUT semanticsdocs/standards/database-guidelines.md— Multi-tenant table design, BigDecimal for thresholdsdocs/standards/react-typescript-guidelines.md— Component structure, form patterns, tabs
Testing Requirements¶
Unit Tests¶
- CI default band creation: 5 bands seeded with correct thresholds
- Compliance status tracking: acknowledged vs not acknowledged
- Config validation: lateToleranceMinutes >= 0, overtimeWeekStartDay 1-7
- Overtime band validation: thresholdStart < thresholdEnd
Integration Tests¶
- GET /config -> returns module config (auto-created on first access)
- PUT /config -> updates config fields
- GET /config/overtime -> returns 5 CI default bands
- PUT /config/overtime -> updates band thresholds
- GET /config/departments/{deptId} -> returns department config
- PUT /config/departments/{deptId} -> upserts department config
- POST /compliance/delegues -> records acknowledgement
- GET /compliance/delegues -> returns compliance status
- Tenant isolation: tenant A cannot see tenant B's config
What QA Will Validate¶
- Onboarding wizard 3-step flow works end-to-end
- Compliance notice displays Art. 16.1 reference
- Module activates regardless of compliance acknowledgement
- 11+ employee threshold note displays when applicable
- Config page tabs work (General, Overtime, Departments)
- CI overtime defaults pre-filled
- Department config override saves correctly
- All breakpoints (360px, 768px, 1024px) render correctly
Out of Scope¶
- Overtime calculation logic (QR-018)
- Late detection using tolerance (QR-009)
- Teletravail limits enforcement (QR-014)
- File upload for PV document (use placeholder — depends on document management)
Definition of Done¶
- [ ] All backend endpoints implemented and returning correct responses
- [ ] All frontend pages render correctly at 360px, 768px, 1024px
- [ ] All 5 UI states implemented (loading, empty, error, offline, success)
- [ ] French micro-copy matches design spec exactly
- [ ] All acceptance criteria pass manually
- [ ] Unit tests written and passing
- [ ] Integration tests written and passing
- [ ] Multi-tenant isolation verified
- [ ] Offline write+sync: N/A (config is online-only)
- [ ] No TypeScript
any, no Java raw types - [ ] API responses follow standard envelope format
- [ ] Audit trail entries for config changes, compliance acknowledgement