src/Controller/ProductsController.php line 25

  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\ActionShock;
  4. use App\Entity\AdditionalCompanyInformation;
  5. use App\Entity\CustomerControl;
  6. use App\Entity\LotNumber;
  7. use App\Entity\Products;
  8. use App\Entity\FreeProductPromotion;
  9. use App\Entity\SpecialDiscount;
  10. use App\Repository\LotNumberRepository;
  11. use App\Service\ActionShockService;
  12. use App\Service\CrossedPriceDiscountService;
  13. use Doctrine\ORM\EntityManagerInterface;
  14. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  15. use Symfony\Component\HttpFoundation\JsonResponse;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\Routing\Annotation\Route;
  18. class ProductsController extends AbstractController
  19. {
  20.     #[Route('/products', name: 'products')]
  21.     public function index(EntityManagerInterface $manager, ActionShockService $actionShockService, CrossedPriceDiscountService $crossedPriceDiscountService): Response
  22.     {
  23.         $actionShockService->updateActionShocksStatus();
  24.         $additionalInformations = null;
  25.         $checkControl = $manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  26.         if (!$checkControl) {
  27.             $user = $this->getUser();
  28.         } else {
  29.             $user = $checkControl->getCustomer();
  30.         }
  31.         if ($user != null) {
  32.             $additionalInformations = $manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  33.             if ($additionalInformations->getCompanyCountry() == null) {
  34.                 $this->addFlash('danger', 'Veuillez remplir vos coordonnées!');
  35.                 return $this->redirectToRoute('profile');
  36.             }
  37.         }
  38.         $information = $manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  39.         $products = $manager->getRepository(Products::class)->getAllProduct($information);
  40.         $products = $this->calculateRebatePricePharmacy($products);
  41.         $products = $crossedPriceDiscountService->decorateProducts($products);
  42.         $specialDiscounts = $manager->getRepository(SpecialDiscount::class)->findAll();
  43.         if ($specialDiscounts != null) {
  44.             $specialDiscount = $specialDiscounts[0];
  45.         } else {
  46.             $specialDiscount = null;
  47.         }
  48.         if (!$checkControl) {
  49.             $checkControl = null;
  50.         }
  51.         $freeProductPromotions = $manager->getRepository(FreeProductPromotion::class)->findAllActiveIndexedByReference();
  52.         return $this->render('products/index.html.twig', [
  53.             'products' => $products,
  54.             'shockActionTypes' => ActionShock::TYPES,
  55.             'user' => $user,
  56.             'information' => $additionalInformations,
  57.             'checkControl' => $checkControl,
  58.             'specialDiscount' => $specialDiscount,
  59.             'freeProductPromotions' => $freeProductPromotions,
  60.         ]);
  61.     }
  62.     private function calculateRebatePricePharmacy($products)
  63.     {
  64.         foreach ($products as &$product) {
  65.             $totalRebate = ($product['pricePharmacy'] / 100) * $this->getUser()->getAdditionalPercentage();
  66.             $item['totalRebate'] = floatval(number_format($totalRebate, 2, '.', ''));
  67.             $item['PriceRebateInclude'] = floatval(number_format($product['pricePharmacy'] - $item['totalRebate'], 2, '.', ''));
  68.             $product['pricePharmacyWithRebate'] = $item['PriceRebateInclude'];
  69.         }
  70.         //        dd($products);
  71.         return $products;
  72.     }
  73.     private function slugify(string $text): string
  74.     {
  75.         // Remplace les caractères non-ASCII
  76.         $text = iconv('UTF-8', 'ASCII//TRANSLIT', $text);
  77.         // Remplace les caractères non alphanumériques par des tirets
  78.         $text = preg_replace('/[^a-z0-9]+/i', '-', $text);
  79.         // Supprime les tirets en début et fin
  80.         $text = trim($text, '-');
  81.         // Convertit en minuscules
  82.         $text = strtolower($text);
  83.         
  84.         return $text;
  85.     }
  86.     #[Route('/product/{id}-{slug}', name: 'product_show', requirements: ['id' => '\d+'])]
  87.     public function show(int $id, string $slug, EntityManagerInterface $manager, CrossedPriceDiscountService $crossedPriceDiscountService): Response
  88.     {
  89.         $checkControl = $manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  90.         if (!$checkControl) {
  91.             $user = $this->getUser();
  92.         } else {
  93.             $user = $checkControl->getCustomer();
  94.         }
  95.         $information = $manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  96.         
  97.         // Récupère TOUS les produits avec la même logique que le DataTable
  98.         $allProducts = $manager->getRepository(Products::class)->getAllProduct($information);
  99.         
  100.         // Applique le même filtrage de stock que allProductJson
  101.         $excludedDepots = LotNumber::EXCLUDED_DEPOTS;
  102.         foreach ($allProducts as $index => $prod) {
  103.             $quantity = 0;
  104.             if ($allProducts[$index]['lotNumber']) {
  105.                 // keep stock only for valid warehouses
  106.                 $allProducts[$index]['lotNumber'] = array_values(array_filter($allProducts[$index]['lotNumber'], function ($lot) use ($excludedDepots) {
  107.                     return $lot['depot'] !== LotNumber::PHYSICAL_DEPOT && $lot['stock'] > 0;
  108.                 }));
  109.                 if (count($allProducts[$index]['lotNumber']) === 1 && $allProducts[$index]['lotNumber'][0]['depot'] === LotNumber::ONLINE_DEPOT) {
  110.                     $quantity = $allProducts[$index]['lotNumber'][0]['stock'];
  111.                 } else {
  112.                     array_walk($allProducts[$index]['lotNumber'], function ($lot) use (&$quantity, $excludedDepots) {
  113.                         if (!in_array($lot['depot'], $excludedDepots)) {
  114.                             $quantity += $lot['stock'];
  115.                         }
  116.                     });
  117.                 }
  118.             }
  119.             $allProducts[$index]['quantity'] = $quantity;
  120.         }
  121.         
  122.         // Trouve le produit spécifique dans le tableau
  123.         $productData = null;
  124.         foreach ($allProducts as $prod) {
  125.             if ($prod['id'] == $id) {
  126.                 $productData = $prod;
  127.                 break;
  128.             }
  129.         }
  130.         
  131.         if (!$productData) {
  132.             throw $this->createNotFoundException('Produit non trouvé');
  133.         }
  134.         // Récupère l'entité produit pour les relations
  135.         $product = $manager->getRepository(Products::class)->find($id);
  136.         
  137.         // Filtre les lots valides avec DLU (pour l'affichage)
  138.         $validLots = [];
  139.         if (isset($productData['lotNumber']) && is_array($productData['lotNumber'])) {
  140.             foreach ($productData['lotNumber'] as $lotData) {
  141.                 if (isset($lotData['dlu']) && $lotData['dlu'] !== null && $lotData['stock'] > 0) {
  142.                     $validLots[] = $lotData;
  143.                 }
  144.             }
  145.             
  146.             // Trie par DLU (du plus lointain au plus proche)
  147.             usort($validLots, function($a, $b) {
  148.                 if (!isset($a['dlu']) || !isset($b['dlu'])) return 0;
  149.                 if (!($a['dlu'] instanceof \DateTime) || !($b['dlu'] instanceof \DateTime)) return 0;
  150.                 return $b['dlu']->getTimestamp() - $a['dlu']->getTimestamp();
  151.             });
  152.         }
  153.         // Calcul du prix pharmacie avec remise si applicable
  154.         $pricePharmacyWithRebate = null;
  155.         if ($information && $information->getCompanyTypePro() === 'Pharmacie' && $product->getPricePharmacy()) {
  156.             $percentPharmacy = $user->getAdditionalPercentage();
  157.             $pricePharmacyWithRebate = $product->getPricePharmacy() - ($product->getPricePharmacy() * $percentPharmacy / 100);
  158.         }
  159.         $crossedPriceDiscount = $crossedPriceDiscountService->resolve($product->getId(), $product->getPrice());
  160.         return $this->render('products/show.html.twig', [
  161.             'product' => $product,
  162.             'productData' => $productData,
  163.             'crossedPriceDiscount' => $crossedPriceDiscount,
  164.             'information' => $information,
  165.             'user' => $user,
  166.             'validLots' => $validLots,
  167.             'totalQuantity' => $productData['quantity'],
  168.             'pricePharmacyWithRebate' => $pricePharmacyWithRebate,
  169.             'checkControl' => $checkControl
  170.         ]);
  171.     }
  172.     #[Route('/productsJson/{brand}', name: 'productsJson')]
  173.     public function allProductJson(EntityManagerInterface $manager, CrossedPriceDiscountService $crossedPriceDiscountService, $brand = null): JsonResponse
  174.     {
  175.         $checkControl = $manager->getRepository(CustomerControl::class)->findOneBy(array('user' => $this->getUser(), 'finish' => false));
  176.         if (!$checkControl) {
  177.             $user = $this->getUser();
  178.         } else {
  179.             $user = $checkControl->getCustomer();
  180.         }
  181.         $information = $manager->getRepository(AdditionalCompanyInformation::class)->findOneBy(array('user' => $user));
  182.         if ($brand == null) {
  183.             $products = $manager->getRepository(Products::class)->getAllProduct($information);
  184.         } elseif ($brand == 'dlu-gooddeal') {
  185.             $products = $manager->getRepository(Products::class)->getAllProductWithGoodDeals($information);
  186.         } elseif ($brand == 'action-shock') {
  187.             $products = $manager->getRepository(Products::class)->getAllProductshockDeals($information);
  188.         } elseif ($brand == 'special-discount') {
  189.             $products = $manager->getRepository(Products::class)->getAllProductSpecialDiscount($information);
  190.         } elseif ($brand == 'action-special') {
  191.             // Combine les produits "action-shock" et "special-discount"
  192.             $productsActionShock = $manager->getRepository(Products::class)->getAllProductshockDeals($information);
  193.             $productsSpecialDiscount = $manager->getRepository(Products::class)->getAllProductSpecialDiscount($information);
  194.             
  195.             // Fusionne les deux tableaux
  196.             $products = array_merge($productsActionShock, $productsSpecialDiscount);
  197.             
  198.             // Élimine les doublons éventuels en se basant sur l'ID du produit
  199.             $uniqueProducts = [];
  200.             $uniqueIds = [];
  201.             foreach ($products as $product) {
  202.                 if (!in_array($product['id'], $uniqueIds)) {
  203.                     $uniqueIds[] = $product['id'];
  204.                     $uniqueProducts[] = $product;
  205.                 }
  206.             }
  207.             $products = $uniqueProducts;
  208.         } elseif ($brand == 'tools') {
  209.             $products = $manager->getRepository(Products::class)->getAllProductTools($information);
  210.         } else {
  211.             if ($brand == "Jacob") {
  212.                 $products = $manager->getRepository(Products::class)->getAllProductWithBrandJacob($brand, $information);
  213.             } else {
  214.                 $products = $manager->getRepository(Products::class)->getAllProductWithBrand($brand, $information);
  215.             }
  216.         }
  217.         // Liège === stock de la boutique physique
  218.         // online === stock boutique en ligne si produit pas mercator
  219.         $excludedDepots = LotNumber::EXCLUDED_DEPOTS;
  220.         foreach ($products as $index => $product) {
  221.             $quantity = 0;
  222.             if ($products[$index]['lotNumber']) {
  223.                 // keep stock only for valid warehouses
  224.                 $products[$index]['lotNumber'] = array_values(array_filter($products[$index]['lotNumber'], function ($lot) use ($excludedDepots) {
  225.                     return $lot['depot'] !== LotNumber::PHYSICAL_DEPOT && $lot['stock'] > 0;
  226.                 }));
  227.                 if (count($products[$index]['lotNumber']) === 1 && $products[$index]['lotNumber'][0]['depot'] === LotNumber::ONLINE_DEPOT) {
  228.                     $quantity = $products[$index]['lotNumber'][0]['stock'];
  229.                 } else {
  230.                     array_walk($products[$index]['lotNumber'], function ($lot) use (&$quantity, $excludedDepots) {
  231.                         if (!in_array($lot['depot'], $excludedDepots)) {
  232.                             $quantity += $lot['stock'];
  233.                         }
  234.                     });
  235.                 }
  236.             }
  237.             $products[$index]['quantity'] = $quantity;
  238.         }
  239.         $products = $crossedPriceDiscountService->decorateProducts($products);
  240.         return new JsonResponse($products, headers: ['Content-Type' => 'application/json;charset=UTF-8']);
  241.     }
  242. }