diff --git a/apps/food/forms.py b/apps/food/forms.py
index dbfd2da3..72bef4c4 100644
--- a/apps/food/forms.py
+++ b/apps/food/forms.py
@@ -219,7 +219,7 @@ SupplementFormSet = forms.inlineformset_factory(
Dish,
Supplement,
form=SupplementForm,
- extra=0,
+ extra=1,
)
diff --git a/apps/food/static/food/js/order.js b/apps/food/static/food/js/order.js
index b7df89bf..0e2043cc 100644
--- a/apps/food/static/food/js/order.js
+++ b/apps/food/static/food/js/order.js
@@ -21,7 +21,6 @@ function delete_button (button_id, table_id) {
* @param table_id: Id of the table to reload
*/
function serve_button(button_id, table_id, current_state) {
- console.log("update")
const new_state = !current_state;
$.ajax({
url: '/api/food/order/' + button_id + '/',
diff --git a/apps/food/tables.py b/apps/food/tables.py
index 5deb8ca0..964797f1 100644
--- a/apps/food/tables.py
+++ b/apps/food/tables.py
@@ -106,14 +106,10 @@ class OrderTable(tables.Table):
get_current_request(), "food.change_order_saved",
record) else '')}}, verbose_name=_("Serve"), )
- request = tables.Column(
- orderable=False
- )
-
class Meta:
model = Order
template_name = 'django_tables2/bootstrap4.html'
- fields = ('ordered_at', 'user', 'dish', 'supplements', 'request', 'serve', 'delete')
+ fields = ('number', 'ordered_at', 'user', 'dish', 'supplements', 'request', 'serve', 'delete')
order_by = ('ordered_at', )
row_attrs = {
'class': 'table-row',
diff --git a/apps/food/templates/food/dish_detail.html b/apps/food/templates/food/dish_detail.html
index 6ac5339f..136672cf 100644
--- a/apps/food/templates/food/dish_detail.html
+++ b/apps/food/templates/food/dish_detail.html
@@ -31,6 +31,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans "Update" %}
{% endif %}
+
+ {% trans "Return to dish list" %}
+
{% if delete %}
{% trans "Delete" %}
diff --git a/apps/food/templates/food/dish_form.html b/apps/food/templates/food/dish_form.html
index 5480847f..f7bc6c90 100644
--- a/apps/food/templates/food/dish_form.html
+++ b/apps/food/templates/food/dish_form.html
@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% crispy form %}
{{ formset.management_form }}
diff --git a/apps/food/templates/food/kitchen.html b/apps/food/templates/food/kitchen.html
new file mode 100644
index 00000000..01674e58
--- /dev/null
+++ b/apps/food/templates/food/kitchen.html
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+{% comment %}
+Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load render_table from django_tables2 %}
+{% load i18n %}
+
+{% block content %}
+
+
+
+ {% for food, quantity in orders.items %}
+
+
+
+
+ {{ quantity }}
+
+ {% endfor %}
+
+
+
+
+
+ {% if table.data %}
+ {% render_table table %}
+ {% else %}
+
+
+ {% trans "There are no special orders." %}
+
+
+ {% endif %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/food/urls.py b/apps/food/urls.py
index 4c4ffe2d..92a21e9f 100644
--- a/apps/food/urls.py
+++ b/apps/food/urls.py
@@ -28,5 +28,5 @@ urlpatterns = [
path('activity//order/', views.OrderCreateView.as_view(), name='order_create'),
path('activity//orders/', views.OrderListView.as_view(), name='order_list'),
path('activity//orders/served', views.ServedOrderListView.as_view(), name='served_order_list'),
- path('activity/orders//delete/', views.OrderDeleteView.as_view(), name='order_delete'),
+ path('activity//kitchen/', views.KitchenView.as_view(), name='kitchen'),
]
diff --git a/apps/food/views.py b/apps/food/views.py
index 2ffbbd4b..7e6bde15 100644
--- a/apps/food/views.py
+++ b/apps/food/views.py
@@ -8,7 +8,7 @@ from crispy_forms.helper import FormHelper
from django_tables2.views import SingleTableView, MultiTableMixin
from django.core.exceptions import PermissionDenied
from django.db import transaction
-from django.db.models import Q
+from django.db.models import Q, Count
from django.http import HttpResponseRedirect, Http404
from django.views.generic import DetailView, UpdateView, CreateView
from django.views.generic.list import ListView
@@ -22,7 +22,7 @@ from activity.models import Activity
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView, LoginRequiredMixin
-from .models import Food, BasicFood, TransformedFood, QRCode, Order, Dish
+from .models import Food, BasicFood, TransformedFood, QRCode, Order, Dish, Supplement
from .forms import QRCodeForms, BasicFoodForms, TransformedFoodForms, \
ManageIngredientsForm, ManageIngredientsFormSet, AddIngredientForms, \
BasicFoodUpdateForms, TransformedFoodUpdateForms, \
@@ -591,7 +591,8 @@ class DishCreateView(ProtectQuerysetMixin, ProtectedCreateView):
formset = SupplementFormSet(self.request.POST, instance=form.instance)
if formset.is_valid():
for f in formset:
- if f.is_valid():
+ # We don't save the product if the price is not entered, ie. if the line is empty
+ if f.is_valid() and f.instance.price:
f.save()
f.instance.save()
else:
@@ -656,12 +657,54 @@ class DishUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form_class = DishForm
extra_context = {"title": _("Update a dish")}
- def get_form(self, **kwargs):
- form = super().get_form(**kwargs)
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ form = context['form']
+ form.helper = FormHelper()
+ # Remove form tag on the generation of the form in the template (already present on the template)
+ form.helper.form_tag = False
+ # The formset handles the set of the supplements
+ form_set = SupplementFormSet(instance=form.instance)
+ context['formset'] = form_set
+ context['helper'] = SupplementFormSetHelper()
+
+ return context
+
+ def get_form(self, form_class=None):
+ form = super().get_form(form_class)
if 'main' in form.fields:
del form.fields["main"]
return form
+ @transaction.atomic
+ def form_valid(self, form):
+ activity = Activity.objects.get(pk=self.kwargs["activity_pk"])
+
+ form.instance.activity = activity
+
+ ret = super().form_valid(form)
+
+ # For each supplement, we save it
+ formset = SupplementFormSet(self.request.POST, instance=form.instance)
+ saved = []
+ if formset.is_valid():
+ for f in formset:
+ # We don't save the product if the price is not entered, ie. if the line is empty
+ if f.is_valid() and f.instance.price:
+ f.save()
+ f.instance.save()
+ saved.append(f.instance.pk)
+ else:
+ f.instance = None
+ # Remove old supplements that weren't given in the form
+ Supplement.objects.filter(~Q(pk__in=saved), dish=form.instance).delete()
+
+ return ret
+
+ def get_success_url(self):
+ return reverse_lazy('food:dish_detail', kwargs={"activity_pk": self.kwargs["activity_pk"], "pk": self.kwargs["pk"]})
+
class DishDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
"""
@@ -787,17 +830,32 @@ class ServedOrderListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableV
return context
-class OrderDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
+class KitchenView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
- Delete an order
+ The view to display useful information for the kitchen
"""
model = Order
- extra_context = {"title": _('Delete dish')}
+ table_class = OrderTable
+ template_name = 'food/kitchen.html'
+ extra_context = {'title': _('Kitchen')}
- def delete(self, request, *args, **kwargs):
- if self.get_object().served:
- raise PermissionDenied(_("This order cannot be deleted because it has already been served"))
- return super().delete(request, *args, **kwargs)
+ def get_queryset(self):
+ return super().get_queryset().filter(~Q(supplements__isnull=True, request=''), activity__pk=self.kwargs["activity_pk"])
- def get_success_url(self):
- return reverse_lazy('food:order_list', kwargs={"activity_pk": self.kwargs["activity_pk"]})
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ orders_count = Order.objects.values('dish__main__name').annotate(quantity=Count('id'))
+
+ context["orders"] = {o['dish__main__name']: o['quantity'] for o in orders_count}
+
+ return context
+
+ def get_table(self, **kwargs):
+ table = super().get_table(**kwargs)
+
+ hide = ["ordered_at", "serve", "delete"]
+ for field in hide:
+ table.columns.hide(field)
+
+ return table