<?php
namespace App\Controller;
use App\Entity\Entreprise;
use App\Entity\ElectricMeter;
use App\Form\ElectricMeterType;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\StreamedResponse;
class ElectricMeterController extends AbstractController
{
/**
* @Route("/entreprise/electric_meter/add/{id}", name="app_electric_meter_add")
*/
public function add(int $id, Request $request, EntityManagerInterface $entityManager): Response
{
$electric_meters = new ElectricMeter();
$electric_meters->setEntrepriseId($id);
$form = $this->createForm(ElectricMeterType::class, $electric_meters);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($electric_meters);
$entityManager->flush();
// Redirect to the new contract page
return $this->redirectToRoute('app_entreprise_new_contrat', [
'id' => $id,
'meterId' => $electric_meters->getId(),
'meterType' => 'electric'
]);
}
return $this->render('entreprise/electric_meter/add.html.twig', [
'electric_meterForm' => $form->createView(),
'entreprise_id' => $id
]);
}
/**
* @Route("/entreprise/electric_meter/edit/{id}", name="app_electric_meter_edit")
*/
public function edit(int $id, Request $request, EntityManagerInterface $entityManager, ManagerRegistry $doctrine): Response
{
$electric_meter = $doctrine->getRepository(ElectricMeter::class)->findOneBy(['id' => $id]);
if (!$electric_meter) {
throw $this->createNotFoundException('Electric meter not found');
}
$form = $this->createForm(ElectricMeterType::class, $electric_meter);
$form->handleRequest($request);
$electric_meter_entity = [
'adresse_compteur' => $electric_meter->getAdresseCompteur(),
'PDL' => $electric_meter->getPDL(),
'date_debut' => $electric_meter->getDateDebut() ? date_format($electric_meter->getDateDebut(),"Y-m-d") : "",
'date_fin' => $electric_meter->getDateFin() ? date_format($electric_meter->getDateFin(),"Y-m-d") : "",
'PS' => $electric_meter->getPS(),
'Profil' => $electric_meter->getProfil(),
'CAR' => $electric_meter->getCAR(),
'fournisseur' => $electric_meter->getFournisseur(),
'prix' => $electric_meter->getPrix(),
];
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush();
return $this->redirectToRoute('app_entreprise_details',['id' => $electric_meter->getEntrepriseId()]);
}
return $this->render('entreprise/electric_meter/edit.html.twig', [
'electric_meterForm' => $form->createView(),
'electric_meter' => $electric_meter_entity,
'entreprise_id' => $electric_meter->getEntrepriseId()
]);
}
/**
* @Route("/entreprise/electric_meter/suppr/{id}", name="app_electric_meter_suppr")
*/
public function suppr(int $id, Request $request, EntityManagerInterface $entityManager, ManagerRegistry $doctrine): Response
{
$electric_meter = $doctrine->getRepository(ElectricMeter::class)->findOneBy(['id' => $id]);
if (!$electric_meter) {
throw $this->createNotFoundException('Electric meter not found');
}
$entreprise_id = $electric_meter->getEntrepriseId();
$entityManager->remove($electric_meter);
$entityManager->flush();
return $this->redirectToRoute('app_entreprise_details',['id' => $entreprise_id]);
}
/**
* @Route("/entreprise/electric_meter/check_pdl/{pdl}", name="app_electric_meter_check_pdl")
*/
public function checkPDL($pdl, ManagerRegistry $doctrine): Response
{
$electric_meter = $doctrine->getRepository(ElectricMeter::class)->findOneBy(['PDL' => $pdl]);
if($electric_meter){
return new Response("exist");
}
return new Response("available");
}
private function normalizeToKVA($raw): ?float
{
if ($raw === null || $raw === '') return null;
$s = (string)$raw;
// uniformiser
$s = str_replace(["\xC2\xA0", ' '], '', $s); // supprime espaces/nbsp
$unit = 'KVA';
// détecte unité (VA/kVA/MVA) puis isole la partie numérique
if (preg_match('/^\s*([\d\.,]+)\s*([a-zA-Z]*)\s*$/u', $s, $m)) {
$num = str_replace(',', '.', $m[1]); // virgule → point
$unit = strtoupper($m[2] ?? 'KVA');
if (!is_numeric($num)) return null;
$val = (float)$num;
// conversion en kVA
switch ($unit) {
case 'MVA': return $val * 1000.0;
case 'KVA': return $val;
case 'VA': return $val / 1000.0;
default: return $val; // si pas d’unité, on considère déjà en kVA
}
}
// si juste un nombre brut
$s = str_replace(',', '.', $s);
return is_numeric($s) ? (float)$s : null;
}
/**
* @Route(
* "/entreprise/electric_meter/generate_study/{pdl}/{type}",
* name="app_electric_meter_generate_study",
* requirements={"type"="C5|C4|C3|C2|C1"}
* )
*/
public function generateStudy(string $pdl, string $type, ManagerRegistry $doctrine): StreamedResponse
{
// --- Récupération des entités
$electricMeter = $doctrine->getRepository(ElectricMeter::class)->findOneBy(['PDL' => $pdl]);
if (!$electricMeter) {
throw $this->createNotFoundException('Electric meter not found');
}
$entreprise = $doctrine->getRepository(Entreprise::class)->find($electricMeter->getEntrepriseId());
if (!$entreprise) {
throw $this->createNotFoundException('Entreprise not found');
}
// --- Normalisation du type (C3/C2/C1 => C3_C2_C1)
$normalizedType = in_array($type, ['C3','C2','C1'], true) ? 'C3_C2_C1' : $type;
// --- Choix du template
$base = $this->getParameter('kernel.project_dir') . '/assets/templates';
$TEMPLATE_BY_TYPE = [
'C5' => $base . '/Etude_C5.xlsx',
'C4' => $base . '/Etude_C4.xlsx',
'C3_C2_C1' => $base . '/Etude_C3_C2_C1.xlsx',
];
$templatePath = $TEMPLATE_BY_TYPE[$normalizedType] ?? null;
if (!$templatePath || !is_file($templatePath)) {
throw new \RuntimeException(sprintf('Template introuvable pour le type %s', $normalizedType));
}
// --- Parsing adresse (voie / CP / ville) robuste
$address = (string) ($electricMeter->getAdresseCompteur() ?? '');
$postalCode = '';
$city = '';
$street = '';
if (preg_match('/\b(\d{5})\b(?:\s+(.+))?$/u', $address, $m, PREG_OFFSET_CAPTURE)) {
$postalCode = $m[1][0] ?? '';
$city = isset($m[2][0]) ? trim($m[2][0]) : '';
$cpPos = $m[1][1]; // offset du CP dans la chaîne complète
$street = trim(mb_substr($address, 0, $cpPos));
} else {
$street = $address;
}
// --- Copier le template vers un fichier temporaire
$tempFile = tempnam(sys_get_temp_dir(), 'excel_');
if ($tempFile === false) {
throw new \RuntimeException('Impossible de créer un fichier temporaire.');
}
if (!@copy($templatePath, $tempFile)) {
@unlink($tempFile);
throw new \RuntimeException('Copie du template échouée.');
}
$ps = $electricMeter->getPS() ?? '';
$psRaw = $electricMeter->getPS();
$psKVA = $this->normalizeToKVA($psRaw);
$reader = IOFactory::createReader('Xlsx');
$reader->setReadDataOnly(false);
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($tempFile);
$sheet = $spreadsheet->getActiveSheet();
$raison = (string) ($entreprise->getRaisonSociale() ?? '');
$siret = (string) ($entreprise->getSIRET() ?? '');
$pdlVal = (string) ($electricMeter->getPDL() ?? '');
$profil = (string) ($electricMeter->getProfil() ?? '');
$nafRaw = (string) ($entreprise->getNaf() ?? '');
$nafNum = preg_replace('/\D+/', '', $nafRaw);
$nafBig = (int) $nafNum > 4500 ? 'Oui' : 'Non';
$dateFin = $electricMeter->getDateFin() ? $electricMeter->getDateFin()->format('d/m/Y') : '';
switch ($normalizedType) {
case 'C3_C2_C1':
$sheet->setCellValue('A1', $raison);
$sheet->setCellValueExplicit('B4', $siret, DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B5', $pdlVal, DataType::TYPE_STRING);
foreach (['B9','C9','D9','E9','F9'] as $cell) {
$sheet->setCellValue($cell, $psKVA);
$sheet->getStyle($cell)->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
}
$sheet->setCellValue('B10', $profil);
$sheet->setCellValue('B6', $address);
$sheet->setCellValueExplicit('J4', $nafRaw, DataType::TYPE_STRING);
$sheet->setCellValue('J5', $nafBig);
$sheet->setCellValue('J7', $dateFin);
break;
case 'C4':
$sheet->setCellValue('A3', $raison);
$sheet->setCellValueExplicit('B6', $siret, DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B7', $pdlVal, DataType::TYPE_STRING);
$sheet->setCellValue('B8', $psKVA);
$sheet->getStyle('B8')->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
$sheet->setCellValue('B16', $address);
$sheet->setCellValueExplicit('G6', $nafRaw, DataType::TYPE_STRING);
$sheet->setCellValue('G7', $nafBig);
$sheet->setCellValue('G9', $dateFin);
break;
case 'C5':
$sheet->setCellValue('A3', $raison);
$sheet->setCellValueExplicit('B6', $siret, DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B7', $pdlVal, DataType::TYPE_STRING);
$sheet->setCellValue('B8', $psKVA);
$sheet->getStyle('B8')->getNumberFormat()->setFormatCode('#,##0.## "kVA"');
$sheet->setCellValue('B16', $address);
$sheet->setCellValueExplicit('G6', $nafRaw, DataType::TYPE_STRING);
$sheet->setCellValue('G7', $nafBig);
$sheet->setCellValue('G9', $dateFin);
break;
}
// --- Écriture du fichier au même endroit temporaire (préserve formules & graphiques)
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setOffice2003Compatibility(false);
$writer->setPreCalculateFormulas(false);
$writer->setIncludeCharts(true);
$writer->save($tempFile);
// --- Préparer un nom de fichier propre (accents/espaces)
$rawName = $raison !== '' ? $raison : 'unknown';
$ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $rawName) ?: $rawName;
$ascii = preg_replace('/[^\w\-]+/u', '_', $ascii);
// On garde le type tel que demandé dans l’URL (C5|C4|C3|C2|C1)
$studyLabel = $type;
// 4 derniers chiffres du PDL
$pdlLast4 = $pdlVal !== '' ? substr($pdlVal, -4) : '0000';
// Timestamp lisible (Europe/Paris)
$stamp = (new \DateTime('now', new \DateTimeZone('Europe/Paris')))->format('dmY');
// Résultat : Etude_C5_1234_Flex_Energie-20250925-223015.xlsx
$filename = sprintf('Etude_%s-%s-%s-%s.xlsx', $studyLabel, $pdlLast4, $ascii, $stamp);
// --- StreamedResponse (gestion mémoire + cleanup même en cas d’erreur)
$response = new StreamedResponse(function () use ($tempFile) {
$out = fopen('php://output', 'wb');
$in = fopen($tempFile, 'rb');
try {
stream_copy_to_stream($in, $out);
} finally {
if (is_resource($in)) fclose($in);
if (is_resource($out)) fclose($out);
@unlink($tempFile);
}
});
$response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response->headers->set('Cache-Control', 'max-age=0, private');
$response->headers->set('Pragma', 'public');
$response->headers->set('X-Accel-Buffering', 'no'); // no-op si non supporté
$response->setStatusCode(200);
$response->headers->set(
'Content-Disposition',
$response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename)
);
return $response;
}
}