feat(contracts) : add EmployeeContractPhaseResolver service

This commit is contained in:
2026-05-19 10:30:41 +02:00
parent 7ee2e91e71
commit a56f797ed7
3 changed files with 251 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Dto\Contracts;
use App\Enum\ContractType;
use DateTimeImmutable;
final readonly class ContractPhase
{
/**
* @param list<int> $periodIds
*/
public function __construct(
public int $id,
public ContractType $contractType,
public ?int $weeklyHours,
public bool $isDriver,
public DateTimeImmutable $startDate,
public ?DateTimeImmutable $endDate,
public array $periodIds,
public bool $isCurrent,
) {}
}

View File

@@ -0,0 +1,83 @@
<?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
{
/**
* @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);
}
// 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,
);
}
}