src/Controller/ElectricMeterController.php line 52

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Entreprise;
  4. use App\Entity\ElectricMeter;
  5. use App\Form\ElectricMeterType;
  6. use PhpOffice\PhpSpreadsheet\IOFactory;
  7. use Doctrine\ORM\EntityManagerInterface;
  8. use Doctrine\Persistence\ManagerRegistry;
  9. use PhpOffice\PhpSpreadsheet\Cell\DataType;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\Response;
  12. use Symfony\Component\Routing\Annotation\Route;
  13. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  14. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  15. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  16. use Symfony\Component\HttpFoundation\StreamedResponse;
  17. class ElectricMeterController extends AbstractController
  18. {
  19.     /**
  20.      * @Route("/entreprise/electric_meter/add/{id}", name="app_electric_meter_add")
  21.      */
  22.     public function add(int $idRequest $requestEntityManagerInterface $entityManager): Response
  23.     {
  24.         $electric_meters = new ElectricMeter();
  25.         $electric_meters->setEntrepriseId($id);
  26.         $form $this->createForm(ElectricMeterType::class, $electric_meters);
  27.         $form->handleRequest($request);
  28.         if ($form->isSubmitted() && $form->isValid()) {
  29.             $entityManager->persist($electric_meters);
  30.             $entityManager->flush();
  31.             
  32.             // Redirect to the new contract page
  33.             return $this->redirectToRoute('app_entreprise_new_contrat', [
  34.                 'id' => $id,
  35.                 'meterId' => $electric_meters->getId(),
  36.                 'meterType' => 'electric'
  37.             ]);
  38.         }
  39.         return $this->render('entreprise/electric_meter/add.html.twig', [
  40.             'electric_meterForm' => $form->createView(),
  41.             'entreprise_id' => $id
  42.         ]);
  43.     }
  44.     /**
  45.      * @Route("/entreprise/electric_meter/edit/{id}", name="app_electric_meter_edit")
  46.      */
  47.     public function edit(int $idRequest $requestEntityManagerInterface $entityManagerManagerRegistry $doctrine): Response
  48.     {
  49.         $electric_meter $doctrine->getRepository(ElectricMeter::class)->findOneBy(['id' => $id]);
  50.         
  51.         if (!$electric_meter) {
  52.             throw $this->createNotFoundException('Electric meter not found');
  53.         }
  54.         $form $this->createForm(ElectricMeterType::class, $electric_meter);
  55.         $form->handleRequest($request);
  56.         $electric_meter_entity = [ 
  57.             'adresse_compteur' => $electric_meter->getAdresseCompteur(),
  58.             'PDL' => $electric_meter->getPDL(),
  59.             'date_debut' => $electric_meter->getDateDebut() ? date_format($electric_meter->getDateDebut(),"Y-m-d") : "",
  60.             'date_fin' => $electric_meter->getDateFin() ? date_format($electric_meter->getDateFin(),"Y-m-d") : "",
  61.             'PS' => $electric_meter->getPS(),
  62.             'Profil' => $electric_meter->getProfil(),
  63.             'CAR' => $electric_meter->getCAR(),
  64.             'fournisseur' => $electric_meter->getFournisseur(),
  65.             'prix' => $electric_meter->getPrix(),
  66.         ];
  67.         if ($form->isSubmitted() && $form->isValid()) {
  68.             $entityManager->flush();
  69.             return $this->redirectToRoute('app_entreprise_details',['id' => $electric_meter->getEntrepriseId()]);
  70.         }
  71.         return $this->render('entreprise/electric_meter/edit.html.twig', [
  72.             'electric_meterForm' => $form->createView(),
  73.             'electric_meter' => $electric_meter_entity,
  74.             'entreprise_id' => $electric_meter->getEntrepriseId()
  75.         ]);
  76.     }
  77.     /**
  78.      * @Route("/entreprise/electric_meter/suppr/{id}", name="app_electric_meter_suppr")
  79.      */
  80.     public function suppr(int $idRequest $requestEntityManagerInterface $entityManagerManagerRegistry $doctrine): Response
  81.     {
  82.         $electric_meter $doctrine->getRepository(ElectricMeter::class)->findOneBy(['id' => $id]);
  83.         
  84.         if (!$electric_meter) {
  85.             throw $this->createNotFoundException('Electric meter not found');
  86.         }
  87.         $entreprise_id $electric_meter->getEntrepriseId();
  88.         $entityManager->remove($electric_meter);
  89.         $entityManager->flush();
  90.         return $this->redirectToRoute('app_entreprise_details',['id' => $entreprise_id]);
  91.     }
  92.     /**
  93.      * @Route("/entreprise/electric_meter/check_pdl/{pdl}", name="app_electric_meter_check_pdl")
  94.      */
  95.     public function checkPDL($pdlManagerRegistry $doctrine): Response
  96.     {
  97.         $electric_meter $doctrine->getRepository(ElectricMeter::class)->findOneBy(['PDL' => $pdl]);
  98.         if($electric_meter){
  99.             return new Response("exist");
  100.         }
  101.         return new Response("available");
  102.     }
  103.     private function normalizeToKVA($raw): ?float
  104.     {
  105.         if ($raw === null || $raw === '') return null;
  106.         $s = (string)$raw;
  107.         // uniformiser
  108.         $s str_replace(["\xC2\xA0"' '], ''$s);   // supprime espaces/nbsp
  109.         $unit 'KVA';
  110.         // détecte unité (VA/kVA/MVA) puis isole la partie numérique
  111.         if (preg_match('/^\s*([\d\.,]+)\s*([a-zA-Z]*)\s*$/u'$s$m)) {
  112.             $num  str_replace(',''.'$m[1]); // virgule → point
  113.             $unit strtoupper($m[2] ?? 'KVA');
  114.             if (!is_numeric($num)) return null;
  115.             $val = (float)$num;
  116.             // conversion en kVA
  117.             switch ($unit) {
  118.                 case 'MVA': return $val 1000.0;
  119.                 case 'KVA': return $val;
  120.                 case 'VA':  return $val 1000.0;
  121.                 default:    return $val// si pas d’unité, on considère déjà en kVA
  122.             }
  123.         }
  124.         // si juste un nombre brut
  125.         $s str_replace(',''.'$s);
  126.         return is_numeric($s) ? (float)$s null;
  127.     }
  128.     /**
  129.      * @Route(
  130.      *   "/entreprise/electric_meter/generate_study/{pdl}/{type}",
  131.      *   name="app_electric_meter_generate_study",
  132.      *   requirements={"type"="C5|C4|C3|C2|C1"}
  133.      * )
  134.      */
  135.     public function generateStudy(string $pdlstring $typeManagerRegistry $doctrine): StreamedResponse
  136.     {
  137.         // --- Récupération des entités
  138.         $electricMeter $doctrine->getRepository(ElectricMeter::class)->findOneBy(['PDL' => $pdl]);
  139.         if (!$electricMeter) {
  140.             throw $this->createNotFoundException('Electric meter not found');
  141.         }
  142.         $entreprise $doctrine->getRepository(Entreprise::class)->find($electricMeter->getEntrepriseId());
  143.         if (!$entreprise) {
  144.             throw $this->createNotFoundException('Entreprise not found');
  145.         }
  146.         // --- Normalisation du type (C3/C2/C1 => C3_C2_C1)
  147.         $normalizedType in_array($type, ['C3','C2','C1'], true) ? 'C3_C2_C1' $type;
  148.         // --- Choix du template
  149.         $base $this->getParameter('kernel.project_dir') . '/assets/templates';
  150.         $TEMPLATE_BY_TYPE = [
  151.             'C5'        => $base '/Etude_C5.xlsx',
  152.             'C4'        => $base '/Etude_C4.xlsx',
  153.             'C3_C2_C1'  => $base '/Etude_C3_C2_C1.xlsx',
  154.         ];
  155.         $templatePath $TEMPLATE_BY_TYPE[$normalizedType] ?? null;
  156.         if (!$templatePath || !is_file($templatePath)) {
  157.             throw new \RuntimeException(sprintf('Template introuvable pour le type %s'$normalizedType));
  158.         }
  159.         // --- Parsing adresse (voie / CP / ville) robuste
  160.         $address     = (string) ($electricMeter->getAdresseCompteur() ?? '');
  161.         $postalCode  '';
  162.         $city        '';
  163.         $street      '';
  164.         if (preg_match('/\b(\d{5})\b(?:\s+(.+))?$/u'$address$mPREG_OFFSET_CAPTURE)) {
  165.             $postalCode $m[1][0] ?? '';
  166.             $city       = isset($m[2][0]) ? trim($m[2][0]) : '';
  167.             $cpPos      $m[1][1]; // offset du CP dans la chaîne complète
  168.             $street     trim(mb_substr($address0$cpPos));
  169.         } else {
  170.             $street $address;
  171.         }
  172.         // --- Copier le template vers un fichier temporaire
  173.         $tempFile tempnam(sys_get_temp_dir(), 'excel_');
  174.         if ($tempFile === false) {
  175.             throw new \RuntimeException('Impossible de créer un fichier temporaire.');
  176.         }
  177.         if (!@copy($templatePath$tempFile)) {
  178.             @unlink($tempFile);
  179.             throw new \RuntimeException('Copie du template échouée.');
  180.         }
  181.         $ps      $electricMeter->getPS() ?? '';
  182.         $psRaw $electricMeter->getPS();
  183.         $psKVA $this->normalizeToKVA($psRaw);
  184.         $reader IOFactory::createReader('Xlsx');
  185.         $reader->setReadDataOnly(false);
  186.         $reader->setIncludeCharts(true);
  187.         
  188.         $spreadsheet $reader->load($tempFile);
  189.         $sheet $spreadsheet->getActiveSheet();
  190.         $raison  = (string) ($entreprise->getRaisonSociale() ?? '');
  191.         $siret   = (string) ($entreprise->getSIRET() ?? '');
  192.         $pdlVal  = (string) ($electricMeter->getPDL() ?? '');
  193.         $profil  = (string) ($electricMeter->getProfil() ?? '');
  194.         $nafRaw  = (string) ($entreprise->getNaf() ?? '');
  195.         $nafNum  preg_replace('/\D+/'''$nafRaw);
  196.         $nafBig  = (int) $nafNum 4500 'Oui' 'Non';
  197.         $dateFin $electricMeter->getDateFin() ? $electricMeter->getDateFin()->format('d/m/Y') : '';
  198.         switch ($normalizedType) {
  199.             case 'C3_C2_C1':
  200.                 $sheet->setCellValue('A1'$raison);
  201.                 $sheet->setCellValueExplicit('B4'$siretDataType::TYPE_STRING);
  202.                 $sheet->setCellValueExplicit('B5'$pdlValDataType::TYPE_STRING);
  203.                 foreach (['B9','C9','D9','E9','F9'] as $cell) {
  204.                     $sheet->setCellValue($cell$psKVA);
  205.                     $sheet->getStyle($cell)->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
  206.                 }
  207.                 $sheet->setCellValue('B10'$profil);
  208.                 $sheet->setCellValue('B6'$address);
  209.                 $sheet->setCellValueExplicit('J4'$nafRawDataType::TYPE_STRING);
  210.                 $sheet->setCellValue('J5'$nafBig);
  211.                 $sheet->setCellValue('J7'$dateFin);
  212.                 break;
  213.             case 'C4':
  214.                 $sheet->setCellValue('A3'$raison);
  215.                 $sheet->setCellValueExplicit('B6'$siretDataType::TYPE_STRING);
  216.                 $sheet->setCellValueExplicit('B7'$pdlValDataType::TYPE_STRING);
  217.                 $sheet->setCellValue('B8'$psKVA);
  218.                 $sheet->getStyle('B8')->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
  219.                 $sheet->setCellValue('B16'$address);
  220.                 $sheet->setCellValueExplicit('G6'$nafRawDataType::TYPE_STRING);
  221.                 $sheet->setCellValue('G7'$nafBig);
  222.                 $sheet->setCellValue('G9'$dateFin);
  223.                 break;
  224.             case 'C5':
  225.                 $sheet->setCellValue('A3'$raison);
  226.                 $sheet->setCellValueExplicit('B6'$siretDataType::TYPE_STRING);
  227.                 $sheet->setCellValueExplicit('B7'$pdlValDataType::TYPE_STRING);
  228.                 $sheet->setCellValue('B8'$psKVA);
  229.                 $sheet->getStyle('B8')->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
  230.                 $sheet->setCellValue('B16'$address);
  231.                 $sheet->setCellValueExplicit('G6'$nafRawDataType::TYPE_STRING);
  232.                 $sheet->setCellValue('G7'$nafBig);
  233.                 $sheet->setCellValue('G9'$dateFin);
  234.                 break;
  235.         }
  236.         // --- Écriture du fichier au même endroit temporaire (préserve formules & graphiques)
  237.         $writer IOFactory::createWriter($spreadsheet'Xlsx');
  238.         $writer->setOffice2003Compatibility(false);
  239.         $writer->setPreCalculateFormulas(false);
  240.         $writer->setIncludeCharts(true);
  241.         $writer->save($tempFile);
  242.         // --- Préparer un nom de fichier propre (accents/espaces)
  243.         $rawName  $raison !== '' $raison 'unknown';
  244.         $ascii    = @iconv('UTF-8''ASCII//TRANSLIT//IGNORE'$rawName) ?: $rawName;
  245.         $ascii    preg_replace('/[^\w\-]+/u''_'$ascii);
  246.         // On garde le type tel que demandé dans l’URL (C5|C4|C3|C2|C1)
  247.         $studyLabel $type;
  248.         // 4 derniers chiffres du PDL
  249.         $pdlLast4 $pdlVal !== '' substr($pdlVal, -4) : '0000';
  250.         // Timestamp lisible (Europe/Paris)
  251.         $stamp = (new \DateTime('now', new \DateTimeZone('Europe/Paris')))->format('dmY');
  252.         // Résultat : Etude_C5_1234_Flex_Energie-20250925-223015.xlsx
  253.         $filename sprintf('Etude_%s-%s-%s-%s.xlsx'$studyLabel$pdlLast4$ascii$stamp);
  254.         // --- StreamedResponse (gestion mémoire + cleanup même en cas d’erreur)
  255.         $response = new StreamedResponse(function () use ($tempFile) {
  256.             $out fopen('php://output''wb');
  257.             $in  fopen($tempFile'rb');
  258.             try {
  259.                 stream_copy_to_stream($in$out);
  260.             } finally {
  261.                 if (is_resource($in))  fclose($in);
  262.                 if (is_resource($out)) fclose($out);
  263.                 @unlink($tempFile);
  264.             }
  265.         });
  266.         $response->headers->set('Content-Type''application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  267.         $response->headers->set('Cache-Control''max-age=0, private');
  268.         $response->headers->set('Pragma''public');
  269.         $response->headers->set('X-Accel-Buffering''no'); // no-op si non supporté
  270.         $response->setStatusCode(200);
  271.         $response->headers->set(
  272.             'Content-Disposition',
  273.             $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT$filename)
  274.         );
  275.         return $response;
  276.     }
  277. }