<?php
namespace App\Controller;
use App\Entity\Payment\CarteBancaire;
use App\Entity\Payment\CarteCadeauPayment;
use App\Entity\Payment\Cash;
use App\Entity\Payment\Cheque;
use App\Entity\Payment\EmptyPayment;
use App\Entity\Payment\Offert;
use App\Entity\Privatisation;
use App\Entity\Reservation;
use App\Entity\Service;
use App\Entity\ServiceResa;
use App\Entity\Token;
use App\Entity\WaitlistEntry;
use App\Repository\WaitlistEntryRepository;
use App\Form\Payment\CarteBancaireType;
use App\Form\Payment\CashType;
use App\Form\Payment\ChequeType;
use App\Form\Payment\OffertType;
use App\Form\PrivatisationType;
use App\Form\ReservationAdminType;
use App\Form\TokenReservationType;
use App\Repository\ExtraRepository;
use App\Service\CarteCadeauService;
use App\Service\CodeService;
use App\Service\ExtraReservationService;
use App\Service\PaymentService;
use App\Service\PayplugService;
use App\Service\PrivatisationService;
use App\Service\ReservationService;
use App\Service\TokenService;
use DateTime;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class ReservationController extends AbstractController
{
public function __construct(private ReservationService $reservationService,
private ExtraReservationService $extraReservationService,
private TokenService $tokenService,
private TranslatorInterface $translator,
private CarteCadeauService $carteCadeauService,
)
{}
#[Route(path: '/reservation', name: 'reservation_tunnel')]
public function tunnel(Request $request, WaitlistEntryRepository $waitlistEntryRepository)
{
$token = new Token();
$this->extraReservationService->generateExtraReservations($token);
$form = $this->createForm(TokenReservationType::class, $token);
$waitlistData = null;
$waitlistToken = $request->query->get('waitlist');
if ($waitlistToken) {
$waitlistEntry = $waitlistEntryRepository->findOneByToken($waitlistToken);
if ($waitlistEntry && !$waitlistEntry->isExpired()) {
$waitlistData = [
'solo' => $waitlistEntry->getSolo(),
'duo' => $waitlistEntry->getDuo(),
'offer' => $waitlistEntry->getOffer(),
'desiredDate' => $waitlistEntry->getDesiredDate()->format('Y-m-d'),
];
}
}
return $this->renderForm('reservation/tunnel/tunnel.html.twig', [
'token' => $token,
'form' => $form,
'paymentUrl' => $this->generateUrl('reservation_payment', ['tokenValue' => $token->getValue()]),
'waitlistData' => $waitlistData,
]);
}
#[Route(path: '/reservation/modifier/{tokenValue}', name: 'reservation_update')]
public function updateReservation(ReservationService $reservationService, TokenService $tokenService, ExtraReservationService $extraReservationService, string $tokenValue)
{
$token = $tokenService->findOneByValue($tokenValue);
if (!$token) {
$this->addFlash(
'error',
'Réservation non trouvée'
);
return $this->redirectToRoute('reservation_tunnel');
}
$reservation = $token->getReservation() ?? $this->reservationService->findOneByToken($token);
// Si la réservation est dans moins de 48 heures, on retourne une erreur
if ($reservation->getDate() < (new \DateTime())->modify('+48 hours')) {
$this->addFlash(
'error',
'Vous ne pouvez pas modifier une réservation moins de 48h avant la date de celle-ci'
);
return $this->redirectToRoute('reservation_tunnel');
}
// Vérifier si le client a déjà modifié la réservation 3 fois
if (!$reservation->canClientEdit()) {
$this->addFlash(
'error',
'Vous avez atteint le nombre maximum de modifications autorisées pour cette réservation'
);
return $this->redirectToRoute('reservation_tunnel');
}
foreach ($reservation->getUsedCarteCadeaux() as $code) {
if ($code->isExpired()) {
$this->addFlash(
'error',
'Vous ne pouvez pas modifier une réservation dont la carte cadeau utilisée est périmée'
);
return $this->redirectToRoute('reservation_tunnel');
}
}
$newToken = new Token;
$tokenService->copyReservationToToken($reservation, $newToken);
$extraReservationService->generateExtraReservations($newToken, false);
$formReservation = $this->createForm(TokenReservationType::class, $newToken);
$priceAlreadyPaid = $reservationService->computePrice($reservation);
return $this->renderForm('reservation/tunnel/tunnel.html.twig', [
'edit' => true,
'token' => $token,
'form' => $formReservation,
'priceOffered' => $priceAlreadyPaid,
'paymentUrl' => $this->generateUrl('reservation_payment', ['tokenValue' => $token->getValue()]),
'reservationId' => $reservation->getId(),
]);
}
#[Route(path: '/reservation/payment/{tokenValue}', name: 'reservation_payment')]
public function createPayment(Request $request, ReservationService $reservationService, TokenService $tokenService, ExtraReservationService $extraReservationService, PayplugService $payplugService, PaymentService $paymentService, string $tokenValue = null)
{
$token = new Token;
$extraReservationService->generateExtraReservations($token);
$formReservation = $this->createForm(TokenReservationType::class, $token);
$formReservation->handleRequest($request);
$originalToken = $tokenService->findOneByValue($tokenValue);
$reservation = $reservationService->findOneByToken($originalToken);
if ( $reservation && !$reservationService->isValidSlot($reservation) ) {
return new JsonResponse([
'action' => 'error',
'message' => "Le créneau demandé n'est pas disponible"
]);
}
if ( !$reservation && !$tokenService->isValidReservation($token)) {
return new JsonResponse([
'action' => 'error',
'message' => "Le créneau demandé n'est pas disponible"
]);
}
$tokenService->save($token);
if ($originalToken) {
$reservation = $reservationService->findOneByToken($originalToken);
$token->setReservation($reservation);
if($paymentService->hasToPayTheDifference($reservation, $token)) {
$payment = $payplugService->generatePayementWithUpdateReservation($token, $reservation);
} else {
$reservationService->copyTokenToReservation($token, $reservation);
$reservationService->associateBains($reservation, $reservation->getSolo() + $reservation->getDuo());
$reservationService->save($reservation);
$payment = new EmptyPayment;
$payment->setDatePayment(new DateTime());
$payment->setToken($token);
$tokenService->save($token);
$paymentService->save($payment);
return new JsonResponse([
'action' => 'redirect',
'url' => $this->generateUrl('reservation_confirmed_paiement', ['payment' => $payment->getId()])
]);
}
} else {
$payment = $payplugService->generatePayment($token, 'reservation');
}
$token->setPaymentid($payment->id);
$tokenService->save($token);
return new JsonResponse([
'action' => 'payment',
'url' => $payment->hosted_payment->payment_url
]);
}
#[Route(path: '/reservation/carte-cadeau', name: 'reservation_carte_cadeau_tunnel')]
public function tunnelCarteCadeau(Request $request, TokenService $tokenService, CodeService $codeService, ExtraReservationService $extraReservationService): Response
{
$codeValue = $request->query->get('code') ?? $request->get('code');
if (!$codeValue) {
return $this->render('reservation/tunnel/ask-code.html.twig');
}
$code = $codeService->findOneByValue($codeValue);
if (!$code) {
return $this->render('reservation/tunnel/ask-code.html.twig', [
"error" => $codeValue ? "Ce code n'existe pas !" : false
]);
}
$token = $code->getToken();
if (!$this->tokenService->isAlreadyPaid($token)) {
return $this->render('reservation/tunnel/ask-code.html.twig', [
"error" => "Cette carte cadeau n'a pas complètement été réglée !"
]);
}
if($code->getUsed()) {
return $this->render('reservation/tunnel/ask-code.html.twig', [
"error" => "Ce code a déjà été utilisé !"
]);
}
if($code->isExpired()) {
return $this->redirectToRoute('carte_cadeau_extend_validity', ['id' => $code->getToken()->getId()]);
}
$services_formated = [];
foreach($token->getExtraReservations() as $oldExtraReservation) {
foreach($oldExtraReservation->getOptionQuantityReservations() as $optionQuantityReservation) {
if($optionQuantityReservation->getQuantity()) {
$services_formated[] = [
"name" => $optionQuantityReservation->getOptionQuantity()->getExtra()->getLabel().' '.$optionQuantityReservation->getOptionQuantity()->getLabel(),
"quantity" => $optionQuantityReservation->getQuantity(),
];
}
}
foreach($oldExtraReservation->getOptionSelectReservations() as $optionSelectReservation) {
$services_formated[] = [
"name" => $optionSelectReservation->getOptionSelect()->getLabel(),
"quantity" => $optionSelectReservation->getOptionSelectItem()->getLabel(),
];
}
}
foreach($token->getCustomExtras() as $customExtra) {
$services_formated[] = [
"name" => $customExtra->getLabel() . ' (' . ($customExtra->getPrice() / 100) . '€)',
"quantity" => $customExtra->getQuantity(),
];
}
$price = $tokenService->computeRedemptionValue($token);
$extraReservationService->generateExtraReservations($token);
$token->setContact(null);
$formToken = $this->createForm(TokenReservationType::class, $token);
return $this->render('reservation/tunnel/tunnel.html.twig', [
'token' => $token,
'code' => $code->getValue(),
'form' => $formToken->createView(),
'priceOffered' => $price,
'services' => $services_formated,
'paymentUrl' => $this->generateUrl('reservation_carte_cadeau_payment'),
]);
}
#[Route(path: '/reservation/carte-cadeau/payment', name: 'reservation_carte_cadeau_payment')]
public function createCarteCadeauPayment(Request $request, TokenService $tokenService, ReservationService $reservationService, PayplugService $payplugService, CodeService $codeService)
{
$token = new Token;
$this->extraReservationService->generateExtraReservations($token);
$formReservation = $this->createForm(TokenReservationType::class, $token);
$formReservation->handleRequest($request);
$code = $codeService->findOneByValue($request->request->get('code'));
if($code->getUsed()) {
return new JsonResponse([
'action' => 'error',
'message' => 'Code déjà utilisé'
]);
}
$tokenCarteCadeau = $tokenService->findOneByCodePromo($code);
$token->setCodePromo($code);
$price = $tokenService->computePrice($token);
$priceOffered = $tokenService->computeRedemptionValue($tokenCarteCadeau);
if($price > $priceOffered) {
$payment = $payplugService->generatePaymentWithCarteCadeau($token, $tokenCarteCadeau);
$token->setPaymentid($payment->id);
return new JsonResponse([
'action' => 'payment',
'url' => $payment->hosted_payment->payment_url
]);
}
$reservation = new Reservation;
$reservationService->copyTokenToReservation($token, $reservation);
$reservationService->associateBains($reservation, $reservation->getSolo() + $reservation->getDuo());
$reservation->setToken($token->getValue());
$reservation->addToken($token);
$reservation->setResteAPayer($reservationService->computePrice($reservation));
$firstTokenCodePromo = $tokenService->findOneByCodePromo($code->getValue());
if ( !$firstTokenCodePromo ) {
$this->addFlash('error', 'Carte cadeau invalide');
return $this->redirectToRoute('reservation_tunnel');
}
$paymentCarteCadeau = new CarteCadeauPayment();
$paymentCarteCadeau->setDatePayment(new \DateTime());
$paymentCarteCadeau->setReservation($reservation);
$paymentCarteCadeau->setCartecadeau($firstTokenCodePromo->getCodePromo());
$paymentCarteCadeau->setAmount($tokenService->computeRedemptionValue($firstTokenCodePromo));
$code->setUsed(true);
if ( !$reservationService->isValidSlot($reservation) ) {
return new JsonResponse([
'action' => 'error',
'message' => "Le créneau demandé n'est plus disponible"
]);
}
$reservationService->addPayment($reservation, $paymentCarteCadeau, $tokenService);
$reservationService->confirm($reservation);
$reservationService->save($reservation);
$reservationService->sendNotificationToClient($reservation);
return new JsonResponse([
'action' => 'redirect',
'url' => $this->generateUrl('reservation_carte_cadeau_confirmed', ['reservation' => $reservation->getId()])
]);
}
#[Route(path: '/reservation/carte-cadeau/confirmed/{reservation}', name: 'reservation_carte_cadeau_confirmed')]
public function confirmedReservation(Reservation $reservation)
{
if ($reservation->getConfirmed()) {
return $this->render('reservation/confirmation.html.twig', [
'reservation' => $reservation,
]);
} else {
return $this->render('reservation/confirmation.html.twig', [
'reservation' => $reservation,
'message' => "Le paiement n'a pas été validé, veuillez réessayer avec un autre moyen de paiement."
]);
}
}
#[Route(path: '/reservation/invalidDays', name: 'reservation_invalid_days')]
public function invalidDays(Request $request, ReservationService $reservationService)
{
$baths = $request->request->get('baths');
$duration = $request->request->get('duration');
$participants = $request->request->get('participants');
$entityType = $request->request->get('entityType');
$entityId = $request->request->get('entityId');
$originalTime = $request->request->get('originalTime');
$originalDate = $request->request->get('originalDate');
$invalidSlots = $reservationService->getInvalidDays(
$baths,
$duration,
$participants,
$entityType,
$entityId,
$originalTime,
$originalDate
);
return new JsonResponse($invalidSlots);
}
#[Route(path: '/reservation/validSlot', name: 'reservation_valid_slots')]
public function validSlotByDay(Request $request, ReservationService $reservationService)
{
$baths = $request->request->get('baths');
$participants = $request->request->get('participants');
$duration = $request->request->get('duration');
$date = $request->request->get('date');
$entityType = $request->request->get('entityType');
$entityId = $request->request->get('entityId');
$originalTime = $request->request->get('originalTime');
$originalDate = $request->request->get('originalDate');
$validSlots = $reservationService->getValidSlotByDay(
$baths,
$duration,
new DateTime($date),
$participants,
$entityType,
$entityId,
$originalTime,
$originalDate
);
$format = [];
foreach ($validSlots as $slot) {
$format[] = [
"start" => $slot,
"end" => $slot,
];
}
return new JsonResponse($format);
}
#[Route(path: '/reservation/waitlist', name: 'reservation_waitlist', methods: ['POST'])]
public function waitlistRegister(Request $request, WaitlistEntryRepository $waitlistEntryRepository): JsonResponse
{
$email = trim($request->request->get('email', ''));
$firstName = trim($request->request->get('firstName', ''));
$lastName = trim($request->request->get('lastName', ''));
$phone = trim($request->request->get('phone', ''));
$comment = trim($request->request->get('comment', ''));
$desiredDate = $request->request->get('desiredDate', '');
$solo = (int) $request->request->get('solo', 0);
$duo = (int) $request->request->get('duo', 0);
$offer = $request->request->get('offer', '60');
$isCarteCadeau = $request->request->get('isCarteCadeau', '0') === '1';
if (!$email || !$firstName || !$lastName || !$desiredDate) {
return new JsonResponse(['success' => false, 'message' => 'Champs requis manquants'], 400);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return new JsonResponse(['success' => false, 'message' => 'Email invalide'], 400);
}
if (($solo + $duo) < 1 || ($solo + $duo) > 4) {
return new JsonResponse(['success' => false, 'message' => 'Nombre de bains invalide'], 400);
}
if (!in_array($offer, ['60', '90'], true)) {
return new JsonResponse(['success' => false, 'message' => 'Offre invalide'], 400);
}
$date = \DateTime::createFromFormat('Y-m-d', $desiredDate);
if (!$date) {
return new JsonResponse(['success' => false, 'message' => 'Format de date invalide'], 400);
}
if ($date < new \DateTime('today')) {
return new JsonResponse(['success' => false, 'message' => 'La date doit être dans le futur'], 400);
}
$existing = $waitlistEntryRepository->findOneBy([
'email' => $email,
'desiredDate' => $date,
'expired' => false,
]);
if ($existing) {
return new JsonResponse([
'success' => true,
'message' => 'Vous êtes déjà inscrit(e) pour cette date. Nous vous préviendrons si un créneau se libère.'
]);
}
$entry = new WaitlistEntry();
$entry->setEmail($email);
$entry->setFirstName($firstName);
$entry->setLastName($lastName);
$entry->setDesiredDate($date);
$entry->setSolo($solo);
$entry->setDuo($duo);
$entry->setOffer($offer);
$entry->setIsCarteCadeau($isCarteCadeau);
if ($phone) {
$entry->setPhone($phone);
}
if ($comment) {
$entry->setComment($comment);
}
$waitlistEntryRepository->save($entry, true);
return new JsonResponse([
'success' => true,
'message' => 'C\'est noté ! Nous vous préviendrons par email si un créneau se libère le ' . $entry->getDesiredDateFormatted() . '.'
]);
}
}