From f54f66cd325023b22034784f120f05f5a682734d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Thu, 11 Dec 2025 11:27:48 +0100 Subject: [PATCH] fix: reset room assignments after updated selection if required --- .../Booking/Create/Step2Controller.php | 3 + src/Service/RoomAssignmentService.php | 44 ++++++++ tests/Service/RoomAssignmentServiceTest.php | 103 ++++++++++++++++++ 3 files changed, 150 insertions(+) diff --git a/src/Controller/Booking/Create/Step2Controller.php b/src/Controller/Booking/Create/Step2Controller.php index b31c178..cadb6f6 100644 --- a/src/Controller/Booking/Create/Step2Controller.php +++ b/src/Controller/Booking/Create/Step2Controller.php @@ -74,6 +74,9 @@ class Step2Controller extends AbstractController fn ($user, $participant) => $this->prepopulationService->prepopulateApplicantFromUser($user, $participant) ); + // Validate room assignments against current selection (handles back-navigation from step 2 to step 1) + $this->roomAssignmentService->validateAndResetInvalidAssignments($bookingCreateDto); + // Auto-assign rooms if needed $this->roomAssignmentService->assignRoomsIfNeeded($bookingCreateDto); diff --git a/src/Service/RoomAssignmentService.php b/src/Service/RoomAssignmentService.php index 59cf6ef..64ccbd8 100644 --- a/src/Service/RoomAssignmentService.php +++ b/src/Service/RoomAssignmentService.php @@ -110,4 +110,48 @@ class RoomAssignmentService return false; } + + /** + * Validates and resets room assignments that reference rooms no longer selected. + * + * This method ensures room assignments remain valid after room selection changes in Step 1. + * When a user goes back to Step 1 and changes room selections, existing assignments may + * reference room IDs that are no longer in the selected rooms list. This method detects + * such invalid assignments and resets them to null. + * + * @param BookingDto $dto The booking DTO to validate + * + * @return bool True if any assignments were reset, false if all were valid + */ + public function validateAndResetInvalidAssignments(BookingDto $dto): bool + { + $selectedRoomIds = $this->getSelectedRoomIds($dto); + $resetOccurred = false; + + foreach ($dto->participants as $participant) { + if (null !== $participant->assignedRoomId && false === in_array($participant->assignedRoomId, $selectedRoomIds, true)) { + $participant->assignedRoomId = null; + $resetOccurred = true; + } + } + + return $resetOccurred; + } + + /** + * Gets the list of room IDs from currently selected rooms. + * + * @param BookingDto $dto The booking DTO containing room selections + * + * @return array Array of selected room IDs + */ + private function getSelectedRoomIds(BookingDto $dto): array + { + $ids = []; + foreach ($dto->getSelectedRooms() as $roomSelection) { + $ids[] = $roomSelection->id; + } + + return $ids; + } } diff --git a/tests/Service/RoomAssignmentServiceTest.php b/tests/Service/RoomAssignmentServiceTest.php index cb5b0e4..5a2839d 100644 --- a/tests/Service/RoomAssignmentServiceTest.php +++ b/tests/Service/RoomAssignmentServiceTest.php @@ -190,6 +190,109 @@ class RoomAssignmentServiceTest extends TestCase self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); } + /** + * @test + */ + public function validateAndResetInvalidAssignmentsResetsOrphanedAssignments(): void + { + // Create DTO with only room 100 selected + $bookingDto = $this->createBookingDtoWithRoomSelections([ + ['roomId' => 100, 'quantity' => 2, 'capacity' => 2], + ]); + + // Create participants with assignments to room 100 and room 200 (no longer selected) + $bookingDto->participants = [ + new ParticipantDto(), + new ParticipantDto(), + new ParticipantDto(), + new ParticipantDto(), + ]; + $bookingDto->participants[0]->assignedRoomId = 100; + $bookingDto->participants[1]->assignedRoomId = 100; + $bookingDto->participants[2]->assignedRoomId = 200; // Invalid - room not selected + $bookingDto->participants[3]->assignedRoomId = 200; // Invalid - room not selected + + $result = $this->service->validateAndResetInvalidAssignments($bookingDto); + + self::assertTrue($result, 'Should return true when assignments were reset'); + self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); + self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); + self::assertNull($bookingDto->participants[2]->assignedRoomId, 'Invalid assignment should be reset to null'); + self::assertNull($bookingDto->participants[3]->assignedRoomId, 'Invalid assignment should be reset to null'); + } + + /** + * @test + */ + public function validateAndResetInvalidAssignmentsReturnsFalseWhenAllValid(): void + { + $bookingDto = $this->createBookingDtoWithRoomSelections([ + ['roomId' => 100, 'quantity' => 2, 'capacity' => 2], + ]); + + // All participants assigned to the valid room + $bookingDto->participants = [ + new ParticipantDto(), + new ParticipantDto(), + ]; + $bookingDto->participants[0]->assignedRoomId = 100; + $bookingDto->participants[1]->assignedRoomId = 100; + + $result = $this->service->validateAndResetInvalidAssignments($bookingDto); + + self::assertFalse($result, 'Should return false when no assignments needed reset'); + self::assertSame(100, $bookingDto->participants[0]->assignedRoomId); + self::assertSame(100, $bookingDto->participants[1]->assignedRoomId); + } + + /** + * @test + */ + public function validateAndResetInvalidAssignmentsHandlesNullAssignments(): void + { + $bookingDto = $this->createBookingDtoWithRoomSelections([ + ['roomId' => 100, 'quantity' => 1, 'capacity' => 2], + ]); + + // Participants with null assignments should not trigger a reset + $bookingDto->participants = [ + new ParticipantDto(), + new ParticipantDto(), + ]; + $bookingDto->participants[0]->assignedRoomId = null; + $bookingDto->participants[1]->assignedRoomId = null; + + $result = $this->service->validateAndResetInvalidAssignments($bookingDto); + + self::assertFalse($result, 'Should return false when only null assignments exist'); + } + + /** + * @test + */ + public function validateAndResetInvalidAssignmentsHandlesEmptySelectedRooms(): void + { + // All rooms have quantity 0 (none selected) + $bookingDto = $this->createBookingDtoWithRoomSelections([ + ['roomId' => 100, 'quantity' => 0, 'capacity' => 2], + ['roomId' => 200, 'quantity' => 0, 'capacity' => 2], + ]); + + // Participants still have old assignments + $bookingDto->participants = [ + new ParticipantDto(), + new ParticipantDto(), + ]; + $bookingDto->participants[0]->assignedRoomId = 100; + $bookingDto->participants[1]->assignedRoomId = 200; + + $result = $this->service->validateAndResetInvalidAssignments($bookingDto); + + self::assertTrue($result, 'Should return true when all assignments become invalid'); + self::assertNull($bookingDto->participants[0]->assignedRoomId); + self::assertNull($bookingDto->participants[1]->assignedRoomId); + } + private function createBookingDtoWithRoomSelections(array $selections): BookingDto { $travel = new Travel();