diff --git a/as-talange-core/src/main/java/com/astalange/core/entity/AuditLogPaiement.java b/as-talange-core/src/main/java/com/astalange/core/entity/AuditLogPaiement.java new file mode 100644 index 0000000..436645f --- /dev/null +++ b/as-talange-core/src/main/java/com/astalange/core/entity/AuditLogPaiement.java @@ -0,0 +1,104 @@ +package com.astalange.core.entity; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.math.BigDecimal; + +@Entity +@Table(name = "audit_log_paiement") +public class AuditLogPaiement { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String action; // CREATE, UPDATE, DELETE + + @Column(nullable = false) + private LocalDateTime dateAction; + + @Column(nullable = false) + private String utilisateur; // Username or 'Système' + + @Column(nullable = true) + private Long paiementId; // Can be null if the payment is completely deleted or we just want to keep track + + @Column(nullable = false, length = 1000) + private String details; + + @Column(nullable = true) + private BigDecimal montant; + + @Column(nullable = true) + private String adherentNomComplet; + + @PrePersist + protected void onCreate() { + this.dateAction = LocalDateTime.now(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public LocalDateTime getDateAction() { + return dateAction; + } + + public void setDateAction(LocalDateTime dateAction) { + this.dateAction = dateAction; + } + + public String getUtilisateur() { + return utilisateur; + } + + public void setUtilisateur(String utilisateur) { + this.utilisateur = utilisateur; + } + + public Long getPaiementId() { + return paiementId; + } + + public void setPaiementId(Long paiementId) { + this.paiementId = paiementId; + } + + public String getDetails() { + return details; + } + + public void setDetails(String details) { + this.details = details; + } + + public BigDecimal getMontant() { + return montant; + } + + public void setMontant(BigDecimal montant) { + this.montant = montant; + } + + public String getAdherentNomComplet() { + return adherentNomComplet; + } + + public void setAdherentNomComplet(String adherentNomComplet) { + this.adherentNomComplet = adherentNomComplet; + } +} diff --git a/as-talange-core/src/main/java/com/astalange/core/repository/AuditLogPaiementRepository.java b/as-talange-core/src/main/java/com/astalange/core/repository/AuditLogPaiementRepository.java new file mode 100644 index 0000000..098bf7c --- /dev/null +++ b/as-talange-core/src/main/java/com/astalange/core/repository/AuditLogPaiementRepository.java @@ -0,0 +1,26 @@ +package com.astalange.core.repository; + +import com.astalange.core.entity.AuditLogPaiement; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +@Repository +public interface AuditLogPaiementRepository extends JpaRepository { + List findAllByOrderByDateActionDesc(); + + @Query("SELECT a FROM AuditLogPaiement a WHERE " + + "(:action IS NULL OR :action = '' OR a.action = :action) AND " + + "(:utilisateur IS NULL OR :utilisateur = '' OR LOWER(a.utilisateur) LIKE LOWER(CONCAT('%', :utilisateur, '%'))) AND " + + "(:adherent IS NULL OR :adherent = '' OR LOWER(a.adherentNomComplet) LIKE LOWER(CONCAT('%', :adherent, '%'))) " + + "ORDER BY a.dateAction DESC") + List findByFilters( + @Param("action") String action, + @Param("utilisateur") String utilisateur, + @Param("adherent") String adherent + ); +} diff --git a/as-talange-core/src/main/resources/db/migration/V25__create_audit_log_paiement.sql b/as-talange-core/src/main/resources/db/migration/V25__create_audit_log_paiement.sql new file mode 100644 index 0000000..dfb2abd --- /dev/null +++ b/as-talange-core/src/main/resources/db/migration/V25__create_audit_log_paiement.sql @@ -0,0 +1,10 @@ +CREATE TABLE audit_log_paiement ( + id BIGSERIAL PRIMARY KEY, + action VARCHAR(50) NOT NULL, + date_action TIMESTAMP NOT NULL, + utilisateur VARCHAR(255) NOT NULL, + paiement_id BIGINT, + details VARCHAR(1000) NOT NULL, + montant DECIMAL(10, 2), + adherent_nom_complet VARCHAR(255) +); diff --git a/as-talange-web/src/main/java/com/astalange/web/config/SecurityConfig.java b/as-talange-web/src/main/java/com/astalange/web/config/SecurityConfig.java index 09f0191..0872131 100644 --- a/as-talange-web/src/main/java/com/astalange/web/config/SecurityConfig.java +++ b/as-talange-web/src/main/java/com/astalange/web/config/SecurityConfig.java @@ -27,8 +27,8 @@ public class SecurityConfig { .authorizeHttpRequests(auth -> auth .requestMatchers("/css/**", "/js/**", "/images/**", "/inscription-public/**").permitAll() .requestMatchers("/change-password").authenticated() - .requestMatchers("/utilisateurs/**", "/saisons/**", "/categories/**", "/equipes/**", "/modespaiement/**", "/equipements/**").hasRole("ADMIN") - .requestMatchers("/paiements/**", "/paiement/**").hasAnyRole("ADMIN", "TRESORERIE") + .requestMatchers("/utilisateurs/**", "/saisons/**", "/categories/**", "/equipes/**", "/modespaiement/**", "/equipements/**", "/admin/logs/**").hasRole("ADMIN") + .requestMatchers("/paiements/**", "/paiement/**").hasAnyRole("ADMIN", "TRESORERIE", "AGENT_SAISIE") .requestMatchers("/planning/**").hasAnyRole("ADMIN", "AGENT_SAISIE") .requestMatchers("/adherents/**", "/dotations/**").hasAnyRole("ADMIN", "TRESORERIE", "AGENT_SAISIE") .anyRequest().authenticated() diff --git a/as-talange-web/src/main/java/com/astalange/web/controller/AuditLogPaiementController.java b/as-talange-web/src/main/java/com/astalange/web/controller/AuditLogPaiementController.java new file mode 100644 index 0000000..2e2d51d --- /dev/null +++ b/as-talange-web/src/main/java/com/astalange/web/controller/AuditLogPaiementController.java @@ -0,0 +1,34 @@ +package com.astalange.web.controller; + +import com.astalange.core.repository.AuditLogPaiementRepository; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +@RequestMapping("/admin/logs/paiements") +public class AuditLogPaiementController { + + private final AuditLogPaiementRepository auditLogPaiementRepository; + + public AuditLogPaiementController(AuditLogPaiementRepository auditLogPaiementRepository) { + this.auditLogPaiementRepository = auditLogPaiementRepository; + } + + @GetMapping + public String listLogs( + @RequestParam(required = false) String action, + @RequestParam(required = false) String utilisateur, + @RequestParam(required = false) String adherent, + Model model) { + + model.addAttribute("logs", auditLogPaiementRepository.findByFilters(action, utilisateur, adherent)); + model.addAttribute("paramAction", action); + model.addAttribute("paramUtilisateur", utilisateur); + model.addAttribute("paramAdherent", adherent); + + return "audit/paiements_logs"; + } +} 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 4b5981c..c5eacb9 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 @@ -3,6 +3,8 @@ package com.astalange.web.controller; import com.astalange.core.entity.Licence; import com.astalange.core.entity.ModePaiement; import com.astalange.core.entity.Paiement; +import com.astalange.core.entity.AuditLogPaiement; +import com.astalange.core.repository.AuditLogPaiementRepository; import com.astalange.core.repository.LicenceRepository; import com.astalange.core.repository.ModePaiementRepository; import com.astalange.core.repository.PaiementRepository; @@ -22,11 +24,13 @@ public class PaiementController { private final LicenceRepository licenceRepository; private final ModePaiementRepository modePaiementRepository; private final PaiementRepository paiementRepository; + private final AuditLogPaiementRepository auditLogPaiementRepository; - public PaiementController(LicenceRepository licenceRepository, ModePaiementRepository modePaiementRepository, PaiementRepository paiementRepository) { + public PaiementController(LicenceRepository licenceRepository, ModePaiementRepository modePaiementRepository, PaiementRepository paiementRepository, AuditLogPaiementRepository auditLogPaiementRepository) { this.licenceRepository = licenceRepository; this.modePaiementRepository = modePaiementRepository; this.paiementRepository = paiementRepository; + this.auditLogPaiementRepository = auditLogPaiementRepository; } @PostMapping("/licences/{id}/paiements") @@ -58,6 +62,15 @@ 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); } return "redirect:/adherents/" + adherentId + "/edit"; @@ -78,6 +91,9 @@ public class PaiementController { ModePaiement mode = modePaiementRepository.findById(modePaiementId) .orElseThrow(() -> new IllegalArgumentException("Invalid ModePaiement ID")); + BigDecimal ancienMontant = paiement.getMontant(); + String ancienMode = paiement.getModePaiement().getNom(); + Licence licence = paiement.getLicence(); // Validation basique pour ne pas dépasser le montant total de la licence @@ -94,6 +110,15 @@ public class PaiementController { paiement.setGestionnaire(principal.getName()); } 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); } if (redirect != null && !redirect.isEmpty()) { @@ -105,12 +130,23 @@ public class PaiementController { @PostMapping("/paiements/{id}/delete") public String deletePaiement( @PathVariable Long id, - @RequestParam(required = false) String redirect) { + @RequestParam(required = false) String redirect, + Principal principal) { Paiement paiement = paiementRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Invalid Paiement ID")); 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); + paiementRepository.delete(paiement); if (redirect != null && !redirect.isEmpty()) { diff --git a/as-talange-web/src/main/resources/templates/audit/paiements_logs.html b/as-talange-web/src/main/resources/templates/audit/paiements_logs.html new file mode 100644 index 0000000..1858dd1 --- /dev/null +++ b/as-talange-web/src/main/resources/templates/audit/paiements_logs.html @@ -0,0 +1,93 @@ + + + + + Logs Paiements - AS Talange + + + + + + + +
+ + +
+ +
+

Audit Logs des Paiements

+
+ + +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
DateActionUtilisateurAdhérentDétails
Aucun log enregistré.
01/01/2026 10:00:00 + CREATE + Jean DupontJohn DoeDétails du changement
+
+
+
+
+ + + diff --git a/as-talange-web/src/main/resources/templates/fragments/sidebar.html b/as-talange-web/src/main/resources/templates/fragments/sidebar.html index 5d70712..87a9e62 100644 --- a/as-talange-web/src/main/resources/templates/fragments/sidebar.html +++ b/as-talange-web/src/main/resources/templates/fragments/sidebar.html @@ -43,6 +43,7 @@
Administration
Utilisateurs + Logs Paiements