feat(absences) : module de gestion des absences + MCP + conformité RGPD/légale #7

Merged
malio merged 9 commits from feat/absence-management into develop 2026-05-26 08:07:55 +00:00
Owner

Module de gestion des absences (type Payfit)

Cette MR introduit un module complet de gestion des absences, supprime le portail client, ajoute des outils MCP CRUD, durcit le RGPD sur /api/users et applique une première vague de mise en conformité légale.

Lots livrés (7 commits)

  1. Fondation backend (de98924) — entités/enum/services du module : AbsenceType, AbsenceRequest, AbsenceBalance, AbsencePolicy, AbsenceDayCalculator, PublicHolidayProvider, providers/processors API Platform, commande d'acquisition.
  2. Avancement + suppression portail client (2a0b202) — front du module (pages /absences, /team-absences, drawers, helpers), retrait complet de la feature portail client (ROLE_CLIENT, ClientTicket).
  3. Outils MCP CRUD absences (2b148fa) — tools MCP pour créer/lister/valider les demandes.
  4. Durcissement RGPD /api/users (11fdf8d) — annuaire minimal (id + username) pour les non-admins ; champs RH (hireDate, contractType, annualLeaveDays, initialLeaveBalance, roles, etc.) réservés à ROLE_ADMIN et à /api/me ; suppression de familySituation/nbChildren.
  5. Aide /help + revue de conformité (4858f73) — chapitre Absences dans l'aide ; audit légal documenté.
  6. Spec de mise en conformité (e9aaccc) — design des corrections retenues (périmètre 1-6).
  7. Mise en conformité légale (f9773b3) — événements familiaux ne décrémentent plus de solde (seul le CP le fait) ; décès = motif obligatoire (REST + MCP), plus de forfait trompeur ; ajout du type Naissance ; congé parental = suspension sans solde ; garde-fou demi-journée sur jour non décompté ; CCN documentée (Syntec IDCC 1486).

Vérifications

  • make test : 52 tests verts.
  • php bin/console lint:container : OK.
  • Build Nuxt (build:dist) : OK.

⚠️ Backlog légal NON traité (à planifier avant mise en prod paie)

  • Congés d'ancienneté Syntec (1/2/3/4 j à 5/10/15/20 ans) + arrondi légal à l'entier supérieur (art. L3141-7).
  • Acquisition de CP pendant arrêt maladie (loi 2024-364 du 22/04/2024 : 2 j ouvrables/mois, plafonné) — AccrueLeaveCommand crédite aujourd'hui annualLeaveDays/12 sans tenir compte des arrêts.
  • Politique de rétention / purge des justificatifs (données de santé) et des demandes — aucune durée de conservation ni purge automatique.
  • CCN à confirmer : l'hypothèse Syntec (IDCC 1486) doit être validée via le code APE/NAF réel de l'entreprise (le module la documente mais ne la déduit pas).
  • Contrôle de solde négatif à l'approbation (choix « poser le N en cours » conservé volontairement).
  • Jours fériés Alsace-Moselle non gérés.

🤖 Generated with Claude Code

## Module de gestion des absences (type Payfit) Cette MR introduit un module complet de gestion des absences, supprime le portail client, ajoute des outils MCP CRUD, durcit le RGPD sur `/api/users` et applique une première vague de mise en conformité légale. ### Lots livrés (7 commits) 1. **Fondation backend** (`de98924`) — entités/enum/services du module : `AbsenceType`, `AbsenceRequest`, `AbsenceBalance`, `AbsencePolicy`, `AbsenceDayCalculator`, `PublicHolidayProvider`, providers/processors API Platform, commande d'acquisition. 2. **Avancement + suppression portail client** (`2a0b202`) — front du module (pages `/absences`, `/team-absences`, drawers, helpers), retrait complet de la feature portail client (`ROLE_CLIENT`, `ClientTicket`). 3. **Outils MCP CRUD absences** (`2b148fa`) — tools MCP pour créer/lister/valider les demandes. 4. **Durcissement RGPD `/api/users`** (`11fdf8d`) — annuaire minimal (id + username) pour les non-admins ; champs RH (`hireDate`, `contractType`, `annualLeaveDays`, `initialLeaveBalance`, `roles`, etc.) réservés à `ROLE_ADMIN` et à `/api/me` ; suppression de `familySituation`/`nbChildren`. 5. **Aide `/help` + revue de conformité** (`4858f73`) — chapitre Absences dans l'aide ; audit légal documenté. 6. **Spec de mise en conformité** (`e9aaccc`) — design des corrections retenues (périmètre 1-6). 7. **Mise en conformité légale** (`f9773b3`) — événements familiaux ne décrémentent plus de solde (seul le CP le fait) ; décès = motif obligatoire (REST + MCP), plus de forfait trompeur ; ajout du type **Naissance** ; congé parental = suspension sans solde ; garde-fou demi-journée sur jour non décompté ; CCN documentée (`Syntec IDCC 1486`). ### Vérifications - `make test` : **52 tests verts**. - `php bin/console lint:container` : OK. - Build Nuxt (`build:dist`) : OK. --- ### ⚠️ Backlog légal NON traité (à planifier avant mise en prod paie) - **Congés d'ancienneté Syntec** (1/2/3/4 j à 5/10/15/20 ans) + **arrondi légal** à l'entier supérieur (art. L3141-7). - **Acquisition de CP pendant arrêt maladie** (loi 2024-364 du 22/04/2024 : 2 j ouvrables/mois, plafonné) — `AccrueLeaveCommand` crédite aujourd'hui `annualLeaveDays/12` sans tenir compte des arrêts. - **Politique de rétention / purge** des justificatifs (données de santé) et des demandes — aucune durée de conservation ni purge automatique. - **CCN à confirmer** : l'hypothèse Syntec (IDCC 1486) doit être validée via le **code APE/NAF** réel de l'entreprise (le module la documente mais ne la déduit pas). - Contrôle de solde négatif à l'approbation (choix « poser le N en cours » conservé volontairement). - Jours fériés Alsace-Moselle non gérés. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
matthieu added 7 commits 2026-05-22 14:21:46 +00:00
Module type Payfit (étapes 1+2 de la spec V1) : demande d'absence, validation
admin, soldes à jour.

- Enums : AbsenceType, AbsenceStatus, HalfDay, ContractType, FamilySituation
- Entités : AbsencePolicy, AbsenceBalance, AbsenceRequest + champs RH sur User
- Services : PublicHolidayProvider (fériés FR métropole en PHP pur, Computus),
  AbsenceDayCalculator (décompte jours ouvrés/ouvrables + demi-journées, TDD),
  AbsenceBalanceService (périodes + pending/taken/recrédit)
- API Platform : providers/processors (création, approve/reject/cancel) + RBAC
  me/admin, contrôleurs preview (dry-run), upload/download justificatif, calendrier
- Migrations : une par table + colonnes RH user (DEFAULT puis DROP DEFAULT)
- Fixtures : 5 policies par défaut, salariés démo, soldes et demandes
- Tests unitaires : PublicHolidayProvider, AbsenceDayCalculator (12 tests)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deux lots regroupés sur la branche feat/absence-management.

Suppression complète du portail client :
- retire ROLE_CLIENT (security.yaml) ; User::getRoles() ajoute toujours ROLE_USER
- supprime l'entité ClientTicket (+ repo, states, relations), User.client et
  User.allowedProjects, NotificationService, ProjectAllowedExtension, le bloc
  ROLE_CLIENT de MailAccessChecker
- front : pages /portal, layout portal, composants client-ticket/,
  AdminClientTicketTab, services/dto/i18n/docs associés
- fixtures : retire les users client-liot / client-acme
- migration Version20260522110000 (drop client_ticket, user_allowed_projects,
  colonnes liées ; task_document.task_id -> NOT NULL)
- tests : retire les cas obsolètes testant le blocage des clients sur le mail

Module gestion des absences (WIP) :
- entités / migrations (Version20260521160000, Version20260522090000)
- pages absences.vue / team-absences.vue, composants frontend/components/absence/
- services front, AccrueLeaveCommand, PublicHolidayController

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expose le module Absences via le serveur MCP et comble les trous CRUD
existants (projets, groupes, métadonnées de tâches, clients, users RH).

Absences (réutilise AbsenceDayCalculator + AbsenceBalanceService pour ne
pas contourner la logique de soldes) :
- list/get/create/review/cancel/delete-absence-request
- list/update-absence-policy, list/update-absence-balance
- create-absence-request prend un userId explicite (agir au nom d'un employé) ;
  review/cancel maintiennent les soldes (pending/taken) cohérents
- AbsenceRequestRepository::findFiltered pour les filtres de liste

Trous CRUD comblés :
- delete-project, delete-group
- CRUD tag, effort, priority
- CRUD status (couplé au workflow, avec category)
- CRUD client, get/update-user (champs RH, sans password ni roles)

Sérialisation centralisée (Serializer::absenceRequest/Policy/Balance/client/userFull).
Instructions MCP (mcp.yaml) mises à jour : statuts par workflow + domaine absences.

Tests : tests/Functional/Mcp/AbsenceRequestLifecycleTest (création / approbation /
annulation admin) vérifient le cycle complet et la cohérence des soldes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Suite à la revue de conformité du module absences.

Fuite corrigée : GET /api/users et /api/users/{id} n'avaient aucun contrôle
d'accès alors que le groupe user:list exposait les données RH/familiales
(date d'embauche, contrat, soldes de CP, rôles…). Tout utilisateur authentifié
pouvait donc lire ces informations sur tous ses collègues.
- chaque champ RH (isEmployee, hireDate, endDate, contractType, workTimeRatio,
  annualLeaveDays, referencePeriodStart, initialLeaveBalance) ainsi que roles
  est désormais exposé via #[ApiProperty(security: "is_granted('ROLE_ADMIN') or
  object == user")] : visible uniquement par un admin ou par l'utilisateur
  lui-même. id et username restent publics (sélecteurs d'assigné, avatars).

Minimisation : suppression de familySituation et nbChildren, collectés et
exposés (form RH, API, outil MCP) mais utilisés par aucun calcul.
- entité User + enum FamilySituation + migration de drop des colonnes
- Serializer MCP, update-user (MCP), EmployeeDrawer, DTO, fixtures, i18n

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- frontend/content/help/06-absences.md : nouveau chapitre d'aide « Absences »
  (poser une demande, lecture des soldes, mode de décompte des CP, ajout d'un
  salarié — nouveau ou déjà présent via le solde initial). Enregistré dans help.vue.
- revue de conformité (Syntec/RGPD) du module absences : rapport d'audit avec
  constats légaux et RGPD + recommandations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design des corrections légales retenues (modèle événements familiaux sans
solde, décès=motif obligatoire, ajout naissance, parental=suspension,
garde-fou demi-journée, CCN documentée). Points lourds (ancienneté, CP
pendant maladie, rétention) explicitement reportés en backlog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Périmètre 1-6 du design 2026-05-22-absence-legal-compliance-fixes (points
lourds — ancienneté, CP pendant maladie, rétention — reportés en backlog).

- Événements familiaux sans solde : AbsenceType::decrementsBalance() ne vaut
  true que pour les CP. Mariage/PACS, naissance, décès = droits par événement ;
  congé parental = suspension ; maladie = Sécu. Plus de solde fantôme.
- Décès : daysPerEvent = null (selon lien de parenté) + motif obligatoire à la
  création (REST + MCP), les minimums légaux étant rappelés dans l'aide.
- Ajout du congé naissance (type, policy 3 j, justificatif, libellés/couleur front).
- Garde-fou demi-journée : -0,5 appliqué uniquement si le jour-borne est
  réellement décompté (corrige un sous-décompte week-end/férié) — TDD.
- CCN documentée : paramètre app.absence.convention = "Syntec (IDCC 1486)",
  rappelée en sous-titre admin et dans l'aide /help.

Tests : AbsenceDayCalculatorTest (garde-fou demi-journée), AbsenceRequestLifecycle
(motif décès obligatoire + aucun solde touché). make test 52/52, build Nuxt OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
matthieu added 1 commit 2026-05-22 14:48:59 +00:00
- AbsenceBalanceService::availableForRequest() : jours disponibles (acquis N-1
  + en cours N − pris) pour la période de la demande, null si type non suivi.
- Blocage de l'approbation si countedDays > disponible, dans les deux chemins
  (REST AbsenceReviewProcessor + MCP ReviewAbsenceRequestTool), comme le motif
  décès. Les CP en cours d'acquisition restent posables, mais pas au-delà du
  droit total (plus de solde négatif silencieux à l'approbation).
- Fixture : demande pending CP d'alice replacée dans sa période de référence
  2025-2026 (26→29/05/2026, 4 j ouvrés) et solde pending aligné (5 → 4) ;
  plus de "en attente" orphelin non lié à une demande.
- Test fonctionnel testApproveBeyondAvailableBalanceIsBlocked + employé de test
  doté d'un droit pour que les approbations existantes passent le garde-fou.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
matthieu added 1 commit 2026-05-26 08:05:53 +00:00
Sur une demande d'un seul jour, le formulaire recopie la demi-journée de
début sur la fin (même date), si bien que les deux bornes portaient une
demi-journée et 0,5 était soustrait deux fois (1 - 0,5 - 0,5 = 0).

Quand start et end tombent le même jour, les deux bornes se confondent :
on ne soustrait désormais 0,5 qu'une seule fois. Comparaison par
getTimestamp() pour rester compatible strict_comparison (=== sur deux
DateTimeImmutable distincts teste l'identité d'instance, pas la valeur).

Couverture complétée : mono-jour plein, week-end, férié, inversion de
dates, demi-journée de fin seule, demi sur samedi en mode ouvrables.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
malio merged commit 26fae393cf into develop 2026-05-26 08:07:55 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MALIO-DEV/Lesstime#7