{% for product in categories[category] -%}
-
+ {{ product.name }}
Date: Mon, 23 Dec 2024 01:18:01 +0100
Subject: [PATCH 21/32] Add more counter click tests
---
counter/tests/test_counter.py | 76 ++++++++++++++++++++++++++++++++++-
1 file changed, 75 insertions(+), 1 deletion(-)
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index 1649196d..99e9ac77 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -21,6 +21,7 @@ from django.conf import settings
from django.contrib.auth.models import make_password
from django.core.cache import cache
from django.http import HttpResponse
+from django.shortcuts import resolve_url
from django.test import Client, TestCase
from django.urls import reverse
from django.utils import timezone
@@ -30,7 +31,7 @@ from model_bakery import baker
from club.models import Club, Membership
from core.baker_recipes import board_user, old_subscriber_user, subscriber_user
-from core.models import User
+from core.models import Group, User
from counter.baker_recipes import product_recipe
from counter.models import (
Counter,
@@ -219,12 +220,22 @@ class TestCounterClick(FullClickSetup, TestCase):
super().setUpTestData()
cls.underage_customer = subscriber_user.make()
+ cls.banned_counter_customer = subscriber_user.make()
+ cls.banned_alcohol_customer = subscriber_user.make()
cls.set_age(cls.customer, 20)
cls.set_age(cls.barmen, 20)
cls.set_age(cls.club_admin, 20)
+ cls.set_age(cls.banned_alcohol_customer, 20)
cls.set_age(cls.underage_customer, 17)
+ cls.banned_alcohol_customer.groups.add(
+ Group.objects.get(pk=settings.SITH_GROUP_BANNED_ALCOHOL_ID)
+ )
+ cls.banned_counter_customer.groups.add(
+ Group.objects.get(pk=settings.SITH_GROUP_BANNED_COUNTER_ID)
+ )
+
cls.beer = product_recipe.make(
limit_age=18, selling_price="1.5", special_selling_price="1"
)
@@ -376,6 +387,69 @@ class TestCounterClick(FullClickSetup, TestCase):
assert self.updated_amount(self.customer) == Decimal("8")
+ def test_click_alcool_unauthorized(self):
+ self.login_in_bar()
+
+ for user in [self.underage_customer, self.banned_alcohol_customer]:
+ self.refill_user(user, 10)
+
+ # Buy product without age limit
+ assert (
+ self.submit_basket(
+ user,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ ).status_code
+ == 302
+ )
+
+ assert self.updated_amount(user) == Decimal("7")
+
+ # Buy product without age limit
+ assert (
+ self.submit_basket(
+ user,
+ [
+ BasketItem(self.beer.id, 2),
+ ],
+ ).status_code
+ == 200
+ )
+
+ assert self.updated_amount(user) == Decimal("7")
+
+ def test_click_unauthorized_customer(self):
+ self.login_in_bar()
+
+ for user in [
+ self.banned_counter_customer,
+ self.customer_old_can_not_buy,
+ ]:
+ self.refill_user(user, 10)
+ resp = self.submit_basket(
+ user,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ )
+ assert resp.status_code == 302
+ assert resp.url == resolve_url(self.counter)
+
+ assert self.updated_amount(self.banned_counter_customer) == Decimal("10")
+
+ def test_click_user_without_customer(self):
+ self.login_in_bar()
+ assert (
+ self.submit_basket(
+ self.customer_can_not_buy,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ ).status_code
+ == 404
+ )
+
def test_annotate_has_barman_queryset(self):
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
counters = Counter.objects.annotate_has_barman(self.barmen)
From 2e5e217842f52187a976cc890732862f6db96da0 Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 01:35:44 +0100
Subject: [PATCH 22/32] Disable eboutic in counter click/main
---
counter/tests/test_counter.py | 14 +++++++++++++-
counter/views/click.py | 3 +++
counter/views/home.py | 3 +++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index 99e9ac77..6ed3e08b 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -70,7 +70,7 @@ class FullClickSetup:
cls.customer_old_can_not_buy = old_subscriber_user.make()
cls.customer_can_not_buy = baker.make(User)
- cls.club_counter = baker.make(Counter)
+ cls.club_counter = baker.make(Counter, type="OFFICE")
baker.make(
Membership,
start_date=now() - timedelta(days=30),
@@ -302,6 +302,18 @@ class TestCounterClick(FullClickSetup, TestCase):
def refill_user(self, user: User, amount: Decimal | int):
baker.make(Refilling, amount=amount, customer=user.customer, is_validated=False)
+ def test_click_eboutic_failure(self):
+ eboutic = baker.make(Counter, type="EBOUTIC")
+ self.client.force_login(self.club_admin)
+ assert (
+ self.submit_basket(
+ self.customer,
+ [BasketItem(self.stamps.id, 5)],
+ counter=eboutic,
+ ).status_code
+ == 404
+ )
+
def test_click_office_success(self):
self.refill_user(self.customer, 10)
self.client.force_login(self.club_admin)
diff --git a/counter/views/click.py b/counter/views/click.py
index f4f3f6b6..e74a48c5 100644
--- a/counter/views/click.py
+++ b/counter/views/click.py
@@ -148,6 +148,9 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView):
pk_url_kwarg = "counter_id"
current_tab = "counter"
+ def get_queryset(self):
+ return super().get_queryset().exclude(type="EBOUTIC")
+
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["form_kwargs"] = {
diff --git a/counter/views/home.py b/counter/views/home.py
index 60cc5a5a..d66b0969 100644
--- a/counter/views/home.py
+++ b/counter/views/home.py
@@ -43,6 +43,9 @@ class CounterMain(
)
current_tab = "counter"
+ def get_queryset(self):
+ return super().get_queryset().exclude(type="EBOUTIC")
+
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.type == "BAR" and not (
From 022c19c0200fe2853a9229e685600eed3d83c000 Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 02:17:28 +0100
Subject: [PATCH 23/32] Fix counter permissions issues
---
counter/models.py | 2 +-
counter/tests/test_counter.py | 85 +++++++++++++++++++++++++++++++++--
counter/views/click.py | 8 ++--
3 files changed, 87 insertions(+), 8 deletions(-)
diff --git a/counter/models.py b/counter/models.py
index 489b0794..6668e520 100644
--- a/counter/models.py
+++ b/counter/models.py
@@ -527,7 +527,7 @@ class Counter(models.Model):
if user.is_anonymous:
return False
mem = self.club.get_membership_for(user)
- if mem and mem.role >= 7:
+ if mem and mem.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index 6ed3e08b..c7b91c4d 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -30,7 +30,7 @@ from freezegun import freeze_time
from model_bakery import baker
from club.models import Club, Membership
-from core.baker_recipes import board_user, old_subscriber_user, subscriber_user
+from core.baker_recipes import board_user, subscriber_user
from core.models import Group, User
from counter.baker_recipes import product_recipe
from counter.models import (
@@ -67,7 +67,11 @@ class FullClickSetup:
sub.subscription_end = localdate() - timedelta(days=89)
sub.save()
- cls.customer_old_can_not_buy = old_subscriber_user.make()
+ cls.customer_old_can_not_buy = subscriber_user.make()
+ sub = cls.customer_old_can_not_buy.subscriptions.first()
+ sub.subscription_end = localdate() - timedelta(days=90)
+ sub.save()
+
cls.customer_can_not_buy = baker.make(User)
cls.club_counter = baker.make(Counter, type="OFFICE")
@@ -448,7 +452,7 @@ class TestCounterClick(FullClickSetup, TestCase):
assert resp.status_code == 302
assert resp.url == resolve_url(self.counter)
- assert self.updated_amount(self.banned_counter_customer) == Decimal("10")
+ assert self.updated_amount(user) == Decimal("10")
def test_click_user_without_customer(self):
self.login_in_bar()
@@ -462,6 +466,81 @@ class TestCounterClick(FullClickSetup, TestCase):
== 404
)
+ def test_click_allowed_old_subscriber(self):
+ self.login_in_bar()
+ self.refill_user(self.customer_old_can_buy, 10)
+ assert (
+ self.submit_basket(
+ self.customer_old_can_buy,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ ).status_code
+ == 302
+ )
+
+ assert self.updated_amount(self.customer_old_can_buy) == Decimal("7")
+
+ def test_click_wrong_counter(self):
+ self.login_in_bar()
+ self.refill_user(self.customer, 10)
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ counter=self.other_counter,
+ ).status_code
+ == 302 # Redirect to counter main
+ )
+
+ # We want to test sending requests from another counter while
+ # we are currently registered to another counter
+ # so we connect to a counter and
+ # we create a new client, in order to check
+ # that using a client not logged to a counter
+ # where another client is logged still isn't authorized.
+ client = Client()
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ counter=self.counter,
+ client=client,
+ ).status_code
+ == 302 # Redirect to counter main
+ )
+
+ assert self.updated_amount(self.customer) == Decimal("10")
+
+ def test_click_not_connected(self):
+ self.refill_user(self.customer, 10)
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ ).status_code
+ == 302 # Redirect to counter main
+ )
+
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.snack.id, 2),
+ ],
+ counter=self.club_counter,
+ ).status_code
+ == 403
+ )
+
+ assert self.updated_amount(self.customer) == Decimal("10")
+
def test_annotate_has_barman_queryset(self):
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
counters = Counter.objects.annotate_has_barman(self.barmen)
diff --git a/counter/views/click.py b/counter/views/click.py
index e74a48c5..3a320337 100644
--- a/counter/views/click.py
+++ b/counter/views/click.py
@@ -149,7 +149,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView):
current_tab = "counter"
def get_queryset(self):
- return super().get_queryset().exclude(type="EBOUTIC")
+ return super().get_queryset().exclude(type="EBOUTIC").annotate_is_open()
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
@@ -167,13 +167,13 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView):
if not self.customer.can_buy or self.customer.user.is_banned_counter:
return redirect(obj) # Redirect to counter
- if obj.type != "BAR" and not request.user.is_authenticated:
+ if obj.type == "OFFICE" and not obj.club.has_rights_in_club(request.user):
raise PermissionDenied
if obj.type == "BAR" and (
- "counter_token" not in request.session
+ not obj.is_open
+ or "counter_token" not in request.session
or request.session["counter_token"] != obj.token
- or len(obj.barmen_list) == 0
):
return redirect(obj) # Redirect to counter
From ccf5118c9dc5fffb3dc9e00b0f126a90957fd460 Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 02:26:39 +0100
Subject: [PATCH 24/32] Add invalid form tests
---
counter/tests/test_counter.py | 53 +++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index c7b91c4d..1d50fdf4 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -541,6 +541,59 @@ class TestCounterClick(FullClickSetup, TestCase):
assert self.updated_amount(self.customer) == Decimal("10")
+ def test_click_product_not_in_counter(self):
+ self.refill_user(self.customer, 10)
+ self.login_in_bar()
+
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.stamps.id, 2),
+ ],
+ ).status_code
+ == 200
+ )
+ assert self.updated_amount(self.customer) == Decimal("10")
+
+ def test_click_product_invalid(self):
+ self.refill_user(self.customer, 10)
+ self.login_in_bar()
+
+ for item in [
+ BasketItem("-1", 2),
+ BasketItem(self.beer.id, -1),
+ BasketItem(None, 1),
+ BasketItem(self.beer.id, None),
+ BasketItem(None, None),
+ ]:
+ assert (
+ self.submit_basket(
+ self.customer,
+ [item],
+ ).status_code
+ == 200
+ )
+
+ assert self.updated_amount(self.customer) == Decimal("10")
+
+ def test_click_not_enough_money(self):
+ self.refill_user(self.customer, 10)
+ self.login_in_bar()
+
+ assert (
+ self.submit_basket(
+ self.customer,
+ [
+ BasketItem(self.beer_tap.id, 5),
+ BasketItem(self.beer.id, 10),
+ ],
+ ).status_code
+ == 200
+ )
+
+ assert self.updated_amount(self.customer) == Decimal("10")
+
def test_annotate_has_barman_queryset(self):
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
counters = Counter.objects.annotate_has_barman(self.barmen)
From 7f6fd7dc4757c1580ff800a46db90a42c45d022c Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 02:37:41 +0100
Subject: [PATCH 25/32] Fix wrong tests/permissions
---
counter/tests/test_counter.py | 4 ++--
counter/views/click.py | 5 ++++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index 1d50fdf4..a62e5fab 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -187,7 +187,7 @@ class TestRefilling(FullClickSetup, TestCase):
self.refill_user(
self.customer_old_can_not_buy, self.counter, 10
).status_code
- == 403
+ == 404
)
def test_refilling_counter_success(self):
@@ -868,4 +868,4 @@ class TestClubCounterClickAccess(TestCase):
self.counter.sellers.add(self.user)
self.client.force_login(self.user)
res = self.client.get(self.click_url)
- assert res.status_code == 200
+ assert res.status_code == 403
diff --git a/counter/views/click.py b/counter/views/click.py
index 3a320337..f324a9fc 100644
--- a/counter/views/click.py
+++ b/counter/views/click.py
@@ -167,7 +167,10 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView):
if not self.customer.can_buy or self.customer.user.is_banned_counter:
return redirect(obj) # Redirect to counter
- if obj.type == "OFFICE" and not obj.club.has_rights_in_club(request.user):
+ if obj.type == "OFFICE" and (
+ obj.sellers.filter(pk=request.user.pk).exists()
+ or not obj.club.has_rights_in_club(request.user)
+ ):
raise PermissionDenied
if obj.type == "BAR" and (
From 6f003ffa539915bd5f1c2b2fd15cb46087b9dcea Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 02:41:41 +0100
Subject: [PATCH 26/32] Add translations
---
locale/fr/LC_MESSAGES/django.po | 508 +++++++++++++++---------------
locale/fr/LC_MESSAGES/djangojs.po | 26 +-
2 files changed, 279 insertions(+), 255 deletions(-)
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 3e15ddf0..d6c2e813 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-21 02:15+0100\n"
+"POT-Creation-Date: 2024-12-23 02:37+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal \n"
@@ -18,8 +18,8 @@ msgstr ""
#: accounting/models.py:62 accounting/models.py:101 accounting/models.py:132
#: accounting/models.py:190 club/models.py:55 com/models.py:287
-#: com/models.py:306 counter/models.py:299 counter/models.py:330
-#: counter/models.py:481 forum/models.py:60 launderette/models.py:29
+#: com/models.py:306 counter/models.py:299 counter/models.py:332
+#: counter/models.py:483 forum/models.py:60 launderette/models.py:29
#: launderette/models.py:80 launderette/models.py:116
msgid "name"
msgstr "nom"
@@ -65,8 +65,8 @@ msgid "account number"
msgstr "numéro de compte"
#: accounting/models.py:107 accounting/models.py:136 club/models.py:345
-#: com/models.py:87 com/models.py:272 com/models.py:312 counter/models.py:359
-#: counter/models.py:483 trombi/models.py:209
+#: com/models.py:87 com/models.py:272 com/models.py:312 counter/models.py:361
+#: counter/models.py:485 trombi/models.py:209
msgid "club"
msgstr "club"
@@ -87,12 +87,12 @@ msgstr "Compte club"
msgid "%(club_account)s on %(bank_account)s"
msgstr "%(club_account)s sur %(bank_account)s"
-#: accounting/models.py:188 club/models.py:351 counter/models.py:966
+#: accounting/models.py:188 club/models.py:351 counter/models.py:997
#: election/models.py:16 launderette/models.py:165
msgid "start date"
msgstr "date de début"
-#: accounting/models.py:189 club/models.py:352 counter/models.py:967
+#: accounting/models.py:189 club/models.py:352 counter/models.py:998
#: election/models.py:17
msgid "end date"
msgstr "date de fin"
@@ -106,7 +106,7 @@ msgid "club account"
msgstr "compte club"
#: accounting/models.py:199 accounting/models.py:255 counter/models.py:93
-#: counter/models.py:684
+#: counter/models.py:714
msgid "amount"
msgstr "montant"
@@ -126,20 +126,20 @@ msgstr "numéro"
msgid "journal"
msgstr "classeur"
-#: accounting/models.py:256 core/models.py:913 core/models.py:1422
-#: core/models.py:1467 core/models.py:1496 core/models.py:1520
-#: counter/models.py:694 counter/models.py:798 counter/models.py:1002
+#: accounting/models.py:256 core/models.py:905 core/models.py:1414
+#: core/models.py:1459 core/models.py:1488 core/models.py:1512
+#: counter/models.py:724 counter/models.py:829 counter/models.py:1033
#: eboutic/models.py:57 eboutic/models.py:193 forum/models.py:312
#: forum/models.py:413
msgid "date"
msgstr "date"
-#: accounting/models.py:257 counter/models.py:302 counter/models.py:1003
+#: accounting/models.py:257 counter/models.py:302 counter/models.py:1034
#: pedagogy/models.py:208
msgid "comment"
msgstr "commentaire"
-#: accounting/models.py:259 counter/models.py:696 counter/models.py:800
+#: accounting/models.py:259 counter/models.py:726 counter/models.py:831
#: subscription/models.py:56
msgid "payment method"
msgstr "méthode de paiement"
@@ -165,8 +165,8 @@ msgid "accounting type"
msgstr "type comptable"
#: accounting/models.py:294 accounting/models.py:429 accounting/models.py:460
-#: accounting/models.py:492 core/models.py:1495 core/models.py:1521
-#: counter/models.py:764
+#: accounting/models.py:492 core/models.py:1487 core/models.py:1513
+#: counter/models.py:795
msgid "label"
msgstr "étiquette"
@@ -218,7 +218,7 @@ msgstr "Compte"
msgid "Company"
msgstr "Entreprise"
-#: accounting/models.py:307 core/models.py:303 sith/settings.py:423
+#: accounting/models.py:307 core/models.py:303 sith/settings.py:424
msgid "Other"
msgstr "Autre"
@@ -264,7 +264,7 @@ msgstr ""
"Vous devez fournir soit un type comptable simplifié ou un type comptable "
"standard"
-#: accounting/models.py:421 counter/models.py:340 pedagogy/models.py:41
+#: accounting/models.py:421 counter/models.py:342 pedagogy/models.py:41
msgid "code"
msgstr "code"
@@ -517,7 +517,7 @@ msgid "Effective amount"
msgstr "Montant effectif"
#: accounting/templates/accounting/club_account_details.jinja:36
-#: sith/settings.py:461
+#: sith/settings.py:462
msgid "Closed"
msgstr "Fermé"
@@ -586,7 +586,7 @@ msgstr "Classeur : "
#: accounting/templates/accounting/journal_statement_accounting.jinja:30
#: core/templates/core/user_account.jinja:39
#: core/templates/core/user_account_detail.jinja:9
-#: counter/templates/counter/counter_click.jinja:37
+#: counter/templates/counter/counter_click.jinja:46
msgid "Amount: "
msgstr "Montant : "
@@ -950,11 +950,11 @@ msgstr "Une action est requise"
msgid "You must specify at least an user or an email address"
msgstr "vous devez spécifier au moins un utilisateur ou une adresse email"
-#: club/forms.py:149 counter/forms.py:206
+#: club/forms.py:149 counter/forms.py:207
msgid "Begin date"
msgstr "Date de début"
-#: club/forms.py:152 com/views.py:84 com/views.py:202 counter/forms.py:209
+#: club/forms.py:152 com/views.py:84 com/views.py:202 counter/forms.py:210
#: election/views.py:170 subscription/forms.py:21
msgid "End date"
msgstr "Date de fin"
@@ -1041,7 +1041,7 @@ msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
msgid "A club with that unix_name already exists"
msgstr "Un club avec ce nom UNIX existe déjà."
-#: club/models.py:337 counter/models.py:957 counter/models.py:993
+#: club/models.py:337 counter/models.py:988 counter/models.py:1024
#: eboutic/models.py:53 eboutic/models.py:189 election/models.py:183
#: launderette/models.py:130 launderette/models.py:184 sas/models.py:273
#: trombi/models.py:205
@@ -1054,7 +1054,7 @@ msgid "role"
msgstr "rôle"
#: club/models.py:359 core/models.py:84 counter/models.py:300
-#: counter/models.py:331 election/models.py:13 election/models.py:115
+#: counter/models.py:333 election/models.py:13 election/models.py:115
#: election/models.py:188 forum/models.py:61 forum/models.py:245
msgid "description"
msgstr "description"
@@ -1068,7 +1068,7 @@ msgid "Enter a valid address. Only the root of the address is needed."
msgstr ""
"Entrez une adresse valide. Seule la racine de l'adresse est nécessaire."
-#: club/models.py:427 com/models.py:97 com/models.py:322 core/models.py:914
+#: club/models.py:427 com/models.py:97 com/models.py:322 core/models.py:906
msgid "is moderated"
msgstr "est modéré"
@@ -1217,7 +1217,7 @@ msgid "Barman"
msgstr "Barman"
#: club/templates/club/club_sellings.jinja:51
-#: counter/templates/counter/counter_click.jinja:34
+#: counter/templates/counter/counter_click.jinja:43
#: counter/templates/counter/last_ops.jinja:22
#: counter/templates/counter/last_ops.jinja:47
#: counter/templates/counter/refilling_list.jinja:12
@@ -1454,7 +1454,7 @@ msgstr "contenu"
msgid "A more detailed and exhaustive description of the event."
msgstr "Une description plus détaillée et exhaustive de l'évènement."
-#: com/models.py:82 core/models.py:1465 launderette/models.py:88
+#: com/models.py:82 core/models.py:1457 launderette/models.py:88
#: launderette/models.py:124 launderette/models.py:167
msgid "type"
msgstr "type"
@@ -1508,7 +1508,7 @@ msgstr "weekmail"
msgid "rank"
msgstr "rang"
-#: com/models.py:308 core/models.py:879 core/models.py:929
+#: com/models.py:308 core/models.py:871 core/models.py:921
msgid "file"
msgstr "fichier"
@@ -1762,12 +1762,12 @@ msgstr "Agenda"
msgid "Birthdays"
msgstr "Anniversaires"
-#: com/templates/com/news_list.jinja:145
+#: com/templates/com/news_list.jinja:143
#, python-format
msgid "%(age)s year old"
msgstr "%(age)s ans"
-#: com/templates/com/news_list.jinja:156 com/tests.py:101 com/tests.py:111
+#: com/templates/com/news_list.jinja:153 com/tests.py:101 com/tests.py:111
msgid "You need an up to date subscription to access this content"
msgstr "Votre cotisation doit être à jour pour accéder à cette section"
@@ -1977,15 +1977,15 @@ msgstr "Ce champ est obligatoire."
msgid "An event cannot end before its beginning."
msgstr "Un évènement ne peut pas se finir avant d'avoir commencé."
-#: com/views.py:445
+#: com/views.py:446
msgid "Delete and save to regenerate"
msgstr "Supprimer et sauver pour régénérer"
-#: com/views.py:460
+#: com/views.py:461
msgid "Weekmail of the "
msgstr "Weekmail du "
-#: com/views.py:564
+#: com/views.py:565
msgid ""
"You must be a board member of the selected club to post in the Weekmail."
msgstr ""
@@ -2181,7 +2181,7 @@ msgstr "profil visible par les cotisants"
msgid "A user with that username already exists"
msgstr "Un utilisateur de ce nom d'utilisateur existe déjà"
-#: core/models.py:718 core/templates/core/macros.jinja:80
+#: core/models.py:710 core/templates/core/macros.jinja:80
#: core/templates/core/macros.jinja:84 core/templates/core/macros.jinja:85
#: core/templates/core/user_detail.jinja:100
#: core/templates/core/user_detail.jinja:101
@@ -2201,101 +2201,101 @@ msgstr "Un utilisateur de ce nom d'utilisateur existe déjà"
msgid "Profile"
msgstr "Profil"
-#: core/models.py:829
+#: core/models.py:821
msgid "Visitor"
msgstr "Visiteur"
-#: core/models.py:836
+#: core/models.py:828
msgid "receive the Weekmail"
msgstr "recevoir le Weekmail"
-#: core/models.py:837
+#: core/models.py:829
msgid "show your stats to others"
msgstr "montrez vos statistiques aux autres"
-#: core/models.py:839
+#: core/models.py:831
msgid "get a notification for every click"
msgstr "avoir une notification pour chaque click"
-#: core/models.py:842
+#: core/models.py:834
msgid "get a notification for every refilling"
msgstr "avoir une notification pour chaque rechargement"
-#: core/models.py:868 sas/forms.py:81
+#: core/models.py:860 sas/forms.py:81
msgid "file name"
msgstr "nom du fichier"
-#: core/models.py:872 core/models.py:1223
+#: core/models.py:864 core/models.py:1215
msgid "parent"
msgstr "parent"
-#: core/models.py:886
+#: core/models.py:878
msgid "compressed file"
msgstr "version allégée"
-#: core/models.py:893
+#: core/models.py:885
msgid "thumbnail"
msgstr "miniature"
-#: core/models.py:901 core/models.py:918
+#: core/models.py:893 core/models.py:910
msgid "owner"
msgstr "propriétaire"
-#: core/models.py:905 core/models.py:1240
+#: core/models.py:897 core/models.py:1232
msgid "edit group"
msgstr "groupe d'édition"
-#: core/models.py:908 core/models.py:1243
+#: core/models.py:900 core/models.py:1235
msgid "view group"
msgstr "groupe de vue"
-#: core/models.py:910
+#: core/models.py:902
msgid "is folder"
msgstr "est un dossier"
-#: core/models.py:911
+#: core/models.py:903
msgid "mime type"
msgstr "type mime"
-#: core/models.py:912
+#: core/models.py:904
msgid "size"
msgstr "taille"
-#: core/models.py:923
+#: core/models.py:915
msgid "asked for removal"
msgstr "retrait demandé"
-#: core/models.py:925
+#: core/models.py:917
msgid "is in the SAS"
msgstr "est dans le SAS"
-#: core/models.py:992
+#: core/models.py:984
msgid "Character '/' not authorized in name"
msgstr "Le caractère '/' n'est pas autorisé dans les noms de fichier"
-#: core/models.py:994 core/models.py:998
+#: core/models.py:986 core/models.py:990
msgid "Loop in folder tree"
msgstr "Boucle dans l'arborescence des dossiers"
-#: core/models.py:1001
+#: core/models.py:993
msgid "You can not make a file be a children of a non folder file"
msgstr ""
"Vous ne pouvez pas mettre un fichier enfant de quelque chose qui n'est pas "
"un dossier"
-#: core/models.py:1012
+#: core/models.py:1004
msgid "Duplicate file"
msgstr "Un fichier de ce nom existe déjà"
-#: core/models.py:1029
+#: core/models.py:1021
msgid "You must provide a file"
msgstr "Vous devez fournir un fichier"
-#: core/models.py:1206
+#: core/models.py:1198
msgid "page unix name"
msgstr "nom unix de la page"
-#: core/models.py:1212
+#: core/models.py:1204
msgid ""
"Enter a valid page name. This value may contain only unaccented letters, "
"numbers and ./+/-/_ characters."
@@ -2303,55 +2303,55 @@ msgstr ""
"Entrez un nom de page correct. Uniquement des lettres non accentuées, "
"numéros, et ./+/-/_"
-#: core/models.py:1230
+#: core/models.py:1222
msgid "page name"
msgstr "nom de la page"
-#: core/models.py:1235
+#: core/models.py:1227
msgid "owner group"
msgstr "groupe propriétaire"
-#: core/models.py:1248
+#: core/models.py:1240
msgid "lock user"
msgstr "utilisateur bloquant"
-#: core/models.py:1255
+#: core/models.py:1247
msgid "lock_timeout"
msgstr "décompte du déblocage"
-#: core/models.py:1305
+#: core/models.py:1297
msgid "Duplicate page"
msgstr "Une page de ce nom existe déjà"
-#: core/models.py:1308
+#: core/models.py:1300
msgid "Loop in page tree"
msgstr "Boucle dans l'arborescence des pages"
-#: core/models.py:1419
+#: core/models.py:1411
msgid "revision"
msgstr "révision"
-#: core/models.py:1420
+#: core/models.py:1412
msgid "page title"
msgstr "titre de la page"
-#: core/models.py:1421
+#: core/models.py:1413
msgid "page content"
msgstr "contenu de la page"
-#: core/models.py:1462
+#: core/models.py:1454
msgid "url"
msgstr "url"
-#: core/models.py:1463
+#: core/models.py:1455
msgid "param"
msgstr "param"
-#: core/models.py:1468
+#: core/models.py:1460
msgid "viewed"
msgstr "vue"
-#: core/models.py:1526
+#: core/models.py:1518
msgid "operation type"
msgstr "type d'opération"
@@ -2476,13 +2476,13 @@ msgstr "Forum"
msgid "Gallery"
msgstr "Photos"
-#: core/templates/core/base/navbar.jinja:22 counter/models.py:491
+#: core/templates/core/base/navbar.jinja:22 counter/models.py:493
#: counter/templates/counter/counter_list.jinja:11
#: eboutic/templates/eboutic/eboutic_main.jinja:4
#: eboutic/templates/eboutic/eboutic_main.jinja:23
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:16
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4
-#: sith/settings.py:422 sith/settings.py:430
+#: sith/settings.py:423 sith/settings.py:431
msgid "Eboutic"
msgstr "Eboutic"
@@ -2562,7 +2562,7 @@ msgstr "Confirmation"
#: core/templates/core/delete_confirm.jinja:20
#: core/templates/core/file_delete_confirm.jinja:46
-#: counter/templates/counter/counter_click.jinja:121
+#: counter/templates/counter/counter_click.jinja:125
#: counter/templates/counter/fragments/delete_student_card.jinja:12
#: sas/templates/sas/ask_picture_removal.jinja:20
msgid "Cancel"
@@ -3276,7 +3276,7 @@ msgid "Go to my Trombi tools"
msgstr "Allez à mes outils de Trombi"
#: core/templates/core/user_preferences.jinja:39
-#: counter/templates/counter/counter_click.jinja:144
+#: counter/templates/counter/counter_click.jinja:149
msgid "Student card"
msgstr "Carte étudiante"
@@ -3355,7 +3355,7 @@ msgstr "Cotisations"
msgid "Subscription stats"
msgstr "Statistiques de cotisation"
-#: core/templates/core/user_tools.jinja:48 counter/forms.py:179
+#: core/templates/core/user_tools.jinja:48 counter/forms.py:180
#: counter/views/mixins.py:89
msgid "Counters"
msgstr "Comptoirs"
@@ -3575,21 +3575,21 @@ msgstr "Photos"
msgid "Galaxy"
msgstr "Galaxie"
-#: counter/apps.py:28 sith/settings.py:412 sith/settings.py:419
+#: counter/apps.py:28 sith/settings.py:413 sith/settings.py:420
msgid "Check"
msgstr "Chèque"
-#: counter/apps.py:29 sith/settings.py:413 sith/settings.py:421
+#: counter/apps.py:29 sith/settings.py:414 sith/settings.py:422
msgid "Cash"
msgstr "Espèces"
-#: counter/apps.py:30 counter/models.py:802 sith/settings.py:415
-#: sith/settings.py:420
+#: counter/apps.py:30 counter/models.py:833 sith/settings.py:416
+#: sith/settings.py:421
msgid "Credit card"
msgstr "Carte bancaire"
-#: counter/apps.py:36 counter/models.py:507 counter/models.py:963
-#: counter/models.py:999 launderette/models.py:32
+#: counter/apps.py:36 counter/models.py:509 counter/models.py:994
+#: counter/models.py:1030 launderette/models.py:32
msgid "counter"
msgstr "comptoir"
@@ -3597,7 +3597,7 @@ msgstr "comptoir"
msgid "This UID is invalid"
msgstr "Cet UID est invalide"
-#: counter/forms.py:107
+#: counter/forms.py:108
msgid "User not found"
msgstr "Utilisateur non trouvé"
@@ -3625,7 +3625,7 @@ msgstr "client"
msgid "customers"
msgstr "clients"
-#: counter/models.py:112 counter/views/click.py:68
+#: counter/models.py:112 counter/views/click.py:117
msgid "Not enough money"
msgstr "Solde insuffisant"
@@ -3677,116 +3677,116 @@ msgstr "L'opération qui a vidé le compte."
msgid "A text that will be shown on the eboutic."
msgstr "Un texte qui sera affiché sur l'eboutic."
-#: counter/models.py:311 counter/models.py:335
+#: counter/models.py:311 counter/models.py:337
msgid "product type"
msgstr "type du produit"
-#: counter/models.py:342
+#: counter/models.py:344
msgid "purchase price"
msgstr "prix d'achat"
-#: counter/models.py:343
+#: counter/models.py:345
msgid "Initial cost of purchasing the product"
msgstr "Coût initial d'achat du produit"
-#: counter/models.py:345
+#: counter/models.py:347
msgid "selling price"
msgstr "prix de vente"
-#: counter/models.py:347
+#: counter/models.py:349
msgid "special selling price"
msgstr "prix de vente spécial"
-#: counter/models.py:348
+#: counter/models.py:350
msgid "Price for barmen during their permanence"
msgstr "Prix pour les barmen durant leur permanence"
-#: counter/models.py:356
+#: counter/models.py:358
msgid "icon"
msgstr "icône"
-#: counter/models.py:361
+#: counter/models.py:363
msgid "limit age"
msgstr "âge limite"
-#: counter/models.py:362
+#: counter/models.py:364
msgid "tray price"
msgstr "prix plateau"
-#: counter/models.py:364
+#: counter/models.py:366
msgid "buying groups"
msgstr "groupe d'achat"
-#: counter/models.py:366 election/models.py:50
+#: counter/models.py:368 election/models.py:50
msgid "archived"
msgstr "archivé"
-#: counter/models.py:369 counter/models.py:1097
+#: counter/models.py:371 counter/models.py:1128
msgid "product"
msgstr "produit"
-#: counter/models.py:486
+#: counter/models.py:488
msgid "products"
msgstr "produits"
-#: counter/models.py:489
+#: counter/models.py:491
msgid "counter type"
msgstr "type de comptoir"
-#: counter/models.py:491
+#: counter/models.py:493
msgid "Bar"
msgstr "Bar"
-#: counter/models.py:491
+#: counter/models.py:493
msgid "Office"
msgstr "Bureau"
-#: counter/models.py:494
+#: counter/models.py:496
msgid "sellers"
msgstr "vendeurs"
-#: counter/models.py:502 launderette/models.py:178
+#: counter/models.py:504 launderette/models.py:178
msgid "token"
msgstr "jeton"
-#: counter/models.py:702
+#: counter/models.py:732
msgid "bank"
msgstr "banque"
-#: counter/models.py:704 counter/models.py:805
+#: counter/models.py:734 counter/models.py:836
msgid "is validated"
msgstr "est validé"
-#: counter/models.py:709
+#: counter/models.py:739
msgid "refilling"
msgstr "rechargement"
-#: counter/models.py:782 eboutic/models.py:249
+#: counter/models.py:813 eboutic/models.py:249
msgid "unit price"
msgstr "prix unitaire"
-#: counter/models.py:783 counter/models.py:1077 eboutic/models.py:250
+#: counter/models.py:814 counter/models.py:1108 eboutic/models.py:250
msgid "quantity"
msgstr "quantité"
-#: counter/models.py:802
+#: counter/models.py:833
msgid "Sith account"
msgstr "Compte utilisateur"
-#: counter/models.py:810
+#: counter/models.py:841
msgid "selling"
msgstr "vente"
-#: counter/models.py:914
+#: counter/models.py:945
msgid "Unknown event"
msgstr "Événement inconnu"
-#: counter/models.py:915
+#: counter/models.py:946
#, python-format
msgid "Eticket bought for the event %(event)s"
msgstr "Eticket acheté pour l'événement %(event)s"
-#: counter/models.py:917 counter/models.py:930
+#: counter/models.py:948 counter/models.py:961
#, python-format
msgid ""
"You bought an eticket for the event %(event)s.\n"
@@ -3798,67 +3798,67 @@ msgstr ""
"Vous pouvez également retrouver tous vos e-tickets sur votre page de compte "
"%(url)s."
-#: counter/models.py:968
+#: counter/models.py:999
msgid "last activity date"
msgstr "dernière activité"
-#: counter/models.py:971
+#: counter/models.py:1002
msgid "permanency"
msgstr "permanence"
-#: counter/models.py:1004
+#: counter/models.py:1035
msgid "emptied"
msgstr "coffre vidée"
-#: counter/models.py:1007
+#: counter/models.py:1038
msgid "cash register summary"
msgstr "relevé de caisse"
-#: counter/models.py:1073
+#: counter/models.py:1104
msgid "cash summary"
msgstr "relevé"
-#: counter/models.py:1076
+#: counter/models.py:1107
msgid "value"
msgstr "valeur"
-#: counter/models.py:1079
+#: counter/models.py:1110
msgid "check"
msgstr "chèque"
-#: counter/models.py:1081
+#: counter/models.py:1112
msgid "True if this is a bank check, else False"
msgstr "Vrai si c'est un chèque, sinon Faux."
-#: counter/models.py:1085
+#: counter/models.py:1116
msgid "cash register summary item"
msgstr "élément de relevé de caisse"
-#: counter/models.py:1101
+#: counter/models.py:1132
msgid "banner"
msgstr "bannière"
-#: counter/models.py:1103
+#: counter/models.py:1134
msgid "event date"
msgstr "date de l'événement"
-#: counter/models.py:1105
+#: counter/models.py:1136
msgid "event title"
msgstr "titre de l'événement"
-#: counter/models.py:1107
+#: counter/models.py:1138
msgid "secret"
msgstr "secret"
-#: counter/models.py:1146
+#: counter/models.py:1177
msgid "uid"
msgstr "uid"
-#: counter/models.py:1151 counter/models.py:1156
+#: counter/models.py:1182 counter/models.py:1187
msgid "student card"
msgstr "carte étudiante"
-#: counter/models.py:1157
+#: counter/models.py:1188
msgid "student cards"
msgstr "cartes étudiantes"
@@ -3918,28 +3918,28 @@ msgstr "oui"
msgid "There is no cash register summary in this website."
msgstr "Il n'y a pas de relevé de caisse dans ce site web."
-#: counter/templates/counter/counter_click.jinja:41
+#: counter/templates/counter/counter_click.jinja:55
#: launderette/templates/launderette/launderette_admin.jinja:8
msgid "Selling"
msgstr "Vente"
-#: counter/templates/counter/counter_click.jinja:52
+#: counter/templates/counter/counter_click.jinja:66
msgid "Select a product..."
msgstr "Sélectionnez un produit…"
-#: counter/templates/counter/counter_click.jinja:54
+#: counter/templates/counter/counter_click.jinja:68
msgid "Operations"
msgstr "Opérations"
-#: counter/templates/counter/counter_click.jinja:55
+#: counter/templates/counter/counter_click.jinja:69
msgid "Confirm (FIN)"
msgstr "Confirmer (FIN)"
-#: counter/templates/counter/counter_click.jinja:56
+#: counter/templates/counter/counter_click.jinja:70
msgid "Cancel (ANN)"
msgstr "Annuler (ANN)"
-#: counter/templates/counter/counter_click.jinja:67
+#: counter/templates/counter/counter_click.jinja:81
#: counter/templates/counter/fragments/create_refill.jinja:8
#: counter/templates/counter/fragments/create_student_card.jinja:10
#: counter/templates/counter/invoices_call.jinja:16
@@ -3950,21 +3950,25 @@ msgstr "Annuler (ANN)"
msgid "Go"
msgstr "Valider"
-#: counter/templates/counter/counter_click.jinja:74
+#: counter/templates/counter/counter_click.jinja:89
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:19
msgid "Basket: "
msgstr "Panier : "
-#: counter/templates/counter/counter_click.jinja:115
+#: counter/templates/counter/counter_click.jinja:95
+msgid "This basket is empty"
+msgstr "Votre panier est vide"
+
+#: counter/templates/counter/counter_click.jinja:124
msgid "Finish"
msgstr "Terminer"
-#: counter/templates/counter/counter_click.jinja:125
+#: counter/templates/counter/counter_click.jinja:130
#: counter/templates/counter/refilling_list.jinja:9
msgid "Refilling"
msgstr "Rechargement"
-#: counter/templates/counter/counter_click.jinja:135
+#: counter/templates/counter/counter_click.jinja:140
msgid ""
"As a barman, you are not able to refill any account on your own. An admin "
"should be connected on this counter for that. The customer can refill by "
@@ -3974,6 +3978,10 @@ msgstr ""
"vous même. Un admin doit être connecté sur ce comptoir pour cela. Le client "
"peut recharger son compte en utilisant l'eboutic"
+#: counter/templates/counter/counter_click.jinja:161
+msgid "No products available on this counter for this user"
+msgstr "Pas de produits disponnibles dans ce comptoir pour cet utilisateur"
+
#: counter/templates/counter/counter_list.jinja:4
#: counter/templates/counter/counter_list.jinja:10
msgid "Counter admin list"
@@ -4317,35 +4325,31 @@ msgstr "Montant du chèque"
msgid "Check quantity"
msgstr "Nombre de chèque"
-#: counter/views/click.py:59
-msgid "Too young for that product"
-msgstr "Trop jeune pour ce produit"
+#: counter/views/click.py:77
+msgid "The selected product isn't available for this user"
+msgstr "Le produit sélectionné n'est pas disponnible pour cet utilisateur"
-#: counter/views/click.py:62
-msgid "Not allowed for that product"
-msgstr "Non autorisé pour ce produit"
+#: counter/views/click.py:112
+msgid "Submmited basket is invalid"
+msgstr "Le panier envoyé est invalide"
-#: counter/views/click.py:65
-msgid "No date of birth provided"
-msgstr "Pas de date de naissance renseignée"
-
-#: counter/views/click.py:325
-msgid "You have not enough money to buy all the basket"
-msgstr "Vous n'avez pas assez d'argent pour acheter le panier"
+#: counter/views/click.py:130
+msgid "This user have reached his recording limit"
+msgstr "Cet utilisateur a atteint sa limite de déconsigne"
#: counter/views/eticket.py:120
msgid "people(s)"
msgstr "personne(s)"
-#: counter/views/home.py:74
+#: counter/views/home.py:77
msgid "Bad credentials"
msgstr "Mauvais identifiants"
-#: counter/views/home.py:76
+#: counter/views/home.py:79
msgid "User is not barman"
msgstr "L'utilisateur n'est pas barman."
-#: counter/views/home.py:81
+#: counter/views/home.py:84
msgid "Bad location, someone is already logged in somewhere else"
msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs"
@@ -4947,12 +4951,12 @@ msgid "Washing and drying"
msgstr "Lavage et séchage"
#: launderette/templates/launderette/launderette_book.jinja:27
-#: sith/settings.py:650
+#: sith/settings.py:651
msgid "Washing"
msgstr "Lavage"
#: launderette/templates/launderette/launderette_book.jinja:31
-#: sith/settings.py:650
+#: sith/settings.py:651
msgid "Drying"
msgstr "Séchage"
@@ -5467,372 +5471,372 @@ msgstr "Personne(s)"
msgid "Identify users on pictures"
msgstr "Identifiez les utilisateurs sur les photos"
-#: sith/settings.py:253 sith/settings.py:469
+#: sith/settings.py:253 sith/settings.py:470
msgid "English"
msgstr "Anglais"
-#: sith/settings.py:253 sith/settings.py:468
+#: sith/settings.py:253 sith/settings.py:469
msgid "French"
msgstr "Français"
-#: sith/settings.py:396
+#: sith/settings.py:397
msgid "TC"
msgstr "TC"
-#: sith/settings.py:397
+#: sith/settings.py:398
msgid "IMSI"
msgstr "IMSI"
-#: sith/settings.py:398
+#: sith/settings.py:399
msgid "IMAP"
msgstr "IMAP"
-#: sith/settings.py:399
+#: sith/settings.py:400
msgid "INFO"
msgstr "INFO"
-#: sith/settings.py:400
+#: sith/settings.py:401
msgid "GI"
msgstr "GI"
-#: sith/settings.py:401 sith/settings.py:479
+#: sith/settings.py:402 sith/settings.py:480
msgid "E"
msgstr "E"
-#: sith/settings.py:402
+#: sith/settings.py:403
msgid "EE"
msgstr "EE"
-#: sith/settings.py:403
+#: sith/settings.py:404
msgid "GESC"
msgstr "GESC"
-#: sith/settings.py:404
+#: sith/settings.py:405
msgid "GMC"
msgstr "GMC"
-#: sith/settings.py:405
+#: sith/settings.py:406
msgid "MC"
msgstr "MC"
-#: sith/settings.py:406
+#: sith/settings.py:407
msgid "EDIM"
msgstr "EDIM"
-#: sith/settings.py:407
+#: sith/settings.py:408
msgid "Humanities"
msgstr "Humanités"
-#: sith/settings.py:408
+#: sith/settings.py:409
msgid "N/A"
msgstr "N/A"
-#: sith/settings.py:414
+#: sith/settings.py:415
msgid "Transfert"
msgstr "Virement"
-#: sith/settings.py:427
+#: sith/settings.py:428
msgid "Belfort"
msgstr "Belfort"
-#: sith/settings.py:428
+#: sith/settings.py:429
msgid "Sevenans"
msgstr "Sevenans"
-#: sith/settings.py:429
+#: sith/settings.py:430
msgid "Montbéliard"
msgstr "Montbéliard"
-#: sith/settings.py:449
+#: sith/settings.py:450
msgid "Free"
msgstr "Libre"
-#: sith/settings.py:450
+#: sith/settings.py:451
msgid "CS"
msgstr "CS"
-#: sith/settings.py:451
+#: sith/settings.py:452
msgid "TM"
msgstr "TM"
-#: sith/settings.py:452
+#: sith/settings.py:453
msgid "OM"
msgstr "OM"
-#: sith/settings.py:453
+#: sith/settings.py:454
msgid "QC"
msgstr "QC"
-#: sith/settings.py:454
+#: sith/settings.py:455
msgid "EC"
msgstr "EC"
-#: sith/settings.py:455
+#: sith/settings.py:456
msgid "RN"
msgstr "RN"
-#: sith/settings.py:456
+#: sith/settings.py:457
msgid "ST"
msgstr "ST"
-#: sith/settings.py:457
+#: sith/settings.py:458
msgid "EXT"
msgstr "EXT"
-#: sith/settings.py:462
+#: sith/settings.py:463
msgid "Autumn"
msgstr "Automne"
-#: sith/settings.py:463
+#: sith/settings.py:464
msgid "Spring"
msgstr "Printemps"
-#: sith/settings.py:464
+#: sith/settings.py:465
msgid "Autumn and spring"
msgstr "Automne et printemps"
-#: sith/settings.py:470
+#: sith/settings.py:471
msgid "German"
msgstr "Allemand"
-#: sith/settings.py:471
+#: sith/settings.py:472
msgid "Spanish"
msgstr "Espagnol"
-#: sith/settings.py:475
+#: sith/settings.py:476
msgid "A"
msgstr "A"
-#: sith/settings.py:476
+#: sith/settings.py:477
msgid "B"
msgstr "B"
-#: sith/settings.py:477
+#: sith/settings.py:478
msgid "C"
msgstr "C"
-#: sith/settings.py:478
+#: sith/settings.py:479
msgid "D"
msgstr "D"
-#: sith/settings.py:480
+#: sith/settings.py:481
msgid "FX"
msgstr "FX"
-#: sith/settings.py:481
+#: sith/settings.py:482
msgid "F"
msgstr "F"
-#: sith/settings.py:482
+#: sith/settings.py:483
msgid "Abs"
msgstr "Abs"
-#: sith/settings.py:486
+#: sith/settings.py:487
msgid "Selling deletion"
msgstr "Suppression de vente"
-#: sith/settings.py:487
+#: sith/settings.py:488
msgid "Refilling deletion"
msgstr "Suppression de rechargement"
-#: sith/settings.py:531
+#: sith/settings.py:532
msgid "One semester"
msgstr "Un semestre, 20 €"
-#: sith/settings.py:532
+#: sith/settings.py:533
msgid "Two semesters"
msgstr "Deux semestres, 35 €"
-#: sith/settings.py:534
+#: sith/settings.py:535
msgid "Common core cursus"
msgstr "Cursus tronc commun, 60 €"
-#: sith/settings.py:538
+#: sith/settings.py:539
msgid "Branch cursus"
msgstr "Cursus branche, 60 €"
-#: sith/settings.py:539
+#: sith/settings.py:540
msgid "Alternating cursus"
msgstr "Cursus alternant, 30 €"
-#: sith/settings.py:540
+#: sith/settings.py:541
msgid "Honorary member"
msgstr "Membre honoraire, 0 €"
-#: sith/settings.py:541
+#: sith/settings.py:542
msgid "Assidu member"
msgstr "Membre d'Assidu, 0 €"
-#: sith/settings.py:542
+#: sith/settings.py:543
msgid "Amicale/DOCEO member"
msgstr "Membre de l'Amicale/DOCEO, 0 €"
-#: sith/settings.py:543
+#: sith/settings.py:544
msgid "UT network member"
msgstr "Cotisant du réseau UT, 0 €"
-#: sith/settings.py:544
+#: sith/settings.py:545
msgid "CROUS member"
msgstr "Membres du CROUS, 0 €"
-#: sith/settings.py:545
+#: sith/settings.py:546
msgid "Sbarro/ESTA member"
msgstr "Membre de Sbarro ou de l'ESTA, 20 €"
-#: sith/settings.py:547
+#: sith/settings.py:548
msgid "One semester Welcome Week"
msgstr "Un semestre Welcome Week"
-#: sith/settings.py:551
+#: sith/settings.py:552
msgid "One month for free"
msgstr "Un mois gratuit"
-#: sith/settings.py:552
+#: sith/settings.py:553
msgid "Two months for free"
msgstr "Deux mois gratuits"
-#: sith/settings.py:553
+#: sith/settings.py:554
msgid "Eurok's volunteer"
msgstr "Bénévole Eurockéennes"
-#: sith/settings.py:555
+#: sith/settings.py:556
msgid "Six weeks for free"
msgstr "6 semaines gratuites"
-#: sith/settings.py:559
+#: sith/settings.py:560
msgid "One day"
msgstr "Un jour"
-#: sith/settings.py:560
+#: sith/settings.py:561
msgid "GA staff member"
msgstr "Membre staff GA (2 semaines), 1 €"
-#: sith/settings.py:563
+#: sith/settings.py:564
msgid "One semester (-20%)"
msgstr "Un semestre (-20%), 12 €"
-#: sith/settings.py:568
+#: sith/settings.py:569
msgid "Two semesters (-20%)"
msgstr "Deux semestres (-20%), 22 €"
-#: sith/settings.py:573
+#: sith/settings.py:574
msgid "Common core cursus (-20%)"
msgstr "Cursus tronc commun (-20%), 36 €"
-#: sith/settings.py:578
+#: sith/settings.py:579
msgid "Branch cursus (-20%)"
msgstr "Cursus branche (-20%), 36 €"
-#: sith/settings.py:583
+#: sith/settings.py:584
msgid "Alternating cursus (-20%)"
msgstr "Cursus alternant (-20%), 24 €"
-#: sith/settings.py:589
+#: sith/settings.py:590
msgid "One year for free(CA offer)"
msgstr "Une année offerte (Offre CA)"
-#: sith/settings.py:609
+#: sith/settings.py:610
msgid "President"
msgstr "Président⸱e"
-#: sith/settings.py:610
+#: sith/settings.py:611
msgid "Vice-President"
msgstr "Vice-Président⸱e"
-#: sith/settings.py:611
+#: sith/settings.py:612
msgid "Treasurer"
msgstr "Trésorier⸱e"
-#: sith/settings.py:612
+#: sith/settings.py:613
msgid "Communication supervisor"
msgstr "Responsable communication"
-#: sith/settings.py:613
+#: sith/settings.py:614
msgid "Secretary"
msgstr "Secrétaire"
-#: sith/settings.py:614
+#: sith/settings.py:615
msgid "IT supervisor"
msgstr "Responsable info"
-#: sith/settings.py:615
+#: sith/settings.py:616
msgid "Board member"
msgstr "Membre du bureau"
-#: sith/settings.py:616
+#: sith/settings.py:617
msgid "Active member"
msgstr "Membre actif⸱ve"
-#: sith/settings.py:617
+#: sith/settings.py:618
msgid "Curious"
msgstr "Curieux⸱euse"
-#: sith/settings.py:654
+#: sith/settings.py:655
msgid "A new poster needs to be moderated"
msgstr "Une nouvelle affiche a besoin d'être modérée"
-#: sith/settings.py:655
+#: sith/settings.py:656
msgid "A new mailing list needs to be moderated"
msgstr "Une nouvelle mailing list a besoin d'être modérée"
-#: sith/settings.py:658
+#: sith/settings.py:659
msgid "A new pedagogy comment has been signaled for moderation"
msgstr ""
"Un nouveau commentaire de la pédagogie a été signalé pour la modération"
-#: sith/settings.py:660
+#: sith/settings.py:661
#, python-format
msgid "There are %s fresh news to be moderated"
msgstr "Il y a %s nouvelles toutes fraîches à modérer"
-#: sith/settings.py:661
+#: sith/settings.py:662
msgid "New files to be moderated"
msgstr "Nouveaux fichiers à modérer"
-#: sith/settings.py:662
+#: sith/settings.py:663
#, python-format
msgid "There are %s pictures to be moderated in the SAS"
msgstr "Il y a %s photos à modérer dans le SAS"
-#: sith/settings.py:663
+#: sith/settings.py:664
msgid "You've been identified on some pictures"
msgstr "Vous avez été identifié sur des photos"
-#: sith/settings.py:664
+#: sith/settings.py:665
#, python-format
msgid "You just refilled of %s €"
msgstr "Vous avez rechargé votre compte de %s€"
-#: sith/settings.py:665
+#: sith/settings.py:666
#, python-format
msgid "You just bought %s"
msgstr "Vous avez acheté %s"
-#: sith/settings.py:666
+#: sith/settings.py:667
msgid "You have a notification"
msgstr "Vous avez une notification"
-#: sith/settings.py:678
+#: sith/settings.py:679
msgid "Success!"
msgstr "Succès !"
-#: sith/settings.py:679
+#: sith/settings.py:680
msgid "Fail!"
msgstr "Échec !"
-#: sith/settings.py:680
+#: sith/settings.py:681
msgid "You successfully posted an article in the Weekmail"
msgstr "Article posté avec succès dans le Weekmail"
-#: sith/settings.py:681
+#: sith/settings.py:682
msgid "You successfully edited an article in the Weekmail"
msgstr "Article édité avec succès dans le Weekmail"
-#: sith/settings.py:682
+#: sith/settings.py:683
msgid "You successfully sent the Weekmail"
msgstr "Weekmail envoyé avec succès"
-#: sith/settings.py:690
+#: sith/settings.py:691
msgid "AE tee-shirt"
msgstr "Tee-shirt AE"
@@ -6205,3 +6209,15 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
#, python-format
msgid "Maximum characters: %(max_length)s"
msgstr "Nombre de caractères max: %(max_length)s"
+
+#~ msgid "Too young for that product"
+#~ msgstr "Trop jeune pour ce produit"
+
+#~ msgid "Not allowed for that product"
+#~ msgstr "Non autorisé pour ce produit"
+
+#~ msgid "No date of birth provided"
+#~ msgstr "Pas de date de naissance renseignée"
+
+#~ msgid "You have not enough money to buy all the basket"
+#~ msgstr "Vous n'avez pas assez d'argent pour acheter le panier"
diff --git a/locale/fr/LC_MESSAGES/djangojs.po b/locale/fr/LC_MESSAGES/djangojs.po
index 26ff64c3..7412eac5 100644
--- a/locale/fr/LC_MESSAGES/djangojs.po
+++ b/locale/fr/LC_MESSAGES/djangojs.po
@@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-18 16:26+0100\n"
+"POT-Creation-Date: 2024-12-23 02:38+0100\n"
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
"Last-Translator: Sli \n"
"Language-Team: AE info \n"
@@ -122,35 +122,43 @@ msgstr "photos.%(extension)s"
msgid "captured.%s"
msgstr "capture.%s"
-#: counter/static/bundled/counter/product-list-index.ts:39
+#: counter/static/bundled/counter/counter-click-index.ts:60
+msgid "Not enough money"
+msgstr "Pas assez d'argent"
+
+#: counter/static/bundled/counter/counter-click-index.ts:113
+msgid "You can't send an empty basket."
+msgstr "Vous ne pouvez pas envoyer un panier vide."
+
+#: counter/static/bundled/counter/product-list-index.ts:40
msgid "name"
msgstr "nom"
-#: counter/static/bundled/counter/product-list-index.ts:42
+#: counter/static/bundled/counter/product-list-index.ts:43
msgid "product type"
msgstr "type de produit"
-#: counter/static/bundled/counter/product-list-index.ts:44
+#: counter/static/bundled/counter/product-list-index.ts:45
msgid "limit age"
msgstr "limite d'âge"
-#: counter/static/bundled/counter/product-list-index.ts:45
+#: counter/static/bundled/counter/product-list-index.ts:46
msgid "purchase price"
msgstr "prix d'achat"
-#: counter/static/bundled/counter/product-list-index.ts:46
+#: counter/static/bundled/counter/product-list-index.ts:47
msgid "selling price"
msgstr "prix de vente"
-#: counter/static/bundled/counter/product-list-index.ts:47
+#: counter/static/bundled/counter/product-list-index.ts:48
msgid "archived"
msgstr "archivé"
-#: counter/static/bundled/counter/product-list-index.ts:116
+#: counter/static/bundled/counter/product-list-index.ts:125
msgid "Uncategorized"
msgstr "Sans catégorie"
-#: counter/static/bundled/counter/product-list-index.ts:134
+#: counter/static/bundled/counter/product-list-index.ts:143
msgid "products.csv"
msgstr "produits.csv"
From 139221dd228814fd2827d4945a53db851f45ec99 Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 15:15:24 +0100
Subject: [PATCH 27/32] Apply review comments
---
.../bundled/counter/counter-click-index.ts | 4 ++--
counter/static/bundled/counter/types.d.ts | 2 ++
counter/tests/test_counter.py | 23 ++++++-------------
3 files changed, 11 insertions(+), 18 deletions(-)
diff --git a/counter/static/bundled/counter/counter-click-index.ts b/counter/static/bundled/counter/counter-click-index.ts
index 5755b7c7..23a8105d 100644
--- a/counter/static/bundled/counter/counter-click-index.ts
+++ b/counter/static/bundled/counter/counter-click-index.ts
@@ -1,6 +1,6 @@
import { exportToHtml } from "#core:utils/globals";
import { BasketItem } from "#counter:counter/basket";
-import type { CounterConfig } from "#counter:counter/types";
+import type { CounterConfig, ErrorMessage } from "#counter:counter/types";
exportToHtml("loadCounter", (config: CounterConfig) => {
document.addEventListener("alpine:init", () => {
@@ -38,7 +38,7 @@ exportToHtml("loadCounter", (config: CounterConfig) => {
delete this.basket[id];
},
- addToBasket(id: string, quantity: number): string {
+ addToBasket(id: string, quantity: number): ErrorMessage {
const item: BasketItem =
this.basket[id] || new BasketItem(config.products[id], 0);
diff --git a/counter/static/bundled/counter/types.d.ts b/counter/static/bundled/counter/types.d.ts
index b28cb01a..4a22a916 100644
--- a/counter/static/bundled/counter/types.d.ts
+++ b/counter/static/bundled/counter/types.d.ts
@@ -1,3 +1,5 @@
+type ErrorMessage = string;
+
export interface InitialFormData {
/* Used to refill the form when the backend raises an error */
id?: keyof Record;
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index a62e5fab..1b378d5b 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -30,7 +30,7 @@ from freezegun import freeze_time
from model_bakery import baker
from club.models import Club, Membership
-from core.baker_recipes import board_user, subscriber_user
+from core.baker_recipes import board_user, subscriber_user, very_old_subscriber_user
from core.models import Group, User
from counter.baker_recipes import product_recipe
from counter.models import (
@@ -43,7 +43,7 @@ from counter.models import (
)
-class FullClickSetup:
+class TestFullClickBase(TestCase):
@classmethod
def setUpTestData(cls):
cls.customer = subscriber_user.make()
@@ -54,8 +54,7 @@ class FullClickSetup:
cls.subscriber = subscriber_user.make()
cls.counter = baker.make(Counter, type="BAR")
- cls.counter.sellers.add(cls.barmen)
- cls.counter.sellers.add(cls.board_admin)
+ cls.counter.sellers.add(cls.barmen, cls.board_admin)
cls.other_counter = baker.make(Counter, type="BAR")
cls.other_counter.sellers.add(cls.barmen)
@@ -67,10 +66,7 @@ class FullClickSetup:
sub.subscription_end = localdate() - timedelta(days=89)
sub.save()
- cls.customer_old_can_not_buy = subscriber_user.make()
- sub = cls.customer_old_can_not_buy.subscriptions.first()
- sub.subscription_end = localdate() - timedelta(days=90)
- sub.save()
+ cls.customer_old_can_not_buy = very_old_subscriber_user.make()
cls.customer_can_not_buy = baker.make(User)
@@ -89,7 +85,7 @@ class FullClickSetup:
return user.customer.amount
-class TestRefilling(FullClickSetup, TestCase):
+class TestRefilling(TestFullClickBase):
def login_in_bar(self, barmen: User | None = None):
used_barman = barmen if barmen is not None else self.board_admin
self.client.post(
@@ -218,7 +214,7 @@ class BasketItem:
}
-class TestCounterClick(FullClickSetup, TestCase):
+class TestCounterClick(TestFullClickBase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
@@ -257,16 +253,11 @@ class TestCounterClick(FullClickSetup, TestCase):
limit_age=0, selling_price="1.5", special_selling_price="1"
)
- cls.counter.products.add(cls.beer)
- cls.counter.products.add(cls.beer_tap)
- cls.counter.products.add(cls.snack)
- cls.counter.save()
+ cls.counter.products.add(cls.beer, cls.beer_tap, cls.snack)
cls.other_counter.products.add(cls.snack)
- cls.other_counter.save()
cls.club_counter.products.add(cls.stamps)
- cls.club_counter.save()
def login_in_bar(self, barmen: User | None = None):
used_barman = barmen if barmen is not None else self.barmen
From c80fe094a255ec0dc1a2c03d4b6633722f989a0a Mon Sep 17 00:00:00 2001
From: Sli
Date: Mon, 23 Dec 2024 20:44:49 +0100
Subject: [PATCH 28/32] Remove useless form elements in counters and improve
alignment
---
core/static/core/style.scss | 10 ++++++++++
counter/templates/counter/counter_click.jinja | 11 +++++------
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/core/static/core/style.scss b/core/static/core/style.scss
index d413b656..06feea50 100644
--- a/core/static/core/style.scss
+++ b/core/static/core/style.scss
@@ -1254,6 +1254,16 @@ u,
flex-wrap: wrap;
flex-direction: row-reverse;
+ .quantity {
+ display: inline-block;
+ min-width: 1.2em;
+ text-align: center;
+ }
+
+ .remove-item {
+ float: right;
+ }
+
#products {
flex-basis: 100%;
margin: 0.2em;
diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja
index 36ab3ad2..37356ea0 100644
--- a/counter/templates/counter/counter_click.jinja
+++ b/counter/templates/counter/counter_click.jinja
@@ -56,12 +56,8 @@
{% set counter_click_url = url('counter:click', counter_id=counter.id, user_id=customer.user_id) %}
- {# Formulaire pour rechercher un produit en tapant son code dans une barre de recherche #}
- {% csrf_token %}
-
-
@@ -87,7 +83,9 @@
{% endfor %}
{% trans %}Basket: {% endtrans %}
+
+
{% csrf_token %}
{{ form.management_form }}
@@ -101,13 +99,14 @@
-
-
+
+
:
€
-
+
+
From 138e1662c72a4a9f897163f4c2511c307194623d Mon Sep 17 00:00:00 2001
From: Sli
Date: Tue, 24 Dec 2024 00:29:23 +0100
Subject: [PATCH 29/32] Add popup css class and display basket error messages
with it on counter click
---
core/static/core/style.scss | 21 ++++++++++++++++++-
counter/templates/counter/counter_click.jinja | 2 +-
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/core/static/core/style.scss b/core/static/core/style.scss
index 06feea50..2f12d380 100644
--- a/core/static/core/style.scss
+++ b/core/static/core/style.scss
@@ -337,12 +337,31 @@ body {
margin-left: -125px;
box-sizing: border-box;
position: fixed;
- z-index: 1;
+ z-index: 10;
+ /* to get on top of tomselect */
left: 50%;
top: 60px;
text-align: center;
}
+ .popup {
+ z-index: 10;
+ /* to get on top of tomselect */
+ display: inline-block;
+ text-align: center;
+ overflow: auto;
+ margin: auto;
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ height: 20vh;
+ width: 20vw;
+ align-content: center;
+ }
+
+
.tabs {
border-radius: 5px;
diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja
index 37356ea0..b4257b76 100644
--- a/counter/templates/counter/counter_click.jinja
+++ b/counter/templates/counter/counter_click.jinja
@@ -33,7 +33,7 @@