mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 07:41:14 +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";
|
||||
|
||||
document.body.addEventListener("htmx:beforeRequest", (event) => {
|
||||
event.target.ariaBusy = true;
|
||||
});
|
||||
|
||||
document.body.addEventListener("htmx:afterRequest", (event) => {
|
||||
event.originalTarget.ariaBusy = null;
|
||||
});
|
||||
|
||||
Object.assign(window, { htmx });
|
||||
|
@ -6,7 +6,16 @@
|
||||
**/
|
||||
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
|
||||
return (component: CustomElementConstructor) => {
|
||||
window.customElements.define(name, component, options);
|
||||
try {
|
||||
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 %}
|
||||
|
||||
|
||||
{% if profile.customer %}
|
||||
<h3>{% trans %}Student cards{% endtrans %}</h3>
|
||||
|
||||
{% 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">
|
||||
{% 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 %}
|
||||
</p>
|
||||
{% 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>
|
||||
{% if student_card %}
|
||||
{{ student_card }}
|
||||
<p class="justify">
|
||||
{% 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 %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -13,22 +13,41 @@
|
||||
#
|
||||
#
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
|
||||
# Image utils
|
||||
from io import BytesIO
|
||||
from typing import Optional
|
||||
from typing import Any
|
||||
|
||||
import PIL
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.forms import BaseForm
|
||||
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 PIL import ExifTags
|
||||
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.
|
||||
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)
|
||||
|
||||
|
||||
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.
|
||||
If no date is given, return the semester code of the current semester.
|
||||
|
||||
|
@ -70,8 +70,8 @@ from core.views.forms import (
|
||||
UserGodfathersForm,
|
||||
UserProfileForm,
|
||||
)
|
||||
from counter.forms import StudentCardForm
|
||||
from counter.models import Refilling, Selling
|
||||
from counter.views.student_card import StudentCardFormView
|
||||
from eboutic.models import Invoice
|
||||
from subscription.models import Subscription
|
||||
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
|
||||
):
|
||||
kwargs["trombi_form"] = UserTrombiForm()
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -45,9 +45,7 @@ class BillingInfoForm(forms.ModelForm):
|
||||
|
||||
|
||||
class StudentCardForm(forms.ModelForm):
|
||||
"""Form for adding student cards
|
||||
Only used for user profile since CounterClick is to complicated.
|
||||
"""
|
||||
"""Form for adding student cards"""
|
||||
|
||||
class Meta:
|
||||
model = StudentCard
|
||||
@ -114,14 +112,6 @@ class GetUserForm(forms.Form):
|
||||
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):
|
||||
error_css_class = "error"
|
||||
required_css_class = "required"
|
||||
|
@ -29,26 +29,9 @@
|
||||
{{ user_mini_profile(customer.user) }}
|
||||
{{ user_subscription(customer.user) }}
|
||||
<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>
|
||||
{% for card in student_cards %}
|
||||
<li>{{ card.uid }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{% trans %}No card registered{% endtrans %}
|
||||
{% if counter.type == 'BAR' %}
|
||||
{{ student_card }}
|
||||
{% endif %}
|
||||
</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 string
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.base_user import make_password
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
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 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
|
||||
@ -162,148 +174,269 @@ class TestStudentCard(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.krophil = User.objects.get(username="krophil")
|
||||
cls.sli = User.objects.get(username="sli")
|
||||
cls.skia = User.objects.get(username="skia")
|
||||
cls.root = User.objects.get(username="root")
|
||||
cls.customer = subscriber_user.make()
|
||||
cls.customer.save()
|
||||
cls.barmen = subscriber_user.make(password=make_password("plop"))
|
||||
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):
|
||||
# Auto login on counter
|
||||
self.client.post(
|
||||
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):
|
||||
response = self.client.post(
|
||||
reverse("counter:details", args=[self.counter.id]),
|
||||
{"code": "9A89B82018B0A0"},
|
||||
{"code": self.valid_card.uid},
|
||||
)
|
||||
|
||||
assert response.url == reverse(
|
||||
"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):
|
||||
# Test card with mixed letters and numbers
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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):
|
||||
# UID too short
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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(
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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(
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:click",
|
||||
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||
"counter:add_student_card",
|
||||
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(
|
||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||
self.assertContains(response, text="Cet UID est invalide")
|
||||
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):
|
||||
self.client.force_login(self.sli)
|
||||
self.client.force_login(self.customer)
|
||||
self.client.post(
|
||||
reverse(
|
||||
"counter:delete_student_card",
|
||||
kwargs={
|
||||
"customer_id": self.sli.customer.pk,
|
||||
"card_id": self.sli.customer.student_cards.first().id,
|
||||
"customer_id": self.customer.customer.pk,
|
||||
"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):
|
||||
self.client.force_login(self.skia)
|
||||
self.client.force_login(self.board_admin)
|
||||
self.client.post(
|
||||
reverse(
|
||||
"counter:delete_student_card",
|
||||
kwargs={
|
||||
"customer_id": self.sli.customer.pk,
|
||||
"card_id": self.sli.customer.student_cards.first().id,
|
||||
"customer_id": self.customer.customer.pk,
|
||||
"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):
|
||||
self.client.force_login(self.root)
|
||||
@ -311,100 +444,107 @@ class TestStudentCard(TestCase):
|
||||
reverse(
|
||||
"counter:delete_student_card",
|
||||
kwargs={
|
||||
"customer_id": self.sli.customer.pk,
|
||||
"card_id": self.sli.customer.student_cards.first().id,
|
||||
"customer_id": self.customer.customer.pk,
|
||||
"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):
|
||||
self.client.force_login(self.krophil)
|
||||
self.client.force_login(self.subscriber)
|
||||
response = self.client.post(
|
||||
reverse(
|
||||
"counter:delete_student_card",
|
||||
kwargs={
|
||||
"customer_id": self.sli.customer.pk,
|
||||
"card_id": self.sli.customer.student_cards.first().id,
|
||||
"customer_id": self.customer.customer.pk,
|
||||
"card_id": self.customer.customer.student_cards.first().id,
|
||||
},
|
||||
)
|
||||
)
|
||||
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):
|
||||
# Test with owner of the card
|
||||
self.client.force_login(self.sli)
|
||||
self.client.post(
|
||||
self.client.force_login(self.customer)
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
|
||||
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="8B90734A802A8F")
|
||||
|
||||
# Test with board member
|
||||
self.client.force_login(self.skia)
|
||||
self.client.post(
|
||||
self.client.force_login(self.board_admin)
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
|
||||
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="8B90734A802A8A")
|
||||
|
||||
# Test card with only numbers
|
||||
self.client.post(
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
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="04786547890123")
|
||||
|
||||
# Test card with only letters
|
||||
self.client.post(
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
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")
|
||||
|
||||
# Test with root
|
||||
self.client.force_login(self.root)
|
||||
self.client.post(
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
|
||||
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="8B90734A802A8B")
|
||||
|
||||
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
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
@ -414,7 +554,8 @@ class TestStudentCard(TestCase):
|
||||
# UID too long
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
@ -423,9 +564,10 @@ class TestStudentCard(TestCase):
|
||||
# Test with already existing card
|
||||
response = self.client.post(
|
||||
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(
|
||||
response, text="Un objet Student card avec ce champ Uid existe déjà."
|
||||
@ -434,7 +576,8 @@ class TestStudentCard(TestCase):
|
||||
# Test with lowercase
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
@ -443,17 +586,19 @@ class TestStudentCard(TestCase):
|
||||
# Test with white spaces
|
||||
response = self.client.post(
|
||||
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},
|
||||
)
|
||||
self.assertContains(response, text="Cet UID est invalide")
|
||||
|
||||
# Test with unauthorized user
|
||||
self.client.force_login(self.krophil)
|
||||
self.client.force_login(self.subscriber)
|
||||
response = self.client.post(
|
||||
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"},
|
||||
)
|
||||
|
@ -52,7 +52,10 @@ from counter.views.home import (
|
||||
CounterMain,
|
||||
)
|
||||
from counter.views.invoice import InvoiceCallView
|
||||
from counter.views.student_card import StudentCardDeleteView, StudentCardFormView
|
||||
from counter.views.student_card import (
|
||||
StudentCardDeleteView,
|
||||
StudentCardFormView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
||||
|
@ -22,14 +22,22 @@ def is_logged_in_counter(request: HttpRequest) -> bool:
|
||||
to the counter)
|
||||
- The current session has a counter token associated with it.
|
||||
- A counter with this token exists.
|
||||
- The counter is open
|
||||
"""
|
||||
referer_ok = (
|
||||
"HTTP_REFERER" in request.META
|
||||
and resolve(urlparse(request.META["HTTP_REFERER"]).path).app_name == "counter"
|
||||
)
|
||||
return (
|
||||
has_token = (
|
||||
(referer_ok or request.resolver_match.app_name == "counter")
|
||||
and "counter_token" in request.session
|
||||
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 core.views import CanViewMixin
|
||||
from counter.forms import NFCCardForm, RefillForm
|
||||
from counter.models import Counter, Customer, Product, Selling, StudentCard
|
||||
from counter.forms import RefillForm
|
||||
from counter.models import Counter, Customer, Product, Selling
|
||||
from counter.views.mixins import CounterTabsMixin
|
||||
from counter.views.student_card import StudentCardFormView
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.models import User
|
||||
@ -134,7 +135,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session["too_young"] = False
|
||||
request.session["not_allowed"] = False
|
||||
request.session["no_age"] = False
|
||||
request.session["not_valid_student_card_uid"] = False
|
||||
if self.object.type != "BAR":
|
||||
self.operator = request.user
|
||||
elif self.customer_is_barman():
|
||||
@ -146,8 +146,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
action = parse_qs(request.body.decode()).get("action", [""])[0]
|
||||
if action == "add_product":
|
||||
self.add_product(request)
|
||||
elif action == "add_student_card":
|
||||
self.add_student_card(request)
|
||||
elif action == "del_product":
|
||||
self.del_product(request)
|
||||
elif action == "refill":
|
||||
@ -284,23 +282,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
request.session.modified = 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):
|
||||
"""Delete a product from the basket."""
|
||||
pid = parse_qs(request.body.decode())["product_id"][0]
|
||||
@ -431,10 +412,10 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
product
|
||||
)
|
||||
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["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["student_card"] = StudentCardFormView.get_template_data(
|
||||
self.customer
|
||||
).render(self.request)
|
||||
return kwargs
|
||||
|
@ -13,14 +13,18 @@
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic.edit import DeleteView, FormView
|
||||
|
||||
from core.utils import FormFragmentTemplateData
|
||||
from core.views import CanEditMixin
|
||||
from counter.forms import StudentCardForm
|
||||
from counter.models import Customer, StudentCard
|
||||
from counter.utils import is_logged_in_counter
|
||||
|
||||
|
||||
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||
@ -41,15 +45,38 @@ class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||
|
||||
|
||||
class StudentCardFormView(FormView):
|
||||
"""Add a new student card."""
|
||||
"""Add a new student card. This is a fragment view !"""
|
||||
|
||||
form_class = StudentCardForm
|
||||
template_name = "core/create.jinja"
|
||||
template_name = "counter/fragments/create_student_card.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
|
||||
if not StudentCard.can_create(self.customer, request.user):
|
||||
@classmethod
|
||||
def get_template_data(
|
||||
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
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
@ -58,7 +85,11 @@ class StudentCardFormView(FormView):
|
||||
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
||||
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):
|
||||
return reverse_lazy(
|
||||
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
||||
)
|
||||
return self.request.path
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.db import transaction
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from model_bakery import baker
|
||||
from model_bakery.recipe import Recipe
|
||||
from pytest_django.asserts import assertNumQueries
|
||||
|
||||
from core.baker_recipes import old_subscriber_user, subscriber_user
|
||||
from core.models import RealGroup, SithFile, User
|
||||
@ -128,9 +128,11 @@ class TestPictureSearch(TestSas):
|
||||
def test_num_queries(self):
|
||||
"""Test that the number of queries is stable."""
|
||||
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
|
||||
# 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 count the total number of items in the pagination
|
||||
self.client.get(self.url)
|
||||
|
Loading…
Reference in New Issue
Block a user