From 37c718c839fb601e3c4b6b274bd171cf831f32c8 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 20 May 2026 16:17:07 +0200 Subject: [PATCH] feat(leave) : forfait mid-year entry credits prorated repos + carried CP Co-Authored-By: Claude Opus 4.7 (1M context) --- src/State/EmployeeLeaveSummaryProvider.php | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/State/EmployeeLeaveSummaryProvider.php b/src/State/EmployeeLeaveSummaryProvider.php index 74d9754..e7d70ff 100644 --- a/src/State/EmployeeLeaveSummaryProvider.php +++ b/src/State/EmployeeLeaveSummaryProvider.php @@ -763,6 +763,33 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface { $type = $phase->contractType; if (ContractType::FORFAIT === $type) { + $year = (int) $from->format('Y'); // période forfait = année civile + + // Entrée en FORFAIT en cours d'année : repos proratisés + CP nets reportés de + // la phase précédente, au lieu de max(0, businessDays − 218) qui donnerait 0. + if ($this->isForfaitEntryYear($phase, $year)) { + $yearStart = new DateTimeImmutable(sprintf('%d-01-01 00:00:00', $year)); + $yearEnd = new DateTimeImmutable(sprintf('%d-12-31 00:00:00', $year)); + $rawYearHolidays = $this->buildRawPublicHolidayMap($yearStart, $yearEnd); + + $businessDaysYear = $this->countBusinessDays($yearStart, $yearEnd, $rawYearHolidays); + $businessDaysPeriod = $this->countBusinessDays($from, $to, $rawYearHolidays); + + $repoDays = $this->computeProratedForfaitRepoDays($businessDaysYear, $businessDaysPeriod); + $carriedCp = $this->resolveCarriedCpFromPriorPhase($employee, $phase); + + return [ + 'ruleCode' => LeaveRuleCode::FORFAIT_218->value, + 'acquiredDays' => $repoDays + $carriedCp, + 'acquiredSaturdays' => 0.0, + 'accrualPerMonth' => 0.0, + 'saturdayAccrualPerMonth' => 0.0, + 'countOnlyCp' => false, + 'splitSaturdays' => false, + ]; + } + + // Année pleine : calcul 218 existant (INCHANGÉ). // Business days for forfait must use the RAW holiday list (excluded holidays like // "Lundi de Pentecôte" / journée de solidarité still count as non-working days for // the 218-day legal target).