feat(paiement): plafonner la saisie des paiements en incluant les paiements non remis
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 5m31s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 6m18s

This commit is contained in:
2026-08-08 00:23:27 +02:00
parent be1ec1c96a
commit 1e2bc23603
5 changed files with 32 additions and 13 deletions
@@ -160,6 +160,22 @@ public class Licence {
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@Transient
public BigDecimal getSoldeRestantEngage() {
BigDecimal prixTotal = getPrixTotal();
if (paiements == null || paiements.isEmpty()) {
return prixTotal;
}
BigDecimal totalTousPaiements = paiements.stream()
.map(Paiement::getMontant)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal reste = prixTotal.subtract(totalTousPaiements);
return reste.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : reste;
}
// Getters and Setters
public Long getId() { return id; }
@@ -114,15 +114,18 @@ class LicenceReductionTest {
p1.setRemis(false); // Non remis (ex: Chèque Sport mairie en attente)
licence.addPaiement(p1);
// La somme de ce chèque non remis n'est PAS déduite => reste à payer = 100.00 €
// La somme de ce chèque non remis n'est PAS déduite du financier => reste à payer = 100.00 €
assertEquals(0, BigDecimal.ZERO.compareTo(licence.getSommePayee()));
assertEquals(0, new BigDecimal("100.00").compareTo(licence.getResteAPayer()));
// En revanche, le solde disponible engagé prend en compte TOUS les paiements => reste engagé = 60.00 €
assertEquals(0, new BigDecimal("60.00").compareTo(licence.getSoldeRestantEngage()));
// Passage à remis = true (remis/encaissé)
p1.setRemis(true);
// La somme est déduite => reste à payer = 60.00 €
// La somme est déduite du financier => reste à payer = 60.00 €
assertEquals(0, new BigDecimal("40.00").compareTo(licence.getSommePayee()));
assertEquals(0, new BigDecimal("60.00").compareTo(licence.getResteAPayer()));
assertEquals(0, new BigDecimal("60.00").compareTo(licence.getSoldeRestantEngage()));
}
}
@@ -63,9 +63,10 @@ public class PaiementController {
log.info(">>> [PaiementController] Demande de création de paiement reçue pour Licence ID: {}, Adhérent ID: {}, Montant: {} €, Remis: {}", id, adherentId, montant, remis);
// Validation basique pour ne pas payer plus que le reste à payer
if (montant.compareTo(licence.getResteAPayer()) > 0) {
montant = licence.getResteAPayer();
// Validation pour ne pas dépasser le tarif global (tous paiements remis ou non inclus)
BigDecimal maxAutorise = licence.getSoldeRestantEngage();
if (montant.compareTo(maxAutorise) > 0) {
montant = maxAutorise;
}
if (montant.compareTo(BigDecimal.ZERO) > 0) {
@@ -128,9 +129,8 @@ public class PaiementController {
Licence licence = paiement.getLicence();
// Validation basique pour ne pas dépasser le montant total de la licence
BigDecimal currentContribution = Boolean.TRUE.equals(paiement.getRemis()) ? paiement.getMontant() : BigDecimal.ZERO;
BigDecimal maxAmount = licence.getResteAPayer().add(currentContribution);
// Validation pour ne pas dépasser le tarif global (tous paiements remis ou non inclus)
BigDecimal maxAmount = licence.getSoldeRestantEngage().add(paiement.getMontant());
if (montant.compareTo(maxAmount) > 0) {
montant = maxAmount;
}
@@ -307,11 +307,11 @@
th:data-pourcentage-reduction="${lic.pourcentageReductionEducateur != null ? lic.pourcentageReductionEducateur : 100}"
onclick="openLicenceModal(this.getAttribute('data-licence-id'), this.getAttribute('data-categorie-id'), this.getAttribute('data-numero-licence'), this.getAttribute('data-etat'), this.getAttribute('data-type-demande'), this.getAttribute('data-type-licence'), this.getAttribute('data-equipe-id'), this.getAttribute('data-commentaire'), this.getAttribute('data-reduction-educateur'), this.getAttribute('data-pourcentage-reduction'))"
class="text-blue-600 hover:text-blue-800 font-medium bg-blue-50 px-3 py-1 rounded-lg">Modifier</button>
<button th:if="${lic.getResteAPayer().compareTo(T(java.math.BigDecimal).ZERO) > 0}"
<button th:if="${lic.getSoldeRestantEngage().compareTo(T(java.math.BigDecimal).ZERO) > 0}"
type="button"
th:data-licence-id="${lic.id}"
th:data-reste-a-payer="${lic.getResteAPayer()}"
onclick="openPaiementModal(this.getAttribute('data-licence-id'), this.getAttribute('data-reste-a-payer'))"
th:data-solde-restant="${lic.getSoldeRestantEngage()}"
onclick="openPaiementModal(this.getAttribute('data-licence-id'), this.getAttribute('data-solde-restant'))"
class="text-green-600 hover:text-green-800 font-medium bg-green-50 px-3 py-1 rounded-lg btn-payer">Payer</button>
<span th:if="${lic.getResteAPayer().compareTo(T(java.math.BigDecimal).ZERO) > 0}" th:id="'relance-licence-container-' + ${lic.id}">
<button type="button"
@@ -372,7 +372,7 @@
th:data-date="${paiement.datePaiement}"
th:data-mode-id="${paiement.modePaiement.id}"
th:data-licence-id="${lic.id}"
th:data-max-amount="${lic.getResteAPayer().add(paiement.remis ? paiement.montant : 0)}"
th:data-max-amount="${lic.getSoldeRestantEngage().add(paiement.montant)}"
th:data-cheque="${paiement.numeroCheque}"
th:data-commentaire="${paiement.commentaire}"
th:data-remis="${paiement.remis}"
@@ -109,7 +109,7 @@
th:data-date="${p.datePaiement}"
th:data-mode-id="${p.modePaiement.id}"
th:data-licence-id="${p.licence.id}"
th:data-max-amount="${p.licence.getResteAPayer().add(p.remis ? p.montant : 0)}"
th:data-max-amount="${p.licence.getSoldeRestantEngage().add(p.montant)}"
th:data-cheque="${p.numeroCheque}"
th:data-remis="${p.remis}"
onclick="openEditPaiementModal(this.getAttribute('data-paiement-id'), this.getAttribute('data-montant'), this.getAttribute('data-date'), this.getAttribute('data-mode-id'), this.getAttribute('data-licence-id'), this.getAttribute('data-max-amount'), this.getAttribute('data-cheque'), this.getAttribute('data-remis'))"