feat: add payment confirmation email service and resend actions

This commit is contained in:
2026-08-06 22:09:44 +02:00
parent 2b2025a78f
commit 9dcd12872a
7 changed files with 540 additions and 26 deletions
@@ -4,10 +4,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication(scanBasePackages = "com.astalange")
@EntityScan(basePackages = "com.astalange.core.entity")
@EnableJpaRepositories(basePackages = "com.astalange.core.repository")
@EnableAsync
public class AsTalangeApplication {
public static void main(String[] args) {
SpringApplication.run(AsTalangeApplication.class, args);
@@ -8,6 +8,9 @@ import com.astalange.core.repository.AuditLogPaiementRepository;
import com.astalange.core.repository.LicenceRepository;
import com.astalange.core.repository.ModePaiementRepository;
import com.astalange.core.repository.PaiementRepository;
import com.astalange.core.service.PaiementEmailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
@@ -21,16 +24,24 @@ import java.time.LocalDate;
@Controller
public class PaiementController {
private static final Logger log = LoggerFactory.getLogger(PaiementController.class);
private final LicenceRepository licenceRepository;
private final ModePaiementRepository modePaiementRepository;
private final PaiementRepository paiementRepository;
private final AuditLogPaiementRepository auditLogPaiementRepository;
private final PaiementEmailService paiementEmailService;
public PaiementController(LicenceRepository licenceRepository, ModePaiementRepository modePaiementRepository, PaiementRepository paiementRepository, AuditLogPaiementRepository auditLogPaiementRepository) {
public PaiementController(LicenceRepository licenceRepository,
ModePaiementRepository modePaiementRepository,
PaiementRepository paiementRepository,
AuditLogPaiementRepository auditLogPaiementRepository,
PaiementEmailService paiementEmailService) {
this.licenceRepository = licenceRepository;
this.modePaiementRepository = modePaiementRepository;
this.paiementRepository = paiementRepository;
this.auditLogPaiementRepository = auditLogPaiementRepository;
this.paiementEmailService = paiementEmailService;
}
@PostMapping("/licences/{id}/paiements")
@@ -49,6 +60,8 @@ 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);
// Validation basique pour ne pas payer plus que le reste à payer
if (montant.compareTo(licence.getResteAPayer()) > 0) {
montant = licence.getResteAPayer();
@@ -66,15 +79,24 @@ public class PaiementController {
paiement.setGestionnaire(principal.getName());
}
paiementRepository.save(paiement);
AuditLogPaiement log = new AuditLogPaiement();
log.setAction("CREATE");
log.setUtilisateur(principal != null ? principal.getName() : "Système");
log.setPaiementId(paiement.getId());
log.setMontant(montant);
log.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom());
log.setDetails("Création d'un paiement de " + montant + "€ via " + mode.getNom() + " pour la licence " + (licence.getNumeroLicence() != null ? licence.getNumeroLicence() : "sans numéro"));
auditLogPaiementRepository.save(log);
licence.addPaiement(paiement);
AuditLogPaiement auditLog = new AuditLogPaiement();
auditLog.setAction("CREATE");
auditLog.setUtilisateur(principal != null ? principal.getName() : "Système");
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"));
auditLogPaiementRepository.save(auditLog);
try {
log.info("Appel de sendPaiementConfirmation depuis addPaiement pour le paiement ID: {}", paiement.getId());
paiementEmailService.sendPaiementConfirmation(paiement);
} catch (Exception e) {
log.error("Erreur lors de l'appel à sendPaiementConfirmation: ", e);
}
}
return "redirect:/adherents/" + adherentId + "/edit";
@@ -119,14 +141,14 @@ public class PaiementController {
}
paiementRepository.save(paiement);
AuditLogPaiement log = new AuditLogPaiement();
log.setAction("UPDATE");
log.setUtilisateur(principal != null ? principal.getName() : "Système");
log.setPaiementId(paiement.getId());
log.setMontant(montant);
log.setAdherentNomComplet(licence.getAdherent().getNom() + " " + licence.getAdherent().getPrenom());
log.setDetails("Modification du paiement: montant " + ancienMontant + "€ -> " + montant + "€, mode " + ancienMode + " -> " + mode.getNom());
auditLogPaiementRepository.save(log);
AuditLogPaiement auditLog = new AuditLogPaiement();
auditLog.setAction("UPDATE");
auditLog.setUtilisateur(principal != null ? principal.getName() : "Système");
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());
auditLogPaiementRepository.save(auditLog);
}
if (redirect != null && !redirect.isEmpty()) {
@@ -146,14 +168,14 @@ public class PaiementController {
Long adherentId = paiement.getLicence().getAdherent().getId();
AuditLogPaiement log = new AuditLogPaiement();
log.setAction("DELETE");
log.setUtilisateur(principal != null ? principal.getName() : "Système");
log.setPaiementId(paiement.getId());
log.setMontant(paiement.getMontant());
log.setAdherentNomComplet(paiement.getLicence().getAdherent().getNom() + " " + paiement.getLicence().getAdherent().getPrenom());
log.setDetails("Suppression du paiement de " + paiement.getMontant() + "€ via " + paiement.getModePaiement().getNom());
auditLogPaiementRepository.save(log);
AuditLogPaiement auditLog = new AuditLogPaiement();
auditLog.setAction("DELETE");
auditLog.setUtilisateur(principal != null ? principal.getName() : "Système");
auditLog.setPaiementId(paiement.getId());
auditLog.setMontant(paiement.getMontant());
auditLog.setAdherentNomComplet(paiement.getLicence().getAdherent().getNom() + " " + paiement.getLicence().getAdherent().getPrenom());
auditLog.setDetails("Suppression du paiement de " + paiement.getMontant() + "€ via " + paiement.getModePaiement().getNom());
auditLogPaiementRepository.save(auditLog);
paiementRepository.delete(paiement);
@@ -162,4 +184,27 @@ public class PaiementController {
}
return "redirect:/adherents/" + adherentId + "/edit";
}
@PostMapping("/paiements/{id}/resend-email")
public String resendPaiementEmail(
@PathVariable Long id,
@RequestParam(required = false) String redirect) {
log.info(">>> [PaiementController] Demande de renvoi d'e-mail reçue pour le paiement ID: {}", id);
Paiement paiement = paiementRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid Paiement ID"));
try {
log.info("Appel de sendPaiementConfirmation depuis resendPaiementEmail pour le paiement ID: {}", id);
paiementEmailService.sendPaiementConfirmation(paiement);
} catch (Exception e) {
log.error("Erreur lors du renvoi de l'e-mail pour le paiement ID {}: ", id, e);
}
if (redirect != null && !redirect.isEmpty()) {
return "redirect:" + redirect;
}
return "redirect:/adherents/" + paiement.getLicence().getAdherent().getId() + "/edit";
}
}
@@ -327,6 +327,14 @@
<td class="py-2 px-3 text-right font-semibold text-green-600" th:text="${paiement.montant + ' €'}">50.00 €</td>
<td class="py-2 px-3 text-right pr-4">
<div class="flex justify-end items-center space-x-2">
<form th:action="@{/paiements/{id}/resend-email(id=${paiement.id})}" method="post" class="inline m-0" onsubmit="return confirm('Renvoyer l\'e-mail de reçu de paiement à l\'adhérent ?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit" class="text-emerald-600 hover:text-emerald-900 bg-emerald-50 hover:bg-emerald-100 p-1.5 rounded transition-colors inline-flex items-center" title="Renvoyer le reçu par e-mail">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
</button>
</form>
<button type="button"
th:data-paiement-id="${paiement.id}"
th:data-montant="${paiement.montant}"
@@ -93,6 +93,15 @@
<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">
<div class="flex justify-end items-center space-x-2">
<form th:action="@{/paiements/{id}/resend-email(id=${p.id})}" method="post" class="inline m-0" onsubmit="return confirm('Renvoyer l\'e-mail de reçu de paiement à l\'adhérent ?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<input type="hidden" name="redirect" value="/paiements" />
<button type="submit" class="text-emerald-600 hover:text-emerald-900 bg-emerald-50 hover:bg-emerald-100 p-1.5 rounded transition-colors inline-flex items-center" title="Renvoyer le reçu par e-mail">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
</button>
</form>
<button type="button"
th:data-paiement-id="${p.id}"
th:data-montant="${p.montant}"