diff --git a/apps/member/apps.py b/apps/member/apps.py index d5b1f630..84799e6a 100644 --- a/apps/member/apps.py +++ b/apps/member/apps.py @@ -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 + ) diff --git a/apps/member/signals.py b/apps/member/signals.py index 869f9117..e74c37ad 100644 --- a/apps/member/signals.py +++ b/apps/member/signals.py @@ -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() diff --git a/apps/wei/migrations/0016_weiregistration_fee_alter_weiclub_fee_soge_credit.py b/apps/wei/migrations/0016_weiregistration_fee_alter_weiclub_fee_soge_credit.py new file mode 100644 index 00000000..6d2d1289 --- /dev/null +++ b/apps/wei/migrations/0016_weiregistration_fee_alter_weiclub_fee_soge_credit.py @@ -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)'), + ), + ] diff --git a/apps/wei/models.py b/apps/wei/models.py index 59f018d7..18ba8a58 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -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): diff --git a/apps/wei/tables.py b/apps/wei/tables.py index 9daadcb6..3f0c322e 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -56,10 +56,13 @@ class WEIRegistrationTable(tables.Table): } ) - validate = tables.Column( + validate = tables.LinkColumn( + 'wei:wei_update_registration', + args=[A('pk')], verbose_name=_("Validate"), - orderable=False, - accessor=A('pk'), + orderable=True, + accessor='validate_status', + text=_("Validate"), attrs={ 'th': { 'id': 'validate-membership-header' @@ -100,10 +103,11 @@ class WEIRegistrationTable(tables.Table): url = reverse_lazy('wei:wei_update_registration', args=(record.pk,)) + '?validate=true' 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 +125,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', diff --git a/apps/wei/views.py b/apps/wei/views.py index 4ac679f9..d5151848 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -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 @@ -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, ] @@ -261,6 +278,23 @@ class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTable def get_queryset(self, **kwargs): qs = super().get_queryset(**kwargs).filter(wei=self.club, membership=None).distinct() + 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", "") if pattern: @@ -963,9 +997,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 +1086,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