From 019a445d2cb67a7b920fd21760e628f1dcb2306e Mon Sep 17 00:00:00 2001 From: Youssef Date: Thu, 6 Aug 2026 22:10:09 +0200 Subject: [PATCH] feat: implement equipment order export and tracking status --- .../com/astalange/core/entity/Dotation.java | 12 +++ .../core/repository/DotationRepository.java | 1 + .../core/service/DotationService.java | 76 ++++++++++++------- .../migration/V41__add_dotation_commandee.sql | 2 + .../web/controller/DotationController.java | 16 ++-- .../parametrage/equipements_recherche.html | 47 +++++++++--- 6 files changed, 112 insertions(+), 42 deletions(-) create mode 100644 as-talange-core/src/main/resources/db/migration/V41__add_dotation_commandee.sql 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 da8e07b..bd5c475 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 @@ -36,6 +36,12 @@ public class Dotation { @Column(nullable = false) private Boolean choisi = false; + @Column(nullable = false) + private Boolean commandee = false; + + @Column(name = "date_commande") + private java.time.LocalDateTime dateCommande; + // Getters and Setters public Long getId() { return id; } @@ -65,6 +71,12 @@ public class Dotation { public Boolean getChoisi() { return choisi; } public void setChoisi(Boolean choisi) { this.choisi = choisi; } + public Boolean getCommandee() { return commandee; } + public void setCommandee(Boolean commandee) { this.commandee = commandee; } + + public java.time.LocalDateTime getDateCommande() { return dateCommande; } + public void setDateCommande(java.time.LocalDateTime dateCommande) { this.dateCommande = dateCommande; } + 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 a2604c8..f6160c2 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 @@ -11,4 +11,5 @@ import com.astalange.core.entity.Saison; @Repository public interface DotationRepository extends JpaRepository, JpaSpecificationExecutor { List findByLicence_SaisonAndChoisiTrueAndFourniFalse(Saison saison); + List findByLicence_SaisonAndChoisiTrueAndCommandeeFalse(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 1a6657c..9b73bf6 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 @@ -4,50 +4,70 @@ import com.astalange.core.entity.Dotation; import com.astalange.core.entity.Saison; import com.astalange.core.repository.DotationRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; @Service public class DotationService { private final DotationRepository dotationRepository; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); public DotationService(DotationRepository dotationRepository) { this.dotationRepository = dotationRepository; } + @Transactional public String genererCsvCommandeEquipement(Saison saisonActive) { - List dotations = dotationRepository.findByLicence_SaisonAndChoisiTrueAndFourniFalse(saisonActive); + return genererCsvCommandeEquipementGrouped(saisonActive, true); + } + + @Transactional + public String genererCsvCommandeEquipementGrouped(Saison saisonActive, boolean markAsCommandee) { + List dotations = dotationRepository.findByLicence_SaisonAndChoisiTrueAndCommandeeFalse(saisonActive); + LocalDateTime now = LocalDateTime.now(); + String dateCommandeFormatted = now.format(DATE_FORMATTER); + + Map groupCountMap = new LinkedHashMap<>(); + + for (Dotation d : dotations) { + String equipement = d.getEquipement() != null ? d.getEquipement().getNom() : "Inconnu"; + String reference = d.getEquipement() != null && d.getEquipement().getReference() != null ? d.getEquipement().getReference() : ""; + String taille = d.getTaille() != null && !d.getTaille().trim().isEmpty() ? d.getTaille() : "Non renseignée"; + + String key = equipement + "|||" + reference + "|||" + taille; + groupCountMap.put(key, groupCountMap.getOrDefault(key, 0) + 1); + + if (markAsCommandee) { + d.setCommandee(true); + d.setDateCommande(now); + dotationRepository.save(d); + } + } StringBuilder sb = new StringBuilder(); // BOM for Excel to open UTF-8 correctly sb.append('\ufeff'); - + // Header - sb.append("Catégorie;Nom;Prénom;Poste;Sexe;Équipement;Référence;Taille;Flocage;Numéro\n"); + sb.append("Équipement;Référence;Taille;Quantité;Commandé;Date de Commande\n"); - for (Dotation d : dotations) { - String categorie = d.getLicence().getCategorie() != null ? d.getLicence().getCategorie().getNom() : ""; - String nom = d.getLicence().getAdherent().getNom(); - String prenom = d.getLicence().getAdherent().getPrenom(); - String poste = d.getLicence().getAdherent().getTypeMaillot() != null ? d.getLicence().getAdherent().getTypeMaillot() : ""; - String sexe = d.getLicence().getAdherent().getSexe() != null ? d.getLicence().getAdherent().getSexe() : ""; - String equipement = d.getEquipement() != null ? d.getEquipement().getNom() : ""; - String reference = d.getEquipement() != null && d.getEquipement().getReference() != null ? d.getEquipement().getReference() : ""; - String taille = d.getTaille() != null ? d.getTaille() : ""; - String flocage = d.getFlocage() != null ? d.getFlocage() : ""; - String numero = d.getNumero() != null ? d.getNumero() : ""; + for (Map.Entry entry : groupCountMap.entrySet()) { + String[] parts = entry.getKey().split("\\|\\|\\|", -1); + String equipement = parts[0]; + String reference = parts[1]; + String taille = parts[2]; + int quantite = entry.getValue(); - sb.append(escapeCsv(categorie)).append(";") - .append(escapeCsv(nom)).append(";") - .append(escapeCsv(prenom)).append(";") - .append(escapeCsv(poste)).append(";") - .append(escapeCsv(sexe)).append(";") - .append(escapeCsv(equipement)).append(";") + sb.append(escapeCsv(equipement)).append(";") .append(escapeCsv(reference)).append(";") .append(escapeCsv(taille)).append(";") - .append(escapeCsv(flocage)).append(";") - .append(escapeCsv(numero)).append("\n"); + .append(quantite).append(";") + .append("Oui").append(";") + .append(escapeCsv(dateCommandeFormatted)).append("\n"); } return sb.toString(); @@ -57,9 +77,9 @@ public class DotationService { StringBuilder sb = new StringBuilder(); // BOM for Excel to open UTF-8 correctly sb.append('\ufeff'); - + // Header - sb.append("Saison;Catégorie;Nom;Prénom;Poste;Sexe;Équipement;Référence;Taille;Flocage;Numéro;Fourni\n"); + sb.append("Saison;Catégorie;Nom;Prénom;Poste;Sexe;Équipement;Référence;Taille;Flocage;Numéro;Fourni;Commandé;Date Commande\n"); for (Dotation d : dotations) { String saison = d.getLicence().getSaison() != null ? d.getLicence().getSaison().getNom() : ""; @@ -74,6 +94,8 @@ public class DotationService { String flocage = d.getFlocage() != null ? d.getFlocage() : ""; String numero = d.getNumero() != null ? d.getNumero() : ""; String fourni = Boolean.TRUE.equals(d.getFourni()) ? "Oui" : "Non"; + String commandee = Boolean.TRUE.equals(d.getCommandee()) ? "Oui" : "Non"; + String dateCommande = d.getDateCommande() != null ? d.getDateCommande().format(DATE_FORMATTER) : ""; sb.append(escapeCsv(saison)).append(";") .append(escapeCsv(categorie)).append(";") @@ -86,7 +108,9 @@ public class DotationService { .append(escapeCsv(taille)).append(";") .append(escapeCsv(flocage)).append(";") .append(escapeCsv(numero)).append(";") - .append(escapeCsv(fourni)).append("\n"); + .append(escapeCsv(fourni)).append(";") + .append(escapeCsv(commandee)).append(";") + .append(escapeCsv(dateCommande)).append("\n"); } return sb.toString(); diff --git a/as-talange-core/src/main/resources/db/migration/V41__add_dotation_commandee.sql b/as-talange-core/src/main/resources/db/migration/V41__add_dotation_commandee.sql new file mode 100644 index 0000000..501b13f --- /dev/null +++ b/as-talange-core/src/main/resources/db/migration/V41__add_dotation_commandee.sql @@ -0,0 +1,2 @@ +ALTER TABLE dotation ADD COLUMN commandee BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE dotation ADD COLUMN date_commande TIMESTAMP WITHOUT TIME ZONE; 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 f3e6b0f..8972feb 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 @@ -45,7 +45,7 @@ public class DotationController { this.categorieRepository = categorieRepository; } - private Specification buildSpecification(Long equipementId, Long categorieId, Boolean fourni, Saison saisonActive) { + private Specification buildSpecification(Long equipementId, Long categorieId, Boolean fourni, Boolean commandee, Saison saisonActive) { return (root, query, cb) -> { if (Long.class != query.getResultType() && long.class != query.getResultType()) { root.fetch("equipement", jakarta.persistence.criteria.JoinType.LEFT); @@ -67,6 +67,9 @@ public class DotationController { if (fourni != null) { predicates.add(cb.equal(root.get("fourni"), fourni)); } + if (commandee != null) { + predicates.add(cb.equal(root.get("commandee"), commandee)); + } return cb.and(predicates.toArray(new Predicate[0])); }; } @@ -76,12 +79,13 @@ public class DotationController { @RequestParam(required = false) Long equipementId, @RequestParam(required = false) Long categorieId, @RequestParam(required = false) Boolean fourni, + @RequestParam(required = false) Boolean commandee, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size, Model model) { Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null); - Specification spec = buildSpecification(equipementId, categorieId, fourni, saisonActive); + Specification spec = buildSpecification(equipementId, categorieId, fourni, commandee, saisonActive); List allDotations = dotationRepository.findAll(spec); int totalElements = allDotations.size(); @@ -99,6 +103,7 @@ public class DotationController { model.addAttribute("equipementId", equipementId); model.addAttribute("categorieId", categorieId); model.addAttribute("fourni", fourni); + model.addAttribute("commandee", commandee); model.addAttribute("currentPage", page); model.addAttribute("totalPages", totalPages); @@ -112,10 +117,11 @@ public class DotationController { public ResponseEntity exportRechercheEquipements( @RequestParam(required = false) Long equipementId, @RequestParam(required = false) Long categorieId, - @RequestParam(required = false) Boolean fourni) { + @RequestParam(required = false) Boolean fourni, + @RequestParam(required = false) Boolean commandee) { Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null); - Specification spec = buildSpecification(equipementId, categorieId, fourni, saisonActive); + Specification spec = buildSpecification(equipementId, categorieId, fourni, commandee, saisonActive); List dotations = dotationRepository.findAll(spec); String csvContent = dotationService.genererCsvSearchEquipement(dotations); @@ -137,7 +143,7 @@ public class DotationController { byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8); HttpHeaders headers = new HttpHeaders(); - headers.setContentDispositionFormData("attachment", "commande_equipements_" + LocalDate.now() + ".csv"); + headers.setContentDispositionFormData("attachment", "commande_equipements_regroupee_" + LocalDate.now() + ".csv"); headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8")); return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK); 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 cf33574..bdbb672 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 @@ -21,7 +21,7 @@
-
+
+
+ + +
- @@ -62,12 +80,13 @@ Équipement Référence Taille / Floc. + Commandé Fourni - Aucun résultat trouvé pour cette recherche. + Aucun résultat trouvé pour cette recherche. @@ -85,6 +104,12 @@
Flocage
+ + + Oui + + Non + Oui Non @@ -106,12 +131,12 @@