Files
as-talange/as-talange-web/src/main/java/com/astalange/web/controller/DotationController.java
T
ucef 658c811634
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 4m33s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 8m33s
feat: ajout du filtre et de la fonction de relance des adherents avec tailles manquantes
2026-08-27 10:40:33 +02:00

376 lines
18 KiB
Java

package com.astalange.web.controller;
import com.astalange.core.entity.AppUser;
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.EquipementRepository;
import com.astalange.core.repository.SaisonRepository;
import com.astalange.core.security.CustomUserDetails;
import com.astalange.core.service.DotationService;
import com.astalange.core.service.ExportHistoryService;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
@Controller
public class DotationController {
private final DotationRepository dotationRepository;
private final DotationService dotationService;
private final SaisonRepository saisonRepository;
private final EquipementRepository equipementRepository;
private final CategorieRepository categorieRepository;
private final ExportHistoryService exportHistoryService;
private final AppUserRepository appUserRepository;
public DotationController(DotationRepository dotationRepository,
DotationService dotationService,
SaisonRepository saisonRepository,
EquipementRepository equipementRepository,
CategorieRepository categorieRepository,
ExportHistoryService exportHistoryService,
AppUserRepository appUserRepository) {
this.dotationRepository = dotationRepository;
this.dotationService = dotationService;
this.saisonRepository = saisonRepository;
this.equipementRepository = equipementRepository;
this.categorieRepository = categorieRepository;
this.exportHistoryService = exportHistoryService;
this.appUserRepository = appUserRepository;
}
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) -> {
if (Long.class != query.getResultType() && long.class != query.getResultType()) {
root.fetch("equipement", jakarta.persistence.criteria.JoinType.LEFT);
jakarta.persistence.criteria.Fetch<Object, Object> licenceFetch = root.fetch("licence", jakarta.persistence.criteria.JoinType.LEFT);
licenceFetch.fetch("adherent", jakarta.persistence.criteria.JoinType.LEFT);
licenceFetch.fetch("categorie", jakarta.persistence.criteria.JoinType.LEFT);
}
List<Predicate> predicates = new ArrayList<>();
if (saisonActive != null) {
predicates.add(cb.equal(root.get("licence").get("saison"), saisonActive));
}
if (equipementId != null) {
predicates.add(cb.equal(root.get("equipement").get("id"), equipementId));
}
if (categorieId != null) {
predicates.add(cb.equal(root.get("licence").get("categorie").get("id"), categorieId));
}
if (fourni != null) {
predicates.add(cb.equal(root.get("fourni"), fourni));
}
if (commandee != null) {
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]));
};
}
@GetMapping("/admin/equipements/recherche")
public String rechercheEquipements(
@RequestParam(required = false) Long equipementId,
@RequestParam(required = false) Long categorieId,
@RequestParam(required = false) Boolean fourni,
@RequestParam(required = false) Boolean commandee,
@RequestParam(required = false) Boolean tailleRenseignee,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
Model model) {
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, tailleRenseignee, saisonActive);
List<Dotation> allDotations = dotationRepository.findAll(spec);
int totalElements = allDotations.size();
int totalPages = (int) Math.ceil((double) totalElements / size);
if (page < 0) page = 0;
if (page >= totalPages && totalPages > 0) page = totalPages - 1;
int start = page * size;
int end = Math.min(start + size, totalElements);
List<Dotation> pageContent = (start < totalElements) ? allDotations.subList(start, end) : List.of();
model.addAttribute("dotations", pageContent);
model.addAttribute("equipements", equipementRepository.findAll());
model.addAttribute("categories", categorieRepository.findAll());
model.addAttribute("equipementId", equipementId);
model.addAttribute("categorieId", categorieId);
model.addAttribute("fourni", fourni);
model.addAttribute("commandee", commandee);
model.addAttribute("tailleRenseignee", tailleRenseignee);
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", totalPages);
model.addAttribute("totalElements", totalElements);
model.addAttribute("pageSize", size);
return "parametrage/equipements_recherche";
}
@GetMapping("/admin/equipements/recherche/export")
public ResponseEntity<byte[]> exportRechercheEquipements(
@RequestParam(required = false) Long equipementId,
@RequestParam(required = false) Long categorieId,
@RequestParam(required = false) Boolean fourni,
@RequestParam(required = false) Boolean commandee,
@RequestParam(required = false) Boolean tailleRenseignee) {
Saison saisonActive = saisonRepository.findByEstActiveTrue().orElse(null);
Specification<Dotation> spec = buildSpecification(equipementId, categorieId, fourni, commandee, tailleRenseignee, saisonActive);
List<Dotation> dotations = dotationRepository.findAll(spec);
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);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", filename);
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
}
@GetMapping("/admin/equipements/export-commande")
public ResponseEntity<byte[]> exportCommande() {
Saison saisonActive = saisonRepository.findByEstActiveTrue()
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
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);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", filename);
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
return new ResponseEntity<>(csvBytes, headers, org.springframework.http.HttpStatus.OK);
}
@GetMapping("/admin/equipements/export-flocage-initiales")
public ResponseEntity<byte[]> exportFlocageInitiales() {
Saison saisonActive = saisonRepository.findByEstActiveTrue()
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
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);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", filename);
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<byte[]> exportFlocagePrenomNumero() {
Saison saisonActive = saisonRepository.findByEstActiveTrue()
.orElseThrow(() -> new IllegalStateException("Aucune saison active"));
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);
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", filename);
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
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}")
public String updateDotation(
@PathVariable Long id,
@RequestParam Long adherentId,
@RequestParam(required = false, defaultValue = "") String taille,
@RequestParam(required = false, defaultValue = "") String flocage,
@RequestParam(required = false, defaultValue = "") String numero,
@RequestParam(required = false) Boolean fourni,
@RequestParam(required = false) Boolean choisi) {
Dotation dotation = dotationRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid Dotation ID: " + id));
dotation.setTaille(taille);
dotation.setFlocage(flocage);
dotation.setNumero(numero);
dotation.setFourni(fourni != null && fourni);
if (dotation.getLicence().getCategorie().isEquipementObligatoire(dotation.getEquipement().getId())) {
dotation.setChoisi(true);
} else {
dotation.setChoisi(choisi != null && choisi);
}
dotationRepository.save(dotation);
return "redirect:/adherents/" + adherentId + "/edit";
}
@Transactional
@PostMapping("/dotations/bulk")
public String bulkUpdateDotations(
@RequestParam Long adherentId,
@RequestParam("dotationIds") List<Long> dotationIds,
HttpServletRequest request) {
for (Long id : dotationIds) {
Dotation dotation = dotationRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid Dotation ID: " + id));
String taille = request.getParameter("taille_" + id);
String couleur = request.getParameter("couleur_" + id);
String numero = request.getParameter("numero_" + id);
String flocage = request.getParameter("flocage_" + id);
String choisiParam = request.getParameter("choisi_" + id);
String fourniParam = request.getParameter("fourni_" + id);
if (taille != null) dotation.setTaille(taille);
if (couleur != null) dotation.setCouleur(couleur);
if (numero != null) dotation.setNumero(numero);
if (flocage != null) dotation.setFlocage(flocage);
if (dotation.getLicence().getCategorie().isEquipementObligatoire(dotation.getEquipement().getId())) {
dotation.setChoisi(true);
} else {
dotation.setChoisi(choisiParam != null);
}
dotation.setFourni(fourniParam != null);
dotationRepository.save(dotation);
}
return "redirect:/adherents/" + adherentId + "/edit";
}
}