From 2f68af6fb50608676f1b556245efc8bfd6caef11 Mon Sep 17 00:00:00 2001 From: Youssef Date: Sat, 8 Aug 2026 00:08:50 +0200 Subject: [PATCH] feat(paiement): gestion specifique du mode Cheque Sport mairie (masquage num cheque, option remis oui/non, calcul reste a payer) --- .../com/astalange/core/entity/Licence.java | 2 + .../com/astalange/core/entity/Paiement.java | 6 ++ .../migration/V43__add_remis_to_paiement.sql | 4 + .../astalange/core/LicenceReductionTest.java | 30 ++++++++ .../web/controller/PaiementController.java | 13 +++- .../resources/templates/adherents/form.html | 75 ++++++++++++++++--- .../resources/templates/paiements/list.html | 72 ++++++++++++++++-- 7 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 as-talange-core/src/main/resources/db/migration/V43__add_remis_to_paiement.sql diff --git a/as-talange-core/src/main/java/com/astalange/core/entity/Licence.java b/as-talange-core/src/main/java/com/astalange/core/entity/Licence.java index 6d4b2e2..32a2e34 100644 --- a/as-talange-core/src/main/java/com/astalange/core/entity/Licence.java +++ b/as-talange-core/src/main/java/com/astalange/core/entity/Licence.java @@ -141,6 +141,7 @@ public class Licence { } BigDecimal totalPaye = paiements.stream() + .filter(p -> p.getRemis() == null || Boolean.TRUE.equals(p.getRemis())) .map(Paiement::getMontant) .reduce(BigDecimal.ZERO, BigDecimal::add); @@ -154,6 +155,7 @@ public class Licence { return BigDecimal.ZERO; } return paiements.stream() + .filter(p -> p.getRemis() == null || Boolean.TRUE.equals(p.getRemis())) .map(Paiement::getMontant) .reduce(BigDecimal.ZERO, BigDecimal::add); } diff --git a/as-talange-core/src/main/java/com/astalange/core/entity/Paiement.java b/as-talange-core/src/main/java/com/astalange/core/entity/Paiement.java index de7d82f..cdbe30c 100644 --- a/as-talange-core/src/main/java/com/astalange/core/entity/Paiement.java +++ b/as-talange-core/src/main/java/com/astalange/core/entity/Paiement.java @@ -35,6 +35,9 @@ public class Paiement { @Column(name = "commentaire", columnDefinition = "TEXT") private String commentaire; + @Column(name = "remis", nullable = false) + private Boolean remis = true; + // Getters and Setters public Long getId() { return id; } @@ -60,4 +63,7 @@ public class Paiement { public String getCommentaire() { return commentaire; } public void setCommentaire(String commentaire) { this.commentaire = commentaire; } + + public Boolean getRemis() { return remis != null ? remis : true; } + public void setRemis(Boolean remis) { this.remis = remis != null ? remis : true; } } diff --git a/as-talange-core/src/main/resources/db/migration/V43__add_remis_to_paiement.sql b/as-talange-core/src/main/resources/db/migration/V43__add_remis_to_paiement.sql new file mode 100644 index 0000000..36671be --- /dev/null +++ b/as-talange-core/src/main/resources/db/migration/V43__add_remis_to_paiement.sql @@ -0,0 +1,4 @@ +ALTER TABLE paiement ADD COLUMN IF NOT EXISTS remis BOOLEAN NOT NULL DEFAULT TRUE; + +INSERT INTO mode_paiement (nom) VALUES ('Chèque Sport mairie') +ON CONFLICT (nom) DO NOTHING; diff --git a/as-talange-core/src/test/java/com/astalange/core/LicenceReductionTest.java b/as-talange-core/src/test/java/com/astalange/core/LicenceReductionTest.java index dcc647c..0ce4252 100644 --- a/as-talange-core/src/test/java/com/astalange/core/LicenceReductionTest.java +++ b/as-talange-core/src/test/java/com/astalange/core/LicenceReductionTest.java @@ -95,4 +95,34 @@ class LicenceReductionTest { assertEquals(new BigDecimal("40.00"), licence.getMontantReduction()); assertEquals(new BigDecimal("160.00"), licence.getResteAPayer()); } + + @Test + @DisplayName("Devrait inclure le paiement dans le total payé uniquement s'il est remis") + void testPaiementRemisVsNonRemis() { + Categorie cat = new Categorie(); + cat.setTarifBase(new BigDecimal("100.00")); + + Adherent adh = new Adherent(); + adh.setResidentTalange(true); + + Licence licence = new Licence(); + licence.setCategorie(cat); + licence.setAdherent(adh); + + com.astalange.core.entity.Paiement p1 = new com.astalange.core.entity.Paiement(); + p1.setMontant(new BigDecimal("40.00")); + 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 € + assertEquals(0, BigDecimal.ZERO.compareTo(licence.getSommePayee())); + assertEquals(0, new BigDecimal("100.00").compareTo(licence.getResteAPayer())); + + // Passage à remis = true (remis/encaissé) + p1.setRemis(true); + + // La somme est déduite => reste à payer = 60.00 € + assertEquals(0, new BigDecimal("40.00").compareTo(licence.getSommePayee())); + assertEquals(0, new BigDecimal("60.00").compareTo(licence.getResteAPayer())); + } } diff --git a/as-talange-web/src/main/java/com/astalange/web/controller/PaiementController.java b/as-talange-web/src/main/java/com/astalange/web/controller/PaiementController.java index 74ee2a4..ddc24a9 100644 --- a/as-talange-web/src/main/java/com/astalange/web/controller/PaiementController.java +++ b/as-talange-web/src/main/java/com/astalange/web/controller/PaiementController.java @@ -52,6 +52,7 @@ public class PaiementController { @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement, @RequestParam Long modePaiementId, @RequestParam(required = false) String numeroCheque, + @RequestParam(required = false, defaultValue = "true") Boolean remis, @RequestParam(required = false) String commentaire, Principal principal) { @@ -60,7 +61,7 @@ public class PaiementController { ModePaiement mode = modePaiementRepository.findById(modePaiementId) .orElseThrow(() -> new IllegalArgumentException("Invalid ModePaiement ID")); - log.info(">>> [PaiementController] Demande de création de paiement reçue pour Licence ID: {}, Adhérent ID: {}, Montant: {} €", id, adherentId, montant); + 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) { @@ -75,6 +76,7 @@ public class PaiementController { paiement.setDatePaiement(datePaiement); paiement.setNumeroCheque(numeroCheque); paiement.setCommentaire(commentaire); + paiement.setRemis(Boolean.TRUE.equals(remis)); if (principal != null) { paiement.setGestionnaire(principal.getName()); } @@ -88,7 +90,7 @@ public class PaiementController { auditLog.setPaiementId(paiement.getId()); auditLog.setMontant(montant); auditLog.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom()); - auditLog.setDetails("Création d'un paiement de " + montant + "€ via " + mode.getNom() + " pour la licence " + (licence.getNumeroLicence() != null ? licence.getNumeroLicence() : "sans numéro")); + auditLog.setDetails("Création d'un paiement de " + montant + "€ via " + mode.getNom() + " (Remis: " + (Boolean.TRUE.equals(remis) ? "Oui" : "Non") + ") pour la licence " + (licence.getNumeroLicence() != null ? licence.getNumeroLicence() : "sans numéro")); auditLogPaiementRepository.save(auditLog); try { @@ -109,6 +111,7 @@ public class PaiementController { @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement, @RequestParam Long modePaiementId, @RequestParam(required = false) String numeroCheque, + @RequestParam(required = false, defaultValue = "true") Boolean remis, @RequestParam(required = false) String commentaire, @RequestParam(required = false) String redirect, Principal principal) { @@ -125,7 +128,8 @@ public class PaiementController { Licence licence = paiement.getLicence(); // Validation basique pour ne pas dépasser le montant total de la licence - BigDecimal maxAmount = licence.getResteAPayer().add(paiement.getMontant()); + BigDecimal currentContribution = Boolean.TRUE.equals(paiement.getRemis()) ? paiement.getMontant() : BigDecimal.ZERO; + BigDecimal maxAmount = licence.getResteAPayer().add(currentContribution); if (montant.compareTo(maxAmount) > 0) { montant = maxAmount; } @@ -136,6 +140,7 @@ public class PaiementController { paiement.setDatePaiement(datePaiement); paiement.setNumeroCheque(numeroCheque); paiement.setCommentaire(commentaire); + paiement.setRemis(Boolean.TRUE.equals(remis)); if (principal != null) { paiement.setGestionnaire(principal.getName()); } @@ -147,7 +152,7 @@ public class PaiementController { auditLog.setPaiementId(paiement.getId()); auditLog.setMontant(montant); auditLog.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom()); - auditLog.setDetails("Modification du paiement: montant " + ancienMontant + "€ -> " + montant + "€, mode " + ancienMode + " -> " + mode.getNom()); + auditLog.setDetails("Modification du paiement: montant " + ancienMontant + "€ -> " + montant + "€, mode " + ancienMode + " -> " + mode.getNom() + ", remis -> " + (Boolean.TRUE.equals(remis) ? "Oui" : "Non")); auditLogPaiementRepository.save(auditLog); } diff --git a/as-talange-web/src/main/resources/templates/adherents/form.html b/as-talange-web/src/main/resources/templates/adherents/form.html index 8d6e4d7..e95bcba 100644 --- a/as-talange-web/src/main/resources/templates/adherents/form.html +++ b/as-talange-web/src/main/resources/templates/adherents/form.html @@ -344,6 +344,7 @@ 01/01/2026 Chèque + Non remis
@@ -371,10 +372,11 @@ 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.montant)}" + th:data-max-amount="${lic.getResteAPayer().add(paiement.remis ? paiement.montant : 0)}" th:data-cheque="${paiement.numeroCheque}" th:data-commentaire="${paiement.commentaire}" - 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-commentaire'))" + th:data-remis="${paiement.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-commentaire'), this.getAttribute('data-remis'))" class="text-blue-600 hover:text-blue-900 bg-blue-50 hover:bg-blue-100 p-1.5 rounded transition-colors inline-flex items-center" title="Modifier"> @@ -624,7 +626,7 @@
- @@ -633,6 +635,20 @@
+
@@ -664,7 +680,7 @@
- @@ -673,6 +689,20 @@
+
@@ -816,12 +846,15 @@ document.getElementById('paiementMontant').value = maxAmount; document.getElementById('paiementMontant').max = maxAmount; document.getElementById('paiementDate').valueAsDate = new Date(); + document.getElementById('addPaiementMode').value = ''; + document.getElementById('addRemisNon').checked = true; + toggleChequeVisibility(document.getElementById('addPaiementMode'), 'addChequeBlock', 'addNumeroCheque', 'addRemisBlock', 'addRemisOui', 'addRemisNon'); } function closePaiementModal() { document.getElementById('paiementModal').classList.add('hidden'); } - function openEditPaiementModal(paiementId, montant, date, modePaiementId, licenceId, maxAmount, numeroCheque, commentaire) { + function openEditPaiementModal(paiementId, montant, date, modePaiementId, licenceId, maxAmount, numeroCheque, commentaire, remis) { document.getElementById('editPaiementModal').classList.remove('hidden'); document.getElementById('editPaiementForm').action = '/paiements/' + paiementId + '/update'; document.getElementById('editPaiementMontant').value = montant; @@ -830,18 +863,34 @@ document.getElementById('editPaiementMode').value = modePaiementId; document.getElementById('editNumeroCheque').value = numeroCheque || ''; document.getElementById('editCommentaire').value = commentaire || ''; - toggleChequeVisibility(document.getElementById('editPaiementMode'), 'editChequeBlock', 'editNumeroCheque'); + + const isRemis = (remis === true || remis === 'true' || remis === null || remis === ''); + if (isRemis) { + document.getElementById('editRemisOui').checked = true; + } else { + document.getElementById('editRemisNon').checked = true; + } + + toggleChequeVisibility(document.getElementById('editPaiementMode'), 'editChequeBlock', 'editNumeroCheque', 'editRemisBlock', 'editRemisOui', 'editRemisNon', true); } function closeEditPaiementModal() { document.getElementById('editPaiementModal').classList.add('hidden'); } - function toggleChequeVisibility(selectElement, blockId, inputId) { + function toggleChequeVisibility(selectElement, blockId, inputId, remisBlockId, remisOuiId, remisNonId, isEdit = false) { const block = document.getElementById(blockId); const input = document.getElementById(inputId); + const remisBlock = remisBlockId ? document.getElementById(remisBlockId) : null; + const remisOui = remisOuiId ? document.getElementById(remisOuiId) : null; + const remisNon = remisNonId ? document.getElementById(remisNonId) : null; + const selectedOption = selectElement.options[selectElement.selectedIndex]; - - if (selectedOption && selectedOption.getAttribute('data-nom') && selectedOption.getAttribute('data-nom').includes('chèque')) { + const dataNom = selectedOption ? (selectedOption.getAttribute('data-nom') || selectedOption.textContent || '').toLowerCase().trim() : ''; + + const isChequeSportMairie = dataNom.includes('mairie') || dataNom.includes('chèque sport') || dataNom.includes('cheque sport'); + const isStandardCheque = dataNom.includes('chèque') && !isChequeSportMairie; + + if (isStandardCheque) { block.classList.remove('hidden'); input.required = true; } else { @@ -849,6 +898,14 @@ input.required = false; input.value = ''; } + + if (isChequeSportMairie) { + if (remisBlock) remisBlock.classList.remove('hidden'); + if (!isEdit && remisNon) remisNon.checked = true; + } else { + if (remisBlock) remisBlock.classList.add('hidden'); + if (remisOui) remisOui.checked = true; + } } document.addEventListener("DOMContentLoaded", function() { const dateInput = document.getElementById('dateNaissance'); diff --git a/as-talange-web/src/main/resources/templates/paiements/list.html b/as-talange-web/src/main/resources/templates/paiements/list.html index d65c03b..ca5d2d9 100644 --- a/as-talange-web/src/main/resources/templates/paiements/list.html +++ b/as-talange-web/src/main/resources/templates/paiements/list.html @@ -89,6 +89,7 @@ Chèque + Non remis 50.00 € @@ -108,8 +109,10 @@ 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.montant)}" - 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'))" + th:data-max-amount="${p.licence.getResteAPayer().add(p.remis ? p.montant : 0)}" + 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'))" class="text-blue-600 hover:text-blue-900 bg-blue-50 hover:bg-blue-100 p-1.5 rounded transition-colors inline-flex items-center" title="Modifier"> @@ -212,11 +215,29 @@
- - +
+ +
@@ -227,17 +248,58 @@