```
✨ feat(reservation/flow): Améliore le flux de réservation et ajoute des options.
Cette commit améliore le flux de réservation, ajoute une estimation des
frais de livraison et gère les options de produit et les paiements.
```
This commit is contained in:
@@ -50,13 +50,18 @@ class ReserverController extends AbstractController
|
||||
string $sessionId,
|
||||
OrderSessionRepository $repository,
|
||||
ProductRepository $productRepository,
|
||||
KernelInterface $kernel
|
||||
KernelInterface $kernel,
|
||||
\App\Repository\OptionsRepository $optionsRepository
|
||||
): Response {
|
||||
$session = $repository->findOneBy(['uuid' => $sessionId]);
|
||||
if (!$session) {
|
||||
return $this->redirectToRoute('reservation');
|
||||
}
|
||||
|
||||
if ($session->getState() === 'send') {
|
||||
return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
$sessionData = $session->getProducts();
|
||||
$ids = $sessionData['ids'] ?? [];
|
||||
$startStr = $sessionData['start'] ?? null;
|
||||
@@ -106,15 +111,50 @@ class ReserverController extends AbstractController
|
||||
$devis->setStartAt($start);
|
||||
$devis->setEndAt($end);
|
||||
|
||||
$selectedOptionsMap = $sessionData['options'] ?? [];
|
||||
|
||||
if (!empty($ids)) {
|
||||
$products = $productRepository->findBy(['id' => $ids]);
|
||||
$processedProductIds = [];
|
||||
|
||||
foreach ($products as $product) {
|
||||
$processedProductIds[] = $product->getId();
|
||||
|
||||
$line = new DevisLine();
|
||||
$line->setProduct($product->getName());
|
||||
$line->setPriceHt($product->getPriceDay());
|
||||
$line->setPriceHtSup($product->getPriceSup());
|
||||
$line->setDay($duration);
|
||||
$devis->addDevisLine($line);
|
||||
|
||||
if (isset($selectedOptionsMap[$product->getId()])) {
|
||||
$optionIds = $selectedOptionsMap[$product->getId()];
|
||||
if (!empty($optionIds)) {
|
||||
$options = $optionsRepository->findBy(['id' => $optionIds]);
|
||||
foreach ($options as $option) {
|
||||
$lineOpt = new DevisLine();
|
||||
$lineOpt->setProduct("Option : " . $option->getName());
|
||||
$lineOpt->setPriceHt($option->getPriceHt());
|
||||
$lineOpt->setPriceHtSup(0);
|
||||
$lineOpt->setDay($duration);
|
||||
$devis->addDevisLine($lineOpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($selectedOptionsMap as $prodId => $optIds) {
|
||||
if (!in_array($prodId, $processedProductIds) && !empty($optIds)) {
|
||||
$options = $optionsRepository->findBy(['id' => $optIds]);
|
||||
foreach ($options as $option) {
|
||||
$lineOpt = new DevisLine();
|
||||
$lineOpt->setProduct("Option : " . $option->getName());
|
||||
$lineOpt->setPriceHt($option->getPriceHt());
|
||||
$lineOpt->setPriceHtSup(0);
|
||||
$lineOpt->setDay($duration);
|
||||
$devis->addDevisLine($lineOpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,13 +534,35 @@ class ReserverController extends AbstractController
|
||||
UploaderHelper $uploaderHelper,
|
||||
ProductReserveRepository $productReserveRepository,
|
||||
EntityManagerInterface $em,
|
||||
\App\Repository\OptionsRepository $optionsRepository
|
||||
\App\Repository\OptionsRepository $optionsRepository,
|
||||
HttpClientInterface $client,
|
||||
Request $request,
|
||||
Mailer $mailer
|
||||
): Response {
|
||||
$session = $repository->findOneBy(['uuid' => $sessionId]);
|
||||
if (!$session) {
|
||||
return $this->render('revervation/session_lost.twig');
|
||||
}
|
||||
|
||||
if ($session->getState() === 'send') {
|
||||
return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
if ($request->isMethod('POST')) {
|
||||
$mailer->send(
|
||||
'contact@ludikevent.fr',
|
||||
"Ludikevent",
|
||||
"[Ludikevent] - Nouvelle demande de réservation",
|
||||
"mails/reserve/confirmation.twig",
|
||||
['session' => $session]
|
||||
);
|
||||
|
||||
$session->setState('send');
|
||||
$em->flush();
|
||||
$request->getSession()->remove('order_session_uuid');
|
||||
return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
$sessionData = $session->getProducts();
|
||||
$ids = $sessionData['ids'] ?? [];
|
||||
$selectedOptionsMap = $sessionData['options'] ?? [];
|
||||
@@ -620,6 +682,73 @@ class ReserverController extends AbstractController
|
||||
$totalTva = $totalHT * $tvaRate;
|
||||
$totalTTC = $totalHT + $totalTva;
|
||||
|
||||
// --- Calcul Frais de Livraison ---
|
||||
$deliveryEstimation = null;
|
||||
$deliveryDetails = null;
|
||||
$deliveryGeometry = null;
|
||||
|
||||
if ($session->getAdressEvent() && $session->getZipCodeEvent() && $session->getTownEvent()) {
|
||||
$query = sprintf('%s %s %s', $session->getAdressEvent(), $session->getZipCodeEvent(), $session->getTownEvent());
|
||||
try {
|
||||
$response = $client->request('GET', 'https://api-adresse.data.gouv.fr/search/', [
|
||||
'query' => [
|
||||
'q' => $query,
|
||||
'limit' => 1
|
||||
]
|
||||
]);
|
||||
|
||||
$content = $response->toArray();
|
||||
|
||||
if (!empty($content['features'])) {
|
||||
$coords = $content['features'][0]['geometry']['coordinates'];
|
||||
$lon = $coords[0];
|
||||
$lat = $coords[1];
|
||||
|
||||
// Point de départ (LudikEvent)
|
||||
$startLat = 49.849;
|
||||
$startLon = 3.286;
|
||||
|
||||
// Calcul itinéraire via API Geoplateforme
|
||||
$itineraireResponse = $client->request('GET', 'https://data.geopf.fr/navigation/itineraire', [
|
||||
'query' => [
|
||||
'resource' => 'bdtopo-osrm',
|
||||
'start' => $startLon . ',' . $startLat,
|
||||
'end' => $lon . ',' . $lat,
|
||||
'profile' => 'car',
|
||||
'optimization' => 'fastest',
|
||||
'distanceUnit' => 'kilometer',
|
||||
'geometryFormat' => 'geojson'
|
||||
]
|
||||
]);
|
||||
|
||||
$itineraire = $itineraireResponse->toArray();
|
||||
$distance = $itineraire['distance'];
|
||||
$deliveryGeometry = $itineraire['geometry'] ?? null;
|
||||
|
||||
$rate = 0.50;
|
||||
$trips = 4;
|
||||
|
||||
if ($distance <= 10) {
|
||||
$deliveryEstimation = 0.0;
|
||||
$chargedDistance = 0.0;
|
||||
} else {
|
||||
$chargedDistance = $distance - 10;
|
||||
$deliveryEstimation = ($chargedDistance * $trips) * $rate;
|
||||
}
|
||||
|
||||
$deliveryDetails = [
|
||||
'distance' => $distance,
|
||||
'chargedDistance' => $chargedDistance,
|
||||
'trips' => $trips,
|
||||
'rate' => $rate,
|
||||
'isFree' => ($distance <= 10)
|
||||
];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Silent fail for delivery calculation in flow
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('revervation/flow_confirmed.twig', [
|
||||
'session' => $session,
|
||||
'cart' => [
|
||||
@@ -632,10 +761,31 @@ class ReserverController extends AbstractController
|
||||
'totalTva' => $totalTva,
|
||||
'totalTTC' => $totalTTC,
|
||||
'tvaEnabled' => $tvaEnabled,
|
||||
],
|
||||
'delivery' => [
|
||||
'estimation' => $deliveryEstimation,
|
||||
'details' => $deliveryDetails,
|
||||
'geometry' => $deliveryGeometry
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/flow/{sessionId}/success', name: 'reservation_flow_success', methods: ['GET'])]
|
||||
public function flowSuccess(string $sessionId, OrderSessionRepository $repository): Response
|
||||
{
|
||||
$session = $repository->findOneBy(['uuid' => $sessionId]);
|
||||
|
||||
if (!$session) {
|
||||
return $this->redirectToRoute('reservation');
|
||||
}
|
||||
|
||||
if ($session->getState() !== 'send') {
|
||||
return $this->redirectToRoute('reservation_flow', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
return $this->render('revervation/success.twig');
|
||||
}
|
||||
|
||||
#[Route('/flow/{sessionId}', name: 'reservation_flow', methods: ['GET', 'POST'])]
|
||||
public function flowLogin(
|
||||
string $sessionId,
|
||||
@@ -643,8 +793,9 @@ class ReserverController extends AbstractController
|
||||
OrderSessionRepository $repository,
|
||||
ProductRepository $productRepository,
|
||||
UploaderHelper $uploaderHelper,
|
||||
ProductReserveRepository $productReserveRepository, // Added dependency
|
||||
EntityManagerInterface $em
|
||||
ProductReserveRepository $productReserveRepository,
|
||||
EntityManagerInterface $em,
|
||||
\App\Repository\OptionsRepository $optionsRepository
|
||||
): 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.
|
||||
@@ -653,8 +804,13 @@ class ReserverController extends AbstractController
|
||||
return $this->render('revervation/session_lost.twig');
|
||||
}
|
||||
|
||||
if ($session->getState() === 'send') {
|
||||
return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
$sessionData = $session->getProducts();
|
||||
$ids = $sessionData['ids'] ?? [];
|
||||
$selectedOptionsMap = $sessionData['options'] ?? [];
|
||||
$startStr = $sessionData['start'] ?? null;
|
||||
$endStr = $sessionData['end'] ?? null;
|
||||
|
||||
@@ -698,12 +854,45 @@ class ReserverController extends AbstractController
|
||||
$tvaEnabled = isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true";
|
||||
$tvaRate = $tvaEnabled ? 0.20 : 0;
|
||||
|
||||
$rootOptions = [];
|
||||
$processedProductIds = [];
|
||||
|
||||
foreach ($products as $product) {
|
||||
$processedProductIds[] = $product->getId();
|
||||
$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));
|
||||
|
||||
// Traitement des options
|
||||
$productOptions = [];
|
||||
$optionsTotalHT = 0;
|
||||
|
||||
if (isset($selectedOptionsMap[$product->getId()])) {
|
||||
$optionIds = $selectedOptionsMap[$product->getId()];
|
||||
if (!empty($optionIds)) {
|
||||
$optionsEntities = $optionsRepository->findBy(['id' => $optionIds]);
|
||||
foreach ($optionsEntities as $option) {
|
||||
$optPrice = $option->getPriceHt();
|
||||
$optData = [
|
||||
'id' => $option->getId(),
|
||||
'name' => $option->getName(),
|
||||
'price' => $optPrice
|
||||
];
|
||||
|
||||
if ($product->getOptions()->contains($option)) {
|
||||
$productOptions[] = $optData;
|
||||
$optionsTotalHT += $optPrice;
|
||||
} else {
|
||||
$rootOptions[] = $optData;
|
||||
$totalHT += $optPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$productTotalHT += $optionsTotalHT;
|
||||
$productTotalTTC = $productTotalHT * (1 + $tvaRate);
|
||||
|
||||
$items[] = [
|
||||
@@ -713,11 +902,28 @@ class ReserverController extends AbstractController
|
||||
'priceSup' => $priceSup,
|
||||
'totalPriceHT' => $productTotalHT,
|
||||
'totalPriceTTC' => $productTotalTTC,
|
||||
'options' => $productOptions
|
||||
];
|
||||
|
||||
$totalHT += $productTotalHT;
|
||||
}
|
||||
|
||||
// Traiter les options orphelines
|
||||
foreach ($selectedOptionsMap as $prodId => $optIds) {
|
||||
if (!in_array($prodId, $processedProductIds) && !empty($optIds)) {
|
||||
$optionsEntities = $optionsRepository->findBy(['id' => $optIds]);
|
||||
foreach ($optionsEntities as $option) {
|
||||
$optPrice = $option->getPriceHt();
|
||||
$rootOptions[] = [
|
||||
'id' => $option->getId(),
|
||||
'name' => $option->getName(),
|
||||
'price' => $optPrice
|
||||
];
|
||||
$totalHT += $optPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$totalTva = $totalHT * $tvaRate;
|
||||
$totalTTC = $totalHT + $totalTva;
|
||||
|
||||
@@ -727,6 +933,7 @@ class ReserverController extends AbstractController
|
||||
'error' => $authenticationUtils->getLastAuthenticationError(),
|
||||
'cart' => [
|
||||
'items' => $items,
|
||||
'options' => $rootOptions,
|
||||
'startDate' => $startStr ? new \DateTimeImmutable($startStr) : null,
|
||||
'endDate' => $endStr ? new \DateTimeImmutable($endStr) : null,
|
||||
'duration' => $duration,
|
||||
@@ -750,6 +957,10 @@ class ReserverController extends AbstractController
|
||||
return $this->redirectToRoute('reservation');
|
||||
}
|
||||
|
||||
if ($session->getState() === 'send') {
|
||||
return $this->redirectToRoute('reservation_flow_success', ['sessionId' => $sessionId]);
|
||||
}
|
||||
|
||||
$session->setBillingAddress($request->request->get('billingAddress'));
|
||||
$session->setBillingZipCode($request->request->get('billingZipCode'));
|
||||
$session->setBillingTown($request->request->get('billingTown'));
|
||||
@@ -922,6 +1133,8 @@ class ReserverController extends AbstractController
|
||||
|
||||
if ($customer->getType() === 'buisness') {
|
||||
$customer->setSiret($payload->getString('siret'));
|
||||
$customer->setRaisonSocial($payload->getString('raisonSocial'));
|
||||
$customer->setTypCompany($payload->getString('typCompany'));
|
||||
}
|
||||
|
||||
$hashedPassword = $hasher->hashPassword($customer, $payload->getString('password'));
|
||||
|
||||
Reference in New Issue
Block a user