- 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>
14 KiB
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
startHalfDayest 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/nbChildrenne 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,
AbsenceJustificationDownloadControllerlignes 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 filtreusern'est appliqué que siROLE_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)
- Restreindre
GET /api/usersetGET /api/users/{id}: ajoutersecurity: "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éserveruser:list(champs RH) à l'admin via un contexte conditionné par le rôle. Retirer impérativementfamilySituation,nbChildren,hireDate,endDate,contractType,annualLeaveDays,initialLeaveBalance,rolesde toute vue accessible àROLE_USER.
Priorité 2 — Moyen
- 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). - 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.
- Ajouter le congé naissance (3 j min) et, le cas échéant, le congé paternité.
- Remodéliser le congé parental comme suspension de contrat (sans décompte de solde annuel).
Priorité 3 — Mineur / robustesse
- Acquisition CP pendant arrêt maladie : aligner
AccrueLeaveCommandsur la loi 2024-364 (2 j ouvrables/mois pour maladie non pro, plafond annuel). - 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.
- Politique de rétention : définir une durée de conservation des
AbsenceRequest,reasonet justificatifs (santé), avec purge automatique ; tracer les accès aux justificatifs. - Contrôle de solde à l'approbation + garde-fou demi-journée sur jour non décompté dans
AbsenceDayCalculator. - 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