0a9b26d31e
Auto Tag Develop / tag (push) Successful in 6s
Les heures contractuelles au-delà de 35h (ex. 39h → 17,33h décimales = 17h20/mois) sont payées chaque mois sans transiter par les paiements RTT (référence 39h). Elles manquaient au contingent. Ajout via StructuralOvertimeContingentCalculator : (weeklyHours-35)×260 min/mois, généralisé aux contrats non-forfait/non-intérim >35h, proratisé aux jours sous contrat. Branché sur l'encart fiche et l'export PDF. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.8 KiB
2.8 KiB
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/12h =(weeklyHours − 35) × 260min. Pour un 39h :4 × 260 = 1040min = 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ètrefindScoped), groupé par site (displayOrder), tridisplayOrder → nom → prénom, colonnes Janv–Déc + colonneTotal payé / payable. BuilderOvertimeContingentExportBuilder, templateovertime-contingent/print.html.twig.
Hors périmètre / connu
- Bug latent récap salaire :
SalaryRecapPrintProviderrequêtefindByYearAndMonthavec 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.