mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-07-20 08:01:26 +02:00
@ -6,7 +6,7 @@ from django.conf import settings
|
||||
from django.db.models.signals import post_save
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .signals import save_user_profile
|
||||
from .signals import save_user_profile, update_wei_registration_fee_on_membership_creation, update_wei_registration_fee_on_club_change
|
||||
|
||||
|
||||
class MemberConfig(AppConfig):
|
||||
@ -17,7 +17,16 @@ class MemberConfig(AppConfig):
|
||||
"""
|
||||
Define app internal signals to interact with other apps
|
||||
"""
|
||||
from .models import Membership, Club
|
||||
post_save.connect(
|
||||
save_user_profile,
|
||||
sender=settings.AUTH_USER_MODEL,
|
||||
)
|
||||
post_save.connect(
|
||||
update_wei_registration_fee_on_membership_creation,
|
||||
sender=Membership
|
||||
)
|
||||
post_save.connect(
|
||||
update_wei_registration_fee_on_club_change,
|
||||
sender=Club
|
||||
)
|
||||
|
@ -13,3 +13,25 @@ def save_user_profile(instance, created, raw, **_kwargs):
|
||||
instance.profile.email_confirmed = True
|
||||
instance.profile.registration_valid = True
|
||||
instance.profile.save()
|
||||
|
||||
|
||||
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
from wei.models import WEIRegistration
|
||||
if instance.club.id == 1 or instance.club.id == 2:
|
||||
registrations = WEIRegistration.objects.filter(
|
||||
user=instance.user,
|
||||
wei__year=instance.date_start.year,
|
||||
)
|
||||
for r in registrations:
|
||||
r.save()
|
||||
|
||||
|
||||
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
|
||||
from wei.models import WEIRegistration
|
||||
if instance.id == 1 or instance.id == 2:
|
||||
registrations = WEIRegistration.objects.filter(
|
||||
wei__year=instance.membership_start.year,
|
||||
)
|
||||
for r in registrations:
|
||||
r.save()
|
||||
|
@ -1,11 +1,11 @@
|
||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .registration import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, WEIMembership1AForm, \
|
||||
from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, \
|
||||
WEIMembershipForm, BusForm, BusTeamForm
|
||||
from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey
|
||||
|
||||
__all__ = [
|
||||
'WEIForm', 'WEIRegistrationForm', 'WEIRegistration1AForm', 'WEIRegistration2AForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm',
|
||||
'WEIForm', 'WEIRegistrationForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm',
|
||||
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
|
||||
]
|
||||
|
@ -44,7 +44,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', 'deposit_check'
|
||||
'first_year', 'information_json', 'deposit_check', 'deposit_type'
|
||||
]
|
||||
widgets = {
|
||||
"user": Autocomplete(
|
||||
@ -62,21 +62,8 @@ class WEIRegistrationForm(forms.ModelForm):
|
||||
"deposit_check": forms.BooleanField(
|
||||
required=False,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class WEIRegistration2AForm(WEIRegistrationForm):
|
||||
class Meta(WEIRegistrationForm.Meta):
|
||||
fields = WEIRegistrationForm.Meta.fields + ['deposit_type']
|
||||
widgets = WEIRegistrationForm.Meta.widgets.copy()
|
||||
widgets.update({
|
||||
"deposit_type": forms.RadioSelect(),
|
||||
})
|
||||
|
||||
|
||||
class WEIRegistration1AForm(WEIRegistrationForm):
|
||||
class Meta(WEIRegistrationForm.Meta):
|
||||
fields = WEIRegistrationForm.Meta.fields
|
||||
}
|
||||
|
||||
|
||||
class WEIChooseBusForm(forms.Form):
|
||||
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.2.4 on 2025-07-19 12:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0015_remove_weiclub_caution_amount_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='weiregistration',
|
||||
name='fee',
|
||||
field=models.PositiveIntegerField(blank=True, default=0, verbose_name='fee'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='weiclub',
|
||||
name='fee_soge_credit',
|
||||
field=models.PositiveIntegerField(default=2000, verbose_name='membership fee (soge credit)'),
|
||||
),
|
||||
]
|
@ -285,6 +285,12 @@ class WEIRegistration(models.Model):
|
||||
"encoded in JSON"),
|
||||
)
|
||||
|
||||
fee = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name=_('fee'),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('user', 'wei',)
|
||||
verbose_name = _("WEI User")
|
||||
@ -309,7 +315,25 @@ class WEIRegistration(models.Model):
|
||||
self.information_json = json.dumps(information, indent=2)
|
||||
|
||||
@property
|
||||
def fee(self):
|
||||
def is_validated(self):
|
||||
try:
|
||||
return self.membership is not None
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@property
|
||||
def validation_status(self):
|
||||
"""
|
||||
Define an order to have easier access to validatable registrations
|
||||
"""
|
||||
if self.fee + (self.wei.deposit_amount if self.deposit_type == 'note' else 0) > self.user.note.balance:
|
||||
return 2
|
||||
elif self.first_year:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def calculate_fee(self):
|
||||
bde = Club.objects.get(pk=1)
|
||||
kfet = Club.objects.get(pk=2)
|
||||
|
||||
@ -336,12 +360,9 @@ class WEIRegistration(models.Model):
|
||||
|
||||
return fee
|
||||
|
||||
@property
|
||||
def is_validated(self):
|
||||
try:
|
||||
return self.membership is not None
|
||||
except AttributeError:
|
||||
return False
|
||||
def save(self, *args, **kwargs):
|
||||
self.fee = self.calculate_fee()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class WEIMembership(Membership):
|
||||
|
@ -58,8 +58,8 @@ class WEIRegistrationTable(tables.Table):
|
||||
|
||||
validate = tables.Column(
|
||||
verbose_name=_("Validate"),
|
||||
orderable=False,
|
||||
accessor=A('pk'),
|
||||
orderable=True,
|
||||
accessor='validate_status',
|
||||
attrs={
|
||||
'th': {
|
||||
'id': 'validate-membership-header'
|
||||
@ -98,12 +98,13 @@ class WEIRegistrationTable(tables.Table):
|
||||
if not hasperm:
|
||||
return format_html("<span class='no-perm'></span>")
|
||||
|
||||
url = reverse_lazy('wei:wei_update_registration', args=(record.pk,)) + '?validate=true'
|
||||
url = reverse_lazy('wei:validate_registration', args=(record.pk,))
|
||||
text = _('Validate')
|
||||
if record.fee > record.user.note.balance and not record.soge_credit:
|
||||
status = record.validation_status
|
||||
if status == 2:
|
||||
btn_class = 'btn-secondary'
|
||||
tooltip = _("The user does not have enough money.")
|
||||
elif record.first_year:
|
||||
elif status == 1:
|
||||
btn_class = 'btn-info'
|
||||
tooltip = _("The user is in first year. You may validate the credit, the algorithm will run later.")
|
||||
else:
|
||||
@ -121,6 +122,7 @@ class WEIRegistrationTable(tables.Table):
|
||||
attrs = {
|
||||
'class': 'table table-condensed table-striped table-hover'
|
||||
}
|
||||
order_by = ('validate', 'user',)
|
||||
model = WEIRegistration
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_check',
|
||||
|
@ -67,20 +67,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if history_list.data %}
|
||||
<div class="card bg-white mb-3">
|
||||
<div class="card-header position-relative" id="historyListHeading">
|
||||
<a class="stretched-link font-weight-bold text-decoration-none" {% if "note.view_note"|has_perm:club.note %}
|
||||
href="{% url 'note:transactions' pk=club.note.pk %}" {% endif %}>
|
||||
<i class="fa fa-euro"></i> {% trans "Transaction history" %}
|
||||
</a>
|
||||
</div>
|
||||
<div id="history_list">
|
||||
{% render_table history_list %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if pre_registrations.data %}
|
||||
<div class="card bg-white mb-3">
|
||||
<div class="card-header position-relative" id="historyListHeading">
|
||||
@ -99,6 +85,19 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if history_list.data %}
|
||||
<div class="card bg-white mt-3">
|
||||
<div class="card-header position-relative" id="historyListHeading">
|
||||
<a class="stretched-link font-weight-bold text-decoration-none" {% if "note.view_note"|has_perm:club.note %}
|
||||
href="{% url 'note:transactions' pk=club.note.pk %}" {% endif %}>
|
||||
<i class="fa fa-euro"></i> {% trans "Transaction history" %}
|
||||
</a>
|
||||
</div>
|
||||
<div id="history_list">
|
||||
{% render_table history_list %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
@ -13,7 +13,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.db.models import Q, Count
|
||||
from django.db.models import Q, Count, Case, When, Value, IntegerField, F
|
||||
from django.db.models.functions.text import Lower
|
||||
from django import forms
|
||||
from django.http import HttpResponse, Http404
|
||||
@ -35,7 +35,7 @@ from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||
|
||||
from .forms.registration import WEIChooseBusForm
|
||||
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
|
||||
from .forms import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, BusForm, BusTeamForm, WEIMembership1AForm, \
|
||||
from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \
|
||||
WEIMembershipForm, CurrentSurvey
|
||||
from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \
|
||||
WEIRegistration1ATable, WEIMembershipTable
|
||||
@ -133,6 +133,23 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, D
|
||||
membership=None,
|
||||
wei=club
|
||||
)
|
||||
# Annotate the query to be able to sort registrations on validate status
|
||||
pre_registrations = pre_registrations.annotate(
|
||||
deposit=Case(
|
||||
When(deposit_type='note', then=F('wei__deposit_amount')),
|
||||
default=Value(0),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
).annotate(
|
||||
total_fee=F('fee') + F('deposit')
|
||||
).annotate(
|
||||
validate_status=Case(
|
||||
When(total_fee__gt=F('user__note__balance'), then=Value(2)),
|
||||
When(first_year=True, then=Value(1)),
|
||||
default=Value(0),
|
||||
output_field=IntegerField(),
|
||||
)
|
||||
)
|
||||
buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
|
||||
.filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
|
||||
return [club_transactions, club_member, pre_registrations, buses, ]
|
||||
@ -260,6 +277,23 @@ class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTable
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
qs = super().get_queryset(**kwargs).filter(wei=self.club, membership=None).distinct()
|
||||
# Annotate the query to be able to sort registrations on validate status
|
||||
qs = qs.annotate(
|
||||
deposit=Case(
|
||||
When(deposit_type='note', then=F('wei__deposit_amount')),
|
||||
default=Value(0),
|
||||
output_field=IntegerField()
|
||||
)
|
||||
).annotate(
|
||||
total_fee=F('fee') + F('deposit')
|
||||
).annotate(
|
||||
validate_status=Case(
|
||||
When(total_fee__gt=F('user__note__balance'), then=Value(2)),
|
||||
When(first_year=True, then=Value(1)),
|
||||
default=Value(0),
|
||||
output_field=IntegerField(),
|
||||
)
|
||||
)
|
||||
|
||||
pattern = self.request.GET.get("search", "")
|
||||
|
||||
@ -510,7 +544,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
Register a new user to the WEI
|
||||
"""
|
||||
model = WEIRegistration
|
||||
form_class = WEIRegistration1AForm
|
||||
form_class = WEIRegistrationForm
|
||||
extra_context = {"title": _("Register first year student to the WEI")}
|
||||
|
||||
def get_sample_object(self):
|
||||
@ -606,7 +640,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
Register an old user to the WEI
|
||||
"""
|
||||
model = WEIRegistration
|
||||
form_class = WEIRegistration2AForm
|
||||
form_class = WEIRegistrationForm
|
||||
extra_context = {"title": _("Register old student to the WEI")}
|
||||
|
||||
def get_sample_object(self):
|
||||
@ -739,14 +773,11 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
if today >= wei.date_start or today < wei.membership_start:
|
||||
return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
|
||||
# Store the validate parameter in the view's state
|
||||
self.should_validate = request.GET.get('validate', False)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["club"] = self.object.wei
|
||||
# Pass the validate parameter to the template
|
||||
context["should_validate"] = self.should_validate
|
||||
|
||||
if self.object.is_validated:
|
||||
membership_form = self.get_membership_form(instance=self.object.membership,
|
||||
@ -788,7 +819,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
if not self.object.first_year and "deposit_type" in form.fields:
|
||||
form.fields["deposit_type"].required = True
|
||||
form.fields["deposit_type"].help_text = _("Choose how you want to pay the deposit")
|
||||
form.fields["deposit_type"].widget = forms.RadioSelect(choices=form.fields["deposit_type"].choices)
|
||||
|
||||
return form
|
||||
|
||||
@ -862,9 +892,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
survey = CurrentSurvey(self.object)
|
||||
if not survey.is_complete():
|
||||
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
|
||||
# On redirige vers la validation uniquement si c'est explicitement demandé (et stocké dans la vue)
|
||||
if self.should_validate and self.request.user.has_perm("wei.add_weimembership"):
|
||||
return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
|
||||
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
|
||||
|
||||
|
||||
@ -963,9 +990,9 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
|
||||
form = context["form"]
|
||||
if registration.soge_credit:
|
||||
form.fields["credit_amount"].initial = registration.fee
|
||||
form.fields["credit_amount"].initial = fee
|
||||
else:
|
||||
form.fields["credit_amount"].initial = max(0, registration.fee - registration.user.note.balance)
|
||||
form.fields["credit_amount"].initial = max(0, fee - registration.user.note.balance)
|
||||
|
||||
return context
|
||||
|
||||
@ -1052,7 +1079,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
|
||||
fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
|
||||
if registration.soge_credit:
|
||||
fee = 2000
|
||||
fee = registration.wei.fee_soge_credit
|
||||
|
||||
kfet = club.parent_club
|
||||
bde = kfet.parent_club
|
||||
|
Reference in New Issue
Block a user