mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-21 05:18:26 +02:00
Restructure payment model
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
@ -97,11 +97,12 @@ class VolunteerRegistrationAdmin(PolymorphicChildModelAdmin):
|
||||
|
||||
@admin.register(Payment)
|
||||
class PaymentAdmin(ModelAdmin):
|
||||
list_display = ('registration', 'registration_type', 'type', 'valid', )
|
||||
search_fields = ('registration__user__last_name', 'registration__user__first_name', 'registration__user__email',)
|
||||
list_filter = ('registration__team__participation__valid', 'type', 'type', 'valid',)
|
||||
autocomplete_fields = ('registration',)
|
||||
list_display = ('id', 'concerned_people', 'grouped', 'type', 'valid', )
|
||||
search_fields = ('registrations__user__last_name', 'registrations__user__first_name', 'registrations__user__email',)
|
||||
list_filter = ('registrations__team__participation__valid', 'type',
|
||||
'grouped', 'valid', 'registrations__polymorphic_ctype',)
|
||||
autocomplete_fields = ('registrations',)
|
||||
|
||||
@admin.display(description=_('registration type'), ordering='registration__polymorphic_ctype')
|
||||
def registration_type(self, record: Payment):
|
||||
return record.registration.get_real_instance().type
|
||||
@admin.display(description=_('concerned people'))
|
||||
def concerned_people(self, record: Payment):
|
||||
return ", ".join(f"{reg.user.first_name} {reg.user.last_name}" for reg in record.registrations.all())
|
||||
|
@ -12,11 +12,8 @@ class RegistrationConfig(AppConfig):
|
||||
name = 'registration'
|
||||
|
||||
def ready(self):
|
||||
from registration.signals import create_admin_registration, create_payment, \
|
||||
from registration.signals import create_admin_registration, \
|
||||
set_username, send_email_link
|
||||
pre_save.connect(set_username, "auth.User")
|
||||
pre_save.connect(send_email_link, "auth.User")
|
||||
post_save.connect(create_admin_registration, "auth.User")
|
||||
post_save.connect(create_payment, "registration.Registration")
|
||||
post_save.connect(create_payment, "registration.StudentRegistration")
|
||||
post_save.connect(create_payment, "registration.CoachRegistration")
|
||||
|
@ -227,25 +227,25 @@ class PaymentForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["valid"].widget.choices[0] = ('unknown', _("Pending"))
|
||||
|
||||
def clean_scholarship_file(self):
|
||||
def clean_receipt(self):
|
||||
print(self.files)
|
||||
if "scholarship_file" in self.files:
|
||||
file = self.files["scholarship_file"]
|
||||
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["scholarship_file"]
|
||||
return self.cleaned_data["receipt"]
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
if "type" in cleaned_data and cleaned_data["type"] == "scholarship" \
|
||||
and "scholarship_file" not in self.files and not self.instance.scholarship_file:
|
||||
self.add_error("scholarship_file", _("You must upload your scholarship attestation."))
|
||||
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:
|
||||
self.add_error("receipt", _("You must upload your receipt."))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = Payment
|
||||
fields = ('type', 'scholarship_file', 'additional_information', 'valid',)
|
||||
fields = ('type', 'receipt', 'additional_information', 'valid',)
|
||||
|
@ -0,0 +1,76 @@
|
||||
# Generated by Django 5.0.1 on 2024-02-12 20:40
|
||||
|
||||
import registration.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registration", "0010_coachregistration_last_degree"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="payment",
|
||||
name="registration",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="payment",
|
||||
name="scholarship_file",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="amount",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
default=0,
|
||||
help_text="Corresponds to the total required amount to pay, in euros.",
|
||||
verbose_name="total amount",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="checkout_intent_id",
|
||||
field=models.IntegerField(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
verbose_name="Hello Asso checkout intent ID",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="final",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="for final tournament"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="grouped",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="If set to true, then one payment is made for the full team, for example if the school pays for all.",
|
||||
verbose_name="grouped",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="receipt",
|
||||
field=models.FileField(
|
||||
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,
|
||||
verbose_name="receipt",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="payment",
|
||||
name="registrations",
|
||||
field=models.ManyToManyField(
|
||||
related_name="payments",
|
||||
to="registration.participantregistration",
|
||||
verbose_name="registrations",
|
||||
),
|
||||
),
|
||||
]
|
@ -364,27 +364,28 @@ class StudentRegistration(ParticipantRegistration):
|
||||
})
|
||||
|
||||
if self.team and self.team.participation.valid:
|
||||
if self.payment.valid is False:
|
||||
text = _("You have to pay {amount} € for your registration, or send a scholarship "
|
||||
"notification or a payment proof. "
|
||||
"You can do it on <a href=\"{url}\">the payment page</a>.")
|
||||
url = reverse_lazy("registration:update_payment", args=(self.payment.id,))
|
||||
content = format_lazy(text, amount=self.team.participation.tournament.price, url=url)
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "danger",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
elif self.payment.valid is None:
|
||||
text = _("Your payment is under approval.")
|
||||
content = text
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "warning",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
for payment in self.payments.all():
|
||||
if payment.valid is False:
|
||||
text = _("You have to pay {amount} € for your registration, or send a scholarship "
|
||||
"notification or a payment proof. "
|
||||
"You can do it on <a href=\"{url}\">the payment page</a>.")
|
||||
url = reverse_lazy("registration:update_payment", args=(payment.id,))
|
||||
content = format_lazy(text, amount=payment.amount, url=url)
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "danger",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
elif self.payment.valid is None:
|
||||
text = _("Your payment is under approval.")
|
||||
content = text
|
||||
informations.append({
|
||||
'title': _("Payment"),
|
||||
'type': "warning",
|
||||
'priority': 3,
|
||||
'content': content,
|
||||
})
|
||||
|
||||
return informations
|
||||
|
||||
@ -496,11 +497,28 @@ def get_scholarship_filename(instance, filename):
|
||||
|
||||
|
||||
class Payment(models.Model):
|
||||
registration = models.OneToOneField(
|
||||
registrations = models.ManyToManyField(
|
||||
ParticipantRegistration,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="payment",
|
||||
verbose_name=_("registration"),
|
||||
related_name="payments",
|
||||
verbose_name=_("registrations"),
|
||||
)
|
||||
|
||||
grouped = models.BooleanField(
|
||||
verbose_name=_("grouped"),
|
||||
default=False,
|
||||
help_text=_("If set to true, then one payment is made for the full team, "
|
||||
"for example if the school pays for all."),
|
||||
)
|
||||
|
||||
amount = models.PositiveSmallIntegerField(
|
||||
verbose_name=_("total amount"),
|
||||
help_text=_("Corresponds to the total required amount to pay, in euros."),
|
||||
default=0,
|
||||
)
|
||||
|
||||
final = models.BooleanField(
|
||||
verbose_name=_("for final tournament"),
|
||||
default=False,
|
||||
)
|
||||
|
||||
type = models.CharField(
|
||||
@ -518,9 +536,16 @@ class Payment(models.Model):
|
||||
default="",
|
||||
)
|
||||
|
||||
scholarship_file = models.FileField(
|
||||
verbose_name=_("scholarship file"),
|
||||
help_text=_("only if you have a scholarship."),
|
||||
checkout_intent_id = models.IntegerField(
|
||||
verbose_name=_("Hello Asso checkout intent ID"),
|
||||
blank=True,
|
||||
null=True,
|
||||
default=None,
|
||||
)
|
||||
|
||||
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,
|
||||
blank=True,
|
||||
default="",
|
||||
@ -540,10 +565,10 @@ class Payment(models.Model):
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy("registration:user_detail", args=(self.registration.user.id,))
|
||||
return reverse_lazy("registration:update_payment", args=(self.pk,))
|
||||
|
||||
def __str__(self):
|
||||
return _("Payment of {registration}").format(registration=self.registration)
|
||||
return _("Payment of {registrations}").format(registrations=", ".join(map(str, self.registrations.all())))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("payment")
|
||||
|
@ -4,7 +4,7 @@
|
||||
from django.contrib.auth.models import User
|
||||
from tfjm.lists import get_sympa_client
|
||||
|
||||
from .models import Payment, Registration, VolunteerRegistration
|
||||
from .models import Registration, VolunteerRegistration
|
||||
|
||||
|
||||
def set_username(instance, **_):
|
||||
@ -41,16 +41,3 @@ def create_admin_registration(instance, **_):
|
||||
"""
|
||||
if instance.is_superuser:
|
||||
VolunteerRegistration.objects.get_or_create(user=instance, admin=True)
|
||||
|
||||
|
||||
def create_payment(instance: Registration, raw, **_):
|
||||
"""
|
||||
When a user is saved, create the associated payment.
|
||||
For a free tournament, the payment is valid.
|
||||
"""
|
||||
if instance.participates and not raw:
|
||||
payment = Payment.objects.get_or_create(registration=instance)[0]
|
||||
if instance.team and instance.team.participation.valid and instance.team.participation.tournament.price == 0:
|
||||
payment.valid = True
|
||||
payment.type = "free"
|
||||
payment.save()
|
||||
|
@ -143,35 +143,42 @@
|
||||
</dl>
|
||||
|
||||
{% if user_object.registration.participates and user_object.registration.team.participation.valid %}
|
||||
<hr>
|
||||
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6 text-end">{% trans "Payment information:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% trans "yes,no,pending" as yesnodefault %}
|
||||
{% with info=user_object.registration.payment.additional_information %}
|
||||
{% if info %}
|
||||
<abbr title="{{ info }}">
|
||||
{{ user_object.registration.payment.get_type_display }}, {% trans "valid:" %} {{ user_object.registration.payment.valid|yesno:yesnodefault }}
|
||||
</abbr>
|
||||
{% 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-bs-toggle="modal" data-bs-target="#updatePaymentModal">
|
||||
<i class="fas fa-money-bill-wave"></i> {% trans "Update payment" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if user_object.registration.payment.type == "scholarship" %}
|
||||
{% if user.registration.is_admin or user == user_object %}
|
||||
<a href="{{ user_object.registration.payment.scholarship_file.url }}" class="btn btn-info">
|
||||
<i class="fas fa-file-pdf"></i> {% trans "Download scholarship attestation" %}
|
||||
{% for payment in user_object.registration.payments.all %}
|
||||
<hr>
|
||||
|
||||
<dl class="row">
|
||||
<dt class="col-sm-6 text-end">{% trans "Payment information:" %}</dt>
|
||||
<dd class="col-sm-6">
|
||||
{% trans "yes,no,pending" as yesnodefault %}
|
||||
{% with info=payment.additional_information %}
|
||||
{% if info %}
|
||||
<abbr title="{{ info }}">
|
||||
{{ payment.get_type_display }}, {% trans "valid:" %} {{ payment.valid|yesno:yesnodefault }}
|
||||
</abbr>
|
||||
{% else %}
|
||||
{{ payment.get_type_display }}, {% trans "valid:" %} {{ payment.valid|yesno:yesnodefault }}
|
||||
{% endif %}
|
||||
{% if user.registration.is_admin or payment.valid is False %}
|
||||
<a href="{% url "registration:update_payment" pk=payment.pk %}" class="btn btn-secondary">
|
||||
<i class="fas fa-money-bill-wave"></i> {% trans "Update payment" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</dd>
|
||||
</dl>
|
||||
{% if payment.type == "scholarship" or payment.type == "bank_transfer" %}
|
||||
{% if user.registration.is_admin or user == user_object %}
|
||||
<a href="{{ payment.receipt.url }}" class="btn btn-info">
|
||||
<i class="fas fa-file-pdf"></i>
|
||||
{% if payment.type == "scholarship" %}
|
||||
{% trans "Download scholarship attestation" %}
|
||||
{% elif payment.type == "bank_transfer" %}
|
||||
{% trans "Download bank transfer receipt" %}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</dd>
|
||||
</dl>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.pk == user_object.pk or user.registration.is_admin %}
|
||||
@ -210,13 +217,6 @@
|
||||
{% 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 %}
|
||||
@ -228,10 +228,6 @@
|
||||
initModal("uploadVaccineSheet", "{% url "registration:upload_user_vaccine_sheet" pk=user_object.registration.pk %}")
|
||||
initModal("uploadParentalAuthorization", "{% url "registration:upload_user_parental_authorization" pk=user_object.registration.pk %}")
|
||||
{% endif %}
|
||||
|
||||
{% if user_object.registration.team.participation.valid %}
|
||||
initModal("updatePayment", "{% url "registration:update_payment" pk=user_object.registration.payment.pk %}")
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -448,7 +448,7 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
|
||||
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
|
||||
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 super().dispatch(request, *args, **kwargs)
|
||||
@ -464,8 +464,8 @@ class PaymentUpdateView(LoginRequiredMixin, UpdateView):
|
||||
if not self.request.user.registration.is_admin:
|
||||
form.instance.valid = None
|
||||
old_instance = Payment.objects.get(pk=self.object.pk)
|
||||
if old_instance.scholarship_file:
|
||||
old_instance.scholarship_file.delete()
|
||||
if old_instance.receipt:
|
||||
old_instance.receipt.delete()
|
||||
old_instance.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
@ -568,12 +568,12 @@ class ScholarshipView(LoginRequiredMixin, View):
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
filename = kwargs["filename"]
|
||||
path = f"media/authorization/scholarship/{filename}"
|
||||
path = f"media/authorization/receipt/{filename}"
|
||||
if not os.path.exists(path):
|
||||
raise Http404
|
||||
payment = Payment.objects.get(scholarship_file__endswith=filename)
|
||||
payment = Payment.objects.get(receipt__endswith=filename)
|
||||
user = request.user
|
||||
if not (payment.registration.user == user or user.registration.is_admin):
|
||||
if not (user.registration in payment.registrations or user.registration.is_admin):
|
||||
raise PermissionDenied
|
||||
# Guess mime type of the file
|
||||
mime = Magic(mime=True)
|
||||
|
Reference in New Issue
Block a user