From 750f2bffa8223004e7103111aee04331793f5ec2 Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 19 May 2026 15:10:51 +0200 Subject: [PATCH] fix(rtt) : skip non-contracted days in overtime 25% threshold computeWeeklyOvertime25StartMinutes used a ternary that fell back to 35h when a day had no active contract. Once periodFrom was uncapped (so the RTT week iterates the full exercise), pre-hire days were silently contributing 7h each to the weekly threshold, erasing the 25% overtime for the employee's first partial week. Dylan CHABOISSON week 12 (Mar 19 hire, worked Thu 9h + Fri 9h30 + Sat 3h30 = 22h): threshold was 36h (incorrect), now back to 15h (Thu 8h + Fri 7h) so the 7h above threshold + 1h45 bonus are correctly credited. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Service/Rtt/RttRecoveryComputationService.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Service/Rtt/RttRecoveryComputationService.php b/src/Service/Rtt/RttRecoveryComputationService.php index 37a56aa..ebd4b5b 100644 --- a/src/Service/Rtt/RttRecoveryComputationService.php +++ b/src/Service/Rtt/RttRecoveryComputationService.php @@ -413,10 +413,17 @@ final readonly class RttRecoveryComputationService { $total = 0; foreach ($days as $date) { - $isoDay = (int) new DateTimeImmutable($date)->format('N'); - $contract = $contractsByDate[$date] ?? null; - $hours = $contract?->getWeeklyHours(); - $startHours = (null !== $hours && $hours >= 39) ? 39 : 35; + $isoDay = (int) new DateTimeImmutable($date)->format('N'); + $contract = $contractsByDate[$date] ?? null; + $hours = $contract?->getWeeklyHours(); + // Days without an active contract (pre-hire, post-termination, contract + // gaps) must NOT contribute to the weekly 25% overtime threshold — + // otherwise hiring mid-week artificially inflates the threshold and + // erases legitimate overtime. + if (null === $hours || $hours <= 0) { + continue; + } + $startHours = $hours >= 39 ? 39 : 35; $total += $this->resolveDailyReferenceMinutes($startHours, $isoDay); }