fix(leave) : do not cap from at phase.startDate for non-forfait phases

The CP exercise (Juin N-1 → Mai N) is annual and continuous across
contract-signature changes within the same leave rule (e.g. 35h → 39h,
isDriver flip, weeklyHours bump). Capping `from` at the phase start
truncated the accrual to just the months under the latest phase,
producing wrong "en cours d'acquisition" values and dropping presence
days from earlier months on the leave-tab calendar.

For Damien GUILLOT (35h until 2025-10-31, then 39h), this gave 15 days
acquired (6 months Nov→Apr) instead of the expected 27.5 days
(11 months Jun→Apr at 2.5/month). After this fix, the H39 view shows
the full annual accrual as expected.

FORFAIT phases keep the from cap: the 218-day target is calendar-year
scoped and only counts the FORFAIT portion of the year.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 14:31:56 +02:00
parent f48f1d2f3a
commit bbde6ddcf3
4 changed files with 113 additions and 10 deletions

View File

@@ -89,7 +89,7 @@
- **Filtre `RTT_START_DATE`** : les phases dont `endDate < RTT_START_DATE` sont masquées (aucune donnée logiciel avant la mise en service). Le resolver reçoit la date via DI (`services.yaml`) ; `Employee::getContractPhases()` lit `$_SERVER['RTT_START_DATE']` pour instancier le resolver côté entité.
- Exposé via `Employee.contractPhases` (`employee:read`). Endpoints `GET /employees/{id}/leave-summary` et `GET /employees/{id}/rtt-summary` acceptent `?phaseId=N` ; défaut = phase courante.
- Sélectionner une phase passée :
- Onglet **Congés** : période et règles de la phase (Juin→Mai non-forfait, Jan→Déc FORFAIT). Exercices bornés à la phase, exercice de transition capé sur `phase.endDate`.
- Onglet **Congés** : période et règles de la phase (Juin→Mai non-forfait, Jan→Déc FORFAIT). Exercice de transition capé sur `phase.endDate`. **Cap `from` au `phase.startDate` uniquement pour FORFAIT** (sémantique année civile). Pour le non-forfait, l'exercice CP reste annuel et continu à travers les changements d'heures (35h→39h, etc.) — seul `resolveEffectivePeriodStart` clampe sur la date d'entrée en contrat des nouveaux embauchés.
- Onglet **RTT** : visible ssi `phase.contractType !== FORFAIT`. `+ Payer les RTT` actif uniquement sur l'exercice contenant `phase.endDate`.
- Bandeau jaune affiché en mode phase passée. Édition d'absences et des stocks de report (jours fractionnés, Année N-1 payés) désactivée.
- Sélection non persistée — chaque ouverture de fiche démarre sur la phase courante.