# Contingent d'heures supplémentaires payées ## Objectif Suivre, par année civile (Janv–Dé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), **+ heures structurelles** (voir ci-dessous). - **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). ## Heures supplémentaires structurelles Les heures contractuelles **au-delà de 35h** (durée légale) sont des heures supplémentaires payées **chaque mois**, qui ne transitent pas par les paiements RTT (la référence d'un 39h est 39h, pas 35h) mais comptent dans le contingent légal. - Montant mensuel plein = `(weeklyHours − 35) × 52/12` h = `(weeklyHours − 35) × 260` min. Pour un 39h : `4 × 260 = 1040` min = **17,33 h/mois**. - **Généralisé** à tout contrat non-forfait/non-intérim dont `weeklyHours > 35` (ex. custom 40h → 21,67 h/mois). Contrats ≤ 35h, FORFAIT, INTERIM → 0. - **Proratisé** au nombre de jours réellement sous contrat dans le mois (entrée/sortie en cours de mois). Itère les périodes de contrat (`employee.contractPeriods`), pas de requête jour/jour. - Cœur partagé : `App\Service\WorkHours\StructuralOvertimeContingentCalculator` (`monthlyStructuralMinutes` / `totalStructuralMinutes`). Ajouté au total des paiements RTT côté provider (encart fiche) **et** export builder (PDF). ## Mapping exercice → année civile Les paiements RTT (`EmployeeRttPayment`) sont stockés par **exercice** (`year` = Juin N-1 → Mai N) + `month` (1–12). 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 1–5) + exercice `Y+1` (mois 6–12). ## Implémentation - Cœur partagé : `App\Service\WorkHours\OvertimePaidContingentCalculator` (pur). - Repo : `EmployeeRttPaymentRepository::findByEmployeesAndYears`. - Fiche employé : `GET /employees/{id}/overtime-contingent?year=YYYY` → encart header (`Total H.payés {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 Janv–Dé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 Juin–Déc sur le récap mensuel). À corriger séparément.