src/Controller/DashboardController.php line 449

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use DateTime;
  4. use App\Entity\Depense;
  5. use App\Entity\Produit;
  6. use App\Entity\Recette;
  7. use App\Entity\Boutique;
  8. use App\Entity\Livraison;
  9. use App\Entity\Vente;
  10. use App\Entity\EntreeStock;
  11. use App\Entity\SortieStock;
  12. use App\Repository\VenteRepository;
  13. use App\Repository\FactureRepository;
  14. use App\Repository\ProduitRepository;
  15. use Doctrine\ORM\EntityManagerInterface;
  16. use App\Repository\EntreeStockRepository;
  17. use Symfony\Component\VarDumper\Cloner\Data;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpFoundation\Response;
  20. use Symfony\Component\Routing\Annotation\Route;
  21. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  22. use App\Service\PaiementService;
  23. use App\Service\CalculFinancierService;
  24. class DashboardController extends AbstractController
  25. {
  26.     private $entityManager;
  27.     private $venteRepository;
  28.     private $paiementService;
  29.     private $calculFinancierService;
  30.     public function __construct(EntityManagerInterface $entityManagerVenteRepository $venteRepositoryPaiementService $paiementServiceCalculFinancierService $calculFinancierService)
  31.     {
  32.         $this->entityManager $entityManager;
  33.         $this->venteRepository $venteRepository;
  34.         $this->paiementService $paiementService;
  35.         $this->calculFinancierService $calculFinancierService;
  36.     }
  37.     #[Route('/dashboard'name'app_dashboard')]
  38.     public function index(Request $requestProduitRepository $menuRepositoryFactureRepository $factureRepository): Response
  39.     {
  40.         // Récupérer l'utilisateur connecté et son rôle
  41.         $user $this->getUser();
  42.         $isSuperAdmin $this->isGranted('ROLE_SUPER_ADMIN');
  43.         $isAdmin $this->isGranted('ROLE_ADMIN');
  44.         
  45.         // Si l'utilisateur n'est pas super admin, afficher un message simple
  46.         if (!$isSuperAdmin) {
  47.             return $this->render('dashboard/index.html.twig', [
  48.                 'isSuperAdmin' => false,
  49.                 'isAdmin' => $isAdmin,
  50.                 'userBoutique' => $user $user->getBoutique() : null,
  51.                 'showData' => false// Indicateur pour le template
  52.             ]);
  53.         }
  54.         
  55.         // Récupérer la période sélectionnée (journalier, hebdomadaire, mensuel, trimestriel, semestriel, annuel)
  56.         $period $request->query->get('period''daily');
  57.         
  58.         // Initialiser les variables de date selon la période
  59.         $startDate = new \DateTime('today');
  60.         $endDate = clone $startDate;
  61.         $endDate->modify('+1 day');
  62.         $periodLabel 'Journalier';
  63.         
  64.         // Définir les dates selon la période sélectionnée
  65.         switch ($period) {
  66.             case 'weekly':
  67.                 $startDate = new \DateTime('monday this week');
  68.                 $endDate = clone $startDate;
  69.                 $endDate->modify('+1 week');
  70.                 $periodLabel 'Hebdomadaire';
  71.                 break;
  72.             case 'monthly':
  73.                 $startDate = new \DateTime('first day of this month');
  74.                 $endDate = clone $startDate;
  75.                 $endDate->modify('+1 month');
  76.                 $periodLabel 'Mensuel';
  77.                 break;
  78.             case 'quarterly':
  79.                 $startDate = new \DateTime('first day of this month');
  80.                 $quarter ceil($startDate->format('n') / 3);
  81.                 $startDate->setDate($startDate->format('Y'), ($quarter 1) * 11);
  82.                 $endDate = clone $startDate;
  83.                 $endDate->modify('+3 months');
  84.                 $periodLabel 'Trimestriel';
  85.                 break;
  86.             case 'biannual':
  87.                 $startDate = new \DateTime('first day of january this year');
  88.                 if ((int)$startDate->format('n') > 6) {
  89.                     $startDate->setDate($startDate->format('Y'), 71);
  90.                 }
  91.                 $endDate = clone $startDate;
  92.                 $endDate->modify('+6 months');
  93.                 $periodLabel 'Semestriel';
  94.                 break;
  95.             case 'annual':
  96.                 $startDate = new \DateTime('first day of january this year');
  97.                 $endDate = clone $startDate;
  98.                 $endDate->modify('+1 year');
  99.                 $periodLabel 'Annuel';
  100.                 break;
  101.         }
  102.         
  103.         // Récupérer toutes les boutiques pour admin/superadmin ou seulement la boutique associée pour magasinier
  104.         $boutiqueRepository $this->entityManager->getRepository(Boutique::class);
  105.         
  106.         if ($isSuperAdmin || $isAdmin) {
  107.             // Admin ou SuperAdmin voit toutes les boutiques
  108.             $boutiques $boutiqueRepository->findAll();
  109.         } else {
  110.             // Magasinier ne voit que sa boutique assignée
  111.             $boutiques = [];
  112.             if ($user && $user->getBoutique()) {
  113.                 $boutiques = [$user->getBoutique()];
  114.             }
  115.         }
  116.         
  117.         // Récupérer les encaissements réels par boutique pour la période sélectionnée
  118.         $encaissementsByBoutique $this->venteRepository->getEncaissementsByBoutique($startDate$endDate);
  119.         $totalEncaissements $this->venteRepository->getTotalEncaissements($startDate$endDate);
  120.         // Calculer les totaux globaux directs et dettes
  121.         $totalDirects 0;
  122.         $totalDettes 0;
  123.         foreach ($encaissementsByBoutique as $encaissement) {
  124.             $totalDirects += $encaissement['encaissements_directs'];
  125.             $totalDettes += $encaissement['encaissements_dettes'];
  126.         }
  127.         // Formater les données pour l'affichage
  128.         $recettesByBoutique = [];
  129.         foreach ($boutiques as $boutique) {
  130.             $encaissement $encaissementsByBoutique[$boutique->getId()] ?? null;
  131.             $montantTotal $encaissement $encaissement['total_encaissements'] : 0;
  132.             $recettesByBoutique[$boutique->getId()] = [
  133.                 'boutique' => $boutique,
  134.                 'montant' => $montantTotal,
  135.                 'encaissements_directs' => $encaissement $encaissement['encaissements_directs'] : 0,
  136.                 'encaissements_dettes' => $encaissement $encaissement['encaissements_dettes'] : 0
  137.             ];
  138.         }
  139.         
  140.         // Récupérer les autres données existantes
  141.         $numberOfMenus $menuRepository->createQueryBuilder('m')
  142.             ->select('COUNT(m.id)')
  143.             ->getQuery()
  144.             ->getSingleScalarResult();
  145.         $topMenus $this->entityManager->getRepository(Produit::class)->findTopMenus(5);
  146.         $bottomProducts $this->entityManager->getRepository(Produit::class)->findBottomProducts(5);
  147.         $today = new DateTime('today');
  148.         $recette $this->entityManager->getRepository(Recette::class)->findOneBy(['dateRecette' => $today]);
  149.         $depenses $this->entityManager->getRepository(Depense::class)->createQueryBuilder('d')
  150.             ->where('d.dateDepense >= :start')
  151.             ->andWhere('d.dateDepense < :end')
  152.             ->setParameter('start'$startDate->format('Y-m-d H:i:s'))
  153.             ->setParameter('end'$endDate->format('Y-m-d H:i:s'))
  154.             ->getQuery()
  155.             ->getResult();
  156.         $livraisonsEnAttente $this->entityManager->getRepository(Livraison::class)->findBy(['statut' => 'en attente']);
  157.         $livraisonsEnCours $this->entityManager->getRepository(Livraison::class)->findBy(['statut' => 'en cours']);
  158.         
  159.         $dailyRevenue $factureRepository->getRevenueByPeriod($today, new \DateTime('tomorrow'));
  160.         $monthlyRevenue $factureRepository->getRevenueByPeriod(new \DateTime('first day of this month'), new \DateTime('last day of this month'));
  161.         $trimesterRevenue $factureRepository->getRevenueByPeriod(new \DateTime(date('Y-m-d'strtotime('-2 months'strtotime('first day of this month')))), new \DateTime('last day of this month'));
  162.         // Récupérer les ventes journalières (exclure les ventes virtuelles)
  163.         $ventesJournalieres $this->entityManager->getRepository(Vente::class)->createQueryBuilder('v')
  164.             ->where('v.dateVente >= :start')
  165.             ->andWhere('v.dateVente < :end')
  166.             ->andWhere('(v.type IS NULL OR v.type != :typeVirtuelle)')
  167.             ->setParameter('start'$today->format('Y-m-d H:i:s'))
  168.             ->setParameter('end', (clone $today)->modify('+1 day')->format('Y-m-d H:i:s'))
  169.             ->setParameter('typeVirtuelle''virtuelle')
  170.             ->orderBy('v.dateVente''DESC')
  171.             ->getQuery()
  172.             ->getResult();
  173.         // Calculer le total des ventes journalières avec frais de transport
  174.         $totalVentesJournalieres 0;
  175.         foreach ($ventesJournalieres as $vente) {
  176.             // Calculer le montant des produits
  177.             $montantProduits 0;
  178.             foreach ($vente->getDetailsVentes() as $detail) {
  179.                 $montantProduits += $detail->getQuantiteVendu() * $detail->getPrixUnitaire();
  180.             }
  181.             
  182.             // Ajouter les frais de transport
  183.             $fraisTransport 0;
  184.             foreach ($vente->getFactures() as $facture) {
  185.                 $fraisTransport += $facture->getFraisTransport() ?? 0;
  186.             }
  187.             
  188.             $totalVentesJournalieres += $montantProduits $fraisTransport;
  189.         }
  190.         // Calculer les encaissements directs pour les ventes d'aujourd'hui (harmonisé)
  191.         $encaissementsDirectsAujourdhui $this->calculFinancierService->getEncaissementsDirectsByVenteDate($today, (clone $today)->modify('+1 day'));
  192.         
  193.         // Calculer les paiements de dettes effectués aujourd'hui (basé sur la date de paiement)
  194.         $paiementsDettesAujourdhui $this->calculFinancierService->getPaiementsDettesByPaiementDate($today, (clone $today)->modify('+1 day'));
  195.         
  196.         // Vérifier la cohérence des calculs
  197.         $coherenceEncaissements $this->calculFinancierService->verifierCohérenceEncaissements($today, (clone $today)->modify('+1 day'));
  198.         return $this->render('dashboard/index.html.twig', [
  199.             'numberOfMenus' => $numberOfMenus,
  200.             'topMenus' => $topMenus,
  201.             'btP' => $bottomProducts,
  202.             'montantRecette' => $recette $recette->getMontant() : 0,
  203.             'depenses' => $depenses,
  204.             'livraisonsEnAttente' => $livraisonsEnAttente,
  205.             'livraisonsEnCours' => $livraisonsEnCours,
  206.             'dailyRevenue' => $dailyRevenue,
  207.             'monthlyRevenue' => $monthlyRevenue,
  208.             'trimesterRevenue' => $trimesterRevenue,
  209.             'recettesByBoutique' => $recettesByBoutique,
  210.             'totalRecette' => $totalEncaissements,
  211.             'totalDirects' => $totalDirects,
  212.             'totalDettes' => $totalDettes,
  213.             'ventesJournalieres' => $ventesJournalieres,
  214.             'totalVentesJournalieres' => $totalVentesJournalieres,
  215.             'encaissementsDirectsAujourdhui' => $encaissementsDirectsAujourdhui,
  216.             'paiementsDettesAujourdhui' => $paiementsDettesAujourdhui,
  217.             'coherenceEncaissements' => $coherenceEncaissements,
  218.             'period' => $period,
  219.             'periodLabel' => $periodLabel,
  220.             'startDate' => $startDate,
  221.             'endDate' => $endDate,
  222.             'isSuperAdmin' => $isSuperAdmin,
  223.             'isAdmin' => $isAdmin,
  224.             'userBoutique' => $user $user->getBoutique() : null,
  225.             'showData' => true// Indicateur pour le template
  226.         ]);
  227.     }
  228.     
  229.     #[Route('/dashboard/boutique/{id}/recettes'name'app_dashboard_boutique_recettes')]
  230.     public function boutiqueRecettes(Request $requestBoutique $boutique): Response
  231.     {
  232.         // Vérifier les permissions
  233.         $user $this->getUser();
  234.         
  235.         if (!$this->isGranted('ROLE_ADMIN') && !$this->isGranted('ROLE_SUPER_ADMIN')) {
  236.             if (!$user->getBoutique() || $user->getBoutique()->getId() !== $boutique->getId()) {
  237.                 throw $this->createAccessDeniedException('Vous n\'avez pas accès à cette boutique');
  238.             }
  239.         }
  240.         
  241.         
  242.         // Récupérer la période et les dates
  243.         $period $request->query->get('period''daily');
  244.         $startDate = new \DateTime('today');
  245.         $endDate = clone $startDate;
  246.         $endDate->modify('+1 day');
  247.         
  248.         // Définir les dates selon la période sélectionnée (même logique que dans index)
  249.         switch ($period) {
  250.             case 'weekly':
  251.                 $startDate = new \DateTime('monday this week');
  252.                 $endDate = clone $startDate;
  253.                 $endDate->modify('+1 week');
  254.                 break;
  255.             case 'monthly':
  256.                 $startDate = new \DateTime('first day of this month');
  257.                 $endDate = clone $startDate;
  258.                 $endDate->modify('+1 month');
  259.                 break;
  260.             case 'quarterly':
  261.                 $startDate = new \DateTime('first day of this month');
  262.                 $quarter ceil($startDate->format('n') / 3);
  263.                 $startDate->setDate($startDate->format('Y'), ($quarter 1) * 11);
  264.                 $endDate = clone $startDate;
  265.                 $endDate->modify('+3 months');
  266.                 break;
  267.             case 'biannual':
  268.                 $startDate = new \DateTime('first day of january this year');
  269.                 if ((int)$startDate->format('n') > 6) {
  270.                     $startDate->setDate($startDate->format('Y'), 71);
  271.                 }
  272.                 $endDate = clone $startDate;
  273.                 $endDate->modify('+6 months');
  274.                 break;
  275.             case 'annual':
  276.                 $startDate = new \DateTime('first day of january this year');
  277.                 $endDate = clone $startDate;
  278.                 $endDate->modify('+1 year');
  279.                 break;
  280.         }
  281.         
  282.         // Récupérer les encaissements réels pour cette boutique dans la période
  283.         $encaissementsByBoutique $this->venteRepository->getEncaissementsByBoutique($startDate$endDate);
  284.         $encaissementBoutique $encaissementsByBoutique[$boutique->getId()] ?? null;
  285.         
  286.         // Récupérer les factures payées pour cette boutique dans la période
  287.         $factures $this->entityManager->getRepository(\App\Entity\Facture::class)->createQueryBuilder('f')
  288.             ->leftJoin('f.vente''v')
  289.             ->where('v.boutique = :boutique')
  290.             ->andWhere('f.createdAt >= :start')
  291.             ->andWhere('f.createdAt < :end')
  292.             ->andWhere('f.statutPaiement = :statut')
  293.             ->setParameter('boutique'$boutique)
  294.             ->setParameter('start'$startDate->format('Y-m-d H:i:s'))
  295.             ->setParameter('end'$endDate->format('Y-m-d H:i:s'))
  296.             ->setParameter('statut''Payé')
  297.             ->orderBy('f.createdAt''DESC')
  298.             ->getQuery()
  299.             ->getResult();
  300.         
  301.         return $this->render('dashboard/boutique_recettes.html.twig', [
  302.             'boutique' => $boutique,
  303.             'factures' => $factures,
  304.             'encaissement' => $encaissementBoutique,
  305.             'period' => $period,
  306.             'startDate' => $startDate,
  307.             'endDate' => $endDate,
  308.         ]);
  309.     }
  310.     #[Route('/ventes-journalieres'name'app_ventes_journalieres')]
  311.     public function ventesJournalieres(): Response
  312.     {
  313.         $user $this->getUser();
  314.         $isSuperAdmin $this->isGranted('ROLE_SUPER_ADMIN');
  315.         $isAdmin $this->isGranted('ROLE_ADMIN');
  316.         
  317.         // Date d'aujourd'hui
  318.         $today = new \DateTime('today');
  319.         $tomorrow = clone $today;
  320.         $tomorrow->modify('+1 day');
  321.         
  322.         // Récupérer les ventes journalières (exclure les ventes virtuelles pour les crédits)
  323.         $queryBuilder $this->entityManager->getRepository(Vente::class)->createQueryBuilder('v')
  324.             ->leftJoin('v.client''c')
  325.             ->leftJoin('v.boutique''b')
  326.             ->leftJoin('v.detailsVentes''dv')
  327.             ->leftJoin('v.echeances''e')
  328.             ->where('v.dateVente >= :start')
  329.             ->andWhere('v.dateVente < :end')
  330.             ->andWhere('(v.type IS NULL OR v.type != :typeVirtuelle)')
  331.             ->setParameter('start'$today->format('Y-m-d H:i:s'))
  332.             ->setParameter('end'$tomorrow->format('Y-m-d H:i:s'))
  333.             ->setParameter('typeVirtuelle''virtuelle')
  334.             ->orderBy('v.dateVente''DESC');
  335.         // Filtrer par boutique si l'utilisateur n'est pas admin
  336.         if (!$isSuperAdmin && !$isAdmin) {
  337.             if ($user && $user->getBoutique()) {
  338.                 $queryBuilder->andWhere('v.boutique = :boutique')
  339.                     ->setParameter('boutique'$user->getBoutique());
  340.             } else {
  341.                 // Si l'utilisateur n'a pas de boutique, retourner un tableau vide
  342.                 return $this->render('dashboard/ventes_journalieres.html.twig', [
  343.                     'ventes' => [],
  344.                     'totalVentes' => 0,
  345.                     'totalEncaisse' => 0,
  346.                     'totalReste' => 0,
  347.                     'date' => $today,
  348.                 ]);
  349.             }
  350.         }
  351.         $ventes $queryBuilder->getQuery()->getResult();
  352.         // Calculer les totaux avec frais de transport
  353.         $totalVentes 0;
  354.         $totalProduits 0;
  355.         $totalFraisTransport 0;
  356.         $totalEncaisse 0;
  357.         $totalReste 0;
  358.         foreach ($ventes as $vente) {
  359.             $montantProduits $this->paiementService->calculerMontantProduitsVente($vente);
  360.             $fraisTransport $this->paiementService->calculerFraisTransportVente($vente);
  361.             $montantTotal $this->paiementService->calculerMontantTotalVente($vente);
  362.             $montantPaye $this->paiementService->calculerMontantPayeVente($vente);
  363.             $reste $this->paiementService->calculerResteAPayerVente($vente);
  364.             
  365.             $totalProduits += $montantProduits;
  366.             $totalFraisTransport += $fraisTransport;
  367.             $totalVentes += $montantTotal;
  368.             $totalEncaisse += $montantPaye;
  369.             $totalReste += $reste;
  370.         }
  371.         // Calculer les encaissements directs harmonisés pour les ventes d'aujourd'hui
  372.         $encaissementsDirectsAujourdhui $this->calculFinancierService->getEncaissementsDirectsByVenteDate($today$tomorrow);
  373.         
  374.         // Calculer les paiements de dettes effectués aujourd'hui (basé sur la date de paiement)
  375.         $paiementsDettesAujourdhui $this->calculFinancierService->getPaiementsDettesByPaiementDate($today$tomorrow);
  376.         
  377.         // Vérifier la cohérence des calculs
  378.         $coherenceEncaissements $this->calculFinancierService->verifierCohérenceEncaissements($today$tomorrow);
  379.         return $this->render('dashboard/ventes_journalieres.html.twig', [
  380.             'ventes' => $ventes,
  381.             'totalProduits' => $totalProduits,
  382.             'totalFraisTransport' => $totalFraisTransport,
  383.             'totalVentes' => $totalVentes,
  384.             'totalEncaisse' => $totalEncaisse,
  385.             'totalReste' => $totalReste,
  386.             'encaissementsDirectsAujourdhui' => $encaissementsDirectsAujourdhui,
  387.             'paiementsDettesAujourdhui' => $paiementsDettesAujourdhui,
  388.             'coherenceEncaissements' => $coherenceEncaissements,
  389.             'date' => $today,
  390.         ]);
  391.     }
  392.     // Les autres méthodes existantes (dashboardData, dashboardData2, dashboardReportsData)
  393.     #[Route('/dashboard/data'name'app_dashboard_data')]
  394.     public function dashboardData(Request $request): Response
  395.     {
  396.         $filter $request->query->get('filter''today');
  397.         if (!in_array($filter, ['today''month''year'])) {
  398.             return $this->json(['error' => 'Invalid filter'], Response::HTTP_BAD_REQUEST);
  399.         }
  400.         switch ($filter) {
  401.             case 'today':
  402.                 $salesCount $this->venteRepository->getSalesCountByPeriod('today');
  403.                 break;
  404.             case 'month':
  405.                 $salesCount $this->venteRepository->getSalesCountByPeriod('month');
  406.                 break;
  407.             case 'year':
  408.                 $salesCount $this->venteRepository->getSalesCountByPeriod('year');
  409.                 break;
  410.             default:
  411.                 $salesCount 0;
  412.                 break;
  413.         }
  414.         // Construire les données au format JSON
  415.         $data = [
  416.             'nombreVentes' => $salesCount
  417.             // Ajoutez ici d'autres données si nécessaire
  418.         ];
  419.         // Renvoyer les données au format JSON
  420.         return $this->json($data);
  421.     }
  422.     #[Route('/dashboard/data2'name'app_dashboard_data2')]
  423.     public function dashboardData2(Request $request): Response
  424.     {
  425.         $filter $request->query->get('filter''today');
  426.         $forceRefresh $request->query->get('refresh'false);
  427.         if (!in_array($filter, ['today''month''year'])) {
  428.             return $this->json(['error' => 'Invalid filter'], Response::HTTP_BAD_REQUEST);
  429.         }
  430.         // Forcer le rafraîchissement si demandé
  431.         if ($forceRefresh) {
  432.             $this->entityManager->clear();
  433.         }
  434.         // Utiliser les nouvelles méthodes qui prennent en compte les paiements réels
  435.         $totalRevenue $this->venteRepository->getTotalPaymentsByPeriod($filter);
  436.         $directRevenue $this->venteRepository->getDirectRevenueByPeriod($filter);
  437.         $echeanceRevenue $this->venteRepository->getRealRevenueByPeriod($filter);
  438.         $data = [
  439.             'totalRevenue' => $totalRevenue,
  440.             'directRevenue' => $directRevenue,
  441.             'echeanceRevenue' => $echeanceRevenue,
  442.             'breakdown' => [
  443.                 'direct' => $directRevenue,
  444.                 'echeances' => $echeanceRevenue
  445.             ],
  446.             'lastUpdate' => (new \DateTime())->format('Y-m-d H:i:s')
  447.         ];
  448.         return $this->json($data);
  449.     }
  450.     #[Route('/dashboard/reports/data'name'app_dashboard_reports_data')]
  451.     public function dashboardReportsData(Request $request): Response
  452.     {
  453.         $filter $request->query->get('filter''today');
  454.         if (!in_array($filter, ['today''month''year'])) {
  455.             return $this->json(['error' => 'Invalid filter'], Response::HTTP_BAD_REQUEST);
  456.         }
  457.         // Initialiser les variables pour les données de ventes et de revenus
  458.         $salesData = [];
  459.         $revenueData = [];
  460.         $directRevenueData = [];
  461.         $echeanceRevenueData = [];
  462.         $categories = [];
  463.         switch ($filter) {
  464.             case 'today':
  465.                 // Générer les catégories pour aujourd'hui (par exemple, chaque heure)
  466.                 for ($i 0$i 24$i++) {
  467.                     $categories[] = (new \DateTime())->setTime($i0)->format('Y-m-d\TH:i:s.v\Z');
  468.                     $salesData[] = $this->venteRepository->getSalesCountByPeriod('today');
  469.                     $revenueData[] = $this->venteRepository->getTotalPaymentsByPeriod('today');
  470.                     $directRevenueData[] = $this->venteRepository->getDirectRevenueByPeriod('today');
  471.                     $echeanceRevenueData[] = $this->venteRepository->getRealRevenueByPeriod('today');
  472.                 }
  473.                 break;
  474.             case 'month':
  475.                 // Générer les catégories pour ce mois (par exemple, chaque jour)
  476.                 $daysInMonth = (int) date('t');
  477.                 $currentDate = new \DateTime('first day of this month');
  478.                 for ($i 0$i $daysInMonth$i++) {
  479.                     $categories[] = $currentDate->format('Y-m-d\TH:i:s.v\Z');
  480.                     $salesData[] = $this->venteRepository->getSalesCountByPeriod('month');
  481.                     $revenueData[] = $this->venteRepository->getTotalPaymentsByPeriod('month');
  482.                     $directRevenueData[] = $this->venteRepository->getDirectRevenueByPeriod('month');
  483.                     $echeanceRevenueData[] = $this->venteRepository->getRealRevenueByPeriod('month');
  484.                 }
  485.                 break;
  486.             case 'year':
  487.                 // Générer les catégories pour cette année (par exemple, chaque mois)
  488.                 for ($i 1$i <= 12$i++) {
  489.                     $categories[] = (new \DateTime())->setDate(date('Y'), $i1)->format('Y-m-d\TH:i:s.v\Z');
  490.                     $salesData[] = $this->venteRepository->getSalesCountByPeriod('year');
  491.                     $revenueData[] = $this->venteRepository->getTotalPaymentsByPeriod('year');
  492.                     $directRevenueData[] = $this->venteRepository->getDirectRevenueByPeriod('year');
  493.                     $echeanceRevenueData[] = $this->venteRepository->getRealRevenueByPeriod('year');
  494.                 }
  495.                 break;
  496.         }
  497.         $data = [
  498.             'categories' => $categories,
  499.             'salesData' => $salesData,
  500.             'revenueData' => $revenueData,
  501.             'directRevenueData' => $directRevenueData,
  502.             'echeanceRevenueData' => $echeanceRevenueData
  503.         ];
  504.         return $this->json($data);
  505.     }
  506.     #[Route('/dashboard/credits-paid-details/{date}'name'app_dashboard_credits_paid_details')]
  507.     public function creditsPaidDetails(string $date): Response
  508.     {
  509.         try {
  510.             $user $this->getUser();
  511.             $isSuperAdmin $this->isGranted('ROLE_SUPER_ADMIN');
  512.             $isAdmin $this->isGranted('ROLE_ADMIN');
  513.             
  514.             // Valider le format de date
  515.             if (!preg_match('/^\d{4}-\d{2}-\d{2}$/'$date)) {
  516.                 return $this->json(['error' => 'Format de date invalide'], 400);
  517.             }
  518.             
  519.             // Convertir la date en DateTime avec gestion d'erreur
  520.             try {
  521.                 $targetDate = new \DateTime($date);
  522.                 $nextDay = clone $targetDate;
  523.                 $nextDay->modify('+1 day');
  524.             } catch (\Exception $e) {
  525.                 return $this->json(['error' => 'Date invalide'], 400);
  526.             }
  527.             
  528.             // Récupérer les paiements de dettes effectués à cette date
  529.             $queryBuilder $this->entityManager->getRepository(\App\Entity\PaiementDette::class)->createQueryBuilder('pd')
  530.                 ->leftJoin('pd.detteClient''dc')
  531.                 ->leftJoin('dc.client''c')
  532.                 ->leftJoin('dc.vente''v')
  533.                 ->leftJoin('v.boutique''b')
  534.                 ->where('pd.datePaiement >= :start')
  535.                 ->andWhere('pd.datePaiement < :end')
  536.                 ->setParameter('start'$targetDate->format('Y-m-d H:i:s'))
  537.                 ->setParameter('end'$nextDay->format('Y-m-d H:i:s'))
  538.                 ->orderBy('pd.datePaiement''DESC');
  539.             // Filtrer par boutique si l'utilisateur n'est pas admin
  540.             if (!$isSuperAdmin && !$isAdmin) {
  541.                 if ($user && method_exists($user'getBoutique') && $user->getBoutique()) {
  542.                     $queryBuilder->andWhere('v.boutique = :boutique')
  543.                         ->setParameter('boutique'$user->getBoutique());
  544.                 } else {
  545.                     return $this->json([]);
  546.                 }
  547.             }
  548.             $paiements $queryBuilder->getQuery()->getResult();
  549.             
  550.             $data = [];
  551.             foreach ($paiements as $paiement) {
  552.                 try {
  553.                     $detteClient $paiement->getDetteClient();
  554.                     if (!$detteClient) {
  555.                         continue; // Ignorer les paiements sans dette associée
  556.                     }
  557.                     
  558.                     $vente $detteClient->getVente();
  559.                     $client $detteClient->getClient();
  560.                     $boutique $vente $vente->getBoutique() : null;
  561.                     
  562.                     $data[] = [
  563.                         'client' => $client $client->getNom() : 'Client non spécifié',
  564.                         'phone' => $client $client->getPhone() : null,
  565.                         'boutique' => $boutique $boutique->getNom() : null,
  566.                         'montant' => $paiement->getMontant() ?? 0,
  567.                         'date' => $paiement->getDatePaiement() ? $paiement->getDatePaiement()->format('d/m/Y H:i') : 'Date inconnue',
  568.                         'statut' => 'Payé'
  569.                     ];
  570.                 } catch (\Exception $e) {
  571.                     // Ignorer les paiements problématiques et continuer
  572.                     continue;
  573.                 }
  574.             }
  575.             
  576.             return $this->json($data);
  577.             
  578.         } catch (\Exception $e) {
  579.             // Log l'erreur pour le débogage
  580.             error_log('Erreur dans creditsPaidDetails: ' $e->getMessage());
  581.             
  582.             return $this->json([
  583.                 'error' => 'Erreur lors du chargement des données',
  584.                 'message' => 'Une erreur est survenue lors de la récupération des crédits payés'
  585.             ], 500);
  586.         }
  587.     }
  588.     #[Route('/dashboard/credits-given-details/{date}'name'app_dashboard_credits_given_details')]
  589.     public function creditsGivenDetails(string $date): Response
  590.     {
  591.         try {
  592.             $user $this->getUser();
  593.             $isSuperAdmin $this->isGranted('ROLE_SUPER_ADMIN');
  594.             $isAdmin $this->isGranted('ROLE_ADMIN');
  595.             
  596.             // Valider le format de date
  597.             if (!preg_match('/^\d{4}-\d{2}-\d{2}$/'$date)) {
  598.                 return $this->json(['error' => 'Format de date invalide'], 400);
  599.             }
  600.             
  601.             // Convertir la date en DateTime avec gestion d'erreur
  602.             try {
  603.                 $targetDate = new \DateTime($date);
  604.                 $nextDay = clone $targetDate;
  605.                 $nextDay->modify('+1 day');
  606.             } catch (\Exception $e) {
  607.                 return $this->json(['error' => 'Date invalide'], 400);
  608.             }
  609.             
  610.             // Récupérer les ventes avec crédit (reste à payer > 0) créées à cette date
  611.             $queryBuilder $this->entityManager->getRepository(Vente::class)->createQueryBuilder('v')
  612.                 ->leftJoin('v.client''c')
  613.                 ->leftJoin('v.boutique''b')
  614.                 ->leftJoin('v.factures''f')
  615.                 ->where('v.dateVente >= :start')
  616.                 ->andWhere('v.dateVente < :end')
  617.                 ->andWhere('(v.type IS NULL OR v.type != :typeVirtuelle)')
  618.                 ->setParameter('start'$targetDate->format('Y-m-d H:i:s'))
  619.                 ->setParameter('end'$nextDay->format('Y-m-d H:i:s'))
  620.                 ->setParameter('typeVirtuelle''virtuelle')
  621.                 ->orderBy('v.dateVente''DESC');
  622.             // Filtrer par boutique si l'utilisateur n'est pas admin
  623.             if (!$isSuperAdmin && !$isAdmin) {
  624.                 if ($user && method_exists($user'getBoutique') && $user->getBoutique()) {
  625.                     $queryBuilder->andWhere('v.boutique = :boutique')
  626.                         ->setParameter('boutique'$user->getBoutique());
  627.                 } else {
  628.                     return $this->json([]);
  629.                 }
  630.             }
  631.             $ventes $queryBuilder->getQuery()->getResult();
  632.             
  633.             $data = [];
  634.             foreach ($ventes as $vente) {
  635.                 try {
  636.                     // Calculer le montant total de la vente
  637.                     $montantTotal $this->paiementService->calculerMontantTotalVente($vente);
  638.                     $montantPaye $this->paiementService->calculerMontantPayeVente($vente);
  639.                     $reste $montantTotal $montantPaye;
  640.                     
  641.                     // Ne garder que les ventes avec un reste à payer > 0
  642.                     if ($reste 0) {
  643.                         $client $vente->getClient();
  644.                         $boutique $vente->getBoutique();
  645.                         
  646.                         $data[] = [
  647.                             'client' => $client $client->getNom() : 'Client non spécifié',
  648.                             'phone' => $client $client->getPhone() : null,
  649.                             'boutique' => $boutique $boutique->getNom() : null,
  650.                             'montant' => $reste,
  651.                             'date' => $vente->getDateVente() ? $vente->getDateVente()->format('d/m/Y H:i') : 'Date inconnue',
  652.                             'statut' => 'En attente'
  653.                         ];
  654.                     }
  655.                 } catch (\Exception $e) {
  656.                     // Ignorer les ventes problématiques et continuer
  657.                     continue;
  658.                 }
  659.             }
  660.             
  661.             return $this->json($data);
  662.             
  663.         } catch (\Exception $e) {
  664.             // Log l'erreur pour le débogage
  665.             error_log('Erreur dans creditsGivenDetails: ' $e->getMessage());
  666.             
  667.             return $this->json([
  668.                 'error' => 'Erreur lors du chargement des données',
  669.                 'message' => 'Une erreur est survenue lors de la récupération des crédits accordés'
  670.             ], 500);
  671.         }
  672.     }
  673. }