feat: offer non-travelling applicant choice only on selected travels

This commit is contained in:
fromme committed 2026-10-19 09:18:22 +02:00
1 parent 5b75938c73
commit b958d1a371
9 files changed
+320 -26

No files matched your search

+15
View File
@@ -20,6 +20,12 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
class Travel
{
use SortByPriceTrait;
/**
* CRM selection that offers the "I am not travelling myself" choice on a travel date.
*/
private const NON_TRAVELLING_APPLICANT_SELECTION_ID = 1488;
#[Groups(['api:list', 'api:single', 'snapshot'])]
public ?int $id = null;
@@ -240,6 +246,15 @@ class Travel
return false;
}
/**
* Whether a booking for this travel date may be placed by an applicant who does not travel
* along, such as a parent booking for their children.
*/
public function allowsNonTravellingApplicant(): bool
{
return $this->hasSelectionId(self::NON_TRAVELLING_APPLICANT_SELECTION_ID);
}
/**
* Retrieves rooms filtered by their IDs.
*
@@ -83,6 +83,12 @@ class Step2Controller extends AbstractBookingCreateController
$this->ensureCorrectNumberOfParticipants($bookingCreateDto);
// A session can still hold a split for a travel date that does not offer the choice.
if (false === $bookingCreateDto->applicantTravels
&& false === $bookingCreateDto->travel->allowsNonTravellingApplicant()) {
$bookingCreateDto->restoreTravellingApplicant();
}
// The account's own data belongs to whoever is the applicant: participant[0] normally,
// and the separate applicant record when they do not travel along.
$user = $this->getUser();
@@ -139,6 +139,8 @@ class Step2ParticipantController extends AbstractController
* Toggling on moves participant[0]'s personal data into the applicant record and leaves
* behind a blank traveller that keeps only its index and room assignment: the services were
* derived from an age that no longer applies. Toggling off moves it back.
*
* Only offered where the travel date allows it; elsewhere the request changes nothing.
*/
#[Route(
path: '/bookings/create/applicant-toggle',
@@ -153,6 +155,10 @@ class Step2ParticipantController extends AbstractController
}
$bookingDto = $result;
if (false === $bookingDto->travel->allowsNonTravellingApplicant()) {
return $this->htmxRedirect($request, $this->generateUrl('app_booking_create_step_2'));
}
$this->travelDataService->enrichWithFreshAvailabilities($bookingDto->travel);
// Captured before handleRequest(), which binds the new choice straight onto the DTO.
@@ -176,13 +182,7 @@ class Step2ParticipantController extends AbstractController
$bookingDto->participants[0] = $this->participantFormSupportService
->createBlankTravellerFrom($bookingDto->applicant);
} else {
$restored = $bookingDto->applicant;
if (null !== $restored) {
$restored->index = 0;
$restored->assignedRoomId = $bookingDto->participants[0]->assignedRoomId;
$bookingDto->participants[0] = $restored;
}
$bookingDto->applicant = null;
$bookingDto->restoreTravellingApplicant();
}
// Family insurance is not offered on a split booking, so a choice already made on
@@ -221,7 +221,7 @@ class Step2ParticipantController extends AbstractController
}
$bookingDto = $result;
if (true === $bookingDto->applicantTravels) {
if (true === $bookingDto->applicantTravels || false === $bookingDto->travel->allowsNonTravellingApplicant()) {
return $this->redirectToRoute('app_booking_create_step_2');
}
+20
View File
@@ -193,6 +193,26 @@ class BookingDto
return $this->participants[$index] ?? null;
}
/**
* Ends a split: the applicant takes the participant[0] slot back and travels again.
*
* The traveller who held the slot is dropped; only the room assignment carries over,
* the mirror image of what the split left behind.
*/
public function restoreTravellingApplicant(): void
{
$restored = $this->applicant;
if (null !== $restored) {
$restored->index = 0;
$restored->assignedRoomId = ($this->participants[0] ?? null)?->assignedRoomId;
$this->participants[0] = $restored;
}
$this->applicant = null;
$this->applicantTravels = true;
}
/**
* Whether participant[0] is also the applicant.
*