ac8a36eb4f
Auto Tag Develop / tag (push) Successful in 7s
La bascule app:rtt:rollover ne reprenait que les RTT acquis de l'exercice qui se terminait : le report d'ouverture déjà présent était perdu et les paiements n'étaient pas déduits. Le nouveau report reprend le solde de clôture = report d'ouverture(N-1) + acquis(N-1) − RTT payés(N-1), soit le "Disponible" affiché par EmployeeRttSummaryProvider. - nouveau RttClosingBalanceService (fold pur testé : invariant somme tranches = disponible, cascade déficit 50% avant 25%, récup CUSTOM non perdue) - RttRolloverCommand branché dessus + option --recompute (écrase les lignes existantes non verrouillées, pour reprise d'une bascule erronée) - test date-sensible EmployeeRttSummaryProviderTest rendu robuste - docs: doc/rtt-rollover.md, CLAUDE.md, documentation-content.ts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #22 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
173 lines
7.0 KiB
Markdown
173 lines
7.0 KiB
Markdown
# 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 juin` au `31 mai`
|
||
- `year` = annee de fin d'exercice (ex: `2026` = 01/06/2025 -> 31/05/2026)
|
||
- employes eligibles: tous sauf `INTERIM` et suivi `PRESENCE`
|
||
|
||
## 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
|
||
- `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_id`
|
||
- `year`
|
||
- `opening_minutes`
|
||
- `is_locked`
|
||
- `created_at`, `updated_at`
|
||
|
||
Contrainte unique:
|
||
- `(employee_id, year)`
|
||
|
||
Etat implementation:
|
||
- la table est creee
|
||
- le calcul de synthese RTT lit en priorite `opening_minutes` de 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
|
||
- `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`:
|
||
- `false` sur exercice ouvert (recalcul possible)
|
||
- `true` apres 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
|
||
- option manuelle: `--force` pour executer hors date metier (reprise/correction)
|
||
- option manuelle: `--recompute` pour 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:
|
||
1. verifier l'eligibilite (ni INTERIM, ni suivi PRESENCE)
|
||
2. en mode normal: si une ligne existe deja pour `(employee, targetYear)`, la sauter (idempotence). En mode `--recompute`: la recalculer, sauf si elle est verrouillee.
|
||
3. 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_balances` de 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.
|
||
4. 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`.
|
||
|
||
> 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)
|
||
- `year`
|
||
- `opening_minutes` (total en minutes)
|
||
|
||
Format recommande:
|
||
- CSV UTF-8
|
||
- separateur `;`
|
||
|
||
Exemple:
|
||
```csv
|
||
employee_id;year;opening_minutes
|
||
42;2026;1260
|
||
17;2026;840
|
||
```
|
||
|
||
Equivalent en insertion SQL directe:
|
||
```sql
|
||
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
|
||
|
||
1. Executer la migration (`employee_rtt_balances`)
|
||
2. Importer les soldes d'ouverture N-1 (CSV ou SQL)
|
||
3. 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)
|
||
4. Activer le cron de rollover
|
||
5. Geler (`is_locked`) les exercices historicises valides
|
||
|
||
Exemple cron (tous les jours a 02:15, juste apres le rollover conges):
|
||
Dev
|
||
```cron
|
||
15 2 * * * cd /var/www/html && php bin/console app:rtt:rollover --no-interaction 2>&1
|
||
```
|
||
Prod
|
||
```cron
|
||
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:15
|
||
- `php bin/console app:rtt:rollover --no-interaction`: execute le rollover sans confirmation
|
||
- hors `01/06`, la commande sort en no-op (normal)
|
||
- `>> var/log/rtt-rollover.log 2>&1`: log sortie standard et erreurs
|
||
|
||
Execution manuelle forcee:
|
||
```bash
|
||
php bin/console app:rtt:rollover --force --no-interaction
|
||
```
|
||
|
||
Exemple de verification rapide:
|
||
```bash
|
||
tail -n 50 /var/www/html/var/log/rtt-rollover.log
|
||
```
|
||
|
||
## 9) Points de vigilance
|
||
|
||
- Ne jamais modifier `opening_minutes` apres 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)
|