getSelectedRooms(); // Calculate individual prices for all participants $participantPrices = $this->priceCalculator->calculateAllParticipantIndividualPrices($bookingDto); // Calculate total price $totalPrice = array_sum($participantPrices); // Get room assignment counts $roomCounts = []; foreach ($bookingDto->participants as $participant) { if (null !== $participant->assignedRoomId) { $roomCounts[$participant->assignedRoomId] = ($roomCounts[$participant->assignedRoomId] ?? 0) + 1; } } // Group selected rooms with counts (for display) $groupedSelectedRooms = []; foreach ($roomCounts as $roomId => $count) { $room = $bookingDto->travel->getRoomById($roomId); if (null !== $room) { $groupedSelectedRooms[] = [ 'room' => $room, 'count' => $count, ]; } } // Get detailed pricing breakdown $pricingData = $this->priceCalculator->getPricingBreakdown($bookingDto); // Fetch CMS data (images, etc.) $cmsData = $this->getCmsData($bookingDto); // Calculate participant count from room capacity (source of truth) $participantCount = $this->calculateParticipantCountFromRooms($bookingDto); return new BookingSummaryDto( selectedRooms: $selectedRooms, participantCount: $participantCount, totalPrice: number_format($totalPrice, 2, ',', '.').' €', groupedSelectedRooms: $groupedSelectedRooms, assignmentCounts: $roomCounts, pricingData: $pricingData, cmsData: $cmsData, ); } /** * Calculates participant count from room selections. * * This is the source of truth for participant count, calculated by * multiplying each selected room's quantity by its maximum capacity (maxPax). */ private function calculateParticipantCountFromRooms(BookingDto $bookingDto): int { $totalCapacity = 0; $availableRooms = $bookingDto->travel->getAvailableRooms(); foreach ($bookingDto->roomSelections as $selection) { if ($selection->quantity > 0 && isset($availableRooms[$selection->roomId])) { $room = $availableRooms[$selection->roomId]; $totalCapacity += $selection->quantity * ($room->maxPax ?? 0); } } return $totalCapacity; } /** * Fetches CMS data for the product and hotel in the booking. * * Data is cached for 1 hour as it rarely changes. Returns null * if the API call fails or if product/hotel codes are not available. */ private function getCmsData(BookingDto $bookingDto): ?array { $productCode = $bookingDto->travel->productCode; $hotelCode = $bookingDto->travel->hotel?->code; if (null === $productCode) { $this->logger->debug('Cannot fetch CMS data: product code is not available', [ 'travel_id' => $bookingDto->travel->id, ]); return null; } $cacheKey = sprintf('cms_data.%s.%s', $productCode, $hotelCode ?? 'none'); try { return $this->cache->get($cacheKey, function (ItemInterface $item) use ($productCode, $hotelCode) { $item->expiresAfter(3600); // 1 hour $result = $this->cmsDataService->getProductDetails($productCode, $hotelCode); // Return null if API call failed if (true === isset($result['success']) && false === $result['success']) { $this->logger->warning('CMS API call failed', [ 'product_code' => $productCode, 'hotel_code' => $hotelCode, 'message' => $result['message'] ?? 'Unknown error', ]); return null; } return $result; }); } catch (\Throwable $e) { $this->logger->error('Failed to fetch CMS data', [ 'product_code' => $productCode, 'hotel_code' => $hotelCode, 'exception' => $e->getMessage(), ]); return null; } } }