feat(paiement): gestion specifique du mode Cheque Sport mairie (masquage num cheque, option remis oui/non, calcul reste a payer)

This commit is contained in:
2026-08-08 00:08:50 +02:00
parent 76e54de182
commit 2f68af6fb5
7 changed files with 184 additions and 18 deletions
@@ -141,6 +141,7 @@ public class Licence {
} }
BigDecimal totalPaye = paiements.stream() BigDecimal totalPaye = paiements.stream()
.filter(p -> p.getRemis() == null || Boolean.TRUE.equals(p.getRemis()))
.map(Paiement::getMontant) .map(Paiement::getMontant)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -154,6 +155,7 @@ public class Licence {
return BigDecimal.ZERO; return BigDecimal.ZERO;
} }
return paiements.stream() return paiements.stream()
.filter(p -> p.getRemis() == null || Boolean.TRUE.equals(p.getRemis()))
.map(Paiement::getMontant) .map(Paiement::getMontant)
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
} }
@@ -35,6 +35,9 @@ public class Paiement {
@Column(name = "commentaire", columnDefinition = "TEXT") @Column(name = "commentaire", columnDefinition = "TEXT")
private String commentaire; private String commentaire;
@Column(name = "remis", nullable = false)
private Boolean remis = true;
// Getters and Setters // Getters and Setters
public Long getId() { return id; } public Long getId() { return id; }
@@ -60,4 +63,7 @@ public class Paiement {
public String getCommentaire() { return commentaire; } public String getCommentaire() { return commentaire; }
public void setCommentaire(String commentaire) { this.commentaire = 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; }
} }
@@ -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;
@@ -95,4 +95,34 @@ class LicenceReductionTest {
assertEquals(new BigDecimal("40.00"), licence.getMontantReduction()); assertEquals(new BigDecimal("40.00"), licence.getMontantReduction());
assertEquals(new BigDecimal("160.00"), licence.getResteAPayer()); 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()));
}
} }
@@ -52,6 +52,7 @@ public class PaiementController {
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement,
@RequestParam Long modePaiementId, @RequestParam Long modePaiementId,
@RequestParam(required = false) String numeroCheque, @RequestParam(required = false) String numeroCheque,
@RequestParam(required = false, defaultValue = "true") Boolean remis,
@RequestParam(required = false) String commentaire, @RequestParam(required = false) String commentaire,
Principal principal) { Principal principal) {
@@ -60,7 +61,7 @@ public class PaiementController {
ModePaiement mode = modePaiementRepository.findById(modePaiementId) ModePaiement mode = modePaiementRepository.findById(modePaiementId)
.orElseThrow(() -> new IllegalArgumentException("Invalid ModePaiement ID")); .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 // Validation basique pour ne pas payer plus que le reste à payer
if (montant.compareTo(licence.getResteAPayer()) > 0) { if (montant.compareTo(licence.getResteAPayer()) > 0) {
@@ -75,6 +76,7 @@ public class PaiementController {
paiement.setDatePaiement(datePaiement); paiement.setDatePaiement(datePaiement);
paiement.setNumeroCheque(numeroCheque); paiement.setNumeroCheque(numeroCheque);
paiement.setCommentaire(commentaire); paiement.setCommentaire(commentaire);
paiement.setRemis(Boolean.TRUE.equals(remis));
if (principal != null) { if (principal != null) {
paiement.setGestionnaire(principal.getName()); paiement.setGestionnaire(principal.getName());
} }
@@ -88,7 +90,7 @@ public class PaiementController {
auditLog.setPaiementId(paiement.getId()); auditLog.setPaiementId(paiement.getId());
auditLog.setMontant(montant); auditLog.setMontant(montant);
auditLog.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom()); 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); auditLogPaiementRepository.save(auditLog);
try { try {
@@ -109,6 +111,7 @@ public class PaiementController {
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate datePaiement,
@RequestParam Long modePaiementId, @RequestParam Long modePaiementId,
@RequestParam(required = false) String numeroCheque, @RequestParam(required = false) String numeroCheque,
@RequestParam(required = false, defaultValue = "true") Boolean remis,
@RequestParam(required = false) String commentaire, @RequestParam(required = false) String commentaire,
@RequestParam(required = false) String redirect, @RequestParam(required = false) String redirect,
Principal principal) { Principal principal) {
@@ -125,7 +128,8 @@ public class PaiementController {
Licence licence = paiement.getLicence(); Licence licence = paiement.getLicence();
// Validation basique pour ne pas dépasser le montant total de la licence // 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) { if (montant.compareTo(maxAmount) > 0) {
montant = maxAmount; montant = maxAmount;
} }
@@ -136,6 +140,7 @@ public class PaiementController {
paiement.setDatePaiement(datePaiement); paiement.setDatePaiement(datePaiement);
paiement.setNumeroCheque(numeroCheque); paiement.setNumeroCheque(numeroCheque);
paiement.setCommentaire(commentaire); paiement.setCommentaire(commentaire);
paiement.setRemis(Boolean.TRUE.equals(remis));
if (principal != null) { if (principal != null) {
paiement.setGestionnaire(principal.getName()); paiement.setGestionnaire(principal.getName());
} }
@@ -147,7 +152,7 @@ public class PaiementController {
auditLog.setPaiementId(paiement.getId()); auditLog.setPaiementId(paiement.getId());
auditLog.setMontant(montant); auditLog.setMontant(montant);
auditLog.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom()); 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); auditLogPaiementRepository.save(auditLog);
} }
@@ -344,6 +344,7 @@
<td class="py-2 px-3 text-gray-600 font-medium" th:text="${#temporals.format(paiement.datePaiement, 'dd/MM/yyyy')}">01/01/2026</td> <td class="py-2 px-3 text-gray-600 font-medium" th:text="${#temporals.format(paiement.datePaiement, 'dd/MM/yyyy')}">01/01/2026</td>
<td class="py-2 px-3 text-gray-600"> <td class="py-2 px-3 text-gray-600">
<span class="px-2 py-0.5 rounded bg-gray-100 text-gray-700 font-mono text-[10px]" th:text="${paiement.modePaiement.nom}">Chèque</span> <span class="px-2 py-0.5 rounded bg-gray-100 text-gray-700 font-mono text-[10px]" th:text="${paiement.modePaiement.nom}">Chèque</span>
<span th:if="${paiement.remis == false}" class="px-1.5 py-0.5 rounded bg-amber-100 text-amber-800 text-[10px] font-semibold ml-1" title="Montant non encore déduit du solde">Non remis</span>
</td> </td>
<td class="py-2 px-3 text-gray-500 text-xs"> <td class="py-2 px-3 text-gray-500 text-xs">
<div th:if="${paiement.numeroCheque != null and !paiement.numeroCheque.isEmpty()}" class="text-[10px]"> <div th:if="${paiement.numeroCheque != null and !paiement.numeroCheque.isEmpty()}" class="text-[10px]">
@@ -371,10 +372,11 @@
th:data-date="${paiement.datePaiement}" th:data-date="${paiement.datePaiement}"
th:data-mode-id="${paiement.modePaiement.id}" th:data-mode-id="${paiement.modePaiement.id}"
th:data-licence-id="${lic.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-cheque="${paiement.numeroCheque}"
th:data-commentaire="${paiement.commentaire}" 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" 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"> title="Modifier">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -624,7 +626,7 @@
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label> <label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label>
<select id="addPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none" onchange="toggleChequeVisibility(this, 'addChequeBlock', 'addNumeroCheque')"> <select id="addPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none" onchange="toggleChequeVisibility(this, 'addChequeBlock', 'addNumeroCheque', 'addRemisBlock', 'addRemisOui', 'addRemisNon')">
<option value="">Sélectionnez un mode...</option> <option value="">Sélectionnez un mode...</option>
<option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:data-nom="${#strings.toLowerCase(mode.nom)}" th:text="${mode.nom}"></option> <option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:data-nom="${#strings.toLowerCase(mode.nom)}" th:text="${mode.nom}"></option>
</select> </select>
@@ -633,6 +635,20 @@
<label class="block text-sm font-medium text-gray-700 mb-1">Numéro du chèque <span class="text-red-500">*</span></label> <label class="block text-sm font-medium text-gray-700 mb-1">Numéro du chèque <span class="text-red-500">*</span></label>
<input type="text" id="addNumeroCheque" name="numeroCheque" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"> <input type="text" id="addNumeroCheque" name="numeroCheque" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
</div> </div>
<div id="addRemisBlock" class="hidden bg-amber-50 p-3 rounded-lg border border-amber-200">
<label class="block text-xs font-semibold text-amber-900 mb-1.5">Remis / Encaissé ?</label>
<div class="flex items-center space-x-6">
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="addRemisOui" name="remis" value="true" class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Oui</span>
</label>
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="addRemisNon" name="remis" value="false" checked class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Non</span>
</label>
</div>
<p class="text-[11px] text-amber-700 mt-1">Si "Non", cette somme reste à payer.</p>
</div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Commentaire (facultatif)</label> <label class="block text-sm font-medium text-gray-700 mb-1">Commentaire (facultatif)</label>
<textarea id="addCommentaire" name="commentaire" rows="2" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea> <textarea id="addCommentaire" name="commentaire" rows="2" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea>
@@ -664,7 +680,7 @@
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label> <label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label>
<select id="editPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none" onchange="toggleChequeVisibility(this, 'editChequeBlock', 'editNumeroCheque')"> <select id="editPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none" onchange="toggleChequeVisibility(this, 'editChequeBlock', 'editNumeroCheque', 'editRemisBlock', 'editRemisOui', 'editRemisNon', true)">
<option value="">Sélectionnez un mode...</option> <option value="">Sélectionnez un mode...</option>
<option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:data-nom="${#strings.toLowerCase(mode.nom)}" th:text="${mode.nom}"></option> <option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:data-nom="${#strings.toLowerCase(mode.nom)}" th:text="${mode.nom}"></option>
</select> </select>
@@ -673,6 +689,20 @@
<label class="block text-sm font-medium text-gray-700 mb-1">Numéro du chèque <span class="text-red-500">*</span></label> <label class="block text-sm font-medium text-gray-700 mb-1">Numéro du chèque <span class="text-red-500">*</span></label>
<input type="text" id="editNumeroCheque" name="numeroCheque" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"> <input type="text" id="editNumeroCheque" name="numeroCheque" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
</div> </div>
<div id="editRemisBlock" class="hidden bg-amber-50 p-3 rounded-lg border border-amber-200">
<label class="block text-xs font-semibold text-amber-900 mb-1.5">Remis / Encaissé ?</label>
<div class="flex items-center space-x-6">
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="editRemisOui" name="remis" value="true" class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Oui</span>
</label>
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="editRemisNon" name="remis" value="false" class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Non</span>
</label>
</div>
<p class="text-[11px] text-amber-700 mt-1">Si "Non", cette somme reste à payer.</p>
</div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Commentaire (facultatif)</label> <label class="block text-sm font-medium text-gray-700 mb-1">Commentaire (facultatif)</label>
<textarea id="editCommentaire" name="commentaire" rows="2" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea> <textarea id="editCommentaire" name="commentaire" rows="2" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea>
@@ -816,12 +846,15 @@
document.getElementById('paiementMontant').value = maxAmount; document.getElementById('paiementMontant').value = maxAmount;
document.getElementById('paiementMontant').max = maxAmount; document.getElementById('paiementMontant').max = maxAmount;
document.getElementById('paiementDate').valueAsDate = new Date(); 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() { function closePaiementModal() {
document.getElementById('paiementModal').classList.add('hidden'); 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('editPaiementModal').classList.remove('hidden');
document.getElementById('editPaiementForm').action = '/paiements/' + paiementId + '/update'; document.getElementById('editPaiementForm').action = '/paiements/' + paiementId + '/update';
document.getElementById('editPaiementMontant').value = montant; document.getElementById('editPaiementMontant').value = montant;
@@ -830,18 +863,34 @@
document.getElementById('editPaiementMode').value = modePaiementId; document.getElementById('editPaiementMode').value = modePaiementId;
document.getElementById('editNumeroCheque').value = numeroCheque || ''; document.getElementById('editNumeroCheque').value = numeroCheque || '';
document.getElementById('editCommentaire').value = commentaire || ''; 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() { function closeEditPaiementModal() {
document.getElementById('editPaiementModal').classList.add('hidden'); 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 block = document.getElementById(blockId);
const input = document.getElementById(inputId); 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]; const selectedOption = selectElement.options[selectElement.selectedIndex];
const dataNom = selectedOption ? (selectedOption.getAttribute('data-nom') || selectedOption.textContent || '').toLowerCase().trim() : '';
if (selectedOption && selectedOption.getAttribute('data-nom') && selectedOption.getAttribute('data-nom').includes('chèque')) {
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'); block.classList.remove('hidden');
input.required = true; input.required = true;
} else { } else {
@@ -849,6 +898,14 @@
input.required = false; input.required = false;
input.value = ''; 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() { document.addEventListener("DOMContentLoaded", function() {
const dateInput = document.getElementById('dateNaissance'); const dateInput = document.getElementById('dateNaissance');
@@ -89,6 +89,7 @@
<td class="py-4 px-6 text-gray-600"> <td class="py-4 px-6 text-gray-600">
<span class="px-2 py-0.5 rounded border border-gray-200 bg-gray-50 text-gray-700 text-xs" <span class="px-2 py-0.5 rounded border border-gray-200 bg-gray-50 text-gray-700 text-xs"
th:text="${p.modePaiement.nom}">Chèque</span> th:text="${p.modePaiement.nom}">Chèque</span>
<span th:if="${p.remis == false}" class="px-1.5 py-0.5 rounded bg-amber-100 text-amber-800 text-xs font-semibold ml-1">Non remis</span>
</td> </td>
<td class="py-4 px-6 text-right font-semibold text-green-600" th:text="${p.montant + ' €'}">50.00 €</td> <td class="py-4 px-6 text-right font-semibold text-green-600" th:text="${p.montant + ' €'}">50.00 €</td>
<td class="py-4 px-6 text-right pr-6"> <td class="py-4 px-6 text-right pr-6">
@@ -108,8 +109,10 @@
th:data-date="${p.datePaiement}" th:data-date="${p.datePaiement}"
th:data-mode-id="${p.modePaiement.id}" th:data-mode-id="${p.modePaiement.id}"
th:data-licence-id="${p.licence.id}" th:data-licence-id="${p.licence.id}"
th:data-max-amount="${p.licence.getResteAPayer().add(p.montant)}" th:data-max-amount="${p.licence.getResteAPayer().add(p.remis ? p.montant : 0)}"
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-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" 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"> title="Modifier">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -212,11 +215,29 @@
</div> </div>
<div> <div>
<label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label> <label class="block text-sm font-medium text-gray-700 mb-1">Mode de paiement</label>
<select id="editPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"> <select id="editPaiementMode" name="modePaiementId" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none" onchange="toggleChequeVisibility(this, 'editChequeBlock', 'editNumeroCheque', 'editRemisBlock', 'editRemisOui', 'editRemisNon', true)">
<option value="">Sélectionnez un mode...</option> <option value="">Sélectionnez un mode...</option>
<option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:text="${mode.nom}"></option> <option th:each="mode : ${modesPaiement}" th:value="${mode.id}" th:data-nom="${#strings.toLowerCase(mode.nom)}" th:text="${mode.nom}"></option>
</select> </select>
</div> </div>
<div id="editChequeBlock" class="hidden">
<label class="block text-sm font-medium text-gray-700 mb-1">Numéro du chèque <span class="text-red-500">*</span></label>
<input type="text" id="editNumeroCheque" name="numeroCheque" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
</div>
<div id="editRemisBlock" class="hidden bg-amber-50 p-3 rounded-lg border border-amber-200">
<label class="block text-xs font-semibold text-amber-900 mb-1.5">Remis / Encaissé ?</label>
<div class="flex items-center space-x-6">
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="editRemisOui" name="remis" value="true" class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Oui</span>
</label>
<label class="flex items-center space-x-1.5 cursor-pointer text-sm text-gray-700">
<input type="radio" id="editRemisNon" name="remis" value="false" class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500">
<span>Non</span>
</label>
</div>
<p class="text-[11px] text-amber-700 mt-1">Si "Non", cette somme reste à payer.</p>
</div>
<div class="items-center px-4 py-3 flex justify-end space-x-2"> <div class="items-center px-4 py-3 flex justify-end space-x-2">
<button type="button" onclick="closeEditPaiementModal()" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">Annuler</button> <button type="button" onclick="closeEditPaiementModal()" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50">Annuler</button>
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">Enregistrer</button> <button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">Enregistrer</button>
@@ -227,17 +248,58 @@
</div> </div>
<script> <script>
function openEditPaiementModal(paiementId, montant, date, modePaiementId, licenceId, maxAmount) { function openEditPaiementModal(paiementId, montant, date, modePaiementId, licenceId, maxAmount, numeroCheque, remis) {
document.getElementById('editPaiementModal').classList.remove('hidden'); document.getElementById('editPaiementModal').classList.remove('hidden');
document.getElementById('editPaiementForm').action = '/paiements/' + paiementId + '/update'; document.getElementById('editPaiementForm').action = '/paiements/' + paiementId + '/update';
document.getElementById('editPaiementMontant').value = montant; document.getElementById('editPaiementMontant').value = montant;
document.getElementById('editPaiementMontant').max = maxAmount; document.getElementById('editPaiementMontant').max = maxAmount;
document.getElementById('editPaiementDate').value = date; document.getElementById('editPaiementDate').value = date;
document.getElementById('editPaiementMode').value = modePaiementId; document.getElementById('editPaiementMode').value = modePaiementId;
document.getElementById('editNumeroCheque').value = numeroCheque || '';
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() { function closeEditPaiementModal() {
document.getElementById('editPaiementModal').classList.add('hidden'); document.getElementById('editPaiementModal').classList.add('hidden');
} }
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];
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 {
block.classList.add('hidden');
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;
}
}
</script> </script>
</body> </body>
</html> </html>