<?phpnamespace App\Controller\Blog;use App\Entity\Core\Pricing;use App\Entity\Core\Promotion;use App\Entity\Core\Reservations;use App\Entity\Core\Season;use App\Entity\Pages\Pages;use App\Form\ReservationForm;use App\Services\Core\Translations;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\RedirectResponse;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;use Doctrine\ORM\EntityManagerInterface;use Symfony\Contracts\HttpClient\HttpClientInterface;use Twig\Environment;class HomepageController extends AbstractController{ private $em; private $httpClient; private $twig; private $ts; public function __construct(EntityManagerInterface $em, HttpClientInterface $httpClient, Translations $translationService, Environment $twig) { $this->em = $em; $this->httpClient = $httpClient; $this->ts = $translationService; $this->twig = $twig; } public function homepage(Request $request): Response { $locale = $request->getLocale(); $page = $this->em->getRepository(Pages::class)->findOneBy(['name' => 'homepage', 'locale' => $locale]); if($page == null) { $page = $this->em->getRepository(Pages::class)->findOneBy(['name' => 'homepage','locale' => "en"]); } $pricingMatrix = $this->em->getRepository(Pricing::class)->getMatrix(Pricing::SEASON_LOW); $reservation = new Reservations(); $form = $this->createForm(ReservationForm::class, $reservation); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $data = $request->request->all(); $data = $data['reservation_form']; $adults = (int)($data['adults'] ?? 0); $children = (int)($data['children'] ?? 0); $rooms = (int)($data['rooms'] ?? 1); $persons = $adults + $children; $reservation->setAdults($adults); $reservation->setChildren($children); $reservation->setRooms($rooms); $reservation->setPersons($persons); $reservation->setLocale($locale); $this->em->persist($reservation); $this->em->flush(); $response = $this->httpClient->request("POST", $_ENV["WEBHOOK_RESERVATION"], [ 'json' => [ 'reservation_id' => $reservation->getId(), 'start' => $data['start'], 'end' => $data['end'], 'rooms' => $rooms, 'adults' => $adults, 'children' => $children, 'persons' => $persons, ], 'headers' => ['Content-Type' => 'application/json'] ]); $dataWebhook = json_decode($response->getContent()); $dispo = ($dataWebhook->available == true) ? 1 : 0; if($dispo == 0) { if($locale == "fr") { return $this->redirectToRoute('locale_reservation_error'); } return $this->redirectToRoute('reservation_error'); } $result = $this->calculateStayPrice($data['start'], $data['end'], $rooms, $persons); if($result['nights'] == 1 || $result['total_price'] === null) { if($locale == "fr") { return $this->redirectToRoute('locale_reservation_error'); } return $this->redirectToRoute('reservation_error'); } $price = $result["total_price"]; $reservation->setPricing($price); $this->em->persist($reservation); $this->em->flush(); $responseStripe = $this->httpClient->request("POST", $_ENV["WEBHOOK_STRIPE"], [ 'json' => [ 'reservation_id' => (string)$reservation->getId(), 'start' => $data['start'], 'end' => $data['end'], 'price' => $price, 'rooms' => $rooms, 'adults' => $adults, 'children' => $children, 'persons' => $persons, 'low_nights' => $result['low_nights'], 'high_nights' => $result['high_nights'], 'promo_label' => $result['promo_label'], 'discount_amount' => $result['discount_amount'], 'locale' => $locale, ], 'headers' => ['Content-Type' => 'application/json'] ]); $dataWebhookStripe = json_decode($responseStripe->getContent()); return new RedirectResponse($dataWebhookStripe->url); } return $this->render('vitrine/'.$locale.'/homepage.html.twig', [ 'form' => $form->createView(), 'page' => $page, 'pricingMatrix' => $pricingMatrix, ]); } public function reservationSuccess(Request $request): Response { $locale = $request->getLocale(); return $this->render('vitrine/'.$locale.'/reservation_success.html.twig', []); } public function reservationError(Request $request): Response { $locale = $request->getLocale(); return $this->render('vitrine/'.$locale.'/reservation_error.html.twig', []); } public function reservationCancel(Request $request): Response { $locale = $request->getLocale(); return $this->render('vitrine/'.$locale.'/reservation_cancel.html.twig', []); } /** * Calcul du prix nuit par nuit + meilleure promotion applicable depuis la BDD */ private function calculateStayPrice(string $startDate, string $endDate, int $rooms, int $persons): array { $start = new \DateTime($startDate); $end = new \DateTime($endDate); $nights = $start->diff($end)->days; if ($nights < 2) { return ['nights' => 1, 'total_price' => null]; } $pricingRepo = $this->em->getRepository(Pricing::class); $seasonRepo = $this->em->getRepository(Season::class); $promotionRepo = $this->em->getRepository(Promotion::class); $seasons = $seasonRepo->getAllCached(); $subtotalAll = 0.0; $lowNights = 0; $highNights = 0; $lowSubtotal = 0.0; $highSubtotal = 0.0; $cursor = clone $start; for ($i = 0; $i < $nights; $i++) { $isHigh = false; foreach ($seasons as $s) { if ($s->contains($cursor)) { $isHigh = true; break; } } $season = $isHigh ? Pricing::SEASON_HIGH : Pricing::SEASON_LOW; $pricePerNight = $pricingRepo->findPricePerNight($rooms, $persons, $season); if ($pricePerNight === null) { return [ 'nights' => $nights, 'total_price' => null, 'invalid_combo' => true, ]; } $subtotalAll += $pricePerNight; if ($isHigh) { $highNights++; $highSubtotal += $pricePerNight; } else { $lowNights++; $lowSubtotal += $pricePerNight; } $cursor->modify('+1 day'); } // Application de la meilleure promotion (depuis la BDD) $best = $promotionRepo->findBestDiscount( $nights, $lowNights, $highNights, $subtotalAll, $lowSubtotal, $highSubtotal ); $discountAmount = $best['discount_amount']; $promotion = $best['promotion']; $totalPrice = $subtotalAll - $discountAmount; return [ 'total_price' => $totalPrice, 'subtotal' => $subtotalAll, 'discount_amount' => $discountAmount, 'promo_label' => $promotion ? $promotion->getLabel() : null, 'promo_percent' => $promotion ? $promotion->getDiscountPercent() : null, 'nights' => $nights, 'low_nights' => $lowNights, 'high_nights' => $highNights, 'low_subtotal' => $lowSubtotal, 'high_subtotal' => $highSubtotal, 'rooms' => $rooms, 'persons' => $persons, ]; } public function simulationPricing(Request $request): Response { $locale = $request->getLocale(); $start = $_POST['start'] ?? null; $end = $_POST['end'] ?? null; $rooms = (int)($_POST['rooms'] ?? 1); $adults = (int)($_POST['adults'] ?? 0); $children = (int)($_POST['children'] ?? 0); $persons = $adults + $children; if (!$start || !$end || $persons < 1) { return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [ 'nights' => 0, ]); } // Check dispo tolérant aux pannes du webhook $dispo = 1; try { $response = $this->httpClient->request("POST", $_ENV["WEBHOOK_RESERVATION"], [ 'json' => [ 'reservation_id' => 0, 'start' => $start, 'end' => $end, 'rooms' => $rooms, 'adults' => $adults, 'children' => $children, 'persons' => $persons, 'locale' => $locale, ], 'headers' => ['Content-Type' => 'application/json'], 'timeout' => 5, ]); $dataWebhook = json_decode($response->getContent()); $dispo = (isset($dataWebhook->available) && $dataWebhook->available == true) ? 1 : 0; } catch (\Throwable $e) { // Webhook indisponible — on continue } $result = $this->calculateStayPrice($start, $end, $rooms, $persons); if ($result['nights'] == 1) { return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [ 'nights' => $result['nights'], ]); } return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [ 'price' => $result['total_price'], 'subtotal' => $result['subtotal'] ?? null, 'discount_amount' => $result['discount_amount'] ?? 0, 'promo_label' => $result['promo_label'] ?? null, 'promo_percent' => $result['promo_percent'] ?? null, 'dispo' => $dispo, 'nights' => $result['nights'], 'low_nights' => $result['low_nights'] ?? 0, 'high_nights' => $result['high_nights'] ?? 0, 'low_subtotal' => $result['low_subtotal'] ?? 0, 'high_subtotal' => $result['high_subtotal'] ?? 0, 'rooms' => $rooms, 'adults' => $adults, 'children' => $children, 'persons' => $persons, 'invalid_combo' => $result['invalid_combo'] ?? false, ]); } public function mentions(Request $request): Response { $locale = $request->getLocale(); return $this->render('vitrine/'.$locale.'/mentions.html.twig', []); } public function cgv(Request $request): Response { $locale = $request->getLocale(); return $this->render('vitrine/'.$locale.'/cgv.html.twig', []); }}