diff --git a/apps/api/urls.py b/apps/api/urls.py
index 0659427f..ef631004 100644
--- a/apps/api/urls.py
+++ b/apps/api/urls.py
@@ -15,29 +15,33 @@ router = routers.DefaultRouter()
router.register('models', ContentTypeViewSet)
router.register('user', UserViewSet)
+if "activity" in settings.INSTALLED_APPS:
+ from activity.api.urls import register_activity_urls
+ register_activity_urls(router, 'activity')
+
+if "food" in settings.INSTALLED_APPS:
+ from food.api.urls import register_food_urls
+ register_food_urls(router, 'food')
+
+if "logs" in settings.INSTALLED_APPS:
+ from logs.api.urls import register_logs_urls
+ register_logs_urls(router, 'logs')
+
if "member" in settings.INSTALLED_APPS:
from member.api.urls import register_members_urls
register_members_urls(router, 'members')
-if "member" in settings.INSTALLED_APPS:
- from activity.api.urls import register_activity_urls
- register_activity_urls(router, 'activity')
-
if "note" in settings.INSTALLED_APPS:
from note.api.urls import register_note_urls
register_note_urls(router, 'note')
-if "treasury" in settings.INSTALLED_APPS:
- from treasury.api.urls import register_treasury_urls
- register_treasury_urls(router, 'treasury')
-
if "permission" in settings.INSTALLED_APPS:
from permission.api.urls import register_permission_urls
register_permission_urls(router, 'permission')
-if "logs" in settings.INSTALLED_APPS:
- from logs.api.urls import register_logs_urls
- register_logs_urls(router, 'logs')
+if "treasury" in settings.INSTALLED_APPS:
+ from treasury.api.urls import register_treasury_urls
+ register_treasury_urls(router, 'treasury')
if "wei" in settings.INSTALLED_APPS:
from wei.api.urls import register_wei_urls
diff --git a/apps/food/__init__.py b/apps/food/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/food/admin.py b/apps/food/admin.py
new file mode 100644
index 00000000..fa32755a
--- /dev/null
+++ b/apps/food/admin.py
@@ -0,0 +1,37 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.contrib import admin
+from django.db import transaction
+from note_kfet.admin import admin_site
+
+from .models import Allergen, BasicFood, QRCode, TransformedFood
+
+
+@admin.register(QRCode, site=admin_site)
+class QRCodeAdmin(admin.ModelAdmin):
+ pass
+
+
+@admin.register(BasicFood, site=admin_site)
+class BasicFoodAdmin(admin.ModelAdmin):
+ @transaction.atomic
+ def save_related(self, *args, **kwargs):
+ ans = super().save_related(*args, **kwargs)
+ args[1].instance.update()
+ return ans
+
+
+@admin.register(TransformedFood, site=admin_site)
+class TransformedFoodAdmin(admin.ModelAdmin):
+ exclude = ["allergens", "expiry_date"]
+
+ @transaction.atomic
+ def save_related(self, request, form, *args, **kwargs):
+ super().save_related(request, form, *args, **kwargs)
+ form.instance.update()
+
+
+@admin.register(Allergen, site=admin_site)
+class AllergenAdmin(admin.ModelAdmin):
+ pass
diff --git a/apps/food/api/__init__.py b/apps/food/api/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/food/api/serializers.py b/apps/food/api/serializers.py
new file mode 100644
index 00000000..9c42013f
--- /dev/null
+++ b/apps/food/api/serializers.py
@@ -0,0 +1,50 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from rest_framework import serializers
+
+from ..models import Allergen, BasicFood, QRCode, TransformedFood
+
+
+class AllergenSerializer(serializers.ModelSerializer):
+ """
+ REST API Serializer for Allergen.
+ The djangorestframework plugin will analyse the model `Allergen` and parse all fields in the API.
+ """
+
+ class Meta:
+ model = Allergen
+ fields = '__all__'
+
+
+class BasicFoodSerializer(serializers.ModelSerializer):
+ """
+ REST API Serializer for BasicFood.
+ The djangorestframework plugin will analyse the model `BasicFood` and parse all fields in the API.
+ """
+
+ class Meta:
+ model = BasicFood
+ fields = '__all__'
+
+
+class QRCodeSerializer(serializers.ModelSerializer):
+ """
+ REST API Serializer for QRCode.
+ The djangorestframework plugin will analyse the model `QRCode` and parse all fields in the API.
+ """
+
+ class Meta:
+ model = QRCode
+ fields = '__all__'
+
+
+class TransformedFoodSerializer(serializers.ModelSerializer):
+ """
+ REST API Serializer for TransformedFood.
+ The djangorestframework plugin will analyse the model `TransformedFood` and parse all fields in the API.
+ """
+
+ class Meta:
+ model = TransformedFood
+ fields = '__all__'
diff --git a/apps/food/api/urls.py b/apps/food/api/urls.py
new file mode 100644
index 00000000..acfb635d
--- /dev/null
+++ b/apps/food/api/urls.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from .views import AllergenViewSet, BasicFoodViewSet, QRCodeViewSet, TransformedFoodViewSet
+
+
+def register_food_urls(router, path):
+ """
+ Configure router for Food REST API.
+ """
+ router.register(path + '/allergen', AllergenViewSet)
+ router.register(path + '/basic_food', BasicFoodViewSet)
+ router.register(path + '/qrcode', QRCodeViewSet)
+ router.register(path + '/transformed_food', TransformedFoodViewSet)
diff --git a/apps/food/api/views.py b/apps/food/api/views.py
new file mode 100644
index 00000000..824ff809
--- /dev/null
+++ b/apps/food/api/views.py
@@ -0,0 +1,61 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from api.viewsets import ReadProtectedModelViewSet
+from django_filters.rest_framework import DjangoFilterBackend
+from rest_framework.filters import SearchFilter
+
+from .serializers import AllergenSerializer, BasicFoodSerializer, QRCodeSerializer, TransformedFoodSerializer
+from ..models import Allergen, BasicFood, QRCode, TransformedFood
+
+
+class AllergenViewSet(ReadProtectedModelViewSet):
+ """
+ REST API View set.
+ The djangorestframework plugin will get all `Allergen` objects, serialize it to JSON with the given serializer,
+ then render it on /api/food/allergen/
+ """
+ queryset = Allergen.objects.order_by('id')
+ serializer_class = AllergenSerializer
+ filter_backends = [DjangoFilterBackend, SearchFilter]
+ filterset_fields = ['name', ]
+ search_fields = ['$name', ]
+
+
+class BasicFoodViewSet(ReadProtectedModelViewSet):
+ """
+ REST API View set.
+ The djangorestframework plugin will get all `BasicFood` objects, serialize it to JSON with the given serializer,
+ then render it on /api/food/basic_food/
+ """
+ queryset = BasicFood.objects.order_by('id')
+ serializer_class = BasicFoodSerializer
+ filter_backends = [DjangoFilterBackend, SearchFilter]
+ filterset_fields = ['name', ]
+ search_fields = ['$name', ]
+
+
+class QRCodeViewSet(ReadProtectedModelViewSet):
+ """
+ REST API View set.
+ The djangorestframework plugin will get all `QRCode` objects, serialize it to JSON with the given serializer,
+ then render it on /api/food/qrcode/
+ """
+ queryset = QRCode.objects.order_by('id')
+ serializer_class = QRCodeSerializer
+ filter_backends = [DjangoFilterBackend, SearchFilter]
+ filterset_fields = ['qr_code_number', ]
+ search_fields = ['$qr_code_number', ]
+
+
+class TransformedFoodViewSet(ReadProtectedModelViewSet):
+ """
+ REST API View set.
+ The djangorestframework plugin will get all `TransformedFood` objects, serialize it to JSON with the given serializer,
+ then render it on /api/food/transformed_food/
+ """
+ queryset = TransformedFood.objects.order_by('id')
+ serializer_class = TransformedFoodSerializer
+ filter_backends = [DjangoFilterBackend, SearchFilter]
+ filterset_fields = ['name', ]
+ search_fields = ['$name', ]
diff --git a/apps/food/apps.py b/apps/food/apps.py
new file mode 100644
index 00000000..62ede85f
--- /dev/null
+++ b/apps/food/apps.py
@@ -0,0 +1,11 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+from django.utils.translation import gettext_lazy as _
+from django.apps import AppConfig
+
+
+class FoodkfetConfig(AppConfig):
+ name = 'food'
+ verbose_name = _('food')
diff --git a/apps/food/forms.py b/apps/food/forms.py
new file mode 100644
index 00000000..4f567e59
--- /dev/null
+++ b/apps/food/forms.py
@@ -0,0 +1,114 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from random import shuffle
+
+from django import forms
+from django.utils.translation import gettext_lazy as _
+from django.utils import timezone
+from member.models import Club
+from bootstrap_datepicker_plus.widgets import DateTimePickerInput
+from note_kfet.inputs import Autocomplete
+from note_kfet.middlewares import get_current_request
+from permission.backends import PermissionBackend
+
+from .models import BasicFood, QRCode, TransformedFood
+
+
+class AddIngredientForms(forms.ModelForm):
+ """
+ Form for add an ingredient
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter(
+ polymorphic_ctype__model='transformedfood',
+ is_ready=False,
+ is_active=True,
+ was_eaten=False,
+ )
+ # Caution, the logic is inverted here, we flip the logic on saving in AddIngredientView
+ self.fields['is_active'].initial = True
+ self.fields['is_active'].label = _("Fully used")
+
+ class Meta:
+ model = TransformedFood
+ fields = ('ingredient', 'is_active')
+
+
+class BasicFoodForms(forms.ModelForm):
+ """
+ Form for add non-transformed food
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields['name'].widget.attrs.update({"autofocus": "autofocus"})
+ self.fields['name'].required = True
+ self.fields['owner'].required = True
+
+ # Some example
+ self.fields['name'].widget.attrs.update({"placeholder": _("Pasta METRO 5kg")})
+ clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all())
+ shuffle(clubs)
+ self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..."
+
+ class Meta:
+ model = BasicFood
+ fields = ('name', 'owner', 'date_type', 'expiry_date', 'is_active', 'was_eaten', 'allergens',)
+ widgets = {
+ "owner": Autocomplete(
+ model=Club,
+ attrs={"api_url": "/api/members/club/"},
+ ),
+ 'expiry_date': DateTimePickerInput(),
+ }
+
+
+class QRCodeForms(forms.ModelForm):
+ """
+ Form for create QRCode
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields['food_container'].queryset = self.fields['food_container'].queryset.filter(
+ is_active=True,
+ was_eaten=False,
+ polymorphic_ctype__model='transformedfood',
+ )
+
+ class Meta:
+ model = QRCode
+ fields = ('food_container',)
+
+
+class TransformedFoodForms(forms.ModelForm):
+ """
+ Form for add transformed food
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields['name'].widget.attrs.update({"autofocus": "autofocus"})
+ self.fields['name'].required = True
+ self.fields['owner'].required = True
+ self.fields['creation_date'].required = True
+ self.fields['creation_date'].initial = timezone.now
+ self.fields['is_active'].initial = True
+ self.fields['is_ready'].initial = False
+ self.fields['was_eaten'].initial = False
+
+ # Some example
+ self.fields['name'].widget.attrs.update({"placeholder": _("Lasagna")})
+ clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all())
+ shuffle(clubs)
+ self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..."
+
+ class Meta:
+ model = TransformedFood
+ fields = ('name', 'creation_date', 'owner', 'is_active', 'is_ready', 'was_eaten', 'shelf_life')
+ widgets = {
+ "owner": Autocomplete(
+ model=Club,
+ attrs={"api_url": "/api/members/club/"},
+ ),
+ 'creation_date': DateTimePickerInput(),
+ }
diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py
new file mode 100644
index 00000000..011d0f3f
--- /dev/null
+++ b/apps/food/migrations/0001_initial.py
@@ -0,0 +1,84 @@
+# Generated by Django 2.2.28 on 2024-07-05 08:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('contenttypes', '0002_remove_content_type_name'),
+ ('member', '0011_profile_vss_charter_read'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Allergen',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255, verbose_name='name')),
+ ],
+ options={
+ 'verbose_name': 'Allergen',
+ 'verbose_name_plural': 'Allergens',
+ },
+ ),
+ migrations.CreateModel(
+ name='Food',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=255, verbose_name='name')),
+ ('expiry_date', models.DateTimeField(verbose_name='expiry date')),
+ ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')),
+ ('is_ready', models.BooleanField(default=False, verbose_name='is ready')),
+ ('allergens', models.ManyToManyField(blank=True, to='food.Allergen', verbose_name='allergen')),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')),
+ ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_food.food_set+', to='contenttypes.ContentType')),
+ ],
+ options={
+ 'verbose_name': 'foods',
+ },
+ ),
+ migrations.CreateModel(
+ name='BasicFood',
+ fields=[
+ ('food_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='food.Food')),
+ ('date_type', models.CharField(choices=[('DLC', 'DLC'), ('DDM', 'DDM')], max_length=255)),
+ ('arrival_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='arrival date')),
+ ],
+ options={
+ 'verbose_name': 'Basic food',
+ 'verbose_name_plural': 'Basic foods',
+ },
+ bases=('food.food',),
+ ),
+ migrations.CreateModel(
+ name='QRCode',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('qr_code_number', models.PositiveIntegerField(unique=True, verbose_name='QR-code number')),
+ ('food_container', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Food', verbose_name='food container')),
+ ],
+ options={
+ 'verbose_name': 'QR-code',
+ 'verbose_name_plural': 'QR-codes',
+ },
+ ),
+ migrations.CreateModel(
+ name='TransformedFood',
+ fields=[
+ ('food_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='food.Food')),
+ ('creation_date', models.DateTimeField(verbose_name='creation date')),
+ ('is_active', models.BooleanField(default=True, verbose_name='is active')),
+ ('ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.Food', verbose_name='transformed ingredient')),
+ ],
+ options={
+ 'verbose_name': 'Transformed food',
+ 'verbose_name_plural': 'Transformed foods',
+ },
+ bases=('food.food',),
+ ),
+ ]
diff --git a/apps/food/migrations/0002_transformedfood_shelf_life.py b/apps/food/migrations/0002_transformedfood_shelf_life.py
new file mode 100644
index 00000000..46673643
--- /dev/null
+++ b/apps/food/migrations/0002_transformedfood_shelf_life.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.2.28 on 2024-07-06 20:37
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('food', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transformedfood',
+ name='shelf_life',
+ field=models.DurationField(default=datetime.timedelta(days=3), verbose_name='shelf life'),
+ ),
+ ]
diff --git a/apps/food/migrations/0003_create_14_allergens_mandatory.py b/apps/food/migrations/0003_create_14_allergens_mandatory.py
new file mode 100644
index 00000000..236eaea4
--- /dev/null
+++ b/apps/food/migrations/0003_create_14_allergens_mandatory.py
@@ -0,0 +1,62 @@
+from django.db import migrations
+
+def create_14_mandatory_allergens(apps, schema_editor):
+ """
+ There are 14 mandatory allergens, they are pre-injected
+ """
+
+ Allergen = apps.get_model("food", "allergen")
+
+ Allergen.objects.get_or_create(
+ name="Gluten",
+ )
+ Allergen.objects.get_or_create(
+ name="Fruits à coques",
+ )
+ Allergen.objects.get_or_create(
+ name="Crustacés",
+ )
+ Allergen.objects.get_or_create(
+ name="Céléri",
+ )
+ Allergen.objects.get_or_create(
+ name="Oeufs",
+ )
+ Allergen.objects.get_or_create(
+ name="Moutarde",
+ )
+ Allergen.objects.get_or_create(
+ name="Poissons",
+ )
+ Allergen.objects.get_or_create(
+ name="Soja",
+ )
+ Allergen.objects.get_or_create(
+ name="Lait",
+ )
+ Allergen.objects.get_or_create(
+ name="Sulfites",
+ )
+ Allergen.objects.get_or_create(
+ name="Sésame",
+ )
+ Allergen.objects.get_or_create(
+ name="Lupin",
+ )
+ Allergen.objects.get_or_create(
+ name="Arachides",
+ )
+ Allergen.objects.get_or_create(
+ name="Mollusques",
+ )
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('food', '0002_transformedfood_shelf_life'),
+ ]
+
+ operations = [
+ migrations.RunPython(create_14_mandatory_allergens),
+ ]
+
+
diff --git a/apps/food/migrations/0004_auto_20240813_2358.py b/apps/food/migrations/0004_auto_20240813_2358.py
new file mode 100644
index 00000000..d7fdf200
--- /dev/null
+++ b/apps/food/migrations/0004_auto_20240813_2358.py
@@ -0,0 +1,28 @@
+# Generated by Django 2.2.28 on 2024-08-13 21:58
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('food', '0003_create_14_allergens_mandatory'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='transformedfood',
+ name='is_active',
+ ),
+ migrations.AddField(
+ model_name='food',
+ name='is_active',
+ field=models.BooleanField(default=True, verbose_name='is active'),
+ ),
+ migrations.AlterField(
+ model_name='qrcode',
+ name='food_container',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='QR_code', to='food.Food', verbose_name='food container'),
+ ),
+ ]
diff --git a/apps/food/migrations/__init__.py b/apps/food/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/food/models.py b/apps/food/models.py
new file mode 100644
index 00000000..97e00ff9
--- /dev/null
+++ b/apps/food/models.py
@@ -0,0 +1,226 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from datetime import timedelta
+
+from django.db import models, transaction
+from django.utils import timezone
+from django.utils.translation import gettext_lazy as _
+from member.models import Club
+from polymorphic.models import PolymorphicModel
+
+
+class QRCode(models.Model):
+ """
+ An QRCode model
+ """
+ qr_code_number = models.PositiveIntegerField(
+ verbose_name=_("QR-code number"),
+ unique=True,
+ )
+
+ food_container = models.ForeignKey(
+ 'Food',
+ on_delete=models.CASCADE,
+ related_name='QR_code',
+ verbose_name=_('food container'),
+ )
+
+ class Meta:
+ verbose_name = _("QR-code")
+ verbose_name_plural = _("QR-codes")
+
+ def __str__(self):
+ return _("QR-code number {qr_code_number}").format(qr_code_number=self.qr_code_number)
+
+
+class Allergen(models.Model):
+ """
+ A list of allergen and alimentary restrictions
+ """
+ name = models.CharField(
+ verbose_name=_('name'),
+ max_length=255,
+ )
+
+ class Meta:
+ verbose_name = _('Allergen')
+ verbose_name_plural = _('Allergens')
+
+ def __str__(self):
+ return self.name
+
+
+class Food(PolymorphicModel):
+ name = models.CharField(
+ verbose_name=_('name'),
+ max_length=255,
+ )
+
+ owner = models.ForeignKey(
+ Club,
+ on_delete=models.PROTECT,
+ related_name='+',
+ verbose_name=_('owner'),
+ )
+
+ allergens = models.ManyToManyField(
+ Allergen,
+ blank=True,
+ verbose_name=_('allergen'),
+ )
+
+ expiry_date = models.DateTimeField(
+ verbose_name=_('expiry date'),
+ null=False,
+ )
+
+ was_eaten = models.BooleanField(
+ default=False,
+ verbose_name=_('was eaten'),
+ )
+
+ # is_ready != is_active : is_ready signifie que la nourriture est prête à être manger,
+ # is_active signifie que la nourriture n'est pas encore archivé
+ # il sert dans les cas où il est plus intéressant que de l'open soit conservé (confiture par ex)
+
+ is_ready = models.BooleanField(
+ default=False,
+ verbose_name=_('is ready'),
+ )
+
+ is_active = models.BooleanField(
+ default=True,
+ verbose_name=_('is active'),
+ )
+
+ def __str__(self):
+ return self.name
+
+ @transaction.atomic
+ def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
+ return super().save(force_insert, force_update, using, update_fields)
+
+ class Meta:
+ verbose_name = _('food')
+ verbose_name = _('foods')
+
+
+class BasicFood(Food):
+ """
+ Food which has been directly buy on supermarket
+ """
+ date_type = models.CharField(
+ max_length=255,
+ choices=(
+ ("DLC", "DLC"),
+ ("DDM", "DDM"),
+ )
+ )
+
+ arrival_date = models.DateTimeField(
+ verbose_name=_('arrival date'),
+ default=timezone.now,
+ )
+
+ # label = models.ImageField(
+ # verbose_name=_('food label'),
+ # max_length=255,
+ # blank=False,
+ # null=False,
+ # upload_to='label/',
+ # )
+
+ @transaction.atomic
+ def update_allergens(self):
+ # update parents
+ for parent in self.transformed_ingredient_inv.iterator():
+ parent.update_allergens()
+
+ @transaction.atomic
+ def update_expiry_date(self):
+ # update parents
+ for parent in self.transformed_ingredient_inv.iterator():
+ parent.update_expiry_date()
+
+ @transaction.atomic
+ def update(self):
+ self.update_allergens()
+ self.update_expiry_date()
+
+ class Meta:
+ verbose_name = _('Basic food')
+ verbose_name_plural = _('Basic foods')
+
+
+class TransformedFood(Food):
+ """
+ Transformed food are a mix between basic food and meal
+ """
+ creation_date = models.DateTimeField(
+ verbose_name=_('creation date'),
+ )
+
+ ingredient = models.ManyToManyField(
+ Food,
+ blank=True,
+ symmetrical=False,
+ related_name='transformed_ingredient_inv',
+ verbose_name=_('transformed ingredient'),
+ )
+
+ # Without microbiological analyzes, the storage time is 3 days
+ shelf_life = models.DurationField(
+ verbose_name=_("shelf life"),
+ default=timedelta(days=3),
+ )
+
+ @transaction.atomic
+ def archive(self):
+ # When a meal are archived, if it was eaten, update ingredient fully used for this meal
+ raise NotImplementedError
+
+ @transaction.atomic
+ def update_allergens(self):
+ # When allergens are changed, simply update the parents' allergens
+ old_allergens = list(self.allergens.all())
+ self.allergens.clear()
+ for ingredient in self.ingredient.iterator():
+ self.allergens.set(self.allergens.union(ingredient.allergens.all()))
+
+ if old_allergens == list(self.allergens.all()):
+ return
+ super().save()
+
+ # update parents
+ for parent in self.transformed_ingredient_inv.iterator():
+ parent.update_allergens()
+
+ @transaction.atomic
+ def update_expiry_date(self):
+ # When expiry_date is changed, simply update the parents' expiry_date
+ old_expiry_date = self.expiry_date
+ self.expiry_date = self.creation_date + self.shelf_life
+ for ingredient in self.ingredient.iterator():
+ self.expiry_date = min(self.expiry_date, ingredient.expiry_date)
+
+ if old_expiry_date == self.expiry_date:
+ return
+ super().save()
+
+ # update parents
+ for parent in self.transformed_ingredient_inv.iterator():
+ parent.update_expiry_date()
+
+ @transaction.atomic
+ def update(self):
+ self.update_allergens()
+ self.update_expiry_date()
+
+ @transaction.atomic
+ def save(self, *args, **kwargs):
+ super().save(*args, **kwargs)
+
+ class Meta:
+ verbose_name = _('Transformed food')
+ verbose_name_plural = _('Transformed foods')
diff --git a/apps/food/tables.py b/apps/food/tables.py
new file mode 100644
index 00000000..4a180c76
--- /dev/null
+++ b/apps/food/tables.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import django_tables2 as tables
+from django_tables2 import A
+
+from .models import TransformedFood
+
+
+class TransformedFoodTable(tables.Table):
+ name = tables.LinkColumn(
+ 'food:food_view',
+ args=[A('pk'), ],
+ )
+
+ class Meta:
+ model = TransformedFood
+ template_name = 'django_tables2/bootstrap4.html'
+ fields = ('name', "owner", "allergens", "expiry_date")
diff --git a/apps/food/templates/food/add_ingredient_form.html b/apps/food/templates/food/add_ingredient_form.html
new file mode 100644
index 00000000..395928e4
--- /dev/null
+++ b/apps/food/templates/food/add_ingredient_form.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/food/templates/food/basicfood_detail.html b/apps/food/templates/food/basicfood_detail.html
new file mode 100644
index 00000000..846fadba
--- /dev/null
+++ b/apps/food/templates/food/basicfood_detail.html
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+
+
+
+ {% trans 'Owner' %} : {{ food.owner }}
+ {% trans 'Arrival date' %} : {{ food.arrival_date }}
+ {% trans 'Expiry date' %} : {{ food.expiry_date }} ({{ food.date_type }})
+ - {% trans 'Allergens' %} :
+
+ {% for allergen in food.allergens.iterator %}
+ - {{ allergen.name }}
+ {% endfor %}
+
+
+
{% trans 'Active' %} : {{ food.is_active }}
+ {% trans 'Eaten' %} : {{ food.was_eaten }}
+
+ {% if can_update %}
+
{% trans 'Update' %}
+ {% endif %}
+ {% if can_add_ingredient %}
+
+ {% trans 'Add to a meal' %}
+
+ {% endif %}
+
+
+{% endblock %}
diff --git a/apps/food/templates/food/basicfood_form.html b/apps/food/templates/food/basicfood_form.html
new file mode 100644
index 00000000..6fe6f06f
--- /dev/null
+++ b/apps/food/templates/food/basicfood_form.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html
new file mode 100644
index 00000000..456b9970
--- /dev/null
+++ b/apps/food/templates/food/create_qrcode_form.html
@@ -0,0 +1,55 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load render_table from django_tables2 %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+
+
+
+ {% trans 'New basic food' %}
+
+
+
+
{% trans "Copy constructor" %}
+
+
+
+
+ {% trans "Name" %}
+ |
+
+ {% trans "Owner" %}
+ |
+
+ {% trans "Arrival date" %}
+ |
+
+ {% trans "Expiry date" %}
+ |
+
+
+
+ {% for basic in last_basic %}
+
+ {{ basic.name }} |
+ {{ basic.owner }} |
+ {{ basic.arrival_date }} |
+ {{ basic.expiry_date }} |
+
+ {% endfor %}
+
+
+
+
+
+{% endblock %}
diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html
new file mode 100644
index 00000000..6e3e8110
--- /dev/null
+++ b/apps/food/templates/food/qrcode_detail.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html
new file mode 100644
index 00000000..ca32bc06
--- /dev/null
+++ b/apps/food/templates/food/transformedfood_detail.html
@@ -0,0 +1,51 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+
+
+
+ {% trans 'Owner' %} : {{ food.owner }}
+ {% if can_see_ready %}
+ {% trans 'Ready' %} : {{ food.is_ready }}
+ {% endif %}
+ {% trans 'Creation date' %} : {{ food.creation_date }}
+ {% trans 'Expiry date' %} : {{ food.expiry_date }}
+ - {% trans 'Allergens' %} :
+
+ {% for allergen in food.allergens.iterator %}
+ - {{ allergen.name }}
+ {% endfor %}
+
+
+
- {% trans 'Ingredients' %} :
+
+
+
{% trans 'Shelf life' %} : {{ food.shelf_life }}
+ {% trans 'Ready' %} : {{ food.is_ready }}
+ {% trans 'Active' %} : {{ food.is_active }}
+ {% trans 'Eaten' %} : {{ food.was_eaten }}
+
+ {% if can_update %}
+
+ {% trans 'Update' %}
+
+ {% endif %}
+ {% if can_add_ingredient %}
+
+ {% trans 'Add to a meal' %}
+
+ {% endif %}
+
+
+{% endblock %}
diff --git a/apps/food/templates/food/transformedfood_form.html b/apps/food/templates/food/transformedfood_form.html
new file mode 100644
index 00000000..395928e4
--- /dev/null
+++ b/apps/food/templates/food/transformedfood_form.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html
new file mode 100644
index 00000000..4416cdb7
--- /dev/null
+++ b/apps/food/templates/food/transformedfood_list.html
@@ -0,0 +1,60 @@
+{% extends "base.html" %}
+{% comment %}
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load render_table from django_tables2 %}
+{% load i18n %}
+
+{% block content %}
+
+
+ {% if can_create_meal %}
+
+ {% endif %}
+ {% if served.data %}
+ {% render_table served %}
+ {% else %}
+
+
+ {% trans "There is no meal served." %}
+
+
+ {% endif %}
+
+
+
+
+ {% if open.data %}
+ {% render_table open %}
+ {% else %}
+
+
+ {% trans "There is no free meal." %}
+
+
+ {% endif %}
+
+
+
+
+ {% if table.data %}
+ {% render_table table %}
+ {% else %}
+
+
+ {% trans "There is no meal." %}
+
+
+ {% endif %}
+
+{% endblock %}
diff --git a/apps/food/tests.py b/apps/food/tests.py
new file mode 100644
index 00000000..a79ca8be
--- /dev/null
+++ b/apps/food/tests.py
@@ -0,0 +1,3 @@
+# from django.test import TestCase
+
+# Create your tests here.
diff --git a/apps/food/urls.py b/apps/food/urls.py
new file mode 100644
index 00000000..09bb8ebe
--- /dev/null
+++ b/apps/food/urls.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.urls import path
+
+from . import views
+
+app_name = 'food'
+
+urlpatterns = [
+ path('', views.TransformedListView.as_view(), name='food_list'),
+ path('', views.QRCodeView.as_view(), name='qrcode_view'),
+ path('detail/', views.FoodView.as_view(), name='food_view'),
+
+ path('/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'),
+ path('/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'),
+ path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'),
+ path('update/basic/', views.BasicFoodUpdateView.as_view(), name='basic_update'),
+ path('update/transformed/', views.TransformedFoodUpdateView.as_view(), name='transformed_update'),
+ path('add/', views.AddIngredientView.as_view(), name='add_ingredient'),
+]
diff --git a/apps/food/views.py b/apps/food/views.py
new file mode 100644
index 00000000..88964a5f
--- /dev/null
+++ b/apps/food/views.py
@@ -0,0 +1,421 @@
+# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from django.db import transaction
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.http import HttpResponseRedirect
+from django_tables2.views import MultiTableMixin
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+from django.utils import timezone
+from django.views.generic import DetailView, UpdateView
+from django.views.generic.list import ListView
+from django.forms import HiddenInput
+from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin, ProtectedCreateView
+
+from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms
+from .models import BasicFood, Food, QRCode, TransformedFood
+from .tables import TransformedFoodTable
+
+
+class AddIngredientView(ProtectQuerysetMixin, UpdateView):
+ """
+ A view to add an ingredient
+ """
+ model = Food
+ template_name = 'food/add_ingredient_form.html'
+ extra_context = {"title": _("Add the ingredient")}
+ form_class = AddIngredientForms
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["pk"] = self.kwargs["pk"]
+ return context
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ food = Food.objects.get(pk=self.kwargs['pk'])
+ add_ingredient_form = AddIngredientForms(data=self.request.POST)
+ if food.is_ready:
+ form.add_error(None, _("The product is already prepared"))
+ return self.form_invalid(form)
+ if not add_ingredient_form.is_valid():
+ return self.form_invalid(form)
+
+ # We flip logic ""fully used = not is_active""
+ food.is_active = not food.is_active
+ # Save the aliment and the allergens associed
+ for transformed_pk in self.request.POST.getlist('ingredient'):
+ transformed = TransformedFood.objects.get(pk=transformed_pk)
+ if not transformed.is_ready:
+ transformed.ingredient.add(food)
+ transformed.update()
+ food.save()
+
+ return HttpResponseRedirect(self.get_success_url())
+
+ def get_success_url(self, **kwargs):
+ return reverse('food:food_list')
+
+
+class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
+ """
+ A view to update a basic food
+ """
+ model = BasicFood
+ form_class = BasicFoodForms
+ template_name = 'food/basicfood_form.html'
+ extra_context = {"title": _("Update an aliment")}
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ basic_food_form = BasicFoodForms(data=self.request.POST)
+ if not basic_food_form.is_valid():
+ return self.form_invalid(form)
+
+ ans = super().form_valid(form)
+ form.instance.update()
+ return ans
+
+ def get_success_url(self, **kwargs):
+ self.object.refresh_from_db()
+ return reverse('food:food_view', kwargs={"pk": self.object.pk})
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ return context
+
+
+class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+ """
+ A view to see a food
+ """
+ model = Food
+ extra_context = {"title": _("Details of:")}
+ context_object_name = "food"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ context["can_update"] = PermissionBackend.check_perm(self.request, "food.change_food")
+ context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
+ return context
+
+
+class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
+ #####################################################################
+ # TO DO
+ # - this feature is very pratical for meat or fish, nevertheless we can implement this later
+ # - fix picture save
+ # - implement solution crop and convert image (reuse or recode ImageForm from members apps)
+ #####################################################################
+ """
+ A view to add a basic food with a qrcode
+ """
+ model = BasicFood
+ form_class = BasicFoodForms
+ template_name = 'food/basicfood_form.html'
+ extra_context = {"title": _("Add a new basic food with QRCode")}
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ basic_food_form = BasicFoodForms(data=self.request.POST)
+ if not basic_food_form.is_valid():
+ return self.form_invalid(form)
+
+ # Save the aliment and the allergens associed
+ basic_food = form.save(commit=False)
+ # We assume the date of labeling and the same as the date of arrival
+ basic_food.arrival_date = timezone.now()
+ basic_food.is_ready = False
+ basic_food.is_active = True
+ basic_food.was_eaten = False
+ basic_food._force_save = True
+ basic_food.save()
+ basic_food.refresh_from_db()
+
+ qrcode = QRCode()
+ qrcode.qr_code_number = self.kwargs['slug']
+ qrcode.food_container = basic_food
+ qrcode.save()
+
+ return super().form_valid(form)
+
+ def get_success_url(self, **kwargs):
+ self.object.refresh_from_db()
+ return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']})
+
+ def get_sample_object(self):
+
+ # We choose a club which may work or BDE else
+ owner_id = 1
+ for membership in self.request.user.memberships.all():
+ club_id = membership.club.id
+ food = BasicFood(name="", expiry_date=timezone.now(), owner_id=club_id)
+ if PermissionBackend.check_perm(self.request, "food.add_basicfood", food):
+ owner_id = club_id
+
+ return BasicFood(
+ name="",
+ expiry_date=timezone.now(),
+ owner_id=owner_id,
+ )
+
+ def get_context_data(self, **kwargs):
+ # Some field are hidden on create
+ context = super().get_context_data(**kwargs)
+
+ form = context['form']
+ form.fields['is_active'].widget = HiddenInput()
+ form.fields['was_eaten'].widget = HiddenInput()
+
+ copy = self.request.GET.get('copy', None)
+ if copy is not None:
+ basic = BasicFood.objects.get(pk=copy)
+ for field in ['date_type', 'expiry_date', 'name', 'owner']:
+ form.fields[field].initial = getattr(basic, field)
+ for field in ['allergens']:
+ form.fields[field].initial = getattr(basic, field).all()
+
+ return context
+
+
+class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
+ """
+ A view to add a new qrcode
+ """
+ model = QRCode
+ template_name = 'food/create_qrcode_form.html'
+ form_class = QRCodeForms
+ extra_context = {"title": _("Add a new QRCode")}
+
+ def get(self, *args, **kwargs):
+ qrcode = kwargs["slug"]
+ if self.model.objects.filter(qr_code_number=qrcode).count() > 0:
+ return HttpResponseRedirect(reverse("food:qrcode_view", kwargs=kwargs))
+ else:
+ return super().get(*args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["slug"] = self.kwargs["slug"]
+
+ context["last_basic"] = BasicFood.objects.order_by('-pk').all()[:10]
+
+ return context
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ qrcode_food_form = QRCodeForms(data=self.request.POST)
+ if not qrcode_food_form.is_valid():
+ return self.form_invalid(form)
+
+ # Save the qrcode
+ qrcode = form.save(commit=False)
+ qrcode.qr_code_number = self.kwargs["slug"]
+ qrcode._force_save = True
+ qrcode.save()
+ qrcode.refresh_from_db()
+
+ qrcode.food_container.save()
+
+ return super().form_valid(form)
+
+ def get_success_url(self, **kwargs):
+ self.object.refresh_from_db()
+ return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']})
+
+ def get_sample_object(self):
+ return QRCode(
+ qr_code_number=self.kwargs["slug"],
+ food_container_id=1
+ )
+
+
+class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+ """
+ A view to see a qrcode
+ """
+ model = QRCode
+ extra_context = {"title": _("QRCode")}
+ context_object_name = "qrcode"
+ slug_field = "qr_code_number"
+
+ def get(self, *args, **kwargs):
+ qrcode = kwargs["slug"]
+ if self.model.objects.filter(qr_code_number=qrcode).count() > 0:
+ return super().get(*args, **kwargs)
+ else:
+ return HttpResponseRedirect(reverse("food:qrcode_create", kwargs=kwargs))
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ qr_code_number = self.kwargs['slug']
+ qrcode = self.model.objects.get(qr_code_number=qr_code_number)
+
+ model = qrcode.food_container.polymorphic_ctype.model
+
+ if model == "basicfood":
+ context["can_update_basic"] = PermissionBackend.check_perm(self.request, "food.change_basicfood")
+ context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_basicfood")
+ if model == "transformedfood":
+ context["can_update_transformed"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
+ context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_transformedfood")
+ context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood")
+ return context
+
+
+class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
+ """
+ A view to add a tranformed food
+ """
+ model = TransformedFood
+ template_name = 'food/transformedfood_form.html'
+ form_class = TransformedFoodForms
+ extra_context = {"title": _("Add a new meal")}
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ transformed_food_form = TransformedFoodForms(data=self.request.POST)
+ if not transformed_food_form.is_valid():
+ return self.form_invalid(form)
+
+ # Save the aliment and allergens associated
+ transformed_food = form.save(commit=False)
+ transformed_food.expiry_date = transformed_food.creation_date
+ transformed_food.is_active = True
+ transformed_food.is_ready = False
+ transformed_food.was_eaten = False
+ transformed_food._force_save = True
+ transformed_food.save()
+ transformed_food.refresh_from_db()
+ ans = super().form_valid(form)
+ transformed_food.update()
+ return ans
+
+ def get_success_url(self, **kwargs):
+ self.object.refresh_from_db()
+ return reverse('food:food_view', kwargs={"pk": self.object.pk})
+
+ def get_sample_object(self):
+ # We choose a club which may work or BDE else
+ owner_id = 1
+ for membership in self.request.user.memberships.all():
+ club_id = membership.club.id
+ food = TransformedFood(name="",
+ creation_date=timezone.now(),
+ expiry_date=timezone.now(),
+ owner_id=club_id)
+ if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food):
+ owner_id = club_id
+ break
+
+ return TransformedFood(
+ name="",
+ owner_id=owner_id,
+ creation_date=timezone.now(),
+ expiry_date=timezone.now(),
+ )
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ # Some field are hidden on create
+ form = context['form']
+ form.fields['is_active'].widget = HiddenInput()
+ form.fields['is_ready'].widget = HiddenInput()
+ form.fields['was_eaten'].widget = HiddenInput()
+ form.fields['shelf_life'].widget = HiddenInput()
+
+ return context
+
+
+class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
+ """
+ A view to update transformed product
+ """
+ model = TransformedFood
+ template_name = 'food/transformedfood_form.html'
+ form_class = TransformedFoodForms
+ extra_context = {'title': _('Update a meal')}
+
+ @transaction.atomic
+ def form_valid(self, form):
+ form.instance.creater = self.request.user
+ transformedfood_form = TransformedFoodForms(data=self.request.POST)
+ if not transformedfood_form.is_valid():
+ return self.form_invalid(form)
+
+ ans = super().form_valid(form)
+ form.instance.update()
+ return ans
+
+ def get_success_url(self, **kwargs):
+ self.object.refresh_from_db()
+ return reverse('food:food_view', kwargs={"pk": self.object.pk})
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ return context
+
+
+class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
+ """
+ Displays ready TransformedFood
+ """
+ model = TransformedFood
+ tables = [TransformedFoodTable, TransformedFoodTable, TransformedFoodTable]
+ extra_context = {"title": _("Transformed food")}
+
+ def get_queryset(self, **kwargs):
+ return super().get_queryset(**kwargs).distinct()
+
+ def get_tables(self):
+ tables = super().get_tables()
+
+ tables[0].prefix = "all-"
+ tables[1].prefix = "open-"
+ tables[2].prefix = "served-"
+ return tables
+
+ def get_tables_data(self):
+ # first table = all transformed food, second table = free, third = served
+ return [
+ self.get_queryset().order_by("-creation_date"),
+ TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__lt=timezone.now())
+ .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view"))
+ .distinct()
+ .order_by("-creation_date"),
+ TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__gte=timezone.now())
+ .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view"))
+ .distinct()
+ .order_by("-creation_date")
+ ]
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ # We choose a club which should work
+ for membership in self.request.user.memberships.all():
+ club_id = membership.club.id
+ food = TransformedFood(
+ name="",
+ owner_id=club_id,
+ creation_date=timezone.now(),
+ expiry_date=timezone.now(),
+ )
+ if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food):
+ context['can_create_meal'] = True
+ break
+
+ tables = context["tables"]
+ for name, table in zip(["table", "open", "served"], tables):
+ context[name] = table
+ return context
diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json
index 1b97acac..9e4ba924 100644
--- a/apps/permission/fixtures/initial.json
+++ b/apps/permission/fixtures/initial.json
@@ -3304,6 +3304,454 @@
"description": "Voir le tableau des ouvreur⋅ses"
}
},
+ {
+ "model": "permission.permission",
+ "pk": 211,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tout les plats"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 212,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"owner\": [\"club\"]}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tout les plats de son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 213,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"is_ready\": true, \"is_active\": true, \"was_eaten\": false}",
+ "type": "view",
+ "mask": 1,
+ "field": "",
+ "permanent": false,
+ "description": "Voir les plats préparés actifs servis"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 214,
+ "fields": {
+ "model": [
+ "food",
+ "qrcode"
+ ],
+ "query": "{}",
+ "type": "add",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Initialiser un QR code de traçabilité"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 215,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"owner\": [\"club\"]}",
+ "type": "add",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Créer un nouvel ingrédient pour son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 216,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{}",
+ "type": "add",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Créer un nouvel ingrédient"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 217,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir toute la bouffe"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 218,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir toute la bouffe active"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 219,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"is_active\": true, \"owner\": [\"club\"]}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir la bouffe active de son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 220,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{}",
+ "type": "change",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Modifier de la bouffe"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 221,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"is_active\": true, \"was_eaten\": false}",
+ "type": "change",
+ "mask": 3,
+ "field": "allergens",
+ "permanent": false,
+ "description": "Modifier les allergènes de la bouffe existante"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 222,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"is_active\": true, \"was_eaten\": false, \"owner\": [\"club\"]}",
+ "type": "change",
+ "mask": 3,
+ "field": "allergens",
+ "permanent": false,
+ "description": "Modifier les allergènes de la bouffe appartenant à son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 223,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{}",
+ "type": "add",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Créer un plat"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 224,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"owner\": [\"club\"]}",
+ "type": "add",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Créer un plat pour son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 225,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{}",
+ "type": "change",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Modifier tout les plats"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 226,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "change",
+ "mask": 3,
+ "field": "was_eaten",
+ "permanent": false,
+ "description": "Indiquer si un plat a été mangé"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 227,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"is_active\": true, \"owner\": [\"club\"]}",
+ "type": "change",
+ "mask": 3,
+ "field": "is_ready",
+ "permanent": false,
+ "description": "Indiquer si un plat de son club est prêt"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 228,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "change",
+ "mask": 3,
+ "field": "is_active",
+ "permanent": false,
+ "description": "Archiver un plat"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 229,
+ "fields": {
+ "model": [
+ "food",
+ "basicfood"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "change",
+ "mask": 3,
+ "field": "is_active",
+ "permanent": false,
+ "description": "Archiver de la bouffe"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 230,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tout les plats actifs"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 231,
+ "fields": {
+ "model": [
+ "food",
+ "qrcode"
+ ],
+ "query": "{}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tous les QR codes"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 232,
+ "fields": {
+ "model": [
+ "food",
+ "qrcode"
+ ],
+ "query": "{\"food_container__is_active\": true}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tous les QR codes actifs"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 233,
+ "fields": {
+ "model": [
+ "food",
+ "qrcode"
+ ],
+ "query": "{\"food_container__owner\": [\"club\"], \"food_container__is_active\": true}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir tous les QR codes actifs de son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk" : 234,
+ "fields": {
+ "model": [
+ "food",
+ "transformedfood"
+ ],
+ "query": "{\"owner\": [\"club\"], \"is_active\": true}",
+ "type": "change",
+ "mask": 3,
+ "field": "ingredients",
+ "permanent": false,
+ "description": "Changer les ingrédients d'un plat actif de son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 235,
+ "fields": {
+ "model": [
+ "food",
+ "food"
+ ],
+ "query": "{}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir bouffe"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 236,
+ "fields": {
+ "model": [
+ "food",
+ "food"
+ ],
+ "query": "{\"is_active\": true}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir bouffe active"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 237,
+ "fields": {
+ "model": [
+ "food",
+ "food"
+ ],
+ "query": "{\"is_active\": true, \"owner\": [\"club\"]}",
+ "type": "view",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Voir bouffe active de son club"
+ }
+ },
+ {
+ "model": "permission.permission",
+ "pk": 238,
+ "fields": {
+ "model": [
+ "food",
+ "food"
+ ],
+ "query": "{}",
+ "type": "change",
+ "mask": 3,
+ "field": "",
+ "permanent": false,
+ "description": "Modifier bouffe"
+ }
+ },
{
"model": "permission.role",
"pk": 1,
@@ -3391,7 +3839,8 @@
157,
158,
159,
- 160
+ 160,
+ 213
]
}
},
@@ -3417,7 +3866,17 @@
49,
50,
141,
- 169
+ 169,
+ 212,
+ 214,
+ 215,
+ 219,
+ 222,
+ 224,
+ 227,
+ 233,
+ 234,
+ 237
]
}
},
@@ -3591,7 +4050,21 @@
166,
167,
168,
- 182
+ 182,
+ 212,
+ 214,
+ 215,
+ 218,
+ 221,
+ 224,
+ 226,
+ 227,
+ 228,
+ 229,
+ 230,
+ 232,
+ 234,
+ 236
]
}
},
@@ -3799,7 +4272,8 @@
168,
176,
177,
- 197
+ 197,
+ 211
]
}
},
@@ -3817,6 +4291,27 @@
]
}
},
+ {
+ "model": "permission.role",
+ "pk": 22,
+ "fields": {
+ "for_club": 2,
+ "name": "Respo Bouffe",
+ "permissions": [
+ 137,
+ 211,
+ 214,
+ 216,
+ 217,
+ 220,
+ 223,
+ 225,
+ 231,
+ 235,
+ 238
+ ]
+ }
+ },
{
"model": "wei.weirole",
"pk": 12,
diff --git a/apps/scripts b/apps/scripts
index 472c9c33..f580f9b9 160000
--- a/apps/scripts
+++ b/apps/scripts
@@ -1 +1 @@
-Subproject commit 472c9c33cea3a9c277033f2108fd81304fb62097
+Subproject commit f580f9b9e9beee76605975fdbc3a2014769e3c61
diff --git a/docs/apps/food.rst b/docs/apps/food.rst
new file mode 100644
index 00000000..e34ad40f
--- /dev/null
+++ b/docs/apps/food.rst
@@ -0,0 +1,83 @@
+Application Food
+================
+
+L'application ``food`` s'occupe de la traçabilité et permet notamment l'obtention de la liste des allergènes.
+
+Modèles
+-------
+
+L'application comporte 5 modèles : Allergen, QRCode, Food, BasicFood, TransformedFood.
+
+Food
+~~~~
+
+Ce modèle est un PolymorphicModel et ne sert uniquement à créer BasicFood et TransformedFood.
+
+Le modèle regroupe :
+
+* Nom du produit
+* Propriétaire (doit-être un Club)
+* Allergènes (ManyToManyField)
+* date d'expiration
+* a été mangé (booléen)
+* est prêt (booléen)
+
+BasicFood
+~~~~~~~~~
+
+Les BasicFood correspondent aux produits non modifiés à la Kfet. Ils peuvent correspondre à la fois à des produits achetés en magasin ou à des produits Terre à Terre. Ces produits seront les ingrédients de tous les plats préparés et en conséquent sont les seuls produits à nécessité une saisie manuelle des allergènes.
+
+Le modèle regroupe :
+
+* Type de date (DLC = date limite de consommation, DDM = date de durabilité minimale)
+* Date d'arrivée
+* Champs de Food
+
+TransformedFood
+~~~~~~~~~~~~~~~
+
+Les TransformedFood correspondent aux produits préparés à la Kfet. Ils peuvent être composés de BasicFood et/ou de TransformedFood. La date d'expiration et les allergènes sont automatiquement mis à jour par update (qui doit être exécuté après modification des ingrédients dans les forms par exemple).
+
+Le modèle regroupe :
+
+* Durée de consommation (par défaut 3 jours)
+* Ingrédients (ManyToManyField vers Food)
+* Date de création
+* Champs de Food
+
+Allergen
+~~~~~~~~
+
+Le modèle regroupe :
+
+* Nom
+
+QRCode
+~~~~~~
+
+Le modèle regroupe :
+
+* nombre (unique, entier positif)
+* food (OneToOneField vers Food)
+
+Création de BasicFood
+~~~~~~~~~~~~~~~~~~~~~
+
+Un BasicFood a toujours besoin d'un QRCode (depuis l'interface web). Il convient donc de coller le QRCode puis de le scanner et de compléter le formulaire.
+
+Création de TransformedFood
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pour créer un TransformedFood, il suffit d'aller dans l'onglet ``traçabilité`` et de cliquer sur l'onglet.
+
+Ajouter un ingrédient
+~~~~~~~~~~~~~~~~~~~~~
+
+Un ingrédient a forcément un QRCode. Il convient donc de scanner le QRCode de l'ingrédient et de sélectionner le produit auquel il doit être ajouté.
+
+Remarque : Un produit fini doit avoir un QRCode et inversement.
+
+Terminer un plat
+~~~~~~~~~~~~~~~~
+
+Il suffit de coller le QRCode sur le plat, de le scanner et de sélectionner le produit.
diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po
index 6542cc2a..3abeae7b 100644
--- a/locale/de/LC_MESSAGES/django.po
+++ b/locale/de/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-22 00:33+0100\n"
+"POT-Creation-Date: 2024-08-17 11:57+0200\n"
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
"Last-Translator: bleizi \n"
"Language-Team: German \n"
@@ -18,8 +18,8 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
-#: apps/activity/apps.py:10 apps/activity/models.py:127
-#: apps/activity/models.py:167
+#: apps/activity/apps.py:10 apps/activity/models.py:129
+#: apps/activity/models.py:169
msgid "activity"
msgstr "Veranstaltung"
@@ -27,33 +27,33 @@ msgstr "Veranstaltung"
msgid "The note of this club is inactive."
msgstr ""
-#: apps/activity/forms.py:41 apps/activity/models.py:140
+#: apps/activity/forms.py:41 apps/activity/models.py:142
msgid "The end date must be after the start date."
msgstr "Das Abschlussdatum muss nach das Anfangsdatum sein."
-#: apps/activity/forms.py:82 apps/activity/models.py:269
+#: apps/activity/forms.py:82 apps/activity/models.py:271
msgid "You can't invite someone once the activity is started."
msgstr ""
"Sie dürfen nicht jemandem einladen wenn die Veranstaltung angefangen hat."
-#: apps/activity/forms.py:85 apps/activity/models.py:272
+#: apps/activity/forms.py:85 apps/activity/models.py:274
msgid "This activity is not validated yet."
msgstr "Diese Veranstaltung ist noch nicht bestätigt."
-#: apps/activity/forms.py:95 apps/activity/models.py:280
+#: apps/activity/forms.py:95 apps/activity/models.py:282
msgid "This person has been already invited 5 times this year."
msgstr "Diese Person wurde schon 5 mal dieses Jahr eingeladen."
-#: apps/activity/forms.py:99 apps/activity/models.py:284
+#: apps/activity/forms.py:99 apps/activity/models.py:286
msgid "This person is already invited."
msgstr "Diese Person wurde schon eingeladen."
-#: apps/activity/forms.py:103 apps/activity/models.py:288
+#: apps/activity/forms.py:103 apps/activity/models.py:290
msgid "You can't invite more than 3 people to this activity."
msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen."
-#: apps/activity/models.py:28 apps/activity/models.py:63
-#: apps/member/models.py:203
+#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42
+#: apps/food/models.py:56 apps/member/models.py:203
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@@ -99,121 +99,121 @@ msgstr "Vearnstaltungarte"
msgid "description"
msgstr "Beschreibung"
-#: apps/activity/models.py:72
+#: apps/activity/models.py:74
msgid "location"
msgstr "Ort"
-#: apps/activity/models.py:76
+#: apps/activity/models.py:78
msgid "Place where the activity is organized, eg. Kfet."
msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)."
-#: apps/activity/models.py:83
+#: apps/activity/models.py:85
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:207 apps/note/models/transactions.py:67
#: apps/permission/models.py:163
msgid "type"
msgstr "Type"
-#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:313
+#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318
#: apps/note/models/notes.py:148 apps/treasury/models.py:293
#: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
msgstr "User"
-#: apps/activity/models.py:96
+#: apps/activity/models.py:98
#: apps/activity/templates/activity/includes/activity_info.html:36
msgid "organizer"
msgstr "Veranstalter"
-#: apps/activity/models.py:97
+#: apps/activity/models.py:99
msgid "Club that organizes the activity. The entry fees will go to this club."
msgstr ""
"Die Veranstaltung wurde von einem Club organisert. Die Eintrittsbeiträge "
"gehen an diesen Club."
-#: apps/activity/models.py:104
+#: apps/activity/models.py:106
#: apps/activity/templates/activity/includes/activity_info.html:39
msgid "attendees club"
msgstr "Teilnehmer"
-#: apps/activity/models.py:105
+#: apps/activity/models.py:107
msgid "Club that is authorized to join the activity. Mostly the Kfet club."
msgstr "Club die an die Veranstaltung teilnehmen können."
-#: apps/activity/models.py:109
+#: apps/activity/models.py:111
#: apps/activity/templates/activity/includes/activity_info.html:25
msgid "start date"
msgstr "Anfangsdatum"
-#: apps/activity/models.py:113
+#: apps/activity/models.py:115
#: apps/activity/templates/activity/includes/activity_info.html:28
msgid "end date"
msgstr "Abschlussdatum"
-#: apps/activity/models.py:118
+#: apps/activity/models.py:120
#: apps/activity/templates/activity/includes/activity_info.html:50
#: apps/note/models/transactions.py:149
msgid "valid"
msgstr "gültig"
-#: apps/activity/models.py:123
+#: apps/activity/models.py:125
#: apps/activity/templates/activity/includes/activity_info.html:65
msgid "open"
msgstr "geöffnet"
-#: apps/activity/models.py:128
+#: apps/activity/models.py:130
msgid "activities"
msgstr "Veranstaltungen"
-#: apps/activity/models.py:172
+#: apps/activity/models.py:174
msgid "entry time"
msgstr "Eintrittzeit"
-#: apps/activity/models.py:178 apps/note/apps.py:14
+#: apps/activity/models.py:180 apps/note/apps.py:14
#: apps/note/models/notes.py:77
msgid "note"
msgstr "Note"
-#: apps/activity/models.py:189
+#: apps/activity/models.py:191
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entry"
msgstr "Eintritt"
-#: apps/activity/models.py:190
+#: apps/activity/models.py:192
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entries"
msgstr "Eintritte"
-#: apps/activity/models.py:193
+#: apps/activity/models.py:195
#, python-brace-format
msgid "Entry for {guest}, invited by {note} to the activity {activity}"
msgstr "Eintritt für {guest}, von {note} zur Vanstaltung {activity} eingeladen"
-#: apps/activity/models.py:195
+#: apps/activity/models.py:197
#, python-brace-format
msgid "Entry for {note} to the activity {activity}"
msgstr "Eintritt von {note} zur Veranstaltung {activity}"
-#: apps/activity/models.py:202
+#: apps/activity/models.py:204
msgid "Already entered on "
msgstr "Schon eingetretten "
-#: apps/activity/models.py:202 apps/activity/tables.py:56
+#: apps/activity/models.py:204 apps/activity/tables.py:56
msgid "{:%Y-%m-%d %H:%M:%S}"
msgstr "{:%Y-%m-%d %H:%M:%S}"
-#: apps/activity/models.py:210
+#: apps/activity/models.py:212
msgid "The balance is negative."
msgstr "Kontostand ist im Rot."
-#: apps/activity/models.py:240
+#: apps/activity/models.py:242
#: apps/treasury/templates/treasury/sogecredit_detail.html:14
#: apps/wei/templates/wei/attribute_bus_1A.html:16
msgid "last name"
msgstr "Nachname"
-#: apps/activity/models.py:245
+#: apps/activity/models.py:247
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/treasury/templates/treasury/sogecredit_detail.html:17
@@ -222,19 +222,19 @@ msgstr "Nachname"
msgid "first name"
msgstr "Vorname"
-#: apps/activity/models.py:252
+#: apps/activity/models.py:254
msgid "inviter"
msgstr "Einlader"
-#: apps/activity/models.py:256
+#: apps/activity/models.py:258
msgid "guest"
msgstr "Gast"
-#: apps/activity/models.py:257
+#: apps/activity/models.py:259
msgid "guests"
msgstr "Gäste"
-#: apps/activity/models.py:310
+#: apps/activity/models.py:312
msgid "Invitation"
msgstr "Einladung"
@@ -246,7 +246,9 @@ msgstr "Die Veranstaltung ist geöffnet."
msgid "The validation of the activity is pending."
msgstr "Diese Veranstaltung ist noch nicht bestätigt."
-#: apps/activity/tables.py:43 apps/treasury/tables.py:107
+#: apps/activity/tables.py:43
+#: apps/member/templates/member/picture_update.html:18
+#: apps/treasury/tables.py:107
msgid "Remove"
msgstr "Entfernen"
@@ -262,15 +264,15 @@ msgstr "entfernen"
msgid "Type"
msgstr "Type"
-#: apps/activity/tables.py:84 apps/member/forms.py:193
-#: apps/registration/forms.py:92 apps/treasury/forms.py:131
+#: apps/activity/tables.py:84 apps/member/forms.py:196
+#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Nachname"
-#: apps/activity/tables.py:86 apps/member/forms.py:198
+#: apps/activity/tables.py:86 apps/member/forms.py:201
#: apps/note/templates/note/transaction_form.html:138
-#: apps/registration/forms.py:97 apps/treasury/forms.py:133
+#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Vorname"
@@ -297,7 +299,7 @@ msgstr "Gastliste"
#: apps/note/models/transactions.py:261
#: apps/note/templates/note/transaction_form.html:17
#: apps/note/templates/note/transaction_form.html:152
-#: note_kfet/templates/base.html:72
+#: note_kfet/templates/base.html:78
msgid "Transfer"
msgstr "Überweisen"
@@ -334,6 +336,10 @@ msgid "Entry done!"
msgstr "Eintrittseite"
#: apps/activity/templates/activity/activity_form.html:16
+#: apps/food/templates/food/add_ingredient_form.html:16
+#: apps/food/templates/food/basicfood_form.html:16
+#: apps/food/templates/food/create_qrcode_form.html:19
+#: apps/food/templates/food/transformedfood_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
#: apps/note/templates/note/transactiontemplate_form.html:18
@@ -399,39 +405,39 @@ msgstr "bearbeiten"
msgid "Invite"
msgstr "Einladen"
-#: apps/activity/views.py:36
+#: apps/activity/views.py:37
msgid "Create new activity"
msgstr "Neue Veranstaltung schaffen"
-#: apps/activity/views.py:67 note_kfet/templates/base.html:90
+#: apps/activity/views.py:67 note_kfet/templates/base.html:96
msgid "Activities"
msgstr "Veranstaltungen"
-#: apps/activity/views.py:93
+#: apps/activity/views.py:108
msgid "Activity detail"
msgstr "Veranstaltunginfo"
-#: apps/activity/views.py:113
+#: apps/activity/views.py:128
msgid "Update activity"
msgstr "Veranstaltung bearbeiten"
-#: apps/activity/views.py:140
+#: apps/activity/views.py:155
msgid "Invite guest to the activity \"{}\""
msgstr "Gast zur Veranstaltung \"{}\" einladen"
-#: apps/activity/views.py:178
+#: apps/activity/views.py:193
msgid "You are not allowed to display the entry interface for this activity."
msgstr "Sie haben nicht das Recht diese Seite zu benuzten."
-#: apps/activity/views.py:181
+#: apps/activity/views.py:196
msgid "This activity does not support activity entries."
msgstr "Diese Veranstaltung braucht nicht Eintritt."
-#: apps/activity/views.py:184
+#: apps/activity/views.py:199
msgid "This activity is closed."
msgstr "Diese Veranstaltung ist geschlossen."
-#: apps/activity/views.py:280
+#: apps/activity/views.py:295
msgid "Entry for activity \"{}\""
msgstr "Eintritt zur Veranstaltung \"{}\""
@@ -439,6 +445,298 @@ msgstr "Eintritt zur Veranstaltung \"{}\""
msgid "API"
msgstr "API"
+#: apps/food/apps.py:11 apps/food/models.py:105
+msgid "food"
+msgstr ""
+
+#: apps/food/forms.py:32
+msgid "Fully used"
+msgstr ""
+
+#: apps/food/forms.py:50
+msgid "Pasta METRO 5kg"
+msgstr ""
+
+#: apps/food/forms.py:96
+msgid "Lasagna"
+msgstr ""
+
+#: apps/food/models.py:18
+#, fuzzy
+#| msgid "phone number"
+msgid "QR-code number"
+msgstr "Telefonnummer"
+
+#: apps/food/models.py:26
+msgid "food container"
+msgstr ""
+
+#: apps/food/models.py:30
+msgid "QR-code"
+msgstr ""
+
+#: apps/food/models.py:31
+msgid "QR-codes"
+msgstr ""
+
+#: apps/food/models.py:34
+#, python-brace-format
+msgid "QR-code number {qr_code_number}"
+msgstr ""
+
+#: apps/food/models.py:47
+msgid "Allergen"
+msgstr ""
+
+#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17
+#: apps/food/templates/food/transformedfood_detail.html:20
+msgid "Allergens"
+msgstr ""
+
+#: apps/food/models.py:64
+msgid "owner"
+msgstr ""
+
+#: apps/food/models.py:70
+msgid "allergen"
+msgstr ""
+
+#: apps/food/models.py:74
+#, fuzzy
+#| msgid "birth date"
+msgid "expiry date"
+msgstr "Geburtsdatum"
+
+#: apps/food/models.py:80
+msgid "was eaten"
+msgstr ""
+
+#: apps/food/models.py:89
+msgid "is ready"
+msgstr ""
+
+#: apps/food/models.py:94
+#, fuzzy
+#| msgid "active"
+msgid "is active"
+msgstr "Aktiv"
+
+#: apps/food/models.py:106
+msgid "foods"
+msgstr ""
+
+#: apps/food/models.py:122
+#, fuzzy
+#| msgid "start date"
+msgid "arrival date"
+msgstr "Anfangsdatum"
+
+#: apps/food/models.py:152
+msgid "Basic food"
+msgstr ""
+
+#: apps/food/models.py:153
+msgid "Basic foods"
+msgstr ""
+
+#: apps/food/models.py:161
+#, fuzzy
+#| msgid "created at"
+msgid "creation date"
+msgstr "erschafft am"
+
+#: apps/food/models.py:169
+msgid "transformed ingredient"
+msgstr ""
+
+#: apps/food/models.py:174
+msgid "shelf life"
+msgstr ""
+
+#: apps/food/models.py:225 apps/food/views.py:365
+#, fuzzy
+#| msgid "Transfer money"
+msgid "Transformed food"
+msgstr "Geld überweisen"
+
+#: apps/food/models.py:226
+msgid "Transformed foods"
+msgstr ""
+
+#: apps/food/templates/food/basicfood_detail.html:14
+#: apps/food/templates/food/qrcode_detail.html:15
+#: apps/food/templates/food/transformedfood_detail.html:14
+#, fuzzy
+#| msgid "Owned"
+msgid "Owner"
+msgstr "Besetzt"
+
+#: apps/food/templates/food/basicfood_detail.html:15
+#, fuzzy
+#| msgid "start date"
+msgid "Arrival date"
+msgstr "Anfangsdatum"
+
+#: apps/food/templates/food/basicfood_detail.html:16
+#: apps/food/templates/food/qrcode_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:19
+#, fuzzy
+#| msgid "birth date"
+msgid "Expiry date"
+msgstr "Geburtsdatum"
+
+#: apps/food/templates/food/basicfood_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:36
+#, fuzzy
+#| msgid "active"
+msgid "Active"
+msgstr "Aktiv"
+
+#: apps/food/templates/food/basicfood_detail.html:25
+#: apps/food/templates/food/transformedfood_detail.html:37
+msgid "Eaten"
+msgstr ""
+
+#: apps/food/templates/food/basicfood_detail.html:28
+#: apps/food/templates/food/qrcode_detail.html:20
+#: apps/food/templates/food/qrcode_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:41
+#, fuzzy
+#| msgid "Update bus"
+msgid "Update"
+msgstr "Bus bearbeiten"
+
+#: apps/food/templates/food/basicfood_detail.html:32
+#: apps/food/templates/food/qrcode_detail.html:34
+#: apps/food/templates/food/transformedfood_detail.html:46
+#, fuzzy
+#| msgid "Add team"
+msgid "Add to a meal"
+msgstr "Neue Team"
+
+#: apps/food/templates/food/create_qrcode_form.html:14
+#, fuzzy
+#| msgid "Transfer money"
+msgid "New basic food"
+msgstr "Geld überweisen"
+
+#: apps/food/templates/food/qrcode_detail.html:10
+#, fuzzy
+#| msgid "phone number"
+msgid "number"
+msgstr "Telefonnummer"
+
+#: apps/food/templates/food/qrcode_detail.html:14
+#: apps/note/templates/note/transaction_form.html:132
+#: apps/treasury/models.py:60
+msgid "Name"
+msgstr "Name"
+
+#: apps/food/templates/food/qrcode_detail.html:29
+#, fuzzy
+#| msgid "Profile detail"
+msgid "View details"
+msgstr "Profile detail"
+
+#: apps/food/templates/food/transformedfood_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:35
+msgid "Ready"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_detail.html:18
+#, fuzzy
+#| msgid "created at"
+msgid "Creation date"
+msgstr "erschafft am"
+
+#: apps/food/templates/food/transformedfood_detail.html:27
+msgid "Ingredients"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_detail.html:34
+msgid "Shelf life"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:11
+msgid "Meal served"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:16
+#, fuzzy
+#| msgid "New user"
+msgid "New meal"
+msgstr "Neue User"
+
+#: apps/food/templates/food/transformedfood_list.html:25
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no meal served."
+msgstr "Es gibt keine Ergebnisse."
+
+#: apps/food/templates/food/transformedfood_list.html:33
+msgid "Open"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:40
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no free meal."
+msgstr "Es gibt keine Ergebnisse."
+
+#: apps/food/templates/food/transformedfood_list.html:48
+msgid "All meals"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:55
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no meal."
+msgstr "Es gibt keine Ergebnisse."
+
+#: apps/food/views.py:28
+msgid "Add the ingredient"
+msgstr ""
+
+#: apps/food/views.py:42
+#, fuzzy
+#| msgid "This credit is already validated."
+msgid "The product is already prepared"
+msgstr "Dieser Kredit ist bereits validiert."
+
+#: apps/food/views.py:70
+#, fuzzy
+#| msgid "Update an invoice"
+msgid "Update an aliment"
+msgstr "Rechnung bearbeiten"
+
+#: apps/food/views.py:97
+#, fuzzy
+#| msgid "WEI Detail"
+msgid "Details of:"
+msgstr "WEI Infos"
+
+#: apps/food/views.py:121
+msgid "Add a new basic food with QRCode"
+msgstr ""
+
+#: apps/food/views.py:185
+msgid "Add a new QRCode"
+msgstr ""
+
+#: apps/food/views.py:235
+msgid "QRCode"
+msgstr ""
+
+#: apps/food/views.py:271
+msgid "Add a new meal"
+msgstr ""
+
+#: apps/food/views.py:337
+#, fuzzy
+#| msgid "Update team"
+msgid "Update a meal"
+msgstr "Team bearbeiten"
+
#: apps/logs/apps.py:11
msgid "Logs"
msgstr "Logs"
@@ -508,11 +806,11 @@ msgstr "Mitgliedschaftpreis (bezahlte Studenten)"
msgid "membership fee (unpaid students)"
msgstr "Mitgliedschaftpreis (unbezahlte Studenten)"
-#: apps/member/admin.py:65 apps/member/models.py:325
+#: apps/member/admin.py:65 apps/member/models.py:330
msgid "roles"
msgstr "Rollen"
-#: apps/member/admin.py:66 apps/member/models.py:339
+#: apps/member/admin.py:66 apps/member/models.py:344
msgid "fee"
msgstr "Preis"
@@ -565,49 +863,49 @@ msgstr "Maximal Größe: 2MB"
msgid "This image cannot be loaded."
msgstr "Dieses Bild kann nicht geladen werden."
-#: apps/member/forms.py:148 apps/member/views.py:102
-#: apps/registration/forms.py:34 apps/registration/views.py:266
+#: apps/member/forms.py:151 apps/member/views.py:102
+#: apps/registration/forms.py:33 apps/registration/views.py:276
msgid "An alias with a similar name already exists."
msgstr "Ein ähnliches Alias ist schon benutzt."
-#: apps/member/forms.py:172
+#: apps/member/forms.py:175
msgid "Inscription paid by Société Générale"
msgstr "Mitgliedschaft von der Société Générale bezahlt"
-#: apps/member/forms.py:174
+#: apps/member/forms.py:177
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Die Société Générale die Mitgliedschaft bezahlt."
-#: apps/member/forms.py:179 apps/registration/forms.py:79
+#: apps/member/forms.py:182 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Kredittype"
-#: apps/member/forms.py:180 apps/registration/forms.py:80
+#: apps/member/forms.py:183 apps/registration/forms.py:79
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "Kein Kredit"
-#: apps/member/forms.py:182
+#: apps/member/forms.py:185
msgid "You can credit the note of the user."
msgstr "Sie dûrfen diese Note kreditieren."
-#: apps/member/forms.py:186 apps/registration/forms.py:85
+#: apps/member/forms.py:189 apps/registration/forms.py:84
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Kreditanzahl"
-#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144
-#: apps/registration/forms.py:102 apps/treasury/forms.py:135
+#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144
+#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Bank"
-#: apps/member/forms.py:230
+#: apps/member/forms.py:233
msgid "User"
msgstr "User"
-#: apps/member/forms.py:244
+#: apps/member/forms.py:247
msgid "Roles"
msgstr "Rollen"
@@ -857,46 +1155,52 @@ msgid "Maximal date of a membership, after which members must renew it."
msgstr ""
"Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen."
-#: apps/member/models.py:263 apps/member/models.py:319
+#: apps/member/models.py:263
+#, fuzzy
+#| msgid "Validate registration"
+msgid "add to registration form"
+msgstr "Registrierung validieren"
+
+#: apps/member/models.py:268 apps/member/models.py:324
#: apps/note/models/notes.py:176
msgid "club"
msgstr "Club"
-#: apps/member/models.py:264
+#: apps/member/models.py:269
msgid "clubs"
msgstr "Clubs"
-#: apps/member/models.py:330
+#: apps/member/models.py:335
msgid "membership starts on"
msgstr "Mitgliedschaft fängt an"
-#: apps/member/models.py:334
+#: apps/member/models.py:339
msgid "membership ends on"
msgstr "Mitgliedschaft endet am"
-#: apps/member/models.py:343 apps/note/models/transactions.py:385
+#: apps/member/models.py:348 apps/note/models/transactions.py:385
msgid "membership"
msgstr "Mitgliedschaft"
-#: apps/member/models.py:344
+#: apps/member/models.py:349
msgid "memberships"
msgstr "Mitgliedschaften"
-#: apps/member/models.py:348
+#: apps/member/models.py:353
#, python-brace-format
msgid "Membership of {user} for the club {club}"
msgstr "Mitgliedschaft von {user} für das Club {club}"
-#: apps/member/models.py:367
+#: apps/member/models.py:372
#, python-brace-format
msgid "The role {role} does not apply to the club {club}."
msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}."
-#: apps/member/models.py:376 apps/member/views.py:712
+#: apps/member/models.py:381 apps/member/views.py:715
msgid "User is already a member of the club"
msgstr "User ist schon ein Mitglied dieser club"
-#: apps/member/models.py:388 apps/member/views.py:721
+#: apps/member/models.py:393 apps/member/views.py:724
msgid "User is not a member of the parent club"
msgstr "User ist noch nicht Mitglied des Urclubs"
@@ -1013,7 +1317,7 @@ msgstr ""
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287
-#: apps/member/views.py:517
+#: apps/member/views.py:520
msgid "Note aliases"
msgstr "Note Aliases"
@@ -1169,11 +1473,11 @@ msgstr ""
msgid "Show my applications"
msgstr ""
-#: apps/member/templates/member/picture_update.html:35
+#: apps/member/templates/member/picture_update.html:38
msgid "Nevermind"
msgstr "Vergessen"
-#: apps/member/templates/member/picture_update.html:36
+#: apps/member/templates/member/picture_update.html:39
msgid "Crop and upload"
msgstr "Beschneiden und hochladen"
@@ -1218,7 +1522,7 @@ msgstr "Speichern"
msgid "Registrations"
msgstr "Anmeldung"
-#: apps/member/views.py:72 apps/registration/forms.py:24
+#: apps/member/views.py:72 apps/registration/forms.py:23
msgid "This address must be valid."
msgstr "Diese Adresse muss gültig sein."
@@ -1238,31 +1542,31 @@ msgstr ""
msgid "Update note picture"
msgstr "Notebild ändern"
-#: apps/member/views.py:354
+#: apps/member/views.py:357
msgid "Manage auth token"
msgstr "Auth token bearbeiten"
-#: apps/member/views.py:381
+#: apps/member/views.py:384
msgid "Create new club"
msgstr "Neue Club"
-#: apps/member/views.py:400
+#: apps/member/views.py:403
msgid "Search club"
msgstr "Club finden"
-#: apps/member/views.py:433
+#: apps/member/views.py:436
msgid "Club detail"
msgstr "Club Details"
-#: apps/member/views.py:540
+#: apps/member/views.py:543
msgid "Update club"
msgstr "Club bearbeiten"
-#: apps/member/views.py:574
+#: apps/member/views.py:577
msgid "Add new member to the club"
msgstr "Neue Mitglieder"
-#: apps/member/views.py:703 apps/wei/views.py:973
+#: apps/member/views.py:706 apps/wei/views.py:973
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@@ -1270,19 +1574,19 @@ msgstr ""
"Diese User hat nicht genug Geld um Mitglied zu werden, und darf nich im Rot "
"sein."
-#: apps/member/views.py:725
+#: apps/member/views.py:728
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen."
-#: apps/member/views.py:730
+#: apps/member/views.py:733
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen."
-#: apps/member/views.py:880
+#: apps/member/views.py:883
msgid "Manage roles of an user in the club"
msgstr "Rollen in diesen Club bearbeiten"
-#: apps/member/views.py:905
+#: apps/member/views.py:908
msgid "Members of the club"
msgstr "Mitlglieder dieses Club"
@@ -1747,11 +2051,6 @@ msgstr "Aktion"
msgid "Amount"
msgstr "Anzahl"
-#: apps/note/templates/note/transaction_form.html:132
-#: apps/treasury/models.py:60
-msgid "Name"
-msgstr "Name"
-
#: apps/note/templates/note/transaction_form.html:177
msgid "Select emitter"
msgstr "Sender auswählen"
@@ -2029,7 +2328,7 @@ msgstr ""
"diesen Parametern zu erstellen. Bitte korrigieren Sie Ihre Daten und "
"versuchen Sie es erneut."
-#: apps/permission/views.py:112 note_kfet/templates/base.html:108
+#: apps/permission/views.py:112 note_kfet/templates/base.html:114
msgid "Rights"
msgstr "Rechten"
@@ -2041,15 +2340,15 @@ msgstr "Alle Rechten"
msgid "registration"
msgstr "Anmeldung"
-#: apps/registration/forms.py:40
+#: apps/registration/forms.py:39
msgid "This email address is already used."
msgstr "Diese email adresse ist schon benutzt."
-#: apps/registration/forms.py:60
+#: apps/registration/forms.py:59
msgid "Register to the WEI"
msgstr "Zu WEI anmelden"
-#: apps/registration/forms.py:62
+#: apps/registration/forms.py:61
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@@ -2058,11 +2357,11 @@ msgstr ""
"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet "
"registrieren."
-#: apps/registration/forms.py:107
+#: apps/registration/forms.py:106
msgid "Join BDE Club"
msgstr "BDE Mitglieder werden"
-#: apps/registration/forms.py:114
+#: apps/registration/forms.py:113
msgid "Join Kfet Club"
msgstr "Kfet Mitglieder werden"
@@ -2173,58 +2472,64 @@ msgstr "Danke"
msgid "The Note Kfet team."
msgstr "Die NoteKfet Team."
-#: apps/registration/views.py:41
+#: apps/registration/views.py:42
msgid "Register new user"
msgstr "Neuen User registrieren"
-#: apps/registration/views.py:99
+#: apps/registration/views.py:100
msgid "Email validation"
msgstr "Email validierung"
-#: apps/registration/views.py:101
+#: apps/registration/views.py:102
msgid "Validate email"
msgstr "Email validieren"
-#: apps/registration/views.py:145
+#: apps/registration/views.py:146
msgid "Email validation unsuccessful"
msgstr "Email validierung unerfolgreich"
-#: apps/registration/views.py:156
+#: apps/registration/views.py:157
msgid "Email validation email sent"
msgstr "Validierungsemail wurde gesendet"
-#: apps/registration/views.py:164
+#: apps/registration/views.py:165
msgid "Resend email validation link"
msgstr "E-Mail-Validierungslink erneut senden"
-#: apps/registration/views.py:182
+#: apps/registration/views.py:183
msgid "Pre-registered users list"
msgstr "Vorregistrierte Userliste"
-#: apps/registration/views.py:206
+#: apps/registration/views.py:207
msgid "Unregistered users"
msgstr "Unregistrierte Users"
-#: apps/registration/views.py:219
+#: apps/registration/views.py:220
msgid "Registration detail"
msgstr "Registrierung Detailen"
-#: apps/registration/views.py:293
+#: apps/registration/views.py:256
+#, fuzzy, python-format
+#| msgid "Note of %(club)s club"
+msgid "Join %(club)s Club"
+msgstr "%(club)s Note"
+
+#: apps/registration/views.py:299
msgid "You must join the BDE."
msgstr "Sie müssen die BDE beitreten."
-#: apps/registration/views.py:323
+#: apps/registration/views.py:330
msgid ""
"The entered amount is not enough for the memberships, should be at least {}"
msgstr ""
"Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte "
"mindestens {} betragen"
-#: apps/registration/views.py:417
+#: apps/registration/views.py:425
msgid "Invalidate pre-registration"
msgstr "Ungültige Vorregistrierung"
-#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
+#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102
msgid "Treasury"
msgstr "Quaestor"
@@ -2650,7 +2955,7 @@ msgstr "Krediten von der Société générale handeln"
#: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38
#: apps/wei/models.py:62 apps/wei/models.py:178
-#: note_kfet/templates/base.html:102
+#: note_kfet/templates/base.html:108
msgid "WEI"
msgstr "WEI"
@@ -3284,19 +3589,19 @@ msgstr ""
msgid "Attribute bus"
msgstr ""
-#: note_kfet/settings/base.py:172
+#: note_kfet/settings/base.py:173
msgid "German"
msgstr "Deutsch"
-#: note_kfet/settings/base.py:173
+#: note_kfet/settings/base.py:174
msgid "English"
msgstr "English"
-#: note_kfet/settings/base.py:174
+#: note_kfet/settings/base.py:175
msgid "Spanish"
msgstr "Spanisch"
-#: note_kfet/settings/base.py:175
+#: note_kfet/settings/base.py:176
msgid "French"
msgstr "Französich"
@@ -3360,34 +3665,38 @@ msgstr "Reset"
msgid "The ENS Paris-Saclay BDE note."
msgstr "Die BDE ENS-Paris-Saclay Note."
-#: note_kfet/templates/base.html:78
+#: note_kfet/templates/base.html:72
+msgid "Food"
+msgstr ""
+
+#: note_kfet/templates/base.html:84
msgid "Users"
msgstr "Users"
-#: note_kfet/templates/base.html:84
+#: note_kfet/templates/base.html:90
msgid "Clubs"
msgstr "Clubs"
-#: note_kfet/templates/base.html:113
+#: note_kfet/templates/base.html:119
msgid "Admin"
msgstr "Admin"
-#: note_kfet/templates/base.html:127
+#: note_kfet/templates/base.html:133
msgid "My account"
msgstr "Mein Konto"
-#: note_kfet/templates/base.html:130
+#: note_kfet/templates/base.html:136
msgid "Log out"
msgstr "Abmelden"
-#: note_kfet/templates/base.html:138
+#: note_kfet/templates/base.html:144
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Registrieren"
-#: note_kfet/templates/base.html:145
+#: note_kfet/templates/base.html:151
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@@ -3395,13 +3704,13 @@ msgstr "Registrieren"
msgid "Log in"
msgstr "Anmelden"
-#: note_kfet/templates/base.html:159
+#: note_kfet/templates/base.html:165
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
msgstr ""
-#: note_kfet/templates/base.html:165
+#: note_kfet/templates/base.html:171
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@@ -3409,7 +3718,7 @@ msgstr ""
"Ihre E-Mail-Adresse ist nicht validiert. Bitte überprüfen Sie Ihren "
"Posteingang und klicken Sie auf den Validierungslink."
-#: note_kfet/templates/base.html:171
+#: note_kfet/templates/base.html:177
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
@@ -3418,15 +3727,19 @@ msgid ""
"creation."
msgstr ""
-#: note_kfet/templates/base.html:194
+#: note_kfet/templates/base.html:200
msgid "Contact us"
msgstr "Kontakt"
-#: note_kfet/templates/base.html:196
+#: note_kfet/templates/base.html:202
msgid "Technical Support"
msgstr ""
-#: note_kfet/templates/base.html:198
+#: note_kfet/templates/base.html:204
+msgid "Charte Info (FR)"
+msgstr ""
+
+#: note_kfet/templates/base.html:206
msgid "FAQ (FR)"
msgstr "FAQ (FR)"
@@ -3677,6 +3990,298 @@ msgstr ""
"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den "
"Sie erhalten haben."
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid color."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid value."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Invitation"
+#~ msgid "Syndication"
+#~ msgstr "Einladung"
+
+#, fuzzy
+#~| msgid "There is no results."
+#~ msgid "That page contains no results"
+#~ msgstr "Es gibt keine Ergebnisse."
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid URL."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid integer."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid email address."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 address."
+#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv6 address."
+#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 or IPv6 address."
+#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt."
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a number."
+#~ msgstr "Telefonnummer"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "and"
+#~ msgstr "hinzufügen"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_labels)s already exists."
+#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden"
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be null."
+#~ msgstr "Dieses Bild kann nicht geladen werden."
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be blank."
+#~ msgstr "Dieses Bild kann nicht geladen werden."
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_label)s already exists."
+#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Decimal number"
+#~ msgstr "Telefonnummer"
+
+#, fuzzy
+#~| msgid "action"
+#~ msgid "Duration"
+#~ msgstr "Aktion"
+
+#, fuzzy
+#~| msgid "address"
+#~ msgid "Email address"
+#~ msgstr "Adresse"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Floating point number"
+#~ msgstr "Telefonnummer"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IPv4 address"
+#~ msgstr "IP Adresse"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IP address"
+#~ msgstr "IP Adresse"
+
+#, fuzzy
+#~| msgid "Invoice identifier"
+#~ msgid "Universally unique identifier"
+#~ msgstr "Rechnungskennung"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model)s instance with %(field)s %(value)r does not exist."
+#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a whole number."
+#~ msgstr "Telefonnummer"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid date."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid time."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid date/time."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid duration."
+#~ msgstr "Email validierung"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a complete value."
+#~ msgstr "Telefonnummer"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid UUID."
+#~ msgstr "Email validierung"
+
+#, fuzzy, python-format
+#~| msgid "This activity is not validated yet."
+#~ msgid "\"%(pk)s\" is not a valid value."
+#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt."
+
+#, fuzzy
+#~| msgid "Current activity"
+#~ msgid "Currently"
+#~ msgstr "Aktuelle Veranstaltung"
+
+#, fuzzy
+#~| msgid "change"
+#~ msgid "Change"
+#~ msgstr "bearbeiten"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgid "March"
+#~ msgstr "WEI finden"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "September"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "November"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "December"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "jan"
+#~ msgstr "hinzufügen"
+
+#, fuzzy
+#~| msgid "fee"
+#~ msgid "feb"
+#~ msgstr "Preis"
+
+#, fuzzy
+#~| msgid "product"
+#~ msgid "oct"
+#~ msgstr "Produkt"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgctxt "abbrev. month"
+#~ msgid "March"
+#~ msgstr "WEI finden"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgctxt "alt. month"
+#~ msgid "March"
+#~ msgstr "WEI finden"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "September"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "November"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "December"
+#~ msgstr "Mitglied"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "This is not a valid IPv6 address."
+#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt."
+
+#, fuzzy, python-format
+#~| msgid "year"
+#~ msgid "%d year"
+#~ msgid_plural "%d years"
+#~ msgstr[0] "Jahr"
+#~ msgstr[1] "Jahr"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No year specified"
+#~ msgstr "Kein Grund gegeben"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No month specified"
+#~ msgstr "Kein Grund gegeben"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No day specified"
+#~ msgstr "Kein Grund gegeben"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No week specified"
+#~ msgstr "Kein Grund gegeben"
+
+#, fuzzy
+#~| msgid "Reset my password"
+#~ msgid "Resource owner password-based"
+#~ msgstr "Mein Passwort zurücksetzen"
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token is invalid."
+#~ msgstr "Diese Adresse muss gültig sein."
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token has expired."
+#~ msgstr "Diese Adresse muss gültig sein."
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token is valid but does not have enough scope."
+#~ msgstr "Diese Adresse muss gültig sein."
+
+#, fuzzy
+#~| msgid "WEI registration"
+#~ msgid "In preparation"
+#~ msgstr "WEI Registrierung"
+
#~ msgid "Join BDA Club"
#~ msgstr "BDA Mitglieder werden"
diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po
index 73702a57..31c5e750 100644
--- a/locale/es/LC_MESSAGES/django.po
+++ b/locale/es/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-22 00:33+0100\n"
+"POT-Creation-Date: 2024-08-17 11:57+0200\n"
"PO-Revision-Date: 2022-04-11 23:12+0200\n"
"Last-Translator: bleizi \n"
"Language-Team: \n"
@@ -18,8 +18,8 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.0.1\n"
-#: apps/activity/apps.py:10 apps/activity/models.py:127
-#: apps/activity/models.py:167
+#: apps/activity/apps.py:10 apps/activity/models.py:129
+#: apps/activity/models.py:169
msgid "activity"
msgstr "actividad"
@@ -27,32 +27,32 @@ msgstr "actividad"
msgid "The note of this club is inactive."
msgstr "La note del club está inactiva."
-#: apps/activity/forms.py:41 apps/activity/models.py:140
+#: apps/activity/forms.py:41 apps/activity/models.py:142
msgid "The end date must be after the start date."
msgstr "La fecha final tiene que ser después de la fecha de inicio."
-#: apps/activity/forms.py:82 apps/activity/models.py:269
+#: apps/activity/forms.py:82 apps/activity/models.py:271
msgid "You can't invite someone once the activity is started."
msgstr "No se puede invitar a alguien una vez que arrancó la actividad."
-#: apps/activity/forms.py:85 apps/activity/models.py:272
+#: apps/activity/forms.py:85 apps/activity/models.py:274
msgid "This activity is not validated yet."
msgstr "Esta actividad no fue validada por ahora."
-#: apps/activity/forms.py:95 apps/activity/models.py:280
+#: apps/activity/forms.py:95 apps/activity/models.py:282
msgid "This person has been already invited 5 times this year."
msgstr "Esta persona ya fue invitada 5 veces este año."
-#: apps/activity/forms.py:99 apps/activity/models.py:284
+#: apps/activity/forms.py:99 apps/activity/models.py:286
msgid "This person is already invited."
msgstr "Esta persona ya está invitada."
-#: apps/activity/forms.py:103 apps/activity/models.py:288
+#: apps/activity/forms.py:103 apps/activity/models.py:290
msgid "You can't invite more than 3 people to this activity."
msgstr "Usted no puede invitar más de 3 persona a esta actividad."
-#: apps/activity/models.py:28 apps/activity/models.py:63
-#: apps/member/models.py:203
+#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42
+#: apps/food/models.py:56 apps/member/models.py:203
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@@ -98,121 +98,121 @@ msgstr "tipos de actividad"
msgid "description"
msgstr "descripción"
-#: apps/activity/models.py:72
+#: apps/activity/models.py:74
msgid "location"
msgstr "ubicación"
-#: apps/activity/models.py:76
+#: apps/activity/models.py:78
msgid "Place where the activity is organized, eg. Kfet."
msgstr "Lugar donde se organiza la actividad, por ejemplo la Kfet."
-#: apps/activity/models.py:83
+#: apps/activity/models.py:85
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:207 apps/note/models/transactions.py:67
#: apps/permission/models.py:163
msgid "type"
msgstr "tipo"
-#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:313
+#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318
#: apps/note/models/notes.py:148 apps/treasury/models.py:293
#: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
msgstr "usuario"
-#: apps/activity/models.py:96
+#: apps/activity/models.py:98
#: apps/activity/templates/activity/includes/activity_info.html:36
msgid "organizer"
msgstr "organizador"
-#: apps/activity/models.py:97
+#: apps/activity/models.py:99
msgid "Club that organizes the activity. The entry fees will go to this club."
msgstr ""
"El club que organiza la actividad. Los pagos de entrada serán dados a este "
"club."
-#: apps/activity/models.py:104
+#: apps/activity/models.py:106
#: apps/activity/templates/activity/includes/activity_info.html:39
msgid "attendees club"
msgstr "club esperado"
-#: apps/activity/models.py:105
+#: apps/activity/models.py:107
msgid "Club that is authorized to join the activity. Mostly the Kfet club."
msgstr "Club permitido unirse a la actividad. Generalmente el club Kfet."
-#: apps/activity/models.py:109
+#: apps/activity/models.py:111
#: apps/activity/templates/activity/includes/activity_info.html:25
msgid "start date"
msgstr "fecha de inicio"
-#: apps/activity/models.py:113
+#: apps/activity/models.py:115
#: apps/activity/templates/activity/includes/activity_info.html:28
msgid "end date"
msgstr "fecha de fin"
-#: apps/activity/models.py:118
+#: apps/activity/models.py:120
#: apps/activity/templates/activity/includes/activity_info.html:50
#: apps/note/models/transactions.py:149
msgid "valid"
msgstr "válido"
-#: apps/activity/models.py:123
+#: apps/activity/models.py:125
#: apps/activity/templates/activity/includes/activity_info.html:65
msgid "open"
msgstr "abierto"
-#: apps/activity/models.py:128
+#: apps/activity/models.py:130
msgid "activities"
msgstr "actividades"
-#: apps/activity/models.py:172
+#: apps/activity/models.py:174
msgid "entry time"
msgstr "hora de entrada"
-#: apps/activity/models.py:178 apps/note/apps.py:14
+#: apps/activity/models.py:180 apps/note/apps.py:14
#: apps/note/models/notes.py:77
msgid "note"
msgstr "note"
-#: apps/activity/models.py:189
+#: apps/activity/models.py:191
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entry"
msgstr "entrada"
-#: apps/activity/models.py:190
+#: apps/activity/models.py:192
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entries"
msgstr "entradas"
-#: apps/activity/models.py:193
+#: apps/activity/models.py:195
#, python-brace-format
msgid "Entry for {guest}, invited by {note} to the activity {activity}"
msgstr "Entrada para {guest}, invitado por {note} en la actividad {activity}"
-#: apps/activity/models.py:195
+#: apps/activity/models.py:197
#, python-brace-format
msgid "Entry for {note} to the activity {activity}"
msgstr "Entrada para {note} en la actividad {activity}"
-#: apps/activity/models.py:202
+#: apps/activity/models.py:204
msgid "Already entered on "
msgstr "Entrado ya el "
-#: apps/activity/models.py:202 apps/activity/tables.py:56
+#: apps/activity/models.py:204 apps/activity/tables.py:56
msgid "{:%Y-%m-%d %H:%M:%S}"
msgstr "{:%d/%m/%Y %H:%M:%S}"
-#: apps/activity/models.py:210
+#: apps/activity/models.py:212
msgid "The balance is negative."
msgstr "El saldo es negativo."
-#: apps/activity/models.py:240
+#: apps/activity/models.py:242
#: apps/treasury/templates/treasury/sogecredit_detail.html:14
#: apps/wei/templates/wei/attribute_bus_1A.html:16
msgid "last name"
msgstr "apellido"
-#: apps/activity/models.py:245
+#: apps/activity/models.py:247
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/treasury/templates/treasury/sogecredit_detail.html:17
@@ -221,19 +221,19 @@ msgstr "apellido"
msgid "first name"
msgstr "nombre"
-#: apps/activity/models.py:252
+#: apps/activity/models.py:254
msgid "inviter"
msgstr "huésped"
-#: apps/activity/models.py:256
+#: apps/activity/models.py:258
msgid "guest"
msgstr "invitado"
-#: apps/activity/models.py:257
+#: apps/activity/models.py:259
msgid "guests"
msgstr "invitados"
-#: apps/activity/models.py:310
+#: apps/activity/models.py:312
msgid "Invitation"
msgstr "Invitación"
@@ -245,7 +245,9 @@ msgstr "La actividad está actualmente abierta."
msgid "The validation of the activity is pending."
msgstr "La validación de esta actividad es pendiente."
-#: apps/activity/tables.py:43 apps/treasury/tables.py:107
+#: apps/activity/tables.py:43
+#: apps/member/templates/member/picture_update.html:18
+#: apps/treasury/tables.py:107
msgid "Remove"
msgstr "Quitar"
@@ -261,15 +263,15 @@ msgstr "quitar"
msgid "Type"
msgstr "Tipo"
-#: apps/activity/tables.py:84 apps/member/forms.py:193
-#: apps/registration/forms.py:92 apps/treasury/forms.py:131
+#: apps/activity/tables.py:84 apps/member/forms.py:196
+#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Apellido"
-#: apps/activity/tables.py:86 apps/member/forms.py:198
+#: apps/activity/tables.py:86 apps/member/forms.py:201
#: apps/note/templates/note/transaction_form.html:138
-#: apps/registration/forms.py:97 apps/treasury/forms.py:133
+#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Nombre"
@@ -294,7 +296,7 @@ msgstr "Invitados suprimidos"
#: apps/note/models/transactions.py:261
#: apps/note/templates/note/transaction_form.html:17
#: apps/note/templates/note/transaction_form.html:152
-#: note_kfet/templates/base.html:72
+#: note_kfet/templates/base.html:78
msgid "Transfer"
msgstr "Transferencia"
@@ -329,6 +331,10 @@ msgid "Entry done!"
msgstr "Entrada echa !"
#: apps/activity/templates/activity/activity_form.html:16
+#: apps/food/templates/food/add_ingredient_form.html:16
+#: apps/food/templates/food/basicfood_form.html:16
+#: apps/food/templates/food/create_qrcode_form.html:19
+#: apps/food/templates/food/transformedfood_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
#: apps/note/templates/note/transactiontemplate_form.html:18
@@ -394,41 +400,41 @@ msgstr "modificar"
msgid "Invite"
msgstr "Invitar"
-#: apps/activity/views.py:36
+#: apps/activity/views.py:37
msgid "Create new activity"
msgstr "Crear una nueva actividad"
-#: apps/activity/views.py:67 note_kfet/templates/base.html:90
+#: apps/activity/views.py:67 note_kfet/templates/base.html:96
msgid "Activities"
msgstr "Actividades"
-#: apps/activity/views.py:93
+#: apps/activity/views.py:108
msgid "Activity detail"
msgstr "Detalles de la actividad"
-#: apps/activity/views.py:113
+#: apps/activity/views.py:128
msgid "Update activity"
msgstr "Modificar la actividad"
-#: apps/activity/views.py:140
+#: apps/activity/views.py:155
msgid "Invite guest to the activity \"{}\""
msgstr "Invitar alguien para la actividad \"{}\""
-#: apps/activity/views.py:178
+#: apps/activity/views.py:193
msgid "You are not allowed to display the entry interface for this activity."
msgstr ""
"Usted no tiene derecho a mostrar la interfaz de las entradas para esta "
"actividad."
-#: apps/activity/views.py:181
+#: apps/activity/views.py:196
msgid "This activity does not support activity entries."
msgstr "Esta actividad no necesita entradas."
-#: apps/activity/views.py:184
+#: apps/activity/views.py:199
msgid "This activity is closed."
msgstr "Esta actividad esta cerrada."
-#: apps/activity/views.py:280
+#: apps/activity/views.py:295
msgid "Entry for activity \"{}\""
msgstr "Entradas para la actividad \"{}\""
@@ -436,6 +442,298 @@ msgstr "Entradas para la actividad \"{}\""
msgid "API"
msgstr "API"
+#: apps/food/apps.py:11 apps/food/models.py:105
+msgid "food"
+msgstr ""
+
+#: apps/food/forms.py:32
+msgid "Fully used"
+msgstr ""
+
+#: apps/food/forms.py:50
+msgid "Pasta METRO 5kg"
+msgstr ""
+
+#: apps/food/forms.py:96
+msgid "Lasagna"
+msgstr ""
+
+#: apps/food/models.py:18
+#, fuzzy
+#| msgid "phone number"
+msgid "QR-code number"
+msgstr "número de teléfono"
+
+#: apps/food/models.py:26
+msgid "food container"
+msgstr ""
+
+#: apps/food/models.py:30
+msgid "QR-code"
+msgstr ""
+
+#: apps/food/models.py:31
+msgid "QR-codes"
+msgstr ""
+
+#: apps/food/models.py:34
+#, python-brace-format
+msgid "QR-code number {qr_code_number}"
+msgstr ""
+
+#: apps/food/models.py:47
+msgid "Allergen"
+msgstr ""
+
+#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17
+#: apps/food/templates/food/transformedfood_detail.html:20
+msgid "Allergens"
+msgstr ""
+
+#: apps/food/models.py:64
+msgid "owner"
+msgstr ""
+
+#: apps/food/models.py:70
+msgid "allergen"
+msgstr ""
+
+#: apps/food/models.py:74
+#, fuzzy
+#| msgid "birth date"
+msgid "expiry date"
+msgstr "fecha de nacimiento"
+
+#: apps/food/models.py:80
+msgid "was eaten"
+msgstr ""
+
+#: apps/food/models.py:89
+msgid "is ready"
+msgstr ""
+
+#: apps/food/models.py:94
+#, fuzzy
+#| msgid "active"
+msgid "is active"
+msgstr "activo"
+
+#: apps/food/models.py:106
+msgid "foods"
+msgstr ""
+
+#: apps/food/models.py:122
+#, fuzzy
+#| msgid "invalidate"
+msgid "arrival date"
+msgstr "invalidar"
+
+#: apps/food/models.py:152
+msgid "Basic food"
+msgstr ""
+
+#: apps/food/models.py:153
+msgid "Basic foods"
+msgstr ""
+
+#: apps/food/models.py:161
+#, fuzzy
+#| msgid "created at"
+msgid "creation date"
+msgstr "creada el"
+
+#: apps/food/models.py:169
+msgid "transformed ingredient"
+msgstr ""
+
+#: apps/food/models.py:174
+msgid "shelf life"
+msgstr ""
+
+#: apps/food/models.py:225 apps/food/views.py:365
+#, fuzzy
+#| msgid "Transfer money"
+msgid "Transformed food"
+msgstr "Transferir dinero"
+
+#: apps/food/models.py:226
+msgid "Transformed foods"
+msgstr ""
+
+#: apps/food/templates/food/basicfood_detail.html:14
+#: apps/food/templates/food/qrcode_detail.html:15
+#: apps/food/templates/food/transformedfood_detail.html:14
+#, fuzzy
+#| msgid "Owned"
+msgid "Owner"
+msgstr "Tenido"
+
+#: apps/food/templates/food/basicfood_detail.html:15
+#, fuzzy
+#| msgid "invalidate"
+msgid "Arrival date"
+msgstr "invalidar"
+
+#: apps/food/templates/food/basicfood_detail.html:16
+#: apps/food/templates/food/qrcode_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:19
+#, fuzzy
+#| msgid "birth date"
+msgid "Expiry date"
+msgstr "fecha de nacimiento"
+
+#: apps/food/templates/food/basicfood_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:36
+#, fuzzy
+#| msgid "active"
+msgid "Active"
+msgstr "activo"
+
+#: apps/food/templates/food/basicfood_detail.html:25
+#: apps/food/templates/food/transformedfood_detail.html:37
+msgid "Eaten"
+msgstr ""
+
+#: apps/food/templates/food/basicfood_detail.html:28
+#: apps/food/templates/food/qrcode_detail.html:20
+#: apps/food/templates/food/qrcode_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:41
+#, fuzzy
+#| msgid "Update bus"
+msgid "Update"
+msgstr "Modificar el bus"
+
+#: apps/food/templates/food/basicfood_detail.html:32
+#: apps/food/templates/food/qrcode_detail.html:34
+#: apps/food/templates/food/transformedfood_detail.html:46
+#, fuzzy
+#| msgid "Add team"
+msgid "Add to a meal"
+msgstr "Añadir un equipo"
+
+#: apps/food/templates/food/create_qrcode_form.html:14
+#, fuzzy
+#| msgid "Transfer money"
+msgid "New basic food"
+msgstr "Transferir dinero"
+
+#: apps/food/templates/food/qrcode_detail.html:10
+#, fuzzy
+#| msgid "phone number"
+msgid "number"
+msgstr "número de teléfono"
+
+#: apps/food/templates/food/qrcode_detail.html:14
+#: apps/note/templates/note/transaction_form.html:132
+#: apps/treasury/models.py:60
+msgid "Name"
+msgstr "Nombre"
+
+#: apps/food/templates/food/qrcode_detail.html:29
+#, fuzzy
+#| msgid "Profile detail"
+msgid "View details"
+msgstr "Detalles del usuario"
+
+#: apps/food/templates/food/transformedfood_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:35
+msgid "Ready"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_detail.html:18
+#, fuzzy
+#| msgid "created at"
+msgid "Creation date"
+msgstr "creada el"
+
+#: apps/food/templates/food/transformedfood_detail.html:27
+msgid "Ingredients"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_detail.html:34
+msgid "Shelf life"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:11
+msgid "Meal served"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:16
+#, fuzzy
+#| msgid "New user"
+msgid "New meal"
+msgstr "Nuevo usuario"
+
+#: apps/food/templates/food/transformedfood_list.html:25
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no meal served."
+msgstr "No hay resultado."
+
+#: apps/food/templates/food/transformedfood_list.html:33
+msgid "Open"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:40
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no free meal."
+msgstr "No hay resultado."
+
+#: apps/food/templates/food/transformedfood_list.html:48
+msgid "All meals"
+msgstr ""
+
+#: apps/food/templates/food/transformedfood_list.html:55
+#, fuzzy
+#| msgid "There is no results."
+msgid "There is no meal."
+msgstr "No hay resultado."
+
+#: apps/food/views.py:28
+msgid "Add the ingredient"
+msgstr ""
+
+#: apps/food/views.py:42
+#, fuzzy
+#| msgid "This credit is already validated."
+msgid "The product is already prepared"
+msgstr "Este crédito ya fue validado."
+
+#: apps/food/views.py:70
+#, fuzzy
+#| msgid "Update an invoice"
+msgid "Update an aliment"
+msgstr "Modificar una factura"
+
+#: apps/food/views.py:97
+#, fuzzy
+#| msgid "WEI Detail"
+msgid "Details of:"
+msgstr "Detalles del WEI"
+
+#: apps/food/views.py:121
+msgid "Add a new basic food with QRCode"
+msgstr ""
+
+#: apps/food/views.py:185
+msgid "Add a new QRCode"
+msgstr ""
+
+#: apps/food/views.py:235
+msgid "QRCode"
+msgstr ""
+
+#: apps/food/views.py:271
+msgid "Add a new meal"
+msgstr ""
+
+#: apps/food/views.py:337
+#, fuzzy
+#| msgid "Update team"
+msgid "Update a meal"
+msgstr "Modificar el equipo"
+
#: apps/logs/apps.py:11
msgid "Logs"
msgstr "Logs"
@@ -505,11 +803,11 @@ msgstr "pago de afiliación (estudiantes pagados)"
msgid "membership fee (unpaid students)"
msgstr "pago de afiliación (estudiantes no pagados)"
-#: apps/member/admin.py:65 apps/member/models.py:325
+#: apps/member/admin.py:65 apps/member/models.py:330
msgid "roles"
msgstr "papel"
-#: apps/member/admin.py:66 apps/member/models.py:339
+#: apps/member/admin.py:66 apps/member/models.py:344
msgid "fee"
msgstr "pago"
@@ -561,49 +859,49 @@ msgstr "Tamaño máximo : 2Mo"
msgid "This image cannot be loaded."
msgstr "Esta imagen no puede ser cargada."
-#: apps/member/forms.py:148 apps/member/views.py:102
-#: apps/registration/forms.py:34 apps/registration/views.py:266
+#: apps/member/forms.py:151 apps/member/views.py:102
+#: apps/registration/forms.py:33 apps/registration/views.py:276
msgid "An alias with a similar name already exists."
msgstr "Un alias similar ya existe."
-#: apps/member/forms.py:172
+#: apps/member/forms.py:175
msgid "Inscription paid by Société Générale"
msgstr "Registración pagadas por Société Générale"
-#: apps/member/forms.py:174
+#: apps/member/forms.py:177
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Marcar esta casilla si Société Générale pagó la registración."
-#: apps/member/forms.py:179 apps/registration/forms.py:79
+#: apps/member/forms.py:182 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Tipo de crédito"
-#: apps/member/forms.py:180 apps/registration/forms.py:80
+#: apps/member/forms.py:183 apps/registration/forms.py:79
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "No crédito"
-#: apps/member/forms.py:182
+#: apps/member/forms.py:185
msgid "You can credit the note of the user."
msgstr "Usted puede acreditar la note del usuario."
-#: apps/member/forms.py:186 apps/registration/forms.py:85
+#: apps/member/forms.py:189 apps/registration/forms.py:84
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Valor del crédito"
-#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144
-#: apps/registration/forms.py:102 apps/treasury/forms.py:135
+#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144
+#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Banco"
-#: apps/member/forms.py:230
+#: apps/member/forms.py:233
msgid "User"
msgstr "Usuario"
-#: apps/member/forms.py:244
+#: apps/member/forms.py:247
msgid "Roles"
msgstr "Papeles"
@@ -850,46 +1148,52 @@ msgstr ""
"Ultima fecha de una afiliación, después de la cual los miembros tienen que "
"prorrogarla."
-#: apps/member/models.py:263 apps/member/models.py:319
+#: apps/member/models.py:263
+#, fuzzy
+#| msgid "Validate registration"
+msgid "add to registration form"
+msgstr "Validar la afiliación"
+
+#: apps/member/models.py:268 apps/member/models.py:324
#: apps/note/models/notes.py:176
msgid "club"
msgstr "club"
-#: apps/member/models.py:264
+#: apps/member/models.py:269
msgid "clubs"
msgstr "clubs"
-#: apps/member/models.py:330
+#: apps/member/models.py:335
msgid "membership starts on"
msgstr "afiliación empezá el"
-#: apps/member/models.py:334
+#: apps/member/models.py:339
msgid "membership ends on"
msgstr "afiliación termina el"
-#: apps/member/models.py:343 apps/note/models/transactions.py:385
+#: apps/member/models.py:348 apps/note/models/transactions.py:385
msgid "membership"
msgstr "afiliación"
-#: apps/member/models.py:344
+#: apps/member/models.py:349
msgid "memberships"
msgstr "afiliaciones"
-#: apps/member/models.py:348
+#: apps/member/models.py:353
#, python-brace-format
msgid "Membership of {user} for the club {club}"
msgstr "Afiliación of {user} for the club {club}"
-#: apps/member/models.py:367
+#: apps/member/models.py:372
#, python-brace-format
msgid "The role {role} does not apply to the club {club}."
msgstr "El papel {role} no se encuentra en el club {club}."
-#: apps/member/models.py:376 apps/member/views.py:712
+#: apps/member/models.py:381 apps/member/views.py:715
msgid "User is already a member of the club"
msgstr "Usuario ya esta un miembro del club"
-#: apps/member/models.py:388 apps/member/views.py:721
+#: apps/member/models.py:393 apps/member/views.py:724
msgid "User is not a member of the parent club"
msgstr "Usuario no es un miembro del club pariente"
@@ -1003,7 +1307,7 @@ msgstr ""
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287
-#: apps/member/views.py:517
+#: apps/member/views.py:520
msgid "Note aliases"
msgstr "Alias de la note"
@@ -1151,11 +1455,11 @@ msgstr "Introspección :"
msgid "Show my applications"
msgstr "Mostrar mis aplicaciones"
-#: apps/member/templates/member/picture_update.html:35
+#: apps/member/templates/member/picture_update.html:38
msgid "Nevermind"
msgstr "No importa"
-#: apps/member/templates/member/picture_update.html:36
+#: apps/member/templates/member/picture_update.html:39
msgid "Crop and upload"
msgstr "Podar y subir"
@@ -1204,7 +1508,7 @@ msgstr "Guardar cambios"
msgid "Registrations"
msgstr "Registraciones"
-#: apps/member/views.py:72 apps/registration/forms.py:24
+#: apps/member/views.py:72 apps/registration/forms.py:23
msgid "This address must be valid."
msgstr "Este correo tiene que ser valido."
@@ -1224,31 +1528,31 @@ msgstr "Amistades de note"
msgid "Update note picture"
msgstr "Modificar la imagen de la note"
-#: apps/member/views.py:354
+#: apps/member/views.py:357
msgid "Manage auth token"
msgstr "Gestionar los token de autentificación"
-#: apps/member/views.py:381
+#: apps/member/views.py:384
msgid "Create new club"
msgstr "Crear un nuevo club"
-#: apps/member/views.py:400
+#: apps/member/views.py:403
msgid "Search club"
msgstr "Buscar un club"
-#: apps/member/views.py:433
+#: apps/member/views.py:436
msgid "Club detail"
msgstr "Detalles del club"
-#: apps/member/views.py:540
+#: apps/member/views.py:543
msgid "Update club"
msgstr "Modificar el club"
-#: apps/member/views.py:574
+#: apps/member/views.py:577
msgid "Add new member to the club"
msgstr "Añadir un nuevo miembro al club"
-#: apps/member/views.py:703 apps/wei/views.py:973
+#: apps/member/views.py:706 apps/wei/views.py:973
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@@ -1256,19 +1560,19 @@ msgstr ""
"Este usuario no tiene suficiente dinero para unirse a este club, y no puede "
"tener un saldo negativo."
-#: apps/member/views.py:725
+#: apps/member/views.py:728
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "La afiliación tiene que empezar después del {:%d-%m-%Y}."
-#: apps/member/views.py:730
+#: apps/member/views.py:733
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "La afiliación tiene que empezar antes del {:%d-%m-%Y}."
-#: apps/member/views.py:880
+#: apps/member/views.py:883
msgid "Manage roles of an user in the club"
msgstr "Gestionar los papeles de un usuario en el club"
-#: apps/member/views.py:905
+#: apps/member/views.py:908
msgid "Members of the club"
msgstr "Miembros del club"
@@ -1731,11 +2035,6 @@ msgstr "Acción"
msgid "Amount"
msgstr "Monto"
-#: apps/note/templates/note/transaction_form.html:132
-#: apps/treasury/models.py:60
-msgid "Name"
-msgstr "Nombre"
-
#: apps/note/templates/note/transaction_form.html:177
msgid "Select emitter"
msgstr "Elegir el remitente"
@@ -2007,7 +2306,7 @@ msgid ""
"with these parameters. Please correct your data and retry."
msgstr ""
-#: apps/permission/views.py:112 note_kfet/templates/base.html:108
+#: apps/permission/views.py:112 note_kfet/templates/base.html:114
msgid "Rights"
msgstr "Permisos"
@@ -2019,15 +2318,15 @@ msgstr "Todos los permisos"
msgid "registration"
msgstr "afiliación"
-#: apps/registration/forms.py:40
+#: apps/registration/forms.py:39
msgid "This email address is already used."
msgstr "Este correo electrónico ya esta utilizado."
-#: apps/registration/forms.py:60
+#: apps/registration/forms.py:59
msgid "Register to the WEI"
msgstr "Registrarse en el WEI"
-#: apps/registration/forms.py:62
+#: apps/registration/forms.py:61
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@@ -2035,11 +2334,11 @@ msgstr ""
"Marcar esta casilla si usted quiere registrarse en el WEI. Si duda, podrá "
"registrarse más tarde, después de validar su cuenta Note Kfet."
-#: apps/registration/forms.py:107
+#: apps/registration/forms.py:106
msgid "Join BDE Club"
msgstr "Afiliarse al club BDE"
-#: apps/registration/forms.py:114
+#: apps/registration/forms.py:113
msgid "Join Kfet Club"
msgstr "Afiliarse al club Kfet"
@@ -2148,58 +2447,64 @@ msgstr "Gracias"
msgid "The Note Kfet team."
msgstr "El equipo Note Kfet."
-#: apps/registration/views.py:41
+#: apps/registration/views.py:42
msgid "Register new user"
msgstr "Registrar un nuevo usuario"
-#: apps/registration/views.py:99
+#: apps/registration/views.py:100
msgid "Email validation"
msgstr "Validación del correo electrónico"
-#: apps/registration/views.py:101
+#: apps/registration/views.py:102
msgid "Validate email"
msgstr "Validar el correo electrónico"
-#: apps/registration/views.py:145
+#: apps/registration/views.py:146
msgid "Email validation unsuccessful"
msgstr "La validación del correo electrónico fracasó"
-#: apps/registration/views.py:156
+#: apps/registration/views.py:157
msgid "Email validation email sent"
msgstr "Correo de validación enviado"
-#: apps/registration/views.py:164
+#: apps/registration/views.py:165
msgid "Resend email validation link"
msgstr "Reenviar el enlace de validación"
-#: apps/registration/views.py:182
+#: apps/registration/views.py:183
msgid "Pre-registered users list"
msgstr "Lista de los usuarios con afiliación pendiente"
-#: apps/registration/views.py:206
+#: apps/registration/views.py:207
msgid "Unregistered users"
msgstr "Usuarios con afiliación pendiente"
-#: apps/registration/views.py:219
+#: apps/registration/views.py:220
msgid "Registration detail"
msgstr "Detalles de la afiliación"
-#: apps/registration/views.py:293
+#: apps/registration/views.py:256
+#, fuzzy, python-format
+#| msgid "Note of %(club)s club"
+msgid "Join %(club)s Club"
+msgstr "Note del club %(club)s"
+
+#: apps/registration/views.py:299
msgid "You must join the BDE."
msgstr "Usted tiene que afiliarse al BDE."
-#: apps/registration/views.py:323
+#: apps/registration/views.py:330
msgid ""
"The entered amount is not enough for the memberships, should be at least {}"
msgstr ""
"El monto dado no es suficiente para las afiliaciones, tiene que ser al menos "
"{}"
-#: apps/registration/views.py:417
+#: apps/registration/views.py:425
msgid "Invalidate pre-registration"
msgstr "Invalidar la afiliación"
-#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
+#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102
msgid "Treasury"
msgstr "Tesorería"
@@ -2616,7 +2921,7 @@ msgstr "Gestionar los créditos de la Société Générale"
#: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38
#: apps/wei/models.py:62 apps/wei/models.py:178
-#: note_kfet/templates/base.html:102
+#: note_kfet/templates/base.html:108
msgid "WEI"
msgstr "WEI"
@@ -3230,19 +3535,19 @@ msgstr "Repartir los primer años en los buses"
msgid "Attribute bus"
msgstr "Repartir en un bus"
-#: note_kfet/settings/base.py:172
+#: note_kfet/settings/base.py:173
msgid "German"
msgstr "Alemán"
-#: note_kfet/settings/base.py:173
+#: note_kfet/settings/base.py:174
msgid "English"
msgstr "Ingles"
-#: note_kfet/settings/base.py:174
+#: note_kfet/settings/base.py:175
msgid "Spanish"
msgstr "Español"
-#: note_kfet/settings/base.py:175
+#: note_kfet/settings/base.py:176
msgid "French"
msgstr "Francés"
@@ -3304,34 +3609,38 @@ msgstr "Reiniciar"
msgid "The ENS Paris-Saclay BDE note."
msgstr "La note del BDE de la ENS Paris-Saclay."
-#: note_kfet/templates/base.html:78
+#: note_kfet/templates/base.html:72
+msgid "Food"
+msgstr ""
+
+#: note_kfet/templates/base.html:84
msgid "Users"
msgstr "Usuarios"
-#: note_kfet/templates/base.html:84
+#: note_kfet/templates/base.html:90
msgid "Clubs"
msgstr "Clubs"
-#: note_kfet/templates/base.html:113
+#: note_kfet/templates/base.html:119
msgid "Admin"
msgstr ""
-#: note_kfet/templates/base.html:127
+#: note_kfet/templates/base.html:133
msgid "My account"
msgstr "Mi cuenta"
-#: note_kfet/templates/base.html:130
+#: note_kfet/templates/base.html:136
msgid "Log out"
msgstr "Desconectarse"
-#: note_kfet/templates/base.html:138
+#: note_kfet/templates/base.html:144
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Registrar"
-#: note_kfet/templates/base.html:145
+#: note_kfet/templates/base.html:151
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@@ -3339,7 +3648,7 @@ msgstr "Registrar"
msgid "Log in"
msgstr "Conectarse"
-#: note_kfet/templates/base.html:159
+#: note_kfet/templates/base.html:165
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
@@ -3347,7 +3656,7 @@ msgstr ""
"Usted ya no está miembro del BDE. Por favor renueva su afiliación si quiere "
"usar la note."
-#: note_kfet/templates/base.html:165
+#: note_kfet/templates/base.html:171
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@@ -3355,7 +3664,7 @@ msgstr ""
"Su correo electrónico no fue validado. Por favor mire en sus correos y haga "
"clic en el enlace de validación."
-#: note_kfet/templates/base.html:171
+#: note_kfet/templates/base.html:177
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
@@ -3368,15 +3677,19 @@ msgstr ""
"pagados. El proceso de convalidación puede durar unos días. Por favor "
"comprueba que fue hasta el final de la creación de la cuenta."
-#: note_kfet/templates/base.html:194
+#: note_kfet/templates/base.html:200
msgid "Contact us"
msgstr "Contactarnos"
-#: note_kfet/templates/base.html:196
+#: note_kfet/templates/base.html:202
msgid "Technical Support"
msgstr "Soporte técnico"
-#: note_kfet/templates/base.html:198
+#: note_kfet/templates/base.html:204
+msgid "Charte Info (FR)"
+msgstr ""
+
+#: note_kfet/templates/base.html:206
msgid "FAQ (FR)"
msgstr "FAQ (FR)"
@@ -3601,6 +3914,323 @@ msgstr ""
"pagar su afiliación. Tambien tiene que validar su correo electronico con el "
"enlace que recibió."
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid color."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid value."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "Invitation"
+#~ msgid "Syndication"
+#~ msgstr "Invitación"
+
+#, fuzzy
+#~| msgid "There is no results."
+#~ msgid "That page contains no results"
+#~ msgstr "No hay resultado."
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid URL."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid integer."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid email address."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 address."
+#~ msgstr "Esta actividad no fue validada por ahora."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv6 address."
+#~ msgstr "Esta actividad no fue validada por ahora."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 or IPv6 address."
+#~ msgstr "Esta actividad no fue validada por ahora."
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a number."
+#~ msgstr "número de teléfono"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "and"
+#~ msgstr "añadir"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_labels)s already exists."
+#~ msgstr "Un plantilla de transacción con un nombre similar ya existe"
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be null."
+#~ msgstr "Esta imagen no puede ser cargada."
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be blank."
+#~ msgstr "Esta imagen no puede ser cargada."
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_label)s already exists."
+#~ msgstr "Un plantilla de transacción con un nombre similar ya existe"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Decimal number"
+#~ msgstr "número de teléfono"
+
+#, fuzzy
+#~| msgid "action"
+#~ msgid "Duration"
+#~ msgstr "acción"
+
+#, fuzzy
+#~| msgid "address"
+#~ msgid "Email address"
+#~ msgstr "dirección"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Floating point number"
+#~ msgstr "número de teléfono"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IPv4 address"
+#~ msgstr "Dirección IP"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IP address"
+#~ msgstr "Dirección IP"
+
+#, fuzzy
+#~| msgid "Invoice identifier"
+#~ msgid "Universally unique identifier"
+#~ msgstr "Numero de factura"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model)s instance with %(field)s %(value)r does not exist."
+#~ msgstr "Un plantilla de transacción con un nombre similar ya existe"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a whole number."
+#~ msgstr "número de teléfono"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid date."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid time."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid date/time."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid duration."
+#~ msgstr "Validación del correo electrónico"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a list of values."
+#~ msgstr "invalidar"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a complete value."
+#~ msgstr "número de teléfono"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid UUID."
+#~ msgstr "invalidar"
+
+#, fuzzy, python-format
+#~| msgid "This activity is not validated yet."
+#~ msgid "\"%(pk)s\" is not a valid value."
+#~ msgstr "Esta actividad no fue validada por ahora."
+
+#, fuzzy
+#~| msgid "Current activity"
+#~ msgid "Currently"
+#~ msgstr "Actividad actual"
+
+#, fuzzy
+#~| msgid "change"
+#~ msgid "Change"
+#~ msgstr "cambiar"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgid "March"
+#~ msgstr "Buscar un WEI"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "September"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "November"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "December"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "jan"
+#~ msgstr "añadir"
+
+#, fuzzy
+#~| msgid "fee"
+#~ msgid "feb"
+#~ msgstr "pago"
+
+#, fuzzy
+#~| msgid "product"
+#~ msgid "oct"
+#~ msgstr "producto"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgctxt "abbrev. month"
+#~ msgid "March"
+#~ msgstr "Buscar un WEI"
+
+#, fuzzy
+#~| msgid "Search WEI"
+#~ msgctxt "alt. month"
+#~ msgid "March"
+#~ msgstr "Buscar un WEI"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "September"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "November"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "December"
+#~ msgstr "miembro"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "This is not a valid IPv6 address."
+#~ msgstr "Esta actividad no fue validada por ahora."
+
+#, fuzzy, python-format
+#~| msgid "year"
+#~ msgid "%d year"
+#~ msgid_plural "%d years"
+#~ msgstr[0] "año"
+#~ msgstr[1] "año"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No year specified"
+#~ msgstr "Ningún motivo dado"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No month specified"
+#~ msgstr "Ningún motivo dado"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No day specified"
+#~ msgstr "Ningún motivo dado"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No week specified"
+#~ msgstr "Ningún motivo dado"
+
+#, fuzzy
+#~| msgid "Client secret"
+#~ msgid "Confidential"
+#~ msgstr "Secreto cliente"
+
+#, fuzzy
+#~| msgid "Authorization:"
+#~ msgid "Authorization code"
+#~ msgstr "Autorizaciones :"
+
+#, fuzzy
+#~| msgid "Reset my password"
+#~ msgid "Resource owner password-based"
+#~ msgstr "Reiniciar mi contraseña"
+
+#, fuzzy
+#~| msgid "Client secret"
+#~ msgid "Client credentials"
+#~ msgstr "Secreto cliente"
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token is invalid."
+#~ msgstr "Este correo tiene que ser valido."
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token has expired."
+#~ msgstr "Este correo tiene que ser valido."
+
+#, fuzzy
+#~| msgid "The user does not have enough money."
+#~ msgid "The access token is valid but does not have enough scope."
+#~ msgstr "El usuario no tiene suficientemente dinero."
+
+#, fuzzy
+#~| msgid "Application requires following permissions:"
+#~ msgid "Application requires following permissions"
+#~ msgstr "La aplicación necesita los derechos siguientes :"
+
+#, fuzzy
+#~| msgid "WEI registration"
+#~ msgid "In preparation"
+#~ msgstr "Apuntación al WEI"
+
#~ msgid "Join BDA Club"
#~ msgstr "Afiliarse al club BDA"
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 50f83a93..e3edfeea 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-22 00:33+0100\n"
+"POT-Creation-Date: 2024-08-17 11:57+0200\n"
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
"Last-Translator: bleizi \n"
"Language-Team: French \n"
@@ -28,33 +28,33 @@ msgstr "activité"
msgid "The note of this club is inactive."
msgstr "La note du club est inactive."
-#: apps/activity/forms.py:41 apps/activity/models.py:140
+#: apps/activity/forms.py:41 apps/activity/models.py:142
msgid "The end date must be after the start date."
msgstr "La date de fin doit être après celle de début."
-#: apps/activity/forms.py:82 apps/activity/models.py:269
+#: apps/activity/forms.py:82 apps/activity/models.py:271
msgid "You can't invite someone once the activity is started."
msgstr ""
"Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré."
-#: apps/activity/forms.py:85 apps/activity/models.py:272
+#: apps/activity/forms.py:85 apps/activity/models.py:274
msgid "This activity is not validated yet."
msgstr "Cette activité n'est pas encore validée."
-#: apps/activity/forms.py:95 apps/activity/models.py:280
+#: apps/activity/forms.py:95 apps/activity/models.py:282
msgid "This person has been already invited 5 times this year."
msgstr "Cette personne a déjà été invitée 5 fois cette année."
-#: apps/activity/forms.py:99 apps/activity/models.py:284
+#: apps/activity/forms.py:99 apps/activity/models.py:286
msgid "This person is already invited."
msgstr "Cette personne est déjà invitée."
-#: apps/activity/forms.py:103 apps/activity/models.py:288
+#: apps/activity/forms.py:103 apps/activity/models.py:290
msgid "You can't invite more than 3 people to this activity."
msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
-#: apps/activity/models.py:28 apps/activity/models.py:63
-#: apps/member/models.py:203
+#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42
+#: apps/food/models.py:56 apps/member/models.py:203
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@@ -100,121 +100,121 @@ msgstr "types d'activité"
msgid "description"
msgstr "description"
-#: apps/activity/models.py:72
+#: apps/activity/models.py:74
msgid "location"
msgstr "lieu"
-#: apps/activity/models.py:76
+#: apps/activity/models.py:78
msgid "Place where the activity is organized, eg. Kfet."
msgstr "Lieu où l'activité est organisée, par exemple la Kfet."
-#: apps/activity/models.py:83
+#: apps/activity/models.py:85
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:207 apps/note/models/transactions.py:67
#: apps/permission/models.py:163
msgid "type"
msgstr "type"
-#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:318
+#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318
#: apps/note/models/notes.py:148 apps/treasury/models.py:293
#: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
msgstr "utilisateur⋅rice"
-#: apps/activity/models.py:96
+#: apps/activity/models.py:98
#: apps/activity/templates/activity/includes/activity_info.html:36
msgid "organizer"
msgstr "organisateur·rice"
-#: apps/activity/models.py:97
+#: apps/activity/models.py:99
msgid "Club that organizes the activity. The entry fees will go to this club."
msgstr ""
"Le club qui organise l'activité. Les coûts d'invitation iront pour ce club."
-#: apps/activity/models.py:104
+#: apps/activity/models.py:106
#: apps/activity/templates/activity/includes/activity_info.html:39
msgid "attendees club"
msgstr "club attendu"
-#: apps/activity/models.py:105
+#: apps/activity/models.py:107
msgid "Club that is authorized to join the activity. Mostly the Kfet club."
msgstr ""
"Club qui est autorisé à rejoindre l'activité. Très souvent le club Kfet."
-#: apps/activity/models.py:109
+#: apps/activity/models.py:111
#: apps/activity/templates/activity/includes/activity_info.html:25
msgid "start date"
msgstr "date de début"
-#: apps/activity/models.py:113
+#: apps/activity/models.py:115
#: apps/activity/templates/activity/includes/activity_info.html:28
msgid "end date"
msgstr "date de fin"
-#: apps/activity/models.py:118
+#: apps/activity/models.py:120
#: apps/activity/templates/activity/includes/activity_info.html:50
#: apps/note/models/transactions.py:149
msgid "valid"
msgstr "valide"
-#: apps/activity/models.py:123
+#: apps/activity/models.py:125
#: apps/activity/templates/activity/includes/activity_info.html:65
msgid "open"
msgstr "ouvrir"
-#: apps/activity/models.py:128
+#: apps/activity/models.py:130
msgid "activities"
msgstr "activités"
-#: apps/activity/models.py:172
+#: apps/activity/models.py:174
msgid "entry time"
msgstr "heure d'entrée"
-#: apps/activity/models.py:178 apps/note/apps.py:14
+#: apps/activity/models.py:180 apps/note/apps.py:14
#: apps/note/models/notes.py:77
msgid "note"
msgstr "note"
-#: apps/activity/models.py:189
+#: apps/activity/models.py:191
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entry"
msgstr "entrée"
-#: apps/activity/models.py:190
+#: apps/activity/models.py:192
#: apps/activity/templates/activity/activity_entry.html:46
msgid "entries"
msgstr "entrées"
-#: apps/activity/models.py:193
+#: apps/activity/models.py:195
#, python-brace-format
msgid "Entry for {guest}, invited by {note} to the activity {activity}"
msgstr "Entrée pour {guest}, invité·e par {note} à l'activité {activity}"
-#: apps/activity/models.py:195
+#: apps/activity/models.py:197
#, python-brace-format
msgid "Entry for {note} to the activity {activity}"
msgstr "Entrée de la note {note} pour l'activité « {activity} »"
-#: apps/activity/models.py:202
+#: apps/activity/models.py:204
msgid "Already entered on "
msgstr "Déjà rentré·e le "
-#: apps/activity/models.py:202 apps/activity/tables.py:56
+#: apps/activity/models.py:204 apps/activity/tables.py:56
msgid "{:%Y-%m-%d %H:%M:%S}"
msgstr "{:%d/%m/%Y %H:%M:%S}"
-#: apps/activity/models.py:210
+#: apps/activity/models.py:212
msgid "The balance is negative."
msgstr "La note est en négatif."
-#: apps/activity/models.py:240
+#: apps/activity/models.py:242
#: apps/treasury/templates/treasury/sogecredit_detail.html:14
#: apps/wei/templates/wei/attribute_bus_1A.html:16
msgid "last name"
msgstr "nom de famille"
-#: apps/activity/models.py:245
+#: apps/activity/models.py:247
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/treasury/templates/treasury/sogecredit_detail.html:17
@@ -223,19 +223,19 @@ msgstr "nom de famille"
msgid "first name"
msgstr "prénom"
-#: apps/activity/models.py:252
+#: apps/activity/models.py:254
msgid "inviter"
msgstr "hôte"
-#: apps/activity/models.py:256
+#: apps/activity/models.py:258
msgid "guest"
msgstr "invité·e"
-#: apps/activity/models.py:257
+#: apps/activity/models.py:259
msgid "guests"
msgstr "invité·e·s"
-#: apps/activity/models.py:310
+#: apps/activity/models.py:312
msgid "Invitation"
msgstr "Invitation"
@@ -257,8 +257,9 @@ msgstr "Cette activité est actuellement ouverte."
msgid "The validation of the activity is pending."
msgstr "La validation de cette activité est en attente."
-#: apps/activity/tables.py:43 apps/treasury/tables.py:107
+#: apps/activity/tables.py:43
#: apps/member/templates/member/picture_update.html:18
+#: apps/treasury/tables.py:107
msgid "Remove"
msgstr "Supprimer"
@@ -275,14 +276,14 @@ msgid "Type"
msgstr "Type"
#: apps/activity/tables.py:84 apps/member/forms.py:196
-#: apps/registration/forms.py:92 apps/treasury/forms.py:131
+#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Nom de famille"
#: apps/activity/tables.py:86 apps/member/forms.py:201
#: apps/note/templates/note/transaction_form.html:138
-#: apps/registration/forms.py:97 apps/treasury/forms.py:133
+#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Prénom"
@@ -307,7 +308,7 @@ msgstr "Invité·e supprimé·e"
#: apps/note/models/transactions.py:261
#: apps/note/templates/note/transaction_form.html:17
#: apps/note/templates/note/transaction_form.html:152
-#: note_kfet/templates/base.html:72
+#: note_kfet/templates/base.html:78
msgid "Transfer"
msgstr "Virement"
@@ -336,13 +337,18 @@ msgstr "Retour à la page de l'activité"
#: apps/activity/templates/activity/activity_entry.html:129
msgid "Entry done, but caution: the user is not a Kfet member."
msgstr ""
-"Entrée effectuée, mais attention : la personne n'est pas un·e adhérent·e Kfet."
+"Entrée effectuée, mais attention : la personne n'est pas un·e adhérent·e "
+"Kfet."
#: apps/activity/templates/activity/activity_entry.html:132
msgid "Entry done!"
msgstr "Entrée effectuée !"
#: apps/activity/templates/activity/activity_form.html:16
+#: apps/food/templates/food/add_ingredient_form.html:16
+#: apps/food/templates/food/basicfood_form.html:16
+#: apps/food/templates/food/create_qrcode_form.html:19
+#: apps/food/templates/food/transformedfood_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
#: apps/note/templates/note/transactiontemplate_form.html:18
@@ -408,11 +414,11 @@ msgstr "modifier"
msgid "Invite"
msgstr "Inviter"
-#: apps/activity/views.py:36
+#: apps/activity/views.py:37
msgid "Create new activity"
msgstr "Créer une nouvelle activité"
-#: apps/activity/views.py:68 note_kfet/templates/base.html:90
+#: apps/activity/views.py:67 note_kfet/templates/base.html:96
msgid "Activities"
msgstr "Activités"
@@ -450,6 +456,258 @@ msgstr "Entrées pour l'activité « {} »"
msgid "API"
msgstr "API"
+#: apps/food/apps.py:11 apps/food/models.py:105
+msgid "food"
+msgstr "bouffe"
+
+#: apps/food/forms.py:32
+msgid "Fully used"
+msgstr "Entièrement utilisé"
+
+#: apps/food/forms.py:50
+msgid "Pasta METRO 5kg"
+msgstr "Pâtes METRO 5kg"
+
+#: apps/food/forms.py:96
+msgid "Lasagna"
+msgstr "Lasagnes"
+
+#: apps/food/models.py:18
+msgid "QR-code number"
+msgstr "numéro de QR-code"
+
+#: apps/food/models.py:26
+msgid "food container"
+msgstr "récipient"
+
+#: apps/food/models.py:30
+msgid "QR-code"
+msgstr "QR-code"
+
+#: apps/food/models.py:31
+msgid "QR-codes"
+msgstr "QR-codes"
+
+#: apps/food/models.py:34
+#, python-brace-format
+msgid "QR-code number {qr_code_number}"
+msgstr "numéro du QR-code {qr_code_number}"
+
+#: apps/food/models.py:47
+msgid "Allergen"
+msgstr "Allergène"
+
+#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17
+#: apps/food/templates/food/transformedfood_detail.html:20
+msgid "Allergens"
+msgstr "Allergènes"
+
+#: apps/food/models.py:64
+msgid "owner"
+msgstr "propriétaire"
+
+#: apps/food/models.py:70
+msgid "allergen"
+msgstr "allergène"
+
+#: apps/food/models.py:74
+msgid "expiry date"
+msgstr "date de péremption"
+
+#: apps/food/models.py:80
+msgid "was eaten"
+msgstr "a été mangé"
+
+#: apps/food/models.py:89
+msgid "is ready"
+msgstr "est prêt"
+
+#: apps/food/models.py:94
+msgid "is active"
+msgstr "est en cours"
+
+#: apps/food/models.py:106
+msgid "foods"
+msgstr "bouffes"
+
+#: apps/food/models.py:122
+msgid "arrival date"
+msgstr "date d'arrivée"
+
+#: apps/food/models.py:152
+msgid "Basic food"
+msgstr "Aliment basique"
+
+#: apps/food/models.py:153
+msgid "Basic foods"
+msgstr "Aliments basiques"
+
+#: apps/food/models.py:161
+msgid "creation date"
+msgstr "date de création"
+
+#: apps/food/models.py:169
+msgid "transformed ingredient"
+msgstr "ingrédients tranformées"
+
+#: apps/food/models.py:174
+msgid "shelf life"
+msgstr "durée de vie"
+
+#: apps/food/models.py:225 apps/food/views.py:365
+msgid "Transformed food"
+msgstr "Aliment transformé"
+
+#: apps/food/models.py:226
+msgid "Transformed foods"
+msgstr "Aliments transformés"
+
+#: apps/food/templates/food/create_qrcode_form.html:31
+#: apps/food/templates/food/basicfood_detail.html:14
+#: apps/food/templates/food/qrcode_detail.html:15
+#: apps/food/templates/food/transformedfood_detail.html:14
+msgid "Owner"
+msgstr "Propriétaire"
+
+#: apps/food/templates/food/create_qrcode_form.html:34
+#: apps/food/templates/food/basicfood_detail.html:15
+msgid "Arrival date"
+msgstr "Date d'arrivée"
+
+#: apps/food/templates/food/create_qrcode_form.html:37
+#: apps/food/templates/food/basicfood_detail.html:16
+#: apps/food/templates/food/qrcode_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:19
+msgid "Expiry date"
+msgstr "Date de péremption"
+
+#: apps/food/templates/food/basicfood_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:36
+msgid "Active"
+msgstr "Actif"
+
+#: apps/food/templates/food/basicfood_detail.html:25
+#: apps/food/templates/food/transformedfood_detail.html:37
+msgid "Eaten"
+msgstr "Mangé"
+
+#: apps/food/templates/food/basicfood_detail.html:28
+#: apps/food/templates/food/qrcode_detail.html:20
+#: apps/food/templates/food/qrcode_detail.html:24
+#: apps/food/templates/food/transformedfood_detail.html:41
+msgid "Update"
+msgstr "Modifier"
+
+#: apps/food/templates/food/basicfood_detail.html:32
+#: apps/food/templates/food/qrcode_detail.html:34
+#: apps/food/templates/food/transformedfood_detail.html:46
+msgid "Add to a meal"
+msgstr "Ajouter à un plat"
+
+#: apps/food/templates/food/create_qrcode_form.html:14
+msgid "New basic food"
+msgstr "Nouvel aliment basique"
+
+#: apps/food/templates/food/create_qrcode_form.html:23
+msgid "Copy constructor"
+msgstr "Constructeur de copie"
+
+#: apps/food/templates/food/qrcode_detail.html:10
+msgid "number"
+msgstr "numéro"
+
+#: apps/food/templates/food/create_qrcode_form.html:28
+#: apps/food/templates/food/qrcode_detail.html:14
+#: apps/note/templates/note/transaction_form.html:132
+#: apps/treasury/models.py:60
+msgid "Name"
+msgstr "Nom"
+
+#: apps/food/templates/food/qrcode_detail.html:29
+msgid "View details"
+msgstr "Voir plus"
+
+#: apps/food/templates/food/transformedfood_detail.html:16
+#: apps/food/templates/food/transformedfood_detail.html:35
+msgid "Ready"
+msgstr "Prêt"
+
+#: apps/food/templates/food/transformedfood_detail.html:18
+msgid "Creation date"
+msgstr "Date de création"
+
+#: apps/food/templates/food/transformedfood_detail.html:27
+msgid "Ingredients"
+msgstr "Ingrédients"
+
+#: apps/food/templates/food/transformedfood_detail.html:34
+msgid "Shelf life"
+msgstr "Durée de vie"
+
+#: apps/food/templates/food/transformedfood_list.html:11
+msgid "Meal served"
+msgstr "Plat servis"
+
+#: apps/food/templates/food/transformedfood_list.html:16
+msgid "New meal"
+msgstr "Nouveau plat"
+
+#: apps/food/templates/food/transformedfood_list.html:25
+msgid "There is no meal served."
+msgstr "Il n'y a pas de plat servi."
+
+#: apps/food/templates/food/transformedfood_list.html:33
+msgid "Open"
+msgstr "Open"
+
+#: apps/food/templates/food/transformedfood_list.html:40
+msgid "There is no free meal."
+msgstr "Il n'y a pas de plat en open"
+
+#: apps/food/templates/food/transformedfood_list.html:48
+msgid "All meals"
+msgstr "Tout les plats"
+
+#: apps/food/templates/food/transformedfood_list.html:55
+msgid "There is no meal."
+msgstr "Il n'y a pas de plat"
+
+#: apps/food/views.py:28
+msgid "Add the ingredient"
+msgstr "Ajouter un ingrédient"
+
+#: apps/food/views.py:42
+msgid "The product is already prepared"
+msgstr "Le produit est déjà prêt"
+
+#: apps/food/views.py:70
+msgid "Update an aliment"
+msgstr "Modifier un aliment"
+
+#: apps/food/views.py:97
+msgid "Details of:"
+msgstr "Détails de:"
+
+#: apps/food/views.py:121
+msgid "Add a new basic food with QRCode"
+msgstr "Ajouter un nouvel ingrédient avec un QR-code"
+
+#: apps/food/views.py:185
+msgid "Add a new QRCode"
+msgstr "Ajouter un nouveau QR-code"
+
+#: apps/food/views.py:235
+msgid "QRCode"
+msgstr "QR-code"
+
+#: apps/food/views.py:271
+msgid "Add a new meal"
+msgstr "Ajouter un nouveau plat"
+
+#: apps/food/views.py:337
+msgid "Update a meal"
+msgstr "Modifier le plat"
+
#: apps/logs/apps.py:11
msgid "Logs"
msgstr "Logs"
@@ -574,8 +832,8 @@ msgstr "Taille maximale : 2 Mo"
msgid "This image cannot be loaded."
msgstr "Cette image ne peut pas être chargée."
-#: apps/member/forms.py:148 apps/member/views.py:102
-#: apps/registration/forms.py:34 apps/registration/views.py:276
+#: apps/member/forms.py:151 apps/member/views.py:102
+#: apps/registration/forms.py:33 apps/registration/views.py:276
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
@@ -587,12 +845,12 @@ msgstr "Inscription payée par la Société générale"
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Cochez cette case si la Société Générale a payé l'inscription."
-#: apps/member/forms.py:182 apps/registration/forms.py:79
+#: apps/member/forms.py:182 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Type de rechargement"
-#: apps/member/forms.py:183 apps/registration/forms.py:80
+#: apps/member/forms.py:183 apps/registration/forms.py:79
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "Pas de rechargement"
@@ -601,13 +859,13 @@ msgstr "Pas de rechargement"
msgid "You can credit the note of the user."
msgstr "Vous pouvez créditer la note de l'utilisateur⋅rice avant l'adhésion."
-#: apps/member/forms.py:186 apps/registration/forms.py:85
+#: apps/member/forms.py:189 apps/registration/forms.py:84
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Montant à créditer"
#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144
-#: apps/registration/forms.py:102 apps/treasury/forms.py:135
+#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Banque"
@@ -766,8 +1024,8 @@ msgid ""
"Register on the mailing list to stay informed of the events of the campus (1 "
"mail/week)"
msgstr ""
-"S'inscrire sur la liste de diffusion pour rester informé·e des événements sur "
-"le campus (1 mail par semaine)"
+"S'inscrire sur la liste de diffusion pour rester informé·e des événements "
+"sur le campus (1 mail par semaine)"
#: apps/member/models.py:108
msgid ""
@@ -860,8 +1118,8 @@ msgstr "fin de l'adhésion"
#: apps/member/models.py:259
msgid "Maximal date of a membership, after which members must renew it."
msgstr ""
-"Date maximale d'une fin d'adhésion, après laquelle les adhérent·e·s doivent la "
-"renouveler."
+"Date maximale d'une fin d'adhésion, après laquelle les adhérent·e·s doivent "
+"la renouveler."
#: apps/member/models.py:263
msgid "add to registration form"
@@ -902,11 +1160,11 @@ msgstr "Adhésion de {user} pour le club {club}"
msgid "The role {role} does not apply to the club {club}."
msgstr "Le rôle {role} ne s'applique pas au club {club}."
-#: apps/member/models.py:381 apps/member/views.py:712
+#: apps/member/models.py:381 apps/member/views.py:715
msgid "User is already a member of the club"
msgstr "L'utilisateur·rice est déjà membre du club"
-#: apps/member/models.py:393 apps/member/views.py:721
+#: apps/member/models.py:393 apps/member/views.py:724
msgid "User is not a member of the parent club"
msgstr "L'utilisateur·rice n'est pas membre du club parent"
@@ -1020,7 +1278,7 @@ msgstr ""
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287
-#: apps/member/views.py:517
+#: apps/member/views.py:520
msgid "Note aliases"
msgstr "Alias de la note"
@@ -1221,7 +1479,7 @@ msgstr "Sauvegarder les changements"
msgid "Registrations"
msgstr "Inscriptions"
-#: apps/member/views.py:72 apps/registration/forms.py:24
+#: apps/member/views.py:72 apps/registration/forms.py:23
msgid "This address must be valid."
msgstr "Cette adresse doit être valide."
@@ -1241,31 +1499,31 @@ msgstr "Amitiés note"
msgid "Update note picture"
msgstr "Modifier la photo de la note"
-#: apps/member/views.py:354
+#: apps/member/views.py:357
msgid "Manage auth token"
msgstr "Gérer les jetons d'authentification"
-#: apps/member/views.py:381
+#: apps/member/views.py:384
msgid "Create new club"
msgstr "Créer un nouveau club"
-#: apps/member/views.py:400
+#: apps/member/views.py:403
msgid "Search club"
msgstr "Chercher un club"
-#: apps/member/views.py:433
+#: apps/member/views.py:436
msgid "Club detail"
msgstr "Détails du club"
-#: apps/member/views.py:540
+#: apps/member/views.py:543
msgid "Update club"
msgstr "Modifier le club"
-#: apps/member/views.py:574
+#: apps/member/views.py:577
msgid "Add new member to the club"
msgstr "Ajouter un·e nouvelleau membre au club"
-#: apps/member/views.py:703 apps/wei/views.py:973
+#: apps/member/views.py:706 apps/wei/views.py:973
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@@ -1273,19 +1531,19 @@ msgstr ""
"Cet⋅te utilisateur⋅rice n'a pas assez d'argent pour rejoindre ce club et ne "
"peut pas avoir un solde négatif."
-#: apps/member/views.py:725
+#: apps/member/views.py:728
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
-#: apps/member/views.py:730
+#: apps/member/views.py:733
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
-#: apps/member/views.py:880
+#: apps/member/views.py:883
msgid "Manage roles of an user in the club"
msgstr "Gérer les rôles d'un⋅e utilisateur⋅rice dans le club"
-#: apps/member/views.py:905
+#: apps/member/views.py:908
msgid "Members of the club"
msgstr "Membres du club"
@@ -1397,6 +1655,7 @@ msgstr ""
"La note est bloquée de force par le BDE et ne peut pas être débloquée par læ "
"propriétaire de la note."
+
#: apps/note/models/notes.py:78
msgid "notes"
msgstr "notes"
@@ -1743,11 +2002,6 @@ msgstr "Action"
msgid "Amount"
msgstr "Montant"
-#: apps/note/templates/note/transaction_form.html:132
-#: apps/treasury/models.py:60
-msgid "Name"
-msgstr "Nom"
-
#: apps/note/templates/note/transaction_form.html:177
msgid "Select emitter"
msgstr "Sélection de l'émetteur⋅rice"
@@ -2022,7 +2276,7 @@ msgstr ""
"Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » "
"avec ces paramètres. Merci de les corriger et de réessayer."
-#: apps/permission/views.py:112 note_kfet/templates/base.html:108
+#: apps/permission/views.py:112 note_kfet/templates/base.html:114
msgid "Rights"
msgstr "Droits"
@@ -2034,15 +2288,15 @@ msgstr "Tous les droits"
msgid "registration"
msgstr "inscription"
-#: apps/registration/forms.py:40
+#: apps/registration/forms.py:39
msgid "This email address is already used."
msgstr "Cette adresse e-mail est déjà prise."
-#: apps/registration/forms.py:60
+#: apps/registration/forms.py:59
msgid "Register to the WEI"
msgstr "S'inscrire au WEI"
-#: apps/registration/forms.py:62
+#: apps/registration/forms.py:61
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@@ -2051,11 +2305,11 @@ msgstr ""
"pourrez toujours vous inscrire plus tard, après avoir validé votre compte à "
"la Kfet."
-#: apps/registration/forms.py:107
+#: apps/registration/forms.py:106
msgid "Join BDE Club"
msgstr "Adhérer au club BDE"
-#: apps/registration/forms.py:114
+#: apps/registration/forms.py:113
msgid "Join Kfet Club"
msgstr "Adhérer au club Kfet"
@@ -2162,39 +2416,39 @@ msgstr "Merci"
msgid "The Note Kfet team."
msgstr "L'équipe de la Note Kfet."
-#: apps/registration/views.py:41
+#: apps/registration/views.py:42
msgid "Register new user"
msgstr "Enregistrer un⋅e nouvel⋅le utilisateur⋅rice"
-#: apps/registration/views.py:99
+#: apps/registration/views.py:100
msgid "Email validation"
msgstr "Validation de l'adresse e-mail"
-#: apps/registration/views.py:101
+#: apps/registration/views.py:102
msgid "Validate email"
msgstr "Valider l'adresse e-mail"
-#: apps/registration/views.py:145
+#: apps/registration/views.py:146
msgid "Email validation unsuccessful"
msgstr "La validation de l'adresse e-mail a échoué"
-#: apps/registration/views.py:156
+#: apps/registration/views.py:157
msgid "Email validation email sent"
msgstr "L'e-mail de vérification de l'adresse e-mail a bien été envoyé"
-#: apps/registration/views.py:164
+#: apps/registration/views.py:165
msgid "Resend email validation link"
msgstr "Renvoyer le lien de validation"
-#: apps/registration/views.py:182
+#: apps/registration/views.py:183
msgid "Pre-registered users list"
msgstr "Liste des utilisateur⋅rices en attente d'inscription"
-#: apps/registration/views.py:206
+#: apps/registration/views.py:207
msgid "Unregistered users"
msgstr "Utilisateur·rices en attente d'inscription"
-#: apps/registration/views.py:219
+#: apps/registration/views.py:220
msgid "Registration detail"
msgstr "Détails de l'inscription"
@@ -2218,7 +2472,7 @@ msgstr ""
msgid "Invalidate pre-registration"
msgstr "Invalider l'inscription"
-#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
+#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102
msgid "Treasury"
msgstr "Trésorerie"
@@ -2544,7 +2798,8 @@ msgstr ""
#: apps/treasury/templates/treasury/sogecredit_detail.html:44
msgid "If you think there is an error, please contact the \"respos info\"."
-msgstr "Si vous pensez qu'il y a une erreur, merci de contacter un·e respo info."
+msgstr ""
+"Si vous pensez qu'il y a une erreur, merci de contacter un·e respo info."
#: apps/treasury/templates/treasury/sogecredit_detail.html:50
msgid "This credit is already validated."
@@ -2634,7 +2889,7 @@ msgstr "Gérer les crédits de la Société générale"
#: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38
#: apps/wei/models.py:62 apps/wei/models.py:178
-#: note_kfet/templates/base.html:102
+#: note_kfet/templates/base.html:108
msgid "WEI"
msgstr "WEI"
@@ -3254,19 +3509,19 @@ msgstr "Répartir les 1A dans les bus"
msgid "Attribute bus"
msgstr "Attribuer un bus"
-#: note_kfet/settings/base.py:172
+#: note_kfet/settings/base.py:173
msgid "German"
msgstr "Allemand"
-#: note_kfet/settings/base.py:173
+#: note_kfet/settings/base.py:174
msgid "English"
msgstr "Anglais"
-#: note_kfet/settings/base.py:174
+#: note_kfet/settings/base.py:175
msgid "Spanish"
msgstr "Espagnol"
-#: note_kfet/settings/base.py:175
+#: note_kfet/settings/base.py:176
msgid "French"
msgstr "Français"
@@ -3331,34 +3586,38 @@ msgstr "Réinitialiser"
msgid "The ENS Paris-Saclay BDE note."
msgstr "La note du BDE de l'ENS Paris-Saclay."
-#: note_kfet/templates/base.html:78
+#: note_kfet/templates/base.html:72
+msgid "Food"
+msgstr "Bouffe"
+
+#: note_kfet/templates/base.html:84
msgid "Users"
msgstr "Utilisateur·rices"
-#: note_kfet/templates/base.html:84
+#: note_kfet/templates/base.html:90
msgid "Clubs"
msgstr "Clubs"
-#: note_kfet/templates/base.html:113
+#: note_kfet/templates/base.html:119
msgid "Admin"
msgstr "Admin"
-#: note_kfet/templates/base.html:127
+#: note_kfet/templates/base.html:133
msgid "My account"
msgstr "Mon compte"
-#: note_kfet/templates/base.html:130
+#: note_kfet/templates/base.html:136
msgid "Log out"
msgstr "Se déconnecter"
-#: note_kfet/templates/base.html:138
+#: note_kfet/templates/base.html:144
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Inscription"
-#: note_kfet/templates/base.html:145
+#: note_kfet/templates/base.html:151
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@@ -3366,15 +3625,15 @@ msgstr "Inscription"
msgid "Log in"
msgstr "Se connecter"
-#: note_kfet/templates/base.html:159
+#: note_kfet/templates/base.html:165
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
msgstr ""
-"Vous n'êtes plus adhérent·e BDE. Merci de réadhérer si vous voulez profiter de "
-"la note."
+"Vous n'êtes plus adhérent·e BDE. Merci de réadhérer si vous voulez profiter "
+"de la note."
-#: note_kfet/templates/base.html:165
+#: note_kfet/templates/base.html:171
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@@ -3382,7 +3641,7 @@ msgstr ""
"Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail "
"et de cliquer sur le lien de validation."
-#: note_kfet/templates/base.html:171
+#: note_kfet/templates/base.html:177
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
@@ -3396,22 +3655,22 @@ msgstr ""
"vérification peut durer quelques jours. Merci de vous assurer de bien aller "
"au bout de vos démarches."
-#: note_kfet/templates/base.html:194
+#: note_kfet/templates/base.html:200
msgid "Contact us"
msgstr "Nous contacter"
-#: note_kfet/templates/base.html:196
+#: note_kfet/templates/base.html:202
msgid "Technical Support"
msgstr "Support technique"
-#: note_kfet/templates/base.html:198
-msgid "FAQ (FR)"
-msgstr "FAQ (FR)"
-
-#: note_kfet/templates/base.html:200
+#: note_kfet/templates/base.html:204
msgid "Charte Info (FR)"
msgstr "Charte Info (FR)"
+#: note_kfet/templates/base.html:206
+msgid "FAQ (FR)"
+msgstr "FAQ (FR)"
+
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name…"
msgstr "Chercher par un attribut tel que le nom…"
@@ -3640,6 +3899,335 @@ msgstr ""
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
"lien que vous avez reçu."
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid color."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid value."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "Invitation"
+#~ msgid "Syndication"
+#~ msgstr "Invitation"
+
+#, fuzzy
+#~| msgid "There is no results."
+#~ msgid "That page contains no results"
+#~ msgstr "Il n'y a pas de résultat."
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid URL."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid integer."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid email address."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 address."
+#~ msgstr "Cette activité n'est pas encore validée."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv6 address."
+#~ msgstr "Cette activité n'est pas encore validée."
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "Enter a valid IPv4 or IPv6 address."
+#~ msgstr "Cette activité n'est pas encore validée."
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a number."
+#~ msgstr "numéro de téléphone"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "and"
+#~ msgstr "ajouter"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_labels)s already exists."
+#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà"
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be null."
+#~ msgstr "Cette image ne peut pas être chargée."
+
+#, fuzzy
+#~| msgid "This image cannot be loaded."
+#~ msgid "This field cannot be blank."
+#~ msgstr "Cette image ne peut pas être chargée."
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model_name)s with this %(field_label)s already exists."
+#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Decimal number"
+#~ msgstr "numéro de téléphone"
+
+#, fuzzy
+#~| msgid "action"
+#~ msgid "Duration"
+#~ msgstr "action"
+
+#, fuzzy
+#~| msgid "address"
+#~ msgid "Email address"
+#~ msgstr "adresse"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Floating point number"
+#~ msgstr "numéro de téléphone"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IPv4 address"
+#~ msgstr "Adresse IP"
+
+#, fuzzy
+#~| msgid "IP Address"
+#~ msgid "IP address"
+#~ msgstr "Adresse IP"
+
+#, fuzzy
+#~| msgid "Invoice identifier"
+#~ msgid "Universally unique identifier"
+#~ msgstr "Numéro de facture"
+
+#, fuzzy, python-format
+#~| msgid "A template with this name already exist"
+#~ msgid "%(model)s instance with %(field)s %(value)r does not exist."
+#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a whole number."
+#~ msgstr "numéro de téléphone"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid date."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid time."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid date/time."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "Email validation"
+#~ msgid "Enter a valid duration."
+#~ msgstr "Validation de l'adresse mail"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a list of values."
+#~ msgstr "dévalider"
+
+#, fuzzy
+#~| msgid "phone number"
+#~ msgid "Enter a complete value."
+#~ msgstr "numéro de téléphone"
+
+#, fuzzy
+#~| msgid "invalidate"
+#~ msgid "Enter a valid UUID."
+#~ msgstr "dévalider"
+
+#, fuzzy, python-format
+#~| msgid "This activity is not validated yet."
+#~ msgid "\"%(pk)s\" is not a valid value."
+#~ msgstr "Cette activité n'est pas encore validée."
+
+#, fuzzy
+#~| msgid "Current activity"
+#~ msgid "Currently"
+#~ msgstr "Activité en cours"
+
+#, fuzzy
+#~| msgid "change"
+#~ msgid "Change"
+#~ msgstr "modifier"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "September"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "November"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgid "December"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "add"
+#~ msgid "jan"
+#~ msgstr "ajouter"
+
+#, fuzzy
+#~| msgid "fee"
+#~ msgid "feb"
+#~ msgstr "cotisation"
+
+#, fuzzy
+#~| msgid "product"
+#~ msgid "oct"
+#~ msgstr "produit"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgctxt "abbrev. month"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "Search"
+#~ msgctxt "alt. month"
+#~ msgid "March"
+#~ msgstr "Recherche"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "September"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "November"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "member"
+#~ msgctxt "alt. month"
+#~ msgid "December"
+#~ msgstr "adhérent·e"
+
+#, fuzzy
+#~| msgid "This activity is not validated yet."
+#~ msgid "This is not a valid IPv6 address."
+#~ msgstr "Cette activité n'est pas encore validée."
+
+#, fuzzy, python-format
+#~| msgid "year"
+#~ msgid "%d year"
+#~ msgid_plural "%d years"
+#~ msgstr[0] "année"
+#~ msgstr[1] "année"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No year specified"
+#~ msgstr "Pas de motif spécifié"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No month specified"
+#~ msgstr "Pas de motif spécifié"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No day specified"
+#~ msgstr "Pas de motif spécifié"
+
+#, fuzzy
+#~| msgid "No reason specified"
+#~ msgid "No week specified"
+#~ msgstr "Pas de motif spécifié"
+
+#, fuzzy
+#~| msgid "Client secret"
+#~ msgid "Confidential"
+#~ msgstr "Secret client"
+
+#, fuzzy
+#~| msgid "Authorization:"
+#~ msgid "Authorization code"
+#~ msgstr "Autorisation :"
+
+#, fuzzy
+#~| msgid "Reset my password"
+#~ msgid "Resource owner password-based"
+#~ msgstr "Réinitialiser mon mot de passe"
+
+#, fuzzy
+#~| msgid "Client secret"
+#~ msgid "Client credentials"
+#~ msgstr "Secret client"
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token is invalid."
+#~ msgstr "Cette adresse doit être valide."
+
+#, fuzzy
+#~| msgid "This address must be valid."
+#~ msgid "The access token has expired."
+#~ msgstr "Cette adresse doit être valide."
+
+#, fuzzy
+#~| msgid "The user does not have enough money."
+#~ msgid "The access token is valid but does not have enough scope."
+#~ msgstr "L'utilisateur·ice n'a pas assez d'argent."
+
+#, fuzzy
+#~| msgid "Application requires following permissions:"
+#~ msgid "Application requires following permissions"
+#~ msgstr "L'application requiert les permissions suivantes :"
+
+#~ msgid "pasta"
+#~ msgstr "pâtes"
+
+#~ msgid "In preparation"
+#~ msgstr "En cours de préparation"
+
+#~ msgid "Free"
+#~ msgstr "Open"
+
+#~ msgid "Add a new aliment"
+#~ msgstr "Ajouter un nouvel aliment"
+
+#, fuzzy
+#~| msgid "Transformed food"
+#~ msgid "New transformed food"
+#~ msgstr "Bouffe transformée"
+
#, fuzzy
#~| msgid "People having you as a friend"
#~ msgid "You already have that person as a friend"
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index 168f34bd..9c5a9458 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -70,6 +70,7 @@ INSTALLED_APPS = [
# Note apps
'api',
'activity',
+ 'food',
'logs',
'member',
'note',
diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html
index 63d0ddfe..9f5ae867 100644
--- a/note_kfet/templates/base.html
+++ b/note_kfet/templates/base.html
@@ -66,10 +66,16 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans 'Consumptions' %}
{% endif %}
+ {% if request.user.is_authenticated %}
+
+ {% url 'food:food_list' as url %}
+ {% trans 'Food' %}
+
+ {% endif %}
{% if user.is_authenticated and user|is_member:"Kfet" %}
{% url 'note:transfer' as url %}
- {% trans 'Transfer' %}
+ {% trans 'Transfer' %}
{% endif %}
{% if "auth.user"|model_list_length >= 2 %}
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index 9008a16d..b2b64dcf 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -21,6 +21,7 @@ urlpatterns = [
path('activity/', include('activity.urls')),
path('treasury/', include('treasury.urls')),
path('wei/', include('wei.urls')),
+ path('food/',include('food.urls')),
# Include Django Contrib and Core routers
path('i18n/', include('django.conf.urls.i18n')),