security->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException('Access denied: ROLE_ADMIN required.'); } if (!in_array($decision, ['approve', 'reject'], true)) { throw new InvalidArgumentException('decision must be "approve" or "reject".'); } $request = $this->requestRepository->findById($id); if (null === $request) { throw new InvalidArgumentException(sprintf('AbsenceRequest with ID %d not found.', $id)); } if (AbsenceStatus::Pending !== $request->getStatus()) { throw new InvalidArgumentException('Only a pending request can be reviewed.'); } $admin = $this->security->getUser(); assert($admin instanceof UserInterface); if ('approve' === $decision) { // Never let an approval push the balance below zero (CP only). $available = $this->balanceService->availableForRequest($request); if (null !== $available && $request->getCountedDays() > $available + 1e-9) { throw new InvalidArgumentException(sprintf( 'Approving this request would put the balance below zero: %g day(s) requested but only %g available.', $request->getCountedDays(), $available, )); } $request->setStatus(AbsenceStatus::Approved); $request->setRejectionReason(null); $this->balanceService->applyApproval($request); } else { if (null === $rejectionReason || '' === trim($rejectionReason)) { throw new InvalidArgumentException('A reason is required when rejecting a request.'); } $request->setStatus(AbsenceStatus::Rejected); $request->setRejectionReason($rejectionReason); $this->balanceService->release($request, false); } $request->setReviewedAt(new DateTimeImmutable()); $request->setReviewedBy($admin); $this->entityManager->flush(); return json_encode(Serializer::absenceRequest($request)); } }