diff --git a/src/Controller/Dashboard/ContratsController.php b/src/Controller/Dashboard/ContratsController.php index b80cda0..df7a224 100644 --- a/src/Controller/Dashboard/ContratsController.php +++ b/src/Controller/Dashboard/ContratsController.php @@ -21,12 +21,15 @@ use Knp\Component\Pager\PaginatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\HeaderUtils; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Vich\UploaderBundle\Storage\StorageInterface; #[Route('/crm/contrats')] class ContratsController extends AbstractController @@ -37,6 +40,7 @@ class ContratsController extends AbstractController private readonly EventDispatcherInterface $eventDispatcher, private readonly Client $signatureClient, private readonly Mailer $mailer, + private readonly StorageInterface $storage, private readonly KernelInterface $kernel, ) { } @@ -79,6 +83,97 @@ class ContratsController extends AbstractController return $this->handleManualPayment($contrat, $type, $method); } + if ($request->query->has('act')) { + $act = $request->query->get('act'); + + if ($act == "downloadFilePv" && $contrat->getEtatLieux()) { + $etatLieux = $contrat->getEtatLieux(); + + $zipName = 'Photos_EDL_' . $contrat->getNumReservation() . '.zip'; + $tempFile = tempnam(sys_get_temp_dir(), 'zip_edl'); + + $zip = new \ZipArchive(); + if ($zip->open($tempFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) { + + // 1. AJOUT DU FICHIER ok.txt + $zip->addFromString('ok.txt', 'ok'); + + // 2. Photos Installation + foreach ($etatLieux->getFiles() as $file) { + $filePath = $this->kernel->getProjectDir() . "/public" . $this->storage->resolveUri($file, 'file'); + if (file_exists($filePath)) { + $zip->addFile($filePath, basename($filePath)); + } + } + + $zip->close(); + } + + if (!file_exists($tempFile)) { + $this->addFlash('error', "Échec de la génération du ZIP."); + return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); + } + + // Préparation de la réponse + $response = new Response(file_get_contents($tempFile)); + + $disposition = HeaderUtils::makeDisposition( + HeaderUtils::DISPOSITION_ATTACHMENT, + $zipName + ); + + $response->headers->set('Content-Type', 'application/zip'); + $response->headers->set('Content-Disposition', $disposition); + + // Nettoyage + unlink($tempFile); + + return $response; + } + if ($act == "downloadFileReturnPv" && $contrat->getEtatLieux()) { + $etatLieux = $contrat->getEtatLieux(); + + $zipName = 'Photos_EDL_' . $contrat->getNumReservation() . '.zip'; + $tempFile = tempnam(sys_get_temp_dir(), 'zip_edl'); + + $zip = new \ZipArchive(); + if ($zip->open($tempFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) { + + // 1. AJOUT DU FICHIER ok.txt + $zip->addFromString('ok.txt', 'ok'); + + // 2. Photos Installation + foreach ($etatLieux->getFileReturn() as $file) { + $filePath = $this->kernel->getProjectDir() . "/public" . $this->storage->resolveUri($file, 'file'); + if (file_exists($filePath)) { + $zip->addFile($filePath, basename($filePath)); + } + } + $zip->close(); + } + + if (!file_exists($tempFile)) { + $this->addFlash('error', "Échec de la génération du ZIP."); + return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); + } + + // Préparation de la réponse + $response = new Response(file_get_contents($tempFile)); + + $disposition = HeaderUtils::makeDisposition( + HeaderUtils::DISPOSITION_ATTACHMENT, + $zipName + ); + + $response->headers->set('Content-Type', 'application/zip'); + $response->headers->set('Content-Disposition', $disposition); + + // Nettoyage + unlink($tempFile); + + return $response; + } + } // 3. Calculs financiers pour l'affichage $totalHt = 0; $totalCaution = 0; diff --git a/src/Controller/EtlController.php b/src/Controller/EtlController.php index 83b6e01..e8ff1e8 100644 --- a/src/Controller/EtlController.php +++ b/src/Controller/EtlController.php @@ -40,7 +40,7 @@ use Vich\UploaderBundle\Storage\StorageInterface; class EtlController extends AbstractController { #[Route('/etl', name: 'etl_home')] - public function eltHome(ContratsRepository $contratsRepository): Response + public function eltHome(EntityManagerInterface $entityManager,ContratsRepository $contratsRepository): Response { $user = $this->getUser(); if (!$user) { @@ -71,9 +71,26 @@ class EtlController extends AbstractController } elseif ($user instanceof Prestaire) { $missions = $contratsRepository->findBy(['reservationState' => $states, 'prestataire' => $user], ['dateAt' => 'ASC'], 5); } + $list = []; + foreach ($missions as $mission) { + if($mission->isSigned()) { + $pl = $entityManager->getRepository(ContratsPayments::class)->findOneBy(['type'=>'accompte','contrat'=>$mission]); + if($pl instanceof ContratsPayments &&$pl->getState() == "complete") { + $etatleiux = $mission->getEtatLieux(); + if(!is_null($etatleiux)) { + if($etatleiux->getStatus() != "edl_return_done" && $etatleiux->getStatus() != "edl_return_refused"){ + $list[] = $mission; + } + } else { + $list[] = $mission; + } + + } + } + } return $this->render('etl/home.twig', [ - 'missions' => $missions, + 'missions' => $list, 'totalMissions' => $totalMissions, 'upcomingMissions' => $upcomingMissions ]); @@ -844,82 +861,161 @@ class EtlController extends AbstractController return $this->redirectToRoute('etl_login'); } + $etatLieux = $contrat->getEtatLieux(); - if (!$etatLieux || !$etatLieux->getSignIdDelivery() || !$etatLieux->getSignIdCustomer()) { - $this->addFlash('error', 'Signatures manquantes.'); - return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]); + if($etatLieux->getStatus() == "edl_return_done") { + if (!$etatLieux || !$etatLieux->getSignIdReturn() || !$etatLieux->getSignIdCustomerReturn()) { + $this->addFlash('error', 'Signatures manquantes.'); + return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]); + } + // Get signed documents + $sub = $signatureClient->getSubmiter($etatLieux->getSignIdReturn()); + $submissionId = $sub['submission_id']; + $submission = $signatureClient->getSubmition($submissionId); + + $signedPdfUrl = $submission['documents'][0]['url'] ?? null; + $auditUrl = $submission['audit_log_url'] ?? null; + + if ($signedPdfUrl) { + $tmpPath = sys_get_temp_dir() . '/edl_retour_signed_' . $contrat->getId() . '.pdf'; + file_put_contents($tmpPath, file_get_contents($signedPdfUrl)); + $file = new UploadedFile($tmpPath, 'edl_retour_signed_.pdf', 'application/pdf', null, true); + $etatLieux->setEtatLieuxSignReturnFile($file); + } + if ($auditUrl) { + $tmpPathAudit = sys_get_temp_dir() . '/edl_retour_audit_signed_' . $contrat->getId() . '.pdf'; + file_put_contents($tmpPathAudit, file_get_contents($auditUrl)); + $file = new UploadedFile($tmpPathAudit, 'edl_retour_audit_signed_.pdf', 'application/pdf', null, true); + $etatLieux->setEtatLieuxAuditReturnFile($file); + } + $etatLieux->setUpdatedAt(new \DateTimeImmutable()); + + $etatLieux->setStatus('edl_return_finised'); + $contrat->setReservationState('finished'); + $em->flush(); + + // Emails + $recipients = [ + $contrat->getCustomer()->getEmail(), + 'contact@ludikevent.fr' + ]; + if ($etatLieux->getPrestataire()) { + $recipients[] = $etatLieux->getPrestataire()->getEmail(); + } + + $attachments = []; + + // Try resolve path from Vich + $signPath = $storage->resolvePath($etatLieux, 'etatLieuxSignReturnFile'); + // If resolvePath returns null (e.g. no mapping or file not found yet?), check manual path + // But flush() should have moved it. resolvePath usually returns absolute path. + if ($signPath && file_exists($signPath)) { + $attachments[] = DataPart::fromPath($signPath, 'Etat_des_retour_signe.pdf'); + } elseif (isset($tmpPath) && file_exists($tmpPath)) { + $attachments[] = DataPart::fromPath($tmpPath, 'Etat_des_retour_signe.pdf'); + } + + $auditPath = $storage->resolvePath($etatLieux, 'etatLieuxAuditReturnFile'); + if ($auditPath && file_exists($auditPath)) { + $attachments[] = DataPart::fromPath($auditPath, 'Audit_Etat_des_retour_signe.pdf'); + } elseif (isset($tmpPathAudit) && file_exists($tmpPathAudit)) { + $attachments[] = DataPart::fromPath($tmpPathAudit, 'Audit_Etat_des_retour_signe.pdf'); + } + + foreach (array_unique($recipients) as $email) { + $mailer->send( + $email, + 'Destinataire', + "État des lieux validé - #" . $contrat->getNumReservation(), + "mails/etl/edl_retour_confirmation.twig", + [ + 'contrat' => $contrat, + 'etatLieux' => $etatLieux + ], + $attachments + ); + } + + $this->addFlash('success', 'État des lieux clôturé et envoyé.'); + return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]); + } else { + if (!$etatLieux || !$etatLieux->getSignIdDelivery() || !$etatLieux->getSignIdCustomer()) { + $this->addFlash('error', 'Signatures manquantes.'); + return $this->redirectToRoute('etl_mission_signed_entry_state', ['id' => $contrat->getId()]); + } + // Get signed documents + $sub = $signatureClient->getSubmiter($etatLieux->getSignIdDelivery()); + $submissionId = $sub['submission_id']; + $submission = $signatureClient->getSubmition($submissionId); + + $signedPdfUrl = $submission['documents'][0]['url'] ?? null; + $auditUrl = $submission['audit_log_url'] ?? null; + + if ($signedPdfUrl) { + $tmpPath = sys_get_temp_dir() . '/edl_signed_' . $contrat->getId() . '.pdf'; + file_put_contents($tmpPath, file_get_contents($signedPdfUrl)); + $file = new UploadedFile($tmpPath, 'edl_entrant_signed.pdf', 'application/pdf', null, true); + $etatLieux->setEtatLieuxSignFile($file); + } + if ($auditUrl) { + $tmpPathAudit = sys_get_temp_dir() . '/edl_audit_signed_' . $contrat->getId() . '.pdf'; + file_put_contents($tmpPathAudit, file_get_contents($auditUrl)); + $file = new UploadedFile($tmpPathAudit, 'edl_audit_signed.pdf', 'application/pdf', null, true); + $etatLieux->setEtatLieuxAuditFile($file); + } + $etatLieux->setUpdatedAt(new \DateTimeImmutable()); + + $etatLieux->setStatus('edl_validated'); + $contrat->setReservationState('progress'); + $em->flush(); + + // Emails + $recipients = [ + $contrat->getCustomer()->getEmail(), + 'contact@ludikevent.fr' + ]; + if ($etatLieux->getPrestataire()) { + $recipients[] = $etatLieux->getPrestataire()->getEmail(); + } + + $attachments = []; + + // Try resolve path from Vich + $signPath = $storage->resolvePath($etatLieux, 'etatLieuxSignFile'); + // If resolvePath returns null (e.g. no mapping or file not found yet?), check manual path + // But flush() should have moved it. resolvePath usually returns absolute path. + if ($signPath && file_exists($signPath)) { + $attachments[] = DataPart::fromPath($signPath, 'Etat_des_lieux_signe.pdf'); + } elseif (isset($tmpPath) && file_exists($tmpPath)) { + $attachments[] = DataPart::fromPath($tmpPath, 'Etat_des_lieux_signe.pdf'); + } + + $auditPath = $storage->resolvePath($etatLieux, 'etatLieuxAuditFile'); + if ($auditPath && file_exists($auditPath)) { + $attachments[] = DataPart::fromPath($auditPath, 'Audit_Etat_des_lieux_signe.pdf'); + } elseif (isset($tmpPathAudit) && file_exists($tmpPathAudit)) { + $attachments[] = DataPart::fromPath($tmpPathAudit, 'Audit_Etat_des_lieux_signe.pdf'); + } + + foreach (array_unique($recipients) as $email) { + $mailer->send( + $email, + 'Destinataire', + "État des lieux validé - #" . $contrat->getNumReservation(), + "mails/etl/edl_confirmation.twig", + [ + 'contrat' => $contrat, + 'etatLieux' => $etatLieux + ], + $attachments + ); + } + + $this->addFlash('success', 'État des lieux clôturé et envoyé.'); + return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]); } - // Get signed documents - $sub = $signatureClient->getSubmiter($etatLieux->getSignIdDelivery()); - $submissionId = $sub['submission_id']; - $submission = $signatureClient->getSubmition($submissionId); - $signedPdfUrl = $submission['documents'][0]['url'] ?? null; - $auditUrl = $submission['audit_log_url'] ?? null; - - if ($signedPdfUrl) { - $tmpPath = sys_get_temp_dir() . '/edl_signed_' . $contrat->getId() . '.pdf'; - file_put_contents($tmpPath, file_get_contents($signedPdfUrl)); - $file = new UploadedFile($tmpPath, 'edl_entrant_signed.pdf', 'application/pdf', null, true); - $etatLieux->setEtatLieuxSignFile($file); - } - if ($auditUrl) { - $tmpPathAudit = sys_get_temp_dir() . '/edl_audit_signed_' . $contrat->getId() . '.pdf'; - file_put_contents($tmpPathAudit, file_get_contents($auditUrl)); - $file = new UploadedFile($tmpPathAudit, 'edl_audit_signed.pdf', 'application/pdf', null, true); - $etatLieux->setEtatLieuxAuditFile($file); - } - $etatLieux->setUpdatedAt(new \DateTimeImmutable()); - - $etatLieux->setStatus('edl_validated'); - $contrat->setReservationState('progress'); - $em->flush(); - - // Emails - $recipients = [ - $contrat->getCustomer()->getEmail(), - 'contact@ludikevent.fr' - ]; - if ($etatLieux->getPrestataire()) { - $recipients[] = $etatLieux->getPrestataire()->getEmail(); - } - - $attachments = []; - - // Try resolve path from Vich - $signPath = $storage->resolvePath($etatLieux, 'etatLieuxSignFile'); - // If resolvePath returns null (e.g. no mapping or file not found yet?), check manual path - // But flush() should have moved it. resolvePath usually returns absolute path. - if ($signPath && file_exists($signPath)) { - $attachments[] = DataPart::fromPath($signPath, 'Etat_des_lieux_signe.pdf'); - } elseif (isset($tmpPath) && file_exists($tmpPath)) { - $attachments[] = DataPart::fromPath($tmpPath, 'Etat_des_lieux_signe.pdf'); - } - - $auditPath = $storage->resolvePath($etatLieux, 'etatLieuxAuditFile'); - if ($auditPath && file_exists($auditPath)) { - $attachments[] = DataPart::fromPath($auditPath, 'Audit_Etat_des_lieux_signe.pdf'); - } elseif (isset($tmpPathAudit) && file_exists($tmpPathAudit)) { - $attachments[] = DataPart::fromPath($tmpPathAudit, 'Audit_Etat_des_lieux_signe.pdf'); - } - - foreach (array_unique($recipients) as $email) { - $mailer->send( - $email, - 'Destinataire', - "État des lieux validé - #" . $contrat->getNumReservation(), - "mails/etl/edl_confirmation.twig", - [ - 'contrat' => $contrat, - 'etatLieux' => $etatLieux - ], - $attachments - ); - } - - $this->addFlash('success', 'État des lieux clôturé et envoyé.'); - return $this->redirectToRoute('etl_contrat_view', ['id' => $contrat->getId()]); } #[Route('/etl/account', name: 'etl_account', methods: ['GET', 'POST'])] @@ -1091,6 +1187,7 @@ class EtlController extends AbstractController #[Route('/etl/mission/{id}/edl/refused', name: 'etl_edl_customer_refused', methods: ['POST'])] public function eltEdlCustomerRefused( Contrats $contrat, + StorageInterface $storage, Request $request, EntityManagerInterface $em, Mailer $mailer @@ -1121,6 +1218,7 @@ class EtlController extends AbstractController $em->flush(); + // Notification par email (Optionnel mais recommandé) $mailer->send( 'contact@ludikevent.fr', diff --git a/templates/dashboard/contrats/list.twig b/templates/dashboard/contrats/list.twig index e2e8518..da0460f 100644 --- a/templates/dashboard/contrats/list.twig +++ b/templates/dashboard/contrats/list.twig @@ -42,25 +42,69 @@ {% set cautionEncaisser = contratPaymentPay(contrat, 'caution_recup') %}
⚠️ Litige : Signature refusée par le client
++ Motif : "{{ contrat.etatLieux.raisonRefus|default('Non renseigné') }}" +
+{{ contrat.townEvent }}
{{ contrat.zipCodeEvent }}
Informations Locataire
+Locataire
{{ contrat.dateAt|date('d/m/Y') }} - {{ contrat.endAt|date('d/m/Y') }}
+{{ contrat.dateAt|date('d/m/Y') }} — {{ contrat.endAt|date('d/m/Y') }}
{{ contrat.addressEvent }}
{{ contrat.zipCodeEvent }} {{ contrat.townEvent }}
Motif : {{ contrat.etatLieux.raisonRefused|default('Non spécifié') }}
+Encaissé
{% endif %}Totalité payée
{% endif %}Caution Globale : {{ contrat.devis.formule.caution }}€
-Caution : {{ product.caution }}€
- {% endif %} -Caution : {{ product.caution }}€
+{{ product.details }}
-| Transaction | +Date | Type | Montant | Justificatif | @@ -362,24 +347,23 @@
|---|---|---|---|---|
| {{ payment.paymentAt|date('d/m/Y H:i') }} | - - {{ payment.type|replace({'_': ' '}) }} - + + {{ payment.type|replace({'_': ' '}) }} + | {{ payment.amount|number_format(2, ',', ' ') }}€ | - + REÇU PDF | |
| Aucun paiement effectué | ||||
| Aucun règlement | ||||
Mes Missions
Planning & Détails
- +{{ mission.dateAt|date('d/m H:i') }} ➜ {{ mission.endAt|date('d/m H:i') }}
-{{ mission.townEvent }}
+{{ mission.numReservation }} - {{ mission.townEvent }}
+{{ mission.customer.name }} {{ mission.customer.surname }}
+ {% if mission.etatLieux is not null and mission.etatLieux.status == "edl_validated" %} +En attends de l'état de retour
+ {% endif %}