diff --git a/src/Controller/Dashboard/FlowController.php b/src/Controller/Dashboard/FlowController.php index a9cb1d3..2d15096 100644 --- a/src/Controller/Dashboard/FlowController.php +++ b/src/Controller/Dashboard/FlowController.php @@ -24,12 +24,15 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Contracts\HttpClient\HttpClientInterface; + #[Route('/crm/flow')] class FlowController extends AbstractController { public function __construct( private readonly AppLogger $appLogger, private readonly OrderSessionRepository $orderSessionRepository, + private readonly HttpClientInterface $client, private readonly DevisRepository $devisRepository, private readonly ProductRepository $productRepository, private readonly OptionsRepository $optionsRepository, @@ -62,6 +65,12 @@ class FlowController extends AbstractController { $this->appLogger->record('VIEW', 'Consultation détails réservation en ligne #' . $session->getId()); + // Auto-calculation of delivery if missing or geometry missing + if (($session->getDeliveryDistance() === null || $session->getDeliveryPrice() === null || $session->getDeliveryGeometry() === null) && $session->getAdressEvent()) { + $this->calculateDelivery($session); + $em->flush(); + } + return $this->render('dashboard/flow/view.twig', [ 'session' => $session, 'prestataires' => $this->prestaireRepository->findAll(), @@ -71,6 +80,12 @@ class FlowController extends AbstractController #[Route('/update/{id}', name: 'app_crm_flow_update', methods: ['POST'])] public function update(\App\Entity\OrderSession $session, Request $request, \Doctrine\ORM\EntityManagerInterface $em): Response { + if ($request->request->has('deliveryDistance')) { + $session->setDeliveryDistance((float)$request->request->get('deliveryDistance')); + } + if ($request->request->has('deliveryPrice')) { + $session->setDeliveryPrice((float)$request->request->get('deliveryPrice')); + } if ($request->request->has('typePaiement')) { $session->setTypePaiement($request->request->get('typePaiement')); } @@ -127,6 +142,8 @@ class FlowController extends AbstractController $devis->setCustomer($session->getCustomer()); // 2.1 Set additional Devis fields from OrderSession + $devis->setDistance($session->getDeliveryDistance()); + $devis->setPriceShip($session->getDeliveryPrice()); $devis->setPaymentMethod($session->getTypePaiement()); $devis->setOrderSession($session); @@ -231,6 +248,18 @@ class FlowController extends AbstractController } } + // 7. Delivery Fee + if ($session->getDeliveryPrice() > 0) { + $devisOpt = new DevisOptions(); + $devisOpt->setOption("Frais de livraison"); + $dist = number_format($session->getDeliveryDistance(), 1, ',', ' '); + $town = $session->getBillingTown() ?: 'Ville inconnue'; + $devisOpt->setDetails("Livraison ($dist km) - $town"); + $devisOpt->setPriceHt($session->getDeliveryPrice()); + $em->persist($devisOpt); + $devis->addDevisOption($devisOpt); + } + // 1. DocuSeal (Version pour signature) $docusealService = new DevisPdfService($this->kernel, $devis, $this->productRepository, true); $this->savePdfFile($devis, $docusealService->generate(), 'dc_', 'setDevisDocuSealFile'); @@ -257,6 +286,68 @@ class FlowController extends AbstractController return $this->redirectToRoute('app_crm_flow'); } + private function calculateDelivery(\App\Entity\OrderSession $session): void + { + if (!$session->getAdressEvent() || !$session->getZipCodeEvent() || !$session->getTownEvent()) { + return; + } + + $query = sprintf('%s %s %s', $session->getAdressEvent(), $session->getZipCodeEvent(), $session->getTownEvent()); + + try { + $response = $this->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 = $this->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']; + $geometry = $itineraire['geometry'] ?? null; + + $rate = 0.50; + $trips = 4; + $price = 0.0; + + if ($distance > 10) { + $chargedDistance = $distance - 10; + $price = ($chargedDistance * $trips) * $rate; + } + + $session->setDeliveryDistance($distance); + $session->setDeliveryPrice($price); + $session->setDeliveryGeometry($geometry); + } + } catch (\Exception $e) { + // Log error or silent fail + } + } + private function savePdfFile(Devis $devis, string $content, string $prefix, string $setterMethod): void { $tmpPath = sys_get_temp_dir() . '/' . $prefix . uniqid() . '.pdf'; diff --git a/src/Controller/ReserverController.php b/src/Controller/ReserverController.php index edaf0d7..1f77bcf 100644 --- a/src/Controller/ReserverController.php +++ b/src/Controller/ReserverController.php @@ -38,6 +38,7 @@ use App\Service\Pdf\DevisPdfService; use App\Entity\Devis; use App\Entity\DevisLine; use App\Entity\CustomerAddress; +use Symfony\Contracts\HttpClient\HttpClientInterface; class ReserverController extends AbstractController { @@ -574,6 +575,7 @@ class ReserverController extends AbstractController ProductReserveRepository $productReserveRepository, EntityManagerInterface $em, OptionsRepository $optionsRepository, + HttpClientInterface $client, Request $request, Mailer $mailer ): Response { @@ -642,6 +644,14 @@ class ReserverController extends AbstractController $cartData = $this->buildCartData($products, $selectedOptionsMap, $duration, $optionsRepository, $uploaderHelper, $promotion, $formule); + // --- Calcul Frais de Livraison --- + $deliveryData = $this->calculateDelivery( + $session->getAdressEvent(), + $session->getZipCodeEvent(), + $session->getTownEvent(), + $client + ); + return $this->render('revervation/flow_confirmed.twig', [ 'session' => $session, 'cart' => [ @@ -658,6 +668,7 @@ class ReserverController extends AbstractController 'formule' => $cartData['total']['formule'], 'tvaEnabled' => $cartData['tvaEnabled'], ], + 'delivery' => $deliveryData ]); } @@ -1180,6 +1191,40 @@ class ReserverController extends AbstractController return $this->render('revervation/hosting.twig'); } + #[Route('/estimer-la-livraison', name: 'reservation_estimate_delivery')] + public function estimateDelivery(Request $request, HttpClientInterface $client): Response + { + $form = $this->createFormBuilder() + ->add('address', TextType::class, ['required' => true]) + ->add('zipCode', TextType::class, ['required' => true]) + ->add('city', TextType::class, ['required' => true]) + ->getForm(); + + $form->handleRequest($request); + $estimation = null; + $details = null; + $geometry = null; + + if ($form->isSubmitted() && $form->isValid()) { + $data = $form->getData(); + $deliveryData = $this->calculateDelivery($data['address'], $data['zipCode'], $data['city'], $client); + + if ($deliveryData['details'] !== null) { + $estimation = $deliveryData['estimation']; + $details = $deliveryData['details']; + $geometry = $deliveryData['geometry']; + } else { + $this->addFlash('warning', 'Adresse introuvable ou erreur lors du calcul.'); + } + } + + return $this->render('revervation/estimate_delivery.twig', [ + 'form' => $form->createView(), + 'estimation' => $estimation, + 'details' => $details, + 'geometry' => $geometry + ]); + } // --- Private Helper Methods --- @@ -1220,6 +1265,80 @@ class ReserverController extends AbstractController return isset($_ENV['TVA_ENABLED']) && $_ENV['TVA_ENABLED'] === "true"; } + private function calculateDelivery(?string $address, ?string $zipCode, ?string $town, HttpClientInterface $client): array + { + $result = [ + 'estimation' => null, + 'details' => null, + 'geometry' => null + ]; + + if (!$address || !$zipCode || !$town) { + return $result; + } + + $query = sprintf('%s %s %s', $address, $zipCode, $town); + 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']; + $result['geometry'] = $itineraire['geometry'] ?? null; + + $rate = 0.50; + $trips = 4; + + if ($distance <= 10) { + $result['estimation'] = 0.0; + $chargedDistance = 0.0; + } else { + $chargedDistance = $distance - 10; + $result['estimation'] = ($chargedDistance * $trips) * $rate; + } + + $result['details'] = [ + 'distance' => $distance, + 'chargedDistance' => $chargedDistance, + 'trips' => $trips, + 'rate' => $rate, + 'isFree' => ($distance <= 10) + ]; + } + } catch (\Exception $e) { + // Return default nulls on error + } + + return $result; + } private function buildCartData(array $products, array $selectedOptionsMap, int $duration, OptionsRepository $optionsRepository, UploaderHelper $uploaderHelper, ?Promotion $promotion = null, ?Formules $formule = null): array { diff --git a/templates/dashboard/flow/view.twig b/templates/dashboard/flow/view.twig index a0c1a5d..4c7b9ca 100644 --- a/templates/dashboard/flow/view.twig +++ b/templates/dashboard/flow/view.twig @@ -4,9 +4,16 @@ {% block title_header %}Réservation #{{ session.id }}{% endblock %} {% block body %} + + +
+ Renseignez l'adresse de votre événement pour obtenir une estimation des frais de livraison. +
+ + {{ form_start(form, {'attr': {'class': 'space-y-6', 'data-turbo': 'false'}}) }} + +Votre événement se trouve à moins de 10km de nos locaux.
++ {{ delivery.estimation|format_currency('EUR') }} +
+Détails du calcul
+ +Formule appliquée
+
+ ({{ delivery.details.distance|number_format(1) }} - 10) x {{ delivery.details.trips }} x {{ delivery.details.rate }}€ = {{ delivery.estimation|number_format(2) }}€
+
+ + Cette estimation est indicative. Le montant définitif figurera sur votre devis. +
+