src/Controller/Admin/ProduitDeclinationValueController.php line 674

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Admin;
  3. use App\Service\ActivityService;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\Routing\Annotation\Route;
  9. use Symfony\Component\HttpFoundation\JsonResponse;
  10. use App\Entity\ProduitDeclinationValue;
  11. use App\Entity\GroupDeclinationValue;
  12. use App\Entity\Produit;
  13. use App\Repository\ProduitRepository;
  14. use App\Repository\DeclinationRepository;
  15. use App\Repository\ValueDeclinationRepository;
  16. use App\Repository\ProduitDeclinationValueRepository;
  17. use App\Repository\GroupDeclinationValueRepository;
  18. use App\Repository\FileRepository;
  19. use App\Entity\Stock;
  20. use App\Entity\File;
  21. use App\Form\UploadFileProduitDecType;
  22. use App\Form\FilterProduitType;
  23. use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
  24. use App\Form\FilterDocumentType;
  25. use App\Service\RightService;
  26. /**
  27.  * @Route("/produit/declination/value")
  28.  */
  29. class ProduitDeclinationValueController extends AbstractController {
  30.     use ImageControllerTrait;
  31.     use AccessTrait;
  32.     private $produitRepository;
  33.     private $declinationRepository;
  34.     private $valueDeclinationRepository;
  35.     private $produitDeclinationValueRepository;
  36.     private $groupDeclinationValueRepository;
  37.     private $fileRepository;
  38.     private $uploaderHelper;
  39.     private $rightService;
  40.     private $activityService;
  41.     public function __construct(
  42.             ProduitRepository $produitRepository,
  43.             ValueDeclinationRepository $valueDeclinationRepository,
  44.             DeclinationRepository $declinationRepository,
  45.             ProduitDeclinationValueRepository $produitDeclinationValueRepository,
  46.             FileRepository $fileRepository,
  47.             GroupDeclinationValueRepository $groupDeclinationValueRepository,
  48.             UploaderHelper $uploaderHelper,
  49.             RightService $rightService,
  50.             ActivityService $activityService
  51.     ) {
  52.         $this->produitRepository $produitRepository;
  53.         $this->valueDeclinationRepository $valueDeclinationRepository;
  54.         $this->declinationRepository $declinationRepository;
  55.         $this->produitDeclinationValueRepository $produitDeclinationValueRepository;
  56.         $this->groupDeclinationValueRepository $groupDeclinationValueRepository;
  57.         $this->fileRepository $fileRepository;
  58.         $this->uploaderHelper $uploaderHelper;
  59.         $this->rightService $rightService;
  60.         $this->activityService $activityService;
  61.     }
  62. /**
  63.  * @Route("/add", name="add_produit_declination_value", methods={"GET","POST"}, options={"expose"=true})
  64.  */
  65. public function add(Request $request,EntityManagerInterface $em): JsonResponse {
  66.     $listDeclinations = [];
  67.     foreach ($request->get('value_declination') as $item) {
  68.         $listDeclinations[] = $item['declinations'];
  69.     }
  70.     $duplicate $this->checkDuplicate($listDeclinations);
  71.     if( $duplicate) {
  72.         return new JsonResponse(array("success" => false"message" => $duplicate));
  73.     } else {
  74.         $this->createEntity($request,$em);
  75.         $request->getSession()->getFlashBag()->add('success'"les changements ont été enregistré");
  76.         return new JsonResponse(array("success" => true"path" => $this->generateUrl('produit_show', ['id' => $request->get('id_produit')])));
  77.     }
  78. }
  79. public function createEntity($request,EntityManagerInterface $em) {
  80.     foreach ($request->get('value_declination') as $item) {
  81.         if( $item['status'] === 'new') {
  82.             $entity = new ProduitDeclinationValue();
  83.             $entity->setBuyingPriceTtc($item['buyingPriceTtc'])
  84.                     ->setDescription($item['description'])
  85.                     ->setName($item['name'])
  86.                     ->setPriceHt($item['price_ht'])
  87.                     ->setReference($item['reference'])
  88.                     ->setCreatedAt(new \DateTime('now'));
  89.             $stock = new Stock();
  90.             if( $request->get('id_produit')) {
  91.                 $produit $this->produitRepository->find($request->get('id_produit'));
  92.                 $entity->setProduit($produit);
  93.                 $stock->setProduit($produit);
  94.             }
  95.             $stock->setQtReserved(0)
  96.                     ->setQtStock(0)
  97.                     ->setDeclinationProduit($entity)
  98.                     ->setStorehouse('Principal');
  99.             $em->persist($stock);
  100.             $em->persist($entity);
  101.             if( $item['declinations']) {
  102.                 foreach ($item['declinations'] as $key => $value) {
  103.                     $group = new GroupDeclinationValue();
  104.                     if( $value && $key) {
  105.                         $valueDeclination $this->valueDeclinationRepository->find($value);
  106.                         $declination $this->declinationRepository->find($key);
  107.                         $group->setValue($valueDeclination);
  108.                         $group->setDeclination($declination);
  109.                     }
  110.                     $group->setProduitDeclination($entity);
  111.                     $em->persist($group);
  112.                 }
  113.             }
  114.         } elseif( $item['status'] === 'update') {
  115.             $entity $this->produitDeclinationValueRepository->find($item['id']);
  116.             $entity->setBuyingPriceTtc($item['buyingPriceTtc'])
  117.                     ->setDescription($item['description'])
  118.                     ->setName($item['name'])
  119.                     ->setPriceHt($item['price_ht'])
  120.                     ->setReference($item['reference']);
  121.         } elseif( isset($item['id']) && $item['status'] === 'delete') {
  122.             $entity $this->produitDeclinationValueRepository->find($item['id']);
  123.             foreach ($entity->getGroupDeclinationValues() as $group) {
  124.                 $em->remove($group);
  125.             }
  126.             foreach ($entity->getStocks() as $stock) {
  127.                 $em->remove($stock);
  128.             }
  129.             $em->remove($entity);
  130.             $em->flush();
  131.         }
  132.     }
  133.     $em->flush();
  134. }
  135. public function checkDuplicate($listDeclinations) {
  136.     $result $this->return_dup($listDeclinations);
  137.     if( empty($result)) {
  138.         return null;
  139.     } else {
  140.         $text "";
  141.         foreach ($result as $group) {
  142.             foreach ($group as $value) {
  143.                 $valueDeclination $this->valueDeclinationRepository->find($value);
  144.                 $text $text ' ' $valueDeclination->getName();
  145.             }
  146.         }
  147.         $text $text " existe déjà, veuillez sélectionner une combinaison différente.";
  148.         return $text;
  149.     }
  150. }
  151. public function return_dup($arr) {
  152.     $dups = array();
  153.     $temp $arr;
  154.     foreach ($arr as $key => $item) {
  155.         unset($temp[$key]);
  156.         if( in_array($item$temp)) {
  157.             $dups[] = $item;
  158.         }
  159.     }
  160.     return $dups;
  161. }
  162. /**
  163.  * @Route("/listData/{idProduit}", name="list_produit_declination_value", methods="GET|POST", options = { "expose" =  true})
  164.  */
  165. /**
  166.  * @Route("/listData/{idProduit}", name="list_produit_declination_value", methods={"GET","POST"}, options={"expose"=true})
  167.  */
  168. public function listData($idProduitRequest $request) {
  169.     $pagination = (array) $request->get('pagination', []);
  170.     $page  max(0, (int)($pagination['page'] ?? 1) - 1);
  171.     $limit = (int)($pagination['perpage'] ?? 10);
  172.     $reference $request->get('reference');
  173.     // Nouveau: filtres multi-déclinaisons
  174.     $declinations $request->get('declinations', []);    
  175.     // Normalisation types: clés => int, valeurs => int|int[]
  176.     if (is_string($declinations)) {
  177.         // si le client envoie un JSON string
  178.         $decoded json_decode($declinationstrue);
  179.         if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
  180.             $declinations $decoded;
  181.         } else {
  182.             $declinations = [];
  183.         }
  184.     }
  185.     $declinations array_combine(
  186.         array_map('intval'array_keys($declinations)),
  187.         array_map(function ($v) {
  188.             return is_array($v) ? array_map('intval'$v) : (int)$v;
  189.         }, array_values($declinations))
  190.     );
  191.     $entities $this->produitDeclinationValueRepository->findProduitGroup(
  192.         $page$limit, (int)$idProduit$reference$declinations
  193.     );
  194.     $count $this->produitDeclinationValueRepository->countProduitGroup(
  195.         (int)$idProduit$reference$declinations
  196.     );
  197.     $output = [
  198.         'data' => [],
  199.         'meta' => [
  200.             'page'    => $page 1,
  201.             'perpage' => $limit,
  202.             'pages'   => (int)ceil($count max(1$limit)),
  203.             'total'   => (int)$count,
  204.         ],
  205.     ];
  206.     foreach ($entities as $entity) {
  207.         // Prix promo éventuel
  208.         $priceHt $entity->getPriceHt();
  209.         if ($entity->getProduit()->getPromotion()) {
  210.             $promotion $entity->getProduit()->getPromotion();
  211.             $now = new \DateTimeImmutable('now');
  212.             if ($promotion->getStartAt() <= $now && $promotion->getEndAt() >= $now) {
  213.                 $priceHt = ($promotion->getDiscountType() === 'percent')
  214.                     ? $entity->getPriceHt() - (($entity->getPriceHt() / 100) * $promotion->getDiscountValue())
  215.                     : $entity->getPriceHt() - $promotion->getDiscountValue();
  216.             }
  217.         }
  218.         $data = [
  219.             'id'              => $entity->getId(),
  220.             'name'            => $entity->getName(),
  221.             'reference'       => $entity->getReference(),
  222.             'buyingPriceTtc'  => ($this->isGranted('ROLE_SUPER_ADMIN')) ? number_format($entity->getBuyingPriceTtc(), 3) : '',
  223.             'price_ht'        => number_format($priceHt3),
  224.             'actions'         =>
  225.                 '<button class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" id="updateDeclination" data-id="' $entity->getId() . '" title="Modifier"><i class="fas fa-edit"></i></button>'
  226.               '<a href="' $this->generateUrl('produit_declination_value_show', ['id' => $entity->getId()]) . '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" title="Afficher" target="_blank"><i class="fa fa-clipboard"></i></a>',
  227.         ];
  228.         // Aplatit les paires declination -> value pour l’affichage tableau
  229.         foreach ($entity->getGroupDeclinationValues() as $group) {
  230.             $key strtolower($group->getDeclination()->getName());
  231.             $data[$key] = $group->getValue()->getName();
  232.         }
  233.         $output['data'][] = $data;
  234.     }
  235.     return new JsonResponse($output);
  236. }
  237. /*
  238. public function listData($idProduit, Request $request) {
  239.     $pagination = $request->get('pagination');
  240.     $page = $pagination['page'] - 1;
  241.     $limit = $pagination['perpage'];
  242.     $entities = $this->produitDeclinationValueRepository->findProduitGroup($page, $limit, $idProduit, $request->get('reference'), $request->get('couleur'));
  243.     $count = $this->produitDeclinationValueRepository->countProduitGroup($idProduit, $request->get('reference'), $request->get('couleur'));
  244.     $output = array(
  245.         'data' => array(),
  246.         'meta' => array(
  247.             'page' => $pagination['page'],
  248.             'perpage' => $limit,
  249.             "pages" => ceil($count / $limit),
  250.             "total" => $count,
  251.         )
  252.     );
  253.     foreach ($entities as $entity) {
  254.         //vérifier s'il ya une promotion
  255.         $priceHt = $entity->getPriceHt();
  256.         if( $entity->getProduit()->getPromotion()) {
  257.             $promotion = $entity->getProduit()->getPromotion();
  258.             $date = new \DateTime('now');
  259.             if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date)
  260.                 $priceHt =($promotion->getDiscountType() == 'percent')?
  261.                     $entity->getPriceHt() - ((($entity->getPriceHt() / 100) * $promotion->getDiscountValue()))
  262.                     : $entity->getPriceHt() - $promotion->getDiscountValue();
  263.         }
  264.         $data = [
  265.             'id' => $entity->getId(),
  266.             'name' => $entity->getName(),
  267.             'reference' => $entity->getReference(),
  268.             'buyingPriceTtc' => ($this->isGranted('ROLE_SUPER_ADMIN')) ? number_format($entity->getBuyingPriceTtc(), 3) : '',
  269.             'price_ht' => number_format($priceHt, 3),
  270.             //'createdAt' => $produit->getCreatedAt()?->format('Y-m-d H:i:s'),
  271.             'actions' => '<button class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" id="updateDeclination" data-id="' . $entity->getId() . '" title="Modifier"><i class="fa fa-pencil-square"></i></button><a href="' . $this->generateUrl('produit_declination_value_show', ['id' => $entity->getId()]) . '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" title="Afficher" target="_blank"><i class="fa fa-clipboard"></i></a>',
  272.         ];
  273.         foreach ($entity->getGroupDeclinationValues() as $group) {
  274.             $key = strtolower($group->getDeclination()->getName());
  275.             $value = $group->getValue()->getName();
  276.             $data[$key] = $value;
  277.         }
  278.         $output['data'][] = $data;
  279.     }
  280.     return new JsonResponse($output);
  281. }
  282. */
  283. /**
  284.  * @Route("/columns/{idProduit}", name="columns_produit_declination_value", methods="GET|POST", options = { "expose" =  true})
  285.  */
  286. public function listColumns($idProduitRequest $request) {
  287.     $produit $this->produitRepository->find($idProduit);
  288.     $output = [
  289.         [
  290.             'field' => 'reference',
  291.             'sortable' => "asc",
  292.             'width' => 140,
  293.             'title' => 'Réference'
  294.         ],
  295.         [
  296.             'field' => 'name',
  297.             'sortable' => "asc",
  298.             'title' => 'Nom commercial',
  299.             'width' => 250,
  300.         ]
  301.     ];
  302.     foreach ($produit->getDeclinations() as $entity) {
  303.         $output[] = [
  304.             'field' => strtolower($entity->getName()),
  305.             //'sortable' => "asc",
  306.             'width' => 60,
  307.             'textAlign' => 'center',
  308.             'title' => $entity->getName()
  309.         ];
  310.     }
  311.     if( $this->isGranted('ROLE_SUPER_ADMIN')) {
  312.         $output[] = [
  313.             'field' => 'buyingPriceTtc',
  314.             //'sortable' => "asc",
  315.             'width' => 80,
  316.             'textAlign' => 'center',
  317.             'title' => 'Prix Achat'
  318.         ];
  319.     }
  320.     
  321.     $output[] = [
  322.         'field' => 'price_ht',
  323.         //'sortable' => "asc",
  324.         'width' => 80,
  325.         'textAlign' => 'center',
  326.         'title' => 'Prix Vente'
  327.     ];
  328.     
  329.     $output[] = [
  330.         'field' => 'actions',
  331.         //'sortable' => "asc",
  332.         'width' => '70',
  333.         'textAlign' => 'center',
  334.         'title' => 'Actions'
  335.     ];
  336.     return new JsonResponse($output);
  337. }
  338. /**
  339.  * @Route("/edit/{id}", name="produit_declination_value_edit", methods={"GET","POST"}, options={"expose"=true})
  340.  */
  341. public function edit(Request $requestProduitDeclinationValue $produitDeclinationValue,EntityManagerInterface $em): Response {
  342.     if( $request->isMethod('POST')) {
  343.         $item $request->get('produit_declination_value');
  344.         $produitDeclinationValue->setBuyingPriceTtc($item['BuyingPriceTtc'])
  345.                 ->setDescription($item['description'])
  346.                 ->setName($item['name'])
  347.                 ->setPriceHt($item['PriceHt'])
  348.                 ->setReference($item['reference']);
  349.         $pictures $request->files->get('produit_declination_value')['picture'];
  350.         if( $request->request->get('selectFile')) {
  351.             $imageSelected $this->fileRepository->find($request->request->get('selectFile'));
  352.             $imageSelected->setIsSelected(true);
  353.             $em->flush();
  354.         }
  355.         foreach ($pictures as $key => $picture) {
  356.             if( !$request->request->get('selectFile') && $key == 0) {
  357.                 $file = new File();
  358.                 $file->setFile($picture);
  359.                 $file->SetIsSelected(true);
  360.                 $em->persist($file);
  361.                 $produitDeclinationValue->addPicture($file);
  362.             } else {
  363.                 $file = new File();
  364.                 $file->setFile($picture);
  365.                 $em->persist($file);
  366.                 $produitDeclinationValue->addPicture($file);
  367.             }
  368.         }
  369.         $em->flush();
  370.         $request->getSession()->getFlashBag()->add('success'"Déclinaison modifié avec succès");
  371.         return $this->redirect($this->generateUrl('produit_show', ['id' => $produitDeclinationValue->getProduit()->getId()]));
  372.         // return $this->redirectToRoute('produit_declination_value_show', ['id' => $produitDeclinationValue->getId(),'tab' => 'content_decli']);
  373.     }
  374.     return $this->render('@admin/produit_declination_value/_formModal.html.twig', [
  375.                 'produitDeclinationValue' => $produitDeclinationValue
  376.     ]);
  377. }
  378. /**
  379.  * @Route("/multi/add/{id}", name="produit_declination_value_multi_add", methods={"GET","POST"}, options={"expose"=true})
  380.  */
  381. public function multiAdd(Request $requestProduit $produit,EntityManagerInterface $em) {
  382.     if( $request->isMethod('POST')) {
  383.         $declinations $produit->getProduitDeclinationValues();
  384.         $list $this->listGroupDeclination($declinations);
  385.         if( $request->get('multi_add')) {
  386.             $newList = [];
  387.             $keys array_keys($request->get('multi_add')['declinationMulti']);
  388.             if( count($keys) == 1) {
  389.                 $declinationFixe $request->get('multi_add')['declinationFixe'];
  390.                 $declinationMulti $request->get('multi_add')['declinationMulti'];
  391.                 $newList = [];
  392.                 foreach ($declinationMulti as $multiKey => $multiValues) {
  393.                     foreach ($multiValues as $multiValue) {
  394.                         foreach ($declinationFixe as $fixeKey => $fixeValues) {
  395.                             foreach ($fixeValues as $fixeValue) {
  396.                                 // Vérifie que les deux sont valides
  397.                                 if (!empty($multiKey) && !empty($multiValue) && !empty($fixeKey) && !empty($fixeValue)) {
  398.                                     $group = [];
  399.                                     $group[$fixeKey] = $fixeValue;
  400.                                     $group[$multiKey] = $multiValue;
  401.                                     $newList[] = $group;
  402.                                 }
  403.                             }
  404.                         }
  405.                     }
  406.                 }
  407.                 $all array_merge($list$newList);
  408.                 $duplicate $this->checkDuplicate($all);
  409.                 if( $duplicate) {
  410.                     $request->getSession()->getFlashBag()->add('danger'$duplicate);
  411.                     return $this->redirect($this->generateUrl('produit_show', ['id' => $produit->getId()]));
  412.                 } else {
  413.                     foreach ($newList as $item) {
  414.                         $entity = new ProduitDeclinationValue();
  415.                         $entity->setBuyingPriceTtc($produit->getBuyingPriceTtc())
  416.                                 ->setDescription($produit->getDescription())
  417.                                 ->setPriceHt($produit->getPriceHt())
  418.                                 ->setCreatedAt(new \DateTime('now'))
  419.                                 ->setProduit($produit);
  420.                         $array = [];
  421.                         foreach ($item as $key => $value) {
  422.                             $group = new GroupDeclinationValue();
  423.                             if( $value && $key) {
  424.                                 $valueDeclination $this->valueDeclinationRepository->find($value);
  425.                                 $declination $this->declinationRepository->find($key);
  426.                                 $group->setValue($valueDeclination);
  427.                                 $group->setDeclination($declination);
  428.                                 $array[] = ['position' => $declination->getPosition(), 'name' => $valueDeclination->getName()];
  429.                             }
  430.                             $group->setProduitDeclination($entity);
  431.                             $em->persist($group);
  432.                         }
  433.                         $refrenece $produit->getReference();
  434.                         $name $produit->getName();
  435.                         foreach ($array as $item) {
  436.                             $refrenece $refrenece "-" strtolower($item['name']);
  437.                             $name $name " " strtolower($item['name']);
  438.                         }
  439.                         $entity->setReference($refrenece)
  440.                                 ->setName($name);
  441.                         if( isset($request->get('multi_add')['image']) && $request->get('multi_add')['image']) {
  442.                             $imageSelected $this->fileRepository->find($request->get('multi_add')['image']);
  443.                             $file = new File();
  444.                             $file->setFile($imageSelected->getFile());
  445.                             $file->setImageName($imageSelected->getFile()->getFilename());
  446.                             $file->setImageSize($imageSelected->getFile()->getSize());
  447.                             $file->SetIsSelected(true);
  448.                             $em->persist($file);
  449.                             $entity->addPicture($file);
  450.                         }
  451.                         $stock = new Stock();
  452.                         $stock->setQtReserved(0)
  453.                                 ->setProduit($produit)
  454.                                 ->setQtStock(0)
  455.                                 ->setDeclinationProduit($entity)
  456.                                 ->setStorehouse('Principal');
  457.                         $em->persist($stock);
  458.                         $em->persist($entity);
  459.                     }
  460.                     $em->flush();
  461.                     $declinaisonNames = [];
  462.                     foreach ($request->get('multi_add')['declinationFixe'] as $key => $valueIds) {
  463.                         foreach ($valueIds as $id) {
  464.                             if (!empty($id)) {
  465.                                 $dec $this->valueDeclinationRepository->find($id);
  466.                                 if ($dec && !in_array($dec->getName(), $declinaisonNames)) {
  467.                                     $declinaisonNames[] = $dec->getName();
  468.                                 }
  469.                             }
  470.                         }
  471.                     }
  472.                     return new JsonResponse(["success" => true,"message" => "Déclinaisons créées pour les déclinaisons : " implode(', '$declinaisonNames)]);
  473.                 }
  474.             }
  475.         }
  476.     }
  477.     /*
  478.     return $this->render('@admin/produit_declination_value/multi-add.html.twig', [
  479.                 'produit' => $produit
  480.     ]);
  481.     */
  482.     $fixe = [];
  483.     $multi = [];
  484.     foreach ($produit->getDeclinations() as $declination) {
  485.     if (stripos($declination->getName(), 'couleur') !== false) {
  486.         $fixe[] = $declination;
  487.     }
  488.     if (stripos($declination->getName(), 'taille') !== false) {
  489.         $multi[] = $declination;
  490.     }
  491.     }
  492.     return $this->render('admin/produit_declination_value/multi-add.html.twig', [
  493.     'produit' => $produit,
  494.     'fixe' => $fixe,
  495.     'multi' => $multi,
  496.     ]);
  497. }
  498. /**
  499.  * @Route("/multi/select", name="select_multi_add", methods={"GET","POST"}, options={"expose"=true})
  500.  */
  501. public function selectMulti(Request $request): Response {
  502.     $fixe = [];
  503.     $multi = [];
  504.     if( $request->get('multi_add')) {
  505.         foreach ($request->get('multi_add')['fixe'] as $id) {
  506.             $declination $this->declinationRepository->find($id);
  507.             $fixe[] = $declination;
  508.         }
  509.         foreach ($request->get('multi_add')['multiple'] as $id) {
  510.             $declination $this->declinationRepository->find($id);
  511.             $multi[] = $declination;
  512.         }
  513.     }
  514.     return $this->render('@admin/produit_declination_value/formule.html.twig', [
  515.                 'fixe' => $fixe,
  516.                 'multi' => $multi
  517.     ]);
  518. }
  519. public function listGroupDeclination($declinations) {
  520.     $list = [];
  521.     foreach ($declinations as $declination) {
  522.         $listGroup = [];
  523.         foreach ($declination->getGroupDeclinationValues() as $group) {
  524.             $listGroup[$group->getDeclination()->getId()] = (string) $group->getValue()->getId();
  525.         }
  526.         $list[] = $listGroup;
  527.     }
  528.     return $list;
  529. }
  530. public function sortAssociativeArrayByKey($array$key$direction) {
  531.     switch ($direction) {
  532.         case "ASC":
  533.             usort($array, function ($first$second) use ($key) {
  534.                 return $first[$key] <=> $second[$key];
  535.             });
  536.             break;
  537.         case "DESC":
  538.             usort($array, function ($first$second) use ($key) {
  539.                 return $second[$key] <=> $first[$key];
  540.             });
  541.             break;
  542.         default:
  543.             break;
  544.     }
  545.     return $array;
  546. }
  547. /**
  548.  * @Route("/search", name="search_item", methods="GET|POST", options = { "expose" =  true})
  549.  */
  550. public function searchItem(Request $request,EntityManagerInterface $em) {
  551.     $types=$request->get("type")??["declinaison"];
  552.     $entitiesproduit=$entities=[];       
  553.     if($types){
  554.         if(in_array("declinaison",$types)){
  555.             $entities $this->produitDeclinationValueRepository->searchItem($request->get('query'),$request->get('is_available'), count($types)==2?6:12);
  556.             foreach ($entities as &$entity) {
  557.                 //dd($entity);
  558.                 $qtReserved=$quantity 0;
  559.                 $entity['type'] = 'produitDeclination';
  560.                 $stocks $em->getRepository(Stock::class)->findByDeclinationProduit($entity['id']);
  561.                 foreach ($stocks as $stock) {
  562.                     $quantity $quantity + ($stock->getQtStock() - $stock->getQtReserved());
  563.                     $qtReserved+=$stock->getQtReserved();
  564.                 }
  565.                 $entity['quantity'] =( $quantity<=3  or $this->isGranted('ROLE_SUPER_ADMIN') or $this->currentUserHasRight('STOCK_SEE_REAL_STOCK') )?$quantity:"3<sup>+</sup>";
  566.                 $entity['qtReserved'] =$qtReserved;
  567.                 $produit $this->produitDeclinationValueRepository->find($entity['id'])->getProduit();
  568.                 $promotion=$produit->getPromotion();
  569.                 $entity['value'] = null;
  570.                 $entity['promotionType'] = null;
  571.                 $in_promo=false;
  572.                 $entity['price_ttc'] =$entity['price_ttc_without_promo'] = $produit->getPriceTtc();
  573.                 if( $promotion) {
  574.                     $date = new \DateTime('now');
  575.                     if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
  576.                         $entity['value'] = $promotion->getDiscountValue();
  577.                         $entity['promotionType'] = $promotion->getDiscountType();
  578.                         $entity['price_ttc'] =($promotion->getDiscountType() == 'percent')?
  579.                             $produit->getPriceTtc() - ((($produit->getPriceTtc() / 100) * $promotion->getDiscountValue()))
  580.                             : $produit->getPriceTtc() - $promotion->getDiscountValue();
  581.                         $in_promo=true;
  582.                     }
  583.                 }
  584.                 $entity['in_promo'] = $in_promo;
  585.                 $picture $this->getPicture($this->produitDeclinationValueRepository->find($entity['id']), $request);
  586.                 $entity['picture'] = $picture;                   
  587.                     
  588.             }
  589.         }
  590.         if(in_array("product",$types)) {
  591.             $entitiesproduit $this->produitRepository->searchItem($request->get('query'),count($types)==2?6:12);
  592.             foreach ($entitiesproduit as &$entity) {
  593.                 $qtReserved $quantity 0;
  594.                 $stocks $em->getRepository(Stock::class)->findByProduit($entity['id']);
  595.                 foreach ($stocks as $stock) {
  596.                     $quantity $quantity + ($stock->getQtStock() - $stock->getQtReserved());
  597.                     $qtReserved += $stock->getQtReserved();
  598.                 }
  599.                 $entity['quantity'] =( $quantity<=3  or $this->isGranted('ROLE_SUPER_ADMIN') or $this->currentUserHasRight('STOCK_SEE_REAL_STOCK') )?$quantity:"3<sup>+</sup>";
  600.                 $entity['qtReserved'] = $qtReserved;
  601.                 $produit $this->produitRepository->find($entity['id']);
  602.                 $promotion $produit->getPromotion();
  603.                 $in_promo false;
  604.                 $entity['value'] = null;
  605.                 $entity['promotionType'] = null;
  606.                 $entity['price_ttc'] = $entity['price_ttc_without_promo'] = $produit->getPriceTtc();
  607.                 if( $promotion) {
  608.                     $date = new \DateTime('now');
  609.                     if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
  610.                         $entity['value'] = $promotion->getDiscountValue();
  611.                         $entity['promotionType'] = $promotion->getDiscountType();
  612.                         $entity['price_ttc'] = ($promotion->getDiscountType() == 'percent') ?
  613.                             $produit->getPriceTtc() - ((($produit->getPriceTtc() / 100) * $promotion->getDiscountValue()))
  614.                             : $produit->getPriceTtc() - $promotion->getDiscountValue();
  615.                         $in_promo true;
  616.                     }
  617.                 }
  618.                 $entity['in_promo'] = $in_promo;
  619.                 $entity['type'] = 'produit';
  620.                 $picture $this->getPicture($this->produitRepository->find($entity['id']), $request);
  621.                 $entity['picture'] = $picture;
  622.             }
  623.         }
  624.     }
  625.     $data array_merge($entitiesproduit$entities);
  626.     return new JsonResponse($data);
  627. }
  628. /**
  629.  * @Route("/list", name="list_produit_declination_table", methods="GET|POST", options = { "expose" =  true})
  630.  */
  631. public function listDatatable(Request $requestProduitDeclinationValueRepository $produitDecRepository): JsonResponse
  632. {
  633.     // ---------- Pagination ----------
  634.     $pagination $request->get('pagination');
  635.     $page  max(0, (int)($pagination['page'] ?? 1) - 1);
  636.     $limit = (int)($pagination['perpage'] ?? 20);
  637.     // ---------- Champs de tri autorisés ----------
  638.     $allowedFields = ['reference''name''createdAt'];
  639.     $sortField 'createdAt';
  640.     $sortType  'DESC';
  641.     if (is_array($request->get('sort'))) {
  642.         $sort $request->get('sort');
  643.         if (isset($sort['field'], $sort['sort']) && in_array($sort['field'], $allowedFieldstrue)) {
  644.             $sortField $sort['field'];
  645.             $sortType  strtoupper($sort['sort']) === 'DESC' 'DESC' 'ASC';
  646.         }
  647.     }
  648.     /* === Filtres déclinaisons dynamiques (nouveau modèle)=== */
  649.     $declinationFilters $request->get('declinations', []);
  650.     // Cas où le JS envoie une string JSON
  651.     if (is_string($declinationFilters)) {
  652.         $decoded json_decode($declinationFilterstrue);
  653.         if (json_last_error() === JSON_ERROR_NONE) {
  654.             $declinationFilters $decoded;
  655.         } else {
  656.             $declinationFilters = [];
  657.         }
  658.     }
  659.     // Sécurisation types (int => int)
  660.     $declinationFilters array_combine(
  661.         array_map('intval'array_keys($declinationFilters)),
  662.         array_map('intval'array_values($declinationFilters))
  663.     );
  664.     
  665.     
  666.     // ---------- Recherche + Count ----------
  667.      $result $produitDecRepository->searchAndCountProduitDeclinations(
  668.         $page,
  669.         $limit,
  670.         $request->get('reference'),
  671.         $request->get('name'),
  672.         $request->get('categories'),
  673.         $declinationFilters,
  674.         $request->get('isAvailable'),
  675.         $request->get('inPromo'),
  676.         $request->get('qtMin'),
  677.         $request->get('qtMax'),
  678.         $request->get('buyingPriceMin'),
  679.         $request->get('buyingPriceMax'),
  680.         $request->get('priceMin'),
  681.         $request->get('priceMax'),
  682.         $sortField,
  683.         $sortType,
  684.         $request->get('withDeleted') == 'true'
  685.     );
  686.     $entities $result['data'];
  687.     $count    $result['total'];
  688.     
  689.     $entities $produitDecRepository->searchProduitDeclinations(
  690.         $page,
  691.         $limit,
  692.         $request->get('reference'),
  693.         $request->get('name'),
  694.         $request->get('categories'),
  695.         $declinationFilters,
  696.         $request->get('isAvailable'),
  697.         $request->get('inPromo'),
  698.         $request->get('qtMin'),
  699.         $request->get('qtMax'),
  700.         $request->get('buyingPriceMin'),
  701.         $request->get('buyingPriceMax'),
  702.         $request->get('priceMin'),
  703.         $request->get('priceMax'),
  704.         $sortField,
  705.         $sortType,
  706.         $request->get('withDeleted') == 'true'
  707.     );
  708.     $count $produitDecRepository->countProduitDeclinations(
  709.         $request->get('reference'),
  710.         $request->get('name'),
  711.         $request->get('categories'),
  712.         $declinationFilters,
  713.         $request->get('isAvailable'),
  714.         $request->get('inPromo'),
  715.         $request->get('qtMin'),
  716.         $request->get('qtMax'),
  717.         $request->get('buyingPriceMin'),
  718.         $request->get('buyingPriceMax'),
  719.         $request->get('priceMin'),
  720.         $request->get('priceMax'),
  721.         $request->get('withDeleted') == 'true'
  722.     );
  723.     
  724.     // ---------- Payload ----------
  725.     $output = [
  726.         'data' => [],
  727.         'meta' => [
  728.             'page'    => (int)($pagination['page'] ?? 1),
  729.             'perpage' => $limit,
  730.             'pages'   => (int)ceil(($limit ?: 1) ? $count max(1$limit) : 1),
  731.             'total'   => (int)$count,
  732.         ],
  733.     ];
  734.     // ---------- Build rows ----------
  735.     foreach ($entities as $entity) {
  736.         // Quantités (réel = dispo - réservé)
  737.         $quantity  0;
  738.         $qtReserved 0;
  739.         foreach ($entity->getStocks() as $stock) {
  740.             $quantity  += max(0$stock->getQtStock() - $stock->getQtReserved());
  741.             $qtReserved += (int)$stock->getQtReserved();
  742.         }
  743.         // === Nouvelle logique pour les deux 1ères déclinaisons dynamiques ===
  744.         $declinationGroups $entity->getGroupDeclinationValues()->toArray();
  745.         usort($declinationGroups, function($a$b) {
  746.             return $a->getDeclination()->getPosition() <=> $b->getDeclination()->getPosition();
  747.         });
  748.         $decli1 = isset($declinationGroups[0]) ? $declinationGroups[0] : null;
  749.         $decli2 = isset($declinationGroups[1]) ? $declinationGroups[1] : null;
  750.         $decli1Name $decli1 $decli1->getDeclination()->getName() : null;
  751.         $decli1Val  $decli1 $decli1->getValue()->getName() : null;
  752.         $decli2Name $decli2 $decli2->getDeclination()->getName() : null;
  753.         $decli2Val  $decli2 $decli2->getValue()->getName() : null;
  754.         // -------- Prix & Promo (sans HTML) --------
  755.         $base  = (float)$entity->getProduit()->getPriceTtc();
  756.         $final $base;
  757.         $promoActive  false;
  758.         $promoType    null;
  759.         $promoValue   null;
  760.         $promoPercent null;
  761.         $promotion $entity->getProduit()->getPromotion();
  762.         if ($promotion) {
  763.             $now = new \DateTime('now');
  764.             if ($promotion->getStartAt() <= $now && $promotion->getEndAt() >= $now) {
  765.                 $promoActive true;
  766.                 $promoType   $promotion->getDiscountType();
  767.                 $promoValue  = (float)$promotion->getDiscountValue();
  768.                 if ($promoType === 'percent') {
  769.                     $final        round($base * ($promoValue 100), 3);
  770.                     $promoPercent = (int)round($promoValue);
  771.                 } else {
  772.                     $final        round(max(0$base $promoValue), 3);
  773.                     $promoPercent $base ? (int)round((($base $final) / $base) * 100) : 0;
  774.                 }
  775.             }
  776.         }
  777.         // -------- Image --------
  778.         $pictures $entity->getPicture()->filter(fn($p) => $p->getIsSelected() == 1);
  779.         $pictureName $pictures->isEmpty() ? null$pictures->first()->getImageName();
  780.         if (
  781.             !$pictures->isEmpty()
  782.             && file_exists($this->getParameter('kernel.project_dir') . "/public/images/" $pictureName)
  783.             && !file_exists($this->getParameter('kernel.project_dir') . "/public/images/thumbs/" $pictureName)
  784.         ) {
  785.             $this->resizeImage($pictureName);
  786.         }
  787.         // -------- Masquage stock réel si non autorisé --------
  788.         $canSeeRealStock $this->isGranted('ROLE_SUPER_ADMIN') || $this->currentUserHasRight('STOCK_SEE_REAL_STOCK');
  789.         $quantityDisplay = ($quantity <= || $canSeeRealStock) ? $quantity "3<sup>+</sup>";
  790.         // -------- Ligne --------
  791.         $output['data'][] = [
  792.             'id'           => $entity->getId(),
  793.             'image'        => $pictureName,
  794.             'name'         => $entity->getName(),
  795.             'reference'    => $entity->getReference(),
  796.             'parent'       => $entity->getProduit()->getReference(),
  797.             'parent_id'    => $entity->getProduit()->getId(),
  798.             'parent_name'  => $entity->getProduit()->getName(),
  799.             'buyingPriceTtc' => $this->isGranted('ROLE_SUPER_ADMIN') ? (float)$entity->getBuyingPriceTtc() : null,
  800.             'price_ht'     => (float)$entity->getPriceHt(),
  801.             // >>> PRIX
  802.             'price_ttc'          => $final,
  803.             'price_ttc_final'    => $final,
  804.             'price_ttc_original' => $base,
  805.             'promo_active'       => $promoActive,
  806.             'promo_type'         => $promoType,
  807.             'promo_value'        => $promoValue,
  808.             'promo_percent'      => $promoPercent,
  809.             // >>> NOUVEAUX CHAMPS DECLINAISONS
  810.             'decli1_name'  => $decli1Name,
  811.             'decli1_value' => $decli1Val,
  812.             'decli2_name'  => $decli2Name,
  813.             'decli2_value' => $decli2Val,
  814.             // Pour compat héritage (peut servir pour les filtres fixes)
  815.             //'taille'      => $taille ?? null,
  816.             //'couleur'     => $couleur ?? null,
  817.             'quantity'    => $quantityDisplay,
  818.             'qtReserved'  => $qtReserved,
  819.             'createdAt'   => $entity->getCreatedAt()?->format('Y-m-d\TH:i:s'),
  820.             'inPromo'     => $promoActive,
  821.         ];
  822.     }
  823.     return new JsonResponse($output);
  824. }
  825. /**
  826.  * @Route("/show/{id}", name="produit_declination_value_show", methods={"GET"}, options={"expose"=true})
  827.  */
  828. public function show(ProduitDeclinationValue $declinaisonRequest $request): Response {
  829.     $rights $this->rightService->getAllRights($this->getUser());
  830.     if( !in_array('PRODUIT'$rights)) {
  831.         $request->getSession()->getFlashBag()->add('danger'"Accès refusé");
  832.         return $this->redirect($this->generateUrl('index'));
  833.     }
  834.     $formFile $this->createForm(UploadFileProduitDecType::class, $declinaison);
  835.     $form $this->createForm(FilterDocumentType::class);
  836.     return $this->render('@admin/produit_declination_value/fiche_declinaison_produit.html.twig', [
  837.                 'produit' => $declinaison,
  838.                 'formFile' => $formFile->createView(),
  839.                 'form' => $form->createView(),
  840.                 'rights' => $rights
  841.     ]);
  842. }
  843. /**
  844.  * @Route("/", name="produit_declination_value_index", methods={"GET"},options = { "expose" =  true})
  845.  */
  846. public function index(Request $request): Response {
  847.     $rights $this->rightService->getAllRights($this->getUser());
  848.     if (!in_array('PRODUIT'$rights)) {
  849.         $request->getSession()->getFlashBag()->add('danger'"Accès refusé");
  850.         return $this->redirect($this->generateUrl('index'));
  851.     }
  852.     $form $this->createForm(FilterProduitType::class);
  853.     // ⚠️ on ne touche pas à l’existant
  854.     $declinations        $this->declinationRepository->findAll();
  855.     // ✅ nouvelle liste pour le filtre uniquement
  856.     $declinationsFilter  $this->declinationRepository->findAllForFilter();
  857.     return $this->render('@admin/produit_declination_value/list_declinaisons_produits.html.twig', [
  858.         'form'               => $form->createView(),
  859.         'declinations'       => $declinations,        // reste disponible ailleurs
  860.         'declinationsFilter' => $declinationsFilter,  // utilisé par le filtre UI
  861.         'rights'             => $rights,
  862.     ]);
  863. }
  864. /**
  865.  * @Route("/stock/edit/{id}", name="stock_edit", methods="GET|POST", options = { "expose" =  true})
  866.  */
  867. public function editStock(Request $requestStock $stock,EntityManagerInterface $em) {
  868.     if( $request->isMethod('POST')) {
  869.         $stock->setQtStock((int) $request->get('edit_stock')['qt_total']);
  870.         $em->flush();
  871.         $request->getSession()->getFlashBag()->add('success'"Stock modifié avec succès");
  872.     }
  873.     return $this->redirectToRoute('produit_declination_value_show', ['id' => $stock->getDeclinationProduit()->getId()]);
  874. }
  875. public function getPicture($entity$request): string
  876. {
  877.     $baseurl $request->getScheme() . '://' $request->getHttpHost() . $request->getBasePath();
  878.     if ($entity->getPicture()) {
  879.         foreach ($entity->getPicture() as $item) {
  880.             if ($item->getIsSelected()) {
  881.                 $path $this->uploaderHelper->asset($item'file');
  882.                 if ($path) {
  883.                     return $baseurl $path;
  884.                 }
  885.             }
  886.         }
  887.     }
  888.     // Aucune image sélectionnée
  889.     return '';
  890. }
  891. /**
  892.  * @Route("/file/{id}", name="produit_declination_value_file", methods={"GET","POST"}, options={"expose"=true})
  893.  */
  894. public function addFile(Request $requestProduitDeclinationValue $produit,EntityManagerInterface $em): Response {
  895.     $form $this->createForm(UploadFileProduitDecType::class, $produit);
  896.     $form->handleRequest($request);
  897.     if( $form->isSubmitted() && $form->isValid()) {
  898.         $pictures $request->files->get('upload_file_produit_dec')['picture'];
  899.         if( $request->request->get('selectFile')) {
  900.             foreach ($produit->getPicture() as $picture) {
  901.                 if( $picture->getIsSelected() == true) {
  902.                     $picture->setIsSelected(null);
  903.                     break;
  904.                 }
  905.             }
  906.             $imageSelected $this->fileRepository->find($request->request->get('selectFile'));
  907.             $imageSelected->setIsSelected(true);
  908.             $em->flush();
  909.         }
  910.         foreach ($pictures as $key => $picture) {
  911.             if( !$request->request->get('selectFile') && $key == 0) {
  912.                 $file = new File();
  913.                 $file->setFile($picture);
  914.                 $file->SetIsSelected(true);
  915.                 $em->persist($file);
  916.                 $produit->addPicture($file);
  917.                 $em->flush();
  918.             } else {
  919.                 $file = new File();
  920.                 $file->setFile($picture);
  921.                 $em->persist($file);
  922.                 $produit->addPicture($file);
  923.                 $em->flush();
  924.             }
  925.         }
  926.         $request->getSession()->getFlashBag()->add('success'"Image ajouté avec succès");
  927.         return $this->redirect($this->generateUrl('produit_declination_value_show', ['id' => $produit->getId()]));
  928.     }
  929.     return false;
  930. }
  931. /**
  932.  * @Route("/searchgroup", name="get_group_produit", methods="GET|POST", options = { "expose" =  true})
  933.  */
  934. public function searchGroupProduit(Request $request,EntityManagerInterface $em) {
  935.     $result = [];
  936.     $entities $this->produitDeclinationValueRepository->findDeclinationValueWithDeclination($request->get('color'), $request->get('produit'));
  937.     $entities $this->orderWithSise($entities);
  938.     foreach ($entities as $entity) {
  939.         $quantity 0;
  940.         $array['type'] = 'produitDeclination';
  941.         $stocks $em->getRepository(Stock::class)->findByDeclinationProduit($entity->getId());
  942.         foreach ($stocks as $stock) {
  943.             $quantity $quantity + ($stock->getQtStock() - $stock->getQtReserved());
  944.         }
  945.         $promotion $this->produitDeclinationValueRepository->find($entity->getId())->getProduit()->getPromotion();
  946.         $array['value'] = null;
  947.         $array['promotionType'] = null;
  948.         if( $promotion) {
  949.             $date = new \DateTime('now');
  950.             if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
  951.                 $array['value'] = $promotion->getDiscountValue();
  952.                 $array['promotionType'] = $promotion->getDiscountType();
  953.             }
  954.         }
  955.         $picture $this->getPicture($this->produitDeclinationValueRepository->find($entity->getId()), $request);
  956.         $array['picture'] = $picture;
  957.         $array['quantity'] = $quantity;
  958.         $array['id'] = $entity->getId();
  959.         $array['name'] = $entity->getName();
  960.         $array['reference'] = $entity->getReference();
  961.         $array['description'] = $entity->getDescription();
  962.         $array['price_ht'] = $entity->getPriceHt();
  963.         $array['price_ttc'] = $entity->getProduit()->getPriceTtc();
  964.         $array['unit'] = $entity->getProduit()->getUnit();
  965.         $array['buyingPriceTtc'] = $entity->getBuyingPriceTtc();
  966.         $array['tva'] = $entity->getProduit()->getTva() ? $entity->getProduit()->getTva()->getNumber() : 0;
  967.         $array['tva_id'] = $entity->getProduit()->getTva() ? $entity->getProduit()->getTva()->getId() : 1;
  968.         $result[] = $array;
  969.     }
  970.     return new JsonResponse($result);
  971. }
  972. public function orderWithSise($entities) {
  973.     $allSize = ["XS""S""M""L""XL""XXL""XXXL""XXXXL"];
  974.     $isLetter false;
  975.     if($entities)
  976.         foreach ($entities[0]->getGroupDeclinationValues() as $group) {
  977.             if( $group->getDeclination()->getName() == "Taille") {
  978.                 if( in_array($group->getValue()->getName(), $allSize)) $isLetter true;
  979.             }
  980.         }
  981.     if( $isLetter == true) {
  982.         usort($entities, function ($a$b) use ($allSize) {
  983.             foreach ($a->getGroupDeclinationValues() as $group) {
  984.                 if( $group->getDeclination()->getName() == "Taille")
  985.                     $pos_a array_search($group->getValue()->getName(), $allSize);
  986.             }
  987.             foreach ($b->getGroupDeclinationValues() as $group) {
  988.                 if( $group->getDeclination()->getName() == "Taille")
  989.                     $pos_b array_search($group->getValue()->getName(), $allSize);
  990.             }
  991.             return $pos_a $pos_b;
  992.         });
  993.     }
  994.     return $entities;
  995. }
  996. /**
  997.  * @Route("/info/edit/{id}", name="produit_dec_info_edit", methods={"GET","POST"}, options={"expose"=true})
  998.  */
  999. public function editInfo(Request $requestProduitDeclinationValue $produit,EntityManagerInterface $em): Response {
  1000.     $this->hasRight($request,'PRODUIT_UPDATE');
  1001.     if( $request->isMethod('POST') && $request->get('form_info')) {
  1002.         $produit->setDescription($request->get('form_info')['description']);
  1003.         $produit->setName($request->get('form_info')['name']);
  1004.         $message $this->getUser()->getFirstName() . " a modifié l'info de Déclinaison  " $produit->getReference();
  1005.         $this->activityService->addActivity('info'$message$produit->getProduit(), $this->getUser(), 'produit');
  1006.         $em->flush();
  1007.         return $this->redirectToRoute('produit_declination_value_show', ['id' => $produit->getId(),"tab"=>"content_informations"]);
  1008.     }
  1009.     return false;
  1010. }
  1011. }