1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-25 11:46:30 +00:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Emmy D'Anello
54dafe1cec
Improve payment messages
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-21 23:12:01 +01:00
Emmy D'Anello
b16b6e422f
Allow anonymous users to perform a payment using a special auth token
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
2024-02-21 22:44:56 +01:00
5 changed files with 346 additions and 77 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-02-20 22:48+0100\n" "POT-Creation-Date: 2024-02-21 23:10+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -1831,7 +1831,7 @@ msgstr ""
msgid "registration" msgid "registration"
msgstr "inscription" msgstr "inscription"
#: registration/models.py:129 registration/models.py:513 #: registration/models.py:129 registration/models.py:517
msgid "registrations" msgid "registrations"
msgstr "inscriptions" msgstr "inscriptions"
@ -1848,7 +1848,7 @@ msgid "Male"
msgstr "Homme" msgstr "Homme"
#: registration/models.py:165 #: registration/models.py:165
#: registration/templates/registration/payment_form.html:73 #: registration/templates/registration/payment_form.html:74
msgid "Other" msgid "Other"
msgstr "Autre" msgstr "Autre"
@ -2131,11 +2131,11 @@ msgstr "inscription de bénévole"
msgid "volunteer registrations" msgid "volunteer registrations"
msgstr "inscriptions de bénévoles" msgstr "inscriptions de bénévoles"
#: registration/models.py:517 #: registration/models.py:521
msgid "grouped" msgid "grouped"
msgstr "groupé" msgstr "groupé"
#: registration/models.py:519 #: registration/models.py:523
msgid "" msgid ""
"If set to true, then one payment is made for the full team, for example if " "If set to true, then one payment is made for the full team, for example if "
"the school pays for all." "the school pays for all."
@ -2143,84 +2143,92 @@ msgstr ""
"Si vrai, alors un seul paiement est fait pour toute l'équipe, par exemple si " "Si vrai, alors un seul paiement est fait pour toute l'équipe, par exemple si "
"le lycée paie pour tout le monde." "le lycée paie pour tout le monde."
#: registration/models.py:524 #: registration/models.py:528
msgid "total amount" msgid "total amount"
msgstr "montant total" msgstr "montant total"
#: registration/models.py:525 #: registration/models.py:529
msgid "Corresponds to the total required amount to pay, in euros." msgid "Corresponds to the total required amount to pay, in euros."
msgstr "Correspond au montant total à payer, en euros." msgstr "Correspond au montant total à payer, en euros."
#: registration/models.py:530 #: registration/models.py:534
msgid "token"
msgstr "jeton"
#: registration/models.py:537
msgid "A token to authorize external users to make this payment."
msgstr "Un jeton pour autoriser des utilisateurs externes à faire ce paiement."
#: registration/models.py:541
msgid "for final tournament" msgid "for final tournament"
msgstr "pour la finale" msgstr "pour la finale"
#: registration/models.py:535 #: registration/models.py:546
msgid "type" msgid "type"
msgstr "type" msgstr "type"
#: registration/models.py:538 #: registration/models.py:549
msgid "No payment" msgid "No payment"
msgstr "Pas de paiement" msgstr "Pas de paiement"
#: registration/models.py:539 #: registration/models.py:550
#: registration/templates/registration/payment_form.html:56 #: registration/templates/registration/payment_form.html:57
msgid "Credit card" msgid "Credit card"
msgstr "Carte bancaire" msgstr "Carte bancaire"
#: registration/models.py:540 #: registration/models.py:551
msgid "Scholarship" msgid "Scholarship"
msgstr "Notification de bourse" msgstr "Notification de bourse"
#: registration/models.py:541 #: registration/models.py:552
#: registration/templates/registration/payment_form.html:61 #: registration/templates/registration/payment_form.html:62
msgid "Bank transfer" msgid "Bank transfer"
msgstr "Virement bancaire" msgstr "Virement bancaire"
#: registration/models.py:542 #: registration/models.py:553
msgid "Other (please indicate)" msgid "Other (please indicate)"
msgstr "Autre (veuillez spécifier)" msgstr "Autre (veuillez spécifier)"
#: registration/models.py:543 #: registration/models.py:554
msgid "The tournament is free" msgid "The tournament is free"
msgstr "Le tournoi est gratuit" msgstr "Le tournoi est gratuit"
#: registration/models.py:550 #: registration/models.py:561
msgid "Hello Asso checkout intent ID" msgid "Hello Asso checkout intent ID"
msgstr "ID de l'intention de paiement Hello Asso" msgstr "ID de l'intention de paiement Hello Asso"
#: registration/models.py:557 #: registration/models.py:568
msgid "receipt" msgid "receipt"
msgstr "justificatif" msgstr "justificatif"
#: registration/models.py:558 #: registration/models.py:569
msgid "only if you have a scholarship or if you chose a bank transfer." msgid "only if you have a scholarship or if you chose a bank transfer."
msgstr "" msgstr ""
"Nécessaire seulement si vous déclarez être boursièr⋅e ou si vous payez par " "Nécessaire seulement si vous déclarez être boursièr⋅e ou si vous payez par "
"virement bancaire." "virement bancaire."
#: registration/models.py:565 #: registration/models.py:576
msgid "additional information" msgid "additional information"
msgstr "informations additionnelles" msgstr "informations additionnelles"
#: registration/models.py:566 #: registration/models.py:577
msgid "To help us to find your payment." msgid "To help us to find your payment."
msgstr "Pour nous aider à retrouver votre paiement, si nécessaire." msgstr "Pour nous aider à retrouver votre paiement, si nécessaire."
#: registration/models.py:572 #: registration/models.py:583
msgid "payment valid" msgid "payment valid"
msgstr "paiement valide" msgstr "paiement valide"
#: registration/models.py:630 #: registration/models.py:641
#, python-brace-format #, python-brace-format
msgid "Payment of {registrations}" msgid "Payment of {registrations}"
msgstr "Paiements de {registrations}" msgstr "Paiements de {registrations}"
#: registration/models.py:633 #: registration/models.py:644
msgid "payment" msgid "payment"
msgstr "paiement" msgstr "paiement"
#: registration/models.py:634 #: registration/models.py:645
msgid "payments" msgid "payments"
msgstr "paiements" msgstr "paiements"
@ -2387,14 +2395,18 @@ msgstr "Réinitialiser mon mot de passe"
#: registration/templates/registration/payment_form.html:9 #: registration/templates/registration/payment_form.html:9
#, python-format #, python-format
msgid "You must pay %(amount)s € for your registration." msgid ""
msgstr "Vous devez payer %(amount)s € pour votre inscription." "You must pay %(amount)s € for your registration in the team %(team)s for the "
"tournament %(tournament)s."
msgstr ""
"Vous devez payer %(amount)s € pour votre inscription dans l'équipe %(team)s "
"pour le tournoi %(tournament)s."
#: registration/templates/registration/payment_form.html:13 #: registration/templates/registration/payment_form.html:14
msgid "This price includes the registrations of all members of your team." msgid "This price includes the registrations of all members of your team."
msgstr "Ce prix inclut les inscriptions de tous les membres de votre équipe." msgstr "Ce prix inclut les inscriptions de tous les membres de votre équipe."
#: registration/templates/registration/payment_form.html:17 #: registration/templates/registration/payment_form.html:18
msgid "" msgid ""
"This price includes only your own registration. You are exempt from payment " "This price includes only your own registration. You are exempt from payment "
"if you have a scholarship, but you must then send us a proof of your " "if you have a scholarship, but you must then send us a proof of your "
@ -2404,7 +2416,7 @@ msgstr ""
"paiement si vous avez une bourse, mais vous devez alors nous envoyer un " "paiement si vous avez une bourse, mais vous devez alors nous envoyer un "
"justificatif de votre bourse." "justificatif de votre bourse."
#: registration/templates/registration/payment_form.html:27 #: registration/templates/registration/payment_form.html:28
msgid "" msgid ""
"You want finally that each member pays its own registration? Then click on " "You want finally that each member pays its own registration? Then click on "
"the button:" "the button:"
@ -2412,11 +2424,11 @@ msgstr ""
"Vous voulez finalement que chaque membre paie sa propre inscription ? Alors " "Vous voulez finalement que chaque membre paie sa propre inscription ? Alors "
"cliquez sur le bouton :" "cliquez sur le bouton :"
#: registration/templates/registration/payment_form.html:32 #: registration/templates/registration/payment_form.html:33
msgid "Back to single payments" msgid "Back to single payments"
msgstr "Retour aux paiements individuels" msgstr "Retour aux paiements individuels"
#: registration/templates/registration/payment_form.html:36 #: registration/templates/registration/payment_form.html:37
msgid "" msgid ""
"You want to pay for the registrations of all members of your team, or your " "You want to pay for the registrations of all members of your team, or your "
"school will pay for all registrations? Then click on the button:" "school will pay for all registrations? Then click on the button:"
@ -2425,14 +2437,104 @@ msgstr ""
"ou votre école paiera pour toutes les inscriptions ? Alors cliquez sur le " "ou votre école paiera pour toutes les inscriptions ? Alors cliquez sur le "
"bouton :" "bouton :"
#: registration/templates/registration/payment_form.html:42 #: registration/templates/registration/payment_form.html:43
msgid "Group the payments of my team" msgid "Group the payments of my team"
msgstr "Regrouper les paiements de mon équipe" msgstr "Regrouper les paiements de mon équipe"
#: registration/templates/registration/payment_form.html:67 #: registration/templates/registration/payment_form.html:68
msgid "I have a scholarship" msgid "I have a scholarship"
msgstr "J'ai une bourse" msgstr "J'ai une bourse"
#: registration/templates/registration/payment_form.html:84
msgid ""
"The payment by credit card is made via Hello Asso. To do this, you can click "
"on the button below, which will redirect you to the secure payment page of "
"Hello Asso. The payment validation will then be done automatically, within a "
"few minutes."
msgstr ""
"Le paiement par carte bancaire se fait via Hello Asso. Pour cela, vous "
"pouvez cliquer sur le bouton ci-dessous, qui vous redirigera vers la page de "
"paiement sécurisée de Hello Asso. La validation du paiement se fera alors "
"automatiquement, dans quelques minutes."
#: registration/templates/registration/payment_form.html:93
msgid "Go to the Hello Asso page"
msgstr "Aller à la page Hello Asso"
#: registration/templates/registration/payment_form.html:98
msgid ""
"If a third party must pay for you (parents, school,…), you can send them the "
"link to pay for you:"
msgstr ""
"Si un tiers doit payer pour vous (parents, école, …), vous pouvez leur "
"envoyer le lien pour payer pour vous :"
#: registration/templates/registration/payment_form.html:108
msgid "Copied!"
msgstr "Copié !"
#: registration/templates/registration/payment_form.html:115
msgid ""
"If this is the case and if an invoice is necessary, please contact the "
"tournament organizers by providing the name of the team, the number of "
"participants, the name of the paying establishment, the email address of the "
"establishment and/or the email address of the establishment manager."
msgstr ""
"Si c'est le cas et si une facture est nécessaire, merci de contacter les "
"organisateur⋅rices du tournoi en fournissant le nom de l'équipe, le nombre "
"de participant⋅es, le nom de l'établissement payeur, l'adresse mail de "
"l'établissement et/ou l'adresse mail du gestionnaire de l'établissement."
#: registration/templates/registration/payment_form.html:126
msgid ""
"You can also pay by bank transfer. To do this, you must put in the reference "
"of the transfer \"TFJMpu\" followed by the last name and the first name of "
"the student."
msgstr ""
"Vous pouvez également payer par virement bancaire. Pour cela, vous devez "
"mettre en référence du virement « TFJMpu » suivi du nom et du prénom de "
"l'élève."
#: registration/templates/registration/payment_form.html:132
msgid "The bank details are as follows:"
msgstr "Les coordonnées bancaires sont les suivantes :"
#: registration/templates/registration/payment_form.html:143
msgid ""
"Once your payment done, please send us a proof of your transfer using the "
"below form. The validation of your payment will then be done manually, "
"within a few days."
msgstr ""
"Une fois votre paiement effectué, merci de nous envoyer un justificatif de "
"votre virement en utilisant le formulaire ci-dessous. La validation de votre "
"paiement se fera alors manuellement, sous quelques jours."
#: registration/templates/registration/payment_form.html:152
#: registration/templates/registration/payment_form.html:167
#: registration/templates/registration/payment_form.html:182
msgid "Submit"
msgstr ""
#: registration/templates/registration/payment_form.html:158
msgid ""
"The tournament is free for you if you have a scholarship. However, you must "
"send us a proof of your scholarship. You can do this using the below form."
msgstr ""
"Le tournoi est gratuit pour vous si vous avez une bourse. Toutefois, vous "
"devez nous envoyer un justificatif de votre bourse. Vous pouvez le faire en "
"utilisant le formulaire ci-dessous."
#: registration/templates/registration/payment_form.html:173
msgid ""
"If you want to use another payment method, please contact the tournament "
"organizers first. Then, if you need to send a proof or your payment, you can "
"use the below form."
msgstr ""
"Si vous voulez utiliser un autre moyen de paiement, merci de contacter les "
"organisateur⋅rices du tournoi d'abord. Ensuite, si vous devez envoyer un "
"justificatif de votre paiement, vous pouvez utiliser le formulaire ci-"
"dessous."
#: registration/templates/registration/signup.html:5 #: registration/templates/registration/signup.html:5
#: registration/templates/registration/signup.html:12 #: registration/templates/registration/signup.html:12
#: registration/templates/registration/signup.html:19 registration/views.py:46 #: registration/templates/registration/signup.html:19 registration/views.py:46
@ -2656,30 +2758,34 @@ msgstr "Mise à jour de l'utilisateur⋅rice {user}"
msgid "This payment is already valid or pending validation." msgid "This payment is already valid or pending validation."
msgstr "Le paiement est déjà validé ou en attente de validation." msgstr "Le paiement est déjà validé ou en attente de validation."
#: registration/views.py:563 #: registration/views.py:570
msgid "The payment is already valid or pending validation."
msgstr "Le paiement est déjà validé ou en attente de validation."
#: registration/views.py:584
msgid "The payment is not found or is already validated." msgid "The payment is not found or is already validated."
msgstr "Le paiement n'est pas trouvé ou déjà validé." msgstr "Le paiement n'est pas trouvé ou déjà validé."
#: registration/views.py:582 #: registration/views.py:603
#, python-brace-format #, python-brace-format
msgid "An error occurred during the payment: {error}" msgid "An error occurred during the payment: {error}"
msgstr "Une erreur est survenue lors du paiement : {error}" msgstr "Une erreur est survenue lors du paiement : {error}"
#: registration/views.py:588 #: registration/views.py:609
msgid "The payment has been refused." msgid "The payment has been refused."
msgstr "Le paiement a été refusé." msgstr "Le paiement a été refusé."
#: registration/views.py:591 #: registration/views.py:612
#, python-brace-format #, python-brace-format
msgid "The return code is unknown: {code}" msgid "The return code is unknown: {code}"
msgstr "Le code de retour est inconnu : {code}" msgstr "Le code de retour est inconnu : {code}"
#: registration/views.py:594 #: registration/views.py:615
#, python-brace-format #, python-brace-format
msgid "The return type is unknown: {type}" msgid "The return type is unknown: {type}"
msgstr "Le type de retour est inconnu : {type}" msgstr "Le type de retour est inconnu : {type}"
#: registration/views.py:601 #: registration/views.py:622
msgid "" msgid ""
"The payment has been successfully validated! Your registration is now " "The payment has been successfully validated! Your registration is now "
"complete." "complete."
@ -2687,7 +2793,7 @@ msgstr ""
"Le paiement a été validé avec succès ! Votre inscription est désormais " "Le paiement a été validé avec succès ! Votre inscription est désormais "
"complète." "complète."
#: registration/views.py:606 #: registration/views.py:627
msgid "" msgid ""
"Your payment is done! The validation of your payment may takes a few " "Your payment is done! The validation of your payment may takes a few "
"minutes, and will be automatically done. If it is not the case, please " "minutes, and will be automatically done. If it is not the case, please "
@ -2697,27 +2803,27 @@ msgstr ""
"quelques minutes, et sera faite automatiquement. Si ce n'est pas le cas, " "quelques minutes, et sera faite automatiquement. Si ce n'est pas le cas, "
"merci de nous contacter." "merci de nous contacter."
#: registration/views.py:641 #: registration/views.py:662
#, python-brace-format #, python-brace-format
msgid "Photo authorization of {student}.{ext}" msgid "Photo authorization of {student}.{ext}"
msgstr "Autorisation de droit à l'image de {student}.{ext}" msgstr "Autorisation de droit à l'image de {student}.{ext}"
#: registration/views.py:665 #: registration/views.py:686
#, python-brace-format #, python-brace-format
msgid "Health sheet of {student}.{ext}" msgid "Health sheet of {student}.{ext}"
msgstr "Fiche sanitaire de {student}.{ext}" msgstr "Fiche sanitaire de {student}.{ext}"
#: registration/views.py:689 #: registration/views.py:710
#, python-brace-format #, python-brace-format
msgid "Vaccine sheet of {student}.{ext}" msgid "Vaccine sheet of {student}.{ext}"
msgstr "Carnet de vaccination de {student}.{ext}" msgstr "Carnet de vaccination de {student}.{ext}"
#: registration/views.py:713 #: registration/views.py:734
#, python-brace-format #, python-brace-format
msgid "Parental authorization of {student}.{ext}" msgid "Parental authorization of {student}.{ext}"
msgstr "Autorisation parentale de {student}.{ext}" msgstr "Autorisation parentale de {student}.{ext}"
#: registration/views.py:736 #: registration/views.py:757
#, python-brace-format #, python-brace-format
msgid "Payment receipt of {user}.{ext}" msgid "Payment receipt of {user}.{ext}"
msgstr "Justificatif de paiement de {user}.{ext}" msgstr "Justificatif de paiement de {user}.{ext}"

View File

@ -0,0 +1,41 @@
# Generated by Django 5.0.1 on 2024-02-20 22:48
import registration.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registration", "0011_remove_payment_registration_and_more"),
]
operations = [
migrations.AddField(
model_name="payment",
name="token",
field=models.CharField(
default=registration.models.get_random_token,
help_text="A token to authorize external users to make this payment.",
max_length=32,
verbose_name="token",
),
),
migrations.AlterField(
model_name="payment",
name="type",
field=models.CharField(
blank=True,
choices=[
("", "No payment"),
("helloasso", "Credit card"),
("scholarship", "Scholarship"),
("bank_transfer", "Bank transfer"),
("other", "Other (please indicate)"),
("free", "The tournament is free"),
],
default="",
max_length=16,
verbose_name="type",
),
),
]

View File

@ -506,6 +506,10 @@ def get_receipt_filename(instance, filename):
return f"authorization/receipt/receipt_{instance.id}" return f"authorization/receipt/receipt_{instance.id}"
def get_random_token():
return get_random_string(32)
class Payment(models.Model): class Payment(models.Model):
registrations = models.ManyToManyField( registrations = models.ManyToManyField(
ParticipantRegistration, ParticipantRegistration,
@ -526,6 +530,13 @@ class Payment(models.Model):
default=0, default=0,
) )
token = models.CharField(
verbose_name=_("token"),
max_length=32,
default=get_random_token,
help_text=_("A token to authorize external users to make this payment."),
)
final = models.BooleanField( final = models.BooleanField(
verbose_name=_("for final tournament"), verbose_name=_("for final tournament"),
default=False, default=False,
@ -585,13 +596,13 @@ class Payment(models.Model):
return Tournament.final_tournament() return Tournament.final_tournament()
return self.registrations.first().team.participation.tournament return self.registrations.first().team.participation.tournament
def get_checkout_intent(self): def get_checkout_intent(self, none_if_link_disabled=False):
if self.checkout_intent_id is None: if self.checkout_intent_id is None:
return None return None
return helloasso.get_checkout_intent(self.checkout_intent_id) return helloasso.get_checkout_intent(self.checkout_intent_id, none_if_link_disabled=none_if_link_disabled)
def create_checkout_intent(self): def create_checkout_intent(self):
checkout_intent = self.get_checkout_intent() checkout_intent = self.get_checkout_intent(none_if_link_disabled=True)
if checkout_intent is not None: if checkout_intent is not None:
return checkout_intent return checkout_intent

View File

@ -6,8 +6,9 @@
{% if payment.valid is False %} {% if payment.valid is False %}
<div class="alert alert-info"> <div class="alert alert-info">
<p> <p>
{% blocktrans trimmed with amount=payment.amount %} {% blocktrans trimmed with amount=payment.amount team=payment.team.trigram tournament=payment.tournament %}
You must pay {{ amount }} € for your registration. You must pay {{ amount }} € for your registration in the team {{ team }}
for the tournament {{ tournament }}.
{% endblocktrans %} {% endblocktrans %}
{% if payment.grouped %} {% if payment.grouped %}
{% blocktrans trimmed %} {% blocktrans trimmed %}
@ -79,40 +80,106 @@
<div class="card-body"> <div class="card-body">
<div class="tab-content" id="payment-form"> <div class="tab-content" id="payment-form">
<div class="tab-pane fade show active" id="credit-card" role="tabpanel" aria-labelledby="credit-card-tab"> <div class="tab-pane fade show active" id="credit-card" role="tabpanel" aria-labelledby="credit-card-tab">
Le paiement par carte bancaire s'effectue via Hello Asso. Pour cela, vous pouvez cliquer sur <p>
le bouton ci-dessous, qui vous redirigera vers la page de paiement sécurisée de Hello Asso. {% blocktrans trimmed %}
La validation du paiement sera ensuite faite automatiquement, sous quelques minutes. The payment by credit card is made via Hello Asso. To do this, you can click on the
Si un tiers doit payer pour vous (parents, lycée,…), vous pouvez lui transmettre le lien pour button below, which will redirect you to the secure payment page of Hello Asso. The payment
payer pour vous. validation will then be done automatically, within a few minutes.
{% endblocktrans %}
</p>
<div class="text-center"> <div class="text-center">
<a href="{% url "registration:payment_hello_asso" pk=payment.pk %}" class="btn btn-primary"> <a href="{% url "registration:payment_hello_asso" pk=payment.pk %}" class="btn btn-primary">
<i class="fas fa-credit-card"></i> Aller sur la page Hello Asso <i class="fas fa-credit-card"></i> {% trans "Go to the Hello Asso page" %}
</a> </a>
</div> </div>
<p>
{% blocktrans trimmed %}
If a third party must pay for you (parents, school,…), you can send them the link to
pay for you:
{% endblocktrans %}
</p>
<div class="text-center border border-1 my-3 p-2 border-danger bg-body-tertiary shadow-lg rounded">
{% url "registration:payment_hello_asso" pk=payment.pk as payment_url %}
{{ request.scheme }}://{{ request.site.domain }}{{ payment_url }}?token={{ payment.token }}
<a id="copyIcon" href="#"
data-bs-title="{% trans "Copied!" %}"
onclick="event.preventDefault();copyToClipboard('{{ request.scheme }}://{{ request.site.domain }}{{ payment_url }}?token={{ payment.token }}')">
<i class="fas fa-copy"></i> Copier
</a>
</div>
<p>
{% blocktrans trimmed %}
If this is the case and if an invoice is necessary, please contact the tournament
organizers by providing the name of the team, the number of participants, the name of the
paying establishment, the email address of the establishment and/or the email address of the
establishment manager.
{% endblocktrans %}
</p>
</div> </div>
<div class="tab-pane fade" id="bank-transfer" role="tabpanel" aria-labelledby="bank-transfer-tab"> <div class="tab-pane fade" id="bank-transfer" role="tabpanel" aria-labelledby="bank-transfer-tab">
<p>
{% blocktrans trimmed %}
You can also pay by bank transfer. To do this, you must put in the reference of
the transfer "TFJMpu" followed by the last name and the first name of the student.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
The bank details are as follows:
{% endblocktrans %}
</p>
<p>
IBAN : FR76 1027 8065 0000 0206 4290 127<br>
BIC : CMCIFR2A
</p>
<p>
{% blocktrans trimmed %}
Once your payment done, please send us a proof of your transfer using the below form.
The validation of your payment will then be done manually, within a few days.
{% endblocktrans %}
</p>
<form id="bank-transfer-form" method="post" enctype="multipart/form-data"> <form id="bank-transfer-form" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{ bank_transfer_form|crispy }} {{ bank_transfer_form|crispy }}
<input type="submit" class="btn btn-primary" /> <input type="submit" class="btn btn-primary" value="{% trans "Submit" %}" />
</form> </form>
</div> </div>
<div class="tab-pane fade" id="scholarship" role="tabpanel" aria-labelledby="scholarship-tab"> <div class="tab-pane fade" id="scholarship" role="tabpanel" aria-labelledby="scholarship-tab">
<p>
{% blocktrans trimmed %}
The tournament is free for you if you have a scholarship. However, you must send us a
proof of your scholarship. You can do this using the below form.
{% endblocktrans %}
</p>
<form id="scholarship-form" method="post" enctype="multipart/form-data"> <form id="scholarship-form" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{ scholarship_form|crispy }} {{ scholarship_form|crispy }}
<input type="submit" class="btn btn-primary" /> <input type="submit" class="btn btn-primary" value="{% trans "Submit" %}" />
</form> </form>
</div> </div>
<div class="tab-pane fade" id="other" role="tabpanel" aria-labelledby="other-tab"> <div class="tab-pane fade" id="other" role="tabpanel" aria-labelledby="other-tab">
<p>
{% blocktrans trimmed %}
If you want to use another payment method, please contact the tournament organizers
first. Then, if you need to send a proof or your payment, you can use the below form.
{% endblocktrans %}
</p>
<form id="other-form" method="post" enctype="multipart/form-data"> <form id="other-form" method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{{ other_form|crispy }} {{ other_form|crispy }}
<input type="submit" class="btn btn-primary" /> <input type="submit" class="btn btn-primary" value="{% trans "Submit" %}" />
</form> </form>
</div> </div>
</div> </div>
@ -138,5 +205,28 @@
elem => elem.addEventListener( elem => elem.addEventListener(
'click', () => document.location.hash = '#' + elem.getAttribute('aria-controls'))) 'click', () => document.location.hash = '#' + elem.getAttribute('aria-controls')))
}) })
function copyToClipboard(text) {
const copyIcon = document.getElementById('copyIcon')
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => {
const tooltip = bootstrap.Tooltip.getOrCreateInstance(copyIcon)
tooltip.setContent('Copied!')
tooltip.show()
}
)
} else {
const input = document.createElement('input')
input.value = text
document.body.appendChild(input)
input.select()
document.execCommand('copy')
document.body.removeChild(input)
const tooltip = bootstrap.Tooltip.getOrCreateInstance(copyIcon)
tooltip.enable()
tooltip.show()
setTimeout(() => {tooltip.disable(); tooltip.hide()}, 2000)
}
}
</script> </script>
{% endblock %} {% endblock %}

View File

@ -7,7 +7,7 @@ from tempfile import mkdtemp
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
@ -535,21 +535,41 @@ class PaymentUpdateGroupView(LoginRequiredMixin, DetailView):
return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,))) return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,)))
class PaymenRedirectHelloAssoView(LoginRequiredMixin, DetailView): class PaymenRedirectHelloAssoView(AccessMixin, DetailView):
model = Payment model = Payment
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated or \ payment = self.get_object()
not self.request.user.registration.is_admin \
and (self.request.user.registration not in self.get_object().registrations.all() # An external user has the link for the payment
or self.get_object().valid is not False): token = request.GET.get('token', "")
if token and token == payment.token:
return super().dispatch(request, *args, **kwargs)
if not request.user.is_authenticated:
return self.handle_no_permission() return self.handle_no_permission()
if not request.user.registration.is_admin:
if request.user.registration.is_volunteer \
and payment.tournament not in request.user.registration.organized_tournaments.all():
return self.handle_no_permission()
if request.user.registration.is_student \
and request.user.registration not in payment.registrations.all():
return self.handle_no_permission()
if request.user.registration.is_coach \
and request.user.registration.team != payment.team:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
payment = self.get_object() payment = self.get_object()
checkout_intent = payment.create_checkout_intent() if payment.valid is not False:
raise PermissionDenied(_("The payment is already valid or pending validation."))
checkout_intent = payment.create_checkout_intent()
return redirect(checkout_intent["redirectUrl"]) return redirect(checkout_intent["redirectUrl"])
@ -558,9 +578,10 @@ class PaymentHelloAssoReturnView(DetailView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
checkout_id = request.GET.get("checkoutIntentId") checkout_id = request.GET.get("checkoutIntentId")
payment = Payment.objects.get(checkout_intent_id=checkout_id).exclude(valid=True) payment = self.get_object()
if payment != self.get_object(): payment_qs = Payment.objects.exclude(valid=True).filter(checkout_intent_id=checkout_id).filter(pk=payment.pk)
messages.error(request, _("The payment is not found or is already validated.")) if not payment_qs.exists():
messages.error(request, _("The payment is not found or is already validated."), "danger")
return redirect("index") return redirect("index")
team = payment.team team = payment.team
@ -580,18 +601,18 @@ class PaymentHelloAssoReturnView(DetailView):
return_type = request.GET.get("type") return_type = request.GET.get("type")
if return_type == "error": if return_type == "error":
messages.error(request, format_lazy(_("An error occurred during the payment: {error}"), messages.error(request, format_lazy(_("An error occurred during the payment: {error}"),
error=request.GET.get("error"))) error=request.GET.get("error")), "danger")
return error_response return error_response
elif return_type == "return": elif return_type == "return":
code = request.GET.get("code") code = request.GET.get("code")
if code == "refused": if code == "refused":
messages.error(request, _("The payment has been refused.")) messages.error(request, _("The payment has been refused."), "danger")
return error_response return error_response
elif code != "success": elif code != "succeeded":
messages.error(request, format_lazy(_("The return code is unknown: {code}"), code=code)) messages.error(request, format_lazy(_("The return code is unknown: {code}"), code=code), "danger")
return error_response return error_response
else: else:
messages.error(request, format_lazy(_("The return type is unknown: {type}"), type=return_type)) messages.error(request, format_lazy(_("The return type is unknown: {type}"), type=return_type), "danger")
return error_response return error_response
checkout_intent = payment.get_checkout_intent() checkout_intent = payment.get_checkout_intent()
@ -608,7 +629,7 @@ class PaymentHelloAssoReturnView(DetailView):
"and will be automatically done. " "and will be automatically done. "
"If it is not the case, please contact us.")) "If it is not the case, please contact us."))
if request.user.registration in payment.registrations.all(): if not request.user.is_anonymous and request.user.registration in payment.registrations.all():
success_response = redirect("registration:user_detail", args=(request.user.pk,)) success_response = redirect("registration:user_detail", args=(request.user.pk,))
elif right_to_see: elif right_to_see:
success_response = redirect("participation:team_detail", args=(team.pk,)) success_response = redirect("participation:team_detail", args=(team.pk,))