src/Controller/CartController.php line 2202

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\ActionShock;
  4. use App\Entity\AdditionalCompanyInformation;
  5. use App\Entity\Billings;
  6. use App\Entity\BrandDiscount;
  7. use App\Entity\Cart;
  8. use App\Entity\CartFinished;
  9. use App\Entity\ConfigSite;
  10. use App\Entity\CustomerControl;
  11. use App\Entity\DeliveryAddress;
  12. use App\Entity\Joker;
  13. use App\Entity\LotNumber;
  14. use App\Entity\Products;
  15. use App\Entity\Rebate;
  16. use App\Entity\SpecialCustomer;
  17. use App\Entity\SpecialDiscount;
  18. use App\Entity\User;
  19. use App\Service\JwtMercator;
  20. use Symfony\Component\Mime\Email;
  21. use Symfony\Component\Mailer\MailerInterface;
  22. use Doctrine\ORM\EntityManagerInterface;
  23. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\HttpFoundation\RedirectResponse;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\Routing\Annotation\Route;
  29. use Symfony\Contracts\HttpClient\HttpClientInterface;
  30. use TCPDF;
  31. class CartController extends AbstractController
  32. {
  33.     private EntityManagerInterface $manager;
  34.     private $client;
  35.     private $jwtMercator;
  36.     public function __construct(EntityManagerInterface $managerHttpClientInterface $clientJwtMercator $jwtMercator)
  37.     {
  38.         $this->manager $manager;
  39.         $this->client $client;
  40.         $this->jwtMercator $jwtMercator;
  41.     }
  42.     #[Route('/addtocart'name'addtocart'methods: ['GET''POST'])]
  43.     public function index(Request $request): Response
  44.     {
  45.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  46.         if (!$checkControl) {
  47.             $user $this->getUser();
  48.         } else {
  49.             $user $checkControl->getCustomer();
  50.         }
  51.         $information $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(['user' => $user]);
  52.         $exist_cart $this->manager->getRepository(Cart::class)->findOneBy(array('user' => $user'finish' => false), array('commandNumber' => 'DESC'));
  53.         if ($exist_cart) {
  54.             $exist_product $this->manager->getRepository(Cart::class)->findOneBy(array('user' => $user'finish' => false'products' => $this->manager->getRepository(Products::class)->find($request->get('id_product'))));
  55.             if ($exist_product) {
  56.                 $availableStock $this->manager->getRepository(LotNumber::class)->getProductAvailableStock($exist_product->getProducts()->getId(), $this->getMercatorFolder($information->getCompanyCountry()));
  57.                 $newQuantity $exist_product->getQuantity() + $request->get('quantity');
  58.                 if ($newQuantity $availableStock) {
  59.                     $newQuantity $availableStock;
  60.                 }
  61.                 $exist_product->setQuantity($newQuantity);
  62.                 $this->manager->flush();
  63.             } else {
  64.                 $cart = new Cart();
  65.                 $cart->setFinish(0);
  66.                 $cart->setCommandNumber($exist_cart->getCommandNumber());
  67.                 $cart->setProducts($this->manager->getRepository(Products::class)->find($request->get('id_product')));
  68.                 $cart->setUser($user);
  69.                 $cart->setQuantity($request->get('quantity'));
  70.                 $this->manager->persist($cart);
  71.                 $this->manager->flush();
  72.             }
  73.         } else {
  74.             $exist_cart_before $this->manager->getRepository(Cart::class)->findOneBy(array('user' => $user'finish' => true), array('commandNumber' => 'DESC'));
  75.             $cart = new Cart();
  76.             $cart->setFinish(0);
  77.             if ($exist_cart_before) {
  78.                 $cart->setCommandNumber($exist_cart_before->getCommandNumber() + 1);
  79.             } else {
  80.                 $cart->setCommandNumber(1);
  81.             }
  82.             $cart->setProducts($this->manager->getRepository(Products::class)->find($request->get('id_product')));
  83.             $cart->setUser($user);
  84.             $cart->setQuantity($request->get('quantity'));
  85.             $this->manager->persist($cart);
  86.             $this->manager->flush();
  87.         }
  88.         // Récupérer les informations pour la réponse JSON
  89.         $cartCounts $this->calculateCountItemInCart();
  90.         
  91.         // Calculer le pourcentage de remise en cours
  92.         $specialCustomer $this->manager->getRepository(SpecialCustomer::class)->findOneBy(array('user' => $user));
  93.         $pourcentSpecialCustomer null;
  94.         if ($specialCustomer) {
  95.             $pourcentSpecialCustomer $specialCustomer;
  96.         }
  97.         $rebatePercent $this->calculateRebate($pourcentSpecialCustomer);
  98.         
  99.         // Récupérer les informations sur les produits gratuits
  100.         $carts $this->retrieveCart();
  101.         $freeProductInfo $this->checkFreeProduct($carts);
  102.         
  103.         // Récupérer les informations de remises
  104.         $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  105.         $rebateInfo = [];
  106.         
  107.         foreach ($rebates as $rebate) {
  108.             $rebateInfo[] = [
  109.                 'min' => $rebate->getMin(),
  110.                 'percent' => $rebate->getPourcentRebate()
  111.             ];
  112.         }
  113.         
  114.         // Créer la réponse JSON avec toutes les informations nécessaires
  115.         return new JsonResponse([
  116.             'success' => true,
  117.             'cartCount' => $cartCounts['total'],
  118.             'cartCountEligible' => $cartCounts['eligible'],
  119.             'rebateInfo' => [
  120.                 'currentPercent' => $rebatePercent,
  121.                 'availableRebates' => $rebateInfo
  122.             ],
  123.             'freeProductInfo' => $freeProductInfo,
  124.             'message' => 'Produit ajouté au panier'
  125.         ]);
  126.     }
  127.     #[Route('/addtocartfromcart'name'addtocartfromcart')]
  128.     public function addToCartFromCart(Request $request): JsonResponse
  129.     {
  130.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  131.         if (!$checkControl) {
  132.             $user $this->getUser();
  133.         } else {
  134.             $user $checkControl->getCustomer();
  135.         }
  136.         $information $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(['user' => $user]);
  137.         $exist_cart $this->manager->getRepository(Cart::class)->findOneBy(array('user' => $user'finish' => false), array('commandNumber' => 'DESC'));
  138.         if ($exist_cart) {
  139.             $exist_product $this->manager->getRepository(Cart::class)->find($request->get('id_product'));
  140.             if (!$exist_product) {
  141.                 return new JsonResponse('Cart item not found');
  142.             }
  143.             if ($request->get('move') == "quantity-increase") {
  144.                 $availableStock $this->manager->getRepository(LotNumber::class)->getProductAvailableStock($exist_product->getProducts()->getId(), $this->getMercatorFolder($information->getCompanyCountry()));
  145.                 $newQuantity $exist_product->getQuantity() + 1;
  146.                 if ($newQuantity $availableStock) {
  147.                     $newQuantity $availableStock;
  148.                 }
  149.                 $exist_product->setQuantity($newQuantity);
  150.             } elseif ($request->get('move') == "quantity-decrease") {
  151.                 $exist_product->setQuantity($exist_product->getQuantity() - 1);
  152.             }
  153.             if ($exist_product->getQuantity() == 0) {
  154.                 $this->manager->remove($exist_product);
  155.             }
  156.             $this->manager->flush();
  157.         }
  158.         // Récupérer les informations pour la réponse JSON
  159.         $cartCounts $this->calculateCountItemInCart();
  160.         
  161.         // Calculer le pourcentage de remise en cours
  162.         $specialCustomer $this->manager->getRepository(SpecialCustomer::class)->findOneBy(array('user' => $user));
  163.         $pourcentSpecialCustomer null;
  164.         if ($specialCustomer) {
  165.             $pourcentSpecialCustomer $specialCustomer;
  166.         }
  167.         $rebatePercent $this->calculateRebate($pourcentSpecialCustomer);
  168.         
  169.         // Récupérer les informations de remises
  170.         $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  171.         $rebateInfo = [];
  172.         
  173.         foreach ($rebates as $rebate) {
  174.             $rebateInfo[] = [
  175.                 'min' => $rebate->getMin(),
  176.                 'percent' => $rebate->getPourcentRebate()
  177.             ];
  178.         }
  179.         return new JsonResponse([
  180.             'success' => true,
  181.             'cartCount' => $cartCounts['total'],
  182.             'cartCountEligible' => $cartCounts['eligible'],
  183.             'rebateInfo' => [
  184.                 'currentPercent' => $rebatePercent,
  185.                 'availableRebates' => $rebateInfo
  186.             ]
  187.         ]);
  188.     }
  189.     #[Route('/cart'name'cartView')]
  190.     public function cartView(JwtMercator $jwtMercatorRequest $request): Response
  191.     {
  192.         if (!$this->getUser()) {
  193.             return $this->redirectToRoute('app_login');
  194.         }
  195.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  196.         if (!$checkControl) {
  197.             $user $this->getUser();
  198.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $this->getUser()));
  199.         } else {
  200.             $user $checkControl->getCustomer();
  201.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $checkControl->getCustomer()));
  202.         }
  203.         $costDelivery $this->manager->getRepository(ConfigSite::class)->findAll();
  204.         $costDelivery $costDelivery[0];
  205.         $informations $this->manager->getRepository(User::class)->getAllInformationsForAccount($user);
  206.         if ($additionalInformations != null && $additionalInformations->getGeneralCondition()) {
  207.             $cart $this->retrieveCart();
  208.         } else {
  209.             $this->addFlash('danger''Veuillez remplir votre pays & accepter les conditions générales de ventes!');
  210.             return $this->redirectToRoute('profile');
  211.         }
  212.         //            $this->applyDiscountOnNewRefItems($cart);
  213.         $faReference = ['NMJ007''NMJ008''NMJ011''NMJ084''NMJ013''NMJ056''NMJ040'];
  214.         $countFaInCart 0;
  215.         foreach ($cart['carts'] as $item) {
  216.             if (isset($item['reference'])) {
  217.                 if (in_array($item['reference'], $faReference)) {
  218.                     $countFaInCart += $item[0]['quantity'];
  219.                 }
  220.             }
  221.         }
  222.         $numberOfFaRef null;
  223.         if ($countFaInCart >= 2) {
  224.             $numberOfFaRef floor($countFaInCart 2);
  225.         }
  226.         $flyersProduct $this->manager->getRepository(Products::class)->findOneBy(array('reference' => 'NMZZ21'));
  227.         $deliveryAdress $this->manager->getRepository(DeliveryAddress::class)->findOneBy(array('user' => $user));
  228.         //            if ($additionalInformations->getCompanyAddress() != null && $additionalInformations->getCompanyPostal() != null && $additionalInformations->getCompanyCountry() &&
  229.         //                $additionalInformations->getTownAddressCompany() != null)
  230.         if (!$deliveryAdress) {
  231.             $this->addFlash('danger''Veuillez ajouter une adresse de livraison! "Bouton bleu gestion des adresses"');
  232.             return $this->redirectToRoute('profile');
  233.         }
  234.         $address $this->manager->getRepository(DeliveryAddress::class)->findBy(array('user' => $user));
  235.         $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  236.         $specificRebates $this->manager->getRepository(Rebate::class)->getActiveSpecificRebates();
  237.         $brandDiscounts $this->manager->getRepository(BrandDiscount::class)->findBy(['active' => true]);
  238.         // Gestion de la préférence flyers via session
  239.         if (!$request->getSession()->has('wantFlyers')) {
  240.             $request->getSession()->set('wantFlyers'true);
  241.         }
  242.         $wantFlyers $request->getSession()->get('wantFlyers');
  243.         // Si l'utilisateur ne veut pas de flyers, on n'ajoute pas de flyers
  244.         if (!$wantFlyers) {
  245.             $numberOfFaRef null;
  246.         }
  247.         return $this->render('cart/index.html.twig', [
  248.             'shockActionTypes' => ActionShock::TYPES,
  249.             'informations' => $informations,
  250.             'carts' => $cart['carts'],
  251.             'rebate' => $cart['rebate'],
  252.             'grandTotalPrice' => $cart['grandTotalPrice'],
  253.             'freeProduct' => $cart['freeProduct'],
  254.             'freeTransport' => $cart['freeTransport'],
  255.             'joker' => $cart['joker'],
  256.             'user' => $this->getUser(),
  257.             'address' => $address,
  258.             'jokerNumber' => $this->checkJokerNumber(),
  259.             'missingItems' => $cart['missingItems'],
  260.             'costDelivery' => $costDelivery->getCostDelivery(),
  261.             'phFlyers' => (int)$numberOfFaRef,
  262.             'countFaInCart' => $countFaInCart,
  263.             'flyersProduct' => $flyersProduct,
  264.             'wantFlyers' => $wantFlyers,
  265.             'rebates' => array_reverse($rebates),
  266.             'specificRebates' => array_reverse($specificRebates),
  267.             // TODO update twig template
  268.             'brandDiscounts' => $brandDiscounts
  269.         ]);
  270.     }
  271.     private function applyDiscountOnNewRefItems(&$cart)
  272.     {
  273.         // Parcourez chaque élément du panier directement.
  274.         foreach ($cart as $key => &$item) {
  275.             // Vérifiez si l'élément est un tableau et a une propriété 'newRef' à true.
  276.             if (is_array($item) && isset($item['newRef']) && $item['newRef']) {
  277.                 // Vérifiez si 'PriceRebateInclude' existe et appliquez la réduction de 2%.
  278.                 if (isset($item['PriceRebateInclude'])) {
  279.                     // Calcul de la réduction de 2%.
  280.                     $discountAmount $item['PriceRebateInclude'] * 0.02;
  281.                     // Calculez le nouveau prix après réduction et stockez-le dans 'PriceRebateIncludeWith2pourcent'.
  282.                     $item['PriceRebateIncludeWith2pourcent'] = $item['PriceRebateInclude'] - $discountAmount;
  283.                 }
  284.             }
  285.         }
  286.         unset($item); // Détruisez la référence sur le dernier élément pour éviter les problèmes potentiels.
  287.         // Pas besoin de retourner $cart car il est modifié par référence.
  288.     }
  289.     #[Route('/cart/send'name'cartSend')]
  290.     public function sendCart(Request $requestMailerInterface $mailerJwtMercator $jwtMercator): RedirectResponse|JsonResponse
  291.     {
  292.         $orderRecipientEmails $this->getParameter('app.order.emails');
  293.         if (!is_array($orderRecipientEmails)) {
  294.             $orderRecipientEmails explode(','$orderRecipientEmails);
  295.         }
  296.         $orderRecipientEmails array_values(array_filter(array_map('trim'$orderRecipientEmails)));
  297.         $lotNumbersFromRequest $request->request->all('lotNumbers');
  298.         if (!is_array($lotNumbersFromRequest)) {
  299.             $lotNumbersFromRequest $lotNumbersFromRequest !== null && $lotNumbersFromRequest !== '' ? [(string) $lotNumbersFromRequest] : [];
  300.         }
  301.         $lotNumbersFromRequest array_values(array_filter(array_map(
  302.             static fn ($lot) => trim(strip_tags((string) $lot)),
  303.             $lotNumbersFromRequest
  304.         )));
  305.         $defaultRebatePercent floatval($request->request->get('rebatePourcent'));
  306.         $costDelivery $this->manager->getRepository(ConfigSite::class)->findAll();
  307.         $costDelivery $costDelivery[0];
  308.         if ($request->request->get('idUser') === null) {
  309.             return $this->redirectToRoute('app_login');
  310.         }
  311.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  312.         if ($checkControl != null) {
  313.             $user $checkControl->getCustomer();
  314.         } else {
  315.             $user $this->manager->getRepository(User::class)->find($request->request->get('idUser'));
  316.         }
  317.         $commandId = (int) $request->request->get('idCommande');
  318.         $existingOrder $this->manager->getRepository(CartFinished::class)->findOneBy([
  319.             'user' => $user,
  320.             'commandId' => $commandId,
  321.         ]);
  322.         if ($existingOrder !== null) {
  323.             return new JsonResponse('ok'headers: ['Content-Type' => 'application/json;charset=UTF-8']);
  324.         }
  325.         $additionalInformation $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  326.         $country $additionalInformation->getCompanyCountry();
  327.         $saleField $country === 'Belgique' 'p.saleBe' 'p.saleFr';
  328.         $folderMercatorValue $this->getMercatorFolder($country);
  329.         $countryShip $this->getCountryCode($country);
  330.         $pourcentSpecialCustomer null;
  331.         if ($user->getSpecialCustomer()) {
  332.             $checkUserSpecialEntity $this->manager->getRepository(SpecialCustomer::class)->findOneBy(array('user' => $user'active' => true));
  333.             if ($checkUserSpecialEntity == null) {
  334.                 $pourcentSpecialCustomer $this->manager->getRepository(ConfigSite::class)->findAll();
  335.                 $pourcentSpecialCustomer $pourcentSpecialCustomer[0];
  336.             } else {
  337.                 $pourcentSpecialCustomer $checkUserSpecialEntity->getPourcentRebate();
  338.             }
  339.         }
  340.         $articles $this->manager->getRepository(Cart::class)->getCurrentCart($user$commandId);
  341.         $articles array_values(array_filter($articles, static fn ($item) => !$item->getFinish()));
  342.         if (empty($articles)) {
  343.             return new JsonResponse(['error' => 'empty_cart'], JsonResponse::HTTP_BAD_REQUEST);
  344.         }
  345.         $jwtToken $this->jwtMercator->getToken();
  346.         // Récupérer l'historique local des commandes pour détecter les nouvelles références
  347.         // L'API Mercator /OrderHistory retourne des IDs incompatibles avec idProductOnMercator
  348.         $finishedCartItems $this->manager->getRepository(Cart::class)->findBy([
  349.             'user' => $user,
  350.             'finish' => true
  351.         ]);
  352.         
  353.         $localHistoryProductIds = [];
  354.         foreach ($finishedCartItems as $cartItem) {
  355.             $productItem $cartItem->getProducts();
  356.             if ($productItem) {
  357.                 $localHistoryProductIds[] = $productItem->getIdProductOnMercator();
  358.             }
  359.         }
  360.         $localHistoryProductIds array_unique($localHistoryProductIds);
  361.         $addressDelivery $this->manager->getRepository(DeliveryAddress::class)->find($request->request->get('deliveryAddress'));
  362.         $cart $this->manager->getRepository(Cart::class)->getAllCart($user);
  363.         $freeProducts $this->checkFreeProduct($cart);
  364.         // Calculer le total de produits dans le panier pour la règle des 2% nouvelles références
  365.         $totalCartQuantity 0;
  366.         foreach ($articles as $article) {
  367.             $totalCartQuantity += $article->getQuantity();
  368.         }
  369.         $specificRebates =  $this->manager->getRepository(Rebate::class)->getActiveSpecificRebates();
  370.         if ($additionalInformation->getCompanyCountry() == 'Belgique') {
  371.             $tvaLow 6;
  372.             $tvaHigh 21;
  373.         } elseif ($additionalInformation->getCompanyCountry() == 'France') {
  374.             $tvaLow 5.5;
  375.             $tvaHigh 20;
  376.         } elseif ($additionalInformation->getCompanyCountry() == 'Luxembourg') {
  377.             $tvaLow 3;
  378.             $tvaHigh 17;
  379.         } else {
  380.             $tvaLow 6;
  381.             $tvaHigh 21;
  382.         }
  383.         // Tableau associatif pour faire correspondre les noms des jours en anglais avec leur équivalent en français
  384.         $daysMapping = [
  385.             'monday' => 'Lu',
  386.             'tuesday' => 'Ma',
  387.             'wednesday' => 'Me',
  388.             'thursday' => 'Je',
  389.             'friday' => 'Ve',
  390.             'saturday' => 'Sa',
  391.             'sunday' => 'Di',
  392.         ];
  393.         // Obtenir les jours en français avec les deux premières lettres où la valeur est true
  394.         $daysTrue = [];
  395.         foreach ($daysMapping as $dayEnglish => $dayFrench) {
  396.             if ($addressDelivery->{'get' ucfirst($dayEnglish)}() === true) {
  397.                 $morningStart $addressDelivery->{'getMorning' ucfirst($dayEnglish)}();
  398.                 $morningEnd $addressDelivery->{'getMorning' ucfirst($dayEnglish) . 'End'}();
  399.                 $afternoonStart $addressDelivery->{'getAfternoon' ucfirst($dayEnglish)}();
  400.                 $afternoonEnd $addressDelivery->{'getAfternoon' ucfirst($dayEnglish) . 'End'}();
  401.                 $daysTrue[$dayFrench] = [
  402.                     'morning' => [
  403.                         'start' => $morningStart instanceof \DateTimeInterface $morningStart->format('H:i') : '',
  404.                         'end' => $morningEnd instanceof \DateTimeInterface $morningEnd->format('H:i') : '',
  405.                     ],
  406.                     'afternoon' => [
  407.                         'start' => $afternoonStart instanceof \DateTimeInterface $afternoonStart->format('H:i') : '',
  408.                         'end' => $afternoonEnd instanceof \DateTimeInterface $afternoonEnd->format('H:i') : '',
  409.                     ],
  410.                 ];
  411.             }
  412.         }
  413.         if ($additionalInformation->getCompanyName() === null || $additionalInformation->getTva() === null || $addressDelivery->getAddress() === null) {
  414.             $this->addFlash('danger''Veuillez complèter votre profil pour continuer.');
  415.             return new JsonResponse('profile'headers: ['Content-Type' => 'application/json;charset=UTF-8']);
  416.         }
  417.         $cartFinished = new CartFinished();
  418.         if (!$checkControl) {
  419.             $cartFinished->setUser($user);
  420.         } else {
  421.             $cartFinished->setUser($checkControl->getCustomer());
  422.         }
  423.         $cartFinished->setCommandId($commandId);
  424.         $cartFinished->setTotalPriceWithNoReduction(floatval($request->request->get('total-price-tvac-no-reduct')));
  425.         $deliveryAddressDb $this->manager->getRepository(DeliveryAddress::class)->find($request->request->get('deliveryAddress'));
  426.         $cartFinished->setDeliveryAddress($deliveryAddressDb);
  427.         if ($request->request->get('addressCheck') == "true") {
  428.             $cartFinished->setDeliveryBillingAddress($deliveryAddressDb);
  429.             $addressBilling $deliveryAddressDb;
  430.         } else {
  431.             $addressBilling $this->manager->getRepository(DeliveryAddress::class)->find($request->request->get('deliveryBilling'));
  432.             $cartFinished->setDeliveryBillingAddress($addressBilling);
  433.         }
  434.         if ($request->request->get('totalPriceProductLowVatHtva') != 0) {
  435.             $cartFinished->setTotalPriceProductLowVatHtva($request->request->get('totalPriceProductLowVatHtva'));
  436.             $cartFinished->setTotalPriceProductLowVatTvac($request->request->get('totalPriceProductLowVatTvac'));
  437.         } else {
  438.             $cartFinished->setTotalPriceProductLowVatHtva(0);
  439.             $cartFinished->setTotalPriceProductLowVatTvac(0);
  440.         }
  441.         if ($request->request->get('totalPriceProductHighVatHtva') != 0) {
  442.             $cartFinished->setTotalPriceProductHighVatHtva($request->request->get('totalPriceProductHighVatHtva'));
  443.             $cartFinished->setTotalPriceProductHighVatTvac($request->request->get('totalPriceProductHighVatTvac'));
  444.         } else {
  445.             $cartFinished->setTotalPriceProductHighVatHtva(0);
  446.             $cartFinished->setTotalPriceProductHighVatTvac(0);
  447.         }
  448.         $cartFinished->setTotalPriceAllProductHtva($request->request->get('totalPriceAllProductHtva'));
  449.         $cartFinished->setTotalPriceTvacWithRebate($request->request->get('totalPriceTvacWithRebate'));
  450.         $cartFinished->setTotalRebate(floatval($request->request->get('totalRemise')));
  451.         $cartFinished->setPourcentRebate($defaultRebatePercent);
  452.         $cartFinished->setTotalPaid(floatval($request->request->get('totalPriceWithVat')));
  453.         $dateFormatted date('d-m-Y'); // Récupère la date du jour formatée "d-m-Y"
  454.         $dateObject \DateTime::createFromFormat('d-m-Y'$dateFormatted);
  455.         $cartFinished->setCommandDate($dateObject);
  456.         if ($request->request->get('joker') == "true") {
  457.             $joker $this->manager->getRepository(Joker::class)->findOneBy(array('user' => $user));
  458.             if ($joker !== null && $joker->getNumberJoker() > 0) {
  459.                 $joker->setNumberJoker($joker->getNumberJoker() - 1);
  460.                 $cartFinished->setUsedJoker(1);
  461.                 $jokerFact 'Oui';
  462.             } else {
  463.                 $cartFinished->setUsedJoker(0);
  464.                 $jokerFact 'Non';
  465.             }
  466.         } else {
  467.             $cartFinished->setUsedJoker(0);
  468.             $jokerFact 'Non';
  469.         }
  470.         $jokerUsedForDelivery = ($jokerFact === 'Oui');
  471.         $duplicateOrder $this->manager->getRepository(CartFinished::class)->findOneBy([
  472.             'user' => $user,
  473.             'commandId' => $commandId,
  474.         ]);
  475.         if ($duplicateOrder !== null) {
  476.             return new JsonResponse('ok'headers: ['Content-Type' => 'application/json;charset=UTF-8']);
  477.         }
  478.         $this->manager->persist($cartFinished);
  479.         foreach ($articles as $item) {
  480.             $item->setFinish(true);
  481.         }
  482.         $this->manager->flush();
  483.         $imagePath $this->getParameter('kernel.project_dir') . '/public/uploads/img/media/logo-naturamedicatrix-pro-v2.png';
  484.         // Création du PDF
  485.         $pdf = new TCPDF('P''mm''A4'true'UTF-8'false);
  486.         $pdf->SetTitle('Facture');
  487.         $pdf->SetPrintHeader(false);
  488.         $pdf->setPrintFooter(false);
  489.         $pdf->SetMargins(101010);
  490.         $pdf->AddPage();
  491.         // Ajout de l'image centrée en haut
  492.         $pdf->Image($imagePath800500'PNG''''C');
  493.         // Ajout des adresses de livraison et de facturation
  494.         $pdf->SetFont('helvetica''B'12);
  495.         $pdf->Ln(5); // Espace après l'image
  496.         $companyName $addressDelivery->getCompanyName();
  497.         // Adresse de livraison
  498.         $pdf->SetFont('helvetica''B'12);
  499.         $pdf->Cell(13010'Adresse de livraison'00'L');
  500.         $pdf->Cell(8010'Adresse de facturation'00'L');
  501.         $pdf->Ln(5);
  502.         $pdf->SetFont('helvetica'''12);
  503.         $deliveryAddress $addressDelivery->getFirstName() . ' ' $addressDelivery->getName();
  504.         $billingAddress $addressBilling->getFirstName() . ' ' $addressBilling->getName();
  505.         $pdf->Cell(13010$companyName00'L');
  506.         $pdf->Cell(8010$companyName00'L');
  507.         $pdf->Ln(5);
  508.         $pdf->Cell(13010$deliveryAddress00'L');
  509.         $pdf->Cell(8010$billingAddress00'L');
  510.         $pdf->Ln(5);
  511.         $pdf->SetFont('helvetica'''12);
  512.         $pdf->Cell(13010$addressDelivery->getAddress(), 00'L');
  513.         $pdf->Cell(8010$addressBilling->getAddress(), 00'L');
  514.         $pdf->Ln(5);
  515.         $pdf->SetFont('helvetica'''12);
  516.         $pdf->Cell(13010$addressDelivery->getTown() . ' ' $addressDelivery->getPostal() . ' ' $addressDelivery->getCountry(), 00'L');
  517.         $pdf->Cell(8010$addressBilling->getTown() . ' ' $addressDelivery->getPostal() . ' ' $addressBilling->getCountry(), 00'L');
  518.         $pdf->Ln(5);
  519.         //                $pdf->SetFont('helvetica', '', 12);
  520.         //                $pdf->Cell(130, 10, "", 0, 0, 'L');
  521.         //                $pdf->Cell(80, 10, 'TVA : ' . $additionalInformation->getTva(), 0, 0, 'L');
  522.         $pdf->Ln(5);
  523.         if ($daysTrue != null) {
  524.             $pdf->SetFont('helvetica'''12);
  525.             $pdf->Cell(1305"Préférences jours et heures de livraison :"00'L');
  526.             //                $pdf->Cell(80, 10, 'Jour livraison : ' . implode(', ', $daysTrue), 0, 0, 'L');
  527.             //                $pdf->Cell(80, 10, 'Jour livraison : ', 0, 0, 'L');
  528.             $pdf->Ln(5);
  529.             foreach ($daysTrue as $day => $hours) {
  530.                 $morningHours $hours['morning']['start'] . ' - ' $hours['morning']['end'];
  531.                 $afternoonHours $hours['afternoon']['start'] . ' - ' $hours['afternoon']['end'];
  532.                 $pdf->Cell(05$day ': Matin ' $morningHours ', Après-midi ' $afternoonHours01'L');
  533.             }
  534.             $pdf->Ln(3);
  535.         }
  536.         // Ajout du nom du client et numéro de commande
  537.         $pdf->SetFont('helvetica'''12);
  538.         //                $pdf->Ln(10); // Espace après les adresses
  539.         $pdf->Cell(1405'Client: ' $additionalInformation->getCompanyName(), 00'L');
  540.         $pdf->Cell(505'Date commande: ' $cartFinished->getCommandDate()->format('d/m/Y'), 00'L');
  541.         $pdf->Ln(5);
  542.         //                $pdf->Cell(0, 10, 'Date commande: ' . $cartFinished->getCommandDate()->format('d/m/Y'), 0, 1, 'L');
  543.         if ($defaultRebatePercent != 0) {
  544.             $pdf->Cell(05'Votre commande avec votre remise de: ' $defaultRebatePercent "%"01'L');
  545.         }
  546.         $pdf->Cell(705'Joker utilisé ? : ' $jokerFact00'L');
  547.         if ($request->get('messageNewRef') != "") {
  548.             $pdf->Ln(5);
  549.             $pdf->MultiCell(05'Nouvelle référence ? : ' $request->get('messageNewRef'), 0'L');
  550.             //                    $pdf->Cell(70, 5, 'Nouvelle référence ? : ' . $request->get('messageNewRef'), 0, 0, 'L');
  551.         }
  552.         // Ajout du tableau des articles
  553.         $pdf->SetFont('helvetica'''7);
  554.         $pdf->Ln(10); // Espace après le titre
  555.         $pdf->Cell(505'Article'10'C');
  556.         $pdf->Cell(305'n° lot'10'C');
  557.         $pdf->Cell(305'Prix professionnel remisé'10'C');
  558.         $pdf->Cell(205'Quantité'10'C');
  559.         $pdf->Cell(305'Remise augmentée'10'C');
  560.         $pdf->Cell(305'Sous-total(HTVA)'10'C');
  561.         $pdf->SetFont('helvetica'''10);
  562.         $pdf->Ln(5);
  563.         $productInfo = [];
  564.         $faReference = ['NMJ007''NMJ008''NMJ011''NMJ084''NMJ013''NMJ056''NMJ040'];
  565.         $countFaInCart 0;
  566.         foreach ($articles as $item) {
  567.             if ($item->getProducts()->getReference() != null) {
  568.                 if (in_array($item->getProducts()->getReference(), $faReference) && $item->getQuantity() > 1) {
  569.                     $countFaInCart += $item->getQuantity();
  570.                 }
  571.             }
  572.         }
  573.         $numberOfFaRef null;
  574.         if ($countFaInCart >= 2) {
  575.             $numberOfFaRef floor($countFaInCart 2);
  576.         }
  577.         $flyersProduct $this->manager->getRepository(Products::class)->findOneBy(array('reference' => 'NMZZ21'));
  578.         $excludedProducts $this->manager->getRepository(Rebate::class)->getExcludedProducts();
  579.         $lotsOrderedQuantity = [];
  580.         foreach ($articles as $article) {
  581.             $shockActions $article->getProducts()->getActionShockCalendar();
  582.             $shockAction null;
  583.             if (count($shockActions) > 0) {
  584.                 $shockAction $shockActions[0];
  585.             }
  586.             $shockActionPercent $shockAction $shockAction->getDiscountPercent() : 0;
  587.             $shockActionType $shockAction $shockAction->getType() : null;
  588.             $rebatePercent $defaultRebatePercent;
  589.             
  590.             // Vérifier d'abord si le produit a une promotion spéciale
  591.             $checkSpecialDiscount $this->manager->getRepository(SpecialDiscount::class)->findOneBy(array('product' => $article->getProducts()));
  592.             
  593.             // Si le produit est dans la liste des exclus mais qu'il a une promotion spéciale
  594.             // et que sa quantité est inférieure à 12, on le laisse bénéficier de la remise globale
  595.             if (in_array($article->getProducts()->getReference(), $excludedProducts) && 
  596.                 !($checkSpecialDiscount != null && $article->getQuantity() < 12)) {
  597.                 $rebatePercent 0;
  598.             }
  599.             foreach ($specificRebates as $rebate) {
  600.                 if (in_array($article->getProducts()->getReference(), $rebate->getSpecificProducts()) && $article->getQuantity() >= $rebate->getMin()) {
  601.                     $rebatePercent $rebate->getPourcentRebate();
  602.                     break;
  603.                 }
  604.             }
  605.             $productIdMercator $article->getProducts()->getIdProductOnMercator();
  606.             // Déterminez si c'est une newRef (basé sur l'historique local)
  607.             $isNewRef = !in_array($productIdMercator$localHistoryProductIds);
  608.             $productName $article->getProducts()->getName();
  609.             $quantity $article->getQuantity();
  610.             $productInfo[$productName]['noLot'] = null;
  611.             $lotss = [];
  612.             foreach ($lotNumbersFromRequest as $lot) {
  613.                 $checkArticle $this->manager->getRepository(LotNumber::class)->findBy(array('lotNumber' => $lot'folderMercator' => $folderMercatorValue));
  614.                 $lotss[] = $checkArticle;
  615.             }
  616.             foreach ($lotss as $lots) {
  617.                 foreach ($lots as $lot) {
  618.                     if ($lot->getProducts()->getName() == $article->getProducts()->getName()) {
  619.                         $productInfo[$productName]['noLot'] = $lot->getLotNumber();
  620.                         if (!in_array($lot->getDepot(), LotNumber::EXCLUDED_DEPOTS)) {
  621.                             $lotsOrderedQuantity[] = [
  622.                                 'product' => $article->getProducts(),
  623.                                 'lot_number' => $lot->getLotNumber(),
  624.                                 'ordered_quantity' => $article->getQuantity(),
  625.                                 'depot' => $lot->getDepot(),
  626.                                 'folderMercator' => $folderMercatorValue,
  627.                             ];
  628.                         }
  629.                     }
  630.                 }
  631.             }
  632.             if (empty($productInfo[$productName]['noLot'])) {
  633.                 $dlus $this->manager->getRepository(LotNumber::class)->findBy([
  634.                     'products' => $article->getProducts(),
  635.                     'folderMercator' => $folderMercatorValue,
  636.                 ]);
  637.                 $maxDlu null;
  638.                 foreach ($dlus as $dlu) {
  639.                     $dluDate $dlu->getDlu();
  640.                     if ($maxDlu === null || ($dluDate !== null && $dluDate $maxDlu)) {
  641.                         $maxDlu $dluDate;
  642.                         $productInfo[$productName]['noLot'] = $dlu->getLotNumber();
  643.                     }
  644.                 }
  645.             }
  646.             if ($checkSpecialDiscount != null && $article->getQuantity() > 11) {
  647.                 $productPrice $article->getProducts()->getPublicPrice();
  648.             } else {
  649.                 $productPrice $article->getProducts()->getPrice();
  650.                 $checkSpecialDiscount null;
  651.             }
  652.             $productBrandName $article->getProducts()->getBrand();
  653.             $pourcentNatura null// Initialisation par défaut
  654.             
  655.             if (strcasecmp($productBrandName'NaturaMedicatrix') === 0) {
  656.                 // Pour les nouvelles références, appliquer la remise NaturaMedicatrix même si rebatePercent = 0
  657.                 $effectiveRebateForNatura $rebatePercent;
  658.                 if ($isNewRef && $quantity >= && $rebatePercent == 0) {
  659.                     $effectiveRebateForNatura 13;
  660.                 }
  661.                 
  662.                 if ($effectiveRebateForNatura >= 19) {
  663.                     $pourcentNatura $this->manager->getRepository(BrandDiscount::class)->findOneBy(array('brandName' => 'NaturaMedicatrix'));
  664.                     $pourcentNatura $pourcentNatura->getRebateHeigh();
  665.                 } elseif ($effectiveRebateForNatura >= 13) {
  666.                     $pourcentNatura $this->manager->getRepository(BrandDiscount::class)->findOneBy(array('brandName' => 'NaturaMedicatrix'));
  667.                     $pourcentNatura $pourcentNatura->getRebateLow();
  668.                 }
  669.             }
  670.             if ($request->get('messageNewRef') != "") {
  671.                 $article->setMessage($request->get('messageNewRef'));
  672.                 $this->manager->flush();
  673.             }
  674.             $actionShock $article->getProducts()->getActionShock();
  675.             $productInfo[$productName] = [
  676.                 'brand' => $productBrandName,
  677.                 'price' => $productPrice,
  678.                 'quantity' => $quantity,
  679.                 'noLot' => $productInfo[$productName]['noLot'] ?? '',
  680.                 'reference' => $article->getProducts()->getReference(),
  681.                 'specialDiscount' => $checkSpecialDiscount,
  682.                 'rebatePercent' => $rebatePercent,
  683.                 'newRef' => $isNewRef,
  684.                 'actionShock' => $actionShock,
  685.                 'shockActionPercent' => $shockActionPercent,
  686.                 'shockActionType' => $shockActionType,
  687.             ];
  688.             $priceTotal $productPrice $quantity;
  689.             $minQuantity $isNewRef 4;
  690.             // Pour les nouvelles références avec qté >= 2, appliquer 13% même si defaultRebatePercent = 0
  691.             $effectiveRebatePercent $rebatePercent;
  692.             if ($isNewRef && $quantity >= && $rebatePercent == 0) {
  693.                 $effectiveRebatePercent 13;
  694.             }
  695.             // Priorité 1 : NaturaMedicatrix (vérifier en premier)
  696.             // DEBUG - Stocker les valeurs pour affichage
  697.             if (strcasecmp($productInfo[$productName]['brand'], 'NaturaMedicatrix') === && $isNewRef) {
  698.                 $productInfo[$productName]['debug'] = "effectiveRebate=$effectiveRebatePercent, qty=$quantity, minQty=$minQuantity, pourcentNatura=$pourcentNatura, specialDiscount=" . ($checkSpecialDiscount 'NOT NULL' 'NULL') . ", actionShock=" . ($productInfo[$productName]['actionShock'] ? 'true' 'false');
  699.             }
  700.             
  701.             if ($effectiveRebatePercent != && $effectiveRebatePercent != null && !$productInfo[$productName]['actionShock'] && $quantity >= $minQuantity && $checkSpecialDiscount == null && strcasecmp($productInfo[$productName]['brand'], 'NaturaMedicatrix') === 0) {
  702.                 $totalRebate floatval(number_format(($priceTotal 100) * ($pourcentNatura +  (($isNewRef && $totalCartQuantity >= 12) ? 0)), 2'.'''));
  703.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($priceTotal $totalRebate2'.'''));
  704.             } 
  705.             // Priorité 2 : Autres marques
  706.             elseif ($effectiveRebatePercent != && $effectiveRebatePercent != null && !$productInfo[$productName]['actionShock'] && $quantity >= $minQuantity && $checkSpecialDiscount == null && $pourcentNatura == null) {
  707.                 $totalRebate floatval(number_format(($priceTotal 100) * ($effectiveRebatePercent + (($isNewRef && $totalCartQuantity >= 12) ? 0)), 2'.'''));
  708.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($priceTotal $totalRebate2'.'''));
  709.             } 
  710.             // Priorité 3 : Action choc
  711.             elseif ($effectiveRebatePercent != && $effectiveRebatePercent != null && $productInfo[$productName]['actionShock'] && $quantity >= $minQuantity) {
  712.                 $totalRebate floatval(number_format(($priceTotal 100) * ($shockActionPercent + (($isNewRef && $totalCartQuantity >= 12) ? 0)), 2'.'''));
  713.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($priceTotal $totalRebate2'.'''));
  714.             } elseif ($productInfo[$productName]['actionShock'] && $quantity >= $minQuantity) {
  715.                 $totalRebate floatval(number_format(($priceTotal 100) * ($shockActionPercent + (($isNewRef && $totalCartQuantity >= 12) ? 0)), 2'.'''));
  716.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($priceTotal $totalRebate2'.'''));
  717.             } elseif (!$productInfo[$productName]['actionShock'] && $quantity 11 && $checkSpecialDiscount != null) {
  718.                 $totalRebate floatval(number_format(($priceTotal 100) * ($checkSpecialDiscount->getPourcentRebate() + (($isNewRef && $totalCartQuantity >= 12) ? 0)), 2'.'''));
  719.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($priceTotal $totalRebate2'.'''));
  720.             } elseif ($quantity && $isNewRef && $totalCartQuantity >= 12) {
  721.                 $productInfo[$productName]['priceTotal'] = $productPrice $quantity;
  722.                 $rebateNewRef floatval(number_format(($productInfo[$productName]['priceTotal'] / 100) * 22'.'''));
  723.                 $productInfo[$productName]['priceTotal'] = floatval(number_format($productInfo[$productName]['priceTotal'] - $rebateNewRef2'.'''));
  724.             } else {
  725.                 $productInfo[$productName]['priceTotal'] = $productPrice $quantity;
  726.             }
  727.             // Vérifier s'il y a des produits gratuits correspondant au produit actuel
  728.             foreach ($freeProducts as $freeProduct) {
  729.                 if ($freeProduct['name'] === $productName) {
  730.                     $productInfo[$productName]['free'] = $freeProduct['free'];
  731.                 }
  732.             }
  733.         }
  734.         foreach ($lotsOrderedQuantity as $ordered) {
  735.             $lotNumber $this->manager->getRepository(LotNumber::class)->findOneBy([
  736.                 'products' => $ordered['product'],
  737.                 'lotNumber' => $ordered['lot_number'],
  738.                 'depot' => $ordered['depot'],
  739.                 'folderMercator' => $ordered['folderMercator'],
  740.             ]);
  741.             if (!$lotNumber) {
  742.                 continue;
  743.             }
  744.             $lotNumber->setStock($lotNumber->getStock() - $ordered['ordered_quantity']);
  745.             $this->manager->persist($lotNumber);
  746.         }
  747.         $this->manager->flush();
  748.         // Calcul du nombre de flyers (Formule Alcalisante)
  749.         $numberOfFaRef null;
  750.         $countFaInCart 0;
  751.         $flyersProduct $this->manager->getRepository(Products::class)->findOneBy(['reference' => 'NMZZ21']);
  752.         
  753.         // Tableau des références de produits Formule Alcalisante
  754.         $faReference = ['NMJ007''NMJ008''NMJ011''NMJ084''NMJ013''NMJ056''NMJ040'];
  755.         
  756.         // Compte le nombre de Formule Alcalisante
  757.         foreach ($articles as $item) {
  758.             if (in_array($item->getProducts()->getReference(), $faReference)) {
  759.                 $countFaInCart += $item->getQuantity();
  760.             }
  761.         }
  762.         
  763.         // Calcul du nombre de flyers gratuits (1 par 2 FA)
  764.         if ($countFaInCart >= 2) {
  765.             $numberOfFaRef floor($countFaInCart 2);
  766.         }
  767.         
  768.         // Respect de la préférence utilisateur
  769.         if (!$request->getSession()->has('wantFlyers') || !$request->getSession()->get('wantFlyers')) {
  770.             $numberOfFaRef null;
  771.         }
  772.         $shockActionTypes ActionShock::TYPES;
  773.         // Fonction helper pour calculer la hauteur nécessaire pour un texte multicell
  774.         $getMultiCellHeight = function($pdf$w$txt) {
  775.             $cw $pdf->GetStringWidth($txt);
  776.             if ($cw == 0) {
  777.                 return 0;
  778.             }
  779.             
  780.             $ratio $w $cw;
  781.             $lines ceil($ratio);
  782.             return $lines 5// 5 est la hauteur de base d'une ligne
  783.         };
  784.         
  785.         // Affiche les informations dans le PDF
  786.         foreach ($productInfo as $productName => $info) {
  787.             $productRebatePercent $info['rebatePercent'];
  788.             if ($info['actionShock']) {
  789.                 $shockActionName $shockActionTypes[$info['shockActionType']->value];
  790.             }
  791.             $freeQuantity = isset($info['free']) ? (int) $info['free'] : 0;
  792.             if ($info['actionShock']) {
  793.                     $pdf->SetFont('helvetica'''7);
  794.                     $productText $productName ' (' $info['reference'] . ') - ' $shockActionName;
  795.                     
  796.                     // Commence une nouvelle ligne
  797.                     $pdf->Ln(0);
  798.                     $x $pdf->GetX();
  799.                     $y $pdf->GetY();
  800.                     
  801.                     // Calcule la hauteur nécessaire pour le texte
  802.                     $height max(5$getMultiCellHeight($pdf50$productText));
  803.                     
  804.                     // Cellule pour le nom du produit avec référence
  805.                     $pdf->MultiCell(50$height$productText1'C');
  806.                     
  807.                     // Repositionne pour les cellules suivantes
  808.                     $pdf->SetXY($x 50$y);
  809.             } elseif (!$info['actionShock'] && $info['specialDiscount'] != null && $info['quantity'] > 11) {
  810.                     $pdf->SetFont('helvetica'''7);
  811.                     $productText $productName ' (' $info['reference'] . ') - ' 'PROMO « SPECIALE »';
  812.                     
  813.                     // Commence une nouvelle ligne
  814.                     $pdf->Ln(0);
  815.                     $x $pdf->GetX();
  816.                     $y $pdf->GetY();
  817.                     
  818.                     // Calcule la hauteur nécessaire pour le texte
  819.                     $height max(5$getMultiCellHeight($pdf50$productText));
  820.                     
  821.                     // Cellule pour le nom du produit avec référence
  822.                     $pdf->MultiCell(50$height$productText1'C');
  823.                     
  824.                     // Repositionne pour les cellules suivantes
  825.                     $pdf->SetXY($x 50$y);
  826.             } else {
  827.                     $pdf->SetFont('helvetica'''7);
  828.                     $productText $productName ' (' $info['reference'] . ')';
  829.                     
  830.                     // Commence une nouvelle ligne
  831.                     $pdf->Ln(0);
  832.                     $x $pdf->GetX();
  833.                     $y $pdf->GetY();
  834.                     
  835.                     // Calcule la hauteur nécessaire pour le texte
  836.                     $height max(5$getMultiCellHeight($pdf50$productText));
  837.                     
  838.                     // Cellule pour le nom du produit avec référence
  839.                     $pdf->MultiCell(50$height$productText1'C');
  840.                     
  841.                     // Repositionne pour les cellules suivantes
  842.                     $pdf->SetXY($x 50$y);
  843.             }
  844.             // Utilise la même hauteur pour les cellules suivantes
  845.             $pdf->Cell(30$height"lot : " $info['noLot'], 10'C');
  846.             $pdf->Cell(30$height$info['price'] . "€"10'C');
  847.             $pdf->Cell(20$height$info['quantity'], 10'C');
  848.             $minQuantityPdf $info['newRef'] ? 4;
  849.             
  850.             if ($info['specialDiscount'] != null && $info['quantity'] > 11) {
  851.                 $pdf->Cell(30$height$info['specialDiscount']->getPourcentRebate() . '%'10'C');
  852.             } elseif ($info['actionShock'] != null && $info['shockActionPercent']) {
  853.                 $pdf->Cell(30$height, ($info['shockActionPercent'] + (($info['newRef'] && $totalCartQuantity >= 12) ? 0)) . '%'10'C');
  854.             } elseif (strcasecmp($info['brand'], 'NaturaMedicatrix') === && $info['quantity'] >= $minQuantityPdf && $productRebatePercent != 0) {
  855.                 $rebateNatura $this->manager->getRepository(BrandDiscount::class)->findOneBy(array('active' => true'brandName' => 'NaturaMedicatrix'));
  856.                 if ($rebateNatura && $productRebatePercent == 13) {
  857.                     if ($info['newRef'] && $totalCartQuantity >= 12) {
  858.                         $pdf->Cell(30$height$rebateNatura->getRebateLow() + '%'10'C');
  859.                     } else {
  860.                         $pdf->Cell(30$height$rebateNatura->getRebateLow() . '%'10'C');
  861.                     }
  862.                 } elseif ($rebateNatura && $productRebatePercent == 19) {
  863.                     if ($info['newRef'] && $totalCartQuantity >= 12) {
  864.                         $pdf->Cell(30$height$rebateNatura->getRebateHeigh() + '%'10'C');
  865.                     } else {
  866.                         $pdf->Cell(30$height$rebateNatura->getRebateHeigh() . '%'10'C');
  867.                     }
  868.                 } else {
  869.                     $pdf->Cell(30$height'0' '%'10'C');
  870.                 }
  871.             } elseif ($productRebatePercent != && $info['quantity'] >= $minQuantityPdf) {
  872.                 if ($info['newRef'] && $totalCartQuantity >= 12) {
  873.                     $pdf->Cell(30$height$productRebatePercent '%'10'C');
  874.                 } else {
  875.                     $pdf->Cell(30$height$productRebatePercent '%'10'C');
  876.                 }
  877.             } elseif ($info['newRef'] && $info['quantity'] > && $totalCartQuantity >= 12) {
  878.                 $pdf->Cell(30$height'2%'10'C');
  879.             } else {
  880.                 $pdf->Cell(30$height'0'10'C');
  881.             }
  882.             // Calculer le pourcentage total pour l'affichage
  883.             $totalDiscountPercent 0;
  884.             if ($info['specialDiscount'] != null && $info['quantity'] > 11) {
  885.                 $totalDiscountPercent $info['specialDiscount']->getPourcentRebate();
  886.             } elseif ($info['actionShock'] != null && $info['shockActionPercent']) {
  887.                 $totalDiscountPercent $info['shockActionPercent'] + (($info['newRef'] && $totalCartQuantity >= 12) ? 0);
  888.             } elseif (strcasecmp($info['brand'], 'NaturaMedicatrix') === && $info['quantity'] >= $minQuantityPdf && $productRebatePercent != 0) {
  889.                 $rebateNatura $this->manager->getRepository(BrandDiscount::class)->findOneBy(array('active' => true'brandName' => 'NaturaMedicatrix'));
  890.                 if ($rebateNatura && $productRebatePercent == 13) {
  891.                     $totalDiscountPercent $rebateNatura->getRebateLow() + (($info['newRef'] && $totalCartQuantity >= 12) ? 0);
  892.                 } elseif ($rebateNatura && $productRebatePercent == 19) {
  893.                     $totalDiscountPercent $rebateNatura->getRebateHeigh() + (($info['newRef'] && $totalCartQuantity >= 12) ? 0);
  894.                 }
  895.             } elseif ($productRebatePercent != && $info['quantity'] >= $minQuantityPdf) {
  896.                 $totalDiscountPercent $productRebatePercent + (($info['newRef'] && $totalCartQuantity >= 12) ? 0);
  897.             } elseif ($info['newRef'] && $info['quantity'] > && $totalCartQuantity >= 12) {
  898.                 $totalDiscountPercent 2;
  899.             }
  900.             
  901.             if ($totalDiscountPercent 0) {
  902.                 $pdf->Cell(30$height$info['priceTotal'] . "€ (-" $totalDiscountPercent "% inclus)"10'C');
  903.                 $pdf->Ln($height);
  904.             } else {
  905.                 $pdf->Cell(30$height$info['priceTotal'] . "€"10'C');
  906.                 $pdf->Ln($height);
  907.             }
  908.             if ($freeQuantity 0) {
  909.                 $pdf->SetFont('helvetica'''7);
  910.                 $productTextFree $productName ' (' $info['reference'] . ') - OFFERT';
  911.                 $xFree $pdf->GetX();
  912.                 $yFree $pdf->GetY();
  913.                 $heightFree max(5$getMultiCellHeight($pdf50$productTextFree));
  914.                 $pdf->MultiCell(50$heightFree$productTextFree1'C');
  915.                 $pdf->SetXY($xFree 50$yFree);
  916.                 $pdf->Cell(30$heightFree"lot : " $info['noLot'], 10'C');
  917.                 $pdf->Cell(30$heightFree"0€"10'C');
  918.                 $pdf->Cell(20$heightFree$freeQuantity10'C');
  919.                 $pdf->Cell(30$heightFree"0"10'C');
  920.                 $pdf->Cell(30$heightFree"0€"10'C');
  921.                 $pdf->Ln($heightFree);
  922.             }
  923.         }
  924.         if ((int)$numberOfFaRef && $flyersProduct !== null) {
  925.             $pdf->SetFont('helvetica'''7);
  926.             $pdf->Cell(505$flyersProduct->getName(), 10'C');
  927.             $pdf->Cell(305"ref : " $flyersProduct->getReference(), 10'C');
  928.             $pdf->Cell(305"0 €"10'C');
  929.             $pdf->Cell(205, (int)$numberOfFaRef10'C');
  930.         }
  931.         $pdf->Ln(5);
  932.         $pdf->Cell(1405""00'C');
  933.         $pdf->Cell(505'Produits HTVA ' $tvaLow '%: ' floatval(number_format($request->request->get('totalPriceProductLowVatHtva'), 2'.''')) . "€"10'L');
  934.         $pdf->Ln(5);
  935.         $pdf->Cell(1405""00'C');
  936.         $pdf->Cell(505'Produits HTVA ' $tvaHigh '%: ' floatval(number_format($request->request->get('totalPriceProductHighVatHtva'), 2'.''')) . "€"10'L');
  937.         $pdf->Ln(5);
  938.         $pdf->Cell(1405""00'C');
  939.         $pdf->Cell(505'Total (HTVA): ' floatval(number_format($request->request->get('totalPriceAllProductHtva'), 2'.''')) . "€"10'L');
  940.         $pdf->Ln(5);
  941.         $pdf->Cell(1405""00'C');
  942.         $pdf->Cell(505'TVA ' $tvaLow '%: ' floatval(number_format($request->request->get('totalPriceProductLowVatTvac'), 2'.''')) . "€"10'L');
  943.         $pdf->Ln(5);
  944.         $pdf->Cell(1405""00'C');
  945.         $pdf->Cell(505'TVA ' $tvaHigh '%: ' floatval(number_format($request->request->get('totalPriceProductHighVatTvac'), 2'.''')) . "€"10'L');
  946.         $pdf->Ln(5);
  947.         if ($jokerUsedForDelivery) {
  948.             $pdf->Cell(1405""00'C');
  949.             $pdf->Cell(505'Frais livraison : GRATUIT (Joker)'10'L');
  950.             $pdf->Ln(5);
  951.             $pdf->SetFont('helvetica''B'12);
  952.             $pdf->Cell(1405'Total TTC'10'C');
  953.             $pdf->Cell(505floatval(number_format($request->request->get('totalPriceTvacWithRebate'), 2'.''')) . "€"11'C');
  954.         } elseif (floatval($request->request->get('totalPriceTvacWithRebate')) < floatval($costDelivery->getFrancoDelivery())) {
  955.             $pdf->Cell(1405""00'C');
  956.             $pdf->Cell(505'Frais livraison : ' $costDelivery->getCostDelivery() . '€'10'L');
  957.             $pdf->Ln(5);
  958.             // Ajout du total
  959.             $totalCost $request->request->get('totalPriceTvacWithRebate') + $costDelivery->getCostDelivery();
  960.             $pdf->SetFont('helvetica''B'12);
  961.             $pdf->Cell(1405'Total TTC'10'C');
  962.             $pdf->Cell(505$totalCost "€"11'C');
  963.         } else {
  964.             $pdf->Cell(1405""00'C');
  965.             $pdf->Cell(505'Frais livraison : GRATUIT (Franco 300€)'10'L');
  966.             $pdf->Ln(5);
  967.             // Ajout du total
  968.             $pdf->SetFont('helvetica''B'12);
  969.             $pdf->Cell(1405'Total TTC'10'C');
  970.             $pdf->Cell(505floatval(number_format($request->request->get('totalPriceTvacWithRebate'), 2'.''')) . "€"11'C');
  971.         }
  972.         // Juste avant de sauvegarder ou de sortir le PDF, ajoutez votre pied de page
  973.         $pdf->SetY(-55); // Positionner le curseur 60 mm du bas de la page
  974.         $pdf->SetFont('helvetica'''10);
  975.         $pdf->SetFont('helvetica''B'10); // Gras pour NATURAMedicatrix
  976.         $pdf->Cell(05"NATURAMedicatrix srl"01'C');
  977.         $pdf->SetFont('helvetica'''10); // Normal pour le reste de l'adresse
  978.         $pdf->Cell(05"22, route de Fagnes, B-4190 Ferrières, Belgique"01'C');
  979.         $pdf->Cell(05"TVA BE 0543.862.766"01'C');
  980.         $pdf->Ln(4); // Un peu d'espace avant la prochaine section
  981.         $pdf->SetFont('helvetica''B'10); // Gras pour NATURAMedicatrix
  982.         $pdf->Cell(05"NATURAMedicatrix sàrl"01'C');
  983.         $pdf->SetFont('helvetica'''10); // Normal pour le reste de l'adresse
  984.         $pdf->Cell(05"8 Hannert dem Duarref, L-9772 Troine (Wincrange), Luxembourg -"01'C');
  985.         $pdf->Cell(05"TVA: LU26788281 - MATRICULE:20142414296"01'C');
  986.         // Nom du fichier PDF
  987.         $pdfFilename trim($additionalInformation->getCompanyName()) . '_' trim($user->getId()) . '_' trim(str_replace(' '''$request->request->get('idCommande'))) . '.pdf';
  988.         // Enregistrement du PDF dans le dossier public/factures/
  989.         $pdfPath $this->getParameter('factures_directory') . $pdfFilename;
  990.         $facturesDirectory $this->getParameter('factures_directory');
  991.         if (!is_dir($facturesDirectory)) {
  992.             mkdir($facturesDirectory0755true);
  993.         }
  994.         $pdf->Output($pdfPath'F');
  995.         $this->createNewBilling($pdfFilename$user$request->request->get('totalPriceTvacWithRebate'), $cartFinished->getCommandDate()->format('d/m/Y'));
  996.         // E-mail du client
  997.         $clientEmail $user->getEmail();
  998.         $email = (new Email())
  999.             ->from('info@b2bnaturamedicatrix.com')
  1000.             ->to($clientEmail)
  1001.             ->subject('Bon de commande B2B')
  1002.             ->text('Veuillez trouver ci-joint le bon de commande en pdf.')
  1003.             ->attachFromPath($pdfPath$pdfFilename'application/pdf');
  1004.         foreach ($orderRecipientEmails as $recipient) {
  1005.             $email->addCc($recipient);
  1006.         }
  1007.         $mailer->send($email);
  1008.         // ========== ENVOI COMMANDE VERS API MERCATOR ==========
  1009.         $this->sendOrderToMercator(
  1010.             $jwtToken,
  1011.             $additionalInformation,
  1012.             $folderMercatorValue,
  1013.             $countryShip,
  1014.             $addressDelivery,
  1015.             $articles,
  1016.             $productInfo,
  1017.             $defaultRebatePercent,
  1018.             $pourcentSpecialCustomer,
  1019.             $cartFinished,
  1020.             $lotNumbersFromRequest,
  1021.             $jokerUsedForDelivery,
  1022.             floatval($request->request->get('totalPriceTvacWithRebate')),
  1023.             floatval($costDelivery->getFrancoDelivery()),
  1024.             floatval($costDelivery->getCostDelivery()),
  1025.             $totalCartQuantity
  1026.         );
  1027.         $this->addFlash('success''Commande passée avec succès.');
  1028.         return new JsonResponse('ok'headers: ['Content-Type' => 'application/json;charset=UTF-8']);
  1029.     }
  1030.     /**
  1031.      * Envoie la commande vers l'API Mercator /Order
  1032.      */
  1033.     private function sendOrderToMercator(
  1034.         string $jwtToken,
  1035.         AdditionalCompanyInformation $additionalInformation,
  1036.         string $folderMercatorValue,
  1037.         string $countryShip,
  1038.         DeliveryAddress $addressDelivery,
  1039.         array $articles,
  1040.         array $productInfo,
  1041.         float $defaultRebatePercent,
  1042.         $pourcentSpecialCustomer,
  1043.         CartFinished $cartFinished,
  1044.         array $lotNumbers,
  1045.         bool $jokerUsedForDelivery false,
  1046.         float $totalPriceTvac 0,
  1047.         float $francoDelivery 300,
  1048.         float $costDelivery 15,
  1049.         int $totalCartQuantity 0
  1050.     ): void {
  1051.         // Validation : le client doit avoir un ID Mercator
  1052.         if (empty($additionalInformation->getIdClientMercator())) {
  1053.             error_log("Erreur envoi Mercator: Client sans ID Mercator - User ID: " $additionalInformation->getUser()->getId());
  1054.             return;
  1055.         }
  1056.         try {
  1057.             // Construction de la requête
  1058.             $data = [
  1059.                 'headers' => [
  1060.                     'Authorization' => 'Bearer ' $jwtToken,
  1061.                 ],
  1062.                 'json' => [
  1063.                     "customerId" => $additionalInformation->getIdClientMercator(),
  1064.                     "reference" => "B2B-" $cartFinished->getCommandId(),
  1065.                     "mercator" => $folderMercatorValue,
  1066.                     "shippingInfo" => [
  1067.                         "firstName" => $addressDelivery->getFirstName() ?? "",
  1068.                         "lastName" => $addressDelivery->getName() ?? "",
  1069.                         "company" => $addressDelivery->getCompanyName() ?? "",
  1070.                         "address1" => $addressDelivery->getAddress() ?? "",
  1071.                         "address2" => "",
  1072.                         "postalCode" => $addressDelivery->getPostal() ?? "",
  1073.                         "city" => $addressDelivery->getTown() ?? "",
  1074.                         "phone" => $addressDelivery->getPhoneNumber() ?? "",
  1075.                         "mobile" => "",
  1076.                         "country" => $countryShip,
  1077.                         "note" => "Commande B2B #" $cartFinished->getCommandId()
  1078.                     ],
  1079.                     "lines" => []
  1080.                 ]
  1081.             ];
  1082.             // Récupération des lots pour ce dossier Mercator
  1083.             // On utilise $productInfo qui contient déjà le bon noLot par produit (résolu via matching par nom)
  1084.             $lotsMap = [];
  1085.             foreach ($articles as $articleForLot) {
  1086.                 $productForLot $articleForLot->getProducts();
  1087.                 $productNameForLot $productForLot->getName();
  1088.                 if (isset($productInfo[$productNameForLot]['noLot']) && !empty($productInfo[$productNameForLot]['noLot'])) {
  1089.                     $lot $this->manager->getRepository(LotNumber::class)->findOneBy([
  1090.                         'products' => $productForLot,
  1091.                         'lotNumber' => $productInfo[$productNameForLot]['noLot'],
  1092.                         'folderMercator' => $folderMercatorValue
  1093.                     ]);
  1094.                     if ($lot !== null) {
  1095.                         $lotsMap[$productNameForLot] = $lot;
  1096.                     }
  1097.                 }
  1098.             }
  1099.             $freeLinesAdded = [];
  1100.             // Construction des lignes de commande
  1101.             foreach ($articles as $article) {
  1102.                 $product $article->getProducts();
  1103.                 $productName $product->getName();
  1104.                 // Validation : le produit doit avoir un ID Mercator
  1105.                 if (empty($product->getIdProductOnMercator())) {
  1106.                     error_log("Erreur envoi Mercator: Produit sans ID Mercator - " $productName);
  1107.                     continue;
  1108.                 }
  1109.                 // Récupération du lot pour ce produit
  1110.                 $lotId "";
  1111.                 if (isset($lotsMap[$productName])) {
  1112.                     $lot $lotsMap[$productName];
  1113.                     $candidateLotId $lot->getIdLotMercator();
  1114.                     // Valider que le lotId n'est pas un fallback (= ID produit Mercator)
  1115.                     // Le fallback est assigné quand Mercator n'a pas de numéro de lot (iD_LOT null)
  1116.                     if (!empty($candidateLotId) && $candidateLotId !== strval($product->getIdProductOnMercator())) {
  1117.                         $lotId $candidateLotId;
  1118.                     }
  1119.                 }
  1120.                 // Validation : le lot doit exister et être un vrai numéro de lot
  1121.                 if (empty($lotId)) {
  1122.                     error_log("Erreur envoi Mercator: Lot non trouvé ou invalide pour produit - " $productName);
  1123.                     continue;
  1124.                 }
  1125.                 // Calcul du discount
  1126.                 $discount $this->calculateMercatorDiscount(
  1127.                     $productName,
  1128.                     $productInfo,
  1129.                     $defaultRebatePercent,
  1130.                     $pourcentSpecialCustomer,
  1131.                     $totalCartQuantity
  1132.                 );
  1133.                 // Détermination de la promotion spéciale
  1134.                 $specialPromotion false;
  1135.                 if (isset($productInfo[$productName])) {
  1136.                     $info $productInfo[$productName];
  1137.                     if (isset($info['specialDiscount']) && $info['specialDiscount'] !== null && $info['quantity'] > 11) {
  1138.                         $specialPromotion true;
  1139.                     }
  1140.                 }
  1141.                 $line = [
  1142.                     "productId" => strval($product->getIdProductOnMercator()),
  1143.                     "quantity" => $article->getQuantity(),
  1144.                     "discount" => intval($discount),
  1145.                     "lotId" => $lotId,
  1146.                     "specialPromotion" => $specialPromotion
  1147.                 ];
  1148.                 $data['json']['lines'][] = $line;
  1149.                 // Ajout d'une ligne OFFERT si applicable (remise 100%)
  1150.                 // La quantité gratuite est calculée côté B2B (checkFreeProduct) et stockée dans $productInfo[$productName]['free']
  1151.                 // On utilise l'ID produit Mercator comme clé pour éviter les doublons liés aux variations de nom
  1152.                 $productMercatorId strval($product->getIdProductOnMercator());
  1153.                 if (!isset($freeLinesAdded[$productMercatorId])
  1154.                     && isset($productInfo[$productName]['free'])
  1155.                     && (int) $productInfo[$productName]['free'] > 0
  1156.                 ) {
  1157.                     $freeLine = [
  1158.                         "productId" => strval($product->getIdProductOnMercator()),
  1159.                         "quantity" => (int) $productInfo[$productName]['free'],
  1160.                         "discount" => 100,
  1161.                         "lotId" => $lotId,
  1162.                         "specialPromotion" => false
  1163.                     ];
  1164.                     $data['json']['lines'][] = $freeLine;
  1165.                     $freeLinesAdded[$productMercatorId] = true;
  1166.                 }
  1167.             }
  1168.             // Ajout des frais de port (produit 55) si < franco et pas de joker utilisé
  1169.             if (!$jokerUsedForDelivery && $totalPriceTvac $francoDelivery) {
  1170.                 $shippingLine = [
  1171.                     "productId" => "55",
  1172.                     "quantity" => 1,
  1173.                     "discount" => 0,
  1174.                     "lotId" => "",
  1175.                     "specialPromotion" => false
  1176.                 ];
  1177.                 $data['json']['lines'][] = $shippingLine;
  1178.             }
  1179.             // Validation : au moins une ligne de commande
  1180.             if (empty($data['json']['lines'])) {
  1181.                 error_log("Erreur envoi Mercator: Aucune ligne de commande valide - Commande B2B #" $cartFinished->getCommandId());
  1182.                 return;
  1183.             }
  1184.             // Log avant envoi
  1185.             error_log("Envoi commande Mercator - Client: " $additionalInformation->getIdClientMercator() . 
  1186.                       " - Dossier: " $folderMercatorValue 
  1187.                       " - Commande B2B #" $cartFinished->getCommandId() . 
  1188.                       " - Nb lignes: " count($data['json']['lines']));
  1189.             // Envoi de la requête
  1190.             $response $this->client->request('POST''https://ns3190747.ip-51-89-219.eu/Order'$data);
  1191.             $responseContent $response->getContent();
  1192.             $statusCode $response->getStatusCode();
  1193.             
  1194.             // Extraction de l'ID Mercator depuis la réponse
  1195.             // Format attendu: "1CB2B 26000063" ou JSON {"id": "..."}
  1196.             $mercatorOrderId 'N/A';
  1197.             $responseData json_decode($responseContenttrue);
  1198.             
  1199.             if (is_array($responseData) && isset($responseData['id'])) {
  1200.                 // Format JSON
  1201.                 $mercatorOrderId $responseData['id'];
  1202.             } elseif (is_string($responseContent)) {
  1203.                 // Format string "1CB2B 26000063"
  1204.                 $responseContent trim(str_replace('"'''$responseContent));
  1205.                 if (preg_match('/(\d+)$/'$responseContent$matches)) {
  1206.                     $mercatorOrderId $matches[1];
  1207.                 } else {
  1208.                     $mercatorOrderId $responseContent;
  1209.                 }
  1210.             }
  1211.             // Log succès
  1212.             error_log("Commande Mercator envoyée avec succès - Status: " $statusCode 
  1213.                       " - ID Mercator: " $mercatorOrderId);
  1214.         } catch (\Exception $e) {
  1215.             // Log erreur détaillé
  1216.             error_log("Erreur envoi commande Mercator - Client: " $additionalInformation->getIdClientMercator() . 
  1217.                       " - Commande B2B #" $cartFinished->getCommandId() . 
  1218.                       " - Erreur: " $e->getMessage());
  1219.         }
  1220.     }
  1221.     /**
  1222.      * Calcule le discount à envoyer à Mercator pour un produit
  1223.      */
  1224.     private function calculateMercatorDiscount(
  1225.         string $productName,
  1226.         array $productInfo,
  1227.         float $defaultRebatePercent,
  1228.         $pourcentSpecialCustomer,
  1229.         int $totalCartQuantity 0
  1230.     ): float {
  1231.         $discount 0;
  1232.         if (!isset($productInfo[$productName])) {
  1233.             return $discount;
  1234.         }
  1235.         $info $productInfo[$productName];
  1236.         $isNewRef $info['newRef'] ?? false;
  1237.         
  1238.         // Utiliser la remise spécifique au produit si disponible (gère les produits exclus)
  1239.         $productRebatePercent $info['rebatePercent'] ?? $defaultRebatePercent;
  1240.         // Déterminer le seuil de quantité : 2 pour nouvelle réf, 4 pour les autres
  1241.         $minQuantity $isNewRef 4;
  1242.         // Priorité 1 : Remise spéciale (quantité > 11)
  1243.         if (isset($info['specialDiscount']) && $info['specialDiscount'] !== null && $info['quantity'] > 11) {
  1244.             $discount $info['specialDiscount']->getPourcentRebate();
  1245.         } 
  1246.         // Priorité 2 : Action choc (quantité >= minQuantity)
  1247.         elseif (isset($info['actionShock']) && $info['actionShock'] !== null && $info['actionShock'] === true && $info['quantity'] >= $minQuantity) {
  1248.             $discount $info['shockActionPercent'] ?? 25;
  1249.         } 
  1250.         // Priorité 3 : Remise par quantité (si pas action choc et quantité >= minQuantity)
  1251.         elseif ($productRebatePercent != && $info['brand'] != "NaturaMedicatrix" && !$pourcentSpecialCustomer && $info['quantity'] >= $minQuantity) {
  1252.             $discount $productRebatePercent;
  1253.         } 
  1254.         // Priorité 4 : Remise NaturaMedicatrix (quantité >= minQuantity ET remise de base existe)
  1255.         elseif ($info['brand'] == "NaturaMedicatrix" && !$pourcentSpecialCustomer && $info['quantity'] >= $minQuantity && $productRebatePercent != 0) {
  1256.             $brandDiscount $this->manager->getRepository(BrandDiscount::class)->findOneBy(['brandName' => 'NaturaMedicatrix']);
  1257.             if ($brandDiscount) {
  1258.                 if ($productRebatePercent == 13) {
  1259.                     $discount $brandDiscount->getRebateLow();
  1260.                 } elseif ($productRebatePercent == 19) {
  1261.                     $discount $brandDiscount->getRebateHeigh();
  1262.                 }
  1263.             }
  1264.         } 
  1265.         // Priorité 5 : Client spécial (quantité >= minQuantity)
  1266.         elseif ($productRebatePercent != && $pourcentSpecialCustomer && $info['quantity'] >= $minQuantity) {
  1267.             $discount is_numeric($pourcentSpecialCustomer) ? $pourcentSpecialCustomer 0;
  1268.         }
  1269.         // Ajouter la remise de 2% pour les nouvelles références (seulement si >= 12 produits dans le panier)
  1270.         if ($isNewRef && $totalCartQuantity >= 12 && $discount 0) {
  1271.             $discount += 2;
  1272.         } elseif ($isNewRef && $totalCartQuantity >= 12 && $discount == 0) {
  1273.             $discount 2;
  1274.         }
  1275.         return $discount;
  1276.     }
  1277.     private function createNewBilling(string $billingsUser $user$totalPaid$dateOrder): void
  1278.     {
  1279.         $billing = new Billings();
  1280.         $billing->setUser($user);
  1281.         $billing->setBilling($billings);
  1282.         $billing->setTotalPaid($totalPaid);
  1283.         $commandDate \DateTime::createFromFormat('d/m/Y'$dateOrder);
  1284.         if ($commandDate) {
  1285.             $billing->setCommandDate($commandDate);
  1286.         }
  1287.         //        $billing->setCommandDate($dateOrder);
  1288.         $this->manager->persist($billing);
  1289.         $this->manager->flush();
  1290.     }
  1291.     /**
  1292.      * Retourne le code dossier Mercator selon le pays du client
  1293.      * Belgique => BE, France/Luxembourg => LU
  1294.      */
  1295.     private function getMercatorFolder(string $country): string
  1296.     {
  1297.         return $country === 'Belgique' 'BE' 'LU';
  1298.     }
  1299.     /**
  1300.      * Retourne le code ISO du pays pour l'adresse de livraison
  1301.      */
  1302.     private function getCountryCode(string $country): string
  1303.     {
  1304.         return match($country) {
  1305.             'France' => 'FR',
  1306.             'Belgique' => 'BE',
  1307.             'Luxembourg' => 'LU',
  1308.             default => 'LU'
  1309.         };
  1310.     }
  1311.     private function checkJokerAvailable($numberItems): bool
  1312.     {
  1313.         $joker $this->manager->getRepository(Joker::class)->findOneBy(array('user' => $this->getUser()));
  1314.         if ($joker) {
  1315.             if ($numberItems <= && $joker->getNumberJoker() > 0) {
  1316.                 return true;
  1317.             } else {
  1318.                 return false;
  1319.             }
  1320.         } else {
  1321.             return false;
  1322.         }
  1323.     }
  1324.     private function checkJokerNumber(): int
  1325.     {
  1326.         $joker $this->manager->getRepository(Joker::class)->findOneBy(array('user' => $this->getUser()));
  1327.         return $joker->getNumberJoker();
  1328.     }
  1329.     private function retrieveCart(): array
  1330.     {
  1331.         $productEligibleReduction null;
  1332.         $freeProduct null;
  1333.         $pourcentSpecialCustomer null;
  1334.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1335.         if (!$checkControl) {
  1336.             $user $this->getUser();
  1337.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $this->getUser()));
  1338.         } else {
  1339.             $user $checkControl->getCustomer();
  1340.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $checkControl->getCustomer()));
  1341.         }
  1342.         if ($user->getSpecialCustomer()) {
  1343.             $checkUserSpecialEntity $this->manager->getRepository(SpecialCustomer::class)->findOneBy(array('user' => $user'active' => true));
  1344.             if ($checkUserSpecialEntity == null) {
  1345.                 $pourcentSpecialCustomer $this->manager->getRepository(ConfigSite::class)->findAll();
  1346.                 $pourcentSpecialCustomer $pourcentSpecialCustomer[0];
  1347.             } else {
  1348.                 $pourcentSpecialCustomer $checkUserSpecialEntity->getPourcentRebate();
  1349.             }
  1350.         }
  1351.         $cart $this->manager->getRepository(Cart::class)->getAllCart($user);
  1352.         $country $additionalInformations->getCompanyCountry();
  1353.         $folderMercatorValue $this->getMercatorFolder($country);
  1354.         // Récupérer l'historique local des commandes pour détecter les nouvelles références
  1355.         // L'API Mercator /OrderHistory retourne des IDs incompatibles avec idProductOnMercator
  1356.         $finishedCartItems $this->manager->getRepository(Cart::class)->findBy([
  1357.             'user' => $user,
  1358.             'finish' => true
  1359.         ]);
  1360.         
  1361.         $localHistoryProductIds = [];
  1362.         foreach ($finishedCartItems as $cartItem) {
  1363.             $productItem $cartItem->getProducts();
  1364.             if ($productItem) {
  1365.                 $localHistoryProductIds[] = $productItem->getIdProductOnMercator();
  1366.             }
  1367.         }
  1368.         $localHistoryProductIds array_unique($localHistoryProductIds);
  1369.         foreach ($cart as &$item) {
  1370.             $product $this->manager->getRepository(Products::class)->find($item['productId']);
  1371.             $dlus $this->manager->getRepository(LotNumber::class)->findBy(['products' => $product'folderMercator' => $folderMercatorValue]);
  1372.             // Déterminer si c'est une nouvelle référence (basé sur l'historique local)
  1373.             $productIdMercator $product->getIdProductOnMercator();
  1374.             $item['newRef'] = !in_array($productIdMercator$localHistoryProductIds);
  1375.             // Convertir la collection en un simple tableau associatif
  1376.             $dlusArray = [];
  1377.             foreach ($dlus as $dlu) {
  1378.                 $dlusArray[] = [
  1379.                     'dlu' => $dlu->getDlu(),
  1380.                     'lotNumber' => $dlu->getLotNumber()
  1381.                     // Ajoutez d'autres propriétés de LotNumber ici si nécessaire
  1382.                     // 'property_name' => $dlu->getPropertyName(),
  1383.                 ];
  1384.             }
  1385.             // Trouver la date la plus lointaine (la plus grande) dans le tableau $dlusArray
  1386.             $maxDlu null;
  1387.             foreach ($dlusArray as $dluItem) {
  1388.                 if ($maxDlu === null || $dluItem['dlu'] > $maxDlu) {
  1389.                     $maxDlu $dluItem['dlu'];
  1390.                     $maxLotNumber $dluItem['lotNumber'];
  1391.                 }
  1392.             }
  1393.             // Mettre à jour le tableau $item avec la date la plus lointaine
  1394.             $item['dlus'] = [
  1395.                 'dlu' => $maxDlu,
  1396.                 'lotNumber' => $maxLotNumber,
  1397.             ];
  1398.             // enlever le commentaire pour avoir toutes les dlus
  1399.             //            $item['dlus'] = $dlusArray;
  1400.         }
  1401.         unset($item); // Dissocier la référence après la boucle foreach
  1402.         $count 0;
  1403.         $grandTotalPrice 0;
  1404.         foreach ($cart as $item) {
  1405.             if (intval($item['totalQuantity']) > 3) {
  1406.                 $productEligibleReduction[] = $item;
  1407.                 $count += $item['totalQuantity'];
  1408.                 $grandTotalPrice += intval($item['price']) * intval($item['totalQuantity']);
  1409.             } else {
  1410.                 $count += $item['totalQuantity'];
  1411.                 $grandTotalPrice += intval($item['price']) * intval($item['totalQuantity']);
  1412.             }
  1413.         }
  1414.         $joker $this->checkJokerAvailable($count);
  1415.         // nous renvoi le % de remise (quantité de produits)
  1416.         $rebate $this->calculateRebate($pourcentSpecialCustomer);
  1417.         $carts $this->calculatePriceHtvaWithRebateEachProduct($cart$rebate);
  1418.         $freeTransport $this->calculateFreeTransport($carts['totalPriceTvacWithRebate']);
  1419.         if ($rebate == null) {
  1420.             $rebate 0;
  1421.         } else {
  1422.             $freeProduct $this->checkFreeProduct($cart);
  1423.         }
  1424.         $missingItems $this->calculateMissingItemsForNextRebate();
  1425.         return [
  1426.             'carts' => $carts,
  1427.             'rebate' => $rebate,
  1428.             'grandTotalPrice' => $grandTotalPrice,
  1429.             'freeProduct' => $freeProduct,
  1430.             'freeTransport' => $freeTransport,
  1431.             'joker' => $joker,
  1432.             'missingItems' => $missingItems
  1433.         ];
  1434.     }
  1435.     private function calculateMissingItemsForNextRebate()
  1436.     {
  1437.         $cartCounts $this->calculateCountItemInCart();
  1438.         $eligibleCount $cartCounts['eligible'];
  1439.         
  1440.         $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  1441.         
  1442.         // Trier les remises par ordre croissant de minimum
  1443.         $rebatesArray = [];
  1444.         foreach ($rebates as $rebate) {
  1445.             $rebatesArray[] = [
  1446.                 'min' => $rebate->getMin(),
  1447.                 'percent' => $rebate->getPourcentRebate()
  1448.             ];
  1449.         }
  1450.         usort($rebatesArray, function($a$b) {
  1451.             return $a['min'] - $b['min'];
  1452.         });
  1453.         
  1454.         // Trouver la prochaine remise à atteindre
  1455.         $nextRebateMin null;
  1456.         foreach ($rebatesArray as $rebate) {
  1457.             if ($eligibleCount $rebate['min']) {
  1458.                 $nextRebateMin $rebate['min'];
  1459.                 break;
  1460.             }
  1461.         }
  1462.         
  1463.         // Si on a trouvé une prochaine remise, calculer combien d'articles manquent
  1464.         if ($nextRebateMin !== null) {
  1465.             return $nextRebateMin $eligibleCount;
  1466.         }
  1467.         
  1468.         // Si on a déjà atteint la remise maximale, retourner un nombre >= 48
  1469.         return 48;
  1470.     }
  1471.     private function calculatePriceHtvaWithRebateEachProduct($cart$rebatePercent)
  1472.     {
  1473.         $carts = [];
  1474.         //        dd($cart);
  1475.         // Calculer le total de produits dans le panier pour la règle des 2% nouvelles références
  1476.         $totalCartQuantity 0;
  1477.         foreach ($cart as $item) {
  1478.             $totalCartQuantity += intval($item['totalQuantity'] ?? 0);
  1479.         }
  1480.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1481.         if (!$checkControl) {
  1482.             $user $this->getUser();
  1483.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $this->getUser()));
  1484.         } else {
  1485.             $user $checkControl->getCustomer();
  1486.             $additionalInformations $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $checkControl->getCustomer()));
  1487.         }
  1488.         $pourcentActionShock $this->manager->getRepository(ConfigSite::class)->findAll();
  1489.         $pourcentActionShock $pourcentActionShock[0];
  1490.         $pourcentSpecialCustomer null;
  1491.         // Récupérer l'historique des produits commandés via l'API Mercator
  1492.         // L'API /OrderHistory retourne maintenant les paires {s_ID, ref} pour chaque produit
  1493.         $mercatorHistoryProductIds $this->getMercatorOrderHistory($additionalInformations);
  1494.         
  1495.         // Parcourir chaque élément du panier et déterminer si c'est une nouvelle référence
  1496.         foreach ($cart as $key => &$item) {
  1497.             if (is_array($item) && isset($item['productIdMercator'])) {
  1498.                 $totalQuantity intval($item['totalQuantity']);
  1499.                 $mercatorId intval($item['productIdMercator']);
  1500.                 
  1501.                 // Vérifier si le produit est dans l'historique Mercator
  1502.                 $isInMercatorHistory in_array($mercatorId$mercatorHistoryProductIds);
  1503.                 
  1504.                 // Nouvelle référence si: pas dans l'historique ET quantité >= 2
  1505.                 if (!$isInMercatorHistory && $totalQuantity >= 2) {
  1506.                     $item['newRef'] = true;
  1507.                 } else {
  1508.                     $item['newRef'] = false;
  1509.                 }
  1510.             }
  1511.         }
  1512.         unset($item);
  1513.         $excludedProducts $this->manager->getRepository(Rebate::class)->getExcludedProducts();
  1514.         $specificRebates $this->manager->getRepository(Rebate::class)->getActiveSpecificRebates();
  1515.         foreach ($cart as $item) {
  1516.             $isNewRef = isset($item['newRef']) && $item['newRef'] ?? false;
  1517.             $productRebatePercent $rebatePercent;
  1518.             $shockActionDiscountPercent 0;
  1519.             if ($item['shockActionDiscountPercent']) {
  1520.                 $shockActionDiscountPercent $item['shockActionDiscountPercent'] ?? 0;
  1521.             }
  1522.             // Vérifier d'abord si le produit a une promotion spéciale
  1523.             $checkProduct $this->manager->getRepository(Products::class)->find($item['productId']);
  1524.             $checkSpecialDiscount $this->manager->getRepository(SpecialDiscount::class)->findOneBy(array('product' => $checkProduct));
  1525.             
  1526.             // Si le produit est dans la liste des exclus mais qu'il a une promotion spéciale
  1527.             // et que sa quantité est inférieure à 12, on le laisse bénéficier de la remise globale
  1528.             if (in_array($item['reference'], $excludedProducts) && 
  1529.                 !($checkSpecialDiscount != null && $item['totalQuantity'] < 12)) {
  1530.                 $productRebatePercent 0;
  1531.             }
  1532.             foreach ($specificRebates as $rebate) {
  1533.                 if (in_array($item['reference'], $rebate->getSpecificProducts()) && $item['totalQuantity'] >= $rebate->getMin()) {
  1534.                     $productRebatePercent $rebate->getPourcentRebate();
  1535.                     break;
  1536.                 }
  1537.             }
  1538.             $item['rebatePercent'] = $productRebatePercent;
  1539.             $checkProduct $this->manager->getRepository(Products::class)->find($item['productId']);
  1540.             $checkSpecialDiscount $this->manager->getRepository(SpecialDiscount::class)->findOneBy(array('product' => $checkProduct));
  1541.             if ($user->getSpecialCustomer()) {
  1542.                 $checkUserSpecialEntity $this->manager->getRepository(SpecialCustomer::class)->findOneBy(array('user' => $user'active' => true));
  1543.                 if ($checkUserSpecialEntity == null) {
  1544.                     $pourcentSpecialCustomer $this->manager->getRepository(ConfigSite::class)->findAll();
  1545.                     $pourcentSpecialCustomer $pourcentSpecialCustomer[0];
  1546.                 } else {
  1547.                     $pourcentSpecialCustomer $checkUserSpecialEntity->getPourcentRebate();
  1548.                 }
  1549.             }
  1550.             // Produits sans promotion spéciale ni action choc
  1551.             if (isset($item['price']) && !$item['actionShock'] && !$checkSpecialDiscount && $pourcentSpecialCustomer == null) {
  1552.                 $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1553.                 $minQuantity $isNewRef 4;
  1554.                 if (strcasecmp($item['brand'], 'NaturaMedicatrix') === 0) {
  1555.                     $brandRebate $this->manager->getRepository(BrandDiscount::class)->findOneBy(array('brandName' => 'NaturaMedicatrix''active' => true));
  1556.                     if ($brandRebate && $productRebatePercent && $item['totalQuantity'] >= $minQuantity) {
  1557.                         // Total panier >= 12 ET quantité >= colissage : remise de base + 2% marque + 2% nouvelle réf si applicable
  1558.                         $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1559.                         $newRefBonus = ($isNewRef && $item['totalQuantity'] >= 2) ? 0;
  1560.                         if ($productRebatePercent == 13) {
  1561.                             // 13% → 15% (13% + 2% marque) + 2% si nouvelle réf = 17%
  1562.                             $totalDiscount $brandRebate->getRebateLow() + $newRefBonus;
  1563.                             $totalRebate = ($totalPriceProduct 100) * $totalDiscount;
  1564.                             $item['rebatePercent'] = $totalDiscount;
  1565.                             $item['newRefDiscount'] = $totalDiscount;
  1566.                             $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1567.                             $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1568.                             $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $totalDiscount), 2'.'''));
  1569.                         } elseif ($productRebatePercent == 19) {
  1570.                             // 19% → 21% (19% + 2% marque) + 2% si nouvelle réf = 23%
  1571.                             $totalDiscount $brandRebate->getRebateHeigh() + $newRefBonus;
  1572.                             $totalRebate = ($totalPriceProduct 100) * $totalDiscount;
  1573.                             $item['rebatePercent'] = $totalDiscount;
  1574.                             $item['newRefDiscount'] = $totalDiscount;
  1575.                             $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1576.                             $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1577.                             $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $totalDiscount), 2'.'''));
  1578.                         }
  1579.                     } elseif ($isNewRef && $item['totalQuantity'] >= && $totalCartQuantity >= 12) {
  1580.                         // Nouvelle réf NaturaMedicatrix sans remise de base : seulement 2% (si >= 12 produits dans le panier)
  1581.                         $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1582.                         $totalRebate = ($totalPriceProduct 100) * 2;
  1583.                         $item['rebatePercent'] = 2;
  1584.                         $item['newRefDiscount'] = 2;
  1585.                         $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1586.                         $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1587.                         $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * 2), 2'.'''));
  1588.                     } else {
  1589.                         // Pas de remise (en dessous du colissage ou pas de remise de base ou < 12 produits)
  1590.                         $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1591.                         $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct2'.'''));
  1592.                         $item['reductionUniteItem'] = $item['price'];
  1593.                         $item['rebatePercent'] = 0;
  1594.                         $item['newRefDiscount'] = 0;
  1595.                     }
  1596.                 } elseif ($productRebatePercent && $item['totalQuantity'] >= $minQuantity) {
  1597.                     // Total panier >= 12 ET quantité >= colissage : remise de base + 2% nouvelle réf si applicable
  1598.                     $newRefBonus = ($isNewRef && $item['totalQuantity'] >= && $totalCartQuantity >= 12) ? 0;
  1599.                     $totalDiscount $productRebatePercent $newRefBonus;
  1600.                     $totalRebate = ($totalPriceProduct 100) * $totalDiscount;
  1601.                     $item['rebatePercent'] = $totalDiscount;
  1602.                     $item['newRefDiscount'] = $totalDiscount;
  1603.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1604.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1605.                     $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $totalDiscount), 2'.'''));
  1606.                 } elseif ($isNewRef && $item['totalQuantity'] >= && $totalCartQuantity >= 12) {
  1607.                     // Nouvelle réf sans remise de base : seulement 2% (si >= 12 produits dans le panier)
  1608.                     $totalRebate = ($totalPriceProduct 100) * 2;
  1609.                     $item['rebatePercent'] = 2;
  1610.                     $item['newRefDiscount'] = 2;
  1611.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1612.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1613.                     $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * 2), 2'.'''));
  1614.                 } else {
  1615.                     // Pas de remise (en dessous du colissage ou pas de remise de base ou < 12 produits)
  1616.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct2'.'''));
  1617.                     $item['reductionUniteItem'] = $item['price'];
  1618.                     $item['rebatePercent'] = 0;
  1619.                     $item['newRefDiscount'] = 0;
  1620.                 }
  1621.             } elseif (isset($item['price']) && $item['actionShock']) {
  1622.                 $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1623.                 $minQuantityShock $isNewRef 4;
  1624.                 $item['rebatePercent'] = 0;
  1625.                 if ($item['totalQuantity'] >= $minQuantityShock) {
  1626.                     $item['rebatePercent'] = $shockActionDiscountPercent + (($isNewRef && $totalCartQuantity >= 12) ? 0);
  1627.                 }
  1628.                 $item['newRefDiscount'] = $item['rebatePercent'];
  1629.                 $totalRebate = ($totalPriceProduct 100) * $item['rebatePercent'];
  1630.                 $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1631.                 $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1632.                 $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $item['rebatePercent']), 2'.'''));
  1633.             } elseif (isset($item['price']) && !$item['actionShock'] && $checkSpecialDiscount != null) {
  1634.                 // Produits avec promotion spéciale
  1635.                 if ($item['totalQuantity'] > 11) {
  1636.                     // Si la quantité est supérieure à 11, on applique la promotion spéciale (50%)
  1637.                     $totalPriceProduct $item['publicPrice'] * $item['totalQuantity'];
  1638.                     $totalRebate = ($totalPriceProduct 100) * $checkSpecialDiscount->getPourcentRebate();
  1639.                     $item['rebatePercent'] = $checkSpecialDiscount->getPourcentRebate();
  1640.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1641.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1642.                     $item['reductionUniteItem'] = floatval(number_format(($item['publicPrice']) - (($item['publicPrice'] / 100) * floatval(number_format($checkSpecialDiscount->getPourcentRebate(), 2'.'''))), 2'.'''));
  1643.                 } elseif ($productRebatePercent && $item['totalQuantity'] >= ($isNewRef 4)) {
  1644.                     // Total panier >= 12 ET quantité >= colissage : remise de base + 2% nouvelle réf si applicable
  1645.                     $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1646.                     $newRefBonus = ($isNewRef && $item['totalQuantity'] >= && $totalCartQuantity >= 12) ? 0;
  1647.                     $totalDiscount $productRebatePercent $newRefBonus;
  1648.                     $totalRebate = ($totalPriceProduct 100) * $totalDiscount;
  1649.                     $item['rebatePercent'] = $totalDiscount;
  1650.                     $item['newRefDiscount'] = $totalDiscount;
  1651.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1652.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1653.                     $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $totalDiscount), 2'.'''));
  1654.                 } elseif ($isNewRef && $item['totalQuantity'] >= && $totalCartQuantity >= 12) {
  1655.                     // Nouvelle réf sans remise de base : seulement 2% (si >= 12 produits dans le panier)
  1656.                     $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1657.                     $totalRebate = ($totalPriceProduct 100) * 2;
  1658.                     $item['rebatePercent'] = 2;
  1659.                     $item['newRefDiscount'] = 2;
  1660.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1661.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1662.                     $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * 2), 2'.'''));
  1663.                 } else {
  1664.                     // Pas de remise (en dessous du colissage ou pas de remise de base)
  1665.                     $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1666.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct2'.'''));
  1667.                     $item['reductionUniteItem'] = $item['price'];
  1668.                     $item['rebatePercent'] = 0;
  1669.                     $item['newRefDiscount'] = 0;
  1670.                 }
  1671.             } elseif (isset($item['price']) && $pourcentSpecialCustomer != null) {
  1672.                 $totalPriceProduct $item['publicPrice'] * $item['totalQuantity'];
  1673.                 if ($item['totalQuantity'] > 11 && $checkSpecialDiscount != null) {
  1674.                     $totalRebate = ($totalPriceProduct 100) * $checkSpecialDiscount->getPourcentRebate();
  1675.                     $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1676.                     $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1677.                     $item['reductionUniteItem'] = floatval(number_format(($item['publicPrice']) - (($item['publicPrice'] / 100) * floatval(number_format($pourcentSpecialCustomer->getPourcentSpecialCustomer(), 2'.'''))), 2'.'''));
  1678.                 } else {
  1679.                     $totalPriceProduct $item['price'] * $item['totalQuantity'];
  1680.                     if ($item['totalQuantity'] > 3) {
  1681.                         $totalRebate = ($totalPriceProduct 100) * $productRebatePercent;
  1682.                         $item['totalRebate'] = floatval(number_format($totalRebate2'.'''));
  1683.                         $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct $item['totalRebate'], 2'.'''));
  1684.                         $item['reductionUniteItem'] = floatval(number_format(($item['price']) - (($item['price'] / 100) * $productRebatePercent), 2'.'''));
  1685.                     } else {
  1686.                         $item['PriceRebateInclude'] = floatval(number_format($totalPriceProduct2'.'''));
  1687.                         $item['reductionUniteItem'] = $item['price'];
  1688.                     }
  1689.                 }
  1690.             }
  1691.             $carts[] = $item;
  1692.         }
  1693.         //        $this->applyDiscountOnNewRefItems($carts);
  1694.         //        dd($carts);
  1695.         $carts $this->groupItemWithVat($carts$user);
  1696.         return $carts;
  1697.     }
  1698.     private function groupItemWithVat($carts$user)
  1699.     {
  1700.         $tvaLow 0;
  1701.         $tvaHigh 0;
  1702.         $productsLowTva 0;
  1703.         $productsHighTva 0;
  1704.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1705.         if (!$checkControl) {
  1706.             $user $this->getUser();
  1707.             $additionalInformation $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $this->getUser()));
  1708.         } else {
  1709.             $user $checkControl->getCustomer();
  1710.             $additionalInformation $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $checkControl->getCustomer()));
  1711.         }
  1712.         //        $additionalInformation = $this->manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  1713.         // Determine country-specific VAT rates and which product VAT field to use
  1714.         $vatField null;
  1715.         if ($additionalInformation->getCompanyCountry() == 'Belgique') {
  1716.             $tvaLow 6;
  1717.             $tvaHigh 21;
  1718.             $vatField 'tvaBe';
  1719.         } elseif ($additionalInformation->getCompanyCountry() == 'France') {
  1720.             $tvaLow 5.5;
  1721.             $tvaHigh 20;
  1722.             $vatField 'tvaFr';
  1723.         } elseif ($additionalInformation->getCompanyCountry() == 'Luxembourg') {
  1724.             $tvaLow 3;
  1725.             $tvaHigh 17;
  1726.             $vatField 'tvaLu';
  1727.         }
  1728.         foreach ($carts as $item) {
  1729.             // Ensure we have the computed HTVA with all discounts applied
  1730.             if (!isset($item['PriceRebateInclude'])) {
  1731.                 continue;
  1732.             }
  1733.             $productVat = isset($item[$vatField]) ? $item[$vatField] : null;
  1734.             if ($productVat == $tvaLow) {
  1735.                 $productsLowTva += $item['PriceRebateInclude'];
  1736.             } else {
  1737.                 $productsHighTva += $item['PriceRebateInclude'];
  1738.             }
  1739.         }
  1740.         $carts['TotalPriceProductLowVatHtva'] = $productsLowTva;
  1741.         $carts['TotalPriceProductHighVatHtva'] = $productsHighTva;
  1742.         $carts $this->calculateTvaOnEachProduct($carts$additionalInformation);
  1743.         return $carts;
  1744.     }
  1745.     private function calculateTvaOnEachProduct($carts$additionalInformation)
  1746.     {
  1747.         $tvaLow 0;
  1748.         $tvaHigh 0;
  1749.         if ($additionalInformation->getCompanyCountry() == 'Belgique') {
  1750.             $tvaLow 6;
  1751.             $tvaHigh 21;
  1752.         } elseif ($additionalInformation->getCompanyCountry() == 'France') {
  1753.             $tvaLow 5.5;
  1754.             $tvaHigh 20;
  1755.         } elseif ($additionalInformation->getCompanyCountry() == 'Luxembourg') {
  1756.             $tvaLow 3;
  1757.             $tvaHigh 17;
  1758.         }
  1759.         $carts['TotalPriceProductLowVatTvac'] = floatval(number_format(($carts['TotalPriceProductLowVatHtva'] / 100) * $tvaLow2'.'''));
  1760.         $carts['TotalPriceProductHighVatTvac'] = floatval(number_format(($carts['TotalPriceProductHighVatHtva'] / 100) * $tvaHigh2'.'''));
  1761.         $carts['totalPriceAllProductHtva'] = floatval(number_format($carts['TotalPriceProductLowVatHtva'] + $carts['TotalPriceProductHighVatHtva'], 2'.'''));
  1762.         $carts['totalPriceTvacWithRebate'] = floatval(number_format($carts['TotalPriceProductLowVatHtva'] + $carts['TotalPriceProductHighVatHtva'] + $carts['TotalPriceProductLowVatTvac'] + $carts['TotalPriceProductHighVatTvac'], 2'.'''));
  1763.         return $carts;
  1764.     }
  1765.     private function calculateCountItemInCart()
  1766. {
  1767.     $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1768.     if (!$checkControl) {
  1769.         $user $this->getUser();
  1770.     } else {
  1771.         $user $checkControl->getCustomer();
  1772.     }
  1773.     $carts $this->manager->getRepository(Cart::class)->getUserPendingItems($user);
  1774.     $excludedProducts $this->manager->getRepository(Rebate::class)->getExcludedProducts();
  1775.     $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  1776.     // Compter les articles éligibles (même logique que calculateRebate)
  1777.     $productCounts = [];
  1778.     $totalCount 0;
  1779.     
  1780.     // Première passe : compter les produits avec promotion spéciale
  1781.     foreach ($carts as $item) {
  1782.         $productRef $item->getProducts()->getReference();
  1783.         $quantity $item->getQuantity();
  1784.         
  1785.         $checkSpecialDiscount $this->manager->getRepository(SpecialDiscount::class)->findOneBy(array('product' => $item->getProducts()));
  1786.         
  1787.         if (!isset($productCounts[$productRef])) {
  1788.             $productCounts[$productRef] = [
  1789.                 'quantity' => 0,
  1790.                 'hasSpecialDiscount' => ($checkSpecialDiscount != null)
  1791.             ];
  1792.         }
  1793.         $productCounts[$productRef]['quantity'] += $quantity;
  1794.         $totalCount += $quantity;
  1795.     }
  1796.     
  1797.     // Deuxième passe : calculer le total d'articles éligibles
  1798.     $eligibleCount 0;
  1799.     foreach ($carts as $item) {
  1800.         $productRef $item->getProducts()->getReference();
  1801.         
  1802.         if (in_array($productRef$excludedProducts)) {
  1803.             // Si produit exclu avec promotion spéciale et quantité >=12 - NE PAS compter
  1804.             if ($productCounts[$productRef]['hasSpecialDiscount'] && $productCounts[$productRef]['quantity'] >= 12) {
  1805.                 continue;
  1806.             }
  1807.         }
  1808.         
  1809.         $eligibleCount += $item->getQuantity();
  1810.     }
  1811.     // Retourner un tableau avec le total et les éligibles
  1812.     return [
  1813.         'total' => $totalCount,
  1814.         'eligible' => $eligibleCount
  1815.     ];
  1816. }    
  1817.     private function calculateRebate($pourcentSpecialCustomer): float|int
  1818.     {
  1819.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1820.         if (!$checkControl) {
  1821.             $user $this->getUser();
  1822.         } else {
  1823.             $user $checkControl->getCustomer();
  1824.         }
  1825.         $rebates $this->manager->getRepository(Rebate::class)->getActiveNormalRebates();
  1826.         $carts $this->manager->getRepository(Cart::class)->getUserPendingItems($user);
  1827.         $excludedProducts $this->manager->getRepository(Rebate::class)->getExcludedProducts();
  1828.         // Initialiser le compteur total d'articles
  1829.         $count 0;
  1830.         $productCounts = [];
  1831.         
  1832.         // Première passe : compter les produits avec promotion spéciale
  1833.         foreach ($carts as $item) {
  1834.             $productRef $item->getProducts()->getReference();
  1835.             $quantity $item->getQuantity();
  1836.             
  1837.             // Vérifier si le produit a une promotion spéciale
  1838.             $checkSpecialDiscount $this->manager->getRepository(SpecialDiscount::class)->findOneBy(array('product' => $item->getProducts()));
  1839.             
  1840.             if (!isset($productCounts[$productRef])) {
  1841.                 $productCounts[$productRef] = [
  1842.                     'quantity' => 0,
  1843.                     'hasSpecialDiscount' => ($checkSpecialDiscount != null)
  1844.                 ];
  1845.             }
  1846.             $productCounts[$productRef]['quantity'] += $quantity;
  1847.         }
  1848.         
  1849.         // Deuxième passe : calculer le total d'articles pour la remise globale
  1850.         foreach ($carts as $item) {
  1851.             $productRef $item->getProducts()->getReference();
  1852.             
  1853.             // Si le produit est dans la liste des exclusions
  1854.             if (in_array($productRef$excludedProducts)) {
  1855.                 // Cas 1: Si c'est un produit exclu sans promotion spéciale - COMPTER DANS LA REMISE 13% 19%
  1856.                 // On ne fait rien de spécial, on laisse passer pour le comptage en bas
  1857.                 
  1858.                 // Cas 2: Si c'est un produit exclu avec promotion spéciale et quantité >=12 - NE PAS compter DANS LA REMISE 13% 19%
  1859.                 if ($productCounts[$productRef]['hasSpecialDiscount'] && $productCounts[$productRef]['quantity'] >= 12) {
  1860.                     continue;
  1861.                 }
  1862.                 
  1863.                 // Cas 3: Si c'est un produit exclu avec promotion spéciale et quantité <12 - COMPTER DANS LA REMISE 13% 19%
  1864.                 // Cette ligne ne fait rien car on va passer au comptage par défaut en dessous
  1865.             } else {
  1866.                 // Produits non-exclus: Toujours compter (pas de condition spéciale)
  1867.             }
  1868.             
  1869.             // Pour tous les autres produits, ajouter leur quantité au compteur
  1870.             $count += $item->getQuantity();
  1871.         }
  1872.         $reduction 0;
  1873.         if ($pourcentSpecialCustomer != null) {
  1874.             if ($count >= 12) {
  1875.                 if (is_float($pourcentSpecialCustomer) || is_int($pourcentSpecialCustomer)) {
  1876.                     $reduction $pourcentSpecialCustomer;
  1877.                 } else {
  1878.                     $reduction $pourcentSpecialCustomer->getPourcentSpecialCustomer() ?? 0;
  1879.                 }
  1880.             }
  1881.         } else {
  1882.             foreach ($rebates as $rebate) {
  1883.                 if ($count >= $rebate->getMin()) {
  1884.                     $reduction $rebate->getPourcentRebate() ?? 0;
  1885.                     break;
  1886.                 }
  1887.             }
  1888.         }
  1889.         return (float) $reduction;
  1890.     }
  1891.     private function checkFreeProduct($cart): array
  1892.     {
  1893.         $countFreeProduct = [];
  1894.         $index 0;
  1895.         foreach ($cart as $item) {
  1896.             if (isset($item['totalQuantity']) && $item['totalQuantity'] >= 24) {
  1897.                 $countFreeProduct[$index]['free'] = floor($item['totalQuantity'] / 24);
  1898.                 $countFreeProduct[$index]['name'] = $item['name'];
  1899.             }
  1900.             $index++;
  1901.         }
  1902.         return $countFreeProduct;
  1903.     }
  1904.     #[Route('/cart/toggle-flyers'name'toggleFlyers')]
  1905.     public function toggleFlyers(Request $request): Response
  1906.     {
  1907.         // Inverser la valeur actuelle (true par défaut)
  1908.         $currentValue $request->getSession()->get('wantFlyers'true);
  1909.         $request->getSession()->set('wantFlyers', !$currentValue);
  1910.         
  1911.         return $this->redirectToRoute('cartView');
  1912.     }
  1913.     private function calculateFreeTransport($totalPrice): bool
  1914.     {
  1915.         $franco $this->manager->getRepository(ConfigSite::class)->findAll();
  1916.         if ($totalPrice >= $franco[0]->getFrancoDelivery()) {
  1917.             return true;
  1918.         } else {
  1919.             return false;
  1920.         }
  1921.     }
  1922.     #[Route('/cart/remove/product/{productID}'name'cartRemoveProduct')]
  1923.     public function removeProductFromCart($productID): RedirectResponse
  1924.     {
  1925.         $checkControl $this->manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  1926.         if (!$checkControl) {
  1927.             $user $this->getUser();
  1928.         } else {
  1929.             $user $checkControl->getCustomer();
  1930.         }
  1931.         $product $this->manager->getRepository(Products::class)->find($productID);
  1932.         $products $this->manager->getRepository(Cart::class)->findBy(array('user' => $user'finish' => false'products' => $product));
  1933.         foreach ($products as $item) {
  1934.             $this->manager->remove($item);
  1935.             $this->manager->flush();
  1936.         }
  1937.         return $this->redirectToRoute('cartView');
  1938.     }
  1939.     /**
  1940.      * Récupère l'historique des produits commandés via l'API Mercator /OrderHistory
  1941.      * Retourne un tableau des s_ID des produits déjà commandés par le client
  1942.      */
  1943.     private function getMercatorOrderHistory(?AdditionalCompanyInformation $additionalInformations): array
  1944.     {
  1945.         // Vérifier que le client a un ID Mercator
  1946.         if (!$additionalInformations || !$additionalInformations->getIdClientMercator()) {
  1947.             return [];
  1948.         }
  1949.         try {
  1950.             // Récupérer le token JWT
  1951.             $jwtToken $this->jwtMercator->getToken();
  1952.             // Appeler l'endpoint /OrderHistory avec le clientId
  1953.             $clientId $additionalInformations->getIdClientMercator();
  1954.             
  1955.             $response $this->client->request('GET''https://ns3190747.ip-51-89-219.eu/OrderHistory?clientId=' $clientId, [
  1956.                 'headers' => [
  1957.                     'Authorization' => 'Bearer ' $jwtToken,
  1958.                 ],
  1959.             ]);
  1960.             if ($response->getStatusCode() === 200) {
  1961.                 $orderHistory json_decode($response->getContent(), true);
  1962.                 
  1963.                 if (empty($orderHistory)) {
  1964.                     return [];
  1965.                 }
  1966.                 
  1967.                 // Extraire tous les s_ID des produits commandés
  1968.                 $productIds = [];
  1969.                 foreach ($orderHistory as $item) {
  1970.                     if (isset($item['s_ID'])) {
  1971.                         $productIds[] = intval($item['s_ID']);
  1972.                     }
  1973.                 }
  1974.                 
  1975.                 return array_unique($productIds);
  1976.             }
  1977.         } catch (\Exception $e) {
  1978.             // En cas d'erreur API, retourner un tableau vide
  1979.             return [];
  1980.         }
  1981.         return [];
  1982.     }
  1983. }