mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-30 23:39:54 +01:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			70abd0f490
			...
			food_bugs
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 108a56745c | ||
|  | fadb289ed7 | ||
|  | 905fc6e7cc | ||
|  | cdd81c1444 | ||
|  | 4afafceba1 | ||
|  | 3065eacc96 | ||
|  | 71ef3aedd8 | ||
|  | 0cf11c6348 | 
							
								
								
									
										19
									
								
								apps/activity/migrations/0007_alter_guest_activity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								apps/activity/migrations/0007_alter_guest_activity.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | # 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.PROTECT, |         on_delete=models.CASCADE, | ||||||
|         related_name='+', |         related_name='+', | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -95,5 +95,23 @@ 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 %} | ||||||
|   | |||||||
| @@ -70,7 +70,10 @@ 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 activity.activity_type.can_invite and not activity_started %} |             {% if not activity.valid and ".delete_"|has_perm:activity %} | ||||||
|  |                 <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,4 +15,5 @@ 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 | from django.http import HttpResponse, JsonResponse | ||||||
| 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,6 +153,34 @@ 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` | ||||||
|   | |||||||
| @@ -168,7 +168,8 @@ 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): | ||||||
|         return BasicFood( |         # We choose a club which may work or BDE else | ||||||
|  |         food = BasicFood( | ||||||
|             name="", |             name="", | ||||||
|             owner_id=1, |             owner_id=1, | ||||||
|             expiry_date=timezone.now(), |             expiry_date=timezone.now(), | ||||||
| @@ -177,6 +178,14 @@ 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: | ||||||
| @@ -227,13 +236,22 @@ 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): | ||||||
|         return TransformedFood( |         # We choose a club which may work or BDE else | ||||||
|  |         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) | ||||||
| @@ -248,7 +266,7 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): | |||||||
| MAX_FORMS = 10 | MAX_FORMS = 10 | ||||||
|  |  | ||||||
|  |  | ||||||
| class ManageIngredientsView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): | class ManageIngredientsView(LoginRequiredMixin, UpdateView): | ||||||
|     """ |     """ | ||||||
|     A view to manage ingredient for a transformed food |     A view to manage ingredient for a transformed food | ||||||
|     """ |     """ | ||||||
| @@ -279,6 +297,14 @@ class ManageIngredientsView(ProtectQuerysetMixin, 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()) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user