fix : correction calcule prorata congés avec un arrêt maladie long
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
This commit is contained in:
116
src/Service/Leave/LongMaladieService.php
Normal file
116
src/Service/Leave/LongMaladieService.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user