feat: correct determination of inquiry status and conditional voucher field display
This commit is contained in:
@@ -5,7 +5,6 @@ namespace App\Service;
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Exception\BookingNotPossibleException;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\NoRoomsAvailableException;
|
||||
use App\Form\Model\BookingDto;
|
||||
@@ -192,7 +191,6 @@ class BookingService
|
||||
* Handles three booking status types:
|
||||
* - 'Frei': Regular booking with availability checks
|
||||
* - 'Anfrage': Inquiry booking, allows booking even with 0 availability
|
||||
* - 'Buchungsstop': Booking stopped, no bookings allowed
|
||||
*/
|
||||
public function startFreshBooking(Request $request, int $dateId, int $hotelId, ?int $agencyId = null): BookingDto
|
||||
{
|
||||
@@ -201,30 +199,23 @@ class BookingService
|
||||
throw new NotFoundHttpException(sprintf('Travel data not found for date ID %d and hotel ID %d', $dateId, $hotelId));
|
||||
}
|
||||
|
||||
// Handle Buchungsstop - no bookings allowed at all
|
||||
if ('Buchungsstop' === $travelData->status) {
|
||||
throw new BookingNotPossibleException($dateId, $hotelId);
|
||||
// Fetch and patch availability data (includes allowedBookingStatus from API)
|
||||
$availabilities = $this->travelDataService->getAvailabilityData($dateId);
|
||||
if (null !== $availabilities) {
|
||||
$this->travelDataService->patchAvailabilities($travelData, $availabilities);
|
||||
}
|
||||
|
||||
// Determine booking status based on travel status
|
||||
$isInquiryBooking = 'Anfrage' === $travelData->status;
|
||||
$bookingStatus = $isInquiryBooking ? 'A' : 'F';
|
||||
|
||||
// Get available rooms
|
||||
// Get bookable rooms (Frei or Anfrage with available > 0)
|
||||
$availableRooms = $travelData->getAvailableRooms();
|
||||
|
||||
// For regular bookings (Frei), prevent entry when no rooms are available
|
||||
// For inquiry bookings (Anfrage), allow even with 0 availability
|
||||
if (false === $isInquiryBooking && empty($availableRooms)) {
|
||||
// No bookable rooms means booking is not possible
|
||||
if ([] === $availableRooms) {
|
||||
throw new NoRoomsAvailableException($dateId, $hotelId);
|
||||
}
|
||||
|
||||
// For inquiry bookings with 0 availability, get all rooms ignoring availability count
|
||||
if ($isInquiryBooking && empty($availableRooms)) {
|
||||
$availableRooms = array_filter($travelData->rooms, function (Room $room) {
|
||||
return Constants::STATUS_AVAILABLE === $room->status;
|
||||
});
|
||||
}
|
||||
// Determine initial booking status based on room availability
|
||||
// This is for early UI decisions (e.g., voucher visibility), final verdict is in step 3
|
||||
$bookingStatus = $this->determineInitialBookingStatus($travelData);
|
||||
|
||||
// Create room selections with zero quantities (user will set these in step 1)
|
||||
$roomSelections = array_map(
|
||||
@@ -334,11 +325,11 @@ class BookingService
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups available rooms by selection type ('by_pax' or 'by_room').
|
||||
* Groups available rooms by selection type ('by_pax' or 'by_room') and sorts them by maxPax.
|
||||
*
|
||||
* @param array<int, Room> $rooms Rooms indexed by room ID
|
||||
*
|
||||
* @return array{by_pax: array<int, Room>, by_room: array<int, Room>}
|
||||
* @return array{by_pax: array<int, Room>, by_room: array<int, Room>} Rooms grouped and sorted by maxPax (ascending)
|
||||
*/
|
||||
public function groupRoomsBySelectionType(array $rooms): array
|
||||
{
|
||||
@@ -354,6 +345,10 @@ class BookingService
|
||||
}
|
||||
}
|
||||
|
||||
// Sort each group by maxPax (ascending order - smallest capacity first)
|
||||
uasort($groups[Room::SELECTION_TYPE_BY_PAX], fn (Room $a, Room $b): int => $a->maxPax <=> $b->maxPax);
|
||||
uasort($groups[Room::SELECTION_TYPE_BY_ROOM], fn (Room $a, Room $b): int => $a->maxPax <=> $b->maxPax);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
@@ -491,4 +486,63 @@ class BookingService
|
||||
$participant->additionalServices = $currentSelections;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the initial booking status based on travel configuration.
|
||||
*
|
||||
* This method implements a multi-stage check to determine if a booking
|
||||
* should start as inquiry ('A') or final ('F') booking:
|
||||
* 1. Checks if final bookings are allowed via buchungstatusmoeglich attribute from API
|
||||
* 2. Checks if only inquiry rooms are available (no Frei rooms with available > 0)
|
||||
*
|
||||
* @param Travel $travelData The travel data to evaluate
|
||||
*
|
||||
* @return string 'A' for inquiry booking, 'F' for final booking
|
||||
*/
|
||||
private function determineInitialBookingStatus(Travel $travelData): string
|
||||
{
|
||||
// Check 1: Allowed booking status from buchungstatusmoeglich attribute (from availability API)
|
||||
// Only applies if the API provided status restrictions
|
||||
if ([] !== $travelData->allowedBookingStatus
|
||||
&& false === $travelData->isBookingStatusAllowed(Constants::BOOKING_STATUS_FREE)) {
|
||||
return 'A';
|
||||
}
|
||||
|
||||
// Check 2: Only inquiry rooms available (all rooms with available > 0 have status 'Anfrage')
|
||||
if ($travelData->requiresInquiryBooking()) {
|
||||
return 'A';
|
||||
}
|
||||
|
||||
return 'F';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates booking status based on selected room requirements.
|
||||
*
|
||||
* Checks if any selected room has inquiry-only status. If so, forces
|
||||
* the entire booking to inquiry mode. This override happens after room
|
||||
* selection in Step 1 and respects the business rule: if ANY room requires
|
||||
* inquiry, the whole booking becomes an inquiry.
|
||||
*
|
||||
* @param BookingDto $bookingDto The booking DTO to update
|
||||
*/
|
||||
public function updateBookingStatusFromRoomSelection(BookingDto $bookingDto): void
|
||||
{
|
||||
// Skip if already inquiry - no need to check
|
||||
if ('A' === $bookingDto->bookingStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check selected rooms for inquiry-only status
|
||||
foreach ($bookingDto->getSelectedRooms() as $roomSelection) {
|
||||
$room = $bookingDto->travel->getRoomById($roomSelection->roomId);
|
||||
if ($room
|
||||
&& Constants::STATUS_ON_REQUEST === $room->status
|
||||
&& $room->available > 0) {
|
||||
$bookingDto->bookingStatus = 'A';
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user