diff --git a/as-talange-core/pom.xml b/as-talange-core/pom.xml
index 5f80c92..2b7214b 100644
--- a/as-talange-core/pom.xml
+++ b/as-talange-core/pom.xml
@@ -61,5 +61,11 @@
1.77
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
diff --git a/as-talange-core/src/test/java/com/astalange/core/AdherentRecalculationTest.java b/as-talange-core/src/test/java/com/astalange/core/AdherentRecalculationTest.java
new file mode 100644
index 0000000..2f1db6f
--- /dev/null
+++ b/as-talange-core/src/test/java/com/astalange/core/AdherentRecalculationTest.java
@@ -0,0 +1,93 @@
+package com.astalange.core;
+
+import com.astalange.core.entity.*;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class AdherentRecalculationTest {
+
+ @Test
+ public void testLicencePriceCalculationBasedOnResidencyAndCategory() {
+ Adherent adherent = new Adherent();
+ adherent.setNom("Doe");
+ adherent.setPrenom("John");
+ adherent.setDateNaissance(LocalDate.of(2015, 5, 10)); // born 2015
+ adherent.setResidentTalange(true);
+
+ Categorie u11 = new Categorie();
+ u11.setNom("U11");
+ u11.setAnneeMin(2015);
+ u11.setAnneeMax(2015);
+ u11.setTarifBase(new BigDecimal("130.00"));
+ u11.setTarifExterieur(new BigDecimal("160.00"));
+
+ Licence licence = new Licence();
+ licence.setAdherent(adherent);
+ licence.setCategorie(u11);
+
+ // Under residentTalange = true, base price should be 130
+ assertEquals(new BigDecimal("130.00"), licence.getPrixTotal());
+
+ // Under residentTalange = false, base price should be 160
+ adherent.setResidentTalange(false);
+ assertEquals(new BigDecimal("160.00"), licence.getPrixTotal());
+ }
+
+ @Test
+ public void testCategoryUpdateOnBirthdateChangeSimulation() {
+ // Set up adherent
+ Adherent adherent = new Adherent();
+ adherent.setDateNaissance(LocalDate.of(2015, 5, 10)); // 2015 birth year
+
+ Saison saison = new Saison();
+ saison.setNom("2025-2026");
+ saison.setEstActive(true);
+
+ // Set up categories
+ Categorie u11 = new Categorie();
+ u11.setNom("U11");
+ u11.setAnneeMin(2015);
+ u11.setAnneeMax(2015);
+ u11.setSaison(saison);
+ u11.setTarifBase(new BigDecimal("130.00"));
+
+ Categorie u12 = new Categorie();
+ u12.setNom("U12");
+ u12.setAnneeMin(2014);
+ u12.setAnneeMax(2014);
+ u12.setSaison(saison);
+ u12.setTarifBase(new BigDecimal("140.00"));
+
+ List categories = List.of(u11, u12);
+
+ // Licence initially created for U11
+ Licence licence = new Licence();
+ licence.setAdherent(adherent);
+ licence.setCategorie(u11);
+ licence.setSaison(saison);
+
+ // Simulation of date of birth changing to 2014 (U12)
+ adherent.setDateNaissance(LocalDate.of(2014, 5, 10));
+ int newBirthYear = adherent.getDateNaissance().getYear();
+
+ // Simulate Controller's update logic
+ Categorie newCat = categories.stream()
+ .filter(c -> newBirthYear >= c.getAnneeMin() && newBirthYear <= c.getAnneeMax())
+ .findFirst()
+ .orElse(null);
+
+ assertNotNull(newCat);
+ assertEquals("U12", newCat.getNom());
+ licence.setCategorie(newCat);
+
+ // Verify the licence's category has updated to U12
+ assertEquals("U12", licence.getCategorie().getNom());
+ // Verify price total is updated dynamically
+ assertEquals(new BigDecimal("140.00"), licence.getPrixTotal());
+ }
+}
diff --git a/as-talange-web/src/main/resources/templates/adherents/form.html b/as-talange-web/src/main/resources/templates/adherents/form.html
index d49b40f..6bbd1b4 100644
--- a/as-talange-web/src/main/resources/templates/adherents/form.html
+++ b/as-talange-web/src/main/resources/templates/adherents/form.html
@@ -322,8 +322,11 @@
@@ -443,6 +446,7 @@
document.addEventListener("DOMContentLoaded", function() {
const dateInput = document.getElementById('dateNaissance');
const repBlock = document.getElementById('representantBlock');
+ const residentCheckbox = document.getElementById('residentTalange');
function calculateAge(birthday) {
const ageDifMs = Date.now() - birthday.getTime();
@@ -464,7 +468,92 @@
}
}
- dateInput.addEventListener('change', updateRepresentantBlock);
+ function updateLicencesCategoryAndPrices() {
+ if (!dateInput.value) return;
+ const dob = new Date(dateInput.value);
+ const birthYear = dob.getFullYear();
+ const isResident = residentCheckbox ? residentCheckbox.checked : true;
+
+ const categories = [];
+ const catSelect = document.getElementById('categorieSelect');
+ if (catSelect) {
+ Array.from(catSelect.options).forEach(opt => {
+ const id = opt.value;
+ const nom = opt.getAttribute('data-nom');
+ const min = parseInt(opt.getAttribute('data-annee-min'));
+ const max = parseInt(opt.getAttribute('data-annee-max'));
+ const tarifBase = parseFloat(opt.getAttribute('data-tarif-base') || '0');
+ const tarifExterieur = parseFloat(opt.getAttribute('data-tarif-exterieur') || '0');
+ if (id) {
+ categories.push({ id, nom, min, max, tarifBase, tarifExterieur });
+ }
+ });
+ }
+
+ if (categories.length === 0) return;
+
+ const matchingCat = categories.find(c => birthYear >= c.min && birthYear <= c.max);
+ if (!matchingCat) return;
+
+ document.querySelectorAll('.licence-row').forEach(row => {
+ row.setAttribute('data-tarif-base', matchingCat.tarifBase);
+ row.setAttribute('data-tarif-exterieur', matchingCat.tarifExterieur);
+
+ const baseTarif = isResident ? matchingCat.tarifBase : matchingCat.tarifExterieur;
+ const equipContrib = parseFloat(row.getAttribute('data-equipements-contrib') || '0');
+ const totalPaye = parseFloat(row.getAttribute('data-total-paye') || '0');
+ const newTotal = baseTarif + equipContrib;
+ const newReste = Math.max(0, newTotal - totalPaye);
+
+ const catCell = row.querySelector('.cell-tarif-categorie');
+ if (catCell) {
+ const parentTd = catCell.parentNode;
+ const spanName = parentTd.querySelector('span:not(.cell-tarif-categorie)');
+ if (spanName) {
+ spanName.textContent = matchingCat.nom;
+ }
+ catCell.textContent = baseTarif.toFixed(2) + ' € (Catégorie)';
+ }
+
+ const globalCell = row.querySelector('.cell-tarif-global');
+ if (globalCell) {
+ globalCell.textContent = newTotal.toFixed(2) + ' €';
+ }
+
+ const resteCell = row.querySelector('.cell-reste-payer');
+ if (resteCell) {
+ resteCell.textContent = newReste.toFixed(2) + ' €';
+ if (newReste === 0) {
+ resteCell.classList.remove('text-red-600');
+ resteCell.classList.add('text-green-600');
+ } else {
+ resteCell.classList.remove('text-green-600');
+ resteCell.classList.add('text-red-600');
+ }
+ }
+
+ const payerBtn = row.querySelector('.btn-payer');
+ if (payerBtn) {
+ if (newReste > 0) {
+ payerBtn.classList.remove('hidden');
+ const licId = payerBtn.getAttribute('data-licence-id');
+ payerBtn.setAttribute('onclick', `openPaiementModal(${licId}, ${newReste})`);
+ } else {
+ payerBtn.classList.add('hidden');
+ }
+ }
+
+ const modifierBtn = row.querySelector('button[onclick^="openLicenceModal"]');
+ if (modifierBtn) {
+ modifierBtn.setAttribute('data-categorie-id', matchingCat.id);
+ }
+ });
+ }
+
+ dateInput.addEventListener('change', function() {
+ updateRepresentantBlock();
+ updateLicencesCategoryAndPrices();
+ });
// Initial check in case it's a validation error reload
updateRepresentantBlock();
@@ -475,7 +564,6 @@
}
// Recalculate price dynamically when residentTalange checkbox changes
- const residentCheckbox = document.getElementById('residentTalange');
if (residentCheckbox) {
residentCheckbox.addEventListener('change', function() {
const isResident = this.checked;