# Audit de conformité légale & RGPD — Module « Gestion des absences » (Lesstime)

> **Date** : 2026-05-22
> **Périmètre** : code backend (`src/Enum`, `src/Entity`, `src/Service`, `src/State`, `src/Command`, `src/Controller/Absence`), fixtures, migrations et front (`frontend/components/absence`, `frontend/pages/absences.vue`, `team-absences.vue`).
> **Nature** : audit documentaire. **Aucune modification de code** n'a été réalisée.
> **Avertissement** : ce document est une analyse technique de conformité, pas un avis juridique formel. Faire valider par un conseil RH/juridique avant mise en production paie.

---

## 1. Convention collective applicable

### Constat
Le code (`AbsencePolicy` par défaut, fixtures `AppFixtures.php`) part d'hypothèses « Syntec » sans le formaliser : 25 jours **ouvrés** de CP, période de référence **1er juin → 31 mai** (`referencePeriodStart = '06-01'`), décompte en jours ouvrés par défaut (`countWorkingDaysOnly = true`).

### Convention probable
Pour une société d'édition de logiciels / services informatiques (« bureau informatique »), c'est très majoritairement la **Convention collective nationale Syntec — IDCC 1486** (« Bureaux d'études techniques, cabinets d'ingénieurs-conseils et sociétés de conseils ») qui s'applique. C'est cohérent avec les valeurs codées (25 jours ouvrés, décompte en jours ouvrés).

### ⚠️ Ambiguïté à lever
La convention applicable **ne se déduit pas de l'activité seule** : elle dépend du **code APE/NAF** de l'entreprise et de l'activité principale réelle. Une SSII/éditeur peut relever de Syntec, mais certaines structures relèvent d'autres CCN. **À confirmer** sur le bulletin de paie ou auprès de l'expert-comptable. Le module **code en dur** des hypothèses Syntec mais ne stocke nulle part la CCN de référence : à documenter.

### Sources
- Code du travail / congés payés : https://www.service-public.gouv.fr/particuliers/vosdroits/F2258
- Syntec — congés payés (25 jours ouvrés, ancienneté) : https://www.legisocial.fr/conventions-collectives-nationales/1486-syntec-bureaux-etudes-techniques-cabinets-ingenieurs-conseils-societes/conges-payes-indemnites-conges-sans-solde.html
- Syntec — congés événements familiaux (Code du travail numérique) : https://code.travail.gouv.fr/contribution/1486-les-conges-pour-evenements-familiaux
- Texte de base Syntec (Légifrance) : https://www.legifrance.gouv.fr/conv_coll/id/KALIARTI000044253087/?idConteneur=KALICONT000005635173

---

## 2. Conformité par type d'absence

Valeurs implémentées : voir `AppFixtures.php` (lignes 648-667) et `User::$annualLeaveDays = 25.0`.

| Type | Minimum légal / Syntec | Implémenté | Verdict |
|------|------------------------|------------|---------|
| **Congés payés (CP)** | Légal : 2,5 j ouvrables/mois = 30 j ouvrables/an (5 sem.). Syntec : 25 j ouvrés/an = équivalent 30 j ouvrables. Période réf. 01/06→31/05. | `daysPerYear = 25`, décompte jours ouvrés, période `06-01`, acquisition 1/12 par mois (`AccrueLeaveCommand`). | ✅ **Conforme** sur le principe. ⚠️ Réserves : (a) pas de **règle d'arrondi** légal à l'entier supérieur (art. L3141-7) ; (b) pas de **congés d'ancienneté Syntec** (1 j à 5 ans, 2 j à 10 ans, 3 j à 15 ans, 4 j à 20 ans) ; (c) acquisition figée à `annualLeaveDays/12` sans assimilation des périodes d'arrêt maladie (voir §3). |
| **Mariage / PACS** | Légal : **4 jours minimum** (L3142-4). Syntec : 4 jours ouvrés. | `daysPerEvent = 4`, justificatif requis. | ✅ **Conforme**. |
| **Décès proche** | Légal : **enfant = 5 j min** (et jusqu'à 7 j + congé deuil 8 j si enfant <25 ans) ; conjoint/partenaire = 3 j ; père/mère/beau-parent/frère/sœur = 3 j (L3142-4). Syntec : barème ≥ légal, dont **jusqu'à ~22 j** pour décès enfant <25 ans. | **Un seul** `Bereavement` à `daysPerEvent = 3`, sans distinction du lien de parenté. | ❌ **Non conforme**. Un forfait unique de 3 j est **inférieur au minimum légal** pour le décès d'un enfant (5 j) et ignore le congé de deuil (8 j) et le barème Syntec. Dérive : sous-attribution de droits. |
| **Naissance** | Légal : **3 jours min** (en sus du congé paternité). | **Type absent** de l'enum `AbsenceType`. | ❌ **Manquant**. La naissance n'est pas gérée ; aucun congé naissance ni paternité. |
| **Congé parental d'éducation** | Cadre L1225-47 s. : suspension du contrat, durée 1 an renouvelable 2×, jusqu'aux 3 ans de l'enfant, **non rémunéré**, ancienneté 1 an. | `ParentalLeave` : `daysPerYear`/`daysPerEvent` null, `decrementsBalance() = true` (décrémente un solde). | ⚠️ **Modélisation incorrecte**. Le congé parental est une **suspension longue** (mois/années), pas un décompte sur solde annuel. Le traiter comme une absence qui « décrémente un solde » (vide ici, donc 0) n'a pas de sens métier. À modéliser comme suspension, sans solde. |
| **Arrêt maladie** | Indemnisé par la Sécu ; **ne se déduit jamais des CP**. Depuis loi 2024-364 du 22/04/2024 : acquiert **2 j ouvrables/mois** de CP (maladie non pro). | `SickLeave` : `decrementsBalance() = false` → ne touche aucun solde. | ✅ **Conforme** sur le non-décompte. ⚠️ **Manque** : l'acquisition de CP pendant l'arrêt (2 j/mois) n'est pas implémentée — `AccrueLeaveCommand` crédite `annualLeaveDays/12` quel que soit le statut, sans tenir compte des arrêts. Impact paie potentiel. |

### Décompte des jours (`AbsenceDayCalculator`)
- ✅ Dimanche jamais compté ; samedi compté seulement en « jours ouvrables » ; jours fériés exclus ; demi-journées aux bornes à -0,5. Logique **correcte**.
- ✅ `PublicHolidayProvider` : 11 jours fériés métropole + Pâques/Ascension/Pentecôte via Computus. Correct **hors Alsace-Moselle** (Vendredi saint + 26/12 non gérés — documenté comme hors périmètre, à signaler si salariés concernés).
- ⚠️ La **demi-journée à -0,5** est appliquée sans vérifier que la borne tombe sur un jour décompté : si `startHalfDay` est posé alors que le jour de début est un week-end/férié (non compté), on retire quand même 0,5, ce qui peut **sous-décompter**. Cas limite à border côté validation.

---

## 3. Constats RGPD / dérive

### 🔴 BLOQUANT — Fuite des données RH/familiales à tous les utilisateurs authentifiés
**Fichiers** : `src/Entity/User.php` (lignes 32-37, groupes `user:list`), `config/packages/security.yaml` (ligne 69).

Les opérations API `GET /api/users` (collection) et `GET /api/users/{id}` n'ont **aucun attribut `security`** : seule la règle globale `^/api → IS_AUTHENTICATED_FULLY` s'applique. **N'importe quel salarié** (`ROLE_USER`) peut donc lister tous ses collègues et lire le groupe `user:list`, qui expose : `familySituation`, `nbChildren`, `hireDate`, `endDate`, `contractType`, `workTimeRatio`, `annualLeaveDays`, `initialLeaveBalance` et **`roles`**.

C'est une violation directe des principes RGPD de **minimisation** et de **limitation d'accès** (référentiel CNIL RH) : la situation familiale et le nombre d'enfants d'un collègue n'ont aucune raison d'être accessibles à un pair. Gravité maximale (donnée personnelle sensible au sens RH diffusée largement).

### 🟠 MOYEN — Collecte de `familySituation` / `nbChildren` non justifiée (non-minimisation)
**Fichiers** : `User.php` (119-125), `EmployeeDrawer.vue`, `Serializer.php` (MCP, 392-393).

Recherche d'usage exhaustive : ces deux champs sont **stockés, saisis dans le formulaire RH et exposés (API + MCP), mais ne sont utilisés dans AUCUN calcul** d'absence (ni `AbsenceDayCalculator`, ni `AbsenceBalanceService`, ni `AccrueLeaveCommand`, ni les policies). Or :
- Le module gère le décès via un forfait unique sans lien de parenté → `familySituation`/`nbChildren` ne servent pas au congé décès.
- La naissance et le congé parental ne sont pas calculés à partir de `nbChildren`.

Conséquence : **collecte sans finalité opérationnelle** = violation du principe de minimisation (art. 5.1.c RGPD). Soit ces champs servent réellement un calcul légal (alors les implémenter et le documenter dans le registre des traitements), soit ils doivent être **supprimés**. En l'état c'est une **dérive de collecte**. De plus la situation familiale peut révéler indirectement l'orientation/la vie privée → prudence renforcée.

### 🟠 MOYEN — Exposition de données RH via le MCP
**Fichier** : `src/Mcp/Tool/Serializer.php` (392-393). Le serializer MCP renvoie `familySituation` et `nbChildren`. Le serveur MCP HTTP **pointe sur la PROD** (cf. mémoire projet). Toute intégration MCP (assistant IA, scripts) peut donc aspirer ces données familiales. À restreindre / retirer du payload MCP.

### 🟡 MINEUR — Justificatif et motif : données potentiellement sensibles
**Fichiers** : `AbsenceRequest::$reason`, `justificationFileName`, `AbsenceJustification*Controller`.
- Le **contrôle d'accès au justificatif est correct** (propriétaire ou admin uniquement, `AbsenceJustificationDownloadController` lignes 38-40). ✅
- Mais un justificatif d'**arrêt maladie** peut contenir des **données de santé**. Le champ `reason` (texte libre) peut aussi en contenir. Recommandations CNIL : ne stocker que le **volet administratif**, durée de conservation limitée, accès tracé. Aucune **politique de rétention/purge** n'est implémentée (fichiers et demandes conservés indéfiniment).

### 🟡 MINEUR — Approbation sans contrôle de solde
**Fichier** : `AbsenceReviewProcessor.php`. L'approbation déplace les jours de `pending` vers `taken` **sans vérifier** que le solde disponible est suffisant — un solde peut devenir négatif. Pas un problème RGPD mais une **dérive de calcul** (congés non acquis posables sans alerte). À noter : `getAvailable()` autorise volontairement de poser les CP « en cours d'acquisition » (N), ce qui est un **choix d'entreprise admis** mais à confirmer (certaines entreprises n'autorisent que le N-1).

### ✅ Points conformes
- Accès aux `AbsenceRequest` / `AbsenceBalance` : les providers (`AbsenceRequestProvider`, `AbsenceBalanceProvider`) **filtrent bien** par propriétaire pour un non-admin (un salarié ne voit que ses propres demandes/soldes). Le filtre `user` n'est appliqué que si `ROLE_ADMIN`. ✅
- Approbation/rejet/suppression réservés à `ROLE_ADMIN`. ✅
- Upload justificatif limité au propriétaire/admin, MIME validé côté serveur, taille plafonnée 10 Mo. ✅

---

## 4. Recommandations actionnables

### Priorité 1 — Bloquant (à corriger avant prod)
1. **Restreindre `GET /api/users` et `GET /api/users/{id}`** : ajouter `security: "is_granted('ROLE_ADMIN')"` sur ces opérations, OU créer un groupe de sérialisation « annuaire » minimal (id, username, avatar) pour les non-admins et réserver `user:list` (champs RH) à l'admin via un contexte conditionné par le rôle. Retirer impérativement `familySituation`, `nbChildren`, `hireDate`, `endDate`, `contractType`, `annualLeaveDays`, `initialLeaveBalance`, `roles` de toute vue accessible à `ROLE_USER`.

### Priorité 2 — Moyen
2. **Trancher sur `familySituation` / `nbChildren`** : soit les supprimer (recommandé tant qu'aucun calcul ne les utilise), soit les rattacher à une finalité réelle (barème décès/naissance) et les inscrire au registre des traitements. Les retirer du payload MCP (`Serializer.php`).
3. **Refondre le congé décès** : remplacer le forfait unique 3 j par un barème par lien de parenté conforme au minimum légal (enfant ≥ 5 j + deuil 8 j ; conjoint/parent/fratrie 3 j) et au barème Syntec applicable.
4. **Ajouter le congé naissance** (3 j min) et, le cas échéant, le congé paternité.
5. **Remodéliser le congé parental** comme suspension de contrat (sans décompte de solde annuel).

### Priorité 3 — Mineur / robustesse
6. **Acquisition CP pendant arrêt maladie** : aligner `AccrueLeaveCommand` sur la loi 2024-364 (2 j ouvrables/mois pour maladie non pro, plafond annuel).
7. **Congés d'ancienneté Syntec** : implémenter le barème (1/2/3/4 j à 5/10/15/20 ans) et l'arrondi légal à l'entier supérieur.
8. **Politique de rétention** : définir une durée de conservation des `AbsenceRequest`, `reason` et justificatifs (santé), avec purge automatique ; tracer les accès aux justificatifs.
9. **Contrôle de solde à l'approbation** + garde-fou demi-journée sur jour non décompté dans `AbsenceDayCalculator`.
10. **Documenter la CCN de référence** dans la config (ne pas la coder en dur implicitement) et confirmer Syntec via le code APE de l'entreprise.

---

## Sources (règles légales citées)
- Congés payés (2,5 j/mois, 30 j, période 01/06→31/05) : https://www.service-public.gouv.fr/particuliers/vosdroits/F2258
- Jours ouvrés vs ouvrables : https://www.juritravail.com/Actualite/decompte-du-nombre-de-jours-de-conges-payes-les-jours-ouvrables-et-les-jours-ouvres-comment-faire/Id/2161
- Syntec — CP & ancienneté : https://www.legisocial.fr/conventions-collectives-nationales/1486-syntec-bureaux-etudes-techniques-cabinets-ingenieurs-conseils-societes/conges-payes-indemnites-conges-sans-solde.html
- Syntec — événements familiaux : https://code.travail.gouv.fr/contribution/1486-les-conges-pour-evenements-familiaux
- Code du travail — congés événements familiaux (L3142-1 s.) : https://www.legifrance.gouv.fr/codes/section_lc/LEGITEXT000006072050/LEGISCTA000006195795/
- Congé de deuil / décès enfant : https://travail-emploi.gouv.fr/les-conges-pour-evenements-familiaux-et-le-conge-de-deuil
- Congé parental d'éducation (L1225-47 s.) : https://www.legifrance.gouv.fr/codes/id/LEGISCTA000006195596/
- CP pendant arrêt maladie (loi 2024-364) : https://code.travail.gouv.fr/information/acquisition-de-conges-payes-pendant-un-arret-maladie-les-nouvelles-regles
- CNIL — données de santé / employeur : https://www.cnil.fr/fr/cnil-direct/question/donnees-sur-la-sante-un-employeur-peut-il-les-connaitre
- CNIL — référentiel gestion RH (minimisation) : https://www.cnil.fr/sites/default/files/atoms/files/referentiel_grh_novembre_2019_0.pdf
