mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-22 07:41:14 +00:00
Remove student card creation from CounterClick view and use fragment instead
Intercept htmx on submit requests, this allows auto submit from nfc fields Fix super call with parameters Add loading wheel on student card form for counter_click.jinja
This commit is contained in:
parent
1da45fdffc
commit
b81cf49d0a
@ -6,7 +6,16 @@
|
||||
**/
|
||||
export function registerComponent(name: string, options?: ElementDefinitionOptions) {
|
||||
return (component: CustomElementConstructor) => {
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
25
counter/templates/counter/add_student_card_fragment.jinja
Normal file
25
counter/templates/counter/add_student_card_fragment.jinja
Normal file
@ -0,0 +1,25 @@
|
||||
<div id="student_card_form">
|
||||
<h3>{% trans %}Add a student card{% endtrans %}</h3>
|
||||
<form
|
||||
hx-trigger="submit"
|
||||
hx-post="{{ action }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#student_card_form"
|
||||
>
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<button>{% trans %}Go{% endtrans %}</button>
|
||||
|
||||
</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 %}
|
||||
{% endif %}
|
||||
</div>
|
@ -29,26 +29,15 @@
|
||||
{{ 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' %}
|
||||
<div
|
||||
hx-get="{{ url('counter:add_student_card_fragment', counter_id=counter.id, customer_id=customer.pk) }}"
|
||||
hx-trigger="load"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<div aria-busy="true" style="min-height: 100px;"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -168,6 +168,7 @@ class TestStudentCard(TestCase):
|
||||
cls.root = User.objects.get(username="root")
|
||||
|
||||
cls.counter = Counter.objects.get(id=2)
|
||||
cls.ae_counter = Counter.objects.get(name="AE")
|
||||
|
||||
def setUp(self):
|
||||
# Auto login on counter
|
||||
@ -191,94 +192,144 @@ class TestStudentCard(TestCase):
|
||||
# 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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "8B90734A802A8F", "action": "add_student_card"},
|
||||
{"uid": "8B90734A802A8F"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "04786547890123", "action": "add_student_card"},
|
||||
{"uid": "04786547890123"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "ABCAAAFAAFAAAB", "action": "add_student_card"},
|
||||
{"uid": "ABCAAAFAAFAAAB"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "8B90734A802A8", "action": "add_student_card"},
|
||||
)
|
||||
self.assertContains(
|
||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||
{"uid": "8B90734A802A8"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "8B90734A802A8FA", "action": "add_student_card"},
|
||||
{"uid": "8B90734A802A8FA"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "9A89B82018B0A0", "action": "add_student_card"},
|
||||
{"uid": "9A89B82018B0A0"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": "8b90734a802a9f", "action": "add_student_card"},
|
||||
)
|
||||
self.assertContains(
|
||||
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||
{"uid": "8b90734a802a9f"},
|
||||
)
|
||||
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_fragment",
|
||||
kwargs={
|
||||
"counter_id": self.counter.id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"student_card_uid": " ", "action": "add_student_card"},
|
||||
{"uid": " "},
|
||||
)
|
||||
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.krophil.id},
|
||||
)
|
||||
|
||||
def send_valid_request(client, counter_id):
|
||||
return client.post(
|
||||
reverse(
|
||||
"counter:add_student_card_fragment",
|
||||
kwargs={
|
||||
"counter_id": counter_id,
|
||||
"customer_id": self.sli.customer.pk,
|
||||
},
|
||||
),
|
||||
{"uid": "8B90734A802A8F"},
|
||||
)
|
||||
|
||||
assert send_valid_request(self.client, self.counter.id).status_code == 403
|
||||
|
||||
# Send to a non bar counter
|
||||
self.client.force_login(self.skia)
|
||||
assert send_valid_request(self.client, self.ae_counter.id)
|
||||
|
||||
def test_delete_student_card_with_owner(self):
|
||||
self.client.force_login(self.sli)
|
||||
self.client.post(
|
||||
|
@ -52,7 +52,11 @@ 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,
|
||||
StudentCardFormFragmentView,
|
||||
StudentCardFormView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
||||
@ -77,6 +81,11 @@ urlpatterns = [
|
||||
StudentCardFormView.as_view(),
|
||||
name="add_student_card",
|
||||
),
|
||||
path(
|
||||
"customer/<int:customer_id>/card/add/counter/<int:counter_id>/",
|
||||
StudentCardFormFragmentView.as_view(),
|
||||
name="add_student_card_fragment",
|
||||
),
|
||||
path(
|
||||
"customer/<int:customer_id>/card/delete/<int:card_id>/",
|
||||
StudentCardDeleteView.as_view(),
|
||||
|
@ -27,8 +27,8 @@ 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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -134,7 +134,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 +145,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 +281,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 +411,7 @@ 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()
|
||||
return kwargs
|
||||
|
@ -18,9 +18,9 @@ from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic.edit import DeleteView, FormView
|
||||
|
||||
from core.views import CanEditMixin
|
||||
from core.views import AllowFragment, CanEditMixin
|
||||
from counter.forms import StudentCardForm
|
||||
from counter.models import Customer, StudentCard
|
||||
from counter.models import Counter, Customer, StudentCard
|
||||
|
||||
|
||||
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||
@ -40,7 +40,7 @@ class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||
)
|
||||
|
||||
|
||||
class StudentCardFormView(FormView):
|
||||
class StudentCardFormView(AllowFragment, FormView):
|
||||
"""Add a new student card."""
|
||||
|
||||
form_class = StudentCardForm
|
||||
@ -62,3 +62,52 @@ class StudentCardFormView(FormView):
|
||||
return reverse_lazy(
|
||||
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
||||
)
|
||||
|
||||
|
||||
class StudentCardFormFragmentView(FormView):
|
||||
"""
|
||||
Add a new student card from a counter
|
||||
This is a fragment only view which integrates with counter_click.jinja
|
||||
"""
|
||||
|
||||
form_class = StudentCardForm
|
||||
template_name = "counter/add_student_card_fragment.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.counter = get_object_or_404(
|
||||
Counter.objects.annotate_is_open(), pk=kwargs["counter_id"]
|
||||
)
|
||||
self.customer = get_object_or_404(
|
||||
Customer.objects.prefetch_related("student_cards"), pk=kwargs["customer_id"]
|
||||
)
|
||||
if not (
|
||||
self.counter.type == "BAR"
|
||||
and "counter_token" in request.session
|
||||
and request.session["counter_token"] == self.counter.token
|
||||
and self.counter.is_open
|
||||
):
|
||||
raise PermissionDenied
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
data = form.clean()
|
||||
res = super().form_valid(form)
|
||||
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
||||
return res
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["counter"] = self.counter
|
||||
context["customer"] = self.customer
|
||||
context["action"] = self.request.path
|
||||
context["student_cards"] = self.customer.student_cards.all()
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
return reverse_lazy(
|
||||
"counter:add_student_card_fragment",
|
||||
kwargs={
|
||||
"customer_id": self.customer.pk,
|
||||
"counter_id": self.counter.id,
|
||||
},
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user