mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			d143a1cd44
			...
			oidc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c411197af3 | ||
|  | cdc6f0a3f8 | 
| @@ -21,3 +21,6 @@ EMAIL_PASSWORD=CHANGE_ME | |||||||
| # Wiki configuration | # Wiki configuration | ||||||
| WIKI_USER=NoteKfet2020 | WIKI_USER=NoteKfet2020 | ||||||
| WIKI_PASSWORD= | WIKI_PASSWORD= | ||||||
|  |  | ||||||
|  | # OIDC | ||||||
|  | OIDC_RSA_PRIVATE_KEY=CHANGE_ME | ||||||
|   | |||||||
| @@ -61,8 +61,8 @@ Bien que cela permette de créer une instance sur toutes les distributions, | |||||||
| 6. (Optionnel) **Création d'une clé privée OpenID Connect** | 6. (Optionnel) **Création d'une clé privée OpenID Connect** | ||||||
|  |  | ||||||
| Pour activer le support d'OpenID Connect, il faut générer une clé privée, par | Pour activer le support d'OpenID Connect, il faut générer une clé privée, par | ||||||
| exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son | exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et copier la clé dans .env dans le champ | ||||||
| emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`). | `OIDC_RSA_PRIVATE_KEY`. | ||||||
|  |  | ||||||
| 7.  Enjoy : | 7.  Enjoy : | ||||||
|  |  | ||||||
| @@ -237,8 +237,8 @@ Sinon vous pouvez suivre les étapes décrites ci-dessous. | |||||||
| 7. **Création d'une clé privée OpenID Connect** | 7. **Création d'une clé privée OpenID Connect** | ||||||
|  |  | ||||||
| Pour activer le support d'OpenID Connect, il faut générer une clé privée, par | Pour activer le support d'OpenID Connect, il faut générer une clé privée, par | ||||||
| exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner son | exemple avec openssl (`openssl genrsa -out oidc.key 4096`), et renseigner le champ | ||||||
| emplacement dans `OIDC_RSA_PRIVATE_KEY` (par défaut `/var/secrets/oidc.key`). | `OIDC_RSA_PRIVATE_KEY` dans le .env (par défaut `/var/secrets/oidc.key`). | ||||||
|  |  | ||||||
| 8.  *Enjoy \o/* | 8.  *Enjoy \o/* | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,19 +0,0 @@ | |||||||
| # Generated by Django 4.2.20 on 2025-05-08 19:07 |  | ||||||
|  |  | ||||||
| from django.db import migrations, models |  | ||||||
| import django.db.models.deletion |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
|  |  | ||||||
|     dependencies = [ |  | ||||||
|         ('activity', '0006_guest_school'), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     operations = [ |  | ||||||
|         migrations.AlterField( |  | ||||||
|             model_name='guest', |  | ||||||
|             name='activity', |  | ||||||
|             field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='activity.activity'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
| @@ -234,7 +234,7 @@ class Guest(models.Model): | |||||||
|     """ |     """ | ||||||
|     activity = models.ForeignKey( |     activity = models.ForeignKey( | ||||||
|         Activity, |         Activity, | ||||||
|         on_delete=models.CASCADE, |         on_delete=models.PROTECT, | ||||||
|         related_name='+', |         related_name='+', | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -95,23 +95,5 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|             errMsg(xhr.responseJSON); |             errMsg(xhr.responseJSON); | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|     $("#delete_activity").click(function () { |  | ||||||
|         if (!confirm("{% trans 'Are you sure you want to delete this activity?' %}")) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $.ajax({ |  | ||||||
|             url: "/api/activity/activity/{{ activity.pk }}/", |  | ||||||
|             type: "DELETE", |  | ||||||
|             headers: { |  | ||||||
|                 "X-CSRFTOKEN": CSRF_TOKEN |  | ||||||
|             } |  | ||||||
|         }).done(function () { |  | ||||||
|             addMsg("{% trans 'Activity deleted' %}", "success"); |  | ||||||
|             window.location.href = "/activity/";  // Redirige vers la liste des activités |  | ||||||
|         }).fail(function (xhr) { |  | ||||||
|             errMsg(xhr.responseJSON); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
| </script> | </script> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|   | |||||||
| @@ -38,7 +38,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
| </a> | </a> | ||||||
|  |  | ||||||
| <input id="alias" type="text" class="form-control" placeholder="Nom/note ..."> | <input id="alias" type="text" class="form-control" placeholder="Nom/note ..."> | ||||||
| <button id="trigger" class="btn btn-secondary">Click me !</button> |  | ||||||
|  |  | ||||||
| <hr> | <hr> | ||||||
|  |  | ||||||
| @@ -64,46 +63,15 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|         refreshBalance(); |         refreshBalance(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function process_qrcode() { |  | ||||||
|         let name = alias_obj.val(); |  | ||||||
|         $.get("/api/note/note?search=" + name + "&format=json").done( |  | ||||||
|             function (res) { |  | ||||||
|                 let note = res.results[0]; |  | ||||||
|                 $.post("/api/activity/entry/?format=json", { |  | ||||||
|                     csrfmiddlewaretoken: CSRF_TOKEN, |  | ||||||
|                     activity: {{ activity.id }}, |  | ||||||
|                     note: note.id, |  | ||||||
|                     guest: null |  | ||||||
|                 }).done(function () { |  | ||||||
|                     addMsg(interpolate(gettext( |  | ||||||
|                         "Entry made for %s whose balance is %s €"), |  | ||||||
|                         [note.name, note.balance / 100]), "success", 4000); |  | ||||||
|                     reloadTable(true); |  | ||||||
|                 }).fail(function (xhr) { |  | ||||||
|                     errMsg(xhr.responseJSON, 4000); |  | ||||||
|                 }); |  | ||||||
|             }).fail(function (xhr) { |  | ||||||
|                 errMsg(xhr.responseJSON, 4000); |  | ||||||
|             }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     alias_obj.keyup(function(event) { |     alias_obj.keyup(function(event) { | ||||||
|         let code = event.originalEvent.keyCode |         let code = event.originalEvent.keyCode | ||||||
|         if (65 <= code <= 122 || code === 13) { |         if (65 <= code <= 122 || code === 13) { | ||||||
|             debounce(reloadTable)() |             debounce(reloadTable)() | ||||||
|         } |         } | ||||||
|         if (code === 0) |  | ||||||
|             process_qrcode(); |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     $(document).ready(init); |     $(document).ready(init); | ||||||
|  |  | ||||||
|     alias_obj2 = document.getElementById("alias"); |  | ||||||
|     $("#trigger").click(function (e) { |  | ||||||
|         addMsg("Clicked", "success", 1000); |  | ||||||
|         alias_obj.val(alias_obj.val() + "\0"); |  | ||||||
|         alias_obj2.dispatchEvent(new KeyboardEvent('keyup')); |  | ||||||
|     }) |  | ||||||
|     function init() { |     function init() { | ||||||
|         $(".table-row").click(function (e) { |         $(".table-row").click(function (e) { | ||||||
|             let target = e.target.parentElement; |             let target = e.target.parentElement; | ||||||
|   | |||||||
| @@ -70,10 +70,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|             {% if ".change_"|has_perm:activity %} |             {% if ".change_"|has_perm:activity %} | ||||||
|                 <a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_update' pk=activity.pk %}" data-turbolinks="false"> {% trans "edit"|capfirst %}</a> |                 <a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_update' pk=activity.pk %}" data-turbolinks="false"> {% trans "edit"|capfirst %}</a> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|             {% if not activity.valid and ".delete_"|has_perm:activity %} |             {% if activity.activity_type.can_invite and not activity_started %} | ||||||
|                 <a class="btn btn-danger btn-sm my-1" id="delete_activity"> {% trans "delete"|capfirst %} </a> |  | ||||||
|             {% endif %} |  | ||||||
|             {% if activity.activity_type.can_invite and not activity_started and activity.valid %} |  | ||||||
|                 <a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_invite' pk=activity.pk %}" data-turbolinks="false"> {% trans "Invite" %}</a> |                 <a class="btn btn-primary btn-sm my-1" href="{% url 'activity:activity_invite' pk=activity.pk %}" data-turbolinks="false"> {% trans "Invite" %}</a> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|         {% endif %} |         {% endif %} | ||||||
|   | |||||||
| @@ -15,5 +15,4 @@ urlpatterns = [ | |||||||
|     path('<int:pk>/update/', views.ActivityUpdateView.as_view(), name='activity_update'), |     path('<int:pk>/update/', views.ActivityUpdateView.as_view(), name='activity_update'), | ||||||
|     path('new/', views.ActivityCreateView.as_view(), name='activity_create'), |     path('new/', views.ActivityCreateView.as_view(), name='activity_create'), | ||||||
|     path('calendar.ics', views.CalendarView.as_view(), name='calendar_ics'), |     path('calendar.ics', views.CalendarView.as_view(), name='calendar_ics'), | ||||||
|     path('<int:pk>/delete', views.ActivityDeleteView.as_view(), name='delete_activity'), |  | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ from django.contrib.contenttypes.models import ContentType | |||||||
| from django.core.exceptions import PermissionDenied | from django.core.exceptions import PermissionDenied | ||||||
| from django.db import transaction | from django.db import transaction | ||||||
| from django.db.models import F, Q | from django.db.models import F, Q | ||||||
| from django.http import HttpResponse, JsonResponse | from django.http import HttpResponse | ||||||
| from django.urls import reverse_lazy | from django.urls import reverse_lazy | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
| from django.utils.decorators import method_decorator | from django.utils.decorators import method_decorator | ||||||
| @@ -153,34 +153,6 @@ class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | |||||||
|         return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) |         return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ActivityDeleteView(View): |  | ||||||
|     """ |  | ||||||
|     Deletes an Activity |  | ||||||
|     """ |  | ||||||
|     def delete(self, request, pk): |  | ||||||
|         try: |  | ||||||
|             activity = Activity.objects.get(pk=pk) |  | ||||||
|             activity.delete() |  | ||||||
|             return JsonResponse({"message": "Activity deleted"}) |  | ||||||
|         except Activity.DoesNotExist: |  | ||||||
|             return JsonResponse({"error": "Activity not found"}, status=404) |  | ||||||
|  |  | ||||||
|     def dispatch(self, *args, **kwargs): |  | ||||||
|         """ |  | ||||||
|         Don't display the delete button if the user has no right to delete. |  | ||||||
|         """ |  | ||||||
|         if not self.request.user.is_authenticated: |  | ||||||
|             return self.handle_no_permission() |  | ||||||
|  |  | ||||||
|         activity = Activity.objects.get(pk=self.kwargs["pk"]) |  | ||||||
|         if not PermissionBackend.check_perm(self.request, "activity.delete_activity", activity): |  | ||||||
|             raise PermissionDenied(_("You are not allowed to delete this activity.")) |  | ||||||
|  |  | ||||||
|         if activity.valid: |  | ||||||
|             raise PermissionDenied(_("This activity is valid.")) |  | ||||||
|         return super().dispatch(*args, **kwargs) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): | class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView): | ||||||
|     """ |     """ | ||||||
|     Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` |     Invite a Guest, The rules to invites someone are defined in `forms:activity.GuestForm` | ||||||
|   | |||||||
| @@ -169,8 +169,7 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     template_name = "food/food_update.html" |     template_name = "food/food_update.html" | ||||||
|  |  | ||||||
|     def get_sample_object(self): |     def get_sample_object(self): | ||||||
|         # We choose a club which may work or BDE else |         return BasicFood( | ||||||
|         food = BasicFood( |  | ||||||
|             name="", |             name="", | ||||||
|             owner_id=1, |             owner_id=1, | ||||||
|             expiry_date=timezone.now(), |             expiry_date=timezone.now(), | ||||||
| @@ -179,14 +178,6 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|             date_type='DLC', |             date_type='DLC', | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         for membership in self.request.user.memberships.all(): |  | ||||||
|             club_id = membership.club.id |  | ||||||
|             food.owner_id = club_id |  | ||||||
|             if PermissionBackend.check_perm(self.request, "food.add_basicfood", food): |  | ||||||
|                 return food |  | ||||||
|  |  | ||||||
|         return food |  | ||||||
|  |  | ||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         if QRCode.objects.filter(qr_code_number=self.kwargs['slug']).count() > 0: |         if QRCode.objects.filter(qr_code_number=self.kwargs['slug']).count() > 0: | ||||||
| @@ -237,22 +228,13 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     template_name = "food/food_update.html" |     template_name = "food/food_update.html" | ||||||
|  |  | ||||||
|     def get_sample_object(self): |     def get_sample_object(self): | ||||||
|         # We choose a club which may work or BDE else |         return TransformedFood( | ||||||
|         food = TransformedFood( |  | ||||||
|             name="", |             name="", | ||||||
|             owner_id=1, |             owner_id=1, | ||||||
|             expiry_date=timezone.now(), |             expiry_date=timezone.now(), | ||||||
|             is_ready=True, |             is_ready=True, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         for membership in self.request.user.memberships.all(): |  | ||||||
|             club_id = membership.club.id |  | ||||||
|             food.owner_id = club_id |  | ||||||
|             if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food): |  | ||||||
|                 return food |  | ||||||
|  |  | ||||||
|         return food |  | ||||||
|  |  | ||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         form.instance.expiry_date = timezone.now() + timedelta(days=3) |         form.instance.expiry_date = timezone.now() + timedelta(days=3) | ||||||
| @@ -264,10 +246,10 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk}) |         return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk}) | ||||||
|  |  | ||||||
|  |  | ||||||
| MAX_FORMS = 100 | MAX_FORMS = 10 | ||||||
|  |  | ||||||
|  |  | ||||||
| class ManageIngredientsView(LoginRequiredMixin, UpdateView): | class ManageIngredientsView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | ||||||
|     """ |     """ | ||||||
|     A view to manage ingredient for a transformed food |     A view to manage ingredient for a transformed food | ||||||
|     """ |     """ | ||||||
| @@ -298,14 +280,6 @@ class ManageIngredientsView(LoginRequiredMixin, UpdateView): | |||||||
|                     ingredient.end_of_life = _('Fully used in {meal}'.format( |                     ingredient.end_of_life = _('Fully used in {meal}'.format( | ||||||
|                         meal=self.object.name)) |                         meal=self.object.name)) | ||||||
|                     ingredient.save() |                     ingredient.save() | ||||||
|         # We recalculate new expiry date and allergens |  | ||||||
|         self.object.expiry_date = self.object.creation_date + self.object.shelf_life |  | ||||||
|         self.object.allergens.clear() |  | ||||||
|  |  | ||||||
|         for ingredient in self.object.ingredients.iterator(): |  | ||||||
|             if not (ingredient.polymorphic_ctype.model == 'basicfood' and ingredient.date_type == 'DDM'): |  | ||||||
|                 self.object.expiry_date = min(self.object.expiry_date, ingredient.expiry_date) |  | ||||||
|             self.object.allergens.set(self.object.allergens.union(ingredient.allergens.all())) |  | ||||||
|  |  | ||||||
|         self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens) |         self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens) | ||||||
|         return HttpResponseRedirect(self.get_success_url()) |         return HttpResponseRedirect(self.get_success_url()) | ||||||
|   | |||||||
| @@ -1,46 +0,0 @@ | |||||||
| from django.db import migrations |  | ||||||
|  |  | ||||||
| def create_bda(apps, schema_editor): |  | ||||||
|     """ |  | ||||||
|     The club BDA is now pre-injected. |  | ||||||
|     """ |  | ||||||
|     Club = apps.get_model("member", "club") |  | ||||||
|     NoteClub = apps.get_model("note", "noteclub") |  | ||||||
|     Alias = apps.get_model("note", "alias") |  | ||||||
|     ContentType = apps.get_model('contenttypes', 'ContentType') |  | ||||||
|     polymorphic_ctype_id = ContentType.objects.get_for_model(NoteClub).id |  | ||||||
|      |  | ||||||
|     Club.objects.get_or_create( |  | ||||||
|         id=10, |  | ||||||
|         name="BDA", |  | ||||||
|         email="bda.ensparissaclay@gmail.com", |  | ||||||
|         require_memberships=True, |  | ||||||
|         membership_fee_paid=750, |  | ||||||
|         membership_fee_unpaid=750, |  | ||||||
|         membership_duration=396, |  | ||||||
|         membership_start="2024-08-01", |  | ||||||
|         membership_end="2025-09-30", |  | ||||||
|     ) |  | ||||||
|     NoteClub.objects.get_or_create( |  | ||||||
|         id=1937, |  | ||||||
|         club_id=10, |  | ||||||
|         polymorphic_ctype_id=polymorphic_ctype_id, |  | ||||||
|     ) |  | ||||||
|     Alias.objects.get_or_create( |  | ||||||
|         id=1937, |  | ||||||
|         note_id=1937, |  | ||||||
|         name="BDA", |  | ||||||
|         normalized_name="bda", |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
|  |  | ||||||
|     dependencies = [ |  | ||||||
|         ('member', '0013_auto_20240801_1436'), |  | ||||||
|     ] |  | ||||||
|      |  | ||||||
|     operations = [ |  | ||||||
|         migrations.RunPython(create_bda), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
| @@ -60,10 +60,7 @@ | |||||||
| {% if user_object.pk == user.pk %} | {% if user_object.pk == user.pk %} | ||||||
|     <div class="text-center"> |     <div class="text-center"> | ||||||
|         <a class="small badge badge-secondary" href="{% url 'member:auth_token' %}"> |         <a class="small badge badge-secondary" href="{% url 'member:auth_token' %}"> | ||||||
|             <i class="fa fa-cogs"></i> {% trans 'API token' %} |             <i class="fa fa-cogs"></i>{% trans 'API token' %} | ||||||
|         </a> |  | ||||||
|         <a class="small badge badge-secondary" href="{% url 'member:qr_code' user_object.pk %}"> |  | ||||||
|             <i class="fa fa-qrcode"></i> {% trans 'QR Code' %} |  | ||||||
|         </a> |         </a> | ||||||
|     </div> |     </div> | ||||||
| {% endif %} | {% endif %} | ||||||
|   | |||||||
| @@ -1,36 +0,0 @@ | |||||||
| {% extends "base.html" %} |  | ||||||
| {% comment %} |  | ||||||
| SPDX-License-Identifier: GPL-3.0-or-later |  | ||||||
| {% endcomment %} |  | ||||||
| {% load i18n %} |  | ||||||
|  |  | ||||||
| {% block content %} |  | ||||||
| <div class="card bg-light"> |  | ||||||
|   	<h3 class="card-header text-center"> |  | ||||||
| 		{% trans "QR Code for" %} {{ user_object.username }} ({{ user_object.first_name }} {{user_object.last_name }}) |  | ||||||
|   	</h3> |  | ||||||
|   	<div class="text-center" id="qrcode"> |  | ||||||
|   	</div> |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| {% endblock %} |  | ||||||
|  |  | ||||||
| {% block extrajavascript %} |  | ||||||
| <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js" integrity="sha512-CNgIRecGo7nphbeZ04Sc13ka07paqdeTu0WR1IM4kNcpmBAUSHSQX0FslNhTDadL4O5SAGapGt4FodqL8My0mA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> |  | ||||||
| <script> |  | ||||||
| 	var qrc = new QRCode(document.getElementById("qrcode"), { |  | ||||||
| 		text: "{{ user_object.pk }}\0", |  | ||||||
| 		width: 1024, |  | ||||||
| 		height: 1024 |  | ||||||
| 	}); |  | ||||||
| </script> |  | ||||||
| {% endblock %} |  | ||||||
|  |  | ||||||
| {% block extracss %} |  | ||||||
| <style> |  | ||||||
| img { |  | ||||||
|     width: 100% |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
| {% endblock %} |  | ||||||
| @@ -25,5 +25,4 @@ urlpatterns = [ | |||||||
|     path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"), |     path('user/<int:pk>/aliases/', views.ProfileAliasView.as_view(), name="user_alias"), | ||||||
|     path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"), |     path('user/<int:pk>/trust', views.ProfileTrustView.as_view(), name="user_trust"), | ||||||
|     path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'), |     path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'), | ||||||
|     path('user/<int:pk>/qr_code/', views.QRCodeView.as_view(), name='qr_code'), |  | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -402,14 +402,6 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView): | |||||||
|         context['token'] = Token.objects.get_or_create(user=self.request.user)[0] |         context['token'] = Token.objects.get_or_create(user=self.request.user)[0] | ||||||
|         return context |         return context | ||||||
|  |  | ||||||
| class QRCodeView(LoginRequiredMixin, DetailView): |  | ||||||
|     """ |  | ||||||
|     Affiche le QR Code |  | ||||||
|     """ |  | ||||||
|     model = User |  | ||||||
|     context_object_name = "user_object" |  | ||||||
|     template_name = "member/qr_code.html" |  | ||||||
|     extra_context = {"title": _("QR Code")} |  | ||||||
|  |  | ||||||
| # ******************************* # | # ******************************* # | ||||||
| #              CLUB               # | #              CLUB               # | ||||||
|   | |||||||
| @@ -1695,7 +1695,7 @@ | |||||||
|                 "wei", |                 "wei", | ||||||
|                 "weimembership" |                 "weimembership" | ||||||
|             ], |             ], | ||||||
|             "query": "{\"club\": [\"club\"]}", |             "query": "[\"AND\", {\"club\": [\"club\"], \"club__weiclub__membership_end__gte\": [\"today\"]}, [\"OR\", {\"registration__soge_credit\": true}, {\"user__note__balance__gte\": {\"F\": [\"F\", \"fee\"]}}]]", | ||||||
|             "type": "add", |             "type": "add", | ||||||
|             "mask": 2, |             "mask": 2, | ||||||
|             "field": "", |             "field": "", | ||||||
| @@ -3998,358 +3998,6 @@ | |||||||
|             "description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -50 €" |             "description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -50 €" | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 271, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "wei", |  | ||||||
|                 "bus" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"wei\": [\"club\"]}", |  | ||||||
|             "type": "change", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Modifier n'importe quel bus du wei" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 272, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "wei", |  | ||||||
|                 "bus" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"wei\": [\"club\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir tous les bus du wei" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 273, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "wei", |  | ||||||
|                 "busteam" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"bus__wei\": [\"club\"], \"bus__wei__membership_end__gte\": [\"today\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir toutes les équipes WEI" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 274, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "club" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les informations de clubs des bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 275, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "club" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "change", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Modifier les clubs des bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 276, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "add", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Ajouter un⋅e membre à un club de bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 277, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les adhérents d'un club de bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 278, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "change", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Modifier l'adhésion d'un club de bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 279, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "note" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"noteclub__club__bus__wei\": [\"club\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir la note d'un club de bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 280, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "transaction" |  | ||||||
|             ], |  | ||||||
|             "query": "[\"OR\", {\"source__noteclub__club__bus__wei\": [\"club\"]}, {\"destination__noteclub__club__bus__wei\": [\"club\"]}]", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les transactions d'un club de bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 281, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "transaction" |  | ||||||
|             ], |  | ||||||
|             "query": "[\"AND\", [\"OR\", {\"source__noteclub__club__bus__wei\": [\"club\"]}, {\"destination__noteclub__club__bus__wei\": [\"club\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]", |  | ||||||
|             "type": "add", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Créer une transaction d'un club de bus tant que la source reste au dessus de -20 €" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 282, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "transaction" |  | ||||||
|             ], |  | ||||||
|             "query": "[\"AND\", [\"OR\", {\"source__noteclub__club\": [\"club\"]}, {\"destination__noteclub__club\": [\"club\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 2000]}}, {\"valid\": false}]]", |  | ||||||
|             "type": "add", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Créer une transaction d'un WEI tant que la source reste au dessus de -20 €" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 283, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "auth", |  | ||||||
|                 "user" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"memberships__club__name\": \"Kfet\", \"memberships__roles__name\": \"Adh\u00e9rent\u22c5e Kfet\", \"memberships__date_start__lte\": [\"today\"], \"memberships__date_end__gte\": [\"today\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir n'importe quel⋅le utilisateur⋅rice qui est adhérent⋅e Kfet" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 284, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "club" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"bus\":  [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les informations de club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 285, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "club" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"bus\": [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "change", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Modifier le club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 286, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus\": [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "add", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Ajouter un⋅e membre au club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 287, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus\": [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les adhérents du club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 288, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__bus\": [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "change", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Modifier l'adhésion au club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 289, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "note" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"noteclub__club__bus\": [\"membership\", \"weimembership\", \"bus\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir la note du club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 290, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "note", |  | ||||||
|                 "transaction" |  | ||||||
|             ], |  | ||||||
|             "query": "[\"OR\", {\"source__noteclub__club__bus\": [\"membership\", \"weimembership\", \"bus\"]}, {\"destination__noteclub__club__bus\": [\"membership\", \"weimembership\", \"bus\"]}]", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir les transactions du club de son bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 291, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "wei", |  | ||||||
|                 "bus" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"pk\": [\"membership\", \"weimembership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}", |  | ||||||
|             "type": "view", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Voir mon bus" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "permission.permission", |  | ||||||
|         "pk": 292, |  | ||||||
|         "fields": { |  | ||||||
|             "model": [ |  | ||||||
|                 "member", |  | ||||||
|                 "membership" |  | ||||||
|             ], |  | ||||||
|             "query": "{\"club__pk__lte\": 2}", |  | ||||||
|             "type": "add", |  | ||||||
|             "mask": 3, |  | ||||||
|             "field": "", |  | ||||||
|             "permanent": false, |  | ||||||
|             "description": "Ajouter un membre au BDE ou à la Kfet" |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |     { | ||||||
|         "model": "permission.role", |         "model": "permission.role", | ||||||
|         "pk": 1, |         "pk": 1, | ||||||
| @@ -4504,8 +4152,8 @@ | |||||||
|             "name": "Pr\u00e9sident\u22c5e de club", |             "name": "Pr\u00e9sident\u22c5e de club", | ||||||
|             "permissions": [ |             "permissions": [ | ||||||
|                 62, |                 62, | ||||||
|                 135, |                 142, | ||||||
|                 142 |                 135 | ||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| @@ -4710,8 +4358,6 @@ | |||||||
|             "name": "GC WEI", |             "name": "GC WEI", | ||||||
|             "permissions": [ |             "permissions": [ | ||||||
|                 22, |                 22, | ||||||
|                 49, |  | ||||||
|                 62, |  | ||||||
|                 70, |                 70, | ||||||
|                 72, |                 72, | ||||||
|                 76, |                 76, | ||||||
| @@ -4736,23 +4382,7 @@ | |||||||
|                 112, |                 112, | ||||||
|                 113, |                 113, | ||||||
|                 128, |                 128, | ||||||
|                 130, |                 130 | ||||||
|                 142, |  | ||||||
|                 269, |  | ||||||
|                 271, |  | ||||||
|                 272, |  | ||||||
|                 273, |  | ||||||
|                 274, |  | ||||||
|                 275, |  | ||||||
|                 276, |  | ||||||
|                 277, |  | ||||||
|                 278, |  | ||||||
|                 279, |  | ||||||
|                 280, |  | ||||||
|                 281, |  | ||||||
|                 282, |  | ||||||
|                 283, |  | ||||||
|                 292 |  | ||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| @@ -4771,14 +4401,7 @@ | |||||||
|                 119, |                 119, | ||||||
|                 120, |                 120, | ||||||
|                 121, |                 121, | ||||||
|                 122, |                 122 | ||||||
|                 284, |  | ||||||
|                 285, |  | ||||||
|                 286, |  | ||||||
|                 287, |  | ||||||
|                 289, |  | ||||||
|                 290, |  | ||||||
|                 291 |  | ||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| @@ -4939,140 +4562,6 @@ | |||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     },   |     },   | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 23, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 2, |  | ||||||
|             "name": "Darbonne", |  | ||||||
|             "permissions": [ |  | ||||||
|                 30, |  | ||||||
|                 31, |  | ||||||
|                 32 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 24, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": null, |  | ||||||
|             "name": "Staffeur⋅euse (S&L,Respo Tech,...)", |  | ||||||
|             "permissions": [] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 25, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": null, |  | ||||||
|             "name": "Référent⋅e Bus", |  | ||||||
|             "permissions": [ |  | ||||||
|                 22, |  | ||||||
|                 84, |  | ||||||
|                 115, |  | ||||||
|                 117, |  | ||||||
|                 118, |  | ||||||
|                 119, |  | ||||||
|                 120, |  | ||||||
|                 121, |  | ||||||
|                 122, |  | ||||||
|                 284, |  | ||||||
|                 285, |  | ||||||
|                 286, |  | ||||||
|                 287, |  | ||||||
|                 289, |  | ||||||
|                 290, |  | ||||||
|                 291 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 28, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 10, |  | ||||||
|             "name": "Trésorièr⸱e BDA", |  | ||||||
|             "permissions": [ |  | ||||||
|                 55, |  | ||||||
|                 56, |  | ||||||
|                 57, |  | ||||||
|                 58, |  | ||||||
|                 135, |  | ||||||
|                 143, |  | ||||||
|                 176, |  | ||||||
|                 177, |  | ||||||
|                 178, |  | ||||||
|                 243, |  | ||||||
|                 260, |  | ||||||
|                 261, |  | ||||||
|                 262, |  | ||||||
|                 263, |  | ||||||
|                 264, |  | ||||||
|                 265, |  | ||||||
|                 266, |  | ||||||
|                 267, |  | ||||||
|                 268, |  | ||||||
|                 269 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 30, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 10, |  | ||||||
|             "name": "Respo sorties", |  | ||||||
|             "permissions": [ |  | ||||||
|                 49,  |  | ||||||
|                 62,  |  | ||||||
|                 141,  |  | ||||||
|                 241,  |  | ||||||
|                 242,  |  | ||||||
|                 243 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 31, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 1, |  | ||||||
|             "name": "Respo comm", |  | ||||||
|             "permissions": [ |  | ||||||
|                 135, |  | ||||||
|                 244 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 32, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 10, |  | ||||||
|             "name": "Respo comm Art", |  | ||||||
|             "permissions": [ |  | ||||||
|                 135, |  | ||||||
|                 245 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |  | ||||||
|         "model": "permission.role", |  | ||||||
|         "pk": 33, |  | ||||||
|             "fields": { |  | ||||||
|             "for_club": 10, |  | ||||||
|             "name": "Respo Jam", |  | ||||||
|             "permissions": [ |  | ||||||
|                 247,  |  | ||||||
|                 250,  |  | ||||||
|                 251,  |  | ||||||
|                 252,  |  | ||||||
|                 253,  |  | ||||||
|                 254 |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
|     },  |  | ||||||
|     { |     { | ||||||
|         "model": "wei.weirole", |         "model": "wei.weirole", | ||||||
|         "pk": 12, |         "pk": 12, | ||||||
| @@ -5107,15 +4596,5 @@ | |||||||
|         "model": "wei.weirole", |         "model": "wei.weirole", | ||||||
|         "pk": 18, |         "pk": 18, | ||||||
|         "fields": {} |         "fields": {} | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "wei.weirole", |  | ||||||
|         "pk": 24, |  | ||||||
|         "fields": {} |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "model": "wei.weirole", |  | ||||||
|         "pk": 25, |  | ||||||
|         "fields": {} |  | ||||||
|     } |     } | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ from django.utils import timezone | |||||||
| from django.utils.crypto import get_random_string | from django.utils.crypto import get_random_string | ||||||
| from activity.models import Activity | from activity.models import Activity | ||||||
| from member.models import Club, Membership | from member.models import Club, Membership | ||||||
| from note.models import NoteUser, NoteClub | from note.models import NoteUser | ||||||
| from wei.models import WEIClub, Bus, WEIRegistration | from wei.models import WEIClub, Bus, WEIRegistration | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -122,13 +122,10 @@ class TestPermissionDenied(TestCase): | |||||||
|  |  | ||||||
|     def test_validate_weiregistration(self): |     def test_validate_weiregistration(self): | ||||||
|         wei = WEIClub.objects.create( |         wei = WEIClub.objects.create( | ||||||
|             name="WEI Test", |  | ||||||
|             membership_start=date.today(), |             membership_start=date.today(), | ||||||
|             date_start=date.today() + timedelta(days=1), |             date_start=date.today() + timedelta(days=1), | ||||||
|             date_end=date.today() + timedelta(days=1), |             date_end=date.today() + timedelta(days=1), | ||||||
|             parent_club=Club.objects.get(name="Kfet"), |  | ||||||
|         ) |         ) | ||||||
|         NoteClub.objects.create(club=wei) |  | ||||||
|         registration = WEIRegistration.objects.create(wei=wei, user=self.user, birth_date="2000-01-01") |         registration = WEIRegistration.objects.create(wei=wei, user=self.user, birth_date="2000-01-01") | ||||||
|         response = self.client.get(reverse("wei:validate_registration", kwargs=dict(pk=registration.pk))) |         response = self.client.get(reverse("wei:validate_registration", kwargs=dict(pk=registration.pk))) | ||||||
|         self.assertEqual(response.status_code, 403) |         self.assertEqual(response.status_code, 403) | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay | # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay | ||||||
| # SPDX-License-Identifier: GPL-3.0-or-later | # SPDX-License-Identifier: GPL-3.0-or-later | ||||||
|  |  | ||||||
| from .registration import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, WEIMembership1AForm, \ | from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, WEIMembershipForm, BusForm, BusTeamForm | ||||||
|     WEIMembershipForm, BusForm, BusTeamForm |  | ||||||
| from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey | from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey | ||||||
|  |  | ||||||
| __all__ = [ | __all__ = [ | ||||||
|     'WEIForm', 'WEIRegistrationForm', 'WEIRegistration1AForm', 'WEIRegistration2AForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', |     'WEIForm', 'WEIRegistrationForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', | ||||||
|     'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey', |     'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey', | ||||||
| ] | ] | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ class WEIForm(forms.ModelForm): | |||||||
|             "membership_end": DatePickerInput(), |             "membership_end": DatePickerInput(), | ||||||
|             "date_start": DatePickerInput(), |             "date_start": DatePickerInput(), | ||||||
|             "date_end": DatePickerInput(), |             "date_end": DatePickerInput(), | ||||||
|             "caution_amount": AmountInput(), |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -40,11 +39,7 @@ class WEIRegistrationForm(forms.ModelForm): | |||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = WEIRegistration |         model = WEIRegistration | ||||||
|         fields = [ |         exclude = ('wei', 'clothing_cut') | ||||||
|             'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size', |  | ||||||
|             'health_issues', 'emergency_contact_name', 'emergency_contact_phone', |  | ||||||
|             'first_year', 'information_json', 'caution_check' |  | ||||||
|         ] |  | ||||||
|         widgets = { |         widgets = { | ||||||
|             "user": Autocomplete( |             "user": Autocomplete( | ||||||
|                 User, |                 User, | ||||||
| @@ -54,30 +49,11 @@ class WEIRegistrationForm(forms.ModelForm): | |||||||
|                     'placeholder': 'Nom ...', |                     'placeholder': 'Nom ...', | ||||||
|                 }, |                 }, | ||||||
|             ), |             ), | ||||||
|             "birth_date": DatePickerInput(options={ |             "birth_date": DatePickerInput(options={'minDate': '1900-01-01', | ||||||
|                 'minDate': '1900-01-01', |                                                    'maxDate': '2100-01-01'}), | ||||||
|                 'maxDate': '2100-01-01' |  | ||||||
|             }), |  | ||||||
|             "caution_check": forms.BooleanField( |  | ||||||
|                 required=False, |  | ||||||
|             ), |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
| class WEIRegistration2AForm(WEIRegistrationForm): |  | ||||||
|     class Meta(WEIRegistrationForm.Meta): |  | ||||||
|         fields = WEIRegistrationForm.Meta.fields + ['caution_type'] |  | ||||||
|         widgets = WEIRegistrationForm.Meta.widgets.copy() |  | ||||||
|         widgets.update({ |  | ||||||
|             "caution_type": forms.RadioSelect(), |  | ||||||
|         }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class WEIRegistration1AForm(WEIRegistrationForm): |  | ||||||
|     class Meta(WEIRegistrationForm.Meta): |  | ||||||
|         fields = WEIRegistrationForm.Meta.fields |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class WEIChooseBusForm(forms.Form): | class WEIChooseBusForm(forms.Form): | ||||||
|     bus = forms.ModelMultipleChoiceField( |     bus = forms.ModelMultipleChoiceField( | ||||||
|         queryset=Bus.objects, |         queryset=Bus.objects, | ||||||
| @@ -96,7 +72,7 @@ class WEIChooseBusForm(forms.Form): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     roles = forms.ModelMultipleChoiceField( |     roles = forms.ModelMultipleChoiceField( | ||||||
|         queryset=WEIRole.objects.filter(~Q(name="1A") & ~Q(name="GC WEI")), |         queryset=WEIRole.objects.filter(~Q(name="1A")), | ||||||
|         label=_("WEI Roles"), |         label=_("WEI Roles"), | ||||||
|         help_text=_("Select the roles that you are interested in."), |         help_text=_("Select the roles that you are interested in."), | ||||||
|         initial=WEIRole.objects.filter(name="Adhérent⋅e WEI").all(), |         initial=WEIRole.objects.filter(name="Adhérent⋅e WEI").all(), | ||||||
| @@ -105,8 +81,13 @@ class WEIChooseBusForm(forms.Form): | |||||||
|  |  | ||||||
|  |  | ||||||
| class WEIMembershipForm(forms.ModelForm): | class WEIMembershipForm(forms.ModelForm): | ||||||
|  |     caution_check = forms.BooleanField( | ||||||
|  |         required=False, | ||||||
|  |         label=_("Caution check given"), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     roles = forms.ModelMultipleChoiceField( |     roles = forms.ModelMultipleChoiceField( | ||||||
|         queryset=WEIRole.objects.filter(~Q(name="GC WEI")), |         queryset=WEIRole.objects, | ||||||
|         label=_("WEI Roles"), |         label=_("WEI Roles"), | ||||||
|         widget=CheckboxSelectMultiple(), |         widget=CheckboxSelectMultiple(), | ||||||
|     ) |     ) | ||||||
| @@ -213,4 +194,3 @@ class BusTeamForm(forms.ModelForm): | |||||||
|             ), |             ), | ||||||
|             "color": ColorWidget(), |             "color": ColorWidget(), | ||||||
|         } |         } | ||||||
|         # "color": ColorWidget(), |  | ||||||
|   | |||||||
| @@ -1,18 +0,0 @@ | |||||||
| # Generated by Django 4.2.21 on 2025-05-25 12:23 |  | ||||||
|  |  | ||||||
| from django.db import migrations, models |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
|  |  | ||||||
|     dependencies = [ |  | ||||||
|         ('wei', '0010_remove_weiregistration_specific_diet'), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     operations = [ |  | ||||||
|         migrations.AlterField( |  | ||||||
|             model_name='weiclub', |  | ||||||
|             name='year', |  | ||||||
|             field=models.PositiveIntegerField(default=2025, unique=True, verbose_name='year'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| # Generated by Django 4.2.21 on 2025-05-29 16:16 |  | ||||||
|  |  | ||||||
| from django.db import migrations, models |  | ||||||
| import django.db.models.deletion |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
|  |  | ||||||
|     dependencies = [ |  | ||||||
|         ('member', '0014_create_bda'), |  | ||||||
|         ('wei', '0011_alter_weiclub_year'), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     operations = [ |  | ||||||
|         migrations.AddField( |  | ||||||
|             model_name='bus', |  | ||||||
|             name='club', |  | ||||||
|             field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='bus', to='member.club', verbose_name='club'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| # Generated by Django 4.2.21 on 2025-06-01 21:43 |  | ||||||
|  |  | ||||||
| from django.db import migrations, models |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
|  |  | ||||||
|     dependencies = [ |  | ||||||
|         ('wei', '0012_bus_club'), |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
|     operations = [ |  | ||||||
|         migrations.AddField( |  | ||||||
|             model_name='weiclub', |  | ||||||
|             name='caution_amount', |  | ||||||
|             field=models.PositiveIntegerField(default=0, verbose_name='caution amount'), |  | ||||||
|         ), |  | ||||||
|         migrations.AddField( |  | ||||||
|             model_name='weiregistration', |  | ||||||
|             name='caution_type', |  | ||||||
|             field=models.CharField(choices=[('check', 'Check'), ('note', 'Note transaction')], default='check', max_length=16, verbose_name='caution type'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
| @@ -33,11 +33,6 @@ class WEIClub(Club): | |||||||
|         verbose_name=_("date end"), |         verbose_name=_("date end"), | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     caution_amount = models.PositiveIntegerField( |  | ||||||
|         verbose_name=_("caution amount"), |  | ||||||
|         default=0, |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         verbose_name = _("WEI") |         verbose_name = _("WEI") | ||||||
|         verbose_name_plural = _("WEI") |         verbose_name_plural = _("WEI") | ||||||
| @@ -77,15 +72,6 @@ class Bus(models.Model): | |||||||
|         default=50, |         default=50, | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     club = models.OneToOneField( |  | ||||||
|         Club, |  | ||||||
|         null=True, |  | ||||||
|         blank=True, |  | ||||||
|         on_delete=models.SET_NULL, |  | ||||||
|         related_name="bus", |  | ||||||
|         verbose_name=_("club"), |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     description = models.TextField( |     description = models.TextField( | ||||||
|         blank=True, |         blank=True, | ||||||
|         default="", |         default="", | ||||||
| @@ -202,16 +188,6 @@ class WEIRegistration(models.Model): | |||||||
|         verbose_name=_("Caution check given") |         verbose_name=_("Caution check given") | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     caution_type = models.CharField( |  | ||||||
|         max_length=16, |  | ||||||
|         choices=( |  | ||||||
|             ('check', _("Check")), |  | ||||||
|             ('note', _("Note transaction")), |  | ||||||
|         ), |  | ||||||
|         default='check', |  | ||||||
|         verbose_name=_("caution type"), |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     birth_date = models.DateField( |     birth_date = models.DateField( | ||||||
|         verbose_name=_("birth date"), |         verbose_name=_("birth date"), | ||||||
|     ) |     ) | ||||||
|   | |||||||
| @@ -98,7 +98,7 @@ class WEIRegistrationTable(tables.Table): | |||||||
|         if not hasperm: |         if not hasperm: | ||||||
|             return format_html("<span class='no-perm'></span>") |             return format_html("<span class='no-perm'></span>") | ||||||
|  |  | ||||||
|         url = reverse_lazy('wei:wei_update_registration', args=(record.pk,)) + '?validate=true' |         url = reverse_lazy('wei:validate_registration', args=(record.pk,)) | ||||||
|         text = _('Validate') |         text = _('Validate') | ||||||
|         if record.fee > record.user.note.balance and not record.soge_credit: |         if record.fee > record.user.note.balance and not record.soge_credit: | ||||||
|             btn_class = 'btn-secondary' |             btn_class = 'btn-secondary' | ||||||
|   | |||||||
| @@ -40,20 +40,22 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|                     <dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt> |                     <dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt> | ||||||
|                     <dd class="col-xl-6">{{ club.membership_fee_paid|pretty_money }}</dd> |                     <dd class="col-xl-6">{{ club.membership_fee_paid|pretty_money }}</dd> | ||||||
|                     {% else %} |                     {% else %} | ||||||
|  |                     {% with bde_kfet_fee=club.parent_club.membership_fee_paid|add:club.parent_club.parent_club.membership_fee_paid %} | ||||||
|                     <dt class="col-xl-6">{% trans 'WEI fee (paid students)'|capfirst %}</dt> |                     <dt class="col-xl-6">{% trans 'WEI fee (paid students)'|capfirst %}</dt> | ||||||
|                     <dd class="col-xl-6">{{ club.membership_fee_paid|pretty_money }} |                     <dd class="col-xl-6">{{ club.membership_fee_paid|add:bde_kfet_fee|pretty_money }} | ||||||
|  |                         <i class="fa fa-question-circle" | ||||||
|  |                             title="{% trans "The BDE membership is included in the WEI registration." %}"></i></dd> | ||||||
|  |                     {% endwith %} | ||||||
|  |  | ||||||
|  |                     {% with bde_kfet_fee=club.parent_club.membership_fee_unpaid|add:club.parent_club.parent_club.membership_fee_unpaid %} | ||||||
|                     <dt class="col-xl-6">{% trans 'WEI fee (unpaid students)'|capfirst %}</dt> |                     <dt class="col-xl-6">{% trans 'WEI fee (unpaid students)'|capfirst %}</dt> | ||||||
|                     <dd class="col-xl-6">{{ club.membership_fee_unpaid|pretty_money }} |                     <dd class="col-xl-6">{{ club.membership_fee_unpaid|add:bde_kfet_fee|pretty_money }} | ||||||
|  |                         <i class="fa fa-question-circle" | ||||||
|  |                             title="{% trans "The BDE membership is included in the WEI registration." %}"></i></dd> | ||||||
|  |                     {% endwith %} | ||||||
|                     {% endif %} |                     {% endif %} | ||||||
|                     {% endif %} |                     {% endif %} | ||||||
|  |  | ||||||
|                     {% if club.caution_amount > 0 %} |  | ||||||
|                     <dt class="col-xl-6">{% trans 'Caution amount'|capfirst %}</dt> |  | ||||||
|                     <dd class="col-xl-6">{{ club.caution_amount|pretty_money }}</dd> |  | ||||||
|                     {% endif %} |  | ||||||
|  |  | ||||||
|                     {% if "note.view_note"|has_perm:club.note %} |                     {% if "note.view_note"|has_perm:club.note %} | ||||||
|                     <dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt> |                     <dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt> | ||||||
|                     <dd class="col-xl-6">{{ club.note.balance | pretty_money }}</dd> |                     <dd class="col-xl-6">{{ club.note.balance | pretty_money }}</dd> | ||||||
|   | |||||||
| @@ -16,10 +16,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div class="card-footer text-center"> |     <div class="card-footer text-center"> | ||||||
|         {% if object.club %} |  | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_detail' pk=object.club.pk %}" |  | ||||||
|             data-turbolinks="false">{% trans "View club" %}</a> |  | ||||||
|         {% endif %} |  | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=object.pk %}" |         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=object.pk %}" | ||||||
|             data-turbolinks="false">{% trans "Edit" %}</a> |             data-turbolinks="false">{% trans "Edit" %}</a> | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=object.pk %}" |         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=object.pk %}" | ||||||
|   | |||||||
| @@ -18,8 +18,6 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|     <div class="card-footer text-center"> |     <div class="card-footer text-center"> | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=bus.pk %}" |         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=bus.pk %}" | ||||||
|             data-turbolinks="false">{% trans "Edit" %}</a> |             data-turbolinks="false">{% trans "Edit" %}</a> | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:manage_bus' pk=bus.pk %}" |  | ||||||
|             data-turbolinks="false">{% trans "View" %}</a> |  | ||||||
|         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=bus.pk %}" |         <a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=bus.pk %}" | ||||||
|             data-turbolinks="false">{% trans "Add team" %}</a> |             data-turbolinks="false">{% trans "Add team" %}</a> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -13,17 +13,9 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|     <div class="card-body"> |     <div class="card-body"> | ||||||
|         <form method="post"> |         <form method="post"> | ||||||
|             {% csrf_token %} |             {% csrf_token %} | ||||||
|             {{ form.media }}  |  | ||||||
|             {{ form|crispy }} |             {{ form|crispy }} | ||||||
|             <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button> |             <button class="btn btn-primary" type="submit">{% trans "Submit" %}</button> | ||||||
|         </form> |         </form> | ||||||
|     </div> |     </div> | ||||||
| </div> | </div> | ||||||
| <script> |  | ||||||
|     document.addEventListener("DOMContentLoaded", function () { |  | ||||||
|         if (window.jscolor && jscolor.install) { |  | ||||||
|             jscolor.install(); |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| </script> |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
| @@ -95,11 +95,9 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
| </div> | </div> | ||||||
| {% endif %} | {% endif %} | ||||||
|  |  | ||||||
| {% if can_validate_1a %} |     {% if can_validate_1a %} | ||||||
|         <a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a> |         <a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a> | ||||||
| {% endif %} |     {% endif %} | ||||||
|  |  | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
| {% block extrajavascript %} | {% block extrajavascript %} | ||||||
|   | |||||||
| @@ -143,35 +143,25 @@ SPDX-License-Identifier: GPL-3.0-or-later | |||||||
|                         {% endblocktrans %} |                         {% endblocktrans %} | ||||||
|                     </div> |                     </div> | ||||||
|                 {% else %} |                 {% else %} | ||||||
|                     <div class="alert {% if registration.user.note.balance < fee %}alert-danger{% else %}alert-success{% endif %}"> |                     {% if registration.user.note.balance < fee %} | ||||||
|                         <h5>{% trans "Required payments:" %}</h5> |                         <div class="alert alert-danger"> | ||||||
|                         <ul> |                             {% with pretty_fee=fee|pretty_money %} | ||||||
|                             <li>{% blocktrans trimmed with amount=fee|pretty_money %} |                             {% blocktrans trimmed with balance=registration.user.note.balance|pretty_money %} | ||||||
|                                 Membership fees: {{ amount }} |                                 The note don't have enough money ({{ balance }}, {{ pretty_fee }} required). | ||||||
|                             {% endblocktrans %}</li> |                                 The registration may fail if you don't credit the note now. | ||||||
|                             {% if registration.caution_type == 'note' %} |                             {% endblocktrans %} | ||||||
|                                 <li>{% blocktrans trimmed with amount=club.caution_amount|pretty_money %} |                             {% endwith %} | ||||||
|                                     Deposit (by Note transaction): {{ amount }} |                         </div> | ||||||
|                                 {% endblocktrans %}</li> |  | ||||||
|                                 <li><strong>{% blocktrans trimmed with total=total_needed|pretty_money %} |  | ||||||
|                                     Total needed: {{ total }} |  | ||||||
|                                 {% endblocktrans %}</strong></li> |  | ||||||
|                     {% else %} |                     {% else %} | ||||||
|                                 <li>{% blocktrans trimmed with amount=club.caution_amount|pretty_money %} |                         <div class="alert alert-success"> | ||||||
|                                     Deposit (by check): {{ amount }} |                             {% blocktrans trimmed with pretty_fee=fee|pretty_money %} | ||||||
|                                 {% endblocktrans %}</li> |                                 The note has enough money ({{ pretty_fee }} required), the registration is possible. | ||||||
|                                 <li><strong>{% blocktrans trimmed with total=fee|pretty_money %} |                             {% endblocktrans %} | ||||||
|                                     Total needed: {{ total }} |  | ||||||
|                                 {% endblocktrans %}</strong></li> |  | ||||||
|                             {% endif %} |  | ||||||
|                         </ul> |  | ||||||
|                         <p>{% blocktrans trimmed with balance=registration.user.note.balance|pretty_money %} |  | ||||||
|                             Current balance: {{ balance }} |  | ||||||
|                         {% endblocktrans %}</p> |  | ||||||
|                         </div> |                         </div> | ||||||
|                     {% endif %} |                     {% endif %} | ||||||
|  |                 {% endif %} | ||||||
|  |  | ||||||
|                 {% if not registration.caution_check and not registration.first_year and registration.caution_type == 'check' %} |                 {% if not registration.caution_check and not registration.first_year %} | ||||||
|                     <div class="alert alert-danger"> |                     <div class="alert alert-danger"> | ||||||
|                         {% trans "The user didn't give her/his caution check." %} |                         {% trans "The user didn't give her/his caution check." %} | ||||||
|                     </div> |                     </div> | ||||||
|   | |||||||
| @@ -126,7 +126,6 @@ class TestWEIRegistration(TestCase): | |||||||
|             year=self.year + 1, |             year=self.year + 1, | ||||||
|             date_start=str(self.year + 1) + "-09-01", |             date_start=str(self.year + 1) + "-09-01", | ||||||
|             date_end=str(self.year + 1) + "-09-03", |             date_end=str(self.year + 1) + "-09-03", | ||||||
|             caution_amount=12000, |  | ||||||
|         )) |         )) | ||||||
|         qs = WEIClub.objects.filter(name="Create WEI Test", year=self.year + 1) |         qs = WEIClub.objects.filter(name="Create WEI Test", year=self.year + 1) | ||||||
|         self.assertTrue(qs.exists()) |         self.assertTrue(qs.exists()) | ||||||
| @@ -161,7 +160,6 @@ class TestWEIRegistration(TestCase): | |||||||
|             membership_end="2000-09-30", |             membership_end="2000-09-30", | ||||||
|             date_start="2000-09-01", |             date_start="2000-09-01", | ||||||
|             date_end="2000-09-03", |             date_end="2000-09-03", | ||||||
|             caution_amount=12000, |  | ||||||
|         )) |         )) | ||||||
|         qs = WEIClub.objects.filter(name="Update WEI Test", id=self.wei.id) |         qs = WEIClub.objects.filter(name="Update WEI Test", id=self.wei.id) | ||||||
|         self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=self.wei.pk)), 302, 200) |         self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=self.wei.pk)), 302, 200) | ||||||
| @@ -320,7 +318,6 @@ class TestWEIRegistration(TestCase): | |||||||
|             bus=[], |             bus=[], | ||||||
|             team=[], |             team=[], | ||||||
|             roles=[], |             roles=[], | ||||||
|             caution_type='check' |  | ||||||
|         )) |         )) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertFalse(response.context["membership_form"].is_valid()) |         self.assertFalse(response.context["membership_form"].is_valid()) | ||||||
| @@ -337,8 +334,7 @@ class TestWEIRegistration(TestCase): | |||||||
|             emergency_contact_phone='+33123456789', |             emergency_contact_phone='+33123456789', | ||||||
|             bus=[self.bus.id], |             bus=[self.bus.id], | ||||||
|             team=[self.team.id], |             team=[self.team.id], | ||||||
|             roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A") & ~Q(name="GC WEI")).all()], |             roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A")).all()], | ||||||
|             caution_type='check' |  | ||||||
|         )) |         )) | ||||||
|         qs = WEIRegistration.objects.filter(user_id=user.id) |         qs = WEIRegistration.objects.filter(user_id=user.id) | ||||||
|         self.assertTrue(qs.exists()) |         self.assertTrue(qs.exists()) | ||||||
| @@ -358,7 +354,6 @@ class TestWEIRegistration(TestCase): | |||||||
|             bus=[self.bus.id], |             bus=[self.bus.id], | ||||||
|             team=[self.team.id], |             team=[self.team.id], | ||||||
|             roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A")).all()], |             roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A")).all()], | ||||||
|             caution_type='check' |  | ||||||
|         )) |         )) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertTrue("This user is already registered to this WEI." in str(response.context["form"].errors)) |         self.assertTrue("This user is already registered to this WEI." in str(response.context["form"].errors)) | ||||||
| @@ -511,12 +506,11 @@ class TestWEIRegistration(TestCase): | |||||||
|                 team=[self.team.id], |                 team=[self.team.id], | ||||||
|                 roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()], |                 roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()], | ||||||
|                 information_json=self.registration.information_json, |                 information_json=self.registration.information_json, | ||||||
|                 caution_type='check' |  | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|         qs = WEIRegistration.objects.filter(user_id=self.user.id, soge_credit=False, clothing_size="M") |         qs = WEIRegistration.objects.filter(user_id=self.user.id, soge_credit=False, clothing_size="M") | ||||||
|         self.assertTrue(qs.exists()) |         self.assertTrue(qs.exists()) | ||||||
|         self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=qs.get().wei.pk)), 302, 200) |         self.assertRedirects(response, reverse("wei:validate_registration", kwargs=dict(pk=qs.get().pk)), 302, 200) | ||||||
|  |  | ||||||
|         # Check the page when the registration is already validated |         # Check the page when the registration is already validated | ||||||
|         membership = WEIMembership( |         membership = WEIMembership( | ||||||
| @@ -566,12 +560,11 @@ class TestWEIRegistration(TestCase): | |||||||
|                 team=[self.team.id], |                 team=[self.team.id], | ||||||
|                 roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()], |                 roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()], | ||||||
|                 information_json=self.registration.information_json, |                 information_json=self.registration.information_json, | ||||||
|                 caution_type='check' |  | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|         qs = WEIRegistration.objects.filter(user_id=self.user.id, clothing_size="L") |         qs = WEIRegistration.objects.filter(user_id=self.user.id, clothing_size="L") | ||||||
|         self.assertTrue(qs.exists()) |         self.assertTrue(qs.exists()) | ||||||
|         self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=qs.get().wei.pk)), 302, 200) |         self.assertRedirects(response, reverse("wei:validate_registration", kwargs=dict(pk=qs.get().pk)), 302, 200) | ||||||
|  |  | ||||||
|         # Test invalid form |         # Test invalid form | ||||||
|         response = self.client.post( |         response = self.client.post( | ||||||
| @@ -590,7 +583,6 @@ class TestWEIRegistration(TestCase): | |||||||
|                 team=[], |                 team=[], | ||||||
|                 roles=[], |                 roles=[], | ||||||
|                 information_json=self.registration.information_json, |                 information_json=self.registration.information_json, | ||||||
|                 caution_type='check' |  | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|         self.assertFalse(response.context["membership_form"].is_valid()) |         self.assertFalse(response.context["membership_form"].is_valid()) | ||||||
| @@ -632,7 +624,7 @@ class TestWEIRegistration(TestCase): | |||||||
|         second_bus = Bus.objects.create(wei=self.wei, name="Second bus") |         second_bus = Bus.objects.create(wei=self.wei, name="Second bus") | ||||||
|         second_team = BusTeam.objects.create(bus=second_bus, name="Second team", color=42) |         second_team = BusTeam.objects.create(bus=second_bus, name="Second team", color=42) | ||||||
|         response = self.client.post(reverse("wei:validate_registration", kwargs=dict(pk=self.registration.pk)), dict( |         response = self.client.post(reverse("wei:validate_registration", kwargs=dict(pk=self.registration.pk)), dict( | ||||||
|             roles=[WEIRole.objects.get(name="Adhérent⋅e WEI").id], |             roles=[WEIRole.objects.get(name="GC WEI").id], | ||||||
|             bus=self.bus.pk, |             bus=self.bus.pk, | ||||||
|             team=second_team.pk, |             team=second_team.pk, | ||||||
|             credit_type=4,  # Bank transfer |             credit_type=4,  # Bank transfer | ||||||
| @@ -640,14 +632,13 @@ class TestWEIRegistration(TestCase): | |||||||
|             last_name="admin", |             last_name="admin", | ||||||
|             first_name="admin", |             first_name="admin", | ||||||
|             bank="Société générale", |             bank="Société générale", | ||||||
|             caution_check=True, |  | ||||||
|         )) |         )) | ||||||
|         self.assertEqual(response.status_code, 200) |         self.assertEqual(response.status_code, 200) | ||||||
|         self.assertFalse(response.context["form"].is_valid()) |         self.assertFalse(response.context["form"].is_valid()) | ||||||
|         self.assertTrue("This team doesn't belong to the given bus." in str(response.context["form"].errors)) |         self.assertTrue("This team doesn't belong to the given bus." in str(response.context["form"].errors)) | ||||||
|  |  | ||||||
|         response = self.client.post(reverse("wei:validate_registration", kwargs=dict(pk=self.registration.pk)), dict( |         response = self.client.post(reverse("wei:validate_registration", kwargs=dict(pk=self.registration.pk)), dict( | ||||||
|             roles=[WEIRole.objects.get(name="Adhérent⋅e WEI").id], |             roles=[WEIRole.objects.get(name="GC WEI").id], | ||||||
|             bus=self.bus.pk, |             bus=self.bus.pk, | ||||||
|             team=self.team.pk, |             team=self.team.pk, | ||||||
|             credit_type=4,  # Bank transfer |             credit_type=4,  # Bank transfer | ||||||
| @@ -655,10 +646,8 @@ class TestWEIRegistration(TestCase): | |||||||
|             last_name="admin", |             last_name="admin", | ||||||
|             first_name="admin", |             first_name="admin", | ||||||
|             bank="Société générale", |             bank="Société générale", | ||||||
|             caution_check=True, |  | ||||||
|         )) |         )) | ||||||
|         self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200) |         self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200) | ||||||
|  |  | ||||||
|         # Check if the membership is successfully created |         # Check if the membership is successfully created | ||||||
|         membership = WEIMembership.objects.filter(user_id=self.user.id, club=self.wei) |         membership = WEIMembership.objects.filter(user_id=self.user.id, club=self.wei) | ||||||
|         self.assertTrue(membership.exists()) |         self.assertTrue(membership.exists()) | ||||||
|   | |||||||
| @@ -4,18 +4,16 @@ | |||||||
| import os | import os | ||||||
| import shutil | import shutil | ||||||
| import subprocess | import subprocess | ||||||
| from datetime import date | from datetime import date, timedelta | ||||||
| from tempfile import mkdtemp | from tempfile import mkdtemp | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.contrib import messages |  | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin | from django.contrib.auth.mixins import LoginRequiredMixin | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
| from django.core.exceptions import PermissionDenied | from django.core.exceptions import PermissionDenied | ||||||
| from django.db import transaction | from django.db import transaction | ||||||
| from django.db.models import Q, Count | from django.db.models import Q, Count | ||||||
| from django.db.models.functions.text import Lower | from django.db.models.functions.text import Lower | ||||||
| from django import forms |  | ||||||
| from django.http import HttpResponse, Http404 | from django.http import HttpResponse, Http404 | ||||||
| from django.shortcuts import redirect | from django.shortcuts import redirect | ||||||
| from django.template.loader import render_to_string | from django.template.loader import render_to_string | ||||||
| @@ -35,7 +33,7 @@ from permission.views import ProtectQuerysetMixin, ProtectedCreateView | |||||||
|  |  | ||||||
| from .forms.registration import WEIChooseBusForm | from .forms.registration import WEIChooseBusForm | ||||||
| from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole | from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole | ||||||
| from .forms import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, BusForm, BusTeamForm, WEIMembership1AForm, \ | from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \ | ||||||
|     WEIMembershipForm, CurrentSurvey |     WEIMembershipForm, CurrentSurvey | ||||||
| from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \ | from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \ | ||||||
|     WEIRegistration1ATable, WEIMembershipTable |     WEIRegistration1ATable, WEIMembershipTable | ||||||
| @@ -443,10 +441,6 @@ class BusTeamCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         self.object.refresh_from_db() |         self.object.refresh_from_db() | ||||||
|         return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk}) |         return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk}) | ||||||
|  |  | ||||||
|     def get_template_names(self): |  | ||||||
|         names = super().get_template_names() |  | ||||||
|         return names |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | ||||||
|     """ |     """ | ||||||
| @@ -479,10 +473,6 @@ class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | |||||||
|         self.object.refresh_from_db() |         self.object.refresh_from_db() | ||||||
|         return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk}) |         return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk}) | ||||||
|  |  | ||||||
|     def get_template_names(self): |  | ||||||
|         names = super().get_template_names() |  | ||||||
|         return names |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): | class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): | ||||||
|     """ |     """ | ||||||
| @@ -510,7 +500,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     Register a new user to the WEI |     Register a new user to the WEI | ||||||
|     """ |     """ | ||||||
|     model = WEIRegistration |     model = WEIRegistration | ||||||
|     form_class = WEIRegistration1AForm |     form_class = WEIRegistrationForm | ||||||
|     extra_context = {"title": _("Register first year student to the WEI")} |     extra_context = {"title": _("Register first year student to the WEI")} | ||||||
|  |  | ||||||
|     def get_sample_object(self): |     def get_sample_object(self): | ||||||
| @@ -556,17 +546,9 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     def get_form(self, form_class=None): |     def get_form(self, form_class=None): | ||||||
|         form = super().get_form(form_class) |         form = super().get_form(form_class) | ||||||
|         form.fields["user"].initial = self.request.user |         form.fields["user"].initial = self.request.user | ||||||
|  |  | ||||||
|         # Cacher les champs pendant l'inscription initiale |  | ||||||
|         if "first_year" in form.fields: |  | ||||||
|         del form.fields["first_year"] |         del form.fields["first_year"] | ||||||
|         if "caution_check" in form.fields: |  | ||||||
|         del form.fields["caution_check"] |         del form.fields["caution_check"] | ||||||
|         if "information_json" in form.fields: |  | ||||||
|         del form.fields["information_json"] |         del form.fields["information_json"] | ||||||
|         if "caution_type" in form.fields: |  | ||||||
|             del form.fields["caution_type"] |  | ||||||
|  |  | ||||||
|         return form |         return form | ||||||
|  |  | ||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
| @@ -604,7 +586,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     Register an old user to the WEI |     Register an old user to the WEI | ||||||
|     """ |     """ | ||||||
|     model = WEIRegistration |     model = WEIRegistration | ||||||
|     form_class = WEIRegistration2AForm |     form_class = WEIRegistrationForm | ||||||
|     extra_context = {"title": _("Register old student to the WEI")} |     extra_context = {"title": _("Register old student to the WEI")} | ||||||
|  |  | ||||||
|     def get_sample_object(self): |     def get_sample_object(self): | ||||||
| @@ -662,20 +644,10 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|             form.fields["soge_credit"].disabled = True |             form.fields["soge_credit"].disabled = True | ||||||
|             form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.") |             form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.") | ||||||
|  |  | ||||||
|         # Cacher les champs pendant l'inscription initiale |  | ||||||
|         if "first_year" in form.fields: |  | ||||||
|             del form.fields["first_year"] |  | ||||||
|         if "caution_check" in form.fields: |  | ||||||
|         del form.fields["caution_check"] |         del form.fields["caution_check"] | ||||||
|         if "information_json" in form.fields: |         del form.fields["first_year"] | ||||||
|         del form.fields["information_json"] |         del form.fields["information_json"] | ||||||
|  |  | ||||||
|         # S'assurer que le champ caution_type est obligatoire |  | ||||||
|         if "caution_type" in form.fields: |  | ||||||
|             form.fields["caution_type"].required = True |  | ||||||
|             form.fields["caution_type"].help_text = _("Choose how you want to pay the deposit") |  | ||||||
|             form.fields["caution_type"].widget = forms.RadioSelect(choices=form.fields["caution_type"].choices) |  | ||||||
|  |  | ||||||
|         return form |         return form | ||||||
|  |  | ||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
| @@ -701,9 +673,6 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]] |         information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]] | ||||||
|         information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]] |         information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]] | ||||||
|         form.instance.information = information |         form.instance.information = information | ||||||
|  |  | ||||||
|         # Sauvegarder le type de caution |  | ||||||
|         form.instance.caution_type = form.cleaned_data["caution_type"] |  | ||||||
|         form.instance.save() |         form.instance.save() | ||||||
|  |  | ||||||
|         if 'treasury' in settings.INSTALLED_APPS: |         if 'treasury' in settings.INSTALLED_APPS: | ||||||
| @@ -733,15 +702,11 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update | |||||||
|         # We can't update a registration once the WEI is started and before the membership start date |         # We can't update a registration once the WEI is started and before the membership start date | ||||||
|         if today >= wei.date_start or today < wei.membership_start: |         if today >= wei.date_start or today < wei.membership_start: | ||||||
|             return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) |             return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) | ||||||
|         # Store the validate parameter in the view's state |  | ||||||
|         self.should_validate = request.GET.get('validate', False) |  | ||||||
|         return super().dispatch(request, *args, **kwargs) |         return super().dispatch(request, *args, **kwargs) | ||||||
|  |  | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         context = super().get_context_data(**kwargs) |         context = super().get_context_data(**kwargs) | ||||||
|         context["club"] = self.object.wei |         context["club"] = self.object.wei | ||||||
|         # Pass the validate parameter to the template |  | ||||||
|         context["should_validate"] = self.should_validate |  | ||||||
|  |  | ||||||
|         if self.object.is_validated: |         if self.object.is_validated: | ||||||
|             membership_form = self.get_membership_form(instance=self.object.membership, |             membership_form = self.get_membership_form(instance=self.object.membership, | ||||||
| @@ -775,16 +740,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update | |||||||
|         # The auto-json-format may cause issues with the default field remove |         # The auto-json-format may cause issues with the default field remove | ||||||
|         if not PermissionBackend.check_perm(self.request, 'wei.change_weiregistration_information_json', self.object): |         if not PermissionBackend.check_perm(self.request, 'wei.change_weiregistration_information_json', self.object): | ||||||
|             del form.fields["information_json"] |             del form.fields["information_json"] | ||||||
|         # Masquer le champ caution_check pour tout le monde dans le formulaire de modification |  | ||||||
|         if "caution_check" in form.fields: |  | ||||||
|             del form.fields["caution_check"] |  | ||||||
|  |  | ||||||
|         # S'assurer que le champ caution_type est obligatoire pour les 2A+ |  | ||||||
|         if not self.object.first_year and "caution_type" in form.fields: |  | ||||||
|             form.fields["caution_type"].required = True |  | ||||||
|             form.fields["caution_type"].help_text = _("Choose how you want to pay the deposit") |  | ||||||
|             form.fields["caution_type"].widget = forms.RadioSelect(choices=form.fields["caution_type"].choices) |  | ||||||
|  |  | ||||||
|         return form |         return form | ||||||
|  |  | ||||||
|     def get_membership_form(self, data=None, instance=None): |     def get_membership_form(self, data=None, instance=None): | ||||||
| @@ -804,30 +759,10 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update | |||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         # If the membership is already validated, then we update the bus and the team (and the roles) |         # If the membership is already validated, then we update the bus and the team (and the roles) | ||||||
|         if form.instance.is_validated: |         if form.instance.is_validated: | ||||||
|             try: |             membership_form = self.get_membership_form(self.request.POST, form.instance.membership) | ||||||
|                 membership = form.instance.membership |  | ||||||
|                 if membership is None: |  | ||||||
|                     raise ValueError(_("No membership found for this registration")) |  | ||||||
|  |  | ||||||
|                 membership_form = self.get_membership_form(self.request.POST, instance=membership) |  | ||||||
|             if not membership_form.is_valid(): |             if not membership_form.is_valid(): | ||||||
|                 return self.form_invalid(form) |                 return self.form_invalid(form) | ||||||
|  |  | ||||||
|                 # Vérifier que l'utilisateur a la permission de modifier le membership |  | ||||||
|                 # On vérifie d'abord si l'utilisateur a la permission générale de modification |  | ||||||
|                 if not self.request.user.has_perm("wei.change_weimembership"): |  | ||||||
|                     raise PermissionDenied(_("You don't have the permission to update memberships")) |  | ||||||
|  |  | ||||||
|                 # On vérifie ensuite les permissions spécifiques pour chaque champ modifié |  | ||||||
|                 for field_name in membership_form.changed_data: |  | ||||||
|                     perm = f"wei.change_weimembership_{field_name}" |  | ||||||
|                     if not self.request.user.has_perm(perm): |  | ||||||
|                         raise PermissionDenied(_("You don't have the permission to update the field %(field)s") % {'field': field_name}) |  | ||||||
|  |  | ||||||
|             membership_form.save() |             membership_form.save() | ||||||
|             except (WEIMembership.DoesNotExist, ValueError, PermissionDenied) as e: |  | ||||||
|                 form.add_error(None, str(e)) |  | ||||||
|                 return self.form_invalid(form) |  | ||||||
|         # If it is not validated and if this is an old member, then we update the choices |         # If it is not validated and if this is an old member, then we update the choices | ||||||
|         elif not form.instance.first_year and PermissionBackend.check_perm( |         elif not form.instance.first_year and PermissionBackend.check_perm( | ||||||
|                 self.request, "wei.change_weiregistration_information_json", self.object): |                 self.request, "wei.change_weiregistration_information_json", self.object): | ||||||
| @@ -842,10 +777,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update | |||||||
|             information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]] |             information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]] | ||||||
|             information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]] |             information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]] | ||||||
|             form.instance.information = information |             form.instance.information = information | ||||||
|  |  | ||||||
|             # Sauvegarder le type de caution pour les 2A+ |  | ||||||
|             if "caution_type" in form.cleaned_data: |  | ||||||
|                 form.instance.caution_type = form.cleaned_data["caution_type"] |  | ||||||
|             form.instance.save() |             form.instance.save() | ||||||
|  |  | ||||||
|         return super().form_valid(form) |         return super().form_valid(form) | ||||||
| @@ -856,8 +787,14 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update | |||||||
|             survey = CurrentSurvey(self.object) |             survey = CurrentSurvey(self.object) | ||||||
|             if not survey.is_complete(): |             if not survey.is_complete(): | ||||||
|                 return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk}) |                 return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk}) | ||||||
|         # On redirige vers la validation uniquement si c'est explicitement demandé (et stocké dans la vue) |         if PermissionBackend.check_perm(self.request, "wei.add_weimembership", WEIMembership( | ||||||
|         if self.should_validate and self.request.user.has_perm("wei.add_weimembership"): |             club=self.object.wei, | ||||||
|  |             user=self.object.user, | ||||||
|  |             date_start=date.today(), | ||||||
|  |             date_end=date.today(), | ||||||
|  |             fee=0, | ||||||
|  |             registration=self.object, | ||||||
|  |         )): | ||||||
|             return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk}) |             return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk}) | ||||||
|         return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk}) |         return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk}) | ||||||
|  |  | ||||||
| @@ -899,23 +836,18 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|     extra_context = {"title": _("Validate WEI registration")} |     extra_context = {"title": _("Validate WEI registration")} | ||||||
|  |  | ||||||
|     def get_sample_object(self): |     def get_sample_object(self): | ||||||
|         """ |  | ||||||
|         Return a sample object for permission checking |  | ||||||
|         """ |  | ||||||
|         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) |         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) | ||||||
|         return WEIMembership( |         return WEIMembership( | ||||||
|             user=registration.user, |  | ||||||
|             club=registration.wei, |             club=registration.wei, | ||||||
|             date_start=registration.wei.date_start, |             user=registration.user, | ||||||
|             fee=registration.wei.membership_fee_paid if registration.user.profile.paid else registration.wei.membership_fee_unpaid, |             date_start=date.today(), | ||||||
|             # Add any fields needed for proper permission checking |             date_end=date.today() + timedelta(days=1), | ||||||
|  |             fee=0, | ||||||
|             registration=registration, |             registration=registration, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def dispatch(self, request, *args, **kwargs): |     def dispatch(self, request, *args, **kwargs): | ||||||
|         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) |         wei = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei | ||||||
|  |  | ||||||
|         wei = registration.wei |  | ||||||
|         today = date.today() |         today = date.today() | ||||||
|         # We can't validate anyone once the WEI is started and before the membership start date |         # We can't validate anyone once the WEI is started and before the membership start date | ||||||
|         if today >= wei.date_start or today < wei.membership_start: |         if today >= wei.date_start or today < wei.membership_start: | ||||||
| @@ -946,14 +878,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|             date_start__gte=bde.membership_start, |             date_start__gte=bde.membership_start, | ||||||
|         ).exists() |         ).exists() | ||||||
|  |  | ||||||
|         fee = registration.fee |         context["fee"] = registration.fee | ||||||
|         context["fee"] = fee |  | ||||||
|  |  | ||||||
|         # Calculer le montant total nécessaire (frais + caution si transaction) |  | ||||||
|         total_needed = fee |  | ||||||
|         if registration.caution_type == 'note': |  | ||||||
|             total_needed += registration.wei.caution_amount |  | ||||||
|         context["total_needed"] = total_needed |  | ||||||
|  |  | ||||||
|         form = context["form"] |         form = context["form"] | ||||||
|         if registration.soge_credit: |         if registration.soge_credit: | ||||||
| @@ -965,7 +890,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|  |  | ||||||
|     def get_form_class(self): |     def get_form_class(self): | ||||||
|         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) |         registration = WEIRegistration.objects.get(pk=self.kwargs["pk"]) | ||||||
|         if registration.first_year and 'selected_bus_pk' not in registration.information: |         if registration.first_year and 'sleected_bus_pk' not in registration.information: | ||||||
|             return WEIMembership1AForm |             return WEIMembership1AForm | ||||||
|         return WEIMembershipForm |         return WEIMembershipForm | ||||||
|  |  | ||||||
| @@ -975,24 +900,8 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         form.fields["last_name"].initial = registration.user.last_name |         form.fields["last_name"].initial = registration.user.last_name | ||||||
|         form.fields["first_name"].initial = registration.user.first_name |         form.fields["first_name"].initial = registration.user.first_name | ||||||
|  |  | ||||||
|         # Ajouter le champ caution_check uniquement pour les non-première année et le rendre obligatoire |         if "caution_check" in form.fields: | ||||||
|         if not registration.first_year: |             form.fields["caution_check"].initial = registration.caution_check | ||||||
|             if registration.caution_type == 'check': |  | ||||||
|                 form.fields["caution_check"] = forms.BooleanField( |  | ||||||
|                     required=True, |  | ||||||
|                     initial=registration.caution_check, |  | ||||||
|                     label=_("Caution check given"), |  | ||||||
|                     help_text=_("Please make sure the check is given before validating the registration") |  | ||||||
|                 ) |  | ||||||
|             else: |  | ||||||
|                 form.fields["caution_check"] = forms.BooleanField( |  | ||||||
|                     required=True, |  | ||||||
|                     initial=False, |  | ||||||
|                     label=_("Create deposit transaction"), |  | ||||||
|                     help_text=_("A transaction of %(amount).2f€ will be created from the user's Note account") % { |  | ||||||
|                         'amount': registration.wei.caution_amount / 100 |  | ||||||
|                     } |  | ||||||
|                 ) |  | ||||||
|  |  | ||||||
|         if registration.soge_credit: |         if registration.soge_credit: | ||||||
|             form.fields["credit_type"].disabled = True |             form.fields["credit_type"].disabled = True | ||||||
| @@ -1076,20 +985,10 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         if credit_type is None or registration.soge_credit: |         if credit_type is None or registration.soge_credit: | ||||||
|             credit_amount = 0 |             credit_amount = 0 | ||||||
|  |  | ||||||
|         # Calculer le montant total nécessaire (frais + caution si transaction) |         if not registration.soge_credit and user.note.balance + credit_amount < fee: | ||||||
|         total_needed = fee |             # Users must have money before registering to the WEI. | ||||||
|         if registration.caution_type == 'note': |  | ||||||
|             total_needed += club.caution_amount |  | ||||||
|  |  | ||||||
|         # Vérifier que l'utilisateur a assez d'argent pour tout payer |  | ||||||
|         if not registration.soge_credit and user.note.balance + credit_amount < total_needed: |  | ||||||
|             form.add_error('credit_type', |             form.add_error('credit_type', | ||||||
|                            _("This user doesn't have enough money to join this club and pay the deposit. " |                            _("This user don't have enough money to join this club, and can't have a negative balance.")) | ||||||
|                                "Current balance: %(balance)d€, credit: %(credit)d€, needed: %(needed)d€") % { |  | ||||||
|                                'balance': user.note.balance, |  | ||||||
|                                'credit': credit_amount, |  | ||||||
|                                'needed': total_needed} |  | ||||||
|                            ) |  | ||||||
|             return super().form_invalid(form) |             return super().form_invalid(form) | ||||||
|  |  | ||||||
|         if credit_amount: |         if credit_amount: | ||||||
| @@ -1129,18 +1028,6 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
|         membership.refresh_from_db() |         membership.refresh_from_db() | ||||||
|         membership.roles.add(WEIRole.objects.get(name="Adhérent⋅e WEI")) |         membership.roles.add(WEIRole.objects.get(name="Adhérent⋅e WEI")) | ||||||
|  |  | ||||||
|         # Créer la transaction de caution si nécessaire |  | ||||||
|         if registration.caution_type == 'note': |  | ||||||
|             from note.models import Transaction |  | ||||||
|             Transaction.objects.create( |  | ||||||
|                 source=user.note, |  | ||||||
|                 destination=club.note, |  | ||||||
|                 quantity=1, |  | ||||||
|                 amount=club.caution_amount, |  | ||||||
|                 reason=_("Caution %(name)s") % {'name': club.name}, |  | ||||||
|                 valid=True, |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         return super().form_valid(form) |         return super().form_valid(form) | ||||||
|  |  | ||||||
|     def get_success_url(self): |     def get_success_url(self): | ||||||
| @@ -1360,7 +1247,6 @@ class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView): | |||||||
|     def get_queryset(self, filter_permissions=True, **kwargs): |     def get_queryset(self, filter_permissions=True, **kwargs): | ||||||
|         qs = super().get_queryset(filter_permissions, **kwargs) |         qs = super().get_queryset(filter_permissions, **kwargs) | ||||||
|         qs = qs.filter(first_year=True, membership__isnull=False) |         qs = qs.filter(first_year=True, membership__isnull=False) | ||||||
|         qs = qs.filter(wei=self.club) |  | ||||||
|         qs = qs.order_by('-membership__bus') |         qs = qs.order_by('-membership__bus') | ||||||
|         return qs |         return qs | ||||||
|  |  | ||||||
| @@ -1403,22 +1289,8 @@ class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView): | |||||||
|         if not wei.exists(): |         if not wei.exists(): | ||||||
|             raise Http404 |             raise Http404 | ||||||
|         wei = wei.get() |         wei = wei.get() | ||||||
|  |         qs = WEIRegistration.objects.filter(wei=wei, membership__isnull=False, membership__bus__isnull=True) | ||||||
|         # On cherche d'abord les 1A qui ont une inscription validée (membership) mais pas de bus |         qs = qs.filter(information_json__contains='selected_bus_pk')  # not perfect, but works... | ||||||
|         qs = WEIRegistration.objects.filter( |         if qs.exists(): | ||||||
|             wei=wei, |             return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, )) | ||||||
|             first_year=True, |         return reverse_lazy('wei:wei_1A_list', args=(wei.pk, )) | ||||||
|             membership__isnull=False, |  | ||||||
|             membership__bus__isnull=True |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Parmi eux, on prend ceux qui ont répondu au questionnaire (ont un bus préféré) |  | ||||||
|         qs = qs.filter(information_json__contains='selected_bus_pk') |  | ||||||
|  |  | ||||||
|         if not qs.exists(): |  | ||||||
|             # Si on ne trouve personne, on affiche un message et on retourne à la liste |  | ||||||
|             messages.info(self.request, _("No first year student without a bus found. Either all of them have a bus, or none has filled the survey yet.")) |  | ||||||
|             return reverse_lazy('wei:wei_1A_list', args=(wei.pk,)) |  | ||||||
|  |  | ||||||
|         # On redirige vers la page d'attribution pour le premier étudiant trouvé |  | ||||||
|         return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk,)) |  | ||||||
|   | |||||||
| @@ -136,7 +136,7 @@ de diffusion utiles. | |||||||
|    Faîtes attention, donc où la sortie est stockée. |    Faîtes attention, donc où la sortie est stockée. | ||||||
|  |  | ||||||
|  |  | ||||||
| Il prend 4 options : | Il prend 2 options : | ||||||
|  |  | ||||||
| * ``--type``, qui prend en argument ``members`` (défaut), ``clubs``, ``events``, ``art``, | * ``--type``, qui prend en argument ``members`` (défaut), ``clubs``, ``events``, ``art``, | ||||||
|   ``sport``, qui permet respectivement de sortir la liste des adresses mails des adhérent⋅es |   ``sport``, qui permet respectivement de sortir la liste des adresses mails des adhérent⋅es | ||||||
| @@ -149,10 +149,7 @@ Il prend 4 options : | |||||||
|   pour la ML Adhérents, pour exporter les mails des adhérents au BDE pendant n'importe  |   pour la ML Adhérents, pour exporter les mails des adhérents au BDE pendant n'importe  | ||||||
|   laquelle des ``n+1`` dernières années.  |   laquelle des ``n+1`` dernières années.  | ||||||
|  |  | ||||||
| * ``--email``, qui prend en argument une chaine de caractère contenant une adresse email. | Le script sort sur la sortie standard la liste des adresses mails à inscrire. | ||||||
|    |  | ||||||
| Si aucun email n'est renseigné, le script sort sur la sortie standard la liste des adresses mails à inscrire. |  | ||||||
| Dans le cas contraire, la liste est envoyée à l'adresse passée en argument. |  | ||||||
|  |  | ||||||
| Attention : il y a parfois certains cas particuliers à prendre en compte, il n'est | Attention : il y a parfois certains cas particuliers à prendre en compte, il n'est | ||||||
| malheureusement pas aussi simple que de simplement supposer que ces listes sont exhaustives. | malheureusement pas aussi simple que de simplement supposer que ces listes sont exhaustives. | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -63,16 +63,8 @@ class ColorWidget(Widget): | |||||||
|     def format_value(self, value): |     def format_value(self, value): | ||||||
|         if value is None: |         if value is None: | ||||||
|             value = 0xFFFFFF |             value = 0xFFFFFF | ||||||
|         if isinstance(value, str): |  | ||||||
|             return value  # Assume it's already a hex string like "#FFAA33" |  | ||||||
|         try: |  | ||||||
|         return "#{:06X}".format(value) |         return "#{:06X}".format(value) | ||||||
|         except Exception: |  | ||||||
|             return "#FFFFFF" |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     def value_from_datadict(self, data, files, name): |     def value_from_datadict(self, data, files, name): | ||||||
|         val = super().value_from_datadict(data, files, name) |         val = super().value_from_datadict(data, files, name) | ||||||
|         if val: |  | ||||||
|         return int(val[1:], 16) |         return int(val[1:], 16) | ||||||
|         return None |  | ||||||
| @@ -270,7 +270,7 @@ OAUTH2_PROVIDER = { | |||||||
|     'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0) |     'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0) | ||||||
|     'OIDC_ENABLED': True, |     'OIDC_ENABLED': True, | ||||||
|     'OIDC_RSA_PRIVATE_KEY': |     'OIDC_RSA_PRIVATE_KEY': | ||||||
|         os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'), |         os.getenv('OIDC_RSA_PRIVATE_KEY', 'CHANGE_ME_IN_ENV_SETTINGS').replace('\\n', '\n'), # for multilines | ||||||
|     'SCOPES': { 'openid': "OpenID Connect scope" }, |     'SCOPES': { 'openid': "OpenID Connect scope" }, | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +0,0 @@ | |||||||
| <input type="text" |  | ||||||
|        name="{{ widget.name }}" |  | ||||||
|        value="{{ widget.value }}" |  | ||||||
|        class="jscolor" |  | ||||||
|        {% include "django/forms/widgets/attrs.html" %}> |  | ||||||
		Reference in New Issue
	
	Block a user