1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-12-15 23:43:52 +01:00

Prepare for first irl test

This commit is contained in:
Ehouarn
2025-12-13 20:37:06 +01:00
parent bac9ed2353
commit a92afab956
15 changed files with 113 additions and 36 deletions

View File

@@ -65,11 +65,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if activity.open and activity.activity_type.manage_entries and ".change__open"|has_perm:activity %} {% if activity.open and activity.activity_type.manage_entries and ".change__open"|has_perm:activity %}
<a class="btn btn-warning btn-sm my-1" href="{% url 'activity:activity_entry' pk=activity.pk %}"> {% trans "Entry page" %}</a> <a class="btn btn-warning btn-sm my-1" href="{% url 'activity:activity_entry' pk=activity.pk %}"> {% trans "Entry page" %}</a>
{% endif %} {% endif %}
{% if false %}
{% if activity.activity_type.name == "Perm bouffe" %} {% if activity.activity_type.name == "Perm bouffe" %}
<a class="btn btn-warning btn-sm my-1" href="{% url 'food:dish_list' activity_pk=activity.pk %}"> {% trans "Dish page" %}</a> <a class="btn btn-warning btn-sm my-1" href="{% url 'food:dish_list' activity_pk=activity.pk %}"> {% trans "Dish page" %}</a>
{% endif %} {% endif %}
{% endif %}
{% if request.path_info == activity_detail_url %} {% if request.path_info == activity_detail_url %}
{% if activity.valid and ".change__open"|has_perm:activity %} {% if activity.valid and ".change__open"|has_perm:activity %}

View File

@@ -13,6 +13,7 @@ from member.models import Club
from note_kfet.inputs import Autocomplete, AmountInput from note_kfet.inputs import Autocomplete, AmountInput
from note_kfet.middlewares import get_current_request from note_kfet.middlewares import get_current_request
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from activity.models import Activity
from .models import Food, BasicFood, TransformedFood, QRCode, Dish, Supplement, Order, Recipe from .models import Food, BasicFood, TransformedFood, QRCode, Dish, Supplement, Order, Recipe
@@ -201,6 +202,17 @@ class DishForm(forms.ModelForm):
""" """
Form to create a dish Form to create a dish
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO find a better way to get pk (be not url scheme dependant)
pk = get_current_request().path.split('/')[3]
club = Activity.objects.get(pk=pk).organizer
qs = self.fields['main'].queryset.filter(
owner=club,
end_of_life=''
).filter(PermissionBackend.filter_queryset(get_current_request(), Food, "change"))
self.fields['main'].queryset = qs
class Meta: class Meta:
model = Dish model = Dish
fields = ('main', 'price', 'available') fields = ('main', 'price', 'available')
@@ -213,6 +225,17 @@ class SupplementForm(forms.ModelForm):
""" """
Form to create a dish Form to create a dish
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO find a better way to get pk (be not url scheme dependant)
pk = get_current_request().path.split('/')[3]
club = Activity.objects.get(pk=pk).organizer
qs = self.fields['food'].queryset.filter(
owner=club,
end_of_life=''
).filter(PermissionBackend.filter_queryset(get_current_request(), Food, "change"))
self.fields['food'].queryset = qs
class Meta: class Meta:
model = Supplement model = Supplement
fields = '__all__' fields = '__all__'
@@ -249,6 +272,20 @@ class OrderForm(forms.ModelForm):
""" """
Form to order food Form to order food
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO find a better way to get pk (be not url scheme dependant)
pk = get_current_request().path.split('/')[3]
qs = self.fields['supplements'].queryset.filter(
available=True
).filter(PermissionBackend.filter_queryset(get_current_request(), Supplement, "view"))
self.fields['supplements'].queryset = qs
qs = self.fields['dish'].queryset.filter(
activity__pk=pk,
available=True
).filter(PermissionBackend.filter_queryset(get_current_request(), Dish, "view"))
self.fields['dish'].queryset = qs
class Meta: class Meta:
model = Order model = Order
exclude = ("activity", "number", "ordered_at", "served", "served_at") exclude = ("activity", "number", "ordered_at", "served", "served_at")

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.9 on 2025-12-13 23:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('food', '0006_recipe'),
]
operations = [
migrations.AddField(
model_name='supplement',
name='available',
field=models.BooleanField(default=True, verbose_name='available'),
),
]

View File

@@ -388,6 +388,11 @@ class Supplement(models.Model):
verbose_name=_('price') verbose_name=_('price')
) )
available = models.BooleanField(
default=True,
verbose_name=_('available'),
)
class Meta: class Meta:
verbose_name = _('Supplement') verbose_name = _('Supplement')
verbose_name_plural = _('Supplements') verbose_name_plural = _('Supplements')
@@ -476,7 +481,7 @@ class Order(models.Model):
dish=str(self.dish), dish=str(self.dish),
user=str(self.user)) user=str(self.user))
def save(self, *args, **kwargs): def save(self, supplements=None, *args, **kwargs):
if self.activity != self.dish.activity: if self.activity != self.dish.activity:
raise ValidationError(_('Activities must be the same.')) raise ValidationError(_('Activities must be the same.'))
created = self.pk is None created = self.pk is None
@@ -487,12 +492,19 @@ class Order(models.Model):
else: else:
self.number = last_order.number + 1 self.number = last_order.number + 1
super().save(*args, **kwargs) super().save(*args, **kwargs)
amount = self.dish.price
if supplements:
self.supplements.set(supplements)
super().save(*args, **kwargs)
for supplement in supplements:
if supplement.dish != self.dish:
raise ValidationError(_('(You cannot select these supplements for this dish.'))
amount += supplement.price
transaction = FoodTransaction( transaction = FoodTransaction(
order=self, order=self,
source=self.user.note, source=self.user.note,
destination=self.activity.organizer.note, destination=self.activity.organizer.note,
amount=self.amount, amount=amount,
quantity=1, quantity=1,
reason=str(self.dish), reason=str(self.dish),
) )
@@ -504,6 +516,12 @@ class Order(models.Model):
self.transaction.save() self.transaction.save()
super().save(*args, **kwargs) super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
if self.transaction.valid:
raise ValidationError(_('You cannot delete this order because the transaction is valid'))
else:
super().delete(*args, **kwargs)
class FoodTransaction(Transaction): class FoodTransaction(Transaction):
""" """
@@ -511,7 +529,7 @@ class FoodTransaction(Transaction):
""" """
order = models.OneToOneField( order = models.OneToOneField(
Order, Order,
on_delete=models.PROTECT, on_delete=models.CASCADE,
related_name='transaction', related_name='transaction',
verbose_name=_('order') verbose_name=_('order')
) )

View File

@@ -32,10 +32,13 @@ function serve_button(button_id, table_id, current_state) {
}) })
}) })
.done(function () { .done(function () {
console.log('done');
if (current_state) { if (current_state) {
console.log('true');
$('table').load(location.pathname + ' table') $('table').load(location.pathname + ' table')
} }
else { else {
console.log('false');
$('#' + table_id).load(location.pathname + ' #' + table_id + ' > *'); $('#' + table_id).load(location.pathname + ' #' + table_id + ' > *');
} }
}) })

View File

@@ -47,7 +47,7 @@ class DishTable(tables.Table):
supplements = tables.Column(empty_values=(), verbose_name=_('Available supplements'), orderable=False) supplements = tables.Column(empty_values=(), verbose_name=_('Available supplements'), orderable=False)
def render_supplements(self, record): def render_supplements(self, record):
return ", ".join(str(q.food) for q in record.supplements.all()) return ", ".join(str(q.food) for q in record.supplements.filter(available=True))
def render_price(self, value): def render_price(self, value):
return pretty_money(value) return pretty_money(value)

View File

@@ -22,7 +22,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<li> {% trans "Available" %} : {{ dish.available|yesno }}</li> <li> {% trans "Available" %} : {{ dish.available|yesno }}</li>
<li> {% trans "Possible supplements" %} : <li> {% trans "Possible supplements" %} :
{% for supp in supplements %} {% for supp in supplements %}
<a href="{% url "food:food_view" pk=supp.food.pk %}">{{ supp.food.name }} ({{ supp.price|pretty_money }})</a>{% if not forloop.last %},{% endif %} <a href="{% url "food:food_view" pk=supp.food.pk %}">{% if not supp.food.available %}<s>{% endif %}{{ supp.food.name }} ({{ supp.price|pretty_money }}) {% if not supp.food.available %}</s>{% endif %}</a>{% if not forloop.last %},{% endif %}
{% endfor %} {% endfor %}
</li> </li>
</ul> </ul>

View File

@@ -26,6 +26,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<tr> <tr>
<th>{{ form.food.label }}<span class="asteriskField">*</span></th> <th>{{ form.food.label }}<span class="asteriskField">*</span></th>
<th>{{ form.price.label }}<span class="asteriskField">*</span></th> <th>{{ form.price.label }}<span class="asteriskField">*</span></th>
<th>{{ form.available.label }}<span class="asteriskField">*</span></th>
</tr> </tr>
</thead> </thead>
<tbody id="form_body"> <tbody id="form_body">
@@ -33,6 +34,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<tr class="row-formset"> <tr class="row-formset">
<td>{{ form.food }}</td> <td>{{ form.food }}</td>
<td>{{ form.price }}</td> <td>{{ form.price }}</td>
<td>{{ form.available }}</td>
{# These fields are hidden but handled by the formset to link the id and the invoice id #} {# These fields are hidden but handled by the formset to link the id and the invoice id #}
{{ form.dish }} {{ form.dish }}
{{ form.id }} {{ form.id }}

View File

@@ -17,6 +17,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="btn btn-sm btn-success" href="{% url 'food:dish_create' activity_pk=activity.pk %}">{% trans "New dish" %}</a> <a class="btn btn-sm btn-success" href="{% url 'food:dish_create' activity_pk=activity.pk %}">{% trans "New dish" %}</a>
{% endif %} {% endif %}
<a class="btn btn-sm btn-secondary" href="{% url 'activity:activity_detail' pk=activity.pk %}">{% trans "Activity page" %}</a> <a class="btn btn-sm btn-secondary" href="{% url 'activity:activity_detail' pk=activity.pk %}">{% trans "Activity page" %}</a>
<a class="btn btn-sm btn-success" href="{% url 'food:order_create' activity_pk=activity.pk %}">{% trans "Order food" %}</a>
<a class="btn btn-sm btn-secondary" href="{% url 'food:order_list' activity_pk=activity.pk %}">{% trans "Order list" %}</a>
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}"> <a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
{% trans "Return to the food list" %} {% trans "Return to the food list" %}
</a> </a>

View File

@@ -47,12 +47,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="btn btn-sm btn-secondary" href="{% url "food:manage_ingredients" pk=food.pk %}"> <a class="btn btn-sm btn-secondary" href="{% url "food:manage_ingredients" pk=food.pk %}">
{% trans "Manage ingredients" %} {% trans "Manage ingredients" %}
</a> </a>
{% if false %}
<a class="btn btn-sm btn-secondary" href="{% url "food:recipe_use" pk=food.pk %}"> <a class="btn btn-sm btn-secondary" href="{% url "food:recipe_use" pk=food.pk %}">
{% trans "Use a recipe" %} {% trans "Use a recipe" %}
</a> </a>
{% endif %} {% endif %}
{% endif %}
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}"> <a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
{% trans "Return to the food list" %} {% trans "Return to the food list" %}
</a> </a>

View File

@@ -70,7 +70,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans "New meal" %} {% trans "New meal" %}
</a> </a>
{% endif %} {% endif %}
{% if false %}
{% if can_view_recipes %} {% if can_view_recipes %}
<a class="btn btn-sm btn-secondary" href="{% url 'food:recipe_list' %}"> <a class="btn btn-sm btn-secondary" href="{% url 'food:recipe_list' %}">
{% trans "View recipes" %} {% trans "View recipes" %}
@@ -86,7 +85,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans "View" %} {{ activity.name }} {% trans "View" %} {{ activity.name }}
</a> </a>
{% endfor %} {% endfor %}
{% endif %}
</div> </div>
{% if served.data %} {% if served.data %}

View File

@@ -21,6 +21,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% render_table table %} {% render_table table %}
{% endif %} {% endif %}
</div> </div>
{% if forloop.last %}<a class="btn btn-primary" href="{% url 'food:dish_list' activity_pk=activity.pk %}">{% trans "Return to dish list" %}</a> {% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -11,7 +11,7 @@ from member.models import Club
from ..api.views import AllergenViewSet, BasicFoodViewSet, TransformedFoodViewSet, QRCodeViewSet, \ from ..api.views import AllergenViewSet, BasicFoodViewSet, TransformedFoodViewSet, QRCodeViewSet, \
DishViewSet, SupplementViewSet, OrderViewSet, FoodTransactionViewSet DishViewSet, SupplementViewSet, OrderViewSet, FoodTransactionViewSet
from ..models import Allergen, BasicFood, TransformedFood, QRCode, Dish, Supplement, Order # TODO FoodTransaction from ..models import Allergen, BasicFood, TransformedFood, QRCode, Dish, Supplement, Order, FoodTransaction
class TestFood(TestCase): class TestFood(TestCase):
@@ -120,7 +120,7 @@ class TestFood(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
'''class TestFoodOrder(TestCase): class TestFoodOrder(TestCase):
""" """
Test Food Order Test Food Order
""" """
@@ -198,7 +198,7 @@ class TestFood(TestCase):
) )
self.supplement = Supplement.objects.create( self.supplement = Supplement.objects.create(
dish=self.dish, dish=self.second_dish,
food=self.basicfood, food=self.basicfood,
price=100, price=100,
) )
@@ -208,8 +208,6 @@ class TestFood(TestCase):
activity=self.activity, activity=self.activity,
dish=self.dish, dish=self.dish,
) )
self.order.supplements.add(self.supplement)
self.order.save()
def test_dish_list(self): def test_dish_list(self):
""" """
@@ -300,8 +298,10 @@ class TestFood(TestCase):
dish=self.second_dish.pk, dish=self.second_dish.pk,
supplements=self.supplement.pk supplements=self.supplement.pk
)) ))
self.assertRedirects(response, reverse("food:food_list")) self.assertRedirects(response, reverse("food:dish_list", kwargs={"activity_pk": self.activity.pk}))
self.assertTrue(Order.objects.filter(user=self.user, dish=self.second_dish, activity=self.activity).exists()) self.assertTrue(Order.objects.filter(user=self.user, dish=self.second_dish, activity=self.activity).exists())
order = Order.objects.get(user=self.user, dish=self.second_dish, activity=self.activity, supplements=self.supplement)
self.assertTrue(FoodTransaction.objects.filter(order=order, amount=order.amount, valid=False, source=order.user.note).exists())
def test_order_list(self): def test_order_list(self):
""" """
@@ -337,7 +337,7 @@ class TestFood(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertTrue(Order.objects.filter(dish=self.dish, user=self.user, served=False).exists()) self.assertTrue(Order.objects.filter(dish=self.dish, user=self.user, served=False).exists())
self.assertTrue(FoodTransaction.objects.filter(order=self.order, valid=False).exists())''' self.assertTrue(FoodTransaction.objects.filter(order=self.order, valid=False).exists())
class TestFoodAPI(TestAPI): class TestFoodAPI(TestAPI):

View File

@@ -20,19 +20,19 @@ urlpatterns = [
path('add/ingredient/<int:pk>/', views.AddIngredientView.as_view(), name='add_ingredient'), path('add/ingredient/<int:pk>/', views.AddIngredientView.as_view(), name='add_ingredient'),
path('redirect/', views.QRCodeRedirectView.as_view(), name='redirect_view'), path('redirect/', views.QRCodeRedirectView.as_view(), name='redirect_view'),
# TODO not always store activity_pk in url # TODO not always store activity_pk in url
# path('activity/<int:activity_pk>/dishes/add/', views.DishCreateView.as_view(), name='dish_create'), path('activity/<int:activity_pk>/dishes/add/', views.DishCreateView.as_view(), name='dish_create'),
# path('activity/<int:activity_pk>/dishes/', views.DishListView.as_view(), name='dish_list'), path('activity/<int:activity_pk>/dishes/', views.DishListView.as_view(), name='dish_list'),
# path('activity/<int:activity_pk>/dishes/<int:pk>/', views.DishDetailView.as_view(), name='dish_detail'), path('activity/<int:activity_pk>/dishes/<int:pk>/', views.DishDetailView.as_view(), name='dish_detail'),
# path('activity/<int:activity_pk>/dishes/<int:pk>/update/', views.DishUpdateView.as_view(), name='dish_update'), path('activity/<int:activity_pk>/dishes/<int:pk>/update/', views.DishUpdateView.as_view(), name='dish_update'),
# path('activity/<int:activity_pk>/dishes/<int:pk>/delete/', views.DishDeleteView.as_view(), name='dish_delete'), path('activity/<int:activity_pk>/dishes/<int:pk>/delete/', views.DishDeleteView.as_view(), name='dish_delete'),
# path('activity/<int:activity_pk>/order/', views.OrderCreateView.as_view(), name='order_create'), 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/', views.OrderListView.as_view(), name='order_list'),
# path('activity/<int:activity_pk>/orders/served', views.ServedOrderListView.as_view(), name='served_order_list'), path('activity/<int:activity_pk>/orders/served', views.ServedOrderListView.as_view(), name='served_order_list'),
# path('activity/<int:activity_pk>/kitchen/', views.KitchenView.as_view(), name='kitchen'), path('activity/<int:activity_pk>/kitchen/', views.KitchenView.as_view(), name='kitchen'),
# path('recipe/add/', views.RecipeCreateView.as_view(), name='recipe_create'), path('recipe/add/', views.RecipeCreateView.as_view(), name='recipe_create'),
# path('recipe/', views.RecipeListView.as_view(), name='recipe_list'), path('recipe/', views.RecipeListView.as_view(), name='recipe_list'),
# path('recipe/<int:pk>/', views.RecipeDetailView.as_view(), name='recipe_detail'), path('recipe/<int:pk>/', views.RecipeDetailView.as_view(), name='recipe_detail'),
# path('recipe/<int:pk>/update/', views.RecipeUpdateView.as_view(), name='recipe_update'), path('recipe/<int:pk>/update/', views.RecipeUpdateView.as_view(), name='recipe_update'),
# path('update/ingredients/<int:pk>/recipe/', views.UseRecipeView.as_view(), name='recipe_use'), path('update/ingredients/<int:pk>/recipe/', views.UseRecipeView.as_view(), name='recipe_use'),
# path('ajax/get_ingredients/', views.get_ingredients_for_recipe, name='get_ingredients'), path('ajax/get_ingredients/', views.get_ingredients_for_recipe, name='get_ingredients'),
] ]

View File

@@ -777,11 +777,13 @@ class OrderCreateView(ProtectQuerysetMixin, ProtectedCreateView):
activity = Activity.objects.get(pk=self.kwargs["activity_pk"]) activity = Activity.objects.get(pk=self.kwargs["activity_pk"])
form.instance.activity = activity form.instance.activity = activity
supplements = Supplement.objects.filter(pk__in=form.data.getlist('supplements')).all()
return super().form_valid(form) form.instance.save(supplements=supplements)
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
return reverse_lazy('food:food_list') return reverse_lazy('food:dish_list', kwargs={"activity_pk": self.kwargs["activity_pk"]})
class OrderListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): class OrderListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):