1
0
mirror of https://gitlab.com/animath/si/plateforme.git synced 2025-02-13 23:41:19 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Yohann D'ANELLO
d05a8339fe
If the tournament is free, then the payment is automatically valid 2021-01-18 21:30:26 +01:00
Yohann D'ANELLO
38dc00b2c9
Scholarships are not unique 2021-01-18 21:29:42 +01:00
Yohann D'ANELLO
4cd1e43564
It is possible to validate payment status 2021-01-18 20:02:49 +01:00
12 changed files with 162 additions and 54 deletions

View File

@ -49,7 +49,7 @@ class Team(models.Model):
return self.participants.filter(studentregistration__isnull=False)
@property
def coachs(self):
def coaches(self):
return self.participants.filter(coachregistration__isnull=False)
@property

View File

@ -31,7 +31,7 @@ def update_mailing_list(instance: Team, **_):
for student in instance.students.all():
get_sympa_client().subscribe(student.user.email, f"equipe-{instance.trigram.lower()}", False,
f"{student.user.first_name} {student.user.last_name}")
for coach in instance.coachs.all():
for coach in instance.coaches.all():
get_sympa_client().subscribe(coach.user.email, f"equipe-{instance.trigram.lower()}", False,
f"{coach.user.first_name} {coach.user.last_name}")

View File

@ -22,9 +22,9 @@
<dt class="col-sm-6 text-right">{% trans "Access code:" %}</dt>
<dd class="col-sm-6">{{ team.access_code }}</dd>
<dt class="col-sm-6 text-right">{% trans "Coachs:" %}</dt>
<dt class="col-sm-6 text-right">{% trans "Coaches:" %}</dt>
<dd class="col-sm-6">
{% for coach in team.coachs.all %}
{% for coach in team.coaches.all %}
<a href="{% url "registration:user_detail" pk=coach.user.pk %}">{{ coach }}</a>{% if not forloop.last %},{% endif %}
{% empty %}
{% trans "any" %}

View File

@ -173,11 +173,11 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
context["validation_form"] = ValidateParticipationForm(self.request.POST or None)
# A team is complete when there are at least 4 members plus a coache that have sent their authorizations,
# their health sheet, they confirmed their email address and under-18 people sent their parental authorization.
context["can_validate"] = team.students.count() >= 4 and team.coachs.exists() and \
all(r.email_confirmed for r in team.students.all()) and \
all(r.photo_authorization for r in team.participants.all()) and \
all(r.health_sheet for r in team.participants.all()) and \
all(r.parental_authorization for r in team.students.all() if r.under_18)
context["can_validate"] = team.students.count() >= 4 and team.coaches.exists() and \
all(r.email_confirmed for r in team.students.all()) and \
all(r.photo_authorization for r in team.participants.all()) and \
all(r.health_sheet for r in team.participants.all()) and \
all(r.parental_authorization for r in team.students.all() if r.under_18)
return context
@ -240,8 +240,16 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
mail_html = render_to_string("participation/mails/team_validated.html", mail_context)
send_mail("[TFJM²] Équipe validée", mail_plain, None, [self.object.email], html_message=mail_html)
get_sympa_client().subscribe(self.object.email, "equipes", False, f"Equipe {self.object.name}")
get_sympa_client().unsubscribe(self.object.email, "equipes-non-valides", False)
if self.object.participation.tournament.price == 0:
for registration in self.object.participants.all():
registration.payment.type = "free"
registration.payment.valid = True
registration.payment.save()
else:
for coach in self.object.coaches.all():
coach.payment.type = "free"
coach.payment.valid = True
coach.payment.save()
elif "invalidate" in self.request.POST:
self.object.participation.valid = None
self.object.participation.save()
@ -372,7 +380,7 @@ class TeamLeaveView(LoginRequiredMixin, TemplateView):
Matrix.kick(f"#equipe-{team.trigram.lower()}:tfjm.org",
f"@{request.user.registration.matrix_username}:tfjm.org",
"Équipe quittée")
if team.students.count() + team.coachs.count() == 0:
if team.students.count() + team.coaches.count() == 0:
team.delete()
return redirect(reverse_lazy("index"))

View File

@ -8,7 +8,7 @@ from django.core.exceptions import ValidationError
from django.forms import FileInput
from django.utils.translation import gettext_lazy as _
from .models import AdminRegistration, CoachRegistration, StudentRegistration, VolunteerRegistration
from .models import AdminRegistration, CoachRegistration, Payment, StudentRegistration, VolunteerRegistration
class SignupForm(UserCreationForm):
@ -197,3 +197,34 @@ class AdminRegistrationForm(forms.ModelForm):
class Meta:
model = AdminRegistration
fields = ('role', 'give_contact_to_animath', 'email_confirmed',)
class PaymentForm(forms.ModelForm):
"""
Indicate payment information
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
def clean_scholarship_file(self):
if "scholarship_file" in self.files:
file = self.files["scholarship_file"]
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["scholarship_file"]
def clean(self):
cleaned_data = super().clean()
if "type" in cleaned_data and cleaned_data["type"] == "scholarship" \
and "scholarship" not in cleaned_data and not self.instance.scholarship_file:
self.add_error("scholarship_file", _("You must upload your scholarship attestation."))
return cleaned_data
class Meta:
model = Payment
fields = ('type', 'scholarship_file', 'additional_information', 'valid',)

View File

@ -17,7 +17,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'), ('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.', unique=True, 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_scholarship_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='registration', to='registration.ParticipantRegistration', verbose_name='registration')),

View File

@ -320,7 +320,6 @@ class Payment(models.Model):
verbose_name=_("scholarship file"),
help_text=_("only if you have a scholarship."),
upload_to=get_scholarship_filename,
unique=True,
blank=True,
default="",
)
@ -338,6 +337,9 @@ class Payment(models.Model):
default=False,
)
def get_absolute_url(self):
return reverse_lazy("registration:user_detail", args=(self.registration.user.id,))
class Meta:
verbose_name = _("payment")
verbose_name_plural = _("payments")

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load crispy_forms_filters i18n %}
{% block content %}
<form method="post" enctype="multipart/form-data">
<div id="form-content">
{% csrf_token %}
{{ form|crispy }}
</div>
<button class="btn btn-primary" type="submit">{% trans "Update" %}</button>
</form>
{% endblock content %}

View File

@ -53,7 +53,7 @@
{% if user_object.registration.photo_authorization %}
<a href="{{ user_object.registration.photo_authorization.url }}" data-turbolinks="false">{% trans "Download" %}</a>
{% endif %}
{% if user_object.pk == user.pk %}
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadPhotoAuthorizationModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
@ -63,7 +63,7 @@
{% if user_object.registration.health_sheet %}
<a href="{{ user_object.registration.health_sheet.url }}" data-turbolinks="false">{% trans "Download" %}</a>
{% endif %}
{% if user_object.pk == user.pk %}
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadHealthSheetModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
@ -76,7 +76,7 @@
{% if user_object.registration.parental_authorization %}
<a href="{{ user_object.registration.parental_authorization.url }}" data-turbolinks="false">{% trans "Download" %}</a>
{% endif %}
{% if user_object.pk == user.pk %}
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
<button class="btn btn-primary" data-toggle="modal" data-target="#uploadParentalAuthorizationModal">{% trans "Replace" %}</button>
{% endif %}
</dd>
@ -125,6 +125,11 @@
{% else %}
{{ user_object.registration.payment.get_type_display }}, {% trans "valid:" %} {{ user_object.registration.payment.valid|yesno:yesnodefault }}
{% endif %}
{% if user.registration.is_admin or user_object.registration.payment.valid is False %}
<button class="btn-sm btn-secondary" data-toggle="modal" data-target="#updatePaymentModal">
<i class="fas fa-money-bill-wave"></i> {% trans "Update payment" %}
</button>
{% endif %}
{% endwith %}
</dd>
</dl>
@ -145,20 +150,34 @@
{% url "registration:update_user" pk=user_object.pk as modal_action %}
{% include "base_modal.html" with modal_id="updateUser" %}
{% trans "Upload photo authorization" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadPhotoAuthorization" modal_enctype="multipart/form-data" %}
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
{% trans "Upload photo authorization" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadPhotoAuthorization" modal_enctype="multipart/form-data" %}
{% trans "Upload health sheet" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadHealthSheet" modal_enctype="multipart/form-data" %}
{% trans "Upload health sheet" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadHealthSheet" modal_enctype="multipart/form-data" %}
{% trans "Upload parental authorization" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
{% trans "Upload parental authorization" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
{% trans "Upload parental authorization" as modal_title %}
{% trans "Upload" as modal_button %}
{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk as modal_action %}
{% include "base_modal.html" with modal_id="uploadParentalAuthorization" modal_enctype="multipart/form-data" %}
{% endif %}
{% if user_object.registration.team.participation.valid %}
{% trans "Update payment" as modal_title %}
{% trans "Update" as modal_button %}
{% url "registration:update_payment" pk=user_object.registration.payment.pk as modal_action %}
{% include "base_modal.html" with modal_id="updatePayment" modal_additional_class="modal-xl" modal_enctype="multipart/form-data" %}
{% endif %}
{% endblock %}
{% block extrajavascript %}
@ -169,21 +188,30 @@
if (!modalBody.html().trim())
modalBody.load("{% url "registration:update_user" pk=user_object.pk %} #form-content");
});
$('button[data-target="#uploadPhotoAuthorizationModal"]').click(function() {
let modalBody = $("#uploadPhotoAuthorizationModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadHealthSheetModal"]').click(function() {
let modalBody = $("#uploadHealthSheetModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadParentalAuthorizationModal"]').click(function() {
let modalBody = $("#uploadParentalAuthorizationModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %} #form-content");
});
{% if user_object.registration.team and not user_object.registration.team.participation.valid %}
$('button[data-target="#uploadPhotoAuthorizationModal"]').click(function() {
let modalBody = $("#uploadPhotoAuthorizationModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_photo_authorization" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadHealthSheetModal"]').click(function() {
let modalBody = $("#uploadHealthSheetModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_health_sheet" pk=user_object.registration.pk %} #form-content");
});
$('button[data-target="#uploadParentalAuthorizationModal"]').click(function() {
let modalBody = $("#uploadParentalAuthorizationModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %} #form-content");
});
{% endif %}
{% if user_object.registration.team.participation.valid %}
$('button[data-target="#updatePaymentModal"]').click(function() {
let modalBody = $("#updatePaymentModal div.modal-body");
if (!modalBody.html().trim())
modalBody.load("{% url "registration:update_payment" pk=user_object.registration.payment.pk %} #form-content");
});
{% endif %}
});
</script>
{% endblock %}

View File

@ -3,10 +3,10 @@
from django.urls import path
from .views import AddOrganizerView, MyAccountDetailView, ResetAdminView, SignupView, UserDetailView,\
UserImpersonateView, UserListView, UserResendValidationEmailView, UserUpdateView, UserUploadHealthSheetView, \
UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, UserValidateView, \
UserValidationEmailSentView
from .views import AddOrganizerView, MyAccountDetailView, PaymentUpdateView, ResetAdminView, SignupView, \
UserDetailView, UserImpersonateView, UserListView, UserResendValidationEmailView, UserUpdateView, \
UserUploadHealthSheetView, UserUploadParentalAuthorizationView, UserUploadPhotoAuthorizationView, \
UserValidateView, UserValidationEmailSentView
app_name = "registration"
@ -26,6 +26,7 @@ urlpatterns = [
name="upload_user_health_sheet"),
path("user/<int:pk>/upload-parental-authorization/", UserUploadParentalAuthorizationView.as_view(),
name="upload_user_parental_authorization"),
path("update-payment/<int:pk>/", PaymentUpdateView.as_view(), name="update_payment"),
path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"),
path("user/list/", UserListView.as_view(), name="user_list"),
path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"),

View File

@ -15,8 +15,7 @@ from django.shortcuts import redirect, resolve_url
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils.crypto import get_random_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.utils.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView
@ -26,9 +25,9 @@ from tfjm.tokens import email_validation_token
from tfjm.views import AdminMixin, UserMixin, VolunteerMixin
from .forms import AddOrganizerForm, AdminRegistrationForm, CoachRegistrationForm, HealthSheetForm, \
ParentalAuthorizationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
ParentalAuthorizationForm, PaymentForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
VolunteerRegistrationForm
from .models import ParticipantRegistration, Registration, StudentRegistration
from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
from .tables import RegistrationTable
@ -381,6 +380,31 @@ class UserUploadParentalAuthorizationView(UserMixin, UpdateView):
return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))
class PaymentUpdateView(LoginRequiredMixin, UpdateView):
model = Payment
form_class = PaymentForm
def dispatch(self, request, *args, **kwargs):
if not self.request.user.is_authenticated or \
not self.request.user.registration.is_admin \
and (self.request.user != self.get_object().registration.user
or self.get_object().valid is not False):
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:
del form.fields["type"].widget.choices[-1]
del form.fields["valid"]
return form
def form_valid(self, form):
if not self.request.user.registration.is_admin:
form.instance.valid = None
return super().form_valid(form)
class PhotoAuthorizationView(LoginRequiredMixin, View):
"""
Display the sent photo authorization.

View File

@ -697,7 +697,7 @@ msgid "Access code:"
msgstr "Code d'accès :"
#: apps/participation/templates/participation/team_detail.html:25
msgid "Coachs:"
msgid "Coaches:"
msgstr "Encadrants :"
#: apps/participation/templates/participation/team_detail.html:34