Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.4 KiB
Rollover RTT - Regles et Mise en Production
Document de reference pour expliquer le fonctionnement metier du report RTT N-1 et preparer le lancement en production.
1) Objectif
Permettre le report des heures supplementaires (RTT) d'un exercice a l'autre et fiabiliser les soldes.
Principe:
- le solde d'ouverture est stocke par exercice
- au changement d'exercice, on ouvre la nouvelle periode avec un "solde d'ouverture" (report N-1)
- au go-live, les soldes d'ouverture sont importes manuellement (CSV ou insertion SQL)
2) Exercice metier
- exercice RTT: du
1er juinau31 mai year= annee de fin d'exercice (ex:2026= 01/06/2025 -> 31/05/2026)- employes eligibles: tous sauf
INTERIMet suiviPRESENCE
3) Logique de compteurs
report N-1:- correspond au solde d'ouverture (
opening_minutes) - source prioritaire: table
employee_rtt_balances - fallback: calcul dynamique de la somme des minutes de recuperation de l'exercice precedent
- correspond au solde d'ouverture (
acquis N:- somme des minutes de recuperation hebdomadaires de l'exercice en cours
- calcul:
HS totales + bonus 25% + bonus 50%par semaine
disponible:report N-1 + acquis N
- affichage du compteur global: en jours (1 jour = 7h = 420 minutes)
4) Attribution mensuelle des semaines
- une semaine ISO qui chevauche deux mois est affichee dans les deux mois, avec les valeurs reparties proportionnellement aux minutes travaillees de chaque portion
- le calcul des heures supplementaires reste hebdomadaire (seuils 35h/39h/43h appliques sur la semaine entiere), seul l'affichage est scinde
- exemple: S14 lundi-mardi en mars, mercredi-dimanche en avril → la S14 apparait en mars (part lun-mar) et en avril (part mer-dim)
5) Table cible
Table employee_rtt_balances (une ligne par employe et exercice):
employee_idyearopening_minutesis_lockedcreated_at,updated_at
Contrainte unique:
(employee_id, year)
Etat implementation:
- la table est creee
- le calcul de synthese RTT lit en priorite
opening_minutesde cette table quand une ligne existe pour(employee, year) - si aucune ligne n'existe, le calcul dynamique sur l'exercice N-1 est effectue
Definition des colonnes
employee_id:- identifiant employe (FK vers
employees) - une ligne de solde par employe / exercice
- identifiant employe (FK vers
year:- annee d'exercice (annee de fin)
2026= 01/06/2025 -> 31/05/2026
opening_minutes:- report N-1 en minutes (solde d'ouverture)
- correspond a la somme des minutes de recuperation de l'exercice precedent
is_locked:falsesur exercice ouvert (recalcul possible)trueapres validation RH (exercice fige)
created_at,updated_at:- trace technique creation / mise a jour
6) Rollover automatique
Commande quotidienne (cron) idempotente.
- commande Symfony:
php bin/console app:rtt:rollover - comportement date metier:
- le
01/06: calcule et persiste le report pour chaque employe eligible - les autres jours: sortie sans action
- le
- option manuelle:
--forcepour executer hors date metier (reprise/correction) - option manuelle:
--recomputepour recalculer et ecraser les lignes existantes au lieu de les sauter (reprise apres correction). Les lignes verrouillees (is_locked = true, validees RH) ne sont jamais ecrasees.
Date d'effet:
- au
1er juin(meme date que le rollover conges non forfait)
Traitement par employe:
- verifier l'eligibilite (ni INTERIM, ni suivi PRESENCE)
- en mode normal: si une ligne existe deja pour
(employee, targetYear), la sauter (idempotence). En mode--recompute: la recalculer, sauf si elle est verrouillee. - calculer le solde de cloture de l'exercice N-1 (= disponible affiche en fin d'exercice) :
report d'ouverture N-1 + acquis N-1 − RTT payes N-1- le report d'ouverture N-1 vient de la ligne
employee_rtt_balancesde l'exercice N-1 (import go-live ou rollover precedent) ; a defaut, calcul dynamique des acquis de N-2. - l'acquis N-1 = somme des minutes de recuperation hebdomadaires de l'exercice N-1.
- les RTT payes N-1 (
employee_rtt_payments) sont deduits.
- le report d'ouverture N-1 vient de la ligne
- creer (ou mettre a jour) la ligne du nouvel exercice avec ce solde, reparti sur les 4 tranches
opening_base25/bonus25/base50/bonus50.
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::foldqui 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, rejouerapp:rtt:rollover --force --recomputepour recalculer les lignesemployee_rtt_balancesnon 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
La RH doit fournir les soldes RTT a reporter.
Colonnes minimales:
employee_id(id interne)yearopening_minutes(total en minutes)
Format recommande:
- CSV UTF-8
- separateur
;
Exemple:
employee_id;year;opening_minutes
42;2026;1260
17;2026;840
Equivalent en insertion SQL directe:
INSERT INTO employee_rtt_balances (employee_id, year, opening_minutes, is_locked, created_at, updated_at)
VALUES
(42, 2026, 1260, false, NOW(), NOW()),
(17, 2026, 840, false, NOW(), NOW());
Conversion rapide: 1260 minutes = 21h00 = 3.00 jours (1 jour = 420 min = 7h)
8) Checklist mise en prod
- Executer la migration (
employee_rtt_balances) - Importer les soldes d'ouverture N-1 (CSV ou SQL)
- Verifier 3 cas metier:
- CDI 39h avec heures supp sur l'exercice precedent
- CDI 35h sans heures supp (report = 0)
- INTERIM (doit etre ignore, pas de ligne creee)
- Activer le cron de rollover
- Geler (
is_locked) les exercices historicises valides
Exemple cron (tous les jours a 02:15, juste apres le rollover conges): Dev
15 2 * * * cd /var/www/html && php bin/console app:rtt:rollover --no-interaction 2>&1
Prod
10 2 * * * cd /var/www/sirh && php bin/console app:rtt:rollover --no-interaction 2>&1
Explication de la ligne cron:
15 2 * * *: tous les jours a 02:15php bin/console app:rtt:rollover --no-interaction: execute le rollover sans confirmation- hors
01/06, la commande sort en no-op (normal)
- hors
>> var/log/rtt-rollover.log 2>&1: log sortie standard et erreurs
Execution manuelle forcee:
php bin/console app:rtt:rollover --force --no-interaction
Exemple de verification rapide:
tail -n 50 /var/www/html/var/log/rtt-rollover.log
9) Points de vigilance
- Ne jamais modifier
opening_minutesapres validation RH sans procedure explicite - Garder une trace de toute correction manuelle (auteur, date, motif)
- Le calcul dynamique N-1 (fallback) parcourt toutes les heures de l'exercice precedent: preferer l'import explicite pour les exercices historiques
- La commande de rollover est idempotente: si une ligne existe deja, l'employe est ignore (pas d'ecrasement)