diff --git a/core/templates/core/user_preferences.jinja b/core/templates/core/user_preferences.jinja
index 0cf4bd57..ac41944e 100644
--- a/core/templates/core/user_preferences.jinja
+++ b/core/templates/core/user_preferences.jinja
@@ -36,34 +36,31 @@
{% if profile.customer %}
-
{% trans %}Student cards{% endtrans %}
+ {% trans %}Student card{% endtrans %}
- {% if profile.customer.student_cards.exists() %}
-
+ {% if profile.customer.student_card %}
+
+ {% trans %}Registered{% endtrans %} -
+
+ {% trans %}Delete{% endtrans %}
+
+
{% else %}
{% trans %}No student card registered.{% endtrans %}
{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
+
{% endif %}
-
-
{% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/counter/forms.py b/counter/forms.py
index 84a92512..6de8bf70 100644
--- a/counter/forms.py
+++ b/counter/forms.py
@@ -52,9 +52,7 @@ class StudentCardForm(forms.ModelForm):
class Meta:
model = StudentCard
fields = ["uid"]
- widgets = {
- "uid": NFCTextInput,
- }
+ widgets = {"uid": NFCTextInput}
def clean(self):
cleaned_data = super().clean()
diff --git a/counter/migrations/0025_alter_studentcard_customer.py b/counter/migrations/0025_alter_studentcard_customer.py
new file mode 100644
index 00000000..f15bb39e
--- /dev/null
+++ b/counter/migrations/0025_alter_studentcard_customer.py
@@ -0,0 +1,52 @@
+# Generated by Django 4.2.16 on 2024-11-15 12:34
+from __future__ import annotations
+
+from operator import attrgetter
+from typing import TYPE_CHECKING
+
+import django.db.models.deletion
+from django.db import migrations, models
+from django.db.models import Count
+
+if TYPE_CHECKING:
+ from django.db.backends.postgresql.schema import DatabaseSchemaEditor
+ from django.db.migrations.state import StateApps
+
+
+def delete_duplicates(apps: StateApps, schema_editor: DatabaseSchemaEditor):
+ """Delete cards of users with more than one student cards.
+
+ For all users who have more than one registered student card, all
+ the cards except the last one are deleted.
+ """
+ Customer = apps.get_model("counter", "Customer")
+ StudentCard = apps.get_model("counter", "StudentCard")
+ customers = (
+ Customer.objects.annotate(nb_cards=Count("student_cards"))
+ .filter(nb_cards__gt=1)
+ .prefetch_related("student_cards")
+ )
+ to_delete = [
+ card.id
+ for customer in customers
+ for card in sorted(customer.student_cards.all(), key=attrgetter("id"))[:-1]
+ ]
+ StudentCard.objects.filter(id__in=to_delete).delete()
+
+
+class Migration(migrations.Migration):
+ dependencies = [("counter", "0024_accountdump_accountdump_unique_ongoing_dump")]
+
+ operations = [
+ migrations.RunPython(delete_duplicates, migrations.RunPython.noop),
+ migrations.AlterField(
+ model_name="studentcard",
+ name="customer",
+ field=models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="student_card",
+ to="counter.customer",
+ verbose_name="student card",
+ ),
+ ),
+ ]
diff --git a/counter/models.py b/counter/models.py
index 1666572b..281f2069 100644
--- a/counter/models.py
+++ b/counter/models.py
@@ -1132,12 +1132,10 @@ class StudentCard(models.Model):
uid = models.CharField(
_("uid"), max_length=UID_SIZE, unique=True, validators=[MinLengthValidator(4)]
)
- customer = models.ForeignKey(
+ customer = models.OneToOneField(
Customer,
- related_name="student_cards",
- verbose_name=_("student cards"),
- null=False,
- blank=False,
+ related_name="student_card",
+ verbose_name=_("student card"),
on_delete=models.CASCADE,
)
@@ -1145,7 +1143,7 @@ class StudentCard(models.Model):
return self.uid
@staticmethod
- def is_valid(uid):
+ def is_valid(uid: str):
return (
(uid.isupper() or uid.isnumeric())
and len(uid) == StudentCard.UID_SIZE
diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja
index cb6bb9cf..72c0a872 100644
--- a/counter/templates/counter/counter_click.jinja
+++ b/counter/templates/counter/counter_click.jinja
@@ -29,26 +29,26 @@
{{ user_mini_profile(customer.user) }}
{{ user_subscription(customer.user) }}
{% trans %}Amount: {% endtrans %}{{ customer.amount }} €
-
- {% trans %}Registered cards{% endtrans %}
- {% if student_cards %}
-
-
- {% for card in student_cards %}
- - {{ card.uid }}
- {% endfor %}
-
+ {% trans %}Student card{% endtrans %}
+ {% if student_card %}
+
+ {% trans %}Registered{% endtrans %} -
+
+ {% trans %}Delete{% endtrans %}
+
+
{% else %}
{% trans %}No card registered{% endtrans %}
+
{% endif %}
diff --git a/counter/tests/test_customer.py b/counter/tests/test_customer.py
index 2d7e1c60..74a1e79a 100644
--- a/counter/tests/test_customer.py
+++ b/counter/tests/test_customer.py
@@ -1,3 +1,4 @@
+import itertools
import json
import string
@@ -188,216 +189,102 @@ class TestStudentCard(TestCase):
)
def test_add_student_card_from_counter(self):
- # Test card with mixed letters and numbers
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "8B90734A802A8F", "action": "add_student_card"},
- )
- self.assertContains(response, text="8B90734A802A8F")
-
- # Test card with only numbers
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "04786547890123", "action": "add_student_card"},
- )
- self.assertContains(response, text="04786547890123")
-
- # Test card with only letters
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "ABCAAAFAAFAAAB", "action": "add_student_card"},
- )
- self.assertContains(response, text="ABCAAAFAAFAAAB")
+ for uid in ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]:
+ self.sli.customer.student_card.delete()
+ self.client.post(
+ reverse(
+ "counter:click",
+ kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
+ ),
+ {"student_card_uid": uid, "action": "add_student_card"},
+ )
+ self.sli.customer.refresh_from_db()
+ assert self.sli.customer.student_card.uid == uid
def test_add_student_card_from_counter_fail(self):
# UID too short
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "8B90734A802A8", "action": "add_student_card"},
- )
- self.assertContains(
- response, text="Ce n'est pas un UID de carte étudiante valide"
- )
-
- # UID too long
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "8B90734A802A8FA", "action": "add_student_card"},
- )
- self.assertContains(
- response, text="Ce n'est pas un UID de carte étudiante valide"
- )
-
- # Test with already existing card
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "9A89B82018B0A0", "action": "add_student_card"},
- )
- self.assertContains(
- response, text="Ce n'est pas un UID de carte étudiante valide"
- )
-
- # Test with lowercase
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": "8b90734a802a9f", "action": "add_student_card"},
- )
- self.assertContains(
- response, text="Ce n'est pas un UID de carte étudiante valide"
- )
-
- # Test with white spaces
- response = self.client.post(
- reverse(
- "counter:click",
- kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
- ),
- {"student_card_uid": " ", "action": "add_student_card"},
- )
- self.assertContains(
- response, text="Ce n'est pas un UID de carte étudiante valide"
- )
+ self.sli.customer.student_card.delete()
+ # too short, then too long, then containing lowercase, then spaces
+ for uid in ["8B90734A802A8", "8B90734A802A8FA", "8b90734a802a9f", " " * 14]:
+ response = self.client.post(
+ reverse(
+ "counter:click",
+ kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
+ ),
+ {"student_card_uid": uid, "action": "add_student_card"},
+ )
+ self.assertContains(
+ response, text="Ce n'est pas un UID de carte étudiante valide"
+ )
+ self.sli.customer.refresh_from_db()
+ assert not hasattr(self.sli.customer, "student_card")
def test_delete_student_card_with_owner(self):
self.client.force_login(self.sli)
self.client.post(
reverse(
"counter:delete_student_card",
- kwargs={
- "customer_id": self.sli.customer.pk,
- "card_id": self.sli.customer.student_cards.first().id,
- },
+ kwargs={"customer_id": self.sli.customer.pk},
)
)
- assert not self.sli.customer.student_cards.exists()
+ self.sli.customer.refresh_from_db()
+ assert not hasattr(self.sli.customer, "student_card")
def test_delete_student_card_with_board_member(self):
self.client.force_login(self.skia)
self.client.post(
reverse(
"counter:delete_student_card",
- kwargs={
- "customer_id": self.sli.customer.pk,
- "card_id": self.sli.customer.student_cards.first().id,
- },
+ kwargs={"customer_id": self.sli.customer.pk},
)
)
- assert not self.sli.customer.student_cards.exists()
+ self.sli.customer.refresh_from_db()
+ assert not hasattr(self.sli.customer, "student_card")
def test_delete_student_card_with_root(self):
self.client.force_login(self.root)
self.client.post(
reverse(
"counter:delete_student_card",
- kwargs={
- "customer_id": self.sli.customer.pk,
- "card_id": self.sli.customer.student_cards.first().id,
- },
+ kwargs={"customer_id": self.sli.customer.pk},
)
)
- assert not self.sli.customer.student_cards.exists()
+ self.sli.customer.refresh_from_db()
+ assert not hasattr(self.sli.customer, "student_card")
def test_delete_student_card_fail(self):
self.client.force_login(self.krophil)
response = self.client.post(
reverse(
"counter:delete_student_card",
- kwargs={
- "customer_id": self.sli.customer.pk,
- "card_id": self.sli.customer.student_cards.first().id,
- },
+ kwargs={"customer_id": self.sli.customer.pk},
)
)
assert response.status_code == 403
- assert self.sli.customer.student_cards.exists()
+ self.sli.customer.refresh_from_db()
+ assert hasattr(self.sli.customer, "student_card")
def test_add_student_card_from_user_preferences(self):
# Test with owner of the card
- self.client.force_login(self.sli)
- self.client.post(
- reverse(
- "counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
- ),
- {"uid": "8B90734A802A8F"},
- )
+ users = [self.sli, self.skia, self.root]
+ uids = ["8B90734A802A8F", "ABCAAAFAAFAAAB", "15248196326518"]
+ for user, uid in itertools.product(users, uids):
+ self.sli.customer.student_card.delete()
+ self.client.force_login(user)
+ self.client.post(
+ reverse(
+ "counter:add_student_card",
+ kwargs={"customer_id": self.sli.customer.pk},
+ ),
+ {"uid": uid},
+ )
- response = self.client.get(
- reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
- )
- self.assertContains(response, text="8B90734A802A8F")
-
- # Test with board member
- self.client.force_login(self.skia)
- self.client.post(
- reverse(
- "counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
- ),
- {"uid": "8B90734A802A8A"},
- )
-
- response = self.client.get(
- reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
- )
- self.assertContains(response, text="8B90734A802A8A")
-
- # Test card with only numbers
- self.client.post(
- reverse(
- "counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
- ),
- {"uid": "04786547890123"},
- )
- response = self.client.get(
- reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
- )
- self.assertContains(response, text="04786547890123")
-
- # Test card with only letters
- self.client.post(
- reverse(
- "counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
- ),
- {"uid": "ABCAAAFAAFAAAB"},
- )
- response = self.client.get(
- reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
- )
- self.assertContains(response, text="ABCAAAFAAFAAAB")
-
- # Test with root
- self.client.force_login(self.root)
- self.client.post(
- reverse(
- "counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
- ),
- {"uid": "8B90734A802A8B"},
- )
-
- response = self.client.get(
- reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
- )
- self.assertContains(response, text="8B90734A802A8B")
+ response = self.client.get(
+ reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
+ )
+ self.sli.customer.refresh_from_db()
+ assert self.sli.customer.student_card.uid == uid
+ self.assertContains(response, text="Enregistré")
def test_add_student_card_from_user_preferences_fail(self):
self.client.force_login(self.sli)
diff --git a/counter/urls.py b/counter/urls.py
index a2732925..92582935 100644
--- a/counter/urls.py
+++ b/counter/urls.py
@@ -74,7 +74,7 @@ urlpatterns = [
name="add_student_card",
),
path(
- "customer//card/delete//",
+ "customer//card/delete",
StudentCardDeleteView.as_view(),
name="delete_student_card",
),
diff --git a/counter/views.py b/counter/views.py
index 9483d335..b44e9ea1 100644
--- a/counter/views.py
+++ b/counter/views.py
@@ -111,16 +111,23 @@ class StudentCardDeleteView(DeleteView, CanEditMixin):
model = StudentCard
template_name = "core/delete_confirm.jinja"
- pk_url_kwarg = "card_id"
def dispatch(self, request, *args, **kwargs):
self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
return super().dispatch(request, *args, **kwargs)
+ def get_object(self, queryset=None):
+ if not hasattr(self.customer, "student_card"):
+ raise Http404(
+ _(
+ "The customer %s has no registered student card",
+ self.customer.user.get_full_name(),
+ )
+ )
+ return self.customer.student_card
+
def get_success_url(self, **kwargs):
- return reverse_lazy(
- "core:user_prefs", kwargs={"user_id": self.customer.user.pk}
- )
+ return reverse("core:user_prefs", kwargs={"user_id": self.customer.user_id})
class CounterTabsMixin(TabedViewMixin):
@@ -627,7 +634,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
product
)
kwargs["customer"] = self.customer
- kwargs["student_cards"] = self.customer.student_cards.all()
+ kwargs["student_card"] = getattr(self.customer, "student_card", None)
kwargs["student_card_input"] = NFCCardForm()
kwargs["basket_total"] = self.sum_basket(self.request)
kwargs["refill_form"] = self.refill_form or RefillForm()
@@ -1527,11 +1534,10 @@ class StudentCardFormView(FormView):
def form_valid(self, form):
data = form.clean()
- res = super(FormView, self).form_valid(form)
- StudentCard(customer=self.customer, uid=data["uid"]).save()
- return res
+ StudentCard.objects.update_or_create(
+ customer=self.customer, defaults={"uid": data["uid"]}
+ )
+ return super(FormView, self).form_valid(form)
def get_success_url(self, **kwargs):
- return reverse_lazy(
- "core:user_prefs", kwargs={"user_id": self.customer.user.pk}
- )
+ return reverse("core:user_prefs", kwargs={"user_id": self.customer.user_id})
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index d357d31c..00548c2c 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-11-14 10:26+0100\n"
+"POT-Creation-Date: 2024-11-15 13:03+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal \n"
@@ -369,7 +369,8 @@ msgstr "Compte en banque : "
#: core/templates/core/user_clubs.jinja:34
#: core/templates/core/user_clubs.jinja:63
#: core/templates/core/user_edit.jinja:62
-#: core/templates/core/user_preferences.jinja:48
+#: core/templates/core/user_preferences.jinja:45
+#: counter/templates/counter/counter_click.jinja:37
#: counter/templates/counter/last_ops.jinja:35
#: counter/templates/counter/last_ops.jinja:65
#: election/templates/election/election_detail.jinja:187
@@ -650,7 +651,7 @@ msgid "Done"
msgstr "Effectuées"
#: accounting/templates/accounting/journal_details.jinja:41
-#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:955
+#: counter/templates/counter/cash_summary_list.jinja:37 counter/views.py:957
#: pedagogy/templates/pedagogy/moderation.jinja:13
#: pedagogy/templates/pedagogy/uv_detail.jinja:142
#: trombi/templates/trombi/comment.jinja:4
@@ -771,7 +772,7 @@ msgstr "Opération liée : "
#: core/templates/core/user_godfathers_tree.jinja:85
#: core/templates/core/user_preferences.jinja:18
#: core/templates/core/user_preferences.jinja:27
-#: core/templates/core/user_preferences.jinja:65
+#: core/templates/core/user_preferences.jinja:61
#: counter/templates/counter/cash_register_summary.jinja:28
#: forum/templates/forum/reply.jinja:39
#: subscription/templates/subscription/subscription.jinja:25
@@ -951,11 +952,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:203
+#: club/forms.py:149 counter/forms.py:201
msgid "Begin date"
msgstr "Date de début"
-#: club/forms.py:152 com/views.py:84 com/views.py:202 counter/forms.py:206
+#: club/forms.py:152 com/views.py:84 com/views.py:202 counter/forms.py:204
#: election/views.py:170 subscription/views.py:38
msgid "End date"
msgstr "Date de fin"
@@ -963,15 +964,15 @@ msgstr "Date de fin"
#: club/forms.py:156 club/templates/club/club_sellings.jinja:49
#: core/templates/core/user_account_detail.jinja:17
#: core/templates/core/user_account_detail.jinja:56
-#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:137
+#: counter/templates/counter/cash_summary_list.jinja:33 counter/views.py:139
msgid "Counter"
msgstr "Comptoir"
-#: club/forms.py:163 counter/views.py:683
+#: club/forms.py:163 counter/views.py:685
msgid "Products"
msgstr "Produits"
-#: club/forms.py:168 counter/views.py:688
+#: club/forms.py:168 counter/views.py:690
msgid "Archived products"
msgstr "Produits archivés"
@@ -3041,11 +3042,11 @@ msgid "Eboutic invoices"
msgstr "Facture eboutic"
#: core/templates/core/user_account.jinja:54
-#: core/templates/core/user_tools.jinja:58 counter/views.py:708
+#: core/templates/core/user_tools.jinja:58 counter/views.py:710
msgid "Etickets"
msgstr "Etickets"
-#: core/templates/core/user_account.jinja:69 core/views/user.py:638
+#: core/templates/core/user_account.jinja:69 core/views/user.py:634
msgid "User has no account"
msgstr "L'utilisateur n'a pas de compte"
@@ -3295,14 +3296,20 @@ msgid "Go to my Trombi tools"
msgstr "Allez à mes outils de Trombi"
#: core/templates/core/user_preferences.jinja:39
-msgid "Student cards"
-msgstr "Cartes étudiante"
+#: counter/templates/counter/counter_click.jinja:32
+msgid "Student card"
+msgstr "Carte étudiante"
-#: core/templates/core/user_preferences.jinja:54
+#: core/templates/core/user_preferences.jinja:43
+#: counter/templates/counter/counter_click.jinja:35
+msgid "Registered"
+msgstr "Enregistré"
+
+#: core/templates/core/user_preferences.jinja:49
msgid "No student card registered."
msgstr "Aucune carte étudiante enregistrée."
-#: core/templates/core/user_preferences.jinja:56
+#: core/templates/core/user_preferences.jinja:51
msgid ""
"You can add a card by asking at a counter or add it yourself here. If you "
"want to manually\n"
@@ -3376,8 +3383,8 @@ msgstr "Cotisations"
msgid "Subscription stats"
msgstr "Statistiques de cotisation"
-#: core/templates/core/user_tools.jinja:48 counter/forms.py:176
-#: counter/views.py:678
+#: core/templates/core/user_tools.jinja:48 counter/forms.py:174
+#: counter/views.py:680
msgid "Counters"
msgstr "Comptoirs"
@@ -3394,12 +3401,12 @@ msgid "Product types management"
msgstr "Gestion des types de produit"
#: core/templates/core/user_tools.jinja:56
-#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:698
+#: counter/templates/counter/cash_summary_list.jinja:23 counter/views.py:700
msgid "Cash register summaries"
msgstr "Relevés de caisse"
#: core/templates/core/user_tools.jinja:57
-#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:703
+#: counter/templates/counter/invoices_call.jinja:4 counter/views.py:705
msgid "Invoices call"
msgstr "Appels à facture"
@@ -3547,7 +3554,7 @@ msgstr "Parrain / Marraine"
msgid "Godchild"
msgstr "Fillot / Fillote"
-#: core/views/forms.py:311 counter/forms.py:82 trombi/views.py:151
+#: core/views/forms.py:311 counter/forms.py:80 trombi/views.py:151
msgid "Select user"
msgstr "Choisir un utilisateur"
@@ -3600,11 +3607,11 @@ msgstr "Galaxie"
msgid "counter"
msgstr "comptoir"
-#: counter/forms.py:63
+#: counter/forms.py:61
msgid "This UID is invalid"
msgstr "Cet UID est invalide"
-#: counter/forms.py:111
+#: counter/forms.py:109
msgid "User not found"
msgstr "Utilisateur non trouvé"
@@ -3632,7 +3639,7 @@ msgstr "client"
msgid "customers"
msgstr "clients"
-#: counter/models.py:110 counter/views.py:261
+#: counter/models.py:110 counter/views.py:263
msgid "Not enough money"
msgstr "Solde insuffisant"
@@ -3858,10 +3865,6 @@ msgstr "secret"
msgid "uid"
msgstr "uid"
-#: counter/models.py:1138
-msgid "student cards"
-msgstr "cartes étudiante"
-
#: counter/templates/counter/account_dump_warning_mail.jinja:1
msgid "Hello"
msgstr "Bonjour"
@@ -3963,7 +3966,7 @@ msgstr "Liste des relevés de caisse"
msgid "Theoric sums"
msgstr "Sommes théoriques"
-#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:956
+#: counter/templates/counter/cash_summary_list.jinja:36 counter/views.py:958
msgid "Emptied"
msgstr "Coffre vidé"
@@ -3975,15 +3978,19 @@ 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:35
+#: counter/templates/counter/counter_click.jinja:41
+msgid "No card registered"
+msgstr "Aucune carte enregistrée"
+
+#: counter/templates/counter/counter_click.jinja:45
msgid "Add a student card"
msgstr "Ajouter une carte étudiante"
-#: counter/templates/counter/counter_click.jinja:38
+#: counter/templates/counter/counter_click.jinja:48
msgid "This is not a valid student card UID"
msgstr "Ce n'est pas un UID de carte étudiante valide"
-#: counter/templates/counter/counter_click.jinja:40
+#: counter/templates/counter/counter_click.jinja:50
#: counter/templates/counter/counter_click.jinja:67
#: counter/templates/counter/counter_click.jinja:132
#: counter/templates/counter/invoices_call.jinja:16
@@ -3994,14 +4001,6 @@ msgstr "Ce n'est pas un UID de carte étudiante valide"
msgid "Go"
msgstr "Valider"
-#: counter/templates/counter/counter_click.jinja:42
-msgid "Registered cards"
-msgstr "Cartes enregistrées"
-
-#: counter/templates/counter/counter_click.jinja:51
-msgid "No card registered"
-msgstr "Aucune carte enregistrée"
-
#: counter/templates/counter/counter_click.jinja:56
#: launderette/templates/launderette/launderette_admin.jinja:8
msgid "Selling"
@@ -4189,101 +4188,101 @@ msgstr "Temps"
msgid "Top 100 barman %(counter_name)s (all semesters)"
msgstr "Top 100 barman %(counter_name)s (tous les semestres)"
-#: counter/views.py:147
+#: counter/views.py:149
msgid "Cash summary"
msgstr "Relevé de caisse"
-#: counter/views.py:156
+#: counter/views.py:158
msgid "Last operations"
msgstr "Dernières opérations"
-#: counter/views.py:203
+#: counter/views.py:205
msgid "Bad credentials"
msgstr "Mauvais identifiants"
-#: counter/views.py:205
+#: counter/views.py:207
msgid "User is not barman"
msgstr "L'utilisateur n'est pas barman."
-#: counter/views.py:210
+#: counter/views.py:212
msgid "Bad location, someone is already logged in somewhere else"
msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs"
-#: counter/views.py:252
+#: counter/views.py:254
msgid "Too young for that product"
msgstr "Trop jeune pour ce produit"
-#: counter/views.py:255
+#: counter/views.py:257
msgid "Not allowed for that product"
msgstr "Non autorisé pour ce produit"
-#: counter/views.py:258
+#: counter/views.py:260
msgid "No date of birth provided"
msgstr "Pas de date de naissance renseignée"
-#: counter/views.py:546
+#: counter/views.py:548
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.py:673
+#: counter/views.py:675
msgid "Counter administration"
msgstr "Administration des comptoirs"
-#: counter/views.py:693
+#: counter/views.py:695
msgid "Product types"
msgstr "Types de produit"
-#: counter/views.py:913
+#: counter/views.py:915
msgid "10 cents"
msgstr "10 centimes"
-#: counter/views.py:914
+#: counter/views.py:916
msgid "20 cents"
msgstr "20 centimes"
-#: counter/views.py:915
+#: counter/views.py:917
msgid "50 cents"
msgstr "50 centimes"
-#: counter/views.py:916
+#: counter/views.py:918
msgid "1 euro"
msgstr "1 €"
-#: counter/views.py:917
+#: counter/views.py:919
msgid "2 euros"
msgstr "2 €"
-#: counter/views.py:918
+#: counter/views.py:920
msgid "5 euros"
msgstr "5 €"
-#: counter/views.py:919
+#: counter/views.py:921
msgid "10 euros"
msgstr "10 €"
-#: counter/views.py:920
+#: counter/views.py:922
msgid "20 euros"
msgstr "20 €"
-#: counter/views.py:921
+#: counter/views.py:923
msgid "50 euros"
msgstr "50 €"
-#: counter/views.py:923
+#: counter/views.py:925
msgid "100 euros"
msgstr "100 €"
-#: counter/views.py:926 counter/views.py:932 counter/views.py:938
-#: counter/views.py:944 counter/views.py:950
+#: counter/views.py:928 counter/views.py:934 counter/views.py:940
+#: counter/views.py:946 counter/views.py:952
msgid "Check amount"
msgstr "Montant du chèque"
-#: counter/views.py:929 counter/views.py:935 counter/views.py:941
-#: counter/views.py:947 counter/views.py:953
+#: counter/views.py:931 counter/views.py:937 counter/views.py:943
+#: counter/views.py:949 counter/views.py:955
msgid "Check quantity"
msgstr "Nombre de chèque"
-#: counter/views.py:1473
+#: counter/views.py:1475
msgid "people(s)"
msgstr "personne(s)"