Travail sur le tunnel de reservation etape final

This commit is contained in:
Serreau Jovann
2026-02-01 10:28:09 +01:00
parent 2790be518b
commit aadf05d5bb
11 changed files with 81294 additions and 16 deletions

View File

@@ -28,9 +28,45 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
use Symfony\Component\HttpKernel\KernelInterface;
class ReserverController extends AbstractController
{
private KernelInterface $kernel;
private ?array $simplifiedCommunes = null;
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
private function loadSimplifiedCommunes(): array
{
if ($this->simplifiedCommunes !== null) {
return $this->simplifiedCommunes;
}
$filePath = $this->kernel->getProjectDir() . '/public/simplified_communes_by_zip.json';
if (!file_exists($filePath)) {
// Log an error or throw an exception if the file doesn't exist
// For now, return an empty array if file is missing
return [];
}
$content = file_get_contents($filePath);
if ($content === false) {
// Log an error or throw an exception if reading fails
return [];
}
$this->simplifiedCommunes = json_decode($content, true);
if ($this->simplifiedCommunes === null && json_last_error() !== JSON_ERROR_NONE) {
// Log JSON decoding error
return [];
}
return $this->simplifiedCommunes;
}
#[Route('/robots.txt', name: 'robots_txt', defaults: ['_format' => 'txt'])]
public function index(Request $request): Response
{
@@ -98,6 +134,76 @@ class ReserverController extends AbstractController
return new JsonResponse(['dispo' => $isAvailable]);
}
#[Route('/produit/check/basket', name: 'produit_check_basket', methods: ['POST'])]
public function productCheckBasket(Request $request, ProductReserveRepository $productReserveRepository, ProductRepository $productRepository): JsonResponse
{
$data = json_decode($request->getContent(), true);
$ids = $data['ids'] ?? [];
$startStr = $data['start'] ?? null;
$endStr = $data['end'] ?? null;
if (!is_array($ids) || empty($ids) || !$startStr || !$endStr) {
return new JsonResponse(['available' => false, 'message' => 'Missing or invalid parameters'], Response::HTTP_BAD_REQUEST);
}
$availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);
if (!$availability['allProductsAvailable']) {
return new JsonResponse([
'available' => false,
'message' => 'Certains produits de votre panier ne sont pas disponibles aux dates sélectionnées.',
'unavailable_products_ids' => $availability['unavailableProductIds']
]);
}
return new JsonResponse(['available' => true, 'message' => 'Tous les produits sont disponibles.']);
}
private function _checkProductsAvailability(
array $ids,
?string $startStr,
?string $endStr,
ProductRepository $productRepository,
ProductReserveRepository $productReserveRepository
): array {
$allProductsAvailable = true;
$unavailableProductIds = [];
if (empty($ids) || !$startStr || !$endStr) {
return ['allProductsAvailable' => false, 'unavailableProductIds' => $ids];
}
try {
$start = new \DateTimeImmutable($startStr);
$end = new \DateTimeImmutable($endStr);
} catch (\Exception $e) {
return ['allProductsAvailable' => false, 'unavailableProductIds' => $ids];
}
foreach ($ids as $productId) {
$product = $productRepository->find($productId);
if (!$product) {
$allProductsAvailable = false;
$unavailableProductIds[] = $productId;
continue;
}
$reserve = new ProductReserve();
$reserve->setProduct($product);
$reserve->setStartAt($start);
$reserve->setEndAt($end);
$isAvailable = $productReserveRepository->checkAvailability($reserve);
if (!$isAvailable) {
$allProductsAvailable = false;
$unavailableProductIds[] = $productId;
}
}
return ['allProductsAvailable' => $allProductsAvailable, 'unavailableProductIds' => $unavailableProductIds];
}
#[Route('/web-vitals', name: 'reservation_web-vitals', methods: ['POST'])]
public function webVitals(Request $request, EntityManagerInterface $em): Response
{
@@ -238,13 +344,87 @@ class ReserverController extends AbstractController
AuthenticationUtils $authenticationUtils,
OrderSessionRepository $repository,
ProductRepository $productRepository,
UploaderHelper $uploaderHelper
UploaderHelper $uploaderHelper,
ProductReserveRepository $productReserveRepository
): Response {
$session = $repository->findOneBy(['uuid' => $sessionId]);
if (!$session) {
return $this->render('revervation/session_lost.twig');
}
$sessionData = $session->getProducts();
$ids = $sessionData['ids'] ?? [];
$startStr = $sessionData['start'] ?? null;
$endStr = $sessionData['end'] ?? null;
// Check product availability
$availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);
if (!$availability['allProductsAvailable']) {
$this->addFlash('danger', 'Certains produits de votre panier ne sont plus disponibles. Veuillez vérifier votre réservation.');
return $this->redirectToRoute('reservation_flow', ['sessionId' => $sessionId]);
}
// Calcul de la durée
$duration = 1;
if ($startStr && $endStr) {
try {
$start = new \DateTimeImmutable($startStr);
$end = new \DateTimeImmutable($endStr);
if ($end >= $start) {
$duration = $start->diff($end)->days + 1;
}
} catch (\Exception $e) {
$duration = 1;
}
}
$products = [];
if (!empty($ids)) {
$products = $productRepository->findBy(['id' => $ids]);
}
$items = [];
$totalHT = 0;
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
$tvaRate = $tvaEnabled ? 0.20 : 0;
foreach ($products as $product) {
$price1Day = $product->getPriceDay();
$priceSup = $product->getPriceSup() ?? 0.0;
// Calcul du coût total pour ce produit selon la durée
$productTotalHT = $price1Day + ($priceSup * max(0, $duration - 1));
$productTotalTTC = $productTotalHT * (1 + $tvaRate);
$items[] = [
'product' => $product,
'image' => $uploaderHelper->asset($product, 'imageFile'),
'price1Day' => $price1Day,
'priceSup' => $priceSup,
'totalPriceHT' => $productTotalHT,
'totalPriceTTC' => $productTotalTTC,
];
$totalHT += $productTotalHT;
}
$totalTva = $totalHT * $tvaRate;
$totalTTC = $totalHT + $totalTva;
return $this->render('revervation/flow_confirmed.twig', [
'session' => $session,
'cart' => [
'items' => $items,
'startDate' => $startStr ? new \DateTimeImmutable($startStr) : null,
'endDate' => $endStr ? new \DateTimeImmutable($endStr) : null,
'duration' => $duration,
'totalHT' => $totalHT,
'totalTva' => $totalTva,
'totalTTC' => $totalTTC,
'tvaEnabled' => $tvaEnabled,
]
]);
}
#[Route('/flow/{sessionId}', name: 'reservation_flow', methods: ['GET', 'POST'])]
@@ -253,7 +433,8 @@ class ReserverController extends AbstractController
AuthenticationUtils $authenticationUtils,
OrderSessionRepository $repository,
ProductRepository $productRepository,
UploaderHelper $uploaderHelper
UploaderHelper $uploaderHelper,
ProductReserveRepository $productReserveRepository // Added dependency
): Response {
// This is the POST target for the login form, but also the GET page.
// The authenticator handles the POST. For GET, we just render the page.
@@ -267,6 +448,14 @@ class ReserverController extends AbstractController
$startStr = $sessionData['start'] ?? null;
$endStr = $sessionData['end'] ?? null;
// Check product availability
$availability = $this->_checkProductsAvailability($ids, $startStr, $endStr, $productRepository, $productReserveRepository);
if (!$availability['allProductsAvailable']) {
$this->addFlash('danger', 'Certains produits de votre panier ne sont plus disponibles. Veuillez vérifier votre sélection.');
return $this->redirectToRoute('reservation');
}
// Calcul de la durée
$duration = 1;
if ($startStr && $endStr) {
@@ -682,6 +871,26 @@ class ReserverController extends AbstractController
return $this->render('revervation/legal.twig');
}
#[Route('/cities/lookup', name: 'api_cities_lookup', methods: ['POST'])]
public function getCityByZipCode(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true);
$zipCode = $data['zipCode'] ?? null;
if (!$zipCode) {
return new JsonResponse(['error' => 'Missing zipCode parameter'], Response::HTTP_BAD_REQUEST);
}
$simplifiedCommunes = $this->loadSimplifiedCommunes();
$cities = $simplifiedCommunes[$zipCode] ?? [];
if (!empty($cities)) {
return new JsonResponse(['cities' => $cities]);
}
return new JsonResponse(['cities' => [], 'message' => 'City not found for this zip code'], Response::HTTP_NOT_FOUND);
}
#[Route('/rgpd', name: 'reservation_rgpd')]
public function revervationRgpd(): Response
{