feat: ajout du filtre et de la fonction de relance des adherents avec tailles manquantes
This commit is contained in:
@@ -89,15 +89,17 @@ public class DotationService {
|
|||||||
sb.append('\ufeff');
|
sb.append('\ufeff');
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
sb.append("Saison;Catégorie;Nom;Prénom;Poste;Sexe;Équipement;Référence;Taille;Flocage;Numéro;Fourni;Commandé;Date Commande\n");
|
sb.append("Saison;Catégorie;Nom;Prénom;Email;Téléphone;Poste;Sexe;Équipement;Référence;Taille;Flocage;Numéro;Fourni;Commandé;Date Commande\n");
|
||||||
|
|
||||||
for (Dotation d : dotations) {
|
for (Dotation d : dotations) {
|
||||||
String saison = d.getLicence().getSaison() != null ? d.getLicence().getSaison().getNom() : "";
|
String saison = d.getLicence().getSaison() != null ? d.getLicence().getSaison().getNom() : "";
|
||||||
String categorie = d.getLicence().getCategorie() != null ? d.getLicence().getCategorie().getNom() : "";
|
String categorie = d.getLicence().getCategorie() != null ? d.getLicence().getCategorie().getNom() : "";
|
||||||
String nom = d.getLicence().getAdherent().getNom();
|
String nom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getNom() : "";
|
||||||
String prenom = d.getLicence().getAdherent().getPrenom();
|
String prenom = d.getLicence().getAdherent() != null ? d.getLicence().getAdherent().getPrenom() : "";
|
||||||
String poste = d.getLicence().getAdherent().getTypeMaillot() != null ? d.getLicence().getAdherent().getTypeMaillot() : "";
|
String email = d.getLicence().getAdherent() != null && d.getLicence().getAdherent().getEmail() != null ? d.getLicence().getAdherent().getEmail() : "";
|
||||||
String sexe = d.getLicence().getAdherent().getSexe() != null ? d.getLicence().getAdherent().getSexe() : "";
|
String telephone = d.getLicence().getAdherent() != null && d.getLicence().getAdherent().getTelephone() != null ? d.getLicence().getAdherent().getTelephone() : "";
|
||||||
|
String poste = d.getLicence().getAdherent() != null && d.getLicence().getAdherent().getTypeMaillot() != null ? d.getLicence().getAdherent().getTypeMaillot() : "";
|
||||||
|
String sexe = d.getLicence().getAdherent() != null && d.getLicence().getAdherent().getSexe() != null ? d.getLicence().getAdherent().getSexe() : "";
|
||||||
String equipement = d.getEquipement() != null ? d.getEquipement().getNom() : "";
|
String equipement = d.getEquipement() != null ? d.getEquipement().getNom() : "";
|
||||||
String reference = d.getEquipement() != null && d.getEquipement().getReference() != null ? d.getEquipement().getReference() : "";
|
String reference = d.getEquipement() != null && d.getEquipement().getReference() != null ? d.getEquipement().getReference() : "";
|
||||||
String taille = d.getTaille() != null ? d.getTaille() : "";
|
String taille = d.getTaille() != null ? d.getTaille() : "";
|
||||||
@@ -111,6 +113,8 @@ public class DotationService {
|
|||||||
.append(escapeCsv(categorie)).append(";")
|
.append(escapeCsv(categorie)).append(";")
|
||||||
.append(escapeCsv(nom)).append(";")
|
.append(escapeCsv(nom)).append(";")
|
||||||
.append(escapeCsv(prenom)).append(";")
|
.append(escapeCsv(prenom)).append(";")
|
||||||
|
.append(escapeCsv(email)).append(";")
|
||||||
|
.append(escapeCsv(telephone)).append(";")
|
||||||
.append(escapeCsv(poste)).append(";")
|
.append(escapeCsv(poste)).append(";")
|
||||||
.append(escapeCsv(sexe)).append(";")
|
.append(escapeCsv(sexe)).append(";")
|
||||||
.append(escapeCsv(equipement)).append(";")
|
.append(escapeCsv(equipement)).append(";")
|
||||||
|
|||||||
+157
-30
@@ -1,32 +1,34 @@
|
|||||||
package com.astalange.web.controller;
|
package com.astalange.web.controller;
|
||||||
|
|
||||||
|
import com.astalange.core.entity.AppUser;
|
||||||
import com.astalange.core.entity.Dotation;
|
import com.astalange.core.entity.Dotation;
|
||||||
|
import com.astalange.core.entity.ExportHistory;
|
||||||
|
import com.astalange.core.entity.Saison;
|
||||||
|
import com.astalange.core.entity.TypeExport;
|
||||||
|
import com.astalange.core.repository.AppUserRepository;
|
||||||
|
import com.astalange.core.repository.CategorieRepository;
|
||||||
import com.astalange.core.repository.DotationRepository;
|
import com.astalange.core.repository.DotationRepository;
|
||||||
import org.springframework.stereotype.Controller;
|
import com.astalange.core.repository.EquipementRepository;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import com.astalange.core.repository.SaisonRepository;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import com.astalange.core.security.CustomUserDetails;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import com.astalange.core.service.DotationService;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import com.astalange.core.service.ExportHistoryService;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import java.util.List;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import java.time.LocalDate;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import com.astalange.core.service.DotationService;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import com.astalange.core.repository.SaisonRepository;
|
import org.springframework.stereotype.Controller;
|
||||||
import com.astalange.core.entity.Saison;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import com.astalange.core.repository.EquipementRepository;
|
|
||||||
import com.astalange.core.repository.CategorieRepository;
|
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
|
||||||
import java.util.ArrayList;
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class DotationController {
|
public class DotationController {
|
||||||
@@ -36,16 +38,44 @@ public class DotationController {
|
|||||||
private final SaisonRepository saisonRepository;
|
private final SaisonRepository saisonRepository;
|
||||||
private final EquipementRepository equipementRepository;
|
private final EquipementRepository equipementRepository;
|
||||||
private final CategorieRepository categorieRepository;
|
private final CategorieRepository categorieRepository;
|
||||||
|
private final ExportHistoryService exportHistoryService;
|
||||||
|
private final AppUserRepository appUserRepository;
|
||||||
|
|
||||||
public DotationController(DotationRepository dotationRepository, DotationService dotationService, SaisonRepository saisonRepository, EquipementRepository equipementRepository, CategorieRepository categorieRepository) {
|
public DotationController(DotationRepository dotationRepository,
|
||||||
|
DotationService dotationService,
|
||||||
|
SaisonRepository saisonRepository,
|
||||||
|
EquipementRepository equipementRepository,
|
||||||
|
CategorieRepository categorieRepository,
|
||||||
|
ExportHistoryService exportHistoryService,
|
||||||
|
AppUserRepository appUserRepository) {
|
||||||
this.dotationRepository = dotationRepository;
|
this.dotationRepository = dotationRepository;
|
||||||
this.dotationService = dotationService;
|
this.dotationService = dotationService;
|
||||||
this.saisonRepository = saisonRepository;
|
this.saisonRepository = saisonRepository;
|
||||||
this.equipementRepository = equipementRepository;
|
this.equipementRepository = equipementRepository;
|
||||||
this.categorieRepository = categorieRepository;
|
this.categorieRepository = categorieRepository;
|
||||||
|
this.exportHistoryService = exportHistoryService;
|
||||||
|
this.appUserRepository = appUserRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Specification<Dotation> buildSpecification(Long equipementId, Long categorieId, Boolean fourni, Boolean commandee, Saison saisonActive) {
|
private AppUser getCurrentUser() {
|
||||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth != null && auth.getPrincipal() instanceof CustomUserDetails userDetails) {
|
||||||
|
return appUserRepository.findById(userDetails.getId()).orElse(null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compterElementsCsv(String csvContent) {
|
||||||
|
if (csvContent == null || csvContent.trim().isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int lines = (int) Arrays.stream(csvContent.split("\n"))
|
||||||
|
.filter(l -> !l.trim().isEmpty())
|
||||||
|
.count();
|
||||||
|
return Math.max(0, lines - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Specification<Dotation> buildSpecification(Long equipementId, Long categorieId, Boolean fourni, Boolean commandee, Boolean tailleRenseignee, Saison saisonActive) {
|
||||||
return (root, query, cb) -> {
|
return (root, query, cb) -> {
|
||||||
if (Long.class != query.getResultType() && long.class != query.getResultType()) {
|
if (Long.class != query.getResultType() && long.class != query.getResultType()) {
|
||||||
root.fetch("equipement", jakarta.persistence.criteria.JoinType.LEFT);
|
root.fetch("equipement", jakarta.persistence.criteria.JoinType.LEFT);
|
||||||
@@ -70,6 +100,24 @@ public class DotationController {
|
|||||||
if (commandee != null) {
|
if (commandee != null) {
|
||||||
predicates.add(cb.equal(root.get("commandee"), commandee));
|
predicates.add(cb.equal(root.get("commandee"), commandee));
|
||||||
}
|
}
|
||||||
|
if (tailleRenseignee != null) {
|
||||||
|
if (tailleRenseignee) {
|
||||||
|
predicates.add(cb.and(
|
||||||
|
cb.isNotNull(root.get("taille")),
|
||||||
|
cb.notEqual(cb.trim(root.get("taille")), "")
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// Uniquement les équipements proposant une grille de tailles mais dont la taille n'est pas encore saisie
|
||||||
|
predicates.add(cb.and(
|
||||||
|
cb.isNotNull(root.get("equipement").get("taillesDisponibles")),
|
||||||
|
cb.notEqual(cb.trim(root.get("equipement").get("taillesDisponibles")), ""),
|
||||||
|
cb.or(
|
||||||
|
cb.isNull(root.get("taille")),
|
||||||
|
cb.equal(cb.trim(root.get("taille")), "")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
return cb.and(predicates.toArray(new Predicate[0]));
|
return cb.and(predicates.toArray(new Predicate[0]));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -80,12 +128,13 @@ public class DotationController {
|
|||||||
@RequestParam(required = false) Long categorieId,
|
@RequestParam(required = false) Long categorieId,
|
||||||
@RequestParam(required = false) Boolean fourni,
|
@RequestParam(required = false) Boolean fourni,
|
||||||
@RequestParam(required = false) Boolean commandee,
|
@RequestParam(required = false) Boolean commandee,
|
||||||
|
@RequestParam(required = false) Boolean tailleRenseignee,
|
||||||
@RequestParam(defaultValue = "0") int page,
|
@RequestParam(defaultValue = "0") int page,
|
||||||
@RequestParam(defaultValue = "20") int size,
|
@RequestParam(defaultValue = "20") int size,
|
||||||
Model model) {
|
Model model) {
|
||||||
|
|
||||||
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
|
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, saisonActive);
|
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, tailleRenseignee, saisonActive);
|
||||||
List<Dotation> allDotations = dotationRepository.findAll(spec);
|
List<Dotation> allDotations = dotationRepository.findAll(spec);
|
||||||
|
|
||||||
int totalElements = allDotations.size();
|
int totalElements = allDotations.size();
|
||||||
@@ -104,6 +153,7 @@ public class DotationController {
|
|||||||
model.addAttribute("categorieId", categorieId);
|
model.addAttribute("categorieId", categorieId);
|
||||||
model.addAttribute("fourni", fourni);
|
model.addAttribute("fourni", fourni);
|
||||||
model.addAttribute("commandee", commandee);
|
model.addAttribute("commandee", commandee);
|
||||||
|
model.addAttribute("tailleRenseignee", tailleRenseignee);
|
||||||
|
|
||||||
model.addAttribute("currentPage", page);
|
model.addAttribute("currentPage", page);
|
||||||
model.addAttribute("totalPages", totalPages);
|
model.addAttribute("totalPages", totalPages);
|
||||||
@@ -118,17 +168,21 @@ public class DotationController {
|
|||||||
@RequestParam(required = false) Long equipementId,
|
@RequestParam(required = false) Long equipementId,
|
||||||
@RequestParam(required = false) Long categorieId,
|
@RequestParam(required = false) Long categorieId,
|
||||||
@RequestParam(required = false) Boolean fourni,
|
@RequestParam(required = false) Boolean fourni,
|
||||||
@RequestParam(required = false) Boolean commandee) {
|
@RequestParam(required = false) Boolean commandee,
|
||||||
|
@RequestParam(required = false) Boolean tailleRenseignee) {
|
||||||
|
|
||||||
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
|
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
|
||||||
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, saisonActive);
|
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, tailleRenseignee, saisonActive);
|
||||||
List<Dotation> dotations = dotationRepository.findAll(spec);
|
List<Dotation> dotations = dotationRepository.findAll(spec);
|
||||||
|
|
||||||
String csvContent = dotationService.genererCsvSearchEquipement(dotations);
|
String csvContent = dotationService.genererCsvSearchEquipement(dotations);
|
||||||
|
String filename = Boolean.FALSE.equals(tailleRenseignee) ? "relance_tailles_manquantes_" + LocalDate.now() + ".csv" : "recherche_equipements_" + LocalDate.now() + ".csv";
|
||||||
|
exportHistoryService.enregistrerExport(TypeExport.RECHERCHE_EQUIPEMENTS, filename, csvContent, compterElementsCsv(csvContent), saisonActive, getCurrentUser());
|
||||||
|
|
||||||
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentDispositionFormData("attachment", "recherche_equipements_" + LocalDate.now() + ".csv");
|
headers.setContentDispositionFormData("attachment", filename);
|
||||||
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
||||||
|
|
||||||
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
||||||
@@ -140,10 +194,13 @@ public class DotationController {
|
|||||||
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
||||||
|
|
||||||
String csvContent = dotationService.genererCsvCommandeEquipement(saisonActive);
|
String csvContent = dotationService.genererCsvCommandeEquipement(saisonActive);
|
||||||
|
String filename = "commande_equipements_regroupee_" + LocalDate.now() + ".csv";
|
||||||
|
exportHistoryService.enregistrerExport(TypeExport.COMMANDE_EQUIPEMENT, filename, csvContent, compterElementsCsv(csvContent), saisonActive, getCurrentUser());
|
||||||
|
|
||||||
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentDispositionFormData("attachment", "commande_equipements_regroupee_" + LocalDate.now() + ".csv");
|
headers.setContentDispositionFormData("attachment", filename);
|
||||||
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
||||||
|
|
||||||
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
||||||
@@ -155,10 +212,13 @@ public class DotationController {
|
|||||||
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
||||||
|
|
||||||
String csvContent = dotationService.genererCsvFlocageInitiales(saisonActive);
|
String csvContent = dotationService.genererCsvFlocageInitiales(saisonActive);
|
||||||
|
String filename = "flocage_initiales_" + LocalDate.now() + ".csv";
|
||||||
|
exportHistoryService.enregistrerExport(TypeExport.FLOCAGE_INITIALES, filename, csvContent, compterElementsCsv(csvContent), saisonActive, getCurrentUser());
|
||||||
|
|
||||||
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentDispositionFormData("attachment", "flocage_initiales_" + LocalDate.now() + ".csv");
|
headers.setContentDispositionFormData("attachment", filename);
|
||||||
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
||||||
|
|
||||||
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
||||||
@@ -170,15 +230,82 @@ public class DotationController {
|
|||||||
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
|
||||||
|
|
||||||
String csvContent = dotationService.genererCsvFlocagePrenomNumero(saisonActive);
|
String csvContent = dotationService.genererCsvFlocagePrenomNumero(saisonActive);
|
||||||
|
String filename = "flocage_prenom_numero_" + LocalDate.now() + ".csv";
|
||||||
|
exportHistoryService.enregistrerExport(TypeExport.FLOCAGE_PRENOM_NUMERO, filename, csvContent, compterElementsCsv(csvContent), saisonActive, getCurrentUser());
|
||||||
|
|
||||||
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
byte[] csvBytes = csvContent.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentDispositionFormData("attachment", "flocage_prenom_numero_" + LocalDate.now() + ".csv");
|
headers.setContentDispositionFormData("attachment", filename);
|
||||||
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
||||||
|
|
||||||
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/admin/exports/historique")
|
||||||
|
public String historiqueExports(@RequestParam(required = false) Long saisonId, Model model) {
|
||||||
|
List<Saison> saisons = saisonRepository.findAll();
|
||||||
|
Saison saisonFiltre = null;
|
||||||
|
if (saisonId != null) {
|
||||||
|
saisonFiltre = saisonRepository.findById(saisonId).orElse(null);
|
||||||
|
}
|
||||||
|
List<ExportHistory> historique = exportHistoryService.obtenirHistoriqueParSaison(saisonFiltre);
|
||||||
|
|
||||||
|
model.addAttribute("historique", historique);
|
||||||
|
model.addAttribute("saisons", saisons);
|
||||||
|
model.addAttribute("saisonId", saisonId);
|
||||||
|
return "parametrage/export_history";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/admin/exports/{id}/download")
|
||||||
|
public ResponseEntity<byte[]> downloadExport(@PathVariable Long id) {
|
||||||
|
ExportHistory history = exportHistoryService.trouverParId(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Export introuvable ID: " + id));
|
||||||
|
|
||||||
|
byte[] csvBytes = history.getContenu().getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentDispositionFormData("attachment", history.getNomFichier());
|
||||||
|
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
|
||||||
|
|
||||||
|
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/admin/exports/{id}/apercu")
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity<Map<String, Object>> apercuExport(@PathVariable Long id) {
|
||||||
|
ExportHistory history = exportHistoryService.trouverParId(id)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Export introuvable ID: " + id));
|
||||||
|
|
||||||
|
String content = history.getContenu();
|
||||||
|
if (content.startsWith("\ufeff")) {
|
||||||
|
content = content.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] lines = content.split("\n");
|
||||||
|
List<String> headers = new ArrayList<>();
|
||||||
|
List<List<String>> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
if (lines.length > 0) {
|
||||||
|
headers = Arrays.asList(lines[0].split(";"));
|
||||||
|
for (int i = 1; i < lines.length; i++) {
|
||||||
|
if (!lines[i].trim().isEmpty()) {
|
||||||
|
rows.add(Arrays.asList(lines[i].split(";", -1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("id", history.getId());
|
||||||
|
response.put("typeExport", history.getTypeExport().getLibelle());
|
||||||
|
response.put("nomFichier", history.getNomFichier());
|
||||||
|
response.put("dateExport", history.getDateExport().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")));
|
||||||
|
response.put("headers", headers);
|
||||||
|
response.put("rows", rows);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/dotations/{id}")
|
@PostMapping("/dotations/{id}")
|
||||||
public String updateDotation(
|
public String updateDotation(
|
||||||
|
|||||||
+55
-12
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<div class="bg-white p-6 rounded-lg border border-gray-200 shadow-sm mb-6">
|
<div class="bg-white p-6 rounded-lg border border-gray-200 shadow-sm mb-6">
|
||||||
<form th:action="@{/admin/equipements/recherche}" method="get" class="space-y-4">
|
<form th:action="@{/admin/equipements/recherche}" method="get" class="space-y-4">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-1">Équipement</label>
|
<label class="block text-sm font-medium text-gray-700 mb-1">Équipement</label>
|
||||||
<select name="equipementId" class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm px-3 py-2 border">
|
<select name="equipementId" class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm px-3 py-2 border">
|
||||||
@@ -36,6 +36,14 @@
|
|||||||
<option th:each="cat : ${categories}" th:value="${cat.id}" th:text="${cat.nom}" th:selected="${cat.id == categorieId}"></option>
|
<option th:each="cat : ${categories}" th:value="${cat.id}" th:text="${cat.nom}" th:selected="${cat.id == categorieId}"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">Taille renseignée</label>
|
||||||
|
<select name="tailleRenseignee" class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm px-3 py-2 border">
|
||||||
|
<option value="">Toutes</option>
|
||||||
|
<option value="true" th:selected="${tailleRenseignee != null && tailleRenseignee}">Oui (Renseignée)</option>
|
||||||
|
<option value="false" th:selected="${tailleRenseignee != null && !tailleRenseignee}">Non (Manquante)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-1">Fourni</label>
|
<label class="block text-sm font-medium text-gray-700 mb-1">Fourni</label>
|
||||||
<select name="fourni" class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm px-3 py-2 border">
|
<select name="fourni" class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm px-3 py-2 border">
|
||||||
@@ -81,10 +89,26 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Exporter Flocage Prénom / N°
|
Exporter Flocage Prénom / N°
|
||||||
</a>
|
</a>
|
||||||
|
<a th:href="@{/admin/equipements/recherche(commandee=false,tailleRenseignee=false)}"
|
||||||
|
class="bg-amber-600 text-white px-3.5 py-2 rounded-lg text-sm font-medium hover:bg-amber-700 flex items-center space-x-1 shadow-sm transition-colors"
|
||||||
|
title="Filtrer immédiatement les équipements non commandés dont la taille est manquante pour relancer les adhérents">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||||
|
</svg>
|
||||||
|
Relance Tailles Manquantes
|
||||||
|
</a>
|
||||||
|
<a th:href="@{/admin/exports/historique}"
|
||||||
|
class="bg-gray-700 text-white px-3.5 py-2 rounded-lg text-sm font-medium hover:bg-gray-800 flex items-center space-x-1 shadow-sm transition-colors"
|
||||||
|
title="Consulter et re-télécharger les anciens exports">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
Historique des Exports
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700">Rechercher</button>
|
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700">Rechercher</button>
|
||||||
<a th:href="@{/admin/equipements/recherche/export(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee})}" class="bg-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-green-700">Exporter Recherche</a>
|
<a th:href="@{/admin/equipements/recherche/export(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},tailleRenseignee=${tailleRenseignee})}" class="bg-green-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-green-700">Exporter Recherche</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -111,7 +135,16 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr th:each="dotation : ${dotations}" class="hover:bg-gray-50">
|
<tr th:each="dotation : ${dotations}" class="hover:bg-gray-50">
|
||||||
<td class="py-3 px-4 font-medium text-gray-900">
|
<td class="py-3 px-4 font-medium text-gray-900">
|
||||||
<span th:text="${dotation.licence.adherent.nom + ' ' + dotation.licence.adherent.prenom}">Nom Prénom</span>
|
<div class="font-semibold text-gray-900" th:text="${dotation.licence.adherent.nom + ' ' + dotation.licence.adherent.prenom}">Nom Prénom</div>
|
||||||
|
<div class="text-xs text-gray-500 font-normal mt-0.5 flex flex-wrap items-center gap-1.5">
|
||||||
|
<a th:if="${dotation.licence.adherent.email != null && !dotation.licence.adherent.email.isEmpty()}"
|
||||||
|
th:href="'mailto:' + ${dotation.licence.adherent.email}"
|
||||||
|
class="hover:underline text-blue-600 flex items-center"
|
||||||
|
th:text="${dotation.licence.adherent.email}">email</a>
|
||||||
|
<span th:if="${dotation.licence.adherent.telephone != null && !dotation.licence.adherent.telephone.isEmpty()}"
|
||||||
|
class="text-gray-500 font-medium"
|
||||||
|
th:text="'📞 ' + ${dotation.licence.adherent.telephone}">tél</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-3 px-4 text-gray-600 text-xs">
|
<td class="py-3 px-4 text-gray-600 text-xs">
|
||||||
<div th:text="${dotation.licence.adherent.typeMaillot}">Joueur</div>
|
<div th:text="${dotation.licence.adherent.typeMaillot}">Joueur</div>
|
||||||
@@ -129,9 +162,19 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-3 px-4 text-gray-600">
|
<td class="py-3 px-4 text-gray-600">
|
||||||
<div th:text="'T: ' + (${dotation.taille != null && !dotation.taille.isEmpty() ? dotation.taille : '-'})">Taille</div>
|
<div th:if="${dotation.taille != null && !dotation.taille.trim().isEmpty()}" th:text="'T: ' + ${dotation.taille}">Taille</div>
|
||||||
<div th:if="${dotation.flocage != null && !dotation.flocage.isEmpty()}" th:text="'F: ' + ${dotation.flocage}">Flocage</div>
|
<div th:unless="${dotation.taille != null && !dotation.taille.trim().isEmpty()}">
|
||||||
<div th:if="${dotation.numero != null && !dotation.numero.isEmpty()}" th:text="'N: ' + ${dotation.numero}">N°</div>
|
<span th:if="${dotation.equipement != null && dotation.equipement.taillesDisponibles != null && !dotation.equipement.taillesDisponibles.trim().isEmpty()}"
|
||||||
|
class="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold bg-red-100 text-red-700 border border-red-200">
|
||||||
|
⚠️ Taille manquante
|
||||||
|
</span>
|
||||||
|
<span th:unless="${dotation.equipement != null && dotation.equipement.taillesDisponibles != null && !dotation.equipement.taillesDisponibles.trim().isEmpty()}"
|
||||||
|
class="text-gray-400 text-xs italic">
|
||||||
|
Sans taille
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div th:if="${dotation.flocage != null && !dotation.flocage.isEmpty()}" th:text="'F: ' + ${dotation.flocage}" class="text-xs">Flocage</div>
|
||||||
|
<div th:if="${dotation.numero != null && !dotation.numero.isEmpty()}" th:text="'N: ' + ${dotation.numero}" class="text-xs">N°</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-3 px-4 text-center">
|
<td class="py-3 px-4 text-center">
|
||||||
<span th:if="${dotation.commandee}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800" th:title="${dotation.dateCommande != null ? #temporals.format(dotation.dateCommande, 'dd/MM/yyyy HH:mm') : ''}">
|
<span th:if="${dotation.commandee}" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800" th:title="${dotation.dateCommande != null ? #temporals.format(dotation.dateCommande, 'dd/MM/yyyy HH:mm') : ''}">
|
||||||
@@ -160,12 +203,12 @@
|
|||||||
<div class="flex items-center space-x-2" th:if="${totalPages > 1}">
|
<div class="flex items-center space-x-2" th:if="${totalPages > 1}">
|
||||||
<!-- Page Précédente -->
|
<!-- Page Précédente -->
|
||||||
<a th:if="${currentPage > 0}"
|
<a th:if="${currentPage > 0}"
|
||||||
th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},page=${currentPage - 1})}"
|
th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},tailleRenseignee=${tailleRenseignee},page=${currentPage - 1})}"
|
||||||
hx-get="/admin/equipements/recherche"
|
hx-get="/admin/equipements/recherche"
|
||||||
hx-target="#equipements-table-container"
|
hx-target="#equipements-table-container"
|
||||||
hx-select="#equipements-table-container"
|
hx-select="#equipements-table-container"
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "page": ${currentPage - 1}}|"
|
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "tailleRenseignee": "${tailleRenseignee != null ? tailleRenseignee : ''}", "page": ${currentPage - 1}}|"
|
||||||
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-100 transition-colors">
|
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-100 transition-colors">
|
||||||
Précédent
|
Précédent
|
||||||
</a>
|
</a>
|
||||||
@@ -175,12 +218,12 @@
|
|||||||
|
|
||||||
<!-- Numéros de pages -->
|
<!-- Numéros de pages -->
|
||||||
<th:block th:each="pageNum : ${#numbers.sequence(0, totalPages - 1)}" th:if="${totalPages > 0}">
|
<th:block th:each="pageNum : ${#numbers.sequence(0, totalPages - 1)}" th:if="${totalPages > 0}">
|
||||||
<a th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},page=${pageNum})}"
|
<a th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},tailleRenseignee=${tailleRenseignee},page=${pageNum})}"
|
||||||
hx-get="/admin/equipements/recherche"
|
hx-get="/admin/equipements/recherche"
|
||||||
hx-target="#equipements-table-container"
|
hx-target="#equipements-table-container"
|
||||||
hx-select="#equipements-table-container"
|
hx-select="#equipements-table-container"
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "page": ${pageNum}}|"
|
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "tailleRenseignee": "${tailleRenseignee != null ? tailleRenseignee : ''}", "page": ${pageNum}}|"
|
||||||
th:text="${pageNum + 1}"
|
th:text="${pageNum + 1}"
|
||||||
class="px-3 py-1 rounded transition-colors"
|
class="px-3 py-1 rounded transition-colors"
|
||||||
th:classappend="${currentPage == pageNum ? 'bg-blue-600 text-white' : 'border border-gray-300 hover:bg-gray-100'}">
|
th:classappend="${currentPage == pageNum ? 'bg-blue-600 text-white' : 'border border-gray-300 hover:bg-gray-100'}">
|
||||||
@@ -190,12 +233,12 @@
|
|||||||
|
|
||||||
<!-- Page Suivante -->
|
<!-- Page Suivante -->
|
||||||
<a th:if="${currentPage < totalPages - 1}"
|
<a th:if="${currentPage < totalPages - 1}"
|
||||||
th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},page=${currentPage + 1})}"
|
th:href="@{/admin/equipements/recherche(equipementId=${equipementId},categorieId=${categorieId},fourni=${fourni},commandee=${commandee},tailleRenseignee=${tailleRenseignee},page=${currentPage + 1})}"
|
||||||
hx-get="/admin/equipements/recherche"
|
hx-get="/admin/equipements/recherche"
|
||||||
hx-target="#equipements-table-container"
|
hx-target="#equipements-table-container"
|
||||||
hx-select="#equipements-table-container"
|
hx-select="#equipements-table-container"
|
||||||
hx-push-url="true"
|
hx-push-url="true"
|
||||||
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "page": ${currentPage + 1}}|"
|
th:attr="hx-vals=|{"equipementId": "${equipementId != null ? equipementId : ''}", "categorieId": "${categorieId != null ? categorieId : ''}", "fourni": "${fourni != null ? fourni : ''}", "commandee": "${commandee != null ? commandee : ''}", "tailleRenseignee": "${tailleRenseignee != null ? tailleRenseignee : ''}", "page": ${currentPage + 1}}|"
|
||||||
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-100 transition-colors">
|
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-100 transition-colors">
|
||||||
Suivant
|
Suivant
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user