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) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,21 @@ class EFlexController extends AbstractController
|
|||||||
throw $this->createNotFoundException('Client introuvable');
|
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'));
|
$description = trim($request->request->getString('description'));
|
||||||
$totalAmount = $request->request->getString('totalAmount');
|
$totalAmount = $request->request->getString('totalAmount');
|
||||||
$nbEcheances = $request->request->getInt('nbEcheances');
|
$nbEcheances = $request->request->getInt('nbEcheances');
|
||||||
|
|||||||
@@ -761,12 +761,52 @@ class WebhookStripeController extends AbstractController
|
|||||||
$line->setStripePaymentIntentId($paymentIntent->id);
|
$line->setStripePaymentIntentId($paymentIntent->id);
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
|
$cancelled = false;
|
||||||
if ($eflex->getNbFailed() >= 2) {
|
if ($eflex->getNbFailed() >= 2) {
|
||||||
$eflex->setState(\App\Entity\EFlex::STATE_CANCELLED);
|
$eflex->setState(\App\Entity\EFlex::STATE_CANCELLED);
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
$cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$customer = $eflex->getCustomer();
|
$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()) {
|
if (null !== $customer->getEmail()) {
|
||||||
try {
|
try {
|
||||||
$payUrl = $this->urlGenerator->generate('app_eflex_pay', [
|
$payUrl = $this->urlGenerator->generate('app_eflex_pay', [
|
||||||
|
|||||||
@@ -1208,9 +1208,26 @@
|
|||||||
|
|
||||||
{# Tab: E-Flex #}
|
{# Tab: E-Flex #}
|
||||||
{% elseif tab == 'esyflex' %}
|
{% 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 %}
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h2 class="text-lg font-bold uppercase">E-Flex</h2>
|
<h2 class="text-lg font-bold uppercase">E-Flex</h2>
|
||||||
<button type="button" data-modal-open="modal-eflex" class="btn-gold px-4 py-2 font-bold uppercase text-[10px] tracking-wider text-gray-900">Creer un E-Flex</button>
|
{% if hasDefaultEflex %}
|
||||||
|
<span class="px-4 py-2 bg-red-500/20 text-red-700 font-bold uppercase text-[10px] tracking-wider cursor-not-allowed" title="E-Flex en defaut - regularisation necessaire">Creation bloquee (defaut)</span>
|
||||||
|
{% elseif hasActiveEflex %}
|
||||||
|
<span class="px-4 py-2 bg-yellow-500/20 text-yellow-700 font-bold uppercase text-[10px] tracking-wider cursor-not-allowed" title="Un E-Flex est deja en cours">E-Flex en cours</span>
|
||||||
|
{% else %}
|
||||||
|
<button type="button" data-modal-open="modal-eflex" class="btn-gold px-4 py-2 font-bold uppercase text-[10px] tracking-wider text-gray-900">Creer un E-Flex</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if eflexList|length > 0 %}
|
{% if eflexList|length > 0 %}
|
||||||
|
|||||||
57
templates/emails/eflex_cancelled.html.twig
Normal file
57
templates/emails/eflex_cancelled.html.twig
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{% extends 'email/base.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<table width="600" cellpadding="0" cellspacing="0" style="margin: 0 auto;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 32px;">
|
||||||
|
{% set greeting = customer.raisonSociale ? 'Chez ' ~ customer.raisonSociale : 'Bonjour ' ~ customer.firstName %}
|
||||||
|
<h1 style="font-family: Arial, Helvetica, sans-serif; font-size: 22px; font-weight: 700; color: #111827; margin: 0 0 16px;">{{ greeting }},</h1>
|
||||||
|
|
||||||
|
<div style="background: #fef2f2; border-left: 4px solid #dc2626; padding: 16px; margin: 0 0 20px;">
|
||||||
|
<p style="font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700; color: #dc2626; margin: 0;">
|
||||||
|
E-Flex {{ eflex.reference }} annule
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #374151; line-height: 22px; margin: 0 0 16px;">
|
||||||
|
Votre contrat de financement E-Flex a ete automatiquement annule suite a des rejets repetes de prelevement. Le solde restant reste du.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 20px 0; border: 1px solid #e5e7eb;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb; width: 35%;">Reference</td>
|
||||||
|
<td style="padding: 10px 16px; font-family: monospace; font-size: 13px; font-weight: 700;">{{ eflex.reference }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb;">Total contrat</td>
|
||||||
|
<td style="padding: 10px 16px; font-family: monospace; font-size: 14px; font-weight: 700;">{{ eflex.totalAmount|number_format(2, ',', ' ') }} €</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb;">Deja paye</td>
|
||||||
|
<td style="padding: 10px 16px; font-family: monospace; font-size: 14px; font-weight: 700; color: #16a34a;">{{ eflex.totalPaid|number_format(2, ',', ' ') }} €</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb;">Restant du</td>
|
||||||
|
<td style="padding: 10px 16px; font-family: monospace; font-size: 16px; font-weight: 700; color: #dc2626;">{{ (eflex.totalAmount|number_format(2, '.', '') - eflex.totalPaid)|number_format(2, ',', ' ') }} €</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb;">Echeances payees</td>
|
||||||
|
<td style="padding: 10px 16px; font-size: 13px;">{{ eflex.nbPaid }}/{{ eflex.nbLines }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 16px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: #9ca3af; background: #f9fafb;">Rejets</td>
|
||||||
|
<td style="padding: 10px 16px; font-size: 13px; font-weight: 700; color: #dc2626;">{{ eflex.nbFailed }} echec(s)</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p style="font-family: Arial, Helvetica, sans-serif; font-size: 14px; color: #374151; line-height: 22px; margin: 16px 0;">
|
||||||
|
Veuillez contacter notre service pour regulariser votre situation dans les plus brefs delais.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: #9ca3af; margin: 16px 0 0;">
|
||||||
|
Pour toute question : <a href="mailto:contact@e-cosplay.fr" style="color: #fabf04;">contact@e-cosplay.fr</a>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user