diff --git a/doc/functional-rules.md b/doc/functional-rules.md index ffd0b80..3852532 100644 --- a/doc/functional-rules.md +++ b/doc/functional-rules.md @@ -212,12 +212,14 @@ Tous les filtres checkbox sont cochés par défaut à l'ouverture du drawer. - 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) - en cas de début/fin en cours de mois, l'acquisition est proratisée au nombre de jours calendaires couverts dans le mois + - en cas de suspension en cours de mois, l'acquisition est proratisée en jours ouvrés (lun-ven hors fériés) travaillés / 22 (standard mensuel) - 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 - en cas de début/fin en cours de mois, l'acquisition est proratisée au nombre de jours calendaires couverts dans le mois + - en cas de suspension en cours de mois, l'acquisition est proratisée en jours ouvrés (lun-ven hors fériés) travaillés / 22 - 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 diff --git a/src/Service/Leave/LeaveBalanceComputationService.php b/src/Service/Leave/LeaveBalanceComputationService.php index ee0a9cd..b62d745 100644 --- a/src/Service/Leave/LeaveBalanceComputationService.php +++ b/src/Service/Leave/LeaveBalanceComputationService.php @@ -278,10 +278,11 @@ final readonly class LeaveBalanceComputationService return 0.0; } - $periodStart = $this->normalizeDate($periodStart); - $periodEnd = $this->normalizeDate($periodEnd); - $coveredMonths = 0.0; - $cursor = $periodStart->modify('first day of this month')->setTime(0, 0); + $periodStart = $this->normalizeDate($periodStart); + $periodEnd = $this->normalizeDate($periodEnd); + $publicHolidays = [] !== $suspensions ? $this->buildPublicHolidayMap($periodStart, $periodEnd) : []; + $coveredMonths = 0.0; + $cursor = $periodStart->modify('first day of this month')->setTime(0, 0); while ($cursor <= $periodEnd) { $monthStart = $cursor > $periodStart ? $cursor : $periodStart; $monthEnd = $cursor->modify('last day of this month')->setTime(0, 0); @@ -289,11 +290,19 @@ final readonly class LeaveBalanceComputationService $monthEnd = $periodEnd; } - $coveredDays = ((int) $monthEnd->diff($monthStart)->format('%a')) + 1; if ([] !== $suspensions) { $suspendedDays = $this->suspensionDaysCalculator->countSuspendedDaysInMonth($monthStart, $monthEnd, $suspensions); - $coveredDays = max(0, $coveredDays - $suspendedDays); + if ($suspendedDays > 0) { + $businessDays = $this->countBusinessDaysInRange($monthStart, $monthEnd, $publicHolidays); + $suspendedBusinessDays = $this->suspensionDaysCalculator->countSuspendedBusinessDays($monthStart, $monthEnd, $suspensions, $publicHolidays); + $coveredMonths += max(0, $businessDays - $suspendedBusinessDays) / 22.0; + $cursor = $cursor->modify('first day of next month'); + + continue; + } } + + $coveredDays = ((int) $monthEnd->diff($monthStart)->format('%a')) + 1; $daysInMonth = (int) $cursor->format('t'); $coveredMonths += $coveredDays / $daysInMonth; @@ -317,8 +326,15 @@ final readonly class LeaveBalanceComputationService private function countBusinessDays(DateTimeImmutable $from, DateTimeImmutable $to): int { - $publicHolidays = $this->buildPublicHolidayMap($from, $to); - $count = 0; + return $this->countBusinessDaysInRange($from, $to, $this->buildPublicHolidayMap($from, $to)); + } + + /** + * @param array $publicHolidays pre-built map + */ + private function countBusinessDaysInRange(DateTimeImmutable $from, DateTimeImmutable $to, array $publicHolidays): int + { + $count = 0; for ($cursor = $from; $cursor <= $to; $cursor = $cursor->modify('+1 day')) { $weekDay = (int) $cursor->format('N'); $dayKey = $cursor->format('Y-m-d'); diff --git a/src/State/EmployeeLeaveSummaryProvider.php b/src/State/EmployeeLeaveSummaryProvider.php index fd30908..0816844 100644 --- a/src/State/EmployeeLeaveSummaryProvider.php +++ b/src/State/EmployeeLeaveSummaryProvider.php @@ -390,10 +390,11 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface return 0.0; } - $periodStart = $this->normalizeDate($periodStart); - $periodEnd = $this->normalizeDate($periodEnd); - $coveredMonths = 0.0; - $cursor = $periodStart->modify('first day of this month')->setTime(0, 0); + $periodStart = $this->normalizeDate($periodStart); + $periodEnd = $this->normalizeDate($periodEnd); + $publicHolidays = [] !== $suspensions ? $this->buildPublicHolidayMap($periodStart, $periodEnd) : []; + $coveredMonths = 0.0; + $cursor = $periodStart->modify('first day of this month')->setTime(0, 0); while ($cursor <= $periodEnd) { $monthStart = $cursor > $periodStart ? $cursor : $periodStart; $monthEnd = $cursor->modify('last day of this month')->setTime(0, 0); @@ -401,11 +402,19 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface $monthEnd = $periodEnd; } - $coveredDays = ((int) $monthEnd->diff($monthStart)->format('%a')) + 1; if ([] !== $suspensions) { $suspendedDays = $this->suspensionDaysCalculator->countSuspendedDaysInMonth($monthStart, $monthEnd, $suspensions); - $coveredDays = max(0, $coveredDays - $suspendedDays); + if ($suspendedDays > 0) { + $businessDays = $this->countBusinessDays($monthStart, $monthEnd, $publicHolidays); + $suspendedBusinessDays = $this->suspensionDaysCalculator->countSuspendedBusinessDays($monthStart, $monthEnd, $suspensions, $publicHolidays); + $coveredMonths += max(0, $businessDays - $suspendedBusinessDays) / 22.0; + $cursor = $cursor->modify('first day of next month'); + + continue; + } } + + $coveredDays = ((int) $monthEnd->diff($monthStart)->format('%a')) + 1; $daysInMonth = (int) $cursor->format('t'); $coveredMonths += $coveredDays / $daysInMonth; @@ -526,10 +535,13 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface ]; } - private function countBusinessDays(DateTimeImmutable $from, DateTimeImmutable $to): int + /** + * @param null|array $publicHolidays pre-built map (built if null) + */ + private function countBusinessDays(DateTimeImmutable $from, DateTimeImmutable $to, ?array $publicHolidays = null): int { - $publicHolidays = $this->buildPublicHolidayMap($from, $to); - $count = 0; + $publicHolidays ??= $this->buildPublicHolidayMap($from, $to); + $count = 0; for ($cursor = $from; $cursor <= $to; $cursor = $cursor->modify('+1 day')) { $weekDay = (int) $cursor->format('N'); $dayKey = $cursor->format('Y-m-d');