From f9b3edcf08ed23f2be0ecb23535cce46702f5ed3 Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 9 Jun 2026 10:15:38 +0200 Subject: [PATCH] docs(rtt) : custom contract deficit now reduces the balance Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 2 +- doc/rtt-rollover.md | 6 ++++++ doc/rtt-tab.md | 11 +++++++++++ frontend/data/documentation-content.ts | 5 +++-- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9af7a88..2776cb1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -65,7 +65,7 @@ ## Overtime Rules - Contracts <= 35h: +25% from 35h to 43h, +50% beyond - Contracts >= 39h: +25% from 39h to 43h, +50% beyond -- CUSTOM contracts (weeklyHours ≠ 35 and ≠ 39, not INTERIM/FORFAIT): reference = actual contractual hours, no 25%/50% bonuses (1h overtime = 1h recovery), deficit doesn't impact balance +- CUSTOM contracts (weeklyHours ≠ 35 and ≠ 39, not INTERIM/FORFAIT): reference = actual contractual hours, no 25%/50% bonuses (1h overtime = 1h recovery). **Le déficit (heures travaillées < heures contractuelles) réduit le cumul RTT 1:1** (peut devenir négatif, reporté à l'exercice suivant). Implémenté via `WeekRecoveryDetail::isFlatRecovery` / `EmployeeRttWeekSummary::isFlatRecovery` : ces semaines portent leur récup/déficit signé dans `totalMinutes` (`RttRecoveryComputationService::buildWeekRecoveryDetail`) et `EmployeeRttSummaryProvider::applyDeficitCascade` **ne draine pas** les tranches 25/50 pour elles (colonnes 25%/50% restent à 0). Le `RttClosingBalanceService::fold` reporte le déficit en N+1. - **Ancre de semaine (type de contrat)** : le type/nature de contrat d'une semaine RTT est résolu sur le **premier jour contracté** de la semaine, pas sur le lundi (`RttRecoveryComputationService::resolveWeekAnchorDate`). Sinon une semaine d'embauche en milieu de semaine (lundi hors contrat) serait classée CUSTOM → bonus 25%/50% désactivés à tort. Ex. CDD 39h embauché le jeudi : la semaine reste 39h, le seuil 25% est proraté aux jours contractés (`computeWeeklyOvertime25StartMinutes`), donc les heures au-delà ouvrent bien le +25%. - **Plafond 25%/50% proraté (mi-semaine)** : le plafond séparant 25% et 50% n'est **pas** codé en dur à 43h mais vaut `seuil_départ_proraté + largeur_bande_25%` (`RttRecoveryComputationService::{resolveOvertime25BandWidthMinutes, computeOvertimeBaseMinutes}`). Largeur = 43h − base (4h pour un 39h, 8h pour un 35h). Pour une semaine pleine le plafond redonne 43h (aucune régression) ; pour une embauche mi-semaine il se décale avec le départ, ouvrant la tranche 50%. Témoin Dylan (CDD 39h embauché jeudi, 22h) : 4h à 25% + 3h à 50%. **Hors périmètre** : l'écran Heures (`WorkHourWeeklySummaryProvider`) n'a pas cette proratisation (calcul dupliqué, laissé tel quel par décision métier). - INTERIM: no overtime bonuses, no recovery time diff --git a/doc/rtt-rollover.md b/doc/rtt-rollover.md index fa6d8c1..c69159e 100644 --- a/doc/rtt-rollover.md +++ b/doc/rtt-rollover.md @@ -96,6 +96,12 @@ Traitement par employe: > Regle clef : le report d'un exercice a l'autre reprend exactement le **disponible** affiche sur l'onglet RTT (cf. `EmployeeRttSummaryProvider`). Le report deja present au debut de l'exercice precedent n'est jamais perdu, et les heures deja payees ne sont pas re-creditees. Service mutualise : `App\Service\Rtt\RttClosingBalanceService`. +> Contrats CUSTOM : le solde de clôture intègre désormais les **déficits** hebdomadaires +> (semaines travaillées sous les heures contractuelles), via `RttClosingBalanceService::fold` +> qui gère les totaux négatifs. La clôture (donc le report d'ouverture N+1) peut être négative. +> Après une mise à jour de cette règle, rejouer `app:rtt:rollover --force --recompute` pour +> recalculer les lignes `employee_rtt_balances` non verrouillées calculées avec l'ancienne règle. + > Bug historique corrige : la version initiale ne reportait que `acquis N-1` (ni report d'ouverture, ni deduction des paiements), ce qui faisait disparaitre le solde de depart. Pour corriger des lignes deja creees a tort, relancer avec `--force --recompute`. ## 7) Donnees a fournir au go-live diff --git a/doc/rtt-tab.md b/doc/rtt-tab.md index 36ed0f8..f4f8a45 100644 --- a/doc/rtt-tab.md +++ b/doc/rtt-tab.md @@ -16,6 +16,17 @@ L'onglet est **masqué pour les contrats FORFAIT** (filtre `showRttTab` dans `us Toujours **Juin (Y-1) → Mai (Y)**. Le champ `EmployeeRttSummary.year` correspond à `Y` (année de fin d'exercice) ; ex. `year=2026` = `01/06/2025 → 31/05/2026`. +## Règle de calcul — contrats CUSTOM (4h, 25h…) + +Pour un contrat CUSTOM, la récupération est **plate** (1h sup = 1h récup, sans bonus 25 %/50 %). +Depuis 2026-06, une semaine **travaillée sous les heures contractuelles** produit un **déficit +signé** dans la colonne « Heure » qui **réduit le « Total » et le « Cumul »** (1h manquante = +-1h). Les colonnes Base/25 %/50 % restent à **0** (pas de tranches pour ces contrats). Le cumul +peut devenir négatif ; il est reporté à l'exercice suivant. + +Techniquement : `WeekRecoveryDetail::isFlatRecovery` marque ces semaines ; +`EmployeeRttSummaryProvider::applyDeficitCascade` les exclut du drainage des tranches 25/50. + ## Sélecteur d'année Position : sous la table, à l'intérieur de la zone scrollable, à gauche. diff --git a/frontend/data/documentation-content.ts b/frontend/data/documentation-content.ts index 8b16866..91d3318 100644 --- a/frontend/data/documentation-content.ts +++ b/frontend/data/documentation-content.ts @@ -364,8 +364,8 @@ export const documentationSections: DocSection[] = [ requiredLevel: 'admin', blocks: [ { type: 'paragraph', content: 'Les règles de calcul des heures supplémentaires dépendent du type de contrat.' }, - { type: 'list', content: 'Contrats ≤ 35h : +25% de 35h à 43h, +50% au-delà de 43h\nContrats ≥ 39h : +25% de 39h à 43h, +50% au-delà de 43h\nContrats CUSTOM (4h, 25h, etc.) : 1h supplémentaire = 1h de récupération, pas de bonus\nINTERIM : aucune récupération, aucun bonus' }, - { type: 'note', content: 'En cas de déficit hebdomadaire (heures travaillées < heures contrat), le déficit est déduit du cumul RTT : d\'abord des heures à 50%, puis des heures à 25%.' }, + { type: 'list', content: 'Contrats ≤ 35h : +25% de 35h à 43h, +50% au-delà de 43h\nContrats ≥ 39h : +25% de 39h à 43h, +50% au-delà de 43h\nContrats CUSTOM (4h, 25h, etc.) : 1h supplémentaire = 1h de récupération, pas de bonus. Une semaine sous les heures contractuelles réduit le cumul RTT (1h manquante = -1h), sans passer par les tranches 25/50\nINTERIM : aucune récupération, aucun bonus' }, + { type: 'note', content: 'En cas de déficit hebdomadaire (heures travaillées < heures contrat), le déficit est déduit du cumul RTT. Pour un 35h/39h, il est puisé d\'abord dans les heures à 50%, puis à 25%. Pour un contrat CUSTOM (4h, etc.), il réduit directement le cumul (pas de tranches 25/50) ; le cumul peut devenir négatif et est reporté à l\'exercice suivant.' }, ], }, { @@ -525,6 +525,7 @@ export const documentationSections: DocSection[] = [ { type: 'note', content: 'Les contrats INTERIM et le mode PRESENCE n\'accumulent pas de RTT (affiché à 0).' }, { type: 'note', content: 'Au passage à l\'exercice suivant (1er juin), le « Report N-1 » du nouvel exercice reprend exactement le « Disponible » de fin d\'exercice précédent, c\'est-à-dire report précédent + acquis − RTT payés. Le report déjà présent en début d\'année n\'est donc jamais perdu.' }, { type: 'paragraph', content: 'La colonne "Cumul" affiche le solde RTT à la fin de chaque semaine : Report N-1 + somme des heures hebdomadaires jusqu\'à la semaine concernée − paiements RTT des mois précédents. Un paiement enregistré sur le mois M n\'est déduit qu\'à partir des semaines du mois M+1. Permet la comparaison ligne à ligne avec un suivi RH externe (Excel).' }, + { type: 'note', content: 'Contrats CUSTOM (ex. 4h) : une semaine travaillée sous les heures contractuelles génère un déficit qui réduit le cumul RTT (1h manquante = -1h), sans tranches 25/50. Le cumul peut devenir négatif et est reporté à l\'exercice suivant.' }, ], }, {