Aller au contenu

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

  • date doit être une date future (ni aujourd'hui ni passée)
  • Si date < today + minRemoteWorkAdvanceDays : reason est 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.md
  • docs/standards/api-guidelines.md — pagination basée sur le curseur (ADR-12)
  • docs/standards/react-typescript-guidelines.md
  • docs/standards/offline-sync-guidelines.md
  • docs/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 atteinte
  • RemoteWorkService.createRequest : date en double rejetée
  • RemoteWorkService.cancel : uniquement pour les propres demandes PENDING, uniquement avant la date

Tests d'intégration

  • POST /remote-work → 201 PENDING
  • POST /remote-work sans motif pour un préavis court → 400
  • POST /remote-work avec limite atteinte → 201 avec avertissement dans la réponse
  • POST /remote-work/{id}/cancel → 200 CANCELLED
  • POST /remote-work/{id}/cancel sur une demande n'appartenant pas à l'utilisateur → 403
  • GET /remote-work avec 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 any en TypeScript, pas de types bruts en Java
  • [ ] Les réponses API respectent le format d'enveloppe standard
  • [ ] ./gradlew test est vert
  • [ ] ./gradlew spotlessApply réussit