From 6539e33f08e347b8110a976b4b60d5c854cc7dd6 Mon Sep 17 00:00:00 2001 From: Youssef Date: Sun, 9 Aug 2026 00:44:51 +0200 Subject: [PATCH] feat: ajouter les exports CSV individuels des equipements commandes pour le flocage (initiales et prenom/numero) --- .../com/astalange/core/entity/Dotation.java | 13 ++ .../core/repository/DotationRepository.java | 2 + .../core/service/DotationService.java | 101 ++++++++++++++ ..._add_flocage_export_fields_to_dotation.sql | 2 + .../core/service/DotationServiceTest.java | 123 ++++++++++++++++++ .../web/controller/DotationController.java | 31 +++++ .../parametrage/equipements_recherche.html | 39 ++++-- 7 files changed, 302 insertions(+), 9 deletions(-) create mode 100644 as-talange-core/src/main/resources/db/migration/V44__add_flocage_export_fields_to_dotation.sql create mode 100644 as-talange-core/src/test/java/com/astalange/core/service/DotationServiceTest.java diff --git a/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java b/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java index bd5c475..5e3de4e 100644 --- a/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java +++ b/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java @@ -42,6 +42,12 @@ public class Dotation { @Column(name = "date_commande") private java.time.LocalDateTime dateCommande; + @Column(name = "flocage_exporte", nullable = false) + private Boolean flocageExporte = false; + + @Column(name = "date_flocage") + private java.time.LocalDateTime dateFlocage; + // Getters and Setters public Long getId() { return id; } @@ -77,6 +83,13 @@ public class Dotation { public java.time.LocalDateTime getDateCommande() { return dateCommande; } public void setDateCommande(java.time.LocalDateTime dateCommande) { this.dateCommande = dateCommande; } + public Boolean getFlocageExporte() { return flocageExporte; } + public void setFlocageExporte(Boolean flocageExporte) { this.flocageExporte = flocageExporte; } + + public java.time.LocalDateTime getDateFlocage() { return dateFlocage; } + public void setDateFlocage(java.time.LocalDateTime dateFlocage) { this.dateFlocage = dateFlocage; } + + public static boolean isSizeMatch(String option, String adherentSize) { if (option == null || adherentSize == null) return false; String opt = option.trim(); diff --git a/as-talange-core/src/main/java/com/astalange/core/repository/DotationRepository.java b/as-talange-core/src/main/java/com/astalange/core/repository/DotationRepository.java index f6160c2..5496705 100644 --- a/as-talange-core/src/main/java/com/astalange/core/repository/DotationRepository.java +++ b/as-talange-core/src/main/java/com/astalange/core/repository/DotationRepository.java @@ -12,4 +12,6 @@ import com.astalange.core.entity.Saison; public interface DotationRepository extends JpaRepository, JpaSpecificationExecutor { List findByLicence_SaisonAndChoisiTrueAndFourniFalse(Saison saison); List findByLicence_SaisonAndChoisiTrueAndCommandeeFalse(Saison saison); + List findByLicence_SaisonAndChoisiTrueAndCommandeeTrueAndFlocageExporteFalse(Saison saison); } + diff --git a/as-talange-core/src/main/java/com/astalange/core/service/DotationService.java b/as-talange-core/src/main/java/com/astalange/core/service/DotationService.java index 9b73bf6..1ee236a 100644 --- a/as-talange-core/src/main/java/com/astalange/core/service/DotationService.java +++ b/as-talange-core/src/main/java/com/astalange/core/service/DotationService.java @@ -116,6 +116,106 @@ public class DotationService { return sb.toString(); } + @Transactional + public String genererCsvFlocageInitiales(Saison saisonActive) { + List dotations = dotationRepository.findByLicence_SaisonAndChoisiTrueAndCommandeeTrueAndFlocageExporteFalse(saisonActive); + LocalDateTime now = LocalDateTime.now(); + String dateExportFormatted = now.format(DATE_FORMATTER); + + StringBuilder sb = new StringBuilder(); + sb.append('\ufeff'); // BOM UTF-8 for Excel + sb.append("Saison;Catégorie;Nom;Prénom;Équipement;Référence;Taille;Initiales;Date Export Flocage\n"); + + for (Dotation d : dotations) { + if (d.getEquipement() == null || !d.getEquipement().isHasFlocageInitiales()) { + continue; + } + String initiales = d.getFlocage() != null ? d.getFlocage().trim() : ""; + if (initiales.isEmpty()) { + continue; + } + + String saison = d.getLicence().getSaison() != null ? d.getLicence().getSaison().getNom() : ""; + String categorie = d.getLicence().getCategorie() != null ? d.getLicence().getCategorie().getNom() : ""; + String nom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getNom() : ""; + String prenom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getPrenom() : ""; + String equipement = d.getEquipement().getNom(); + String reference = d.getEquipement().getReference() != null ? d.getEquipement().getReference() : ""; + String taille = d.getTaille() != null ? d.getTaille() : ""; + + sb.append(escapeCsv(saison)).append(";") + .append(escapeCsv(categorie)).append(";") + .append(escapeCsv(nom)).append(";") + .append(escapeCsv(prenom)).append(";") + .append(escapeCsv(equipement)).append(";") + .append(escapeCsv(reference)).append(";") + .append(escapeCsv(taille)).append(";") + .append(escapeCsv(initiales)).append(";") + .append(escapeCsv(dateExportFormatted)).append("\n"); + + d.setFlocageExporte(true); + d.setDateFlocage(now); + dotationRepository.save(d); + } + + return sb.toString(); + } + + @Transactional + public String genererCsvFlocagePrenomNumero(Saison saisonActive) { + List dotations = dotationRepository.findByLicence_SaisonAndChoisiTrueAndCommandeeTrueAndFlocageExporteFalse(saisonActive); + LocalDateTime now = LocalDateTime.now(); + String dateExportFormatted = now.format(DATE_FORMATTER); + + StringBuilder sb = new StringBuilder(); + sb.append('\ufeff'); // BOM UTF-8 for Excel + sb.append("Saison;Catégorie;Nom;Prénom;Équipement;Référence;Taille;Prénom Floqué;Numéro;Date Export Flocage\n"); + + for (Dotation d : dotations) { + if (d.getEquipement() == null) { + continue; + } + boolean handlesPrenom = d.getEquipement().isHasFlocagePrenom(); + boolean handlesNumero = d.getEquipement().isHasFlocageNumero(); + + if (!handlesPrenom && !handlesNumero) { + continue; + } + + String prenomFloque = d.getFlocage() != null ? d.getFlocage().trim() : ""; + String numero = d.getNumero() != null ? d.getNumero().trim() : ""; + + if (prenomFloque.isEmpty() && numero.isEmpty()) { + continue; + } + + String saison = d.getLicence().getSaison() != null ? d.getLicence().getSaison().getNom() : ""; + String categorie = d.getLicence().getCategorie() != null ? d.getLicence().getCategorie().getNom() : ""; + String nom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getNom() : ""; + String prenom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getPrenom() : ""; + String equipement = d.getEquipement().getNom(); + String reference = d.getEquipement().getReference() != null ? d.getEquipement().getReference() : ""; + String taille = d.getTaille() != null ? d.getTaille() : ""; + + sb.append(escapeCsv(saison)).append(";") + .append(escapeCsv(categorie)).append(";") + .append(escapeCsv(nom)).append(";") + .append(escapeCsv(prenom)).append(";") + .append(escapeCsv(equipement)).append(";") + .append(escapeCsv(reference)).append(";") + .append(escapeCsv(taille)).append(";") + .append(escapeCsv(prenomFloque)).append(";") + .append(escapeCsv(numero)).append(";") + .append(escapeCsv(dateExportFormatted)).append("\n"); + + d.setFlocageExporte(true); + d.setDateFlocage(now); + dotationRepository.save(d); + } + + return sb.toString(); + } + private String escapeCsv(String value) { if (value == null) { return ""; @@ -130,3 +230,4 @@ public class DotationService { return result; } } + diff --git a/as-talange-core/src/main/resources/db/migration/V44__add_flocage_export_fields_to_dotation.sql b/as-talange-core/src/main/resources/db/migration/V44__add_flocage_export_fields_to_dotation.sql new file mode 100644 index 0000000..d5ad5a8 --- /dev/null +++ b/as-talange-core/src/main/resources/db/migration/V44__add_flocage_export_fields_to_dotation.sql @@ -0,0 +1,2 @@ +ALTER TABLE dotation ADD COLUMN flocage_exporte BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE dotation ADD COLUMN date_flocage TIMESTAMP NULL; diff --git a/as-talange-core/src/test/java/com/astalange/core/service/DotationServiceTest.java b/as-talange-core/src/test/java/com/astalange/core/service/DotationServiceTest.java new file mode 100644 index 0000000..8a2d0b8 --- /dev/null +++ b/as-talange-core/src/test/java/com/astalange/core/service/DotationServiceTest.java @@ -0,0 +1,123 @@ +package com.astalange.core.service; + +import com.astalange.core.entity.*; +import com.astalange.core.repository.DotationRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class DotationServiceTest { + + @Mock + private DotationRepository dotationRepository; + + @InjectMocks + private DotationService dotationService; + + private Saison saison; + private Categorie categorie; + private Adherent adherent; + private Licence licence; + + @BeforeEach + public void setUp() { + saison = new Saison(); + saison.setId(1L); + saison.setNom("2026-2027"); + + categorie = new Categorie(); + categorie.setId(10L); + categorie.setNom("U15"); + + adherent = new Adherent(); + adherent.setId(100L); + adherent.setNom("Dupont"); + adherent.setPrenom("Jean"); + + licence = new Licence(); + licence.setId(500L); + licence.setSaison(saison); + licence.setCategorie(categorie); + licence.setAdherent(adherent); + } + + @Test + @DisplayName("Générer CSV Flocage Initiales - Uniquement équipements avec initiales et commandés") + public void testGenererCsvFlocageInitiales() { + Equipement eqVeste = new Equipement(); + eqVeste.setId(1L); + eqVeste.setNom("Veste de sortie"); + eqVeste.setReference("REF-VESTE"); + eqVeste.setHasFlocageInitiales(true); + + Dotation d1 = new Dotation(); + d1.setId(1L); + d1.setLicence(licence); + d1.setEquipement(eqVeste); + d1.setTaille("M"); + d1.setFlocage("JD"); + d1.setChoisi(true); + d1.setCommandee(true); + d1.setFlocageExporte(false); + + when(dotationRepository.findByLicence_SaisonAndChoisiTrueAndCommandeeTrueAndFlocageExporteFalse(saison)) + .thenReturn(List.of(d1)); + + String csv = dotationService.genererCsvFlocageInitiales(saison); + + assertNotNull(csv); + assertTrue(csv.contains("JD")); + assertTrue(csv.contains("Veste de sortie")); + assertTrue(csv.contains("Dupont")); + assertTrue(d1.getFlocageExporte()); + assertNotNull(d1.getDateFlocage()); + verify(dotationRepository, times(1)).save(d1); + } + + @Test + @DisplayName("Générer CSV Flocage Prénom / Numéro - Uniquement équipements commandés avec prénom ou numéro") + public void testGenererCsvFlocagePrenomNumero() { + Equipement eqMaillot = new Equipement(); + eqMaillot.setId(2L); + eqMaillot.setNom("Maillot de match"); + eqMaillot.setReference("REF-MAILLOT"); + eqMaillot.setHasFlocagePrenom(true); + eqMaillot.setHasFlocageNumero(true); + + Dotation d2 = new Dotation(); + d2.setId(2L); + d2.setLicence(licence); + d2.setEquipement(eqMaillot); + d2.setTaille("L"); + d2.setFlocage("JEAN"); + d2.setNumero("10"); + d2.setChoisi(true); + d2.setCommandee(true); + d2.setFlocageExporte(false); + + when(dotationRepository.findByLicence_SaisonAndChoisiTrueAndCommandeeTrueAndFlocageExporteFalse(saison)) + .thenReturn(List.of(d2)); + + String csv = dotationService.genererCsvFlocagePrenomNumero(saison); + + assertNotNull(csv); + assertTrue(csv.contains("JEAN")); + assertTrue(csv.contains("10")); + assertTrue(csv.contains("Maillot de match")); + assertTrue(d2.getFlocageExporte()); + assertNotNull(d2.getDateFlocage()); + verify(dotationRepository, times(1)).save(d2); + } +} diff --git a/as-talange-web/src/main/java/com/astalange/web/controller/DotationController.java b/as-talange-web/src/main/java/com/astalange/web/controller/DotationController.java index 8972feb..a568b12 100644 --- a/as-talange-web/src/main/java/com/astalange/web/controller/DotationController.java +++ b/as-talange-web/src/main/java/com/astalange/web/controller/DotationController.java @@ -149,6 +149,37 @@ public class DotationController { return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK); } + @GetMapping("/admin/equipements/export-flocage-initiales") + public ResponseEntity exportFlocageInitiales() { + Saison saisonActive = saisonRepository.findByEstActiveTrue() + .orElseThrow(() -> new IllegalStateException("Aucune saison active")); + + String csvContent = dotationService.genererCsvFlocageInitiales(saisonActive); + byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentDispositionFormData("attachment", "flocage_initiales_" + LocalDate.now() + ".csv"); + headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8")); + + return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK); + } + + @GetMapping("/admin/equipements/export-flocage-prenom-numero") + public ResponseEntity exportFlocagePrenomNumero() { + Saison saisonActive = saisonRepository.findByEstActiveTrue() + .orElseThrow(() -> new IllegalStateException("Aucune saison active")); + + String csvContent = dotationService.genererCsvFlocagePrenomNumero(saisonActive); + byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentDispositionFormData("attachment", "flocage_prenom_numero_" + LocalDate.now() + ".csv"); + headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8")); + + return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK); + } + + @PostMapping("/dotations/{id}") public String updateDotation( @PathVariable Long id, diff --git a/as-talange-web/src/main/resources/templates/parametrage/equipements_recherche.html b/as-talange-web/src/main/resources/templates/parametrage/equipements_recherche.html index 0030774..afee0e4 100644 --- a/as-talange-web/src/main/resources/templates/parametrage/equipements_recherche.html +++ b/as-talange-web/src/main/resources/templates/parametrage/equipements_recherche.html @@ -53,20 +53,41 @@ -