src/Controller/PanierController.php line 422

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use DateTime;
  4. use Dompdf\Dompdf;
  5. use Dompdf\Options;
  6. use App\Entity\Vente;
  7. use App\Entity\Client;
  8. use App\Entity\Facture;
  9. use App\Entity\Produit;
  10. use App\Entity\Recette;
  11. use App\Entity\Boutique;
  12. use App\Entity\Echeance;
  13. use App\Entity\ActionLog;
  14. use App\Entity\SortieStock;
  15. use App\Entity\DetailsVente;
  16. use App\Entity\ProduitStock;
  17. use Psr\Log\LoggerInterface;
  18. use App\Service\RecetteService;
  19. use App\Service\PaiementService;
  20. use App\Repository\VenteRepository;
  21. use App\Repository\ClientRepository;
  22. use App\Repository\ProduitRepository;
  23. use App\Repository\BoutiqueRepository;
  24. use Doctrine\ORM\EntityManagerInterface;
  25. use App\Repository\ProduitStockRepository;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\Security\Core\Security;
  28. use Symfony\Component\HttpFoundation\Response;
  29. use Symfony\Component\Routing\Annotation\Route;
  30. use Symfony\Component\HttpFoundation\JsonResponse;
  31. use Symfony\Component\HttpFoundation\RedirectResponse;
  32. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  33. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  34. class PanierController extends AbstractController
  35. {
  36.     private $recetteService;
  37.     private $paiementService;
  38.     private $logger;
  39.     public function __construct(
  40.         RecetteService $recetteService,
  41.         PaiementService $paiementService,
  42.         LoggerInterface $logger
  43.     ) {
  44.         $this->recetteService $recetteService;
  45.         $this->paiementService $paiementService;
  46.         $this->logger $logger;
  47.     }
  48.     #[Route('/panier'name'app_panier')]
  49.     public function index(
  50.         SessionInterface $session,
  51.         ProduitRepository $productsRepository,
  52.         ClientRepository $clientRepository,
  53.         EntityManagerInterface $entityManager
  54.     ): Response {
  55.         $panier $session->get("panier", []);
  56.         $clients $clientRepository->findAll();
  57.         $venteEnModification $session->get('vente_en_modification');
  58.         // On "fabrique" les données avec une structure cohérente
  59.         $dataPanier = [];
  60.         $total 0;
  61.         foreach ($panier as $id => $item) {
  62.             $product $productsRepository->find($id);
  63.             if (!$product) {
  64.                 continue; // Ignorer les produits qui n'existent plus
  65.             }
  66.             // CORRECTION : Normaliser la structure du panier
  67.             if (is_array($item)) {
  68.                 $quantite $item['quantite'] ?? 1;
  69.                 $prixVenteUnitaire $item['prixVenteUnitaire'] ?? $product->getPrixUnitaire();
  70.             } else {
  71.                 $quantite $item;
  72.                 $prixVenteUnitaire $product->getPrixUnitaire();
  73.                 // Mettre à jour la structure du panier pour la cohérence
  74.                 $panier[$id] = [
  75.                     'quantite' => $quantite,
  76.                     'prixVenteUnitaire' => $prixVenteUnitaire
  77.                 ];
  78.             }
  79.             $dataPanier[] = [
  80.                 "produit" => $product,
  81.                 "quantite" => $quantite,
  82.                 "prixVenteUnitaire" => $prixVenteUnitaire
  83.             ];
  84.             $total += $prixVenteUnitaire $quantite;
  85.         }
  86.         // Sauvegarder le panier normalisé
  87.         $session->set("panier"$panier);
  88.         // Si en mode modification, charger les données additionnelles
  89.         $venteData null;
  90.         if ($venteEnModification) {
  91.             $vente $entityManager->getRepository(Vente::class)->find($venteEnModification);
  92.             if ($vente) {
  93.                 $venteData $this->prepareVenteDataForTemplate($vente);
  94.             }
  95.         }
  96.         // Logs détaillés pour debug
  97.         $this->logger->info('PanierController::index - Données envoyées au template', [
  98.             'dataPanier' => $dataPanier,
  99.             'total' => $total,
  100.             'venteEnModification' => $venteEnModification,
  101.             'venteData' => $venteData
  102.         ]);
  103.         
  104.         // Log spécifique pour la vente 866
  105.         if ($venteEnModification === 866) {
  106.             $this->logger->info('=== VENTE 866 - ANALYSE DÉTAILLÉE ===');
  107.             foreach ($dataPanier as $index => $item) {
  108.                 $this->logger->info("Item $index:", [
  109.                     'produit_id' => $item['produit']->getId(),
  110.                     'produit_nom' => $item['produit']->getNom(),
  111.                     'produit_nom_json' => json_encode($item['produit']->getNom()),
  112.                     'produit_nom_length' => strlen($item['produit']->getNom()),
  113.                     'quantite' => $item['quantite'],
  114.                     'prixVenteUnitaire' => $item['prixVenteUnitaire']
  115.                 ]);
  116.             }
  117.         }
  118.         return $this->render('panier/index.html.twig'compact(
  119.             "dataPanier",
  120.             "total",
  121.             "clients",
  122.             "venteEnModification",
  123.             "venteData"
  124.         ));
  125.     }
  126.     // Nouvelle route pour recevoir les données de vente en AJAX
  127.     #[Route('/panier/vente-ajax'name'vente_panier_ajax'methods: ['POST'])]
  128.     public function ventePanierAjax(
  129.         SessionInterface $session,
  130.         EntityManagerInterface $entityManager,
  131.         ProduitRepository $produitRepository,
  132.         ProduitStockRepository $produitStockRepository,
  133.         Security $security,
  134.         Request $request
  135.     ): JsonResponse {
  136.         try {
  137.             $panier $session->get('panier', []);
  138.             if (empty($panier)) {
  139.                 return new JsonResponse(['success' => false'message' => 'Votre panier est vide.'], 400);
  140.             }
  141.             $user $security->getUser();
  142.             $boutique $user->getBoutique();
  143.             if (!$boutique) {
  144.                 return new JsonResponse(['success' => false'message' => 'Aucune boutique associée à votre compte.'], 400);
  145.             }
  146.             $data json_decode($request->getContent(), true);
  147.             $clientId $data['client_id'] ?? null;
  148.             $tva = (float)($data['tva'] ?? 0);
  149.             $fraisTransport = (float)($data['frais_transport'] ?? 0);
  150.             $statutPaiement $data['statut_paiement'] ?? 'Payé';
  151.             $echeancesData $data['echeances'] ?? [];
  152.             $acompteDirect = isset($data['acompte_direct']) ? (float)$data['acompte_direct'] : 0;
  153.             $dateVente $data['date_vente'] ?? null;
  154.             $entityManager->beginTransaction();
  155.             // Déterminer la date de vente
  156.             $dateVenteFinale null;
  157.             if ($dateVente && !empty($dateVente)) {
  158.                 try {
  159.                     $dateVenteFinale = new DateTime($dateVente);
  160.                 } catch (\Exception $e) {
  161.                     // Si la date est invalide, utiliser la date actuelle
  162.                     $dateVenteFinale = new DateTime();
  163.                 }
  164.             } else {
  165.                 // Si aucune date n'est fournie, utiliser la date actuelle
  166.                 $dateVenteFinale = new DateTime();
  167.             }
  168.             $vente = (new Vente())
  169.                 ->setDescription('Vente de produits')
  170.                 ->setDateVente($dateVenteFinale)
  171.                 ->setVendeur($user)
  172.                 ->setBoutique($boutique);
  173.             $montantTotal 0;
  174.             foreach ($panier as $produitId => $item) {
  175.                 $produit $produitRepository->find($produitId);
  176.                 if (!$produit) continue;
  177.                 $quantite is_array($item) ? ($item['quantite'] ?? 1) : (int)$item;
  178.                 $prixVenteUnitaire is_array($item) ? ($item['prixVenteUnitaire'] ?? $produit->getPrixUnitaire()) : $produit->getPrixUnitaire();
  179.                 $stock $produitStockRepository->findOneBy(['produit' => $produit'boutique' => $boutique]) ?? (new ProduitStock())
  180.                     ->setProduit($produit)->setBoutique($boutique)->setQuantite(0);
  181.                 $stock->setQuantite($stock->getQuantite() - $quantite);
  182.                 $entityManager->persist($stock);
  183.                 $sortie = (new SortieStock())
  184.                     ->setProduit($produit)
  185.                     ->setQuantite($quantite)
  186.                     ->setDateSortie(new DateTime())
  187.                     ->setPrixVente($prixVenteUnitaire)
  188.                     ->setBoutique($boutique)
  189.                     ->setUtilisateur($user);
  190.                 $entityManager->persist($sortie);
  191.                 $prixTotal $quantite $prixVenteUnitaire;
  192.                 $montantTotal += $prixTotal;
  193.                 $detail = (new DetailsVente())
  194.                     ->setVente($vente)
  195.                     ->setProduit($produit)
  196.                     ->setQuantiteVendu($quantite)
  197.                     ->setPrixUnitaire($prixVenteUnitaire)
  198.                     ->setPrixTotal($prixTotal);
  199.                 $entityManager->persist($detail);
  200.             }
  201.             $client $clientId $entityManager->getRepository(Client::class)->find($clientId) : null;
  202.             if ($client$vente->setClient($client);
  203.             $entityManager->persist($vente);
  204.             $entityManager->flush();
  205.             $montantTotalFinal $montantTotal $fraisTransport;
  206.             $facture = (new Facture())
  207.                 ->setVente($vente)
  208.                 ->setMontantTotal($montantTotal)
  209.                 ->setCreatedAt(new DateTime())
  210.                 ->setType('Facture')
  211.                 ->setTva($tva)
  212.                 ->setFraisTransport($fraisTransport)
  213.                 ->setStatutPaiement($statutPaiement);
  214.             if ($client$facture->setClient($client);
  215.             if ($statutPaiement === 'Payé') {
  216.                 $facture->setMontantPaye($montantTotalFinal);
  217.                 $entityManager->persist($facture);
  218.                 $entityManager->flush();
  219.                 $echeanceAcompte = new Echeance();
  220.                 $echeanceAcompte->setFacture($facture)
  221.                     ->setAmount($montantTotalFinal)
  222.                     ->setDate(new DateTime())
  223.                     ->setIsPaid(true)
  224.                     ->setClient($client)
  225.                     ->setVente($vente)
  226.                     ->setType('acompte');
  227.                 $entityManager->persist($echeanceAcompte);
  228.                 $entityManager->flush();
  229.                 $this->recetteService->creerRecetteDepuisVente($vente$montantTotalFinal$boutique);
  230.             } elseif ($statutPaiement === 'Partiellement payé') {
  231.                 $facture->setMontantPaye($acompteDirect);
  232.                 $entityManager->persist($facture);
  233.                 $entityManager->flush();
  234.                 if ($acompteDirect 0) {
  235.                     $echeanceAcompte = new Echeance();
  236.                     $echeanceAcompte->setFacture($facture)
  237.                         ->setAmount($acompteDirect)
  238.                         ->setDate(new DateTime())
  239.                         ->setIsPaid(true)
  240.                         ->setClient($client)
  241.                         ->setVente($vente)
  242.                         ->setType('acompte');
  243.                     $entityManager->persist($echeanceAcompte);
  244.                     $this->recetteService->creerRecetteDepuisVente($vente$acompteDirect$boutique);
  245.                 }
  246.                 $resteAPayer $montantTotalFinal $acompteDirect;
  247.                 $totalEcheances 0;
  248.                 foreach ($echeancesData as $echeanceData) {
  249.                     $echeanceAmount = (float)$echeanceData['amount'];
  250.                     $totalEcheances += $echeanceAmount;
  251.                     $echeance = new Echeance();
  252.                     $echeance->setAmount($echeanceAmount)
  253.                         ->setDate(new DateTime($echeanceData['date']))
  254.                         ->setIsPaid(false)
  255.                         ->setFacture($facture)
  256.                         ->setClient($client)
  257.                         ->setVente($vente)
  258.                         ->setType('echeance');
  259.                     $entityManager->persist($echeance);
  260.                 }
  261.                 if ($resteAPayer && $totalEcheances $resteAPayer) {
  262.                     $echeance = new Echeance();
  263.                     $echeance->setAmount($resteAPayer $totalEcheances)
  264.                         ->setDate(new DateTime())
  265.                         ->setIsPaid(false)
  266.                         ->setFacture($facture)
  267.                         ->setClient($client)
  268.                         ->setVente($vente)
  269.                         ->setType('echeance');
  270.                     $entityManager->persist($echeance);
  271.                 }
  272.             } else {
  273.                 $facture->setMontantPaye(0);
  274.                 $entityManager->persist($facture);
  275.                 $entityManager->flush();
  276.             }
  277.             $entityManager->flush();
  278.             $entityManager->commit();
  279.             $session->set('panier', []);
  280.             return new JsonResponse([
  281.                 'success' => true,
  282.                 'message' => 'La vente a été effectuée avec succès.',
  283.                 'factureId' => $facture->getId(),
  284.                 'redirect' => $this->generateUrl('app_facture_show', ['id' => $facture->getId()])
  285.             ]);
  286.         } catch (\Exception $e) {
  287.             return new JsonResponse([
  288.                 'success' => false,
  289.                 'message' => 'Erreur lors de la vente : ' $e->getMessage()
  290.             ], 500);
  291.         }
  292.     }
  293.     #[Route('panier/remove/{id}'name'remove')]
  294.     public function remove(Produit $productSessionInterface $session)
  295.     {
  296.         // On récupère le panier actuel
  297.         $panier $session->get("panier", []);
  298.         $id $product->getId();
  299.         if (!empty($panier[$id])) {
  300.             if (is_array($panier[$id])) {
  301.                 if ($panier[$id]['quantite'] > 1) {
  302.                     $panier[$id]['quantite']--;
  303.                 } else {
  304.                     unset($panier[$id]);
  305.                 }
  306.             } else {
  307.                 if ($panier[$id] > 1) {
  308.                     $panier[$id]--;
  309.                 } else {
  310.                     unset($panier[$id]);
  311.                 }
  312.             }
  313.         }
  314.         // On sauvegarde dans la session
  315.         $session->set("panier"$panier);
  316.         return $this->redirectToRoute("app_panier");
  317.     }
  318.     #[Route('panier/delete/{id}'name'delete')]
  319.     public function supprimer(Produit $productSessionInterface $session)
  320.     {
  321.         // On récupère le panier actuel
  322.         $panier $session->get("panier", []);
  323.         $id $product->getId();
  324.         if (!empty($panier[$id])) {
  325.             unset($panier[$id]);
  326.         }
  327.         // On sauvegarde dans la session
  328.         $session->set("panier"$panier);
  329.         return $this->redirectToRoute("app_panier");
  330.     }
  331.     #[Route('delete'name'delete_all')]
  332.     public function deleteAll(SessionInterface $session)
  333.     {
  334.         $session->remove("panier");
  335.         return $this->redirectToRoute("app_panier");
  336.     }
  337.     #[Route('/panier/clear-ajax'name'clear_cart_ajax'methods: ['POST'])]
  338.     public function clearCartAjax(SessionInterface $session): JsonResponse
  339.     {
  340.         try {
  341.             $session->remove("panier");
  342.             return new JsonResponse([
  343.                 'success' => true,
  344.                 'message' => 'Panier vidé avec succès'
  345.             ]);
  346.         } catch (\Exception $e) {
  347.             return new JsonResponse([
  348.                 'success' => false,
  349.                 'message' => 'Erreur lors du vidage du panier : ' $e->getMessage()
  350.             ], 500);
  351.         }
  352.     }
  353.     #[Route('/panier/count'name'count_panier')]
  354.     public function countPanier(SessionInterface $session)
  355.     {
  356.         $panier $session->get("panier", []);
  357.         $panierSize count($panier);
  358.         return new JsonResponse(['panierSize' => $panierSize]);
  359.     }
  360.     #[Route('/panier/search'name'search_product'methods: ['GET'])]
  361.     public function searchProduct(Request $requestProduitRepository $produitRepositoryProduitStockRepository $produitStockRepository): JsonResponse
  362.     {
  363.         $query $request->query->get('q');
  364.         $this->logger->info('searchProduct appelée avec query:', ['query' => $query]);
  365.         
  366.         if (!$query) {
  367.             $this->logger->info('Query vide, retour vide');
  368.             return new JsonResponse(['success' => false'products' => [], 'search' => $query]);
  369.         }
  370.         // Utilise la recherche flexible optimisée
  371.         $produits $produitRepository->searchProduitsFlexible($query);
  372.         $this->logger->info('Produits trouvés:', ['count' => count($produits)]);
  373.         $resultats = [];
  374.         foreach ($produits as $produit) {
  375.             $quantiteDisponible $produitStockRepository->findTotalQuantityByProduit($produit);
  376.             $resultat = [
  377.                 'id' => $produit->getId(),
  378.                 'nom' => $produit->getNom(),
  379.                 'prixUnitaire' => $produit->getPrixUnitaire(),
  380.                 'quantiteDisponible' => $quantiteDisponible ?? 0
  381.             ];
  382.             $resultats[] = $resultat;
  383.             $this->logger->info('Produit ajouté au résultat:'$resultat);
  384.         }
  385.         $response = ['success' => true'products' => $resultats'search' => $query];
  386.         $this->logger->info('Réponse searchProduct:'$response);
  387.         
  388.         return new JsonResponse($response);
  389.     }
  390.     // CORRECTION PRINCIPALE : Modification de la méthode addProductAjax
  391.     #[Route('/panier/add-ajax'name'add_product_ajax'methods: ['POST'])]
  392.     public function addProductAjax(
  393.         Request $request,
  394.         SessionInterface $session,
  395.         ProduitRepository $produitRepository,
  396.         BoutiqueRepository $boutiqueRepository,
  397.         ProduitStockRepository $produitStockRepository
  398.     ): JsonResponse {
  399.         // Log d'entrée
  400.         error_log('=== DÉBUT addProductAjax ===');
  401.         error_log('Request content: ' $request->getContent());
  402.         error_log('Content-Type: ' $request->headers->get('Content-Type'));
  403.         try {
  404.             $data json_decode($request->getContent(), true);
  405.             error_log('Data décodé: ' json_encode($data));
  406.             $produitId $data['id'] ?? null;
  407.             $quantite = (int)($data['quantite'] ?? 1);
  408.             $prixVenteUnitaire = (float)($data['prixVenteUnitaire'] ?? 0);
  409.             error_log("Produit ID: $produitId, Quantité: $quantite, Prix: $prixVenteUnitaire");
  410.             if (!$produitId) {
  411.                 error_log('Erreur: ID produit manquant');
  412.                 return new JsonResponse(['success' => false'message' => 'ID produit manquant.'], 400);
  413.             }
  414.             $produit $produitRepository->find($produitId);
  415.             if (!$produit) {
  416.                 error_log('Erreur: Produit introuvable');
  417.                 return new JsonResponse(['success' => false'message' => 'Produit introuvable.'], 404);
  418.             }
  419.             if ($quantite 1) {
  420.                 error_log('Erreur: Quantité invalide');
  421.                 return new JsonResponse(['success' => false'message' => 'Quantité invalide.'], 400);
  422.             }
  423.             if ($prixVenteUnitaire 0) {
  424.                 error_log('Erreur: Prix de vente invalide');
  425.                 return new JsonResponse(['success' => false'message' => 'Prix de vente invalide.'], 400);
  426.             }
  427.             // Permettre les prix de vente à 0
  428.             // Si le prix n'est pas fourni (null ou undefined), utiliser le prix de base
  429.             // Mais permettre explicitement les prix de 0
  430.             if ($prixVenteUnitaire === null || $prixVenteUnitaire === && !isset($data['prixVenteUnitaire'])) {
  431.                 $prixVenteUnitaire $produit->getPrixUnitaire();
  432.                 error_log("Prix mis à jour: $prixVenteUnitaire");
  433.             }
  434.             $quantiteDisponible $produitStockRepository->findTotalQuantityByProduit($produit);
  435.             error_log("Quantité disponible: $quantiteDisponible");
  436.             $panier $session->get("panier", []);
  437.             error_log('Panier avant: ' json_encode($panier));
  438.             // Vérifier si le produit existe déjà dans le panier
  439.             if (isset($panier[$produitId])) {
  440.                 if (is_array($panier[$produitId])) {
  441.                     // Ajouter à la quantité existante
  442.                     $panier[$produitId]['quantite'] += $quantite;
  443.                     $panier[$produitId]['prixVenteUnitaire'] = $prixVenteUnitaire;
  444.                 } else {
  445.                     // Convertir l'ancienne structure en nouvelle
  446.                     $panier[$produitId] = [
  447.                         'quantite' => $panier[$produitId] + $quantite,
  448.                         'prixVenteUnitaire' => $prixVenteUnitaire
  449.                     ];
  450.                 }
  451.             } else {
  452.                 // Nouveau produit dans le panier
  453.                 $panier[$produitId] = [
  454.                     'quantite' => $quantite,
  455.                     'prixVenteUnitaire' => $prixVenteUnitaire
  456.                 ];
  457.             }
  458.             $session->set("panier"$panier);
  459.             error_log('Panier après: ' json_encode($panier));
  460.             $quantiteFinal $panier[$produitId]['quantite'];
  461.             $prixVenteFinal $panier[$produitId]['prixVenteUnitaire'];
  462.             $response = [
  463.                 'success' => true,
  464.                 'id' => $produit->getId(),
  465.                 'nom' => $produit->getNom(),
  466.                 'prixUnitaire' => $produit->getPrixUnitaire(),
  467.                 'prixVenteUnitaire' => $prixVenteFinal,
  468.                 'quantite' => $quantiteFinal,
  469.                 'total' => $prixVenteFinal $quantiteFinal,
  470.                 'quantiteDisponible' => $quantiteDisponible
  471.             ];
  472.             error_log('Réponse finale: ' json_encode($response));
  473.             error_log('=== FIN addProductAjax SUCCESS ===');
  474.             return new JsonResponse($response);
  475.         } catch (\Exception $e) {
  476.             error_log('=== EXCEPTION dans addProductAjax ===');
  477.             error_log('Exception: ' $e->getMessage());
  478.             error_log('Stack trace: ' $e->getTraceAsString());
  479.             return new JsonResponse([
  480.                 'success' => false,
  481.                 'message' => 'Erreur lors de l\'ajout : ' $e->getMessage()
  482.             ], 500);
  483.         }
  484.     }
  485.     #[Route('/panier/update-quantity'name'update_quantity'methods: ['POST'])]
  486.     public function updateQuantity(Request $requestSessionInterface $sessionProduitRepository $produitRepository): JsonResponse
  487.     {
  488.         try {
  489.             $data json_decode($request->getContent(), true);
  490.             $produitId $data['id'] ?? null;
  491.             $quantite = (int) ($data['quantite'] ?? 0);
  492.             if (!$produitId || $quantite 1) {
  493.                 return new JsonResponse(['success' => false'message' => 'Données invalides.'], 400);
  494.             }
  495.             $produit $produitRepository->find($produitId);
  496.             if (!$produit) {
  497.                 return new JsonResponse(['success' => false'message' => 'Produit introuvable.'], 404);
  498.             }
  499.             $panier $session->get("panier", []);
  500.             if (isset($panier[$produitId])) {
  501.                 if (is_array($panier[$produitId])) {
  502.                     $panier[$produitId]['quantite'] = $quantite;
  503.                 } else {
  504.                     $panier[$produitId] = [
  505.                         'quantite' => $quantite,
  506.                         'prixVenteUnitaire' => $produit->getPrixUnitaire()
  507.                     ];
  508.                 }
  509.             } else {
  510.                 $panier[$produitId] = [
  511.                     'quantite' => $quantite,
  512.                     'prixVenteUnitaire' => $produit->getPrixUnitaire()
  513.                 ];
  514.             }
  515.             $session->set("panier"$panier);
  516.             $prixUnitaire is_array($panier[$produitId])
  517.                 ? $panier[$produitId]['prixVenteUnitaire']
  518.                 : $produit->getPrixUnitaire();
  519.             return new JsonResponse([
  520.                 'success' => true,
  521.                 'id' => $produit->getId(),
  522.                 'quantite' => $quantite,
  523.                 'total' => $quantite $prixUnitaire
  524.             ]);
  525.         } catch (\Exception $e) {
  526.             return new JsonResponse([
  527.                 'success' => false,
  528.                 'message' => 'Erreur lors de la mise à jour : ' $e->getMessage()
  529.             ], 500);
  530.         }
  531.     }
  532.     #[Route('/panier/update-price'name'update_price'methods: ['POST'])]
  533.     public function updatePrice(Request $requestSessionInterface $sessionProduitRepository $produitRepository): JsonResponse
  534.     {
  535.         try {
  536.             $data json_decode($request->getContent(), true);
  537.             $produitId $data['id'];
  538.             $prixVenteUnitaire = (float)$data['prixVenteUnitaire'];
  539.             if ($prixVenteUnitaire 0) {
  540.                 return new JsonResponse(['success' => false'message' => 'Prix invalide.'], 400);
  541.             }
  542.             $panier $session->get('panier', []);
  543.             if (!isset($panier[$produitId])) {
  544.                 return new JsonResponse(['success' => false'message' => 'Produit non trouvé dans le panier'], 400);
  545.             }
  546.             // Convertir en tableau si nécessaire
  547.             if (!is_array($panier[$produitId])) {
  548.                 $panier[$produitId] = [
  549.                     'quantite' => $panier[$produitId],
  550.                     'prixVenteUnitaire' => $prixVenteUnitaire
  551.                 ];
  552.             } else {
  553.                 $panier[$produitId]['prixVenteUnitaire'] = $prixVenteUnitaire;
  554.             }
  555.             $session->set('panier'$panier);
  556.             return new JsonResponse([
  557.                 'success' => true,
  558.                 'prixVenteUnitaire' => $prixVenteUnitaire
  559.             ]);
  560.         } catch (\Exception $e) {
  561.             return new JsonResponse([
  562.                 'success' => false,
  563.                 'message' => 'Erreur lors de la mise à jour du prix : ' $e->getMessage()
  564.             ], 500);
  565.         }
  566.     }
  567.     #[Route('/panier/add-client-ajax'name'add_client_ajax'methods: ['POST'])]
  568.     public function addClientAjax(
  569.         Request $request,
  570.         EntityManagerInterface $entityManager
  571.     ): JsonResponse {
  572.         try {
  573.             error_log('=== [addClientAjax] Début appel ===');
  574.             $data json_decode($request->getContent(), true);
  575.             error_log('Payload reçu : ' json_encode($data));
  576.             $nom $data['nom'] ?? '';
  577.             $phone $data['phone'] ?? '';
  578.             $adresse $data['adresse'] ?? null;
  579.             if (empty($nom) || empty($phone)) {
  580.                 error_log('Erreur : nom ou téléphone manquant');
  581.                 return new JsonResponse(['success' => false'message' => 'Nom et téléphone requis.'], 400);
  582.             }
  583.             $client = new Client();
  584.             $client->setNom($nom);
  585.             $client->setPhone($phone);
  586.             $client->setAdresse($adresse);
  587.             $entityManager->persist($client);
  588.             $entityManager->flush();
  589.             error_log('Client ajouté avec succès, id=' $client->getId());
  590.             return new JsonResponse([
  591.                 'success' => true,
  592.                 'client' => [
  593.                     'id' => $client->getId(),
  594.                     'nom' => $client->getNom()
  595.                 ]
  596.             ]);
  597.         } catch (\Exception $e) {
  598.             error_log('Exception dans addClientAjax : ' $e->getMessage());
  599.             return new JsonResponse([
  600.                 'success' => false,
  601.                 'message' => 'Erreur lors de l\'ajout du client : ' $e->getMessage()
  602.             ], 500);
  603.         }
  604.     }
  605.     #[Route('/panier/delete-ajax/{id}'name'delete_ajax'methods: ['DELETE'])]
  606.     public function deleteAjax(
  607.         Produit $produit,
  608.         SessionInterface $session,
  609.         ProduitRepository $produitRepository
  610.     ): JsonResponse {
  611.         try {
  612.             // Récupérer le panier
  613.             $panier $session->get("panier", []);
  614.             $id $produit->getId();
  615.             // Supprimer le produit du panier
  616.             if (!empty($panier[$id])) {
  617.                 unset($panier[$id]);
  618.             }
  619.             // Recalculer le total
  620.             $total 0;
  621.             foreach ($panier as $produitId => $item) {
  622.                 $produitPanier $produitRepository->find($produitId);
  623.                 if ($produitPanier) {
  624.                     $quantite is_array($item) ? $item['quantite'] : $item;
  625.                     $prix is_array($item) && isset($item['prixVenteUnitaire'])
  626.                         ? $item['prixVenteUnitaire']
  627.                         : $produitPanier->getPrixUnitaire();
  628.                     $total += $prix $quantite;
  629.                 }
  630.             }
  631.             // Sauvegarder le panier mis à jour
  632.             $session->set("panier"$panier);
  633.             return new JsonResponse([
  634.                 'success' => true,
  635.                 'newTotal' => number_format($total2),
  636.                 'isEmpty' => empty($panier)
  637.             ]);
  638.         } catch (\Exception $e) {
  639.             return new JsonResponse([
  640.                 'success' => false,
  641.                 'message' => 'Erreur lors de la suppression : ' $e->getMessage()
  642.             ], 500);
  643.         }
  644.     }
  645.     /**
  646.      * Route AJAX pour récupérer les données d'une vente
  647.      */
  648.     #[Route('/panier/get-vente-data/{id}'name'get_vente_data_ajax'methods: ['GET'])]
  649.     public function getVenteDataAjax(
  650.         int $id,
  651.         EntityManagerInterface $entityManager
  652.     ): JsonResponse {
  653.         try {
  654.             $vente $entityManager->getRepository(Vente::class)->find($id);
  655.             if (!$vente) {
  656.                 return new JsonResponse(['success' => false'message' => 'Vente introuvable.'], 404);
  657.             }
  658.             $factures $vente->getFactures();
  659.             $facture $factures->count() > $factures->first() : null;
  660.             $venteData = [
  661.                 'id' => $vente->getId(),
  662.                 'client' => $vente->getClient() ? [
  663.                     'id' => $vente->getClient()->getId(),
  664.                     'nom' => $vente->getClient()->getNom()
  665.                 ] : null,
  666.                 'tva' => $facture $facture->getTva() : 0,
  667.                 'fraisTransport' => $facture $facture->getFraisTransport() : 0,
  668.                 'statutPaiement' => $facture $facture->getStatutPaiement() : 'Payé',
  669.                 'montantPaye' => $facture $facture->getMontantPaye() : 0,
  670.                 'montantTotal' => $facture $facture->getMontantTotal() : 0,
  671.             ];
  672.             // Récupérer les échéances
  673.             $echeances = [];
  674.             if ($facture) {
  675.                 foreach ($facture->getEcheance() as $echeance) {
  676.                     if ($echeance->getType() !== 'acompte') {
  677.                         $echeances[] = [
  678.                             'id' => $echeance->getId(),
  679.                             'amount' => $echeance->getAmount(),
  680.                             'date' => $echeance->getDate()->format('Y-m-d'),
  681.                             'isPaid' => $echeance->isIsPaid()
  682.                         ];
  683.                     }
  684.                 }
  685.             }
  686.             $venteData['echeances'] = $echeances;
  687.             return new JsonResponse(['success' => true'data' => $venteData]);
  688.         } catch (\Exception $e) {
  689.             return new JsonResponse([
  690.                 'success' => false,
  691.                 'message' => 'Erreur lors de la récupération des données : ' $e->getMessage()
  692.             ], 500);
  693.         }
  694.     }
  695.     #[Route('/vente/{id}/modifier'name'edit_vente')]
  696.     public function editVente(
  697.         int $id,
  698.         EntityManagerInterface $entityManager,
  699.         SessionInterface $session
  700.     ): Response {
  701.         $vente $entityManager->getRepository(Vente::class)->find($id);
  702.         if (!$vente) {
  703.             throw $this->createNotFoundException("Vente introuvable");
  704.         }
  705.         $panier = [];
  706.         foreach ($vente->getDetailsVentes() as $detail) {
  707.             $produit $detail->getProduit();
  708.             $panier[$produit->getId()] = [
  709.                 'quantite' => $detail->getQuantiteVendu(),
  710.                 'prixVenteUnitaire' => $detail->getPrixUnitaire()
  711.             ];
  712.         }
  713.         $session->set('panier'$panier);
  714.         $session->set('vente_en_modification'$vente->getId());
  715.         return $this->redirectToRoute('modifier_vente', ['id' => $vente->getId()]); // On affiche le panier existant
  716.     }
  717.     /**
  718.      * Prépare les données d'une vente pour le template
  719.      */
  720.     private function prepareVenteDataForTemplate(Vente $vente): array
  721.     {
  722.         $factures $vente->getFactures();
  723.         $facture $factures->count() > $factures->first() : null;
  724.         $echeances = [];
  725.         if ($facture) {
  726.             foreach ($facture->getEcheance() as $echeance) {
  727.                 if ($echeance->getType() !== 'acompte') {
  728.                     $echeances[] = [
  729.                         'id' => $echeance->getId(),
  730.                         'amount' => $echeance->getAmount(),
  731.                         'date' => $echeance->getDate()->format('Y-m-d'),
  732.                         'isPaid' => $echeance->isIsPaid()
  733.                     ];
  734.                 }
  735.             }
  736.         }
  737.         return [
  738.             'id' => $vente->getId(),
  739.             'client' => $vente->getClient() ? [
  740.                 'id' => $vente->getClient()->getId(),
  741.                 'nom' => $vente->getClient()->getNom()
  742.             ] : null,
  743.             'tva' => $facture $facture->getTva() : 0,
  744.             'fraisTransport' => $facture $facture->getFraisTransport() : 0,
  745.             'statutPaiement' => $facture $facture->getStatutPaiement() : 'Payé',
  746.             'montantPaye' => $facture $facture->getMontantPaye() : 0,
  747.             'echeances' => $echeances
  748.         ];
  749.     }
  750.     /**
  751.      * Route unifiée pour rediriger vers le panier en mode modification
  752.      */
  753.     #[Route('/panier/modifier-vente/{id}'name'modifier_vente'methods: ['GET'])]
  754.     public function modifierVente(
  755.         int $id,
  756.         SessionInterface $session,
  757.         EntityManagerInterface $entityManager
  758.     ): Response {
  759.         $vente $entityManager->getRepository(Vente::class)->find($id);
  760.         if (!$vente) {
  761.             $this->addFlash('error''Vente introuvable.');
  762.             return $this->redirectToRoute('app_vente_index');
  763.         }
  764.         $user $this->getUser();
  765.         if ($vente->getBoutique() !== $user->getBoutique()) {
  766.             $this->addFlash('error''Vous n\'avez pas l\'autorisation de modifier cette vente.');
  767.             return $this->redirectToRoute('app_vente_index');
  768.         }
  769.         // Vérifier si la vente peut être modifiée
  770.         if (!$this->peutModifierVente($vente)) {
  771.             $this->addFlash('error''Cette vente ne peut plus être modifiée.');
  772.             return $this->redirectToRoute('app_vente_index');
  773.         }
  774.         // Charger les données de la vente dans la session
  775.         $this->chargerDonneesVenteEnSession($vente$session);
  776.         $this->addFlash('info''Vente chargée pour modification. Vous pouvez maintenant modifier les produits et les informations.');
  777.         return $this->redirectToRoute('app_panier');
  778.     }
  779.     /**
  780.      * Charge les données d'une vente dans la session pour modification
  781.      */
  782.     private function chargerDonneesVenteEnSession(Vente $venteSessionInterface $session): void
  783.     {
  784.         // Log spécifique pour la vente 866
  785.         if ($vente->getId() === 866) {
  786.             $this->logger->info('=== DÉBUT chargerDonneesVenteEnSession pour vente 866 ===');
  787.         }
  788.         
  789.         // Charger les produits dans le panier
  790.         $panier = [];
  791.         foreach ($vente->getDetailsVentes() as $detail) {
  792.             $produit $detail->getProduit();
  793.             $panierItem = [
  794.                 'quantite' => $detail->getQuantiteVendu(),
  795.                 'prixVenteUnitaire' => $detail->getPrixUnitaire()
  796.             ];
  797.             $panier[$produit->getId()] = $panierItem;
  798.             
  799.             // Log spécifique pour la vente 866
  800.             if ($vente->getId() === 866) {
  801.                 $this->logger->info('Produit ajouté au panier:', [
  802.                     'produit_id' => $produit->getId(),
  803.                     'produit_nom' => $produit->getNom(),
  804.                     'produit_nom_length' => strlen($produit->getNom()),
  805.                     'produit_nom_json' => json_encode($produit->getNom()),
  806.                     'detail' => $panierItem
  807.                 ]);
  808.             }
  809.         }
  810.         
  811.         if ($vente->getId() === 866) {
  812.             $this->logger->info('Panier final pour vente 866:'$panier);
  813.         }
  814.         
  815.         $session->set('panier'$panier);
  816.         $session->set('vente_en_modification'$vente->getId());
  817.         // Préremplir les informations annexes
  818.         $factures $vente->getFactures();
  819.         $facture $factures->count() > $factures->first() : null;
  820.         $session->set('panier_client_id'$vente->getClient() ? $vente->getClient()->getId() : null);
  821.         $session->set('panier_tva'$facture $facture->getTva() : 0);
  822.         $session->set('panier_frais_transport'$facture $facture->getFraisTransport() : 0);
  823.         $session->set('panier_statut_paiement'$facture $facture->getStatutPaiement() : 'Payé');
  824.         $session->set('panier_montant_paye'$facture $facture->getMontantPaye() : 0);
  825.         // Charger les échéances (hors acomptes)
  826.         $echeances = [];
  827.         if ($facture) {
  828.             foreach ($facture->getEcheance() as $echeance) {
  829.                 if ($echeance->getType() !== 'acompte') {
  830.                     $echeances[] = [
  831.                         'id' => $echeance->getId(),
  832.                         'amount' => $echeance->getAmount(),
  833.                         'date' => $echeance->getDate()->format('Y-m-d'),
  834.                         'isPaid' => $echeance->isIsPaid()
  835.                     ];
  836.                 }
  837.             }
  838.         }
  839.         $session->set('panier_echeances'$echeances);
  840.     }
  841.     /**
  842.      * Méthode utilitaire pour vérifier si une vente peut être modifiée
  843.      */
  844.     private function peutModifierVente(Vente $vente): bool
  845.     {
  846.         // Vérifier si la vente n'est pas trop ancienne (ex: plus de 7 jours)
  847.         $dateLimit = new DateTime('-7 days');
  848.         if ($vente->getDateVente() < $dateLimit) {
  849.             return false;
  850.         }
  851.         // Vérifier si la vente n'a pas d'échéances payées (sauf acomptes)
  852.         $factures $vente->getFactures();
  853.         if ($factures->count() > 0) {
  854.             $facture $factures->first();
  855.             foreach ($facture->getEcheance() as $echeance) {
  856.                 if ($echeance->isIsPaid() && $echeance->getType() !== 'acompte') {
  857.                     return false// Ne peut pas modifier si des échéances sont payées
  858.                 }
  859.             }
  860.         }
  861.         return true;
  862.     }
  863.     /**
  864.      * Route AJAX pour traiter la modification d'une vente
  865.      */
  866.     #[Route('/panier/modifier-vente-ajax'name'modifier_vente_ajax'methods: ['POST'])]
  867.     public function modifierVentZeAjax(
  868.         SessionInterface $session,
  869.         EntityManagerInterface $entityManager,
  870.         ProduitRepository $produitRepository,
  871.         ProduitStockRepository $produitStockRepository,
  872.         Security $security,
  873.         Request $request
  874.     ): JsonResponse {
  875.         try {
  876.             $panier $session->get('panier', []);
  877.             if (empty($panier)) {
  878.                 return new JsonResponse(['success' => false'message' => 'Votre panier est vide.'], 400);
  879.             }
  880.             $venteId $session->get('vente_en_modification');
  881.             if (!$venteId) {
  882.                 return new JsonResponse(['success' => false'message' => 'Aucune vente en cours de modification.'], 400);
  883.             }
  884.             $user $security->getUser();
  885.             $boutique $user->getBoutique();
  886.             if (!$boutique) {
  887.                 return new JsonResponse(['success' => false'message' => 'Aucune boutique associée à votre compte.'], 400);
  888.             }
  889.             $data json_decode($request->getContent(), true);
  890.             $clientId $data['client_id'] ?? null;
  891.             $tva = (float)($data['tva'] ?? 0);
  892.             $fraisTransport = (float)($data['frais_transport'] ?? 0);
  893.             $statutPaiement $data['statut_paiement'] ?? 'Payé';
  894.             $echeancesData $data['echeances'] ?? [];
  895.             $acompteDirect = isset($data['acompte_direct']) ? (float)$data['acompte_direct'] : 0;
  896.             $dateVente $data['date_vente'] ?? null;
  897.             $entityManager->beginTransaction();
  898.             // Récupérer la vente originale
  899.             $venteOriginale $entityManager->getRepository(Vente::class)->find($venteId);
  900.             if (!$venteOriginale) {
  901.                 $entityManager->rollback();
  902.                 return new JsonResponse(['success' => false'message' => 'Vente originale introuvable.'], 404);
  903.             }
  904.             // Vérifier les permissions
  905.             if ($venteOriginale->getBoutique() !== $boutique) {
  906.                 $entityManager->rollback();
  907.                 return new JsonResponse(['success' => false'message' => 'Vous n\'avez pas l\'autorisation de modifier cette vente.'], 403);
  908.             }
  909.             // Vérifier si la vente peut être modifiée
  910.             if (!$this->peutModifierVente($venteOriginale)) {
  911.                 $entityManager->rollback();
  912.                 return new JsonResponse(['success' => false'message' => 'Cette vente ne peut plus être modifiée.'], 400);
  913.             }
  914.             // Étape 1: Annuler la vente originale (restaurer le stock)
  915.             $this->annulerVenteOriginale($venteOriginale$entityManager$produitStockRepository);
  916.             // Étape 2: Gérer la date de vente (permettre la modification si une nouvelle date est fournie)
  917.             if ($dateVente && !empty($dateVente)) {
  918.                 try {
  919.                     $nouvelleDate = new DateTime($dateVente);
  920.                     $venteOriginale->setDateVente($nouvelleDate);
  921.                 } catch (\Exception $e) {
  922.                     // Si la date est invalide, conserver la date originale
  923.                     // Pas de modification de la date
  924.                 }
  925.             }
  926.             // Si aucune date n'est fournie, la date originale est conservée
  927.             // Étape 3: Supprimer les anciens détails de vente
  928.             foreach ($venteOriginale->getDetailsVentes() as $ancienDetail) {
  929.                 $entityManager->remove($ancienDetail);
  930.             }
  931.             // Étape 4: Créer les nouveaux détails de vente et mettre à jour le stock
  932.             $montantTotal 0;
  933.             foreach ($panier as $produitId => $item) {
  934.                 $produit $produitRepository->find($produitId);
  935.                 if (!$produit) continue;
  936.                 $quantite is_array($item) ? ($item['quantite'] ?? 1) : (int)$item;
  937.                 $prixVenteUnitaire is_array($item) ? ($item['prixVenteUnitaire'] ?? $produit->getPrixUnitaire()) : $produit->getPrixUnitaire();
  938.                 // Mettre à jour le stock
  939.                 $stock $produitStockRepository->findOneBy(['produit' => $produit'boutique' => $boutique]) ?? (new ProduitStock())
  940.                     ->setProduit($produit)->setBoutique($boutique)->setQuantite(0);
  941.                 $stock->setQuantite($stock->getQuantite() - $quantite);
  942.                 $entityManager->persist($stock);
  943.                 // Créer une nouvelle sortie de stock
  944.                 $sortie = (new SortieStock())
  945.                     ->setProduit($produit)
  946.                     ->setQuantite($quantite)
  947.                     ->setDateSortie(new DateTime())
  948.                     ->setPrixVente($prixVenteUnitaire)
  949.                     ->setBoutique($boutique)
  950.                     ->setUtilisateur($user);
  951.                 $entityManager->persist($sortie);
  952.                 $prixTotal $quantite $prixVenteUnitaire;
  953.                 $montantTotal += $prixTotal;
  954.                 // Créer le nouveau détail de vente
  955.                 $detail = (new DetailsVente())
  956.                     ->setVente($venteOriginale)
  957.                     ->setProduit($produit)
  958.                     ->setQuantiteVendu($quantite)
  959.                     ->setPrixUnitaire($prixVenteUnitaire)
  960.                     ->setPrixTotal($prixTotal);
  961.                 $entityManager->persist($detail);
  962.             }
  963.             // Étape 5: Mettre à jour le client
  964.             $client $clientId $entityManager->getRepository(Client::class)->find($clientId) : null;
  965.             $venteOriginale->setClient($client);
  966.             // Étape 6: Mettre à jour ou créer la facture
  967.             $factures $venteOriginale->getFactures();
  968.             $facture $factures->count() > $factures->first() : null;
  969.             if (!$facture) {
  970.                 $facture = new Facture();
  971.                 $facture->setVente($venteOriginale);
  972.                 $facture->setCreatedAt(new DateTime());
  973.             }
  974.             $montantTotalFinal $montantTotal $fraisTransport;
  975.             $facture->setMontantTotal($montantTotal)
  976.                 ->setType('Facture')
  977.                 ->setTva($tva)
  978.                 ->setFraisTransport($fraisTransport)
  979.                 ->setStatutPaiement($statutPaiement);
  980.             if ($client) {
  981.                 $facture->setClient($client);
  982.             }
  983.             // Étape 7: Supprimer les anciennes échéances et recettes
  984.             foreach ($facture->getEcheance() as $ancienneEcheance) {
  985.                 $entityManager->remove($ancienneEcheance);
  986.             }
  987.             foreach ($venteOriginale->getRecettes() as $ancienneRecette) {
  988.                 $entityManager->remove($ancienneRecette);
  989.             }
  990.             // Étape 8: Traiter les nouveaux paiements
  991.             if ($statutPaiement === 'Payé') {
  992.                 $facture->setMontantPaye($montantTotalFinal);
  993.                 $entityManager->persist($facture);
  994.                 $entityManager->flush();
  995.                 $echeanceAcompte = new Echeance();
  996.                 $echeanceAcompte->setFacture($facture)
  997.                     ->setAmount($montantTotalFinal)
  998.                     ->setDate(new DateTime())
  999.                     ->setIsPaid(true)
  1000.                     ->setClient($client)
  1001.                     ->setVente($venteOriginale)
  1002.                     ->setType('acompte');
  1003.                 $entityManager->persist($echeanceAcompte);
  1004.                 $entityManager->flush();
  1005.                 $this->recetteService->creerRecetteDepuisVente($venteOriginale$montantTotalFinal$boutique);
  1006.             } elseif ($statutPaiement === 'Partiellement payé') {
  1007.                 $facture->setMontantPaye($acompteDirect);
  1008.                 $entityManager->persist($facture);
  1009.                 $entityManager->flush();
  1010.                 if ($acompteDirect 0) {
  1011.                     $echeanceAcompte = new Echeance();
  1012.                     $echeanceAcompte->setFacture($facture)
  1013.                         ->setAmount($acompteDirect)
  1014.                         ->setDate(new DateTime())
  1015.                         ->setIsPaid(true)
  1016.                         ->setClient($client)
  1017.                         ->setVente($venteOriginale)
  1018.                         ->setType('acompte');
  1019.                     $entityManager->persist($echeanceAcompte);
  1020.                     $this->recetteService->creerRecetteDepuisVente($venteOriginale$acompteDirect$boutique);
  1021.                 }
  1022.                 $resteAPayer $montantTotalFinal $acompteDirect;
  1023.                 $totalEcheances 0;
  1024.                 foreach ($echeancesData as $echeanceData) {
  1025.                     $echeanceAmount = (float)$echeanceData['amount'];
  1026.                     $totalEcheances += $echeanceAmount;
  1027.                     $echeance = new Echeance();
  1028.                     $echeance->setAmount($echeanceAmount)
  1029.                         ->setDate(new DateTime($echeanceData['date']))
  1030.                         ->setIsPaid(false)
  1031.                         ->setFacture($facture)
  1032.                         ->setClient($client)
  1033.                         ->setVente($venteOriginale)
  1034.                         ->setType('echeance');
  1035.                     $entityManager->persist($echeance);
  1036.                 }
  1037.                 if ($resteAPayer && $totalEcheances $resteAPayer) {
  1038.                     $echeance = new Echeance();
  1039.                     $echeance->setAmount($resteAPayer $totalEcheances)
  1040.                         ->setDate(new DateTime())
  1041.                         ->setIsPaid(false)
  1042.                         ->setFacture($facture)
  1043.                         ->setClient($client)
  1044.                         ->setVente($venteOriginale)
  1045.                         ->setType('echeance');
  1046.                     $entityManager->persist($echeance);
  1047.                 }
  1048.             } else {
  1049.                 $facture->setMontantPaye(0);
  1050.                 $entityManager->persist($facture);
  1051.                 $entityManager->flush();
  1052.             }
  1053.             $entityManager->flush();
  1054.             $entityManager->commit();
  1055.             // Nettoyer la session
  1056.             $session->set('panier', []);
  1057.             $session->remove('vente_en_modification');
  1058.             $this->nettoyerSessionPanier($session);
  1059.             return new JsonResponse([
  1060.                 'success' => true,
  1061.                 'message' => 'La vente a été modifiée avec succès.',
  1062.                 'factureId' => $facture->getId(),
  1063.                 'redirect' => $this->generateUrl('app_dashboard')
  1064.             ]);
  1065.         } catch (\Exception $e) {
  1066.             $entityManager->rollback();
  1067.             $this->logger->error('Erreur lors de la modification de vente', [
  1068.                 'error' => $e->getMessage(),
  1069.                 'trace' => $e->getTraceAsString()
  1070.             ]);
  1071.             return new JsonResponse([
  1072.                 'success' => false,
  1073.                 'message' => 'Erreur lors de la modification : ' $e->getMessage()
  1074.             ], 500);
  1075.         }
  1076.     }
  1077.     /**
  1078.      * Annule une vente originale en restaurant le stock
  1079.      */
  1080.     private function annulerVenteOriginale(
  1081.         Vente $vente,
  1082.         EntityManagerInterface $entityManager,
  1083.         ProduitStockRepository $produitStockRepository
  1084.     ): void {
  1085.         $boutique $vente->getBoutique();
  1086.         // Restaurer le stock pour chaque produit de la vente originale
  1087.         foreach ($vente->getDetailsVentes() as $detail) {
  1088.             $produit $detail->getProduit();
  1089.             $quantiteARestaurer $detail->getQuantiteVendu();
  1090.             // Trouver le stock du produit pour cette boutique
  1091.             $stock $produitStockRepository->findOneBy(['produit' => $produit'boutique' => $boutique]);
  1092.             if ($stock) {
  1093.                 $stock->setQuantite($stock->getQuantite() + $quantiteARestaurer);
  1094.                 $entityManager->persist($stock);
  1095.             } else {
  1096.                 // Si le stock n'existe pas, le créer avec la quantité restaurée
  1097.                 $nouveauStock = new ProduitStock();
  1098.                 $nouveauStock->setProduit($produit)
  1099.                     ->setBoutique($boutique)
  1100.                     ->setQuantite($quantiteARestaurer);
  1101.                 $entityManager->persist($nouveauStock);
  1102.             }
  1103.         }
  1104.     }
  1105.     /**
  1106.      * Nettoie toutes les données de session liées au panier
  1107.      */
  1108.     private function nettoyerSessionPanier(SessionInterface $session): void
  1109.     {
  1110.         $session->remove('panier_client_id');
  1111.         $session->remove('panier_tva');
  1112.         $session->remove('panier_frais_transport');
  1113.         $session->remove('panier_statut_paiement');
  1114.         $session->remove('panier_montant_paye');
  1115.         $session->remove('panier_echeances');
  1116.     }
  1117.     /**
  1118.      * Route pour annuler complètement une modification en cours
  1119.      */
  1120.     #[Route('/panier/annuler-modification'name'annuler_modification_ajax'methods: ['POST'])]
  1121.     public function annulerModification(SessionInterface $session): JsonResponse
  1122.     {
  1123.         try {
  1124.             $session->remove('panier');
  1125.             $session->remove('vente_en_modification');
  1126.             $this->nettoyerSessionPanier($session);
  1127.             return new JsonResponse([
  1128.                 'success' => true,
  1129.                 'message' => 'Modification annulée avec succès.',
  1130.                 'redirect' => $this->generateUrl('app_vente_index')
  1131.             ]);
  1132.         } catch (\Exception $e) {
  1133.             return new JsonResponse([
  1134.                 'success' => false,
  1135.                 'message' => 'Erreur lors de l\'annulation : ' $e->getMessage()
  1136.             ], 500);
  1137.         }
  1138.     }
  1139.     /**
  1140.      * Route pour vérifier si une vente peut être modifiée
  1141.      */
  1142.     #[Route('/panier/peut-modifier-vente/{id}'name'peut_modifier_vente_ajax'methods: ['GET'])]
  1143.     public function peutModifierVenteAjax(
  1144.         int $id,
  1145.         EntityManagerInterface $entityManager
  1146.     ): JsonResponse {
  1147.         try {
  1148.             $vente $entityManager->getRepository(Vente::class)->find($id);
  1149.             if (!$vente) {
  1150.                 return new JsonResponse(['success' => false'message' => 'Vente introuvable.'], 404);
  1151.             }
  1152.             $user $this->getUser();
  1153.             if ($vente->getBoutique() !== $user->getBoutique()) {
  1154.                 return new JsonResponse(['success' => false'message' => 'Vous n\'avez pas l\'autorisation.'], 403);
  1155.             }
  1156.             $peutModifier $this->peutModifierVente($vente);
  1157.             return new JsonResponse([
  1158.                 'success' => true,
  1159.                 'peutModifier' => $peutModifier,
  1160.                 'message' => $peutModifier 'Vente modifiable' 'Cette vente ne peut plus être modifiée'
  1161.             ]);
  1162.         } catch (\Exception $e) {
  1163.             return new JsonResponse([
  1164.                 'success' => false,
  1165.                 'message' => 'Erreur lors de la vérification : ' $e->getMessage()
  1166.             ], 500);
  1167.         }
  1168.     }
  1169. }