query->get('hotelCode'); $dateFromRaw = $request->query->get('dateFrom'); $dateToRaw = $request->query->get('dateTo'); if (!$hotelCode || !$dateFromRaw || !$dateToRaw) { return $this->json(['error' => 'hotelCode, dateFrom and dateTo are required.'], Response::HTTP_BAD_REQUEST); } $dateFrom = \DateTimeImmutable::createFromFormat('Y-m-d', $dateFromRaw)?->setTime(0, 0); $dateTo = \DateTimeImmutable::createFromFormat('Y-m-d', $dateToRaw)?->setTime(0, 0); if (!$dateFrom || !$dateTo) { return $this->json(['error' => 'dateFrom and dateTo must be in Y-m-d format.'], Response::HTTP_BAD_REQUEST); } try { $cacheKey = sprintf('contingents_calendar_%s_%s_%s', $hotelCode, $dateFromRaw, $dateToRaw); $calendar = $this->cache->get($cacheKey, function (ItemInterface $item) use ($hotelCode, $dateFromRaw, $dateToRaw) { $item->expiresAfter(3600); return $this->contingentsClient->getContingentCalendar($hotelCode, $dateFromRaw, $dateToRaw); }); } catch (BpnConnectException|InvalidArgumentException $e) { return $this->json(['error' => 'Failed to fetch contingent data.'], Response::HTTP_BAD_GATEWAY); } $prices = $this->priceRepository->findByHotelCodeAndDateRange($hotelCode, $dateFrom, $dateTo); $data = array_map( fn ($entry) => $this->enrichEntry($entry->date, $entry->status->value, $prices), $calendar->data, ); return $this->json($data); } /** * @param AccommodationPrice[] $prices * @return array{date: string, status: string, pricePerNight: float|null, pricePerNightOverride: float|null, includedPax: int|null} */ private function enrichEntry(string $date, string $status, array $prices): array { $day = (new \DateTimeImmutable($date))->setTime(0, 0); $price = null; foreach ($prices as $candidate) { if ($candidate->getDateFrom() > $day || $candidate->getDateTo() < $day) { continue; } if (null === $price || $candidate->getDateFrom() > $price->getDateFrom()) { $price = $candidate; } } return [ 'date' => $date, 'status' => $status, 'pricePerNight' => null !== $price ? round($price->getPricePerNight() / 100, 2) : null, 'pricePerNightOverride' => null !== $price?->getPricePerNightOverride() ? round($price->getPricePerNightOverride() / 100, 2) : null, 'priceAdditionalPerson' => null !== $price ? round($price->getPriceAdditionalPerson() / 100, 2) : null, 'priceAdditionalPersonOverride' => null !== $price?->getPriceAdditionalPersonOverride() ? round($price->getPriceAdditionalPersonOverride() / 100, 2) : null, 'includedPax' => $price?->getIncludedPax(), 'minPax' => $price?->getAccommodation()->getMinPaxSummer(), 'minNights' => $price?->getMinNights(), ]; } }