src/Repository/ProductionRepository.php line 2352

Open in your IDE?
  1. <?php
  2. namespace App\Repository;
  3. use App\Entity\Cluster;
  4. use DateTime;
  5. use App\Entity\User;
  6. use App\Entity\Production;
  7. use App\Entity\EtatProduction;
  8. use App\Entity\PointOfSale;
  9. use App\Service\HierarchyService;
  10. use Doctrine\Persistence\ManagerRegistry;
  11. use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
  12. use Doctrine\ORM\Tools\Pagination\Paginator;
  13. use Doctrine\Common\Collections\Criteria;
  14. use Doctrine\ORM\Query\Expr\Join;
  15. use Doctrine\ORM\QueryBuilder;
  16. /**
  17.  * @extends ServiceEntityRepository<Production>
  18.  *
  19.  * @method Production|null find($id, $lockMode = null, $lockVersion = null)
  20.  * @method Production|null findOneBy(array $criteria, array $orderBy = null)
  21.  * @method Production[]    findAll()
  22.  * @method Production[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
  23.  */
  24. class ProductionRepository extends ServiceEntityRepository
  25. {
  26.     private $hierarchyService;
  27.     public function __construct(ManagerRegistry $registryHierarchyService $hierarchyService)
  28.     {
  29.         parent::__construct($registryProduction::class);
  30.         $this->hierarchyService $hierarchyService;
  31.     }
  32.     public function add(Production $entitybool $flush false): void
  33.     {
  34.         $this->getEntityManager()->persist($entity);
  35.         if ($flush) {
  36.             $this->getEntityManager()->flush();
  37.         }
  38.     }
  39.     public function remove(Production $entitybool $flush false): void
  40.     {
  41.         $this->getEntityManager()->remove($entity);
  42.         if ($flush) {
  43.             $this->getEntityManager()->flush();
  44.         }
  45.     }
  46.     public function findProductionsByStatusAndDateByIdOfSales($debut$fin$pointOfSale$page$limit$category$product$etat)
  47.     {
  48.         if ($debut != null) {
  49.             $dateDebutFormat $debut->format('Y-m-d');
  50.         }
  51.         if ($fin != null) {
  52.             $dateFinFormat $fin->format('Y-m-d');
  53.         } elseif ($debut != null && $fin == null) {
  54.             $dateFinFormat $debut->format('Y-m-d');
  55.         }
  56.         $stmt $this->createQueryBuilder('p')
  57.             ->where('p.pointOfSale = :pointOfSale')
  58.             ->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :dateDebut')
  59.             ->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :dateFin')
  60.             ->setParameter('pointOfSale'$pointOfSale)
  61.             ->setParameter('dateDebut'$dateDebutFormat)
  62.             ->setParameter('dateFin'$dateFinFormat);
  63.         if ($category != null) {
  64.             $stmt->andWhere('p.category = :category')
  65.                 ->setParameter('category'$category);
  66.         }
  67.         if ($product != null) {
  68.             $stmt->andWhere('p.product = :product')
  69.                 ->setParameter('product'$product);
  70.         }
  71.         if ($etat != null) {
  72.             $stmt->andWhere('p.etat = :etat')
  73.                 ->setParameter('etat'$etat);
  74.         }
  75.         $stmt->setFirstResult(($page 1) * $limit)
  76.             ->setMaxResults($limit);
  77.         $query $stmt->getQuery();
  78.         $paginator = new Paginator($query$fetchJoinCollection true);
  79.         return $paginator;
  80.     }
  81.     public function findProductionsByStatusAndDateByIdOfSaleV2($debut$fin$pointOfSale)
  82.     {
  83.         $criteria Criteria::create()
  84.             ->where(Criteria::expr()->eq('pointOfSale'$pointOfSale));
  85.         if ($debut !== null) {
  86.             $criteria->andWhere(Criteria::expr()->gte('dateCmdA'$debut));
  87.         }
  88.         if ($fin !== null) {
  89.             $criteria->andWhere(Criteria::expr()->lte('dateCmdA'$fin));
  90.         }
  91.         return $this->matching($criteria)->toArray();
  92.     }
  93.     public function findProductionsByStatusAndDateByIdOfSaleV2_test($debut$fin$pointOfSale)
  94.     {
  95.         $qb $this->createQueryBuilder('p')
  96.             ->where('p.pointOfSale = :pointOfSale')
  97.             ->setParameter('pointOfSale'$pointOfSale);
  98.         if ($debut !== null) {
  99.             $qb->andWhere('p.dateCmdA >= :debut')
  100.                 ->setParameter('debut'$debut);
  101.         }
  102.         if ($fin !== null) {
  103.             $qb->andWhere('p.dateCmdA <= :fin')
  104.                 ->setParameter('fin'$fin);
  105.         }
  106.         // Assurez-vous qu'aucune limite n'est définie
  107.         $qb->setMaxResults(null)->setFirstResult(null);
  108.         return $qb->getQuery()->getResult();
  109.     }
  110.     // public function findProductionsByStatusAndDateByIdOfSaleV2($debut, $fin, $pointOfSale)
  111.     // {
  112.     //     if ($debut != null) {
  113.     //         $dateDebutFormat = $debut->format('Y-m-d');
  114.     //     }
  115.     //     if ($fin != null) {
  116.     //         $dateFinFormat = $debut->format('Y-m-d');
  117.     //     }
  118.     //     $stmt = $this->createQueryBuilder('p')
  119.     //         ->where('p.pointOfSale = :pointOfSale')
  120.     //         ->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :dateDebut')
  121.     //         ->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :dateFin')
  122.     //         ->setParameter('pointOfSale', $pointOfSale)
  123.     //         ->setParameter('dateDebut', $dateDebutFormat)
  124.     //         ->setParameter('dateFin', $dateFinFormat);
  125.     //     $query = $stmt->getQuery();
  126.     //     return $query->getArrayResult();
  127.     // }
  128.     public function findProductionsByStatusAndSale($children, ?DateTime $debut, ?int $id, ?EtatProduction $etat)
  129.     {
  130.         $stmt $this->createQueryBuilder('p')
  131.             ->leftJoin('p.pointOfSale''f')
  132.             ->leftJoin('p.declarative''d')
  133.             ->leftJoin('d.seller''s')
  134.             ->where('s.id IN (:sellers)')
  135.             ->setParameter('sellers'$children)
  136.             ->andWhere('f.id IN (:point_of_sale)')
  137.             ->setParameter('point_of_sale'$id);
  138.         if ($debut != null) {
  139.             $dateDebutFormat $debut->format('Y-m-d 00:00:00:00');
  140.             $stmt->andWhere('p.dateVenteValidB >= :dateDebut')
  141.                 ->setParameter('dateDebut'$dateDebutFormat);
  142.         }
  143.         if ($etat != null) {
  144.             $stmt->andWhere('p.etat = :etat')
  145.                 ->setParameter('etat'$etat->getId())
  146.             ;
  147.         }
  148.         return $stmt
  149.             ->getQuery()
  150.             ->getResult();
  151.     }
  152.     public function getProductionForTurnover(?string $year, ?string $monthUser $user, ?int $etatId, ?int $operatorId)
  153.     {
  154.         $children $this->hierarchyService->childrenArray($user);
  155.         $stmt $this->createQueryBuilder('p')
  156.             ->leftJoin('p.declarative''d')
  157.             ->leftJoin('d.seller''s')
  158.             ->where('s.id IN (:sellers)')
  159.             ->setParameter('sellers'$children);
  160.         if ($year) {
  161.             $stmt->andwhere('YEAR(p.createdAt) = :year');
  162.             $stmt->setParameter('year'$year);
  163.         }
  164.         if ($month) {
  165.             $stmt->andwhere('MONTH(p.createdAt) = :month');
  166.             $stmt->setParameter('month'$month);
  167.         }
  168.         if ($etatId) {
  169.             $stmt->andwhere('p.etat = :etatId');
  170.             $stmt->setParameter('etatId'$etatId);
  171.         }
  172.         if ($operatorId) {
  173.             $stmt->andwhere('p.operator = :operatorId');
  174.             $stmt->setParameter('operatorId'$operatorId);
  175.         }
  176.         return $stmt->getQuery()->getResult();
  177.     }
  178.     /**
  179.      * @return Production[] Returns an array of Production objects
  180.      */
  181.     public function getProductionCoordinates($codeCluster$codeInsee, ?DateTime $debut, ?DateTime $fin): array
  182.     {
  183.         if ($debut != null) {
  184.             $dateDebutFormat $debut->format('Y-m-d');
  185.         }
  186.         if ($fin != null) {
  187.             $dateFinFormat $fin->format('Y-m-d');
  188.         } elseif ($debut != null && $fin == null) {
  189.             $dateFinFormat $debut->format('Y-m-d');
  190.         }
  191.         $qb $this->createQueryBuilder('p')
  192.             ->select(
  193.                 'p.codeInsee AS code_insee',
  194.                 'p.latitude as latitude',
  195.                 'p.longitude as longitude',
  196.             )
  197.             ->leftJoin('p.cluster''c')
  198.             ->addSelect('c.codeCluster AS code_cluster')
  199.             ->andWhere('c.codeCluster = :codeCluster')
  200.             ->setParameter('codeCluster'$codeCluster);
  201.         if ($codeInsee) {
  202.             $qb->andWhere('p.codeInsee = :codeInsee')
  203.                 ->setParameter('codeInsee'$codeInsee);
  204.         }
  205.         if ($debut != null) {
  206.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut')
  207.                 ->setParameter('dateDebut'$dateDebutFormat)
  208.                 ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin')
  209.                 ->setParameter('dateFin'$dateFinFormat)
  210.             ;
  211.         }
  212.         return $qb->getQuery()->getResult();
  213.     }
  214.     /**
  215.      * @return Production[] Returns an array of Production objects
  216.      */
  217.     public function getProductionDeclarativeCoordinates($children, ?DateTime $debut, ?DateTime $fin): array
  218.     {
  219.         if ($debut != null) {
  220.             $dateDebutFormat $debut->format('Y-m-d');
  221.         }
  222.         if ($fin != null) {
  223.             $dateFinFormat $fin->format('Y-m-d');
  224.         } elseif ($debut != null && $fin == null) {
  225.             $dateFinFormat $debut->format('Y-m-d');
  226.         }
  227.         $qb $this->createQueryBuilder('p')
  228.             ->select(
  229.                 'p.latitude as latitude',
  230.                 'p.longitude as longitude',
  231.             )
  232.             ->leftJoin('p.declarative''d''WITH''p.numCommande = d.numCommande')
  233.             ->leftJoin('d.seller''s')
  234.             ->where('s.id IN (:sellers)')
  235.             ->setParameter('sellers'$children);
  236.         if ($debut != null) {
  237.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut')
  238.                 ->setParameter('dateDebut'$dateDebutFormat)
  239.                 ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin')
  240.                 ->setParameter('dateFin'$dateFinFormat)
  241.             ;
  242.         }
  243.         return $qb->getQuery()->getResult();
  244.     }
  245.     /**
  246.      * @return Production[] Returns an array of Production objects
  247.      */
  248.     public function findProductionsTotalsByStatusAndDateAndUserIdGroupByCategory(
  249.         $children,
  250.         $user,
  251.         PointOfSale $pointOfSale,
  252.         ?DateTime $debut,
  253.         ?DateTime $fin
  254.     ): array {
  255.         $qb $this->createQueryBuilder('p')
  256.             ->select(
  257.                 // Totaux globaux
  258.                 '(SELECT SUM(CASE WHEN DATE_FORMAT(p1.dateCmdA, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p1.dateCmdA, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) 
  259.               FROM App\Entity\Production p1 
  260.               WHERE p1.pointOfSale = :pointOfSale AND p1.seller IN (:sellers)) as totalLignes_BRUT',
  261.                 '(SELECT SUM(CASE WHEN DATE_FORMAT(p2.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p2.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) 
  262.               FROM App\Entity\Production p2
  263.               WHERE p2.pointOfSale = :pointOfSale AND p2.seller IN (:sellers)) as totalLignes_VALID',
  264.                 '(SELECT SUM(CASE WHEN DATE_FORMAT(p3.dateRacc, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p3.dateRacc, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) 
  265.               FROM App\Entity\Production p3
  266.               WHERE p3.pointOfSale = :pointOfSale AND p3.seller IN (:sellers)) as totalLignes_RACC',
  267.                 // Data par catégorie et par seller
  268.                 'c.name as categoryName',
  269.                 's.id as id',
  270.                 's.prenom as prenom',
  271.                 'SUM(CASE WHEN DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) as BRUT',
  272.                 'SUM(CASE WHEN DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) as VALID',
  273.                 'SUM(CASE WHEN DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :dateFin THEN 1 ELSE 0 END) as RACC'
  274.             )
  275.             ->leftJoin('p.product''pr')
  276.             ->leftJoin('pr.category''c')
  277.             ->leftJoin('p.seller''s')
  278.             ->where('p.pointOfSale = :pointOfSale')
  279.             ->andWhere('p.seller IN (:sellers)')
  280.             ->setParameter('sellers'$children)
  281.             ->setParameter('pointOfSale'$pointOfSale)
  282.             ->setParameter('dateDebut'$debut $debut->format('Y-m-d') : null)
  283.             ->setParameter('dateFin'$fin $fin->format('Y-m-d') : null)
  284.             ->groupBy('c.name, s.id, s.prenom');
  285.         if ($debut !== null) {
  286.             $qb->andWhere(
  287.                 '(DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :dateFin) 
  288.             OR (DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin)
  289.             OR (DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :dateDebut AND DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :dateFin)'
  290.             );
  291.         }
  292.         if ($fin !== null) {
  293.             $qb->andWhere(
  294.                 '(DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :dateFin)
  295.                 OR (DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin)
  296.                 OR (DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :dateFin)'
  297.             );
  298.         }
  299.         return $qb->getQuery()->getArrayResult();
  300.     }
  301.     public function findProductionsUserIdTreeFirstkpi(
  302.         $parents,
  303.         $pointOfSale,
  304.         $clusterId,
  305.         $codeCluster,
  306.         $codeInsee,
  307.         ?DateTime $debut,
  308.         ?DateTime $fin,
  309.         $category,
  310.         $optionSelect
  311.     ): array {
  312.         $qb $this->createQueryBuilder('p')
  313.             ->select('COUNT(p.id) as totalVentes, po.code as cpv');
  314.         $qb->leftJoin('p.pointOfSale''po')
  315.             ->where('p.pointOfSale = :pointOfSale')
  316.             ->setParameter('pointOfSale'$pointOfSale)
  317.         ;
  318.         // Optionals
  319.         $qb->leftJoin('p.cluster''c')
  320.             ->addSelect('c.codeCluster as codeCluster, c.libelleCluster as nomCluster,c.id as idCluster');
  321.         $groupByFields = ['p.cluster'];
  322.         if ($clusterId !== null) {
  323.             $qb->andWhere('c.id = :cluster')
  324.                 ->setParameter('cluster'$clusterId)
  325.                 ->addSelect('p.codeInsee as codeInsee,p.ville as ville');
  326.             $groupByFields[] = 'p.codeInsee';
  327.         } else {
  328.             if ($codeCluster) {
  329.                 $qb->andWhere('c.codeCluster = :codeCluster')
  330.                     ->setParameter('codeCluster'$codeCluster)
  331.                     ->addSelect('p.codeInsee as codeInsee,p.ville as ville');
  332.                 $groupByFields[] = 'p.codeInsee';
  333.             }
  334.         }
  335.         if ($category !== null) {
  336.             $qb->andWhere('p.category = :category')
  337.                 ->leftJoin('p.category''cat')
  338.                 ->addSelect('cat.name as categoryName')
  339.                 ->setParameter('category'$category);
  340.         }
  341.         if ($codeInsee !== null) {
  342.             $qb
  343.                 ->addSelect('p.streetName as nomVoie')
  344.                 ->andWhere('p.codeInsee = :codeInsee')
  345.                 ->setParameter('codeInsee'$codeInsee);
  346.             $groupByFields[] = 'p.streetName';
  347.         }
  348.         switch ($optionSelect) {
  349.             case 'V':
  350.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  351.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  352.                 break;
  353.             case 'R':
  354.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  355.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  356.                 break;
  357.             case 'B':
  358.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  359.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  360.                 break;
  361.         }
  362.         $qb->setParameter('debut'$debut->format('Y-m-d'))
  363.             ->setParameter('fin'$fin->format('Y-m-d'));
  364.         if (is_array($parents) && count($parents) > 0) {
  365.             $qb->andWhere('p.seller IN (:parents)')
  366.                 ->leftJoin('p.seller''s')
  367.                 ->addSelect('s.id as idSeller , s.prenom as prenomSeller')
  368.                 ->setParameter('parents'$parents);
  369.             $groupByFields[] = 's.id';
  370.         }
  371.         $qb->orderBy('totalVentes''DESC');
  372.         $qb->groupBy(
  373.             implode(', '$groupByFields)
  374.         );
  375.         // var_dump($qb->getQuery()->getSQL());
  376.         return $qb->getQuery()->getArrayResult();
  377.     }
  378.     public function findProductionsByOption(
  379.         $pointOfSale,
  380.         $month,
  381.         $year,
  382.         $etat,
  383.         $option,
  384.         $children = []
  385.     ): array {
  386.         $qb $this->createQueryBuilder('p')
  387.             ->select('
  388.                 COUNT(p.id) as totalVentes,
  389.                 po.code as cpv
  390.             ')
  391.             ->leftJoin('p.pointOfSale''po')
  392.             ->where('p.pointOfSale = :pointOfSale')
  393.             ->setParameter('pointOfSale'$pointOfSale)
  394.             ->andWhere('YEAR(p.dateRacc) = :year')
  395.             ->setParameter('year'$year)
  396.             ->andWhere('p.etat = :etat')
  397.             ->setParameter('etat'$etat);
  398.         // Si option est 'raccordement', filtrer sur le mois de raccordement
  399.         if ($option === 'raccordement') {
  400.             $qb->andWhere('MONTH(p.dateRacc) = :month')
  401.                 ->addSelect('MONTH(p.dateVenteValidB) as moisValidation')
  402.                 ->setParameter('month'$month);
  403.             $qb->groupBy('moisValidation'); // Regrouper par mois de raccordement
  404.         } else {
  405.             $qb->andWhere('MONTH(p.dateVenteValidB) = :month')
  406.                 ->addSelect('MONTH(p.dateRacc) as moisRacc')
  407.                 ->setParameter('month'$month);
  408.             $qb->groupBy('moisRacc');
  409.         }
  410.         if (count($children) > 0) {
  411.             $qb->andWhere('p.seller IN (:children)')
  412.                 ->leftJoin('p.seller''s')
  413.                 ->addSelect('
  414.                     s.id as idSeller,
  415.                     s.prenom as prenomSeller
  416.                 ')
  417.                 ->setParameter('children'$children);
  418.         }
  419.         $qb->orderBy('totalVentes''DESC');
  420.         return $qb->getQuery()->getArrayResult();
  421.     }
  422.     public function findProductionsByStatusAndDate(
  423.         $children,
  424.         $dateDebutVenteValidB,
  425.         $dateFinVenteValidB,
  426.         $etat
  427.     ) {
  428.         $qb $this->createQueryBuilder('p');
  429.         if ($etat != null) {
  430.             $qb->andWhere('p.etat = :etat')
  431.                 ->setParameter('etat'$etat);
  432.         }
  433.         if ($children) {
  434.             $qb->andWhere('s.id IN (:children)')
  435.                 ->leftJoin('p.seller''s')
  436.                 ->setParameter('children'$children);
  437.         }
  438.         if ($dateDebutVenteValidB) {
  439.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut')
  440.                 ->setParameter('dateDebut'$dateDebutVenteValidB->format('Y-m-d'));
  441.         }
  442.         if ($dateFinVenteValidB) {
  443.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin')
  444.                 ->setParameter('dateFin'$dateFinVenteValidB->format('Y-m-d'));
  445.         }
  446.         return $qb->getQuery()->getResult();
  447.     }
  448.     public function findProductionsByStatusAndDateAndPointOfSale(
  449.         $children,
  450.         $pointOfSale,
  451.         $dateDebutVenteValidB,
  452.         $dateFinVenteValidB,
  453.         $etat
  454.     ) {
  455.         $qb $this->createQueryBuilder('p')
  456.             ->leftJoin('p.pointOfSale''po')
  457.             ->where('p.pointOfSale = :pointOfSale')
  458.             ->setParameter('pointOfSale'$pointOfSale);
  459.         if ($etat != null) {
  460.             $qb->andWhere('p.etat = :etat')
  461.                 ->setParameter('etat'$etat);
  462.         }
  463.         if (count($children) > 0) {
  464.             $qb->andWhere('p.seller IN (:children)')
  465.                 ->leftJoin('p.seller''s')
  466.                 // ->addSelect('
  467.                 //         s.id as idSeller,
  468.                 //         s.prenom as prenomSeller
  469.                 //     ')
  470.                 ->setParameter('children'$children);
  471.         }
  472.         if ($dateDebutVenteValidB) {
  473.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :dateDebut')
  474.                 ->setParameter('dateDebut'$dateDebutVenteValidB->format('Y-m-d'));
  475.         }
  476.         if ($dateFinVenteValidB) {
  477.             $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :dateFin')
  478.                 ->setParameter('dateFin'$dateFinVenteValidB->format('Y-m-d'));
  479.         }
  480.         return $qb->getQuery()->getResult();
  481.     }
  482.     public function findProductionsByOptionGroup(
  483.         $pointOfSale,
  484.         $optionSelect,
  485.         $optionGroup,
  486.         $debut,
  487.         $fin,
  488.         $codeCluster,
  489.         $codeInsee,
  490.         $childs,
  491.         $organisationId,
  492.         $category
  493.     ) {
  494.         $qb $this->createQueryBuilder('p')
  495.             ->select('COUNT(p.id) as totalVentes');
  496.         // Joindre et sélectionner cpv uniquement si pas d’organisation
  497.         if (!$organisationId) {
  498.             $qb->addSelect('po.code as cpv')
  499.                 ->leftJoin('p.pointOfSale''po');
  500.         }
  501.         if ($pointOfSale !== null) {
  502.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  503.                 ->setParameter('pointOfSale'$pointOfSale);
  504.         }
  505.         // Filtre de dates
  506.         switch ($optionSelect) {
  507.             case 'V':
  508.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  509.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  510.                 break;
  511.             case 'R':
  512.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  513.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  514.                 break;
  515.             case 'B':
  516.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  517.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  518.                 break;
  519.         }
  520.         $qb->setParameter('debut'$debut->format('Y-m-d'))
  521.             ->setParameter('fin'$fin->format('Y-m-d'));
  522.         // Filtre cluster
  523.         if ($codeCluster) {
  524.             $qb->leftJoin('p.cluster''cluster')
  525.                 ->addSelect('cluster.codeCluster as clusterCode')
  526.                 ->andWhere('cluster.codeCluster = :codeCluster')
  527.                 ->setParameter('codeCluster'$codeCluster);
  528.         }
  529.         if ($codeInsee) {
  530.             $qb->addSelect('p.codeInsee')
  531.                 ->andWhere('p.codeInsee = :codeInsee')
  532.                 ->setParameter('codeInsee'$codeInsee);
  533.         }
  534.         if ($organisationId) {
  535.             $qb->andWhere('p.organisation = :organisationId')
  536.                 ->setParameter('organisationId'$organisationId);
  537.         }
  538.         if (is_array($childs) && count($childs) > 0) {
  539.             $qb->andWhere('p.seller IN (:childs)')
  540.                 ->leftJoin('p.seller''s')
  541.                 ->setParameter('childs'$childs);
  542.         }
  543.         // Gestion du groupement
  544.         switch ($optionGroup) {
  545.             case 'Tech':
  546.                 $qb->addSelect('p.tech as tech');
  547.                 if (!$organisationId) {
  548.                     $qb->groupBy('po.code, p.tech');
  549.                 } else {
  550.                     $qb->groupBy('p.tech');
  551.                 }
  552.                 break;
  553.             case 'Categorie':
  554.                 $qb->addSelect('category.name as categoryName')
  555.                     ->leftJoin('p.category''category');
  556.                 if (!$organisationId) {
  557.                     $qb->groupBy('po.code, category.id');
  558.                 } else {
  559.                     $qb->groupBy('category.id');
  560.                 }
  561.                 break;
  562.             case 'Produit':
  563.                 $qb->addSelect('product.name as productName, cat.name as categoryName')
  564.                     ->leftJoin('p.product''product')
  565.                     ->leftJoin('product.category''cat');
  566.                 if (!$organisationId) {
  567.                     $qb->groupBy('po.code, product.id');
  568.                 } else {
  569.                     $qb->groupBy('product.id');
  570.                 }
  571.                 if ($category) {
  572.                     $category array_filter(
  573.                         array_map('intval'explode(','$category)),
  574.                         fn($id) => $id 0
  575.                     );
  576.                     $qb->andWhere('p.category IN (:categories)')
  577.                         ->setParameter('categories'$category);
  578.                 }
  579.                 break;
  580.             case 'PreCommande':
  581.                 $qb->addSelect('p.preCommande as preCommande');
  582.                 if (!$organisationId) {
  583.                     $qb->groupBy('po.code, p.preCommande');
  584.                 } else {
  585.                     $qb->groupBy('p.preCommande');
  586.                 }
  587.                 break;
  588.             default:
  589.                 if (!$organisationId) {
  590.                     $qb->groupBy('po.code');
  591.                 }
  592.                 break;
  593.         }
  594.         return $qb->getQuery()->getResult();
  595.     }
  596.     public function  findProductionsByLblMotifInstance(
  597.         $pointOfSale,
  598.         $debut,
  599.         $fin,
  600.         $codeCluster,
  601.         $codeInsee,
  602.         $childs,
  603.         $organisationId
  604.     ) {
  605.         $qb $this->createQueryBuilder('p')
  606.             ->select('
  607.                 COUNT(p.id) as totalVentes,
  608.                 po.code as cpv,
  609.                 p.libelleMotifInstance as libelle_motif_instance
  610.             ')
  611.             ->leftJoin('p.pointOfSale''po');
  612.         if ($pointOfSale) {
  613.             $qb->where('p.pointOfSale = :pointOfSale')
  614.                 ->setParameter('pointOfSale'$pointOfSale);
  615.         }
  616.         //seuelemnt les KO
  617.         $qb->leftJoin('p.etat''e')
  618.             ->andWhere('e.id = :idEtat')
  619.             ->setParameter('idEtat'2)
  620.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut')
  621.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin')
  622.             ->setParameter('debut'$debut->format('Y-m-d'))
  623.             ->setParameter('fin'$fin->format('Y-m-d'));
  624.         if ($codeCluster) {
  625.             $qb->leftJoin('p.cluster''c')
  626.                 ->addSelect('c.codeCluster')
  627.                 ->andWhere('c.codeCluster = :codeCluster')
  628.                 ->setParameter('codeCluster'$codeCluster)
  629.             ;
  630.         }
  631.         if ($codeInsee) {
  632.             $qb->addSelect('p.codeInsee')
  633.                 ->andWhere('p.codeInsee = :codeInsee')
  634.                 ->setParameter('codeInsee'$codeInsee)
  635.             ;
  636.         }
  637.         if ($organisationId) {
  638.             $qb->andWhere('p.organisation = :organisationId')
  639.                 ->setParameter('organisationId'$organisationId);
  640.         }
  641.         if (is_array($childs) && count($childs) > 0) {
  642.             $qb->andWhere('p.seller IN (:childs)')
  643.                 ->leftJoin('p.seller''s')
  644.                 ->setParameter('childs'$childs);
  645.         }
  646.         $qb->groupBy('p.libelleMotifInstance, po.id');
  647.         return $qb->getQuery()->getResult();
  648.     }
  649.     public function findProductionsByStatus($children$pointOfSale$debut$fin)
  650.     {
  651.         $qb $this->createQueryBuilder('p')
  652.             ->select('
  653.                 COUNT(p.id) as totalVentes,
  654.                 po.code as cpv,
  655.                 e.nom as status
  656.             ')
  657.             ->leftJoin('p.pointOfSale''po')
  658.             ->where('p.pointOfSale = :pointOfSale')
  659.             ->setParameter('pointOfSale'$pointOfSale)
  660.             ->leftJoin('p.etat''e');
  661.         $groupByFields = ['e.nom''e.id'];
  662.         if (count($children) > 0) {
  663.             $qb->andWhere('p.seller IN (:children)')
  664.                 ->leftJoin('p.seller''s')
  665.                 ->addSelect('
  666.                             s.id as idSeller,
  667.                             s.prenom as prenomSeller
  668.                         ')
  669.                 ->setParameter('children'$children);
  670.             $groupByFields[] = 's.id';
  671.         }
  672.         $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut')
  673.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin')
  674.             ->setParameter('debut'$debut->format('Y-m-d'))
  675.             ->setParameter('fin'$fin->format('Y-m-d'))
  676.             ->orderBy('e.id')
  677.             ->groupBy(
  678.                 implode(', '$groupByFields)
  679.             );
  680.         return $qb->getQuery()->getResult();
  681.     }
  682.     public function findProductionsByAnnulationTypes($pointOfSale$debut$fin$codeCluster$codeInsee$childs$organisationId)
  683.     {
  684.         $qb $this->createQueryBuilder('p')
  685.             ->select(
  686.                 'SUM(CASE 
  687.                 WHEN p.motifInstance LIKE :reseauMotif1 
  688.                   OR p.motifInstance LIKE :reseauMotif2 
  689.                   OR p.motifInstance LIKE :reseauMotif3 
  690.                   OR p.motifInstance LIKE :reseauMotif4 THEN 1 
  691.                 ELSE 0 
  692.             END) AS totalReseau',
  693.                 'SUM(CASE 
  694.                 WHEN p.motifInstance LIKE :ventesMotif1 
  695.                   OR p.motifInstance LIKE :ventesMotif2 THEN 1 
  696.                 ELSE 0 
  697.             END) AS totalVentes',
  698.                 'po.code AS cpv'
  699.             )
  700.             ->leftJoin('p.pointOfSale''po')
  701.             ->leftJoin('p.etat''e')
  702.             ->andWhere('e.id = :idEtat')
  703.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut')
  704.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin')
  705.             ->setParameter('idEtat'2)
  706.             ->setParameter('debut'$debut->format('Y-m-d'))
  707.             ->setParameter('fin'$fin->format('Y-m-d'))
  708.             ->setParameter('reseauMotif1''T%')
  709.             ->setParameter('reseauMotif2''R%')
  710.             ->setParameter('reseauMotif3''O%')
  711.             ->setParameter('reseauMotif4''E%')
  712.             ->setParameter('ventesMotif1''S%')
  713.             ->setParameter('ventesMotif2''A%');
  714.         // CORRECTION : utiliser andWhere, jamais where seul
  715.         if (!empty($pointOfSale)) {
  716.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  717.                 ->setParameter('pointOfSale'$pointOfSale);
  718.         }
  719.         if (!empty($codeCluster)) {
  720.             $qb->leftJoin('p.cluster''c')
  721.                 ->addSelect('c.codeCluster')
  722.                 ->andWhere('c.codeCluster = :codeCluster')
  723.                 ->setParameter('codeCluster'$codeCluster);
  724.         }
  725.         if (!empty($codeInsee)) {
  726.             $qb->addSelect('p.codeInsee')
  727.                 ->andWhere('p.codeInsee = :codeInsee')
  728.                 ->setParameter('codeInsee'$codeInsee);
  729.         }
  730.         if ($organisationId) {
  731.             $qb->andWhere('p.organisation = :organisationId')
  732.                 ->setParameter('organisationId'$organisationId);
  733.         }
  734.         // if (!empty($children)) {
  735.         //     $qb->leftJoin('p.seller', 's')
  736.         //         ->addSelect('s.id as idSeller, s.prenom as prenomSeller')
  737.         //         ->andWhere('p.seller IN (:children)')
  738.         //         ->setParameter('children', $children)
  739.         //         ->groupBy('s.id');
  740.         // }
  741.         if (is_array($childs) && count($childs) > 0) {
  742.             $qb->andWhere('p.seller IN (:childs)')
  743.                 ->leftJoin('p.seller''s')
  744.                 ->setParameter('childs'$childs);
  745.         }
  746.         $qb->groupBy('po.id');
  747.         return $qb->getQuery()->getResult();
  748.     }
  749.     public function findProductionsByStatutIntervention($pointOfSale$debut$fin$codeCluster$codeInsee$childs$organisationId)
  750.     {
  751.         $qb $this->createQueryBuilder('p')
  752.             ->select(
  753.                 'SUM(CASE WHEN p.statutIntervention = :statut1 THEN 1 ELSE 0 END) AS totalXapRDV',
  754.                 'SUM(CASE WHEN p.statutIntervention = :statut2 THEN 1 ELSE 0 END) AS totalXavRDV',
  755.                 'po.code AS cpv'
  756.             )
  757.             ->leftJoin('p.pointOfSale''po')
  758.             ->leftJoin('p.etat''e')
  759.             ->andWhere('e.id = :idEtat')
  760.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut')
  761.             ->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin')
  762.             ->groupBy('po.id')
  763.             ->setParameter('statut1''XapRDV')
  764.             ->setParameter('statut2''XavRDV')
  765.             ->setParameter('idEtat'2)
  766.             ->setParameter('debut'$debut->format('Y-m-d'))
  767.             ->setParameter('fin'$fin->format('Y-m-d'));
  768.         if (!empty($pointOfSale)) {
  769.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  770.                 ->setParameter('pointOfSale'$pointOfSale);
  771.         }
  772.         if (!empty($codeCluster)) {
  773.             $qb->leftJoin('p.cluster''c')
  774.                 ->addSelect('c.codeCluster')
  775.                 ->andWhere('c.codeCluster = :codeCluster')
  776.                 ->setParameter('codeCluster'$codeCluster);
  777.         }
  778.         if (!empty($codeInsee)) {
  779.             $qb->addSelect('p.codeInsee')
  780.                 ->andWhere('p.codeInsee = :codeInsee')
  781.                 ->setParameter('codeInsee'$codeInsee);
  782.         }
  783.         if (is_array($childs) && count($childs) > 0) {
  784.             $qb->andWhere('p.seller IN (:childs)')
  785.                 ->leftJoin('p.seller''s')
  786.                 ->setParameter('childs'$childs);
  787.         }
  788.         if ($organisationId) {
  789.             $qb->andWhere('p.organisation = :organisationId')
  790.                 ->setParameter('organisationId'$organisationId);
  791.         }
  792.         return $qb->getQuery()->getResult();
  793.     }
  794.     public function findProductionsByCategoryFor12Months($pointOfSale$mois$year$codeCluster$codeInsee$childs$organisationId)
  795.     {
  796.         $mois $this->generatePreviousMonths($year$mois12);
  797.         $qb $this->createQueryBuilder('p');
  798.         foreach ($mois as $cle => $moisData) {
  799.             $qb->addSelect(sprintf(
  800.                 "SUM(CASE WHEN DATE_FORMAT(p.dateCmdA, '%%Y-%%m') = '%s' THEN 1 ELSE 0 END) AS total_date_cmd_a_%d",
  801.                 $moisData['mois_annee'],
  802.                 $cle
  803.             ));
  804.             $qb->addSelect(sprintf(
  805.                 "SUM(CASE WHEN DATE_FORMAT(p.dateVenteValidB, '%%Y-%%m') = '%s' THEN 1 ELSE 0 END) AS total_date_vente_valid_b_%d",
  806.                 $moisData['mois_annee'],
  807.                 $cle
  808.             ));
  809.             $qb->addSelect(sprintf(
  810.                 "SUM(CASE WHEN DATE_FORMAT(p.dateRacc, '%%Y-%%m') = '%s' THEN 1 ELSE 0 END) AS total_date_racc_%d",
  811.                 $moisData['mois_annee'],
  812.                 $cle
  813.             ));
  814.         }
  815.         $qb
  816.             ->leftJoin('App\Entity\CategoryProduct''cp''WITH''p.category = cp.id')
  817.             ->leftJoin('App\Entity\PointOfSale''po''WITH''p.pointOfSale = po.id')
  818.             ->addSelect('cp.name as categoryName, po.code as cpv');
  819.         if ($pointOfSale) {
  820.             $qb
  821.                 ->where('p.pointOfSale = :pointOfSale')
  822.                 ->setParameter('pointOfSale'$pointOfSale);
  823.         }
  824.         if ($codeCluster) {
  825.             $qb->leftJoin('p.cluster''c')
  826.                 ->addSelect('c.codeCluster')
  827.                 ->andWhere('c.codeCluster = :codeCluster')
  828.                 ->setParameter('codeCluster'$codeCluster)
  829.             ;
  830.         }
  831.         if ($codeInsee) {
  832.             $qb->addSelect('p.codeInsee')
  833.                 ->andWhere('p.codeInsee = :codeInsee')
  834.                 ->setParameter('codeInsee'$codeInsee)
  835.             ;
  836.         }
  837.         if ($organisationId) {
  838.             $qb->andWhere('p.organisation = :organisationId')
  839.                 ->setParameter('organisationId'$organisationId);
  840.         }
  841.         $qb
  842.             ->groupBy('cp.name,po.id')
  843.             ->orderBy('cp.name''ASC');
  844.         // Ajout des colonnes statiques pour les mois
  845.         foreach ($mois as $cle => $moisData) {
  846.             $qb->addSelect(sprintf("'%s' AS mois_annee_%d"$moisData['mois_annee'], $cle));
  847.         }
  848.         if (is_array($childs) && count($childs) > 0) {
  849.             $qb->andWhere('p.seller IN (:childs)')
  850.                 ->leftJoin('p.seller''s')
  851.                 ->setParameter('childs'$childs);
  852.         }
  853.         return $qb->getQuery()->getResult();
  854.     }
  855.     public function getTotalVentesObjectifs($mois$annee)
  856.     {
  857.         $qb $this->createQueryBuilder('p')
  858.             // Filtre sur la date_vente_valid_b en fonction du mois et de l'année
  859.             ->innerJoin('App\Entity\PointOfSale''po'Join::WITH'p.pointOfSale = po.id')
  860.             ->innerJoin('App\Entity\NmdObjectifClusters''o'Join::WITH'po.code = o.cpv AND o.mois = :mois AND o.year = :annee')
  861.             ->innerJoin('App\Entity\Cluster''c'Join::WITH'p.cluster = c.id AND c.codeCluster = o.codeCluster')
  862.             ->innerJoin('App\Entity\EtatProduction''e'Join::WITH'p.etat = e.id')
  863.             // Sélectionner les champs
  864.             ->addSelect('po.code AS cpv')
  865.             ->addSelect('c.codeCluster AS codeCluster')
  866.             ->addSelect('MAX(c.libelleCluster) AS libelleCluster')
  867.             ->addSelect('e.nom AS etat')
  868.             ->addSelect('COUNT(p.id) AS totalVente')
  869.             ->addSelect('o.vv')
  870.             ->addSelect('o.vr')
  871.             ->addSelect('o.mois')
  872.             ->addSelect('o.year')
  873.             // Conditions de filtrage par mois et année
  874.             ->where('MONTH(p.dateVenteValidB) = :mois')
  875.             ->andWhere('YEAR(p.dateVenteValidB) = :annee')
  876.             ->setParameter('mois'$mois)
  877.             ->setParameter('annee'$annee)
  878.             // Regroupement
  879.             ->groupBy('po.code, c.codeCluster, e.id, o.mois, o.year')
  880.             // Ordre
  881.             ->orderBy('po.code''ASC')
  882.             ->addOrderBy('c.codeCluster''ASC')
  883.             // Exécuter la requête
  884.             ->getQuery();
  885.         return $qb->getArrayResult();
  886.     }
  887.     private function createBaseQueryBuilder(
  888.         $pointOfSale,
  889.         $codeCluster,
  890.         $codeInsee,
  891.         $mois,
  892.         $annee,
  893.         $optionSelect,
  894.         $departement
  895.     ): QueryBuilder {
  896.         $qb $this->createQueryBuilder('p')
  897.             ->leftJoin('p.pointOfSale''po')
  898.             ->leftJoin('p.cluster''c');
  899.         // ✅ N’ajouter la condition que si un point de vente est défini
  900.         if ($pointOfSale !== null) {
  901.             $qb->where('p.pointOfSale = :pointOfSale')
  902.                 ->setParameter('pointOfSale'$pointOfSale);
  903.         } else {
  904.             // sinon on s'assure juste d'avoir une clause WHERE neutre
  905.             $qb->where('1 = 1');
  906.         }
  907.         if ($codeCluster) {
  908.             $qb->andWhere('c.codeCluster = :codeCluster')
  909.                 ->setParameter('codeCluster'$codeCluster);
  910.         }
  911.         if ($codeInsee) {
  912.             $qb->andWhere('p.codeInsee = :codeInsee')
  913.                 ->setParameter('codeInsee'$codeInsee);
  914.         }
  915.         switch ($optionSelect) {
  916.             case 'V':
  917.                 $qb->andWhere('MONTH(p.dateVenteValidB) = :mois')
  918.                     ->andWhere('YEAR(p.dateVenteValidB) = :annee');
  919.                 break;
  920.             case 'R':
  921.                 $qb->andWhere('MONTH(p.dateRacc) = :mois')
  922.                     ->andWhere('YEAR(p.dateRacc) = :annee');
  923.                 break;
  924.             case 'B':
  925.                 $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  926.                     ->andWhere('YEAR(p.dateCmdA) = :annee');
  927.                 break;
  928.         }
  929.         if ($departement) {
  930.             $qb->andWhere('c.codeCluster LIKE :prefix')
  931.                 ->setParameter('prefix'$departement '-%');
  932.         }
  933.         $qb->setParameter('mois'$mois)
  934.             ->setParameter('annee'$annee);
  935.         return $qb;
  936.     }
  937.     private function createBaseQueryBuilderTest($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$organisationId$departement): QueryBuilder
  938.     {
  939.         $qb $this->createQueryBuilder('p')
  940.             ->leftJoin('p.cluster''c');
  941.         if ($organisationId) {
  942.             $qb->andWhere('p.organisation = :organisationId')
  943.                 ->setParameter('organisationId'$organisationId);
  944.         } else {
  945.             $qb->leftJoin('p.pointOfSale''po')
  946.                 ->where('p.pointOfSale = :pointOfSale')
  947.                 ->setParameter('pointOfSale'$pointOfSale);
  948.         }
  949.         if ($codeCluster) {
  950.             $qb
  951.                 ->andWhere('c.codeCluster = :codeCluster')
  952.                 ->setParameter('codeCluster'$codeCluster);
  953.         }
  954.         if ($codeInsee) {
  955.             $qb->andWhere('p.codeInsee = :codeInsee')
  956.                 ->setParameter('codeInsee'$codeInsee);
  957.         }
  958.         switch ($optionSelect) {
  959.             case 'V':
  960.                 $qb->andWhere('MONTH(p.dateVenteValidB) = :mois')
  961.                     ->andWhere('YEAR(p.dateVenteValidB) = :annee');
  962.                 break;
  963.             case 'R':
  964.                 $qb->andWhere('MONTH(p.dateRacc) = :mois')
  965.                     ->andWhere('YEAR(p.dateRacc) = :annee');
  966.                 break;
  967.             case 'B':
  968.                 $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  969.                     ->andWhere('YEAR(p.dateCmdA) = :annee');
  970.                 break;
  971.         }
  972.         if ($departement) {
  973.             // Si le département est fourni, on cherche les clusters dont le code commence par "departement-"
  974.             $qb->andWhere('c.codeCluster LIKE :prefix')
  975.                 ->setParameter('prefix'$departement '-%');
  976.         }
  977.         $qb->setParameter('mois'$mois)
  978.             ->setParameter('annee'$annee);
  979.         return $qb;
  980.     }
  981.     private function createBaseQueryBuilder2($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement): QueryBuilder
  982.     {
  983.         $qb $this->createQueryBuilder('p')
  984.             ->leftJoin('p.cluster''c')
  985.             ->where('p.pointOfSale = :pointOfSale')
  986.             ->setParameter('pointOfSale'$pointOfSale);
  987.         if ($codeCluster) {
  988.             $qb
  989.                 ->andWhere('c.codeCluster = :codeCluster')
  990.                 ->setParameter('codeCluster'$codeCluster);
  991.         }
  992.         if ($departement) {
  993.             // Si le département est fourni, on cherche les clusters dont le code commence par "departement-"
  994.             $qb->andWhere('c.codeCluster LIKE :prefix')
  995.                 ->setParameter('prefix'$departement '-%');
  996.         }
  997.         if ($codeInsee) {
  998.             $qb->andWhere('p.codeInsee = :codeInsee')
  999.                 ->setParameter('codeInsee'$codeInsee);
  1000.         }
  1001.         $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  1002.             ->andWhere('YEAR(p.dateCmdA) = :annee');
  1003.         $qb->setParameter('mois'$mois)
  1004.             ->setParameter('annee'$annee);
  1005.         return $qb;
  1006.     }
  1007.     public function getProductionsAnalyticsVentes($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  1008.     {
  1009.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement)
  1010.             ->select('COUNT(p.id) as total_ventes');
  1011.         if ($category != null) {
  1012.             $category array_filter(
  1013.                 array_map('intval'explode(','$category)),
  1014.                 fn($id) => $id 0
  1015.             );
  1016.             $qb->andWhere('p.category IN (:categories)')
  1017.                 ->setParameter('categories'$category);
  1018.         }
  1019.         if ($organisationId) {
  1020.             $qb->andWhere('p.organisation = :organisationId')
  1021.                 ->setParameter('organisationId'$organisationId);
  1022.         }
  1023.         if ($perid) {
  1024.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1025.                 ->setParameter('perid'$perid);
  1026.         }
  1027.         if ($sellerId) {
  1028.             $qb->andWhere('p.seller = :sellerId')
  1029.                 ->setParameter('sellerId'$sellerId);
  1030.         }
  1031.         /*Gestion de cas des produits fixe qui existent dans
  1032.          le fichier mobile on doit le supprimer depuis mobile et on le prend dans fixe */
  1033.         if ($etat == "Raccorde") {
  1034.             $qb->andWhere('p.dateRacc IS NOT NULL')
  1035.                 ->andWhere(
  1036.                     $qb->expr()->orX(
  1037.                         $qb->expr()->in('p.category', [123]),
  1038.                         $qb->expr()->in('p.product', [150262264266334335341344345379441442443444])
  1039.                     )
  1040.                 );
  1041.         }
  1042.         #MOBILE
  1043.         else if ($etat == "Raccorde_mobile") {
  1044.             $qb->andWhere('p.category IN (31,32,33,48)')
  1045.                 // ->andWhere('p.etat = 1')
  1046.                 ->andWhere('p.dateRacc IS NOT NULL ')
  1047.                 ->andWhere('p.product NOT IN (150,262,264,266,334,335,341,344,345,379,441,442,443,444) ')
  1048.             ;
  1049.         }
  1050.         if (is_array($childs) && count($childs) > 0) {
  1051.             $qb->andWhere('p.seller IN (:childs)')
  1052.                 ->leftJoin('p.seller''s')
  1053.                 ->setParameter('childs'$childs);
  1054.         }
  1055.         $qb->orderBy('total_ventes''DESC');
  1056.         return $qb->getQuery()->getArrayResult();
  1057.     }
  1058.     public function getProductionsAnalyticsVentesForChurn($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  1059.     {
  1060.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement)
  1061.             ->select('COUNT(p.id) as total_ventes');
  1062.         if ($category != null) {
  1063.             $category array_filter(
  1064.                 array_map('intval'explode(','$category)),
  1065.                 fn($id) => $id 0
  1066.             );
  1067.             $qb->andWhere('p.category IN (:categories)')
  1068.                 ->setParameter('categories'$category);
  1069.         }
  1070.         if ($organisationId) {
  1071.             $qb->andWhere('p.organisation = :organisationId')
  1072.                 ->setParameter('organisationId'$organisationId);
  1073.         }
  1074.         if ($perid) {
  1075.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1076.                 ->setParameter('perid'$perid);
  1077.         }
  1078.         if ($sellerId) {
  1079.             $qb->andWhere('p.seller = :sellerId')
  1080.                 ->setParameter('sellerId'$sellerId);
  1081.         }
  1082.         /*Gestion de cas des produits fixe qui existent dans
  1083.          le fichier mobile on doit le supprimer depuis mobile et on le prend dans fixe */
  1084.         if ($etat == "Raccorde") {
  1085.             $qb->andWhere('p.dateRacc IS NOT NULL')
  1086.                 ->andWhere(
  1087.                     $qb->expr()->orX(
  1088.                         $qb->expr()->in('p.category', [13]),
  1089.                         $qb->expr()->andX(
  1090.                             $qb->expr()->in('p.category', [31]),
  1091.                             $qb->expr()->in('p.product', [150262264266334335341344345379441442443444])
  1092.                         )
  1093.                     )
  1094.                 );
  1095.         }
  1096.         #MOBILE
  1097.         else if ($etat == "Raccorde_mobile") {
  1098.             $qb->andWhere('p.category IN (31,32,33,48)')
  1099.                 // ->andWhere('p.etat = 1')
  1100.                 ->andWhere('p.dateRacc IS NOT NULL ')
  1101.                 ->andWhere('p.product NOT IN (150,262,264,266,334,335,341,344,345,379,441,442,443,444) ')
  1102.             ;
  1103.         }
  1104.         if (is_array($childs) && count($childs) > 0) {
  1105.             $qb->andWhere('p.seller IN (:childs)')
  1106.                 ->leftJoin('p.seller''s')
  1107.                 ->setParameter('childs'$childs);
  1108.         }
  1109.         $qb->orderBy('total_ventes''DESC');
  1110.         return $qb->getQuery()->getArrayResult();
  1111.     }
  1112.     public function getProductionsAnalyticsVentesForChurn5G($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  1113.     {
  1114.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement)
  1115.             ->select('COUNT(p.id) as total_ventes');
  1116.         if ($category != null) {
  1117.             $category array_filter(
  1118.                 array_map('intval'explode(','$category)),
  1119.                 fn($id) => $id 0
  1120.             );
  1121.             $qb->andWhere('p.category IN (:categories)')
  1122.                 ->setParameter('categories'$category);
  1123.         }
  1124.         if ($organisationId) {
  1125.             $qb->andWhere('p.organisation = :organisationId')
  1126.                 ->setParameter('organisationId'$organisationId);
  1127.         }
  1128.         if ($perid) {
  1129.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1130.                 ->setParameter('perid'$perid);
  1131.         }
  1132.         if ($sellerId) {
  1133.             $qb->andWhere('p.seller = :sellerId')
  1134.                 ->setParameter('sellerId'$sellerId);
  1135.         }
  1136.         /*Gestion de cas des produits fixe qui existent dans
  1137.          le fichier mobile on doit le supprimer depuis mobile et on le prend dans fixe */
  1138.         if ($etat == "Raccorde") {
  1139.             $qb->andWhere('p.dateRacc IS NOT NULL')
  1140.                 ->andWhere(
  1141.                     $qb->expr()->in('p.product', [150262264266334335341344345379441442443444])
  1142.                 );
  1143.         }
  1144.         #MOBILE
  1145.         else if ($etat == "Raccorde_mobile") {
  1146.             $qb->andWhere('p.category IN (31,32,33,48)')
  1147.                 // ->andWhere('p.etat = 1')
  1148.                 ->andWhere('p.dateRacc IS NOT NULL ')
  1149.                 ->andWhere('p.product NOT IN (150,262,264,266,334,335,341,344,345,379,441,442,443,444) ')
  1150.             ;
  1151.         }
  1152.         if (is_array($childs) && count($childs) > 0) {
  1153.             $qb->andWhere('p.seller IN (:childs)')
  1154.                 ->leftJoin('p.seller''s')
  1155.                 ->setParameter('childs'$childs);
  1156.         }
  1157.         $qb->orderBy('total_ventes''DESC');
  1158.         return $qb->getQuery()->getArrayResult();
  1159.     }
  1160.     public function getProductionsAnalyticsVentesBox5G($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  1161.     {
  1162.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee,  $mois$annee"B"$departement)
  1163.             ->select('COUNT(p.id) as total_ventes');
  1164.         if ($organisationId) {
  1165.             $qb->andWhere('p.organisation = :organisationId')
  1166.                 ->setParameter('organisationId'$organisationId);
  1167.         }
  1168.         if ($perid) {
  1169.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1170.                 ->setParameter('perid'$perid);
  1171.         }
  1172.         if ($sellerId) {
  1173.             $qb->andWhere('p.seller = :sellerId')
  1174.                 ->setParameter('sellerId'$sellerId);
  1175.         }
  1176.         $qb->andWhere('p.product IN (150,262,264,266,334,335,341,344,345,379,441,442,443,444)');
  1177.         if (is_array($childs) && count($childs) > 0) {
  1178.             $qb->andWhere('p.seller IN (:childs)')
  1179.                 ->leftJoin('p.seller''s')
  1180.                 ->setParameter('childs'$childs);
  1181.         }
  1182.         $qb->orderBy('total_ventes''DESC');
  1183.         return $qb->getQuery()->getArrayResult();
  1184.     }
  1185.     public function getProductionsAnalyticsVentesVlaConquete($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect,  $category$childs$organisationId$perid$sellerId$departement): array
  1186.     {
  1187.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$departement)
  1188.             ->select('COUNT(p.id) as total_ventes');
  1189.         if ($category) {
  1190.             $category array_filter(
  1191.                 array_map('intval'explode(','$category)),
  1192.                 fn($id) => $id 0
  1193.             );
  1194.             $qb->andWhere('p.category IN (:categories)')
  1195.                 ->setParameter('categories'$category);
  1196.         }
  1197.         if ($organisationId) {
  1198.             $qb->andWhere('p.organisation = :organisationId')
  1199.                 ->setParameter('organisationId'$organisationId);
  1200.         }
  1201.         if ($perid) {
  1202.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1203.                 ->setParameter('perid'$perid);
  1204.         }
  1205.         if ($sellerId) {
  1206.             $qb->andWhere('p.seller = :sellerId')
  1207.                 ->setParameter('sellerId'$sellerId);
  1208.         }
  1209.         if (is_array($childs) && count($childs) > 0) {
  1210.             $qb->andWhere('p.seller IN (:childs)')
  1211.                 ->leftJoin('p.seller''s')
  1212.                 ->setParameter('childs'$childs);
  1213.         }
  1214.         $qb->orderBy('total_ventes''DESC');
  1215.         return $qb->getQuery()->getArrayResult();
  1216.     }
  1217.     public function getProductionsAnalyticsVendeurs($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etatKO$category$childs$organisationId$perid$sellerId$departement): array
  1218.     {
  1219.         $qb $this->createBaseQueryBuilderTest($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$organisationId$departement)
  1220.             ->select('COUNT(DISTINCT s.id) as nombre_vendeurs')
  1221.             ->leftJoin('p.seller''s');
  1222.         if ($category) {
  1223.             $category array_filter(
  1224.                 array_map('intval'explode(','$category)),
  1225.                 fn($id) => $id 0
  1226.             );
  1227.             $qb->andWhere('p.category IN (:categories)')
  1228.                 ->setParameter('categories'$category);
  1229.         }
  1230.         if ($perid) {
  1231.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1232.                 ->setParameter('perid'$perid);
  1233.         }
  1234.         if ($sellerId) {
  1235.             $qb->andWhere('p.seller = :sellerId')
  1236.                 ->setParameter('sellerId'$sellerId);
  1237.         }
  1238.         if (is_array($childs) && count($childs) > 0) {
  1239.             $qb->andWhere('p.seller IN (:childs)')
  1240.                 ->setParameter('childs'$childs);
  1241.         }
  1242.         return $qb->getQuery()->getArrayResult();
  1243.     }
  1244.     public function getProductionsAnalyticsETP($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$etatKO$category$childs$organisationId$perid$sellerId$departement): array
  1245.     {
  1246.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement)
  1247.             ->select('COUNT(p.id) / NULLIF(COUNT(DISTINCT s.id), 0) AS etp')
  1248.             ->leftJoin('p.seller''s');
  1249.         if ($category) {
  1250.             $category array_filter(
  1251.                 array_map('intval'explode(','$category)),
  1252.                 fn($id) => $id 0
  1253.             );
  1254.             $qb->andWhere('p.category IN (:categories)')
  1255.                 ->setParameter('categories'$category);
  1256.         }
  1257.         if (is_array($childs) && count($childs) > 0) {
  1258.             $qb->andWhere('p.seller IN (:childs)')
  1259.                 ->setParameter('childs'$childs);
  1260.         }
  1261.         if ($organisationId) {
  1262.             $qb->andWhere('p.organisation = :organisationId')
  1263.                 ->setParameter('organisationId'$organisationId);
  1264.         }
  1265.         if ($perid) {
  1266.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1267.                 ->setParameter('perid'$perid);
  1268.         }
  1269.         if ($sellerId) {
  1270.             $qb->andWhere('p.seller = :sellerId')
  1271.                 ->setParameter('sellerId'$sellerId);
  1272.         }
  1273.         return $qb->getQuery()->getArrayResult();
  1274.     }
  1275.     public function getProductionsAnalyticsKO($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etatKO$category$childs$organisationId$perid$sellerId$departement): array
  1276.     {
  1277.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$departement)
  1278.             ->select('COUNT(p.id) AS total_ventes_ko')
  1279.             ->andWhere('p.etat = :etat')
  1280.             ->setParameter('etat'$etatKO);
  1281.         if ($category) {
  1282.             $category array_filter(
  1283.                 array_map('intval'explode(','$category)),
  1284.                 fn($id) => $id 0
  1285.             );
  1286.             $qb->andWhere('p.category IN (:categories)')
  1287.                 ->setParameter('categories'$category);
  1288.         }
  1289.         if (is_array($childs) && count($childs) > 0) {
  1290.             $qb->andWhere('p.seller IN (:childs)')
  1291.                 ->setParameter('childs'$childs);
  1292.         }
  1293.         if ($organisationId) {
  1294.             $qb->andWhere('p.organisation = :organisationId')
  1295.                 ->setParameter('organisationId'$organisationId);
  1296.         }
  1297.         if ($perid) {
  1298.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1299.                 ->setParameter('perid'$perid);
  1300.         }
  1301.         if ($sellerId) {
  1302.             $qb->andWhere('p.seller = :sellerId')
  1303.                 ->setParameter('sellerId'$sellerId);
  1304.         }
  1305.         return $qb->getQuery()->getArrayResult();
  1306.     }
  1307.     public function getProductionsAnalyticsObjectifs(
  1308.         $pointOfSale,
  1309.         $codeCluster,
  1310.         $mois,
  1311.         $annee,
  1312.         $optionSelect,
  1313.         $etatKO,
  1314.         $category,
  1315.         $childs,
  1316.         $organisationId,
  1317.         $departement
  1318.     ): array {
  1319.         $qb $this->getEntityManager()->createQueryBuilder()
  1320.             ->select('SUM(o.vv) AS total_objectif')
  1321.             ->from('App\Entity\NmdObjectifClusters''o')
  1322.             ->where('o.cpv = :cpv')
  1323.             ->setParameter('cpv'$pointOfSale->getCode())
  1324.             ->andWhere('o.mois = :mois')
  1325.             ->andWhere('o.year = :annee')
  1326.             ->setParameter('mois'$mois)
  1327.             ->setParameter('annee'$annee);
  1328.         if ($codeCluster) {
  1329.             $qb
  1330.                 ->andWhere('o.codeCluster = :codeCluster')
  1331.                 ->setParameter('codeCluster'$codeCluster);
  1332.         }
  1333.         if ($departement) {
  1334.             // Si le département est fourni, on cherche les clusters dont le code commence par "departement-"
  1335.             $qb->andWhere('o.codeCluster LIKE :prefix')
  1336.                 ->setParameter('prefix'$departement '-%');
  1337.         }
  1338.         if (is_array($childs) && count($childs) > 0) {
  1339.             $qb $this->getEntityManager()->createQueryBuilder()
  1340.                 ->select('SUM(o.objectivesVv) AS total_objectif')
  1341.                 ->from('App\Entity\ObjectivesManagement''o')
  1342.                 ->andWhere('MONTH(o.monthAt) = :mois')
  1343.                 ->andWhere('YEAR(o.monthAt) = :annee')
  1344.                 ->setParameter('mois'$mois)
  1345.                 ->setParameter('annee'$annee);
  1346.             $qb->andWhere('o.user IN (:childs)')
  1347.                 ->leftJoin('o.user''s')
  1348.                 ->setParameter('childs'$childs);
  1349.         }
  1350.         return $qb->getQuery()->getArrayResult();
  1351.     }
  1352.     public function getProductionsAnalyticsProjection($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etatKO$category$childs$organisationId$perid$sellerId$departement): array
  1353.     {
  1354.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$departement);
  1355.         $dateField '';
  1356.         $dateFieldSql '';
  1357.         switch ($optionSelect) {
  1358.             case 'V':
  1359.                 $dateField 'p.dateVenteValidB';
  1360.                 $dateFieldSql 'p.date_vente_valid_b';
  1361.                 break;
  1362.             case 'R':
  1363.                 $dateField 'p.dateRacc';
  1364.                 $dateFieldSql 'p.date_racc';
  1365.                 break;
  1366.             case 'B':
  1367.                 $dateField 'p.dateCmdA';
  1368.                 $dateFieldSql 'p.date_cmd_a';
  1369.                 break;
  1370.         }
  1371.         // 3. Calcul du nombre de ventes réalisées jusqu'à aujourd'hui
  1372.         $qb->select('COUNT(p.id)')
  1373.             ->andWhere("$dateField <= :currentDate")
  1374.             ->setParameter('currentDate', new \DateTime());
  1375.         if ($category) {
  1376.             $category array_filter(
  1377.                 array_map('intval'explode(','$category)),
  1378.                 fn($id) => $id 0
  1379.             );
  1380.             $qb->andWhere('p.category IN (:categories)')
  1381.                 ->setParameter('categories'$category);
  1382.         }
  1383.         if (is_array($childs) && count($childs) > 0) {
  1384.             $qb->andWhere('p.seller IN (:childs)')
  1385.                 ->leftJoin('p.seller''s')
  1386.                 ->setParameter('childs'$childs);
  1387.         }
  1388.         if ($organisationId) {
  1389.             $qb->andWhere('p.organisation = :organisationId')
  1390.                 ->setParameter('organisationId'$organisationId);
  1391.         }
  1392.         if ($perid) {
  1393.             $qb->andWhere('p.loginVendeurInit = :perid ')
  1394.                 ->setParameter('perid'$perid);
  1395.         }
  1396.         if ($sellerId) {
  1397.             $qb->andWhere('p.seller = :sellerId')
  1398.                 ->setParameter('sellerId'$sellerId);
  1399.         }
  1400.         $ventesRealisees $qb->getQuery()
  1401.             ->getSingleScalarResult();
  1402.         // Récupérer la connexion DBAL
  1403.         $conn $this->getEntityManager()->getConnection();
  1404.         // 4. Calcul du nombre de jours de travail effectués jusqu'à aujourd'hui
  1405.         $sql "
  1406.         SELECT COUNT(DISTINCT DAY($dateFieldSql))
  1407.         FROM production p
  1408.         WHERE p.point_of_sale_id = :pointOfSale
  1409.         AND MONTH($dateFieldSql) = :mois
  1410.         AND YEAR($dateFieldSql) = :annee
  1411.         AND DAYOFWEEK($dateFieldSql) >= 2 AND DAYOFWEEK($dateFieldSql) <= 6  -- Jours de travail (lundi à vendredi)
  1412.         AND $dateFieldSql <= CURDATE()  -- Jusqu'à aujourd'hui
  1413.     ";
  1414.         $parameters = [];
  1415.         if ($category) {
  1416.             $categories $category;
  1417.             if (!empty($categories)) {
  1418.                 $placeholders = [];
  1419.                 foreach ($categories as $index => $catId) {
  1420.                     $placeholder ":cat$index";
  1421.                     $placeholders[] = $placeholder;
  1422.                     $parameters[$placeholder] = $catId;
  1423.                 }
  1424.                 $sql .= " AND p.category_id IN (" implode(','$placeholders) . ")";
  1425.             }
  1426.         }
  1427.         $stmt $conn->prepare($sql);
  1428.         // Bind des paramètres
  1429.         foreach ($parameters as $placeholder => $value) {
  1430.             $stmt->bindValue($placeholder$value, \PDO::PARAM_INT);
  1431.         }
  1432.         $stmt->bindValue('pointOfSale'$pointOfSale->getId());
  1433.         $stmt->bindValue('mois'$mois);
  1434.         $stmt->bindValue('annee'$annee);
  1435.         $joursTravailEffectues $stmt->executeQuery()->fetchOne();
  1436.         // 5. Calcul de la moyenne des ventes par jour
  1437.         $ventesParJour $ventesRealisees / (($joursTravailEffectues 0) ? $joursTravailEffectues 1);
  1438.         // 6. Calcul du nombre de jours restants dans le mois
  1439.         $dateFinMois = new \DateTime();
  1440.         $dateFinMois->setDate($annee$mois1)->modify('last day of this month');  // Dernier jour du mois
  1441.         $currentDate = new \DateTime();  // Date actuelle
  1442.         $joursRestants 0;
  1443.         // Si la date de fin du mois est après la date actuelle, on calcule les jours restants
  1444.         if ($dateFinMois $currentDate) {
  1445.             $interval $currentDate->diff($dateFinMois);  // Différence entre la date actuelle et la fin du mois
  1446.             for ($i 0$i <= $interval->days$i++) {
  1447.                 $jourTemp = (clone $currentDate)->modify("+$i day");
  1448.                 // Vérifier si c'est un jour de semaine (lundi à vendredi)
  1449.                 if ($jourTemp->format('N') < 6) {
  1450.                     $joursRestants++;
  1451.                 }
  1452.             }
  1453.         }
  1454.         // ✅ Ajouter 1 jour si aujourd’hui est le dernier jour du mois
  1455.         if ($currentDate->format('t') == $currentDate->format('d')) {
  1456.             $joursRestants++;
  1457.         }
  1458.         // 7. Calcul de la projection des ventes pour le reste du mois
  1459.         #joursRestants +1 car on reçoit les ventes d'aujourd'hui le lendemain
  1460.         $projectionVentes = ($ventesParJour * ($joursRestants)) + $ventesRealisees;
  1461.         return [
  1462.             'ventes_realisees' => $ventesRealisees,
  1463.             'ventes_par_jour' => $ventesParJour,
  1464.             'jours_travail_effectues' => $joursTravailEffectues,
  1465.             'jours_restants' => $joursRestants,
  1466.             'projection_ventes' => $projectionVentes,
  1467.         ];
  1468.     }
  1469.     public function getSalesStats($pointOfSale$cluster$annee$optionSelect$groupBy$codeInsee$byCategory$childs$organisationId)
  1470.     {
  1471.         $dateField '';
  1472.         switch ($optionSelect) {
  1473.             case 'V':
  1474.                 $dateField 'p.date_vente_valid_b';
  1475.                 break;
  1476.             case 'R':
  1477.                 $dateField 'p.date_racc';
  1478.                 break;
  1479.             case 'B':
  1480.                 $dateField 'p.date_cmd_a';
  1481.                 break;
  1482.         }
  1483.         $groupByField '';
  1484.         $selectField '';
  1485.         $range = [];
  1486.         switch ($groupBy) {
  1487.             case 'month':
  1488.                 $groupByField 'MONTH(' $dateField ')';
  1489.                 $selectField 'MONTH(' $dateField ') AS cle';
  1490.                 break;
  1491.             case 'day':
  1492.                 $groupByField 'DAY(' $dateField ')';
  1493.                 $selectField 'DAY(' $dateField ') AS cle';
  1494.                 $range range(131);
  1495.                 break;
  1496.             case 'week':
  1497.                 $groupByField 'WEEK(' $dateField ', 1)';
  1498.                 $selectField 'WEEK(' $dateField ', 1) AS cle';
  1499.                 break;
  1500.             case 'hour':
  1501.                 $groupByField 'HOUR(' $dateField ')';
  1502.                 $selectField 'HOUR(' $dateField ') AS cle';
  1503.                 $range range(023);
  1504.                 break;
  1505.             case 'dayOfWeek':
  1506.                 $groupByField 'DAYOFWEEK(' $dateField ')';
  1507.                 $selectField 'DAYOFWEEK(' $dateField ') AS cle';
  1508.                 $range range(17);
  1509.                 break;
  1510.         }
  1511.         // Base SQL
  1512.         $sql "SELECT $selectField, COUNT(p.id) AS total_ventes";
  1513.         if ($byCategory == 1) {
  1514.             $sql .= ", c.name AS category_name";
  1515.         }
  1516.         $sql .= " FROM production p";
  1517.         if ($byCategory == 1) {
  1518.             $sql .= " LEFT JOIN category_product c ON c.id = p.category_id";
  1519.         }
  1520.         $conditions = [];
  1521.         $params = [];
  1522.         if ($pointOfSale !== null) {
  1523.             $conditions[] = "p.point_of_sale_id = :pointOfSale";
  1524.             $params['pointOfSale'] = $pointOfSale->getId();
  1525.         }
  1526.         if ($annee) {
  1527.             $conditions[] = "YEAR($dateField) = :annee";
  1528.             $params['annee'] = $annee;
  1529.         }
  1530.         if ($cluster) {
  1531.             $conditions[] = "p.cluster_id = :clusterId";
  1532.             $params['clusterId'] = $cluster->getId();
  1533.         }
  1534.         if ($codeInsee) {
  1535.             $conditions[] = "p.code_insee = :codeInsee";
  1536.             $params['codeInsee'] = $codeInsee;
  1537.         }
  1538.         if ($organisationId) {
  1539.             $conditions[] = 'p.organisation_id = :organisationId';
  1540.             $params['organisationId'] = $organisationId;
  1541.         }
  1542.         if (is_array($childs) && count($childs) > 0) {
  1543.             $placeholders = [];
  1544.             foreach ($childs as $i => $child) {
  1545.                 $placeholder ':child_' $i;
  1546.                 $placeholders[] = $placeholder;
  1547.                 $params['child_' $i] = $child;
  1548.             }
  1549.             $conditions[] = ' p.seller_id IN (' implode(', '$placeholders) . ')';
  1550.         }
  1551.         if (!empty($conditions)) {
  1552.             $sql .= " WHERE " implode(' AND '$conditions);
  1553.         }
  1554.         // Group by
  1555.         $sql .= " GROUP BY $groupByField";
  1556.         if ($byCategory == 1) {
  1557.             $sql .= ", p.category_id, c.name";
  1558.         }
  1559.         $sql .= " ORDER BY $groupByField ASC";
  1560.         $conn $this->getEntityManager()->getConnection();
  1561.         $stmt $conn->prepare($sql);
  1562.         $result $stmt->executeQuery($params)->fetchAllAssociative();
  1563.         // Format des résultats
  1564.         $formattedResult = [];
  1565.         if ($byCategory == 1) {
  1566.             foreach ($result as $row) {
  1567.                 $cat $row['category_name'] ?? 'Inconnu';
  1568.                 $cle $row['cle'];
  1569.                 if (!isset($formattedResult[$cat])) {
  1570.                     $formattedResult[$cat] = ($groupBy !== 'month') ? array_fill_keys($range0) : [];
  1571.                 }
  1572.                 $formattedResult[$cat][$cle] = (int) $row['total_ventes'];
  1573.             }
  1574.         } else {
  1575.             if ($groupBy !== 'month') {
  1576.                 foreach ($range as $cle) {
  1577.                     $formattedResult[$cle] = 0;
  1578.                 }
  1579.             }
  1580.             foreach ($result as $row) {
  1581.                 $formattedResult[$row['cle']] = (int) $row['total_ventes'];
  1582.             }
  1583.         }
  1584.         return (object) $formattedResult;
  1585.     }
  1586.     public function getMissingInseeCodes($annee$mois$idCluster$codeCluster$pointOfSale$optionSelect)
  1587.     {
  1588.         $entityManager $this->getEntityManager();
  1589.         $subQuery $entityManager->createQueryBuilder()
  1590.             ->select('DISTINCT(p.codeInsee)')
  1591.             ->from('App\Entity\Production''p')
  1592.             ->leftJoin('App\Entity\Cluster''c''WITH''c.id = p.cluster')
  1593.             ->where('p.pointOfSale = :pointOfSale')
  1594.             ->andWhere('c.id = :idCluster')
  1595.             ->setParameter('idCluster'$idCluster)
  1596.             ->setParameter('pointOfSale'$pointOfSale);
  1597.         switch ($optionSelect) {
  1598.             case 'V':
  1599.                 $subQuery->andWhere('MONTH(p.dateVenteValidB) = :mois')
  1600.                     ->andWhere('YEAR(p.dateVenteValidB) = :annee');
  1601.                 break;
  1602.             case 'R':
  1603.                 $subQuery->andWhere('MONTH(p.dateRacc) = :mois')
  1604.                     ->andWhere('YEAR(p.dateRacc) = :annee');
  1605.                 break;
  1606.             case 'B':
  1607.                 $subQuery->andWhere('MONTH(p.dateCmdA) = :mois')
  1608.                     ->andWhere('YEAR(p.dateCmdA) = :annee');
  1609.                 break;
  1610.         }
  1611.         $subQuery->setParameter('mois'$mois)
  1612.             ->setParameter('annee'$annee);
  1613.         $qb $entityManager->createQueryBuilder();
  1614.         $qb
  1615.             ->select('a.CODE_CLUSTER, a.COD_INSEE,a.VILL')
  1616.             ->from('App\Entity\NmdAdresse''a')
  1617.             ->where('a.CODE_CLUSTER = :codeCluster')
  1618.             ->andWhere($qb->expr()->notIn('a.COD_INSEE'$subQuery->getDQL()))
  1619.             ->groupBy('a.COD_INSEE')
  1620.             ->setParameter('codeCluster'$codeCluster)
  1621.             ->setParameter('idCluster'$idCluster)
  1622.             ->setParameter('pointOfSale'$pointOfSale)
  1623.             ->setParameter('mois'$mois)
  1624.             ->setParameter('annee'$annee);
  1625.         return $qb->getQuery()->getResult();
  1626.     }
  1627.     public function getZeroSalesDays($pointOfSale$cluster$codeInsee$optionSelect)
  1628.     {
  1629.         $dateField '';
  1630.         switch ($optionSelect) {
  1631.             case 'V':
  1632.                 $dateField 'p.date_vente_valid_b';
  1633.                 break;
  1634.             case 'R':
  1635.                 $dateField 'p.date_racc';
  1636.                 break;
  1637.             case 'B':
  1638.                 $dateField 'p.date_cmd_a';
  1639.                 break;
  1640.         }
  1641.         $sql "SELECT 
  1642.                     DATEDIFF(CURRENT_DATE(), MAX($dateField)) AS nombre_jours,
  1643.                     p.cluster_id AS clusterId,
  1644.                     p.code_insee AS codeInsee,
  1645.                     p.ville AS ville
  1646.                 FROM production p
  1647.                 LEFT JOIN category_product cp ON p.category_id = cp.id
  1648.                 WHERE p.point_of_sale_id = :idPointOfSale
  1649.                 AND p.cluster_id = :idCluster";
  1650.         if (!empty($codeInsee)) {
  1651.             $sql .= " AND p.code_insee = :codeInsee";
  1652.         }
  1653.         $sql .= " GROUP BY p.cluster_id, p.code_insee, p.ville
  1654.                   ORDER BY nombre_jours DESC";
  1655.         $conn $this->getEntityManager()->getConnection();
  1656.         $stmt $conn->prepare($sql);
  1657.         $params = [
  1658.             'idPointOfSale' => $pointOfSale->getId(),
  1659.             'idCluster' => $cluster->getId()
  1660.         ];
  1661.         if (!empty($codeInsee)) {
  1662.             $params['codeInsee'] = $codeInsee;
  1663.         }
  1664.         $resultSet $stmt->executeQuery($params);
  1665.         return $resultSet->fetchAllAssociative();
  1666.     }
  1667.     public function findBySellerAndDate($user$year$month$optionSelect$cluster$codeInsee)
  1668.     {
  1669.         $qb $this->createQueryBuilder('p')
  1670.             ->andWhere('p.seller = :user')
  1671.             ->setParameter('user'$user);
  1672.         if ($cluster) {
  1673.             $qb->andWhere('p.cluster = :cluster')
  1674.                 ->setParameter('cluster'$cluster);
  1675.         }
  1676.         if ($codeInsee) {
  1677.             $qb->andWhere('p.codeInsee = :codeInsee')
  1678.                 ->setParameter('codeInsee'$codeInsee);
  1679.         }
  1680.         switch ($optionSelect) {
  1681.             case 'V':
  1682.                 $qb->andWhere('MONTH(p.dateVenteValidB) = :mois')
  1683.                     ->andWhere('YEAR(p.dateVenteValidB) = :annee');
  1684.                 break;
  1685.             case 'R':
  1686.                 $qb->andWhere('MONTH(p.dateRacc) = :mois')
  1687.                     ->andWhere('YEAR(p.dateRacc) = :annee');
  1688.                 break;
  1689.             case 'B':
  1690.                 $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  1691.                     ->andWhere('YEAR(p.dateCmdA) = :annee');
  1692.                 break;
  1693.         }
  1694.         $qb
  1695.             ->setParameter('mois'$month)
  1696.             ->setParameter('annee'$year);
  1697.         return $qb->getQuery()
  1698.             ->getResult();
  1699.     }
  1700.     public function findBySellerAndDateCategoryStatus($user$year$month$optionSelect$cluster$codeInsee)
  1701.     {
  1702.         $qb $this->createQueryBuilder('p')
  1703.             ->leftJoin('p.category''c')
  1704.             ->leftJoin('p.etat''e')
  1705.             ->andWhere('p.seller = :user')
  1706.             ->setParameter('user'$user);
  1707.         if ($cluster) {
  1708.             $qb->andWhere('p.cluster = :cluster')
  1709.                 ->setParameter('cluster'$cluster);
  1710.         }
  1711.         if ($codeInsee) {
  1712.             $qb->andWhere('p.codeInsee = :codeInsee')
  1713.                 ->setParameter('codeInsee'$codeInsee);
  1714.         }
  1715.         switch ($optionSelect) {
  1716.             case 'V':
  1717.                 $qb->andWhere('MONTH(p.dateVenteValidB) = :mois')
  1718.                     ->andWhere('YEAR(p.dateVenteValidB) = :annee');
  1719.                 break;
  1720.             case 'R':
  1721.                 $qb->andWhere('MONTH(p.dateRacc) = :mois')
  1722.                     ->andWhere('YEAR(p.dateRacc) = :annee');
  1723.                 break;
  1724.             case 'B':
  1725.                 $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  1726.                     ->andWhere('YEAR(p.dateCmdA) = :annee');
  1727.                 break;
  1728.         }
  1729.         $qb
  1730.             ->setParameter('mois'$month)
  1731.             ->setParameter('annee'$year);
  1732.         $qb->groupBy('c.id''e.id');
  1733.         return $qb->getQuery()
  1734.             ->getResult();
  1735.     }
  1736.     public function getSalesByDay($pointOfSale$cluster$mois$annee$codeInsee,  $optionSelect$roleUser$category$childs)
  1737.     {
  1738.         $dateField '';
  1739.         switch ($optionSelect) {
  1740.             case 'V':
  1741.                 $dateField 'p.date_vente_valid_b';
  1742.                 break;
  1743.             case 'R':
  1744.                 $dateField 'p.date_racc';
  1745.                 break;
  1746.             case 'B':
  1747.                 $dateField 'p.date_cmd_a';
  1748.                 break;
  1749.         }
  1750.         $groupByField 'DAY(' $dateField ')';
  1751.         $selectField 'DAY(' $dateField ') AS cle';
  1752.         $range $this->getDaysInMonth($mois$annee);
  1753.         $sql "
  1754.         SELECT 
  1755.             $selectField,
  1756.             COUNT(p.id) AS total_ventes
  1757.         FROM production p
  1758.     ";
  1759.         $params = [];
  1760.         $whereConditions = [];
  1761.         if ($roleUser != "ROLE_ORGANISATION") {
  1762.             $whereConditions[] = "p.point_of_sale_id = :pointOfSale";
  1763.             $params['pointOfSale'] = $pointOfSale->getId();
  1764.         }
  1765.         if ($annee) {
  1766.             $whereConditions[] = "YEAR($dateField) = :annee";
  1767.             $params['annee'] = $annee;
  1768.         }
  1769.         if ($mois) {
  1770.             $whereConditions[] = "MONTH($dateField) = :mois";
  1771.             $params['mois'] = $mois;
  1772.         }
  1773.         if ($cluster) {
  1774.             $whereConditions[] = "p.cluster_id = :clusterId";
  1775.             $params['clusterId'] = $cluster->getId();
  1776.         }
  1777.         if ($category) {
  1778.             // Si $category est une chaîne "1,2,3", transforme en tableau
  1779.             if (is_string($category)) {
  1780.                 $category array_filter(array_map('intval'explode(','$category)));
  1781.             }
  1782.             if (count($category) === 1) {
  1783.                 $whereConditions[] = "p.category_id = :category0";
  1784.                 $params['category0'] = $category[0];
  1785.             } elseif (count($category) > 1) {
  1786.                 $placeholders = [];
  1787.                 foreach ($category as $index => $catId) {
  1788.                     $placeholders[] = ":category$index";
  1789.                     $params["category$index"] = $catId;
  1790.                 }
  1791.                 $whereConditions[] = "p.category_id IN (" implode(','$placeholders) . ")";
  1792.             }
  1793.         }
  1794.         if ($codeInsee) {
  1795.             $whereConditions[] = "p.code_insee = :codeInsee";
  1796.             $params['codeInsee'] = $codeInsee;
  1797.         }
  1798.         if (is_array($childs) && count($childs) > 0) {
  1799.             $placeholders = [];
  1800.             foreach ($childs as $i => $child) {
  1801.                 $placeholder ':child_' $i;
  1802.                 $placeholders[] = $placeholder;
  1803.                 $params['child_' $i] = $child;
  1804.             }
  1805.             $whereConditions[] = ' p.seller_id IN (' implode(', '$placeholders) . ')';
  1806.         }
  1807.         if (!empty($whereConditions)) {
  1808.             $sql .= " WHERE " implode(' AND '$whereConditions);
  1809.         }
  1810.         $sql .= "
  1811.             GROUP BY $groupByField
  1812.             ORDER BY $groupByField ASC
  1813.         ";
  1814.         $conn $this->getEntityManager()->getConnection();
  1815.         $stmt $conn->prepare($sql);
  1816.         $result $stmt->executeQuery($params)->fetchAllAssociative();
  1817.         // Reformater les résultats sous forme { cle: total_ventes }
  1818.         $formattedResult = [];
  1819.         foreach ($range as $cle) {
  1820.             $formattedResult[$cle] = 0;
  1821.         }
  1822.         foreach ($result as $row) {
  1823.             $formattedResult[$row['cle']] = (int) $row['total_ventes'];
  1824.         }
  1825.         return (object) $formattedResult;
  1826.     }
  1827.     function getDaysInMonth($month$year)
  1828.     {
  1829.         $daysInMonth cal_days_in_month(CAL_GREGORIAN$month$year);
  1830.         return range(1$daysInMonth);
  1831.     }
  1832.     public function getProductionsDetailsForOneDay($pointOfSale$date$cluster,  $codeInsee,  $optionSelect$anneeMois$etatId$roleUser$childs$organisationId)
  1833.     {
  1834.         $dateField '';
  1835.         switch ($optionSelect) {
  1836.             case 'V':
  1837.                 $dateField 'p.date_vente_valid_b';
  1838.                 break;
  1839.             case 'R':
  1840.                 $dateField 'p.date_racc';
  1841.                 break;
  1842.             case 'B':
  1843.                 $dateField 'p.date_cmd_a';
  1844.                 break;
  1845.         }
  1846.         $sql "
  1847.         SELECT 
  1848.             p.*, e.nom AS nom_etat, s.verbatim, s.note_satisfaction
  1849.         FROM production p
  1850.         LEFT JOIN satisfaction_client s ON s.production_id = p.id AND s.order_number = p.num_commande
  1851.         LEFT JOIN etat_production e ON e.id = p.etat_id 
  1852.     ";
  1853.         $whereConditions = [];
  1854.         $params = [];
  1855.         if ($anneeMois) {
  1856.             $whereConditions[] = "MONTH($dateField) = :mois";
  1857.             $whereConditions[] = "YEAR($dateField) = :annee";
  1858.             $params['mois'] = (int)explode('-'$anneeMois)[1];
  1859.             $params['annee'] = (int)explode('-'$anneeMois)[0];
  1860.         } elseif ($date) {
  1861.             $whereConditions[] = "DATE($dateField) = :dateJour";
  1862.             $params['dateJour'] = $date->format('Y-m-d');
  1863.         }
  1864.         if ($cluster) {
  1865.             $whereConditions[] = "p.cluster_id = :clusterId";
  1866.             $params['clusterId'] = $cluster->getId();
  1867.         }
  1868.         if ($etatId) {
  1869.             $whereConditions[] = "p.etat_id = :etatId";
  1870.             $params['etatId'] = $etatId;
  1871.         }
  1872.         if ($codeInsee) {
  1873.             $whereConditions[] = "p.code_insee = :codeInsee";
  1874.             $params['codeInsee'] = $codeInsee;
  1875.         }
  1876.         if ($organisationId) {
  1877.             $whereConditions[] = 'p.organisation_id = :organisationId';
  1878.             $params['organisationId'] = $organisationId;
  1879.         } else {
  1880.             $whereConditions[] = "p.point_of_sale_id = :pointOfSale";
  1881.             $params['pointOfSale'] = $pointOfSale->getId();
  1882.         }
  1883.         if (is_array($childs) && count($childs) > 0) {
  1884.             $placeholders = [];
  1885.             foreach ($childs as $i => $child) {
  1886.                 $placeholder ':child_' $i;
  1887.                 $placeholders[] = $placeholder;
  1888.                 $params['child_' $i] = $child;
  1889.             }
  1890.             $whereConditions[] = ' p.seller_id IN (' implode(', '$placeholders) . ')';
  1891.         }
  1892.         if (!empty($whereConditions)) {
  1893.             $sql .= " WHERE " implode(' AND '$whereConditions);
  1894.         }
  1895.         $sql .= "
  1896.         ORDER BY $dateField DESC
  1897.     ";
  1898.         $conn $this->getEntityManager()->getConnection();
  1899.         $stmt $conn->prepare($sql);
  1900.         $result $stmt->executeQuery($params)->fetchAllAssociative();
  1901.         return  $result;
  1902.     }
  1903.     public function getSalesForOneDayByHour($pointOfSale$date$cluster,  $codeInsee$user$optionSelect$roleUser)
  1904.     {
  1905.         $dateField '';
  1906.         switch ($optionSelect) {
  1907.             case 'V':
  1908.                 $dateField 'p.date_vente_valid_b';
  1909.                 break;
  1910.             case 'R':
  1911.                 $dateField 'p.date_racc';
  1912.                 break;
  1913.             case 'B':
  1914.                 $dateField 'p.date_cmd_a';
  1915.                 break;
  1916.         }
  1917.         $groupByField 'HOUR(' $dateField ')';
  1918.         $selectField 'HOUR(' $dateField ') AS cle';
  1919.         $range range(023);
  1920.         $sql "
  1921.         SELECT 
  1922.             $selectField,
  1923.             COUNT(p.id) AS total_ventes
  1924.         FROM production p
  1925.     ";
  1926.         $params = [];
  1927.         $whereConditions = [];
  1928.         if ($roleUser != "ROLE_ORGANISATION") {
  1929.             $whereConditions[] = "p.point_of_sale_id = :pointOfSale";
  1930.             $params['pointOfSale'] = $pointOfSale->getId();
  1931.         }
  1932.         if ($date) {
  1933.             $whereConditions[] = "DATE($dateField) = :dateJour";
  1934.             $params['dateJour'] = $date->format('Y-m-d');
  1935.         }
  1936.         if ($cluster) {
  1937.             $whereConditions[] = "p.cluster_id = :clusterId";
  1938.             $params['clusterId'] = $cluster->getId();
  1939.         }
  1940.         if ($codeInsee) {
  1941.             $whereConditions[] = "p.code_insee = :codeInsee";
  1942.             $params['codeInsee'] = $codeInsee;
  1943.         }
  1944.         if ($user) {
  1945.             $whereConditions[] = "p.seller_id = :seller_id";
  1946.             $params['seller_id'] = $user->getId();
  1947.         }
  1948.         if (!empty($whereConditions)) {
  1949.             $sql .= " WHERE " implode(' AND '$whereConditions);
  1950.         }
  1951.         $sql .= "
  1952.             GROUP BY $groupByField
  1953.             ORDER BY $groupByField ASC
  1954.         ";
  1955.         $conn $this->getEntityManager()->getConnection();
  1956.         $stmt $conn->prepare($sql);
  1957.         $result $stmt->executeQuery($params)->fetchAllAssociative();
  1958.         // Reformater les résultats sous forme { cle: total_ventes }
  1959.         $formattedResult = [];
  1960.         foreach ($range as $cle) {
  1961.             $formattedResult[$cle] = 0;
  1962.         }
  1963.         foreach ($result as $row) {
  1964.             $formattedResult[$row['cle']] = (int) $row['total_ventes'];
  1965.         }
  1966.         return (object) $formattedResult;
  1967.     }
  1968.     public function getTopAndFlopClusters($pointOfSale$anneeMois$optionSelect$option$childs$organisationId)
  1969.     {
  1970.         $dateField '';
  1971.         switch ($optionSelect) {
  1972.             case 'V':
  1973.                 $dateField 'p.date_vente_valid_b';
  1974.                 break;
  1975.             case 'R':
  1976.                 $dateField 'p.date_racc';
  1977.                 break;
  1978.             case 'B':
  1979.                 $dateField 'p.date_cmd_a';
  1980.                 break;
  1981.         }
  1982.         $sql "
  1983.             SELECT 
  1984.                 c.code_cluster,
  1985.                 c.libelle_cluster,
  1986.                 COUNT(p.id) AS total_ventes
  1987.             FROM production p
  1988.             LEFT JOIN cluster c ON c.id = p.cluster_id
  1989.         ";
  1990.         $params = [];
  1991.         $whereAdded false;
  1992.         if ($pointOfSale) {
  1993.             $sql .= " WHERE p.point_of_sale_id = :pointOfSale ";
  1994.             $params['pointOfSale'] = $pointOfSale->getId();
  1995.             $whereAdded true;
  1996.         }
  1997.         if ($anneeMois) {
  1998.             $sql .= $whereAdded " AND " " WHERE ";
  1999.             $sql .= "MONTH($dateField) = :mois AND YEAR($dateField) = :annee ";
  2000.             [$annee$mois] = explode('-'$anneeMois);
  2001.             $params['mois'] = (int)$mois;
  2002.             $params['annee'] = (int)$annee;
  2003.             $whereAdded true;
  2004.         }
  2005.         if ($organisationId) {
  2006.             $sql .= $whereAdded " AND " " WHERE ";
  2007.             $sql .= "p.organisation_id = :organisationId";
  2008.             $params['organisationId'] = (int)$organisationId;
  2009.             $whereAdded true;
  2010.         }
  2011.         if (is_array($childs) && count($childs) > 0) {
  2012.             $placeholders = [];
  2013.             foreach ($childs as $i => $child) {
  2014.                 $placeholder ':child_' $i;
  2015.                 $placeholders[] = $placeholder;
  2016.                 $params['child_' $i] = $child;
  2017.             }
  2018.             // Ajouter correctement WHERE ou AND selon le contexte
  2019.             $sql .= $whereAdded " AND " " WHERE ";
  2020.             $sql .= 'p.seller_id IN (' implode(', '$placeholders) . ')';
  2021.             $whereAdded true;
  2022.         }
  2023.         $sql .= " GROUP BY p.cluster_id, p.point_of_sale_id ";
  2024.         $sql .= ($option === "top")
  2025.             ? " ORDER BY total_ventes DESC "
  2026.             " ORDER BY total_ventes ASC ";
  2027.         $sql .= " LIMIT 5";
  2028.         $conn $this->getEntityManager()->getConnection();
  2029.         $stmt $conn->prepare($sql);
  2030.         $result $stmt->executeQuery($params)->fetchAllAssociative();
  2031.         return $result;
  2032.     }
  2033.     public function getProductionsRaccoValidRate($pointOfSale$cluster$mois$annee$codeInsee$etat$option$childs$organisationId): array
  2034.     {
  2035.         $qb $this->createQueryBuilder('p')
  2036.             ->select('
  2037.             COUNT(p.id) as total_ventes,
  2038.             MONTH(p.dateVenteValidB) as mois_validation,
  2039.             YEAR(p.dateVenteValidB) as annee_validation,
  2040.             MONTH(p.dateRacc) as mois_racc,
  2041.             YEAR(p.dateRacc) as annee_racc
  2042.         ')
  2043.             ->where('p.etat = :etat')
  2044.             ->setParameter('etat'$etat)
  2045.             ->leftJoin('p.pointOfSale''po');
  2046.         if ($pointOfSale) {
  2047.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  2048.                 ->setParameter('pointOfSale'$pointOfSale);
  2049.         }
  2050.         // Vérifier si un mois est fourni
  2051.         if ($mois) {
  2052.             // Récupérer les trois mois précédents avec année
  2053.             $months $this->generatePreviousMonths($annee$mois3);
  2054.             // Extraire les mois et années sous forme de tuples
  2055.             $moisAnneeArray array_map(function ($entry) {
  2056.                 return [
  2057.                     'mois' => (int)substr($entry['mois_annee'], 52),
  2058.                     'annee' => (int)substr($entry['mois_annee'], 04)
  2059.                 ];
  2060.             }, $months);
  2061.             if ($option === "R") {
  2062.                 $qb->andWhere(
  2063.                     '(YEAR(p.dateRacc) = :year1 AND MONTH(p.dateRacc) = :month1) 
  2064.                 OR (YEAR(p.dateRacc) = :year2 AND MONTH(p.dateRacc) = :month2)
  2065.                 OR (YEAR(p.dateRacc) = :year3 AND MONTH(p.dateRacc) = :month3)'
  2066.                 )
  2067.                     ->setParameter('year1'$moisAnneeArray[0]['annee'])
  2068.                     ->setParameter('month1'$moisAnneeArray[0]['mois'])
  2069.                     ->setParameter('year2'$moisAnneeArray[1]['annee'])
  2070.                     ->setParameter('month2'$moisAnneeArray[1]['mois'])
  2071.                     ->setParameter('year3'$moisAnneeArray[2]['annee'])
  2072.                     ->setParameter('month3'$moisAnneeArray[2]['mois']);
  2073.             } elseif ($option === "V") {
  2074.                 $qb->andWhere(
  2075.                     '(YEAR(p.dateVenteValidB) = :year1 AND MONTH(p.dateVenteValidB) = :month1) 
  2076.                 OR (YEAR(p.dateVenteValidB) = :year2 AND MONTH(p.dateVenteValidB) = :month2)
  2077.                 OR (YEAR(p.dateVenteValidB) = :year3 AND MONTH(p.dateVenteValidB) = :month3)'
  2078.                 )
  2079.                     ->setParameter('year1'$moisAnneeArray[0]['annee'])
  2080.                     ->setParameter('month1'$moisAnneeArray[0]['mois'])
  2081.                     ->setParameter('year2'$moisAnneeArray[1]['annee'])
  2082.                     ->setParameter('month2'$moisAnneeArray[1]['mois'])
  2083.                     ->setParameter('year3'$moisAnneeArray[2]['annee'])
  2084.                     ->setParameter('month3'$moisAnneeArray[2]['mois']);
  2085.             }
  2086.         } else {
  2087.             // Si aucun mois n'est spécifié, appliquer uniquement le filtre sur l'année
  2088.             if ($option === "R") {
  2089.                 $qb->andWhere('YEAR(p.dateRacc) = :year')
  2090.                     ->setParameter('year'$annee);
  2091.             } elseif ($option === "V") {
  2092.                 $qb->andWhere('YEAR(p.dateVenteValidB) = :year')
  2093.                     ->setParameter('year'$annee);
  2094.             }
  2095.         }
  2096.         // Filtrage supplémentaire
  2097.         if ($cluster) {
  2098.             $qb->andWhere('p.cluster = :cluster')
  2099.                 ->setParameter('cluster'$cluster);
  2100.         }
  2101.         if ($codeInsee) {
  2102.             $qb->andWhere('p.codeInsee = :codeInsee')
  2103.                 ->setParameter('codeInsee'$codeInsee);
  2104.         }
  2105.         if (is_array($childs) && count($childs) > 0) {
  2106.             $qb->andWhere('p.seller IN (:childs)')
  2107.                 ->leftJoin('p.seller''s')
  2108.                 ->setParameter('childs'$childs);
  2109.         }
  2110.         if ($organisationId) {
  2111.             $qb->andWhere('p.organisation = :organisationId')
  2112.                 ->setParameter('organisationId'$organisationId);
  2113.         }
  2114.         $qb->groupBy('mois_validation''mois_racc''po.id')
  2115.             ->orderBy("DATE_FORMAT(p.dateRacc, '%Y%m')""DESC")  // Formater l'année et le mois en 'YYYYMM'
  2116.             ->addOrderBy("DATE_FORMAT(p.dateVenteValidB, '%Y%m')""DESC");;
  2117.         $results $qb->getQuery()->getArrayResult();
  2118.         // Formater les mois et trier en PHP
  2119.         foreach ($results as &$result) {
  2120.             $result['mois_racc'] = str_pad($result['mois_racc'], 2'0'STR_PAD_LEFT);
  2121.             $result['mois_validation'] = str_pad($result['mois_validation'], 2'0'STR_PAD_LEFT);
  2122.         }
  2123.         // Trier les résultats par année et mois (formaté en 'YYYYMM')
  2124.         usort($results, function ($a$b) {
  2125.             $aDate $a['annee_racc'] . str_pad($a['mois_racc'], 2'0'STR_PAD_LEFT);
  2126.             $bDate $b['annee_racc'] . str_pad($b['mois_racc'], 2'0'STR_PAD_LEFT);
  2127.             return strcmp($bDate$aDate);  // Tri décroissant
  2128.         });
  2129.         return $results;
  2130.     }
  2131.     function generatePreviousMonths($year$month$nbMonths)
  2132.     {
  2133.         // Crée une DateTime pour le mois et l'année donnés
  2134.         $date = new \DateTime("$year-$month-01");
  2135.         // Tableau pour stocker les mois
  2136.         $months = [];
  2137.         // Générer les NBRe de  mois
  2138.         for ($i 0$i $nbMonths$i++) {
  2139.             // Ajouter le mois formaté au tableau
  2140.             $months[] = ['mois_annee' => $date->format('Y-m')];
  2141.             // Subir la date de 1 mois
  2142.             $date->sub(new \DateInterval('P1M'));
  2143.         }
  2144.         // Retourner le tableau avec les mois du plus récent au plus ancien
  2145.         return $months;
  2146.     }
  2147.     public function getTotalVentesObjectivesMangamentByUsers($mois$annee)
  2148.     {
  2149.         $qb $this->getEntityManager()->createQueryBuilder()
  2150.             ->from('App\Entity\ObjectivesManagement''o')
  2151.             ->leftJoin('App\Entity\Production''p'Join::WITH'p.seller = o.user')
  2152.             ->leftJoin('App\Entity\Cluster''c'Join::WITH'o.codeCluster = c.codeCluster')
  2153.             ->leftJoin('App\Entity\EtatProduction''e'Join::WITH'p.etat = e.id')
  2154.             ->leftJoin('App\Entity\User''u'Join::WITH'o.user = u.id')
  2155.             // Sélectionner les champs
  2156.             ->addSelect('u.id AS user_id')
  2157.             ->addSelect('o.codeCluster AS codeCluster')
  2158.             ->addSelect('MAX(c.libelleCluster) AS libelleCluster')
  2159.             ->addSelect('e.nom AS etat')
  2160.             ->addSelect('COUNT(p.id) AS totalVente')
  2161.             ->addSelect('o.objectivesVv AS vv')
  2162.             ->addSelect('o.objectivesVr AS vr')
  2163.             ->where('MONTH(p.dateVenteValidB) = :mois')
  2164.             ->andWhere('YEAR(p.dateVenteValidB) = :annee')
  2165.             ->andWhere('YEAR(o.monthAt) = :annee')
  2166.             ->andWhere('MONTH(o.monthAt) = :mois')
  2167.             ->setParameter('mois'$mois)
  2168.             ->setParameter('annee'$annee)
  2169.             // Regroupement
  2170.             ->groupBy('u.id, o.codeCluster, e.id')
  2171.             // Ordre
  2172.             ->orderBy('u.id''ASC')
  2173.             ->addOrderBy('c.codeCluster''ASC')
  2174.             // Exécuter la requête
  2175.             ->getQuery();
  2176.         return $qb->getArrayResult();
  2177.     }
  2178.     public function searchProductions(?string $searchTerm, ?int $pointOfSaleId)
  2179.     {
  2180.         $qb $this->getEntityManager()->createQueryBuilder();
  2181.         $qb->select('p AS production''n.titulaireEmail AS nomClient')
  2182.             ->from('App\Entity\Production''p')
  2183.             ->leftJoin('App\Entity\NmdAppPanier''n''WITH''p.codePanier = n.numPanier')
  2184.             ->where(
  2185.                 $qb->expr()->orX(
  2186.                     $qb->expr()->like('LOWER(p.codePanier)'':search'),
  2187.                     $qb->expr()->like('LOWER(p.numCommande)'':search'),
  2188.                     $qb->expr()->like('LOWER(p.ville)'':search'),
  2189.                     $qb->expr()->like('LOWER(p.streetName)'':search'),
  2190.                     $qb->expr()->like('LOWER(p.codeInsee)'':search'),
  2191.                     $qb->expr()->like('LOWER(p.codePostTitu)'':search'),
  2192.                     $qb->expr()->like('LOWER(n.titulaireEmail)'':search')
  2193.                 )
  2194.             )
  2195.             ->andWhere('p.pointOfSale = :pointOfSaleId')
  2196.             ->setParameter('search'$searchTerm '%' strtolower($searchTerm) . '%' '%%')
  2197.             ->setParameter('pointOfSaleId'$pointOfSaleId);
  2198.         return $qb->getQuery()->getResult();
  2199.     }
  2200.     public function filterDataTotal($option$code$place$category$pointOfSaleId$dateDebut$dateFin)
  2201.     {
  2202.         $qb $this->getEntityManager()->createQueryBuilder();
  2203.         $qb->select('po.code AS cpv, cat.name AS category_name, COUNT(p.id) AS total_ventes')
  2204.             ->from('App\Entity\Production''p')
  2205.             ->leftJoin('p.pointOfSale''po')
  2206.             ->leftJoin('p.category''cat')
  2207.             ->leftJoin('p.cluster''c');
  2208.         $groupByFields = ['po.id''c.id''cat.id'];
  2209.         // Initialiser un tableau pour stocker les champs déjà sélectionnés
  2210.         $selectedFields = [];
  2211.         $codeType null;
  2212.         // CODE ou PLACE
  2213.         if ($code) {
  2214.             // Vérification si $code est un codeCluster ou un codeInsee
  2215.             if (strpos($code'-') !== false) {
  2216.                 // Si c'est un codeCluster
  2217.                 if (!in_array('c.libelleCluster AS libelle_cluster'$selectedFields)) {
  2218.                     $qb->addSelect('c.libelleCluster AS libelle_cluster');
  2219.                     $selectedFields[] = 'c.libelleCluster AS libelle_cluster';
  2220.                 }
  2221.                 if (!in_array('p.codeInsee AS cod_insee'$selectedFields)) {
  2222.                     $qb->addSelect('p.codeInsee AS cod_insee');
  2223.                     $selectedFields[] = 'p.codeInsee AS cod_insee';
  2224.                 }
  2225.                 if (!in_array('p.ville AS ville'$selectedFields)) {
  2226.                     $qb->addSelect('p.ville AS ville');
  2227.                     $selectedFields[] = 'p.ville AS ville';
  2228.                 }
  2229.                 $qb->andWhere('c.codeCluster = :codeCluster')
  2230.                     ->setParameter('codeCluster'$code);
  2231.                 // Groupement par COD_INSEE pour un codeCluster
  2232.                 $qb->groupBy('p.codeInsee');
  2233.                 $groupByFields[] = 'p.codeInsee';
  2234.                 $codeType "codeCluster";
  2235.             } else {
  2236.                 // Si c'est un codeInsee
  2237.                 $qb->andWhere('p.codeInsee = :codeInsee')
  2238.                     ->setParameter('codeInsee'$code);
  2239.                 if (!in_array('p.codeInsee AS cod_insee'$selectedFields)) {
  2240.                     $qb->addSelect('p.codeInsee AS cod_insee');
  2241.                     $selectedFields[] = 'p.codeInsee AS cod_insee';
  2242.                 }
  2243.                 if (!in_array('p.ville AS ville'$selectedFields)) {
  2244.                     $qb->addSelect('p.ville AS ville');
  2245.                     $selectedFields[] = 'p.ville AS ville';
  2246.                 }
  2247.                 if (!in_array('p.streetName AS nom_voie'$selectedFields)) {
  2248.                     $qb->addSelect('p.streetName AS nom_voie');
  2249.                     $selectedFields[] = 'p.streetName AS nom_voie';
  2250.                 }
  2251.                 $groupByFields[] = 'p.streetName';
  2252.                 $codeType "codeInsee";
  2253.             }
  2254.         }
  2255.         if ($place) {
  2256.             if (strpos(trim($place), 'v:') === 0) {
  2257.                 $qb->andWhere('LOWER(p.ville) = :ville')
  2258.                     ->setParameter('ville'strtolower(substr($place2)));
  2259.                 if (!in_array('p.streetName AS nom_voie'$selectedFields)) {
  2260.                     $qb->addSelect('p.streetName AS nom_voie');
  2261.                     $selectedFields[] = 'p.streetName AS nom_voie';
  2262.                 }
  2263.                 if (!in_array('p.ville AS ville'$selectedFields)) {
  2264.                     $qb->addSelect('p.ville AS ville');
  2265.                     $selectedFields[] = 'p.ville AS ville';
  2266.                 }
  2267.                 $groupByFields[] = 'p.streetName';
  2268.                 $codeType "codeInsee";
  2269.             } elseif (strpos(trim($place), 'c:') === 0) {
  2270.                 if (!in_array('c.libelleCluster AS libelle_cluster'$selectedFields)) {
  2271.                     $qb->addSelect('c.libelleCluster AS libelle_cluster');
  2272.                     $selectedFields[] = 'c.libelleCluster AS libelle_cluster';
  2273.                 }
  2274.                 if (!in_array('p.codeInsee AS cod_insee'$selectedFields)) {
  2275.                     $qb->addSelect('p.codeInsee AS cod_insee');
  2276.                     $selectedFields[] = 'p.codeInsee AS cod_insee';
  2277.                 }
  2278.                 if (!in_array('p.ville AS ville'$selectedFields)) {
  2279.                     $qb->addSelect('p.ville AS ville');
  2280.                     $selectedFields[] = 'p.ville AS ville';
  2281.                 }
  2282.                 $qb->andWhere('LOWER(c.libelleCluster) = :libelleCluster')
  2283.                     ->setParameter('libelleCluster'strtolower(substr($place2)));
  2284.                 // Groupement par COD_INSEE pour un codeCluster
  2285.                 $groupByFields[] = 'p.codeInsee';
  2286.                 $codeType "codeCluster";
  2287.             }
  2288.         }
  2289.         switch ($option) {
  2290.             case 'V':
  2291.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  2292.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  2293.                 break;
  2294.             case 'R':
  2295.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  2296.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  2297.                 break;
  2298.             case 'B':
  2299.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  2300.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  2301.                 break;
  2302.         }
  2303.         $qb->setParameter('debut'$dateDebut)
  2304.             ->setParameter('fin'$dateFin);
  2305.         if ($category) {
  2306.             $qb->andWhere('LOWER(cat.name) = :category')
  2307.                 ->setParameter('category'strtolower($category));
  2308.         }
  2309.         if ($pointOfSaleId) {
  2310.             $qb->andWhere('p.pointOfSale = :pointOfSaleId')
  2311.                 ->setParameter('pointOfSaleId'$pointOfSaleId);
  2312.         }
  2313.         $qb->groupBy(
  2314.             implode(', '$groupByFields)
  2315.         );
  2316.         // Trier par total_ventes
  2317.         $qb->orderBy('total_ventes''DESC');
  2318.         // Exécuter la requête et retourner les résultats
  2319.         $results $qb->getQuery()->getArrayResult();
  2320.         // Ajouter codeType dans les résultats
  2321.         foreach ($results as &$result) {
  2322.             if ($codeType) {
  2323.                 $result['codeType'] = $codeType;
  2324.             }
  2325.         }
  2326.         // Retourner les résultats
  2327.         return $results;
  2328.     }
  2329.     public function filterDataDetails($option$code$place$category$pointOfSaleId$dateDebut$dateFin)
  2330.     {
  2331.         $qb $this->getEntityManager()->createQueryBuilder();
  2332.         $qb->select('p')
  2333.             ->from('App\Entity\Production''p')
  2334.             ->leftJoin('p.pointOfSale''po')
  2335.             ->leftJoin('p.category''cat')
  2336.             ->leftJoin('p.cluster''c')
  2337.         ;
  2338.         $groupByFields = ['po.id''c.id''cat.id'];
  2339.         //CODE ou PLACE 
  2340.         if ($code) {
  2341.             // Vérification si $code est un codeCluster ou un codeInsee
  2342.             if (strpos($code'-') !== false) {
  2343.                 // Si c'est un codeCluster
  2344.                 $qb
  2345.                     ->andWhere('c.codeCluster = :codeCluster')
  2346.                     ->setParameter('codeCluster'$code);
  2347.                 // Groupement par COD_INSEE pour un codeCluster
  2348.                 $qb->groupBy('p.codeInsee');
  2349.                 $groupByFields[] = 'p.codeInsee';
  2350.                 $codeType "codeCluster";
  2351.             } else {
  2352.                 // Si c'est un codeInsee
  2353.                 $qb->andWhere('p.codeInsee = :codeInsee')
  2354.                     ->setParameter('codeInsee'$code);
  2355.                 $groupByFields[] = 'p.streetName';
  2356.                 $codeType "codeInsee";
  2357.             }
  2358.         }
  2359.         if ($place) {
  2360.             if (strpos($place'v:') === 0) {
  2361.                 $qb->andWhere('LOWER(p.ville) = :ville')
  2362.                     ->setParameter('ville'strtolower(substr($place2)));
  2363.                 $groupByFields[] = 'p.streetName';
  2364.                 $codeType "codeInsee";
  2365.             } elseif (strpos($place'c:') === 0) {
  2366.                 $qb
  2367.                     ->andWhere('LOWER(c.libelleCluster) = :libelleCluster')
  2368.                     ->setParameter('libelleCluster'strtolower(substr($place2)));
  2369.                 // Groupement par COD_INSEE pour un codeCluster
  2370.                 $groupByFields[] = 'p.codeInsee';
  2371.                 $codeType "codeCluster";
  2372.             }
  2373.         }
  2374.         switch ($option) {
  2375.             case 'V':
  2376.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  2377.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  2378.                 break;
  2379.             case 'R':
  2380.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  2381.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  2382.                 break;
  2383.             case 'B':
  2384.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  2385.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  2386.                 break;
  2387.         }
  2388.         $qb->setParameter('debut'$dateDebut)
  2389.             ->setParameter('fin'$dateFin);
  2390.         if ($category) {
  2391.             $qb->andWhere('LOWER(cat.name) = :category')
  2392.                 ->setParameter('category'strtolower($category));
  2393.         }
  2394.         if ($pointOfSaleId) {
  2395.             $qb->andWhere('p.pointOfSale = :pointOfSaleId')
  2396.                 ->setParameter('pointOfSaleId'$pointOfSaleId);
  2397.         }
  2398.         $qb->groupBy(
  2399.             implode(', '$groupByFields)
  2400.         );
  2401.         // Exécuter la requête et retourner les résultats
  2402.         $results $qb->getQuery()->getArrayResult();
  2403.         return $results;
  2404.     }
  2405.     public function getProductionsUsersCategoriesByMonth($pointOfSale): string
  2406.     {
  2407.         $sql "
  2408.             SELECT CONCAT('{', 
  2409.                 GROUP_CONCAT(
  2410.                     '\"', mois, '\": {', days_data, '}'
  2411.                     SEPARATOR ','
  2412.                 ), 
  2413.             '}') AS json_result
  2414.             FROM (
  2415.                 SELECT mois, 
  2416.                     GROUP_CONCAT(
  2417.                         '\"', jour, '\": {', seller_data, '}'
  2418.                         SEPARATOR ','
  2419.                     ) AS days_data
  2420.                 FROM (
  2421.                     SELECT mois, jour, 
  2422.                         GROUP_CONCAT(
  2423.                             '\"', seller_id, ' - ', seller_name, '\": {', category_data, '}'
  2424.                             SEPARATOR ','
  2425.                         ) AS seller_data
  2426.                     FROM (
  2427.                         SELECT mois, jour, seller_id, seller_name, 
  2428.                             GROUP_CONCAT(
  2429.                                 '\"', category_name, '\": {', product_data, '}'
  2430.                                 SEPARATOR ','
  2431.                             ) AS category_data
  2432.                         FROM (
  2433.                             SELECT mois, jour, seller_id, seller_name, category_name, 
  2434.                                 GROUP_CONCAT(
  2435.                                     '\"', product_name, '\": ', total_lignes
  2436.                                     SEPARATOR ','
  2437.                                 ) AS product_data
  2438.                             FROM (
  2439.                                 SELECT 
  2440.                                     DATE_FORMAT(d.date, '%Y-%m') AS mois,
  2441.                                     DATE_FORMAT(d.date, '%Y-%m-%d') AS jour,
  2442.                                     d.seller_id, 
  2443.                                     CONCAT(u.nom, ' ', u.prenom) AS seller_name, 
  2444.                                     c.name AS category_name, 
  2445.                                     p.name AS product_name, 
  2446.                                     COUNT(*) AS total_lignes
  2447.                                 FROM declarative d
  2448.                                 JOIN product p ON d.product_id = p.id
  2449.                                 JOIN category_product c ON p.category_id = c.id
  2450.                                 JOIN user u ON d.seller_id = u.id
  2451.                                 WHERE u.point_of_sale_id = :pointOfSaleId
  2452.                                 GROUP BY mois, jour, seller_id, seller_name, category_name, product_name
  2453.                             ) AS products_grouped
  2454.                             GROUP BY mois, jour, seller_id, seller_name, category_name
  2455.                         ) AS categories_grouped
  2456.                         GROUP BY mois, jour, seller_id, seller_name
  2457.                     ) AS sellers_grouped
  2458.                     GROUP BY mois, jour
  2459.                 ) AS days_grouped
  2460.                 GROUP BY mois
  2461.             ) AS final_json;
  2462.         ";
  2463.         $conn $this->getEntityManager()->getConnection();
  2464.         $stmt $conn->prepare($sql);
  2465.         $stmt->bindValue('pointOfSaleId'$pointOfSale->getId());
  2466.         $result $stmt->executeQuery()->fetchOne();
  2467.         return $result ?: '{}';
  2468.     }
  2469.     public function getProductionsByCpv($pointOfSaleId$codeCluster$codeInsee$optionSelect$debut$fin$category$etatId$organisationId)
  2470.     {
  2471.         $qb $this->createQueryBuilder('p')
  2472.             ->leftJoin('p.pointOfSale''ps')
  2473.             ->leftJoin('p.cluster''c')
  2474.             ->select('
  2475.                 ps.id AS pointOfSaleId,
  2476.                 ps.code AS pointOfSaleCode,
  2477.                 ps.name AS pointOfSaleName,
  2478.                 c.codeCluster AS codeCluster,
  2479.                 c.libelleCluster AS libelleCluster,
  2480.                 p.codeInsee AS codeInsee,
  2481.                 p.ville AS ville,
  2482.                 COUNT(p.id) AS total_ventes
  2483.             ');
  2484.         if ($category) {
  2485.             $category array_filter(
  2486.                 array_map('intval'explode(','$category)),
  2487.                 fn($id) => $id 0
  2488.             );
  2489.             $qb->andWhere('p.category IN (:categories)')
  2490.                 ->setParameter('categories'$category);
  2491.         }
  2492.         if ($etatId !== null) {
  2493.             $qb->andWhere('p.etat = :etatId')
  2494.                 ->setParameter('etatId'$etatId);
  2495.         }
  2496.         if ($pointOfSaleId !== null) {
  2497.             $qb->andWhere('p.pointOfSale = :pointOfSaleId')
  2498.                 ->setParameter('pointOfSaleId'$pointOfSaleId);
  2499.         }
  2500.         if ($codeCluster !== null) {
  2501.             $qb->andWhere('c.codeCluster = :codeCluster')
  2502.                 ->setParameter('codeCluster'$codeCluster);
  2503.         }
  2504.         if ($codeInsee !== null) {
  2505.             $qb->andWhere('p.codeInsee = :codeInsee')
  2506.                 ->setParameter('codeInsee'$codeInsee);
  2507.         }
  2508.         if ($organisationId !== null) {
  2509.             $qb
  2510.                 ->andWhere('p.organisation = :organisationId')
  2511.                 ->andWhere('ps.organisation = :organisationId')
  2512.                 ->setParameter('organisationId'$organisationId);
  2513.         }
  2514.         $qb->groupBy('ps.id, c.codeCluster, p.codeInsee');
  2515.         if ($optionSelect && $debut && $fin) {
  2516.             switch ($optionSelect) {
  2517.                 case 'V':
  2518.                     $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  2519.                     $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  2520.                     break;
  2521.                 case 'R':
  2522.                     $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  2523.                     $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  2524.                     break;
  2525.                 case 'B':
  2526.                     $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  2527.                     $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  2528.                     break;
  2529.             }
  2530.             $qb->setParameter('debut'$debut->format('Y-m-d'))
  2531.                 ->setParameter('fin'$fin->format('Y-m-d'));
  2532.         }
  2533.         $results $qb->getQuery()->getArrayResult();
  2534.         return $this->structureResults($results);
  2535.     }
  2536.     private function structureResults(array $rows): array
  2537.     {
  2538.         $final = [];
  2539.         foreach ($rows as $row) {
  2540.             $psId $row['pointOfSaleId'];
  2541.             $psCode $row['pointOfSaleCode'];
  2542.             $psName $row['pointOfSaleName'];
  2543.             $clusterCode $row['codeCluster'];
  2544.             $insee $row['codeInsee'];
  2545.             if (!isset($final[$psCode])) {
  2546.                 $final[$psCode] = [
  2547.                     'pointOfSaleId' => $psId,
  2548.                     'cpv' => $psCode,
  2549.                     'cpv_name' => $psName,
  2550.                     'total_ventes' => 0,
  2551.                     'clusters' => []
  2552.                 ];
  2553.             }
  2554.             if (!isset($final[$psCode]['clusters'][$clusterCode])) {
  2555.                 $final[$psCode]['clusters'][$clusterCode] = [
  2556.                     'code_cluster' => $clusterCode,
  2557.                     'libelle_cluster' => $row['libelleCluster'],
  2558.                     'total_ventes' => 0,
  2559.                     'villes' => []
  2560.                 ];
  2561.             }
  2562.             $final[$psCode]['total_ventes'] += (int) $row['total_ventes'];
  2563.             $final[$psCode]['clusters'][$clusterCode]['total_ventes'] += (int)$row['total_ventes'];
  2564.             $final[$psCode]['clusters'][$clusterCode]['villes'][] = [
  2565.                 'code_insee' => $insee,
  2566.                 'ville' => $row['ville'],
  2567.                 'total_ventes' => (int)$row['total_ventes']
  2568.             ];
  2569.         }
  2570.         // Convert associative arrays to indexed arrays for JSON-friendly output
  2571.         return array_values(array_map(function ($ps) {
  2572.             $ps['clusters'] = array_values($ps['clusters']);
  2573.             return $ps;
  2574.         }, $final));
  2575.     }
  2576.     public function getFixeDetails($pointOfSale$cluster$codeInsee$optionSelect$dateDebut$dateFin$childs$organisationId)
  2577.     {
  2578.         $qb $this->getEntityManager()->createQueryBuilder();
  2579.         $qb->select('p')
  2580.             ->from('App\Entity\Production''p');
  2581.         if ($pointOfSale !== null) {
  2582.             $qb
  2583.                 ->andWhere('p.pointOfSale = :pointOfSale')
  2584.                 ->setParameter('pointOfSale'$pointOfSale);
  2585.         }
  2586.         switch ($optionSelect) {
  2587.             case 'V':
  2588.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  2589.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  2590.                 break;
  2591.             case 'R':
  2592.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  2593.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  2594.                 break;
  2595.             case 'B':
  2596.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  2597.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  2598.                 break;
  2599.         }
  2600.         $qb->setParameter('debut'$dateDebut)
  2601.             ->setParameter('fin'$dateFin);
  2602.         if ($cluster) {
  2603.             $qb->andWhere('p.cluster = :cluster')
  2604.                 ->setParameter('cluster'$cluster);
  2605.         }
  2606.         if ($codeInsee) {
  2607.             $qb->andWhere('p.codeInsee = :codeInsee')
  2608.                 ->setParameter('codeInsee'$codeInsee);
  2609.         }
  2610.         if ($organisationId) {
  2611.             $qb->andWhere('p.organisation = :organisationId')
  2612.                 ->setParameter('organisationId'$organisationId);
  2613.         }
  2614.         if (is_array($childs) && count($childs) > 0) {
  2615.             $qb->andWhere('p.seller IN (:childs)')
  2616.                 ->leftJoin('p.seller''s')
  2617.                 ->setParameter('childs'$childs);
  2618.         }
  2619.         // $qb->andWhere('p.mobileAutre IS NOT NULL OR p.mobileChainage IS NOT NULL');
  2620.         $qb->andWhere('p.fixeChainage IS NOT NULL ');
  2621.         $qb->andWhere('p.category IN (31,32,33)');
  2622.         $qb->orderBy('p.mobileAutre''DESC');
  2623.         $qb->orderBy('p.mobileChainage''ASC');
  2624.         return $qb->getQuery()->getArrayResult();
  2625.     }
  2626.     public function getProductionsGroupedByDateAnnulation($pointOfSale$cluster$codeInsee$optionSelect$dateDebut$dateFin)
  2627.     {
  2628.         $qb $this->getEntityManager()->createQueryBuilder();
  2629.         $qb->select('p')
  2630.             ->from('App\Entity\Production''p')
  2631.             ->where('p.pointOfSale = :pointOfSale')
  2632.             ->andWhere('p.dateResiliation IS NOT NULL'// on ne veut que les résiliées
  2633.             ->setParameter('pointOfSale'$pointOfSale);
  2634.         switch ($optionSelect) {
  2635.             case 'V':
  2636.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  2637.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  2638.                 break;
  2639.             case 'R':
  2640.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  2641.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  2642.                 break;
  2643.             case 'B':
  2644.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  2645.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  2646.                 break;
  2647.         }
  2648.         $qb->setParameter('debut'$dateDebut)
  2649.             ->setParameter('fin'$dateFin);
  2650.         if ($cluster) {
  2651.             $qb->andWhere('p.cluster = :cluster')
  2652.                 ->setParameter('cluster'$cluster);
  2653.         }
  2654.         if ($codeInsee) {
  2655.             $qb->andWhere('p.codeInsee = :codeInsee')
  2656.                 ->setParameter('codeInsee'$codeInsee);
  2657.         }
  2658.         $productions $qb->getQuery()->getArrayResult();
  2659.         // Regroupement manuel par date d'annulation
  2660.         $grouped = [];
  2661.         foreach ($productions as $prod) {
  2662.             $date $prod['dateResiliation']->format('Y-m-d');
  2663.             if (!isset($grouped[$date])) {
  2664.                 $grouped[$date] = [
  2665.                     'date_resiliation' => $date,
  2666.                     'total_lignes' => 0,
  2667.                     'details' => []
  2668.                 ];
  2669.             }
  2670.             $grouped[$date]['total_lignes'] += 1;
  2671.             $grouped[$date]['details'][] = $prod;
  2672.         }
  2673.         return array_values($grouped); // reset des clés numériques
  2674.     }
  2675.     public function getProductionsAnalyticsTitulaireVentes($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etatKO$category$option$byWeek$childs$organisationId$perid$sellerId$departement): array
  2676.     {
  2677.         $qb $this->createBaseQueryBuilder2($pointOfSale$codeCluster$codeInsee,  $mois$annee$optionSelect$departement)
  2678.             ->select('COUNT(DISTINCT p.titulaireEmail) as nombre_titulaire_email');
  2679.         if ($option == "chainage") {
  2680.             $qb->andWhere('p.fixeChainage IS NOT NULL ');
  2681.             $qb->andWhere('p.category IN (31,32,33)');
  2682.         } else {
  2683.             $qb->andWhere('p.fixeChainage IS NOT NULL OR p.fixeAutre IS NOT NULL');
  2684.             $qb->andWhere('p.category IN (31,32,33)');
  2685.         }
  2686.         if (is_array($childs) && count($childs) > 0) {
  2687.             $qb->andWhere('p.seller IN (:childs)')
  2688.                 ->leftJoin('p.seller''s')
  2689.                 ->setParameter('childs'$childs);
  2690.         }
  2691.         if ($organisationId) {
  2692.             $qb->andWhere('p.organisation = :organisationId')
  2693.                 ->setParameter('organisationId'$organisationId);
  2694.         }
  2695.         if ($perid) {
  2696.             $qb->andWhere('p.loginVendeurInit = :perid ')
  2697.                 ->setParameter('perid'$perid);
  2698.         }
  2699.         if ($sellerId) {
  2700.             $qb->andWhere('p.seller = :sellerId')
  2701.                 ->setParameter('sellerId'$sellerId);
  2702.         }
  2703.         return $qb->getQuery()->getArrayResult();
  2704.     }
  2705.     public function get4PCByWeek(
  2706.         $pointOfSale,
  2707.         $codeCluster,
  2708.         $codeInsee,
  2709.         $mois,
  2710.         $annee,
  2711.         $optionSelect,
  2712.         $etatKO,
  2713.         $category,
  2714.         $option,
  2715.         $byWeek,
  2716.         $childs,
  2717.         $organisationId,
  2718.         $perid,
  2719.         $sellerId,
  2720.         $departement
  2721.     ): array {
  2722.         $conn $this->getEntityManager()->getConnection();
  2723.         $where = [];
  2724.         $params = [];
  2725.         // pointOfSale optionnel
  2726.         if ($pointOfSale !== null) {
  2727.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  2728.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  2729.         }
  2730.         // codeCluster optionnel
  2731.         if ($codeCluster !== null) {
  2732.             $where[] = 'c.code_cluster = :codeCluster';
  2733.             $params['codeCluster'] = $codeCluster;
  2734.         }
  2735.         if ($departement) {
  2736.             $where[] = 'c.code_cluster LIKE :departement';
  2737.             $params["departement"] = $departement '-%';
  2738.         }
  2739.         if ($codeInsee !== null) {
  2740.             $where[] = 'p.code_insee = :codeInsee';
  2741.             $params['codeInsee'] = $codeInsee;
  2742.         }
  2743.         if ($organisationId) {
  2744.             $where[] = 'p.organisation_id = :organisationId';
  2745.             $params['organisationId'] = $organisationId;
  2746.         }
  2747.         if ($perid) {
  2748.             $where[] = 'p.login_vendeur_init = :perid';
  2749.             $params['perid'] = $perid;
  2750.         }
  2751.         if ($sellerId) {
  2752.             $where[] = 'p.seller_id = :sellerId';
  2753.             $params['sellerId'] = $sellerId;
  2754.         }
  2755.         // mois optionnel
  2756.         if ($mois !== null) {
  2757.             $where[] = 'MONTH(p.date_cmd_a) = :mois';
  2758.             $params['mois'] = $mois;
  2759.         }
  2760.         // annee optionnel
  2761.         if ($annee !== null) {
  2762.             $where[] = 'YEAR(p.date_cmd_a) = :annee';
  2763.             $params['annee'] = $annee;
  2764.         }
  2765.         if (is_array($childs) && count($childs) > 0) {
  2766.             $placeholders = [];
  2767.             foreach ($childs as $i => $child) {
  2768.                 $placeholder ':child_' $i;
  2769.                 $placeholders[] = $placeholder;
  2770.                 $params['child_' $i] = $child;
  2771.             }
  2772.             $where[] = ' p.seller_id IN (' implode(', '$placeholders) . ')';
  2773.         }
  2774.         // Si aucune condition ajoutée, on peut ne rien mettre dans WHERE ou ajouter "1=1"
  2775.         if (empty($where)) {
  2776.             $where[] = '1=1'// condition toujours vraie pour éviter erreur SQL
  2777.         }
  2778.         $select 'COUNT(DISTINCT p.titulaire_email) AS total';
  2779.         $groupBy '';
  2780.         if ($option === 'chainage') {
  2781.             $where[] = 'p.fixe_chainage IS NOT NULL';
  2782.             if (!empty($category)) {
  2783.                 $categoryArray array_filter(
  2784.                     array_map('intval'explode(','$category)),
  2785.                     fn($id) => $id 0
  2786.                 );
  2787.                 if (!empty($categoryArray)) {
  2788.                     $placeholders = [];
  2789.                     foreach ($categoryArray as $k => $catId) {
  2790.                         $key 'cat' $k;
  2791.                         $placeholders[] = ':' $key;
  2792.                         $params[$key] = $catId;
  2793.                     }
  2794.                     $where[] = 'p.category_id IN (' implode(','$placeholders) . ')';
  2795.                 }
  2796.             }
  2797.             if ($byWeek == true) {
  2798.                 $select .= ', WEEK(p.date_cmd_a,3) AS semaine';
  2799.                 $groupBy 'GROUP BY semaine';
  2800.             }
  2801.         }
  2802.         $sql "
  2803.             SELECT $select
  2804.             FROM production p
  2805.             LEFT JOIN cluster c ON c.id = p.cluster_id
  2806.             WHERE " implode(' AND '$where) . "
  2807.             $groupBy
  2808.         ";
  2809.         $stmt $conn->prepare($sql);
  2810.         $result $stmt->executeQuery($params);
  2811.         return $result->fetchAllAssociative();
  2812.     }
  2813.     public function get4PByWeek($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$filtre$childs$organisationId$perid$sellerId$departement): array
  2814.     {
  2815.         $conn $this->getEntityManager()->getConnection();
  2816.         $sql '
  2817.             SELECT 
  2818.                 COUNT(DISTINCT result.titulaire_email) AS total, 
  2819.                 WEEK(result.date_cmd_a, 3) AS semaine
  2820.             FROM (
  2821.                 -- Partie 1 : Catégorie 32 avec emails en catégorie 1
  2822.                 SELECT p1.titulaire_email, p1.date_cmd_a
  2823.                 FROM production p1
  2824.                 LEFT JOIN cluster c1 ON p1.cluster_id = c1.id
  2825.                 WHERE p1.category_id = :cat1
  2826.                   AND p1.point_of_sale_id = :pointOfSale
  2827.                   AND MONTH(p1.date_cmd_a) = :mois
  2828.                   AND YEAR(p1.date_cmd_a) = :annee
  2829.                   AND p1.titulaire_email IN (
  2830.                       SELECT DISTINCT p_sub1.titulaire_email
  2831.                       FROM production p_sub1
  2832.                       WHERE p_sub1.category_id = 1
  2833.                         AND p_sub1.point_of_sale_id = :pointOfSale
  2834.                         AND MONTH(p_sub1.date_cmd_a) = :mois
  2835.                         AND YEAR(p_sub1.date_cmd_a) = :annee
  2836.                   )
  2837.         ';
  2838.         $params = [
  2839.             'pointOfSale' => $pointOfSale->getId(),
  2840.             'mois' => $mois,
  2841.             'annee' => $annee,
  2842.             'cat1' => 32,
  2843.             'cat2' => 31,
  2844.         ];
  2845.         if ($codeCluster) {
  2846.             $sql .= ' AND c1.code_cluster = :codeCluster';
  2847.             $params['codeCluster'] = $codeCluster;
  2848.         }
  2849.         if ($departement) {
  2850.             $sql .= " AND c1.code_cluster LIKE :departement";
  2851.             $params["departement"] = $departement '-%';
  2852.         }
  2853.         if ($codeInsee) {
  2854.             $sql .= ' AND p1.code_insee = :codeInsee';
  2855.             $params['codeInsee'] = $codeInsee;
  2856.         }
  2857.         if ($organisationId) {
  2858.             $sql .= ' AND p1.organisation_id = :organisationId';
  2859.             $params['organisationId'] = $organisationId;
  2860.         }
  2861.         if ($perid) {
  2862.             $sql .= ' AND p1.login_vendeur_init = :perid';
  2863.             $params['perid'] = $perid;
  2864.         }
  2865.         if ($sellerId) {
  2866.             $sql .= ' AND p1.seller_id = :sellerId';
  2867.             $params['sellerId'] = $sellerId;
  2868.         }
  2869.         // Gestion dynamique des placeholders pour p1.seller_id IN (...)
  2870.         if (is_array($childs) && count($childs) > 0) {
  2871.             $placeholdersP1 = [];
  2872.             foreach ($childs as $i => $child) {
  2873.                 $key 'child_p1_' $i;
  2874.                 $placeholdersP1[] = ':' $key;
  2875.                 $params[$key] = $child;
  2876.             }
  2877.             $sql .= ' AND p1.seller_id IN (' implode(', '$placeholdersP1) . ')';
  2878.         }
  2879.         $sql .= '
  2880.                 UNION
  2881.                 -- Partie 2 : Catégorie 31 avec emails en catégorie 1 ou 3
  2882.                 SELECT p2.titulaire_email, p2.date_cmd_a
  2883.                 FROM production p2
  2884.                 LEFT JOIN cluster c2 ON p2.cluster_id = c2.id
  2885.                 WHERE p2.category_id = :cat2
  2886.                   AND p2.point_of_sale_id = :pointOfSale
  2887.                   AND MONTH(p2.date_cmd_a) = :mois
  2888.                   AND YEAR(p2.date_cmd_a) = :annee
  2889.                   AND p2.titulaire_email IN (
  2890.                       SELECT DISTINCT p_sub2.titulaire_email
  2891.                       FROM production p_sub2
  2892.                       WHERE p_sub2.category_id IN (1, 3)
  2893.                         AND p_sub2.point_of_sale_id = :pointOfSale
  2894.                         AND MONTH(p_sub2.date_cmd_a) = :mois
  2895.                         AND YEAR(p_sub2.date_cmd_a) = :annee
  2896.                   )
  2897.         ';
  2898.         if ($codeCluster) {
  2899.             $sql .= ' AND c2.code_cluster = :codeCluster';
  2900.             // Pas besoin de le rajouter à $params, déjà fait avant
  2901.         }
  2902.         if ($departement) {
  2903.             $sql .= " AND c2.code_cluster LIKE :departement";
  2904.         }
  2905.         if ($codeInsee) {
  2906.             $sql .= ' AND p2.code_insee = :codeInsee';
  2907.         }
  2908.         if ($organisationId) {
  2909.             $sql .= ' AND p2.organisation_id = :organisationId';
  2910.         }
  2911.         if ($perid) {
  2912.             $sql .= ' AND p2.login_vendeur_init = :perid';
  2913.         }
  2914.         if ($sellerId) {
  2915.             $sql .= ' AND p2.seller_id = :sellerId';
  2916.         }
  2917.         // Gestion dynamique des placeholders pour p2.seller_id IN (...)
  2918.         if (is_array($childs) && count($childs) > 0) {
  2919.             $placeholdersP2 = [];
  2920.             foreach ($childs as $i => $child) {
  2921.                 $key 'child_p2_' $i;
  2922.                 $placeholdersP2[] = ':' $key;
  2923.                 $params[$key] = $child;
  2924.             }
  2925.             $sql .= ' AND p2.seller_id IN (' implode(', '$placeholdersP2) . ')';
  2926.         }
  2927.         $sql .= '
  2928.             ) AS result
  2929.             GROUP BY WEEK(result.date_cmd_a, 3)
  2930.             ORDER BY semaine
  2931.         ';
  2932.         $stmt $conn->prepare($sql);
  2933.         $result $stmt->executeQuery($params);
  2934.         return $result->fetchAllAssociative();
  2935.     }
  2936.     public function getProductionsAnalyticsTotalVentesByDateResiliationInf30($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  2937.     {
  2938.         $conn $this->getEntityManager()->getConnection();
  2939.         $sql '
  2940.         SELECT COUNT(DISTINCT p.id) AS total_vente
  2941.         FROM production p
  2942.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  2943.         LEFT JOIN cluster c ON p.cluster_id = c.id
  2944.         WHERE p.point_of_sale_id = :pointOfSale
  2945.           AND p.date_resiliation IS NOT NULL
  2946.           AND p.date_racc IS NOT NULL
  2947.           AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  2948.           AND (p.category_id NOT IN (2, 31, 32, 33,48) OR p.product_id IN(150,262,264,266,334,335,341,344,345,379))
  2949.         ';
  2950.         $params = [
  2951.             'pointOfSale' => $pointOfSale->getId(),
  2952.             'mois' => $mois,
  2953.             'annee' => $annee,
  2954.         ];
  2955.         if ($codeCluster) {
  2956.             $sql .= ' AND c.code_cluster = :codeCluster';
  2957.             $params['codeCluster'] = $codeCluster;
  2958.         }
  2959.         if ($departement) {
  2960.             $sql .= " AND c.code_cluster LIKE :departement";
  2961.             $params["departement"] = $departement '-%';
  2962.         }
  2963.         if ($codeInsee) {
  2964.             $sql .= ' AND p.code_insee = :codeInsee';
  2965.             $params['codeInsee'] = $codeInsee;
  2966.         }
  2967.         if ($organisationId) {
  2968.             $sql .= ' AND p.organisation_id = :organisationId';
  2969.             $params['organisationId'] = $organisationId;
  2970.         }
  2971.         if ($perid) {
  2972.             $sql .= ' AND p.login_vendeur_init = :perid';
  2973.             $params['perid'] = $perid;
  2974.         }
  2975.         if ($sellerId) {
  2976.             $sql .= ' AND p.seller_id = :sellerId';
  2977.             $params['sellerId'] = $sellerId;
  2978.         }
  2979.         if (is_array($childs) && count($childs) > 0) {
  2980.             $placeholders = [];
  2981.             foreach ($childs as $i => $child) {
  2982.                 $placeholder ':child_' $i;
  2983.                 $placeholders[] = $placeholder;
  2984.                 $params['child_' $i] = $child;
  2985.             }
  2986.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  2987.         }
  2988.         switch ($optionSelect) {
  2989.             case 'V':
  2990.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  2991.                 break;
  2992.             case 'R':
  2993.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  2994.                 break;
  2995.             case 'B':
  2996.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  2997.                 break;
  2998.         }
  2999.         $stmt $conn->prepare($sql);
  3000.         $result $stmt->executeQuery($params);
  3001.         return $result->fetchAssociative();
  3002.     }
  3003.     public function getProductionsAnalyticsTotalVentesByDateResiliationInf30ForChurn($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  3004.     {
  3005.         $conn $this->getEntityManager()->getConnection();
  3006.         $sql '
  3007.         SELECT COUNT(DISTINCT p.id) AS total_vente
  3008.         FROM production p
  3009.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3010.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3011.         WHERE p.point_of_sale_id = :pointOfSale
  3012.           AND p.date_resiliation IS NOT NULL
  3013.           AND p.date_racc IS NOT NULL
  3014.           AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  3015.          AND 
  3016.           (
  3017.             p.category_id IN(1,3) 
  3018.             OR 
  3019.             ( 
  3020.                 p.category_id IN(31) AND p.product_id IN(150, 262, 264, 266, 334, 335, 341, 344, 345, 379,441,442,443,444)
  3021.             ) )
  3022.         ';
  3023.         $params = [
  3024.             'pointOfSale' => $pointOfSale->getId(),
  3025.             'mois' => $mois,
  3026.             'annee' => $annee,
  3027.         ];
  3028.         if ($codeCluster) {
  3029.             $sql .= ' AND c.code_cluster = :codeCluster';
  3030.             $params['codeCluster'] = $codeCluster;
  3031.         }
  3032.         if ($departement) {
  3033.             $sql .= " AND c.code_cluster LIKE :departement";
  3034.             $params["departement"] = $departement '-%';
  3035.         }
  3036.         if ($codeInsee) {
  3037.             $sql .= ' AND p.code_insee = :codeInsee';
  3038.             $params['codeInsee'] = $codeInsee;
  3039.         }
  3040.         if ($organisationId) {
  3041.             $sql .= ' AND p.organisation_id = :organisationId';
  3042.             $params['organisationId'] = $organisationId;
  3043.         }
  3044.         if ($perid) {
  3045.             $sql .= ' AND p.login_vendeur_init = :perid';
  3046.             $params['perid'] = $perid;
  3047.         }
  3048.         if ($sellerId) {
  3049.             $sql .= ' AND p.seller_id = :sellerId';
  3050.             $params['sellerId'] = $sellerId;
  3051.         }
  3052.         if (is_array($childs) && count($childs) > 0) {
  3053.             $placeholders = [];
  3054.             foreach ($childs as $i => $child) {
  3055.                 $placeholder ':child_' $i;
  3056.                 $placeholders[] = $placeholder;
  3057.                 $params['child_' $i] = $child;
  3058.             }
  3059.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3060.         }
  3061.         switch ($optionSelect) {
  3062.             case 'V':
  3063.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3064.                 break;
  3065.             case 'R':
  3066.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3067.                 break;
  3068.             case 'B':
  3069.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3070.                 break;
  3071.         }
  3072.         $stmt $conn->prepare($sql);
  3073.         $result $stmt->executeQuery($params);
  3074.         return $result->fetchAssociative();
  3075.     }
  3076.     public function getProductionsAnalyticsTotalVentesByDateResiliationInf30ForChurn5G($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  3077.     {
  3078.         $conn $this->getEntityManager()->getConnection();
  3079.         $sql '
  3080.         SELECT COUNT(DISTINCT p.id) AS total_vente
  3081.         FROM production p
  3082.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3083.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3084.         WHERE p.point_of_sale_id = :pointOfSale
  3085.           AND p.date_resiliation IS NOT NULL
  3086.           AND p.date_racc IS NOT NULL
  3087.           AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  3088.          AND 
  3089.            p.product_id IN(150, 262, 264, 266, 334, 335, 341, 344, 345, 379,441,442,443,444)
  3090.           
  3091.         ';
  3092.         $params = [
  3093.             'pointOfSale' => $pointOfSale->getId(),
  3094.             'mois' => $mois,
  3095.             'annee' => $annee,
  3096.         ];
  3097.         if ($codeCluster) {
  3098.             $sql .= ' AND c.code_cluster = :codeCluster';
  3099.             $params['codeCluster'] = $codeCluster;
  3100.         }
  3101.         if ($departement) {
  3102.             $sql .= " AND c.code_cluster LIKE :departement";
  3103.             $params["departement"] = $departement '-%';
  3104.         }
  3105.         if ($codeInsee) {
  3106.             $sql .= ' AND p.code_insee = :codeInsee';
  3107.             $params['codeInsee'] = $codeInsee;
  3108.         }
  3109.         if ($organisationId) {
  3110.             $sql .= ' AND p.organisation_id = :organisationId';
  3111.             $params['organisationId'] = $organisationId;
  3112.         }
  3113.         if ($perid) {
  3114.             $sql .= ' AND p.login_vendeur_init = :perid';
  3115.             $params['perid'] = $perid;
  3116.         }
  3117.         if ($sellerId) {
  3118.             $sql .= ' AND p.seller_id = :sellerId';
  3119.             $params['sellerId'] = $sellerId;
  3120.         }
  3121.         if (is_array($childs) && count($childs) > 0) {
  3122.             $placeholders = [];
  3123.             foreach ($childs as $i => $child) {
  3124.                 $placeholder ':child_' $i;
  3125.                 $placeholders[] = $placeholder;
  3126.                 $params['child_' $i] = $child;
  3127.             }
  3128.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3129.         }
  3130.         switch ($optionSelect) {
  3131.             case 'V':
  3132.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3133.                 break;
  3134.             case 'R':
  3135.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3136.                 break;
  3137.             case 'B':
  3138.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3139.                 break;
  3140.         }
  3141.         $stmt $conn->prepare($sql);
  3142.         $result $stmt->executeQuery($params);
  3143.         return $result->fetchAssociative();
  3144.     }
  3145.     public function getProductionsAnalyticsTotalVentesByDateResiliationInf30Mobile($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$category$childs$organisationId$perid$sellerId$departement): array
  3146.     {
  3147.         $conn $this->getEntityManager()->getConnection();
  3148.         $sql '
  3149.         SELECT COUNT(DISTINCT p.id) AS total_vente
  3150.         FROM production p
  3151.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3152.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3153.         WHERE p.point_of_sale_id = :pointOfSale
  3154.           AND p.date_resiliation IS NOT NULL
  3155.           AND p.date_racc  IS NOT NULL
  3156.           AND DATEDIFF(p.date_racc , p.date_resiliation) > -31
  3157.           AND p.category_id IN(31,32,33,48) 
  3158.     ';
  3159.         $params = [
  3160.             'pointOfSale' => $pointOfSale->getId(),
  3161.             'mois' => $mois,
  3162.             'annee' => $annee
  3163.         ];
  3164.         if ($etat === "Raccorde_mobile") {
  3165.             $sql .= ' AND p.product_id NOT IN (150, 262, 264, 266, 334, 335, 341, 344, 345, 379,441,442,443,444)';
  3166.         }
  3167.         if ($codeCluster) {
  3168.             $sql .= ' AND c.code_cluster = :codeCluster';
  3169.             $params['codeCluster'] = $codeCluster;
  3170.         }
  3171.         if ($departement) {
  3172.             $sql .= " AND c.code_cluster LIKE :departement";
  3173.             $params["departement"] = $departement '-%';
  3174.         }
  3175.         if ($codeInsee) {
  3176.             $sql .= ' AND p.code_insee = :codeInsee';
  3177.             $params['codeInsee'] = $codeInsee;
  3178.         }
  3179.         if ($organisationId) {
  3180.             $sql .= ' AND p.organisation_id = :organisationId';
  3181.             $params['organisationId'] = $organisationId;
  3182.         }
  3183.         if ($perid) {
  3184.             $sql .= ' AND p.login_vendeur_init = :perid';
  3185.             $params['perid'] = $perid;
  3186.         }
  3187.         if ($sellerId) {
  3188.             $sql .= ' AND p.seller_id = :sellerId';
  3189.             $params['sellerId'] = $sellerId;
  3190.         }
  3191.         if (is_array($childs) && count($childs) > 0) {
  3192.             $placeholders = [];
  3193.             foreach ($childs as $i => $child) {
  3194.                 $placeholder ':child_' $i;
  3195.                 $placeholders[] = $placeholder;
  3196.                 $params['child_' $i] = $child;
  3197.             }
  3198.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3199.         }
  3200.         // if ($category) {
  3201.         //     $sql .= " AND p.category_id = :category";
  3202.         //     $params['category'] = $category;
  3203.         // }
  3204.         // // Ajout du filtre date selon $optionSelect
  3205.         switch ($optionSelect) {
  3206.             case 'V'// Vente
  3207.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3208.                 break;
  3209.             case 'R'// Raccordement
  3210.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3211.                 break;
  3212.             case 'B'// Commande
  3213.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3214.                 break;
  3215.         }
  3216.         $stmt $conn->prepare($sql);
  3217.         $result $stmt->executeQuery($params);
  3218.         return $result->fetchAssociative();
  3219.     }
  3220.     public function getProductionsAnalyticsTotalMobile($pointOfSale$codeCluster$mois$annee$optionSelect$filtre): array
  3221.     {
  3222.         $conn $this->getEntityManager()->getConnection();
  3223.         $sql '
  3224.         SELECT COUNT(DISTINCT p.id) AS total_vente
  3225.         FROM production p
  3226.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3227.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3228.         WHERE p.point_of_sale_id = :pointOfSale
  3229.           AND p.category_id IN (SELECT DISTINCT(id) FROM category_product WHERE parent_category_id =4)
  3230.     ';
  3231.         $params = [
  3232.             'pointOfSale' => $pointOfSale->getId(),
  3233.             'mois' => $mois,
  3234.             'annee' => $annee,
  3235.         ];
  3236.         if ($codeCluster) {
  3237.             $sql .= ' AND c.code_cluster = :codeCluster';
  3238.             $params['codeCluster'] = $codeCluster;
  3239.         }
  3240.         if ($filtre == "oui") {
  3241.             $sql .= "
  3242.                 AND (
  3243.                     p.fixe_chainage IS NOT NULL 
  3244.                     OR p.fixe_autre IS NOT NULL
  3245.                 )
  3246.             ";
  3247.         }
  3248.         $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3249.         $stmt $conn->prepare($sql);
  3250.         $result $stmt->executeQuery($params);
  3251.         return $result->fetchAssociative();
  3252.     }
  3253.     public function getProductionsAnalyticsTotalMobileV2($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$filtre$childs$organisationId$perid$sellerId$departement): array
  3254.     {
  3255.         $conn $this->getEntityManager()->getConnection();
  3256.         $sql '
  3257.         SELECT COUNT(DISTINCT p.titulaire_email) AS total_vente
  3258.         FROM production p
  3259.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3260.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3261.         WHERE p.point_of_sale_id = :pointOfSale
  3262.           AND p.category_id IN (SELECT DISTINCT(id) FROM category_product WHERE parent_category_id =4)
  3263.           AND (
  3264.             p.fixe_chainage IS NOT NULL 
  3265.             OR p.fixe_autre IS NOT NULL
  3266.         )
  3267.     ';
  3268.         $params = [
  3269.             'pointOfSale' => $pointOfSale->getId(),
  3270.             'mois' => $mois,
  3271.             'annee' => $annee
  3272.         ];
  3273.         if (is_array($childs) && count($childs) > 0) {
  3274.             $placeholders = [];
  3275.             foreach ($childs as $i => $child) {
  3276.                 $placeholder ':child_' $i;
  3277.                 $placeholders[] = $placeholder;
  3278.                 $params['child_' $i] = $child;
  3279.             }
  3280.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3281.         }
  3282.         if ($codeCluster) {
  3283.             $sql .= ' AND c.code_cluster = :codeCluster';
  3284.             $params['codeCluster'] = $codeCluster;
  3285.         }
  3286.         if ($departement) {
  3287.             $sql .= " AND c.code_cluster LIKE :departement";
  3288.             $params["departement"] = $departement '-%';
  3289.         }
  3290.         if ($codeInsee) {
  3291.             $sql .= ' AND p.code_insee = :codeInsee';
  3292.             $params['codeInsee'] = $codeInsee;
  3293.         }
  3294.         if ($organisationId) {
  3295.             $sql .= ' AND p.organisation_id = :organisationId';
  3296.             $params['organisationId'] = $organisationId;
  3297.         }
  3298.         if ($perid) {
  3299.             $sql .= ' AND p.login_vendeur_init = :perid';
  3300.             $params['perid'] = $perid;
  3301.         }
  3302.         if ($sellerId) {
  3303.             $sql .= ' AND p.seller_id = :sellerId';
  3304.             $params['sellerId'] = $sellerId;
  3305.         }
  3306.         // // Ajout du filtre date selon $optionSelect
  3307.         switch ($optionSelect) {
  3308.             case 'V'// Vente
  3309.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3310.                 break;
  3311.             case 'R'// Raccordement
  3312.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3313.                 break;
  3314.             case 'B'// Commande
  3315.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3316.                 break;
  3317.         }
  3318.         $stmt $conn->prepare($sql);
  3319.         $result $stmt->executeQuery($params);
  3320.         return $result->fetchAssociative();
  3321.     }
  3322.     public function getProductionsAnalyticsTotalMobileChainage($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$filtre$childs$organisationId$perid$sellerId$departement): array
  3323.     {
  3324.         $conn $this->getEntityManager()->getConnection();
  3325.         $sql '
  3326.         SELECT COUNT(DISTINCT p.titulaire_email) AS total_vente
  3327.         FROM production p
  3328.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3329.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3330.         WHERE p.point_of_sale_id = :pointOfSale
  3331.           AND p.category_id IN (SELECT DISTINCT(id) FROM category_product WHERE parent_category_id =4)
  3332.           AND (
  3333.             p.fixe_chainage IS NOT NULL 
  3334.         )
  3335.     ';
  3336.         $params = [
  3337.             'pointOfSale' => $pointOfSale->getId(),
  3338.             'mois' => $mois,
  3339.             'annee' => $annee
  3340.         ];
  3341.         if ($codeCluster) {
  3342.             $sql .= ' AND c.code_cluster = :codeCluster';
  3343.             $params['codeCluster'] = $codeCluster;
  3344.         }
  3345.         if ($departement) {
  3346.             $sql .= " AND c.code_cluster LIKE :departement";
  3347.             $params["departement"] = $departement '-%';
  3348.         }
  3349.         if ($codeInsee) {
  3350.             $sql .= ' AND p.code_insee = :codeInsee';
  3351.             $params['codeInsee'] = $codeInsee;
  3352.         }
  3353.         if ($organisationId) {
  3354.             $sql .= ' AND p.organisation_id = :organisationId';
  3355.             $params['organisationId'] = $organisationId;
  3356.         }
  3357.         if ($perid) {
  3358.             $sql .= ' AND p.login_vendeur_init = :perid';
  3359.             $params['perid'] = $perid;
  3360.         }
  3361.         if ($sellerId) {
  3362.             $sql .= ' AND p.seller_id = :sellerId';
  3363.             $params['sellerId'] = $sellerId;
  3364.         }
  3365.         if (is_array($childs) && count($childs) > 0) {
  3366.             $placeholders = [];
  3367.             foreach ($childs as $i => $child) {
  3368.                 $placeholder ':child_' $i;
  3369.                 $placeholders[] = $placeholder;
  3370.                 $params['child_' $i] = $child;
  3371.             }
  3372.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3373.         }
  3374.         // // Ajout du filtre date selon $optionSelect
  3375.         switch ($optionSelect) {
  3376.             case 'V'// Vente
  3377.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3378.                 break;
  3379.             case 'R'// Raccordement
  3380.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3381.                 break;
  3382.             case 'B'// Commande
  3383.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3384.                 break;
  3385.         }
  3386.         $stmt $conn->prepare($sql);
  3387.         $result $stmt->executeQuery($params);
  3388.         return $result->fetchAssociative();
  3389.     }
  3390.     public function getProductionsAnalyticsTotalClientsHosting($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$filtre$childs$organisationId$perid$sellerId$departement): array
  3391.     {
  3392.         $conn $this->getEntityManager()->getConnection();
  3393.         $sql '
  3394.         SELECT COUNT(DISTINCT p.titulaire_email) AS total_vente
  3395.         FROM production p
  3396.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3397.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3398.         WHERE p.point_of_sale_id = :pointOfSale
  3399.           AND p.category_id NOT IN (2)
  3400.           AND MONTH(p.date_cmd_a) = :mois
  3401.           AND YEAR(p.date_cmd_a) = :annee
  3402.     ';
  3403.         $params = [
  3404.             'pointOfSale' => $pointOfSale->getId(),
  3405.             'mois' => $mois,
  3406.             'annee' => $annee,
  3407.         ];
  3408.         if ($codeCluster) {
  3409.             $sql .= ' AND c.code_cluster = :codeCluster';
  3410.             $params['codeCluster'] = $codeCluster;
  3411.         }
  3412.         if ($departement) {
  3413.             $sql .= " AND c.code_cluster LIKE :departement";
  3414.             $params["departement"] = $departement '-%';
  3415.         }
  3416.         if ($codeInsee) {
  3417.             $sql .= ' AND p.code_insee = :codeInsee';
  3418.             $params['codeInsee'] = $codeInsee;
  3419.         }
  3420.         if ($organisationId) {
  3421.             $sql .= ' AND p.organisation_id = :organisationId';
  3422.             $params['organisationId'] = $organisationId;
  3423.         }
  3424.         if ($perid) {
  3425.             $sql .= ' AND p.login_vendeur_init = :perid';
  3426.             $params['perid'] = $perid;
  3427.         }
  3428.         if ($sellerId) {
  3429.             $sql .= ' AND p.seller_id = :sellerId';
  3430.             $params['sellerId'] = $sellerId;
  3431.         }
  3432.         if (is_array($childs) && count($childs) > 0) {
  3433.             $placeholders = [];
  3434.             foreach ($childs as $i => $child) {
  3435.                 $placeholder ':child_' $i;
  3436.                 $placeholders[] = $placeholder;
  3437.                 $params['child_' $i] = $child;
  3438.             }
  3439.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3440.         }
  3441.         $stmt $conn->prepare($sql);
  3442.         $result $stmt->executeQuery($params);
  3443.         return $result->fetchAssociative();
  3444.     }
  3445.     public function getProductionsReselies($pointOfSale$codeCluster$mois$annee$optionSelect$page)
  3446.     {
  3447.         $conn $this->getEntityManager()->getConnection();
  3448.         $limit 30;
  3449.         $offset = ($page 1) * $limit;
  3450.         $sql '
  3451.             SELECT *
  3452.             FROM production p
  3453.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3454.             LEFT JOIN cluster c ON p.cluster_id = c.id
  3455.             WHERE 
  3456.                p.date_resiliation IS NOT NULL
  3457.                AND p.date_racc IS NOT NULL
  3458.                AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  3459.                AND p.category_id NOT IN (2, 31, 32, 33)
  3460.         ';
  3461.         $params = [];
  3462.         if ($pointOfSale && $pointOfSale->getId()) {
  3463.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  3464.             $params["pointOfSale"] = $pointOfSale->getId();
  3465.         }
  3466.         if ($codeCluster) {
  3467.             $sql .= ' AND c.code_cluster = :codeCluster';
  3468.             $params['codeCluster'] = $codeCluster;
  3469.         }
  3470.         switch ($optionSelect) {
  3471.             case 'V':
  3472.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3473.                 $params['mois'] = $mois;
  3474.                 $params['annee'] = $annee;
  3475.                 break;
  3476.             case 'R':
  3477.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3478.                 $params['mois'] = $mois;
  3479.                 $params['annee'] = $annee;
  3480.                 break;
  3481.             case 'B':
  3482.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3483.                 $params['mois'] = $mois;
  3484.                 $params['annee'] = $annee;
  3485.                 break;
  3486.         }
  3487.         // $sql .= ' LIMIT :limit OFFSET :offset';
  3488.         $stmt $conn->prepare($sql);
  3489.         if (isset($params['pointOfSale'])) {
  3490.             $stmt->bindValue('pointOfSale'$params['pointOfSale'], \PDO::PARAM_INT);
  3491.         }
  3492.         if (isset($params['codeCluster'])) {
  3493.             $stmt->bindValue('codeCluster'$params['codeCluster']);
  3494.         }
  3495.         if (isset($params['mois'])) {
  3496.             $stmt->bindValue('mois'$params['mois'], \PDO::PARAM_INT);
  3497.         }
  3498.         if (isset($params['annee'])) {
  3499.             $stmt->bindValue('annee'$params['annee'], \PDO::PARAM_INT);
  3500.         }
  3501.         // $stmt->bindValue('limit', $limit, \PDO::PARAM_INT);
  3502.         // $stmt->bindValue('offset', $offset, \PDO::PARAM_INT);
  3503.         $result $stmt->executeQuery();
  3504.         return $result->fetchAllAssociative();
  3505.     }
  3506.     public function getProductionsReseliesMobile($pointOfSale$codeCluster$mois$annee$optionSelect$page)
  3507.     {
  3508.         $conn $this->getEntityManager()->getConnection();
  3509.         $limit 30;
  3510.         $offset = ($page 1) * $limit;
  3511.         $sql '
  3512.         SELECT *
  3513.         FROM production p
  3514.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3515.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3516.         WHERE 
  3517.            p.date_resiliation IS NOT NULL
  3518.            AND p.date_racc IS NOT NULL
  3519.            AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  3520.            AND p.category_id IN (31, 32, 33)
  3521.     ';
  3522.         $params = [];
  3523.         if ($pointOfSale && $pointOfSale->getId()) {
  3524.             $sql .= ' AND p.point_of_sale_id = :pointOfSale';
  3525.             $params['pointOfSale'] = $pointOfSale->getId();
  3526.         }
  3527.         if ($codeCluster) {
  3528.             $sql .= ' AND c.code_cluster = :codeCluster';
  3529.             $params['codeCluster'] = $codeCluster;
  3530.         }
  3531.         switch ($optionSelect) {
  3532.             case 'V':
  3533.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3534.                 $params['mois'] = $mois;
  3535.                 $params['annee'] = $annee;
  3536.                 break;
  3537.             case 'R':
  3538.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3539.                 $params['mois'] = $mois;
  3540.                 $params['annee'] = $annee;
  3541.                 break;
  3542.             case 'B':
  3543.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3544.                 $params['mois'] = $mois;
  3545.                 $params['annee'] = $annee;
  3546.                 break;
  3547.         }
  3548.         // $sql .= ' LIMIT :limit OFFSET :offset';
  3549.         $stmt $conn->prepare($sql);
  3550.         if (isset($params['pointOfSale'])) {
  3551.             $stmt->bindValue('pointOfSale'$params['pointOfSale'], \PDO::PARAM_INT);
  3552.         }
  3553.         if (isset($params['codeCluster'])) {
  3554.             $stmt->bindValue('codeCluster'$params['codeCluster']);
  3555.         }
  3556.         if (isset($params['mois'])) {
  3557.             $stmt->bindValue('mois'$params['mois'], \PDO::PARAM_INT);
  3558.         }
  3559.         if (isset($params['annee'])) {
  3560.             $stmt->bindValue('annee'$params['annee'], \PDO::PARAM_INT);
  3561.         }
  3562.         // $stmt->bindValue('limit', $limit, \PDO::PARAM_INT);
  3563.         // $stmt->bindValue('offset', $offset, \PDO::PARAM_INT);
  3564.         $result $stmt->executeQuery();
  3565.         return $result->fetchAllAssociative();
  3566.     }
  3567.     public function getMobileDetails($pointOfSale$cluster$codeInsee$optionSelect$dateDebut$dateFin$childs$organisationId)
  3568.     {
  3569.         $qb $this->getEntityManager()->createQueryBuilder();
  3570.         $qb->select('p')
  3571.             ->from('App\Entity\Production''p');
  3572.         if ($pointOfSale !== null) {
  3573.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  3574.                 ->setParameter('pointOfSale'$pointOfSale);
  3575.         }
  3576.         switch ($optionSelect) {
  3577.             case 'V':
  3578.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') >= :debut');
  3579.                 $qb->andWhere('DATE_FORMAT(p.dateVenteValidB, \'%Y-%m-%d\') <= :fin');
  3580.                 break;
  3581.             case 'R':
  3582.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') >= :debut');
  3583.                 $qb->andWhere('DATE_FORMAT(p.dateRacc, \'%Y-%m-%d\') <= :fin');
  3584.                 break;
  3585.             case 'B':
  3586.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') >= :debut');
  3587.                 $qb->andWhere('DATE_FORMAT(p.dateCmdA, \'%Y-%m-%d\') <= :fin');
  3588.                 break;
  3589.         }
  3590.         $qb->setParameter('debut'$dateDebut)
  3591.             ->setParameter('fin'$dateFin);
  3592.         if ($cluster) {
  3593.             $qb->andWhere('p.cluster = :cluster')
  3594.                 ->setParameter('cluster'$cluster);
  3595.         }
  3596.         if ($codeInsee) {
  3597.             $qb->andWhere('p.codeInsee = :codeInsee')
  3598.                 ->setParameter('codeInsee'$codeInsee);
  3599.         }
  3600.         if ($organisationId) {
  3601.             $qb->andWhere('p.organisation = :organisationId')
  3602.                 ->setParameter('organisationId'$organisationId);
  3603.         }
  3604.         if (is_array($childs) && count($childs) > 0) {
  3605.             $qb->andWhere('p.seller IN (:childs)')
  3606.                 ->leftJoin('p.seller''s')
  3607.                 ->setParameter('childs'$childs);
  3608.         }
  3609.         $qb->andWhere('p.fixeAutre IS NOT NULL OR p.fixeChainage IS NOT NULL');
  3610.         $qb->join('p.category''c')
  3611.             ->andWhere('c.parentCategory = :parentCategory')
  3612.             ->setParameter('parentCategory'4);
  3613.         return $qb->getQuery()->getArrayResult();
  3614.     }
  3615.     public function getProductionsAnalyticsTotalByIdentityCtrl($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$filtre$childs$organisationId$perid$sellerId$departement)
  3616.     {
  3617.         $conn $this->getEntityManager()->getConnection();
  3618.         $sql '
  3619.         SELECT COUNT(p.id) AS total_vente, p.identity_ctrl
  3620.         FROM production p
  3621.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3622.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3623.         WHERE p.point_of_sale_id = :pointOfSale
  3624.           AND p.identity_ctrl IS NOT NULL 
  3625.          
  3626.     ';
  3627.         $params = [
  3628.             'pointOfSale' => $pointOfSale->getId(),
  3629.             'mois' => $mois,
  3630.             'annee' => $annee
  3631.         ];
  3632.         if (is_array($childs) && count($childs) > 0) {
  3633.             $placeholders = [];
  3634.             foreach ($childs as $i => $child) {
  3635.                 $placeholder ':child_' $i;
  3636.                 $placeholders[] = $placeholder;
  3637.                 $params['child_' $i] = $child;
  3638.             }
  3639.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  3640.         }
  3641.         if ($codeCluster) {
  3642.             $sql .= ' AND c.code_cluster = :codeCluster';
  3643.             $params['codeCluster'] = $codeCluster;
  3644.         }
  3645.         if ($departement) {
  3646.             $sql .= " AND c.code_cluster LIKE :departement";
  3647.             $params["departement"] = $departement '-%';
  3648.         }
  3649.         if ($codeInsee) {
  3650.             $sql .= ' AND p.code_insee = :codeInsee';
  3651.             $params['codeInsee'] = $codeInsee;
  3652.         }
  3653.         if ($organisationId) {
  3654.             $sql .= ' AND p.organisation_id = :organisationId';
  3655.             $params['organisationId'] = $organisationId;
  3656.         }
  3657.         if ($perid) {
  3658.             $sql .= ' AND p.login_vendeur_init = :perid';
  3659.             $params['perid'] = $perid;
  3660.         }
  3661.         if ($sellerId) {
  3662.             $sql .= ' AND p.seller_id = :sellerId';
  3663.             $params['sellerId'] = $sellerId;
  3664.         }
  3665.         switch ($optionSelect) {
  3666.             case 'V':
  3667.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3668.                 $params['mois'] = $mois;
  3669.                 $params['annee'] = $annee;
  3670.                 break;
  3671.             case 'R':
  3672.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3673.                 $params['mois'] = $mois;
  3674.                 $params['annee'] = $annee;
  3675.                 break;
  3676.             case 'B':
  3677.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3678.                 $params['mois'] = $mois;
  3679.                 $params['annee'] = $annee;
  3680.                 break;
  3681.         }
  3682.         $sql .= ' GROUP BY p.identity_ctrl ';
  3683.         $stmt $conn->prepare($sql);
  3684.         $result $stmt->executeQuery($params);
  3685.         return $result->fetchAllAssociative();
  3686.     }
  3687.     public function getProductionsDetailsByIdentityCtrl($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$identityCtrl$page)
  3688.     {
  3689.         $limit 30;
  3690.         $offset = ($page 1) * $limit;
  3691.         $conn $this->getEntityManager()->getConnection();
  3692.         $sql '
  3693.         SELECT *
  3694.         FROM production p
  3695.         LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  3696.         LEFT JOIN cluster c ON p.cluster_id = c.id
  3697.         WHERE p.point_of_sale_id = :pointOfSale
  3698.          
  3699.     ';
  3700.         $params = [
  3701.             'pointOfSale' => $pointOfSale->getId(),
  3702.             'mois' => $mois,
  3703.             'annee' => $annee,
  3704.         ];
  3705.         if ($identityCtrl) {
  3706.             $sql .= ' AND p.identity_ctrl = :identityCtrl';
  3707.             $params['identityCtrl'] = $identityCtrl;
  3708.         }
  3709.         if ($codeCluster) {
  3710.             $sql .= ' AND c.code_cluster = :codeCluster';
  3711.             $params['codeCluster'] = $codeCluster;
  3712.         }
  3713.         if ($codeInsee) {
  3714.             $sql .= ' AND p.code_insee = :codeInsee';
  3715.             $params['codeInsee'] = $codeInsee;
  3716.         }
  3717.         switch ($optionSelect) {
  3718.             case 'V':
  3719.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  3720.                 break;
  3721.             case 'R':
  3722.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  3723.                 break;
  3724.             case 'B':
  3725.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  3726.                 break;
  3727.         }
  3728.         $sql .= ' ORDER BY p.id DESC ';
  3729.         // // Ajouter les paramètres de pagination
  3730.         // $params['limit'] = $limit;
  3731.         // $params['offset'] = $offset;
  3732.         // Préparer la requête
  3733.         $stmt $conn->prepare($sql);
  3734.         // Liaison explicite des paramètres LIMIT/OFFSET pour éviter les erreurs PDO
  3735.         $stmt->bindValue('pointOfSale'$params['pointOfSale']);
  3736.         if ($identityCtrl) {
  3737.             $stmt->bindValue('identityCtrl'$params['identityCtrl']);
  3738.         }
  3739.         if (isset($params['codeCluster'])) {
  3740.             $stmt->bindValue('codeCluster'$params['codeCluster']);
  3741.         }
  3742.         if (isset($params['codeInsee'])) {
  3743.             $stmt->bindValue('codeInsee'$params['codeInsee']);
  3744.         }
  3745.         $stmt->bindValue('mois'$params['mois']);
  3746.         $stmt->bindValue('annee'$params['annee']);
  3747.         // $stmt->bindValue('limit', $params['limit'], \PDO::PARAM_INT);
  3748.         // $stmt->bindValue('offset', $params['offset'], \PDO::PARAM_INT);
  3749.         $result $stmt->executeQuery();
  3750.         return $result->fetchAllAssociative();
  3751.     }
  3752.     public function getProductionsAnalyticsTotalClientsUnique($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etatKO$category$option$childs$organisationId$perid$sellerId$departement): array
  3753.     {
  3754.         $qb $this->createBaseQueryBuilder2($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$departement)
  3755.             ->select('COUNT(DISTINCT p.titulaireEmail) as total_client_unique');
  3756.         if (is_array($childs) && count($childs) > 0) {
  3757.             $qb->andWhere('p.seller IN (:childs)')
  3758.                 ->leftJoin('p.seller''s')
  3759.                 ->setParameter('childs'$childs);
  3760.         }
  3761.         if ($organisationId) {
  3762.             $qb->andWhere('p.organisation = :organisationId')
  3763.                 ->setParameter('organisationId'$organisationId);
  3764.         }
  3765.         if ($perid) {
  3766.             $qb->andWhere('p.loginVendeurInit = :perid ')
  3767.                 ->setParameter('perid'$perid);
  3768.         }
  3769.         if ($sellerId) {
  3770.             $qb->andWhere('p.seller = :sellerId')
  3771.                 ->setParameter('sellerId'$sellerId);
  3772.         }
  3773.         return $qb->getQuery()->getArrayResult();
  3774.     }
  3775.     public function getProductionsAnalyticsTotal4P($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$etat$filtre$childs$organisationId$perid$sellerId$departement): array
  3776.     {
  3777.         $conn $this->getEntityManager()->getConnection();
  3778.         $params = [
  3779.             'pointOfSale' => $pointOfSale->getId(),
  3780.             'mois' => $mois,
  3781.             'annee' => $annee,
  3782.             'cat1' => 32,
  3783.             'cat2' => 31
  3784.         ];
  3785.         $sql '
  3786.             SELECT COUNT(DISTINCT result.titulaire_email) AS total_vente
  3787.             FROM (
  3788.                 SELECT p1.titulaire_email
  3789.                 FROM production p1
  3790.                 LEFT JOIN cluster c1 ON c1.id=p1.cluster_id
  3791.                 WHERE p1.category_id = :cat1
  3792.                   AND p1.point_of_sale_id = :pointOfSale
  3793.                   AND MONTH(p1.date_cmd_a) = :mois
  3794.                   AND YEAR(p1.date_cmd_a) = :annee
  3795.                   AND p1.titulaire_email IN (
  3796.                       SELECT DISTINCT p_sub1.titulaire_email
  3797.                       FROM production p_sub1
  3798.                       WHERE p_sub1.category_id = 1
  3799.                         AND p_sub1.point_of_sale_id = :pointOfSale
  3800.                         AND MONTH(p_sub1.date_cmd_a) = :mois
  3801.                         AND YEAR(p_sub1.date_cmd_a) = :annee
  3802.                   )
  3803.         ';
  3804.         if ($organisationId) {
  3805.             $sql .= ' AND p1.organisation_id = :organisationId';
  3806.             $params['organisationId'] = $organisationId;
  3807.         }
  3808.         if ($codeCluster) {
  3809.             $sql .= " AND c1.code_cluster = :codeCluster";
  3810.             $params["codeCluster"] = $codeCluster;
  3811.         }
  3812.         if ($departement) {
  3813.             $sql .= " AND c1.code_cluster LIKE :departement";
  3814.             $params["departement"] = $departement '-%';
  3815.         }
  3816.         if ($codeInsee) {
  3817.             $sql .= " AND p1.code_insee = :codeInsee";
  3818.             $params["codeInsee"] = $codeInsee;
  3819.         }
  3820.         if ($perid) {
  3821.             $sql .= " AND p1.login_vendeur_init = :perid";
  3822.             $params["perid"] = $perid;
  3823.         }
  3824.         if ($sellerId) {
  3825.             $sql .= ' AND p1.seller_id = :sellerId';
  3826.             $params['sellerId'] = $sellerId;
  3827.         }
  3828.         if (is_array($childs) && count($childs) > 0) {
  3829.             $placeholders = [];
  3830.             foreach ($childs as $i => $child) {
  3831.                 $placeholder ':child_' $i;
  3832.                 $placeholders[] = $placeholder;
  3833.                 $params['child_' $i] = $child;
  3834.             }
  3835.             $sql .= ' AND p1.seller_id IN (' implode(', '$placeholders) . ')';
  3836.         }
  3837.         $sql .= '
  3838.                 UNION
  3839.                 SELECT p2.titulaire_email
  3840.                 FROM production p2
  3841.                 LEFT JOIN cluster c2 ON c2.id=p2.cluster_id
  3842.                 WHERE p2.category_id = :cat2
  3843.                   AND p2.point_of_sale_id = :pointOfSale
  3844.                   AND MONTH(p2.date_cmd_a) = :mois
  3845.                   AND YEAR(p2.date_cmd_a) = :annee
  3846.                   AND p2.titulaire_email IN (
  3847.                       SELECT DISTINCT p_sub2.titulaire_email
  3848.                       FROM production p_sub2
  3849.                       WHERE p_sub2.category_id IN (1, 3)
  3850.                         AND p_sub2.point_of_sale_id = :pointOfSale
  3851.                         AND MONTH(p_sub2.date_cmd_a) = :mois
  3852.                         AND YEAR(p_sub2.date_cmd_a) = :annee
  3853.                   )
  3854.         ';
  3855.         if ($organisationId) {
  3856.             $sql .= ' AND p2.organisation_id = :organisationId';
  3857.         }
  3858.         if ($codeCluster) {
  3859.             $sql .= " AND c2.code_cluster = :codeCluster";
  3860.         }
  3861.         if ($departement) {
  3862.             $sql .= " AND c2.code_cluster LIKE :departement";
  3863.         }
  3864.         if ($perid) {
  3865.             $sql .= ' AND p2.login_vendeur_init = :perid';
  3866.         }
  3867.         if ($sellerId) {
  3868.             $sql .= ' AND p2.seller_id = :sellerId';
  3869.         }
  3870.         if ($codeInsee) {
  3871.             $sql .= ' AND p2.code_insee = :codeInsee';
  3872.         }
  3873.         if (is_array($childs) && count($childs) > 0) {
  3874.             $placeholders = [];
  3875.             foreach ($childs as $i => $child) {
  3876.                 $placeholder ':child_' $i;
  3877.                 $placeholders[] = $placeholder;
  3878.             }
  3879.             $sql .= ' AND p2.seller_id IN (' implode(', '$placeholders) . ')';
  3880.         }
  3881.         $sql .= ') AS result';
  3882.         $stmt $conn->prepare($sql);
  3883.         $result $stmt->executeQuery($params);
  3884.         return $result->fetchAssociative();
  3885.     }
  3886.     public function getDetails4P($pointOfSale$codeCluster$codeInsee$mois$annee$childs$organisationId$perid$sellerId): array
  3887.     {
  3888.         $conn $this->getEntityManager()->getConnection();
  3889.         $params = [
  3890.             'pointOfSale' => $pointOfSale->getId(),
  3891.             'mois' => $mois,
  3892.             'annee' => $annee,
  3893.             'cat1' => 32,
  3894.             'cat2' => 31,
  3895.         ];
  3896.         $sql '
  3897.         SELECT result.*
  3898.         FROM (
  3899.             SELECT p1.*
  3900.             FROM production p1
  3901.             LEFT JOIN cluster c1 ON p1.cluster_id = c1.id
  3902.             WHERE p1.category_id = :cat1
  3903.               AND p1.point_of_sale_id = :pointOfSale
  3904.               AND MONTH(p1.date_cmd_a) = :mois
  3905.               AND YEAR(p1.date_cmd_a) = :annee
  3906.               AND p1.titulaire_email IN (
  3907.                   SELECT p_sub1.titulaire_email
  3908.                   FROM production p_sub1
  3909.                   WHERE p_sub1.category_id = 1
  3910.                     AND p_sub1.point_of_sale_id = :pointOfSale
  3911.                     AND MONTH(p_sub1.date_cmd_a) = :mois
  3912.                     AND YEAR(p_sub1.date_cmd_a) = :annee
  3913.               )
  3914.     ';
  3915.         if ($organisationId) {
  3916.             $sql .= ' AND p1.organisation_id = :organisationId';
  3917.             $params['organisationId'] = $organisationId;
  3918.         }
  3919.         if ($codeCluster) {
  3920.             $sql .= ' AND c1.code_cluster = :codeCluster';
  3921.             $params['codeCluster'] = $codeCluster;
  3922.         }
  3923.         if ($perid) {
  3924.             $sql .= ' AND p1.login_vendeur_init = :perid';
  3925.             $params['perid'] = $perid;
  3926.         }
  3927.         if ($sellerId) {
  3928.             $sql .= ' AND p1.seller_id = :sellerId';
  3929.             $params['sellerId'] = $sellerId;
  3930.         }
  3931.         if ($codeInsee) {
  3932.             $sql .= ' AND p1.code_insee = :codeInsee';
  3933.             $params['codeInsee'] = $codeInsee;
  3934.         }
  3935.         if (is_array($childs) && count($childs) > 0) {
  3936.             $placeholders = [];
  3937.             foreach ($childs as $i => $child) {
  3938.                 $placeholder ':child_' $i;
  3939.                 $placeholders[] = $placeholder;
  3940.                 $params[$placeholder] = $child;
  3941.             }
  3942.             $sql .= ' AND p1.seller_id IN (' implode(', '$placeholders) . ')';
  3943.         }
  3944.         $sql .= '
  3945.             UNION
  3946.             SELECT p2.*
  3947.             FROM production p2
  3948.             LEFT JOIN cluster c2 ON p2.cluster_id = c2.id
  3949.             WHERE p2.category_id = :cat2
  3950.               AND p2.point_of_sale_id = :pointOfSale
  3951.               AND MONTH(p2.date_cmd_a) = :mois
  3952.               AND YEAR(p2.date_cmd_a) = :annee
  3953.               AND p2.titulaire_email IN (
  3954.                   SELECT p_sub2.titulaire_email
  3955.                   FROM production p_sub2
  3956.                   WHERE p_sub2.category_id IN (1, 3)
  3957.                     AND p_sub2.point_of_sale_id = :pointOfSale
  3958.                     AND MONTH(p_sub2.date_cmd_a) = :mois
  3959.                     AND YEAR(p_sub2.date_cmd_a) = :annee
  3960.               )
  3961.     ';
  3962.         if ($organisationId) {
  3963.             $sql .= ' AND p2.organisation_id = :organisationId';
  3964.         }
  3965.         if ($codeCluster) {
  3966.             $sql .= ' AND c2.code_cluster = :codeCluster';
  3967.         }
  3968.         if ($perid) {
  3969.             $sql .= ' AND p2.login_vendeur_init = :perid';
  3970.         }
  3971.         if ($sellerId) {
  3972.             $sql .= ' AND p2.seller_id = :sellerId';
  3973.         }
  3974.         if ($codeInsee) {
  3975.             $sql .= ' AND p2.code_insee = :codeInsee';
  3976.         }
  3977.         if (is_array($childs) && count($childs) > 0) {
  3978.             $placeholders = [];
  3979.             foreach ($childs as $i => $child) {
  3980.                 $placeholder ':child_' $i;
  3981.                 $placeholders[] = $placeholder;
  3982.                 $params[$placeholder] = $child;
  3983.             }
  3984.             $sql .= ' AND p2.seller_id IN (' implode(', '$placeholders) . ')';
  3985.         }
  3986.         $sql .= ') AS result';
  3987.         $stmt $conn->prepare($sql);
  3988.         $result $stmt->executeQuery($params);
  3989.         // retourne tous les enregistrements (sinon tu n'auras qu’un seul résultat)
  3990.         return $result->fetchAllAssociative();
  3991.     }
  3992.     public function findDistinctTech(): array
  3993.     {
  3994.         return $this->createQueryBuilder('p')
  3995.             ->select('DISTINCT p.tech')
  3996.             ->where('p.tech IS NOT NULL')
  3997.             ->getQuery()
  3998.             ->getResult();
  3999.     }
  4000.     public function filterProductions(
  4001.         $periode null,
  4002.         $pointOfSaleId null,
  4003.         $categoryId null,
  4004.         $technologie null,
  4005.         $clusterId null,
  4006.         $optionSelect 'B'
  4007.     ) {
  4008.         $conn $this->getEntityManager()->getConnection();
  4009.         // Choix de la colonne de date en fonction de $optionSelect
  4010.         switch ($optionSelect) {
  4011.             case 'V':
  4012.                 $dateField 'date_vente_valid_b';
  4013.                 break;
  4014.             case 'R':
  4015.                 $dateField 'date_racc';
  4016.                 break;
  4017.             case 'B':
  4018.             default:
  4019.                 $dateField 'date_cmd_a';
  4020.                 break;
  4021.         }
  4022.         // Base SQL
  4023.         $sql "SELECT COUNT(p.id) AS total_vente";
  4024.         // Ajout des regroupements par période
  4025.         switch ($periode) {
  4026.             case 'jour':
  4027.                 $sql .= ", DATE(p.$dateField) AS jour";
  4028.                 $groupBy "GROUP BY jour";
  4029.                 break;
  4030.             case 'semaine':
  4031.                 $sql .= ", YEAR(p.$dateField) AS annee, WEEK(p.$dateField, 1) AS semaine";
  4032.                 $groupBy "GROUP BY annee, semaine";
  4033.                 break;
  4034.             case 'mois':
  4035.                 $sql .= ", YEAR(p.$dateField) AS annee, MONTH(p.$dateField) AS mois";
  4036.                 $groupBy "GROUP BY annee, mois";
  4037.                 break;
  4038.             case 'annee':
  4039.                 $sql .= ", YEAR(p.$dateField) AS annee";
  4040.                 $groupBy "GROUP BY annee";
  4041.                 break;
  4042.             default:
  4043.                 $groupBy "";
  4044.         }
  4045.         // Suite de la requête
  4046.         $sql .= " FROM production p WHERE 1=1";
  4047.         $params = [];
  4048.         if ($pointOfSaleId) {
  4049.             $sql .= " AND p.point_of_sale_id = :pointOfSaleId";
  4050.             $params['pointOfSaleId'] = $pointOfSaleId;
  4051.         }
  4052.         if ($clusterId) {
  4053.             $sql .= " AND p.cluster_id = :clusterId";
  4054.             $params['clusterId'] = $clusterId;
  4055.         }
  4056.         if ($categoryId) {
  4057.             $sql .= " AND p.category_id = :categoryId";
  4058.             $params['categoryId'] = $categoryId;
  4059.         }
  4060.         if ($technologie) {
  4061.             $sql .= " AND p.tech LIKE :technologie";
  4062.             $params['technologie'] = '%' $technologie '%';
  4063.         }
  4064.         if ($groupBy) {
  4065.             $sql .= " " $groupBy;
  4066.         }
  4067.         $stmt $conn->prepare($sql);
  4068.         $resultSet $stmt->executeQuery($params);
  4069.         return $resultSet->fetchAllAssociative();
  4070.     }
  4071.     public function getProductionsAnalyticsTotalVenteByOption(
  4072.         $pointOfSale,
  4073.         ?string $codeCluster,
  4074.         $codeInsee,
  4075.         int $mois,
  4076.         int $annee,
  4077.         array $childs,
  4078.         ?int $organisationId,
  4079.         ?string $optionCalcul,
  4080.         ?string $perid,
  4081.         $sellerId,
  4082.         $departement
  4083.     ): array {
  4084.         $conn $this->getEntityManager()->getConnection();
  4085.         $sql '
  4086.             SELECT COUNT(DISTINCT p.id) AS total_vente
  4087.             FROM production p
  4088.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4089.             LEFT JOIN cluster c ON c.id =p.cluster_id
  4090.         ';
  4091.         $sql .= '
  4092.             WHERE p.point_of_sale_id = :pointOfSale
  4093.               AND p.category_id IN (1,3)
  4094.               AND MONTH(p.date_cmd_a) = :mois
  4095.               AND YEAR(p.date_cmd_a) = :annee
  4096.         ';
  4097.         $params = [
  4098.             'pointOfSale' => $pointOfSale->getId(),
  4099.             'mois' => $mois,
  4100.             'annee' => $annee,
  4101.         ];
  4102.         if ($organisationId) {
  4103.             $sql .= ' AND p.organisation_id = :organisationId';
  4104.             $params['organisationId'] = $organisationId;
  4105.         }
  4106.         if (!empty($childs)) {
  4107.             $placeholders = [];
  4108.             foreach ($childs as $i => $child) {
  4109.                 $placeholder ':child_' $i;
  4110.                 $placeholders[] = $placeholder;
  4111.                 $params['child_' $i] = $child;
  4112.             }
  4113.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  4114.         }
  4115.         if ($codeInsee) {
  4116.             $sql .= ' AND p.code_insee = :codeInsee';
  4117.             $params['codeInsee'] = $codeInsee;
  4118.         }
  4119.         if ($codeCluster) {
  4120.             $sql .= ' AND c.code_cluster = :codeCluster';
  4121.             $params['codeCluster'] = $codeCluster;
  4122.         }
  4123.         if ($departement) {
  4124.             $sql .= " AND c.code_cluster LIKE :departement";
  4125.             $params["departement"] = $departement '-%';
  4126.         }
  4127.         if ($perid) {
  4128.             $sql .= ' AND p.login_vendeur_init = :perid';
  4129.             $params['perid'] = $perid;
  4130.         }
  4131.         if ($sellerId) {
  4132.             $sql .= ' AND p.seller_id = :sellerId';
  4133.             $params['sellerId'] = $sellerId;
  4134.         }
  4135.         if ($optionCalcul) {
  4136.             switch ($optionCalcul) {
  4137.                 case 'RIO':
  4138.                     $sql .= ' AND p.portabilite = :opt';
  4139.                     $params['opt'] = 'OUI';
  4140.                     break;
  4141.                 case 'PTO_SAISIE':
  4142.                     $sql .= ' AND p.prise_saisie_commande = :opt';
  4143.                     $params['opt'] = 'OUI';
  4144.                     break;
  4145.                 case 'PTO_NON_SAISIE':
  4146.                     $sql .= ' AND p.prise_saisie_commande = :opt AND p.prise_existante_commande = :opt2';
  4147.                     $params['opt'] = 'NON';
  4148.                     $params['opt2'] = 'OUI';
  4149.                     break;
  4150.                 case 'PTO_EXISTANTE':
  4151.                     $sql .= ' AND p.prise_existante_commande = :opt';
  4152.                     $params['opt'] = 'OUI';
  4153.                     break;
  4154.                 case 'PTO_NON_EXISTANTE':
  4155.                     $sql .= ' AND p.prise_existante_commande = :opt';
  4156.                     $params['opt'] = 'NON';
  4157.                     break;
  4158.             }
  4159.         }
  4160.         $stmt $conn->prepare($sql);
  4161.         $result $stmt->executeQuery($params);
  4162.         return $result->fetchAssociative();
  4163.     }
  4164.     /*******DASHBOARD *******/
  4165.     public function getTotalVentesByPeriod(
  4166.         $pointOfSale,
  4167.         $codeCluster,
  4168.         $departement,
  4169.         $codeInsee,
  4170.         int $annee,
  4171.         string $optionSelect,
  4172.         $etat,
  4173.         $category,
  4174.         $childs,
  4175.         $organisationId,
  4176.         $perid,
  4177.         string $periode 'month' // 'day', 'week', 'month'
  4178.     ): array {
  4179.         $conn $this->getEntityManager()->getConnection();
  4180.         // Choisir la colonne date selon optionSelect
  4181.         switch ($optionSelect) {
  4182.             case 'R':
  4183.                 $dateCol 'p.date_racc';
  4184.                 break;
  4185.             case 'B':
  4186.                 $dateCol 'p.date_cmd_a';
  4187.                 break;
  4188.             case 'V':
  4189.             default:
  4190.                 $dateCol 'p.date_vente_valid_b';
  4191.         }
  4192.         // Déterminer la colonne de regroupement selon la période
  4193.         switch ($periode) {
  4194.             case 'day':
  4195.                 $groupCol "DATE($dateCol)";
  4196.                 $selectCol "DATE($dateCol) AS periode";
  4197.                 $currentMonth = (int)date('m');
  4198.                 $previousMonth $currentMonth === 12 $currentMonth 1;
  4199.                 $monthCondition " AND MONTH($dateCol) IN (:currentMonth, :previousMonth)";
  4200.                 break;
  4201.             case 'week':
  4202.                 $groupCol "WEEK($dateCol, 1)";
  4203.                 $selectCol "WEEK($dateCol, 1) AS periode";
  4204.                 $monthCondition "";
  4205.                 break;
  4206.             case 'month':
  4207.             default:
  4208.                 $groupCol "MONTH($dateCol)";
  4209.                 $selectCol "MONTH($dateCol) AS periode";
  4210.                 $monthCondition "";
  4211.         }
  4212.         // --- Construction SQL ---
  4213.         $sql "
  4214.             SELECT
  4215.                 $selectCol,
  4216.                 COUNT(p.id) AS total_ventes
  4217.         ";
  4218.         // Si organisationId existe, on ajoute po.code et po.name
  4219.         if ($organisationId) {
  4220.             $sql .= ",
  4221.                 po.code AS code_point,
  4222.                 po.name AS nom_point
  4223.             ";
  4224.         }
  4225.         $sql .= "
  4226.             FROM production p
  4227.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4228.             LEFT JOIN cluster c ON p.cluster_id = c.id
  4229.             WHERE YEAR($dateCol) = :annee
  4230.         ";
  4231.         $params = ['annee' => $annee];
  4232.         if ($organisationId) {
  4233.             $sql .= " AND p.organisation_id = :organisationId";
  4234.             $params['organisationId'] = $organisationId;
  4235.         } else {
  4236.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  4237.             $params['pointOfSale'] = $pointOfSale->getId();
  4238.         }
  4239.         if ($codeCluster) {
  4240.             $sql .= " AND c.code_cluster = :codeCluster";
  4241.             $params['codeCluster'] = $codeCluster;
  4242.         }
  4243.         if ($departement) {
  4244.             $sql .= " AND c.code_cluster LIKE :prefix";
  4245.             $params['prefix'] = $departement '-%';
  4246.         }
  4247.         if ($codeInsee) {
  4248.             $sql .= " AND p.code_insee = :codeInsee";
  4249.             $params['codeInsee'] = $codeInsee;
  4250.         }
  4251.         if ($perid) {
  4252.             $sql .= " AND p.login_vendeur_init = :perid";
  4253.             $params['perid'] = $perid;
  4254.         }
  4255.         if ($etat === "Raccorde") {
  4256.             $sql .= " AND p.date_racc IS NOT NULL AND (p.category_id IN (1,3) OR p.product_id IN (150, 262, 264, 266, 334, 335, 341, 344, 345, 379))";
  4257.         } elseif ($etat === "Raccorde_mobile") {
  4258.             $sql .= " AND p.category_id IN (31,32,33) AND p.date_racc IS NOT NULL AND p.product_id NOT IN (150,262,264,266,334,335,341,344,345,379)";
  4259.         }
  4260.         if ($category) {
  4261.             $categoryArray = [];
  4262.             if (is_string($category)) {
  4263.                 $categoryArray array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  4264.             } elseif (is_array($category) || $category instanceof \Traversable) {
  4265.                 foreach ($category as $cat) {
  4266.                     $categoryArray[] = is_object($cat) && method_exists($cat'getId') ? $cat->getId() : (int)$cat;
  4267.                 }
  4268.                 $categoryArray array_filter($categoryArray, fn($id) => $id 0);
  4269.             }
  4270.             if (!empty($categoryArray)) {
  4271.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  4272.             }
  4273.         }
  4274.         if (is_array($childs) && count($childs) > 0) {
  4275.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  4276.         }
  4277.         if ($periode === 'day') {
  4278.             $sql .= $monthCondition;
  4279.             $params['currentMonth'] = $currentMonth;
  4280.             $params['previousMonth'] = $previousMonth;
  4281.         }
  4282.         // GROUP BY dynamique
  4283.         if ($organisationId) {
  4284.             $sql .= " GROUP BY po.id, $groupCol
  4285.                       ORDER BY po.id, $groupCol ASC";
  4286.         } else {
  4287.             $sql .= " GROUP BY $groupCol
  4288.                       ORDER BY $groupCol ASC";
  4289.         }
  4290.         // --- Exécution ---
  4291.         $stmt $conn->executeQuery($sql$params);
  4292.         $results $stmt->fetchAllAssociative();
  4293.         // --- Réorganisation du résultat ---
  4294.         $data = [];
  4295.         foreach ($results as $row) {
  4296.             if ($organisationId) {
  4297.                 $codePoint $row['code_point'];
  4298.                 $periode $row['periode'];
  4299.                 $total = (int)$row['total_ventes'];
  4300.                 if (!isset($data[$codePoint])) {
  4301.                     $data[$codePoint] = [];
  4302.                 }
  4303.                 // On alimente la période avec le total
  4304.                 $data[$codePoint][$periode] = $total;
  4305.             } else {
  4306.                 $data[$row['periode']] = (int)$row['total_ventes'];
  4307.             }
  4308.         }
  4309.         return $data;
  4310.     }
  4311.     public function getTotalObjectifsByMonth(
  4312.         $pointOfSale,
  4313.         $codeCluster,
  4314.         $annee,
  4315.         $optionSelect,
  4316.         $etatKO,
  4317.         $category,
  4318.         $childs,
  4319.         $organisationId,
  4320.         $departement
  4321.     ): array {
  4322.         $em $this->getEntityManager();
  4323.         // --- 1️⃣ Construction de la requête ---
  4324.         if (is_array($childs) && count($childs) > 0) {
  4325.             $qb $em->createQueryBuilder()
  4326.                 ->select('MONTH(o.monthAt) AS mois, SUM(o.objectivesVv) AS total_objectif')
  4327.                 ->from('App\Entity\ObjectivesManagement''o')
  4328.                 ->where('YEAR(o.monthAt) = :annee ')
  4329.                 ->setParameter('annee'$annee)
  4330.                 ->andWhere('o.user IN (:childs)')
  4331.                 ->setParameter('childs'$childs)
  4332.                 ->groupBy('mois')
  4333.                 ->orderBy('mois''ASC');
  4334.         } else {
  4335.             $qb $em->createQueryBuilder()
  4336.                 ->select('o.mois AS mois, SUM(o.vv) AS total_objectif')
  4337.                 ->from('App\Entity\NmdObjectifClusters''o')
  4338.                 ->leftJoin('App\Entity\PointOfSale''po''WITH''o.cpv = po.code')
  4339.                 ->where('o.year = :annee ')
  4340.                 ->andWhere('o.mois != \'\'')
  4341.                 ->setParameter('annee'$annee);
  4342.             if ($codeCluster) {
  4343.                 $qb->andWhere('o.codeCluster = :codeCluster')
  4344.                     ->setParameter('codeCluster'$codeCluster);
  4345.             }
  4346.             if ($departement) {
  4347.                 $qb->andWhere('o.codeCluster LIKE :prefix')
  4348.                     ->setParameter('prefix'$departement '-%');
  4349.             }
  4350.             if ($organisationId) {
  4351.                 $qb->addSelect('o.cpv')
  4352.                     ->addGroupBy('o.cpv')
  4353.                     ->addGroupBy('o.mois')
  4354.                     ->addOrderBy('o.cpv''ASC')
  4355.                     ->addOrderBy('o.mois''ASC');
  4356.             } else {
  4357.                 if ($pointOfSale !== null) {
  4358.                     $qb->andWhere('o.cpv = :cpv')
  4359.                         ->setParameter('cpv'$pointOfSale->getCode());
  4360.                 }
  4361.                 $qb->addGroupBy('o.mois')
  4362.                     ->addOrderBy('o.mois''ASC');
  4363.             }
  4364.         }
  4365.         // --- 2️⃣ Exécution de la requête ---
  4366.         $results $qb->getQuery()->getArrayResult();
  4367.         // --- 3️⃣ Formatage du résultat ---
  4368.         $formatted = ['parMois' => []];
  4369.         if ($organisationId) {
  4370.             // Structure : parMois[cpv][mois] = total
  4371.             foreach ($results as $row) {
  4372.                 $mois = isset($row['mois']) ? (int) $row['mois'] : 0;
  4373.                 $total = isset($row['total_objectif']) ? (float) $row['total_objectif'] : 0.0;
  4374.                 $cpv = isset($row['cpv']) && $row['cpv'] !== null $row['cpv'] : 'UNKNOWN';
  4375.                 if (!isset($formatted['parMois'][$cpv])) {
  4376.                     $formatted['parMois'][$cpv] = [];
  4377.                 }
  4378.                 $formatted['parMois'][$cpv][$mois] = (int) $total;
  4379.             }
  4380.             // Tri des mois pour chaque CPV
  4381.             foreach ($formatted['parMois'] as &$data) {
  4382.                 ksort($data);
  4383.             }
  4384.             unset($data);
  4385.         } else {
  4386.             // Structure : parMois[mois] = total
  4387.             foreach ($results as $row) {
  4388.                 $mois = isset($row['mois']) ? (int) $row['mois'] : 0;
  4389.                 $total = isset($row['total_objectif']) ? (float) $row['total_objectif'] : 0.0;
  4390.                 $formatted['parMois'][$mois] = (int) $total;
  4391.             }
  4392.             ksort($formatted['parMois']); // Tri des mois
  4393.         }
  4394.         return $formatted;
  4395.     }
  4396.     public function getTotalVentesBOX5G(
  4397.         $pointOfSale,
  4398.         $codeCluster,
  4399.         $departement,
  4400.         $codeInsee,
  4401.         int $annee,
  4402.         string $optionSelect,
  4403.         $etat,
  4404.         $category,
  4405.         $childs,
  4406.         $organisationId,
  4407.         $perid,
  4408.         string $periode 'month' // 'day', 'week', 'month'
  4409.     ): array {
  4410.         $conn $this->getEntityManager()->getConnection();
  4411.         // Choisir la colonne date selon optionSelect
  4412.         switch ($optionSelect) {
  4413.             case 'R':
  4414.                 $dateCol 'p.date_racc';
  4415.                 break;
  4416.             case 'B':
  4417.                 $dateCol 'p.date_cmd_a';
  4418.                 break;
  4419.             case 'V':
  4420.             default:
  4421.                 $dateCol 'p.date_vente_valid_b';
  4422.         }
  4423.         // Déterminer la colonne de regroupement selon la période
  4424.         switch ($periode) {
  4425.             case 'day':
  4426.                 $groupCol "DATE($dateCol)";
  4427.                 $selectCol "DATE($dateCol) AS periode";
  4428.                 $currentMonth = (int)date('m');
  4429.                 $previousMonth $currentMonth === 12 $currentMonth 1;
  4430.                 $monthCondition " AND MONTH($dateCol) IN (:currentMonth, :previousMonth)";
  4431.                 break;
  4432.             case 'week':
  4433.                 $groupCol "WEEK($dateCol, 1)";
  4434.                 $selectCol "WEEK($dateCol, 1) AS periode";
  4435.                 $monthCondition "";
  4436.                 break;
  4437.             case 'month':
  4438.             default:
  4439.                 $groupCol "MONTH($dateCol)";
  4440.                 $selectCol "MONTH($dateCol) AS periode";
  4441.                 $monthCondition "";
  4442.         }
  4443.         // --- Construction SQL ---
  4444.         $sql "
  4445.             SELECT
  4446.                 $selectCol,
  4447.                 COUNT(p.id) AS total_ventes
  4448.         ";
  4449.         // Si organisationId existe, on ajoute po.code et po.name
  4450.         if ($organisationId) {
  4451.             $sql .= ",
  4452.                 po.code AS code_point,
  4453.                 po.name AS nom_point
  4454.             ";
  4455.         }
  4456.         $sql .= "
  4457.             FROM production p
  4458.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4459.             LEFT JOIN cluster c ON p.cluster_id = c.id
  4460.             WHERE YEAR($dateCol) = :annee
  4461.         ";
  4462.         $params = ['annee' => $annee];
  4463.         if ($organisationId) {
  4464.             $sql .= " AND p.organisation_id = :organisationId";
  4465.             $params['organisationId'] = $organisationId;
  4466.         } else {
  4467.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  4468.             $params['pointOfSale'] = $pointOfSale->getId();
  4469.         }
  4470.         if ($codeCluster) {
  4471.             $sql .= " AND c.code_cluster = :codeCluster";
  4472.             $params['codeCluster'] = $codeCluster;
  4473.         }
  4474.         if ($departement) {
  4475.             $sql .= " AND c.code_cluster LIKE :prefix";
  4476.             $params['prefix'] = $departement '-%';
  4477.         }
  4478.         if ($codeInsee) {
  4479.             $sql .= " AND p.code_insee = :codeInsee";
  4480.             $params['codeInsee'] = $codeInsee;
  4481.         }
  4482.         if ($perid) {
  4483.             $sql .= " AND p.login_vendeur_init = :perid";
  4484.             $params['perid'] = $perid;
  4485.         }
  4486.         $sql .= " AND  p.product_id IN (150, 262, 264, 266, 334, 335, 341, 344, 345, 379)";
  4487.         if (is_array($childs) && count($childs) > 0) {
  4488.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  4489.         }
  4490.         if ($periode === 'day') {
  4491.             $sql .= $monthCondition;
  4492.             $params['currentMonth'] = $currentMonth;
  4493.             $params['previousMonth'] = $previousMonth;
  4494.         }
  4495.         // GROUP BY dynamique
  4496.         if ($organisationId) {
  4497.             $sql .= " GROUP BY po.id, $groupCol
  4498.                       ORDER BY po.id, $groupCol ASC";
  4499.         } else {
  4500.             $sql .= " GROUP BY $groupCol
  4501.                       ORDER BY $groupCol ASC";
  4502.         }
  4503.         // --- Exécution ---
  4504.         $stmt $conn->executeQuery($sql$params);
  4505.         $results $stmt->fetchAllAssociative();
  4506.         // --- Réorganisation du résultat ---
  4507.         $data = [];
  4508.         foreach ($results as $row) {
  4509.             if ($organisationId) {
  4510.                 $codePoint $row['code_point'];
  4511.                 $periode $row['periode'];
  4512.                 $total = (int)$row['total_ventes'];
  4513.                 if (!isset($data[$codePoint])) {
  4514.                     $data[$codePoint] = [];
  4515.                 }
  4516.                 // On alimente la période avec le total
  4517.                 $data[$codePoint][$periode] = $total;
  4518.             } else {
  4519.                 $data[$row['periode']] = (int)$row['total_ventes'];
  4520.             }
  4521.         }
  4522.         return $data;
  4523.     }
  4524.     public function getNbVendeursByMonthWeekDay(
  4525.         $pointOfSale,
  4526.         $codeCluster,
  4527.         $departement,
  4528.         $codeInsee,
  4529.         int $annee,
  4530.         string $optionSelect,
  4531.         $etat,
  4532.         $category,
  4533.         $childs,
  4534.         $organisationId,
  4535.         $perid,
  4536.         string $groupBy // 'mois', 'semaine' ou 'jour'
  4537.     ): array {
  4538.         $conn $this->getEntityManager()->getConnection();
  4539.         // Choix de la colonne date
  4540.         switch ($optionSelect) {
  4541.             case 'R':
  4542.                 $dateCol 'p.date_racc';
  4543.                 break;
  4544.             case 'B':
  4545.                 $dateCol 'p.date_cmd_a';
  4546.                 break;
  4547.             case 'V':
  4548.             default:
  4549.                 $dateCol 'p.date_vente_valid_b';
  4550.         }
  4551.         // Déterminer les colonnes SQL pour le regroupement
  4552.         switch ($groupBy) {
  4553.             case 'mois':
  4554.                 $selectGroup "MONTH($dateCol) AS periode";
  4555.                 $groupSQL "MONTH($dateCol)";
  4556.                 break;
  4557.             case 'semaine':
  4558.                 $selectGroup "WEEK($dateCol, 1) AS periode";
  4559.                 $groupSQL "WEEK($dateCol, 1)";
  4560.                 break;
  4561.             case 'jour':
  4562.             default:
  4563.                 $selectGroup "$dateCol AS periode";
  4564.                 $groupSQL "$dateCol";
  4565.         }
  4566.         $now = new \DateTime();
  4567.         $moisActuel = (int)$now->format('n');
  4568.         $moisPrecedent = (int)((clone $now)->modify('-1 month')->format('n'));
  4569.         // --- Début du SQL ---
  4570.         $sql "
  4571.             SELECT 
  4572.                 $selectGroup,
  4573.                 COUNT(DISTINCT p.seller_id) AS nombre_vendeurs
  4574.         ";
  4575.         // Si organisationId existe, on ajoute les infos du point de vente
  4576.         if ($organisationId) {
  4577.             $sql .= ",
  4578.                 po.code AS code_point,
  4579.                 po.name AS nom_point
  4580.             ";
  4581.         }
  4582.         $sql .= "
  4583.             FROM production p
  4584.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4585.             LEFT JOIN cluster c ON p.cluster_id = c.id
  4586.             WHERE YEAR($dateCol) = :annee
  4587.         ";
  4588.         $params = ['annee' => $annee];
  4589.         // Filtrer par mois courant et précédent si on groupe par jour
  4590.         if ($groupBy === 'jour') {
  4591.             $sql .= " AND MONTH($dateCol) IN (:moisPrecedent, :moisActuel)";
  4592.             $params['moisPrecedent'] = $moisPrecedent;
  4593.             $params['moisActuel'] = $moisActuel;
  4594.         }
  4595.         // Conditions d'organisation / point de vente
  4596.         if ($organisationId) {
  4597.             $sql .= " AND p.organisation_id = :organisationId";
  4598.             $params['organisationId'] = $organisationId;
  4599.         } else {
  4600.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  4601.             $params['pointOfSale'] = $pointOfSale->getId();
  4602.         }
  4603.         // Autres filtres
  4604.         if ($codeCluster) {
  4605.             $sql .= " AND c.code_cluster = :codeCluster";
  4606.             $params['codeCluster'] = $codeCluster;
  4607.         }
  4608.         if ($departement) {
  4609.             $sql .= " AND c.code_cluster LIKE :prefix";
  4610.             $params['prefix'] = $departement '-%';
  4611.         }
  4612.         if ($codeInsee) {
  4613.             $sql .= " AND p.code_insee = :codeInsee";
  4614.             $params['codeInsee'] = $codeInsee;
  4615.         }
  4616.         if ($perid) {
  4617.             $sql .= " AND p.login_vendeur_init = :perid";
  4618.             $params['perid'] = $perid;
  4619.         }
  4620.         if ($etat === "Raccorde") {
  4621.             $sql .= " AND p.etat_id = 1";
  4622.         }
  4623.         if ($etat === "Raccorde_mobile") {
  4624.             $sql .= " AND p.category_id IN (31,32,33) AND p.date_racc IS NOT NULL";
  4625.         }
  4626.         if ($category) {
  4627.             $categoryArray = [];
  4628.             if (is_string($category)) {
  4629.                 $categoryArray array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  4630.             }
  4631.             if (!empty($categoryArray)) {
  4632.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  4633.             }
  4634.         }
  4635.         if (is_array($childs) && count($childs) > 0) {
  4636.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  4637.         }
  4638.         // GROUP BY dynamique
  4639.         if ($organisationId) {
  4640.             $sql .= " GROUP BY po.id, $groupSQL
  4641.                       ORDER BY po.id, $groupSQL ASC";
  4642.         } else {
  4643.             $sql .= " GROUP BY $groupSQL
  4644.                       ORDER BY $groupSQL ASC";
  4645.         }
  4646.         // --- Exécution ---
  4647.         $stmt $conn->executeQuery($sql$params);
  4648.         $results $stmt->fetchAllAssociative();
  4649.         // --- Réorganisation du résultat ---
  4650.         $data = [];
  4651.         foreach ($results as $row) {
  4652.             if ($organisationId) {
  4653.                 $codePoint $row['code_point'];
  4654.                 $periode $row['periode'];
  4655.                 $nbVendeurs = (int)$row['nombre_vendeurs'];
  4656.                 if (!isset($data[$codePoint])) {
  4657.                     $data[$codePoint] = [];
  4658.                 }
  4659.                 $data[$codePoint][$periode] = $nbVendeurs;
  4660.             } else {
  4661.                 $data[$row['periode']] = (int)$row['nombre_vendeurs'];
  4662.             }
  4663.         }
  4664.         return $data;
  4665.     }
  4666.     public function getEtpByMonthWeekDay(
  4667.         $pointOfSale,
  4668.         $codeCluster,
  4669.         $departement,
  4670.         $codeInsee,
  4671.         int $annee,
  4672.         string $optionSelect,
  4673.         $etat,
  4674.         $category,
  4675.         $childs,
  4676.         $organisationId,
  4677.         $perid
  4678.     ): array {
  4679.         $conn $this->getEntityManager()->getConnection();
  4680.         // Choix de la colonne date
  4681.         switch ($optionSelect) {
  4682.             case 'R':
  4683.                 $dateCol 'p.date_racc';
  4684.                 break;
  4685.             case 'B':
  4686.                 $dateCol 'p.date_cmd_a';
  4687.                 break;
  4688.             case 'V':
  4689.             default:
  4690.                 $dateCol 'p.date_vente_valid_b';
  4691.         }
  4692.         $now = new \DateTime();
  4693.         $moisActuel = (int)$now->format('n');
  4694.         $moisPrecedent = (int)((clone $now)->modify('-1 month')->format('n'));
  4695.         // --- Début du SQL ---
  4696.         $sql "
  4697.             SELECT 
  4698.                 $dateCol AS dateStat,
  4699.                 MONTH($dateCol) AS mois,
  4700.                 WEEK($dateCol, 1) AS semaine,
  4701.                 COUNT(p.id) / NULLIF(COUNT(DISTINCT p.seller_id), 0) AS etp
  4702.         ";
  4703.         // Si organisationId existe → on ajoute les infos du point de vente
  4704.         if ($organisationId) {
  4705.             $sql .= ",
  4706.                 po.code AS code_point,
  4707.                 po.name AS nom_point
  4708.             ";
  4709.         }
  4710.         $sql .= "
  4711.             FROM production p
  4712.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4713.             LEFT JOIN cluster c ON p.cluster_id = c.id
  4714.             WHERE YEAR($dateCol) = :annee
  4715.         ";
  4716.         $params = ['annee' => $annee];
  4717.         if ($organisationId) {
  4718.             $sql .= " AND p.organisation_id = :organisationId";
  4719.             $params['organisationId'] = $organisationId;
  4720.         } else {
  4721.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  4722.             $params['pointOfSale'] = $pointOfSale->getId();
  4723.         }
  4724.         if ($codeCluster) {
  4725.             $sql .= " AND c.code_cluster = :codeCluster";
  4726.             $params['codeCluster'] = $codeCluster;
  4727.         }
  4728.         if ($departement) {
  4729.             $sql .= " AND c.code_cluster LIKE :prefix";
  4730.             $params['prefix'] = $departement '-%';
  4731.         }
  4732.         if ($codeInsee) {
  4733.             $sql .= " AND p.code_insee = :codeInsee";
  4734.             $params['codeInsee'] = $codeInsee;
  4735.         }
  4736.         if ($perid) {
  4737.             $sql .= " AND p.login_vendeur_init = :perid";
  4738.             $params['perid'] = $perid;
  4739.         }
  4740.         if ($etat === "Raccorde") {
  4741.             $sql .= " AND p.etat_id = 1";
  4742.         }
  4743.         if ($etat === "Raccorde_mobile") {
  4744.             $sql .= " AND p.category_id IN (31,32,33) AND p.date_racc IS NOT NULL";
  4745.         }
  4746.         if ($category) {
  4747.             $categoryArray is_string($category)
  4748.                 ? array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0)
  4749.                 : [];
  4750.             if (!empty($categoryArray)) {
  4751.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  4752.             }
  4753.         }
  4754.         if (is_array($childs) && count($childs) > 0) {
  4755.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  4756.         }
  4757.         // GROUP BY dynamique
  4758.         if ($organisationId) {
  4759.             $sql .= " GROUP BY po.id, $dateCol ORDER BY po.id, $dateCol ASC";
  4760.         } else {
  4761.             $sql .= " GROUP BY $dateCol ORDER BY $dateCol ASC";
  4762.         }
  4763.         // --- Exécution ---
  4764.         $stmt $conn->executeQuery($sql$params);
  4765.         $results $stmt->fetchAllAssociative();
  4766.         // --- Réorganisation du résultat ---
  4767.         $data = [];
  4768.         foreach ($results as $row) {
  4769.             $mois = (int)$row['mois'];
  4770.             $semaine = (int)$row['semaine'];
  4771.             $jour $row['dateStat'];
  4772.             $etp = (float)$row['etp'];
  4773.             if ($organisationId) {
  4774.                 $codePoint $row['code_point'];
  4775.                 if (!isset($data[$codePoint])) {
  4776.                     $data[$codePoint] = [
  4777.                         'parMois' => [],
  4778.                         'parSemaine' => [],
  4779.                         'parJour' => [],
  4780.                     ];
  4781.                 }
  4782.                 $data[$codePoint]['parMois'][$mois] = ($data[$codePoint]['parMois'][$mois] ?? 0) + $etp;
  4783.                 $data[$codePoint]['parSemaine'][$semaine] = ($data[$codePoint]['parSemaine'][$semaine] ?? 0) + $etp;
  4784.                 if ($mois === $moisActuel || $mois === $moisPrecedent) {
  4785.                     $data[$codePoint]['parJour'][$jour] = $etp;
  4786.                 }
  4787.             } else {
  4788.                 $data['parMois'][$mois] = ($data['parMois'][$mois] ?? 0) + $etp;
  4789.                 $data['parSemaine'][$semaine] = ($data['parSemaine'][$semaine] ?? 0) + $etp;
  4790.                 if ($mois === $moisActuel || $mois === $moisPrecedent) {
  4791.                     $data['parJour'][$jour] = $etp;
  4792.                 }
  4793.             }
  4794.         }
  4795.         // --- Arrondir les valeurs ---
  4796.         if ($organisationId) {
  4797.             foreach ($data as $codePoint => &$valeurs) {
  4798.                 foreach (['parMois''parSemaine'] as $type) {
  4799.                     foreach ($valeurs[$type] as $period => $value) {
  4800.                         $valeurs[$type][$period] = round($value2);
  4801.                     }
  4802.                 }
  4803.             }
  4804.         } else {
  4805.             foreach (['parMois''parSemaine'] as $type) {
  4806.                 foreach ($data[$type] as $period => $value) {
  4807.                     $data[$type][$period] = round($value2);
  4808.                 }
  4809.             }
  4810.         }
  4811.         return $data;
  4812.     }
  4813.     public function getVentesKOByMonthWeekDay(
  4814.         $pointOfSale,
  4815.         $codeCluster,
  4816.         $departement,
  4817.         $codeInsee,
  4818.         int $annee,
  4819.         string $optionSelect,
  4820.         $etatKO,
  4821.         $category,
  4822.         $childs,
  4823.         $organisationId,
  4824.         $perid
  4825.     ): array {
  4826.         $conn $this->getEntityManager()->getConnection();
  4827.         // Déterminer la colonne date
  4828.         switch ($optionSelect) {
  4829.             case 'R':
  4830.                 $dateCol 'p.date_racc';
  4831.                 break;
  4832.             case 'B':
  4833.                 $dateCol 'p.date_cmd_a';
  4834.                 break;
  4835.             case 'V':
  4836.             default:
  4837.                 $dateCol 'p.date_vente_valid_b';
  4838.         }
  4839.         $now = new \DateTime();
  4840.         $moisActuel = (int)$now->format('n');
  4841.         $moisPrecedent = (int)((clone $now)->modify('-1 month')->format('n'));
  4842.         // --- Début du SQL ---
  4843.         $sql "
  4844.             SELECT 
  4845.                 DATE($dateCol) AS dateStat,
  4846.                 MONTH($dateCol) AS mois,
  4847.                 WEEK($dateCol, 1) AS semaine,
  4848.                 COUNT(p.id) AS total_ventes_ko
  4849.         ";
  4850.         // Si organisationId → on ajoute code_point et nom_point
  4851.         if ($organisationId) {
  4852.             $sql .= ",
  4853.                 po.code AS code_point,
  4854.                 po.name AS nom_point
  4855.             ";
  4856.         }
  4857.         $sql .= "
  4858.             FROM production p
  4859.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4860.             LEFT JOIN cluster c ON p.cluster_id = c.id
  4861.             WHERE YEAR($dateCol) = :annee
  4862.               AND p.etat_id = :etatKO
  4863.         ";
  4864.         $params = [
  4865.             'annee'  => $annee,
  4866.             'etatKO' => $etatKO->getId(),
  4867.         ];
  4868.         // Conditions organisation / point de vente
  4869.         if ($organisationId) {
  4870.             $sql .= " AND p.organisation_id = :organisationId";
  4871.             $params['organisationId'] = $organisationId;
  4872.         } else {
  4873.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  4874.             $params['pointOfSale'] = $pointOfSale->getId();
  4875.         }
  4876.         if ($codeCluster) {
  4877.             $sql .= " AND c.code_cluster = :codeCluster";
  4878.             $params['codeCluster'] = $codeCluster;
  4879.         }
  4880.         if ($departement) {
  4881.             $sql .= " AND c.code_cluster LIKE :prefix";
  4882.             $params['prefix'] = $departement '-%';
  4883.         }
  4884.         if ($codeInsee) {
  4885.             $sql .= " AND p.code_insee = :codeInsee";
  4886.             $params['codeInsee'] = $codeInsee;
  4887.         }
  4888.         if ($perid) {
  4889.             $sql .= " AND p.login_vendeur_init = :perid";
  4890.             $params['perid'] = $perid;
  4891.         }
  4892.         if ($category) {
  4893.             $categoryArray is_string($category)
  4894.                 ? array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0)
  4895.                 : [];
  4896.             if (!empty($categoryArray)) {
  4897.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  4898.             }
  4899.         }
  4900.         if (is_array($childs) && count($childs) > 0) {
  4901.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  4902.         }
  4903.         // GROUP BY dynamique
  4904.         if ($organisationId) {
  4905.             $sql .= " GROUP BY po.id, $dateCol ORDER BY po.id, $dateCol ASC";
  4906.         } else {
  4907.             $sql .= " GROUP BY $dateCol ORDER BY $dateCol ASC";
  4908.         }
  4909.         // --- Exécution ---
  4910.         $stmt $conn->executeQuery($sql$params);
  4911.         $results $stmt->fetchAllAssociative();
  4912.         // --- Réorganisation du résultat ---
  4913.         $data = [];
  4914.         foreach ($results as $row) {
  4915.             $mois = (int)$row['mois'];
  4916.             $semaine = (int)$row['semaine'];
  4917.             $jour $row['dateStat'];
  4918.             $total = (int)$row['total_ventes_ko'];
  4919.             if ($organisationId) {
  4920.                 $codePoint $row['code_point'];
  4921.                 // Initialiser les sous-tableaux
  4922.                 if (!isset($data['parMois'][$codePoint])) {
  4923.                     $data['parMois'][$codePoint] = [];
  4924.                 }
  4925.                 if (!isset($data['parSemaine'][$codePoint])) {
  4926.                     $data['parSemaine'][$codePoint] = [];
  4927.                 }
  4928.                 if (!isset($data['parJour'][$codePoint])) {
  4929.                     $data['parJour'][$codePoint] = [];
  4930.                 }
  4931.                 // Agrégations
  4932.                 $data['parMois'][$codePoint][$mois] = ($data['parMois'][$codePoint][$mois] ?? 0) + $total;
  4933.                 $data['parSemaine'][$codePoint][$semaine] = ($data['parSemaine'][$codePoint][$semaine] ?? 0) + $total;
  4934.                 // Par jour → mois courant et précédent uniquement
  4935.                 if ($mois === $moisActuel || $mois === $moisPrecedent) {
  4936.                     $data['parJour'][$codePoint][$jour] = $total;
  4937.                 }
  4938.             } else {
  4939.                 // Cas sans organisation : structure simple
  4940.                 $data['parMois'][$mois] = ($data['parMois'][$mois] ?? 0) + $total;
  4941.                 $data['parSemaine'][$semaine] = ($data['parSemaine'][$semaine] ?? 0) + $total;
  4942.                 if ($mois === $moisActuel || $mois === $moisPrecedent) {
  4943.                     $data['parJour'][$jour] = $total;
  4944.                 }
  4945.             }
  4946.         }
  4947.         return $data;
  4948.     }
  4949.     public function getVentesKOByPeriod(
  4950.         $pointOfSale,
  4951.         $codeCluster,
  4952.         $departement,
  4953.         $codeInsee,
  4954.         int $annee,
  4955.         string $optionSelect,
  4956.         $etatKO,
  4957.         $category,
  4958.         $childs,
  4959.         $organisationId,
  4960.         $perid,
  4961.         string $periode 'month'
  4962.     ): array {
  4963.         $conn $this->getEntityManager()->getConnection();
  4964.         // Sélection de la date
  4965.         switch ($optionSelect) {
  4966.             case 'R':
  4967.                 $dateCol 'p.date_racc';
  4968.                 break;
  4969.             case 'B':
  4970.                 $dateCol 'p.date_cmd_a';
  4971.                 break;
  4972.             case 'V':
  4973.             default:
  4974.                 $dateCol 'p.date_vente_valid_b';
  4975.                 break;
  4976.         }
  4977.         switch ($periode) {
  4978.             case 'week':
  4979.                 $selectGroup "WEEK($dateCol, 1) AS periode";
  4980.                 break;
  4981.             case 'day':
  4982.                 $selectGroup "DATE($dateCol) AS periode";
  4983.                 break;
  4984.             default:
  4985.                 $selectGroup "MONTH($dateCol) AS periode";
  4986.                 break;
  4987.         }
  4988.         $sql "
  4989.             SELECT 
  4990.                 $selectGroup,
  4991.                 COUNT(p.id) AS total_ventes_ko
  4992.         ";
  4993.         if ($organisationId) {
  4994.             $sql .= ", po.code AS code_point, po.name AS nom_point";
  4995.         }
  4996.         $sql .= "
  4997.             FROM production p
  4998.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  4999.             LEFT JOIN cluster c ON p.cluster_id = c.id
  5000.             WHERE YEAR($dateCol) = :annee
  5001.               AND p.etat_id = :etatKO
  5002.         ";
  5003.         $params = ['annee' => $annee'etatKO' => $etatKO->getId()];
  5004.         if ($organisationId) {
  5005.             $sql .= " AND p.organisation_id = :organisationId";
  5006.             $params['organisationId'] = $organisationId;
  5007.         } else {
  5008.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  5009.             $params['pointOfSale'] = $pointOfSale->getId();
  5010.         }
  5011.         if ($codeCluster) {
  5012.             $sql .= " AND c.code_cluster = :codeCluster";
  5013.             $params['codeCluster'] = $codeCluster;
  5014.         }
  5015.         if ($departement) {
  5016.             $sql .= " AND c.code_cluster LIKE :prefix";
  5017.             $params['prefix'] = $departement '-%';
  5018.         }
  5019.         if ($codeInsee) {
  5020.             $sql .= " AND p.code_insee = :codeInsee";
  5021.             $params['codeInsee'] = $codeInsee;
  5022.         }
  5023.         if ($perid) {
  5024.             $sql .= " AND p.login_vendeur_init = :perid";
  5025.             $params['perid'] = $perid;
  5026.         }
  5027.         if ($category) {
  5028.             $categoryArray is_string($category)
  5029.                 ? array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0)
  5030.                 : [];
  5031.             if (!empty($categoryArray)) {
  5032.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  5033.             }
  5034.         }
  5035.         if (is_array($childs) && count($childs) > 0) {
  5036.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  5037.         }
  5038.         if ($organisationId) {
  5039.             $sql .= " GROUP BY po.id, periode ORDER BY po.id, periode ASC";
  5040.         } else {
  5041.             $sql .= " GROUP BY periode ORDER BY periode ASC";
  5042.         }
  5043.         $stmt $conn->executeQuery($sql$params);
  5044.         $results $stmt->fetchAllAssociative();
  5045.         $data = [];
  5046.         foreach ($results as $row) {
  5047.             $periodeKey $row['periode'];
  5048.             $total = (int)$row['total_ventes_ko'];
  5049.             if ($organisationId) {
  5050.                 $codePoint $row['code_point'];
  5051.                 if (!isset($data[$codePoint])) {
  5052.                     $data[$codePoint] = [];
  5053.                 }
  5054.                 $data[$codePoint][$periodeKey] = $total;
  5055.             } else {
  5056.                 $data[$periodeKey] = $total;
  5057.             }
  5058.         }
  5059.         return $data;
  5060.     }
  5061.     public function getEtpByPeriod(
  5062.         $pointOfSale,
  5063.         $codeCluster,
  5064.         $departement,
  5065.         $codeInsee,
  5066.         int $annee,
  5067.         string $optionSelect,
  5068.         $etat,
  5069.         $category,
  5070.         $childs,
  5071.         $organisationId,
  5072.         $perid,
  5073.         string $periode 'month' // 'day' | 'week' | 'month'
  5074.     ): array {
  5075.         $conn $this->getEntityManager()->getConnection();
  5076.         // Choix de la colonne date
  5077.         switch ($optionSelect) {
  5078.             case 'R':
  5079.                 $dateCol 'p.date_racc';
  5080.                 break;
  5081.             case 'B':
  5082.                 $dateCol 'p.date_cmd_a';
  5083.                 break;
  5084.             case 'V':
  5085.             default:
  5086.                 $dateCol 'p.date_vente_valid_b';
  5087.         }
  5088.         // Début SQL
  5089.         $selectGroup '';
  5090.         switch ($periode) {
  5091.             case 'week':
  5092.                 $selectGroup "WEEK($dateCol, 1) AS periode";
  5093.                 break;
  5094.             case 'day':
  5095.                 $selectGroup "DATE($dateCol) AS periode";
  5096.                 break;
  5097.             case 'month':
  5098.             default:
  5099.                 $selectGroup "MONTH($dateCol) AS periode";
  5100.                 break;
  5101.         }
  5102.         $sql "
  5103.             SELECT 
  5104.                 $selectGroup,
  5105.                 COUNT(p.id) / NULLIF(COUNT(DISTINCT p.seller_id), 0) AS etp
  5106.         ";
  5107.         if ($organisationId) {
  5108.             $sql .= ",
  5109.                 po.code AS code_point,
  5110.                 po.name AS nom_point
  5111.             ";
  5112.         }
  5113.         $sql .= "
  5114.             FROM production p
  5115.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  5116.             LEFT JOIN cluster c ON p.cluster_id = c.id
  5117.             WHERE YEAR($dateCol) = :annee
  5118.         ";
  5119.         $params = ['annee' => $annee];
  5120.         // Conditions dynamiques
  5121.         if ($organisationId) {
  5122.             $sql .= " AND p.organisation_id = :organisationId";
  5123.             $params['organisationId'] = $organisationId;
  5124.         } else {
  5125.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  5126.             $params['pointOfSale'] = $pointOfSale->getId();
  5127.         }
  5128.         if ($codeCluster) {
  5129.             $sql .= " AND c.code_cluster = :codeCluster";
  5130.             $params['codeCluster'] = $codeCluster;
  5131.         }
  5132.         if ($departement) {
  5133.             $sql .= " AND c.code_cluster LIKE :prefix";
  5134.             $params['prefix'] = $departement '-%';
  5135.         }
  5136.         if ($codeInsee) {
  5137.             $sql .= " AND p.code_insee = :codeInsee";
  5138.             $params['codeInsee'] = $codeInsee;
  5139.         }
  5140.         if ($perid) {
  5141.             $sql .= " AND p.login_vendeur_init = :perid";
  5142.             $params['perid'] = $perid;
  5143.         }
  5144.         if ($etat === "Raccorde") {
  5145.             $sql .= " AND p.etat_id = 1";
  5146.         } elseif ($etat === "Raccorde_mobile") {
  5147.             $sql .= " AND p.category_id IN (31,32,33) AND p.date_racc IS NOT NULL";
  5148.         }
  5149.         if ($category) {
  5150.             $categoryArray is_string($category)
  5151.                 ? array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0)
  5152.                 : [];
  5153.             if (!empty($categoryArray)) {
  5154.                 $sql .= " AND p.category_id IN (" implode(','$categoryArray) . ")";
  5155.             }
  5156.         }
  5157.         if (is_array($childs) && count($childs) > 0) {
  5158.             $sql .= " AND p.seller_id IN (" implode(','$childs) . ")";
  5159.         }
  5160.         // Group by selon la période
  5161.         if ($organisationId) {
  5162.             $sql .= " GROUP BY po.id, periode ORDER BY po.id, periode ASC";
  5163.         } else {
  5164.             $sql .= " GROUP BY periode ORDER BY periode ASC";
  5165.         }
  5166.         // Exécution
  5167.         $stmt $conn->executeQuery($sql$params);
  5168.         $results $stmt->fetchAllAssociative();
  5169.         // Résultat final
  5170.         $data = [];
  5171.         foreach ($results as $row) {
  5172.             $periodeKey $row['periode'];
  5173.             $etp = (float)$row['etp'];
  5174.             if ($organisationId) {
  5175.                 $codePoint $row['code_point'];
  5176.                 if (!isset($data[$codePoint])) {
  5177.                     $data[$codePoint] = [];
  5178.                 }
  5179.                 $data[$codePoint][$periodeKey] = round($etp2);
  5180.             } else {
  5181.                 $data[$periodeKey] = round($etp2);
  5182.             }
  5183.         }
  5184.         return $data;
  5185.     }
  5186.     public function get4PByPeriod(
  5187.         $pointOfSale,
  5188.         $codeCluster,
  5189.         $departement,
  5190.         $codeInsee,
  5191.         int $annee,
  5192.         $optionSelect,
  5193.         $etat,
  5194.         $filtre,
  5195.         $childs,
  5196.         ?int $organisationId,
  5197.         $perid,
  5198.         string $periode 'month' // week | month | day
  5199.     ): array {
  5200.         $conn $this->getEntityManager()->getConnection();
  5201.         $now = new \DateTime();
  5202.         $moisActuel = (int)$now->format('n');
  5203.         $moisPrecedent = (int)((clone $now)->modify('-1 month')->format('n'));
  5204.         // Déterminer la période et alias
  5205.         switch (strtolower($periode)) {
  5206.             case 'day':
  5207.                 $groupField 'DATE(result.date_cmd_a)';
  5208.                 $alias 'jour';
  5209.                 break;
  5210.             case 'month':
  5211.                 $groupField 'MONTH(result.date_cmd_a)';
  5212.                 $alias 'mois';
  5213.                 break;
  5214.             case 'week':
  5215.             default:
  5216.                 $groupField 'WEEK(result.date_cmd_a, 3)';
  5217.                 $alias 'semaine';
  5218.                 break;
  5219.         }
  5220.         $params = [
  5221.             'annee' => $annee,
  5222.             'cat1' => 32,
  5223.             'cat2' => 31,
  5224.             'moisPrecedent' => $moisPrecedent,
  5225.             'moisActuel' => $moisActuel
  5226.         ];
  5227.         // Filtrage selon organisation ou point de vente
  5228.         $pointOrOrg $organisationId 'organisation_id = :organisationId' 'point_of_sale_id = :pointOfSale';
  5229.         if ($organisationId) {
  5230.             $params['organisationId'] = $organisationId;
  5231.         } else {
  5232.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  5233.         }
  5234.         // Sélection et groupement conditionnel par point de vente
  5235.         $selectPosFields '';
  5236.         $groupByPos '';
  5237.         $joinPos '';
  5238.         if ($organisationId) {
  5239.             $selectPosFields ', result.point_of_sale_id, pos.code AS point_of_sale_code';
  5240.             $groupByPos ', result.point_of_sale_id, pos.code';
  5241.             $joinPos 'LEFT JOIN point_of_sale pos ON pos.id = result.point_of_sale_id';
  5242.         }
  5243.         // Sous-requête UNION combinée
  5244.         $sql "
  5245.             SELECT COUNT(DISTINCT result.titulaire_email) AS total, 
  5246.                    $groupField AS $alias
  5247.                    $selectPosFields
  5248.             FROM (
  5249.                 SELECT p1.titulaire_email, p1.date_cmd_a, p1.point_of_sale_id
  5250.                 FROM production p1
  5251.                 LEFT JOIN cluster c1 ON p1.cluster_id = c1.id
  5252.                 WHERE p1.category_id = :cat1
  5253.                   AND YEAR(p1.date_cmd_a) = :annee
  5254.                   AND $pointOrOrg
  5255.         ";
  5256.         if (strtolower($periode) === 'day') {
  5257.             $sql .= " AND MONTH(p1.date_cmd_a) IN (:moisPrecedent, :moisActuel)";
  5258.         }
  5259.         $sql .= " AND p1.titulaire_email IN (
  5260.                     SELECT DISTINCT p_sub1.titulaire_email
  5261.                     FROM production p_sub1
  5262.                     WHERE p_sub1.category_id = 1
  5263.                       AND YEAR(p_sub1.date_cmd_a) = :annee
  5264.                       AND $pointOrOrg
  5265.                   )";
  5266.         if ($codeCluster) {
  5267.             $sql .= " AND c1.code_cluster = :codeCluster";
  5268.             $params["codeCluster"] = $codeCluster;
  5269.         }
  5270.         if ($departement) {
  5271.             $sql .= " AND c1.code_cluster LIKE :prefix";
  5272.             $params['prefix'] = $departement '-%';
  5273.         }
  5274.         if ($codeInsee) {
  5275.             $sql .= " AND p1.code_insee = :codeInsee";
  5276.             $params["codeInsee"] = $codeInsee;
  5277.         }
  5278.         if ($perid) {
  5279.             $sql .= " AND p1.login_vendeur_init = :perid";
  5280.             $params["perid"] = $perid;
  5281.         }
  5282.         if (!empty($childs)) {
  5283.             $placeholders = [];
  5284.             foreach ($childs as $i => $child) {
  5285.                 $key 'child_' $i;
  5286.                 $placeholders[] = ':' $key;
  5287.                 $params[$key] = $child;
  5288.             }
  5289.             $sql .= " AND p1.seller_id IN (" implode(','$placeholders) . ")";
  5290.         }
  5291.         // UNION partie 2
  5292.         $sql .= "
  5293.             UNION
  5294.             SELECT p2.titulaire_email, p2.date_cmd_a, p2.point_of_sale_id
  5295.             FROM production p2
  5296.             LEFT JOIN cluster c2 ON p2.cluster_id = c2.id
  5297.             WHERE p2.category_id = :cat2
  5298.               AND YEAR(p2.date_cmd_a) = :annee
  5299.               AND $pointOrOrg
  5300.         ";
  5301.         if (strtolower($periode) === 'day') {
  5302.             $sql .= " AND MONTH(p2.date_cmd_a) IN (:moisPrecedent, :moisActuel)";
  5303.         }
  5304.         $sql .= " AND p2.titulaire_email IN (
  5305.                     SELECT DISTINCT p_sub2.titulaire_email
  5306.                     FROM production p_sub2
  5307.                     WHERE p_sub2.category_id IN (1,3)
  5308.                       AND YEAR(p_sub2.date_cmd_a) = :annee
  5309.                       AND $pointOrOrg
  5310.                   )";
  5311.         if ($codeCluster$sql .= " AND c2.code_cluster = :codeCluster";
  5312.         if ($departement) {
  5313.             $sql .= " AND c2.code_cluster LIKE :prefix";
  5314.         }
  5315.         if ($codeInsee$sql .= " AND p2.code_insee = :codeInsee";
  5316.         if ($perid$sql .= " AND p2.login_vendeur_init = :perid";
  5317.         if (!empty($childs)) {
  5318.             $placeholders = [];
  5319.             foreach ($childs as $i => $child) {
  5320.                 $key 'child2_' $i;
  5321.                 $placeholders[] = ':' $key;
  5322.                 $params[$key] = $child;
  5323.             }
  5324.             $sql .= " AND p2.seller_id IN (" implode(','$placeholders) . ")";
  5325.         }
  5326.         $sql .= ") AS result
  5327.                   $joinPos
  5328.                   GROUP BY $groupField $groupByPos
  5329.                   ORDER BY $alias
  5330.         ";
  5331.         $stmt $conn->prepare($sql);
  5332.         $rawResult $stmt->executeQuery($params)->fetchAllAssociative();
  5333.         // Mapping final : [période => code_point => total]
  5334.         $formatted = [];
  5335.         foreach ($rawResult as $item) {
  5336.             switch (strtolower($periode)) {
  5337.                 case 'day':
  5338.                     $key substr($item['jour'], 010);
  5339.                     break;
  5340.                 case 'week':
  5341.                     $key = (int)$item['semaine'];
  5342.                     break;
  5343.                 case 'month':
  5344.                 default:
  5345.                     $key = (int)$item['mois'];
  5346.                     break;
  5347.             }
  5348.             if ($organisationId) {
  5349.                 $posCode $item['point_of_sale_code'] ?? 'UNKNOWN';
  5350.                 $formatted[$posCode][$key] = (int)$item['total'];
  5351.             } else {
  5352.                 $formatted[$key] = (int)$item['total'];
  5353.             }
  5354.         }
  5355.         return $formatted;
  5356.     }
  5357.     public function get4PCByPeriod(
  5358.         $pointOfSale,
  5359.         $codeCluster,
  5360.         $departement,
  5361.         $codeInsee,
  5362.         $annee,
  5363.         $optionSelect,
  5364.         $etatKO,
  5365.         $category,
  5366.         $option,
  5367.         $childs,
  5368.         $organisationId,
  5369.         $perid,
  5370.         string $periode 'month' // day | week | month
  5371.     ): array {
  5372.         $conn $this->getEntityManager()->getConnection();
  5373.         $where = [];
  5374.         $params = [];
  5375.         // --- Filtre organisation ou point de vente ---
  5376.         if ($organisationId) {
  5377.             $where[] = 'p.organisation_id = :organisationId';
  5378.             $params['organisationId'] = $organisationId;
  5379.         } elseif ($pointOfSale !== null) {
  5380.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  5381.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  5382.         }
  5383.         if ($codeCluster !== null) {
  5384.             $where[] = 'c.code_cluster = :codeCluster';
  5385.             $params['codeCluster'] = $codeCluster;
  5386.         }
  5387.         if ($departement) {
  5388.             $where[] = " c.code_cluster LIKE :prefix";
  5389.             $params['prefix'] = $departement '-%';
  5390.         }
  5391.         if ($codeInsee) {
  5392.             $where[] = 'p.code_insee = :codeInsee';
  5393.             $params['codeInsee'] = $codeInsee;
  5394.         }
  5395.         if ($perid) {
  5396.             $where[] = 'p.login_vendeur_init = :perid';
  5397.             $params['perid'] = $perid;
  5398.         }
  5399.         if ($annee !== null) {
  5400.             $where[] = 'YEAR(p.date_cmd_a) = :annee';
  5401.             $params['annee'] = $annee;
  5402.         }
  5403.         if (is_array($childs) && count($childs) > 0) {
  5404.             $placeholders = [];
  5405.             foreach ($childs as $i => $child) {
  5406.                 $placeholder ':child_' $i;
  5407.                 $placeholders[] = $placeholder;
  5408.                 $params['child_' $i] = $child;
  5409.             }
  5410.             $where[] = 'p.seller_id IN (' implode(', '$placeholders) . ')';
  5411.         }
  5412.         if (empty($where)) {
  5413.             $where[] = '1=1';
  5414.         }
  5415.         // --- Sélection selon la période ---
  5416.         $select 'COUNT(DISTINCT p.titulaire_email) AS total';
  5417.         $groupBy '';
  5418.         $alias '';
  5419.         switch (strtolower($periode)) {
  5420.             case 'month':
  5421.                 $select .= ', MONTH(p.date_cmd_a) AS mois';
  5422.                 $groupBy 'MONTH(p.date_cmd_a)';
  5423.                 $alias 'mois';
  5424.                 break;
  5425.             case 'day':
  5426.                 $select .= ', p.date_cmd_a AS dateJour, MONTH(p.date_cmd_a) AS mois';
  5427.                 $groupBy 'p.date_cmd_a';
  5428.                 $alias 'dateJour';
  5429.                 break;
  5430.             case 'week':
  5431.             default:
  5432.                 $select .= ', WEEK(p.date_cmd_a, 3) AS semaine';
  5433.                 $groupBy 'WEEK(p.date_cmd_a, 3)';
  5434.                 $alias 'semaine';
  5435.                 break;
  5436.         }
  5437.         // --- Option spécifique chainage ---
  5438.         if ($option === 'chainage') {
  5439.             $where[] = 'p.fixe_chainage IS NOT NULL';
  5440.             if (!empty($category)) {
  5441.                 $categoryArray array_filter(
  5442.                     array_map('intval'explode(','$category)),
  5443.                     fn($id) => $id 0
  5444.                 );
  5445.                 if (!empty($categoryArray)) {
  5446.                     $placeholders = [];
  5447.                     foreach ($categoryArray as $k => $catId) {
  5448.                         $key 'cat' $k;
  5449.                         $placeholders[] = ':' $key;
  5450.                         $params[$key] = $catId;
  5451.                     }
  5452.                     $where[] = 'p.category_id IN (' implode(','$placeholders) . ')';
  5453.                 }
  5454.             }
  5455.         }
  5456.         // --- Construction du SQL ---
  5457.         $sql "
  5458.             SELECT $select
  5459.         ";
  5460.         if ($organisationId) {
  5461.             $sql .= ",
  5462.                 po.code AS code_point,
  5463.                 po.name AS nom_point
  5464.             ";
  5465.         }
  5466.         $sql .= "
  5467.             FROM production p
  5468.             LEFT JOIN point_of_sale po ON po.id = p.point_of_sale_id
  5469.             LEFT JOIN cluster c ON c.id = p.cluster_id
  5470.             WHERE " implode(' AND '$where) . "
  5471.         ";
  5472.         // --- Groupement ---
  5473.         if ($organisationId) {
  5474.             $sql .= " GROUP BY po.id, $groupBy ORDER BY po.id, $groupBy";
  5475.         } else {
  5476.             $sql .= " GROUP BY $groupBy ORDER BY $groupBy";
  5477.         }
  5478.         $stmt $conn->prepare($sql);
  5479.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  5480.         // --- Limiter aux 2 derniers mois si 'day' ---
  5481.         if (strtolower($periode) === 'day') {
  5482.             $currentMonth = (int)date('m');
  5483.             $previousMonth $currentMonth === 12 $currentMonth 1;
  5484.             $results array_filter($results, function ($row) use ($currentMonth$previousMonth) {
  5485.                 return ((int)$row['mois'] === $currentMonth || (int)$row['mois'] === $previousMonth);
  5486.             });
  5487.         }
  5488.         // --- Réorganisation du résultat ---
  5489.         $mappedResults = [];
  5490.         foreach ($results as $row) {
  5491.             $total = (int)$row['total'];
  5492.             if ($organisationId) {
  5493.                 $codePoint $row['code_point'] ?? 'N/A';
  5494.                 if (!isset($mappedResults[$codePoint])) {
  5495.                     $mappedResults[$codePoint] = [];
  5496.                 }
  5497.                 switch (strtolower($periode)) {
  5498.                     case 'day':
  5499.                         $mappedResults[$codePoint][$row['dateJour']] = $total;
  5500.                         break;
  5501.                     case 'week':
  5502.                         $mappedResults[$codePoint][$row['semaine']] = $total;
  5503.                         break;
  5504.                     case 'month':
  5505.                     default:
  5506.                         $mappedResults[$codePoint][$row['mois']] = $total;
  5507.                         break;
  5508.                 }
  5509.             } else {
  5510.                 switch (strtolower($periode)) {
  5511.                     case 'day':
  5512.                         $mappedResults[$row['dateJour']] = $total;
  5513.                         break;
  5514.                     case 'week':
  5515.                         $mappedResults[$row['semaine']] = $total;
  5516.                         break;
  5517.                     case 'month':
  5518.                     default:
  5519.                         $mappedResults[$row['mois']] = $total;
  5520.                         break;
  5521.                 }
  5522.             }
  5523.         }
  5524.         return $mappedResults;
  5525.     }
  5526.     public function getJ30ByPeriod(
  5527.         $pointOfSale,
  5528.         $codeCluster,
  5529.         $departement,
  5530.         $codeInsee,
  5531.         $annee,
  5532.         $optionSelect,
  5533.         $etat,
  5534.         $category,
  5535.         $childs,
  5536.         $organisationId,
  5537.         $perid,
  5538.         string $periode 'month' // day | week | month
  5539.     ): array {
  5540.         $conn $this->getEntityManager()->getConnection();
  5541.         $where = [];
  5542.         $params = [];
  5543.         // --- Filtres de base ---
  5544.         if ($organisationId) {
  5545.             $where[] = 'p.organisation_id = :organisationId';
  5546.             $params['organisationId'] = $organisationId;
  5547.         } elseif ($pointOfSale !== null) {
  5548.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  5549.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  5550.         }
  5551.         if ($codeCluster) {
  5552.             $where[] = 'c.code_cluster = :codeCluster';
  5553.             $params['codeCluster'] = $codeCluster;
  5554.         }
  5555.         if ($departement) {
  5556.             $where[] = " c.code_cluster LIKE :prefix";
  5557.             $params['prefix'] = $departement '-%';
  5558.         }
  5559.         if ($codeInsee) {
  5560.             $where[] = 'p.code_insee = :codeInsee';
  5561.             $params['codeInsee'] = $codeInsee;
  5562.         }
  5563.         if ($perid) {
  5564.             $where[] = 'p.login_vendeur_init = :perid';
  5565.             $params['perid'] = $perid;
  5566.         }
  5567.         if (is_array($childs) && count($childs) > 0) {
  5568.             $placeholders = [];
  5569.             foreach ($childs as $i => $child) {
  5570.                 $placeholder ':child_' $i;
  5571.                 $placeholders[] = $placeholder;
  5572.                 $params['child_' $i] = $child;
  5573.             }
  5574.             $where[] = 'p.seller_id IN (' implode(', '$placeholders) . ')';
  5575.         }
  5576.         // --- Conditions fixes ---
  5577.         $where[] = 'p.date_resiliation IS NOT NULL';
  5578.         $where[] = 'p.date_racc IS NOT NULL';
  5579.         $where[] = 'DATEDIFF(p.date_racc, p.date_resiliation) > -31';
  5580.         if ($etat === "Raccorde") {
  5581.             $where[] = '(p.category_id IN (1,3) OR p.product_id IN (150, 262, 264, 266, 334, 335, 341, 344, 345, 379))';
  5582.         } else {
  5583.             $where[] = 'p.category_id NOT IN (2, 31, 32, 33)';
  5584.         }
  5585.         // --- Champ de date selon optionSelect ---
  5586.         switch ($optionSelect) {
  5587.             case 'V':
  5588.                 $dateField 'p.date_vente_valid_b';
  5589.                 break;
  5590.             case 'R':
  5591.                 $dateField 'p.date_racc';
  5592.                 break;
  5593.             case 'B':
  5594.             default:
  5595.                 $dateField 'p.date_cmd_a';
  5596.                 break;
  5597.         }
  5598.         // --- SELECT selon la période ---
  5599.         switch (strtolower($periode)) {
  5600.             case 'day':
  5601.                 $selectField "DATE($dateField) AS dateJour, MONTH($dateField) AS mois";
  5602.                 $alias 'dateJour';
  5603.                 $groupBy 'dateJour';
  5604.                 break;
  5605.             case 'week':
  5606.                 $selectField "WEEK($dateField,3) AS semaine";
  5607.                 $alias 'semaine';
  5608.                 $groupBy 'semaine';
  5609.                 break;
  5610.             case 'month':
  5611.             default:
  5612.                 $selectField "MONTH($dateField) AS mois";
  5613.                 $alias 'mois';
  5614.                 $groupBy 'mois';
  5615.                 break;
  5616.         }
  5617.         // --- Construction du SQL ---
  5618.         $sql "
  5619.             SELECT COUNT(DISTINCT p.id) AS total_vente, $selectField
  5620.         ";
  5621.         if ($organisationId) {
  5622.             $sql .= ",
  5623.                 po.code AS code_point,
  5624.                 po.name AS nom_point
  5625.             ";
  5626.         }
  5627.         $sql .= "
  5628.             FROM production p
  5629.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  5630.             LEFT JOIN cluster c ON p.cluster_id = c.id
  5631.             WHERE " implode(' AND '$where) . "
  5632.             AND YEAR($dateField) = :annee
  5633.         ";
  5634.         if ($organisationId) {
  5635.             $sql .= " GROUP BY po.id, $groupBy ORDER BY po.id, $groupBy";
  5636.         } else {
  5637.             $sql .= " GROUP BY $groupBy ORDER BY $groupBy";
  5638.         }
  5639.         $params['annee'] = $annee;
  5640.         $stmt $conn->prepare($sql);
  5641.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  5642.         // --- Limiter par jour aux deux derniers mois ---
  5643.         if (strtolower($periode) === 'day') {
  5644.             $currentMonth = (int)date('m');
  5645.             $previousMonth $currentMonth === 12 $currentMonth 1;
  5646.             $results array_values(array_filter($results, function ($row) use ($currentMonth$previousMonth) {
  5647.                 return ((int)$row['mois'] === $currentMonth || (int)$row['mois'] === $previousMonth);
  5648.             }));
  5649.         }
  5650.         // --- Réorganisation du résultat ---
  5651.         $mappedResults = [];
  5652.         foreach ($results as $row) {
  5653.             $total = (int)$row['total_vente'];
  5654.             if ($organisationId) {
  5655.                 $codePoint $row['code_point'] ?? 'N/A';
  5656.                 if (!isset($mappedResults[$codePoint])) {
  5657.                     $mappedResults[$codePoint] = [];
  5658.                 }
  5659.                 switch (strtolower($periode)) {
  5660.                     case 'day':
  5661.                         $mappedResults[$codePoint][$row['dateJour']] = $total;
  5662.                         break;
  5663.                     case 'week':
  5664.                         $mappedResults[$codePoint][$row['semaine']] = $total;
  5665.                         break;
  5666.                     case 'month':
  5667.                     default:
  5668.                         $mappedResults[$codePoint][$row['mois']] = $total;
  5669.                         break;
  5670.                 }
  5671.             } else {
  5672.                 switch (strtolower($periode)) {
  5673.                     case 'day':
  5674.                         $mappedResults[$row['dateJour']] = $total;
  5675.                         break;
  5676.                     case 'week':
  5677.                         $mappedResults[$row['semaine']] = $total;
  5678.                         break;
  5679.                     case 'month':
  5680.                     default:
  5681.                         $mappedResults[$row['mois']] = $total;
  5682.                         break;
  5683.                 }
  5684.             }
  5685.         }
  5686.         return $mappedResults;
  5687.     }
  5688.     public function getJ30MobileByPeriod(
  5689.         $pointOfSale,
  5690.         $codeCluster,
  5691.         $departement,
  5692.         $codeInsee,
  5693.         $annee,
  5694.         $optionSelect,
  5695.         $etat,
  5696.         $category,
  5697.         $childs,
  5698.         $organisationId,
  5699.         $perid,
  5700.         string $periode 'month' // day | week | month
  5701.     ): array {
  5702.         $conn $this->getEntityManager()->getConnection();
  5703.         $where = [];
  5704.         $params = [];
  5705.         // --- Filtre organisation ou point de vente ---
  5706.         if ($organisationId) {
  5707.             $where[] = 'p.organisation_id = :organisationId';
  5708.             $params['organisationId'] = $organisationId;
  5709.         } elseif ($pointOfSale !== null) {
  5710.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  5711.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  5712.         }
  5713.         if ($codeCluster) {
  5714.             $where[] = 'c.code_cluster = :codeCluster';
  5715.             $params['codeCluster'] = $codeCluster;
  5716.         }
  5717.         if ($departement) {
  5718.             $where[] = " c.code_cluster LIKE :prefix";
  5719.             $params['prefix'] = $departement '-%';
  5720.         }
  5721.         if ($codeInsee) {
  5722.             $where[] = 'p.code_insee = :codeInsee';
  5723.             $params['codeInsee'] = $codeInsee;
  5724.         }
  5725.         if ($perid) {
  5726.             $where[] = 'p.login_vendeur_init = :perid';
  5727.             $params['perid'] = $perid;
  5728.         }
  5729.         if (is_array($childs) && count($childs) > 0) {
  5730.             $placeholders = [];
  5731.             foreach ($childs as $i => $child) {
  5732.                 $placeholder ':child_' $i;
  5733.                 $placeholders[] = $placeholder;
  5734.                 $params['child_' $i] = $child;
  5735.             }
  5736.             $where[] = 'p.seller_id IN (' implode(', '$placeholders) . ')';
  5737.         }
  5738.         // --- Conditions fixes ---
  5739.         $where[] = 'p.date_resiliation IS NOT NULL';
  5740.         $where[] = 'p.date_racc IS NOT NULL';
  5741.         $where[] = 'DATEDIFF(p.date_racc, p.date_resiliation) > -31';
  5742.         $where[] = 'p.category_id IN (31, 32, 33)';
  5743.         if ($etat === "Raccorde_mobile") {
  5744.             $where[] = 'p.product_id NOT IN (150,262,264,266,334,335,341,344,345,379)';
  5745.         }
  5746.         // --- Champ de date selon optionSelect ---
  5747.         switch ($optionSelect) {
  5748.             case 'V':
  5749.                 $dateField 'p.date_vente_valid_b';
  5750.                 break;
  5751.             case 'R':
  5752.                 $dateField 'p.date_racc';
  5753.                 break;
  5754.             case 'B':
  5755.             default:
  5756.                 $dateField 'p.date_cmd_a';
  5757.                 break;
  5758.         }
  5759.         // --- Sélection et groupement selon la période ---
  5760.         switch (strtolower($periode)) {
  5761.             case 'day':
  5762.                 $selectField "DATE($dateField) AS dateJour, MONTH($dateField) AS mois";
  5763.                 $alias 'dateJour';
  5764.                 $groupBy 'dateJour';
  5765.                 break;
  5766.             case 'week':
  5767.                 $selectField "WEEK($dateField,3) AS semaine";
  5768.                 $alias 'semaine';
  5769.                 $groupBy 'semaine';
  5770.                 break;
  5771.             case 'month':
  5772.             default:
  5773.                 $selectField "MONTH($dateField) AS mois";
  5774.                 $alias 'mois';
  5775.                 $groupBy 'mois';
  5776.                 break;
  5777.         }
  5778.         // --- Construction du SQL ---
  5779.         $sql "
  5780.             SELECT COUNT(DISTINCT p.id) AS total_vente, $selectField
  5781.         ";
  5782.         if ($organisationId) {
  5783.             $sql .= ",
  5784.                 po.code AS code_point,
  5785.                 po.name AS nom_point
  5786.             ";
  5787.         }
  5788.         $sql .= "
  5789.             FROM production p
  5790.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  5791.             LEFT JOIN cluster c ON p.cluster_id = c.id
  5792.             WHERE " implode(' AND '$where) . "
  5793.             AND YEAR($dateField) = :annee
  5794.         ";
  5795.         if ($organisationId) {
  5796.             $sql .= " GROUP BY po.id, $groupBy ORDER BY po.id, $groupBy";
  5797.         } else {
  5798.             $sql .= " GROUP BY $groupBy ORDER BY $groupBy";
  5799.         }
  5800.         $params['annee'] = $annee;
  5801.         $stmt $conn->prepare($sql);
  5802.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  5803.         // --- Limiter les jours aux deux derniers mois ---
  5804.         if (strtolower($periode) === 'day') {
  5805.             $currentMonth  = (int)date('m');
  5806.             $previousMonth $currentMonth === 12 $currentMonth 1;
  5807.             $results array_values(array_filter($results, function ($row) use ($currentMonth$previousMonth) {
  5808.                 return ((int)$row['mois'] === $currentMonth || (int)$row['mois'] === $previousMonth);
  5809.             }));
  5810.         }
  5811.         // --- Réorganisation des résultats ---
  5812.         $mappedResults = [];
  5813.         foreach ($results as $row) {
  5814.             $total = (int)$row['total_vente'];
  5815.             if ($organisationId) {
  5816.                 $codePoint $row['code_point'] ?? 'N/A';
  5817.                 if (!isset($mappedResults[$codePoint])) {
  5818.                     $mappedResults[$codePoint] = [];
  5819.                 }
  5820.                 switch (strtolower($periode)) {
  5821.                     case 'day':
  5822.                         $mappedResults[$codePoint][$row['dateJour']] = $total;
  5823.                         break;
  5824.                     case 'week':
  5825.                         $mappedResults[$codePoint][$row['semaine']] = $total;
  5826.                         break;
  5827.                     case 'month':
  5828.                     default:
  5829.                         $mappedResults[$codePoint][$row['mois']] = $total;
  5830.                         break;
  5831.                 }
  5832.             } else {
  5833.                 switch (strtolower($periode)) {
  5834.                     case 'day':
  5835.                         $mappedResults[$row['dateJour']] = $total;
  5836.                         break;
  5837.                     case 'week':
  5838.                         $mappedResults[$row['semaine']] = $total;
  5839.                         break;
  5840.                     case 'month':
  5841.                     default:
  5842.                         $mappedResults[$row['mois']] = $total;
  5843.                         break;
  5844.                 }
  5845.             }
  5846.         }
  5847.         return $mappedResults;
  5848.     }
  5849.     public function getMrzByPeriod(
  5850.         $pointOfSale,
  5851.         $codeCluster,
  5852.         $departement,
  5853.         $codeInsee,
  5854.         $annee,
  5855.         $optionSelect,
  5856.         $filtre,
  5857.         $childs,
  5858.         $organisationId,
  5859.         $perid,
  5860.         string $periode 'month' // day | week | month
  5861.     ): array {
  5862.         $conn $this->getEntityManager()->getConnection();
  5863.         $where = [];
  5864.         $params = [];
  5865.         // --- Filtre organisation ou point de vente ---
  5866.         if ($organisationId) {
  5867.             $where[] = 'p.organisation_id = :organisationId';
  5868.             $params['organisationId'] = $organisationId;
  5869.         } elseif ($pointOfSale !== null) {
  5870.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  5871.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  5872.         }
  5873.         if ($codeCluster) {
  5874.             $where[] = 'c.code_cluster = :codeCluster';
  5875.             $params['codeCluster'] = $codeCluster;
  5876.         }
  5877.         if ($departement) {
  5878.             $where[] = "c.code_cluster LIKE :prefix";
  5879.             $params['prefix'] = $departement '-%';
  5880.         }
  5881.         if ($codeInsee) {
  5882.             $where[] = 'p.code_insee = :codeInsee';
  5883.             $params['codeInsee'] = $codeInsee;
  5884.         }
  5885.         if (is_array($childs) && count($childs) > 0) {
  5886.             $placeholders = [];
  5887.             foreach ($childs as $i => $child) {
  5888.                 $placeholder ':child_' $i;
  5889.                 $placeholders[] = $placeholder;
  5890.                 $params['child_' $i] = $child;
  5891.             }
  5892.             $where[] = 'p.seller_id IN (' implode(', '$placeholders) . ')';
  5893.         }
  5894.         if ($perid) {
  5895.             $where[] = 'p.login_vendeur_init = :perid';
  5896.             $params['perid'] = $perid;
  5897.         }
  5898.         $where[] = 'p.identity_ctrl IS NOT NULL';
  5899.         // --- Choix du champ de date ---
  5900.         switch ($optionSelect) {
  5901.             case 'V':
  5902.                 $dateField 'p.date_vente_valid_b';
  5903.                 break;
  5904.             case 'R':
  5905.                 $dateField 'p.date_racc';
  5906.                 break;
  5907.             case 'B':
  5908.             default:
  5909.                 $dateField 'p.date_cmd_a';
  5910.                 break;
  5911.         }
  5912.         // --- Définition du SELECT selon la période ---
  5913.         switch (strtolower($periode)) {
  5914.             case 'day':
  5915.                 $selectField "DATE($dateField)";
  5916.                 $alias 'jour';
  5917.                 $startDate = (new \DateTime('first day of last month'))->format('Y-m-d');
  5918.                 $endDate   = (new \DateTime('last day of this month'))->format('Y-m-d');
  5919.                 $where[] = "$dateField BETWEEN :startDate AND :endDate";
  5920.                 $params['startDate'] = $startDate;
  5921.                 $params['endDate']   = $endDate;
  5922.                 break;
  5923.             case 'week':
  5924.                 $selectField "WEEK($dateField, 3)";
  5925.                 $alias 'semaine';
  5926.                 $where[] = "YEAR($dateField) = :annee";
  5927.                 $params['annee'] = $annee;
  5928.                 break;
  5929.             case 'month':
  5930.             default:
  5931.                 $selectField "MONTH($dateField)";
  5932.                 $alias 'mois';
  5933.                 $where[] = "YEAR($dateField) = :annee";
  5934.                 $params['annee'] = $annee;
  5935.                 break;
  5936.         }
  5937.         // --- Construction du SQL ---
  5938.         $sql "
  5939.             SELECT 
  5940.                 COUNT(p.id) AS total_vente,
  5941.                 p.identity_ctrl,
  5942.                 $selectField AS $alias
  5943.         ";
  5944.         if ($organisationId) {
  5945.             $sql .= ",
  5946.                 po.code AS code_point,
  5947.                 po.name AS nom_point
  5948.             ";
  5949.         }
  5950.         $sql .= "
  5951.             FROM production p
  5952.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  5953.             LEFT JOIN cluster c ON p.cluster_id = c.id
  5954.             WHERE " implode(' AND '$where) . "
  5955.         ";
  5956.         if ($organisationId) {
  5957.             $sql .= " GROUP BY po.id, p.identity_ctrl, $selectField ORDER BY po.id, $selectField";
  5958.         } else {
  5959.             $sql .= " GROUP BY p.identity_ctrl, $selectField ORDER BY $selectField";
  5960.         }
  5961.         // --- Exécution ---
  5962.         $stmt $conn->prepare($sql);
  5963.         $rows $stmt->executeQuery($params)->fetchAllAssociative();
  5964.         // --- Structuration du résultat ---
  5965.         $structuredResult = [];
  5966.         foreach ($rows as $row) {
  5967.             $periodValue $row[$alias];
  5968.             $entry = [
  5969.                 'identity_ctrl' => $row['identity_ctrl'],
  5970.                 'total_vente'   => (int)$row['total_vente']
  5971.             ];
  5972.             if ($organisationId) {
  5973.                 $codePoint $row['code_point'] ?? 'N/A';
  5974.                 if (!isset($structuredResult[$codePoint])) {
  5975.                     $structuredResult[$codePoint] = [];
  5976.                 }
  5977.                 if (!isset($structuredResult[$codePoint][$periodValue])) {
  5978.                     $structuredResult[$codePoint][$periodValue] = [];
  5979.                 }
  5980.                 $structuredResult[$codePoint][$periodValue][] = $entry;
  5981.             } else {
  5982.                 if (!isset($structuredResult[$periodValue])) {
  5983.                     $structuredResult[$periodValue] = [];
  5984.                 }
  5985.                 $structuredResult[$periodValue][] = $entry;
  5986.             }
  5987.         }
  5988.         return $structuredResult;
  5989.     }
  5990.     public function getClientsHorsMigByPeriod(
  5991.         $pointOfSale,
  5992.         $codeCluster,
  5993.         $departement,
  5994.         $codeInsee,
  5995.         $annee,
  5996.         $optionSelect,
  5997.         $filtre,
  5998.         $childs,
  5999.         $organisationId,
  6000.         $perid,
  6001.         string $periode 'month' // day | week | month
  6002.     ): array {
  6003.         $conn $this->getEntityManager()->getConnection();
  6004.         $where = [];
  6005.         $params = [];
  6006.         // Filtre organisation ou pointOfSale
  6007.         if ($organisationId) {
  6008.             $where[] = 'p.organisation_id = :organisationId';
  6009.             $params['organisationId'] = $organisationId;
  6010.         } elseif ($pointOfSale !== null) {
  6011.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  6012.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6013.         }
  6014.         if ($codeCluster) {
  6015.             $where[] = 'c.code_cluster = :codeCluster';
  6016.             $params['codeCluster'] = $codeCluster;
  6017.         }
  6018.         if ($departement) {
  6019.             $where[] = " c.code_cluster LIKE :prefix";
  6020.             $params['prefix'] = $departement '-%';
  6021.         }
  6022.         if ($codeInsee) {
  6023.             $where[] = 'p.code_insee = :codeInsee';
  6024.             $params['codeInsee'] = $codeInsee;
  6025.         }
  6026.         // child sellers
  6027.         if (is_array($childs) && count($childs) > 0) {
  6028.             $placeholders = [];
  6029.             foreach ($childs as $i => $child) {
  6030.                 $placeholder ':child_' $i;
  6031.                 $placeholders[] = $placeholder;
  6032.                 $params['child_' $i] = $child;
  6033.             }
  6034.             $where[] = 'p.seller_id IN (' implode(', '$placeholders) . ')';
  6035.         }
  6036.         // Conditions fixes
  6037.         $where[] = 'p.category_id NOT IN (2)';
  6038.         // Champ de date
  6039.         $dateField 'p.date_cmd_a';
  6040.         // Sélection et groupement selon la période
  6041.         switch (strtolower($periode)) {
  6042.             case 'day':
  6043.                 $selectField "DATE($dateField)";
  6044.                 $alias 'jour';
  6045.                 $startDate = (new \DateTime('first day of last month'))->format('Y-m-d');
  6046.                 $endDate   = (new \DateTime('last day of this month'))->format('Y-m-d');
  6047.                 $where[] = "$dateField BETWEEN :startDate AND :endDate";
  6048.                 $params['startDate'] = $startDate;
  6049.                 $params['endDate']   = $endDate;
  6050.                 break;
  6051.             case 'week':
  6052.                 $selectField "WEEK($dateField,3)";
  6053.                 $alias 'semaine';
  6054.                 $where[] = "YEAR($dateField) = :annee";
  6055.                 $params['annee'] = $annee;
  6056.                 break;
  6057.             case 'month':
  6058.             default:
  6059.                 $selectField "MONTH($dateField)";
  6060.                 $alias 'mois';
  6061.                 $where[] = "YEAR($dateField) = :annee";
  6062.                 $params['annee'] = $annee;
  6063.                 break;
  6064.         }
  6065.         $sql "
  6066.             SELECT 
  6067.                 COUNT(DISTINCT p.titulaire_email) AS total_vente,
  6068.                 $selectField AS $alias
  6069.         ";
  6070.         if ($organisationId) {
  6071.             $sql .= ",
  6072.                 po.code AS code_point,
  6073.                 po.name AS nom_point
  6074.             ";
  6075.         }
  6076.         $sql .= "
  6077.             FROM production p
  6078.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  6079.             LEFT JOIN cluster c ON p.cluster_id = c.id
  6080.             WHERE " implode(' AND '$where);
  6081.         // GROUP BY selon présence organisationId
  6082.         if ($organisationId) {
  6083.             $sql .= " GROUP BY po.id, $selectField ORDER BY po.id, $selectField";
  6084.         } else {
  6085.             $sql .= " GROUP BY $selectField ORDER BY $selectField";
  6086.         }
  6087.         $stmt $conn->prepare($sql);
  6088.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  6089.         // --- Structuration du résultat ---
  6090.         $structuredResult = [];
  6091.         foreach ($results as $row) {
  6092.             $periodValue $row[$alias];
  6093.             $total = (int)$row['total_vente'];
  6094.             if ($organisationId) {
  6095.                 $codePoint $row['code_point'] ?? 'N/A';
  6096.                 if (!isset($structuredResult[$codePoint])) {
  6097.                     $structuredResult[$codePoint] = [];
  6098.                 }
  6099.                 $structuredResult[$codePoint][$periodValue] = $total;
  6100.             } else {
  6101.                 $structuredResult[$periodValue] = $total;
  6102.             }
  6103.         }
  6104.         return $structuredResult;
  6105.     }
  6106.     public function getProductionsAnalyticsTotalVenteByOptionByPeriod(
  6107.         $pointOfSale,
  6108.         ?string $codeCluster,
  6109.         $codeInsee,
  6110.         int $annee,
  6111.         array $childs,
  6112.         ?int $organisationId,
  6113.         ?string $optionCalcul,
  6114.         ?string $perid,
  6115.         string $periode 'month' // day | week | month
  6116.     ): array {
  6117.         $conn $this->getEntityManager()->getConnection();
  6118.         $dateField 'p.date_cmd_a'// par défaut
  6119.         switch ($periode) {
  6120.             case 'day':
  6121.                 $groupField "DATE($dateField)"// date complète
  6122.                 $alias 'parJour';
  6123.                 break;
  6124.             case 'week':
  6125.                 $groupField "WEEK($dateField,3)";
  6126.                 $alias 'parSemaine';
  6127.                 break;
  6128.             case 'month':
  6129.             default:
  6130.                 $groupField "MONTH($dateField)";
  6131.                 $alias 'parMois';
  6132.                 break;
  6133.         }
  6134.         $sql "
  6135.             SELECT COUNT(DISTINCT p.id) AS total_vente, $groupField AS $alias
  6136.             FROM production p
  6137.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  6138.             WHERE p.category_id IN (1,3)
  6139.               AND YEAR($dateField) = :annee
  6140.         ";
  6141.         $params = ['annee' => $annee];
  6142.         // Limiter aux jours du mois courant et précédent si parJour
  6143.         if ($periode === 'day') {
  6144.             $sql .= " AND $dateField >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')
  6145.                       AND $dateField <= LAST_DAY(CURRENT_DATE)";
  6146.         }
  6147.         if ($organisationId) {
  6148.             $sql .= " AND p.organisation_id = :organisationId";
  6149.             $params['organisationId'] = $organisationId;
  6150.         } elseif ($pointOfSale !== null) {
  6151.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  6152.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6153.         }
  6154.         if (!empty($childs)) {
  6155.             $placeholders = [];
  6156.             foreach ($childs as $i => $child) {
  6157.                 $key ':child_' $i;
  6158.                 $placeholders[] = $key;
  6159.                 $params['child_' $i] = $child;
  6160.             }
  6161.             $sql .= " AND p.seller_id IN (" implode(','$placeholders) . ")";
  6162.         }
  6163.         if ($optionCalcul) {
  6164.             switch ($optionCalcul) {
  6165.                 case 'RIO':
  6166.                     $sql .= " AND p.portabilite = 'OUI'";
  6167.                     break;
  6168.                 case 'PTO_SAISIE':
  6169.                     $sql .= " AND p.prise_saisie_commande = 'OUI'";
  6170.                     break;
  6171.                 case 'PTO_NON_SAISIE':
  6172.                     $sql .= " AND p.prise_saisie_commande = 'NON' AND p.prise_existante_commande = 'OUI'";
  6173.                     break;
  6174.                 case 'PTO_EXISTANTE':
  6175.                     $sql .= " AND p.prise_existante_commande = 'OUI'";
  6176.                     break;
  6177.                 case 'PTO_NON_EXISTANTE':
  6178.                     $sql .= " AND p.prise_existante_commande = 'NON'";
  6179.                     break;
  6180.             }
  6181.             if ($perid) {
  6182.                 $sql .= " AND p.login_vendeur_init = :perid";
  6183.                 $params['perid'] = $perid;
  6184.             }
  6185.             if ($codeCluster) {
  6186.                 $sql .= " AND c.code_cluster = :codeCluster";
  6187.                 $params['codeCluster'] = $codeCluster;
  6188.             }
  6189.             if ($codeInsee) {
  6190.                 $sql .= " AND p.code_insee = :codeInsee";
  6191.                 $params['codeInsee'] = $codeInsee;
  6192.             }
  6193.         }
  6194.         $sql .= " GROUP BY $groupField ORDER BY $alias";
  6195.         $stmt $conn->prepare($sql);
  6196.         return $stmt->executeQuery($params)->fetchAllAssociative();
  6197.     }
  6198.     public function getProductionsAnalyticsTotalVenteByOptionByPeriod2(
  6199.         $pointOfSale,
  6200.         ?string $codeCluster,
  6201.         $departement,
  6202.         $codeInsee,
  6203.         int $annee,
  6204.         array $childs,
  6205.         ?int $organisationId,
  6206.         ?string $optionCalcul,
  6207.         ?string $perid,
  6208.         string $periode 'month' // day | week | month
  6209.     ): array {
  6210.         $conn $this->getEntityManager()->getConnection();
  6211.         $dateField 'p.date_cmd_a'// par défaut
  6212.         switch ($periode) {
  6213.             case 'day':
  6214.                 $groupField "DATE($dateField)";
  6215.                 $alias 'parJour';
  6216.                 break;
  6217.             case 'week':
  6218.                 $groupField "WEEK($dateField,3)";
  6219.                 $alias 'parSemaine';
  6220.                 break;
  6221.             case 'month':
  6222.             default:
  6223.                 $groupField "MONTH($dateField)";
  6224.                 $alias 'parMois';
  6225.                 break;
  6226.         }
  6227.         $sql "SELECT COUNT(DISTINCT p.id) AS total_vente, $groupField AS $alias";
  6228.         if ($organisationId) {
  6229.             $sql .= ", po.code AS code_point, po.name AS nom_point";
  6230.         }
  6231.         $sql .= " FROM production p
  6232.                   LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  6233.                   LEFT JOIN cluster c ON p.cluster_id = c.id
  6234.                   WHERE p.category_id IN (1,3)
  6235.                   AND YEAR($dateField) = :annee";
  6236.         $params = ['annee' => $annee];
  6237.         if ($periode === 'day') {
  6238.             $sql .= " AND $dateField >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')
  6239.                       AND $dateField <= LAST_DAY(CURRENT_DATE)";
  6240.         }
  6241.         if ($organisationId) {
  6242.             $sql .= " AND p.organisation_id = :organisationId";
  6243.             $params['organisationId'] = $organisationId;
  6244.         } elseif ($pointOfSale !== null) {
  6245.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  6246.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6247.         }
  6248.         if (!empty($childs)) {
  6249.             $placeholders = [];
  6250.             foreach ($childs as $i => $child) {
  6251.                 $key ':child_' $i;
  6252.                 $placeholders[] = $key;
  6253.                 $params['child_' $i] = $child;
  6254.             }
  6255.             $sql .= " AND p.seller_id IN (" implode(','$placeholders) . ")";
  6256.         }
  6257.         if ($optionCalcul) {
  6258.             switch ($optionCalcul) {
  6259.                 case 'RIO':
  6260.                     $sql .= " AND p.portabilite = 'OUI'";
  6261.                     break;
  6262.                 case 'PTO_SAISIE':
  6263.                     $sql .= " AND p.prise_saisie_commande = 'OUI'";
  6264.                     break;
  6265.                 case 'PTO_NON_SAISIE':
  6266.                     $sql .= " AND p.prise_saisie_commande = 'NON' AND p.prise_existante_commande = 'OUI'";
  6267.                     break;
  6268.                 case 'PTO_EXISTANTE':
  6269.                     $sql .= " AND p.prise_existante_commande = 'OUI'";
  6270.                     break;
  6271.                 case 'PTO_NON_EXISTANTE':
  6272.                     $sql .= " AND p.prise_existante_commande = 'NON'";
  6273.                     break;
  6274.             }
  6275.             if ($perid) {
  6276.                 $sql .= " AND p.login_vendeur_init = :perid";
  6277.                 $params['perid'] = $perid;
  6278.             }
  6279.             if ($codeCluster) {
  6280.                 $sql .= " AND c.code_cluster = :codeCluster";
  6281.                 $params['codeCluster'] = $codeCluster;
  6282.             }
  6283.             if ($departement) {
  6284.                 $sql .= " AND c.code_cluster LIKE :prefix";
  6285.                 $params['prefix'] = $departement '-%';
  6286.             }
  6287.             if ($codeInsee) {
  6288.                 $sql .= " AND p.code_insee = :codeInsee";
  6289.                 $params['codeInsee'] = $codeInsee;
  6290.             }
  6291.         }
  6292.         // GROUP BY selon organisationId
  6293.         if ($organisationId) {
  6294.             $sql .= " GROUP BY po.id, $groupField ORDER BY po.id, $groupField";
  6295.         } else {
  6296.             $sql .= " GROUP BY $groupField ORDER BY $groupField";
  6297.         }
  6298.         $stmt $conn->prepare($sql);
  6299.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  6300.         // Structuration
  6301.         $structuredResult = [];
  6302.         foreach ($results as $row) {
  6303.             $periodValue $row[$alias];
  6304.             $total = (int)$row['total_vente'];
  6305.             if ($organisationId) {
  6306.                 $codePoint $row['code_point'] ?? 'N/A';
  6307.                 if (!isset($structuredResult[$codePoint])) {
  6308.                     $structuredResult[$codePoint] = [];
  6309.                 }
  6310.                 $structuredResult[$codePoint][$periodValue] = $total;
  6311.             } else {
  6312.                 $structuredResult[$periodValue] = $total;
  6313.             }
  6314.         }
  6315.         return $structuredResult;
  6316.     }
  6317.     public function getVentesVlaConqueteByPeriod(
  6318.         $pointOfSale,
  6319.         ?string $codeCluster,
  6320.         $codeInsee,
  6321.         int $annee,
  6322.         string $optionSelect,
  6323.         ?string $category,
  6324.         array $childs = [],
  6325.         ?int $organisationId null,
  6326.         ?string $perid null,
  6327.         string $periode 'week' // day | week | month
  6328.     ): array {
  6329.         $conn $this->getEntityManager()->getConnection();
  6330.         // Choix du champ date
  6331.         switch ($optionSelect) {
  6332.             case 'V':
  6333.                 $dateField 'date_vente_valid_b';
  6334.                 break;
  6335.             case 'R':
  6336.                 $dateField 'date_racc';
  6337.                 break;
  6338.             case 'B':
  6339.             default:
  6340.                 $dateField 'date_cmd_a';
  6341.                 break;
  6342.         }
  6343.         // Choix de la fonction de groupement
  6344.         switch (strtolower($periode)) {
  6345.             case 'day':
  6346.                 $groupField "DATE(p.$dateField)"// date complète
  6347.                 $alias 'parJour';
  6348.                 break;
  6349.             case 'month':
  6350.                 $groupField "MONTH(p.$dateField)";
  6351.                 $alias 'parMois';
  6352.                 break;
  6353.             case 'week':
  6354.             default:
  6355.                 $groupField "WEEK(p.$dateField, 3)";
  6356.                 $alias 'parSemaine';
  6357.                 break;
  6358.         }
  6359.         $sql "
  6360.             SELECT COUNT(p.id) AS total_ventes, $groupField AS $alias
  6361.             FROM production p
  6362.             LEFT JOIN cluster c ON p.cluster_id = c.id
  6363.             WHERE YEAR(p.$dateField) = :annee
  6364.         ";
  6365.         $params = ['annee' => $annee];
  6366.         // Limiter aux jours du mois courant et précédent si parJour
  6367.         if (strtolower($periode) === 'day') {
  6368.             $sql .= " AND p.$dateField >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')
  6369.                       AND p.$dateField <= LAST_DAY(CURRENT_DATE)";
  6370.         }
  6371.         // pointOfSale / organisation
  6372.         if ($organisationId) {
  6373.             $sql .= " AND p.organisation_id = :organisationId";
  6374.             $params['organisationId'] = $organisationId;
  6375.         } elseif ($pointOfSale !== null) {
  6376.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  6377.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6378.         }
  6379.         // cluster
  6380.         if ($codeCluster) {
  6381.             $sql .= " AND c.code_cluster = :codeCluster";
  6382.             $params['codeCluster'] = $codeCluster;
  6383.         }
  6384.         if ($codeInsee) {
  6385.             $sql .= " AND p.code_insee = :codeInsee";
  6386.             $params['codeInsee'] = $codeInsee;
  6387.         }
  6388.         // category
  6389.         if ($category) {
  6390.             $categoryArray array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  6391.             if (!empty($categoryArray)) {
  6392.                 $placeholders = [];
  6393.                 foreach ($categoryArray as $k => $catId) {
  6394.                     $key 'cat' $k;
  6395.                     $placeholders[] = ':' $key;
  6396.                     $params[$key] = $catId;
  6397.                 }
  6398.                 $sql .= " AND p.category_id IN (" implode(','$placeholders) . ")";
  6399.             }
  6400.         }
  6401.         // perid
  6402.         if ($perid) {
  6403.             $sql .= " AND p.login_vendeur_init = :perid";
  6404.             $params['perid'] = $perid;
  6405.         }
  6406.         // childs
  6407.         if (!empty($childs)) {
  6408.             $placeholders = [];
  6409.             foreach ($childs as $i => $child) {
  6410.                 $key 'child_' $i;
  6411.                 $placeholders[] = ':' $key;
  6412.                 $params[$key] = $child;
  6413.             }
  6414.             $sql .= " AND p.seller_id IN (" implode(','$placeholders) . ")";
  6415.         }
  6416.         $sql .= " GROUP BY $groupField ORDER BY $alias";
  6417.         $stmt $conn->prepare($sql);
  6418.         return $stmt->executeQuery($params)->fetchAllAssociative();
  6419.     }
  6420.     public function getVentesVlaConqueteByPeriod2(
  6421.         $pointOfSale,
  6422.         ?string $codeCluster,
  6423.         $departement,
  6424.         $codeInsee,
  6425.         int $annee,
  6426.         string $optionSelect,
  6427.         ?string $category,
  6428.         array $childs = [],
  6429.         ?int $organisationId null,
  6430.         ?string $perid null,
  6431.         string $periode 'week' // day | week | month
  6432.     ): array {
  6433.         $conn $this->getEntityManager()->getConnection();
  6434.         // Choix du champ date
  6435.         switch ($optionSelect) {
  6436.             case 'V':
  6437.                 $dateField 'date_vente_valid_b';
  6438.                 break;
  6439.             case 'R':
  6440.                 $dateField 'date_racc';
  6441.                 break;
  6442.             case 'B':
  6443.             default:
  6444.                 $dateField 'date_cmd_a';
  6445.                 break;
  6446.         }
  6447.         // Choix de la fonction de groupement
  6448.         switch (strtolower($periode)) {
  6449.             case 'day':
  6450.                 $groupField "DATE(p.$dateField)";
  6451.                 $alias 'parJour';
  6452.                 break;
  6453.             case 'month':
  6454.                 $groupField "MONTH(p.$dateField)";
  6455.                 $alias 'parMois';
  6456.                 break;
  6457.             case 'week':
  6458.             default:
  6459.                 $groupField "WEEK(p.$dateField, 3)";
  6460.                 $alias 'parSemaine';
  6461.                 break;
  6462.         }
  6463.         $sql "SELECT COUNT(p.id) AS total_ventes, $groupField AS $alias";
  6464.         if ($organisationId) {
  6465.             $sql .= ", po.code AS code_point, po.name AS nom_point";
  6466.         }
  6467.         $sql .= " FROM production p
  6468.                   LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  6469.                   LEFT JOIN cluster c ON p.cluster_id = c.id
  6470.                   WHERE YEAR(p.$dateField) = :annee";
  6471.         $params = ['annee' => $annee];
  6472.         if (strtolower($periode) === 'day') {
  6473.             $sql .= " AND p.$dateField >= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-01')
  6474.                       AND p.$dateField <= LAST_DAY(CURRENT_DATE)";
  6475.         }
  6476.         if ($organisationId) {
  6477.             $sql .= " AND p.organisation_id = :organisationId";
  6478.             $params['organisationId'] = $organisationId;
  6479.         } elseif ($pointOfSale !== null) {
  6480.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  6481.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6482.         }
  6483.         if ($codeCluster) {
  6484.             $sql .= " AND c.code_cluster = :codeCluster";
  6485.             $params['codeCluster'] = $codeCluster;
  6486.         }
  6487.         if ($departement) {
  6488.             $sql .= " AND c.code_cluster LIKE :prefix";
  6489.             $params['prefix'] = $departement '-%';
  6490.         }
  6491.         if ($codeInsee) {
  6492.             $sql .= " AND p.code_insee = :codeInsee";
  6493.             $params['codeInsee'] = $codeInsee;
  6494.         }
  6495.         if ($category) {
  6496.             $categoryArray array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  6497.             if (!empty($categoryArray)) {
  6498.                 $placeholders = [];
  6499.                 foreach ($categoryArray as $k => $catId) {
  6500.                     $key 'cat' $k;
  6501.                     $placeholders[] = ':' $key;
  6502.                     $params[$key] = $catId;
  6503.                 }
  6504.                 $sql .= " AND p.category_id IN (" implode(','$placeholders) . ")";
  6505.             }
  6506.         }
  6507.         if ($perid) {
  6508.             $sql .= " AND p.login_vendeur_init = :perid";
  6509.             $params['perid'] = $perid;
  6510.         }
  6511.         if (!empty($childs)) {
  6512.             $placeholders = [];
  6513.             foreach ($childs as $i => $child) {
  6514.                 $key 'child_' $i;
  6515.                 $placeholders[] = ':' $key;
  6516.                 $params[$key] = $child;
  6517.             }
  6518.             $sql .= " AND p.seller_id IN (" implode(','$placeholders) . ")";
  6519.         }
  6520.         // GROUP BY selon organisationId
  6521.         if ($organisationId) {
  6522.             $sql .= " GROUP BY po.id, $groupField ORDER BY po.id, $groupField";
  6523.         } else {
  6524.             $sql .= " GROUP BY $groupField ORDER BY $groupField";
  6525.         }
  6526.         $stmt $conn->prepare($sql);
  6527.         $results $stmt->executeQuery($params)->fetchAllAssociative();
  6528.         // Structuration du résultat
  6529.         $structuredResult = [];
  6530.         foreach ($results as $row) {
  6531.             $periodValue $row[$alias];
  6532.             $total = (int)$row['total_ventes'];
  6533.             if ($organisationId) {
  6534.                 $codePoint $row['code_point'] ?? 'N/A';
  6535.                 if (!isset($structuredResult[$codePoint])) {
  6536.                     $structuredResult[$codePoint] = [];
  6537.                 }
  6538.                 $structuredResult[$codePoint][$periodValue] = $total;
  6539.             } else {
  6540.                 $structuredResult[$periodValue] = $total;
  6541.             }
  6542.         }
  6543.         return $structuredResult;
  6544.     }
  6545.     public function getProjectionByPeriod(
  6546.         $pointOfSale,
  6547.         $codeCluster,
  6548.         $departement,
  6549.         $codeInsee,
  6550.         $annee,
  6551.         $optionSelect,
  6552.         $category,
  6553.         $childs,
  6554.         $organisationId,
  6555.         $perid,
  6556.         string $periode 'month' // day | week | month
  6557.     ): array {
  6558.         $conn $this->getEntityManager()->getConnection();
  6559.         // Champ de date selon l'option
  6560.         switch ($optionSelect) {
  6561.             case 'V':
  6562.                 $dateField 'p.date_vente_valid_b';
  6563.                 break;
  6564.             case 'R':
  6565.                 $dateField 'p.date_racc';
  6566.                 break;
  6567.             case 'B':
  6568.             default:
  6569.                 $dateField 'p.date_cmd_a';
  6570.                 break;
  6571.         }
  6572.         $where = [];
  6573.         $params = [];
  6574.         // Filtres principaux
  6575.         if ($organisationId) {
  6576.             $where[] = 'p.organisation_id = :organisationId';
  6577.             $params['organisationId'] = $organisationId;
  6578.         } elseif ($pointOfSale !== null) {
  6579.             $where[] = 'p.point_of_sale_id = :pointOfSale';
  6580.             $params['pointOfSale'] = is_object($pointOfSale) ? $pointOfSale->getId() : $pointOfSale;
  6581.         }
  6582.         if ($codeCluster) {
  6583.             $where[] = 'c.code_cluster = :codeCluster';
  6584.             $params['codeCluster'] = $codeCluster;
  6585.         }
  6586.         if ($departement) {
  6587.             $where[] = " c.code_cluster LIKE :prefix";
  6588.             $params['prefix'] = $departement '-%';
  6589.         }
  6590.         if ($codeInsee) {
  6591.             $where[] = 'p.code_insee = :codeInsee';
  6592.             $params['codeInsee'] = $codeInsee;
  6593.         }
  6594.         if ($perid) {
  6595.             $where[] = 'p.login_vendeur_init = :perid';
  6596.             $params['perid'] = $perid;
  6597.         }
  6598.         // Filtre catégories
  6599.         if ($category) {
  6600.             $categoryArray array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  6601.             if (!empty($categoryArray)) {
  6602.                 $placeholders = [];
  6603.                 foreach ($categoryArray as $i => $catId) {
  6604.                     $key 'cat' $i;
  6605.                     $placeholders[] = ':' $key;
  6606.                     $params[$key] = $catId;
  6607.                 }
  6608.                 $where[] = 'p.category_id IN (' implode(','$placeholders) . ')';
  6609.             }
  6610.         }
  6611.         // Filtre vendeurs enfants
  6612.         if (is_array($childs) && count($childs) > 0) {
  6613.             $placeholders = [];
  6614.             foreach ($childs as $i => $child) {
  6615.                 $key 'child_' $i;
  6616.                 $placeholders[] = ':' $key;
  6617.                 $params[$key] = $child;
  6618.             }
  6619.             $where[] = 'p.seller_id IN (' implode(','$placeholders) . ')';
  6620.         }
  6621.         // Select et group selon la période
  6622.         switch (strtolower($periode)) {
  6623.             case 'day':
  6624.                 $selectField "DATE($dateField)";
  6625.                 $alias 'jour';
  6626.                 $currentMonth = (int)date('n');
  6627.                 $previousMonth = (int)((new \DateTime('first day of last month'))->format('n'));
  6628.                 $where[] = "MONTH($dateField) IN (:currentMonth, :previousMonth)";
  6629.                 $params['currentMonth'] = $currentMonth;
  6630.                 $params['previousMonth'] = $previousMonth;
  6631.                 break;
  6632.             case 'week':
  6633.                 $selectField "WEEK($dateField, 3)";
  6634.                 $alias 'semaine';
  6635.                 $where[] = "YEAR($dateField) = :annee";
  6636.                 $params['annee'] = $annee;
  6637.                 break;
  6638.             case 'month':
  6639.             default:
  6640.                 $selectField "MONTH($dateField)";
  6641.                 $alias 'mois';
  6642.                 $where[] = "YEAR($dateField) = :annee";
  6643.                 $params['annee'] = $annee;
  6644.                 break;
  6645.         }
  6646.         // Gestion point de vente
  6647.         $selectPosFields '';
  6648.         $groupByExtra '';
  6649.         $joinPos '';
  6650.         if ($organisationId) {
  6651.             $selectPosFields ', pos.code AS point_of_sale_code, pos.name AS point_of_sale_name';
  6652.             $groupByExtra ', p.point_of_sale_id, pos.code, pos.name';
  6653.             $joinPos 'LEFT JOIN point_of_sale pos ON pos.id = p.point_of_sale_id';
  6654.         }
  6655.         // Requête principale optimisée
  6656.         $sql "
  6657.             SELECT
  6658.                 $selectField AS $alias,
  6659.                 COUNT(DISTINCT p.id) AS ventes_realisees,
  6660.                 COUNT(DISTINCT CASE WHEN DAYOFWEEK($dateField) BETWEEN 2 AND 6 THEN DATE($dateField) END) AS jours_travail_effectues
  6661.                 $selectPosFields
  6662.             FROM production p
  6663.             LEFT JOIN cluster c ON c.id = p.cluster_id
  6664.             $joinPos
  6665.             WHERE " implode(' AND '$where) . "
  6666.             AND DATE($dateField) <= CURDATE()
  6667.             GROUP BY $alias $groupByExtra
  6668.             ORDER BY $alias
  6669.         ";
  6670.         $results $conn->prepare($sql)->executeQuery($params)->fetchAllAssociative();
  6671.         $final = [];
  6672.         foreach ($results as &$row) {
  6673.             $joursEffectues = (int)$row['jours_travail_effectues'];
  6674.             $ventesParJour $row['ventes_realisees'] / max($joursEffectues1);
  6675.             // -------------------------
  6676.             // Jours restants - version exacte que tu as donnée
  6677.             // -------------------------
  6678.             $joursRestants 0;
  6679.             if ($periode === 'month') {
  6680.                 $mois $row[$alias];
  6681.                 $dateFinMois = new \DateTime();
  6682.                 $dateFinMois->setDate($annee$mois1)->modify('last day of this month');
  6683.                 $currentDate = new \DateTime();
  6684.                 if ($dateFinMois $currentDate) {
  6685.                     $interval $currentDate->diff($dateFinMois);
  6686.                     for ($i 0$i <= $interval->days$i++) {
  6687.                         $jourTemp = (clone $currentDate)->modify("+$i day");
  6688.                         if ($jourTemp->format('N') < 6$joursRestants++;
  6689.                     }
  6690.                 }
  6691.                 // Ajouter 1 jour si aujourd'hui est le dernier jour du mois
  6692.                 if ($currentDate->format('t') == $currentDate->format('d')) {
  6693.                     $joursRestants++;
  6694.                 }
  6695.             } elseif ($periode === 'week') {
  6696.                 $currentWeek = (int)date('W');
  6697.                 $joursRestants max(0max(0$row[$alias] - $currentWeek));
  6698.             }
  6699.             $row['ventes_par_jour'] = $ventesParJour;
  6700.             $row['jours_restants'] = $joursRestants;
  6701.             $row['projection_ventes'] = ($ventesParJour $joursRestants) + $row['ventes_realisees'];
  6702.             if ($organisationId) {
  6703.                 $posCode $row['point_of_sale_code'];
  6704.                 $final[$posCode][$row[$alias]] = round($row['projection_ventes'], 2);
  6705.             } else {
  6706.                 $final[$row[$alias]] = round($row['projection_ventes'], 2);
  6707.             }
  6708.         }
  6709.         return $final;
  6710.     }
  6711.     public function getVentesByPrisesNeuves($pointOfSaleId$codeCluster null$codeInsee null)
  6712.     {
  6713.         $conn $this->getEntityManager()->getConnection();
  6714.         $params = ['pointOfSaleId' => $pointOfSaleId];
  6715.         // === Sous-requête PRISES (pn) ===
  6716.         $wherePn = ['ps.id = :pointOfSaleId'];
  6717.         if ($codeInsee !== null) {
  6718.             $wherePn[] = 'pn.cod_insee = :codeInsee';
  6719.             $params['codeInsee'] = $codeInsee;
  6720.         }
  6721.         if ($codeCluster !== null) {
  6722.             $wherePn[] = 'pn.code_cluster = :codeCluster';
  6723.             $params['codeCluster'] = $codeCluster;
  6724.         }
  6725.         $wherePnClause implode(' AND '$wherePn);
  6726.         // === Sous-requête VENTES (pr + cluster) ===
  6727.         $wherePr = ['pr.point_of_sale_id = :pointOfSaleId'];
  6728.         if ($codeInsee !== null) {
  6729.             $wherePr[] = 'pr.code_insee = :codeInsee';
  6730.         }
  6731.         if ($codeCluster !== null) {
  6732.             $wherePr[] = 'c.code_cluster = :codeCluster';
  6733.         }
  6734.         $wherePrClause implode(' AND '$wherePr);
  6735.         $sql "
  6736.             SELECT
  6737.                 p.annee,
  6738.                 p.mois_num,
  6739.                 p.mois,
  6740.                 SUM(p.nb_prises) AS nb_prises_recues,
  6741.                 COALESCE(SUM(v.nb_ventes), 0) AS nb_ventes_sur_prises_meme_mois
  6742.             FROM (
  6743.                 SELECT
  6744.                     YEAR(pn.date_import_data) AS annee,
  6745.                     MONTH(pn.date_import_data) AS mois_num,
  6746.                     DATE_FORMAT(pn.date_import_data,'%Y-%m') AS mois,
  6747.                     pn.cod_hexc AS code_hexc,
  6748.                     COUNT(*) AS nb_prises
  6749.                 FROM nmd_adresses_incomming AS pn
  6750.                 JOIN point_of_sale AS ps ON ps.code = pn.cpv
  6751.                 WHERE $wherePnClause
  6752.                 GROUP BY annee, mois_num, mois, code_hexc
  6753.             ) AS p
  6754.             LEFT JOIN (
  6755.                 SELECT
  6756.                     YEAR(pr.date_cmd_a) AS annee,
  6757.                     MONTH(pr.date_cmd_a) AS mois_num,
  6758.                     DATE_FORMAT(pr.date_cmd_a,'%Y-%m') AS mois,
  6759.                     pr.code_hexc AS code_hexc,
  6760.                     COUNT(DISTINCT pr.id) AS nb_ventes
  6761.                 FROM production AS pr
  6762.                 JOIN cluster AS c ON c.id = pr.cluster_id
  6763.                 WHERE $wherePrClause
  6764.                 GROUP BY annee, mois_num, mois, code_hexc
  6765.             ) AS v
  6766.             ON v.mois = p.mois
  6767.                AND v.code_hexc = p.code_hexc
  6768.             GROUP BY p.annee, p.mois_num, p.mois
  6769.             ORDER BY p.annee, p.mois_num;
  6770.         ";
  6771.         // Préparation + exécution
  6772.         $stmt $conn->prepare($sql);
  6773.         $result $stmt->executeQuery($params);
  6774.         $ventes $result->fetchAllAssociative();
  6775.         // Transformation hiérarchique
  6776.         $ventesHierarchique = [];
  6777.         foreach ($ventes as $ligne) {
  6778.             $annee $ligne['annee'];
  6779.             $mois  $ligne['mois'];
  6780.             if (!isset($ventesHierarchique[$annee])) {
  6781.                 $ventesHierarchique[$annee] = [];
  6782.             }
  6783.             $ventesHierarchique[$annee][$mois] = [
  6784.                 'nb_prises_recues' => $ligne['nb_prises_recues'],
  6785.                 'nb_ventes_sur_prises_meme_mois' => $ligne['nb_ventes_sur_prises_meme_mois'],
  6786.             ];
  6787.         }
  6788.         return $ventesHierarchique;
  6789.     }
  6790.     public function findVentesByTechByPeriod(
  6791.         $pointOfSale,
  6792.         $optionSelect,
  6793.         $annee,
  6794.         $periode// "jour", "semaine" ou "mois"
  6795.         $codeCluster,
  6796.         $codeInsee,
  6797.         $childs,
  6798.         $organisationId
  6799.     ) {
  6800.         $qb $this->createQueryBuilder('p')
  6801.             // Dénominateur : ventes selon optionSelect (B ou V)
  6802.             ->select('COUNT(p.id) as totalVentes, po.code as cpv, p.tech ')
  6803.             // Numérateur : ventes avec date_racc
  6804.             ->addSelect('SUM(CASE WHEN p.dateRacc IS NOT NULL THEN 1 ELSE 0 END) as ventesRacc')
  6805.             ->leftJoin('p.pointOfSale''po');
  6806.         if ($pointOfSale !== null) {
  6807.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  6808.                 ->setParameter('pointOfSale'$pointOfSale);
  6809.         }
  6810.         // Champ de date pour filtrer l’année et la période
  6811.         switch (strtoupper($optionSelect)) {
  6812.             case 'V':
  6813.                 $dateField 'p.dateVenteValidB';
  6814.                 break;
  6815.             case 'B':
  6816.                 $dateField 'p.dateCmdA';
  6817.                 break;
  6818.             default:
  6819.                 $dateField 'p.dateVenteValidB';
  6820.         }
  6821.         $qb->andWhere("YEAR($dateField) = :annee")
  6822.             ->setParameter('annee'$annee);
  6823.         // Sélection dynamique selon la période
  6824.         switch (strtolower($periode)) {
  6825.             case 'jour':
  6826.                 $qb->addSelect("DATE_FORMAT($dateField, '%Y-%m-%d') as periode");
  6827.                 break;
  6828.             case 'semaine':
  6829.                 $qb->addSelect("WEEK($dateField, 1) as periode");
  6830.                 break;
  6831.             case 'mois':
  6832.             default:
  6833.                 $qb->addSelect("DATE_FORMAT($dateField, '%Y-%m') as periode");
  6834.                 break;
  6835.         }
  6836.         // Filtres cluster, codeInsee, organisation, seller
  6837.         if ($codeCluster) {
  6838.             $qb->leftJoin('p.cluster''cluster')
  6839.                 ->addSelect('cluster.codeCluster as clusterCode')
  6840.                 ->andWhere('cluster.codeCluster = :codeCluster')
  6841.                 ->setParameter('codeCluster'$codeCluster);
  6842.         }
  6843.         if ($codeInsee) {
  6844.             $qb->addSelect('p.codeInsee')
  6845.                 ->andWhere('p.codeInsee = :codeInsee')
  6846.                 ->setParameter('codeInsee'$codeInsee);
  6847.         }
  6848.         if ($organisationId) {
  6849.             $qb->andWhere('p.organisation = :organisationId')
  6850.                 ->setParameter('organisationId'$organisationId);
  6851.         }
  6852.         if (is_array($childs) && count($childs) > 0) {
  6853.             $qb->andWhere('p.seller IN (:childs)')
  6854.                 ->leftJoin('p.seller''s')
  6855.                 ->setParameter('childs'$childs);
  6856.         }
  6857.         $qb->groupBy('po.code, p.tech, periode');
  6858.         // Exécution de la requête
  6859.         $results $qb->getQuery()->getArrayResult();
  6860.         // Calcul du taux_racc côté PHP
  6861.         foreach ($results as &$row) {
  6862.             $row['taux_racc'] = $row['totalVentes'] > 0
  6863.                 ? ($row['ventesRacc'] / $row['totalVentes']) * 100
  6864.                 0;
  6865.         }
  6866.         return $results;
  6867.     }
  6868.     public function findVentesByProductByPeriod(
  6869.         $pointOfSale,
  6870.         $optionSelect,
  6871.         $annee,
  6872.         $periode// "jour", "semaine" ou "mois"
  6873.         $codeCluster,
  6874.         $codeInsee,
  6875.         $childs,
  6876.         $organisationId
  6877.     ) {
  6878.         // Sélection du champ date
  6879.         switch (strtoupper($optionSelect)) {
  6880.             case 'V':
  6881.                 $dateField 'pr.date_vente_valid_b';
  6882.                 break;
  6883.             case 'R':
  6884.                 $dateField 'pr.date_racc';
  6885.                 break;
  6886.             case 'B':
  6887.                 $dateField 'pr.date_cmd_a';
  6888.                 break;
  6889.             default:
  6890.                 $dateField 'pr.date_vente_valid_b';
  6891.         }
  6892.         // Format de la période
  6893.         switch (strtolower($periode)) {
  6894.             case 'jour':
  6895.                 $periodeFormat "DATE_FORMAT($dateField, '%Y-%m-%d')";
  6896.                 break;
  6897.             case 'semaine':
  6898.                 $periodeFormat "WEEK($dateField, 1)";
  6899.                 break;
  6900.             case 'mois':
  6901.             default:
  6902.                 $periodeFormat "DATE_FORMAT($dateField, '%Y-%m')";
  6903.         }
  6904.         $conn $this->getEntityManager()->getConnection();
  6905.         // Filtres et paramètres
  6906.         $whereFilters = ["YEAR($dateField) = :annee"];
  6907.         $params = ['annee' => $annee];
  6908.         $joinCluster "";
  6909.         if ($pointOfSale) {
  6910.             $whereFilters[] = "pr.point_of_sale_id = :pointOfSale";
  6911.             $params['pointOfSale'] = $pointOfSale->getId();
  6912.         }
  6913.         if ($organisationId) {
  6914.             $whereFilters[] = "pr.organisation_id = :organisationId";
  6915.             $params['organisationId'] = $organisationId;
  6916.         }
  6917.         if ($codeCluster) {
  6918.             $joinCluster "LEFT JOIN cluster clu ON clu.id = pr.cluster_id";
  6919.             $whereFilters[] = "clu.code_cluster = :codeCluster";
  6920.             $params['codeCluster'] = $codeCluster;
  6921.         }
  6922.         if ($codeInsee) {
  6923.             $whereFilters[] = "pr.code_insee = :codeInsee";
  6924.             $params['codeInsee'] = $codeInsee;
  6925.         }
  6926.         if (is_array($childs) && count($childs) > 0) {
  6927.             $whereFilters[] = "pr.seller_id IN (:childs)";
  6928.             $params['childs'] = $childs;
  6929.         }
  6930.         $whereSql implode(" AND "$whereFilters);
  6931.         // 🧠 Fenêtre analytique partitionnée par cpv (point de vente)
  6932.         // Cela garantit que le total = 100% par cpv (et par période si applicable)
  6933.         $sql "
  6934.             SELECT
  6935.                 $periodeFormat AS periode,
  6936.                 pos.code AS cpv,
  6937.                 pr.product_id,
  6938.                 p.name AS product_name,
  6939.                 cat.intern_name AS category_name,
  6940.                 COUNT(*) AS total_ventes,
  6941.                 ROUND(
  6942.                     COUNT(*) * 100.0 /
  6943.                     SUM(COUNT(*)) OVER (PARTITION BY pos.code, $periodeFormat),
  6944.                     2
  6945.                 ) AS pourcentage
  6946.             FROM production pr
  6947.             LEFT JOIN product p ON p.id = pr.product_id
  6948.             LEFT JOIN category_product cat ON cat.id = p.category_id
  6949.             LEFT JOIN point_of_sale pos ON pos.id = pr.point_of_sale_id
  6950.             $joinCluster
  6951.             WHERE $whereSql
  6952.             GROUP BY periode, pos.code, pr.product_id, p.name, cat.intern_name
  6953.             ORDER BY periode, pos.code, pr.product_id
  6954.         ";
  6955.         $stmt $conn->prepare($sql);
  6956.         $result $stmt->executeQuery($params);
  6957.         return $result->fetchAllAssociative();
  6958.     }
  6959.     public function getVentesPrisesCohorte(
  6960.         $pointOfSaleId null,
  6961.         $annee null,
  6962.         $mois null,
  6963.         $semaine null,
  6964.         $codeCluster null,
  6965.         $departement null
  6966.     ) {
  6967.         $conn $this->getEntityManager()->getConnection();
  6968.         $bindings = [];
  6969.         $wherePrises = [];
  6970.         $whereVentes = [];
  6971.         // Point of sale (optionnel)
  6972.         if ($pointOfSaleId) {
  6973.             $wherePrises[] = "ps.id = :pointOfSaleId";
  6974.             $whereVentes[] = "pr.point_of_sale_id = :pointOfSaleId";
  6975.             $bindings['pointOfSaleId'] = $pointOfSaleId;
  6976.         }
  6977.         // Filtres dynamiques
  6978.         if ($annee) {
  6979.             $wherePrises[] = "YEAR(pn.date_import_data) = :annee";
  6980.             // $whereVentes[] = "YEAR(pr.date_cmd_a) = :annee";
  6981.             $bindings['annee'] = $annee;
  6982.         }
  6983.         if ($mois) {
  6984.             $wherePrises[] = "DATE_FORMAT(pn.date_import_data, '%Y-%m') = :mois";
  6985.             // $whereVentes[] = "DATE_FORMAT(pr.date_cmd_a, '%Y-%m') = :mois";
  6986.             $bindings['mois'] = $mois;
  6987.         }
  6988.         if ($semaine) {
  6989.             $wherePrises[] = "WEEK(pn.date_import_data, 3) = :semaine";
  6990.             // $whereVentes[] = "WEEK(pr.date_cmd_a, 3) = :semaine";
  6991.             $bindings['semaine'] = $semaine;
  6992.         }
  6993.         if ($codeCluster) {
  6994.             $wherePrises[] = "pn.code_cluster = :codeCluster";
  6995.             $whereVentes[] = "c.code_cluster = :codeCluster";
  6996.             $bindings['codeCluster'] = $codeCluster;
  6997.         }
  6998.         if ($departement) {
  6999.             $wherePrises[] = "pn.code_cluster LIKE :departement";
  7000.             // $whereVentes[] = "c.code_cluster LIKE :departement";
  7001.             $bindings['departement'] = $departement '-%';
  7002.         }
  7003.         // Construction des WHERE
  7004.         $wherePrisesSql count($wherePrises) > 'WHERE ' implode(' AND '$wherePrises) : '';
  7005.         $whereVentesSql count($whereVentes) > 'WHERE ' implode(' AND '$whereVentes) : '';
  7006.         // --- SQL ---
  7007.         $sql " WITH p AS (
  7008.             SELECT
  7009.               YEAR(pn.date_import_data)                AS annee,
  7010.               MONTH(pn.date_import_data)               AS mois_num,
  7011.               DATE_FORMAT(pn.date_import_data,'%Y-%m') AS mois_livraison,
  7012.               pn.cod_hexc                               AS code_hexc,
  7013.               pn.cpv                                    AS cpv,
  7014.               pn.lbl_cpv                                AS cpv_name,
  7015.               pn.code_cluster                           AS code_cluster,
  7016.               SUM(pn.nb_logt)                           AS nb_prises
  7017.             FROM nmd_adresses_incomming AS pn
  7018.             JOIN point_of_sale AS ps
  7019.               ON ps.code = pn.cpv                       -- filtre POS côté prises, dans ON pour éviter d'éliminer des lignes
  7020.             $wherePrisesSql -- filtre année côté prises
  7021.             GROUP BY
  7022.               annee, mois_num, mois_livraison,
  7023.               code_hexc, cpv, code_cluster
  7024.           ),
  7025.           v AS (
  7026.             SELECT
  7027.               YEAR(pr.date_cmd_a)                AS annee,
  7028.               MONTH(pr.date_cmd_a)               AS mois_num,
  7029.               DATE_FORMAT(pr.date_cmd_a,'%Y-%m') AS mois_vente,
  7030.               pr.code_hexc                        AS code_hexc,
  7031.               c.code_cluster                      AS code_cluster,
  7032.               COUNT(DISTINCT pr.id)               AS nb_ventes
  7033.             FROM production AS pr
  7034.             LEFT JOIN cluster c ON pr.cluster_id = c.id
  7035.           $whereVentesSql      -- filtre POS côté ventes
  7036.             GROUP BY
  7037.               annee, mois_num, mois_vente,
  7038.               code_hexc, code_cluster
  7039.           )
  7040.           SELECT
  7041.             p.annee,
  7042.             p.cpv,
  7043.             p.cpv_name,
  7044.             p.mois_livraison,
  7045.             v.mois_vente,
  7046.             p.code_cluster,
  7047.             SUM(p.nb_prises)              AS nb_prises_recues,
  7048.             COALESCE(SUM(v.nb_ventes), 0) AS nb_ventes_cohorte
  7049.           FROM p
  7050.           LEFT JOIN v
  7051.             ON v.code_hexc   = p.code_hexc
  7052.           GROUP BY
  7053.             p.annee, p.cpv, p.mois_livraison, v.mois_vente, p.code_cluster
  7054.           ORDER BY
  7055.             p.annee, p.cpv, p.mois_num, p.code_cluster;
  7056. ";
  7057.         $stmt $conn->prepare($sql);
  7058.         $rows $stmt->executeQuery($bindings)->fetchAllAssociative();
  7059.         return $rows;
  7060.     }
  7061.     public function getProductionsAnalyticsObjectifsOptimized(
  7062.         $pointOfSale,
  7063.         $codeCluster,
  7064.         $mois,
  7065.         $annee,
  7066.         $optionSelect,
  7067.         $etatKO,
  7068.         $category,
  7069.         $childs,
  7070.         $organisationId,
  7071.         $departement
  7072.     ): array {
  7073.         // Cas où on a des enfants (hiérarchie utilisateurs)
  7074.         if (is_array($childs) && count($childs) > 0) {
  7075.             $qb $this->getEntityManager()->createQueryBuilder()
  7076.                 ->select('
  7077.                     SUM(o.objectivesVv) AS total_objectif,
  7078.                     IDENTITY(o.user) AS userId
  7079.                 ')
  7080.                 ->from('App\Entity\ObjectivesManagement''o')
  7081.                 ->andWhere('MONTH(o.monthAt) = :mois')
  7082.                 ->andWhere('YEAR(o.monthAt) = :annee')
  7083.                 ->setParameter('mois'$mois)
  7084.                 ->setParameter('annee'$annee)
  7085.                 ->andWhere('o.user IN (:childs)')
  7086.                 ->leftJoin('o.user''s')
  7087.                 ->setParameter('childs'$childs)
  7088.                 ->groupBy('o.user');
  7089.             return $qb->getQuery()->getArrayResult();
  7090.         }
  7091.         // Cas standard (objectif par cluster ou par CPV)
  7092.         $qb $this->getEntityManager()->createQueryBuilder()
  7093.             ->select('
  7094.                 SUM(o.vv) AS total_objectif,
  7095.                 o.codeCluster,
  7096.                 SUBSTRING(o.codeCluster, 1, 2) AS departement
  7097.             ')
  7098.             ->from('App\Entity\NmdObjectifClusters''o')
  7099.             ->andWhere('o.mois = :mois')
  7100.             ->andWhere('o.year = :annee')
  7101.             ->setParameter('mois'$mois)
  7102.             ->setParameter('annee'$annee);
  7103.         // Filtre principal : si point de vente, filtrer par CPV
  7104.         if ($pointOfSale) {
  7105.             $qb->andWhere('o.cpv = :cpv')
  7106.                 ->setParameter('cpv'$pointOfSale->getCode());
  7107.         }
  7108.         // Filtre par codeCluster si fourni
  7109.         if ($codeCluster) {
  7110.             $qb->andWhere('o.codeCluster = :codeCluster')
  7111.                 ->setParameter('codeCluster'$codeCluster);
  7112.         }
  7113.         // Filtre par département
  7114.         if ($departement) {
  7115.             $qb->andWhere('o.codeCluster LIKE :prefix')
  7116.                 ->setParameter('prefix'$departement '-%');
  7117.         }
  7118.         $qb->groupBy('o.codeCluster');
  7119.         return $qb->getQuery()->getArrayResult();
  7120.     }
  7121.     public function getProductionsAnalyticsVentesOptimized(
  7122.         $pointOfSale,
  7123.         $codeCluster,
  7124.         $codeInsee,
  7125.         $mois,
  7126.         $annee,
  7127.         $optionSelect,
  7128.         $category,
  7129.         $etat,
  7130.         $childs,
  7131.         $organisationId,
  7132.         $perid,
  7133.         $sellerId,
  7134.         $departement
  7135.     ): array {
  7136.         $boxProducts = [150262264266334335341344345379];
  7137.         // 🔹 Base commune
  7138.         $qb $this->createBaseQueryBuilder(
  7139.             $pointOfSale,
  7140.             $codeCluster,
  7141.             $codeInsee,
  7142.             $mois,
  7143.             $annee,
  7144.             $optionSelect,
  7145.             $departement
  7146.         );
  7147.         $qb->select("
  7148.             po.code AS cpv,
  7149.             po.name AS nomCpv,
  7150.             c.codeCluster AS codeCluster,
  7151.             c.libelleCluster AS libelleCluster,
  7152.             SUBSTRING(c.codeCluster, 1, 2) AS departement,
  7153.             COUNT(p.id) AS total_ventes,
  7154.             SUM(CASE WHEN p.dateRacc IS NOT NULL 
  7155.                      AND (p.category IN (1,3) OR p.product IN (:boxProducts))
  7156.                      THEN 1 ELSE 0 END) AS total_ventes_raccordes_fixe,
  7157.             SUM(CASE WHEN p.category IN (31,32,33)
  7158.                      AND p.dateRacc IS NOT NULL
  7159.                      AND p.product NOT IN (:boxProducts)
  7160.                      THEN 1 ELSE 0 END) AS total_ventes_raccorde_mobile,
  7161.             SUM(CASE WHEN p.product IN (:boxProducts) THEN 1 ELSE 0 END) AS total_ventes_box_5G,
  7162.             SUM(CASE WHEN p.category IN (1,3) 
  7163.                      THEN 1 ELSE 0 END) AS total_ventes_conq_vla,
  7164.             SUM(CASE WHEN  p.etat = :etat  THEN 1 ELSE 0 END) AS total_ventes_ko
  7165.         ");
  7166.         // 🔹 Groupement par défaut sur le point de vente
  7167.         $qb->groupBy('c.codeCluster')
  7168.             ->orderBy('po.code''ASC');
  7169.         $qb->setParameter('boxProducts'$boxProducts);
  7170.         // ------------------------------------------
  7171.         // 🔸 Filtres dynamiques additionnels
  7172.         // ------------------------------------------
  7173.         if ($organisationId) {
  7174.             $qb->andWhere('p.organisation = :organisationId')
  7175.                 ->setParameter('organisationId'$organisationId);
  7176.         }
  7177.         if ($perid) {
  7178.             $qb->andWhere('p.loginVendeurInit = :perid')
  7179.                 ->setParameter('perid'$perid);
  7180.         }
  7181.         if ($sellerId) {
  7182.             $qb->andWhere('p.seller = :sellerId')
  7183.                 ->setParameter('sellerId'$sellerId);
  7184.         }
  7185.         if (is_array($childs) && count($childs) > 0) {
  7186.             $qb->andWhere('p.seller IN (:childs)')
  7187.                 ->setParameter('childs'$childs);
  7188.         }
  7189.         if ($category) {
  7190.             $categoryIds array_filter(
  7191.                 array_map('intval'explode(','$category)),
  7192.                 fn($id) => $id 0
  7193.             );
  7194.             $qb->andWhere('p.category IN (:categories)')
  7195.                 ->setParameter('categories'$categoryIds);
  7196.         }
  7197.         if ($etat) {
  7198.             $qb->setParameter('etat'$etat);
  7199.         }
  7200.         // ✅ Exécution
  7201.         return $qb->getQuery()->getArrayResult();
  7202.     }
  7203.     public function getProductionsAnalyticsVentesOptimized2(
  7204.         $pointOfSale,
  7205.         $codeCluster,
  7206.         $codeInsee,
  7207.         $mois,
  7208.         $annee,
  7209.         $optionSelect,
  7210.         $category,
  7211.         $etat,
  7212.         $childs,
  7213.         $organisationId,
  7214.         $perid,
  7215.         $sellerId,
  7216.         $departement
  7217.     ): array {
  7218.         $boxProducts = [150262264266334335341344345379];
  7219.         // 🔹 Base commune
  7220.         $qb $this->createBaseQueryBuilder(
  7221.             $pointOfSale,
  7222.             $codeCluster,
  7223.             $codeInsee,
  7224.             $mois,
  7225.             $annee,
  7226.             $optionSelect,
  7227.             $departement
  7228.         );
  7229.         $qb->select("
  7230.             po.code AS cpv,
  7231.             po.name AS nomCpv,
  7232.             c.codeCluster AS codeCluster,
  7233.             c.libelleCluster AS libelleCluster,
  7234.             SUBSTRING(c.codeCluster, 1, 2) AS departement,
  7235.             COUNT(p.id) AS total_ventes,
  7236.             SUM(CASE WHEN p.dateRacc IS NOT NULL 
  7237.                      AND (p.category IN (1,3) OR p.product IN (:boxProducts))
  7238.                      THEN 1 ELSE 0 END) AS total_ventes_raccordes_fixe,
  7239.             SUM(CASE WHEN p.category IN (1,3) 
  7240.                      THEN 1 ELSE 0 END) AS total_ventes_conq_vla,
  7241.             SUM(CASE WHEN  p.etat = :etat  THEN 1 ELSE 0 END) AS total_ventes_ko
  7242.         ");
  7243.         // 🔹 Groupement par défaut sur le point de vente
  7244.         $qb->groupBy('c.codeCluster')
  7245.             ->orderBy('po.code''ASC');
  7246.         $qb->setParameter('boxProducts'$boxProducts);
  7247.         // ------------------------------------------
  7248.         // 🔸 Filtres dynamiques additionnels
  7249.         // ------------------------------------------
  7250.         if ($organisationId) {
  7251.             $qb->andWhere('p.organisation = :organisationId')
  7252.                 ->setParameter('organisationId'$organisationId);
  7253.         }
  7254.         if ($perid) {
  7255.             $qb->andWhere('p.loginVendeurInit = :perid')
  7256.                 ->setParameter('perid'$perid);
  7257.         }
  7258.         if ($sellerId) {
  7259.             $qb->andWhere('p.seller = :sellerId')
  7260.                 ->setParameter('sellerId'$sellerId);
  7261.         }
  7262.         if (is_array($childs) && count($childs) > 0) {
  7263.             $qb->andWhere('p.seller IN (:childs)')
  7264.                 ->setParameter('childs'$childs);
  7265.         }
  7266.         if ($category) {
  7267.             $categoryIds array_filter(
  7268.                 array_map('intval'explode(','$category)),
  7269.                 fn($id) => $id 0
  7270.             );
  7271.             $qb->andWhere('p.category IN (:categories)')
  7272.                 ->setParameter('categories'$categoryIds);
  7273.         }
  7274.         if ($etat) {
  7275.             $qb->setParameter('etat'$etat);
  7276.         }
  7277.         // ✅ Exécution
  7278.         return $qb->getQuery()->getArrayResult();
  7279.     }
  7280.     public function getProductionsAnalyticsVentesMobileOptimized(
  7281.         $pointOfSale,
  7282.         $codeCluster,
  7283.         $codeInsee,
  7284.         $mois,
  7285.         $annee,
  7286.         $optionSelect,
  7287.         $childs,
  7288.         $organisationId,
  7289.         $perid,
  7290.         $sellerId,
  7291.         $departement
  7292.     ): array {
  7293.         $boxProducts = [150262264266334335341344345379];
  7294.         // 🔹 Base commune
  7295.         $qb $this->createBaseQueryBuilder(
  7296.             $pointOfSale,
  7297.             $codeCluster,
  7298.             $codeInsee,
  7299.             $mois,
  7300.             $annee,
  7301.             $optionSelect,
  7302.             $departement
  7303.         );
  7304.         $qb->select("
  7305.             SUBSTRING(p.titulaireZipCode, 1, 2) AS departement,
  7306.             SUM(CASE WHEN p.category IN (31,32,33)
  7307.                      AND p.dateRacc IS NOT NULL
  7308.                      AND p.product NOT IN (:boxProducts)
  7309.                      THEN 1 ELSE 0 END) AS total_ventes_raccorde_mobile,
  7310.             SUM(CASE WHEN p.product IN (:boxProducts) THEN 1 ELSE 0 END) AS total_ventes_box_5G
  7311.            
  7312.         ");
  7313.         // 🔹 Groupement par défaut sur le point de vente
  7314.         $qb->groupBy('departement')
  7315.             ->orderBy('po.code''ASC');
  7316.         $qb->setParameter('boxProducts'$boxProducts);
  7317.         // ------------------------------------------
  7318.         // 🔸 Filtres dynamiques additionnels
  7319.         // ------------------------------------------
  7320.         if ($organisationId) {
  7321.             $qb->andWhere('p.organisation = :organisationId')
  7322.                 ->setParameter('organisationId'$organisationId);
  7323.         }
  7324.         if ($perid) {
  7325.             $qb->andWhere('p.loginVendeurInit = :perid')
  7326.                 ->setParameter('perid'$perid);
  7327.         }
  7328.         if ($sellerId) {
  7329.             $qb->andWhere('p.seller = :sellerId')
  7330.                 ->setParameter('sellerId'$sellerId);
  7331.         }
  7332.         if (is_array($childs) && count($childs) > 0) {
  7333.             $qb->andWhere('p.seller IN (:childs)')
  7334.                 ->setParameter('childs'$childs);
  7335.         }
  7336.         // ✅ Exécution
  7337.         return $qb->getQuery()->getArrayResult();
  7338.     }
  7339.     public function getTotalVenteCnqVla(
  7340.         $pointOfSale,
  7341.         $codeCluster,
  7342.         $codeInsee,
  7343.         $mois,
  7344.         $annee,
  7345.         $optionSelect,
  7346.         $childs,
  7347.         $organisationId,
  7348.         $perid,
  7349.         $sellerId,
  7350.         $departement
  7351.     ): array {
  7352.         // 🔹 Base commune
  7353.         $qb $this->createBaseQueryBuilder(
  7354.             $pointOfSale,
  7355.             $codeCluster,
  7356.             $codeInsee,
  7357.             $mois,
  7358.             $annee,
  7359.             $optionSelect,
  7360.             $departement
  7361.         );
  7362.         $qb->select("
  7363.             c.codeCluster AS codeCluster,
  7364.             SUBSTRING(c.codeCluster, 1, 2) AS departement,
  7365.             COUNT(p.id) AS total_ventes,
  7366.             SUM(CASE WHEN p.category IN (1,3) 
  7367.                      THEN 1 ELSE 0 END) AS total_ventes_conq_vla
  7368.         ");
  7369.         // 🔹 Groupement par défaut sur le point de vente
  7370.         $qb->groupBy('c.codeCluster')
  7371.             ->orderBy('po.code''ASC');
  7372.         // ------------------------------------------
  7373.         // 🔸 Filtres dynamiques additionnels
  7374.         // ------------------------------------------
  7375.         if ($organisationId) {
  7376.             $qb->andWhere('p.organisation = :organisationId')
  7377.                 ->setParameter('organisationId'$organisationId);
  7378.         }
  7379.         if ($perid) {
  7380.             $qb->andWhere('p.loginVendeurInit = :perid')
  7381.                 ->setParameter('perid'$perid);
  7382.         }
  7383.         if ($sellerId) {
  7384.             $qb->andWhere('p.seller = :sellerId')
  7385.                 ->setParameter('sellerId'$sellerId);
  7386.         }
  7387.         if (is_array($childs) && count($childs) > 0) {
  7388.             $qb->andWhere('p.seller IN (:childs)')
  7389.                 ->setParameter('childs'$childs);
  7390.         }
  7391.         // ✅ Exécution
  7392.         return $qb->getQuery()->getArrayResult();
  7393.     }
  7394.     public function getMrzList($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$childs$organisationId$perid$sellerId$departement)
  7395.     {
  7396.         $conn $this->getEntityManager()->getConnection();
  7397.         $sql '
  7398.             SELECT COUNT(p.id) AS total_ventes, 
  7399.                    p.identity_ctrl,
  7400.                    c.code_cluster AS codeCluster
  7401.             FROM production p
  7402.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  7403.             LEFT JOIN cluster c ON p.cluster_id = c.id
  7404.             WHERE p.identity_ctrl IS NOT NULL
  7405.         ';
  7406.         $params = [
  7407.             'mois' => $mois,
  7408.             'annee' => $annee
  7409.         ];
  7410.         // ✅ Ajout condition dynamique sur pointOfSale
  7411.         if ($pointOfSale) {
  7412.             $sql .= ' AND p.point_of_sale_id = :pointOfSale';
  7413.             $params['pointOfSale'] = $pointOfSale->getId();
  7414.         }
  7415.         // ✅ Condition sur les enfants
  7416.         if (is_array($childs) && count($childs) > 0) {
  7417.             $placeholders = [];
  7418.             foreach ($childs as $i => $child) {
  7419.                 $placeholder ':child_' $i;
  7420.                 $placeholders[] = $placeholder;
  7421.                 $params['child_' $i] = $child;
  7422.             }
  7423.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  7424.         }
  7425.         // ✅ Autres conditions
  7426.         if ($codeCluster) {
  7427.             $sql .= ' AND c.code_cluster = :codeCluster';
  7428.             $params['codeCluster'] = $codeCluster;
  7429.         }
  7430.         if ($departement) {
  7431.             $sql .= ' AND c.code_cluster LIKE :departement';
  7432.             $params['departement'] = $departement '-%';
  7433.         }
  7434.         if ($codeInsee) {
  7435.             $sql .= ' AND p.code_insee = :codeInsee';
  7436.             $params['codeInsee'] = $codeInsee;
  7437.         }
  7438.         if ($organisationId) {
  7439.             $sql .= ' AND p.organisation_id = :organisationId';
  7440.             $params['organisationId'] = $organisationId;
  7441.         }
  7442.         if ($perid) {
  7443.             $sql .= ' AND p.login_vendeur_init = :perid';
  7444.             $params['perid'] = $perid;
  7445.         }
  7446.         if ($sellerId) {
  7447.             $sql .= ' AND p.seller_id = :sellerId';
  7448.             $params['sellerId'] = $sellerId;
  7449.         }
  7450.         // ✅ Condition sur le mois / année selon optionSelect
  7451.         switch ($optionSelect) {
  7452.             case 'V':
  7453.                 $sql .= ' AND MONTH(p.date_vente_valid_b) = :mois AND YEAR(p.date_vente_valid_b) = :annee';
  7454.                 break;
  7455.             case 'R':
  7456.                 $sql .= ' AND MONTH(p.date_racc) = :mois AND YEAR(p.date_racc) = :annee';
  7457.                 break;
  7458.             case 'B':
  7459.                 $sql .= ' AND MONTH(p.date_cmd_a) = :mois AND YEAR(p.date_cmd_a) = :annee';
  7460.                 break;
  7461.         }
  7462.         $sql .= ' GROUP BY p.identity_ctrl, p.point_of_sale_id, c.code_cluster ';
  7463.         $stmt $conn->prepare($sql);
  7464.         $result $stmt->executeQuery($params);
  7465.         return $result->fetchAllAssociative();
  7466.     }
  7467.     public function get4P(
  7468.         $pointOfSale,
  7469.         $codeCluster,
  7470.         $codeInsee,
  7471.         $mois,
  7472.         $annee,
  7473.         $childs,
  7474.         $organisationId,
  7475.         $perid,
  7476.         $sellerId,
  7477.         $departement
  7478.     ): array {
  7479.         $conn $this->getEntityManager()->getConnection();
  7480.         // 🔹 Base des paramètres
  7481.         $params = [
  7482.             'mois' => $mois,
  7483.             'annee' => $annee,
  7484.             'cat32' => 32,
  7485.             'cat31' => 31
  7486.         ];
  7487.         if ($pointOfSale) {
  7488.             $params['pointOfSale'] = $pointOfSale->getId();
  7489.         }
  7490.         // ------------------------------------------
  7491.         // 🧱 SQL Construction (partie principale)
  7492.         // ------------------------------------------
  7493.         $sql '
  7494.             SELECT 
  7495.                 SUBSTRING(result.titulaire_zip_code, 1, 2) AS departement,
  7496.                 COUNT(DISTINCT result.titulaire_email) AS total_vente
  7497.             FROM (
  7498.                 -- Partie 1 : Catégorie 32 avec emails en catégorie 1
  7499.                 SELECT 
  7500.                     p1.titulaire_email,
  7501.                     p1.titulaire_zip_code
  7502.                 FROM production p1
  7503.                 WHERE p1.category_id = :cat32
  7504.                   AND MONTH(p1.date_cmd_a) = :mois
  7505.                   AND YEAR(p1.date_cmd_a) = :annee
  7506.                   AND p1.titulaire_email IN (
  7507.                       SELECT DISTINCT p_sub1.titulaire_email
  7508.                       FROM production p_sub1
  7509.                       WHERE p_sub1.category_id = 1
  7510.                         AND MONTH(p_sub1.date_cmd_a) = :mois
  7511.                         AND YEAR(p_sub1.date_cmd_a) = :annee
  7512.         ';
  7513.         // Ajout des filtres dynamiques pour la sous-requête p_sub1
  7514.         if ($pointOfSale) {
  7515.             $sql .= ' AND p_sub1.point_of_sale_id = :pointOfSale';
  7516.         }
  7517.         $sql .= ')';
  7518.         // 🔹 Filtres optionnels pour p1
  7519.         if ($pointOfSale) {
  7520.             $sql .= ' AND p1.point_of_sale_id = :pointOfSale';
  7521.         }
  7522.         if ($organisationId) {
  7523.             $sql .= ' AND p1.organisation_id = :organisationId';
  7524.             $params['organisationId'] = $organisationId;
  7525.         }
  7526.         if ($codeCluster) {
  7527.             $sql .= ' AND p1.cluster_id IN (SELECT id FROM cluster WHERE code_cluster = :codeCluster)';
  7528.             $params['codeCluster'] = $codeCluster;
  7529.         }
  7530.         if ($departement) {
  7531.             // On filtre sur le code postal du titulaire, pas sur le cluster
  7532.             $sql .= ' AND SUBSTRING(p1.titulaire_zip_code, 1, 2) = :departement';
  7533.             $params['departement'] = $departement;
  7534.         }
  7535.         if ($codeInsee) {
  7536.             $sql .= ' AND p1.code_insee = :codeInsee';
  7537.             $params['codeInsee'] = $codeInsee;
  7538.         }
  7539.         if ($perid) {
  7540.             $sql .= ' AND p1.login_vendeur_init = :perid';
  7541.             $params['perid'] = $perid;
  7542.         }
  7543.         if ($sellerId) {
  7544.             $sql .= ' AND p1.seller_id = :sellerId';
  7545.             $params['sellerId'] = $sellerId;
  7546.         }
  7547.         if (is_array($childs) && count($childs) > 0) {
  7548.             $placeholders = [];
  7549.             foreach ($childs as $i => $child) {
  7550.                 $placeholder ':child_' $i;
  7551.                 $placeholders[] = $placeholder;
  7552.                 $params['child_' $i] = $child;
  7553.             }
  7554.             $sql .= ' AND p1.seller_id IN (' implode(', '$placeholders) . ')';
  7555.         }
  7556.         // ------------------------------------------
  7557.         // 🧩 UNION : catégorie 31
  7558.         // ------------------------------------------
  7559.         $sql .= '
  7560.                 UNION
  7561.                 SELECT 
  7562.                     p2.titulaire_email,
  7563.                     p2.titulaire_zip_code
  7564.                 FROM production p2
  7565.                 WHERE p2.category_id = :cat31
  7566.                   AND MONTH(p2.date_cmd_a) = :mois
  7567.                   AND YEAR(p2.date_cmd_a) = :annee
  7568.                   AND p2.titulaire_email IN (
  7569.                       SELECT DISTINCT p_sub2.titulaire_email
  7570.                       FROM production p_sub2
  7571.                       WHERE p_sub2.category_id IN (1, 3)
  7572.                         AND MONTH(p_sub2.date_cmd_a) = :mois
  7573.                         AND YEAR(p_sub2.date_cmd_a) = :annee
  7574.         ';
  7575.         // Filtres dynamiques pour sous-requête p_sub2
  7576.         if ($pointOfSale) {
  7577.             $sql .= ' AND p_sub2.point_of_sale_id = :pointOfSale';
  7578.         }
  7579.         $sql .= ')';
  7580.         // 🔹 Filtres optionnels pour p2
  7581.         if ($pointOfSale) {
  7582.             $sql .= ' AND p2.point_of_sale_id = :pointOfSale';
  7583.         }
  7584.         if ($organisationId) {
  7585.             $sql .= ' AND p2.organisation_id = :organisationId';
  7586.         }
  7587.         if ($codeCluster) {
  7588.             $sql .= ' AND p2.cluster_id IN (SELECT id FROM cluster WHERE code_cluster = :codeCluster)';
  7589.         }
  7590.         if ($departement) {
  7591.             $sql .= ' AND SUBSTRING(p2.titulaire_zip_code, 1, 2) = :departement';
  7592.         }
  7593.         if ($codeInsee) {
  7594.             $sql .= ' AND p2.code_insee = :codeInsee';
  7595.         }
  7596.         if ($perid) {
  7597.             $sql .= ' AND p2.login_vendeur_init = :perid';
  7598.         }
  7599.         if ($sellerId) {
  7600.             $sql .= ' AND p2.seller_id = :sellerId';
  7601.         }
  7602.         if (is_array($childs) && count($childs) > 0) {
  7603.             $placeholders = [];
  7604.             foreach ($childs as $i => $child) {
  7605.                 $placeholder ':child_' $i;
  7606.                 $placeholders[] = $placeholder;
  7607.             }
  7608.             $sql .= ' AND p2.seller_id IN (' implode(', '$placeholders) . ')';
  7609.         }
  7610.         // ------------------------------------------
  7611.         // 🔚 Fermeture du bloc + agrégation
  7612.         // ------------------------------------------
  7613.         $sql .= '
  7614.             ) AS result
  7615.             GROUP BY SUBSTRING(result.titulaire_zip_code, 1, 2)
  7616.             ORDER BY departement
  7617.         ';
  7618.         // ------------------------------------------
  7619.         // ▶️ Exécution
  7620.         // ------------------------------------------
  7621.         $stmt $conn->prepare($sql);
  7622.         $result $stmt->executeQuery($params);
  7623.         return $result->fetchAllAssociative();
  7624.     }
  7625.     public function get4PC(
  7626.         $pointOfSale,
  7627.         $codeCluster,
  7628.         $codeInsee,
  7629.         $mois,
  7630.         $annee,
  7631.         $childs,
  7632.         $organisationId,
  7633.         $perid,
  7634.         $sellerId,
  7635.         $departement
  7636.     ): array {
  7637.         $qb $this->createQueryBuilder('p')
  7638.             ->leftJoin('p.cluster''c')
  7639.             ->select('COUNT(DISTINCT p.titulaireEmail) AS nombre_titulaire_email')
  7640.             ->addSelect('SUBSTRING(p.titulaireZipCode, 1, 2) AS departement')
  7641.             ->groupBy('departement');
  7642.         // 🔹 Filtres temporels
  7643.         $qb->andWhere('MONTH(p.dateCmdA) = :mois')
  7644.             ->andWhere('YEAR(p.dateCmdA) = :annee')
  7645.             ->setParameter('mois'$mois)
  7646.             ->setParameter('annee'$annee);
  7647.         // 🔹 Point de vente (optionnel)
  7648.         if ($pointOfSale) {
  7649.             $qb->andWhere('p.pointOfSale = :pointOfSale')
  7650.                 ->setParameter('pointOfSale'$pointOfSale);
  7651.         }
  7652.         // 🔹 Cluster
  7653.         if ($codeCluster) {
  7654.             $qb->andWhere('c.codeCluster = :codeCluster')
  7655.                 ->setParameter('codeCluster'$codeCluster);
  7656.         }
  7657.         // 🔹 INSEE
  7658.         if ($codeInsee) {
  7659.             $qb->andWhere('p.codeInsee = :codeInsee')
  7660.                 ->setParameter('codeInsee'$codeInsee);
  7661.         }
  7662.         // 🔹 Département
  7663.         if ($departement) {
  7664.             $qb->andWhere('c.codeCluster LIKE :prefix')
  7665.                 ->setParameter('prefix'$departement '-%');
  7666.         }
  7667.         // 🔹 Logique chainage
  7668.         $qb->andWhere('p.fixeChainage IS NOT NULL')
  7669.             ->andWhere('p.category IN (31,32,33)');
  7670.         // 🔹 Filtres vendeurs
  7671.         if (is_array($childs) && count($childs) > 0) {
  7672.             $qb->andWhere('p.seller IN (:childs)')
  7673.                 ->setParameter('childs'$childs);
  7674.         }
  7675.         if ($organisationId) {
  7676.             $qb->andWhere('p.organisation = :organisationId')
  7677.                 ->setParameter('organisationId'$organisationId);
  7678.         }
  7679.         if ($perid) {
  7680.             $qb->andWhere('p.loginVendeurInit = :perid')
  7681.                 ->setParameter('perid'$perid);
  7682.         }
  7683.         if ($sellerId) {
  7684.             $qb->andWhere('p.seller = :sellerId')
  7685.                 ->setParameter('sellerId'$sellerId);
  7686.         }
  7687.         return $qb->getQuery()->getArrayResult();
  7688.     }
  7689.     public function getJ30(
  7690.         $pointOfSale,
  7691.         $codeCluster,
  7692.         $codeInsee,
  7693.         $mois,
  7694.         $annee,
  7695.         $childs,
  7696.         $organisationId,
  7697.         $perid,
  7698.         $sellerId,
  7699.         $departement
  7700.     ): array {
  7701.         $conn $this->getEntityManager()->getConnection();
  7702.         $sql "
  7703.             SELECT 
  7704.                 c.code_cluster AS codeCluster,
  7705.     
  7706.                 -- 🟩 Total global (fixe + mobile)
  7707.                 COUNT(DISTINCT CASE 
  7708.                     WHEN p.date_resiliation IS NOT NULL
  7709.                     AND p.date_racc IS NOT NULL
  7710.                     AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  7711.                     AND MONTH(p.date_vente_valid_b) = :mois 
  7712.                     AND YEAR(p.date_vente_valid_b) = :annee
  7713.                     AND (p.category_id NOT IN (2, 31, 32, 33) OR p.product_id IN(150,262,264,266,334,335,341,344,345,379))
  7714.                     THEN p.id END) AS total_vente,
  7715.     
  7716.                 -- 🟦 Total raccordé fixe
  7717.                 COUNT(DISTINCT CASE 
  7718.                     WHEN (p.category_id NOT IN (31,32,33)
  7719.                           OR p.product_id IN (150,262,264,266,334,335,341,344,345,379))
  7720.                          AND p.date_resiliation IS NOT NULL
  7721.                          AND p.date_racc IS NOT NULL
  7722.                          AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  7723.                          AND MONTH(p.date_cmd_a) = :mois
  7724.                          AND YEAR(p.date_cmd_a) = :annee
  7725.                     THEN p.id END) AS total_vente_fixe_raccorde
  7726.     
  7727.               
  7728.             FROM production p
  7729.             LEFT JOIN cluster c ON p.cluster_id = c.id
  7730.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  7731.             WHERE 1 = 1
  7732.         ";
  7733.         $params = [
  7734.             'mois' => $mois,
  7735.             'annee' => $annee,
  7736.         ];
  7737.         // ✅ Point de vente optionnel
  7738.         if ($pointOfSale) {
  7739.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  7740.             $params['pointOfSale'] = $pointOfSale->getId();
  7741.         }
  7742.         if ($codeCluster) {
  7743.             $sql .= " AND c.code_cluster = :codeCluster";
  7744.             $params['codeCluster'] = $codeCluster;
  7745.         }
  7746.         if ($departement) {
  7747.             $sql .= " AND c.code_cluster LIKE :departement";
  7748.             $params['departement'] = $departement '-%';
  7749.         }
  7750.         if ($codeInsee) {
  7751.             $sql .= " AND p.code_insee = :codeInsee";
  7752.             $params['codeInsee'] = $codeInsee;
  7753.         }
  7754.         if ($organisationId) {
  7755.             $sql .= " AND p.organisation_id = :organisationId";
  7756.             $params['organisationId'] = $organisationId;
  7757.         }
  7758.         if ($perid) {
  7759.             $sql .= " AND p.login_vendeur_init = :perid";
  7760.             $params['perid'] = $perid;
  7761.         }
  7762.         if ($sellerId) {
  7763.             $sql .= " AND p.seller_id = :sellerId";
  7764.             $params['sellerId'] = $sellerId;
  7765.         }
  7766.         if (is_array($childs) && count($childs) > 0) {
  7767.             $placeholders = [];
  7768.             foreach ($childs as $i => $child) {
  7769.                 $placeholder ':child_' $i;
  7770.                 $placeholders[] = $placeholder;
  7771.                 $params['child_' $i] = $child;
  7772.             }
  7773.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  7774.         }
  7775.         // ✅ Groupement
  7776.         $sql .= ' GROUP BY c.code_cluster';
  7777.         $stmt $conn->prepare($sql);
  7778.         $result $stmt->executeQuery($params);
  7779.         return $result->fetchAllAssociative();
  7780.     }
  7781.     public function getJ30Mobile(
  7782.         $pointOfSale,
  7783.         $codeCluster,
  7784.         $codeInsee,
  7785.         $mois,
  7786.         $annee,
  7787.         $childs,
  7788.         $organisationId,
  7789.         $perid,
  7790.         $sellerId,
  7791.         $departement
  7792.     ): array {
  7793.         $conn $this->getEntityManager()->getConnection();
  7794.         $sql "
  7795.             SELECT 
  7796.                 SUBSTRING(p.titulaire_zip_code,1,2)  AS departement,
  7797.     
  7798.                 -- 🟧 Total mobile
  7799.                 COUNT(DISTINCT CASE 
  7800.                     WHEN p.category_id IN (31,32,33)
  7801.                          AND p.date_resiliation IS NOT NULL
  7802.                          AND p.date_racc IS NOT NULL
  7803.                          AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  7804.                          AND MONTH(p.date_cmd_a) = :mois
  7805.                          AND YEAR(p.date_cmd_a) = :annee
  7806.                     THEN p.id END) AS total_vente_mobile,
  7807.     
  7808.                 -- 🟥 Total raccordé mobile
  7809.                 COUNT(DISTINCT CASE 
  7810.                     WHEN p.category_id IN (31,32,33)
  7811.                          AND p.date_resiliation IS NOT NULL
  7812.                          AND p.date_racc IS NOT NULL
  7813.                          AND DATEDIFF(p.date_racc, p.date_resiliation) > -31
  7814.                          AND p.product_id NOT IN (150,262,264,266,334,335,341,344,345,379)
  7815.                          AND MONTH(p.date_cmd_a) = :mois
  7816.                          AND YEAR(p.date_cmd_a) = :annee
  7817.                     THEN p.id END) AS total_vente_mobile_raccorde
  7818.     
  7819.             FROM production p
  7820.             LEFT JOIN cluster c ON p.cluster_id = c.id
  7821.             LEFT JOIN point_of_sale po ON p.point_of_sale_id = po.id
  7822.             WHERE 1 = 1
  7823.         ";
  7824.         $params = [
  7825.             'mois' => $mois,
  7826.             'annee' => $annee,
  7827.         ];
  7828.         // ✅ Point de vente optionnel
  7829.         if ($pointOfSale) {
  7830.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  7831.             $params['pointOfSale'] = $pointOfSale->getId();
  7832.         }
  7833.         if ($codeCluster) {
  7834.             $sql .= " AND c.code_cluster = :codeCluster";
  7835.             $params['codeCluster'] = $codeCluster;
  7836.         }
  7837.         if ($departement) {
  7838.             $sql .= " AND c.code_cluster LIKE :departement";
  7839.             $params['departement'] = $departement '-%';
  7840.         }
  7841.         if ($codeInsee) {
  7842.             $sql .= " AND p.code_insee = :codeInsee";
  7843.             $params['codeInsee'] = $codeInsee;
  7844.         }
  7845.         if ($organisationId) {
  7846.             $sql .= " AND p.organisation_id = :organisationId";
  7847.             $params['organisationId'] = $organisationId;
  7848.         }
  7849.         if ($perid) {
  7850.             $sql .= " AND p.login_vendeur_init = :perid";
  7851.             $params['perid'] = $perid;
  7852.         }
  7853.         if ($sellerId) {
  7854.             $sql .= " AND p.seller_id = :sellerId";
  7855.             $params['sellerId'] = $sellerId;
  7856.         }
  7857.         if (is_array($childs) && count($childs) > 0) {
  7858.             $placeholders = [];
  7859.             foreach ($childs as $i => $child) {
  7860.                 $placeholder ':child_' $i;
  7861.                 $placeholders[] = $placeholder;
  7862.                 $params['child_' $i] = $child;
  7863.             }
  7864.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  7865.         }
  7866.         // ✅ Groupement
  7867.         $sql .= ' GROUP BY departement';
  7868.         $stmt $conn->prepare($sql);
  7869.         $result $stmt->executeQuery($params);
  7870.         return $result->fetchAllAssociative();
  7871.     }
  7872.     public function getPto(
  7873.         $pointOfSale,
  7874.         ?string $codeCluster,
  7875.         $codeInsee,
  7876.         int $mois,
  7877.         int $annee,
  7878.         array $childs,
  7879.         ?int $organisationId,
  7880.         ?string $perid,
  7881.         $sellerId,
  7882.         $departement
  7883.     ): array {
  7884.         $conn $this->getEntityManager()->getConnection();
  7885.         $sql "
  7886.             SELECT 
  7887.                 c.code_cluster AS codeCluster,
  7888.     
  7889.                 -- RIO
  7890.                 COUNT(DISTINCT CASE WHEN p.portabilite = 'OUI' THEN p.id END) AS total_rio,
  7891.     
  7892.                 -- PTO_SAISIE
  7893.                 COUNT(DISTINCT CASE WHEN p.prise_saisie_commande = 'OUI' THEN p.id END) AS total_pto_saisie,
  7894.     
  7895.                 -- PTO_NON_SAISIE
  7896.                 COUNT(DISTINCT CASE WHEN p.prise_saisie_commande = 'NON' AND p.prise_existante_commande = 'OUI' THEN p.id END) AS total_pto_non_saisie,
  7897.     
  7898.                 -- PTO_EXISTANTE
  7899.                 COUNT(DISTINCT CASE WHEN p.prise_existante_commande = 'OUI' THEN p.id END) AS total_pto_existante,
  7900.     
  7901.                 -- PTO_NON_EXISTANTE
  7902.                 COUNT(DISTINCT CASE WHEN p.prise_existante_commande = 'NON' THEN p.id END) AS total_pto_non_existante
  7903.     
  7904.             FROM production p
  7905.             LEFT JOIN cluster c ON c.id = p.cluster_id
  7906.             LEFT JOIN point_of_sale po ON po.id = p.point_of_sale_id
  7907.             WHERE p.category_id IN (1,3)
  7908.               AND MONTH(p.date_cmd_a) = :mois
  7909.               AND YEAR(p.date_cmd_a) = :annee
  7910.         ";
  7911.         $params = [
  7912.             'mois' => $mois,
  7913.             'annee' => $annee,
  7914.         ];
  7915.         // 🔹 Point de vente optionnel
  7916.         if ($pointOfSale) {
  7917.             $sql .= " AND p.point_of_sale_id = :pointOfSale";
  7918.             $params['pointOfSale'] = $pointOfSale->getId();
  7919.         }
  7920.         if ($organisationId) {
  7921.             $sql .= " AND p.organisation_id = :organisationId";
  7922.             $params['organisationId'] = $organisationId;
  7923.         }
  7924.         if (!empty($childs)) {
  7925.             $placeholders = [];
  7926.             foreach ($childs as $i => $child) {
  7927.                 $placeholder ':child_' $i;
  7928.                 $placeholders[] = $placeholder;
  7929.                 $params['child_' $i] = $child;
  7930.             }
  7931.             $sql .= ' AND p.seller_id IN (' implode(', '$placeholders) . ')';
  7932.         }
  7933.         if ($codeInsee) {
  7934.             $sql .= " AND p.code_insee = :codeInsee";
  7935.             $params['codeInsee'] = $codeInsee;
  7936.         }
  7937.         if ($codeCluster) {
  7938.             $sql .= " AND c.code_cluster = :codeCluster";
  7939.             $params['codeCluster'] = $codeCluster;
  7940.         }
  7941.         if ($departement) {
  7942.             $sql .= " AND c.code_cluster LIKE :departement";
  7943.             $params['departement'] = $departement '-%';
  7944.         }
  7945.         if ($perid) {
  7946.             $sql .= " AND p.login_vendeur_init = :perid";
  7947.             $params['perid'] = $perid;
  7948.         }
  7949.         if ($sellerId) {
  7950.             $sql .= " AND p.seller_id = :sellerId";
  7951.             $params['sellerId'] = $sellerId;
  7952.         }
  7953.         // 🔹 Group by cluster
  7954.         $sql .= " GROUP BY c.code_cluster";
  7955.         $stmt $conn->prepare($sql);
  7956.         $result $stmt->executeQuery($params);
  7957.         return $result->fetchAllAssociative();
  7958.     }
  7959.     public function getProjection(
  7960.         $pointOfSale,
  7961.         $codeCluster,
  7962.         $codeInsee,
  7963.         $mois,
  7964.         $annee,
  7965.         $optionSelect,
  7966.         $etatKO,
  7967.         $category,
  7968.         $childs,
  7969.         $organisationId,
  7970.         $perid,
  7971.         $sellerId,
  7972.         $departement
  7973.     ): array {
  7974.         $qb $this->createBaseQueryBuilder($pointOfSale$codeCluster$codeInsee$mois$annee$optionSelect$departement);
  7975.         $dateField '';
  7976.         $dateFieldSql '';
  7977.         switch ($optionSelect) {
  7978.             case 'V':
  7979.                 $dateField 'p.dateVenteValidB';
  7980.                 $dateFieldSql 'p.date_vente_valid_b';
  7981.                 break;
  7982.             case 'R':
  7983.                 $dateField 'p.dateRacc';
  7984.                 $dateFieldSql 'p.date_racc';
  7985.                 break;
  7986.             case 'B':
  7987.                 $dateField 'p.dateCmdA';
  7988.                 $dateFieldSql 'p.date_cmd_a';
  7989.                 break;
  7990.         }
  7991.         // Sélectionner codeCluster et count des ventes
  7992.         $qb->select('c.codeCluster AS codeCluster, COUNT(p.id) AS ventes_realisees')
  7993.             ->andWhere("$dateField <= :currentDate")
  7994.             ->setParameter('currentDate', new \DateTime());
  7995.         if ($category) {
  7996.             $categories array_filter(array_map('intval'explode(','$category)), fn($id) => $id 0);
  7997.             if ($categories) {
  7998.                 $qb->andWhere('p.category IN (:categories)')
  7999.                     ->setParameter('categories'$categories);
  8000.             }
  8001.         }
  8002.         if ($childs) {
  8003.             $qb->andWhere('p.seller IN (:childs)')
  8004.                 ->leftJoin('p.seller''s')
  8005.                 ->setParameter('childs'$childs);
  8006.         }
  8007.         if ($organisationId$qb->andWhere('p.organisation = :organisationId')->setParameter('organisationId'$organisationId);
  8008.         if ($perid$qb->andWhere('p.loginVendeurInit = :perid')->setParameter('perid'$perid);
  8009.         if ($sellerId$qb->andWhere('p.seller = :sellerId')->setParameter('sellerId'$sellerId);
  8010.         // Grouper par codeCluster
  8011.         $qb->groupBy('c.codeCluster');
  8012.         $ventesRealiseesData $qb->getQuery()->getResult();
  8013.         // Connexion DBAL pour calcul des jours de travail par cluster
  8014.         $conn $this->getEntityManager()->getConnection();
  8015.         $parameters = ['mois' => $mois'annee' => $annee];
  8016.         $sql "
  8017.             SELECT c.code_cluster,
  8018.                    COUNT(DISTINCT DAY($dateFieldSql)) AS jours_travail
  8019.             FROM production p
  8020.             LEFT JOIN cluster c ON p.cluster_id = c.id
  8021.             WHERE 
  8022.                MONTH($dateFieldSql) = :mois
  8023.               AND YEAR($dateFieldSql) = :annee
  8024.               AND DAYOFWEEK($dateFieldSql) BETWEEN 2 AND 6
  8025.               AND $dateFieldSql <= CURDATE()
  8026.         ";
  8027.         if ($pointOfSale) {
  8028.             $sql .= ' AND p.point_of_sale_id = :pointOfSale';
  8029.             $parameters["pointOfSale"] = $pointOfSale->getId();
  8030.         }
  8031.         if ($category) {
  8032.             $placeholders = [];
  8033.             foreach ($categories as $i => $catId) {
  8034.                 $ph ":cat$i";
  8035.                 $placeholders[] = $ph;
  8036.                 $parameters[$ph] = $catId;
  8037.             }
  8038.             $sql .= " AND p.category_id IN (" implode(','$placeholders) . ")";
  8039.         }
  8040.         $sql .= " GROUP BY c.code_cluster";
  8041.         $stmt $conn->prepare($sql);
  8042.         foreach ($parameters as $key => $value$stmt->bindValue($key$value, \PDO::PARAM_INT);
  8043.         $joursTravailData $stmt->executeQuery()->fetchAllAssociative();
  8044.         // Fusionner résultats pour calcul de projection par cluster
  8045.         $projectionData = [];
  8046.         foreach ($ventesRealiseesData as $row) {
  8047.             $codeCluster $row['codeCluster'];
  8048.             $ventesRealisees = (int)$row['ventes_realisees'];
  8049.             $joursTravailEffectues 1// fallback
  8050.             foreach ($joursTravailData as $j) {
  8051.                 if ($j['code_cluster'] === $codeCluster) {
  8052.                     $joursTravailEffectues = (int)$j['jours_travail'];
  8053.                     break;
  8054.                 }
  8055.             }
  8056.             $ventesParJour $ventesRealisees max($joursTravailEffectues1);
  8057.             $dateFinMois = new \DateTime();
  8058.             $dateFinMois->setDate($annee$mois1)->modify('last day of this month');
  8059.             $currentDate = new \DateTime();
  8060.             $joursRestants 0;
  8061.             if ($dateFinMois $currentDate) {
  8062.                 $interval $currentDate->diff($dateFinMois);
  8063.                 for ($i 0$i <= $interval->days$i++) {
  8064.                     $jourTemp = (clone $currentDate)->modify("+$i day");
  8065.                     if ($jourTemp->format('N') < 6$joursRestants++;
  8066.                 }
  8067.             }
  8068.             if ($currentDate->format('t') == $currentDate->format('d')) $joursRestants++;
  8069.             $projectionVentes = ($ventesParJour $joursRestants) + $ventesRealisees;
  8070.             $projectionData[$codeCluster] = [
  8071.                 'ventes_realisees' => $ventesRealisees,
  8072.                 'ventes_par_jour' => $ventesParJour,
  8073.                 'jours_travail_effectues' => $joursTravailEffectues,
  8074.                 'jours_restants' => $joursRestants,
  8075.                 'projection_ventes' => $projectionVentes,
  8076.             ];
  8077.         }
  8078.         return $projectionData;
  8079.     }
  8080.     public function getVentesParPrisesFttb($cpv null$annee null$mois null$semaine null$codeCluster null$codeInsee null)
  8081.     {
  8082.         $conn $this->getEntityManager()->getConnection();
  8083.         // Sous-requête dynamique
  8084.         $sub "
  8085.         SELECT a.cod_hexc
  8086.         FROM nmd_arret_fttb a
  8087.         WHERE 1 = 1
  8088.     ";
  8089.         $params = [];
  8090.         if ($cpv !== null) {
  8091.             $sub .= " AND a.cpv = :cpv";
  8092.             $params['cpv'] = $cpv;
  8093.         }
  8094.         if ($annee !== null) {
  8095.             $sub .= " AND YEAR(a.date_arret_fttb) = :annee";
  8096.             $params['annee'] = $annee;
  8097.         }
  8098.         if ($mois !== null) {
  8099.             $sub .= " AND MONTH(a.date_arret_fttb) = :mois";
  8100.             $params['mois'] = $mois;
  8101.         }
  8102.         if ($semaine !== null) {
  8103.             $sub .= " AND WEEK(a.date_arret_fttb, 1) = :semaine";
  8104.             $params['semaine'] = $semaine;
  8105.         }
  8106.         if ($codeCluster !== null) {
  8107.             $sub .= " AND a.code_cluster = :codeCluster";
  8108.             $params['codeCluster'] = $codeCluster;
  8109.         }
  8110.         if ($codeInsee !== null) {
  8111.             $sub .= " AND a.cod_insee = :codeInsee";
  8112.             $params['codeInsee'] = $codeInsee;
  8113.         }
  8114.         // Requête principale
  8115.         $sql "
  8116.         SELECT COUNT(*) AS total
  8117.         FROM production p
  8118.         WHERE p.code_hexc IN ($sub)
  8119.     ";
  8120.         $stmt $conn->executeQuery($sql$params);
  8121.         return $stmt->fetchOne();
  8122.     }
  8123.     public function getVentesParPrisesCuivre($cpv null$annee null$mois null$semaine null$codeCluster null$codeInsee null)
  8124.     {
  8125.         $conn $this->getEntityManager()->getConnection();
  8126.         // Sous-requête dynamique
  8127.         $sub "
  8128.         SELECT a.cod_hexc
  8129.         FROM nmd_arret_cuivre a
  8130.         WHERE 1 = 1
  8131.     ";
  8132.         $params = [];
  8133.         if ($cpv !== null) {
  8134.             $sub .= " AND a.cpv = :cpv";
  8135.             $params['cpv'] = $cpv;
  8136.         }
  8137.         if ($annee !== null) {
  8138.             $sub .= " AND YEAR(a.arret_cu) = :annee";
  8139.             $params['annee'] = $annee;
  8140.         }
  8141.         if ($mois !== null) {
  8142.             $sub .= " AND MONTH(a.arret_cu) = :mois";
  8143.             $params['mois'] = $mois;
  8144.         }
  8145.         if ($semaine !== null) {
  8146.             $sub .= " AND WEEK(a.arret_cu, 1) = :semaine";
  8147.             $params['semaine'] = $semaine;
  8148.         }
  8149.         if ($codeCluster !== null) {
  8150.             $sub .= " AND a.code_cluster = :codeCluster";
  8151.             $params['codeCluster'] = $codeCluster;
  8152.         }
  8153.         if ($codeInsee !== null) {
  8154.             $sub .= " AND a.cod_insee = :codeInsee";
  8155.             $params['codeInsee'] = $codeInsee;
  8156.         }
  8157.         // Requête principale
  8158.         $sql "
  8159.         SELECT COUNT(*) AS total
  8160.         FROM production p
  8161.         WHERE p.code_hexc IN ($sub)
  8162.     ";
  8163.         $stmt $conn->executeQuery($sql$params);
  8164.         return $stmt->fetchOne();
  8165.     }
  8166. }