src/Controller/Admin/SupplierController.php line 150

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Admin;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  5. use Symfony\Component\HttpFoundation\Response;
  6. use Symfony\Component\Routing\Annotation\Route;
  7. use Symfony\Component\HttpFoundation\JsonResponse;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use App\Repository\AddressRepository;
  10. use App\Repository\UserRepository;
  11. use App\Repository\SupplierRepository;
  12. use App\Form\FilterSupplierType;
  13. use App\Form\SupplierType;
  14. use App\Entity\Supplier;
  15. use App\Entity\Document;
  16. use App\Entity\User;
  17. use App\Repository\DocumentDeclinationProduitRepository;
  18. use App\Repository\DocumentRepository;
  19. use App\Entity\Comment;
  20. /**
  21.  * @Route("/supplier")
  22.  */
  23. class SupplierController extends AbstractController {
  24.     private $addressRepository;
  25.     private $userRepository;
  26.     public function __construct(
  27.             AddressRepository $addressRepository,
  28.             UserRepository $userRepository
  29.     ) {
  30.         $this->addressRepository $addressRepository;
  31.         $this->userRepository $userRepository;
  32.     }
  33.     /**
  34.      * @Route("/supplier", name="supplier_index")
  35.      */
  36.     public function index(): Response {
  37.         $form $this->createForm(FilterSupplierType::class);
  38.         return $this->render('@admin/supplier/list_suppliers.html.twig', [
  39.                     'form' => $form->createView()
  40.         ]);
  41.     }
  42.     /**
  43.      * @Route("/search", name="search_supplier", methods="GET|POST", options = { "expose" =  true})
  44.      */
  45.     public function searchSupplier(Request $requestSupplierRepository $suppliers,EntityManagerInterface $em) {
  46.         $entities $suppliers->search(010$request->get('query'), null);
  47.         foreach ($entities as $entity) {
  48.             $output[] = [
  49.                 'id' => $entity->getId(),
  50.                 'name' => $entity->getName(),
  51.                 'namerasionsocial' => $entity->getRaisonSociale() ,
  52.                 'phone' => $entity->getPhone(),
  53.                 'email' => $entity->getEmail(),
  54.                 'actions' => 'actions',
  55.             ];
  56.         }
  57.         if( !$entities) {
  58.             $output = [];
  59.         }
  60.         return new JsonResponse($output);
  61.     }
  62.     /**
  63.      * @Route("/new", name="new_supplier", methods={"GET","POST"}, options={"expose"=true})
  64.      */
  65.     public function new_supplier(
  66.         Request $request,
  67.         SupplierRepository $suppliers,
  68.         EntityManagerInterface $em
  69.     ): Response {
  70.         $supplier = new Supplier();
  71.         // Formulaire
  72.         $form $this->createForm(SupplierType::class, $supplier);
  73.         $form->handleRequest($request);
  74.         if ($form->isSubmitted() && $form->isValid()) {
  75.             // Vérifs doublons (au submit uniquement)
  76.             $name  $request->request->get('name') ?? ($supplier->getName() ?: null);
  77.             $phone $request->request->get('phone') ?? ($supplier->getPhone() ?: null);
  78.             if ($name && $suppliers->findOneBy(['name' => $name])) {
  79.                 $this->addFlash('danger'"Référence que vous avez entrée existe déjà !");
  80.                 return $this->render('@admin/supplier/_formAddEditSupplierModal.html.twig', [
  81.                     'supplier' => $supplier,
  82.                     'form'     => $form->createView(),
  83.                     'type'     => 'new',
  84.                 ]);
  85.             }
  86.             if ($phone && $suppliers->findOneBy(['phone' => $phone])) {
  87.                 $this->addFlash('danger'"Téléphone que vous avez entré existe déjà !");
  88.                 return $this->render('@admin/supplier/_formAddEditSupplierModal.html.twig', [
  89.                     'supplier' => $supplier,
  90.                     'form'     => $form->createView(),
  91.                     'type'     => 'new',
  92.                 ]);
  93.             }
  94.             // Upload du logo (champ non mappé "logoFile" dans SupplierType)
  95.             /** @var UploadedFile|null $file */
  96.             $file $form->get('logoFile')->getData();
  97.             if ($file instanceof UploadedFile) {
  98.                 $uploadDir $this->getParameter('kernel.project_dir') . '/public/uploads/suppliers';
  99.                 if (!is_dir($uploadDir)) { @mkdir($uploadDir0775true); }
  100.                 $safeName pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
  101.                 $safeName substr(preg_replace('/[^a-z0-9-_]/i''-'$safeName), 040);
  102.                 $ext $file->guessExtension() ?: 'png';
  103.                 $newName $safeName '-' uniqid() . '.' $ext;
  104.                 $file->move($uploadDir$newName);
  105.                 $supplier->setLogo($newName); // propriété string nullable dans l'entité Supplier
  106.             }
  107.             // Champs non mappés (si vos selects ne sont pas mappés dans le form)
  108.             $supplier->setCountry($request->request->get('supplier')['country'] ?? null);
  109.             $supplier->setRegion($request->request->get('supplier')['region'] ?? null);
  110.             // Métadonnées
  111.             $supplier->setCreatedAt(new \DateTime());
  112.             // Persist
  113.             $em->persist($supplier);
  114.             $em->flush();
  115.             $this->addFlash('success'"Fournisseur ajouté avec succès");
  116.             return $this->redirectToRoute('supplier_show', ['id' => $supplier->getId()]);
  117.         }
  118.         return $this->render('@admin/supplier/_formAddEditSupplierModal.html.twig', [
  119.             'supplier' => $supplier,
  120.             'form'     => $form->createView(),
  121.             'type'     => 'new',
  122.         ]);
  123. }
  124.     /**
  125.      * @Route("/edit/{id}/{document}", name="edit_supplier", methods={"GET","POST"}, options={"expose"=true}, defaults={"document"=null})
  126.      */
  127.     public function edit_supplier(Request $requestSupplierRepository $suppliersSupplier $supplier$document null,EntityManagerInterface $em): Response {
  128.         $existName $suppliers->findOneBy(array('name' => $request->get('name')));
  129.         if( $existName$request->getSession()->getFlashBag()->add('danger'"Référence que vous avez entré existe déjà!");
  130.         $existPhone $suppliers->findOneBy(array('phone' => $request->get('phone')));
  131.         if( $existPhone$request->getSession()->getFlashBag()->add('danger'"Téléphone que vous avez entré existe déjà!");
  132.         
  133.         $form $this->createForm(SupplierType::class, $supplier);
  134.         $form->handleRequest($request);
  135.         //dd($form->getData()->getRegion());
  136.         if( $form->isSubmitted()) {
  137.             $supplier->setCountry($request->request->get('supplier')['country'] ?? null);
  138.             $supplier->setRegion($request->request->get('supplier')['region'] ?? null);
  139.             $em->flush();
  140.             $request->getSession()->getFlashBag()->add('success'"Fournisseur modifié avec succès");
  141.             if( $document)
  142.                 return $this->redirect($this->generateUrl('document_show', ['id' => $document]));
  143.             else
  144.                 return $this->redirect($this->generateUrl('supplier_show', ['id' => $supplier->getId()]));
  145.         }
  146.         return $this->render('@admin/supplier/_formAddEditSupplierModal.html.twig', [
  147.                     'supplier' => $supplier,
  148.                     'form' => $form->createView(),
  149.                     'document' => $document,
  150.                     'type' => 'edit'
  151.         ]);
  152.     }
  153.     /**
  154.      * @Route("/show/{id}", name="supplier_show", methods={"GET"}, options = { "expose" =  true})
  155.      */
  156.     public function show_supplier(Supplier $supplierEntityManagerInterface $em,documentRepository $documentRepositoryDocumentDeclinationProduitRepository $documentDeclinationProduitRepository): Response
  157.     {
  158.         $documents $supplier->getDocuments();
  159.         // Compter les types de documents
  160.         $nbAchat $documents->filter(fn($d) => $d->getType() === 'achat')->count();
  161.         $nbReception $documents->filter(fn($d) => $d->getType() === 'reception')->count();
  162.         $nbFacture $documents->filter(fn($d) => $d->getType() === 'facture')->count();
  163.         $nbRetour $documents->filter(fn($d) => $d->getType() === 'retour')->count();
  164.         // Total achats TTC (sans frais de livraison)
  165.         $totalAchat $documents->filter(fn($d) =>
  166.             $d->getType() === 'reception' &&
  167.             in_array($d->getStatus(), ['brouillon''valide''paye']) 
  168.         )->map(fn($d) => $d->getTotalAmountTtc() - $d->getDeliveryTotal())
  169.         ->reduce(fn($carry$item) => $carry $item0);
  170.         $totalVente $documentDeclinationProduitRepository->getTotalVenteByFournisseur($supplier);
  171.         $lastReceptionDate $documentRepository->getLastDocumentDateByType($supplier'reception');
  172.         $lastFactureDate $documentRepository->getLastDocumentDateByType($supplier'facture');
  173.         $daysSinceLastReception $lastReceptionDate ? (new \DateTime())->diff($lastReceptionDate)->days null;
  174.         $daysSinceLastFacture $lastFactureDate ? (new \DateTime())->diff($lastFactureDate)->days null;
  175.         $receptionColorClass $daysSinceLastReception !== null && $daysSinceLastReception 30 'text-danger' 'text-success';
  176.         $factureColorClass $daysSinceLastFacture !== null && $daysSinceLastFacture 30 'text-danger' 'text-success';
  177.         $statistiques compact('nbAchat''nbReception''nbFacture''nbRetour''totalAchat''totalVente');
  178.         return $this->render('@admin/supplier/fiche_supplier.html.twig', [
  179.             'user' => $supplier,
  180.             'statistiques' => $statistiques,
  181.             'lastReceptionDate' => $lastReceptionDate,         // corrigé (espace supprimé)
  182.             'lastFactureDate' => $lastFactureDate,
  183.             'daysSinceLastReception' => $daysSinceLastReception,
  184.             'daysSinceLastFacture' => $daysSinceLastFacture,
  185.             'receptionColorClass' => $receptionColorClass,
  186.             'factureColorClass' => $factureColorClass,
  187.         ]);
  188.         
  189.     }
  190.     
  191.     
  192.     /**
  193.      * @Route("/list", name="list_supplier", methods="GET|POST", options = { "expose" = true })
  194.      */
  195.     public function listData_supplier(Request $requestSupplierRepository $supplierRepositoryDocumentRepository $documentRepository): JsonResponse
  196.     {
  197.         $pagination $request->get('pagination');
  198.         $page $pagination['page'] - 1;
  199.         $limit $pagination['perpage'];
  200.         $filters = [
  201.             'name' => $request->get('name'),
  202.             'phone' => $request->get('phone'),
  203.             'email' => $request->get('email'),
  204.             'type' => $request->get('type'),
  205.             'dateBefore' => $request->get('dateBefore'),
  206.             'dateAfter' => $request->get('dateAfter'),
  207.         ];
  208.         $entities $supplierRepository->search($page$limit, ...array_values($filters));
  209.         $count $supplierRepository->countSupplier(...array_values($filters));
  210.         $output = [
  211.             'data' => [],
  212.             'meta' => [
  213.                 'page' => $pagination['page'],
  214.                 'perpage' => $limit,
  215.                 'pages' => ceil($count $limit),
  216.                 'total' => $count,
  217.             ]
  218.         ];
  219.         foreach ($entities as $entity) {
  220.             $nbAchats $documentRepository->countDocumentsBySupplier($entity'achat');
  221.             $nbReceptions $documentRepository->countDocumentsBySupplier($entity'reception');
  222.             $nbFactures $documentRepository->countDocumentsBySupplier($entity'facture');
  223.             $nbRetours $documentRepository->countDocumentsBySupplier($entity'retour');
  224.             $totalAchat $documentRepository->getTotalAchatByFournisseur($entity);
  225.             $lastReceptionDate $documentRepository->getLastDocumentDateByType($entity'reception');
  226.             $output['data'][] = [
  227.                 'id' => $entity->getId(),
  228.                 'name' => $entity->getName(),
  229.                 'raisonSociale' => $entity->getRaisonSociale(),
  230.                 'civility' => $entity->getCivility(),
  231.                 'name_responsable' => $entity->getNameResponsable(),               
  232.                 'phone_responsible1' => $entity->getPhoneResponsible1(),
  233.                 'phone_responsible2' => $entity->getPhoneResponsible2(),
  234.                 'adress' => $entity->getAdress(),
  235.                 'type' => $entity->getType(),
  236.                 'isActive' => $entity->isActive(),
  237.                 'phone' => $entity->getPhone() ?: 'Pas mentionné',
  238.                 'email' => $entity->getEmail() ?: 'Pas mentionné',
  239.                 'createdAt' => $entity->getCreatedAt()->format('d/m/Y'),
  240.                 'nbAchat' => $nbAchats,
  241.                 'nbReception' => $nbReceptions,
  242.                 'nbFacture' => $nbFactures,
  243.                 'nbRetour' => $nbRetours,
  244.                 'totalAchat' => $totalAchat,
  245.                 'dateLastReception' => $lastReceptionDate $lastReceptionDate->format('d/m/Y') : null,
  246.             ];
  247.         }
  248.         return new JsonResponse($output);
  249.     }
  250.     //suppression d'un fournisseur
  251.     /**
  252.      * @Route("/admin/fournisseurs/{id}/delete", name="supplier_delete", methods={"POST"}, options={"expose"=true})
  253.      */
  254.     public function delete_supplier(Request $requestSupplier $supplierEntityManagerInterface $em): JsonResponse 
  255.     {
  256.         if ($supplier->getDocuments()->count() > 0) {
  257.             return new JsonResponse([
  258.                 'success' => false,
  259.                 'message' => 'Ce fournisseur a des documents associés et ne peut pas être supprimé !'
  260.             ], 400);
  261.         }
  262.         $em->remove($supplier);
  263.         $em->flush();
  264.         return new JsonResponse(['success' => true]);
  265.     }
  266.     /**
  267.      * @Route("/admin/fournisseur/{id}/new_comment", name="supplier_comment_new", methods={"POST"})
  268.      */
  269.     public function newComment(Request $requestSupplier $supplierEntityManagerInterface $em): Response
  270.     {
  271.         if ($request->isMethod('POST') && $request->request->get('form_comment')) {
  272.             $comment = new Comment();
  273.             $comment->setDescription($request->request->get('form_comment')['comment']);
  274.             $comment->setUser($this->getUser());
  275.             $comment->setCreateAt(new \DateTime());
  276.             $comment->setSupplier($supplier); // lien vers le fournisseur
  277.             $em->persist($comment);
  278.             $em->flush();
  279.             return $this->redirectToRoute("supplier_show", ['id' => $supplier->getId(), "tab" => "comments"]);
  280.         }
  281.         throw $this->createNotFoundException("Commentaire non soumis.");
  282.     }
  283.     /**
  284.      * @Route("/admin/fournisseur/{id}/edit_comment/{comment}", name="supplier_comment_edit", methods={"POST"})
  285.      */
  286.     public function editComment(Request $requestSupplier $supplierComment $commentEntityManagerInterface $em): Response
  287.     {
  288.         if ($request->isMethod('POST') && $request->request->get('form_comment')) {
  289.             $comment->setDescription($request->request->get('form_comment')['comment']);
  290.             $comment->setUser($this->getUser());
  291.             $comment->setCreateAt(new \DateTime());
  292.             $em->flush();
  293.             return $this->redirectToRoute("supplier_show", ['id' => $supplier->getId(), "tab" => "comments"]);
  294.         }
  295.         throw $this->createNotFoundException("Commentaire non soumis.");
  296.     }
  297.     
  298.     /**
  299.     * @Route("/fournisseur/{id}/statistiques/premiere-annee", name="supplier_premiere_annee", methods={"GET"})
  300.      */
  301.     public function getPremiereAnneeReception(Supplier $supplierDocumentRepository $docRepo): JsonResponse
  302.     {
  303.         $annee $docRepo->getPremiereAnneeBonReception($supplier->getId());
  304.         if (!$annee) {
  305.             return $this->json(['annee' => date('Y')]); // fallback si aucun bon
  306.         }
  307.         return $this->json(['annee' => $annee]);
  308.     }
  309.     
  310.     /**
  311.      * @Route("/fournisseur/{id}/statistiques/achat-par-annee", name="supplier_achat_par_annee", methods={"GET"})
  312.      */
  313.     public function achatParAnnee(Supplier $supplierRequest $requestDocumentRepository $docRepo): JsonResponse
  314.     {
  315.         $startYear $request->query->get('startYear');
  316.         $endYear $request->query->get('endYear');
  317.         $type $request->query->get('type''reception');
  318.         $mode $request->query->get('mode''montant');
  319.         $dataset $request->query->get('dataset''achat'); // ← Nouveau
  320.         if ($dataset === 'achat') {
  321.             $data $docRepo->getTotalAchatsParAnnee($supplier->getId(), $startYear$endYear$type$mode);
  322.             return $this->json($data);
  323.         }
  324.         if ($dataset === 'vente') {
  325.             $data $docRepo->getTotalVentesParAnnee($supplier->getId(), $startYear$endYear$mode); // ← à créer
  326.             return $this->json($data);
  327.         }
  328.         if ($dataset === 'les_deux') {
  329.             $achats $docRepo->getTotalAchatsParAnnee($supplier->getId(), $startYear$endYear$type$mode);
  330.             $ventes $docRepo->getTotalVentesParAnnee($supplier->getId(), $startYear$endYear$mode);
  331.             // Fusionner les deux par année
  332.             $merged = [];
  333.             foreach ($achats as $a) {
  334.                 $merged[$a['year']] = ['year' => $a['year'], 'achat' => $a['total'], 'vente' => 0];
  335.             }
  336.             foreach ($ventes as $v) {
  337.                 if (!isset($merged[$v['year']])) {
  338.                     $merged[$v['year']] = ['year' => $v['year'], 'achat' => 0'vente' => $v['total']];
  339.                 } else {
  340.                     $merged[$v['year']]['vente'] = $v['total'];
  341.                 }
  342.             }
  343.             // Réordonner par année
  344.             ksort($merged);
  345.             return $this->json(array_values($merged));
  346.         }
  347.         return $this->json(['error' => 'Invalid dataset'], 400);
  348.     }
  349.     /**
  350.      * @Route("/fournisseur/{id}/statistiques/achat-par-mois", name="supplier_achat_par_mois", methods={"GET"})
  351.      */
  352.     public function achatParMoisSupplier $supplier,Request $request,DocumentRepository $docRepo): JsonResponse {
  353.         $year $request->query->get('year');
  354.         $type $request->query->get('type''reception'); // par défaut réception
  355.         $mode $request->query->get('mode''montant'); // par défaut montant
  356.         $data $docRepo->getTotalAchatParMois($supplier->getId(), $year$type$mode);
  357.         return $this->json($data);
  358.     }
  359.     /**
  360.      * @Route("/fournisseur/{id}/statistiques/evolution-mensuelle", name="supplier_evolution_mensuelle", methods={"GET"})
  361.      */
  362.     public function evolutionAchatMensuelle(Supplier $supplierRequest $requestDocumentRepository $docRepo): JsonResponse
  363.     {
  364.         $type $request->query->get('type''reception');
  365.         $data $docRepo->getEvolutionAchatMensuelle($supplier->getId(),$type);
  366.         return $this->json($data);
  367.     }
  368.     /**
  369.      * @Route("/fournisseur/{id}/statistiques/montant-moyen", name="supplier_montant_moyen", methods={"GET"})
  370.      */
  371.     public function montantMoyenParAnnee(Supplier $supplierRequest $requestDocumentRepository $docRepo): JsonResponse
  372.     {
  373.         $type $request->query->get('type');
  374.         $data $docRepo->getMontantMoyenParAnnee($supplier->getId(), $type);
  375.         return $this->json($data);
  376.     }
  377.     /**
  378.      * @Route("/fournisseur/{id}/statistiques/repartition-paiement", name="supplier_repartition_paiement", methods={"GET"})
  379.      */
  380.     public function repartitionPaiement(Supplier $supplierRequest $requestDocumentRepository $docRepo): JsonResponse
  381.     {
  382.         $type $request->query->get('type''reception');
  383.         $year $request->query->get('year');
  384.         $data $docRepo->getPaiementRepartition($supplier->getId(), $type$year);
  385.         return $this->json($data);
  386.     }
  387.     /**
  388.      * @Route("/fournisseur/{id}/statistiques/achat-par-categorie", name="supplier_achat_par_categorie", methods={"GET"})
  389.      */
  390.     public function achatParCategorie(Supplier $supplier,Request $requestDocumentRepository $documentRepository): JsonResponse {
  391.         $year $request->query->get('year');
  392.         $type $request->query->get('type''reception');
  393.         $mode $request->query->get('mode''montant'); // 'montant' ou 'quantite'
  394.         $result $documentRepository->getAchatParCategorie($supplier->getId(), $year$type$mode);
  395.         return $this->json($result);
  396.     }
  397. }