getOrCreateBookingCreateDto($this->bookingService, $request); if ($result instanceof Response) { return $result; } $bookingCreateDto = $result; // Validate step access if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) { return $redirect; } $form = $this->createForm(BookingCreateStep4Type::class, $bookingCreateDto, [ 'attr' => [ 'hx-post' => $this->generateUrl('app_booking_create_step_4'), 'hx-target' => '#form-wrapper', 'hx-select' => '#form-wrapper', 'hx-swap' => 'outerHTML', ], ]); $form->handleRequest($request); if (true === $form->isSubmitted() && true === $form->isValid()) { try { // Submit final booking (already validated in Step 3) $bookingResponse = $this->apiClient->createBooking($bookingCreateDto); if ($bookingResponse instanceof Notification) { return $this->handleApiError( 'Booking creation failed - API notification', ['message' => $bookingResponse->message], $bookingResponse->message ?? 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.', $bookingCreateDto, $form ); } if (false === $bookingResponse->isBookingSuccessful()) { $errorMessage = 'Buchung konnte nicht erstellt werden.'; if (null !== $bookingResponse->message && '' !== trim($bookingResponse->message)) { $errorMessage .= ' '.$bookingResponse->message; } return $this->handleApiError( 'Booking creation unsuccessful', ['status' => $bookingResponse->status, 'message' => $bookingResponse->message], $errorMessage, $bookingCreateDto, $form ); } // Success: Store booking number in flash and clear session $this->addFlash('booking_number', $bookingResponse->transactionNumber); $this->clearTravelDataCache($bookingCreateDto); $this->bookingService->clearBookingCreateDto($request); return $this->hxRedirect($request, $this->generateUrl('app_booking_create_success')); } catch (TimeoutException $e) { return $this->handleApiError( 'Booking creation timeout', [ 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ], 'Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut.', $bookingCreateDto, $form ); } catch (\Exception $e) { return $this->handleApiError( 'Booking creation exception', [ 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ], 'Ein technischer Fehler ist aufgetreten.', $bookingCreateDto, $form ); } } return $this->renderStepForm($bookingCreateDto, $form); } /** * Renders the step 4 form with standard template variables. */ private function renderStepForm(BookingDto $bookingCreateDto, FormInterface $form): Response { // Get complete summary data (pricing, rooms, CMS data) $summaryData = $this->summaryDataService->getSummaryData($bookingCreateDto); $availableRooms = $bookingCreateDto->travel->getAvailableRooms(); $groupedSelectedRooms = $this->bookingService->groupRoomSelectionsByType($summaryData['selectedRooms'], $availableRooms); return $this->render('booking/create/step_4.html.twig', [ 'bookingCreateDto' => $bookingCreateDto, 'form' => $form->createView(), 'participantCount' => $summaryData['participantCount'], 'pricingData' => $summaryData['pricingData'], 'cmsData' => $summaryData['cmsData'], 'assignmentCounts' => $summaryData['assignmentCounts'], 'groupedSelectedRooms' => $groupedSelectedRooms, 'participantPrices' => $this->priceCalculator->calculateAllParticipantIndividualPrices($bookingCreateDto), ]); } /** * Clears travel data and availability cache after successful booking. */ private function clearTravelDataCache(BookingDto $bookingDto): void { $dateId = $bookingDto->travel->id; $hotelId = $bookingDto->hotelId; // Clear availability cache $this->cache->delete(sprintf('availability_%d', $dateId)); // Clear travel data cache (both local and remote variants) $this->cache->delete(sprintf('travel_unified_%d_%d_local', $dateId, $hotelId)); $this->cache->delete(sprintf('travel_unified_%d_%d_remote', $dateId, $hotelId)); $this->logger->info('Cleared travel data cache after successful booking', [ 'dateId' => $dateId, 'hotelId' => $hotelId, ]); } }