*/ final readonly class AbsenceRequestProcessor implements ProcessorInterface { public function __construct( private EntityManagerInterface $entityManager, private Security $security, private AbsenceDayCalculator $calculator, private AbsencePolicyRepositoryInterface $policyRepository, private AbsenceRequestRepositoryInterface $requestRepository, private AbsenceBalanceService $balanceService, ) {} public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): AbsenceRequest { assert($data instanceof AbsenceRequest); $user = $this->security->getUser(); assert($user instanceof UserInterface); $type = $data->getType(); $startDate = $data->getStartDate(); $endDate = $data->getEndDate(); if (null === $type || null === $startDate || null === $endDate) { throw new UnprocessableEntityHttpException('Type, start date and end date are required.'); } if ($endDate < $startDate) { throw new UnprocessableEntityHttpException('End date must be on or after start date.'); } $policy = $this->policyRepository->findOneByType($type); if (null === $policy || !$policy->isActive()) { throw new UnprocessableEntityHttpException('This absence type is not available.'); } // Bereavement has no fixed entitlement: the relationship to the deceased // drives the legal number of days, so the reason is mandatory. if (AbsenceType::Bereavement === $type && '' === trim((string) $data->getReason())) { throw new UnprocessableEntityHttpException('A reason (relationship to the deceased) is required for bereavement leave.'); } if ($this->requestRepository->hasOverlap($user, $startDate, $endDate)) { throw new ConflictHttpException('This request overlaps an existing absence.'); } $countedDays = $this->calculator->countWorkingDays( $startDate, $endDate, $data->getStartHalfDay(), $data->getEndHalfDay(), $policy->isCountWorkingDaysOnly(), ); if ($countedDays <= 0.0) { throw new UnprocessableEntityHttpException('The selected range contains no working day.'); } $data->setUser($user); $data->setCountedDays($countedDays); $data->setStatus(AbsenceStatus::Pending); $data->setRejectionReason(null); $data->setCreatedAt(new DateTimeImmutable()); $this->entityManager->persist($data); $this->balanceService->reservePending($data); $this->entityManager->flush(); return $data; } }