mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-06-21 18:08:21 +02:00
Meilleure gestion des cautions
This commit is contained in:
@ -24,6 +24,7 @@ class WEIForm(forms.ModelForm):
|
||||
"membership_end": DatePickerInput(),
|
||||
"date_start": DatePickerInput(),
|
||||
"date_end": DatePickerInput(),
|
||||
"caution_amount": AmountInput(),
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +43,7 @@ class WEIRegistrationForm(forms.ModelForm):
|
||||
fields = [
|
||||
'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size',
|
||||
'health_issues', 'emergency_contact_name', 'emergency_contact_phone',
|
||||
'first_year', 'information_json', 'caution_check'
|
||||
'first_year', 'information_json', 'caution_check', 'caution_type'
|
||||
]
|
||||
widgets = {
|
||||
"user": Autocomplete(
|
||||
@ -58,9 +59,9 @@ class WEIRegistrationForm(forms.ModelForm):
|
||||
'maxDate': '2100-01-01'
|
||||
}),
|
||||
"caution_check": forms.BooleanField(
|
||||
label=_("I confirm that I have read the caution and that I am aware of the risks involved."),
|
||||
required=False,
|
||||
),
|
||||
"caution_type": forms.RadioSelect(),
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.21 on 2025-06-01 21:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0012_bus_club'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='weiclub',
|
||||
name='caution_amount',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='caution amount'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='weiregistration',
|
||||
name='caution_type',
|
||||
field=models.CharField(choices=[('check', 'Check'), ('note', 'Note transaction')], default='check', max_length=16, verbose_name='caution type'),
|
||||
),
|
||||
]
|
@ -33,6 +33,11 @@ class WEIClub(Club):
|
||||
verbose_name=_("date end"),
|
||||
)
|
||||
|
||||
caution_amount = models.PositiveIntegerField(
|
||||
verbose_name=_("caution amount"),
|
||||
default=0,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("WEI")
|
||||
verbose_name_plural = _("WEI")
|
||||
@ -197,6 +202,16 @@ class WEIRegistration(models.Model):
|
||||
verbose_name=_("Caution check given")
|
||||
)
|
||||
|
||||
caution_type = models.CharField(
|
||||
max_length=16,
|
||||
choices=(
|
||||
('check', _("Check")),
|
||||
('note', _("Note transaction")),
|
||||
),
|
||||
default='check',
|
||||
verbose_name=_("caution type"),
|
||||
)
|
||||
|
||||
birth_date = models.DateField(
|
||||
verbose_name=_("birth date"),
|
||||
)
|
||||
|
@ -49,6 +49,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if club.caution_amount > 0 %}
|
||||
<dt class="col-xl-6">{% trans 'Caution amount'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ club.caution_amount|pretty_money }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if "note.view_note"|has_perm:club.note %}
|
||||
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ club.note.balance | pretty_money }}</dd>
|
||||
|
@ -95,9 +95,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_validate_1a %}
|
||||
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
|
||||
{% endif %}
|
||||
{% if can_validate_1a %}
|
||||
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
|
@ -143,25 +143,35 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% if registration.user.note.balance < fee %}
|
||||
<div class="alert alert-danger">
|
||||
{% with pretty_fee=fee|pretty_money %}
|
||||
{% blocktrans trimmed with balance=registration.user.note.balance|pretty_money %}
|
||||
The note don't have enough money ({{ balance }}, {{ pretty_fee }} required).
|
||||
The registration may fail if you don't credit the note now.
|
||||
{% endblocktrans %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-success">
|
||||
{% blocktrans trimmed with pretty_fee=fee|pretty_money %}
|
||||
The note has enough money ({{ pretty_fee }} required), the registration is possible.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="alert {% if registration.user.note.balance < fee %}alert-danger{% else %}alert-success{% endif %}">
|
||||
<h5>{% trans "Required payments:" %}</h5>
|
||||
<ul>
|
||||
<li>{% blocktrans trimmed with amount=fee|pretty_money %}
|
||||
Membership fees: {{ amount }}
|
||||
{% endblocktrans %}</li>
|
||||
{% if registration.caution_type == 'note' %}
|
||||
<li>{% blocktrans trimmed with amount=club.caution_amount|pretty_money %}
|
||||
Deposit (by Note transaction): {{ amount }}
|
||||
{% endblocktrans %}</li>
|
||||
<li><strong>{% blocktrans trimmed with total=total_needed|pretty_money %}
|
||||
Total needed: {{ total }}
|
||||
{% endblocktrans %}</strong></li>
|
||||
{% else %}
|
||||
<li>{% blocktrans trimmed with amount=club.caution_amount|pretty_money %}
|
||||
Deposit (by check): {{ amount }}
|
||||
{% endblocktrans %}</li>
|
||||
<li><strong>{% blocktrans trimmed with total=fee|pretty_money %}
|
||||
Total needed: {{ total }}
|
||||
{% endblocktrans %}</strong></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<p>{% blocktrans trimmed with balance=registration.user.note.balance|pretty_money %}
|
||||
Current balance: {{ balance }}
|
||||
{% endblocktrans %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not registration.caution_check and not registration.first_year %}
|
||||
{% if not registration.caution_check and not registration.first_year and registration.caution_type == 'check' %}
|
||||
<div class="alert alert-danger">
|
||||
{% trans "The user didn't give her/his caution check." %}
|
||||
</div>
|
||||
|
@ -564,6 +564,8 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
del form.fields["caution_check"]
|
||||
if "information_json" in form.fields:
|
||||
del form.fields["information_json"]
|
||||
if "caution_type" in form.fields:
|
||||
del form.fields["caution_type"]
|
||||
|
||||
return form
|
||||
|
||||
@ -668,6 +670,12 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
if "information_json" in form.fields:
|
||||
del form.fields["information_json"]
|
||||
|
||||
# S'assurer que le champ caution_type est obligatoire
|
||||
if "caution_type" in form.fields:
|
||||
form.fields["caution_type"].required = True
|
||||
form.fields["caution_type"].help_text = _("Choose how you want to pay the deposit")
|
||||
form.fields["caution_type"].widget = forms.RadioSelect(choices=form.fields["caution_type"].choices)
|
||||
|
||||
return form
|
||||
|
||||
@transaction.atomic
|
||||
@ -693,6 +701,9 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]]
|
||||
information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]]
|
||||
form.instance.information = information
|
||||
|
||||
# Sauvegarder le type de caution
|
||||
form.instance.caution_type = form.cleaned_data["caution_type"]
|
||||
form.instance.save()
|
||||
|
||||
if 'treasury' in settings.INSTALLED_APPS:
|
||||
@ -767,6 +778,13 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
# Masquer le champ caution_check pour tout le monde dans le formulaire de modification
|
||||
if "caution_check" in form.fields:
|
||||
del form.fields["caution_check"]
|
||||
|
||||
# S'assurer que le champ caution_type est obligatoire pour les 2A+
|
||||
if not self.object.first_year and "caution_type" in form.fields:
|
||||
form.fields["caution_type"].required = True
|
||||
form.fields["caution_type"].help_text = _("Choose how you want to pay the deposit")
|
||||
form.fields["caution_type"].widget = forms.RadioSelect(choices=form.fields["caution_type"].choices)
|
||||
|
||||
return form
|
||||
|
||||
def get_membership_form(self, data=None, instance=None):
|
||||
@ -824,6 +842,10 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]]
|
||||
information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]]
|
||||
form.instance.information = information
|
||||
|
||||
# Sauvegarder le type de caution pour les 2A+
|
||||
if "caution_type" in form.cleaned_data:
|
||||
form.instance.caution_type = form.cleaned_data["caution_type"]
|
||||
form.instance.save()
|
||||
|
||||
return super().form_valid(form)
|
||||
@ -924,7 +946,14 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
date_start__gte=bde.membership_start,
|
||||
).exists()
|
||||
|
||||
context["fee"] = registration.fee
|
||||
fee = registration.fee
|
||||
context["fee"] = fee
|
||||
|
||||
# Calculer le montant total nécessaire (frais + caution si transaction)
|
||||
total_needed = fee
|
||||
if registration.caution_type == 'note':
|
||||
total_needed += registration.wei.caution_amount
|
||||
context["total_needed"] = total_needed
|
||||
|
||||
form = context["form"]
|
||||
if registration.soge_credit:
|
||||
@ -948,12 +977,22 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
|
||||
# Ajouter le champ caution_check uniquement pour les non-première année et le rendre obligatoire
|
||||
if not registration.first_year:
|
||||
form.fields["caution_check"] = forms.BooleanField(
|
||||
required=True,
|
||||
initial=registration.caution_check,
|
||||
label=_("Caution check given"),
|
||||
help_text=_("Please make sure the check is given before validating the registration")
|
||||
)
|
||||
if registration.caution_type == 'check':
|
||||
form.fields["caution_check"] = forms.BooleanField(
|
||||
required=True,
|
||||
initial=registration.caution_check,
|
||||
label=_("Caution check given"),
|
||||
help_text=_("Please make sure the check is given before validating the registration")
|
||||
)
|
||||
else:
|
||||
form.fields["caution_check"] = forms.BooleanField(
|
||||
required=True,
|
||||
initial=False,
|
||||
label=_("Create deposit transaction"),
|
||||
help_text=_("A transaction of %(amount).2f€ will be created from the user's Note account") % {
|
||||
'amount': registration.wei.caution_amount / 100
|
||||
}
|
||||
)
|
||||
|
||||
if registration.soge_credit:
|
||||
form.fields["credit_type"].disabled = True
|
||||
@ -1037,10 +1076,20 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
if credit_type is None or registration.soge_credit:
|
||||
credit_amount = 0
|
||||
|
||||
if not registration.soge_credit and user.note.balance + credit_amount < fee:
|
||||
# Users must have money before registering to the WEI.
|
||||
# Calculer le montant total nécessaire (frais + caution si transaction)
|
||||
total_needed = fee
|
||||
if registration.caution_type == 'note':
|
||||
total_needed += club.caution_amount
|
||||
|
||||
# Vérifier que l'utilisateur a assez d'argent pour tout payer
|
||||
if not registration.soge_credit and user.note.balance + credit_amount < total_needed:
|
||||
form.add_error('credit_type',
|
||||
_("This user don't have enough money to join this club, and can't have a negative balance."))
|
||||
_("This user doesn't have enough money to join this club and pay the deposit. "
|
||||
"Current balance: %(balance)d€, credit: %(credit)d€, needed: %(needed)d€") % {
|
||||
'balance': user.note.balance,
|
||||
'credit': credit_amount,
|
||||
'needed': total_needed,
|
||||
})
|
||||
return super().form_invalid(form)
|
||||
|
||||
if credit_amount:
|
||||
@ -1080,6 +1129,18 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
membership.refresh_from_db()
|
||||
membership.roles.add(WEIRole.objects.get(name="Adhérent⋅e WEI"))
|
||||
|
||||
# Créer la transaction de caution si nécessaire
|
||||
if registration.caution_type == 'note':
|
||||
from note.models import Transaction
|
||||
Transaction.objects.create(
|
||||
source=user.note,
|
||||
destination=club.note,
|
||||
quantity=1,
|
||||
amount=club.caution_amount,
|
||||
reason=_("Caution %(name)s") % {'name': club.name},
|
||||
valid=True,
|
||||
)
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
|
Reference in New Issue
Block a user