feat(contracts) : add EmployeeContractPhaseResolver service
This commit is contained in:
25
src/Dto/Contracts/ContractPhase.php
Normal file
25
src/Dto/Contracts/ContractPhase.php
Normal 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,
|
||||
) {}
|
||||
}
|
||||
83
src/Service/Contracts/EmployeeContractPhaseResolver.php
Normal file
83
src/Service/Contracts/EmployeeContractPhaseResolver.php
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user