From 06f53ba6eb8ebbf8aa7ba694ca1c168cf5ecd4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Thu, 5 Feb 2026 17:57:11 +0100 Subject: [PATCH] feat: fill default names for canceled participants in agency bookings --- .../DataProcessor/BookingDataProcessor.php | 6 +- .../PersonalDataSynchronizer.php | 26 +++- .../BookingDataProcessorTest.php | 119 ++++++++++++++++++ 3 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/BusProNet/DataProcessor/BookingDataProcessor.php b/src/BusProNet/DataProcessor/BookingDataProcessor.php index 76bb7b0..aa318b2 100644 --- a/src/BusProNet/DataProcessor/BookingDataProcessor.php +++ b/src/BusProNet/DataProcessor/BookingDataProcessor.php @@ -290,7 +290,11 @@ class BookingDataProcessor } $this->serviceProcessor->removeUnusedServices($bookingData); - $this->personalDataSynchronizer->updateParticipantPersonalData($formData->participants, $bookingData); + $this->personalDataSynchronizer->updateParticipantPersonalData( + $formData->participants, + $bookingData, + $formData->isInternalAgencyBooking() + ); $payload = $this->payloadBuilder->buildBasePayload($bookingData); $this->payloadBuilder->addBankAccountToPayload($payload, $bookingData); diff --git a/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php b/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php index 013ac26..b760b0d 100644 --- a/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php +++ b/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php @@ -33,9 +33,13 @@ class PersonalDataSynchronizer * * @param array $participants The participants array from the form * @param Booking $bookingData The booking data object to update + * @param bool $isInternalAgencyBooking Whether to fill default names for canceled participants */ - public function updateParticipantPersonalData(array $participants, Booking $bookingData): void - { + public function updateParticipantPersonalData( + array $participants, + Booking $bookingData, + bool $isInternalAgencyBooking = false + ): void { // Fill missing mandatory fields on applicant (for company bookings by travel agencies) // This is separate from participants - applicant may be a company while participants are real people $this->fillMissingApplicantFields($bookingData); @@ -43,6 +47,24 @@ class PersonalDataSynchronizer foreach ($participants as $participant) { $bookingData->participants[$participant->index]->firstName = $participant->firstName; $bookingData->participants[$participant->index]->name = $participant->lastName; + + // For internal agency bookings, canceled participants with empty names get default values + // since they are read-only in the UI and cannot be fixed by applicants + if ($isInternalAgencyBooking) { + $status = $bookingData->participantsStatus[$participant->index] ?? null; + if ('S' === $status) { + $defaultName = sprintf('Teilnehmer:in %d', $participant->index + 1); + $personalData = $bookingData->participants[$participant->index]; + + if (null === $personalData->firstName || '' === $personalData->firstName) { + $personalData->firstName = $defaultName; + } + if (null === $personalData->name || '' === $personalData->name) { + $personalData->name = $defaultName; + } + } + } + $bookingData->participants[$participant->index]->dateOfBirth = $participant->dateOfBirth; $bookingData->participants[$participant->index]->gender = $participant->gender; $bookingData->participants[$participant->index]->nationality = $participant->nationality; diff --git a/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php b/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php index 3af9f25..c25e448 100644 --- a/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php +++ b/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php @@ -534,4 +534,123 @@ class BookingDataProcessorTest extends TestCase return $pickup; } + + public function testCanceledParticipantEmptyNamesFilledForAgencyBooking(): void + { + $formData = $this->createFormDataWithCanceledParticipantEmptyNames(); + + $this->processor->createUpdateRequestPayload($formData); + + // Canceled participant (index 1) should have default names filled + $canceledParticipant = $formData->booking->participants[1]; + $this->assertEquals('Teilnehmer:in 2', $canceledParticipant->firstName); + $this->assertEquals('Teilnehmer:in 2', $canceledParticipant->name); + + // Active participant (index 0) should retain original names from form + $activeParticipant = $formData->booking->participants[0]; + $this->assertEquals('Participant0', $activeParticipant->firstName); + $this->assertEquals('LastName', $activeParticipant->name); + } + + public function testCanceledParticipantNamesNotFilledForNonAgencyBooking(): void + { + $formData = $this->createFormDataWithCanceledParticipantEmptyNamesNonAgency(); + + $this->processor->createUpdateRequestPayload($formData); + + // Canceled participant should retain empty names (non-agency booking) + $canceledParticipant = $formData->booking->participants[1]; + $this->assertEquals('', $canceledParticipant->firstName); + $this->assertEquals('', $canceledParticipant->name); + } + + private function createFormDataWithCanceledParticipantEmptyNames(): BookingDto + { + $bookingDto = new BookingDto($this->createMockTravel(), 1); + $bookingDto->booking = $this->createMockBookingWithCanceledParticipantEmptyNames(); + $bookingDto->agencyCode = '0004'; // Internal agency code to trigger isInternalAgencyBooking() + $bookingDto->participants = [ + $this->createMockParticipantDto(0, 'F'), + $this->createMockParticipantDtoWithEmptyNames(1, 'S'), + ]; + + return $bookingDto; + } + + private function createFormDataWithCanceledParticipantEmptyNamesNonAgency(): BookingDto + { + $bookingDto = new BookingDto($this->createMockTravel(), 1); + $bookingDto->booking = $this->createMockBookingWithCanceledParticipantEmptyNames(); + $bookingDto->agencyCode = null; // No agency - not an internal agency booking + $bookingDto->participants = [ + $this->createMockParticipantDto(0, 'F'), + $this->createMockParticipantDtoWithEmptyNames(1, 'S'), + ]; + + return $bookingDto; + } + + private function createMockBookingWithCanceledParticipantEmptyNames(): Booking + { + $booking = new Booking(); + $booking->id = 123; + $booking->status = 'ACTIVE'; + $booking->agencyId = 999; + $booking->dateId = 1; + $booking->hotelId = 1; + $booking->paymentId = '1'; + $booking->paymentLabel = 'Credit Card'; + $booking->paymentType = 'CC'; + $booking->additionalServices = []; + $booking->transportationServices = []; + $booking->pickupsOutbound = []; + $booking->pickupsInbound = []; + + $canceledParticipant = new PersonalData(); + $canceledParticipant->firstName = ''; + $canceledParticipant->name = ''; + $canceledParticipant->dateOfBirth = new \DateTimeImmutable('1990-01-01'); + $canceledParticipant->gender = 'M'; + $canceledParticipant->nationality = 'DE'; + $canceledParticipant->address = new Address(); + $canceledParticipant->communication = new Communication(); + + $booking->participants = [ + $this->createMockPersonalData('Participant0'), + $canceledParticipant, + ]; + $booking->participantsStatus = ['F', 'S']; // Second participant is canceled + $booking->applicant = $this->createMockPersonalData('Applicant'); + $booking->bankAccount = null; + $booking->rooms = []; + + return $booking; + } + + private function createMockParticipantDtoWithEmptyNames(int $index, string $status): ParticipantDto + { + $participant = new ParticipantDto(); + $participant->index = $index; + $participant->status = $status; + $participant->firstName = ''; + $participant->lastName = ''; + $participant->dateOfBirth = new \DateTimeImmutable('1990-01-01'); + $participant->gender = 'M'; + $participant->nationality = 'DE'; + $participant->height = '175'; + $participant->weight = '70'; + $participant->shoeSize = '40'; + $participant->email = null; + $participant->mobile = null; + $participant->courses = []; + $participant->additionalServices = []; + $participant->skiPass = null; + $participant->board = []; + $participant->rentals = []; + $participant->transportationOutbound = $this->createMockService(1); + $participant->transportationInbound = $this->createMockService(2); + $participant->pickup = null; + + return $participant; + } }