From c0c8ad515c9ef5968a20f6d9c86c50e935839a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Wed, 29 Apr 2026 11:59:39 +0200 Subject: [PATCH] fix: properly handle 'veg' services in booking edit flow --- .../DataProcessor/BookingDataProcessor.php | 8 ++++ .../DataProcessor/BookingPayloadBuilder.php | 4 +- .../ParticipantServiceProcessor.php | 5 +++ src/Service/BookingChangeTracker.php | 1 + src/Service/BookingEditDraftMerger.php | 8 ++++ src/Service/BookingEditSubmitGuard.php | 5 +++ .../BookingDataProcessorTest.php | 43 +++++++++++++++++++ .../BookingEditDraftManagerMutabilityTest.php | 16 ++++++- tests/Service/BookingEditSubmitGuardTest.php | 3 ++ 9 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/BusProNet/DataProcessor/BookingDataProcessor.php b/src/BusProNet/DataProcessor/BookingDataProcessor.php index 30f789d..c7e20cd 100644 --- a/src/BusProNet/DataProcessor/BookingDataProcessor.php +++ b/src/BusProNet/DataProcessor/BookingDataProcessor.php @@ -105,6 +105,9 @@ class BookingDataProcessor $participantData->board = $booking ->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_BOARD); + $vegServices = $booking->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_VEG); + $participantData->veg = false === empty($vegServices) ? reset($vegServices) : null; + $participantData->rentals = $booking ->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_RENTALS); @@ -211,6 +214,11 @@ class BookingDataProcessor } } + // Enrich veg + if (null !== $participant->veg && isset($travel->additionalServices[$participant->veg->id])) { + $participant->veg = $travel->additionalServices[$participant->veg->id]; + } + // Enrich rentals foreach ($participant->rentals as $key => $rental) { if (isset($travel->additionalServices[$rental->id])) { diff --git a/src/BusProNet/DataProcessor/BookingPayloadBuilder.php b/src/BusProNet/DataProcessor/BookingPayloadBuilder.php index 6940d86..d64eb83 100644 --- a/src/BusProNet/DataProcessor/BookingPayloadBuilder.php +++ b/src/BusProNet/DataProcessor/BookingPayloadBuilder.php @@ -171,8 +171,8 @@ class BookingPayloadBuilder '@idzimmer' => $room->id, '@kategorie' => $room->category, '@idverpflegung' => $room->boardId, - '@anreise' => $room->dateFrom ? $room->dateFrom->format('d.m.Y') : null, - '@abreise' => $room->dateTo ? $room->dateTo->format('d.m.Y') : null, + '@anreise' => $room->dateFrom?->format('d.m.Y'), + '@abreise' => $room->dateTo?->format('d.m.Y'), '@anzahl' => $room->totalCount, '@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $uniqueMapping)), ]; diff --git a/src/BusProNet/DataProcessor/ParticipantServiceProcessor.php b/src/BusProNet/DataProcessor/ParticipantServiceProcessor.php index dfe4d2a..0f4cd44 100644 --- a/src/BusProNet/DataProcessor/ParticipantServiceProcessor.php +++ b/src/BusProNet/DataProcessor/ParticipantServiceProcessor.php @@ -131,6 +131,11 @@ class ParticipantServiceProcessor ...$participant->rentals, ]; + // Veg is encoded as an additional service in the edit/update payload. + if (null !== $participant->veg) { + $services[] = $participant->veg; + } + // Add ski pass if selected (single service, not an array) if (null !== $participant->skiPass) { $services[] = $participant->skiPass; diff --git a/src/Service/BookingChangeTracker.php b/src/Service/BookingChangeTracker.php index a59b403..4e0d83b 100644 --- a/src/Service/BookingChangeTracker.php +++ b/src/Service/BookingChangeTracker.php @@ -96,6 +96,7 @@ class BookingChangeTracker 'skiPass' => $participant->skiPass?->id, 'courses' => $this->normalizeServiceArray($participant->courses), 'board' => $this->normalizeServiceArray($participant->board), + 'veg' => $participant->veg?->id, 'rentals' => $this->normalizeServiceArray($participant->rentals), 'rentalInsurance' => $participant->rentalInsurance?->id, 'additionalServices' => $this->normalizeServiceArray($participant->additionalServices), diff --git a/src/Service/BookingEditDraftMerger.php b/src/Service/BookingEditDraftMerger.php index 1ad55e7..6c8f289 100644 --- a/src/Service/BookingEditDraftMerger.php +++ b/src/Service/BookingEditDraftMerger.php @@ -209,6 +209,14 @@ class BookingEditDraftMerger $participant->board = $this->resolveServiceArray($data['board'], $travel->additionalServices); } + // Veg (single service) - merge strategy: only apply if resolves to valid service + if (true === array_key_exists('veg', $data) && null !== $data['veg']) { + $resolved = $this->resolveService($data['veg'], $travel->additionalServices); + if (null !== $resolved) { + $participant->veg = $resolved; + } + } + // Rentals (array) - overwrite strategy: user can deselect all if (true === array_key_exists('rentals', $data) && true === is_array($data['rentals'])) { $participant->rentals = $this->resolveServiceArray($data['rentals'], $travel->additionalServices); diff --git a/src/Service/BookingEditSubmitGuard.php b/src/Service/BookingEditSubmitGuard.php index 8fb0786..f092a45 100644 --- a/src/Service/BookingEditSubmitGuard.php +++ b/src/Service/BookingEditSubmitGuard.php @@ -93,6 +93,11 @@ class BookingEditSubmitGuard $changed = true; } + if (false === $this->isSameService($participant->veg, $baseline->veg)) { + $participant->veg = $baseline->veg; + $changed = true; + } + if (false === $this->isSameService($participant->skiPass, $baseline->skiPass)) { $participant->skiPass = $baseline->skiPass; $changed = true; diff --git a/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php b/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php index 08efcf3..d0f9949 100644 --- a/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php +++ b/tests/BusProNet/DataProcessor/BookingDataProcessorTest.php @@ -109,6 +109,49 @@ class BookingDataProcessorTest extends TestCase $this->assertEquals('1', $service['@zuordnung']); } + public function testVegServiceIsIncludedInUpdatePayload(): void + { + $formData = $this->createCompleteFormData(); + + $vegService = $this->createMockService(194672); + $formData->travel->additionalServices[194672] = $vegService; + $formData->participants[0]->veg = $vegService; + + $result = $this->processor->createUpdateRequestPayload($formData); + + $this->assertNotEmpty($result['zusatzleistungen']['zusatzleistung']); + + $vegPayload = null; + foreach ($result['zusatzleistungen']['zusatzleistung'] as $service) { + if (194672 === $service['@idleistung']) { + $vegPayload = $service; + break; + } + } + + $this->assertNotNull($vegPayload); + $this->assertSame(1, $vegPayload['@anzahl']); + $this->assertSame('1', $vegPayload['@zuordnung']); + } + + public function testVegSelectionIsRestoredWhenHydratingBookingDto(): void + { + $booking = $this->createMockBooking(); + $travel = $this->createMockTravel(); + + $vegService = $this->createMockService(194672); + $vegService->subType = 'VEG'; + $vegService->mapping = [0]; + + $booking->additionalServices[194672] = $vegService; + $travel->additionalServices[194672] = $vegService; + + $dto = $this->processor->createBookingDtoFromBooking($booking, $travel); + + $this->assertNotNull($dto->participants[0]->veg); + $this->assertSame(194672, $dto->participants[0]->veg?->id); + } + public function testTransportationServicesProcessing(): void { $formData = $this->createFormDataWithTransportation(); diff --git a/tests/Service/BookingEditDraftManagerMutabilityTest.php b/tests/Service/BookingEditDraftManagerMutabilityTest.php index d2f9660..f8635ae 100644 --- a/tests/Service/BookingEditDraftManagerMutabilityTest.php +++ b/tests/Service/BookingEditDraftManagerMutabilityTest.php @@ -47,17 +47,23 @@ class BookingEditDraftManagerMutabilityTest extends TestCase public function testDraftSkipsAdditionalServicesWhenImmutable(): void { $originalService = $this->createService(10, 'Original'); + $originalVeg = $this->createService(11, 'Original Veg'); $participant = new ParticipantDto(); $participant->additionalServices = [$originalService]; $participant->courses = []; $participant->board = []; + $participant->veg = $originalVeg; $participant->rentals = []; $participant->skiPass = null; $participant->rentalInsurance = null; $travel = $this->createTravel( additionalServicesMutable: false, - additionalServices: [10 => $originalService, 99 => $this->createService(99, 'Draft Added')], + additionalServices: [ + 10 => $originalService, + 11 => $originalVeg, + 99 => $this->createService(99, 'Draft Added'), + ], ); $dto = $this->createBookingDto($travel, [$participant]); @@ -69,6 +75,7 @@ class BookingEditDraftManagerMutabilityTest extends TestCase 'additionalServices' => [99], 'courses' => [99], 'board' => [99], + 'veg' => 99, 'rentals' => [99], 'skiPass' => 99, 'rentalInsurance' => 99, @@ -83,6 +90,7 @@ class BookingEditDraftManagerMutabilityTest extends TestCase $this->assertSame([$originalService], $participant->additionalServices); $this->assertSame([], $participant->courses); $this->assertSame([], $participant->board); + $this->assertSame($originalVeg, $participant->veg); $this->assertSame([], $participant->rentals); $this->assertNull($participant->skiPass); $this->assertNull($participant->rentalInsurance); @@ -91,16 +99,18 @@ class BookingEditDraftManagerMutabilityTest extends TestCase public function testDraftAppliesAdditionalServicesWhenMutable(): void { $originalService = $this->createService(10, 'Original'); + $originalVeg = $this->createService(11, 'Original Veg'); $draftService = $this->createService(99, 'Draft Added'); $participant = new ParticipantDto(); $participant->additionalServices = [$originalService]; $participant->courses = []; + $participant->veg = $originalVeg; $participant->skiPass = null; $travel = $this->createTravel( additionalServicesMutable: true, - additionalServices: [10 => $originalService, 99 => $draftService], + additionalServices: [10 => $originalService, 11 => $originalVeg, 99 => $draftService], ); $dto = $this->createBookingDto($travel, [$participant]); @@ -111,6 +121,7 @@ class BookingEditDraftManagerMutabilityTest extends TestCase 'services' => [ 'additionalServices' => [99], 'courses' => [99], + 'veg' => 99, 'skiPass' => 99, ], ], @@ -124,6 +135,7 @@ class BookingEditDraftManagerMutabilityTest extends TestCase $this->assertSame(99, $participant->additionalServices[0]->id); $this->assertCount(1, $participant->courses); $this->assertSame(99, $participant->courses[0]->id); + $this->assertSame($draftService, $participant->veg); $this->assertInstanceOf(Service::class, $participant->skiPass); $skiPass = $participant->skiPass; $this->assertNotNull($skiPass); diff --git a/tests/Service/BookingEditSubmitGuardTest.php b/tests/Service/BookingEditSubmitGuardTest.php index aecdcce..c9899b7 100644 --- a/tests/Service/BookingEditSubmitGuardTest.php +++ b/tests/Service/BookingEditSubmitGuardTest.php @@ -32,6 +32,7 @@ class BookingEditSubmitGuardTest extends TestCase $workingParticipant = new ParticipantDto(); $workingParticipant->index = 0; $workingParticipant->additionalServices = [$this->createService(99)]; + $workingParticipant->veg = $this->createService(88); $workingParticipant->transportationOutbound = $this->createService(88); $workingParticipant->pickup = $this->createPickup(77); @@ -41,6 +42,7 @@ class BookingEditSubmitGuardTest extends TestCase $baselineParticipant = new ParticipantDto(); $baselineParticipant->index = 0; $baselineParticipant->additionalServices = [$this->createService(10)]; + $baselineParticipant->veg = $this->createService(11); $baselineParticipant->transportationOutbound = $this->createService(20); $baselineParticipant->pickup = $this->createPickup(30); @@ -58,6 +60,7 @@ class BookingEditSubmitGuardTest extends TestCase $this->assertTrue($changed); $this->assertSame([10], array_map(static fn (Service $s) => $s->id, $workingParticipant->additionalServices)); + $this->assertSame(11, $workingParticipant->veg?->id); $this->assertSame(20, $workingParticipant->transportationOutbound?->id); $this->assertSame(30, $workingParticipant->pickup?->id); }