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') %}
+ + {# --- BANNIÈRE DE LITIGE (Si signature refusée) --- #} + {% if contrat.etatLieux and contrat.etatLieux.status == 'edl_return_refused' %} +
+
+ + + + +
+

⚠️ Litige : Signature refusée par le client

+

+ Motif : "{{ contrat.etatLieux.raisonRefus|default('Non renseigné') }}" +

+
+
+
+ Action Requise +
+
+ {% endif %} +
- {# 1. REF & STATUS #} + {# 1. RÉFÉRENCE & STATUTS #}
- Contrat + Réservation

{{ contrat.numReservation }}

- {% if contrat.isSigned %} -
- Signé -
- {% else %} -
- Attente -
- {% endif %} +
+ {# Status Contrat #} + {% if contrat.isSigned %} +
+ Contrat Signé +
+ {% else %} +
+ Attente Signature +
+ {% endif %} + + {# Status État des Lieux avec Traductions et Couleurs #} + {% if contrat.etatLieux %} + {% set status_config = { + 'delivery_progress': { 'color': 'bg-blue-500/10 text-blue-400 border-blue-500/20', 'label': '🚚 Livraison en cours' }, + 'delivery_done': { 'color': 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20', 'label': '📍 Livré' }, + 'edl_progress': { 'color': 'bg-indigo-500/10 text-indigo-400 border-indigo-500/20', 'label': '📝 EDL Installation...' }, + 'edl_validated': { 'color': 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30', 'label': '✅ EDL Validé' }, + 'return_edl_progress': { 'color': 'bg-orange-500/10 text-orange-400 border-orange-500/20', 'label': '🔍 EDL Retour...' }, + 'edl_return_done': { 'color': 'bg-purple-500/10 text-purple-400 border-purple-500/20', 'label': '📦 Retour Effectué' }, + 'edl_return_refused': { 'color': 'bg-red-600 text-white border-red-500', 'label': '❌ Signature Refusée' } + } %} + + {% set current_status = status_config[contrat.etatLieux.status] ?? { 'color': 'bg-slate-500/10 text-slate-400 border-white/5', 'label': contrat.etatLieux.status } %} + +
+ {{ current_status.label }} +
+ {% endif %} +
{# 2. CLIENT #} @@ -79,93 +123,78 @@ {# 3. LIEU #}
- Destination + Ville / CP

{{ contrat.townEvent }}

{{ contrat.zipCodeEvent }}

- {# 4. PAIEMENTS & DETAILS #} + {# 4. PAIEMENTS & FINANCES #}
- {# INFO PRIX & FORMULE #}
- {% if contrat.devis %} - {% if contrat.devis.orderSession and contrat.devis.orderSession.promotion %} - - {{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}€ - -
- - {{ (contrat.devis|totalQuotoBeforeDiscount)|number_format(2, ',', ' ') }}€ - - - -{{ contrat.devis.orderSession.promotion.percentage }}% - -
- {% else %} - - {{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}€ - - {% endif %} + {% if contrat.devis %} + + {{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }}€ HT + {% if contrat.devis.formule %} - - {{ contrat.devis.formule.name }} - + + {{ contrat.devis.formule.name }} + {% endif %} - Caution: {{ (contrat.devis|totalCaution)|number_format(2, ',', ' ') }}€ - + Caution : {{ (contrat.devis|totalCaution)|number_format(2, ',', ' ') }}€ + {% else %} - No Devis + Pas de devis {% endif %}
- {# STATUTS PAIEMENTS (Icons) #} + {# BADGES DE PAIEMENT #}
{# ACOMPTE #}
+ {{ acompteOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-red-500/10 text-red-500 border border-red-500/20' }}">
- Acompte + Acompte
{# CAUTION #}
{% if cautionEncaisser %} -
+
- Encaissée + Encaissée {% elseif cautionRelase %} -
+
Libérée {% elseif cautionOk %} -
+
Détenue {% else %} -
+
- Requise + Requise {% endif %}
{# SOLDE #}
+ {{ soldeOk ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-red-500/10 text-red-500 border border-red-500/20' }}">
- Solde + Solde
@@ -174,12 +203,12 @@ {# 5. ACTIONS #} diff --git a/templates/dashboard/contrats/view.twig b/templates/dashboard/contrats/view.twig index 894c103..827fa5a 100644 --- a/templates/dashboard/contrats/view.twig +++ b/templates/dashboard/contrats/view.twig @@ -35,7 +35,6 @@ {# Définition des états de paiement #} {% set acompteOk = contratPaymentPay(contrat, 'accompte') %} {% set cautionOk = contratPaymentPay(contrat, 'caution') %} - {% set soldeOk = (solde <= 0.05) %}
@@ -53,60 +52,34 @@
Total Contrat - {% if contrat.devis and contrat.devis.orderSession and contrat.devis.orderSession.promotion %} -
- - {{ (contrat.devis|totalQuotoBeforeDiscount)|number_format(2, ',', ' ') }} € - - - -{{ contrat.devis.orderSession.promotion.percentage }}% - -
- {% endif %} - {% if contrat.devis and contrat.devis.formule %} - - {{ contrat.devis.formule.name }} - - {% endif %} - Com. Stripe (EEE) : ~ {{ ((totalHT * 0.015) + 0.25)|number_format(2, ',', ' ') }} € + HT / Jour
- {% if contrat.devis %} - {{ (contrat.devis|totalQuotoHT)|number_format(2, ',', ' ') }} € - {% else %} - {{ totalHT|number_format(2, ',', ' ') }} € - {% endif %} + {{ (contrat.devis ? (contrat.devis|totalQuotoHT) : totalHT)|number_format(2, ',', ' ') }} €
-
- Reste à percevoir - {% if not soldeOk %} - Com. Stripe (EEE) : ~ {{ ((solde * 0.015) + 0.25)|number_format(2, ',', ' ') }} € - {% endif %} -
+ Reste à percevoir {% set totalAmount = (contrat.devis ? (contrat.devis|totalQuotoHT) : totalHT) %} {% set totalPaid = 0 %} {% for payment in contrat.contratsPayments %} - {% if payment.type in ['accompte', 'solde'] %} - {% set totalPaid = totalPaid + payment.amount %} - {% endif %} + {% if payment.type in ['accompte', 'solde', 'etl_payment'] %} + {% set totalPaid = totalPaid + payment.amount %} + {% endif %} {% endfor %} {% set remaining = totalAmount - totalPaid %} - - {{ remaining <= 0.05 ? 'CONTRAT SOLDÉ' : (remaining|number_format(2, ',', ' ') ~ ' €') }} + {{ remaining <= 0.05 ? 'SOLDÉ' : (remaining|number_format(2, ',', ' ') ~ ' €') }}
- Référence + Référence #{{ contrat.numReservation }}
{# --- 2. INFOS CLIENT & ÉVÉNEMENT --- #}
- {# CARTE CLIENT #}
@@ -116,23 +89,16 @@

{{ contrat.customer.surname }} {{ contrat.customer.name }}

-

Informations Locataire

+

Locataire

-
- - {{ contrat.customer.email }} -
-
- - {{ contrat.customer.phone }} -
+
{{ contrat.customer.email }}
+
{{ contrat.customer.phone }}
- {# CARTE ÉVÉNEMENT #}
@@ -142,11 +108,10 @@

Lieu de l'événement

-

{{ contrat.dateAt|date('d/m/Y') }} - {{ contrat.endAt|date('d/m/Y') }}

+

{{ contrat.dateAt|date('d/m/Y') }} — {{ contrat.endAt|date('d/m/Y') }}

- Adresse de livraison

{{ contrat.addressEvent }}

{{ contrat.zipCodeEvent }} {{ contrat.townEvent }}

@@ -154,14 +119,107 @@
- {# --- 3. ÉTAT DES PAIEMENTS AVEC ACTIONS DE GESTION --- #} -
-
+ {# --- 3. SECTION LOGISTIQUE & ÉTAT DES LIEUX --- #} + {% if contrat.etatLieux %} +
+

Logistique & État des lieux

- {# --- ACOMPTE --- #} + {% if contrat.etatLieux.status == 'edl_return_refused' %} +
+
+
+ +
+
+

Signature Refusée au Retour

+

Motif : {{ contrat.etatLieux.raisonRefused|default('Non spécifié') }}

+
+
+ Litige à traiter +
+ {% endif %} + +
+ {# EDL INSTALLATION #} +
+
+

📝 État des lieux Installation

+ + {{ contrat.etatLieux.status in ['edl_validated','edl_return_done', 'edl_return_refused','edl_return_finised'] ? 'VALIDÉ' : 'EN COURS' }} + +
+
+ {% if contrat.etatLieux.etatLieuxSignFileName %} + + + Voir le PV signé (Installation) + + + + Télécharger tout ses photos le PV + + {% endif %} +
+
+ {{ contrat.etatLieux.files|length }} + Médias Photos/Vid. +
+
+ {{ contrat.etatLieux.pointControls|length }} + Points d'inspection +
+
+
+
+ + {# EDL RETOUR #} +
+
+

📦 État des lieux Retour

+ {% if contrat.etatLieux.status in ['edl_return_done', 'edl_return_refused','edl_return_finised'] %} + EFFECTUÉ + {% else %} + EN ATTENTE + {% endif %} +
+
+ {% if contrat.etatLieux.etatLieuxSignReturnFileName %} + + + Voir le PV signé (Retour) + + + + Télécharger tout ses photos le PV + + {% elseif contrat.etatLieux.status == 'edl_return_refused' %} +
+ PV non signé (Refus client) +
+ {% endif %} +
+
+ {{ contrat.etatLieux.fileReturn|length }} + Médias Retour +
+
+ {{ contrat.etatLieux.pointControlsReturn|length }} + Points Vérifiés +
+
+ +
+
+
+
+ {% endif %} + + {# --- 4. ÉTAT DES PAIEMENTS --- #} +
+
+ {# ACOMPTE #}
-
+
{% if acompteOk %}{% else %}{% endif %} @@ -169,77 +227,43 @@
Acompte {% if not acompteOk %} -
+
{% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} - - {{ method|slice(0, 4) }}. - + {{ method|slice(0, 4) }}. {% endfor %}
- {% else %} -

Encaissé

{% endif %}
- {# --- CAUTION --- #} + {# CAUTION #}
-
+
- {% if cautionOk %}{% else %}{% endif %} +
Caution -
+
{% set cautionRelase = contratPaymentPay(contrat, 'caution_free') %} {% set cautionEncaisser = contratPaymentPay(contrat, 'caution_recup') %} - {# 1. ON TESTE D'ABORD LES ÉTATS FINAUX #} {% if cautionRelase %} -
- - - - Caution Libérée -
- + Libérée {% elseif cautionEncaisser %} -
- - - - Caution Encaissée -
- - {# 2. SI PAS DE STATUT FINAL, ON REGARDE SI ELLE EST AU MOINS REÇUE #} + Encaissée {% elseif cautionOk %} - - {# 3. SINON, C'EST QU'ELLE N'EST PAS ENCORE ENREGISTRÉE #} {% else %} -
+
{% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} - - {{ method|slice(0, 4) }}. - + {{ method|slice(0, 4) }}. {% endfor %}
{% endif %} @@ -249,10 +273,9 @@ - {# --- SOLDE --- #} + {# SOLDE #}
-
+
{% if soldeOk %}{% else %}{% endif %} @@ -260,82 +283,44 @@
Solde Final {% if not soldeOk %} -
+
{% for method in ['Carte Bancaire', 'Chèque', 'Espèces', 'Virement'] %} - - {{ method|slice(0, 4) }}. - + {{ method|slice(0, 4) }}. {% endfor %}
- {% else %} -

Totalité payée

{% endif %}
-
- {# --- 4. PRODUITS ET OPTIONS --- #} + + {# --- 5. PRODUITS ET OPTIONS --- #}
- {# PRODUITS #}

Équipements loués

- {% if contrat.devis and contrat.devis.formule %} - {# BLOC FORMULE #} -
-
-
-

FORMULE : {{ contrat.devis.formule.name }}

-

Caution Globale : {{ contrat.devis.formule.caution }}€

-
-
- Package -
-
-
- {% endif %} - {% for product in contrat.contratsLines %} -
-
-
-

{{ product.name }}

- {% if not (contrat.devis and contrat.devis.formule) %} -

Caution : {{ product.caution }}€

- {% endif %} -
-
- {% if contrat.devis and contrat.devis.formule %} - Inclus - {% else %} - {{ product.price1DayHt }}€ - HT / Jour - {% endif %} -
+
+
+

{{ product.name }}

+

Caution : {{ product.caution }}€

+
+
+ {{ product.price1DayHt }}€ + HT / J
{% endfor %}
- {# OPTIONS #}

Options & Services

- {% for product in contrat.contratsOptions %} -
-
-
-

{{ product.name }}

-

{{ product.details }}

-
-
- {{ product.price }}€ -
-
+ {% for opt in contrat.contratsOptions %} +
+

{{ opt.name }}

+ {{ opt.price }}€ HT
{% else %}
Aucun supplément
@@ -344,14 +329,14 @@
- {# --- 5. HISTORIQUE DES PAIEMENTS --- #} + {# --- 6. HISTORIQUE FINANCIER --- #}
-

Historique Financier

-
+

Historique des transactions

+
- + @@ -362,24 +347,23 @@ {% else %} - + {% endfor %}
TransactionDate Type Montant Justificatif
{{ 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
diff --git a/templates/etl/home.twig b/templates/etl/home.twig index cbb86fe..5833e7a 100644 --- a/templates/etl/home.twig +++ b/templates/etl/home.twig @@ -36,7 +36,7 @@

Mes Missions

Planning & Détails

- +
- + {# UPCOMING LIST #}

Prochainement

- + {% if missions|length > 0 %}
{% for mission in missions %} @@ -59,9 +59,12 @@

{{ 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 %}
- {{ mission.zipCodeEvent }} {% endfor %}
{% else %}
diff --git a/templates/etl/view.twig b/templates/etl/view.twig index 3372060..18383ac 100644 --- a/templates/etl/view.twig +++ b/templates/etl/view.twig @@ -16,6 +16,7 @@
+ {# ACTION LIVRAISON #} {% if not mission.etatLieux or mission.etatLieux.status == 'delivery_ready' %}
diff --git a/templates/mails/etl/edl_confirmation.twig b/templates/mails/etl/edl_confirmation.twig index 41f09b4..50ed0f7 100644 --- a/templates/mails/etl/edl_confirmation.twig +++ b/templates/mails/etl/edl_confirmation.twig @@ -7,7 +7,7 @@ L'état des lieux d'installation pour la réservation #{{ datas.contrat.numReservation }} a été validé et signé par les deux parties. - + Détails de l'intervention : diff --git a/templates/mails/etl/edl_refused_alert.twig b/templates/mails/etl/edl_refused_alert.twig index f38a382..78a72cc 100644 --- a/templates/mails/etl/edl_refused_alert.twig +++ b/templates/mails/etl/edl_refused_alert.twig @@ -43,12 +43,4 @@ {% endfor %} {% endif %} - - - Consulter le dossier complet - - - - L'état des lieux a été marqué avec le statut "Refusé". Veuillez vérifier les fichiers médias joints pour constater d'éventuels dommages. - {% endblock %} diff --git a/templates/mails/etl/edl_retour_confirmation.twig b/templates/mails/etl/edl_retour_confirmation.twig new file mode 100644 index 0000000..536ec8f --- /dev/null +++ b/templates/mails/etl/edl_retour_confirmation.twig @@ -0,0 +1,57 @@ +{% extends 'mails/base.twig' %} + +{% block content %} + + Bonjour, + + + + L'état des lieux pour la réservation #{{ datas.contrat.numReservation }} a été validé et signé par les deux parties. + + + + Détails de l'intervention : + + + Client : {{ datas.contrat.customer.surname }} {{ datas.contrat.customer.name }}
+ Lieu : {{ datas.contrat.addressEvent }} {{ datas.contrat.zipCodeEvent }} {{ datas.contrat.townEvent }} +
+ + {# Section Observations (Entrant ou Sortant selon le contexte) #} + {% set comments = datas.etatLieux.status == 'edl_validated' ? datas.etatLieux.comments : datas.etatLieux.commentsReturn %} + + {% if comments|length > 0 %} + + Observations / Commentaires : + + {% for comment in comments %} + + - [{{ comment.createdAt|date('d/m H:i') }}] {{ comment.content }} + + {% endfor %} + {% endif %} + + {# Section Médias #} + {% set files = datas.etatLieux.status == 'edl_validated' ? datas.etatLieux.files : datas.etatLieux.filesReturn %} + + {% if files|length > 0 %} + + Médias joints au dossier ({{ files|length }}) : + + {% for file in files %} + + - + Voir le fichier {{ loop.index }} ({{ file.type|capitalize }}) + + + {% endfor %} + {% endif %} + + + L'exemplaire signé de votre état des lieux est joint à cet e-mail en version PDF. + + + + Accéder à mon dossier + +{% endblock %}