1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-11-01 16:14:30 +01:00

Fix dish form and add kitchen view

This commit is contained in:
Ehouarn
2025-10-31 17:47:11 +01:00
parent 6bf21b103f
commit 30a598c0b7
8 changed files with 120 additions and 23 deletions

View File

@@ -219,7 +219,7 @@ SupplementFormSet = forms.inlineformset_factory(
Dish,
Supplement,
form=SupplementForm,
extra=0,
extra=1,
)

View File

@@ -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 + '/',

View File

@@ -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',

View File

@@ -31,6 +31,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans "Update" %}
</a>
{% endif %}
<a class="btn btn-sm btn-primary" href="{% url "food:dish_list" activity_pk=dish.activity.pk %}">
{% trans "Return to dish list" %}
</a>
{% if delete %}
<a class="btn btn-sm btn-danger" href="{% url "food:dish_delete" activity_pk=dish.activity.pk pk=dish.pk %}">
{% trans "Delete" %}

View File

@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% crispy form %}
</div>
<h3 class="card-header text-center">
{% trans "Ajouter des suppléments (optionnel)" %}
{% trans "Add supplements (optional)" %}
</h3>
{{ formset.management_form }}
<table class="table table-condensed table-striped">

View File

@@ -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 %}
<!-- Colonne de plats -->
<div style="display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 2rem;">
{% for food, quantity in orders.items %}
<div class="card bg-white mb-3" style="flex: 1 1 calc(33.333% - 1rem); border: 1px solid #ccc; padding: 1rem; border-radius: 0.5rem; box-sizing: border-box;">
<h3 class="card-header text-center">
<strong>{{ food }}</strong><br>
</h3>
<h1 class="card-body text-center">
{{ quantity }}</h1>
</div>
{% endfor %}
</div>
<!-- Colonne de la table -->
<div class="card bg-white mb-3">
<h3 class="card-header text-center">
{% trans "Special orders" %}
</h3>
{% if table.data %}
{% render_table table %}
{% else %}
<div class="card-body">
<div class="alert alert-warning">
{% trans "There are no special orders." %}
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -28,5 +28,5 @@ urlpatterns = [
path('activity/<int:activity_pk>/order/', views.OrderCreateView.as_view(), name='order_create'),
path('activity/<int:activity_pk>/orders/', views.OrderListView.as_view(), name='order_list'),
path('activity/<int:activity_pk>/orders/served', views.ServedOrderListView.as_view(), name='served_order_list'),
path('activity/orders/<int:pk>/delete/', views.OrderDeleteView.as_view(), name='order_delete'),
path('activity/<int:activity_pk>/kitchen/', views.KitchenView.as_view(), name='kitchen'),
]

View File

@@ -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