fix: paiement manuel partiel - un avis peut avoir plusieurs paiements

- Ne passe en 'accepted' que quand totalPaid >= totalTtc
- Facture generee uniquement quand totalement paye
- Message flash avec reste a payer si paiement partiel
- Modal affiche "Deja paye / Reste" si paiements existants
- Bouton visible sur avis send + tout state non cancel/accepted

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Serreau Jovann
2026-04-08 19:42:53 +02:00
parent 49a4cf3ec2
commit 8d91795250
2 changed files with 45 additions and 12 deletions

View File

@@ -308,27 +308,49 @@ class AdvertController extends AbstractController
} }
// Creer l'AdvertPayment // Creer l'AdvertPayment
$payment = new \App\Entity\AdvertPayment($advert, \App\Entity\AdvertPayment::TYPE_SUCCESS, number_format((float) $amount, 2, '.', '')); $amountFormatted = number_format((float) $amount, 2, '.', '');
$payment = new \App\Entity\AdvertPayment($advert, \App\Entity\AdvertPayment::TYPE_SUCCESS, $amountFormatted);
$payment->setMethod($method); $payment->setMethod($method);
$this->em->persist($payment); $this->em->persist($payment);
// Mettre a jour l'etat de l'avis
$advert->setState(Advert::STATE_ACCEPTED);
// Tracker l'evenement // Tracker l'evenement
$this->em->persist(new \App\Entity\AdvertEvent($advert, \App\Entity\AdvertEvent::TYPE_PAY, 'Paiement manuel : '.$methodLabel.' - '.$amount.' EUR')); $this->em->persist(new \App\Entity\AdvertEvent($advert, \App\Entity\AdvertEvent::TYPE_PAY, 'Paiement manuel : '.$methodLabel.' - '.$amount.' EUR'));
// Calculer le total des paiements reussis
$totalPaid = 0.0;
foreach ($advert->getPayments() as $p) {
if (\App\Entity\AdvertPayment::TYPE_SUCCESS === $p->getType()) {
$totalPaid += (float) $p->getAmount();
}
}
$totalPaid += (float) $amountFormatted;
$advertTotal = (float) $advert->getTotalTtc();
$isFullyPaid = $totalPaid >= ($advertTotal - 0.01);
if ($isFullyPaid) {
$advert->setState(Advert::STATE_ACCEPTED);
}
$this->em->flush(); $this->em->flush();
// Generer la facture // Generer la facture uniquement si totalement paye
try { if ($isFullyPaid) {
$factureService->createPaidFactureFromAdvert($advert, number_format((float) $amount, 2, '.', ''), $methodLabel); try {
} catch (\Throwable $e) { $factureService->createPaidFactureFromAdvert($advert, number_format($advertTotal, 2, '.', ''), $methodLabel);
$this->addFlash('warning', 'Paiement enregistre mais erreur generation facture : '.$e->getMessage()); } catch (\Throwable $e) {
$this->addFlash('warning', 'Paiement enregistre mais erreur generation facture : '.$e->getMessage());
}
} }
$this->meilisearch->indexAdvert($advert); $this->meilisearch->indexAdvert($advert);
$this->addFlash('success', 'Paiement de '.$amount.' EUR enregistre ('.$methodLabel.') pour l\'avis '.$advert->getOrderNumber()->getNumOrder().'.'); $remaining = $advertTotal - $totalPaid;
if ($isFullyPaid) {
$this->addFlash('success', 'Paiement complet de '.$amount.' EUR enregistre ('.$methodLabel.'). Avis '.$advert->getOrderNumber()->getNumOrder().' marque paye.');
} else {
$this->addFlash('success', 'Paiement partiel de '.$amount.' EUR enregistre ('.$methodLabel.'). Reste a payer : '.number_format($remaining, 2, ',', ' ').' EUR.');
}
return $this->redirectToRoute('app_admin_clients_show', [ return $this->redirectToRoute('app_admin_clients_show', [
'id' => $advert->getCustomer()?->getId() ?? 0, 'id' => $advert->getCustomer()?->getId() ?? 0,

View File

@@ -641,6 +641,8 @@
<form method="post" action="{{ path('app_admin_advert_resend', {id: a.id}) }}" class="inline" data-confirm="Renvoyer l'avis de paiement au client ?"> <form method="post" action="{{ path('app_admin_advert_resend', {id: a.id}) }}" class="inline" data-confirm="Renvoyer l'avis de paiement au client ?">
<button type="submit" class="px-3 py-1 bg-purple-500/20 text-purple-700 hover:bg-purple-500 hover:text-white font-bold uppercase text-[10px]transition-all">Renvoyer</button> <button type="submit" class="px-3 py-1 bg-purple-500/20 text-purple-700 hover:bg-purple-500 hover:text-white font-bold uppercase text-[10px]transition-all">Renvoyer</button>
</form> </form>
{% endif %}
{% if a.state == 'send' or (a.state != 'cancel' and a.state != 'accepted') %}
<button type="button" data-modal-open="modal-manual-pay-{{ a.id }}" <button type="button" data-modal-open="modal-manual-pay-{{ a.id }}"
class="px-3 py-1 bg-green-500/20 text-green-700 hover:bg-green-500 hover:text-white font-bold uppercase text-[10px] transition-all"> class="px-3 py-1 bg-green-500/20 text-green-700 hover:bg-green-500 hover:text-white font-bold uppercase text-[10px] transition-all">
Enregistrer paiement Enregistrer paiement
@@ -688,11 +690,20 @@
{# Modals paiement manuel #} {# Modals paiement manuel #}
{% for a in advertsList %} {% for a in advertsList %}
{% if a.state == 'send' %} {% if a.state == 'send' or (a.state != 'cancel' and a.state != 'accepted') %}
<div id="modal-manual-pay-{{ a.id }}" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"> <div id="modal-manual-pay-{{ a.id }}" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="glass-heavy p-6 w-full max-w-md"> <div class="glass-heavy p-6 w-full max-w-md">
<h2 class="text-lg font-bold uppercase mb-4">Enregistrer un paiement</h2> <h2 class="text-lg font-bold uppercase mb-4">Enregistrer un paiement</h2>
<p class="text-xs text-gray-500 mb-4">Avis {{ a.orderNumber.numOrder }} - {{ a.totalTtc }} &euro;</p> <p class="text-xs text-gray-500 mb-2">Avis {{ a.orderNumber.numOrder }} - Total : {{ a.totalTtc }} &euro;</p>
{% set totalPaid = 0 %}
{% for p in a.payments %}
{% if p.type == 'success' %}
{% set totalPaid = totalPaid + p.amount %}
{% endif %}
{% endfor %}
{% if totalPaid > 0 %}
<p class="text-xs text-green-600 font-bold mb-2">Deja paye : {{ totalPaid|number_format(2, ',', ' ') }} &euro; - Reste : {{ (a.totalTtc - totalPaid)|number_format(2, ',', ' ') }} &euro;</p>
{% endif %}
<form method="post" action="{{ path('app_admin_advert_manual_payment', {id: a.id}) }}"> <form method="post" action="{{ path('app_admin_advert_manual_payment', {id: a.id}) }}">
<div class="grid grid-cols-1 gap-3 mb-4"> <div class="grid grid-cols-1 gap-3 mb-4">
<div> <div>