mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-26 11:04:20 +00:00
Merge branch 'nfc_card' into 'master'
Can identify user on counter with student card UID See merge request ae/Sith!172
This commit is contained in:
commit
0f832a2774
@ -48,7 +48,7 @@ from accounting.models import (
|
|||||||
from core.utils import resize_image
|
from core.utils import resize_image
|
||||||
from club.models import Club, Membership
|
from club.models import Club, Membership
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from counter.models import Customer, ProductType, Product, Counter, Selling
|
from counter.models import Customer, ProductType, Product, Counter, Selling, StudentCard
|
||||||
from com.models import Sith, Weekmail, News, NewsDate
|
from com.models import Sith, Weekmail, News, NewsDate
|
||||||
from election.models import Election, Role, Candidature, ElectionList
|
from election.models import Election, Role, Candidature, ElectionList
|
||||||
from forum.models import Forum, ForumTopic
|
from forum.models import Forum, ForumTopic
|
||||||
@ -870,6 +870,7 @@ Welcome to the wiki page!
|
|||||||
start=s.subscription_start,
|
start=s.subscription_start,
|
||||||
)
|
)
|
||||||
s.save()
|
s.save()
|
||||||
|
StudentCard(uid="9A89B82018B0A0", customer=sli.customer).save()
|
||||||
# Adding subscription for Krophil
|
# Adding subscription for Krophil
|
||||||
s = Subscription(
|
s = Subscription(
|
||||||
member=User.objects.filter(pk=krophil.pk).first(),
|
member=User.objects.filter(pk=krophil.pk).first(),
|
||||||
|
@ -22,6 +22,24 @@
|
|||||||
<p>{% trans trombi=user.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
|
<p>{% trans trombi=user.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
|
||||||
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a></p>
|
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if profile.customer %}
|
||||||
|
<h4>{% trans %}Student cards{% endtrans %}</h4>
|
||||||
|
<p>{% 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>
|
||||||
|
<form action="{{ url('counter:add_student_card', customer_id=profile.customer.pk) }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ student_card_form.as_p() }}
|
||||||
|
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
|
||||||
|
</form>
|
||||||
|
{% if profile.customer.student_cards.exists() %}
|
||||||
|
<ul>
|
||||||
|
{% 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 %}
|
||||||
|
<p>{% trans %}No student cards registered.{% endtrans %}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ from core.views.forms import (
|
|||||||
)
|
)
|
||||||
from core.models import User, SithFile, Preferences, Gift
|
from core.models import User, SithFile, Preferences, Gift
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
|
from counter.views import StudentCardForm
|
||||||
from trombi.views import UserTrombiForm
|
from trombi.views import UserTrombiForm
|
||||||
|
|
||||||
|
|
||||||
@ -741,6 +742,8 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
|||||||
kwargs = super(UserPreferencesView, self).get_context_data(**kwargs)
|
kwargs = super(UserPreferencesView, self).get_context_data(**kwargs)
|
||||||
if not hasattr(self.object, "trombi_user"):
|
if not hasattr(self.object, "trombi_user"):
|
||||||
kwargs["trombi_form"] = UserTrombiForm()
|
kwargs["trombi_form"] = UserTrombiForm()
|
||||||
|
if self.object.customer:
|
||||||
|
kwargs["student_card_form"] = StudentCardForm()
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
47
counter/migrations/0017_studentcard.py
Normal file
47
counter/migrations/0017_studentcard.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.13 on 2018-10-18 23:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [("counter", "0016_producttype_comment")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="StudentCard",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"uid",
|
||||||
|
models.CharField(
|
||||||
|
max_length=14,
|
||||||
|
unique=True,
|
||||||
|
validators=[django.core.validators.MinLengthValidator(4)],
|
||||||
|
verbose_name="uid",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"customer",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="student_cards",
|
||||||
|
to="counter.Customer",
|
||||||
|
verbose_name="student cards",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]
|
@ -27,8 +27,10 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.core.validators import MinLengthValidator
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
from datetime import timedelta, date
|
from datetime import timedelta, date
|
||||||
import random
|
import random
|
||||||
@ -732,3 +734,42 @@ class Eticket(models.Model):
|
|||||||
return hmac.new(
|
return hmac.new(
|
||||||
bytes(self.secret, "utf-8"), bytes(string, "utf-8"), hashlib.sha1
|
bytes(self.secret, "utf-8"), bytes(string, "utf-8"), hashlib.sha1
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCard(models.Model):
|
||||||
|
"""
|
||||||
|
Alternative way to connect a customer into a counter
|
||||||
|
We are using Mifare DESFire EV1 specs since it's used for izly cards
|
||||||
|
https://www.nxp.com/docs/en/application-note/AN10927.pdf
|
||||||
|
UID is 7 byte long that means 14 hexa characters
|
||||||
|
"""
|
||||||
|
|
||||||
|
UID_SIZE = 14
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_valid(uid):
|
||||||
|
return (
|
||||||
|
uid.isupper()
|
||||||
|
and len(uid) == StudentCard.UID_SIZE
|
||||||
|
and not StudentCard.objects.filter(uid=uid).exists()
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def can_create(customer, user):
|
||||||
|
return user.pk == customer.user.pk or user.is_board_member or user.is_root
|
||||||
|
|
||||||
|
def can_be_edited_by(self, obj):
|
||||||
|
if isinstance(obj, User):
|
||||||
|
return StudentCard.can_create(self.customer, obj)
|
||||||
|
return False
|
||||||
|
|
||||||
|
uid = models.CharField(
|
||||||
|
_("uid"), max_length=14, unique=True, validators=[MinLengthValidator(4)]
|
||||||
|
)
|
||||||
|
customer = models.ForeignKey(
|
||||||
|
Customer,
|
||||||
|
related_name="student_cards",
|
||||||
|
verbose_name=_("student cards"),
|
||||||
|
null=False,
|
||||||
|
blank=False,
|
||||||
|
)
|
||||||
|
@ -30,6 +30,26 @@
|
|||||||
{{ 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 %}
|
||||||
|
<input type="input" name="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 customer.student_cards.exists() %}
|
||||||
|
<ul>
|
||||||
|
{% for card in customer.student_cards.all() %}
|
||||||
|
<li>{{ card.uid }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
{% trans %}No card registered{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="bar_ui">
|
<div id="bar_ui">
|
||||||
<h5>{% trans %}Selling{% endtrans %}</h5>
|
<h5>{% trans %}Selling{% endtrans %}</h5>
|
||||||
|
240
counter/tests.py
240
counter/tests.py
@ -142,3 +142,243 @@ class BarmanConnectionTest(TestCase):
|
|||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
'<li><a href="/user/1/">S' Kia</a></li>' in str(response_get.content)
|
'<li><a href="/user/1/">S' Kia</a></li>' in str(response_get.content)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCardTest(TestCase):
|
||||||
|
"""
|
||||||
|
Tests for adding and deleting Stundent Cards
|
||||||
|
Test that an user can be found with it's student card
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
call_command("populate")
|
||||||
|
self.krophil = User.objects.get(username="krophil")
|
||||||
|
self.sli = User.objects.get(username="sli")
|
||||||
|
|
||||||
|
self.counter = Counter.objects.filter(id=2).first()
|
||||||
|
|
||||||
|
# Auto login on counter
|
||||||
|
self.client.post(
|
||||||
|
reverse("counter:login", args=[self.counter.id]),
|
||||||
|
{"username": "krophil", "password": "plop"},
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_search_user_with_student_card(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("counter:details", args=[self.counter.id]),
|
||||||
|
{"code": "9A89B82018B0A0"},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
response.url,
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_add_student_card_from_counter(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
{"student_card_uid": "8B90734A802A8F", "action": "add_student_card"},
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="8B90734A802A8F")
|
||||||
|
|
||||||
|
def test_add_student_card_from_counter_fail(self):
|
||||||
|
# UID too short
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
{"student_card_uid": "8B90734A802A8", "action": "add_student_card"},
|
||||||
|
)
|
||||||
|
self.assertContains(
|
||||||
|
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||||
|
)
|
||||||
|
|
||||||
|
# UID too long
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
{"student_card_uid": "8B90734A802A8FA", "action": "add_student_card"},
|
||||||
|
)
|
||||||
|
self.assertContains(
|
||||||
|
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with already existing card
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
{"student_card_uid": "9A89B82018B0A0", "action": "add_student_card"},
|
||||||
|
)
|
||||||
|
self.assertContains(
|
||||||
|
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with lowercase
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:click",
|
||||||
|
kwargs={"counter_id": self.counter.id, "user_id": self.sli.id},
|
||||||
|
),
|
||||||
|
{"student_card_uid": "8b90734a802a9f", "action": "add_student_card"},
|
||||||
|
)
|
||||||
|
self.assertContains(
|
||||||
|
response, text="Ce n'est pas un UID de carte étudiante valide"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_delete_student_card_with_owner(self):
|
||||||
|
self.client.login(username="sli", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:delete_student_card",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.sli.customer.pk,
|
||||||
|
"card_id": self.sli.customer.student_cards.first().id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertFalse(self.sli.customer.student_cards.exists())
|
||||||
|
|
||||||
|
def test_delete_student_card_with_board_member(self):
|
||||||
|
self.client.login(username="skia", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:delete_student_card",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.sli.customer.pk,
|
||||||
|
"card_id": self.sli.customer.student_cards.first().id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertFalse(self.sli.customer.student_cards.exists())
|
||||||
|
|
||||||
|
def test_delete_student_card_with_root(self):
|
||||||
|
self.client.login(username="root", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:delete_student_card",
|
||||||
|
kwargs={
|
||||||
|
"customer_id": self.sli.customer.pk,
|
||||||
|
"card_id": self.sli.customer.student_cards.first().id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertFalse(self.sli.customer.student_cards.exists())
|
||||||
|
|
||||||
|
def test_delete_student_card_fail(self):
|
||||||
|
self.client.login(username="krophil", password="plop")
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
self.assertTrue(self.sli.customer.student_cards.exists())
|
||||||
|
|
||||||
|
def test_add_student_card_from_user_preferences(self):
|
||||||
|
# Test with owner of the card
|
||||||
|
self.client.login(username="sli", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8F"},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="8B90734A802A8F")
|
||||||
|
|
||||||
|
# Test with board member
|
||||||
|
self.client.login(username="skia", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8A"},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="8B90734A802A8A")
|
||||||
|
|
||||||
|
# Test with root
|
||||||
|
self.client.login(username="root", password="plop")
|
||||||
|
self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8B"},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("core:user_prefs", kwargs={"user_id": self.sli.id})
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="8B90734A802A8B")
|
||||||
|
|
||||||
|
def test_add_student_card_from_user_preferences_fail(self):
|
||||||
|
self.client.login(username="sli", password="plop")
|
||||||
|
# UID too short
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8"},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
|
# UID too long
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8FA"},
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
|
# Test with already existing card
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "9A89B82018B0A0"},
|
||||||
|
)
|
||||||
|
self.assertContains(
|
||||||
|
response, text="Un objet Student card avec ce champ Uid existe déjà."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with lowercase
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8b90734a802a9f"},
|
||||||
|
)
|
||||||
|
self.assertContains(response, text="Cet UID est invalide")
|
||||||
|
|
||||||
|
# Test with unauthorized user
|
||||||
|
self.client.login(username="krophil", password="plop")
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"counter:add_student_card", kwargs={"customer_id": self.sli.customer.pk}
|
||||||
|
),
|
||||||
|
{"uid": "8B90734A802A8F"},
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
@ -56,6 +56,16 @@ urlpatterns = [
|
|||||||
EticketPDFView.as_view(),
|
EticketPDFView.as_view(),
|
||||||
name="eticket_pdf",
|
name="eticket_pdf",
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r"^customer/(?P<customer_id>[0-9]+)/card/add$",
|
||||||
|
StudentCardFormView.as_view(),
|
||||||
|
name="add_student_card",
|
||||||
|
),
|
||||||
|
url(
|
||||||
|
r"^customer/(?P<customer_id>[0-9]+)/card/delete/(?P<card_id>[0-9]+)/$",
|
||||||
|
StudentCardDeleteView.as_view(),
|
||||||
|
name="delete_student_card",
|
||||||
|
),
|
||||||
url(r"^admin/(?P<counter_id>[0-9]+)$", CounterEditView.as_view(), name="admin"),
|
url(r"^admin/(?P<counter_id>[0-9]+)$", CounterEditView.as_view(), name="admin"),
|
||||||
url(
|
url(
|
||||||
r"^admin/(?P<counter_id>[0-9]+)/prop$",
|
r"^admin/(?P<counter_id>[0-9]+)/prop$",
|
||||||
|
107
counter/views.py
107
counter/views.py
@ -33,6 +33,7 @@ from django.views.generic.edit import (
|
|||||||
DeleteView,
|
DeleteView,
|
||||||
ProcessFormView,
|
ProcessFormView,
|
||||||
FormMixin,
|
FormMixin,
|
||||||
|
FormView,
|
||||||
)
|
)
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.forms import CheckboxSelectMultiple
|
from django.forms import CheckboxSelectMultiple
|
||||||
@ -50,13 +51,14 @@ from datetime import date, timedelta, datetime
|
|||||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||||
from ajax_select import make_ajax_field
|
from ajax_select import make_ajax_field
|
||||||
|
|
||||||
from core.views import CanViewMixin, TabedViewMixin
|
from core.views import CanViewMixin, TabedViewMixin, CanEditMixin
|
||||||
from core.views.forms import LoginForm, SelectDate, SelectDateTime
|
from core.views.forms import LoginForm, SelectDate, SelectDateTime
|
||||||
from core.models import User
|
from core.models import User
|
||||||
from subscription.models import Subscription
|
from subscription.models import Subscription
|
||||||
from counter.models import (
|
from counter.models import (
|
||||||
Counter,
|
Counter,
|
||||||
Customer,
|
Customer,
|
||||||
|
StudentCard,
|
||||||
Product,
|
Product,
|
||||||
Selling,
|
Selling,
|
||||||
Refilling,
|
Refilling,
|
||||||
@ -99,6 +101,43 @@ class CounterAdminMixin(View):
|
|||||||
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCardForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
Form for adding student cards
|
||||||
|
Only used for user profile since CounterClick is to complicated
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = StudentCard
|
||||||
|
fields = ["uid"]
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super(StudentCardForm, self).clean()
|
||||||
|
uid = cleaned_data.get("uid", None)
|
||||||
|
if not uid or not StudentCard.is_valid(uid):
|
||||||
|
raise forms.ValidationError(_("This UID is invalid"), code="invalid")
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCardDeleteView(DeleteView, CanEditMixin):
|
||||||
|
"""
|
||||||
|
View used to delete a card from a user
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = StudentCard
|
||||||
|
template_name = "core/delete_confirm.jinja"
|
||||||
|
pk_url_kwarg = "card_id"
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
self.customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
|
||||||
|
return super(StudentCardDeleteView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
return reverse_lazy(
|
||||||
|
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GetUserForm(forms.Form):
|
class GetUserForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||||
@ -108,7 +147,9 @@ class GetUserForm(forms.Form):
|
|||||||
some nickname, first name, or last name (TODO)
|
some nickname, first name, or last name (TODO)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
code = forms.CharField(label="Code", max_length=10, required=False)
|
code = forms.CharField(
|
||||||
|
label="Code", max_length=StudentCard.UID_SIZE, required=False
|
||||||
|
)
|
||||||
id = AutoCompleteSelectField(
|
id = AutoCompleteSelectField(
|
||||||
"users", required=False, label=_("Select user"), help_text=None
|
"users", required=False, label=_("Select user"), help_text=None
|
||||||
)
|
)
|
||||||
@ -121,9 +162,14 @@ class GetUserForm(forms.Form):
|
|||||||
cleaned_data = super(GetUserForm, self).clean()
|
cleaned_data = super(GetUserForm, self).clean()
|
||||||
cus = None
|
cus = None
|
||||||
if cleaned_data["code"] != "":
|
if cleaned_data["code"] != "":
|
||||||
cus = Customer.objects.filter(
|
if len(cleaned_data["code"]) == StudentCard.UID_SIZE:
|
||||||
account_id__iexact=cleaned_data["code"]
|
card = StudentCard.objects.filter(uid=cleaned_data["code"])
|
||||||
).first()
|
if card.exists():
|
||||||
|
cus = card.first().customer
|
||||||
|
if cus is None:
|
||||||
|
cus = Customer.objects.filter(
|
||||||
|
account_id__iexact=cleaned_data["code"]
|
||||||
|
).first()
|
||||||
elif cleaned_data["id"] is not None:
|
elif cleaned_data["id"] is not None:
|
||||||
cus = Customer.objects.filter(user=cleaned_data["id"]).first()
|
cus = Customer.objects.filter(user=cleaned_data["id"]).first()
|
||||||
if cus is None or not cus.can_buy:
|
if cus is None or not cus.can_buy:
|
||||||
@ -374,6 +420,7 @@ 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.is_barman_price():
|
elif self.is_barman_price():
|
||||||
@ -383,6 +430,8 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
|
|
||||||
if "add_product" in request.POST["action"]:
|
if "add_product" in request.POST["action"]:
|
||||||
self.add_product(request)
|
self.add_product(request)
|
||||||
|
elif "add_student_card" in request.POST["action"]:
|
||||||
|
self.add_student_card(request)
|
||||||
elif "del_product" in request.POST["action"]:
|
elif "del_product" in request.POST["action"]:
|
||||||
self.del_product(request)
|
self.del_product(request)
|
||||||
elif "refill" in request.POST["action"]:
|
elif "refill" in request.POST["action"]:
|
||||||
@ -519,6 +568,27 @@ 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 = request.POST["student_card_uid"]
|
||||||
|
uid = str(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.keys()
|
||||||
|
and request.session["counter_token"] == self.object.token
|
||||||
|
and len(self.object.get_barmen_list()) > 0
|
||||||
|
):
|
||||||
|
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 = str(request.POST["product_id"])
|
pid = str(request.POST["product_id"])
|
||||||
@ -642,6 +712,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
|||||||
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["categories"] = ProductType.objects.all()
|
kwargs["categories"] = ProductType.objects.all()
|
||||||
|
kwargs["student_card_max_uid_size"] = StudentCard.UID_SIZE
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
@ -1765,3 +1836,29 @@ class CounterRefillingListView(CounterAdminTabsMixin, CounterAdminMixin, ListVie
|
|||||||
kwargs = super(CounterRefillingListView, self).get_context_data(**kwargs)
|
kwargs = super(CounterRefillingListView, self).get_context_data(**kwargs)
|
||||||
kwargs["counter"] = self.counter
|
kwargs["counter"] = self.counter
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class StudentCardFormView(FormView):
|
||||||
|
"""
|
||||||
|
Add a new student card
|
||||||
|
"""
|
||||||
|
|
||||||
|
form_class = StudentCardForm
|
||||||
|
template_name = "core/create.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):
|
||||||
|
raise PermissionDenied
|
||||||
|
return super(StudentCardFormView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
data = form.clean()
|
||||||
|
res = super(FormView, self).form_valid(form)
|
||||||
|
StudentCard(customer=self.customer, uid=data["uid"]).save()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_success_url(self, **kwargs):
|
||||||
|
return reverse_lazy(
|
||||||
|
"core:user_prefs", kwargs={"user_id": self.customer.user.pk}
|
||||||
|
)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user