# 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::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.