fix: improve adherent size matching and conditional visibility in dotations
AS Talange CI/CD Pipeline / Build & Run Unit Tests (push) Successful in 4m12s
AS Talange CI/CD Pipeline / Deploy to Test Environment (push) Successful in 6m0s

This commit is contained in:
2026-07-29 16:28:51 +02:00
parent baa4d3c0cd
commit da0443f655
4 changed files with 102 additions and 13 deletions
@@ -64,4 +64,93 @@ public class Dotation {
public Boolean getChoisi() { return choisi; } public Boolean getChoisi() { return choisi; }
public void setChoisi(Boolean choisi) { this.choisi = choisi; } public void setChoisi(Boolean choisi) { this.choisi = choisi; }
public static boolean isSizeMatch(String option, String adherentSize) {
if (option == null || adherentSize == null) return false;
String opt = option.trim();
String adh = adherentSize.trim();
if (opt.isEmpty() || adh.isEmpty()) return false;
if (opt.equalsIgnoreCase(adh)) return true;
String optLower = opt.toLowerCase();
String adhLower = adh.toLowerCase();
// Extract height numbers (116, 128, 140, 152, 164)
java.util.regex.Pattern numPattern = java.util.regex.Pattern.compile("\\b(116|128|140|152|164)\\b");
java.util.regex.Matcher adhNumMatcher = numPattern.matcher(adhLower);
java.util.regex.Matcher optNumMatcher = numPattern.matcher(optLower);
if (adhNumMatcher.find()) {
String num = adhNumMatcher.group(1);
if (optLower.contains(num)) {
return true;
} }
} else if (optNumMatcher.find()) {
String num = optNumMatcher.group(1);
if (adhLower.contains(num)) {
return true;
}
}
// Extract age patterns (5/6, 7/8, 9/10, 11/12, 13/14) or (5-6, 7-8, 9-10, 11-12, 13-14)
java.util.regex.Pattern agePattern = java.util.regex.Pattern.compile("\\b(5[/\\-]6|7[/\\-]8|9[/\\-]10|11[/\\-]12|13[/\\-]14)\\b");
java.util.regex.Matcher adhAge = agePattern.matcher(adhLower);
if (adhAge.find()) {
String ageKey = adhAge.group(1).replace("-", "/");
String altAgeKey = ageKey.replace("/", "-");
if (optLower.contains(ageKey) || optLower.contains(altAgeKey)) {
return true;
}
}
// Extract shoe sizes / pointures (27/30, 31/34, 35/38, 39/42, 43/46)
java.util.regex.Pattern ptPattern = java.util.regex.Pattern.compile("\\b(27[/\\-]30|31[/\\-]34|35[/\\-]38|39[/\\-]42|43[/\\-]46)\\b");
java.util.regex.Matcher adhPt = ptPattern.matcher(adhLower);
if (adhPt.find()) {
String ptKey = adhPt.group(1).replace("-", "/");
String altPtKey = ptKey.replace("/", "-");
if (optLower.contains(ptKey) || optLower.contains(altPtKey)) {
return true;
}
}
// Extract "Taille X"
java.util.regex.Pattern tNumPattern = java.util.regex.Pattern.compile("taille\\s*([0-4])");
java.util.regex.Matcher adhTNum = tNumPattern.matcher(adhLower);
if (adhTNum.find()) {
String tNum = adhTNum.group(1);
if (optLower.contains("taille " + tNum) || optLower.contains("taille" + tNum) || optLower.equals(tNum)) {
return true;
}
}
// Standalone letter sizes (XXL, XL, L, M, S, XS)
java.util.regex.Pattern letterPattern = java.util.regex.Pattern.compile("\\b(xxl|xl|l|m|s|xs)\\b");
java.util.regex.Matcher adhLetter = letterPattern.matcher(adhLower);
java.util.regex.Matcher optLetter = letterPattern.matcher(optLower);
if (adhLetter.find() && optLetter.find()) {
if (adhLetter.group(1).equals(optLetter.group(1))) {
return true;
}
}
if (opt.length() >= 2 && adhLower.contains(optLower)) return true;
if (adh.length() >= 2 && optLower.contains(adhLower)) return true;
return false;
}
public boolean isTailleOptionSelected(String option) {
if (option == null) return false;
String trimmedOpt = option.trim();
if (this.taille != null && !this.taille.trim().isEmpty()) {
return this.taille.trim().equalsIgnoreCase(trimmedOpt);
}
if (this.licence == null || this.licence.getAdherent() == null) {
return false;
}
Adherent adherent = this.licence.getAdherent();
return isSizeMatch(trimmedOpt, adherent.getTailleVetement()) || isSizeMatch(trimmedOpt, adherent.getPointure());
}
}
@@ -132,11 +132,8 @@ public class CategorieService {
String pt = licence.getAdherent().getPointure(); String pt = licence.getAdherent().getPointure();
for (String t : dispos) { for (String t : dispos) {
String trimmed = t.trim(); String trimmed = t.trim();
if (tv != null && !tv.trim().isEmpty() && trimmed.equalsIgnoreCase(tv.trim())) { if (Dotation.isSizeMatch(trimmed, tv) || Dotation.isSizeMatch(trimmed, pt)) {
matchedTaille = tv.trim(); matchedTaille = trimmed;
break;
} else if (pt != null && !pt.trim().isEmpty() && trimmed.equalsIgnoreCase(pt.trim())) {
matchedTaille = pt.trim();
break; break;
} }
} }
@@ -234,12 +234,20 @@ public class AdherentController {
existing.setLieuNaissancePays(adherent.getLieuNaissancePays()); existing.setLieuNaissancePays(adherent.getLieuNaissancePays());
existing.setNationalite(adherent.getNationalite()); existing.setNationalite(adherent.getNationalite());
existing.setEmail(adherent.getEmail()); existing.setEmail(adherent.getEmail());
existing.setTelephone(adherent.getTelephone());
existing.setRepresentantLegal(adherent.getRepresentantLegal()); existing.setRepresentantLegal(adherent.getRepresentantLegal());
existing.setResidentTalange(adherent.isResidentTalange()); existing.setResidentTalange(adherent.isResidentTalange());
existing.setSexe(adherent.getSexe()); existing.setSexe(adherent.getSexe());
existing.setTypeMaillot(adherent.getTypeMaillot()); existing.setTypeMaillot(adherent.getTypeMaillot());
existing.setTailleVetement(adherent.getTailleVetement());
existing.setPointure(adherent.getPointure());
adherentRepository.save(existing); adherentRepository.save(existing);
List<Licence> licences = licenceRepository.findByAdherentId(existing.getId());
for (Licence licence : licences) {
categorieService.syncDotationsForLicence(licence);
}
} else { } else {
adherentRepository.save(adherent); adherentRepository.save(adherent);
} }
@@ -413,22 +413,17 @@
<div th:if="${dot.equipement.taillesDisponibles != null and !dot.equipement.taillesDisponibles.trim().isEmpty()}"> <div th:if="${dot.equipement.taillesDisponibles != null and !dot.equipement.taillesDisponibles.trim().isEmpty()}">
<label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label> <label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label>
<select th:name="'taille_' + ${dot.id}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none"> <select th:name="'taille_' + ${dot.id}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none">
<option value="" th:selected="${dot.taille == null or dot.taille.isEmpty()}">Choisir...</option> <option value="">Choisir...</option>
<option th:if="${dot.taille != null and !dot.taille.isEmpty()}" <option th:if="${dot.taille != null and !dot.taille.isEmpty() and !#strings.contains(dot.equipement.taillesDisponibles, dot.taille)}"
th:value="${dot.taille}" th:value="${dot.taille}"
th:text="${dot.taille} + ' (Actuelle)'" th:text="${dot.taille} + ' (Actuelle)'"
selected></option> selected></option>
<option th:each="t : ${#strings.arraySplit(dot.equipement.taillesDisponibles, ',')}" <option th:each="t : ${#strings.arraySplit(dot.equipement.taillesDisponibles, ',')}"
th:if="${dot.taille == null or dot.taille != #strings.trim(t)}"
th:value="${#strings.trim(t)}" th:value="${#strings.trim(t)}"
th:text="${#strings.trim(t)}" th:text="${#strings.trim(t)}"
th:selected="${dot.taille == null and (#strings.trim(t) == adherent.tailleVetement or #strings.trim(t) == adherent.pointure)}"></option> th:selected="${dot.isTailleOptionSelected(#strings.trim(t))}"></option>
</select> </select>
</div> </div>
<div th:if="${(dot.equipement.taillesDisponibles == null or dot.equipement.taillesDisponibles.trim().isEmpty())}">
<label class="block text-[9px] uppercase font-bold text-gray-400">Taille</label>
<input type="text" th:name="'taille_' + ${dot.id}" th:value="${dot.taille != null and !dot.taille.isEmpty() ? dot.taille : (adherent.tailleVetement != null ? adherent.tailleVetement : '')}" class="w-full text-xs border border-gray-300 rounded px-1 py-0.5 focus:ring-1 focus:ring-blue-500 outline-none" placeholder="Taille/Pointure">
</div>
<div th:if="${dot.equipement.couleursDisponibles != null and !dot.equipement.couleursDisponibles.trim().isEmpty()}"> <div th:if="${dot.equipement.couleursDisponibles != null and !dot.equipement.couleursDisponibles.trim().isEmpty()}">
<label class="block text-[9px] uppercase font-bold text-gray-400">Couleur</label> <label class="block text-[9px] uppercase font-bold text-gray-400">Couleur</label>