src/Controller/StockController.php line 86

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Category;
  4. use App\Entity\Discount;
  5. use App\Entity\DiscountProducts;
  6. use App\Entity\Pack;
  7. use App\Entity\PackProducts;
  8. use App\Entity\Product;
  9. use App\Entity\Shop;
  10. use App\Entity\Stock;
  11. use App\Entity\User;
  12. use App\Form\StockFormType;
  13. use App\Repository\ProductRepository;
  14. use App\Repository\ShopRepository;
  15. use App\Repository\StockRepository;
  16. use App\Service\CrmLogger;
  17. use App\Service\Tools;
  18. use Doctrine\ORM\EntityManagerInterface;
  19. use Mike42\Escpos\PrintConnectors\WindowsPrintConnector;
  20. use Mike42\Escpos\Printer;
  21. use Picqer\Barcode\Barcode;
  22. use Picqer\Barcode\BarcodeGenerator;
  23. use Picqer\Barcode\BarcodeGeneratorHTML;
  24. use Picqer\Barcode\BarcodeGeneratorPNG;
  25. use Saci\Escpos\PrintConnectors\FilePrintConnector;
  26. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  27. use Symfony\Component\HttpFoundation\JsonResponse;
  28. use Symfony\Component\HttpFoundation\Request;
  29. use Symfony\Component\HttpFoundation\Response;
  30. use Symfony\Component\Routing\Annotation\Route;
  31. use function MongoDB\BSON\fromJSON;
  32. class StockController extends AppController
  33. {
  34.     /**
  35.      * @Route("/stocks", name="app_stocks")
  36.      */
  37.     public function index1(EntityManagerInterface $entityManager): Response
  38.     {
  39.         /** @var Category[] $categories */
  40.         $categories $entityManager->getRepository(Category::class)->findBy([],['label' => 'asc']);
  41.         return $this->renderWithParams('stocks/index.html.twig', [
  42.             'catsFilter' => $categories
  43.         ]);
  44.     }
  45.     /**
  46.      * @Route("/details-stock/{productId}/{shopId?}", name="app_stock_details")
  47.      */
  48.     public function stockDetails$productIdEntityManagerInterface $entityManagerProductRepository $productRepositoryShopRepository $shopRepository, ?string $shopId null): Response
  49.     {
  50.         /** @var Product $product */
  51.         $product $productRepository->find($productId);
  52.         /** @var Shop $shop */
  53.         $shop $shopId $shopRepository->find($shopId) : null;
  54.         /** @var Category[] $categories */
  55.         $categories $entityManager->getRepository(Category::class)->findBy([],['label' => 'asc']);
  56.         if($this->getParameter('app.local') !== null){
  57.             $local $this->getParameter('app.local') == 'CTR' 'Centre Ville''Bensouna';
  58.         }else{
  59.             $local 'Bensouna';
  60.         }
  61.         return $this->renderWithParams('stocks/details.html.twig', [
  62.             'catsFilter' => $categories,
  63.             'product' => $product,
  64.             'selectedShop' => $shop,
  65.             'local' => $local
  66.         ]);
  67.     }
  68.     /**
  69.      * @Route("/stock-creation/{productId?}", name="create_stock")
  70.      */
  71.     public function createRequest $request,  EntityManagerInterface $em ,?string $productId nullCrmLogger $crmLogger): Response
  72.     {
  73.         $stock = new Stock();
  74.         $stock->setCreationDate(new \DateTime());
  75.         $stock->setIsSynch(true);
  76.         $stock->setTarget($this->getParameter('app.target'));
  77.         $stock->setDeleted(false);
  78.         /** @var Product $productSelec */
  79.         $productSelec $productId $em->getRepository(Product::class)->find($productId) : null;
  80.         $stock->setProduct($productSelec);
  81.         $creationForm $this->createForm(StockFormType::class, $stock, []);
  82.         // Gestion de requête du formulaire
  83.         $creationForm->handleRequest($request);
  84.         // Validation du formulaire
  85.         if ($creationForm->isSubmitted() && $creationForm->isValid()) {
  86.             try {
  87.                 $shops $em->getRepository(Shop::class)->findAll();
  88.                 /** @var Product $selectedProduct */
  89.                 $selectedProduct $em->getRepository(Product::class)->find($creationForm->get('selectedProduct')->getViewData());
  90.                 if($selectedProduct != null) {
  91.                     $stock->setProduct($selectedProduct);
  92.                 }
  93.                 foreach ($shops as $key => $shop){
  94.                     $existing true;
  95.                     $lot '';
  96.                     $qteShop $creationForm->get('shop_'.($key+1))->getData();
  97.                     $stockShop = clone $stock;
  98.                     if($productSelec != null && $stockShop->getProduct()->getId() != $productSelec->getId()){
  99.                         $this->addFlash(
  100.                             'danger',
  101.                             "Erreur | Le produit sélectionné ne correspond pas au stock actuel"
  102.                         );
  103.                        $crmLogger->log(
  104.                            'ERREUR',
  105.                             'STOCK_CREATION_MISMATCH_PRODUCT',
  106.                             'Le produit sélectionné ne correspond pas au stock actuel ID Produit Formulaire : '.$stockShop->getProduct()->getId().' | ID Produit URL : '.$productSelec->getId(),
  107.                             $shop->getLabel(),
  108.                             $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  109.                        );
  110.                         return $this->renderWithParams('stocks/create.html.twig', [
  111.                             'form' => $creationForm->createView(),
  112.                             'edition' => false
  113.                         ]);
  114.                     }
  115.                     $stockShop->setInitialQuantity($qteShop);
  116.                     $stockShop->setQuantity($qteShop);
  117.                     $stockShop->setShop($shop);
  118.                     while ($existing){
  119.                         $lot $this->guessLot($stockShop);
  120.                         $stockLot $em->getRepository(Stock::class)->findBy(['lot'=>$lot]);
  121.                         if(empty($stockLot)){
  122.                            $existing false;
  123.                         }
  124.                     }
  125.                     $stockShop->setLot($lot);
  126.                     if($stockShop->getQuantity() > ){
  127.                         $em->persist($stockShop);
  128.                         $crmLogger->log(
  129.                             'CREATION',
  130.                             'STOCK_CREATION_SUCCESS',
  131.                             sprintf('Produit : %d %s barcode : %s Qte %d prix %s pmp %s'$stockShop->getProduct()->getId(), $stockShop->getProduct()->getLabel(), $stockShop->getBarcode(), $stockShop->getQuantity(), $stockShop->getUnitAmount(), $stockShop->getPmpAmount()),
  132.                             $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  133.                             $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  134.                         );
  135.                         // 04/05/2024 => application du prix de vente à tous les stocks du même article.
  136.                         $otherStocks $stockShop->getProduct()->getStocks()->toArray();
  137.                         $stocksPropaged '';
  138.                         foreach ($otherStocks as $stk){
  139.                             $stk->setUnitAmount($stockShop->getUnitAmount());
  140.                             if($stk->getPackageCount() == $stockShop->getPackageCount()){
  141.                                 $stk->setPackagePrice($stockShop->getPackagePrice());
  142.                             }
  143.                             $stk->setPmpAmount($stockShop->getPmpAmount());
  144. //                            $stk->setPurchasePrice($stock->getPurchasePrice());
  145.                             $stk->setPriceChange(true);
  146.                             $stk->setTarget($this->getParameter('app.target'));
  147.                             $em->persist($stk);
  148.                             $stocksPropaged .= $stk->getId() .' | ';
  149.                         }
  150.                         $crmLogger->log(
  151.                             'PROPAGATION',
  152.                             'STOCK_PROPAGATION_CREATION_SUCCESS',
  153.                             sprintf('List des stocks propagés : %s '$stocksPropaged),
  154.                             $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  155.                             $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  156.                         );
  157.                     }
  158.                 }
  159.                 $em->flush();
  160.             } catch (\Exception $exception) {
  161.                 $this->addFlash(
  162.                     'danger',
  163.                     "Erreur | " $exception->getMessage()
  164.                 );
  165.                 $crmLogger->log(
  166.                     'ERREUR',
  167.                     'STOCK_CREATION_EXCEPTION',
  168.                     'Exception lors de la création du stock : '.$exception->getMessage(),
  169.                     $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  170.                     $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  171.                 );
  172.                 return $this->renderWithParams('stocks/create.html.twig', [
  173.                     'form' => $creationForm->createView(),
  174.                     'edition' => false,
  175.                     'product' => $productSelec
  176.                 ]);
  177.             }
  178.             $this->addFlash(
  179.                 'success',
  180.                 'Stock enregistré.'
  181.             );
  182.             if($productSelec != null){
  183.                 return $this->redirectToRoute('app_stock_details', ['productId' => $productSelec->getId()]);
  184.             }else{
  185.                 return $this->redirectToRoute('app_stocks');
  186.             }
  187.         }
  188.         return $this->renderWithParams('stocks/create.html.twig', [
  189.             'form' => $creationForm->createView(),
  190.             'edition' => false,
  191.             'product' => $productSelec
  192.         ]);
  193.     }
  194.     /**
  195.      * @Route("/stock-edition/{id}", name="edit_stock")
  196.      */
  197.     public function edit($idRequest $requestEntityManagerInterface $emCrmLogger $crmLogger): Response
  198.     {
  199.         /** @var Stock $stock */
  200.         $stock $em->getRepository(Stock::class)->find($id);
  201.         $creationForm $this->createForm(StockFormType::class, $stock, []);
  202.         // Gestion de requête du formulaire
  203.         $creationForm->handleRequest($request);
  204.         // Validation du formulaire
  205.         if ($creationForm->isSubmitted() && $creationForm->isValid()) {
  206.             try {
  207.                 $otherStocks $stock->getProduct()->getStocks()->toArray();
  208.                 $stocksPropaged '';
  209.                 foreach ($otherStocks as $stk){
  210.                     $stk->setUnitAmount($stock->getUnitAmount());
  211.                     if($stk->getPackageCount() == $stock->getPackageCount()){
  212.                         $stk->setPackagePrice($stock->getPackagePrice());
  213.                     }
  214.                     $stk->setPmpAmount($stock->getPmpAmount());
  215.                     // on propage le prix d'achat que si c'est le même lot de l'autre boutique code barres + date de création
  216.                     if($stk->getBarcode() === $stock->getBarcode() and $stk->getCreationDate() == $stock->getCreationDate()){
  217.                         $stk->setPurchasePrice($stock->getPurchasePrice());
  218.                     }
  219.                     $stk->setPriceChange(true);
  220.                     $stk->setTarget($this->getParameter('app.target'));
  221.                     $em->persist($stk);
  222.                     $stocksPropaged .= $stk->getId() .' | ';
  223.                 }
  224.                 $crmLogger->log(
  225.                     'PROPAGATION',
  226.                     'STOCK_PROPAGATION_MODIF_SUCCESS',
  227.                     sprintf('List des stocks propagés : %s '$stocksPropaged),
  228.                     $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  229.                     $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  230.                 );
  231.                 $stock->setIsSynch(true);
  232.                 $stock->setTarget($this->getParameter('app.target'));
  233.                 $em->persist($stock);
  234.                 $em->flush();
  235.                 $crmLogger->log(
  236.                     'MODIFICATION',
  237.                     'STOCK_MODIF_SUCCESS',
  238.                     sprintf('Modification du stock ID : %d | Produit : %d %s barcode : %s Qte %d prix %s pmp %s'$stock->getId(), $stock->getProduct()->getId(), $stock->getProduct()->getLabel(), $stock->getBarcode(), $stock->getQuantity(), $stock->getUnitAmount(), $stock->getPmpAmount()),
  239.                     $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  240.                     $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  241.                 );
  242.             } catch (\Exception $exception) {
  243.                 $this->addFlash(
  244.                     'danger',
  245.                     "Erreur | " $exception->getMessage()
  246.                 );
  247.                 return $this->redirectToRoute('app_stocks');
  248.             }
  249.             $this->addFlash(
  250.                 'success',
  251.                 'Stock enregistré.'
  252.             );
  253.             return $this->redirectToRoute('app_stocks');
  254.         }
  255.         return $this->renderWithParams('stocks/create.html.twig', [
  256.             'form' => $creationForm->createView(),
  257.             'edition' => true,
  258.             'stockId' => $stock->getId()
  259.         ]);
  260.     }
  261.     /**
  262.      * @Route("/stock-remove/{id}", name="remove_stock")
  263.      */
  264.     public function remove($idRequest $requestEntityManagerInterface $emCrmLogger $crmLogger): Response
  265.     {
  266.         /** @var Stock $stock */
  267.         $stock $em->getRepository(Stock::class)->find($id);
  268.         try {
  269.             $stock->setIsSynch(true);
  270.             $stock->setTarget($this->getParameter('app.target'));
  271.             $stock->setDeleted(true);
  272.             $em->persist($stock);
  273.             $em->flush();
  274.             $crmLogger->log(
  275.                 'SUPPRESSION',
  276.                 'STOCK_DELETION_SUCCESS',
  277.                 sprintf('Suppression du stock ID : %d | Produit : %d %s barcode : %s Qte %d prix %s pmp %s'$stock->getId(), $stock->getProduct()->getId(), $stock->getProduct()->getLabel(), $stock->getBarcode(), $stock->getQuantity(), $stock->getUnitAmount(), $stock->getPmpAmount()),
  278.                 $stock->getShop() ? $stock->getShop()->getLabel() : 'N/A',
  279.                 $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  280.             );
  281.         } catch (\Exception $exception) {
  282.             $this->addFlash(
  283.                 'danger',
  284.                 "Erreur | " $exception->getMessage()
  285.             );
  286.             return $this->redirectToRoute('app_stocks');
  287.         }
  288.         $this->addFlash(
  289.             'success',
  290.             'Stock supprimé.'
  291.         );
  292.         return $this->redirectToRoute('app_stocks');
  293.     }
  294.     /**
  295.      * @Route("/stock-get-product/{code}", name="get_stock_product")
  296.      */
  297.     public function getStockProduct($codeRequest $requestEntityManagerInterface $em): JsonResponse
  298.     {
  299.         $product=[];
  300.         if($this->getUser() != null){
  301.             /** @var User $user */
  302.             $user $em->getRepository(User::class)->findOneBy(['username' => $this->getUser()->getUserIdentifier()]);
  303.         }else{
  304.             /** @var User $user */
  305.             $user $em->getRepository(User::class)->find(5);
  306.         }
  307.         $shop $em->getRepository(Shop::class)->find($user->getCurrentshop());
  308.         /** @var Stock $stock */
  309.         $stock $em->getRepository(Stock::class)->findOneBy(['packageBarcode' => $code'shop'=> $shop->getId(), 'deleted' => false], ['id' => 'desc']);
  310.         if($stock != null){
  311.             $rate 0;
  312.             $min 0;
  313.             $stock->getProduct()->getDiscountedProducts();
  314.             /** @var DiscountProducts $discount */
  315.             foreach ($stock->getProduct()->getDiscountedProducts()->toArray() as $discount){
  316.                 if($discount->getDiscount()->isStatus()){
  317.                     $rate $discount->getDiscount()->getRate();
  318.                     $min $discount->getDiscount()->getMinimum();
  319.                 }
  320.             }
  321.             $product = [
  322.                 'id' => $stock->getProduct()->getId(),
  323.                 'label' => $stock->getPackageName() != null $stock->getPackageName() : $stock->getProduct()->getLabel(),
  324.                 'price' => $stock->getPackagePrice() != null $stock->getPackagePrice() : $stock->getUnitAmount(),
  325.                 'rate' => $rate,
  326.                 'min' => $min,
  327.                 'code' => $stock->getBarcode(),
  328.                 'weight' => $stock->getProduct()->getUnit() == 'WEIGHT',
  329.                 'sold' => $rate != 0,
  330.                 'pack' => false,
  331.                 'package' => true,
  332.                 'packageCount' => $stock->getPackageCount()
  333.             ];
  334.         }
  335.         /** @var Stock $stock */
  336.         $stock $em->getRepository(Stock::class)->findOneBy(['barcode' => $code'shop'=> $shop->getId(), 'deleted' => false], ['id' => 'desc']);
  337.         if($stock != null /*and $stock->getQuantity() > 0*/ ){
  338.             $rate 0;
  339.             $min 0;
  340.             $stock->getProduct()->getDiscountedProducts();
  341.             /** @var DiscountProducts $discount */
  342.             foreach ($stock->getProduct()->getDiscountedProducts()->toArray() as $discount){
  343.                 if($discount->getDiscount()->isStatus()){
  344.                     $rate $discount->getDiscount()->getRate();
  345.                     $min $discount->getDiscount()->getMinimum();
  346.                 }
  347.             }
  348.             $product = [
  349.                 'id' => $stock->getProduct()->getId(),
  350.                 'label' => $stock->getProduct()->getLabel(),
  351.                 'price' => $stock->getUnitAmount(),
  352.                 'rate' => $rate,
  353.                 'min' => $min,
  354.                 'code' => $stock->getBarcode(),
  355.                 'weight' => $stock->getProduct()->getUnit() == 'WEIGHT',
  356.                 'sold' => $rate != 0,
  357.                 'pack' => false,
  358.                 'package' => false,
  359.                 'packageCount' => 0
  360.             ];
  361.         }
  362.         /** @var Pack $stock */
  363.         $stock $em->getRepository(Pack::class)->findOneBy(['barcode' => $code], ['id' => 'desc']);
  364.         if($stock != null){
  365.             $product = [
  366.                 'id' => $stock->getId(),
  367.                 'label' => $stock->getLabel(),
  368.                 'price' => $stock->getPrice(),
  369.                 'rate' => 0,
  370.                 'min' => 0,
  371.                 'code' => $stock->getBarcode(),
  372.                 'weight' => false,
  373.                 'sold' => false,
  374.                 'pack' => true,
  375.                 'package' => false,
  376.                 'packageCount' => 0
  377.             ];
  378.         }
  379.         $response = new JsonResponse(
  380.             json_encode($product),
  381.             200
  382.         );
  383.         return  $response;
  384.     }
  385.     /**
  386.      * @Route("/stock-get-by-product-id/{id}", name="get_stock_product_byId")
  387.      */
  388.     public function getStockByProductId($idRequest $requestEntityManagerInterface $em): JsonResponse
  389.     {
  390.         $products=[];
  391.         if($this->getUser() != null){
  392.             /** @var User $user */
  393.             $user $em->getRepository(User::class)->findOneBy(['username' => $this->getUser()->getUserIdentifier()]);
  394.         }else{
  395.             /** @var User $user */
  396.             $user $em->getRepository(User::class)->find(5);
  397.         }
  398.         $shop $em->getRepository(Shop::class)->find($user->getCurrentshop());
  399.         /** @var Product $product */
  400.         $product $em->getRepository(Product::class)->find($id);
  401. //        dd($product->getId(), $shop->getId(),$em->getRepository(Stock::class)->findCurrentStock($product->getId(), $shop->getId()));
  402.         /** @var Stock $stock */
  403.         $stock $em->getRepository(Stock::class)->findCurrentStock($product->getId(), $shop->getId())[0];
  404.         if($stock != null){
  405.             $rate 0;
  406.             $min 0;
  407.             $stock->getProduct()->getDiscountedProducts();
  408.             /** @var DiscountProducts $discount */
  409.             foreach ($stock->getProduct()->getDiscountedProducts()->toArray() as $discount){
  410.                 if($discount->getDiscount()->isStatus()){
  411.                     $rate $discount->getDiscount()->getRate();
  412.                     $min $discount->getDiscount()->getMinimum();
  413.                 }
  414.             }
  415.             $products = [
  416.                 'id' => $stock->getProduct()->getId(),
  417.                 'label' => $stock->getProduct()->getLabel(),
  418.                 'price' => $stock->getUnitAmount(),
  419.                 'rate' => $rate,
  420.                 'min' => $min,
  421.                 'code' => $stock->getBarcode(),
  422.                 'weight' => $stock->getProduct()->getUnit() == 'WEIGHT',
  423.                 'sold' => $rate != 0,
  424.                 'pack' => false,
  425.                 'package' => false,
  426.                 'packageCount' => 0
  427.             ];
  428.         }
  429.         $response = new JsonResponse(
  430.             json_encode($products),
  431.             200
  432.         );
  433.         return  $response;
  434.     }
  435.     /**
  436.      * @Route("/stock-get-by-product-id-checkout/{id}", name="get_stock_product_byId_Checkout")
  437.      */
  438.     public function getStockByProductIdForCheckout($idRequest $requestEntityManagerInterface $em): JsonResponse
  439.     {
  440.         $products=[];
  441.         if($this->getUser() != null){
  442.             /** @var User $user */
  443.             $user $em->getRepository(User::class)->findOneBy(['username' => $this->getUser()->getUserIdentifier()]);
  444.         }else{
  445.             /** @var User $user */
  446.             $user $em->getRepository(User::class)->find(5);
  447.         }
  448.         $shop $em->getRepository(Shop::class)->find($user->getCurrentshop());
  449.         /** @var Product $product */
  450.         $product $em->getRepository(Product::class)->find($id);
  451. //        dd($product->getId(), $shop->getId(),$em->getRepository(Stock::class)->findCurrentStock($product->getId(), $shop->getId()));
  452.         /** @var Stock $stock */
  453.         $currentStock $em->getRepository(Stock::class)->findCurrentStock($product->getId(), $shop->getId());
  454.         if(empty($currentStock)){
  455.             $stock $em->getRepository(Stock::class)->findLastStock($product->getId(), $shop->getId())[0];
  456.         }else{
  457.             $stock $currentStock[0];
  458.         }
  459.         if($stock != null){
  460.         $rate 0;
  461.         $min 0;
  462.         $stock->getProduct()->getDiscountedProducts();
  463.         /** @var DiscountProducts $discount */
  464.         foreach ($stock->getProduct()->getDiscountedProducts()->toArray() as $discount){
  465.             if($discount->getDiscount()->isStatus()){
  466.                 $rate $discount->getDiscount()->getRate();
  467.                 $min $discount->getDiscount()->getMinimum();
  468.             }
  469.         }
  470.         $products = [
  471.             'id' => $stock->getProduct()->getId(),
  472.             'label' => $stock->getProduct()->getLabel(),
  473.             'price' => $stock->getUnitAmount(),
  474.             'rate' => $rate,
  475.             'min' => $min,
  476.             'code' => $stock->getBarcode(),
  477.             'weight' => $stock->getProduct()->getUnit() == 'WEIGHT',
  478.             'sold' => $rate != 0,
  479.             'pack' => false,
  480.             'package' => false,
  481.             'packageCount' => 0
  482.         ];
  483.         }
  484.         $response = new JsonResponse(
  485.             json_encode($products),
  486.             200
  487.         );
  488.         return  $response;
  489.     }
  490.     /**
  491.      * @Route("/stock-generate-barcode/{code}", name="stock_generate_barcode")
  492.      */
  493.     public function generateBarCode(string $code): Response
  494.     {
  495.         $generator = new BarcodeGeneratorPNG();
  496. //        $image = 'barcode.png';
  497. //        file_put_contents('barcode.png', $generator->getBarcode($code, BarcodeGenerator::TYPE_CODE_128, 3, 100));
  498. //        return new BinaryFileResponse($image);
  499.         $data $generator->getBarcode($codeBarcodeGenerator::TYPE_CODE_1282100);
  500.         return new JsonResponse(
  501.             base64_encode($data),
  502.             200
  503.         );
  504.     }
  505.     /**
  506.      * @Route("/stock-print-barcode/{code}", name="stock_print_barcode")
  507.      */
  508.     public function printCodeBar(string $code):Response
  509.     {
  510.         try {
  511.             // Enter the share name for your USB printer here
  512.             // $connector = null;
  513. //            $connector = new WindowsPrintConnector("Generic  Text Only");
  514.             $connector = new WindowsPrintConnector("PRINTER");
  515.             /* Print a "Hello world" receipt" */
  516.             $printer = new Printer($connector);
  517.             $printer -> text("Hello World!\n");
  518.             $printer -> cut();
  519.             /* Close printer */
  520.             $printer -> close();
  521.         } catch (\Exception $e) {
  522.             echo "Couldn't print to this printer: " $e -> getMessage() . "\n";
  523.         }
  524.         return new JsonResponse();
  525.     }
  526.     /**
  527.      * @Route("/get-pmp/{id}/{stockId}", name="stock_get_pmp", defaults={"stockId"=null})
  528.      */
  529.     public function getPMP(
  530.         string $id,
  531.         ?string $stockId,
  532.         ProductRepository $productRepository,
  533.         StockRepository $stockRepository
  534.     ): JsonResponse {
  535.         $product $productRepository->find($id);
  536.         $qb $stockRepository->createQueryBuilder('s')
  537.             ->where('s.product = :product')
  538.             ->andWhere('s.deleted = :deleted')
  539.             ->andWhere('s.quantity > 0')
  540.             ->setParameter('product'$product)
  541.             ->setParameter('deleted'false)
  542.             ->orderBy('s.creation_date''ASC');
  543. //        if ($stockId !== null) {
  544. //            $qb->andWhere('s.id != :stockId')
  545. //                ->setParameter('stockId', $stockId);
  546. //        }
  547.         $stocks $qb->getQuery()->getResult();
  548.         $qteAvailable 0;
  549. //        dump($stocks);
  550.         /** @var Stock $stock */
  551.         foreach ($stocks as $stock) {
  552.             if ($stock->getQuantity() > 0) {
  553.                 $qteAvailable += $stock->getQuantity();
  554.             }
  555.         }
  556.         $lastPmp end($stocks);
  557.         $response = [
  558.             'lastPmp' => $lastPmp $lastPmp->getPmpAmount() : 0,
  559.             'qteAvailable' => $qteAvailable
  560.         ];
  561.         return new JsonResponse($response200);
  562.     }
  563.     private function guessLot(Stock $stock):int
  564.     {
  565.         $uniqueCode rand(1000000,9999999);
  566.         return '700'.str_shuffle($uniqueCode);
  567.     }
  568.     /**
  569.      * @Route("/update-remaining/{lotId}", name="app_stock_update_remaining")
  570.      */
  571.     public function updateRemaining(
  572.         string $lotId,
  573.         Request $request,
  574.         StockRepository $lotRepository,
  575.         EntityManagerInterface $em,
  576.         CrmLogger $crmLogger
  577.     ): JsonResponse {
  578.         // 1) Parse JSON
  579.         $payload json_decode($request->getContent(), true);
  580.         if (!is_array($payload) || !array_key_exists('remainingQty'$payload)) {
  581.             return $this->json(['success' => false'error' => 'Paramètre "remainingQty" manquant.'], 400);
  582.         }
  583.         if (!is_numeric($payload['remainingQty'])) {
  584.             return $this->json(['success' => false'error' => '"remainingQty" doit être un nombre.'], 400);
  585.         }
  586.         $remainingQty = (int) $payload['remainingQty'];
  587.         if ($remainingQty 0) {
  588.             return $this->json(['success' => false'error' => '"remainingQty" doit être ≥ 0.'], 422);
  589.         }
  590.         // 2) Retrouver le lot
  591.         // Si ton identifiant public est le N° de lot (ex: 8001751) stocké dans une colonne lotNumber :
  592.         $lot $lotRepository->find($lotId);
  593.         // Sinon, si {lotId} est l'ID auto-incrémenté, dé-commente :
  594.         // $lot = $lotRepository->find($lotId);
  595.         if (!$lot) {
  596.             return $this->json(['success' => false'error' => 'Lot introuvable.'], 404);
  597.         }
  598.         // 3) Règles métier optionnelles (décommente/ajuste selon ton modèle)
  599.         // if ($remainingQty > $lot->getQtyInitial()) {
  600.         //     return $this->json(['success' => false, 'error' => 'La quantité restante ne peut pas dépasser la quantité initiale.'], 422);
  601.         // }
  602.         // 4) Mise à jour + flush
  603.         $lot->setQuantity($remainingQty);
  604.         // Exemple : maj d’un flag "finished"
  605.         // $lot->setFinished($remainingQty === 0);
  606.         $em->flush();
  607.         $crmLogger->log(
  608.             'MODIFICATION',
  609.             'STOCK_LOT_UPDATE_REMAINING_SUCCESS',
  610.             sprintf('Mise à jour du lot ID : %s | Nouvelle quantité restante : %d'$lotId$remainingQty),
  611.             $lot->getShop() ? $lot->getShop()->getLabel() : 'N/A',
  612.             $this->getUser() ? $this->getUser()->getUserIdentifier() : 'Anonyme'
  613.         );
  614.         // 5) Réponse
  615.         return $this->json([
  616.             'success' => true,
  617.             'lotId' => $lotId,
  618.             'remainingQty' => $remainingQty,
  619.             // 'finished' => $lot->isFinished(),
  620.         ], 200);
  621.     }
  622. }