Story QR-011: Regularization Request (Employee/Manager)¶
Module: qr-attendance Slice: QR-T4-001 (partial: create + submit) Brand context: [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority: 11 Depends on: QR-006 (DailyAttendance exists) Can develop concurrently with: QR-009, QR-014, QR-016 Merge order: Before QR-012, QR-013 Estimated complexity: M PRD User Stories: US-QR-004
Objective¶
Create the regularization request flow that allows employees and managers to submit corrections for missed or incorrect clock events. The request captures a reason (from a predefined list), corrected times, and enters a pending approval state. This is the first half of the regularization feature — creation and submission only; approval/rejection is handled in QR-012.
Backend Scope¶
Entities & Records¶
RegularizationRequest:
- id (UUID)
- tenantId (UUID)
- employeeId (UUID)
- requestDate (LocalDate)
- reason (enum: OUBLI_POINTAGE, PROBLEME_TECHNIQUE, MISSION_EXTERIEURE, AUTRE)
- reasonDetail (String, nullable)
- correctedClockIn (LocalTime, nullable)
- correctedClockOut (LocalTime, nullable)
- status (enum: PENDING, APPROVED, REJECTED — default PENDING)
- requestedBy (UUID)
- approvedBy (UUID, nullable)
- approvedAt (Timestamp, nullable)
- isDirectCreate (boolean, default false)
- overrideOf (UUID, nullable)
- createdAt (Timestamp)
- updatedAt (Timestamp)
Flyway Migrations¶
V001_015__attendance_regularization_request.sql:
CREATE TABLE attendance_regularization_request (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
employee_id UUID NOT NULL,
request_date DATE NOT NULL,
reason VARCHAR(30) NOT NULL CHECK (reason IN ('OUBLI_POINTAGE', 'PROBLEME_TECHNIQUE', 'MISSION_EXTERIEURE', 'AUTRE')),
reason_detail TEXT,
corrected_clock_in TIME,
corrected_clock_out TIME,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED')),
requested_by UUID NOT NULL,
approved_by UUID,
approved_at TIMESTAMP,
is_direct_create BOOLEAN NOT NULL DEFAULT false,
override_of UUID,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_regularization_tenant_status ON attendance_regularization_request(tenant_id, status, created_at);
Repository Layer¶
RegularizationRepository:
- findByTenantIdAndStatus(UUID tenantId, String status) — list by status
- findByTenantIdAndEmployeeId(UUID tenantId, UUID employeeId) — list by employee
- findByTenantIdAndId(UUID tenantId, UUID id) — single request
Service Layer¶
RegularizationService:
- createRequest(CreateRegularizationRequest) — validates fields, creates with PENDING status, publishes notification event to manager + HR
API Endpoints¶
| Method | Path | Request Body | Response | Auth |
|---|---|---|---|---|
| POST | /api/v1/qrcontr/regularizations |
CreateRegularizationRequest |
ApiResponse<RegularizationResponse> |
Employee, Manager |
| GET | /api/v1/qrcontr/regularizations |
?status=&employeeId=&cursor=&size= |
ApiResponse<CursorPage<RegularizationResponse>> |
HR, Manager (team) |
| GET | /api/v1/qrcontr/regularizations/{id} |
— | ApiResponse<RegularizationResponse> |
HR, Manager, Employee (own) |
DTOs¶
CreateRegularizationRequest:
- employeeId (UUID)
- date (LocalDate)
- reason (enum)
- reasonDetail (String, conditional)
- correctedClockIn (LocalTime, optional)
- correctedClockOut (LocalTime, optional)
RegularizationResponse:
- All entity fields + employeeName (String), requesterName (String)
Validation Rules¶
reasonrequired, must be one of the enum values- If
reason=AUTRE,reasonDetailis mandatory -> error "Precisez la raison." - At least one of
correctedClockIn/correctedClockOutmust be provided -> error "Indiquez au moins une heure corrigee." datemust not be in the futurecorrectedClockInmust be beforecorrectedClockOutwhen both provided
Multi-Tenant Considerations¶
All queries filter by tenant_id. Notification events include tenant context. Employee ID validation is tenant-scoped.
Frontend Scope¶
Components¶
RegularizationBottomSheet (design spec wireframe 3.6) — 3-step wizard on mobile:
- Step 1: Select reason (radio buttons: Oubli de pointage, Probleme technique, Mission exterieure, Autre)
- Step 2: Correct times (time pickers for arrival and/or departure)
- Step 3: Confirmation summary
RegularizationForm — side panel on desktop (all fields visible)
UI States¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | N/A (form is local) | — |
| Empty | Pre-filled date from context, reason options visible | — |
| Error | Inline field errors, red border, auto-focus first error | "Indiquez au moins une heure corrigee." / "Precisez la raison." |
| Offline | Form works, submit queued | "Demande sauvegardee — sera envoyee des la reconnexion." |
| Success | Step 3: animated checkmark + summary | "Demande envoyee. Votre manager et les RH seront notifies." |
Responsive Behavior¶
- 360px: Bottom sheet wizard (3 steps), slide-left animation between steps (300ms)
- 768px: Bottom sheet (2 steps: reason+times together, then confirm)
- 1024px+: Side panel with all fields visible
Interactions¶
- Drag handle on bottom sheet to dismiss (velocity threshold)
- Time picker: native scroll pickers (hours:minutes), 15-min increments suggested, any minute selectable
- Back button preserves entered data (no data loss)
- Triggered from: employee history (tap day with problem -> "Demander une regularisation" button)
Acceptance Criteria¶
AC-001: Regularization request with reason and corrected times
Given I forgot to clock in/out
When I submit a regularization request
Then I must select a reason from the predefined list and specify the corrected time(s)
AC-002: Request enters pending status with notifications
Given I submit a request
When it is saved
Then it enters PENDING status and my manager and HR are notified
AC-003: Offline regularization request
Given I am offline
When I submit a request
Then it is queued for sync
OHADA & Regulatory Rules¶
- FR-QR-064: Every regularization action logged with actor, timestamp, before/after values
- Original clock events NEVER modified or deleted — regularizations are additive records
Standards & Conventions¶
docs/standards/java-spring-guidelines.mddocs/standards/api-guidelines.mddocs/standards/react-typescript-guidelines.mddocs/standards/offline-sync-guidelines.md
Testing Requirements¶
Unit Tests¶
- Validation:
reason=AUTRErequiresreasonDetail, at least one corrected time required, future date rejected, clockIn before clockOut when both provided - PENDING status set on creation
- Notification event published on creation
Integration Tests¶
- POST
/regularizationswith valid data -> 201 PENDING - POST
/regularizationswithreason=AUTREand no detail -> 400 - POST
/regularizationswith no corrected times -> 400 - GET
/regularizations?status=PENDING-> filtered list - GET
/regularizations/{id}-> single request with employee name - Tenant isolation: requests from tenant A not visible to tenant B
What QA Will Validate¶
- Complete wizard flow on mobile (3 steps: reason, times, confirm)
- Verify offline queue (submit while offline, sync when online)
- Verify notifications sent to manager and HR
- Verify error messages for validation failures
- Verify back button preserves entered data
Out of Scope¶
- Approve/reject workflow (QR-012)
- HR direct-create (QR-013)
- DailyAttendance recalculation on approval (QR-012)
- HR override of manager approval (QR-012)
Definition of Done¶
- [ ] All backend endpoints implemented and returning correct responses
- [ ] Bottom sheet wizard works on mobile (360px)
- [ ] Side panel form works on desktop (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 and integration tests passing
- [ ] Multi-tenant isolation verified
- [ ] Offline write+sync works (IndexedDB queue)
- [ ] No TypeScript
any, no Java raw types - [ ] API responses follow standard envelope format
- [ ] Audit trail entries for regularization creation
- [ ]
./gradlew testis green - [ ]
./gradlew spotlessApplypasses