From 4d157b2bd70d0a50ab8e9d9a4d5d7d12c4994bce Mon Sep 17 00:00:00 2001 From: Emmy D'Anello Date: Sun, 18 Feb 2024 22:36:01 +0100 Subject: [PATCH] Setup payment interface Signed-off-by: Emmy D'Anello --- locale/fr/LC_MESSAGES/django.po | 261 +++++++++++------- registration/forms.py | 39 ++- registration/migrations/0001_initial.py | 2 +- ...11_remove_payment_registration_and_more.py | 2 +- registration/models.py | 8 +- .../templates/registration/payment_form.html | 138 ++++++++- registration/views.py | 39 ++- tfjm/urls.py | 8 +- 8 files changed, 366 insertions(+), 131 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index ce7bd0a..df731b2 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: TFJM\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-02-12 22:29+0100\n" +"POT-Creation-Date: 2024-02-18 22:25+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Emmy D'Anello \n" "Language-Team: LANGUAGE \n" @@ -29,12 +29,12 @@ msgstr "équipes" #: draw/admin.py:40 draw/admin.py:56 draw/models.py:24 #: participation/admin.py:16 participation/admin.py:73 #: participation/admin.py:104 participation/models.py:419 -#: participation/models.py:443 participation/models.py:512 +#: participation/models.py:443 participation/models.py:513 msgid "tournament" msgstr "tournoi" #: draw/admin.py:60 draw/models.py:231 draw/models.py:426 -#: participation/models.py:516 +#: participation/models.py:517 msgid "round" msgstr "tour" @@ -168,7 +168,7 @@ msgstr "La poule en cours, où les équipes choisissent leurs problèmes" msgid "rounds" msgstr "tours" -#: draw/models.py:254 participation/models.py:530 +#: draw/models.py:254 participation/models.py:531 msgid "letter" msgstr "lettre" @@ -207,17 +207,17 @@ msgid "Pool {letter}{number}" msgstr "Poule {letter}{number}" #: draw/models.py:407 draw/models.py:434 participation/admin.py:69 -#: participation/admin.py:88 participation/models.py:582 -#: participation/models.py:591 participation/tables.py:83 +#: participation/admin.py:88 participation/models.py:583 +#: participation/models.py:592 participation/tables.py:83 msgid "pool" msgstr "poule" -#: draw/models.py:408 participation/models.py:583 +#: draw/models.py:408 participation/models.py:584 msgid "pools" msgstr "poules" -#: draw/models.py:420 participation/models.py:502 participation/models.py:752 -#: participation/models.py:782 participation/models.py:820 +#: draw/models.py:420 participation/models.py:503 participation/models.py:753 +#: participation/models.py:783 participation/models.py:821 msgid "participation" msgstr "participation" @@ -241,8 +241,8 @@ msgid "" msgstr "" "L'ordre de choix dans la poule, entre 0 et la taille de la poule moins 1." -#: draw/models.py:457 draw/models.py:480 participation/models.py:605 -#: participation/models.py:789 +#: draw/models.py:457 draw/models.py:480 participation/models.py:606 +#: participation/models.py:790 #, python-brace-format msgid "Problem #{problem}" msgstr "Problème n°{problem}" @@ -368,8 +368,8 @@ msgstr "Êtes-vous sûr·e de vouloir annuler le tirage au sort ?" msgid "Close" msgstr "Fermer" -#: draw/views.py:31 participation/views.py:151 participation/views.py:438 -#: participation/views.py:469 +#: draw/views.py:31 participation/views.py:151 participation/views.py:444 +#: participation/views.py:475 msgid "You are not in a team." msgstr "Vous n'êtes pas dans une équipe." @@ -447,21 +447,21 @@ msgid "selected for final" msgstr "sélectionnée pour la finale" #: participation/admin.py:57 participation/admin.py:116 -#: participation/models.py:612 participation/tables.py:111 +#: participation/models.py:613 participation/tables.py:111 msgid "defender" msgstr "défenseur⋅se" -#: participation/admin.py:61 participation/models.py:619 -#: participation/models.py:832 +#: participation/admin.py:61 participation/models.py:620 +#: participation/models.py:833 msgid "opponent" msgstr "opposant⋅e" -#: participation/admin.py:65 participation/models.py:626 -#: participation/models.py:833 +#: participation/admin.py:65 participation/models.py:627 +#: participation/models.py:834 msgid "reporter" msgstr "rapporteur⋅e" -#: participation/admin.py:120 participation/models.py:787 +#: participation/admin.py:120 participation/models.py:788 msgid "problem" msgstr "numéro de problème" @@ -484,13 +484,14 @@ msgstr "Aucune équipe n'a été trouvée avec ce code d'accès." #: participation/forms.py:86 participation/forms.py:351 #: registration/forms.py:122 registration/forms.py:144 #: registration/forms.py:166 registration/forms.py:188 -#: registration/forms.py:235 +#: registration/forms.py:234 registration/forms.py:267 msgid "The uploaded file size must be under 2 Mo." msgstr "Le fichier envoyé doit peser moins de 2 Mo." #: participation/forms.py:88 registration/forms.py:124 #: registration/forms.py:146 registration/forms.py:168 -#: registration/forms.py:190 registration/forms.py:237 +#: registration/forms.py:190 registration/forms.py:236 +#: registration/forms.py:269 msgid "The uploaded file must be a PDF, PNG of JPEG file." msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG." @@ -818,11 +819,11 @@ msgstr "" "notification de bourse) pour participer au tournoi.

Les participant⋅es " "qui n'ont pas encore payé sont : {participants}.

" -#: participation/models.py:477 +#: participation/models.py:478 msgid "Missing payments" msgstr "Paiements manquants" -#: participation/models.py:484 +#: participation/models.py:485 msgid "" "

The solutions for the tournament of {tournament} are due on the {date:%Y-" "%m-%d %H:%M}.

You have currently sent {nb_solutions} " @@ -837,36 +838,36 @@ msgstr "" "pouvez envoyer vos solutions sur votre page de " "participation.

" -#: participation/models.py:493 +#: participation/models.py:494 msgid "Solutions due" msgstr "Rendu des solutions" -#: participation/models.py:503 participation/models.py:536 +#: participation/models.py:504 participation/models.py:537 msgid "participations" msgstr "participations" -#: participation/models.py:518 participation/models.py:519 +#: participation/models.py:519 participation/models.py:520 #, python-brace-format msgid "Round {round}" msgstr "Tour {round}" -#: participation/models.py:542 +#: participation/models.py:543 msgid "juries" msgstr "jurys" -#: participation/models.py:549 +#: participation/models.py:550 msgid "BigBlueButton URL" msgstr "Lien BigBlueButton" -#: participation/models.py:550 +#: participation/models.py:551 msgid "The link of the BBB visio for this pool." msgstr "Le lien du salon BBB pour cette poule." -#: participation/models.py:555 +#: participation/models.py:556 msgid "results available" msgstr "résultats disponibles" -#: participation/models.py:556 +#: participation/models.py:557 msgid "" "Check this case when results become accessible to teams. They stay " "accessible to you. Only averages are given." @@ -875,28 +876,28 @@ msgstr "" "Ils restent toujours accessibles pour vous. Seules les moyennes sont " "communiquées." -#: participation/models.py:576 +#: participation/models.py:577 #, python-brace-format msgid "Pool of day {round} for tournament {tournament} with teams {teams}" msgstr "Poule du jour {round} du tournoi {tournament} avec les équipes {teams}" -#: participation/models.py:596 +#: participation/models.py:597 msgid "position" msgstr "position" -#: participation/models.py:603 +#: participation/models.py:604 msgid "defended solution" msgstr "solution défendue" -#: participation/models.py:636 +#: participation/models.py:637 msgid "observer" msgstr "observateur⋅rice" -#: participation/models.py:641 +#: participation/models.py:642 msgid "penalties" msgstr "pénalités" -#: participation/models.py:643 +#: participation/models.py:644 msgid "" "Number of penalties for the defender. The defender will loose a 0.5 " "coefficient per penalty." @@ -904,124 +905,124 @@ msgstr "" "Nombre de pénalités pour l'équipe défenseuse. Elle perd un coefficient 0.5 " "sur sa présentation orale par pénalité." -#: participation/models.py:719 participation/models.py:722 -#: participation/models.py:725 participation/models.py:728 +#: participation/models.py:720 participation/models.py:723 +#: participation/models.py:726 participation/models.py:729 #, python-brace-format msgid "Team {trigram} is not registered in the pool." msgstr "L'équipe {trigram} n'est pas inscrite dans la poule." -#: participation/models.py:733 +#: participation/models.py:734 #, python-brace-format msgid "Passage of {defender} for problem {problem}" msgstr "Passage de {defender} pour le problème {problem}" -#: participation/models.py:737 participation/models.py:746 -#: participation/models.py:827 participation/models.py:869 +#: participation/models.py:738 participation/models.py:747 +#: participation/models.py:828 participation/models.py:870 msgid "passage" msgstr "passage" -#: participation/models.py:738 +#: participation/models.py:739 msgid "passages" msgstr "passages" -#: participation/models.py:757 +#: participation/models.py:758 msgid "difference" msgstr "différence" -#: participation/models.py:758 +#: participation/models.py:759 msgid "Score to add/remove on the final score" msgstr "Score à ajouter/retrancher au score final" -#: participation/models.py:765 +#: participation/models.py:766 msgid "tweak" msgstr "harmonisation" -#: participation/models.py:766 +#: participation/models.py:767 msgid "tweaks" msgstr "harmonisations" -#: participation/models.py:794 +#: participation/models.py:795 msgid "solution for the final tournament" msgstr "solution pour la finale" -#: participation/models.py:799 participation/models.py:838 +#: participation/models.py:800 participation/models.py:839 msgid "file" msgstr "fichier" -#: participation/models.py:805 +#: participation/models.py:806 #, python-brace-format msgid "Solution of team {team} for problem {problem}" msgstr "Solution de l'équipe {team} pour le problème {problem}" -#: participation/models.py:807 +#: participation/models.py:808 msgid "for final" msgstr "pour la finale" -#: participation/models.py:810 +#: participation/models.py:811 msgid "solution" msgstr "solution" -#: participation/models.py:811 +#: participation/models.py:812 msgid "solutions" msgstr "solutions" -#: participation/models.py:844 +#: participation/models.py:845 #, python-brace-format msgid "Synthesis of {team} as {type} for problem {problem} of {defender}" msgstr "" "Note de synthèse de l'équipe {team} en tant que {type} pour le problème " "{problem} de {defender}" -#: participation/models.py:852 +#: participation/models.py:853 msgid "synthesis" msgstr "note de synthèse" -#: participation/models.py:853 +#: participation/models.py:854 msgid "syntheses" msgstr "notes de synthèse" -#: participation/models.py:862 +#: participation/models.py:863 msgid "jury" msgstr "jury" -#: participation/models.py:874 +#: participation/models.py:875 msgid "defender writing note" msgstr "note d'écrit de la défense" -#: participation/models.py:880 +#: participation/models.py:881 msgid "defender oral note" msgstr "note d'oral de la défense" -#: participation/models.py:886 +#: participation/models.py:887 msgid "opponent writing note" msgstr "note d'écrit de l'opposition" -#: participation/models.py:892 +#: participation/models.py:893 msgid "opponent oral note" msgstr "note d'oral de l'opposition" -#: participation/models.py:898 +#: participation/models.py:899 msgid "reporter writing note" msgstr "note d'écrit du rapportage" -#: participation/models.py:904 +#: participation/models.py:905 msgid "reporter oral note" msgstr "note d'oral du rapportage" -#: participation/models.py:910 +#: participation/models.py:911 msgid "observer note" msgstr "note de l'observation" -#: participation/models.py:939 +#: participation/models.py:940 #, python-brace-format msgid "Notes of {jury} for {passage}" msgstr "Notes de {jury} pour le {passage}" -#: participation/models.py:946 +#: participation/models.py:947 msgid "note" msgstr "note" -#: participation/models.py:947 +#: participation/models.py:948 msgid "notes" msgstr "notes" @@ -1090,7 +1091,6 @@ msgstr "Rejoindre" #: participation/templates/participation/update_team.html:12 #: registration/templates/registration/update_user.html:16 #: registration/templates/registration/user_detail.html:186 -#: registration/templates/registration/user_detail.html:223 msgid "Update" msgstr "Modifier" @@ -1498,7 +1498,7 @@ msgid "Invalidate" msgstr "Invalider" #: participation/templates/participation/team_detail.html:186 -#: participation/views.py:323 +#: participation/views.py:329 msgid "Upload motivation letter" msgstr "Envoyer la lettre de motivation" @@ -1507,7 +1507,7 @@ msgid "Update team" msgstr "Modifier l'équipe" #: participation/templates/participation/team_detail.html:196 -#: participation/views.py:432 +#: participation/views.py:438 msgid "Leave team" msgstr "Quitter l'équipe" @@ -1641,7 +1641,7 @@ msgstr "Vous êtes déjà dans une équipe." msgid "Join team" msgstr "Rejoindre une équipe" -#: participation/views.py:152 participation/views.py:470 +#: participation/views.py:152 participation/views.py:476 msgid "You don't participate, so you don't have any team." msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe." @@ -1677,95 +1677,95 @@ msgstr "Vous n'êtes pas un⋅e organisateur⋅rice du tournoi." msgid "This team has no pending validation." msgstr "L'équipe n'a pas de validation en attente." -#: participation/views.py:266 +#: participation/views.py:272 msgid "You must specify if you validate the registration or not." msgstr "Vous devez spécifier si vous validez l'inscription ou non." -#: participation/views.py:301 +#: participation/views.py:307 #, python-brace-format msgid "Update team {trigram}" msgstr "Mise à jour de l'équipe {trigram}" -#: participation/views.py:362 participation/views.py:418 +#: participation/views.py:368 participation/views.py:424 #, python-brace-format msgid "Motivation letter of {team}.{ext}" msgstr "Lettre de motivation de {team}.{ext}" -#: participation/views.py:393 +#: participation/views.py:399 #, python-brace-format msgid "Photo authorization of {participant}.{ext}" msgstr "Autorisation de droit à l'image de {participant}.{ext}" -#: participation/views.py:399 +#: participation/views.py:405 #, python-brace-format msgid "Parental authorization of {participant}.{ext}" msgstr "Autorisation parentale de {participant}.{ext}" -#: participation/views.py:406 +#: participation/views.py:412 #, python-brace-format msgid "Health sheet of {participant}.{ext}" msgstr "Fiche sanitaire de {participant}.{ext}" -#: participation/views.py:412 +#: participation/views.py:418 #, python-brace-format msgid "Vaccine sheet of {participant}.{ext}" msgstr "Carnet de vaccination de {participant}.{ext}" -#: participation/views.py:422 +#: participation/views.py:428 #, python-brace-format msgid "Photo authorizations of team {trigram}.zip" msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip" -#: participation/views.py:440 +#: participation/views.py:446 msgid "The team is already validated or the validation is pending." msgstr "La validation de l'équipe est déjà faite ou en cours." -#: participation/views.py:484 +#: participation/views.py:490 msgid "The team is not validated yet." msgstr "L'équipe n'est pas encore validée." -#: participation/views.py:498 +#: participation/views.py:504 #, python-brace-format msgid "Participation of team {trigram}" msgstr "Participation de l'équipe {trigram}" -#: participation/views.py:634 +#: participation/views.py:640 msgid "You can't upload a solution after the deadline." msgstr "Vous ne pouvez pas envoyer de solution après la date limite." -#: participation/views.py:742 +#: participation/views.py:748 #, python-brace-format msgid "Solutions for pool {pool} of tournament {tournament}.zip" msgstr "Solutions pour la poule {pool} du tournoi {tournament}.zip" -#: participation/views.py:743 +#: participation/views.py:749 #, python-brace-format msgid "Syntheses for pool {pool} of tournament {tournament}.zip" msgstr "Notes de synthèses pour la poule {pool} du tournoi {tournament}.zip" -#: participation/views.py:761 +#: participation/views.py:767 #, python-brace-format msgid "Jurys of {pool}" msgstr "Juré⋅es de la {pool}" -#: participation/views.py:788 +#: participation/views.py:794 msgid "New TFJM² jury account" msgstr "Nouveau compte de juré⋅e pour le TFJM²" -#: participation/views.py:801 +#: participation/views.py:807 #, python-brace-format msgid "The jury {name} has been successfully added!" msgstr "{name} a été ajouté⋅e avec succès en tant que juré⋅e !" -#: participation/views.py:838 +#: participation/views.py:844 msgid "The following user is not registered as a jury:" msgstr "L'utilisateur⋅rice suivant n'est pas inscrit⋅e en tant que juré⋅e :" -#: participation/views.py:852 +#: participation/views.py:858 msgid "Notes were successfully uploaded." msgstr "Les notes ont bien été envoyées." -#: participation/views.py:1516 +#: participation/views.py:1522 msgid "You can't upload a synthesis after the deadline." msgstr "Vous ne pouvez pas envoyer de note de synthèse après la date limite." @@ -1799,7 +1799,7 @@ msgstr "encadrant⋅e" msgid "Pending" msgstr "En attente" -#: registration/forms.py:245 +#: registration/forms.py:244 registration/forms.py:277 msgid "You must upload your receipt." msgstr "Vous devez envoyer votre justificatif." @@ -1848,6 +1848,7 @@ msgid "Male" msgstr "Homme" #: registration/models.py:155 +#: registration/templates/registration/payment_form.html:73 msgid "Other" msgstr "Autre" @@ -2162,11 +2163,17 @@ msgstr "type" msgid "No payment" msgstr "Pas de paiement" +#: registration/models.py:529 +#: registration/templates/registration/payment_form.html:56 +msgid "Credit card" +msgstr "Carte bancaire" + #: registration/models.py:530 msgid "Scholarship" msgstr "Notification de bourse" #: registration/models.py:531 +#: registration/templates/registration/payment_form.html:61 msgid "Bank transfer" msgstr "Virement bancaire" @@ -2207,7 +2214,7 @@ msgstr "paiement valide" #: registration/models.py:571 #, python-brace-format msgid "Payment of {registrations}" -msgstr "Paiements de {registration}" +msgstr "Paiements de {registrations}" #: registration/models.py:574 msgid "payment" @@ -2378,6 +2385,54 @@ msgstr "" msgid "Reset my password" msgstr "Réinitialiser mon mot de passe" +#: registration/templates/registration/payment_form.html:9 +#, python-format +msgid "You must pay %(amount)s € for your registration." +msgstr "Vous devez payez %(amount)s € pour votre inscription." + +#: registration/templates/registration/payment_form.html:13 +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." + +#: registration/templates/registration/payment_form.html:17 +msgid "" +"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 " +"scholarship." +msgstr "" +"Ce prix inclut seulement votre propre inscription. Vous êtes exempté⋅e de " +"paiement si vous avez une bourse, mais vous devez alors nous envoyer un " +"justificatif de votre bourse." + +#: registration/templates/registration/payment_form.html:27 +msgid "" +"You want finally that each member pays its own registration? Then click on " +"the button:" +msgstr "" +"Vous voulez finalement que chaque membre paie sa propre inscription ? Alors " +"cliquez sur le bouton :" + +#: registration/templates/registration/payment_form.html:32 +msgid "Back to single payments" +msgstr "Retour aux paiements individuels" + +#: registration/templates/registration/payment_form.html:36 +msgid "" +"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:" +msgstr "" +"Vous voulez payer pour les inscriptions de tous les membres de votre équipe, " +"ou votre école paiera pour toutes les inscriptions ? Alors cliquez sur le " +"bouton :" + +#: registration/templates/registration/payment_form.html:42 +msgid "Group the payments of my team" +msgstr "Regrouper les paiements de mon équipe" + +#: registration/templates/registration/payment_form.html:67 +msgid "I have a scholarship" +msgstr "J'ai une bourse" + #: registration/templates/registration/signup.html:5 #: registration/templates/registration/signup.html:12 #: registration/templates/registration/signup.html:19 registration/views.py:44 @@ -2526,7 +2581,7 @@ msgid "valid:" msgstr "valide :" #: registration/templates/registration/user_detail.html:163 -#: registration/templates/registration/user_detail.html:222 +#: registration/views.py:458 msgid "Update payment" msgstr "Modifier le paiement" @@ -2597,30 +2652,30 @@ msgstr "Détails de l'utilisateur⋅rice {user}" msgid "Update user {user}" msgstr "Mise à jour de l'utilisateur⋅rice {user}" -#: registration/views.py:492 +#: registration/views.py:503 #, python-brace-format msgid "Photo authorization of {student}.{ext}" msgstr "Autorisation de droit à l'image de {student}.{ext}" -#: registration/views.py:515 +#: registration/views.py:526 #, python-brace-format msgid "Health sheet of {student}.{ext}" msgstr "Fiche sanitaire de {student}.{ext}" -#: registration/views.py:538 +#: registration/views.py:549 #, python-brace-format msgid "Vaccine sheet of {student}.{ext}" msgstr "Carnet de vaccination de {student}.{ext}" -#: registration/views.py:561 +#: registration/views.py:572 #, python-brace-format msgid "Parental authorization of {student}.{ext}" msgstr "Autorisation parentale de {student}.{ext}" -#: registration/views.py:583 +#: registration/views.py:594 #, python-brace-format -msgid "Scholarship attestation of {user}.{ext}" -msgstr "Notification de bourse de {user}.{ext}" +msgid "Payment receipt of {user}.{ext}" +msgstr "Justificatif de paiement de {user}.{ext}" #: tfjm/settings.py:164 msgid "English" @@ -2783,3 +2838,7 @@ msgstr "Aucun résultat." #: tfjm/templates/sidebar.html:10 tfjm/templates/sidebar.html:21 msgid "Informations" msgstr "Informations" + +#, python-brace-format +#~ msgid "Scholarship attestation of {user}.{ext}" +#~ msgstr "Notification de bourse de {user}.{ext}" diff --git a/registration/forms.py b/registration/forms.py index 3d09225..a25d573 100644 --- a/registration/forms.py +++ b/registration/forms.py @@ -219,7 +219,7 @@ class VolunteerRegistrationForm(forms.ModelForm): fields = ('professional_activity', 'admin', 'give_contact_to_animath', 'email_confirmed',) -class PaymentForm(forms.ModelForm): +class PaymentAdminForm(forms.ModelForm): """ Indicate payment information """ @@ -228,7 +228,6 @@ class PaymentForm(forms.ModelForm): self.fields["valid"].widget.choices[0] = ('unknown', _("Pending")) def clean_receipt(self): - print(self.files) if "receipt" in self.files: file = self.files["receipt"] if file.size > 2e6: @@ -241,7 +240,7 @@ class PaymentForm(forms.ModelForm): cleaned_data = super().clean() if "type" in cleaned_data and cleaned_data['type'] in ["scholarship", "bank_transfer"] \ - and "receipt" not in self.files and not self.instance.scholarship_file: + and "receipt" not in self.files and not self.instance.receipt: self.add_error("receipt", _("You must upload your receipt.")) return cleaned_data @@ -249,3 +248,37 @@ class PaymentForm(forms.ModelForm): class Meta: model = Payment fields = ('type', 'receipt', 'additional_information', 'valid',) + + +class PaymentForm(forms.ModelForm): + """ + Indicate payment information + """ + def __init__(self, payment_type, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['type'].widget = forms.HiddenInput(attrs={'value': payment_type}) + self.fields['receipt'].required = payment_type in ["scholarship", "bank_transfer"] + self.fields['additional_information'].required = payment_type in ["other"] + + def clean_receipt(self): + if "receipt" in self.files: + file = self.files["receipt"] + if file.size > 2e6: + raise ValidationError(_("The uploaded file size must be under 2 Mo.")) + if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]: + raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file.")) + return self.cleaned_data["receipt"] + + def clean(self): + cleaned_data = super().clean() + + if "type" in cleaned_data and cleaned_data['type'] in ["scholarship", "bank_transfer"] \ + and "receipt" not in self.files and not self.instance.receipt: + self.add_error("receipt", _("You must upload your receipt.")) + + return cleaned_data + + class Meta: + model = Payment + fields = ('type', 'receipt', 'additional_information',) + diff --git a/registration/migrations/0001_initial.py b/registration/migrations/0001_initial.py index 001c850..a941262 100644 --- a/registration/migrations/0001_initial.py +++ b/registration/migrations/0001_initial.py @@ -113,7 +113,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('type', models.CharField(blank=True, choices=[('', 'No payment'), ('helloasso', 'Hello Asso'), ('scholarship', 'Scholarship'), ('bank_transfer', 'Bank transfer'), ('other', 'Other (please indicate)'), ('free', 'The tournament is free')], default='', max_length=16, verbose_name='type')), - ('scholarship_file', models.FileField(blank=True, default='', help_text='only if you have a scholarship.', upload_to=registration.models.get_scholarship_filename, verbose_name='scholarship file')), + ('scholarship_file', models.FileField(blank=True, default='', help_text='only if you have a scholarship.', upload_to=registration.models.get_receipt_filename, verbose_name='scholarship file')), ('additional_information', models.TextField(blank=True, default='', help_text='To help us to find your payment.', verbose_name='additional information')), ('valid', models.BooleanField(default=False, null=True, verbose_name='valid')), ('registration', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='payment', to='registration.participantregistration', verbose_name='registration')), diff --git a/registration/migrations/0011_remove_payment_registration_and_more.py b/registration/migrations/0011_remove_payment_registration_and_more.py index 6de8358..5dfd939 100644 --- a/registration/migrations/0011_remove_payment_registration_and_more.py +++ b/registration/migrations/0011_remove_payment_registration_and_more.py @@ -60,7 +60,7 @@ class Migration(migrations.Migration): blank=True, default="", help_text="only if you have a scholarship or if you chose a bank transfer.", - upload_to=registration.models.get_scholarship_filename, + upload_to=registration.models.get_receipt_filename, verbose_name="receipt", ), ), diff --git a/registration/models.py b/registration/models.py index 87f95ee..407e5aa 100644 --- a/registration/models.py +++ b/registration/models.py @@ -492,8 +492,8 @@ class VolunteerRegistration(Registration): verbose_name_plural = _("volunteer registrations") -def get_scholarship_filename(instance, filename): - return f"authorization/scholarship/scholarship_{instance.registration.pk}" +def get_receipt_filename(instance, filename): + return f"authorization/receipt/receipt_{instance.id}" class Payment(models.Model): @@ -526,7 +526,7 @@ class Payment(models.Model): max_length=16, choices=[ ('', _("No payment")), - ('helloasso', "Hello Asso"), + ('helloasso', _("Credit card")), ('scholarship', _("Scholarship")), ('bank_transfer', _("Bank transfer")), ('other', _("Other (please indicate)")), @@ -546,7 +546,7 @@ class Payment(models.Model): receipt = models.FileField( verbose_name=_("receipt"), help_text=_("only if you have a scholarship or if you chose a bank transfer."), - upload_to=get_scholarship_filename, + upload_to=get_receipt_filename, blank=True, default="", ) diff --git a/registration/templates/registration/payment_form.html b/registration/templates/registration/payment_form.html index 59366d8..0cab8f3 100644 --- a/registration/templates/registration/payment_form.html +++ b/registration/templates/registration/payment_form.html @@ -3,8 +3,140 @@ {% load crispy_forms_filters i18n %} {% block content %} -
- Le formulaire de paiement est temporairement désactivé. Il sera accessible d'ici quelques jours. -
+ {% if payment.valid is False %} +
+

+ {% blocktrans trimmed with amount=payment.amount %} + You must pay {{ amount }} € for your registration. + {% endblocktrans %} + {% if payment.grouped %} + {% blocktrans trimmed %} + This price includes the registrations of all members of your team. + {% endblocktrans %} + {% else %} + {% blocktrans trimmed %} + 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 scholarship. + {% endblocktrans %} + {% endif %} +

+ +

+ {% if payment.grouped %} + {% blocktrans trimmed %} + You want finally that each member pays its own registration? Then click on the button: + {% endblocktrans %} +

+ +
+ {% else %} + {% blocktrans trimmed %} + 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: + {% endblocktrans %} +
+ +
+ {% endif %} +

+
+ +
+
+ +
+ +
+
+
+ Le paiement par carte bancaire s'effectue 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 sera ensuite faite automatiquement, sous quelques minutes. + Si un tiers doit payer pour vous (parents, lycée,…), vous pouvez lui transmettre le lien pour + payer pour vous. + + +
+ +
+
+ {% csrf_token %} + {{ bank_transfer_form|crispy }} + +
+
+ +
+
+ {% csrf_token %} + {{ scholarship_form|crispy }} + +
+
+ +
+
+ {% csrf_token %} + {{ other_form|crispy }} + +
+
+
+
+
+ {% endif %} {% endblock content %} +{% block extrajavascript %} + +{% endblock %} diff --git a/registration/views.py b/registration/views.py index f3ea53f..a759384 100644 --- a/registration/views.py +++ b/registration/views.py @@ -29,7 +29,7 @@ from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \ ParentalAuthorizationForm, PaymentForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \ - VaccineSheetForm, VolunteerRegistrationForm + VaccineSheetForm, VolunteerRegistrationForm, PaymentAdminForm from .models import ParticipantRegistration, Payment, Registration, StudentRegistration from .tables import RegistrationTable @@ -443,7 +443,7 @@ class InstructionsTemplateView(AuthorizationTemplateView): class PaymentUpdateView(LoginRequiredMixin, UpdateView): model = Payment - form_class = PaymentForm + form_class = PaymentAdminForm def dispatch(self, request, *args, **kwargs): if not self.request.user.is_authenticated or \ @@ -453,22 +453,33 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView): return self.handle_no_permission() return super().dispatch(request, *args, **kwargs) - def get_form(self, form_class=None): - form = super().get_form(form_class) - if not self.request.user.registration.is_admin: - form.fields["type"].widget.choices = list(form.fields["type"].widget.choices)[:-1] - del form.fields["valid"] - return form + def get_context_data(self, **kwargs): + context = super().get_context_data() + context['title'] = _("Update payment") + context['bank_transfer_form'] = PaymentForm(payment_type='bank_transfer', + data=self.request.POST or None, + instance=self.object) + + context['scholarship_form'] = PaymentForm(payment_type='scholarship', + data=self.request.POST or None, + instance=self.object) + + context['other_form'] = PaymentForm(payment_type='other', + data=self.request.POST or None, + instance=self.object) + return context def form_valid(self, form): - if not self.request.user.registration.is_admin: - form.instance.valid = None + form.instance.valid = None old_instance = Payment.objects.get(pk=self.object.pk) if old_instance.receipt: old_instance.receipt.delete() old_instance.save() return super().form_valid(form) + def get_success_url(self): + return reverse_lazy("registration:user_detail", args=(self.object.registrations.first().user.pk,)) + class PhotoAuthorizationView(LoginRequiredMixin, View): """ @@ -562,9 +573,9 @@ class ParentalAuthorizationView(LoginRequiredMixin, View): return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name) -class ScholarshipView(LoginRequiredMixin, View): +class ReceiptView(LoginRequiredMixin, View): """ - Display the sent scholarship paper. + Display the sent payment receipt or scholarship notification. """ def get(self, request, *args, **kwargs): filename = kwargs["filename"] @@ -573,14 +584,14 @@ class ScholarshipView(LoginRequiredMixin, View): raise Http404 payment = Payment.objects.get(receipt__endswith=filename) user = request.user - if not (user.registration in payment.registrations or user.registration.is_admin): + if not (user.registration in payment.registrations.all() or user.registration.is_admin): raise PermissionDenied # Guess mime type of the file mime = Magic(mime=True) mime_type = mime.from_file(path) ext = mime_type.split("/")[1].replace("jpeg", "jpg") # Replace file name - true_file_name = _("Scholarship attestation of {user}.{ext}").format(user=str(user.registration), ext=ext) + true_file_name = _("Payment receipt of {user}.{ext}").format(user=str(user.registration), ext=ext) return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name) diff --git a/tfjm/urls.py b/tfjm/urls.py index 2a1af01..1185dbc 100644 --- a/tfjm/urls.py +++ b/tfjm/urls.py @@ -23,7 +23,7 @@ from django.views.defaults import bad_request, page_not_found, permission_denied from django.views.generic import TemplateView from participation.views import MotivationLetterView from registration.views import HealthSheetView, ParentalAuthorizationView, PhotoAuthorizationView, \ - ScholarshipView, SolutionView, SynthesisView, VaccineSheetView + ReceiptView, SolutionView, SynthesisView, VaccineSheetView from .views import AdminSearchView @@ -49,10 +49,10 @@ urlpatterns = [ name='vaccine_sheet'), path('media/authorization/parental//', ParentalAuthorizationView.as_view(), name='parental_authorization'), - path('media/authorization/scholarship//', ScholarshipView.as_view(), - name='scholarship'), + path('media/authorization/receipt//', ReceiptView.as_view(), + name='receipt'), path('media/authorization/motivation_letters//', MotivationLetterView.as_view(), - name='scholarship'), + name='motivation_letter'), path('media/solutions//', SolutionView.as_view(), name='solution'),