Refactor: Move equipment price and mandatory status to Categorie relationship
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 3m7s
AS Talange CI/CD Pipeline / Build & Run in Docker Container (push) Successful in 3m34s

- Created CategorieEquipement entity to act as a join table between Category and Equipment.
- Moved 'obligatoire' and 'prix' (formerly prixContribution) fields from Equipement to the new CategorieEquipement entity.
- Added Flyway migrations V14 and V15 to update the database schema and migrate existing data.
- Updated Categorie UI to use a select dropdown and price input for managing category-specific equipment configurations.
- Replaced 'equipement.obligatoire' and 'equipement.prixContribution' calls with their new category-level equivalents in logic and templates.
- Updated pricing calculation in Licence entity to source prices dynamically from the Categorie configuration.
- Updated Dotation generation logic to sync with category requirements automatically.
This commit is contained in:
2026-06-11 22:57:16 +02:00
parent 1a4ff6fdac
commit bca84be16a
17 changed files with 351 additions and 59 deletions
@@ -1,19 +1,31 @@
package com.astalange.web.controller;
import com.astalange.core.entity.Categorie;
import com.astalange.core.entity.Saison;
import com.astalange.core.repository.CategorieRepository;
import com.astalange.core.repository.SaisonRepository;
import com.astalange.core.repository.EquipementRepository;
import com.astalange.core.service.CategorieService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/categories")
public class CategorieController {
private final CategorieRepository categorieRepository;
private final SaisonRepository saisonRepository;
private final EquipementRepository equipementRepository;
private final CategorieService categorieService;
public CategorieController(CategorieRepository categorieRepository) {
public CategorieController(CategorieRepository categorieRepository, SaisonRepository saisonRepository, EquipementRepository equipementRepository, CategorieService categorieService) {
this.categorieRepository = categorieRepository;
this.saisonRepository = saisonRepository;
this.equipementRepository = equipementRepository;
this.categorieService = categorieService;
}
@GetMapping
@@ -25,6 +37,8 @@ public class CategorieController {
@GetMapping("/new")
public String showCreateForm(Model model) {
model.addAttribute("categorie", new Categorie());
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
model.addAttribute("allEquipements", activeSaison != null ? equipementRepository.findBySaison(activeSaison) : List.of());
return "parametrage/categories_form";
}
@@ -33,12 +47,37 @@ public class CategorieController {
Categorie categorie = categorieRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid category ID: " + id));
model.addAttribute("categorie", categorie);
Saison activeSaison = saisonRepository.findByEstActiveTrue().orElse(null);
model.addAttribute("allEquipements", activeSaison != null ? equipementRepository.findBySaison(activeSaison) : List.of());
return "parametrage/categories_form";
}
@PostMapping
public String saveCategorie(@ModelAttribute("categorie") Categorie categorie) {
categorieRepository.save(categorie);
public String saveCategorie(@ModelAttribute("categorie") Categorie categorie,
jakarta.servlet.http.HttpServletRequest request) {
java.util.Map<Long, com.astalange.core.service.CategorieService.EquipementConfig> configs = new java.util.HashMap<>();
request.getParameterMap().forEach((key, values) -> {
if (key.startsWith("etatEquipement_") && values.length > 0) {
String idStr = key.substring("etatEquipement_".length());
try {
Long eqId = Long.parseLong(idStr);
String etat = values[0];
if (!"NON_LIE".equals(etat)) {
boolean obligatoire = "OBLIGATOIRE".equals(etat);
String prixStr = request.getParameter("prixEquipement_" + eqId);
java.math.BigDecimal prix = java.math.BigDecimal.ZERO;
if (prixStr != null && !prixStr.trim().isEmpty()) {
prix = new java.math.BigDecimal(prixStr.trim());
}
configs.put(eqId, new com.astalange.core.service.CategorieService.EquipementConfig(obligatoire, prix));
}
} catch (NumberFormatException ignored) {}
}
});
categorieService.saveCategorie(categorie, configs);
return "redirect:/categories";
}
@@ -38,7 +38,7 @@ public class DotationController {
dotation.setNumero(numero);
dotation.setFourni(fourni != null && fourni);
if (dotation.getEquipement().getObligatoire()) {
if (dotation.getLicence().getCategorie().isEquipementObligatoire(dotation.getEquipement().getId())) {
dotation.setChoisi(true);
} else {
dotation.setChoisi(choisi != null && choisi);
@@ -70,7 +70,7 @@ public class DotationController {
if (numero != null) dotation.setNumero(numero);
if (flocage != null) dotation.setFlocage(flocage);
if (dotation.getEquipement().getObligatoire()) {
if (dotation.getLicence().getCategorie().isEquipementObligatoire(dotation.getEquipement().getId())) {
dotation.setChoisi(true);
} else {
dotation.setChoisi(choisiParam != null);
@@ -1,7 +1,9 @@
package com.astalange.web.controller;
import com.astalange.core.entity.Equipement;
import com.astalange.core.entity.Saison;
import com.astalange.core.repository.EquipementRepository;
import com.astalange.core.repository.SaisonRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@@ -11,9 +13,11 @@ import org.springframework.web.bind.annotation.*;
public class EquipementController {
private final EquipementRepository equipementRepository;
private final SaisonRepository saisonRepository;
public EquipementController(EquipementRepository equipementRepository) {
public EquipementController(EquipementRepository equipementRepository, SaisonRepository saisonRepository) {
this.equipementRepository = equipementRepository;
this.saisonRepository = saisonRepository;
}
@GetMapping
@@ -38,8 +42,15 @@ public class EquipementController {
@PostMapping
public String saveEquipement(@ModelAttribute("equipement") Equipement equipement) {
if (equipement.getObligatoire() == null) {
equipement.setObligatoire(false);
if (equipement.getId() == null) {
Saison activeSaison = saisonRepository.findByEstActiveTrue()
.orElseThrow(() -> new IllegalStateException("Aucune saison active trouvée"));
equipement.setSaison(activeSaison);
} else {
Equipement existing = equipementRepository.findById(equipement.getId())
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + equipement.getId()));
equipement.setSaison(existing.getSaison());
}
equipementRepository.save(equipement);
return "redirect:/equipements";
@@ -6,10 +6,12 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Controller
@Transactional
public class LicenceController {
private final AdherentRepository adherentRepository;
@@ -73,18 +75,20 @@ public class LicenceController {
licence = licenceRepository.save(licence);
// Auto-initialize dotations for all configured equipments of the active season
List<Equipement> equipements = equipementRepository.findBySaison(saisonActive);
for (Equipement eq : equipements) {
Dotation dotation = new Dotation();
dotation.setLicence(licence);
dotation.setEquipement(eq);
dotation.setTaille("");
dotation.setFlocage(adherent.getNom().toUpperCase() + " " + adherent.getPrenom());
dotation.setNumero("");
dotation.setFourni(false);
dotation.setChoisi(eq.getObligatoire());
dotationRepository.save(dotation);
// Auto-initialize dotations for all configured equipments of the category
List<CategorieEquipement> catEquipements = categorie.getCategorieEquipements();
if (catEquipements != null) {
for (CategorieEquipement ce : catEquipements) {
Dotation dotation = new Dotation();
dotation.setLicence(licence);
dotation.setEquipement(ce.getEquipement());
dotation.setTaille("");
dotation.setFlocage(adherent.getNom().toUpperCase() + " " + adherent.getPrenom());
dotation.setNumero("");
dotation.setFourni(false);
dotation.setChoisi(Boolean.TRUE.equals(ce.getObligatoire()));
dotationRepository.save(dotation);
}
}
return "redirect:/adherents/" + id + "/edit";