Files
SIRH/src/Service/Leave/LongMaladieService.php
tristan 9787231052
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
fix : correction calcule prorata congés avec un arrêt maladie long
2026-03-17 13:27:51 +01:00

117 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service\Leave;
use App\Entity\Employee;
use App\Repository\AbsenceRepository;
use DateTimeImmutable;
use function count;
/**
* Detects continuous MALADIE (sick leave) periods and computes
* the date ranges where reduced accrual applies (after the first month grace).
*/
final readonly class LongMaladieService
{
private const int MAX_GAP_DAYS = 3;
public function __construct(
private AbsenceRepository $absenceRepository,
) {}
/**
* Returns date ranges where the reduced maladie accrual rate applies.
* For continuous maladie periods > 1 month, the first month is excluded (grace period).
*
* @return list<array{start: DateTimeImmutable, end: DateTimeImmutable}>
*/
public function findReducedRatePeriods(
Employee $employee,
DateTimeImmutable $from,
DateTimeImmutable $to
): array {
// Look back 13 months to catch maladie that started before the exercise period
$extendedFrom = $from->modify('-13 months');
$dates = $this->absenceRepository->findMaladieDatesByEmployee($employee, $extendedFrom, $to);
if ([] === $dates) {
return [];
}
$periods = $this->consolidateIntoPeriods($dates);
return $this->applyFirstMonthGrace($periods);
}
/**
* Count calendar days in [monthStart, monthEnd] that fall within reduced maladie periods.
*
* @param list<array{start: DateTimeImmutable, end: DateTimeImmutable}> $reducedPeriods
*/
public function countReducedDaysInMonth(
DateTimeImmutable $monthStart,
DateTimeImmutable $monthEnd,
array $reducedPeriods
): int {
$total = 0;
foreach ($reducedPeriods as $period) {
$overlapStart = $period['start'] > $monthStart ? $period['start'] : $monthStart;
$overlapEnd = $period['end'] < $monthEnd ? $period['end'] : $monthEnd;
if ($overlapStart > $overlapEnd) {
continue;
}
$total += ((int) $overlapEnd->diff($overlapStart)->format('%a')) + 1;
}
return $total;
}
/**
* @param list<DateTimeImmutable> $dates sorted chronologically
*
* @return list<array{start: DateTimeImmutable, end: DateTimeImmutable}>
*/
private function consolidateIntoPeriods(array $dates): array
{
$periods = [];
$start = $dates[0];
$prev = $start;
for ($i = 1, $count = count($dates); $i < $count; ++$i) {
$current = $dates[$i];
$gap = (int) $prev->diff($current)->format('%a');
if ($gap > self::MAX_GAP_DAYS) {
$periods[] = ['start' => $start, 'end' => $prev];
$start = $current;
}
$prev = $current;
}
$periods[] = ['start' => $start, 'end' => $prev];
return $periods;
}
/**
* @param list<array{start: DateTimeImmutable, end: DateTimeImmutable}> $periods
*
* @return list<array{start: DateTimeImmutable, end: DateTimeImmutable}>
*/
private function applyFirstMonthGrace(array $periods): array
{
$result = [];
foreach ($periods as $period) {
$gracedStart = $period['start']->modify('+1 month');
if ($gracedStart > $period['end']) {
continue;
}
$result[] = ['start' => $gracedStart, 'end' => $period['end']];
}
return $result;
}
}