mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 15:51:19 +00:00
Merge pull request #921 from ae-utbm/counter-click
Casser counter click étape 1 : introduire des fragments
This commit is contained in:
commit
902cafc5e4
@ -1,3 +1,11 @@
|
|||||||
import htmx from "htmx.org";
|
import htmx from "htmx.org";
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:beforeRequest", (event) => {
|
||||||
|
event.target.ariaBusy = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:afterRequest", (event) => {
|
||||||
|
event.originalTarget.ariaBusy = null;
|
||||||
|
});
|
||||||
|
|
||||||
Object.assign(window, { htmx });
|
Object.assign(window, { htmx });
|
||||||
|
@ -6,7 +6,16 @@
|
|||||||
**/
|
**/
|
||||||
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
|
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
|
||||||
return (component: CustomElementConstructor) => {
|
return (component: CustomElementConstructor) => {
|
||||||
|
try {
|
||||||
window.customElements.define(name, component, options);
|
window.customElements.define(name, component, options);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof DOMException) {
|
||||||
|
// biome-ignore lint/suspicious/noConsole: it's handy to troobleshot
|
||||||
|
console.warn(e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,35 +35,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if profile.customer %}
|
{% if student_card %}
|
||||||
<h3>{% trans %}Student cards{% endtrans %}</h3>
|
{{ student_card }}
|
||||||
|
|
||||||
{% if profile.customer.student_cards.exists() %}
|
|
||||||
<ul class="student-cards">
|
|
||||||
{% for card in profile.customer.student_cards.all() %}
|
|
||||||
<li>
|
|
||||||
{{ card.uid }}
|
|
||||||
-
|
|
||||||
<a href="{{ url('counter:delete_student_card', customer_id=profile.customer.pk, card_id=card.id) }}">
|
|
||||||
{% trans %}Delete{% endtrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em>
|
|
||||||
<p class="justify">
|
<p class="justify">
|
||||||
{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually
|
{% 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 %}
|
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form class="form form-cards" action="{{ url('counter:add_student_card', customer_id=profile.customer.pk) }}"
|
|
||||||
method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ student_card_form.as_p() }}
|
|
||||||
<input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -13,22 +13,41 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
# Image utils
|
# Image utils
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Optional
|
from typing import Any
|
||||||
|
|
||||||
import PIL
|
import PIL
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
from django.forms import BaseForm
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils.html import SafeString
|
||||||
from django.utils.timezone import localdate
|
from django.utils.timezone import localdate
|
||||||
from PIL import ExifTags
|
from PIL import ExifTags
|
||||||
from PIL.Image import Image, Resampling
|
from PIL.Image import Image, Resampling
|
||||||
|
|
||||||
|
|
||||||
def get_start_of_semester(today: Optional[date] = None) -> date:
|
@dataclass
|
||||||
|
class FormFragmentTemplateData[T: BaseForm]:
|
||||||
|
"""Dataclass used to pre-render form fragments"""
|
||||||
|
|
||||||
|
form: T
|
||||||
|
template: str
|
||||||
|
context: dict[str, Any]
|
||||||
|
|
||||||
|
def render(self, request: HttpRequest) -> SafeString:
|
||||||
|
# Request is needed for csrf_tokens
|
||||||
|
return render_to_string(
|
||||||
|
self.template, context={"form": self.form, **self.context}, request=request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_start_of_semester(today: date | None = None) -> date:
|
||||||
"""Return the date of the start of the semester of the given date.
|
"""Return the date of the start of the semester of the given date.
|
||||||
If no date is given, return the start date of the current semester.
|
If no date is given, return the start date of the current semester.
|
||||||
|
|
||||||
@ -58,7 +77,7 @@ def get_start_of_semester(today: Optional[date] = None) -> date:
|
|||||||
return autumn.replace(year=autumn.year - 1)
|
return autumn.replace(year=autumn.year - 1)
|
||||||
|
|
||||||
|
|
||||||
def get_semester_code(d: Optional[date] = None) -> str:
|
def get_semester_code(d: date | None = None) -> str:
|
||||||
"""Return the semester code of the given date.
|
"""Return the semester code of the given date.
|
||||||
If no date is given, return the semester code of the current semester.
|
If no date is given, return the semester code of the current semester.
|
||||||
|
|
||||||
|
@ -70,8 +70,8 @@ from core.views.forms import (
|
|||||||
UserGodfathersForm,
|
UserGodfathersForm,
|
||||||
UserProfileForm,
|
UserProfileForm,
|
||||||
)
|
)
|
||||||
from counter.forms import StudentCardForm
|
|
||||||
from counter.models import Refilling, Selling
|
from counter.models import Refilling, Selling
|
||||||
|
from counter.views.student_card import StudentCardFormView
|
||||||
from eboutic.models import Invoice
|
from eboutic.models import Invoice
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from trombi.views import UserTrombiForm
|
from trombi.views import UserTrombiForm
|
||||||
@ -576,9 +576,10 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
|||||||
hasattr(self.object, "trombi_user") and self.request.user.trombi_user.trombi
|
hasattr(self.object, "trombi_user") and self.request.user.trombi_user.trombi
|
||||||
):
|
):
|
||||||
kwargs["trombi_form"] = UserTrombiForm()
|
kwargs["trombi_form"] = UserTrombiForm()
|
||||||
|
|
||||||
if hasattr(self.object, "customer"):
|
if hasattr(self.object, "customer"):
|
||||||
kwargs["student_card_form"] = StudentCardForm()
|
kwargs["student_card"] = StudentCardFormView.get_template_data(
|
||||||
|
self.object.customer
|
||||||
|
).render(self.request)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,9 +45,7 @@ class BillingInfoForm(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class StudentCardForm(forms.ModelForm):
|
class StudentCardForm(forms.ModelForm):
|
||||||
"""Form for adding student cards
|
"""Form for adding student cards"""
|
||||||
Only used for user profile since CounterClick is to complicated.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = StudentCard
|
model = StudentCard
|
||||||
@ -114,14 +112,6 @@ class GetUserForm(forms.Form):
|
|||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class NFCCardForm(forms.Form):
|
|
||||||
student_card_uid = forms.CharField(
|
|
||||||
max_length=StudentCard.UID_SIZE,
|
|
||||||
required=False,
|
|
||||||
widget=NFCTextInput,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RefillForm(forms.ModelForm):
|
class RefillForm(forms.ModelForm):
|
||||||
error_css_class = "error"
|
error_css_class = "error"
|
||||||
required_css_class = "required"
|
required_css_class = "required"
|
||||||
|
@ -29,26 +29,9 @@
|
|||||||
{{ user_mini_profile(customer.user) }}
|
{{ user_mini_profile(customer.user) }}
|
||||||
{{ user_subscription(customer.user) }}
|
{{ user_subscription(customer.user) }}
|
||||||
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
|
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
|
||||||
<form method="post" action="{{ url('counter:click', counter_id=counter.id, user_id=customer.user.id) }}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="action" value="add_student_card">
|
|
||||||
{% trans %}Add a student card{% endtrans %}
|
|
||||||
{{ student_card_input.student_card_uid }}
|
|
||||||
{% if request.session['not_valid_student_card_uid'] %}
|
|
||||||
<p><strong>{% trans %}This is not a valid student card UID{% endtrans %}</strong></p>
|
|
||||||
{% endif %}
|
|
||||||
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
|
|
||||||
</form>
|
|
||||||
<h6>{% trans %}Registered cards{% endtrans %}</h6>
|
|
||||||
{% if student_cards %}
|
|
||||||
|
|
||||||
<ul>
|
{% if counter.type == 'BAR' %}
|
||||||
{% for card in student_cards %}
|
{{ student_card }}
|
||||||
<li>{{ card.uid }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
{% trans %}No card registered{% endtrans %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<div id="student_card_form">
|
||||||
|
<h3>{% trans %}Add a student card{% endtrans %}</h3>
|
||||||
|
<form
|
||||||
|
hx-post="{{ action }}"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
hx-target="#student_card_form"
|
||||||
|
>
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p() }}
|
||||||
|
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<h6>{% trans %}Registered cards{% endtrans %}</h6>
|
||||||
|
{% if student_cards %}
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for card in student_cards %}
|
||||||
|
<li>
|
||||||
|
{{ card.uid }}
|
||||||
|
<a href="{{ url('counter:delete_student_card', customer_id=customer.pk, card_id=card.id) }}">
|
||||||
|
{% trans %}Delete{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -1,15 +1,27 @@
|
|||||||
import json
|
import json
|
||||||
import string
|
import string
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.base_user import make_password
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.timezone import now
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
|
|
||||||
from core.baker_recipes import subscriber_user
|
from club.models import Membership
|
||||||
|
from core.baker_recipes import board_user, subscriber_user
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from counter.baker_recipes import refill_recipe, sale_recipe
|
from counter.baker_recipes import refill_recipe, sale_recipe
|
||||||
from counter.models import BillingInfo, Counter, Customer, Refilling, Selling
|
from counter.models import (
|
||||||
|
BillingInfo,
|
||||||
|
Counter,
|
||||||
|
Customer,
|
||||||
|
Refilling,
|
||||||
|
Selling,
|
||||||
|
StudentCard,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@ -162,148 +174,269 @@ class TestStudentCard(TestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
cls.krophil = User.objects.get(username="krophil")
|
cls.customer = subscriber_user.make()
|
||||||
cls.sli = User.objects.get(username="sli")
|
cls.customer.save()
|
||||||
cls.skia = User.objects.get(username="skia")
|
cls.barmen = subscriber_user.make(password=make_password("plop"))
|
||||||
cls.root = User.objects.get(username="root")
|
cls.board_admin = board_user.make()
|
||||||
|
cls.club_admin = baker.make(User)
|
||||||
|
cls.root = baker.make(User, is_superuser=True)
|
||||||
|
cls.subscriber = subscriber_user.make()
|
||||||
|
|
||||||
cls.counter = Counter.objects.get(id=2)
|
cls.counter = baker.make(Counter, type="BAR")
|
||||||
|
cls.counter.sellers.add(cls.barmen)
|
||||||
|
|
||||||
|
cls.club_counter = baker.make(Counter)
|
||||||
|
baker.make(
|
||||||
|
Membership,
|
||||||
|
start_date=now() - timedelta(days=30),
|
||||||
|
club=cls.club_counter.club,
|
||||||
|
role=settings.SITH_CLUB_ROLES_ID["Board member"],
|
||||||
|
user=cls.club_admin,
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.valid_card = baker.make(
|
||||||
|
StudentCard, customer=cls.customer.customer, uid="8A89B82018B0A0"
|
||||||
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Auto login on counter
|
# Auto login on counter
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse("counter:login", args=[self.counter.id]),
|
reverse("counter:login", args=[self.counter.id]),
|
||||||
{"username": "krophil", "password": "plop"},
|
{"username": self.barmen.username, "password": "plop"},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_search_user_with_student_card(self):
|
def test_search_user_with_student_card(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("counter:details", args=[self.counter.id]),
|
reverse("counter:details", args=[self.counter.id]),
|
||||||
{"code": "9A89B82018B0A0"},
|
{"code": self.valid_card.uid},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.url == reverse(
|
assert response.url == reverse(
|
||||||
"counter:click",
|
"counter:click",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={"counter_id": self.counter.id, "user_id": self.customer.id},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_add_student_card_from_counter(self):
|
def test_add_student_card_from_counter(self):
|
||||||
# Test card with mixed letters and numbers
|
# Test card with mixed letters and numbers
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8F"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "8B90734A802A8F", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
self.assertContains(response, text="8B90734A802A8F")
|
assert response.status_code == 302
|
||||||
|
self.assertContains(self.client.get(response.url), text="8B90734A802A8F")
|
||||||
|
|
||||||
# Test card with only numbers
|
# Test card with only numbers
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "04786547890123"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "04786547890123", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
self.assertContains(response, text="04786547890123")
|
assert response.status_code == 302
|
||||||
|
self.assertContains(self.client.get(response.url), text="04786547890123")
|
||||||
|
|
||||||
# Test card with only letters
|
# Test card with only letters
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "ABCAAAFAAFAAAB"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "ABCAAAFAAFAAAB", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
self.assertContains(response, text="ABCAAAFAAFAAAB")
|
assert response.status_code == 302
|
||||||
|
self.assertContains(self.client.get(response.url), text="ABCAAAFAAFAAAB")
|
||||||
|
|
||||||
def test_add_student_card_from_counter_fail(self):
|
def test_add_student_card_from_counter_fail(self):
|
||||||
# UID too short
|
# UID too short
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "8B90734A802A8", "action": "add_student_card"},
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
|
||||||
)
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
# UID too long
|
# UID too long
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8FA"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "8B90734A802A8FA", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
self.assertContains(
|
self.assertContains(
|
||||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
response,
|
||||||
|
text="Assurez-vous que cette valeur comporte au plus 14 caractères (actuellement 15).",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test with already existing card
|
# Test with already existing card
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": self.valid_card.uid},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "9A89B82018B0A0", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
self.assertContains(
|
self.assertContains(
|
||||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
response, text="Un objet Student card avec ce champ Uid existe déjà."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test with lowercase
|
# Test with lowercase
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "8b90734a802a9f"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": "8b90734a802a9f", "action": "add_student_card"},
|
|
||||||
)
|
|
||||||
self.assertContains(
|
|
||||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
|
||||||
)
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
# Test with white spaces
|
# Test with white spaces
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:click",
|
"counter:add_student_card",
|
||||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": " "},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": self.counter.id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
{"student_card_uid": " ", "action": "add_student_card"},
|
|
||||||
)
|
)
|
||||||
self.assertContains(
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
self.assertContains(response, text="Ce champ est obligatoire.")
|
||||||
|
|
||||||
|
def test_add_student_card_from_counter_unauthorized(self):
|
||||||
|
# Send to a counter where you aren't logged in
|
||||||
|
self.client.post(
|
||||||
|
reverse("counter:logout", args=[self.counter.id]),
|
||||||
|
{"user_id": self.barmen.id},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def send_valid_request(client, counter_id):
|
||||||
|
return client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8F"},
|
||||||
|
HTTP_REFERER=reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={
|
||||||
|
"counter_id": counter_id,
|
||||||
|
"user_id": self.customer.customer.pk,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert send_valid_request(self.client, self.counter.id).status_code == 403
|
||||||
|
|
||||||
|
# Send to a non bar counter
|
||||||
|
self.client.force_login(self.club_admin)
|
||||||
|
assert send_valid_request(self.client, self.club_counter.id).status_code == 403
|
||||||
|
|
||||||
def test_delete_student_card_with_owner(self):
|
def test_delete_student_card_with_owner(self):
|
||||||
self.client.force_login(self.sli)
|
self.client.force_login(self.customer)
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:delete_student_card",
|
"counter:delete_student_card",
|
||||||
kwargs={
|
kwargs={
|
||||||
"customer_id": self.sli.customer.pk,
|
"customer_id": self.customer.customer.pk,
|
||||||
"card_id": self.sli.customer.student_cards.first().id,
|
"card_id": self.customer.customer.student_cards.first().id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert not self.sli.customer.student_cards.exists()
|
assert not self.customer.customer.student_cards.exists()
|
||||||
|
|
||||||
def test_delete_student_card_with_board_member(self):
|
def test_delete_student_card_with_board_member(self):
|
||||||
self.client.force_login(self.skia)
|
self.client.force_login(self.board_admin)
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:delete_student_card",
|
"counter:delete_student_card",
|
||||||
kwargs={
|
kwargs={
|
||||||
"customer_id": self.sli.customer.pk,
|
"customer_id": self.customer.customer.pk,
|
||||||
"card_id": self.sli.customer.student_cards.first().id,
|
"card_id": self.customer.customer.student_cards.first().id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert not self.sli.customer.student_cards.exists()
|
assert not self.customer.customer.student_cards.exists()
|
||||||
|
|
||||||
def test_delete_student_card_with_root(self):
|
def test_delete_student_card_with_root(self):
|
||||||
self.client.force_login(self.root)
|
self.client.force_login(self.root)
|
||||||
@ -311,100 +444,107 @@ class TestStudentCard(TestCase):
|
|||||||
reverse(
|
reverse(
|
||||||
"counter:delete_student_card",
|
"counter:delete_student_card",
|
||||||
kwargs={
|
kwargs={
|
||||||
"customer_id": self.sli.customer.pk,
|
"customer_id": self.customer.customer.pk,
|
||||||
"card_id": self.sli.customer.student_cards.first().id,
|
"card_id": self.customer.customer.student_cards.first().id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert not self.sli.customer.student_cards.exists()
|
assert not self.customer.customer.student_cards.exists()
|
||||||
|
|
||||||
def test_delete_student_card_fail(self):
|
def test_delete_student_card_fail(self):
|
||||||
self.client.force_login(self.krophil)
|
self.client.force_login(self.subscriber)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:delete_student_card",
|
"counter:delete_student_card",
|
||||||
kwargs={
|
kwargs={
|
||||||
"customer_id": self.sli.customer.pk,
|
"customer_id": self.customer.customer.pk,
|
||||||
"card_id": self.sli.customer.student_cards.first().id,
|
"card_id": self.customer.customer.student_cards.first().id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
assert self.sli.customer.student_cards.exists()
|
assert self.customer.customer.student_cards.exists()
|
||||||
|
|
||||||
def test_add_student_card_from_user_preferences(self):
|
def test_add_student_card_from_user_preferences(self):
|
||||||
# Test with owner of the card
|
# Test with owner of the card
|
||||||
self.client.force_login(self.sli)
|
self.client.force_login(self.customer)
|
||||||
self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8F"},
|
{"uid": "8B90734A802A8F"},
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get(
|
assert response.status_code == 302
|
||||||
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
|
||||||
)
|
response = self.client.get(response.url)
|
||||||
self.assertContains(response, text="8B90734A802A8F")
|
self.assertContains(response, text="8B90734A802A8F")
|
||||||
|
|
||||||
# Test with board member
|
# Test with board member
|
||||||
self.client.force_login(self.skia)
|
self.client.force_login(self.board_admin)
|
||||||
self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8A"},
|
{"uid": "8B90734A802A8A"},
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get(
|
assert response.status_code == 302
|
||||||
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
|
||||||
)
|
response = self.client.get(response.url)
|
||||||
self.assertContains(response, text="8B90734A802A8A")
|
self.assertContains(response, text="8B90734A802A8A")
|
||||||
|
|
||||||
# Test card with only numbers
|
# Test card with only numbers
|
||||||
self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "04786547890123"},
|
{"uid": "04786547890123"},
|
||||||
)
|
)
|
||||||
response = self.client.get(
|
assert response.status_code == 302
|
||||||
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
|
||||||
)
|
response = self.client.get(response.url)
|
||||||
self.assertContains(response, text="04786547890123")
|
self.assertContains(response, text="04786547890123")
|
||||||
|
|
||||||
# Test card with only letters
|
# Test card with only letters
|
||||||
self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "ABCAAAFAAFAAAB"},
|
{"uid": "ABCAAAFAAFAAAB"},
|
||||||
)
|
)
|
||||||
response = self.client.get(
|
|
||||||
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
assert response.status_code == 302
|
||||||
)
|
|
||||||
|
response = self.client.get(response.url)
|
||||||
self.assertContains(response, text="ABCAAAFAAFAAAB")
|
self.assertContains(response, text="ABCAAAFAAFAAAB")
|
||||||
|
|
||||||
# Test with root
|
# Test with root
|
||||||
self.client.force_login(self.root)
|
self.client.force_login(self.root)
|
||||||
self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8B"},
|
{"uid": "8B90734A802A8B"},
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get(
|
assert response.status_code == 302
|
||||||
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
|
||||||
)
|
response = self.client.get(response.url)
|
||||||
self.assertContains(response, text="8B90734A802A8B")
|
self.assertContains(response, text="8B90734A802A8B")
|
||||||
|
|
||||||
def test_add_student_card_from_user_preferences_fail(self):
|
def test_add_student_card_from_user_preferences_fail(self):
|
||||||
self.client.force_login(self.sli)
|
self.client.force_login(self.customer)
|
||||||
# UID too short
|
# UID too short
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8"},
|
{"uid": "8B90734A802A8"},
|
||||||
)
|
)
|
||||||
@ -414,7 +554,8 @@ class TestStudentCard(TestCase):
|
|||||||
# UID too long
|
# UID too long
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8FA"},
|
{"uid": "8B90734A802A8FA"},
|
||||||
)
|
)
|
||||||
@ -423,9 +564,10 @@ class TestStudentCard(TestCase):
|
|||||||
# Test with already existing card
|
# Test with already existing card
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "9A89B82018B0A0"},
|
{"uid": self.valid_card.uid},
|
||||||
)
|
)
|
||||||
self.assertContains(
|
self.assertContains(
|
||||||
response, text="Un objet Student card avec ce champ Uid existe déjà."
|
response, text="Un objet Student card avec ce champ Uid existe déjà."
|
||||||
@ -434,7 +576,8 @@ class TestStudentCard(TestCase):
|
|||||||
# Test with lowercase
|
# Test with lowercase
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8b90734a802a9f"},
|
{"uid": "8b90734a802a9f"},
|
||||||
)
|
)
|
||||||
@ -443,17 +586,19 @@ class TestStudentCard(TestCase):
|
|||||||
# Test with white spaces
|
# Test with white spaces
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": " " * 14},
|
{"uid": " " * 14},
|
||||||
)
|
)
|
||||||
self.assertContains(response, text="Cet UID est invalide")
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
# Test with unauthorized user
|
# Test with unauthorized user
|
||||||
self.client.force_login(self.krophil)
|
self.client.force_login(self.subscriber)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
"counter:add_student_card",
|
||||||
|
kwargs={"customer_id": self.customer.customer.pk},
|
||||||
),
|
),
|
||||||
{"uid": "8B90734A802A8F"},
|
{"uid": "8B90734A802A8F"},
|
||||||
)
|
)
|
||||||
|
@ -52,7 +52,10 @@ from counter.views.home import (
|
|||||||
CounterMain,
|
CounterMain,
|
||||||
)
|
)
|
||||||
from counter.views.invoice import InvoiceCallView
|
from counter.views.invoice import InvoiceCallView
|
||||||
from counter.views.student_card import StudentCardDeleteView, StudentCardFormView
|
from counter.views.student_card import (
|
||||||
|
StudentCardDeleteView,
|
||||||
|
StudentCardFormView,
|
||||||
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
||||||
|
@ -22,14 +22,22 @@ def is_logged_in_counter(request: HttpRequest) -> bool:
|
|||||||
to the counter)
|
to the counter)
|
||||||
- The current session has a counter token associated with it.
|
- The current session has a counter token associated with it.
|
||||||
- A counter with this token exists.
|
- A counter with this token exists.
|
||||||
|
- The counter is open
|
||||||
"""
|
"""
|
||||||
referer_ok = (
|
referer_ok = (
|
||||||
"HTTP_REFERER" in request.META
|
"HTTP_REFERER" in request.META
|
||||||
and resolve(urlparse(request.META["HTTP_REFERER"]).path).app_name == "counter"
|
and resolve(urlparse(request.META["HTTP_REFERER"]).path).app_name == "counter"
|
||||||
)
|
)
|
||||||
return (
|
has_token = (
|
||||||
(referer_ok or request.resolver_match.app_name == "counter")
|
(referer_ok or request.resolver_match.app_name == "counter")
|
||||||
and "counter_token" in request.session
|
and "counter_token" in request.session
|
||||||
and request.session["counter_token"]
|
and request.session["counter_token"]
|
||||||
and Counter.objects.filter(token=request.session["counter_token"]).exists()
|
)
|
||||||
|
if not has_token:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return (
|
||||||
|
Counter.objects.annotate_is_open()
|
||||||
|
.filter(token=request.session["counter_token"], is_open=True)
|
||||||
|
.exists()
|
||||||
)
|
)
|
||||||
|
@ -27,9 +27,10 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.views.generic import DetailView
|
from django.views.generic import DetailView
|
||||||
|
|
||||||
from core.views import CanViewMixin
|
from core.views import CanViewMixin
|
||||||
from counter.forms import NFCCardForm, RefillForm
|
from counter.forms import RefillForm
|
||||||
from counter.models import Counter, Customer, Product, Selling, StudentCard
|
from counter.models import Counter, Customer, Product, Selling
|
||||||
from counter.views.mixins import CounterTabsMixin
|
from counter.views.mixins import CounterTabsMixin
|
||||||
|
from counter.views.student_card import StudentCardFormView
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.models import User
|
from core.models import User
|
||||||
@ -134,7 +135,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
request.session["too_young"] = False
|
request.session["too_young"] = False
|
||||||
request.session["not_allowed"] = False
|
request.session["not_allowed"] = False
|
||||||
request.session["no_age"] = False
|
request.session["no_age"] = False
|
||||||
request.session["not_valid_student_card_uid"] = False
|
|
||||||
if self.object.type != "BAR":
|
if self.object.type != "BAR":
|
||||||
self.operator = request.user
|
self.operator = request.user
|
||||||
elif self.customer_is_barman():
|
elif self.customer_is_barman():
|
||||||
@ -146,8 +146,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
action = parse_qs(request.body.decode()).get("action", [""])[0]
|
action = parse_qs(request.body.decode()).get("action", [""])[0]
|
||||||
if action == "add_product":
|
if action == "add_product":
|
||||||
self.add_product(request)
|
self.add_product(request)
|
||||||
elif action == "add_student_card":
|
|
||||||
self.add_student_card(request)
|
|
||||||
elif action == "del_product":
|
elif action == "del_product":
|
||||||
self.del_product(request)
|
self.del_product(request)
|
||||||
elif action == "refill":
|
elif action == "refill":
|
||||||
@ -284,23 +282,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
request.session.modified = True
|
request.session.modified = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def add_student_card(self, request):
|
|
||||||
"""Add a new student card on the customer account."""
|
|
||||||
uid = str(request.POST["student_card_uid"])
|
|
||||||
if not StudentCard.is_valid(uid):
|
|
||||||
request.session["not_valid_student_card_uid"] = True
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not (
|
|
||||||
self.object.type == "BAR"
|
|
||||||
and "counter_token" in request.session
|
|
||||||
and request.session["counter_token"] == self.object.token
|
|
||||||
and self.object.is_open
|
|
||||||
):
|
|
||||||
raise PermissionDenied
|
|
||||||
StudentCard(customer=self.customer, uid=uid).save()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def del_product(self, request):
|
def del_product(self, request):
|
||||||
"""Delete a product from the basket."""
|
"""Delete a product from the basket."""
|
||||||
pid = parse_qs(request.body.decode())["product_id"][0]
|
pid = parse_qs(request.body.decode())["product_id"][0]
|
||||||
@ -431,10 +412,10 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
product
|
product
|
||||||
)
|
)
|
||||||
kwargs["customer"] = self.customer
|
kwargs["customer"] = self.customer
|
||||||
kwargs["student_cards"] = self.customer.student_cards.all()
|
|
||||||
kwargs["student_card_input"] = NFCCardForm()
|
|
||||||
kwargs["basket_total"] = self.sum_basket(self.request)
|
kwargs["basket_total"] = self.sum_basket(self.request)
|
||||||
kwargs["refill_form"] = self.refill_form or RefillForm()
|
kwargs["refill_form"] = self.refill_form or RefillForm()
|
||||||
kwargs["student_card_max_uid_size"] = StudentCard.UID_SIZE
|
|
||||||
kwargs["barmens_can_refill"] = self.object.can_refill()
|
kwargs["barmens_can_refill"] = self.object.can_refill()
|
||||||
|
kwargs["student_card"] = StudentCardFormView.get_template_data(
|
||||||
|
self.customer
|
||||||
|
).render(self.request)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
@ -13,14 +13,18 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
from django.http import HttpRequest
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic.edit import DeleteView, FormView
|
from django.views.generic.edit import DeleteView, FormView
|
||||||
|
|
||||||
|
from core.utils import FormFragmentTemplateData
|
||||||
from core.views import CanEditMixin
|
from core.views import CanEditMixin
|
||||||
from counter.forms import StudentCardForm
|
from counter.forms import StudentCardForm
|
||||||
from counter.models import Customer, StudentCard
|
from counter.models import Customer, StudentCard
|
||||||
|
from counter.utils import is_logged_in_counter
|
||||||
|
|
||||||
|
|
||||||
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||||
@ -41,15 +45,38 @@ class StudentCardDeleteView(DeleteView, CanEditMixin):
|
|||||||
|
|
||||||
|
|
||||||
class StudentCardFormView(FormView):
|
class StudentCardFormView(FormView):
|
||||||
"""Add a new student card."""
|
"""Add a new student card. This is a fragment view !"""
|
||||||
|
|
||||||
form_class = StudentCardForm
|
form_class = StudentCardForm
|
||||||
template_name = "core/create.jinja"
|
template_name = "counter/fragments/create_student_card.jinja"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
@classmethod
|
||||||
self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
|
def get_template_data(
|
||||||
if not StudentCard.can_create(self.customer, request.user):
|
cls, customer: Customer
|
||||||
|
) -> FormFragmentTemplateData[form_class]:
|
||||||
|
"""Get necessary data to pre-render the fragment"""
|
||||||
|
return FormFragmentTemplateData[cls.form_class](
|
||||||
|
form=cls.form_class(),
|
||||||
|
template=cls.template_name,
|
||||||
|
context={
|
||||||
|
"action": reverse_lazy(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": customer.pk}
|
||||||
|
),
|
||||||
|
"customer": customer,
|
||||||
|
"student_cards": customer.student_cards.all(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def dispatch(self, request: HttpRequest, *args, **kwargs):
|
||||||
|
self.customer = get_object_or_404(
|
||||||
|
Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_logged_in_counter(request) and not StudentCard.can_create(
|
||||||
|
self.customer, request.user
|
||||||
|
):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@ -58,7 +85,11 @@ class StudentCardFormView(FormView):
|
|||||||
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
data = self.get_template_data(self.customer)
|
||||||
|
context.update(data.context)
|
||||||
|
return context
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
def get_success_url(self, **kwargs):
|
||||||
return reverse_lazy(
|
return self.request.path
|
||||||
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
|
||||||
)
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-29 18:04+0100\n"
|
"POT-Creation-Date: 2024-12-08 00:29+0100\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@ -218,7 +218,7 @@ msgstr "Compte"
|
|||||||
msgid "Company"
|
msgid "Company"
|
||||||
msgstr "Entreprise"
|
msgstr "Entreprise"
|
||||||
|
|
||||||
#: accounting/models.py:307 core/models.py:338 sith/settings.py:421
|
#: accounting/models.py:307 core/models.py:338 sith/settings.py:423
|
||||||
msgid "Other"
|
msgid "Other"
|
||||||
msgstr "Autre"
|
msgstr "Autre"
|
||||||
|
|
||||||
@ -369,7 +369,7 @@ msgstr "Compte en banque : "
|
|||||||
#: core/templates/core/user_clubs.jinja:34
|
#: core/templates/core/user_clubs.jinja:34
|
||||||
#: core/templates/core/user_clubs.jinja:63
|
#: core/templates/core/user_clubs.jinja:63
|
||||||
#: core/templates/core/user_edit.jinja:62
|
#: core/templates/core/user_edit.jinja:62
|
||||||
#: core/templates/core/user_preferences.jinja:48
|
#: counter/templates/counter/fragments/create_student_card.jinja:21
|
||||||
#: counter/templates/counter/last_ops.jinja:35
|
#: counter/templates/counter/last_ops.jinja:35
|
||||||
#: counter/templates/counter/last_ops.jinja:65
|
#: counter/templates/counter/last_ops.jinja:65
|
||||||
#: election/templates/election/election_detail.jinja:191
|
#: election/templates/election/election_detail.jinja:191
|
||||||
@ -517,7 +517,7 @@ msgid "Effective amount"
|
|||||||
msgstr "Montant effectif"
|
msgstr "Montant effectif"
|
||||||
|
|
||||||
#: accounting/templates/accounting/club_account_details.jinja:36
|
#: accounting/templates/accounting/club_account_details.jinja:36
|
||||||
#: sith/settings.py:467
|
#: sith/settings.py:469
|
||||||
msgid "Closed"
|
msgid "Closed"
|
||||||
msgstr "Fermé"
|
msgstr "Fermé"
|
||||||
|
|
||||||
@ -650,8 +650,8 @@ msgid "Done"
|
|||||||
msgstr "Effectuées"
|
msgstr "Effectuées"
|
||||||
|
|
||||||
#: accounting/templates/accounting/journal_details.jinja:41
|
#: 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
|
||||||
#: pedagogy/templates/pedagogy/moderation.jinja:13
|
#: counter/views/cash.py:87 pedagogy/templates/pedagogy/moderation.jinja:13
|
||||||
#: pedagogy/templates/pedagogy/uv_detail.jinja:142
|
#: pedagogy/templates/pedagogy/uv_detail.jinja:142
|
||||||
#: trombi/templates/trombi/comment.jinja:4
|
#: trombi/templates/trombi/comment.jinja:4
|
||||||
#: trombi/templates/trombi/comment.jinja:8
|
#: trombi/templates/trombi/comment.jinja:8
|
||||||
@ -771,7 +771,6 @@ msgstr "Opération liée : "
|
|||||||
#: core/templates/core/user_godfathers_tree.jinja:85
|
#: core/templates/core/user_godfathers_tree.jinja:85
|
||||||
#: core/templates/core/user_preferences.jinja:18
|
#: core/templates/core/user_preferences.jinja:18
|
||||||
#: core/templates/core/user_preferences.jinja:27
|
#: core/templates/core/user_preferences.jinja:27
|
||||||
#: core/templates/core/user_preferences.jinja:65
|
|
||||||
#: counter/templates/counter/cash_register_summary.jinja:28
|
#: counter/templates/counter/cash_register_summary.jinja:28
|
||||||
#: forum/templates/forum/reply.jinja:39
|
#: forum/templates/forum/reply.jinja:39
|
||||||
#: subscription/templates/subscription/fragments/creation_form.jinja:9
|
#: subscription/templates/subscription/fragments/creation_form.jinja:9
|
||||||
@ -951,11 +950,11 @@ msgstr "Une action est requise"
|
|||||||
msgid "You must specify at least an user or an email address"
|
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"
|
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:193
|
||||||
msgid "Begin date"
|
msgid "Begin date"
|
||||||
msgstr "Date de début"
|
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:196
|
||||||
#: election/views.py:170 subscription/forms.py:21
|
#: election/views.py:170 subscription/forms.py:21
|
||||||
msgid "End date"
|
msgid "End date"
|
||||||
msgstr "Date de fin"
|
msgstr "Date de fin"
|
||||||
@ -963,15 +962,16 @@ msgstr "Date de fin"
|
|||||||
#: club/forms.py:156 club/templates/club/club_sellings.jinja:49
|
#: 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:17
|
||||||
#: core/templates/core/user_account_detail.jinja:56
|
#: 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/mixins.py:58
|
||||||
msgid "Counter"
|
msgid "Counter"
|
||||||
msgstr "Comptoir"
|
msgstr "Comptoir"
|
||||||
|
|
||||||
#: club/forms.py:163 counter/views.py:683
|
#: club/forms.py:163 counter/views/mixins.py:94
|
||||||
msgid "Products"
|
msgid "Products"
|
||||||
msgstr "Produits"
|
msgstr "Produits"
|
||||||
|
|
||||||
#: club/forms.py:168 counter/views.py:688
|
#: club/forms.py:168 counter/views/mixins.py:99
|
||||||
msgid "Archived products"
|
msgid "Archived products"
|
||||||
msgstr "Produits archivés"
|
msgstr "Produits archivés"
|
||||||
|
|
||||||
@ -1334,7 +1334,7 @@ msgid "No mailing list existing for this club"
|
|||||||
msgstr "Aucune mailing liste n'existe pour ce club"
|
msgstr "Aucune mailing liste n'existe pour ce club"
|
||||||
|
|
||||||
#: club/templates/club/mailing.jinja:72
|
#: club/templates/club/mailing.jinja:72
|
||||||
#: subscription/templates/subscription/subscription.jinja:39
|
#: subscription/templates/subscription/subscription.jinja:38
|
||||||
msgid "New member"
|
msgid "New member"
|
||||||
msgstr "Nouveau membre"
|
msgstr "Nouveau membre"
|
||||||
|
|
||||||
@ -1426,7 +1426,8 @@ msgstr "Hebdomadaire"
|
|||||||
msgid "Call"
|
msgid "Call"
|
||||||
msgstr "Appel"
|
msgstr "Appel"
|
||||||
|
|
||||||
#: com/models.py:67 com/models.py:174 com/models.py:248 election/models.py:12
|
#: com/models.py:67 com/models.py:174 com/models.py:248
|
||||||
|
#: core/templates/core/macros.jinja:301 election/models.py:12
|
||||||
#: election/models.py:114 election/models.py:152 forum/models.py:256
|
#: election/models.py:114 election/models.py:152 forum/models.py:256
|
||||||
#: forum/models.py:310 pedagogy/models.py:97
|
#: forum/models.py:310 pedagogy/models.py:97
|
||||||
msgid "title"
|
msgid "title"
|
||||||
@ -1835,6 +1836,7 @@ msgid "Articles in no weekmail yet"
|
|||||||
msgstr "Articles dans aucun weekmail"
|
msgstr "Articles dans aucun weekmail"
|
||||||
|
|
||||||
#: com/templates/com/weekmail.jinja:20 com/templates/com/weekmail.jinja:49
|
#: com/templates/com/weekmail.jinja:20 com/templates/com/weekmail.jinja:49
|
||||||
|
#: core/templates/core/macros.jinja:301
|
||||||
msgid "Content"
|
msgid "Content"
|
||||||
msgstr "Contenu"
|
msgstr "Contenu"
|
||||||
|
|
||||||
@ -2505,7 +2507,7 @@ msgstr "Photos"
|
|||||||
#: eboutic/templates/eboutic/eboutic_main.jinja:22
|
#: eboutic/templates/eboutic/eboutic_main.jinja:22
|
||||||
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:16
|
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:16
|
||||||
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4
|
#: eboutic/templates/eboutic/eboutic_payment_result.jinja:4
|
||||||
#: sith/settings.py:420 sith/settings.py:428
|
#: sith/settings.py:422 sith/settings.py:430
|
||||||
msgid "Eboutic"
|
msgid "Eboutic"
|
||||||
msgstr "Eboutic"
|
msgstr "Eboutic"
|
||||||
|
|
||||||
@ -2583,7 +2585,7 @@ msgstr "Confirmation"
|
|||||||
|
|
||||||
#: core/templates/core/delete_confirm.jinja:20
|
#: core/templates/core/delete_confirm.jinja:20
|
||||||
#: core/templates/core/file_delete_confirm.jinja:46
|
#: core/templates/core/file_delete_confirm.jinja:46
|
||||||
#: counter/templates/counter/counter_click.jinja:121
|
#: counter/templates/counter/counter_click.jinja:111
|
||||||
#: sas/templates/sas/ask_picture_removal.jinja:20
|
#: sas/templates/sas/ask_picture_removal.jinja:20
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Annuler"
|
msgstr "Annuler"
|
||||||
@ -3042,11 +3044,11 @@ msgid "Eboutic invoices"
|
|||||||
msgstr "Facture eboutic"
|
msgstr "Facture eboutic"
|
||||||
|
|
||||||
#: core/templates/core/user_account.jinja:54
|
#: 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/mixins.py:119
|
||||||
msgid "Etickets"
|
msgid "Etickets"
|
||||||
msgstr "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:639
|
||||||
msgid "User has no account"
|
msgid "User has no account"
|
||||||
msgstr "L'utilisateur n'a pas de compte"
|
msgstr "L'utilisateur n'a pas de compte"
|
||||||
|
|
||||||
@ -3137,7 +3139,7 @@ msgstr "Non cotisant"
|
|||||||
|
|
||||||
#: core/templates/core/user_detail.jinja:162
|
#: core/templates/core/user_detail.jinja:162
|
||||||
#: subscription/templates/subscription/subscription.jinja:6
|
#: subscription/templates/subscription/subscription.jinja:6
|
||||||
#: subscription/templates/subscription/subscription.jinja:37
|
#: subscription/templates/subscription/subscription.jinja:36
|
||||||
msgid "New subscription"
|
msgid "New subscription"
|
||||||
msgstr "Nouvelle cotisation"
|
msgstr "Nouvelle cotisation"
|
||||||
|
|
||||||
@ -3295,15 +3297,7 @@ msgstr "Vous avez déjà choisi ce Trombi: %(trombi)s."
|
|||||||
msgid "Go to my Trombi tools"
|
msgid "Go to my Trombi tools"
|
||||||
msgstr "Allez à mes outils de Trombi"
|
msgstr "Allez à mes outils de Trombi"
|
||||||
|
|
||||||
#: core/templates/core/user_preferences.jinja:39
|
#: core/templates/core/user_preferences.jinja:49
|
||||||
msgid "Student cards"
|
|
||||||
msgstr "Cartes étudiante"
|
|
||||||
|
|
||||||
#: core/templates/core/user_preferences.jinja:54
|
|
||||||
msgid "No student card registered."
|
|
||||||
msgstr "Aucune carte étudiante enregistrée."
|
|
||||||
|
|
||||||
#: core/templates/core/user_preferences.jinja:56
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can add a card by asking at a counter or add it yourself here. If you "
|
"You can add a card by asking at a counter or add it yourself here. If you "
|
||||||
"want to manually\n"
|
"want to manually\n"
|
||||||
@ -3377,8 +3371,8 @@ msgstr "Cotisations"
|
|||||||
msgid "Subscription stats"
|
msgid "Subscription stats"
|
||||||
msgstr "Statistiques de cotisation"
|
msgstr "Statistiques de cotisation"
|
||||||
|
|
||||||
#: core/templates/core/user_tools.jinja:48 counter/forms.py:176
|
#: core/templates/core/user_tools.jinja:48 counter/forms.py:166
|
||||||
#: counter/views.py:678
|
#: counter/views/mixins.py:89
|
||||||
msgid "Counters"
|
msgid "Counters"
|
||||||
msgstr "Comptoirs"
|
msgstr "Comptoirs"
|
||||||
|
|
||||||
@ -3395,12 +3389,13 @@ msgid "Product types management"
|
|||||||
msgstr "Gestion des types de produit"
|
msgstr "Gestion des types de produit"
|
||||||
|
|
||||||
#: core/templates/core/user_tools.jinja:56
|
#: 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/mixins.py:109
|
||||||
msgid "Cash register summaries"
|
msgid "Cash register summaries"
|
||||||
msgstr "Relevés de caisse"
|
msgstr "Relevés de caisse"
|
||||||
|
|
||||||
#: core/templates/core/user_tools.jinja:57
|
#: 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/mixins.py:114
|
||||||
msgid "Invoices call"
|
msgid "Invoices call"
|
||||||
msgstr "Appels à facture"
|
msgstr "Appels à facture"
|
||||||
|
|
||||||
@ -3548,7 +3543,7 @@ msgstr "Parrain / Marraine"
|
|||||||
msgid "Godchild"
|
msgid "Godchild"
|
||||||
msgstr "Fillot / Fillote"
|
msgstr "Fillot / Fillote"
|
||||||
|
|
||||||
#: core/views/forms.py:310 counter/forms.py:82 trombi/views.py:151
|
#: core/views/forms.py:310 counter/forms.py:80 trombi/views.py:151
|
||||||
msgid "Select user"
|
msgid "Select user"
|
||||||
msgstr "Choisir un utilisateur"
|
msgstr "Choisir un utilisateur"
|
||||||
|
|
||||||
@ -3601,15 +3596,15 @@ msgstr "Galaxie"
|
|||||||
msgid "counter"
|
msgid "counter"
|
||||||
msgstr "comptoir"
|
msgstr "comptoir"
|
||||||
|
|
||||||
#: counter/forms.py:63
|
#: counter/forms.py:61
|
||||||
msgid "This UID is invalid"
|
msgid "This UID is invalid"
|
||||||
msgstr "Cet UID est invalide"
|
msgstr "Cet UID est invalide"
|
||||||
|
|
||||||
#: counter/forms.py:111
|
#: counter/forms.py:109
|
||||||
msgid "User not found"
|
msgid "User not found"
|
||||||
msgstr "Utilisateur non trouvé"
|
msgstr "Utilisateur non trouvé"
|
||||||
|
|
||||||
#: counter/management/commands/dump_accounts.py:141
|
#: counter/management/commands/dump_accounts.py:148
|
||||||
msgid "Your AE account has been emptied"
|
msgid "Your AE account has been emptied"
|
||||||
msgstr "Votre compte AE a été vidé"
|
msgstr "Votre compte AE a été vidé"
|
||||||
|
|
||||||
@ -3637,7 +3632,7 @@ msgstr "client"
|
|||||||
msgid "customers"
|
msgid "customers"
|
||||||
msgstr "clients"
|
msgstr "clients"
|
||||||
|
|
||||||
#: counter/models.py:110 counter/views.py:261
|
#: counter/models.py:110 counter/views/click.py:66
|
||||||
msgid "Not enough money"
|
msgid "Not enough money"
|
||||||
msgstr "Solde insuffisant"
|
msgstr "Solde insuffisant"
|
||||||
|
|
||||||
@ -3777,8 +3772,8 @@ msgstr "quantité"
|
|||||||
msgid "Sith account"
|
msgid "Sith account"
|
||||||
msgstr "Compte utilisateur"
|
msgstr "Compte utilisateur"
|
||||||
|
|
||||||
#: counter/models.py:797 sith/settings.py:413 sith/settings.py:418
|
#: counter/models.py:797 sith/settings.py:415 sith/settings.py:420
|
||||||
#: sith/settings.py:438
|
#: sith/settings.py:440
|
||||||
msgid "Credit card"
|
msgid "Credit card"
|
||||||
msgstr "Carte bancaire"
|
msgstr "Carte bancaire"
|
||||||
|
|
||||||
@ -3910,7 +3905,8 @@ msgstr "Liste des relevés de caisse"
|
|||||||
msgid "Theoric sums"
|
msgid "Theoric sums"
|
||||||
msgstr "Sommes théoriques"
|
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/cash.py:88
|
||||||
msgid "Emptied"
|
msgid "Emptied"
|
||||||
msgstr "Coffre vidé"
|
msgstr "Coffre vidé"
|
||||||
|
|
||||||
@ -3922,17 +3918,14 @@ msgstr "oui"
|
|||||||
msgid "There is no cash register summary in this website."
|
msgid "There is no cash register summary in this website."
|
||||||
msgstr "Il n'y a pas de relevé de caisse dans ce site web."
|
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:46
|
||||||
msgid "Add a student card"
|
#: launderette/templates/launderette/launderette_admin.jinja:8
|
||||||
msgstr "Ajouter une carte étudiante"
|
msgid "Selling"
|
||||||
|
msgstr "Vente"
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:38
|
#: counter/templates/counter/counter_click.jinja:57
|
||||||
msgid "This is not a valid student card UID"
|
#: counter/templates/counter/counter_click.jinja:122
|
||||||
msgstr "Ce n'est pas un UID de carte étudiante valide"
|
#: counter/templates/counter/fragments/create_student_card.jinja:10
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:40
|
|
||||||
#: counter/templates/counter/counter_click.jinja:67
|
|
||||||
#: counter/templates/counter/counter_click.jinja:132
|
|
||||||
#: counter/templates/counter/invoices_call.jinja:16
|
#: counter/templates/counter/invoices_call.jinja:16
|
||||||
#: launderette/templates/launderette/launderette_admin.jinja:35
|
#: launderette/templates/launderette/launderette_admin.jinja:35
|
||||||
#: launderette/templates/launderette/launderette_click.jinja:13
|
#: launderette/templates/launderette/launderette_click.jinja:13
|
||||||
@ -3941,29 +3934,16 @@ msgstr "Ce n'est pas un UID de carte étudiante valide"
|
|||||||
msgid "Go"
|
msgid "Go"
|
||||||
msgstr "Valider"
|
msgstr "Valider"
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:42
|
#: counter/templates/counter/counter_click.jinja:64
|
||||||
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"
|
|
||||||
msgstr "Vente"
|
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:74
|
|
||||||
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:19
|
#: eboutic/templates/eboutic/eboutic_makecommand.jinja:19
|
||||||
msgid "Basket: "
|
msgid "Basket: "
|
||||||
msgstr "Panier : "
|
msgstr "Panier : "
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:115
|
#: counter/templates/counter/counter_click.jinja:105
|
||||||
msgid "Finish"
|
msgid "Finish"
|
||||||
msgstr "Terminer"
|
msgstr "Terminer"
|
||||||
|
|
||||||
#: counter/templates/counter/counter_click.jinja:125
|
#: counter/templates/counter/counter_click.jinja:115
|
||||||
#: counter/templates/counter/refilling_list.jinja:9
|
#: counter/templates/counter/refilling_list.jinja:9
|
||||||
msgid "Refilling"
|
msgid "Refilling"
|
||||||
msgstr "Rechargement"
|
msgstr "Rechargement"
|
||||||
@ -4047,6 +4027,18 @@ msgstr "Nouveau eticket"
|
|||||||
msgid "There is no eticket in this website."
|
msgid "There is no eticket in this website."
|
||||||
msgstr "Il n'y a pas de eticket sur ce site web."
|
msgstr "Il n'y a pas de eticket sur ce site web."
|
||||||
|
|
||||||
|
#: counter/templates/counter/fragments/create_student_card.jinja:2
|
||||||
|
msgid "Add a student card"
|
||||||
|
msgstr "Ajouter une carte étudiante"
|
||||||
|
|
||||||
|
#: counter/templates/counter/fragments/create_student_card.jinja:13
|
||||||
|
msgid "Registered cards"
|
||||||
|
msgstr "Cartes enregistrées"
|
||||||
|
|
||||||
|
#: counter/templates/counter/fragments/create_student_card.jinja:27
|
||||||
|
msgid "No student card registered."
|
||||||
|
msgstr "Aucune carte étudiante enregistrée."
|
||||||
|
|
||||||
#: counter/templates/counter/invoices_call.jinja:8
|
#: counter/templates/counter/invoices_call.jinja:8
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Invoices call for %(date)s"
|
msgid "Invoices call for %(date)s"
|
||||||
@ -4219,104 +4211,104 @@ msgstr "Temps"
|
|||||||
msgid "Top 100 barman %(counter_name)s (all semesters)"
|
msgid "Top 100 barman %(counter_name)s (all semesters)"
|
||||||
msgstr "Top 100 barman %(counter_name)s (tous les semestres)"
|
msgstr "Top 100 barman %(counter_name)s (tous les semestres)"
|
||||||
|
|
||||||
#: counter/views.py:147
|
#: counter/views/cash.py:45
|
||||||
msgid "Cash summary"
|
|
||||||
msgstr "Relevé de caisse"
|
|
||||||
|
|
||||||
#: counter/views.py:156
|
|
||||||
msgid "Last operations"
|
|
||||||
msgstr "Dernières opérations"
|
|
||||||
|
|
||||||
#: counter/views.py:203
|
|
||||||
msgid "Bad credentials"
|
|
||||||
msgstr "Mauvais identifiants"
|
|
||||||
|
|
||||||
#: counter/views.py:205
|
|
||||||
msgid "User is not barman"
|
|
||||||
msgstr "L'utilisateur n'est pas barman."
|
|
||||||
|
|
||||||
#: counter/views.py:210
|
|
||||||
msgid "Bad location, someone is already logged in somewhere else"
|
|
||||||
msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs"
|
|
||||||
|
|
||||||
#: counter/views.py:252
|
|
||||||
msgid "Too young for that product"
|
|
||||||
msgstr "Trop jeune pour ce produit"
|
|
||||||
|
|
||||||
#: counter/views.py:255
|
|
||||||
msgid "Not allowed for that product"
|
|
||||||
msgstr "Non autorisé pour ce produit"
|
|
||||||
|
|
||||||
#: counter/views.py:258
|
|
||||||
msgid "No date of birth provided"
|
|
||||||
msgstr "Pas de date de naissance renseignée"
|
|
||||||
|
|
||||||
#: counter/views.py:546
|
|
||||||
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
|
|
||||||
msgid "Counter administration"
|
|
||||||
msgstr "Administration des comptoirs"
|
|
||||||
|
|
||||||
#: counter/views.py:693
|
|
||||||
msgid "Product types"
|
|
||||||
msgstr "Types de produit"
|
|
||||||
|
|
||||||
#: counter/views.py:913
|
|
||||||
msgid "10 cents"
|
msgid "10 cents"
|
||||||
msgstr "10 centimes"
|
msgstr "10 centimes"
|
||||||
|
|
||||||
#: counter/views.py:914
|
#: counter/views/cash.py:46
|
||||||
msgid "20 cents"
|
msgid "20 cents"
|
||||||
msgstr "20 centimes"
|
msgstr "20 centimes"
|
||||||
|
|
||||||
#: counter/views.py:915
|
#: counter/views/cash.py:47
|
||||||
msgid "50 cents"
|
msgid "50 cents"
|
||||||
msgstr "50 centimes"
|
msgstr "50 centimes"
|
||||||
|
|
||||||
#: counter/views.py:916
|
#: counter/views/cash.py:48
|
||||||
msgid "1 euro"
|
msgid "1 euro"
|
||||||
msgstr "1 €"
|
msgstr "1 €"
|
||||||
|
|
||||||
#: counter/views.py:917
|
#: counter/views/cash.py:49
|
||||||
msgid "2 euros"
|
msgid "2 euros"
|
||||||
msgstr "2 €"
|
msgstr "2 €"
|
||||||
|
|
||||||
#: counter/views.py:918
|
#: counter/views/cash.py:50
|
||||||
msgid "5 euros"
|
msgid "5 euros"
|
||||||
msgstr "5 €"
|
msgstr "5 €"
|
||||||
|
|
||||||
#: counter/views.py:919
|
#: counter/views/cash.py:51
|
||||||
msgid "10 euros"
|
msgid "10 euros"
|
||||||
msgstr "10 €"
|
msgstr "10 €"
|
||||||
|
|
||||||
#: counter/views.py:920
|
#: counter/views/cash.py:52
|
||||||
msgid "20 euros"
|
msgid "20 euros"
|
||||||
msgstr "20 €"
|
msgstr "20 €"
|
||||||
|
|
||||||
#: counter/views.py:921
|
#: counter/views/cash.py:53
|
||||||
msgid "50 euros"
|
msgid "50 euros"
|
||||||
msgstr "50 €"
|
msgstr "50 €"
|
||||||
|
|
||||||
#: counter/views.py:923
|
#: counter/views/cash.py:55
|
||||||
msgid "100 euros"
|
msgid "100 euros"
|
||||||
msgstr "100 €"
|
msgstr "100 €"
|
||||||
|
|
||||||
#: counter/views.py:926 counter/views.py:932 counter/views.py:938
|
#: counter/views/cash.py:58 counter/views/cash.py:64 counter/views/cash.py:70
|
||||||
#: counter/views.py:944 counter/views.py:950
|
#: counter/views/cash.py:76 counter/views/cash.py:82
|
||||||
msgid "Check amount"
|
msgid "Check amount"
|
||||||
msgstr "Montant du chèque"
|
msgstr "Montant du chèque"
|
||||||
|
|
||||||
#: counter/views.py:929 counter/views.py:935 counter/views.py:941
|
#: counter/views/cash.py:61 counter/views/cash.py:67 counter/views/cash.py:73
|
||||||
#: counter/views.py:947 counter/views.py:953
|
#: counter/views/cash.py:79 counter/views/cash.py:85
|
||||||
msgid "Check quantity"
|
msgid "Check quantity"
|
||||||
msgstr "Nombre de chèque"
|
msgstr "Nombre de chèque"
|
||||||
|
|
||||||
#: counter/views.py:1473
|
#: counter/views/click.py:57
|
||||||
|
msgid "Too young for that product"
|
||||||
|
msgstr "Trop jeune pour ce produit"
|
||||||
|
|
||||||
|
#: counter/views/click.py:60
|
||||||
|
msgid "Not allowed for that product"
|
||||||
|
msgstr "Non autorisé pour ce produit"
|
||||||
|
|
||||||
|
#: counter/views/click.py:63
|
||||||
|
msgid "No date of birth provided"
|
||||||
|
msgstr "Pas de date de naissance renseignée"
|
||||||
|
|
||||||
|
#: counter/views/click.py:331
|
||||||
|
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/eticket.py:120
|
||||||
msgid "people(s)"
|
msgid "people(s)"
|
||||||
msgstr "personne(s)"
|
msgstr "personne(s)"
|
||||||
|
|
||||||
|
#: counter/views/home.py:74
|
||||||
|
msgid "Bad credentials"
|
||||||
|
msgstr "Mauvais identifiants"
|
||||||
|
|
||||||
|
#: counter/views/home.py:76
|
||||||
|
msgid "User is not barman"
|
||||||
|
msgstr "L'utilisateur n'est pas barman."
|
||||||
|
|
||||||
|
#: counter/views/home.py:81
|
||||||
|
msgid "Bad location, someone is already logged in somewhere else"
|
||||||
|
msgstr "Mauvais comptoir, quelqu'un est déjà connecté ailleurs"
|
||||||
|
|
||||||
|
#: counter/views/mixins.py:68
|
||||||
|
msgid "Cash summary"
|
||||||
|
msgstr "Relevé de caisse"
|
||||||
|
|
||||||
|
#: counter/views/mixins.py:77
|
||||||
|
msgid "Last operations"
|
||||||
|
msgstr "Dernières opérations"
|
||||||
|
|
||||||
|
#: counter/views/mixins.py:84
|
||||||
|
msgid "Counter administration"
|
||||||
|
msgstr "Administration des comptoirs"
|
||||||
|
|
||||||
|
#: counter/views/mixins.py:104
|
||||||
|
msgid "Product types"
|
||||||
|
msgstr "Types de produit"
|
||||||
|
|
||||||
#: eboutic/forms.py:88
|
#: eboutic/forms.py:88
|
||||||
msgid "The request was badly formatted."
|
msgid "The request was badly formatted."
|
||||||
msgstr "La requête a été mal formatée."
|
msgstr "La requête a été mal formatée."
|
||||||
@ -4894,12 +4886,12 @@ msgid "Washing and drying"
|
|||||||
msgstr "Lavage et séchage"
|
msgstr "Lavage et séchage"
|
||||||
|
|
||||||
#: launderette/templates/launderette/launderette_book.jinja:27
|
#: launderette/templates/launderette/launderette_book.jinja:27
|
||||||
#: sith/settings.py:656
|
#: sith/settings.py:658
|
||||||
msgid "Washing"
|
msgid "Washing"
|
||||||
msgstr "Lavage"
|
msgstr "Lavage"
|
||||||
|
|
||||||
#: launderette/templates/launderette/launderette_book.jinja:31
|
#: launderette/templates/launderette/launderette_book.jinja:31
|
||||||
#: sith/settings.py:656
|
#: sith/settings.py:658
|
||||||
msgid "Drying"
|
msgid "Drying"
|
||||||
msgstr "Séchage"
|
msgstr "Séchage"
|
||||||
|
|
||||||
@ -5414,380 +5406,380 @@ msgstr "Personne(s)"
|
|||||||
msgid "Identify users on pictures"
|
msgid "Identify users on pictures"
|
||||||
msgstr "Identifiez les utilisateurs sur les photos"
|
msgstr "Identifiez les utilisateurs sur les photos"
|
||||||
|
|
||||||
#: sith/settings.py:254 sith/settings.py:475
|
#: sith/settings.py:253 sith/settings.py:477
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Anglais"
|
msgstr "Anglais"
|
||||||
|
|
||||||
#: sith/settings.py:254 sith/settings.py:474
|
#: sith/settings.py:253 sith/settings.py:476
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr "Français"
|
msgstr "Français"
|
||||||
|
|
||||||
#: sith/settings.py:394
|
#: sith/settings.py:396
|
||||||
msgid "TC"
|
msgid "TC"
|
||||||
msgstr "TC"
|
msgstr "TC"
|
||||||
|
|
||||||
#: sith/settings.py:395
|
#: sith/settings.py:397
|
||||||
msgid "IMSI"
|
msgid "IMSI"
|
||||||
msgstr "IMSI"
|
msgstr "IMSI"
|
||||||
|
|
||||||
#: sith/settings.py:396
|
#: sith/settings.py:398
|
||||||
msgid "IMAP"
|
msgid "IMAP"
|
||||||
msgstr "IMAP"
|
msgstr "IMAP"
|
||||||
|
|
||||||
#: sith/settings.py:397
|
#: sith/settings.py:399
|
||||||
msgid "INFO"
|
msgid "INFO"
|
||||||
msgstr "INFO"
|
msgstr "INFO"
|
||||||
|
|
||||||
#: sith/settings.py:398
|
#: sith/settings.py:400
|
||||||
msgid "GI"
|
msgid "GI"
|
||||||
msgstr "GI"
|
msgstr "GI"
|
||||||
|
|
||||||
#: sith/settings.py:399 sith/settings.py:485
|
#: sith/settings.py:401 sith/settings.py:487
|
||||||
msgid "E"
|
msgid "E"
|
||||||
msgstr "E"
|
msgstr "E"
|
||||||
|
|
||||||
#: sith/settings.py:400
|
#: sith/settings.py:402
|
||||||
msgid "EE"
|
msgid "EE"
|
||||||
msgstr "EE"
|
msgstr "EE"
|
||||||
|
|
||||||
#: sith/settings.py:401
|
#: sith/settings.py:403
|
||||||
msgid "GESC"
|
msgid "GESC"
|
||||||
msgstr "GESC"
|
msgstr "GESC"
|
||||||
|
|
||||||
#: sith/settings.py:402
|
#: sith/settings.py:404
|
||||||
msgid "GMC"
|
msgid "GMC"
|
||||||
msgstr "GMC"
|
msgstr "GMC"
|
||||||
|
|
||||||
#: sith/settings.py:403
|
#: sith/settings.py:405
|
||||||
msgid "MC"
|
msgid "MC"
|
||||||
msgstr "MC"
|
msgstr "MC"
|
||||||
|
|
||||||
#: sith/settings.py:404
|
#: sith/settings.py:406
|
||||||
msgid "EDIM"
|
msgid "EDIM"
|
||||||
msgstr "EDIM"
|
msgstr "EDIM"
|
||||||
|
|
||||||
#: sith/settings.py:405
|
#: sith/settings.py:407
|
||||||
msgid "Humanities"
|
msgid "Humanities"
|
||||||
msgstr "Humanités"
|
msgstr "Humanités"
|
||||||
|
|
||||||
#: sith/settings.py:406
|
#: sith/settings.py:408
|
||||||
msgid "N/A"
|
msgid "N/A"
|
||||||
msgstr "N/A"
|
msgstr "N/A"
|
||||||
|
|
||||||
#: sith/settings.py:410 sith/settings.py:417 sith/settings.py:436
|
#: sith/settings.py:412 sith/settings.py:419 sith/settings.py:438
|
||||||
msgid "Check"
|
msgid "Check"
|
||||||
msgstr "Chèque"
|
msgstr "Chèque"
|
||||||
|
|
||||||
#: sith/settings.py:411 sith/settings.py:419 sith/settings.py:437
|
#: sith/settings.py:413 sith/settings.py:421 sith/settings.py:439
|
||||||
msgid "Cash"
|
msgid "Cash"
|
||||||
msgstr "Espèces"
|
msgstr "Espèces"
|
||||||
|
|
||||||
#: sith/settings.py:412
|
#: sith/settings.py:414
|
||||||
msgid "Transfert"
|
msgid "Transfert"
|
||||||
msgstr "Virement"
|
msgstr "Virement"
|
||||||
|
|
||||||
#: sith/settings.py:425
|
#: sith/settings.py:427
|
||||||
msgid "Belfort"
|
msgid "Belfort"
|
||||||
msgstr "Belfort"
|
msgstr "Belfort"
|
||||||
|
|
||||||
#: sith/settings.py:426
|
#: sith/settings.py:428
|
||||||
msgid "Sevenans"
|
msgid "Sevenans"
|
||||||
msgstr "Sevenans"
|
msgstr "Sevenans"
|
||||||
|
|
||||||
#: sith/settings.py:427
|
#: sith/settings.py:429
|
||||||
msgid "Montbéliard"
|
msgid "Montbéliard"
|
||||||
msgstr "Montbéliard"
|
msgstr "Montbéliard"
|
||||||
|
|
||||||
#: sith/settings.py:455
|
#: sith/settings.py:457
|
||||||
msgid "Free"
|
msgid "Free"
|
||||||
msgstr "Libre"
|
msgstr "Libre"
|
||||||
|
|
||||||
#: sith/settings.py:456
|
#: sith/settings.py:458
|
||||||
msgid "CS"
|
msgid "CS"
|
||||||
msgstr "CS"
|
msgstr "CS"
|
||||||
|
|
||||||
#: sith/settings.py:457
|
#: sith/settings.py:459
|
||||||
msgid "TM"
|
msgid "TM"
|
||||||
msgstr "TM"
|
msgstr "TM"
|
||||||
|
|
||||||
#: sith/settings.py:458
|
#: sith/settings.py:460
|
||||||
msgid "OM"
|
msgid "OM"
|
||||||
msgstr "OM"
|
msgstr "OM"
|
||||||
|
|
||||||
#: sith/settings.py:459
|
#: sith/settings.py:461
|
||||||
msgid "QC"
|
msgid "QC"
|
||||||
msgstr "QC"
|
msgstr "QC"
|
||||||
|
|
||||||
#: sith/settings.py:460
|
#: sith/settings.py:462
|
||||||
msgid "EC"
|
msgid "EC"
|
||||||
msgstr "EC"
|
msgstr "EC"
|
||||||
|
|
||||||
#: sith/settings.py:461
|
#: sith/settings.py:463
|
||||||
msgid "RN"
|
msgid "RN"
|
||||||
msgstr "RN"
|
msgstr "RN"
|
||||||
|
|
||||||
#: sith/settings.py:462
|
#: sith/settings.py:464
|
||||||
msgid "ST"
|
msgid "ST"
|
||||||
msgstr "ST"
|
msgstr "ST"
|
||||||
|
|
||||||
#: sith/settings.py:463
|
#: sith/settings.py:465
|
||||||
msgid "EXT"
|
msgid "EXT"
|
||||||
msgstr "EXT"
|
msgstr "EXT"
|
||||||
|
|
||||||
#: sith/settings.py:468
|
#: sith/settings.py:470
|
||||||
msgid "Autumn"
|
msgid "Autumn"
|
||||||
msgstr "Automne"
|
msgstr "Automne"
|
||||||
|
|
||||||
#: sith/settings.py:469
|
#: sith/settings.py:471
|
||||||
msgid "Spring"
|
msgid "Spring"
|
||||||
msgstr "Printemps"
|
msgstr "Printemps"
|
||||||
|
|
||||||
#: sith/settings.py:470
|
#: sith/settings.py:472
|
||||||
msgid "Autumn and spring"
|
msgid "Autumn and spring"
|
||||||
msgstr "Automne et printemps"
|
msgstr "Automne et printemps"
|
||||||
|
|
||||||
#: sith/settings.py:476
|
#: sith/settings.py:478
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Allemand"
|
msgstr "Allemand"
|
||||||
|
|
||||||
#: sith/settings.py:477
|
#: sith/settings.py:479
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr "Espagnol"
|
msgstr "Espagnol"
|
||||||
|
|
||||||
#: sith/settings.py:481
|
#: sith/settings.py:483
|
||||||
msgid "A"
|
msgid "A"
|
||||||
msgstr "A"
|
msgstr "A"
|
||||||
|
|
||||||
#: sith/settings.py:482
|
#: sith/settings.py:484
|
||||||
msgid "B"
|
msgid "B"
|
||||||
msgstr "B"
|
msgstr "B"
|
||||||
|
|
||||||
#: sith/settings.py:483
|
#: sith/settings.py:485
|
||||||
msgid "C"
|
msgid "C"
|
||||||
msgstr "C"
|
msgstr "C"
|
||||||
|
|
||||||
#: sith/settings.py:484
|
#: sith/settings.py:486
|
||||||
msgid "D"
|
msgid "D"
|
||||||
msgstr "D"
|
msgstr "D"
|
||||||
|
|
||||||
#: sith/settings.py:486
|
#: sith/settings.py:488
|
||||||
msgid "FX"
|
msgid "FX"
|
||||||
msgstr "FX"
|
msgstr "FX"
|
||||||
|
|
||||||
#: sith/settings.py:487
|
#: sith/settings.py:489
|
||||||
msgid "F"
|
msgid "F"
|
||||||
msgstr "F"
|
msgstr "F"
|
||||||
|
|
||||||
#: sith/settings.py:488
|
#: sith/settings.py:490
|
||||||
msgid "Abs"
|
msgid "Abs"
|
||||||
msgstr "Abs"
|
msgstr "Abs"
|
||||||
|
|
||||||
#: sith/settings.py:492
|
#: sith/settings.py:494
|
||||||
msgid "Selling deletion"
|
msgid "Selling deletion"
|
||||||
msgstr "Suppression de vente"
|
msgstr "Suppression de vente"
|
||||||
|
|
||||||
#: sith/settings.py:493
|
#: sith/settings.py:495
|
||||||
msgid "Refilling deletion"
|
msgid "Refilling deletion"
|
||||||
msgstr "Suppression de rechargement"
|
msgstr "Suppression de rechargement"
|
||||||
|
|
||||||
#: sith/settings.py:537
|
#: sith/settings.py:539
|
||||||
msgid "One semester"
|
msgid "One semester"
|
||||||
msgstr "Un semestre, 20 €"
|
msgstr "Un semestre, 20 €"
|
||||||
|
|
||||||
#: sith/settings.py:538
|
#: sith/settings.py:540
|
||||||
msgid "Two semesters"
|
msgid "Two semesters"
|
||||||
msgstr "Deux semestres, 35 €"
|
msgstr "Deux semestres, 35 €"
|
||||||
|
|
||||||
#: sith/settings.py:540
|
#: sith/settings.py:542
|
||||||
msgid "Common core cursus"
|
msgid "Common core cursus"
|
||||||
msgstr "Cursus tronc commun, 60 €"
|
msgstr "Cursus tronc commun, 60 €"
|
||||||
|
|
||||||
#: sith/settings.py:544
|
#: sith/settings.py:546
|
||||||
msgid "Branch cursus"
|
msgid "Branch cursus"
|
||||||
msgstr "Cursus branche, 60 €"
|
msgstr "Cursus branche, 60 €"
|
||||||
|
|
||||||
#: sith/settings.py:545
|
#: sith/settings.py:547
|
||||||
msgid "Alternating cursus"
|
msgid "Alternating cursus"
|
||||||
msgstr "Cursus alternant, 30 €"
|
msgstr "Cursus alternant, 30 €"
|
||||||
|
|
||||||
#: sith/settings.py:546
|
#: sith/settings.py:548
|
||||||
msgid "Honorary member"
|
msgid "Honorary member"
|
||||||
msgstr "Membre honoraire, 0 €"
|
msgstr "Membre honoraire, 0 €"
|
||||||
|
|
||||||
#: sith/settings.py:547
|
#: sith/settings.py:549
|
||||||
msgid "Assidu member"
|
msgid "Assidu member"
|
||||||
msgstr "Membre d'Assidu, 0 €"
|
msgstr "Membre d'Assidu, 0 €"
|
||||||
|
|
||||||
#: sith/settings.py:548
|
#: sith/settings.py:550
|
||||||
msgid "Amicale/DOCEO member"
|
msgid "Amicale/DOCEO member"
|
||||||
msgstr "Membre de l'Amicale/DOCEO, 0 €"
|
msgstr "Membre de l'Amicale/DOCEO, 0 €"
|
||||||
|
|
||||||
#: sith/settings.py:549
|
#: sith/settings.py:551
|
||||||
msgid "UT network member"
|
msgid "UT network member"
|
||||||
msgstr "Cotisant du réseau UT, 0 €"
|
msgstr "Cotisant du réseau UT, 0 €"
|
||||||
|
|
||||||
#: sith/settings.py:550
|
#: sith/settings.py:552
|
||||||
msgid "CROUS member"
|
msgid "CROUS member"
|
||||||
msgstr "Membres du CROUS, 0 €"
|
msgstr "Membres du CROUS, 0 €"
|
||||||
|
|
||||||
#: sith/settings.py:551
|
#: sith/settings.py:553
|
||||||
msgid "Sbarro/ESTA member"
|
msgid "Sbarro/ESTA member"
|
||||||
msgstr "Membre de Sbarro ou de l'ESTA, 20 €"
|
msgstr "Membre de Sbarro ou de l'ESTA, 20 €"
|
||||||
|
|
||||||
#: sith/settings.py:553
|
#: sith/settings.py:555
|
||||||
msgid "One semester Welcome Week"
|
msgid "One semester Welcome Week"
|
||||||
msgstr "Un semestre Welcome Week"
|
msgstr "Un semestre Welcome Week"
|
||||||
|
|
||||||
#: sith/settings.py:557
|
#: sith/settings.py:559
|
||||||
msgid "One month for free"
|
msgid "One month for free"
|
||||||
msgstr "Un mois gratuit"
|
msgstr "Un mois gratuit"
|
||||||
|
|
||||||
#: sith/settings.py:558
|
#: sith/settings.py:560
|
||||||
msgid "Two months for free"
|
msgid "Two months for free"
|
||||||
msgstr "Deux mois gratuits"
|
msgstr "Deux mois gratuits"
|
||||||
|
|
||||||
#: sith/settings.py:559
|
#: sith/settings.py:561
|
||||||
msgid "Eurok's volunteer"
|
msgid "Eurok's volunteer"
|
||||||
msgstr "Bénévole Eurockéennes"
|
msgstr "Bénévole Eurockéennes"
|
||||||
|
|
||||||
#: sith/settings.py:561
|
#: sith/settings.py:563
|
||||||
msgid "Six weeks for free"
|
msgid "Six weeks for free"
|
||||||
msgstr "6 semaines gratuites"
|
msgstr "6 semaines gratuites"
|
||||||
|
|
||||||
#: sith/settings.py:565
|
#: sith/settings.py:567
|
||||||
msgid "One day"
|
msgid "One day"
|
||||||
msgstr "Un jour"
|
msgstr "Un jour"
|
||||||
|
|
||||||
#: sith/settings.py:566
|
#: sith/settings.py:568
|
||||||
msgid "GA staff member"
|
msgid "GA staff member"
|
||||||
msgstr "Membre staff GA (2 semaines), 1 €"
|
msgstr "Membre staff GA (2 semaines), 1 €"
|
||||||
|
|
||||||
#: sith/settings.py:569
|
#: sith/settings.py:571
|
||||||
msgid "One semester (-20%)"
|
msgid "One semester (-20%)"
|
||||||
msgstr "Un semestre (-20%), 12 €"
|
msgstr "Un semestre (-20%), 12 €"
|
||||||
|
|
||||||
#: sith/settings.py:574
|
#: sith/settings.py:576
|
||||||
msgid "Two semesters (-20%)"
|
msgid "Two semesters (-20%)"
|
||||||
msgstr "Deux semestres (-20%), 22 €"
|
msgstr "Deux semestres (-20%), 22 €"
|
||||||
|
|
||||||
#: sith/settings.py:579
|
#: sith/settings.py:581
|
||||||
msgid "Common core cursus (-20%)"
|
msgid "Common core cursus (-20%)"
|
||||||
msgstr "Cursus tronc commun (-20%), 36 €"
|
msgstr "Cursus tronc commun (-20%), 36 €"
|
||||||
|
|
||||||
#: sith/settings.py:584
|
#: sith/settings.py:586
|
||||||
msgid "Branch cursus (-20%)"
|
msgid "Branch cursus (-20%)"
|
||||||
msgstr "Cursus branche (-20%), 36 €"
|
msgstr "Cursus branche (-20%), 36 €"
|
||||||
|
|
||||||
#: sith/settings.py:589
|
#: sith/settings.py:591
|
||||||
msgid "Alternating cursus (-20%)"
|
msgid "Alternating cursus (-20%)"
|
||||||
msgstr "Cursus alternant (-20%), 24 €"
|
msgstr "Cursus alternant (-20%), 24 €"
|
||||||
|
|
||||||
#: sith/settings.py:595
|
#: sith/settings.py:597
|
||||||
msgid "One year for free(CA offer)"
|
msgid "One year for free(CA offer)"
|
||||||
msgstr "Une année offerte (Offre CA)"
|
msgstr "Une année offerte (Offre CA)"
|
||||||
|
|
||||||
#: sith/settings.py:615
|
#: sith/settings.py:617
|
||||||
msgid "President"
|
msgid "President"
|
||||||
msgstr "Président⸱e"
|
msgstr "Président⸱e"
|
||||||
|
|
||||||
#: sith/settings.py:616
|
#: sith/settings.py:618
|
||||||
msgid "Vice-President"
|
msgid "Vice-President"
|
||||||
msgstr "Vice-Président⸱e"
|
msgstr "Vice-Président⸱e"
|
||||||
|
|
||||||
#: sith/settings.py:617
|
#: sith/settings.py:619
|
||||||
msgid "Treasurer"
|
msgid "Treasurer"
|
||||||
msgstr "Trésorier⸱e"
|
msgstr "Trésorier⸱e"
|
||||||
|
|
||||||
#: sith/settings.py:618
|
#: sith/settings.py:620
|
||||||
msgid "Communication supervisor"
|
msgid "Communication supervisor"
|
||||||
msgstr "Responsable communication"
|
msgstr "Responsable communication"
|
||||||
|
|
||||||
#: sith/settings.py:619
|
#: sith/settings.py:621
|
||||||
msgid "Secretary"
|
msgid "Secretary"
|
||||||
msgstr "Secrétaire"
|
msgstr "Secrétaire"
|
||||||
|
|
||||||
#: sith/settings.py:620
|
#: sith/settings.py:622
|
||||||
msgid "IT supervisor"
|
msgid "IT supervisor"
|
||||||
msgstr "Responsable info"
|
msgstr "Responsable info"
|
||||||
|
|
||||||
#: sith/settings.py:621
|
#: sith/settings.py:623
|
||||||
msgid "Board member"
|
msgid "Board member"
|
||||||
msgstr "Membre du bureau"
|
msgstr "Membre du bureau"
|
||||||
|
|
||||||
#: sith/settings.py:622
|
#: sith/settings.py:624
|
||||||
msgid "Active member"
|
msgid "Active member"
|
||||||
msgstr "Membre actif⸱ve"
|
msgstr "Membre actif⸱ve"
|
||||||
|
|
||||||
#: sith/settings.py:623
|
#: sith/settings.py:625
|
||||||
msgid "Curious"
|
msgid "Curious"
|
||||||
msgstr "Curieux⸱euse"
|
msgstr "Curieux⸱euse"
|
||||||
|
|
||||||
#: sith/settings.py:660
|
#: sith/settings.py:662
|
||||||
msgid "A new poster needs to be moderated"
|
msgid "A new poster needs to be moderated"
|
||||||
msgstr "Une nouvelle affiche a besoin d'être modérée"
|
msgstr "Une nouvelle affiche a besoin d'être modérée"
|
||||||
|
|
||||||
#: sith/settings.py:661
|
#: sith/settings.py:663
|
||||||
msgid "A new mailing list needs to be moderated"
|
msgid "A new mailing list needs to be moderated"
|
||||||
msgstr "Une nouvelle mailing list a besoin d'être modérée"
|
msgstr "Une nouvelle mailing list a besoin d'être modérée"
|
||||||
|
|
||||||
#: sith/settings.py:664
|
#: sith/settings.py:666
|
||||||
msgid "A new pedagogy comment has been signaled for moderation"
|
msgid "A new pedagogy comment has been signaled for moderation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Un nouveau commentaire de la pédagogie a été signalé pour la modération"
|
"Un nouveau commentaire de la pédagogie a été signalé pour la modération"
|
||||||
|
|
||||||
#: sith/settings.py:666
|
#: sith/settings.py:668
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "There are %s fresh news to be moderated"
|
msgid "There are %s fresh news to be moderated"
|
||||||
msgstr "Il y a %s nouvelles toutes fraîches à modérer"
|
msgstr "Il y a %s nouvelles toutes fraîches à modérer"
|
||||||
|
|
||||||
#: sith/settings.py:667
|
#: sith/settings.py:669
|
||||||
msgid "New files to be moderated"
|
msgid "New files to be moderated"
|
||||||
msgstr "Nouveaux fichiers à modérer"
|
msgstr "Nouveaux fichiers à modérer"
|
||||||
|
|
||||||
#: sith/settings.py:668
|
#: sith/settings.py:670
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "There are %s pictures to be moderated in the SAS"
|
msgid "There are %s pictures to be moderated in the SAS"
|
||||||
msgstr "Il y a %s photos à modérer dans le SAS"
|
msgstr "Il y a %s photos à modérer dans le SAS"
|
||||||
|
|
||||||
#: sith/settings.py:669
|
#: sith/settings.py:671
|
||||||
msgid "You've been identified on some pictures"
|
msgid "You've been identified on some pictures"
|
||||||
msgstr "Vous avez été identifié sur des photos"
|
msgstr "Vous avez été identifié sur des photos"
|
||||||
|
|
||||||
#: sith/settings.py:670
|
#: sith/settings.py:672
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You just refilled of %s €"
|
msgid "You just refilled of %s €"
|
||||||
msgstr "Vous avez rechargé votre compte de %s€"
|
msgstr "Vous avez rechargé votre compte de %s€"
|
||||||
|
|
||||||
#: sith/settings.py:671
|
#: sith/settings.py:673
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "You just bought %s"
|
msgid "You just bought %s"
|
||||||
msgstr "Vous avez acheté %s"
|
msgstr "Vous avez acheté %s"
|
||||||
|
|
||||||
#: sith/settings.py:672
|
#: sith/settings.py:674
|
||||||
msgid "You have a notification"
|
msgid "You have a notification"
|
||||||
msgstr "Vous avez une notification"
|
msgstr "Vous avez une notification"
|
||||||
|
|
||||||
#: sith/settings.py:684
|
#: sith/settings.py:686
|
||||||
msgid "Success!"
|
msgid "Success!"
|
||||||
msgstr "Succès !"
|
msgstr "Succès !"
|
||||||
|
|
||||||
#: sith/settings.py:685
|
#: sith/settings.py:687
|
||||||
msgid "Fail!"
|
msgid "Fail!"
|
||||||
msgstr "Échec !"
|
msgstr "Échec !"
|
||||||
|
|
||||||
#: sith/settings.py:686
|
#: sith/settings.py:688
|
||||||
msgid "You successfully posted an article in the Weekmail"
|
msgid "You successfully posted an article in the Weekmail"
|
||||||
msgstr "Article posté avec succès dans le Weekmail"
|
msgstr "Article posté avec succès dans le Weekmail"
|
||||||
|
|
||||||
#: sith/settings.py:687
|
#: sith/settings.py:689
|
||||||
msgid "You successfully edited an article in the Weekmail"
|
msgid "You successfully edited an article in the Weekmail"
|
||||||
msgstr "Article édité avec succès dans le Weekmail"
|
msgstr "Article édité avec succès dans le Weekmail"
|
||||||
|
|
||||||
#: sith/settings.py:688
|
#: sith/settings.py:690
|
||||||
msgid "You successfully sent the Weekmail"
|
msgid "You successfully sent the Weekmail"
|
||||||
msgstr "Weekmail envoyé avec succès"
|
msgstr "Weekmail envoyé avec succès"
|
||||||
|
|
||||||
#: sith/settings.py:696
|
#: sith/settings.py:698
|
||||||
msgid "AE tee-shirt"
|
msgid "AE tee-shirt"
|
||||||
msgstr "Tee-shirt AE"
|
msgstr "Tee-shirt AE"
|
||||||
|
|
||||||
@ -5828,27 +5820,14 @@ msgstr "Vous ne pouvez pas cotiser plusieurs fois pour la même période"
|
|||||||
msgid "Subscription created for %(user)s"
|
msgid "Subscription created for %(user)s"
|
||||||
msgstr "Cotisation créée pour %(user)s"
|
msgstr "Cotisation créée pour %(user)s"
|
||||||
|
|
||||||
#: subscription/templates/subscription/fragments/creation_success.jinja:8
|
#: subscription/templates/subscription/fragments/creation_success.jinja:19
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"%(user)s received its new %(type)s subscription. It will be active until "
|
|
||||||
"%(end)s included."
|
|
||||||
msgstr ""
|
|
||||||
"%(user)s a reçu sa nouvelle cotisaton %(type)s. Elle sert active jusqu'au "
|
|
||||||
"%(end)s inclu."
|
|
||||||
|
|
||||||
#: subscription/templates/subscription/fragments/creation_success.jinja:16
|
|
||||||
msgid "Go to user profile"
|
msgid "Go to user profile"
|
||||||
msgstr "Voir le profil de l'utilisateur"
|
msgstr "Voir le profil de l'utilisateur"
|
||||||
|
|
||||||
#: subscription/templates/subscription/fragments/creation_success.jinja:24
|
#: subscription/templates/subscription/fragments/creation_success.jinja:27
|
||||||
msgid "Create another subscription"
|
msgid "Create another subscription"
|
||||||
msgstr "Créer une nouvelle cotisation"
|
msgstr "Créer une nouvelle cotisation"
|
||||||
|
|
||||||
#: subscription/templates/subscription/subscription.jinja
|
|
||||||
msgid "Existing member"
|
|
||||||
msgstr "Membre existant"
|
|
||||||
|
|
||||||
#: subscription/templates/subscription/stats.jinja:27
|
#: subscription/templates/subscription/stats.jinja:27
|
||||||
msgid "Total subscriptions"
|
msgid "Total subscriptions"
|
||||||
msgstr "Cotisations totales"
|
msgstr "Cotisations totales"
|
||||||
@ -5857,6 +5836,10 @@ msgstr "Cotisations totales"
|
|||||||
msgid "Subscriptions by type"
|
msgid "Subscriptions by type"
|
||||||
msgstr "Cotisations par type"
|
msgstr "Cotisations par type"
|
||||||
|
|
||||||
|
#: subscription/templates/subscription/subscription.jinja:38
|
||||||
|
msgid "Existing member"
|
||||||
|
msgstr "Membre existant"
|
||||||
|
|
||||||
#: trombi/models.py:55
|
#: trombi/models.py:55
|
||||||
msgid "subscription deadline"
|
msgid "subscription deadline"
|
||||||
msgstr "fin des inscriptions"
|
msgstr "fin des inscriptions"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
from model_bakery.recipe import Recipe
|
from model_bakery.recipe import Recipe
|
||||||
from pytest_django.asserts import assertNumQueries
|
|
||||||
|
|
||||||
from core.baker_recipes import old_subscriber_user, subscriber_user
|
from core.baker_recipes import old_subscriber_user, subscriber_user
|
||||||
from core.models import RealGroup, SithFile, User
|
from core.models import RealGroup, SithFile, User
|
||||||
@ -128,9 +128,11 @@ class TestPictureSearch(TestSas):
|
|||||||
def test_num_queries(self):
|
def test_num_queries(self):
|
||||||
"""Test that the number of queries is stable."""
|
"""Test that the number of queries is stable."""
|
||||||
self.client.force_login(subscriber_user.make())
|
self.client.force_login(subscriber_user.make())
|
||||||
with assertNumQueries(5):
|
cache.clear()
|
||||||
|
with self.assertNumQueries(7):
|
||||||
|
# 2 requests to create the session
|
||||||
# 1 request to fetch the user from the db
|
# 1 request to fetch the user from the db
|
||||||
# 2 requests to check the user permissions
|
# 2 requests to check the user permissions, depends on the db engine
|
||||||
# 1 request to fetch the pictures
|
# 1 request to fetch the pictures
|
||||||
# 1 request to count the total number of items in the pagination
|
# 1 request to count the total number of items in the pagination
|
||||||
self.client.get(self.url)
|
self.client.get(self.url)
|
||||||
|
Loading…
Reference in New Issue
Block a user