diff --git a/src/Form/Service/ParticipantFieldOptionsProvider.php b/src/Form/Service/ParticipantFieldOptionsProvider.php index 4bfad1c..4a7cee0 100644 --- a/src/Form/Service/ParticipantFieldOptionsProvider.php +++ b/src/Form/Service/ParticipantFieldOptionsProvider.php @@ -10,11 +10,13 @@ use App\BusProNet\Model\Pickup; use App\BusProNet\Model\Service; use App\BusProNet\Utility\DirectionMapper; use App\Form\Model\BookingDto; +use App\Form\Model\RoomSelectionDto; use App\Form\Service\Abstract\AbstractFieldOptionsProvider; use App\Form\Service\Factory\ParticipantRoomChoiceLoaderFactory; use App\Service\BookingPriceCalculatorService; use App\Service\InsuranceService; use App\Service\ServiceAvailabilityCalculator; +use App\Validator\Constraints\RoomSelection; /** * Provides dynamic field options for participant form fields. @@ -119,35 +121,35 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider // Room assignment field provider (available for both create and edit workflows) $this->fieldOptionProviders['assignedRoomId'] = function (BookingDto $bookingDto, int $participantIndex, array $options = []) { $choiceLoader = null; - $disabled = false; + $singleChoice = false; if (BookingDto::MODE_CREATE === $bookingDto->getMode()) { - // Create context: use selected rooms from step 1 - $choiceLoader = $this->roomChoiceLoaderFactory->createForCreate( - $bookingDto->getSelectedRooms() + // Create context: use selected rooms from step 1, filtered by participant age + $filteredRooms = $this->filterRoomsByParticipantAge( + $bookingDto->getSelectedRooms(), + $bookingDto, + $participantIndex ); - // Disable when only one room type selected (auto-assigned) - $disabled = 1 === count($bookingDto->getSelectedRooms()); + $choiceLoader = $this->roomChoiceLoaderFactory->createForCreate($filteredRooms); + $singleChoice = 1 === count($filteredRooms); } elseif (BookingDto::MODE_EDIT === $bookingDto->getMode()) { // Edit context: use already-booked rooms from booking $choiceLoader = $this->roomChoiceLoaderFactory->createForEdit( $bookingDto->booking->rooms ); - // Disable when only one room in booking (no reassignment needed) - $disabled = 1 === count($bookingDto->booking->rooms); + $singleChoice = 1 === count($bookingDto->booking->rooms); } return [ 'label' => 'Zimmer', - 'placeholder' => 'Nicht zugeordnet', + // No placeholder when only one room available - the only option is pre-selected + 'placeholder' => $singleChoice ? false : 'Nicht zugeordnet', // Use factory to create context-aware choice loader that: // - Shows only available rooms for this participant // - Excludes rooms already assigned to other participants // - Respects room capacity and booking constraints + // - Filters Baby rooms for participants over 2 years old 'choice_loader' => $choiceLoader, - // Make field read-only when only one room type is available - // Room is auto-assigned or cannot be changed, no user choice needed - 'disabled' => $disabled, ]; }; @@ -540,6 +542,54 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider // ]; } + /** + * Filters room selections by participant age constraints. + * + * Baby rooms (code 'Baby') are only available for participants aged 2 years or younger. + * Other room types are available for all participants regardless of age. + * + * @param RoomSelectionDto[] $roomSelections Array of room selections from Step 1 + * @param BookingDto $bookingDto The booking DTO containing participant and travel data + * @param int $participantIndex Index of the participant to evaluate + * + * @return RoomSelectionDto[] Filtered array of room selections available for this participant + */ + private function filterRoomsByParticipantAge(array $roomSelections, BookingDto $bookingDto, int $participantIndex): array + { + $participant = $bookingDto->getParticipant($participantIndex); + + // If no participant or no date of birth, return all rooms (field visibility handles this case) + if (null === $participant || null === $participant->dateOfBirth) { + return $roomSelections; + } + + // Calculate participant's age at travel start date + $age = $participant->getAge($bookingDto->travel->dateFrom); + if (null === $age) { + return $roomSelections; + } + + // Get available rooms from travel data to look up room codes + $availableRooms = $bookingDto->travel->getAvailableRooms(); + + return array_filter($roomSelections, function (RoomSelectionDto $roomSelection) use ($availableRooms, $age) { + $room = $availableRooms[$roomSelection->roomId] ?? null; + + // Exclude rooms not found in travel data to avoid validation issues + if (null === $room) { + return false; + } + + // Baby rooms only available for participants 2 years or younger + if (RoomSelection::BABY_ROOM_CODE === $room->code) { + return $age <= 2; + } + + // All other rooms available for all ages + return true; + }); + } + /** * Filters rental services by selected skipass duration. *