CI / Docker Lint (push) Successful in 21s
- Added Symfony Validation Lib. - Updated API DTOs to include validation constraints.
193 lines
7.1 KiB
PHP
193 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\ApiErrorException;
|
|
use App\DTO\ApiErrorMessage;
|
|
use App\Entity\Booking;
|
|
use App\Entity\BookingRoom;
|
|
use App\Repository\BookingRepository;
|
|
use App\Repository\RoomCategoryRepository;
|
|
use App\Repository\RoomAvailabilityRepository;
|
|
use App\Request\BookingCreateRequest;
|
|
use App\Request\BookingDetailsRequest;
|
|
use App\Response\BookingCreateResponse;
|
|
use App\Service\PricesService;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Nelmio\ApiDocBundle\Annotation\Model;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use OpenApi\Attributes as OA;
|
|
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
|
|
class BookingController extends AbstractController
|
|
{
|
|
/**
|
|
* Get booking details
|
|
*/
|
|
#[Route('/api/booking/details', name: 'app_booking_details', methods: ["POST"], format: "json")]
|
|
#[OA\Tag(name: "Booking")]
|
|
#[OA\Response(
|
|
response: 200,
|
|
description: 'Returns booking details',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
ref: new Model(type: Booking::class),
|
|
)
|
|
)]
|
|
#[OA\RequestBody(
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
ref: new Model(type: BookingDetailsRequest::class),
|
|
)
|
|
)]
|
|
public function getBookingDetails(
|
|
BookingRepository $repoBooking,
|
|
#[MapRequestPayload()] BookingDetailsRequest $detailsRequest
|
|
): JsonResponse {
|
|
|
|
$selectedBooking = $repoBooking->findOneBy(["refNumber" => $detailsRequest->refNumber]);
|
|
if (\is_null($selectedBooking)) {
|
|
throw new NotFoundHttpException('Booking not found');
|
|
}
|
|
|
|
if ($selectedBooking->getBookingCustomer()->getEmail() != $detailsRequest->email) {
|
|
throw new NotFoundHttpException('Booking not found');
|
|
}
|
|
|
|
return $this->json($selectedBooking);
|
|
}
|
|
|
|
/**
|
|
* Create a new Booking.
|
|
*/
|
|
#[Route('/api/booking/create', name: 'app_booking_create', methods: ["POST"], format: "json")]
|
|
#[OA\Tag(name: "Booking")]
|
|
#[OA\Response(
|
|
response: 200,
|
|
description: 'Returns booking details',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
ref: new Model(type: BookingCreateResponse::class)
|
|
)
|
|
)]
|
|
#[OA\RequestBody(
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
ref: new Model(type: BookingCreateRequest::class),
|
|
)
|
|
)]
|
|
public function newBooking(
|
|
EntityManagerInterface $entityManager,
|
|
RoomCategoryRepository $repoCat,
|
|
RoomAvailabilityRepository $repoAvailability,
|
|
PricesService $priceSrv,
|
|
#[MapRequestPayload()] BookingCreateRequest $createRequest
|
|
): JsonResponse {
|
|
|
|
$booking = new Booking();
|
|
try {
|
|
if ($createRequest->arrivalDate >= $createRequest->departureDate) {
|
|
throw new ApiErrorException(
|
|
new ApiErrorMessage(422, ApiErrorMessage::TYPE_INVALID_DATE),
|
|
'Arrival cant be before departure',
|
|
);
|
|
}
|
|
|
|
$entityManager->beginTransaction();
|
|
|
|
$currentTime = new \DateTime("now");
|
|
|
|
// Create booking from request data.
|
|
$booking->setBookingTime($currentTime);
|
|
$booking->setArrivalDate($createRequest->arrivalDate);
|
|
$booking->setDepartureDate($createRequest->departureDate);
|
|
$booking->setBookingCustomer($createRequest->customerDetails);
|
|
|
|
//TODOME: Improve refNumber generation.
|
|
$highest_id = $entityManager->createQueryBuilder()
|
|
->select('MAX(e.id)')
|
|
->from('App\Entity\Booking', 'e')
|
|
->getQuery()
|
|
->getSingleScalarResult();
|
|
|
|
$booking->setRefNumber($currentTime->format("Y") . $highest_id + 1);
|
|
|
|
$entityManager->persist($booking);
|
|
|
|
if (\count($createRequest->rooms) == 0) {
|
|
throw new ApiErrorException(
|
|
new ApiErrorMessage(422, ApiErrorMessage::TYPE_VALIDATION_ERROR),
|
|
'Booking must contain rooms',
|
|
);
|
|
}
|
|
|
|
// Create each room.
|
|
foreach ($createRequest->rooms as $roomItem) {
|
|
$room = new BookingRoom();
|
|
$room->setBooking($booking);
|
|
|
|
//Get Room category.
|
|
$roomCategory = $repoCat->find($roomItem->roomCategory);
|
|
if ($roomCategory == null) {
|
|
throw new ApiErrorException(
|
|
new ApiErrorMessage(422, ApiErrorMessage::TYPE_VALIDATION_ERROR),
|
|
'Invalid RoomCategory: ' . $roomItem->roomCategory,
|
|
);
|
|
}
|
|
|
|
//Check room availability.
|
|
$amountAvailable = $repoAvailability->getAvailabilityForCategoryInPeriod($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate);
|
|
$selectedAmount = $roomItem->amount;
|
|
$selectedGuestAmount = $roomItem->guestNumber;
|
|
|
|
if ($amountAvailable == null || $selectedAmount > $amountAvailable) {
|
|
throw new ApiErrorException(
|
|
new ApiErrorMessage(400, ApiErrorMessage::TYPE_LOGICAL_ERROR),
|
|
'Not enought rooms available for category: ' . $roomItem->roomCategory,
|
|
);
|
|
}
|
|
|
|
$room->setRoomCategory($roomCategory);
|
|
$room->setGuestNumber($selectedGuestAmount);
|
|
$room->setAmount($selectedAmount);
|
|
|
|
//Get Price for period.
|
|
$priceList = $priceSrv->getFinalPricesForSearch($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate);
|
|
$priceFound = false;
|
|
foreach ($priceList as $key => $value) {
|
|
if ($key == $selectedGuestAmount) {
|
|
$room->setRoomPrice($value);
|
|
$priceFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$priceFound) {
|
|
throw new ApiErrorException(
|
|
new ApiErrorMessage(422, ApiErrorMessage::TYPE_LOGICAL_ERROR),
|
|
'Price not found for category: ' . $roomCategory->getId() . ', guests: ' . $selectedGuestAmount,
|
|
);
|
|
}
|
|
|
|
$entityManager->persist($room);
|
|
|
|
//If booking is nice, let's update current availability.
|
|
$repoAvailability->updateAvailabilityForRoomCategoryOnPeriod($roomCategory, $createRequest->arrivalDate, $createRequest->departureDate, $selectedAmount, true); //Update using "diff" mode.
|
|
}
|
|
|
|
$entityManager->commit();
|
|
$entityManager->flush();
|
|
} catch (\Throwable $th) {
|
|
$entityManager->rollback();
|
|
throw $th;
|
|
}
|
|
|
|
$results = ["refNumber" => $booking->getRefNumber()];
|
|
|
|
return $this->json($results);
|
|
}
|
|
}
|