[#SIRH-32] Ajouter l'exercice 2026/2027 dans les congés/RTT (#20)
Auto Tag Develop / tag (push) Successful in 9s

| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [ ] CHANGELOG modifié

Reviewed-on: #20
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #20.
This commit is contained in:
2026-05-26 14:09:02 +00:00
committed by Autin
parent 25083f00c8
commit cf2e12c8ba
18 changed files with 939 additions and 59 deletions
@@ -0,0 +1,94 @@
# Afficher l'en-cours d'acquisition « net / brut » sur l'onglet Congés
**Date** : 2026-05-26
**Ticket** : SIRH-32 (retour RH)
**Statut** : design validé
## Contexte
Sur l'onglet **Congés** de la fiche employé, la case **« En cours d'acquisition »**
affiche un seul nombre. Ce nombre est l'en-cours **net** : quand un salarié pose
des congés **en anticipé** (au-delà de son report acquis), ces jours sont imputés
sur la génération de l'exercice et **réduisent** l'en-cours affiché.
La RH a besoin de voir aussi le **total brut généré** sur l'exercice à ce jour,
afin de connaître ce qui a réellement été acquis indépendamment de ce qui a déjà
été consommé en anticipé.
Nouveau format demandé : `{net} / {brut}` — ex. `14,50 / 17,50`.
## Définitions (non-forfait, `CDI_CDD_NON_FORFAIT`)
Dans `EmployeeLeaveSummaryProvider::computeYearSummary` :
- `generatedDays` / `generatedSaturdays` = acquisition **brute** de l'exercice à
ce jour (prorata mensuel, capée à aujourd'hui pour l'exercice courant).
- `remainingToImpute` (+ samedis) = congés pris au-delà du report acquis, imputés
sur la génération.
- `accruingDays` (champ existant, **numérateur**) =
`(generatedDays remainingToImpute) + (generatedSaturdays remainingSaturdaysToImpute)`
→ en-cours **net**.
- **Nouveau** : `accruingDaysTotal` (**dénominateur**) = `generatedDays + generatedSaturdays`
→ brut généré à ce jour, avant imputation des congés anticipés.
Invariant : `accruingDays ≤ accruingDaysTotal`. La différence = jours pris en
anticipé imputés sur la génération.
Côté **forfait** (`FORFAIT_218`) : pas d'acquisition « en cours »
(`accruingDays = 0`) → `accruingDaysTotal = 0`.
## Changements
### 1. Backend
- `EmployeeLeaveSummaryProvider::computeYearSummary` : ajouter
`accruingDaysTotal` au tableau retourné.
- Branche non-forfait : `generatedDays + generatedSaturdays`.
- Branche forfait : `0.0`.
- `App\ApiResource\EmployeeLeaveSummary` : nouvelle propriété publique
`float $accruingDaysTotal = 0.0`.
- Le provider recopie `yearSummary['accruingDaysTotal']` dans
`$summary->accruingDaysTotal` (à côté de `accruingDays`). Sur retour anticipé
(`yearSummary === null`), la valeur reste `0.0` (comme `accruingDays`).
### 2. Frontend
- `frontend/services/dto/employee-leave-summary.ts` : ajouter
`accruingDaysTotal: number`.
- `frontend/components/employees/LeaveTab.vue`, case « En cours d'acquisition » :
- **Non-forfait** : afficher `{{ formatCount(accruingDays) }} / {{ formatCount(accruingDaysTotal) }}`.
- **Forfait** : inchangé (afficher `accruingDays` seul, soit `0`).
- Réutiliser `isForfaitRule` (déjà présent) pour la condition.
### 3. Documentation
- `doc/leave-tab.md` : décrire le format `net / brut` de l'en-cours d'acquisition.
- `frontend/data/documentation-content.ts` : note expliquant que l'en-cours
affiche `net / total brut acquis` et que les congés anticipés réduisent le net.
## Comportements conservés
- Quand aucun congé anticipé n'est pris : numérateur = dénominateur
(ex. `17,50 / 17,50`). C'est voulu — la RH veut le total visible en permanence.
- Les autres compteurs du header (Année acquis, Pris, Reste à prendre, Samedis,
N-1…) sont inchangés.
- Aucun changement de calcul : `accruingDaysTotal` est une **exposition** d'une
valeur déjà calculée (`generatedDays + generatedSaturdays`), pas une nouvelle
règle métier.
## Hors périmètre
- Onglet RTT (pas d'en-cours d'acquisition).
- Écran Récap congés (pas de colonne en-cours d'acquisition).
- Header de la fiche employé (présence / jours à travailler) — inchangé.
- Affichage de la fraction pour le forfait (pas d'en-cours → non pertinent).
## Tests
- Backend : ajouter un test sur `computeYearSummary` (via le harnais de tests
existant du provider, par réflexion) vérifiant que `accruingDaysTotal` =
`generatedDays + generatedSaturdays` et `≥ accruingDays` dans un cas avec congés
anticipés. À défaut de chemin testable simple (collaborateurs `final`), couvrir
l'arithmétique exposée et vérifier manuellement l'affichage.
- Pas de harnais de test frontend ; vérification manuelle de l'affichage
`net / brut` (non-forfait) et `0` (forfait).
@@ -0,0 +1,118 @@
# Proposer toujours l'exercice suivant dans les sélecteurs Congés et RTT
**Date** : 2026-05-26
**Ticket** : SIRH-32
**Statut** : design validé
## Contexte
Les onglets **Congés** et **RTT** de la fiche employé proposent un sélecteur
d'exercice (`availableLeaveYears` / `availableRttYears`) dont la borne haute est
plafonnée à l'**exercice courant**. La RH a commencé à poser des congés sur
l'**exercice suivant**, mais ne peut pas le consulter dans la fiche employé :
l'exercice suivant n'apparaît pas dans le menu déroulant.
On veut que le sélecteur propose **toujours** l'exercice suivant pour une phase
de contrat ouverte, afin que ce besoin ne ressurgisse jamais.
## Faisabilité — déjà supportée côté backend
Aucun changement backend n'est nécessaire :
- `EmployeeLeaveSummaryProvider::clampYearToPhase` et son équivalent RTT ne
plafonnent **pas** vers le haut quand la phase est ouverte (`phase.endDate`
nul → `lastYear = null`). Une requête `?year=<exercice+1>&phaseId=<phase ouverte>`
est donc déjà calculée correctement.
- La validation `year` (20002100) couvre largement l'exercice suivant.
Le seul blocage est le **frontend**, qui calcule `maxYear = exercice courant`.
## Le changement
Frontend uniquement. Dans `frontend/composables/useEmployeeLeave.ts`
(`availableLeaveYears`) et `frontend/composables/useEmployeeRtt.ts`
(`availableRttYears`), la borne haute devient :
- **Phase ouverte** (pas de `phase.endDate`) :
`maxYear = exercice courant + 1`.
- **Phase clôturée** (`phase.endDate` présent) : **inchangé**
`maxYear = exercice de fin de phase` (on ne propose pas au-delà d'une phase
terminée).
Le `+1` porte sur le **numéro d'exercice**, donc il est correct pour le forfait
(année civile) comme pour le non-forfait (Juin N-1 → Mai N), via
`computeLeaveYearForDate` / `computeRttYearForDate`.
### Pseudo-code de la borne
```
maxYear = phase.endDate
? computeYearForDate(phase.endDate) // phase clôturée : cap à la fin de phase
: currentYear + 1 // phase ouverte : on propose l'exercice suivant
```
La borne basse (`minYear = max(phaseStartYear, dataFloor)`) est **inchangée**.
## Comportements conservés
- **Sélection par défaut** : inchangée. L'onglet s'ouvre toujours sur l'exercice
**courant** ; l'exercice suivant est seulement disponible dans le menu (pas de
saut automatique sur le futur). `initSelected*Year` continue d'initialiser sur
`current*Year`, qui reste dans la plage `[minYear ; maxYear]`.
- **Verrouillage des éditions** : `isHistoricalYear` (`selectedYear !== currentYear`)
reste tel quel. Sur l'exercice suivant, les boutons **Jours fractionnés**,
**Année N-1 payés** (onglet Congés) et **+ Payer les RTT** (onglet RTT) sont
**désactivés** — souhaitable : pas d'édition de stocks ni de paiement sur un
exercice pas encore démarré.
- **Aucune mention « passé » trompeuse** : le bandeau « Vous consultez
l'historique » est piloté par la phase (`isViewingPastPhase`), pas par l'année
sélectionnée ; sélectionner un exercice futur ne l'affiche pas.
## Affichage des congés posés sur l'exercice suivant (réponse Q1)
Le header congés (grille de compteurs de l'onglet + libellé présence du header
de fiche) reflète **le récap de l'exercice sélectionné**. Chaque récap est
calculé sur sa fenêtre `[from, to]` ; les absences/jours pris ne sont comptés que
dans cette fenêtre.
- **Sur l'exercice courant** (vue par défaut) : les congés posés sur l'exercice
suivant **n'apparaissent pas** dans les compteurs — comportement correct.
- **Sur l'exercice suivant** (sélectionné) : ils s'affichent (calendrier +
compteur « Pris »).
### Caveat fonctionnel
Sur l'exercice suivant, les compteurs **report / Année N-1 / reste** sont
**provisoires** jusqu'à la clôture de l'exercice courant (ils en dépendent). En
revanche, le **« Pris » et le calendrier** des congés posés sont exacts. À
communiquer à la RH.
## Périmètre
- Onglet **Congés** et onglet **RTT** (les deux sélecteurs partagent la même
mécanique).
- Forfait (année civile) et non-forfait (Juin→Mai).
## Hors périmètre
- Aucune modification de la mécanique de saisie d'absences (la RH pose déjà des
congés sur l'exercice suivant via les écrans Calendrier / Heures, indépendamment
de ce sélecteur).
- Pas de proposition de plusieurs exercices futurs (un seul : N+1).
- Pas d'activation des éditions de stocks/paiement sur l'exercice futur.
## Documentation à mettre à jour (règle obligatoire CLAUDE.md)
- `doc/leave-tab.md` — plage du sélecteur.
- `doc/rtt-tab.md` — plage du sélecteur.
- `CLAUDE.md` — sections « Onglet Congés » et « Onglet RTT » (description de la
plage `max(...)` → borne haute `exercice courant + 1` sur phase ouverte).
- `frontend/data/documentation-content.ts` — documentation in-app.
## Tests
Pas de harnais de test frontend dans le projet (backend PHPUnit uniquement). La
modification est de la logique de calcul de plage dans deux `computed` :
vérification manuelle (dev Nuxt) que l'exercice suivant apparaît dans les deux
sélecteurs pour une phase ouverte, et n'apparaît pas pour une phase clôturée. Les
tests backend existants doivent rester verts (aucun changement backend).