Refactor: Move equipment price and mandatory status to Categorie relationship
- 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:
@@ -282,8 +282,8 @@
|
||||
<div class="flex justify-between items-start mb-1 gap-1">
|
||||
<span class="font-semibold text-xs text-gray-800 truncate" th:text="${dot.equipement.nom}">Maillot</span>
|
||||
<span class="text-[9px] px-1.5 py-0.5 rounded-full font-bold flex-shrink-0"
|
||||
th:classappend="${dot.equipement.obligatoire ? 'bg-red-50 text-red-700 border border-red-100' : 'bg-gray-50 text-gray-600 border border-gray-100'}"
|
||||
th:text="${dot.equipement.obligatoire ? 'Obligatoire' : 'Facultatif'}">Obligatoire</span>
|
||||
th:classappend="${dot.licence.categorie.isEquipementObligatoire(dot.equipement.id) ? 'bg-red-50 text-red-700 border border-red-100' : 'bg-gray-50 text-gray-600 border border-gray-100'}"
|
||||
th:text="${dot.licence.categorie.isEquipementObligatoire(dot.equipement.id) ? 'Obligatoire' : 'Facultatif'}">Obligatoire</span>
|
||||
</div>
|
||||
<div class="text-[10px] text-gray-400 mb-2 truncate" th:title="${dot.equipement.description}" th:text="${dot.equipement.description != null and !dot.equipement.description.isEmpty() ? dot.equipement.description : 'Pas de description'}">Description</div>
|
||||
<!-- Special dynamic label for equipment style -->
|
||||
@@ -306,10 +306,10 @@
|
||||
<!-- Form inputs for bulk updating -->
|
||||
<div class="space-y-2 pt-2 border-t border-gray-100">
|
||||
<!-- Choisi checkbox for optional equipment -->
|
||||
<div class="flex items-center mb-1.5 bg-blue-50/50 p-1.5 rounded border border-blue-100/50" th:if="${!dot.equipement.obligatoire}">
|
||||
<div class="flex items-center mb-1.5 bg-blue-50/50 p-1.5 rounded border border-blue-100/50" th:if="${!dot.licence.categorie.isEquipementObligatoire(dot.equipement.id)}">
|
||||
<label class="flex items-center space-x-1.5 cursor-pointer select-none">
|
||||
<input type="checkbox" th:name="'choisi_' + ${dot.id}" th:checked="${dot.choisi}" class="w-3.5 h-3.5 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
|
||||
<span class="text-[10px] font-bold text-blue-800 uppercase tracking-wide" th:text="'Commander (+ ' + ${dot.equipement.prixContribution} + ' €)'">Commander (+ 30 €)</span>
|
||||
<span class="text-[10px] font-bold text-blue-800 uppercase tracking-wide" th:text="'Commander (+ ' + ${dot.licence.categorie.getEquipementPrix(dot.equipement.id)} + ' €)'">Commander (+ 30 €)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -50,6 +50,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 border-t border-gray-100">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Équipements liés à la catégorie</label>
|
||||
<div class="space-y-3 max-h-64 overflow-y-auto">
|
||||
<th:block th:each="equi : ${allEquipements}">
|
||||
<div class="flex items-center space-x-4 bg-gray-50 p-2 rounded-lg border border-gray-100">
|
||||
<div class="w-1/3">
|
||||
<span class="block text-sm font-medium text-gray-900" th:text="${equi.nom}"></span>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
<select th:name="'etatEquipement_' + ${equi.id}" class="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
<option value="NON_LIE" th:selected="${!categorie.hasEquipement(equi.id)}">Non lié</option>
|
||||
<option value="OBLIGATOIRE" th:selected="${categorie.hasEquipement(equi.id) and categorie.isEquipementObligatoire(equi.id)}">Obligatoire (inclus)</option>
|
||||
<option value="OPTIONNEL" th:selected="${categorie.hasEquipement(equi.id) and !categorie.isEquipementObligatoire(equi.id)}">Optionnel (payant)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-1/3 flex items-center space-x-2">
|
||||
<label class="text-xs text-gray-500">Prix (€)</label>
|
||||
<input type="number" step="0.01" th:name="'prixEquipement_' + ${equi.id}"
|
||||
th:value="${categorie.getEquipementPrix(equi.id)}"
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4 flex justify-end space-x-3 border-t border-gray-100">
|
||||
<a th:href="@{/categories}" class="px-4 py-2 text-sm font-medium text-gray-700 border border-gray-300 rounded-lg hover:bg-gray-50">Annuler</a>
|
||||
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700">Enregistrer</button>
|
||||
|
||||
@@ -31,18 +31,8 @@
|
||||
<textarea th:field="*{description}" rows="3" class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Prix de contribution (€) - Pour équipement facultatif</label>
|
||||
<input type="number" step="0.01" th:field="*{prixContribution}" required class="w-full border border-gray-300 rounded-lg px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
<p class="text-xs text-gray-500 mt-1">Si l'équipement est facultatif, ce montant sera ajouté au tarif global de la licence s'il est commandé.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3">
|
||||
<input type="checkbox" id="obligatoire" th:field="*{obligatoire}" class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
|
||||
<label for="obligatoire" class="text-sm font-medium text-gray-700">
|
||||
Cet équipement est obligatoire pour tous les licenciés (inclus dans la licence)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pt-4 flex justify-end space-x-3 border-t border-gray-100">
|
||||
<a th:href="@{/equipements}" class="px-4 py-2 text-sm font-medium text-gray-700 border border-gray-300 rounded-lg hover:bg-gray-50">Annuler</a>
|
||||
|
||||
@@ -30,24 +30,16 @@
|
||||
<tr class="bg-gray-50 text-gray-500 text-sm uppercase tracking-wider border-b border-gray-200">
|
||||
<th class="py-3 px-6 font-medium text-left">Nom de l'équipement</th>
|
||||
<th class="py-3 px-6 font-medium text-left">Description</th>
|
||||
<th class="py-3 px-6 font-medium text-center">Obligatoire</th>
|
||||
<th class="py-3 px-6 font-medium text-center">Prix Contribution</th>
|
||||
<th class="py-3 px-6 font-medium text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 text-sm">
|
||||
<tr th:if="${#lists.isEmpty(equipements)}">
|
||||
<td colspan="5" class="py-8 text-center text-gray-500">Aucun équipement n'est paramétré.</td>
|
||||
<td colspan="3" class="py-8 text-center text-gray-500">Aucun équipement n'est paramétré.</td>
|
||||
</tr>
|
||||
<tr th:each="equip : ${equipements}" class="hover:bg-gray-50 transition-colors">
|
||||
<td class="py-4 px-6 font-medium text-gray-900" th:text="${equip.nom}">Maillot</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-center">
|
||||
<span class="px-2.5 py-1 text-xs font-semibold rounded-full"
|
||||
th:classappend="${equip.obligatoire ? 'bg-red-100 text-red-800' : 'bg-gray-100 text-gray-800'}"
|
||||
th:text="${equip.obligatoire ? 'Oui' : 'Non'}">Oui</span>
|
||||
</td>
|
||||
<td class="py-4 px-6 text-center font-medium text-gray-700" th:text="${equip.obligatoire ? 'Inclus' : (equip.prixContribution + ' €')}">30 €</td>
|
||||
<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>
|
||||
<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.');">
|
||||
|
||||
Reference in New Issue
Block a user