From 6411db64c22c45ef1396b1639bd1d36438a811bd Mon Sep 17 00:00:00 2001 From: Serreau Jovann Date: Thu, 9 Apr 2026 08:00:01 +0200 Subject: [PATCH] feat: E-Flex - annulation auto apres 2 rejets + blocage creation Annulation automatique: - Apres 2 echecs de prelevement, E-Flex passe en STATE_CANCELLED - Email d'annulation envoye au client (detail: total, paye, restant, rejets) + notification admin - Template eflex_cancelled.html.twig Blocage creation: - Controller: refuse la creation si un E-Flex est en cours (active, pending_setup, draft) ou en defaut (cancelled avec nbFailed > 0) - Template: bouton "Creer" remplace par "Creation bloquee (defaut)" ou "E-Flex en cours" selon le cas Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Controller/Admin/EFlexController.php | 15 ++++++ src/Controller/WebhookStripeController.php | 40 +++++++++++++++ templates/admin/clients/show.html.twig | 19 +++++++- templates/emails/eflex_cancelled.html.twig | 57 ++++++++++++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 templates/emails/eflex_cancelled.html.twig diff --git a/src/Controller/Admin/EFlexController.php b/src/Controller/Admin/EFlexController.php index 35daab5..af0203f 100644 --- a/src/Controller/Admin/EFlexController.php +++ b/src/Controller/Admin/EFlexController.php @@ -39,6 +39,21 @@ class EFlexController extends AbstractController throw $this->createNotFoundException('Client introuvable'); } + // Bloquer si un E-Flex est en cours ou annule (defaut) + $existingEflex = $this->em->getRepository(EFlex::class)->findBy(['customer' => $customer]); + foreach ($existingEflex as $existing) { + if (\in_array($existing->getState(), [EFlex::STATE_ACTIVE, EFlex::STATE_PENDING_SETUP, EFlex::STATE_DRAFT], true)) { + $this->addFlash('error', 'Un E-Flex est deja en cours ('.$existing->getReference().'). Impossible d\'en creer un nouveau.'); + + return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']); + } + if (EFlex::STATE_CANCELLED === $existing->getState() && $existing->getNbFailed() > 0) { + $this->addFlash('error', 'Un E-Flex est en defaut ('.$existing->getReference().'). Le client doit regulariser avant de creer un nouveau E-Flex.'); + + return $this->redirectToRoute('app_admin_clients_show', ['id' => $customerId, 'tab' => 'esyflex']); + } + } + $description = trim($request->request->getString('description')); $totalAmount = $request->request->getString('totalAmount'); $nbEcheances = $request->request->getInt('nbEcheances'); diff --git a/src/Controller/WebhookStripeController.php b/src/Controller/WebhookStripeController.php index f46d989..6448f7b 100644 --- a/src/Controller/WebhookStripeController.php +++ b/src/Controller/WebhookStripeController.php @@ -761,12 +761,52 @@ class WebhookStripeController extends AbstractController $line->setStripePaymentIntentId($paymentIntent->id); $this->em->flush(); + $cancelled = false; if ($eflex->getNbFailed() >= 2) { $eflex->setState(\App\Entity\EFlex::STATE_CANCELLED); $this->em->flush(); + $cancelled = true; } $customer = $eflex->getCustomer(); + + // Si annule : envoyer mail d'annulation au client + admin + if ($cancelled) { + if (null !== $customer->getEmail()) { + try { + $this->mailer->sendEmail( + $customer->getEmail(), + 'E-Flex '.$eflex->getReference().' annule - Rejets de prelevement', + $this->twig->render('emails/eflex_cancelled.html.twig', [ + 'customer' => $customer, + 'eflex' => $eflex, + ]), + null, + null, + false, + ); + } catch (\Throwable) { + // silencieux + } + } + + try { + $this->mailer->sendEmail( + self::NOTIFICATION_EMAIL, + 'E-Flex '.$eflex->getReference().' annule (2 rejets) - '.$customer->getFullName(), + $this->twig->render('emails/eflex_cancelled.html.twig', [ + 'customer' => $customer, + 'eflex' => $eflex, + ]), + null, + null, + false, + ); + } catch (\Throwable) { + // silencieux + } + } + if (null !== $customer->getEmail()) { try { $payUrl = $this->urlGenerator->generate('app_eflex_pay', [ diff --git a/templates/admin/clients/show.html.twig b/templates/admin/clients/show.html.twig index 81a5f88..cdc4e13 100644 --- a/templates/admin/clients/show.html.twig +++ b/templates/admin/clients/show.html.twig @@ -1208,9 +1208,26 @@ {# Tab: E-Flex #} {% elseif tab == 'esyflex' %} + {% set hasActiveEflex = false %} + {% set hasDefaultEflex = false %} + {% for e in eflexList %} + {% if e.state in ['active', 'pending_setup', 'draft'] %} + {% set hasActiveEflex = true %} + {% endif %} + {% if e.state == 'cancelled' and e.nbFailed > 0 %} + {% set hasDefaultEflex = true %} + {% endif %} + {% endfor %} +

E-Flex

- + {% if hasDefaultEflex %} + Creation bloquee (defaut) + {% elseif hasActiveEflex %} + E-Flex en cours + {% else %} + + {% endif %}
{% if eflexList|length > 0 %} diff --git a/templates/emails/eflex_cancelled.html.twig b/templates/emails/eflex_cancelled.html.twig new file mode 100644 index 0000000..e370200 --- /dev/null +++ b/templates/emails/eflex_cancelled.html.twig @@ -0,0 +1,57 @@ +{% extends 'email/base.html.twig' %} + +{% block content %} + + + + +
+ {% set greeting = customer.raisonSociale ? 'Chez ' ~ customer.raisonSociale : 'Bonjour ' ~ customer.firstName %} +

{{ greeting }},

+ +
+

+ E-Flex {{ eflex.reference }} annule +

+
+ +

+ Votre contrat de financement E-Flex a ete automatiquement annule suite a des rejets repetes de prelevement. Le solde restant reste du. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Reference{{ eflex.reference }}
Total contrat{{ eflex.totalAmount|number_format(2, ',', ' ') }} €
Deja paye{{ eflex.totalPaid|number_format(2, ',', ' ') }} €
Restant du{{ (eflex.totalAmount|number_format(2, '.', '') - eflex.totalPaid)|number_format(2, ',', ' ') }} €
Echeances payees{{ eflex.nbPaid }}/{{ eflex.nbLines }}
Rejets{{ eflex.nbFailed }} echec(s)
+ +

+ Veuillez contacter notre service pour regulariser votre situation dans les plus brefs delais. +

+ +

+ Pour toute question : contact@e-cosplay.fr +

+
+{% endblock %}