1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-05-19 12:11:21 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Emmy D'Anello
01ba0a1df9
Replace assertEquals by assertEqual (deprecated and removed in Python 3.12)
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-24 23:10:06 +01:00
Emmy D'Anello
207af441a0
Add payment interface tests
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-24 23:05:21 +01:00
Emmy D'Anello
2a2786ba6d
Add payment information after payment
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-24 22:58:06 +01:00
8 changed files with 703 additions and 184 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,11 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command from django.core.management import call_command
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from registration.models import CoachRegistration, StudentRegistration from registration.models import CoachRegistration, Payment, StudentRegistration
from .models import Participation, Team, Tournament from .models import Participation, Team, Tournament
@ -515,6 +516,365 @@ class TestStudentParticipation(TestCase):
self.assertEqual(resp.status_code, 403) self.assertEqual(resp.status_code, 403)
class TestPayment(TestCase):
"""
Tests that are relative to a payment
"""
def setUp(self):
self.superuser = User.objects.create_superuser(
username="admin",
email="admin@example.com",
password="admin",
)
self.tournament = Tournament.objects.create(
name="France",
place="Here",
price=21,
)
self.team = Team.objects.create(
name="Super team",
trigram="AAA",
access_code="azerty",
)
self.user = User.objects.create(
first_name="Toto",
last_name="Toto",
email="toto@example.com",
password="toto",
)
StudentRegistration.objects.create(
user=self.user,
team=self.team,
student_class=12,
address="1 Rue de Rivoli",
zip_code=75001,
city="Paris",
school="Earth",
give_contact_to_animath=True,
email_confirmed=True,
)
self.second_user = User.objects.create(
first_name="Lalala",
last_name="Lalala",
email="lalala@example.com",
password="lalala",
)
StudentRegistration.objects.create(
user=self.second_user,
team=self.team,
student_class=11,
address="1 Rue de Rivoli",
zip_code=75001,
city="Paris",
school="Moon",
give_contact_to_animath=True,
email_confirmed=True,
)
self.coach = User.objects.create(
first_name="Coach",
last_name="Coach",
email="coach@example.com",
password="coach",
)
CoachRegistration.objects.create(
user=self.coach,
team=self.team,
address="1 Rue de Rivoli",
zip_code=75001,
city="Paris",
)
self.team.participation.tournament = self.tournament
self.team.participation.valid = True
self.team.participation.save()
self.client.force_login(self.user)
def test_check_payments_exists(self):
"""
Check that users in a validated team have an invalid payment, but not for the final,
and that coaches are not concerned.
"""
self.assertTrue(Payment.objects.filter(final=False, valid=False, type='',
registrations=self.user.registration).exists())
self.assertTrue(Payment.objects.filter(final=False, valid=False, type='',
registrations=self.second_user.registration).exists())
self.assertFalse(Payment.objects.filter(final=False, valid=False, type='',
registrations=self.coach.registration).exists())
self.assertFalse(Payment.objects.filter(final=True, valid=False, type='',
registrations=self.user.registration).exists())
self.assertFalse(Payment.objects.filter(final=True, valid=False, type='',
registrations=self.second_user.registration).exists())
self.assertFalse(Payment.objects.filter(final=True, valid=False, type='',
registrations=self.coach.registration).exists())
def test_load_payment_page(self):
"""
Ensure that the payment page loads correctly.
"""
response = self.client.get(reverse('participation:team_detail', args=(self.team.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('registration:user_detail', args=(self.user.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('participation:tournament_payments', args=(self.tournament.pk,)))
self.assertEqual(response.status_code, 403)
payment = Payment.objects.get(registrations=self.user.registration, final=False)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
def test_bank_transfer_payment(self):
"""
Try to send a bank transfer.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "bank_transfer",
'additional_information': "This is a bank transfer"})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["This field is required.", "You must upload your receipt."])
payment.refresh_from_db()
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
# README is not a valid PDF file
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "bank_transfer",
'additional_information': "This is a bank transfer",
'receipt': open("README.md", "rb")})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["The uploaded file must be a PDF, PNG of JPEG file."])
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
# Don't send too large files
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "bank_transfer",
'additional_information': "This is a bank transfer",
'receipt': SimpleUploadedFile(
"file.pdf",
content=int(0).to_bytes(2000001, "big"),
content_type="application/pdf"),
})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["The uploaded file size must be under 2 Mo."])
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "bank_transfer",
'additional_information': "This is a bank transfer",
'receipt': open("tfjm/static/Fiche_sanitaire.pdf", "rb")})
self.assertRedirects(response, reverse('participation:team_detail', args=(self.team.pk,)), 302, 200)
payment.refresh_from_db()
self.assertIsNone(payment.valid)
self.assertEqual(payment.type, "bank_transfer")
self.assertEqual(payment.additional_information, "This is a bank transfer")
self.assertIsNotNone(payment.receipt)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
def test_scholarship(self):
"""
Try to don't pay because of a scholarship.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "scholarship",
'additional_information': "I don't have to pay because I have a scholarship"})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["This field is required.", "You must upload your receipt."])
payment.refresh_from_db()
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
self.assertEqual(payment.amount, self.tournament.price)
# README is not a valid PDF file
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "scholarship",
'additional_information': "I don't have to pay because I have a scholarship",
'receipt': open("README.md", "rb")})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["The uploaded file must be a PDF, PNG of JPEG file."])
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
self.assertEqual(payment.amount, self.tournament.price)
# Don't send too large files
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "scholarship",
'additional_information': "I don't have to pay because I have a scholarship",
'receipt': SimpleUploadedFile(
"file.pdf",
content=int(0).to_bytes(2000001, "big"),
content_type="application/pdf"),
})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'receipt',
["The uploaded file size must be under 2 Mo."])
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
self.assertEqual(payment.amount, self.tournament.price)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "scholarship",
'additional_information': "I don't have to pay because I have a scholarship",
'receipt': open("tfjm/static/Fiche_sanitaire.pdf", "rb")})
self.assertRedirects(response, reverse('participation:team_detail', args=(self.team.pk,)), 302, 200)
payment.refresh_from_db()
self.assertIsNone(payment.valid)
self.assertEqual(payment.type, "scholarship")
self.assertEqual(payment.additional_information, "I don't have to pay because I have a scholarship")
self.assertIsNotNone(payment.receipt)
self.assertEqual(payment.amount, 0)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
def test_other(self):
"""
Try to send a different type of payment.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "other"})
self.assertEqual(response.status_code, 200)
self.assertFormError(response.context['form'], 'additional_information',
["This field is required."])
payment.refresh_from_db()
self.assertFalse(payment.valid)
self.assertEqual(payment.type, "")
self.assertEqual(payment.amount, self.tournament.price)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'type': "other",
'additional_information': "Why should I pay"})
self.assertRedirects(response, reverse('participation:team_detail', args=(self.team.pk,)), 302, 200)
payment.refresh_from_db()
self.assertIsNone(payment.valid)
self.assertEqual(payment.type, "other")
self.assertEqual(payment.additional_information, "Why should I pay")
self.assertIsNotNone(payment.receipt)
self.assertEqual(payment.amount, self.tournament.price)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
def test_group(self):
payment = Payment.objects.get(registrations=self.user.registration, final=False)
self.assertFalse(payment.grouped)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('registration:update_payment_group_mode', args=(payment.pk,)))
self.assertRedirects(response, reverse('registration:update_payment', args=(payment.pk,)), 302, 200)
payment.refresh_from_db()
self.assertTrue(payment.grouped)
self.assertEqual(Payment.objects.count(), 1)
self.assertIn(self.user.registration, payment.registrations.all())
self.assertIn(self.second_user.registration, payment.registrations.all())
self.assertEqual(payment.amount, 2 * self.tournament.price)
def test_ungroup(self):
"""
Test to ungroup payments
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
self.client.get(reverse('registration:update_payment_group_mode', args=(payment.pk,)))
payment.refresh_from_db()
self.assertTrue(payment.grouped)
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(reverse('registration:update_payment_group_mode', args=(payment.pk,)))
self.assertRedirects(response, reverse('registration:update_payment', args=(payment.pk,)), 302, 200)
payment.refresh_from_db()
self.assertFalse(payment.grouped)
self.assertEqual(Payment.objects.count(), 2)
self.assertIn(self.user.registration, payment.registrations.all())
self.assertNotIn(self.second_user.registration, payment.registrations.all())
self.assertEqual(payment.amount, self.tournament.price)
def test_group_forbidden(self):
"""
Payment grouping is forbidden if at least one payment is already valid.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
payment.valid = True
payment.save()
payment2 = Payment.objects.get(registrations=self.second_user.registration, final=False)
response = self.client.get(reverse('registration:update_payment_group_mode', args=(payment.pk,)))
self.assertEqual(response.status_code, 403)
response = self.client.get(reverse('registration:update_payment_group_mode', args=(payment2.pk,)))
self.assertEqual(response.status_code, 403)
def test_validate_payment(self):
"""
Try to validate a payment.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
payment.type = "other"
payment.valid = None
payment.save()
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'valid': True})
self.assertEqual(response.status_code, 403)
self.assertFalse(payment.valid)
self.client.force_login(self.superuser)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'valid': True})
self.assertRedirects(response, reverse('participation:team_detail', args=(self.team.pk,)), 302, 200)
payment.refresh_from_db()
self.assertTrue(payment.valid)
def test_invalidate_payment(self):
"""
Try to invalidate a payment.
"""
payment = Payment.objects.get(registrations=self.user.registration, final=False)
payment.type = "other"
payment.valid = None
payment.save()
response = self.client.get(reverse('registration:update_payment', args=(payment.pk,)))
self.assertEqual(response.status_code, 200)
self.client.force_login(self.superuser)
response = self.client.post(reverse('registration:update_payment', args=(payment.pk,)),
data={'valid': False})
self.assertRedirects(response, reverse('participation:team_detail', args=(self.team.pk,)), 302, 200)
payment.refresh_from_db()
self.assertFalse(payment.valid)
class TestAdmin(TestCase): class TestAdmin(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.user = User.objects.create_superuser( self.user = User.objects.create_superuser(

View File

@ -12,8 +12,8 @@ class RegistrationConfig(AppConfig):
name = 'registration' name = 'registration'
def ready(self): def ready(self):
from registration.signals import create_admin_registration, \ from registration import signals
set_username, send_email_link pre_save.connect(signals.set_username, 'auth.User')
pre_save.connect(set_username, "auth.User") pre_save.connect(signals.send_email_link, 'auth.User')
pre_save.connect(send_email_link, "auth.User") pre_save.connect(signals.update_payment_amount, 'registration.Payment')
post_save.connect(create_admin_registration, "auth.User") post_save.connect(signals.create_admin_registration, 'auth.User')

View File

@ -226,6 +226,9 @@ class PaymentAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending")) self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
payment_type = kwargs.get('data', {}).get('type', "")
self.fields['receipt'].required = payment_type in ["scholarship", "bank_transfer"]
self.fields['additional_information'].required = payment_type in ["other"]
def clean_receipt(self): def clean_receipt(self):
if "receipt" in self.files: if "receipt" in self.files:

View File

@ -387,7 +387,7 @@ class StudentRegistration(ParticipantRegistration):
'priority': 3, 'priority': 3,
'content': content, 'content': content,
}) })
elif self.payment.valid is None: elif payment.valid is None:
text = _("Your payment is under approval.") text = _("Your payment is under approval.")
content = text content = text
informations.append({ informations.append({

View File

@ -41,3 +41,13 @@ def create_admin_registration(instance, **_):
""" """
if instance.is_superuser: if instance.is_superuser:
VolunteerRegistration.objects.get_or_create(user=instance, admin=True) VolunteerRegistration.objects.get_or_create(user=instance, admin=True)
def update_payment_amount(instance, **_):
"""
When a payment got created, ensure that the amount is set.
"""
if instance.type == 'free' or instance.type == 'scholarship':
instance.amount = 0
elif instance.pk:
instance.amount = instance.registrations.count() * instance.tournament.price

View File

@ -201,13 +201,82 @@
</div> </div>
</div> </div>
{% else %} {% else %}
<form method="post" enctype="multipart/form-data"> {% if user.registration.is_volunteer %}
<div id="form-content"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} <div id="form-content">
{{ form|crispy }} {% csrf_token %}
</div> {{ form|crispy }}
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button> </div>
</form> <button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
</form>
{% else %}
{% if payment.type == 'helloasso' %}
{% if payment.valid is True %}
<div class="alert alert-success">
{% with order=payment.get_checkout_intent.order %}
{% trans "Your payment by credit card via Hello Asso is successfully validated." %}
{% trans "The paid amount is" %} {% widthratio order.amount.total 100 1 %} €.
{% if grouped %}
{% trans "It includes the registrations of all members of the team." %}
{% endif %}
{% trans "The payer was " %} {{ order.payer.firstName }} {{ order.payer.lastName }}.
{% trans "The payment was done on" %} {{ order.date }}.
{% endwith %}
</div>
{% elif payment.valid is None %}
<div class="alert alert-warning">
{% trans "The payment by credit card via Hello Asso is pending validation." %}
{% trans "It should takes only few minutes. If it takes longer, please contact us." %}
</div>
{% endif %}
{% else %}
{% if payment.valid is True %}
<div class="alert alert-success">
{% trans "Your payment is successfully validated by the organizers." %}
<ul>
<li>{% trans "Type:" %} {{ payment.get_type_display }}</li>
<li>
{% trans "Amount:" %} {{ payment.amount }} €
{% if payment.grouped %}
({% trans "It includes the registrations of all members of the team." %})
{% endif %}
</li>
{% if payment.receipt %}
<li>
{% trans "Receipt:" %}
<a href="{{ payment.receipt.url }}"><i class="fas fa-download"></i> {% trans "Download" %}</a>
</li>
{% endif %}
{% if payment.additional_information %}
<li>{% trans "Additional information:" %} {{ payment.additional_information }}</li>
{% endif %}
</ul>
</div>
{% elif payment.valid is None %}
<div class="alert alert-warning">
{% trans "Your payment is pending validation from the organizers." %}
<ul>
<li>{% trans "Type:" %} {{ payment.get_type_display }}</li>
<li>
{% trans "Amount:" %} {{ payment.amount }} €
{% if payment.grouped %}
({% trans "It includes the registrations of all members of the team." %})
{% endif %}
</li>
{% if payment.receipt %}
<li>
{% trans "Receipt:" %}
<a href="{{ payment.receipt.url }}"><i class="fas fa-download"></i> {% trans "Download" %}</a>
</li>
{% endif %}
{% if payment.additional_information %}
<li>{% trans "Additional information:" %} {{ payment.additional_information }}</li>
{% endif %}
</ul>
</div>
{% endif %}
{% endif %}
{% endif %}
{% endif %} {% endif %}
{% endblock content %} {% endblock content %}

View File

@ -451,8 +451,7 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated or \ if not self.request.user.is_authenticated or \
not self.request.user.registration.is_admin \ not self.request.user.registration.is_admin \
and (self.request.user.registration not in self.get_object().registrations.all() and self.request.user.registration not in self.get_object().registrations.all():
or self.get_object().valid is not False):
return self.handle_no_permission() return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
@ -477,8 +476,12 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
return context return context
def form_valid(self, form): def form_valid(self, form):
form.instance.valid = None
old_instance = Payment.objects.get(pk=self.object.pk) old_instance = Payment.objects.get(pk=self.object.pk)
if self.request.user.registration.participates:
if old_instance.valid is not False:
raise PermissionDenied(_("This payment is already valid or pending validation."))
else:
form.instance.valid = None
if old_instance.receipt: if old_instance.receipt:
old_instance.receipt.delete() old_instance.receipt.delete()
old_instance.save() old_instance.save()