getSelectedRooms(); // Calculate individual prices for all participants $participantPrices = $this->priceCalculator->calculateAllParticipantIndividualPrices($bookingDto); // Get room assignment counts $roomCounts = []; foreach ($bookingDto->participants as $participant) { if (null !== $participant->assignedRoomId) { $roomCounts[$participant->assignedRoomId] = ($roomCounts[$participant->assignedRoomId] ?? 0) + 1; } } // Get detailed pricing breakdown $pricingData = $this->pricingAssembler->getPricingBreakdown($bookingDto, $roomPricingMode); // Fetch CMS data (images, etc.) $productCode = $bookingDto->travel->productCode; $hotelCode = $bookingDto->travel->hotel?->code; if (null === $hotelCode) { $this->logger->debug('Cannot fetch hotel data: hotel code is not available', [ 'travel_id' => $bookingDto->travel->id, ]); $cmsData = null; } else { $cmsData = $this->getCmsDataForProduct($productCode, $hotelCode); } $participantCount = $this->calculateParticipantCount($bookingDto); $acceptedVouchers = $bookingDto->getAcceptedVouchers($participantPrices); // Calculate payable amount after voucher deductions $payableAmount = $this->calculatePayableAmount($pricingData['grandTotal'], $acceptedVouchers); $pricing = new BookingSummaryPricingDto( rooms: $pricingData['rooms'], services: $pricingData['services'], surcharges: $pricingData['surcharges'] ?? null, assignmentCounts: $roomCounts, grandTotal: $pricingData['grandTotal'], ); $vouchers = new BookingSummaryVoucherDto( acceptedVouchers: $acceptedVouchers, payableAmount: $payableAmount, ); return new BookingSummaryDto( selectedRooms: $selectedRooms, participantCount: $participantCount, pricing: $pricing, vouchers: $vouchers, cmsData: $cmsData, ); } /** * Calculates the payable amount after voucher deductions. * * @param AcceptedVouchersDto|null $acceptedVouchers Accepted vouchers for discount calculation */ private function calculatePayableAmount(float $grandTotal, ?AcceptedVouchersDto $acceptedVouchers): float { if (null === $acceptedVouchers) { return $grandTotal; } return max(0.0, $grandTotal - $acceptedVouchers->getTotalDiscount()); } /** * Fetches hotel display data combining local base hotel data with CMS images. * * Local hotel data provides name and address while CMS provides images * as a nice-to-have enhancement. * * Data is cached for 1 hour. This method can be called early in the booking * flow to warm the cache. */ public function getCmsDataForProduct(?string $productCode, ?string $hotelCode): ?BookingSummaryCmsHotelData { if (null === $hotelCode) { return null; } $cacheKey = sprintf('cms_data.%s.%s', $productCode, $hotelCode); try { return $this->cache->get($cacheKey, function (ItemInterface $item) use ($productCode, $hotelCode) { $item->expiresAfter(3600); // 1 hour // Fetch base hotel from local BusProNet data $baseHotel = $this->hotelLoader->loadByCode($hotelCode); // Fetch CMS images (nice to have) $images = $this->cmsDataService->getProductImages($productCode, $hotelCode); return new BookingSummaryCmsHotelData( name: $baseHotel?->name, address: $this->formatHotelAddress($baseHotel), images: $images, ); }); } catch (\Throwable $e) { $this->logger->error('Failed to fetch hotel display data', [ 'product_code' => $productCode, 'hotel_code' => $hotelCode, 'exception' => $e->getMessage(), ]); return null; } } /** * Formats a hotel's address from street, city and country. */ private function formatHotelAddress(?Hotel $hotel): ?string { if (null === $hotel) { return null; } $countryName = $this->countryDataProvider->get($hotel->country)?->name; $parts = array_filter([$hotel->city, $countryName]); return [] === $parts ? null : implode("\n", $parts); } /** * Returns the participant count for the summary sidebar. * * In create step 1, derives the expected count from room selections. * In later create steps and edit mode, uses the actual participant list. */ private function calculateParticipantCount(BookingDto $bookingDto): int { if (BookingDto::MODE_CREATE === $bookingDto->getMode() && 1 === $bookingDto->currentStep) { $totalCapacity = 0; $availableRooms = $bookingDto->travel->getAvailableRooms(); foreach ($bookingDto->roomSelections as $selection) { if ($selection->quantity > 0 && isset($availableRooms[$selection->id])) { $room = $availableRooms[$selection->id]; $totalCapacity += $selection->quantity * ($room->maxPax ?? 0); } } return $totalCapacity; } return count($bookingDto->participants); } }