feat(rtt) : allow payment on closed phase last exercise

This commit is contained in:
2026-05-19 11:23:20 +02:00
parent 8684d240bc
commit 613ac02e1d
2 changed files with 210 additions and 3 deletions

View File

@@ -12,8 +12,10 @@ use App\Entity\EmployeeRttPayment;
use App\Repository\EmployeeRepository;
use App\Repository\EmployeeRttPaymentRepository;
use App\Service\AuditLogger;
use App\Service\Contracts\EmployeeContractPhaseResolver;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Clock\ClockInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
@@ -24,6 +26,8 @@ final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
private EmployeeRttPaymentRepository $rttPaymentRepository,
private EntityManagerInterface $entityManager,
private AuditLogger $auditLogger,
private EmployeeContractPhaseResolver $phaseResolver,
private ClockInterface $clock,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): EmployeeRttPaymentInput
@@ -48,6 +52,8 @@ final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
$year = $data->year ?? $this->resolveCurrentExerciseYear();
$this->assertYearAllowedForPayment($employee, $year);
$payment = $this->rttPaymentRepository->findOneByEmployeeYearMonth($employee, $year, $data->month);
if (null === $payment) {
@@ -83,10 +89,44 @@ final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
private function resolveCurrentExerciseYear(): int
{
$today = new DateTimeImmutable('today');
$year = (int) $today->format('Y');
$month = (int) $today->format('n');
return $this->resolveExerciseYearForDate($this->clock->now());
}
/**
* Map a date to the RTT exercise year it belongs to (Juin N-1 → Mai N convention).
*/
private function resolveExerciseYearForDate(DateTimeImmutable $date): int
{
$year = (int) $date->format('Y');
$month = (int) $date->format('n');
return $month >= 6 ? $year + 1 : $year;
}
/**
* Allow payment when the requested exercise is either the current one
* or the last exercise of a closed contract phase (the one containing
* the phase end date). Reject any other exercise (past or future).
*/
private function assertYearAllowedForPayment(Employee $employee, int $year): void
{
$currentExerciseYear = $this->resolveCurrentExerciseYear();
if ($year === $currentExerciseYear) {
return;
}
$phases = $this->phaseResolver->resolvePhases($employee);
foreach ($phases as $phase) {
if ($phase->isCurrent || null === $phase->endDate) {
continue;
}
if ($year === $this->resolveExerciseYearForDate($phase->endDate)) {
return;
}
}
throw new UnprocessableEntityHttpException(
'RTT payment is only allowed on the current exercise or the last exercise of a closed contract phase.'
);
}
}