1
0
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:
Ehouarn
2025-06-02 01:09:51 +02:00
parent 9eb6edb37d
commit e617048332
10 changed files with 841 additions and 453 deletions

View File

@ -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(),
}

View File

@ -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'),
),
]

View File

@ -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"),
)

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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):