src/Controller/SocietyController.php line 79

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\ClauseScoiety;
  4. use App\Entity\Client;
  5. use App\Entity\DomiciliationAddress;
  6. use App\Entity\DocumentSociety;
  7. use App\Entity\Files;
  8. use App\Entity\President;
  9. use App\Entity\Signatures;
  10. use App\Entity\SirenEnterprise;
  11. use App\Entity\Society;
  12. use App\Entity\UserClauseSelection;
  13. use App\Entity\UserSocietyActivities;
  14. use App\Events\DocumentEvent;
  15. use App\Form\AssociesType;
  16. use App\Form\PresidentType;
  17. use App\Form\SocietyConditionsType;
  18. use App\Form\SocietyIndicatorType;
  19. use App\Form\SocietyType;
  20. use App\Manager\InpiManager;
  21. use App\Repository\ClauseScoietyRepository;
  22. use App\Repository\ClientRepository;
  23. use App\Repository\DocumentSocietyRepository;
  24. use App\Repository\FilesRepository;
  25. use App\Repository\IbanRepository;
  26. use App\Repository\SirenEnterpriseRepository;
  27. use App\Repository\SocietyRepository;
  28. use App\Repository\UserClauseSelectionRepository;
  29. use App\Repository\UserRepository;
  30. use App\Service\MailService;
  31. use App\Service\S3Service;
  32. use Doctrine\ORM\EntityManagerInterface;
  33. use Knp\Component\Pager\PaginatorInterface;
  34. use Psr\EventDispatcher\EventDispatcherInterface;
  35. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  36. use Symfony\Component\EventDispatcher\GenericEvent;
  37. use Symfony\Component\HttpClient\HttpClient;
  38. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  39. use Symfony\Component\HttpFoundation\JsonResponse;
  40. use Symfony\Component\HttpFoundation\RedirectResponse;
  41. use Symfony\Component\HttpFoundation\Request;
  42. use Symfony\Component\HttpFoundation\RequestStack;
  43. use Symfony\Component\HttpFoundation\Response;
  44. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  45. use Symfony\Component\Routing\Annotation\Route;
  46. use PhpOffice\PhpWord\Shared\ZipArchive;
  47. use Symfony\Component\Security\Core\Security;
  48. use Symfony\Contracts\HttpClient\HttpClientInterface;
  49. /**
  50.  * @Route("/society")
  51.  */
  52. class SocietyController extends AbstractController
  53. {
  54.     private $mailService;
  55.     private $documentSocietyRepository;
  56.     private $entityManager;
  57.     private $s3Service;
  58.     private $httpClient;
  59.     public function __construct(MailService $mailServiceDocumentSocietyRepository $documentSocietyRepositoryEntityManagerInterface $entityManagerS3Service $s3Service HttpClientInterface $httpClient)
  60.     {
  61.         $this->mailService $mailService;
  62.         $this->documentSocietyRepository $documentSocietyRepository;
  63.         $this->entityManager $entityManager;
  64.         $this->s3Service $s3Service;
  65.         $this->httpClient $httpClient;    }
  66.     /**
  67.      * @Route("/", name="society_index", methods={"GET"})
  68.      */
  69.     public function index(Request $requestPaginatorInterface $paginatorSocietyRepository $societyRepositoryUserRepository $userRepository)
  70.     {
  71.         $connectedUser $this->getUser();
  72.         if (!$connectedUser) {
  73.             throw $this->createAccessDeniedException('Vous devez être connecté pour accéder à cette page.');
  74.         }
  75.         $collaborator $request->query->getInt('collaborator') ?: null;
  76.         $postalCode   trim(strip_tags((string) $request->query->get('postal_code'''))) ?: null;
  77.         $address      trim(strip_tags((string) $request->query->get('address'''))) ?: null;
  78.         $name         trim(strip_tags((string) $request->query->get('name'''))) ?: null;
  79.         $number       trim(strip_tags((string) $request->query->get('number'''))) ?: null;
  80.         $created      trim(strip_tags((string) $request->query->get('created'''))) ?: null;
  81.         $users = [];
  82.         $collaborators = [];
  83.         $isClient false;
  84.         $isCollaborator false;
  85.         if ($this->isGranted('ROLE_ADMIN')) {
  86.             $users $userRepository->findAll();
  87.         } elseif ($this->isGranted('ROLE_CLIENT')) {
  88.             $users $userRepository->findCollaboratorsByClient($connectedUser);
  89.             $collaborators array_map(function ($user) {
  90.                 return $user->getId();
  91.             }, $users);
  92.             $isClient true;
  93.         } elseif ($this->isGranted('ROLE_COLLABORATEUR')) {
  94.             // If the user is a collaborator, show only their own societies
  95.             $collaborators = [$connectedUser->getId()];
  96.             $isCollaborator true;
  97.         }
  98.         $queryBuilder $societyRepository->findByFiltersQueryBuilder(
  99.             $collaborator$postalCode$address$name$number$connectedUser->getClient(), $collaborators$isClient$isCollaborator$created
  100.         );
  101.         // Pagination côté serveur avec Knp Paginator
  102.         $pagination $paginator->paginate(
  103.             $queryBuilder,
  104.             $request->query->getInt('page'1),
  105.             12 // Nombre d'items par page
  106.         );
  107.         // Calcul des statistiques significatives
  108.         $allSocieties $queryBuilder->getQuery()->getResult();
  109.         // Sociétés créées ce mois
  110.         $startOfMonth = new \DateTime('first day of this month 00:00:00');
  111.         $createdThisMonth array_filter($allSocieties, function($society) use ($startOfMonth) {
  112.             return $society->getCreated() && $society->getCreated() >= $startOfMonth;
  113.         });
  114.         // Sociétés modifiées dans les 7 derniers jours
  115.         $sevenDaysAgo = new \DateTime('-7 days');
  116.         $recentlyUpdated array_filter($allSocieties, function($society) use ($sevenDaysAgo) {
  117.             return $society->getUpdated() && $society->getUpdated() >= $sevenDaysAgo;
  118.         });
  119.         // Sociétés en attente (status = 'created')
  120.         $pendingSocieties array_filter($allSocieties, function($society) {
  121.             return $society->getStatus() === Society::STATUS_CREATED;
  122.         });
  123.         return $this->render('society/index.html.twig', [
  124.             'pagination' => $pagination,
  125.             'users' => $users,
  126.             'stats' => [
  127.                 'createdThisMonth' => count($createdThisMonth),
  128.                 'recentlyUpdated' => count($recentlyUpdated),
  129.                 'pending' => count($pendingSocieties),
  130.             ],
  131.         ]);
  132.     }
  133.     /**
  134.      * @Route("/new", name="society_new", methods={"GET", "POST"})
  135.      */
  136.     public
  137.     function new(Request $requestEntityManagerInterface $entityManagerRequestStack $requestStack): Response
  138.     {
  139.         $society = new Society();
  140.         $user $this->getUser();
  141.         /**
  142.          * @var Client $client
  143.          */
  144.         $client $user->getClient();
  145.         // Pré-remplir les infos de domiciliation depuis le client existant
  146.         if ($client) {
  147.             $domClient = new \App\Entity\Client();
  148.             $domClient->setName($client->getName());
  149.             $domClient->setSocietyForme($client->getSocietyForme());
  150.             $domClient->setSiren($client->getSiren());
  151.             $domClient->setAgreementNumber($client->getAgreementNumber());
  152.             $domClient->setSocietySiege($client->getSocietySiege());
  153.             $domClient->setCp($client->getCp());
  154.             $domClient->setVille($client->getVille());
  155.             $domClient->setSocietyCapitalChiffre($client->getSocietyCapitalChiffre());
  156.             $domClient->setSocietyCapitalLettre($client->getSocietyCapitalLettre());
  157.             $domClient->setQualityRepresentant($client->getQualityRepresentant());
  158.             $domClient->setCivility($client->getCivility());
  159.             $domClient->setRepresantantLastName($client->getRepresantantLastName());
  160.             $domClient->setRepresantantFirstName($client->getRepresantantFirstName());
  161.             $domClient->setDescriptionService($client->getDescriptionService());
  162.             $domClient->setImmeubleDescription($client->getImmeubleDescription());
  163.             $domClient->setEquipment($client->getEquipment());
  164.             $domClient->setSurfaceOffice($client->getSurfaceOffice());
  165.             $domClient->setPieceNumber($client->getPieceNumber());
  166.             $domClient->setMontantRedevance($client->getMontantRedevance());
  167.             $domClient->setDureeContrat($client->getDureeContrat());
  168.             $domClient->setDelaiDay($client->getDelaiDay());
  169.             $society->setClient($domClient);
  170.         }
  171.         $form $this->createForm(SocietyType::class, $society);
  172.         $form->handleRequest($request);
  173.         $formAssocy $this->createForm(AssociesType::class);
  174.         $client->getSociety()->count();
  175.         if ($form->isSubmitted() && $form->isValid()) {
  176.             $number sprintf("%08d"$client->getSociety()->count() + 1);
  177.             $society->setCreated(new \DateTime("now"));
  178.             $society->setClient($user->getClient());
  179.             $society->setStatus(Society::STATUS_CREATED);
  180.             $society->setNumber(sprintf('%08d'$number));
  181.             $society->setUser($user);
  182.             $society->setFreeOrder(true);
  183.             // Store the selected domiciliation address reference
  184.             $domiciliationAddressId $request->request->get('domiciliation_address_id');
  185.             if ($domiciliationAddressId) {
  186.                 $domiciliationAddress $entityManager->getRepository(DomiciliationAddress::class)->find($domiciliationAddressId);
  187.                 if ($domiciliationAddress && $domiciliationAddress->getClient() === $client) {
  188.                     $society->setDomiciliationAddress($domiciliationAddress);
  189.                 }
  190.             }
  191.             $entityManager->persist($society);
  192.             $entityManager->flush();
  193.             $publicDirectory $this->getParameter('pdf_directory');
  194.             $filepath $publicDirectory "/" $society->getId() . "/";
  195.             if (!file_exists($filepath)) {
  196.                 mkdir($filepath0777true);
  197.             }
  198.             $requestStack->getSession()->set('tab''society');
  199.             $entityManager->flush();
  200.             return $this->redirectToRoute('society_show', ['id' => $society->getId()], Response::HTTP_SEE_OTHER);
  201.         }
  202.         $domiciliationAddresses $client->getDomiciliationAddresses();
  203.         $yearEnd date('j/m'strtotime('12/31'));
  204.         $now = new \DateTime();
  205.         return $this->renderForm('society/new.html.twig', [
  206.             'society' => $society,
  207.             'endDate' => $yearEnd,
  208.             'now' => date('j/m/Y'),
  209.             'form' => $form,
  210.             'formAssocy' => $formAssocy,
  211.             'client' => $user,
  212.             'domiciliationAddresses' => $domiciliationAddresses,
  213.         ]);
  214.     }
  215.     /**
  216.      * @Route("/api/domiciliation-address/{id}", name="api_domiciliation_address", methods={"GET"})
  217.      */
  218.     public function getDomiciliationAddress(int $idEntityManagerInterface $entityManager): JsonResponse
  219.     {
  220.         $user $this->getUser();
  221.         $client $user->getClient();
  222.         $address $entityManager->getRepository(DomiciliationAddress::class)->find($id);
  223.         if (!$address || $address->getClient() !== $client) {
  224.             return new JsonResponse(['error' => 'Adresse non trouvée'], Response::HTTP_NOT_FOUND);
  225.         }
  226.         return new JsonResponse([
  227.             'id' => $address->getId(),
  228.             'label' => $address->getLabel(),
  229.             'name' => $address->getName(),
  230.             'societyForme' => $address->getSocietyForme(),
  231.             'siren' => $address->getSiren(),
  232.             'agreementNumber' => $address->getAgreementNumber(),
  233.             'societySiege' => $address->getSocietySiege(),
  234.             'cp' => $address->getCp(),
  235.             'ville' => $address->getVille(),
  236.             'societyCapitalChiffre' => $address->getSocietyCapitalChiffre(),
  237.             'societyCapitalLettre' => $address->getSocietyCapitalLettre(),
  238.             'qualityRepresentant' => $address->getQualityRepresentant(),
  239.             'civility' => $address->getCivility(),
  240.             'represantantLastName' => $address->getRepresantantLastName(),
  241.             'represantantFirstName' => $address->getRepresantantFirstName(),
  242.             'descriptionService' => $address->getDescriptionService(),
  243.             'immeubleDescription' => $address->getImmeubleDescription(),
  244.             'equipment' => $address->getEquipment(),
  245.             'surfaceOffice' => $address->getSurfaceOffice(),
  246.             'pieceNumber' => $address->getPieceNumber(),
  247.             'montantRedevance' => $address->getMontantRedevance(),
  248.             'domiciliationDate' => $address->getDomiciliationDate() ? $address->getDomiciliationDate()->format('Y-m-d') : null,
  249.             'dureeContrat' => $address->getDureeContrat(),
  250.             'delaiDay' => $address->getDelaiDay(),
  251.         ]);
  252.     }
  253.     /**
  254.      * @Route("/searchSiren", name="society_siren_info")
  255.      */
  256.     public function fetchEnterpriseData(SirenEnterpriseRepository $sirenEnterpriseRepositoryEntityManagerInterface $entityManagerRequest $request)
  257.     {
  258.         $siren $request->request->get('siren');
  259.         $enterprise $sirenEnterpriseRepository->findOneBy(['siren' => $siren]);
  260.         if ($enterprise) {
  261.             $html $this->renderView('society/enterpriseInfo.html.twig', ['data' => $enterprise->getData(), 'siren' => $siren]);
  262.             return new Response($html);
  263.         }
  264.         $apiToken 'fa81590fc90352718e3dd057c27a9f34cd57188d83fd002f';
  265.         $url "https://api.pappers.fr/v2/entreprise?api_token=$apiToken&siren=$siren";
  266.         $httpClient HttpClient::create();
  267.         $response $httpClient->request('GET'$url);
  268.         $data = [];
  269.         $apiError null;
  270.         $statusCode $response->getStatusCode();
  271.         if ($statusCode === 200) {
  272.             $content $response->getContent();
  273.             $data json_decode($contenttrue);
  274.             $enterprise = new SirenEnterprise();
  275.             $enterprise->setSiren($siren);
  276.             $enterprise->setData($data);
  277.             $entityManager->persist($enterprise);
  278.             $entityManager->flush();
  279.         } else {
  280.             $content $response->getContent(false);
  281.             $errorData json_decode($contenttrue);
  282.             if (isset($errorData['error'])) {
  283.                 $apiError $errorData['error'];
  284.             }
  285.         }
  286.         //normalement data hna fil edit njib associe ou bien dirigent -> getRepresentantData mech donnés mta3 l api, 5ater badel fihom
  287.         $html $this->renderView('society/enterpriseInfo.html.twig', ['data' => $data'siren' => $siren'apiError' => $apiError]);
  288.         return new Response($html);
  289.     }
  290.     /**
  291.      * @Route("/{id}", name="society_show", methods={"GET"})
  292.      */
  293.     public function show(Society $societyClauseScoietyRepository $clauseScoietyRepositoryIbanRepository $ibanRepositoryFilesRepository $filesRepositoryEntityManagerInterface $entityManagerRequest $requestInpiManager $inpiManagerSecurity $securityUserClauseSelectionRepository $userClauseSelectionRepositoryRequestStack $requestStack): Response
  294.     {
  295.         $user $security->getUser();
  296.         if ($society->getUser() !== $user) {
  297.             return $this->render('redirection/AccessDenied.html.twig');
  298.         }
  299.         $selectedProfile null;
  300.         $statutsBySocietyType null;
  301.         if ($society->getType()) {
  302.             $statutsBySocietyType $userClauseSelectionRepository->StatusSociety($user->getId(), $society->getType()->getId());
  303.             if ($society->getSelectedProfil()) {
  304.                 $selectedProfile $society->getSelectedProfil()->getClauses();
  305.             }
  306.         }
  307.         $actifTabParam $request->query->get('actifTab');
  308.         if ($actifTabParam === 'signature') {
  309.             $actifTab 'signature';
  310.         } else {
  311.             $tab $requestStack->getSession()->get('tab');
  312.             if ($tab && $tab == 'society') {
  313.                 $actifTab 'society';
  314.             } else
  315.                 $actifTab 'document';
  316.             if (count($society->getDocumentSocieties()) > 0) {
  317.                 $requestStack->getSession()->remove('tab');
  318.             }else{
  319.                 $actifTab 'society';
  320.             }
  321.         }
  322.         $form $this->createForm(SocietyType::class, $society);
  323.         $formSocietyIndicator $this->createForm(SocietyIndicatorType::class);
  324.         $firstAssocy $society->getAssocies()->first() ?: null;
  325.         $formAssocy $this->createForm(AssociesType::class, $firstAssocy, ['pourcentageMax' => $society->getTotalPourcentageAssocies()]);
  326.         $formPresident $this->createForm(PresidentType::class,new President());
  327.         //dd($formPresident);
  328.         $SocietyConditions $society->getSocietyConditions() ? $society->getSocietyConditions() : null;
  329.         $formSocietyConditions $this->createForm(SocietyConditionsType::class, $SocietyConditions);
  330.         $form->handleRequest($request);
  331.         $files $filesRepository->findBy(['society' => $society->getId()]);
  332.         $documents $society->getDocumentSocieties();
  333.         //$signatairesByDocument = [];
  334.         //foreach ($documents as $document) {
  335.         //$signatairesByDocument[$document->getId()] = $document->getSociety()->getSignataires() ?? [];
  336.         //}
  337.         mb_internal_encoding("UTF-8"); // Assurez-vous que l'encodage interne est défini sur UTF-8
  338.         $societyTypeName $society->getType() ? $society->getType()->getName() : "NC";
  339.         $decodedName mb_convert_case($societyTypeNameMB_CASE_LOWER"UTF-8");
  340.         $includeCommon = !str_contains($decodedName'sasu');
  341.         $statuts $clauseScoietyRepository->findBySocietyType(trim($decodedName), $includeCommon);
  342.         $recipients $society->getAssociesEmails();
  343.         //b$documentsSigned = $entityManager->getRepository(Signatures::class)->findBy(['status' => $society->getId(), 'si' => ['!=' => null]]);
  344.         return $this->render('society/show.html.twig', [
  345.             'society' => $society,
  346.             'recipients' => $recipients,
  347.             'documents' => $documents,
  348.             'signataires' => $society->getSignataires(),
  349.             'form' => $form->createView(),
  350.             'formAssocy' => $formAssocy->createView(),
  351.             'formSocietyConditions' => $formSocietyConditions->createView(),
  352.             'formPresident' => $formPresident->createView(),
  353.             'formSocietyIndicator' => $formSocietyIndicator->createView(),
  354.             'associes' => $society->getAssocies(),
  355.             'presidents' => $society->getPresidents(),
  356.             'society_indicators' => $society->getSocietyIndicators(),
  357.             'files' => $files,
  358.             'directory' => "uploads/societyDocuments/",
  359.             'statuts' => $statuts,
  360.             'statutsBySocietyType' => $statutsBySocietyType,
  361.             'selectedProfile' => $selectedProfile,
  362.             'actifTab' => $actifTab,
  363.             'domiciliationAddresses' => $user->getClient() ? $user->getClient()->getDomiciliationAddresses() : [],
  364.         ]);
  365.     }
  366.     /**
  367.      * @Route("/api/society/{id}/can-update", name="api_society_can_update", methods={"GET"})
  368.      */
  369.     public function canUpdateSocietyApi(int $idEntityManagerInterface $em): JsonResponse
  370.     {
  371.         $society $em->getRepository(Society::class)->find($id);
  372.         if (!$society) {
  373.             return new JsonResponse(['error' => 'Société introuvable'], 404);
  374.         }
  375.         $associes count($society->getAssocies());
  376.         $dirigeants count($society->getPresidents());
  377.         $canUpdate $associes && $dirigeants 0;
  378.         return new JsonResponse(['canUpdate' => $canUpdate]);
  379.     }
  380.     /**
  381.      * @Route("/{id}/edit", name="society_edit", methods={"GET", "POST"})
  382.      */
  383.     public function edit(Request $requestSociety $societyEntityManagerInterface $entityManagerFilesRepository $filesRepositoryEventDispatcherInterface $dispatcher): Response
  384.     {
  385.         $form $this->createForm(SocietyType::class, $society);
  386.         $form->handleRequest($request);
  387.         $publicDirectory $this->getParameter('pdf_directory');
  388.         $filepath $publicDirectory "/" $society->getId() . "/";
  389.         if (!file_exists($filepath)) {
  390.             mkdir($filepath0777true);
  391.         }
  392.         if ($form->isSubmitted()) {
  393.             // Sauvegarder les champs condition_* des formulaires AJAX dans societyStatus
  394.             $allParams $request->request->all();
  395.             $societyStatus $society->getSocietyStatus() ?? [];
  396.             foreach ($allParams as $key => $value) {
  397.                 if (strpos($key'condition_') === && !empty($value)) {
  398.                     $societyStatus[$key] = $value;
  399.                 }
  400.             }
  401.             $society->setSocietyStatus($societyStatus);
  402.             // Sauvegarder les champs capitalVariable / capitalMontantMin / capitalMontantMax sur le premier associé
  403.             $associesPostData $request->request->all('associes');
  404.             if (!empty($associesPostData)) {
  405.                 $firstAssocy $society->getAssocies()->first();
  406.                 if ($firstAssocy) {
  407.                     $firstAssocy->setCapitalVariable(!empty($associesPostData['capitalVariable']));
  408.                     $firstAssocy->setCapitalMontantMin($associesPostData['capitalMontantMin'] ?? null);
  409.                     $firstAssocy->setCapitalMontantMax($associesPostData['capitalMontantMax'] ?? null);
  410.                     if (!empty($associesPostData['Periodicite'])) {
  411.                         $firstAssocy->setPeriodicite($associesPostData['Periodicite']);
  412.                     }
  413.                     $entityManager->persist($firstAssocy);
  414.                 }
  415.             } else {
  416.                 // Checkbox non cochée = décochée
  417.                 $firstAssocy $society->getAssocies()->first();
  418.                 if ($firstAssocy) {
  419.                     $firstAssocy->setCapitalVariable(false);
  420.                     $entityManager->persist($firstAssocy);
  421.                 }
  422.             }
  423.             // Store the selected domiciliation address reference
  424.             $domiciliationAddressId $request->request->get('domiciliation_address_id');
  425.             if ($domiciliationAddressId) {
  426.                 $domiciliationAddress $entityManager->getRepository(DomiciliationAddress::class)->find($domiciliationAddressId);
  427.                 if ($domiciliationAddress && $domiciliationAddress->getClient() === $society->getClient()) {
  428.                     $society->setDomiciliationAddress($domiciliationAddress);
  429.                 }
  430.             } else {
  431.                 $society->setDomiciliationAddress(null);
  432.             }
  433.             $society->setUpdated(new \DateTime("now"));
  434.             $userSocietyAcitvities = new UserSocietyActivities();
  435.             $userSocietyAcitvities->setSociety($society);
  436.             $userSocietyAcitvities->setUser($this->getUser());
  437.             $userSocietyAcitvities->setClient($society->getClient());
  438.             $userSocietyAcitvities->setUpdatedAt(new \DateTime());
  439.             $entityManager->persist($userSocietyAcitvities);
  440.             $entityManager->flush();
  441.             $dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_REMOVE_DOCUMENTS);
  442.             $dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_CREATE);
  443.             //$dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_UPDATE);
  444.             $entityManager->flush();
  445.             return $this->redirectToRoute('society_show', ['id' => $society->getId()], Response::HTTP_SEE_OTHER);
  446.         }
  447.         $user $this->getUser();
  448.         $client $user->getClient();
  449.         $domiciliationAddresses $client $client->getDomiciliationAddresses() : [];
  450.         $files $filesRepository->findBy(['society' => $society->getId()]);
  451.         $publicDirectory $this->getParameter('file_directory');
  452.         return $this->renderForm('society/edit.html.twig', [
  453.             'society' => $society,
  454.             'form' => $form,
  455.             'associes' => $society->getAssocies(),
  456.             'presidents' => $society->getPresidents(),
  457.             'files' => $files,
  458.             'directory' => "uploads/societyDocuments/",
  459.             'domiciliationAddresses' => $domiciliationAddresses,
  460.         ]);
  461.     }
  462. //    *****************************************************edit button ************************************************************************
  463.     /**
  464.      * @Route("/{id}/generate-documents", name="society_generate_documents", methods={"GET", "POST"})
  465.      */
  466.     public function generate(Request $requestSociety $societyEntityManagerInterface $entityManagerEventDispatcherInterface $dispatcher): Response
  467.     {
  468.         if ($society->getDocumentSocieties()) {
  469.             foreach ($society->getDocumentSocieties() as $documentSociety) {
  470.                 if ($documentSociety->getSignatures()) {
  471.                     foreach ($documentSociety->getSignatures() as $signature) {
  472.                         $entityManager->remove($signature);
  473.                     }
  474.                 }
  475.                 $entityManager->flush();
  476.             }
  477.         }
  478.         $publicDirectory $this->getParameter('pdf_directory');
  479.         $filepath $publicDirectory "/" $society->getId() . "/";
  480.         if (!file_exists($filepath)) {
  481.             mkdir($filepath0777true);
  482.         }
  483.         //hna n3adi fil path li houwa ploads/societyPdf/
  484.         $dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_REMOVE_DOCUMENTS);
  485.         $dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_CREATE);
  486.         $dispatcher->dispatch(new GenericEvent($society, [$filepath]), DocumentEvent::SOCIETY_REMOVE_DOCUMENTS_FOLDER);
  487.         $entityManager->flush();
  488.         // Rediriger après modification
  489.         return $this->redirectToRoute('society_show', ['id' => $society->getId()], Response::HTTP_SEE_OTHER);
  490.     }
  491. //    *****************************************************edit button ************************************************************************
  492.     /**
  493.      * @Route("/{id}", name="society_delete", methods={"POST"})
  494.      */
  495.     public function delete(Request $requestSociety $societyEntityManagerInterface $entityManager): Response
  496.     {
  497.         if ($this->isCsrfTokenValid('delete' $society->getId(), $request->request->get('_token'))) {
  498.             try {
  499.                 $societyName $society->getName();
  500.                 $entityManager->remove($society);
  501.                 $entityManager->flush();
  502.                 $this->addFlash('success''La société "' $societyName '" a été supprimée avec succès.');
  503.             } catch (\Exception $e) {
  504.                 $this->addFlash('error''Impossible de supprimer cette société : ' $e->getMessage());
  505.             }
  506.         }
  507.         return $this->redirectToRoute('society_index', [], Response::HTTP_SEE_OTHER);
  508.     }
  509.     /**
  510.      * @Route("/upload/{id}", name="society_upload", methods={"POST"})
  511.      */
  512.     public function uploadDocuments(Request $requestSociety $societyFilesRepository $filesRepository)
  513.     {
  514.         if ($request->getMethod() == "POST") {
  515.             $entityManager $this->getDoctrine()->getManager();
  516.             $file $request->files->get('file');
  517.             $fileName $file->getClientOriginalName();
  518.             $publicDirectory $this->getParameter('file_directory');
  519.             $filepath $publicDirectory "/" $society->getId();
  520.             if (!file_exists($filepath)) {
  521.                 mkdir($filepath0777true);
  522.             }
  523.             $fileName $file->getClientOriginalName();
  524.             $file->move(
  525.                 $filepath,
  526.                 $fileName
  527.             );
  528.             $fileEntity = new Files();
  529.             $fileEntity->setName($fileName);
  530.             $fileEntity->setPath($society->getId() . '/' $fileName);
  531.             $fileEntity->setSociety($society);
  532.             $entityManager->persist($fileEntity);
  533.             $entityManager->flush();
  534.             $html $this->renderView('society/_document_ajax.html.twig', [
  535.                 'files' => $filesRepository->findBy(['society' => $society->getId()]),
  536.                 'directory' => "uploads/societyDocuments/",
  537.                 'society' => $society
  538.             ]);
  539.             return new Response($html);
  540.         }
  541.     }
  542.     /**
  543.      * @Route("/deleteFile/{id}/{file}", name="delete_file")
  544.      */
  545.     public function refreshDocuments(Request $requestSociety $society$fileFilesRepository $filesRepository)
  546.     {
  547.         $entityManager $this->getDoctrine()->getManager();
  548.         $fileEntity $filesRepository->find($file);
  549.         $publicDirectory $this->getParameter('file_directory');
  550.         $filepath $publicDirectory "/" $fileEntity->getPath();
  551.         if (file_exists($filepath)) unlink($filepath);
  552.         $entityManager->remove($fileEntity);
  553.         $entityManager->flush();
  554.         $referer $request->headers->get('referer');
  555.         return new RedirectResponse($referer);
  556.     }
  557.     /**
  558.      * @Route("/new/{id}", name="app_society_update_Clauses", methods={"GET", "POST"})
  559.      */
  560.     public function updateStatus(Request $requestSociety $societyEntityManagerInterface $entityManager): Response
  561.     {
  562.         if ($request->isXmlHttpRequest()) {
  563.             $data $request->request->all();
  564.             $result $selectedClauses = [];
  565.             $profilName = !empty($data['profil-name']) ? $data['profil-name'] : null;
  566.             $profilId   = !empty($data['profil-id'])   ? (int)$data['profil-id']   : null;
  567.             // Keys posted by the form that are NOT clause refs — must not pollute societyStatus
  568.             $nonClauseKeys = ['profil-name''profil-id''profile''_token''save-mode'];
  569.             foreach ($data as $key => $value) {
  570.                 if (in_array($key$nonClauseKeystrue)) {
  571.                     continue;
  572.                 }
  573.                 if (strpos($key'condition_') === 0) {
  574.                     $result[$key] = $value;
  575.                 } else {
  576.                     $result[] = $key;
  577.                     if ($profilName || $profilId) {
  578.                         $clause $entityManager->getRepository(ClauseScoiety::class)->findOneBy(['ref' => $key]);
  579.                         if ($clause) {
  580.                             $selectedClauses[] = $clause->getId();
  581.                         }
  582.                     }
  583.                 }
  584.             }
  585.             if ($profilName && $selectedClauses) {
  586.                 // Créer un nouveau profil
  587.                 $selection = new UserClauseSelection();
  588.                 $selection->setUser($this->getUser());
  589.                 $selection->setSocietyType($society->getType());
  590.                 $selection->setName($profilName);
  591.                 $selection->setCreatedAt(new \DateTime());
  592.                 $selection->setClauses($selectedClauses);
  593.                 $entityManager->persist($selection);
  594.                 $entityManager->flush();
  595.                 $society->setSelectedProfil($selection);
  596.                 $entityManager->flush();
  597.             } elseif ($profilId && $selectedClauses) {
  598.                 // Mettre à jour un profil existant
  599.                 $selection $entityManager->getRepository(UserClauseSelection::class)->find($profilId);
  600.                 if ($selection) {
  601.                     $selection->setClauses($selectedClauses);
  602.                     $entityManager->flush();
  603.                     $society->setSelectedProfil($selection);
  604.                     $entityManager->flush();
  605.                 }
  606.             } else {
  607.                 // Pas d'action profil : la sélection de la société ne correspond plus à un profil sauvegardé
  608.                 $society->setSelectedProfil(null);
  609.             }
  610.             $society->setSocietyStatus($result);
  611.             $entityManager->persist($society);
  612.             $entityManager->flush();
  613.             return new Response('succes'Response::HTTP_OK);
  614.         }
  615.         return new Response('failed'Response::HTTP_BAD_REQUEST);
  616.     }
  617.     /**
  618.      * @Route("/preview-statuts/{id}", name="app_society_preview_statuts", methods={"POST"})
  619.      */
  620.     public function previewStatuts(Request $requestSociety $societyEntityManagerInterface $entityManager): Response
  621.     {
  622.         if (!$request->isXmlHttpRequest()) {
  623.             return new JsonResponse(['error' => 'Invalid request'], Response::HTTP_BAD_REQUEST);
  624.         }
  625.         $data $request->request->all();
  626.         
  627.         // Traitement des clauses similaire à updateStatus
  628.         $result = [];
  629.         foreach ($data as $key => $value) {
  630.             if (strpos($key'condition_') === 0) {
  631.                 $result[$key] = $value;
  632.             } else {
  633.                 $result[] = $key;
  634.             }
  635.         }
  636.         
  637.         // Récupération des clauses/statuts pour le type de société
  638.         mb_internal_encoding("UTF-8");
  639.         $societyTypeName $society->getType()->getName();
  640.         $decodedName mb_convert_case($societyTypeNameMB_CASE_LOWER"UTF-8");
  641.         $includeCommon = !str_contains($decodedName'sasu');
  642.         $allStatuts $entityManager->getRepository(ClauseScoiety::class)->findBySocietyType($decodedName$includeCommon);
  643.         $checkedRefs array_values(array_filter(array_keys($result), 'is_int'));
  644.         $checkedRefs array_map(function($k) use ($result) { return $result[$k]; }, $checkedRefs);
  645.         $statuts array_values(array_filter($allStatuts, function($s) use ($checkedRefs) {
  646.             return in_array($s->getRef(), $checkedRefs);
  647.         }));
  648.         
  649.         // Simulation de la mise à jour temporaire pour la preview
  650.         $conditionData = [];
  651.         foreach ($result as $key => $value) {
  652.             if (is_string($key) && strpos($key'condition_') === 0) {
  653.                 $conditionData[$key] = $value;
  654.             }
  655.         }
  656.         $tempSociety = clone $society;
  657.         $tempSociety->setSocietyStatus($result);
  658.         // Override DB clauseConditionsArray with live form data so preview reflects current state
  659.         $tempSociety->setClauseConditionsArray($conditionData);
  660.         
  661.         // Génération du PDF en mémoire
  662.         $pdfOptions = new \Dompdf\Options();
  663.         $pdfOptions->set('defaultFont''Arial');
  664.         $pdfOptions->set('isRemoteEnabled'true);
  665.         
  666.         $dompdf = new \Dompdf\Dompdf($pdfOptions);
  667.         
  668.         // Template du document statuts
  669.         $template 'documentsPdf/statuts.html.twig';
  670.         if (!$this->container->get('twig')->getLoader()->exists($template)) {
  671.             return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND);
  672.         }
  673.         try {
  674.             $html $this->container->get('twig')->render($template, [
  675.                 'society' => $tempSociety,
  676.                 'statuts' => $statuts,
  677.                 'data' => $result
  678.             ]);
  679.             
  680.             $dompdf->loadHtml($html);
  681.             $dompdf->setPaper('A4''portrait');
  682.             $dompdf->render();
  683.             
  684.             // Ajout du footer
  685.             $canvas $dompdf->get_canvas();
  686.             $w $canvas->get_width();
  687.             $h $canvas->get_height();
  688.             // Résolution correcte du font via FontMetrics (évite arial.afm introuvable sur Linux)
  689.             $fontMetrics $dompdf->getFontMetrics();
  690.             $footerFont $fontMetrics->getFont('Helvetica''normal')
  691.                 ?? $fontMetrics->getFont('helvetica''normal')
  692.                 ?? $fontMetrics->getFont('Times''normal');
  693.             // Footer personnalisé ou par défaut
  694.             $customFooter null;
  695.             if ($society->getClient() && $society->getClient()->getLogo()) {
  696.                 $customFooter $society->getClient()->getLogo()->getPiedDePage();
  697.             }
  698.             if ($customFooter) {
  699.                 $canvas->page_text(30$h 30$customFooter$footerFont9, [000]);
  700.             } else {
  701.                 $canvas->page_text(30$h 30$society->getName(), $footerFont9, [000]);
  702.                 $canvas->page_text($w 60$h 30"Généré le " date('d/m/Y'), $footerFont9, [000]);
  703.             }
  704.             $canvas->page_text($w 120$h 30"Page {PAGE_NUM} de {PAGE_COUNT}"$footerFont9, [000]);
  705.             $canvas->line(30$h 45$w 30$h 45, [0.50.50.5], 0.5);
  706.             
  707.             $output $dompdf->output();
  708.             
  709.             // Retourner le PDF en base64 pour affichage dans l'iframe
  710.             return new JsonResponse([
  711.                 'success' => true,
  712.                 'pdf' => base64_encode($output),
  713.                 'mime' => 'data:application/pdf;base64,'
  714.             ]);
  715.             
  716.         } catch (\Exception $e) {
  717.             error_log('[previewStatuts] Exception: ' $e->getMessage() . ' in ' $e->getFile() . ':' $e->getLine());
  718.             return new JsonResponse(['error' => 'Erreur: ' $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
  719.         }
  720.     }
  721.     /**
  722.      * @Route("/sendMail/{id}", name="app_society_send_document", methods={"GET", "POST"})
  723.      */
  724.     public function sendDocumentByMail(Request $requestint $id): Response
  725.     {
  726.         // Récupère le document en utilisant son ID
  727.         $documentSociety $this->documentSocietyRepository->find($id);
  728.         if (!$documentSociety) {
  729.             throw $this->createNotFoundException('Le document n\'existe pas');
  730.         }
  731.         // Récupère la société associée au document
  732.         $society $documentSociety->getSociety();
  733.         if ($request->isMethod('POST')) {
  734.             $recipients $request->request->get('emails', []);
  735.             $content $request->request->get('content''');
  736.             // Récupère le chemin S3 du fichier PDF
  737.             $s3Key $this->s3Service->extractKeyFromUrl($documentSociety->getPath());
  738.             // Utilise le service S3 pour récupérer le contenu du fichier
  739.             $fileContent $this->s3Service->downloadDocument($s3Key);
  740.             if (!$fileContent) {
  741.                 throw $this->createNotFoundException('Impossible de récupérer le fichier PDF depuis AWS S3');
  742.             }
  743.             // Convertit le flux en contenu lisible
  744.             if ($fileContent instanceof \Psr\Http\Message\StreamInterface) {
  745.                 $fileContent $fileContent->getContents();
  746.             }
  747.             // Détermine le nom du fichier
  748.             $filename $documentSociety->getNameFormated() . '.pdf';
  749.             // Envoie un email avec le fichier PDF en pièce jointe à chaque destinataire
  750.             foreach ($recipients as $recipient) {
  751.                 $this->mailService->sendEmailWithAttachment($recipient'MacsSolution.fr (Nouveau document)'$content$fileContent$filename);
  752.             }
  753.             $this->addFlash('success''Le mail a été envoyé avec succès.');
  754.             return $this->redirectToRoute('society_show', ['id' => $society->getId()]);
  755.         }
  756.         return $this->redirectToRoute('society_show', ['id' => $society->getId()]);
  757.     }
  758.     /**
  759.      * @Route("/society/update-signataires-by-document", name="app_society_update_signataires_document", methods={"POST"})
  760.      */
  761.     public function updateSignataires(Request $requestEntityManagerInterface $entityManagerDocumentSocietyRepository $documentSocietyRepository): Response
  762.     {
  763.         if ($request->isXmlHttpRequest()) {
  764.             $data $request->request->all();
  765.             $document $documentSocietyRepository->find($data['documentId']);
  766.             $signataires $data['selectedSignataires'];
  767.             $document->setSignataires($signataires);
  768.             $entityManager->persist($document);
  769.             $entityManager->flush();
  770.             return new Response('succes'Response::HTTP_OK);
  771.         }
  772.         return new Response('failed'Response::HTTP_BAD_REQUEST);
  773.     }
  774.     /**
  775.      * @Route("/society/sendMailMultiple/{id}", name="app_society_send_document_multiple", methods={"POST"})
  776.      */
  777.     public function sendDocumentMultipleByMail(Request $requestSociety $societyEntityManagerInterface $entityManager): Response
  778.     {
  779.         $action      $request->request->get('action');
  780.         $documentIds $request->request->get('documents', []);
  781.         $documents   $this->documentSocietyRepository->findBy(['id' => $documentIds]);
  782.         if (empty($documents)) {
  783.             return $this->json(['error' => 'Aucun document sélectionné.'], 400);
  784.         }
  785.         // Crée un fichier ZIP temporaire
  786.         $zipName sys_get_temp_dir() . '/' uniqid() . '.zip';
  787.         $zip = new \ZipArchive();
  788.         $zip->open($zipName, \ZipArchive::CREATE);
  789.         $tempFiles = [];
  790.         foreach ($documents as $documentSociety) {
  791.             $key         $this->s3Service->extractKeyFromUrl($documentSociety->getPath());
  792.             $fileContent $this->s3Service->downloadDocument($key);
  793.             if ($fileContent && strlen($fileContent) > 0) {
  794.                 $tempFilePath sys_get_temp_dir() . '/' uniqid() . '.pdf';
  795.                 file_put_contents($tempFilePath$fileContent);
  796.                 $tempFiles[] = $tempFilePath;
  797.                 $zip->addFile($tempFilePath, ($documentSociety->getNameFormated() ?: $documentSociety->getName()) . '.pdf');
  798.             }
  799.             if ($action === 'download') {
  800.                 $documentSociety->setIsDownload(true);
  801.             } elseif ($action === 'send') {
  802.                 $documentSociety->setIsSend(true);
  803.             }
  804.             $entityManager->persist($documentSociety);
  805.         }
  806.         $zip->close();
  807.         $entityManager->flush();
  808.         // Nettoyer les fichiers temporaires PDF
  809.         foreach ($tempFiles as $tmp) {
  810.             @unlink($tmp);
  811.         }
  812.         if ($action === 'download') {
  813.             $response = new BinaryFileResponse($zipName);
  814.             $response->headers->set('Content-Type''application/zip');
  815.             $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT'documents.zip');
  816.             $response->deleteFileAfterSend(true);
  817.             return $response;
  818.         }
  819.         if ($action === 'send') {
  820.             // Destinataires choisis dans le modal
  821.             $emailsParam $request->request->get('emails', []);
  822.             $recipients  array_unique(array_filter((array)$emailsParam));
  823.             if (empty($recipients)) {
  824.                 @unlink($zipName);
  825.                 return $this->json(['error' => 'Aucun destinataire sélectionné.'], 400);
  826.             }
  827.             $message $request->request->get('message''Veuillez trouver en pièce jointe les documents de la société ' $society->getName() . '.');
  828.             try {
  829.                 $zipContent file_get_contents($zipName);
  830.                 foreach ($recipients as $recipient) {
  831.                     $this->mailService->sendEmailWithAttachment(
  832.                         $recipient,
  833.                         'MacsSolution.fr – Documents société ' $society->getName(),
  834.                         $message,
  835.                         $zipContent,
  836.                         'documents.zip'
  837.                     );
  838.                 }
  839.                 @unlink($zipName);
  840.                 return $this->json(['success' => true'recipients' => count($recipients)]);
  841.             } catch (\Exception $e) {
  842.                 @unlink($zipName);
  843.                 return $this->json(['error' => 'Erreur lors de l\'envoi : ' $e->getMessage()], 500);
  844.             }
  845.         }
  846.         return $this->json(['error' => 'Action invalide.'], 400);
  847.     }
  848.     /**
  849.      * @Route("/activity/reformulate", name="app_activity_reformulate")
  850.      */
  851.     public function reformulateActivity(Request $request): JsonResponse
  852.     {
  853.         $data json_decode($request->getContent(), true);
  854.         $text $data['text'] ?? '';
  855.         try {
  856.             $response $this->httpClient->request('POST''https://api.openai.com/v1/chat/completions', [
  857.                 'headers' => [
  858.                     'Authorization' => 'Bearer ' . ($_ENV['OPENAI_API_KEY'] ?? ''),
  859.                     'Content-Type' => 'application/json',
  860.                 ],
  861.                 'json' => [
  862.                     'model' => 'gpt-4o',
  863.                     'messages' => [
  864.                         ['role' => 'system''content' => 'Tu es un assistant juridique. Génère un objet social clair à partir de mots-clés. Réponds uniquement avec le texte de l\'objet social, sans titre, sans introduction, sans mise en forme Markdown, sans astérisques, sans gras. Le texte doit commencer directement par "La société a pour objet" ou par une liste numérotée.'],
  865.                         ['role' => 'user''content' => $text],
  866.                     ],
  867.                     'temperature' => 0.7,
  868.                 ]
  869.             ]);
  870.             $statusCode $response->getStatusCode();
  871.             if ($statusCode === 401) {
  872.                 return new JsonResponse(['error' => 'Clé API OpenAI invalide ou expirée. Veuillez la renouveler.'], 200);
  873.             }
  874.             $result $response->toArray();
  875.             $reformulated $result['choices'][0]['message']['content'] ?? '';
  876.             return new JsonResponse(['reformulated' => $reformulated]);
  877.         } catch (\Exception $e) {
  878.             $message str_contains($e->getMessage(), '401')
  879.                 ? 'Clé API OpenAI invalide ou expirée. Veuillez la renouveler.'
  880.                 'Erreur lors de la génération : ' $e->getMessage();
  881.             return new JsonResponse(['error' => $message], 200);
  882.         }
  883.     }
  884.     /**
  885.      * @param EntityManagerInterface $entityManager
  886.      * @return null
  887.      */
  888.     public function getFlush(EntityManagerInterface $entityManager)
  889.     {
  890.         return $entityManager->flush();
  891.     }
  892. }