feat : ajout de la gestion Congé

This commit is contained in:
2026-03-05 14:09:50 +01:00
parent fc2b184c50
commit 20a651895f
55 changed files with 4171 additions and 144 deletions

View File

@@ -2,6 +2,9 @@
Ce document centralise les règles métier actuellement implémentées dans l'application.
Document complementaire (rollover conges et checklist de lancement):
- `doc/leave-rollover.md`
## 1) Utilisateurs et accès
- `ROLE_ADMIN`
@@ -138,11 +141,61 @@ Tous les filtres checkbox sont cochés par défaut à l'ouverture du drawer.
- action `Clôturer`:
- bouton actif uniquement s'il existe un contrat en cours non déjà clôturé à la date du jour
- ouvre un drawer en lecture seule (type/temps de travail/date de début)
- seule la date de fin est saisissable (préremplie à aujourd'hui)
- backend: en mode clôture, seule `contractEndDate` est acceptée
- champs saisissables:
- `contractEndDate` (prérempli à aujourd'hui)
- `contractPaidLeaveSettled` (checkbox "Soldé dans le solde de tout compte")
- backend: en mode clôture, le flag `contractPaidLeaveSettled` est persisté sur la période clôturée
- action `Ajouter`:
- conserve le flux d'ajout d'un nouveau contrat via drawer dédié
- disponible uniquement s'il n'y a pas de contrat en cours, ou si le contrat en cours a déjà une date de fin
- onglet `Congé`:
- endpoint de synthèse: `GET /api/employees/{id}/leave-summary?year=YYYY`
- phase 1 métier (`CDI`/`CDD` non forfait + `FORFAIT`):
- exercice CP:
- `CDI`/`CDD` non forfait: du `1er juin (YYYY-1)` au `31 mai (YYYY)` (paramètre `year` = année de fin d'exercice)
- `FORFAIT`: du `1er janvier (YYYY)` au `31 décembre (YYYY)` (paramètre `year` = année civile)
- contrats `39h` / `35h` / `25h` (et plus largement CDI/CDD non forfait hors `4h`):
- acquis annuel CP: `25`
- acquis annuel samedi: `5`
- en cours d'acquisition jours: `25/12 = 2,08` jours/mois
- en cours d'acquisition samedis: `5/12 = 0,42` samedi/mois (non detaille en UI)
- samedis acquis affiches: uniquement `opening_saturdays` (report N-1)
- contrat `4h`:
- acquis annuel CP: `10`
- acquis annuel samedi: `0`
- en cours d'acquisition: `0.83` jour/mois
- contrat `FORFAIT`:
- base annuelle: `jours ouvrés de l'exercice (lundi-vendredi, hors jours fériés métropole) - 218`
- prorata: en cas de démarrage/fin de contrat en cours d'année civile, le calcul ne couvre que l'intervalle actif du contrat dans l'année
- reste à prendre: `acquis - absences` (toutes absences, demi-journées incluses)
- pas de samedi (`0`)
- pas de jours en cours d'acquisition (`0`)
- fractionné: `0` (saisie RH ultérieure, non calculée automatiquement)
- pour `CDI`/`CDD` non forfait:
- pris CP: basé sur absences de type code `C` (CONGÉ), en tenant compte des demi-journées
- samedi pris: absences `C` posées le samedi (demi-journée incluse)
- restants = acquis - pris (borné à 0)
- pour `FORFAIT`:
- pris: basé sur toutes les absences (demi-journées incluses)
- restants = acquis - pris (borné à 0)
- report annuel:
- le reliquat (`restants`) de l'exercice précédent est reporté dans les acquis de l'exercice courant
- pour `CDI`/`CDD` non forfait: report séparé jours + samedis
- pour `FORFAIT`: report uniquement sur les jours
- si un solde d'ouverture existe en base (`employee_leave_balances`) pour l'exercice courant, ce solde devient la source prioritaire du report
- si une clôture de contrat est marquée `contractPaidLeaveSettled=true` sur l'exercice précédent, le report vers l'exercice suivant est remis à `0`
- si une clôture `contractPaidLeaveSettled=true` existe dans l'exercice courant, le calcul est réinitialisé à partir du lendemain de cette clôture (pas de continuité intra-exercice)
- lecture des compteurs:
- `acquis` = droits reportés de l'exercice N-1 (après application des règles de soldé)
- `en cours d'acquisition` = total droits générés sur l'exercice N (jours + samedis en cours), sans detail séparé en UI
- règle de consommation:
- les absences s'imputent d'abord sur `acquis`, puis sur `en cours d'acquisition`
- la prise sur `en cours d'acquisition` est autorisée (usage anticipé)
- `en cours d'acquisition` peut devenir négatif si la prise dépasse le généré (ex: `2.08 - 3 = -0.92`), puis se reconstitue avec les acquisitions suivantes
- date d'arret de calcul:
- les compteurs sont calculés jusqu'au dernier jour du mois précédent (le mois en cours est exclu)
- exemple: au `04/03/2026`, l'arret de calcul est le `28/02/2026` (ou `29/02` en année bissextile)
- hors périmètre phase 1: `INTERIM` (retour non supporté)
## 10) Notifications

226
doc/leave-rollover.md Normal file
View File

@@ -0,0 +1,226 @@
# Rollover Conges - Regles et Mise en Production
Document de reference pour expliquer le fonctionnement metier du report N-1 et preparer le lancement en production.
## 1) Objectif
Eviter les recalculs "depuis le debut du contrat" et fiabiliser les soldes.
Principe:
- le solde est stocké par exercice
- au changement d'exercice, on ouvre la nouvelle période avec un "solde d'ouverture" (report N-1)
- un indicateur de cloture (`contractPaidLeaveSettled`) permet de couper la continuité entre 2 contrats
## 2) Exercices metier
- `CDI` / `CDD` non forfait:
- exercice: `1er juin` au `31 mai`
- `year` = annee de fin d'exercice (ex: `2026` = 01/06/2025 -> 31/05/2026)
- `FORFAIT`:
- exercice: `1er janvier` au `31 decembre`
- `year` = annee civile
- `INTERIM`:
- hors perimetre conges
## 3) Logique de compteurs
- `acquis`:
- correspond au report N-1 (solde d'ouverture)
- `en cours d'acquisition`:
- correspond aux droits generes sur l'exercice en cours
- `pris`:
- non forfait: absences type `C` (conge)
- forfait: toutes absences
- `restant`:
- `acquis + en_cours - pris` (borne a 0 dans l'affichage)
## 4) Effet du "solde de tout compte"
Le champ de cloture `contractPaidLeaveSettled` est saisi lors de la fermeture d'une periode contrat.
- `false`:
- continuite des droits entre contrats
- `true`:
- pas de reprise des droits precedents
- reset de continuite au lendemain de la date de cloture
## 5) Table cible
Table `employee_leave_balances` (une ligne par employe et exercice):
- `employee_id`
- `rule_code` (`CDI_CDD_NON_FORFAIT` ou `FORFAIT_218`)
- `year`
- `opening_days`
- `opening_saturdays`
- `accrued_days`
- `accrued_saturdays` (optionnel selon implementation)
- `taken_days`
- `taken_saturdays`
- `closing_days`
- `closing_saturdays`
- `is_locked`
- `created_at`, `updated_at`
Contrainte unique recommandee:
- `(employee_id, rule_code, year)`
Etat implementation:
- la table est creee
- le calcul de synthese conges lit en priorite `opening_days/opening_saturdays` de cette table quand une ligne existe pour `(employee, rule_code, year)`
- si aucune ligne n'existe, le calcul reste base sur le report dynamique N-1
- la commande `app:leave:rollover` calcule aussi le report dynamique N-1 si la ligne N-1 n'est pas encore persistée (pas de reset a 0 par defaut)
### Definition des colonnes
- `employee_id`:
- identifiant employe (FK vers `employees`)
- une ligne de solde par employe / regle / exercice
- `rule_code`:
- code de regle appliquee (`CDI_CDD_NON_FORFAIT`, `FORFAIT_218`)
- permet de savoir quelles regles de calcul sont utilisees
- `year`:
- annee d'exercice
- non forfait: annee de fin d'exercice (`2026` = 01/06/2025 -> 31/05/2026)
- forfait: annee civile (`2026` = 01/01/2026 -> 31/12/2026)
- `opening_days`:
- report N-1 en jours (solde d'ouverture)
- `opening_saturdays`:
- report N-1 "samedis" (0 pour forfait)
- `accrued_days`:
- droits generes sur l'exercice courant (N)
- `accrued_saturdays`:
- droits samedis generes sur N (0 pour forfait)
- `taken_days`:
- jours poses sur l'exercice
- `taken_saturdays`:
- samedis poses sur l'exercice (0 pour forfait)
- `closing_days`:
- solde de cloture jours (`opening_days + accrued_days - taken_days`)
- `closing_saturdays`:
- solde de cloture samedis (`opening_saturdays + accrued_saturdays - taken_saturdays`)
- `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:leave:rollover`
- comportement date metier:
- le `01/01`: traite uniquement `FORFAIT_218`
- le `01/06`: traite uniquement `CDI_CDD_NON_FORFAIT`
- les autres jours: sortie sans action
- option manuelle: `--force` pour executer hors date metier (reprise/correction)
Date d'effet:
- forfait: au `1er janvier`
- non forfait: au `1er juin`
Traitement par employe:
1. lire l'exercice precedent
2. determiner le report:
- si cloture `paidLeaveSettled=true` sur la periode precedente => report `0`
- sinon report = `closing` exercice precedent
3. creer la ligne du nouvel exercice avec ce report en `opening_*`
4. initialiser `accrued/taken/closing` pour le nouvel exercice
## 7) Donnees a fournir au go-live
La RH doit fournir un import d'ouverture:
Colonnes minimales:
- `employee_identifier` (id interne ou matricule)
- `rule_code`
- `year`
- `opening_days`
- `opening_saturdays` (0 pour forfait)
- `source_date` (date de reference du relevé RH)
- `comment` (optionnel)
Format recommande:
- CSV UTF-8
- separateur `;`
- decimales en point (`7.5`)
Exemple:
```csv
employee_id;rule_code;year;opening_days;opening_saturdays;source_date;comment
42;CDI_CDD_NON_FORFAIT;2026;12.5;2;2026-05-31;Reprise fichier RH
17;FORFAIT_218;2026;8;0;2025-12-31;Reprise fichier RH
```
## 8) Checklist mise en prod
1. Valider le mapping employe RH -> employe applicatif
2. Importer les soldes d'ouverture N-1
3. Verifier 5 cas metier:
- CDI simple sans changement de contrat
- CDD -> CDI avec `paidLeaveSettled=false`
- CDD -> CDI avec `paidLeaveSettled=true`
- Forfait sur annee complete
- Forfait avec debut en cours d'annee
4. Activer le cron de rollover
5. Geler (`is_locked`) les exercices historicises valides
Exemple cron (tous les jours a 02:10):
Dev
```cron
10 2 * * * cd /var/www/html && php bin/console app:leave:rollover --no-interaction >> var/log/leave-rollover.log 2>&1
```
Prod
```cron
10 2 * * * cd /var/www/sirh && php bin/console app:leave:rollover --no-interaction >> var/log/leave-rollover.log 2>&1
```
Explication de la ligne cron:
- `10 2 * * *`: planification
- `10` = minute
- `2` = heure
- `*` = tous les jours du mois
- `*` = tous les mois
- `*` = tous les jours de la semaine
- `cd /var/www/html`: se place dans le dossier de l application Symfony
- `php bin/console app:leave:rollover --no-interaction`: execute le rollover sans demander de confirmation
- hors `01/01` et `01/06`, la commande sort en no-op (normal)
- `>> var/log/leave-rollover.log`: ajoute la sortie standard dans le fichier de log (sans ecraser l historique)
- `2>&1`: redirige aussi les erreurs dans le meme fichier de log
Execution manuelle forcee:
```bash
php bin/console app:leave:rollover --force --no-interaction
```
Exemple de verification rapide:
```bash
tail -n 50 /var/www/html/var/log/leave-rollover.log
```
## 9) Points de vigilance
- Ne jamais recalculer les soldes historiques apres validation RH sans procedure explicite
- Garder une trace de toute correction manuelle (auteur, date, motif)
- Aligner strictement les regles UI et API sur les memes compteurs (pas de formule differente front/back)
## 10) Regle de consommation des droits
Regle metier:
- un employe peut poser des conges en cours d'acquisition
- la consommation se fait par ordre:
1. `acquis` (report N-1)
2. `en cours d'acquisition` (droits N)
Effet attendu:
- si `acquis = 0` et `en cours = 7.5`, puis prise de `7`, alors:
- `acquis` reste `0`
- `en cours` devient `0.5`
- si `acquis = 0` et `en cours = 2.5`, puis prise de `3`, alors:
- `acquis` reste `0`
- `en cours` devient `-0.5` (dette)
- le mois suivant, une acquisition de `2.5` ramené `en cours` a `2.0`
Formule de lecture recommandée:
- `restant_acquis = max(0, acquis - pris)`
- `reste_a_imputer_sur_en_cours = max(0, pris - acquis)`
- `restant_en_cours = en_cours - reste_a_imputer_sur_en_cours` (valeur negative autorisee)