From e530538af8c9036a27c5819e352daee1434f211e Mon Sep 17 00:00:00 2001 From: Serreau Jovann Date: Thu, 29 Jan 2026 10:40:03 +0100 Subject: [PATCH] =?UTF-8?q?```text=20=E2=9C=A8=20feat(contrats):=20Ajoute?= =?UTF-8?q?=20d=C3=A9tails=20option,=20actions=20paiements=20et=20style=20?= =?UTF-8?q?liste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajoute un champ détails pour les options de contrat, permet la validation manuelle des paiements (accompte, caution, solde) et améliore le style de la liste des contrats. ``` --- migrations/Version20260129091410.php | 32 ++ .../Dashboard/ContratsController.php | 209 +++++++-- src/Controller/SignatureController.php | 12 - src/Entity/ContratsOption.php | 16 + src/Service/Pdf/PlPdf.php | 7 +- templates/dashboard/contrats/add.twig | 7 +- templates/dashboard/contrats/list.twig | 254 ++++++---- templates/dashboard/contrats/view.twig | 439 +++++++++--------- .../mails/customer/accompte_confirmation.twig | 4 + 9 files changed, 603 insertions(+), 377 deletions(-) create mode 100644 migrations/Version20260129091410.php diff --git a/migrations/Version20260129091410.php b/migrations/Version20260129091410.php new file mode 100644 index 0000000..0bf0a0e --- /dev/null +++ b/migrations/Version20260129091410.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE contrats_option ADD details TEXT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE contrats_option DROP details'); + } +} diff --git a/src/Controller/Dashboard/ContratsController.php b/src/Controller/Dashboard/ContratsController.php index 5f4c6d9..be80f7c 100644 --- a/src/Controller/Dashboard/ContratsController.php +++ b/src/Controller/Dashboard/ContratsController.php @@ -7,15 +7,19 @@ use App\Entity\ContratsLine; use App\Entity\ContratsOption; use App\Entity\ContratsPayments; use App\Entity\Devis; +use App\Entity\Product; use App\Event\Signature\ContratEvent; use App\Form\Type\ContratsType; use App\Logger\AppLogger; use App\Repository\AccountRepository; use App\Repository\DevisRepository; use App\Repository\ContratsRepository; +use App\Service\Mailer\Mailer; use App\Service\Pdf\ContratPdfService; +use App\Service\Pdf\PlPdf; use App\Service\Signature\Client; use Doctrine\ORM\EntityManagerInterface; +use Illuminate\Support\Facades\Mail; use Knp\Component\Pager\PaginatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -85,6 +89,7 @@ class ContratsController extends AbstractController AppLogger $appLogger, EventDispatcherInterface $eventDispatcher, KernelInterface $kernel, + Mailer $mailer, ): Response { if (!$contrat) { throw $this->createNotFoundException('Contrat non trouvé.'); @@ -131,37 +136,182 @@ class ContratsController extends AbstractController $solde = $totalHt - $dejaPaye; - if($request->query->has('act') && $request->query->get('act') === 'cautionCapture') { - $amount = $request->query->get('amountToCapture'); - $paiementCaution = $entityManager->getRepository(ContratsPayments::class)->findOneBy([ + if($request->query->has('type') && $request->query->get('type') === 'accompte') { + $paiementAccompte = $entityManager->getRepository(ContratsPayments::class)->findOneBy([ 'contrat' => $contrat, - 'type' => 'caution', + 'type' => 'accompte', ]); - $result = $stripeClient->capture($paiementCaution->getPaymentId()); - if($result['state']) { - $contrat->setCautionState("recover"); - $entityManager->persist($contrat); - $entityManager->flush(); - $this->addFlash("success","Caution restitué"); - } else { - $this->addFlash("error",$result['message']); + if(!$paiementAccompte) { + $paiementAccompte = new ContratsPayments(); + $paiementAccompte->setContrat($contrat); + $paiementAccompte->setType('accompte'); } - } - if($request->query->has('act') && $request->query->get('act') === 'cautionRelease') { + $paiementAccompte->setValidateAt(new \DateTimeImmutable()); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable()); + $paiementAccompte->setPaymentAt(new \DateTimeImmutable()); + $paiementAccompte->setAmount( $totalHt * 0.25); + $paiementAccompte->setState("complete"); + $paiementAccompte->setPaymentId(""); + $paiementAccompte->setCard([ + 'type' => 'manuel' + ]); + $pdf = new PlPdf($kernel, $paiementAccompte, $contrat); + $pdf->generate(); + $content = $pdf->Output('S'); + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + file_put_contents($tmpSigned, $content); - $paiementCaution = $entityManager->getRepository(ContratsPayments::class)->findOneBy([ + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $data = $client->autoSignConfirmedPayment($paiementAccompte); + // 1. Gestion du PDF SIGNÉ + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + $signedContent = file_get_contents($data); + file_put_contents($tmpSigned, $signedContent); + + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $customer = $contrat->getCustomer(); + $subjectCustomer = "[Ludikevent] Confirmation de votre acompte - #" . $contrat->getNumReservation(); + $mailer->send( + $customer->getEmail(), + $customer->getSurname() . ' ' . $customer->getName(), + $subjectCustomer, + "mails/customer/accompte_confirmation.twig", + [ + 'contrat' => $contrat, + 'payment' => $paiementAccompte, + 'customer' => $customer, + 'reservationLink' => "https://reservation.ludikevent.fr" . $this->generateUrl('gestion_contrat_view', ['num' => $contrat->getNumReservation()]) + ] + ); + $appLogger->record('PAYMENT','Validation accompte manuel pour contrat #' . $contrat->getNumReservation()); + $this->addFlash("success","Validation accompte effectuée"); + return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); + } + if($request->query->has('type') && $request->query->get('type') === 'caution') { + $paiementAccompte = $entityManager->getRepository(ContratsPayments::class)->findOneBy([ 'contrat' => $contrat, 'type' => 'caution', ]); - $result = $stripeClient->cancelPayment($paiementCaution->getPaymentId()); - if($result['state']) { - $contrat->setCautionState("restitue"); - $entityManager->persist($contrat); - $entityManager->flush(); - $this->addFlash("success","Caution restitué"); - } else { - $this->addFlash("error",$result['message']); + if(!$paiementAccompte) { + $paiementAccompte = new ContratsPayments(); + $paiementAccompte->setContrat($contrat); + $paiementAccompte->setType('caution'); } + $paiementAccompte->setValidateAt(new \DateTimeImmutable()); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable()); + $paiementAccompte->setPaymentAt(new \DateTimeImmutable()); + $paiementAccompte->setAmount( $totalCaution); + $paiementAccompte->setState("complete"); + $paiementAccompte->setPaymentId(""); + $paiementAccompte->setCard([ + 'type' => 'manuel' + ]); + $pdf = new PlPdf($kernel, $paiementAccompte, $contrat); + $pdf->generate(); + $content = $pdf->Output('S'); + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + file_put_contents($tmpSigned, $content); + + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $data = $client->autoSignConfirmedPayment($paiementAccompte); + // 1. Gestion du PDF SIGNÉ + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + $signedContent = file_get_contents($data); + file_put_contents($tmpSigned, $signedContent); + + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $customer = $contrat->getCustomer(); + $subjectCustomer = "[Ludikevent] Confirmation de votre caution - #" . $contrat->getNumReservation(); + $mailer->send( + $customer->getEmail(), + $customer->getSurname() . ' ' . $customer->getName(), + $subjectCustomer, + "mails/customer/accompte_confirmation.twig", + [ + 'contrat' => $contrat, + 'payment' => $paiementAccompte, + 'customer' => $customer, + 'reservationLink' => "https://reservation.ludikevent.fr" . $this->generateUrl('gestion_contrat_view', ['num' => $contrat->getNumReservation()]) + ] + ); + $appLogger->record('PAYMENT','Validation caution manuel pour contrat #' . $contrat->getNumReservation()); + $this->addFlash("success","Validation caution effectuée"); + return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); + } + if($request->query->has('type') && $request->query->get('type') === 'solde') { + $paiementAccompte = $entityManager->getRepository(ContratsPayments::class)->findOneBy([ + 'contrat' => $contrat, + 'type' => 'solde', + ]); + if(!$paiementAccompte) { + $paiementAccompte = new ContratsPayments(); + $paiementAccompte->setContrat($contrat); + $paiementAccompte->setType('solde'); + } + $paiementAccompte->setValidateAt(new \DateTimeImmutable()); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable()); + $paiementAccompte->setPaymentAt(new \DateTimeImmutable()); + $paiementAccompte->setAmount( $totalHt); + $paiementAccompte->setState("complete"); + $paiementAccompte->setPaymentId(""); + $paiementAccompte->setCard([ + 'type' => 'manuel' + ]); + $pdf = new PlPdf($kernel, $paiementAccompte, $contrat); + $pdf->generate(); + $content = $pdf->Output('S'); + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + file_put_contents($tmpSigned, $content); + + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentFile(new UploadedFile($tmpSigned, "confirmed-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $data = $client->autoSignConfirmedPayment($paiementAccompte); + // 1. Gestion du PDF SIGNÉ + $tmpSigned = sys_get_temp_dir() . '/sign_' . uniqid() . '.pdf'; + $signedContent = file_get_contents($data); + file_put_contents($tmpSigned, $signedContent); + + // On utilise UploadedFile pour simuler un upload propre pour VichUploader + $paiementAccompte->setPaymentSignedFile(new UploadedFile($tmpSigned, "confirmed-certificate-" . $paiementAccompte->getId() . ".pdf", "application/pdf", null, true)); + $paiementAccompte->setUpdateAt(new \DateTimeImmutable('now')); + $entityManager->persist($paiementAccompte); + $entityManager->flush(); + $customer = $contrat->getCustomer(); + $subjectCustomer = "[Ludikevent] Votre réservation est désormais soldée - #" . $contrat->getNumReservation(); + $mailer->send( + $customer->getEmail(), + $customer->getSurname() . ' ' . $customer->getName(), + $subjectCustomer, + "mails/customer/accompte_confirmation.twig", + [ + 'contrat' => $contrat, + 'payment' => $paiementAccompte, + 'customer' => $customer, + 'reservationLink' => "https://reservation.ludikevent.fr" . $this->generateUrl('gestion_contrat_view', ['num' => $contrat->getNumReservation()]) + ] + ); + $appLogger->record('PAYMENT','Validation solde manuel pour contrat #' . $contrat->getNumReservation()); + $this->addFlash("success","Validation solde effectuée"); + return $this->redirectToRoute('app_crm_contrats_view', ['id' => $contrat->getId()]); } return $this->render('dashboard/contrats/view.twig', [ @@ -192,7 +342,7 @@ class ContratsController extends AbstractController $c = new Contrats(); $lines = [['id' => 0, 'name' => '', 'priceHt1Day' => 0, 'priceHtSupDay' => 0, 'caution' => 0]]; - $options = [['id' => 0, 'name' => '', 'priceHt' => 0]]; + $options = [['id' => 0, 'name' => '', 'priceHt' => 0,'details'=>'']]; if ($devis instanceof Devis) { $c->setDateAt($devis->getStartAt()); @@ -200,6 +350,7 @@ class ContratsController extends AbstractController $c->setCustomer($devis->getCustomer()); $c->setDevis($devis); + // Mapping adresse de l'événement if ($devis->getAddressShip()) { $c->setAddressEvent($devis->getAddressShip()->getAddress()); @@ -213,19 +364,22 @@ class ContratsController extends AbstractController $options = []; foreach ($devis->getDevisLines() as $line) { + $p = $entityManager->getRepository(Product::class)->findOneBy(['name'=>$line->getProduct()]); + $lines[] = [ 'id' => $line->getId(), - 'name' => $line->getProduct()->getName() . " - " . $line->getProduct()->getRef(), + 'name' =>$p->getName() . " - " . $p->getRef(), 'priceHt1Day' => $line->getPriceHt(), 'priceHtSupDay' => $line->getPriceHtSup(), - 'caution' => $line->getProduct()->getCaution(), + 'caution' => $p->getCaution(), ]; } foreach ($devis->getDevisOptions() as $line) { $options[] = [ 'id' => $line->getId(), - 'name' => $line->getOption()->getName(), + 'name' => $line->getOption(), + 'details' => $line->getDetails(), 'priceHt' => $line->getPriceHt(), ]; } @@ -257,6 +411,7 @@ class ContratsController extends AbstractController $vc = new ContratsOption(); $vc->setContrat($c); $vc->setName($line['name']); + $vc->setDetails($line['details']); $vc->setPrice($line['priceHt']); $entityManager->persist($vc); } diff --git a/src/Controller/SignatureController.php b/src/Controller/SignatureController.php index c9f0603..6a840b5 100644 --- a/src/Controller/SignatureController.php +++ b/src/Controller/SignatureController.php @@ -158,18 +158,6 @@ class SignatureController extends AbstractController // 4. Sauvegarde en base de données $devis->setUpdateAt(new \DateTimeImmutable()); - - foreach ($devis->getDevisLines() as $line) { - $product = $line->getProduct(); - - $productReserve = new ProductReserve(); - $productReserve->setProduct($product); - $productReserve->setCustomer($devis->getCustomer()); - $productReserve->setStartAt($devis->getStartAt()); - $productReserve->setEndAt($devis->getEndAt()); - $productReserve->setDevis($devis); - $entityManager->persist($productReserve); - } $entityManager->persist($devis); $entityManager->flush(); diff --git a/src/Entity/ContratsOption.php b/src/Entity/ContratsOption.php index 9cb9716..df5d8d1 100644 --- a/src/Entity/ContratsOption.php +++ b/src/Entity/ContratsOption.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\ContratsOptionRepository; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ContratsOptionRepository::class)] @@ -22,6 +23,9 @@ class ContratsOption #[ORM\Column] private ?float $price = null; + #[ORM\Column(type: Types::TEXT, nullable: true)] + private ?string $details = null; + public function getId(): ?int { return $this->id; @@ -62,4 +66,16 @@ class ContratsOption return $this; } + + public function getDetails(): ?string + { + return $this->details; + } + + public function setDetails(?string $details): static + { + $this->details = $details; + + return $this; + } } diff --git a/src/Service/Pdf/PlPdf.php b/src/Service/Pdf/PlPdf.php index 89e53f6..55a4e78 100644 --- a/src/Service/Pdf/PlPdf.php +++ b/src/Service/Pdf/PlPdf.php @@ -134,7 +134,12 @@ class PlPdf extends Fpdf $this->SetFont('Arial', 'B', 10); $this->Cell(50, 8, $this->clean("Mode de règlement :"), 0, 0); $this->SetFont('Arial', '', 10); - $this->Cell(0, 8, $this->clean($this->contratsPayments->getCard()['type'] ?? 'Carte Bancaire'), 0, 1); + if($this->contratsPayments->getCard()['type'] == "manuel") { + $this->Cell(0, 8, $this->clean("Paiement valider par Ludikevent"), 0, 1); + } else { + $this->Cell(0, 8, $this->clean($this->contratsPayments->getCard()['type'] ?? 'Carte Bancaire'), 0, 1); + } + $this->SetFont('Arial', 'B', 10); $this->Cell(50, 8, $this->clean("Référence transaction :"), 0, 0); diff --git a/templates/dashboard/contrats/add.twig b/templates/dashboard/contrats/add.twig index 7636a30..5096ad1 100644 --- a/templates/dashboard/contrats/add.twig +++ b/templates/dashboard/contrats/add.twig @@ -224,7 +224,7 @@
{# 1. PRODUIT AVEC BOUTON RECHERCHE #} -
+
@@ -235,7 +235,10 @@
- +
+ + +
{# 2. PRIX 1J #}
diff --git a/templates/dashboard/contrats/list.twig b/templates/dashboard/contrats/list.twig index 48bc8f5..03bb67a 100644 --- a/templates/dashboard/contrats/list.twig +++ b/templates/dashboard/contrats/list.twig @@ -3,138 +3,186 @@ {% block title %}Contrats de locations{% endblock %} {% block title_header %}Contrats de locations{% endblock %} -{% block body %} -
+{% block actions %} + +{% endblock %} - {# --- BARRE DE RECHERCHE --- #} -
-
- - - +{% block body %} +
+ + {# --- RECHERCHE STYLE NÉO-GLASS --- #} +
+
+
+
+ + + +
+
-
-
+
{% for contrat in contrats %} - {# CALCUL DES ÉTATS FINANCIERS VIA TES FILTRES TWIG #} {% set acompteOk = contratPaymentPay(contrat, 'accompte') %} {% set cautionOk = contratPaymentPay(contrat, 'caution') %} {% set soldeOk = contratPaymentPay(contrat, 'solde') %} -
-
+
+ {# Background Glow Effect #} +
+ +
+
+ + {# 1. REF & STATUS #} +
+ Contrat +

{{ contrat.numReservation }}

- {# --- COLONNE 1 : RÉFÉRENCE & SIGNATURE (2/12) --- #} -
- Référence - -
{% if contrat.isSigned %} - - - Signé - +
+ Signé +
{% else %} - - - En attente - +
+ Attente +
{% endif %}
-
- {# --- COLONNE 2 : CLIENT (3/12) --- #} -
-
-
- -
-
- Locataire - -

{{ contrat.customer.email }}

+ {# 2. CLIENT #} +
+
+
+
+ +
+
+
+

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

+

{{ contrat.customer.email }}

+
-
- {# --- COLONNE 3 : LIEU & VILLE (2/12) --- #} -
- Lieu - - -
- - {# --- COLONNE 4 : STATUT FINANCIER (3/12) --- #} -
- Suivi Paiements -
- {# ACOMPTE #} -
-
-
- Acompte -
- {% if acompteOk %} - - {% endif %} -
- - {# CAUTION #} -
-
-
- Caution -
- {{ cautionOk ? 'OK' : 'REQUISE' }} -
- - {# SOLDE #} -
- {{ soldeOk ? 'SOLDÉ' : 'À PERCEVOIR' }} - {% if soldeOk %} - - - - - {% endif %} + {# 3. LIEU #} +
+
+ Destination +

{{ contrat.townEvent }}

+

{{ contrat.zipCodeEvent }}

-
- {# --- COLONNE 5 : ACTIONS (2/12) --- #} -
- - - + {# 4. PAIEMENTS #} +
+
- - - + {# --- ACOMPTE --- #} +
+
+ {% if acompteOk %} + + {% else %} + + {% endif %} +
+ + Acompte + +
- - - + {# --- CAUTION --- #} +
+
+ + {% if cautionOk %} + + {% else %} + + {% endif %} + +
+ + Caution + +
+ + {# --- SOLDE --- #} +
+
+ + {% if soldeOk %} + + {% else %} + + {% endif %} + +
+ + Solde + +
+ +
+
+ + {# 5. ACTIONS #} +
{% endfor %}
+ +
+ {{ knp_pagination_render(contrats) }} +
- {{ knp_pagination_render(contrats) }} - + {% endblock %} diff --git a/templates/dashboard/contrats/view.twig b/templates/dashboard/contrats/view.twig index 2ec9d57..7f9cb12 100644 --- a/templates/dashboard/contrats/view.twig +++ b/templates/dashboard/contrats/view.twig @@ -4,311 +4,286 @@ {% block actions %} {% endblock %} {% block body %} - {# Définition des états de paiement basés sur les variables du contrôleur #} + {# Définition des états de paiement #} {% set acompteOk = contratPaymentPay(contrat, 'accompte') %} {% set cautionOk = contratPaymentPay(contrat, 'caution') %} {% set soldeOk = (solde <= 0.05) %}
- {# --- GRILLE DES 4 CARTES DE STATUT --- #} -
+ {# --- 1. BANDEAU DE STATUT RAPIDE --- #} +
+
+ État Juridique + {% if contrat.signed %} + SIGNÉ + {% else %} + EN ATTENTE + {% endif %} +
+
+ Reste à percevoir + + {{ soldeOk ? 'CONTRAT SOLDÉ' : (solde|number_format(2, ',', ' ') ~ ' €') }} + +
+
+ Référence + #{{ contrat.numReservation }} +
+
- {# 1. SIGNATURE #} -
-
-
- + {# --- 2. INFOS CLIENT & ÉVÉNEMENT --- #} +
+ {# CARTE CLIENT #} +
+
+
- {# 2. ARRHES (ACOMPTE) #} -
-
-
- + {# CARTE ÉVÉNEMENT #} +
+
+
- {# 3. CAUTION (MODE ADMINISTRATION) #} -
-
- {# Icône dynamique : Pulse si non déposée, fixe si OK #} -
- - +
+ + {# --- 3. ÉTAT DES PAIEMENTS AVEC ACTIONS DE GESTION --- #} +
+
+ + {# --- ACOMPTE --- #} +
+
+ + {% if acompteOk %}{% else %}{% endif %}
-
-

Garantie (Caution)

- - {% if not cautionOk %} - {# ÉTAT 1 : ATTENTE DE DÉPÔT #} -
-

Non Déposée

-

Attendu : {{ totalCaution|number_format(2, ',', ' ') }}€

-
+ Acompte + {% if not acompteOk %} + + Marquer réglé + {% else %} - {# ÉTAT 2 : CAUTION DÉPOSÉE #} - {% if contrat.cautionState == null %} - {# ACTIONS DISPONIBLES #} -
-

Empreinte active

-
- {# LIBÉRER #} - - Libérer la caution - +

Encaissé

+ {% endif %} +
+
- {# ENCAISSER #} -
- -
- - -
-
-
-
+ + + {# --- CAUTION --- #} +
+
+ + {% if cautionOk %}{% else %}{% endif %} + +
+
+ Caution +
+ {% if not cautionOk %} + + Marquer reçue + {% else %} - {# ÉTAT 3 : ARCHIVÉ (RESTITUÉ OU RÉCUPÉRÉ) #} -
- {% if contrat.cautionState == 'restitue' %} - - Restituée - - {% elseif contrat.cautionState == 'recover' %} - - Encaissée - - {% else %} - {{ contrat.cautionState }} - {% endif %} + {% endif %} - {% endif %} +
-
- {# 4. SOLDE #} -
-
-
- + + + + {# --- SOLDE --- #} +
+
+ + {% if soldeOk %}{% else %}{% endif %} +
-

État du solde

- {% if soldeOk %} -

Dossier Soldé

+ Solde Final + {% if not soldeOk %} + + Régler le solde + {% else %} -
- -
- - -
- Reste : {{ solde|number_format(2, ',', ' ') }}€ -
+

Totalité payée

{% endif %}
+
- - {# --- SECTION DÉTAILS : CLIENT / LOGISTIQUE / FINANCES --- #} -
- - {# CARD 1 : CLIENT #} -
-
-

- - Locataire -

-
-

{{ contrat.customer.surname }}

-

{{ contrat.customer.name }}

-
-
-
- - {{ contrat.customer.email }} -
-
- - {{ contrat.customer.phone }} -
+ {# --- 4. PRODUITS ET OPTIONS --- #} +
+ {# PRODUITS #} +
+

Équipements loués

+
+ {% for product in contrat.contratsLines %} +
+
+
+

{{ product.name }}

+

Caution : {{ product.caution }}€

+
+
+ {{ product.price1DayHt }}€ + HT / Jour +
+
+
+ {% endfor %}
- {# CARD 2 : LOGISTIQUE #} -
-

- - Lieu de l'événement -

-
-
-

Adresse

-

- {{ contrat.addressEvent }}
- {{ contrat.zipCodeEvent }} {{ contrat.townEvent|upper }} -

-
-
-
-

Du

-

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

+ {# OPTIONS #} +
+

Options & Services

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

{{ product.name }}

+

{{ product.details }}

+
+
+ {{ product.price }}€ +
+
-
-

Au

-

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

-
-
-
-
- - {# CARD 3 : RÉCAPITULATIF FINANCIER #} -
-
-

- - Détail Règlement -

-
-
- Total Prestation - {{ totalHT|number_format(2, ',', ' ') }}€ -
-
-
-

Acompte Requis

-

{{ arrhes|number_format(2, ',', ' ') }}€

-
-
-
-

Montant Caution

-

{{ totalCaution|number_format(2, ',', ' ') }}€

-
-
+ {% else %} +
Aucun supplément
+ {% endfor %}
- {# --- HISTORIQUE DES PAIEMENTS --- #} -
-

- Historique des transactions -

-
- + {# --- 5. HISTORIQUE DES PAIEMENTS --- #} +
+

Historique Financier

+
+
- - - - - + + + + + {% for payment in contrat.contratsPayments %} {% if payment.state == 'complete' %} - + {% endif %} {% else %} - - - + {% endfor %}
DateTypeMontantAction
TransactionTypeMontantJustificatif
{{ payment.paymentAt|date('d/m/Y H:i') }}{{ payment.paymentAt|date('d/m/Y H:i') }} - - {{ payment.type|replace({'_': ' '}) }} - + + {{ payment.type|replace({'_': ' '}) }} + {{ payment.amount|number_format(2, ',', ' ') }}€ - - - Reçu + + + REÇU PDF
Aucune transaction enregistrée
Aucun paiement effectué
+
{% endblock %} diff --git a/templates/mails/customer/accompte_confirmation.twig b/templates/mails/customer/accompte_confirmation.twig index fc700b7..c203ff4 100644 --- a/templates/mails/customer/accompte_confirmation.twig +++ b/templates/mails/customer/accompte_confirmation.twig @@ -49,7 +49,11 @@ Méthode + {% if datas.payment.card.type is defined and datas.payment.card.type == "manuel" %} + Paiement effectuer par Ludikevent + {% else %} {{ datas.payment.card.method_label|default('Carte Bancaire') }} + {% endif %}