diff --git a/src/Controller/Dashboard/FlowController.php b/src/Controller/Dashboard/FlowController.php index 2d15096..a9cb1d3 100644 --- a/src/Controller/Dashboard/FlowController.php +++ b/src/Controller/Dashboard/FlowController.php @@ -24,15 +24,12 @@ 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, @@ -65,12 +62,6 @@ 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(), @@ -80,12 +71,6 @@ 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')); } @@ -142,8 +127,6 @@ 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); @@ -248,18 +231,6 @@ 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'); @@ -286,68 +257,6 @@ 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 1f77bcf..edaf0d7 100644 --- a/src/Controller/ReserverController.php +++ b/src/Controller/ReserverController.php @@ -38,7 +38,6 @@ 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 { @@ -575,7 +574,6 @@ class ReserverController extends AbstractController ProductReserveRepository $productReserveRepository, EntityManagerInterface $em, OptionsRepository $optionsRepository, - HttpClientInterface $client, Request $request, Mailer $mailer ): Response { @@ -644,14 +642,6 @@ 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' => [ @@ -668,7 +658,6 @@ class ReserverController extends AbstractController 'formule' => $cartData['total']['formule'], 'tvaEnabled' => $cartData['tvaEnabled'], ], - 'delivery' => $deliveryData ]); } @@ -1191,40 +1180,6 @@ 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 --- @@ -1265,80 +1220,6 @@ 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/src/Service/Pdf/ContratPdfService.php b/src/Service/Pdf/ContratPdfService.php index 11abb08..edc4567 100644 --- a/src/Service/Pdf/ContratPdfService.php +++ b/src/Service/Pdf/ContratPdfService.php @@ -239,7 +239,7 @@ class ContratPdfService extends Fpdf "ARTICLE 6 – CAUTION DE GARANTIE" => "Une caution de garantie peut être exigée selon le type et la valeur du matériel loué :\n• Structures professionnelles Lilian SEGARD - Ludikevent | Selon devis | Restitution immédiate après état des lieux de fin de location et contrôle de conformité\n• Matériel mis en relation (propriétaires privés) | Selon convention propriétaire | Restitution selon accord entre parties\nLa caution peut être conservée totalement ou partiellement en cas de : dégradation du matériel, salissure importante nécessitant un nettoyage approfondi, perte d'éléments ou d'accessoires, non-respect des conditions d'utilisation ayant entraîné des dommages.", - "ARTICLE 7 – LIVRAISON, INSTALLATION ET RÉCUPÉRATION" => "7.1 – Structures professionnelles Lilian SEGARD - Ludikevent :\nInstallation : L'installation est réalisée exclusivement par le personnel qualifié de Lilian SEGARD - Ludikevent. Le Client doit fournir un terrain plat, propre, dégagé, avec une alimentation électrique 220V à moins de 50 mètres. L'accès doit être praticable pour les véhicules.\nRécupération : La récupération du matériel est effectuée par Lilian SEGARD - Ludikevent aux horaires convenus.\n7.2 – Matériel en mise en relation :\nLilian SEGARD - Ludikevent peut assurer l'installation et la récupération pour le compte du propriétaire privé. Le Client locataire reste tenu d'assurer une surveillance permanente et constante. Des frais de déplacement supplémentaires peuvent s'appliquer au-delà d'un rayon de 30 km depuis Danizy.", + "ARTICLE 7 – LIVRAISON, INSTALLATION ET RÉCUPÉRATION" => "7.1 – Structures professionnelles Lilian SEGARD - Ludikevent :\nInstallation : L'installation est réalisée exclusivement par le personnel qualifié de Lilian SEGARD - Ludikevent. Le Client doit fournir un terrain plat, propre, dégagé, avec une alimentation électrique 220V à moins de 50 mètres. L'accès doit être praticable pour les véhicules.\nRécupération : La récupération du matériel est effectuée par Lilian SEGARD - Ludikevent aux horaires convenus.", "ARTICLE 8 – ÉTATS DES LIEUX ET TRANSFERT DE RESPONSABILITÉ" => "8.1 – État des lieux d'installation : Un état des lieux contradictoire est OBLIGATOIREMENT réalisé. La signature par le Client vaut reconnaissance de conformité et TRANSFERT COMPLET DE LA RESPONSABILITÉ DU MATÉRIEL AU CLIENT. Le Client assume l'ENTIÈRE et EXCLUSIVE responsabilité du matériel, de son utilisation, de sa surveillance et des dommages qui pourraient en résulter.\n8.2 – État des lieux de fin de location : Un état des lieux contradictoire est réalisé lors de la récupération. Sa signature vaut RETOUR DE LA RESPONSABILITÉ À Lilian SEGARD - Ludikevent.\n8.3 – Absence de signature : En cas d'absence ou de refus de signature, l'état des lieux établi par Lilian SEGARD - Ludikevent fera foi. Le matériel sera réputé avoir été livré en parfait état et la responsabilité du Client reste pleine et entière durant toute la période de location.", diff --git a/src/Service/Pdf/DevisPdfService.php b/src/Service/Pdf/DevisPdfService.php index 30a8112..c3e29e7 100644 --- a/src/Service/Pdf/DevisPdfService.php +++ b/src/Service/Pdf/DevisPdfService.php @@ -383,7 +383,7 @@ class DevisPdfService extends Fpdf "ARTICLE 6 – CAUTION DE GARANTIE" => "Une caution de garantie peut être exigée selon le type et la valeur du matériel loué :\n• Structures professionnelles Lilian SEGARD - Ludikevent | Selon devis | Restitution immédiate après état des lieux de fin de location et contrôle de conformité\n• Matériel mis en relation (propriétaires privés) | Selon convention propriétaire | Restitution selon accord entre parties\nLa caution peut être conservée totalement ou partiellement en cas de : dégradation du matériel, salissure importante nécessitant un nettoyage approfondi, perte d'éléments ou d'accessoires, non-respect des conditions d'utilisation ayant entraîné des dommages.", - "ARTICLE 7 – LIVRAISON, INSTALLATION ET RÉCUPÉRATION" => "7.1 – Structures professionnelles Lilian SEGARD - Ludikevent :\nInstallation : L'installation est réalisée exclusivement par le personnel qualifié de Lilian SEGARD - Ludikevent. Le Client doit fournir un terrain plat, propre, dégagé, avec une alimentation électrique 220V à moins de 50 mètres. L'accès doit être praticable pour les véhicules.\nRécupération : La récupération du matériel est effectuée par Lilian SEGARD - Ludikevent aux horaires convenus.\n7.2 – Matériel en mise en relation :\nLilian SEGARD - Ludikevent peut assurer l'installation et la récupération pour le compte du propriétaire privé. Le Client locataire reste tenu d'assurer une surveillance permanente et constante. Des frais de déplacement supplémentaires peuvent s'appliquer au-delà d'un rayon de 30 km depuis Danizy.", + "ARTICLE 7 – LIVRAISON, INSTALLATION ET RÉCUPÉRATION" => "7.1 – Structures professionnelles Lilian SEGARD - Ludikevent :\nInstallation : L'installation est réalisée exclusivement par le personnel qualifié de Lilian SEGARD - Ludikevent. Le Client doit fournir un terrain plat, propre, dégagé, avec une alimentation électrique 220V à moins de 50 mètres. L'accès doit être praticable pour les véhicules.\nRécupération : La récupération du matériel est effectuée par Lilian SEGARD - Ludikevent aux horaires convenus.", "ARTICLE 8 – ÉTATS DES LIEUX ET TRANSFERT DE RESPONSABILITÉ" => "8.1 – État des lieux d'installation : Un état des lieux contradictoire est OBLIGATOIREMENT réalisé. La signature par le Client vaut reconnaissance de conformité et TRANSFERT COMPLET DE LA RESPONSABILITÉ DU MATÉRIEL AU CLIENT. Le Client assume l'ENTIÈRE et EXCLUSIVE responsabilité du matériel, de son utilisation, de sa surveillance et des dommages qui pourraient en résulter.\n8.2 – État des lieux de fin de location : Un état des lieux contradictoire est réalisé lors de la récupération. Sa signature vaut RETOUR DE LA RESPONSABILITÉ À Lilian SEGARD - Ludikevent.\n8.3 – Absence de signature : En cas d'absence ou de refus de signature, l'état des lieux établi par Lilian SEGARD - Ludikevent fera foi. Le matériel sera réputé avoir été livré en parfait état et la responsabilité du Client reste pleine et entière durant toute la période de location.", diff --git a/templates/dashboard/flow/view.twig b/templates/dashboard/flow/view.twig index 4c7b9ca..a0c1a5d 100644 --- a/templates/dashboard/flow/view.twig +++ b/templates/dashboard/flow/view.twig @@ -4,16 +4,9 @@ {% block title_header %}Réservation #{{ session.id }}{% endblock %} {% block body %} - - -
- Livraison incluse dans un rayon de 30 km depuis Danizy. - Des frais de déplacement s'appliquent au-delà. -
-- 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. -
-