Files
SIRH/doc/holiday-virtual-hours.md
tristan a8fe244b5c
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
feat : modification de la gestion des jours fériés
2026-04-16 15:52:19 +02:00

7.2 KiB
Raw Blame History

Crédit automatique des heures sur jour férié (Lun-Ven)

Règle

Tout jour férié du lundi au vendredi crédite automatiquement les heures contractuelles attendues pour ce jour, pour tout contrat autre que Forfait (trackingModePRESENCE). Les heures ainsi créditées sont dites virtuelles : aucune ligne n'est créée dans work_hours, elles sont injectées à l'affichage et au calcul.

Référence contractuelle par jour

Contrat Lun-Jeu Ven Sam-Dim
35h 7h 7h 0
39h 8h 7h 0
CUSTOM (avec planning workDaysHours) minutes du jour programmé, 0 sinon idem 0
INTERIM 35h 7h 7h 0
FORFAIT

La référence par jour est calculée par App\Service\WorkHours\DailyReferenceMinutesResolver.

Planning workDaysHours

Tout contrat TIME hors 35h/39h/INTERIM (ex. 4h, 25h, 28h) doit déclarer un planning précis sur sa EmployeeContractPeriod : colonne JSON work_days_hours = {"1": 120, "4": 120} (iso day → minutes). La somme doit égaler weeklyHours × 60.

  • Sur un jour du planning : crédit férié = minutes programmées (ex. Ewa Lun → 120 min).
  • Sur un jour hors planning : crédit férié = 0 (elle n'aurait pas travaillé).
  • Même logique appliquée par WorkedHoursCreditPolicy::resolveContractDayMinutes pour les crédits d'absence — un 4h en absence mardi (non programmée) = 0 crédit.

Validation à l'écriture : EmployeeContractPeriodValidator::assertWorkDaysHours. Le frontend expose un bloc « Jours travaillés » (cases Lun-Ven + input HH:MM) sur les formulaires de création employé + d'ajout de contrat, visible uniquement quand le contrat le requiert.

Limitation actuelle : l'édition in-place d'un schedule sur une période active existante n'est pas exposée via l'UI. Le drawer « Modifier le contrat » affiche le schedule en lecture seule à titre informatif. Pour corriger un schedule, la démarche est : clôturer le contrat en cours + créer un nouveau contrat avec le schedule corrigé. Si un besoin d'édition directe émerge, ajouter workDaysHours dans EmployeeContractChangeRequest::hasPeriodChangeRequest() et la logique d'update dans EmployeeContractPeriodManager.

Fériés exclus

Les fériés listés dans l'env EXCLUDED_PUBLIC_HOLIDAYS (par défaut Lundi de Pentecôte — journée de solidarité) ne donnent pas de crédit virtuel : le PublicHolidayService les filtre en amont, donc HolidayVirtualHoursResolver ne les voit pas comme fériés.

Interaction avec saisie

Quand l'employé saisit des heures ce jour-là :

  • heures finales = max(heures saisies + crédit d'absence éventuel, heures contractuelles de référence)

Exemples avec un contrat 39h et un férié un lundi :

Saisie employé Total affiché Interprétation
Aucune 8h Crédit 100% virtuel
Matin 09:00-13:00 (4h) 8h Le minimum contractuel l'emporte
09:00-12:00 + 13:00-19:00 (9h) 9h Les heures saisies l'emportent

Interaction avec absences

La création d'absence sur un férié Lun-Ven est autorisée (bouton Modifier visible). Dès qu'une absence est déclarée sur le jour (matin et/ou après-midi), le crédit virtuel férié est désactivé pour ce jour : c'est absence.type.countAsWorkedHours qui pilote le crédit d'heures, via WorkedHoursCreditPolicy.

  • countAsWorkedHours = true (ex. maladie payée) : crédit calculé normalement (7h/8h selon contrat × halfUnits/2). Même quantité que la référence virtuelle si journée complète, donc résultat identique — mais la source du crédit est l'absence, pas le férié.
  • countAsWorkedHours = false (ex. congé sans solde) : crédit = 0. Le férié ne compense pas.

Cette règle évite le double-crédit (absence + férié virtuel) et respecte le paramétrage fonctionnel du type d'absence.

Impact technique

Affichage

  • Écran Heures (vue jour) : sur un férié Lun-Ven non-Forfait, la colonne Total affiche la valeur effective (référence ou saisie, selon max). Un chip "Férié : Xh comptées" apparaît sous le pill bleu du férié.
  • Écran Heures Conducteurs (vue jour) : idem, plus un indicateur = Xh (férié) sous l'input "Heures jour" pour signaler que le crédit est imputé au bucket jour.
  • Vues semaine : les totaux hebdomadaires intègrent les minutes virtuelles. Un marqueur F + Xh apparaît dans la cellule du jour férié.
  • Onglet RTT : les semaines contenant un férié Lun-Ven gagnent du temps crédité, ce qui peut générer des heures sup (25% / 50%) là où l'ancienne règle produisait un déficit.

Calcul RTT

Le service App\Service\WorkHours\HolidayVirtualHoursResolver est injecté dans RttRecoveryComputationService::computeRecoveryByWeek(). Pour chaque jour ouvré :

effectiveMinutes = resolveEffectiveDailyMinutes(contract, date, metrics.totalMinutes + credited)
weeklyTotalMinutes += effectiveMinutes

Le reste du calcul (tranches +25%, +50%, base 25% à partir de 35h/39h) demeure inchangé ; seul le total hebdo injecté a évolué.

Calcul hebdomadaire d'affichage

WorkHourWeeklySummaryProvider applique la même substitution sur weeklyDayMinutes et weeklyTotalMinutes. Le DTO WeeklyDaySummary expose désormais un champ virtualHolidayMinutes utilisé par les vues semaine.

Contexte jour

WorkHourDayContextProvider expose virtualHolidayMinutes dans DayContextRow pour permettre au frontend de calculer le total journalier en temps réel pendant la saisie (sans aller-retour).

Frontend

Le composable frontend/composables/useHolidayVirtualHours.ts réplique la règle côté client et est consommé par useHoursPage.ts::getRowMetrics et useDriverHoursPage.ts::getRowMetrics.

Impact historique

La règle est appliquée à chaque lecture depuis les WorkHour — donc l'exercice courant et tout exercice recalculé live bénéficient automatiquement de la nouvelle règle sans migration.

Les reports N-1 stockés dans employee_rtt_balances.opening_*_minutes ont été saisis manuellement par la RH (valeurs officielles) et ne sont pas recalculés : ces snapshots restent la source de vérité pour les soldes d'ouverture.

Services impliqués

Composant Rôle
DailyReferenceMinutesResolver Résolution "minutes contractuelles par jour" (logique partagée, anciennement dupliquée).
HolidayVirtualHoursResolver Décide si la règle s'applique et renvoie le crédit virtuel ou la valeur effective.
RttRecoveryComputationService Applique la substitution dans le calcul hebdo RTT.
WorkHourWeeklySummaryProvider Applique la substitution dans les totaux hebdo UI.
WorkHourDayContextProvider Expose virtualHolidayMinutes par salarié/jour.
useHolidayVirtualHours.ts (frontend) Réplique la règle en live côté client.

Tests

  • tests/Service/WorkHours/HolidayVirtualHoursResolverTest.php couvre les scénarios par contrat + jours ouvrés/chômés.
  • make test (PHPUnit) valide l'intégration RTT / hebdo / contexte jour.