abdaf809f8
Auto Tag Develop / tag (push) Has been cancelled
| 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>
123 lines
4.5 KiB
PHP
123 lines
4.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\State;
|
|
|
|
use ApiPlatform\Metadata\Operation;
|
|
use ApiPlatform\State\ProcessorInterface;
|
|
use App\ApiResource\EmployeeRttPaymentInput;
|
|
use App\Entity\Employee;
|
|
use App\Entity\EmployeeRttPayment;
|
|
use App\Repository\EmployeeRepository;
|
|
use App\Repository\EmployeeRttPaymentRepository;
|
|
use App\Service\AuditLogger;
|
|
use App\Service\Contracts\EmployeeContractPhaseResolver;
|
|
use App\Service\Exercise\ExerciseYearResolver;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Psr\Clock\ClockInterface;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
|
|
|
final readonly class EmployeeRttPaymentProcessor implements ProcessorInterface
|
|
{
|
|
public function __construct(
|
|
private EmployeeRepository $employeeRepository,
|
|
private EmployeeRttPaymentRepository $rttPaymentRepository,
|
|
private EntityManagerInterface $entityManager,
|
|
private AuditLogger $auditLogger,
|
|
private EmployeeContractPhaseResolver $phaseResolver,
|
|
private ClockInterface $clock,
|
|
private ExerciseYearResolver $exerciseYearResolver,
|
|
) {}
|
|
|
|
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): EmployeeRttPaymentInput
|
|
{
|
|
if (!$data instanceof EmployeeRttPaymentInput) {
|
|
throw new UnprocessableEntityHttpException('Invalid payload.');
|
|
}
|
|
|
|
$employeeId = (int) ($uriVariables['id'] ?? 0);
|
|
if ($employeeId <= 0) {
|
|
throw new UnprocessableEntityHttpException('id must be a positive integer.');
|
|
}
|
|
|
|
$employee = $this->employeeRepository->find($employeeId);
|
|
if (!$employee instanceof Employee) {
|
|
throw new NotFoundHttpException('Employee not found.');
|
|
}
|
|
|
|
if ($data->month < 1 || $data->month > 12) {
|
|
throw new UnprocessableEntityHttpException('month must be between 1 and 12.');
|
|
}
|
|
|
|
$year = $data->year ?? $this->resolveCurrentExerciseYear();
|
|
|
|
$this->assertYearAllowedForPayment($employee, $year);
|
|
|
|
$payment = $this->rttPaymentRepository->findOneByEmployeeYearMonth($employee, $year, $data->month);
|
|
|
|
if (null === $payment) {
|
|
$payment = new EmployeeRttPayment();
|
|
$payment->setEmployee($employee);
|
|
$payment->setYear($year);
|
|
$payment->setMonth($data->month);
|
|
$this->entityManager->persist($payment);
|
|
}
|
|
|
|
$payment->setBase25Minutes($data->base25Minutes);
|
|
$payment->setBonus25Minutes($data->bonus25Minutes);
|
|
$payment->setBase50Minutes($data->base50Minutes);
|
|
$payment->setBonus50Minutes($data->bonus50Minutes);
|
|
$payment->touch();
|
|
|
|
$empName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
|
$this->auditLogger->log(
|
|
$employee,
|
|
'update',
|
|
'rtt_payment',
|
|
$payment->getId(),
|
|
sprintf('Paiement RTT modifié pour %s (%02d/%d)', $empName, $data->month, $year),
|
|
['new' => ['month' => $data->month, 'year' => $year, 'base25' => $data->base25Minutes, 'bonus25' => $data->bonus25Minutes, 'base50' => $data->base50Minutes, 'bonus50' => $data->bonus50Minutes]],
|
|
);
|
|
|
|
$this->entityManager->flush();
|
|
|
|
$data->year = $year;
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function resolveCurrentExerciseYear(): int
|
|
{
|
|
return $this->exerciseYearResolver->forDate($this->clock->now());
|
|
}
|
|
|
|
/**
|
|
* 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->exerciseYearResolver->forDate($phase->endDate)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
throw new UnprocessableEntityHttpException(
|
|
'RTT payment is only allowed on the current exercise or the last exercise of a closed contract phase.'
|
|
);
|
|
}
|
|
}
|