7.2 KiB
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 (trackingMode ≠ PRESENCE). 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::resolveContractDayMinutespour 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 + Xhapparaî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.phpcouvre les scénarios par contrat + jours ouvrés/chômés.make test(PHPUnit) valide l'intégration RTT / hebdo / contexte jour.