Files
SIRH/src/Service/Contracts/EmployeeContractPhaseResolver.php
T
tristan abdaf809f8
Auto Tag Develop / tag (push) Has been cancelled
Gestion du changement de type de contrat + correction du calcule des RTT sur un contrat qui commence en milieu de semaine (#19)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [x] CHANGELOG modifié

Reviewed-on: #19
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-05-22 06:42:33 +00:00

111 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service\Contracts;
use App\Dto\Contracts\ContractPhase;
use App\Entity\Employee;
use App\Entity\EmployeeContractPeriod;
use DateTimeImmutable;
use LogicException;
final readonly class EmployeeContractPhaseResolver
{
private ?DateTimeImmutable $dataStartDate;
public function __construct(string $dataStartDate = '')
{
$trimmed = trim($dataStartDate);
if ('' === $trimmed) {
$this->dataStartDate = null;
return;
}
$parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $trimmed);
$this->dataStartDate = $parsed instanceof DateTimeImmutable ? $parsed : null;
}
/**
* @return list<ContractPhase>
*/
public function resolvePhases(Employee $employee): array
{
$periods = $employee->getContractPeriods()->toArray();
usort(
$periods,
static fn (EmployeeContractPeriod $a, EmployeeContractPeriod $b): int => $a->getStartDate() <=> $b->getStartDate()
);
$today = new DateTimeImmutable('today');
$phases = [];
$group = [];
$signature = null;
foreach ($periods as $period) {
$currentSignature = $this->signature($period);
if (null !== $signature && $currentSignature !== $signature) {
$phases[] = $this->buildPhase($group, $today);
$group = [];
}
$group[] = $period;
$signature = $currentSignature;
}
if ([] !== $group) {
$phases[] = $this->buildPhase($group, $today);
}
// Hide phases entirely before the application's data start date: no usable
// work-hour or absence data exists before that date, so exposing them would
// confuse HR (e.g. legacy contract periods predating the software launch).
if (null !== $this->dataStartDate) {
$dataStart = $this->dataStartDate;
$phases = array_values(array_filter(
$phases,
static fn (ContractPhase $phase): bool => null === $phase->endDate || $phase->endDate >= $dataStart,
));
}
// Most recent first.
return array_reverse($phases);
}
private function signature(EmployeeContractPeriod $period): string
{
$contract = $period->getContract();
$type = $contract?->getType()->value ?? '';
$hours = $contract?->getWeeklyHours() ?? -1;
$driver = $period->getIsDriver() ? '1' : '0';
return sprintf('%s|%d|%s', $type, $hours, $driver);
}
/**
* @param non-empty-list<EmployeeContractPeriod> $group
*/
private function buildPhase(array $group, DateTimeImmutable $today): ContractPhase
{
$first = $group[0];
$last = end($group);
$endDate = $last->getEndDate();
$isCurrent = null === $endDate || $endDate >= $today;
$contract = $first->getContract();
return new ContractPhase(
id: (int) $first->getId(),
contractType: $contract?->getType() ?? throw new LogicException('Phase requires a contract type'),
weeklyHours: $contract?->getWeeklyHours(),
isDriver: $first->getIsDriver(),
startDate: $first->getStartDate(),
endDate: $endDate,
periodIds: array_map(static fn (EmployeeContractPeriod $p): int => (int) $p->getId(), $group),
isCurrent: $isCurrent,
contractNature: $first->getContractNatureEnum(),
);
}
}