5 Commits
Author SHA1 Message Date
ucef d7b62ddf15 feat: map adherent size and shoe size to dotations
AS Talange CI/CD Pipeline - Production / Build & Run Unit Tests (push) Successful in 1m42s
AS Talange CI/CD Pipeline - Production / Deploy to Prod Environment (push) Successful in 2m40s
2026-07-18 00:27:51 +02:00
ucef a577e91e94 fix: correct hx-vals JSON syntax for payments pagination 2026-07-18 00:27:51 +02:00
ucef 8ca20c9a4f chore: release v1.5
AS Talange CI/CD Pipeline - Production / Build & Run Unit Tests (push) Successful in 1m53s
AS Talange CI/CD Pipeline - Production / Deploy to Prod Environment (push) Successful in 2m54s
2026-07-08 02:49:29 +02:00
ucef 92c9e80bc3 feat: update predefined shoe sizes list in public pre-registration
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 3m48s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 5m28s
2026-07-07 22:56:47 +02:00
ucef c94f89997f feat: add equipment duplication and name uniqueness validation 2026-07-07 22:56:29 +02:00
10 changed files with 90 additions and 18 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>as-talange-parent</artifactId> <artifactId>as-talange-parent</artifactId>
<groupId>com.astalange</groupId> <groupId>com.astalange</groupId>
<version>1.5-SNAPSHOT</version> <version>1.5</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>as-talange-parent</artifactId> <artifactId>as-talange-parent</artifactId>
<groupId>com.astalange</groupId> <groupId>com.astalange</groupId>
<version>1.5-SNAPSHOT</version> <version>1.5</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -123,6 +123,26 @@ public class CategorieService {
continue; continue;
} }
String matchedTaille = "";
if (licence.getAdherent() != null) {
String taillesDispo = ce.getEquipement().getTaillesDisponibles();
if (taillesDispo != null && !taillesDispo.trim().isEmpty()) {
String[] dispos = taillesDispo.split(",");
String tv = licence.getAdherent().getTailleVetement();
String pt = licence.getAdherent().getPointure();
for (String t : dispos) {
String trimmed = t.trim();
if (tv != null && !tv.trim().isEmpty() && trimmed.equalsIgnoreCase(tv.trim())) {
matchedTaille = tv.trim();
break;
} else if (pt != null && !pt.trim().isEmpty() && trimmed.equalsIgnoreCase(pt.trim())) {
matchedTaille = pt.trim();
break;
}
}
}
}
boolean found = false; boolean found = false;
for (Dotation d : currentDotations) { for (Dotation d : currentDotations) {
if (ce.getEquipement().getId().equals(d.getEquipement().getId())) { if (ce.getEquipement().getId().equals(d.getEquipement().getId())) {
@@ -130,6 +150,9 @@ public class CategorieService {
if (Boolean.TRUE.equals(ce.getObligatoire()) && !Boolean.TRUE.equals(d.getChoisi())) { if (Boolean.TRUE.equals(ce.getObligatoire()) && !Boolean.TRUE.equals(d.getChoisi())) {
d.setChoisi(true); d.setChoisi(true);
} }
if ((d.getTaille() == null || d.getTaille().trim().isEmpty()) && !matchedTaille.isEmpty()) {
d.setTaille(matchedTaille);
}
break; break;
} }
} }
@@ -146,7 +169,7 @@ public class CategorieService {
} }
newDotation.setChoisi(ce.getObligatoire() || (isMaillot && isNouvelle)); // Checked if obligatoire or if it's a new registration newDotation.setChoisi(ce.getObligatoire() || (isMaillot && isNouvelle)); // Checked if obligatoire or if it's a new registration
newDotation.setFourni(false); newDotation.setFourni(false);
newDotation.setTaille(""); newDotation.setTaille(matchedTaille);
newDotation.setNumero(""); newDotation.setNumero("");
if (licence.getAdherent() != null) { if (licence.getAdherent() != null) {
String defaultFlocage = ""; String defaultFlocage = "";
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>as-talange-parent</artifactId> <artifactId>as-talange-parent</artifactId>
<groupId>com.astalange</groupId> <groupId>com.astalange</groupId>
<version>1.5-SNAPSHOT</version> <version>1.5</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -33,34 +33,78 @@ public class EquipementController {
@GetMapping("/new") @GetMapping("/new")
public String showCreateForm(Model model) { public String showCreateForm(Model model) {
if (!model.containsAttribute("equipement")) {
model.addAttribute("equipement", new Equipement()); model.addAttribute("equipement", new Equipement());
}
return "parametrage/equipements_form"; return "parametrage/equipements_form";
} }
@GetMapping("/{id}/edit") @GetMapping("/{id}/edit")
public String showEditForm(@PathVariable Long id, Model model) { public String showEditForm(@PathVariable Long id, Model model) {
if (!model.containsAttribute("equipement")) {
Equipement equipement = equipementRepository.findById(id) Equipement equipement = equipementRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + id)); .orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + id));
model.addAttribute("equipement", equipement); model.addAttribute("equipement", equipement);
}
return "parametrage/equipements_form"; return "parametrage/equipements_form";
} }
@PostMapping @PostMapping
public String saveEquipement(@ModelAttribute("equipement") Equipement equipement) { public String saveEquipement(@ModelAttribute("equipement") Equipement equipement,
org.springframework.web.servlet.mvc.support.RedirectAttributes redirectAttributes) {
Saison activeSaison;
if (equipement.getId() == null) { if (equipement.getId() == null) {
Saison activeSaison = saisonRepository.findByEstActiveTrue() activeSaison = saisonRepository.findByEstActiveTrue()
.orElseThrow(() -> new IllegalStateException("Aucune saison active trouvée")); .orElseThrow(() -> new IllegalStateException("Aucune saison active trouvée"));
equipement.setSaison(activeSaison); equipement.setSaison(activeSaison);
} else { } else {
Equipement existing = equipementRepository.findById(equipement.getId()) Equipement existing = equipementRepository.findById(equipement.getId())
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + equipement.getId())); .orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + equipement.getId()));
equipement.setSaison(existing.getSaison()); equipement.setSaison(existing.getSaison());
activeSaison = existing.getSaison();
} }
if (activeSaison != null) {
for (Equipement e : equipementRepository.findBySaison(activeSaison)) {
if (e.getNom().equalsIgnoreCase(equipement.getNom()) && (equipement.getId() == null || !e.getId().equals(equipement.getId()))) {
redirectAttributes.addFlashAttribute("errorMessage", "Erreur : L'équipement '" + equipement.getNom() + "' existe déjà pour cette saison.");
redirectAttributes.addFlashAttribute("equipement", equipement);
return equipement.getId() == null ? "redirect:/equipements/new" : "redirect:/equipements/" + equipement.getId() + "/edit";
}
}
}
equipementRepository.save(equipement); equipementRepository.save(equipement);
return "redirect:/equipements"; return "redirect:/equipements";
} }
@PostMapping("/{id}/duplicate")
public String duplicateEquipement(@PathVariable Long id) {
Equipement existing = equipementRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Invalid equipment ID: " + id));
Equipement duplicated = new Equipement();
String newNom = existing.getNom() + " (dupliqué)";
if (newNom.length() > 100) {
newNom = newNom.substring(0, 100);
}
duplicated.setNom(newNom);
duplicated.setReference(existing.getReference());
duplicated.setDescription(existing.getDescription());
duplicated.setGestionSexe(existing.isGestionSexe());
duplicated.setTypePublic(existing.getTypePublic());
duplicated.setTaillesDisponibles(existing.getTaillesDisponibles());
duplicated.setCouleursDisponibles(existing.getCouleursDisponibles());
duplicated.setHasFlocagePrenom(existing.isHasFlocagePrenom());
duplicated.setHasFlocageInitiales(existing.isHasFlocageInitiales());
duplicated.setHasFlocageNumero(existing.isHasFlocageNumero());
duplicated.setSaison(existing.getSaison());
equipementRepository.save(duplicated);
return "redirect:/equipements";
}
@PostMapping("/{id}/delete") @PostMapping("/{id}/delete")
public String deleteEquipement(@PathVariable Long id) { public String deleteEquipement(@PathVariable Long id) {
Equipement equipement = equipementRepository.findById(id) Equipement equipement = equipementRepository.findById(id)
@@ -110,10 +110,9 @@ public class PublicInscriptionController {
"5/6ANS (Taille 116cm)", "7/8ANS (Taille 128cm)", "9/10ANS (Taille 140cm)", "5/6ANS (Taille 116cm)", "7/8ANS (Taille 128cm)", "9/10ANS (Taille 140cm)",
"11/12ANS (Taille 152cm)", "13/14ANS (Taille 164cm)", "XS", "S", "M", "L", "XL", "XXL" "11/12ANS (Taille 152cm)", "13/14ANS (Taille 164cm)", "XS", "S", "M", "L", "XL", "XXL"
)); ));
Set<String> pointures = new java.util.LinkedHashSet<>(); Set<String> pointures = new java.util.LinkedHashSet<>(java.util.Arrays.asList(
for (int i = 25; i <= 50; i++) { "27/30 (Taille 0)", "31/34 (Taille 1)", "35/38 (Taille 2)", "39/42 (Taille 3)", "43/46 (Taille 4)"
pointures.add(String.valueOf(i)); ));
}
model.addAttribute("taillesDisponibles", tailles); model.addAttribute("taillesDisponibles", tailles);
model.addAttribute("pointuresDisponibles", pointures); model.addAttribute("pointuresDisponibles", pointures);
@@ -142,7 +142,7 @@
hx-target="#paiements-table-container" hx-target="#paiements-table-container"
hx-select="#paiements-table-container" hx-select="#paiements-table-container"
hx-include="#filter-form" hx-include="#filter-form"
th:attr="hx-vals=|{'page': ${currentPage - 1}}|" th:attr="hx-vals=|{&quot;page&quot;: ${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
</button> </button>
@@ -157,7 +157,7 @@
hx-target="#paiements-table-container" hx-target="#paiements-table-container"
hx-select="#paiements-table-container" hx-select="#paiements-table-container"
hx-include="#filter-form" hx-include="#filter-form"
th:attr="hx-vals=|{'page': ${pageNum}}|" th:attr="hx-vals=|{&quot;page&quot;: ${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'}">
@@ -172,7 +172,7 @@
hx-target="#paiements-table-container" hx-target="#paiements-table-container"
hx-select="#paiements-table-container" hx-select="#paiements-table-container"
hx-include="#filter-form" hx-include="#filter-form"
th:attr="hx-vals=|{'page': ${currentPage + 1}}|" th:attr="hx-vals=|{&quot;page&quot;: ${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
</button> </button>
@@ -19,6 +19,9 @@
<div class="flex-1 overflow-auto p-6"> <div class="flex-1 overflow-auto p-6">
<div class="max-w-xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-8"> <div class="max-w-xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-8">
<div th:if="${errorMessage}" class="bg-red-50 border-l-4 border-red-500 p-4 mb-6">
<p class="text-sm text-red-700" th:text="${errorMessage}"></p>
</div>
<form th:action="@{/equipements}" th:object="${equipement}" method="post" class="space-y-6"> <form th:action="@{/equipements}" th:object="${equipement}" method="post" class="space-y-6">
<input type="hidden" th:field="*{id}" /> <input type="hidden" th:field="*{id}" />
@@ -44,6 +44,9 @@
<td class="py-4 px-6 text-gray-600" th:text="${equip.description != null ? equip.description : '-'}">Maillot de match officiel</td> <td class="py-4 px-6 text-gray-600" th:text="${equip.description != null ? equip.description : '-'}">Maillot de match officiel</td>
<td class="py-4 px-6 text-right flex justify-end space-x-3 items-center"> <td class="py-4 px-6 text-right flex justify-end space-x-3 items-center">
<a th:href="@{/equipements/{id}/edit(id=${equip.id})}" class="text-indigo-600 hover:text-indigo-900 font-medium bg-indigo-50 px-3 py-1 rounded-lg">Modifier</a> <a th:href="@{/equipements/{id}/edit(id=${equip.id})}" class="text-indigo-600 hover:text-indigo-900 font-medium bg-indigo-50 px-3 py-1 rounded-lg">Modifier</a>
<form th:action="@{/equipements/{id}/duplicate(id=${equip.id})}" method="post">
<button type="submit" class="text-green-600 hover:text-green-800 font-medium bg-green-50 px-3 py-1 rounded-lg">Dupliquer</button>
</form>
<form th:action="@{/equipements/{id}/delete(id=${equip.id})}" method="post" onsubmit="return confirm('Supprimer cet équipement ? Cela supprimera également les dotations associées de toutes les licences.');"> <form th:action="@{/equipements/{id}/delete(id=${equip.id})}" method="post" onsubmit="return confirm('Supprimer cet équipement ? Cela supprimera également les dotations associées de toutes les licences.');">
<button type="submit" class="text-red-600 hover:text-red-800 font-medium">Supprimer</button> <button type="submit" class="text-red-600 hover:text-red-800 font-medium">Supprimer</button>
</form> </form>
+1 -1
View File
@@ -13,7 +13,7 @@
<groupId>com.astalange</groupId> <groupId>com.astalange</groupId>
<artifactId>as-talange-parent</artifactId> <artifactId>as-talange-parent</artifactId>
<version>1.5-SNAPSHOT</version> <version>1.5</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>as-talange-parent</name> <name>as-talange-parent</name>