mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-12-18 21:42:21 +00:00
Manage food options
Signed-off-by: Emmy D'ANELLO <ynerant@crans.org>
This commit is contained in:
parent
51e5e3669e
commit
5174c84b33
@ -1,12 +1,12 @@
|
|||||||
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from member.models import Club
|
from member.models import Club
|
||||||
from note_kfet.inputs import AmountInput, Autocomplete, DateTimePickerInput
|
from note_kfet.inputs import AmountInput, Autocomplete, DateTimePickerInput
|
||||||
|
|
||||||
from .models import Food, Meal, Sheet
|
from .models import Food, FoodOption, Meal, Sheet
|
||||||
|
|
||||||
|
|
||||||
class SheetForm(forms.ModelForm):
|
class SheetForm(forms.ModelForm):
|
||||||
@ -31,6 +31,32 @@ class FoodForm(forms.ModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = FoodOption
|
||||||
|
fields = '__all__'
|
||||||
|
widgets = {
|
||||||
|
'extra_cost': AmountInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FoodOptionsFormSet = forms.inlineformset_factory(
|
||||||
|
Food,
|
||||||
|
FoodOption,
|
||||||
|
form=FoodOptionForm,
|
||||||
|
extra=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionFormSetHelper(FormHelper):
|
||||||
|
def __init__(self, form=None):
|
||||||
|
super().__init__(form)
|
||||||
|
self.form_tag = False
|
||||||
|
self.form_method = 'POST'
|
||||||
|
self.form_class = 'form-inline'
|
||||||
|
self.template = 'bootstrap4/table_inline_formset.html'
|
||||||
|
|
||||||
|
|
||||||
class MealForm(forms.ModelForm):
|
class MealForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Meal
|
model = Meal
|
||||||
|
@ -14,8 +14,73 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
|
||||||
|
{# The next part concerns the option formset #}
|
||||||
|
{# Generate some hidden fields that manage the number of options, and make easier the parsing #}
|
||||||
|
{{ formset.management_form }}
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
{# Fill initial data #}
|
||||||
|
{% for form in formset %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ form.name.label }}<span class="asteriskField">*</span></th>
|
||||||
|
<th>{{ form.extra_cost.label }}<span class="asteriskField">*</span></th>
|
||||||
|
<th>{{ form.available.label }}<span class="asteriskField">*</span></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="form_body">
|
||||||
|
{% endif %}
|
||||||
|
<tr class="row-formset">
|
||||||
|
<td>{{ form.name }}</td>
|
||||||
|
<td>{{ form.extra_cost }}</td>
|
||||||
|
<td>{{ form.available }}</td>
|
||||||
|
{# These fields are hidden but handled by the formset to link the id and the invoice id #}
|
||||||
|
{{ form.food }}
|
||||||
|
{{ form.id }}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{# Display buttons to add and remove options #}
|
||||||
|
<div class="card-body">
|
||||||
|
<button type="button" id="add_more" class="btn btn-success">{% trans "Add option" %}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
|
{# Hidden div that store an empty product form, to be copied into new forms #}
|
||||||
|
<div id="empty_form" style="display: none;">
|
||||||
|
<table class='no_error'>
|
||||||
|
<tbody id="for_real">
|
||||||
|
<tr class="row-formset">
|
||||||
|
<td>{{ formset.empty_form.name }}</td>
|
||||||
|
<td>{{ formset.empty_form.extra_cost }} </td>
|
||||||
|
<td>{{ formset.empty_form.available }}</td>
|
||||||
|
{{ formset.empty_form.food }}
|
||||||
|
{{ formset.empty_form.id }}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script>
|
||||||
|
/* script that handles add and remove lines */
|
||||||
|
IDS = {};
|
||||||
|
|
||||||
|
$("#id_foodoption_set-TOTAL_FORMS").val($(".row-formset").length - 1);
|
||||||
|
|
||||||
|
$('#add_more').click(function () {
|
||||||
|
let form_idx = $('#id_foodoption_set-TOTAL_FORMS').val();
|
||||||
|
$('#form_body').append($('#for_real').html().replace(/__prefix__/g, form_idx));
|
||||||
|
$('#id_foodoption_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
|
$('#id_foodoption_set-' + parseInt(form_idx) + '-id').val(IDS[parseInt(form_idx)]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -28,7 +28,7 @@
|
|||||||
<h3>{% trans "menu"|capfirst %} :</h3>
|
<h3>{% trans "menu"|capfirst %} :</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{% for meal in sheet.meal_set.all %}
|
{% for meal in sheet.meal_set.all %}
|
||||||
<li{% if not meal.available %} class="text-danger" style="text-decoration: line-through !important;"{% endif %}>
|
<li{% if not meal.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
{{ meal }} ({{ meal.price|pretty_money }})
|
{{ meal }} ({{ meal.price|pretty_money }})
|
||||||
{% if can_change_sheet %}
|
{% if can_change_sheet %}
|
||||||
<a href="{% url 'sheets:meal_update' pk=meal.pk %}" class="badge badge-primary">
|
<a href="{% url 'sheets:meal_update' pk=meal.pk %}" class="badge badge-primary">
|
||||||
@ -40,7 +40,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
<hr>
|
<hr>
|
||||||
{% for food in sheet.food_set.all %}
|
{% for food in sheet.food_set.all %}
|
||||||
<li{% if not food.available %} class="text-danger" style="text-decoration: line-through !important;"{% endif %}>
|
<li{% if not food.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
{{ food }} ({{ food.price|pretty_money }})
|
{{ food }} ({{ food.price|pretty_money }})
|
||||||
{% if can_change_sheet %}
|
{% if can_change_sheet %}
|
||||||
<a href="{% url 'sheets:food_update' pk=food.pk %}" class="badge badge-primary">
|
<a href="{% url 'sheets:food_update' pk=food.pk %}" class="badge badge-primary">
|
||||||
@ -48,11 +48,11 @@
|
|||||||
{% trans "Edit" %}
|
{% trans "Edit" %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if food.option_set.all %}
|
{% if food.foodoption_set.all %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for option in food.available_options %}
|
{% for option in food.foodoption_set.all %}
|
||||||
<li{% if not option.available %} class="text-danger" style="text-decoration: line-through !important;"{% endif %}>
|
<li{% if not option.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
{{ option }}{% if option.extra_price %} ({{ option.extra_price|pretty_money }}){% endif %}
|
{{ option }}{% if option.extra_cost %} ({{ option.extra_cost|pretty_money }}){% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -11,7 +11,7 @@ from django_tables2 import SingleTableView
|
|||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||||
|
|
||||||
from .forms import FoodForm, MealForm, SheetForm
|
from .forms import FoodForm, MealForm, SheetForm, FoodOptionsFormSet, FoodOptionFormSetHelper
|
||||||
from .models import Sheet, Food, Meal
|
from .models import Sheet, Food, Meal
|
||||||
from .tables import SheetTable
|
from .tables import SheetTable
|
||||||
|
|
||||||
@ -80,8 +80,34 @@ class FoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
price=500,
|
price=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 products
|
||||||
|
form_set = FoodOptionsFormSet(instance=form.instance)
|
||||||
|
context['formset'] = form_set
|
||||||
|
context['helper'] = FoodOptionFormSetHelper()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.sheet_id = self.kwargs['pk']
|
form.instance.sheet_id = self.kwargs['pk']
|
||||||
|
|
||||||
|
# For each product, we save it
|
||||||
|
formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
|
||||||
|
if formset.is_valid():
|
||||||
|
for f in formset:
|
||||||
|
# We don't save the product if the designation is not entered, ie. if the line is empty
|
||||||
|
if f.is_valid() and f.instance.name:
|
||||||
|
f.save()
|
||||||
|
f.instance.save()
|
||||||
|
else:
|
||||||
|
f.instance = None
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
@ -93,8 +119,36 @@ class FoodUpdateView(ProtectQuerysetMixin, UpdateView):
|
|||||||
form_class = FoodForm
|
form_class = FoodForm
|
||||||
extra_context = {"title": _("Update food")}
|
extra_context = {"title": _("Update food")}
|
||||||
|
|
||||||
|
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 products
|
||||||
|
form_set = FoodOptionsFormSet(instance=form.instance)
|
||||||
|
context['formset'] = form_set
|
||||||
|
context['helper'] = FoodOptionFormSetHelper()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
# For each product, we save it
|
||||||
|
formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
|
||||||
|
if formset.is_valid():
|
||||||
|
for f in formset:
|
||||||
|
# We don't save the product if the designation is not entered, ie. if the line is empty
|
||||||
|
if f.is_valid() and f.instance.name:
|
||||||
|
f.save()
|
||||||
|
f.instance.save()
|
||||||
|
else:
|
||||||
|
f.instance = None
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse_lazy('sheets:sheet_detail', args=(self.kwargs['pk'],))
|
return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
|
||||||
|
|
||||||
|
|
||||||
class MealCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
class MealCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-08-18 14:24+0200\n"
|
"POT-Creation-Date: 2022-08-18 14:49+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
||||||
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
||||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||||
@ -337,7 +337,7 @@ msgstr "Entrée effectuée !"
|
|||||||
#: apps/member/templates/member/add_members.html:46
|
#: apps/member/templates/member/add_members.html:46
|
||||||
#: apps/member/templates/member/club_form.html:16
|
#: apps/member/templates/member/club_form.html:16
|
||||||
#: apps/note/templates/note/transactiontemplate_form.html:18
|
#: apps/note/templates/note/transactiontemplate_form.html:18
|
||||||
#: apps/sheets/templates/sheets/food_form.html:17
|
#: apps/sheets/templates/sheets/food_form.html:51
|
||||||
#: apps/sheets/templates/sheets/meal_form.html:17
|
#: apps/sheets/templates/sheets/meal_form.html:17
|
||||||
#: apps/sheets/templates/sheets/sheet_form.html:17 apps/treasury/forms.py:89
|
#: apps/sheets/templates/sheets/sheet_form.html:17 apps/treasury/forms.py:89
|
||||||
#: apps/treasury/forms.py:143
|
#: apps/treasury/forms.py:143
|
||||||
@ -2321,10 +2321,20 @@ msgstr "transaction de commande sur feuille de note"
|
|||||||
msgid "sheet order transactions"
|
msgid "sheet order transactions"
|
||||||
msgstr "transactions de commande sur feuille de note"
|
msgstr "transactions de commande sur feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/food_form.html:48
|
||||||
|
msgid "Add option"
|
||||||
|
msgstr "Ajouter une option"
|
||||||
|
|
||||||
#: apps/sheets/templates/sheets/sheet_detail.html:28
|
#: apps/sheets/templates/sheets/sheet_detail.html:28
|
||||||
msgid "menu"
|
msgid "menu"
|
||||||
msgstr "menu"
|
msgstr "menu"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:31
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:43
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:54
|
||||||
|
msgid "This product is unavailable."
|
||||||
|
msgstr "Ce produit est indisponible."
|
||||||
|
|
||||||
#: apps/sheets/templates/sheets/sheet_detail.html:63
|
#: apps/sheets/templates/sheets/sheet_detail.html:63
|
||||||
msgid "The menu is empty for now."
|
msgid "The menu is empty for now."
|
||||||
msgstr "Le menu est vide pour le moment."
|
msgstr "Le menu est vide pour le moment."
|
||||||
@ -2365,15 +2375,15 @@ msgstr "Modifier une feuille de note"
|
|||||||
msgid "Create new food"
|
msgid "Create new food"
|
||||||
msgstr "Créer un plat"
|
msgstr "Créer un plat"
|
||||||
|
|
||||||
#: apps/sheets/views.py:94
|
#: apps/sheets/views.py:120
|
||||||
msgid "Update food"
|
msgid "Update food"
|
||||||
msgstr "Modifier un plat"
|
msgstr "Modifier un plat"
|
||||||
|
|
||||||
#: apps/sheets/views.py:103
|
#: apps/sheets/views.py:157
|
||||||
msgid "Create new meal"
|
msgid "Create new meal"
|
||||||
msgstr "Créer un menu"
|
msgstr "Créer un menu"
|
||||||
|
|
||||||
#: apps/sheets/views.py:128
|
#: apps/sheets/views.py:182
|
||||||
msgid "Update meal"
|
msgid "Update meal"
|
||||||
msgstr "Modifier un menu"
|
msgstr "Modifier un menu"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user