docs(overtime-contingent) : doc fonctionnelle, CLAUDE.md et doc in-app

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 17:28:59 +02:00
parent 39560548b6
commit 3590c8ea12
3 changed files with 63 additions and 0 deletions
+18
View File
@@ -108,6 +108,24 @@
- **Verrou** : si le report de l'exercice courant est `is_locked`, le paiement rétroactif est **refusé** (`assertReportNotLocked`) — la RH doit déverrouiller d'abord.
- Portée limitée à N-1 (chaîne de recalcul = 1 étape). Si la ligne courante n'existe pas encore, le fallback provider couvre l'affichage (cf. ci-dessus).
## Contingent heures supplémentaires payées
- Suivi par **année civile** (JanvDéc) des heures supp payées vs plafond légal (350 h
chauffeur / 220 h autres), non-forfait uniquement.
- **Heures payées** = `base25 + base50` (hors bonus). **Mapping** : paiements RTT stockés par
exercice → `annéeCivile = mois ≥ 6 ? exercice 1 : exercice` ; année civile Y = exercice Y
(mois 15) + exercice Y+1 (mois 612). Cœur partagé pur `OvertimePaidContingentCalculator`.
- **Plafond** résolu sur `isDriver` du **contrat courant**.
- **Fiche employé** : encart header `Contingent {année} : X h / plafond h` (année civile
courante, rouge si dépassement), via `GET /employees/{id}/overtime-contingent`. Encart
volontairement indépendant de la phase sélectionnée (toujours l'année civile courante).
- **Export PDF** (`GET /overtime-contingent/print?year=&siteIds=`, `ROLE_USER`,
`findScoped`) : groupé par site (`displayOrder`), tri `displayOrder → nom → prénom`,
colonnes JanvDéc + `Total payé / payable`. Drawer liste employés : sélecteur année +
sites (vide = périmètre complet). Exclut les FORFAIT (contrat courant).
- ⚠️ Bug latent consigné : `SalaryRecapPrintProvider` rattache mal les paiements RTT des mois
JuinDéc (requête par année civile sur un stockage par exercice). Hors périmètre.
- Doc : `doc/overtime-contingent.md`.
## Vue contrat (sélecteur de phase)
- Picker `Vue contrat` en haut de la fiche employé (`pages/employees/[id].vue`). Caché si l'employé n'a qu'une phase.
- Phase = groupe d'`EmployeeContractPeriod` consécutifs partageant la signature `(contract.type, weeklyHours, isDriver)`. Résolu par `App\Service\Contracts\EmployeeContractPhaseResolver`.
+33
View File
@@ -0,0 +1,33 @@
# Contingent d'heures supplémentaires payées
## Objectif
Suivre, par année civile (JanvDéc), les heures supplémentaires payées de chaque employé
non-forfait (chauffeurs inclus) face au plafond légal annuel.
## Règles
- **Heures payées** = `base25 + base50` (en minutes), hors majoration (bonus).
- **Plafond** : 350 h pour les chauffeurs (contrat courant `isDriver`), 220 h sinon.
- **Périmètre** : non-forfait uniquement (FORFAIT exclus, ni RTT ni heures supp payées).
## Mapping exercice → année civile
Les paiements RTT (`EmployeeRttPayment`) sont stockés par **exercice** (`year` = Juin N-1 →
Mai N) + `month` (112). L'année civile d'un paiement :
annéeCivile = month >= 6 ? exerciseYear - 1 : exerciseYear
Donc l'année civile **Y** agrège : exercice `Y` (mois 15) + exercice `Y+1` (mois 612).
## Implémentation
- Cœur partagé : `App\Service\WorkHours\OvertimePaidContingentCalculator` (pur).
- Repo : `EmployeeRttPaymentRepository::findByEmployeesAndYears`.
- Fiche employé : `GET /employees/{id}/overtime-contingent?year=YYYY` → encart header
(`Contingent {année} : X h / plafond h`, rouge si dépassement, année civile courante).
- Export PDF : `GET /overtime-contingent/print?year=&siteIds=` (`ROLE_USER`, périmètre
`findScoped`), groupé par site (`displayOrder`), tri `displayOrder → nom → prénom`,
colonnes JanvDéc + colonne `Total payé / payable`. Builder
`OvertimeContingentExportBuilder`, template `overtime-contingent/print.html.twig`.
## Hors périmètre / connu
- Bug latent récap salaire : `SalaryRecapPrintProvider` requête `findByYearAndMonth` avec
l'année civile alors que le stockage est par exercice (mauvais rattachement des paiements
des mois JuinDéc sur le récap mensuel). À corriger séparément.
+12
View File
@@ -643,6 +643,18 @@ export const documentationSections: DocSection[] = [
{ type: 'note', content: 'Export « Contingent H.nuit » : depuis la liste des employés, bouton Export → « Contingent H.nuit » + année. Génère un PDF A4 paysage avec une ligne par employé (groupés par site) et une colonne par mois, chacune avec le total d\'heures de nuit (travail entre 21h et 6h) et le nombre de nuits (jours où au moins 4h ont été travaillées de nuit). Les conducteurs utilisent leurs heures de nuit saisies.' },
],
},
{
id: 'contingent-heures-supp',
title: 'Export Contingent H.supp.',
requiredLevel: 'admin',
blocks: [
{ type: 'paragraph', content: 'L\'encart « Contingent {année} : X h / plafond h », affiché dans l\'en-tête de la fiche d\'un employé non-forfait, indique le total d\'heures supplémentaires payées sur l\'année civile en cours face au plafond légal. Il passe en rouge si ce plafond est dépassé.' },
{ type: 'list', content: 'Plafond chauffeur (contrat courant « conducteur ») : 350 h\nPlafond autres salariés non-forfait : 220 h\nSeuls les employés non-forfait disposent de cet encart (FORFAIT exclus)' },
{ type: 'paragraph', content: 'L\'export PDF « Contingent H.supp. » est accessible depuis la liste des employés, via le bouton Export → option « Contingent H.supp. ». Choisissez l\'année civile (par défaut l\'année courante) et éventuellement des sites ; sans sélection de site, tous les sites de votre périmètre sont inclus.' },
{ type: 'list', content: 'PDF A4 paysage, une ligne par employé non-forfait, groupé par site\nTri : ordre d\'affichage du site, puis nom, puis prénom\nColonnes : Janv à Déc (heures payées par mois) + colonne « Total payé / payable »\nLes employés FORFAIT n\'apparaissent pas dans cet export' },
{ type: 'note', content: 'Les heures prises en compte sont les bases payées (25 % et 50 % confondus), hors majorations. Le contingent est calculé sur l\'année civile (janvierdécembre), indépendamment de l\'exercice RTT (juinmai) : un paiement RTT saisi pour le mois de juin est rattaché à l\'année civile précédente.' },
],
},
{
id: 'impression-absences',
title: 'Impression absences',