Scénario QR-014 : Demande de télétravail +¶
Module Calendrier : qr-attendance Segment : QR-T4-002 (partiel : demande + limites) Contexte de la marque : [LES DEUX] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priorité : 14 Dépend de : QR-006 (DailyAttendance), QR-004 (configuration des limites de télétravail) Peut être développé en parallèle avec : QR-011, QR-016 Ordre de fusion : Avant QR-015 Complexité estimée : M PRD User Stories : US-QR-007 (AC-001, AC-002, AC-003)
Objectif¶
Mettre en place le flux de demande de télétravail et l'affichage du calendrier pour les employés. Les employés peuvent demander du télétravail pour des dates futures via un calendrier interactif, avec des exigences de préavis configurables et des avertissements concernant les limites hebdomadaires/mensuelles. Les demandes de dernière minute nécessitent une justification obligatoire. Les limites donnent lieu à un avertissement mais ne bloquent pas la demande — les RH conservent le pouvoir d'approbation final.
Portée du backend¶
Entités et enregistrements¶
RemoteWorkRequest :
- id (UUID)
- tenantId (UUID)
- employeeId (UUID)
- requestDate (LocalDate)
- reason (Chaîne de caractères, peut être nul)
- status (énumération : PENDING, APPROVED, REJECTED, CANCELLED)
- approvedBy (UUID, nullable)
- approvedAt (Instant, nullable)
- rejectionReason (Chaîne, nullable)
- createdAt (Instant)
- updatedAt (Instant)
Migrations Flyway¶
V001_016__attendance_remote_work_request.sql :
sql
CREATE TABLE attendance_remote_work_request (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
employee_id UUID NOT NULL,
request_date DATE NOT NULL,
reason TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING', 'APPROVED', 'REJECTED', 'CANCELLED')),
approved_by UUID,
approved_at TIMESTAMP,
rejection_reason TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, employee_id, request_date)
);
CREATE INDEX idx_remote_work_tenant_emp ON attendance_remote_work_request(tenant_id, employee_id, request_date);
CREATE INDEX idx_remote_work_tenant_status ON attendance_remote_work_request(tenant_id, status, request_date);### Couche de repository
RemoteWorkRepository
:
- findByTenantIdAndEmployeeIdAndRequestDate(tenantId, employeeId, date) — vérification de l'unicité
- findByTenantIdAndStatus(tenantId, status) — liste par statut
- countByTenantIdAndEmployeeIdAndStatusAndWeek(tenantId, employeeId, statuses, weekStart, weekEnd) — vérification de la limite hebdomadaire
- countByTenantIdAndEmployeeIdAndStatusAndMonth(tenantId, employeeId, statuses, monthStart, monthEnd) — vérification de la limite mensuelle
Couche de service¶
RemoteWorkService
:
- createRequest(tenantId, employeeId, request) :
- Vérifie que la date est dans le futur (ni aujourd'hui ni passée)
- Vérifie la contrainte d'unicité (une demande par employé et par date)
- Vérification du préavis : si requestDate < today + minRemoteWorkAdvanceDays (à partir de la configuration), reason est obligatoire
- Vérification de la limite : compte les demandes approuvées et en attente pour la semaine/le mois en cours. Si >= max, inclut un avertissement dans la réponse (ne bloque PAS)
- Crée une demande avec status=PENDING
- Publie un événement d'audit
- cancel(tenantId, requestId, employeeId) :
- Uniquement pour ses propres demandes PENDING
- Uniquement avant la date de la demande
- Définit status=CANCELLED
- Publie un événement d'audit
Endpoints API¶
| Method | Path | Request Body | Response | Auth |
|---|---|---|---|---|
| POST | /api/v1/qrcontr/remote-work |
CreateRemoteWorkRequest |
ApiResponse<RemoteWorkResponse> |
Employee (P4) |
| GET | /api/v1/qrcontr/remote-work |
?status=&employeeId=&dateFrom=&dateTo=&cursor=&size= |
ApiResponse<CursorPage<RemoteWorkResponse>> |
HR, Manager (team), Employee (own) |
| GET | /api/v1/qrcontr/remote-work/{id} |
— | ApiResponse<RemoteWorkResponse> |
HR, Manager, Employee (own) |
| POST | /api/v1/qrcontr/remote-work/{id}/cancel |
— | ApiResponse<RemoteWorkResponse> |
Employee (own, PENDING only) |
| ### DTOs |
CreateRemoteWorkRequest
:
- date (LocalDate, obligatoire) — doit être une date future
- reason (chaîne de caractères, conditionnelle) — obligatoire en cas de préavis court
RemoteWorkResponse
:
- Tous les champs de l'entity + employeeName (chaîne de caractères)
- limitWarning (String, nullable) — par ex., « 1 jour de télétravail cette semaine (max 2) »
Règles de validation¶
datedoit être une date future (ni aujourd'hui ni passée)- Si
date < today + minRemoteWorkAdvanceDays:reasonest obligatoire → erreur « Raison obligatoire pour les demandes de dernière minute » - Contrainte d'unicité : une demande par employé et par date → erreur « Une demande existe déjà pour cette date »
- Annulation : uniquement les propres demandes EN ATTENTE, uniquement avant la date de la demande
Considérations multi-tenant¶
Toutes les requêtes filtrent par tenant_id. La configuration (délai de préavis, limites hebdomadaires/mensuelles) est définie au niveau du locataire via AttendanceModuleConfig de QR-004. L'employé ne peut voir que ses propres demandes ; le responsable voit celles de son équipe ; les RH voient toutes les demandes.
Portée du frontend¶
Pages et routes¶
/qrcontr/teletravail— Calendrier de télétravail + formulaire de demande
Composants¶
TeletravailPage
(spécification de conception, wireframe 3.7) :
- Vue du calendrier mensuel avec indicateurs de demande + formulaire de demande
TeletravailCalendar
:
- Composant de calendrier interactif
- Dates passées grisées, jours fériés désactivés
- Demandes existantes affichées sous forme de points colorés : vert = approuvée, orange = en attente, rouge = rejetée
- Date sélectionnée : surlignée par un cercle orange
- Navigation mensuelle : flèches gauche/droite + geste de balayage sur mobile
TeletravailRequestForm
:
- Champ Date (prérempli à partir de la sélection du calendrier)
- Champ Motif (conditionnel : obligatoire en cas de demande de dernière minute, facultatif dans les autres cas)
- Bouton Soumettre
LimitWarning
:
- Composant d'avertissement intégré affiché sous le formulaire lorsque la limite hebdomadaire/mensuelle est atteinte
- Format : « i 1 jour de télétravail cette semaine (max 2) »
États de l'interface utilisateur¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | Calendar skeleton shimmer | — |
| Empty | Calendar with no requests, today highlighted | "Selectionnez une date pour demander le teletravail." |
| Error | Inline validation | "Selectionnez une date future." / "Raison obligatoire pour les demandes de derniere minute" |
| Offline | Full form works, queued | "Demande sauvegardee localement." |
| Success | Date gets pending amber dot, toast | "Demande soumise." |
Comportement adaptatif¶
- 360px : calendrier en pleine largeur, formulaire en dessous
- 768px : calendrier + panneau latéral pour le formulaire
- 1024px+ : calendrier à gauche, détails à droite (affichage divisé)
Interactions¶
- Appui sur une date : mise en surbrillance par un cercle orange ; les dates désactivées (passées, jours fériés) ne réagissent pas
- Navigation par mois : flèches gauche/droite + geste de balayage sur mobile
- Avertissement de limite : badge d'information sous le formulaire lorsque la limite est atteinte
- Annuler : depuis les détails de la demande en attente, boîte de dialogue de confirmation : « Êtes-vous sûr de vouloir annuler cette demande ? »
- Hors ligne : le formulaire est envoyé à la file d'attente locale, synchronisation une fois en ligne
Critères d'acceptation¶
```gherkin AC-001: Advance notice teletravail request Given I request teletravail for a future date When the date is more than the configured advance notice (default 2 days) Then the request is submitted without mandatory reason
AC-002: Short-notice teletravail requires reason Given I request same-day or short-notice teletravail When I submit the request Then a reason field is mandatory (e.g., 'Enfant malade', 'Probleme de transport')
AC-003: Weekly limit warning (not blocker) Given my company has a maximum teletravail days/week configuration When I already have the maximum approved for this week Then I am warned before submitting (not blocked — HR can still approve) ```---
OHADA et règles réglementaires¶
- Le télétravail est régi par la politique du tenant, et non par la loi dans le cadre actuel de CI
- Approche prudente pour les demandes en attente : considérées comme des absences jusqu'à leur approbation (traitées dans QR-015)
- Les configurations relatives aux préavis et aux limites sont spécifiques à chaque tenant
Normes et conventions¶
docs/standards/java-Spring-guidelines.mddocs/standards/api-guidelines.md— pagination basée sur le curseur (ADR-12)docs/standards/react-typescript-guidelines.mddocs/standards/offline-sync-guidelines.mddocs/standards/database-guidelines.md— Nommage des migrations Flyway
Exigences de test¶
Tests unitaires¶
RemoteWorkService.createRequest: vérification de l'avis préalable (motif obligatoire vs facultatif)RemoteWorkService.createRequest: avertissement de limite inclus dans la réponse lorsque la limite est atteinteRemoteWorkService.createRequest: date en double rejetéeRemoteWorkService.cancel: uniquement pour les propres demandes PENDING, uniquement avant la date
Tests d'intégration¶
- POST
/remote-work→ 201 PENDING - POST
/remote-worksans motif pour un préavis court → 400 - POST
/remote-workavec limite atteinte → 201 avec avertissement dans la réponse - POST
/remote-work/{id}/cancel→ 200 CANCELLED - POST
/remote-work/{id}/cancelsur une demande n'appartenant pas à l'utilisateur → 403 - GET
/remote-workavec filtres → résultats corrects - Isolation des locataires vérifiée
- Interaction avec le calendrier : sélectionner une date, vérifier que le formulaire s'affiche
- Soumettre avec/sans préavis, vérifier l'exigence de motif
- Vérifier que l'avertissement de limite s'affiche lorsque la limite est atteinte
- Annuler le flux à partir des détails de la demande en attente
- Hors ligne : le formulaire fonctionne et se synchronise lors de la reconnexion
- Navigation mensuelle avec les flèches et le balayage
Hors du champ d'application¶
- Workflow d'approbation/rejet (QR-015)
- Gestion du statut du jour (QR-015)
- Intégration de DailyAttendance pour le télétravail (QR-015)
Définition de « Terminé »¶
- [ ] Tous les endpoints backend sont implémentés et renvoient des réponses correctes
- [ ] Migration Flyway appliquée avec succès
- [ ] Le composant calendrier s'affiche 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
- [ ] L'isolation multi-tenant est vérifiée
- [ ] L'écriture et la synchronisation hors ligne fonctionnent
- [ ] Entrées de piste d'audit pour la création et l'annulation des requêtes
- [ ] Pas de
anyen TypeScript, pas de types bruts en Java - [ ] Les réponses API respectent le format d'enveloppe standard
- [ ]
./gradlew testest vert - [ ]
./gradlew spotlessApplyréussit