1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-20 17:41:55 +02:00

Handle credits from the Société générale

This commit is contained in:
Yohann D'ANELLO
2020-04-22 03:26:45 +02:00
parent f83f6da79a
commit b23814aef0
25 changed files with 954 additions and 500 deletions

View File

@ -28,7 +28,7 @@ class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ('user', 'email_confirmed', 'registration_valid', 'soge', )
exclude = ('user', 'email_confirmed', 'registration_valid', )
class ClubForm(forms.ModelForm):

View File

@ -64,11 +64,12 @@ class Profile(models.Model):
default=False,
)
soge = models.BooleanField(
verbose_name=_("Société générale"),
help_text=_("Has the user ever be paid by the Société générale?"),
default=False,
)
@property
def soge(self):
if "treasury" in settings.INSTALLED_APPS:
from treasury.models import SogeCredit
return SogeCredit.objects.filter(user=self.user, credit_transaction=None).exists()
return False
class Meta:
verbose_name = _('user profile')
@ -309,7 +310,20 @@ class Membership(models.Model):
reason="Adhésion " + self.club.name,
)
transaction._force_save = True
transaction.save(force_insert=True)
print(hasattr(self, '_soge'))
if hasattr(self, '_soge') and "treasury" in settings.INSTALLED_APPS:
# If the soge pays, then the transaction is unvalidated in a first time, then submitted for control
# to treasurers.
transaction.valid = False
from treasury.models import SogeCredit
soge_credit = SogeCredit.objects.get_or_create(user=self.user)[0]
soge_credit.refresh_from_db()
transaction.save(force_insert=True)
transaction.refresh_from_db()
soge_credit.transactions.add(transaction)
soge_credit.save()
else:
transaction.save(force_insert=True)
def __str__(self):
return _("Membership of {user} for the club {club}").format(user=self.user.username, club=self.club.name, )

View File

@ -18,7 +18,7 @@ from django.views.generic.edit import FormMixin
from django_tables2.views import SingleTableView
from rest_framework.authtoken.models import Token
from note.forms import ImageForm
from note.models import Alias, NoteUser, NoteSpecial
from note.models import Alias, NoteUser
from note.models.transactions import Transaction, SpecialTransaction
from note.tables import HistoryTable, AliasTable
from permission.backends import PermissionBackend
@ -128,7 +128,8 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data(**kwargs)
user = context['user_object']
history_list = \
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))\
.order_by("-created_at", "-id")\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))
history_table = HistoryTable(history_list, prefix='transaction-')
history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1))
@ -314,7 +315,8 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
club.update_membership_dates()
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\
.order_by('-created_at', '-id')
history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
context['history_list'] = history_table
@ -365,6 +367,15 @@ class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form_class = ClubForm
template_name = "member/club_form.html"
def get_queryset(self, **kwargs):
qs = super().get_queryset(**kwargs)
# Don't update a WEI club through this view
if "wei" in settings.INSTALLED_APPS:
qs = qs.filter(weiclub=None)
return qs
def get_success_url(self):
return reverse_lazy("member:club_detail", kwargs={"pk": self.object.pk})
@ -396,7 +407,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
if "club_pk" in self.kwargs:
# We create a new membership.
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
.get(pk=self.kwargs["club_pk"])
.get(pk=self.kwargs["club_pk"], weiclub=None)
form.fields['credit_amount'].initial = club.membership_fee_paid
form.fields['roles'].initial = Role.objects.filter(name="Membre de club").all()
@ -463,17 +474,11 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
bank = form.cleaned_data["bank"]
soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE"
# If Société générale pays, then we auto-fill some data
# If Société générale pays, then we store that information but the payment must be controlled by treasurers
# later. The membership transaction will be invalidated.
if soge:
credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
bde = club
kfet = Club.objects.get(name="Kfet")
if user.profile.paid:
fee = bde.membership_fee_paid + kfet.membership_fee_paid
else:
fee = bde.membership_fee_unpaid + kfet.membership_fee_unpaid
credit_amount = fee
bank = "Société générale"
credit_type = None
form.instance._soge = True
if credit_type is None:
credit_amount = 0
@ -546,11 +551,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
ret = super().form_valid(form)
# If Société générale pays, then we store the information: the bank can't pay twice to a same person.
# If Société générale pays, then we assume that this is the BDE membership, and we auto-renew the
# Kfet membership.
if soge:
user.profile.soge = True
user.profile.save()
kfet = Club.objects.get(name="Kfet")
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
@ -562,13 +565,16 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
date_end__gte=datetime.today(),
)
membership = Membership.objects.create(
membership = Membership(
club=kfet,
user=user,
fee=kfet_fee,
date_start=old_membership.get().date_end + timedelta(days=1)
if old_membership.exists() else form.instance.date_start,
)
membership._soge = True
membership.save()
membership.refresh_from_db()
if old_membership.exists():
membership.roles.set(old_membership.get().roles.all())
else:

View File

@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel
from .notes import Note, NoteClub, NoteSpecial
from ..templatetags.pretty_money import pretty_money
"""
Defines transactions
@ -198,6 +199,14 @@ class Transaction(PolymorphicModel):
self.source.save()
self.destination.save()
def delete(self, **kwargs):
"""
Whenever we want to delete a transaction (caution with this), we ensure the transaction is invalid first.
"""
self.valid = False
self.save(**kwargs)
super().delete(**kwargs)
@property
def total(self):
return self.amount * self.quantity
@ -206,6 +215,10 @@ class Transaction(PolymorphicModel):
def type(self):
return _('Transfer')
def __str__(self):
return self.__class__.__name__ + " from " + str(self.source) + " to " + str(self.destination) + " of "\
+ pretty_money(self.quantity * self.amount) + ("" if self.valid else " invalid")
class RecurrentTransaction(Transaction):
"""

View File

@ -10,7 +10,7 @@ def save_user_note(instance, raw, **_kwargs):
# When provisionning data, do not try to autocreate
return
if (instance.is_superuser or instance.profile.registration_valid) and instance.is_active:
if instance.is_superuser or instance.profile.registration_valid:
# Create note only when the registration is validated
from note.models import NoteUser
NoteUser.objects.get_or_create(user=instance)

View File

@ -30,7 +30,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
table_class = HistoryTable
def get_queryset(self, **kwargs):
return super().get_queryset(**kwargs).order_by("-id").all()[:20]
return super().get_queryset(**kwargs).order_by("-created_at", "-id").all()[:20]
def get_context_data(self, **kwargs):
"""
@ -93,7 +93,7 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
table_class = HistoryTable
def get_queryset(self, **kwargs):
return super().get_queryset(**kwargs).order_by("-id").all()[:20]
return super().get_queryset(**kwargs).order_by("-created_at", "-id").all()[:20]
def get_context_data(self, **kwargs):
"""

View File

@ -16,13 +16,12 @@ from django.views.generic.edit import FormMixin
from django_tables2 import SingleTableView
from member.forms import ProfileForm
from member.models import Membership, Club, Role
from note.models import SpecialTransaction, NoteSpecial
from note.models import SpecialTransaction
from note.templatetags.pretty_money import pretty_money
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
from wei.models import WEIClub
from .forms import SignUpForm, ValidationForm, WEISignupForm
from .forms import SignUpForm, ValidationForm
from .tables import FutureUserTable
from .tokens import email_validation_token
@ -40,16 +39,6 @@ class UserCreateView(CreateView):
context = super().get_context_data(**kwargs)
context["profile_form"] = self.second_form()
if "wei" in settings.INSTALLED_APPS:
from wei.forms import WEIRegistrationForm
wei_form = WEIRegistrationForm()
del wei_form.fields["user"]
del wei_form.fields["caution_check"]
del wei_form.fields["first_year"]
del wei_form.fields["information_json"]
context["wei_form"] = wei_form
context["wei_registration_form"] = WEISignupForm()
return context
def form_valid(self, form):
@ -62,23 +51,6 @@ class UserCreateView(CreateView):
if not profile_form.is_valid():
return self.form_invalid(form)
wei_form = None
self.wei = False
if "wei" in settings.INSTALLED_APPS:
wei_signup_form = WEISignupForm(self.request.POST)
if wei_signup_form.is_valid() and wei_signup_form.cleaned_data["wei_registration"]:
from wei.forms import WEIRegistrationForm
wei_form = WEIRegistrationForm(self.request.POST)
del wei_form.fields["user"]
del wei_form.fields["caution_check"]
del wei_form.fields["first_year"]
if not wei_form.is_valid():
return self.form_invalid(wei_form)
self.wei = True
# Save the user and the profile
user = form.save(commit=False)
user.is_active = False
@ -92,21 +64,10 @@ class UserCreateView(CreateView):
user.profile.send_email_validation_link()
if self.wei:
wei_registration = wei_form.instance
wei_registration.user = user
wei_registration.wei = WEIClub.objects.order_by('date_start').last()
wei_registration.caution_check = False
wei_registration.first_year = True
wei_registration.save()
return super().form_valid(form)
def get_success_url(self):
if self.wei:
return reverse_lazy('registration:email_validation_sent') # TODO Load WEI survey
else:
return reverse_lazy('registration:email_validation_sent')
return reverse_lazy('registration:email_validation_sent')
class UserValidateView(TemplateView):
@ -304,17 +265,17 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
fee += kfet_fee
if soge:
# Fill payment information if Société Générale pays the inscription
credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
credit_amount = fee
bank = "Société générale"
# If the bank pays, then we don't credit now. Treasurers will validate the transaction
# and credit the note later.
credit_type = None
print("OK")
if credit_type is None:
credit_amount = 0
if join_Kfet and not join_BDE:
form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club."))
if fee > credit_amount:
if fee > credit_amount and not soge:
# Check if the user credits enough money
form.add_error('credit_type',
_("The entered amount is not enough for the memberships, should be at least {}")
@ -336,10 +297,9 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
ret = super().form_valid(form)
user.is_active = user.profile.email_confirmed or user.is_superuser
user.profile.registration_valid = True
# Store if Société générale paid for next years
user.profile.soge = soge
user.save()
user.profile.save()
user.refresh_from_db()
if credit_type is not None and credit_amount > 0:
# Credit the note
@ -357,21 +317,29 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
if join_BDE:
# Create membership for the user to the BDE starting today
membership = Membership.objects.create(
membership = Membership(
club=bde,
user=user,
fee=bde_fee,
)
if soge:
membership._soge = True
membership.save()
membership.refresh_from_db()
membership.roles.add(Role.objects.get(name="Adhérent BDE"))
membership.save()
if join_Kfet:
# Create membership for the user to the Kfet starting today
membership = Membership.objects.create(
membership = Membership(
club=kfet,
user=user,
fee=kfet_fee,
)
if soge:
membership._soge = True
membership.save()
membership.refresh_from_db()
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
membership.save()

View File

@ -3,7 +3,7 @@
from django.contrib import admin
from .models import RemittanceType, Remittance
from .models import RemittanceType, Remittance, SogeCredit
@admin.register(RemittanceType)
@ -25,3 +25,6 @@ class RemittanceAdmin(admin.ModelAdmin):
if not obj:
return True
return not obj.closed and super().has_change_permission(request, obj)
admin.site.register(SogeCredit)

View File

@ -4,7 +4,7 @@
from rest_framework import serializers
from note.api.serializers import SpecialTransactionSerializer
from ..models import Invoice, Product, RemittanceType, Remittance
from ..models import Invoice, Product, RemittanceType, Remittance, SogeCredit
class ProductSerializer(serializers.ModelSerializer):
@ -60,3 +60,14 @@ class RemittanceSerializer(serializers.ModelSerializer):
def get_transactions(self, obj):
return serializers.ListSerializer(child=SpecialTransactionSerializer()).to_representation(obj.transactions)
class SogeCreditSerializer(serializers.ModelSerializer):
"""
REST API Serializer for SogeCredit types.
The djangorestframework plugin will analyse the model `SogeCredit` and parse all fields in the API.
"""
class Meta:
model = SogeCredit
fields = '__all__'

View File

@ -1,7 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet
from .views import InvoiceViewSet, ProductViewSet, RemittanceViewSet, RemittanceTypeViewSet, SogeCreditViewSet
def register_treasury_urls(router, path):
@ -12,3 +12,4 @@ def register_treasury_urls(router, path):
router.register(path + '/product', ProductViewSet)
router.register(path + '/remittance_type', RemittanceTypeViewSet)
router.register(path + '/remittance', RemittanceViewSet)
router.register(path + '/soge_credit', SogeCreditViewSet)

View File

@ -5,8 +5,9 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter
from api.viewsets import ReadProtectedModelViewSet
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer
from ..models import Invoice, Product, RemittanceType, Remittance
from .serializers import InvoiceSerializer, ProductSerializer, RemittanceTypeSerializer, RemittanceSerializer,\
SogeCreditSerializer
from ..models import Invoice, Product, RemittanceType, Remittance, SogeCredit
class InvoiceViewSet(ReadProtectedModelViewSet):
@ -39,7 +40,7 @@ class RemittanceTypeViewSet(ReadProtectedModelViewSet):
The djangorestframework plugin will get all `RemittanceType` objects, serialize it to JSON with the given serializer
then render it on /api/treasury/remittance_type/
"""
queryset = RemittanceType.objects.all()
queryset = RemittanceType.objects
serializer_class = RemittanceTypeSerializer
@ -49,5 +50,15 @@ class RemittanceViewSet(ReadProtectedModelViewSet):
The djangorestframework plugin will get all `Remittance` objects, serialize it to JSON with the given serializer,
then render it on /api/treasury/remittance/
"""
queryset = Remittance.objects.all()
queryset = Remittance.objects
serializer_class = RemittanceSerializer
class SogeCreditViewSet(ReadProtectedModelViewSet):
"""
REST API View set.
The djangorestframework plugin will get all `SogeCredit` objects, serialize it to JSON with the given serializer,
then render it on /api/treasury/soge_credit/
"""
queryset = SogeCredit.objects
serializer_class = SogeCreditSerializer

View File

@ -1,11 +1,13 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import datetime
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial, SpecialTransaction
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction
class Invoice(models.Model):
@ -207,3 +209,90 @@ class SpecialTransactionProxy(models.Model):
class Meta:
verbose_name = _("special transaction proxy")
verbose_name_plural = _("special transaction proxies")
class SogeCredit(models.Model):
"""
Manage the credits from the Société générale.
"""
user = models.OneToOneField(
User,
on_delete=models.PROTECT,
verbose_name=_("user"),
)
transactions = models.ManyToManyField(
MembershipTransaction,
related_name="+",
verbose_name=_("membership transactions"),
)
credit_transaction = models.OneToOneField(
SpecialTransaction,
on_delete=models.SET_NULL,
verbose_name=_("credit transaction"),
null=True,
)
@property
def valid(self):
return self.credit_transaction is not None
@property
def amount(self):
return sum(transaction.quantity * transaction.amount for transaction in self.transactions.all())
def invalidate(self):
"""
Invalidating a Société générale delete the transaction of the bank if it was already created.
Treasurers must know what they do, With Great Power Comes Great Responsibility...
"""
if self.valid:
self.credit_transaction.valid = False
self.credit_transaction.save()
self.credit_transaction.delete()
self.credit_transaction = None
for transaction in self.transactions.all():
transaction.valid = False
transaction.save()
def validate(self, force=False):
if self.valid and not force:
# The credit is already done
return
# First invalidate all transaction and delete the credit if already did (and force mode)
self.invalidate()
self.credit_transaction = SpecialTransaction.objects.create(
source=NoteSpecial.objects.get(special_type="Virement bancaire"),
destination=self.user.note,
quantity=1,
amount=self.amount,
reason="Crédit société générale",
last_name=self.user.last_name,
first_name=self.user.first_name,
bank="Société générale",
)
self.save()
for transaction in self.transactions.all():
transaction.valid = True
transaction.created_at = datetime.now()
transaction.save()
def delete(self, **kwargs):
"""
Deleting a SogeCredit is equivalent to say that the Société générale didn't pay.
Treasurers must know what they do, this is difficult to undo this operation.
With Great Power Comes Great Responsibility...
"""
self.invalidate()
for transaction in self.transactions.all():
transaction.valid = True
transaction.created_at = datetime.now()
transaction.save()
super().delete(**kwargs)
class Meta:
verbose_name = _("Credit from the Société générale")
verbose_name_plural = _("Credits from the Société générale")

View File

@ -7,7 +7,7 @@ from django_tables2 import A
from note.models import SpecialTransaction
from note.templatetags.pretty_money import pretty_money
from .models import Invoice, Remittance
from .models import Invoice, Remittance, SogeCredit
class InvoiceTable(tables.Table):
@ -101,3 +101,28 @@ class SpecialTransactionTable(tables.Table):
model = SpecialTransaction
template_name = 'django_tables2/bootstrap4.html'
fields = ('id', 'source', 'destination', 'last_name', 'first_name', 'bank', 'amount', 'reason',)
class SogeCreditTable(tables.Table):
user = tables.LinkColumn(
'treasury:manage_soge_credit',
args=[A('pk')],
)
amount = tables.Column(
verbose_name=_("Amount"),
)
valid = tables.Column(
verbose_name=_("Valid"),
)
def render_amount(self, value):
return pretty_money(value)
def render_valid(self, value):
return _("Yes") if value else _("No")
class Meta:
model = SogeCredit
fields = ('user', 'amount', 'valid', )

View File

@ -4,7 +4,8 @@
from django.urls import path
from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceRenderView, RemittanceListView,\
RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView, UnlinkTransactionToRemittanceView
RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView, UnlinkTransactionToRemittanceView,\
SogeCreditListView, SogeCreditManageView
app_name = 'treasury'
urlpatterns = [
@ -21,4 +22,7 @@ urlpatterns = [
path('remittance/link_transaction/<int:pk>/', LinkTransactionToRemittanceView.as_view(), name='link_transaction'),
path('remittance/unlink_transaction/<int:pk>/', UnlinkTransactionToRemittanceView.as_view(),
name='unlink_transaction'),
path('soge-credits/list/', SogeCreditListView.as_view(), name='soge_credits'),
path('soge-credits/manage/<int:pk>/', SogeCreditManageView.as_view(), name='manage_soge_credit'),
]

View File

@ -10,21 +10,23 @@ from crispy_forms.helper import FormHelper
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.forms import Form
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.views.generic import CreateView, UpdateView
from django.views.generic import CreateView, UpdateView, DetailView
from django.views.generic.base import View, TemplateView
from django.views.generic.edit import BaseFormView
from django_tables2 import SingleTableView
from note.models import SpecialTransaction, NoteSpecial
from note.models import SpecialTransaction, NoteSpecial, Alias
from note_kfet.settings.base import BASE_DIR
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm
from .models import Invoice, Product, Remittance, SpecialTransactionProxy
from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable
from .models import Invoice, Product, Remittance, SpecialTransactionProxy, SogeCredit
from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable, SogeCreditTable
class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
@ -307,3 +309,61 @@ class UnlinkTransactionToRemittanceView(LoginRequiredMixin, View):
transaction.save()
return redirect('treasury:remittance_list')
class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView):
"""
List all Société Générale credits
"""
model = SogeCredit
table_class = SogeCreditTable
def get_queryset(self, **kwargs):
"""
Filter the table with the given parameter.
:param kwargs:
:return:
"""
qs = super().get_queryset()
if "search" in self.request.GET:
pattern = self.request.GET["search"]
if not pattern:
return qs.none()
qs = qs.filter(
Q(user__first_name__iregex=pattern)
| Q(user__last_name__iregex=pattern)
| Q(user__note__alias__name__iregex="^" + pattern)
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
)
else:
qs = qs.none()
if "valid" in self.request.GET:
q = Q(credit_transaction=None)
if not self.request.GET["valid"]:
q = ~q
qs = qs.filter(q)
return qs[:20]
class SogeCreditManageView(LoginRequiredMixin, ProtectQuerysetMixin, BaseFormView, DetailView):
"""
Manage credits from the Société générale.
"""
model = SogeCredit
form_class = Form
def form_valid(self, form):
if "validate" in form.data:
self.get_object().validate(True)
elif "delete" in form.data:
self.get_object().delete()
return super().form_valid(form)
def get_success_url(self):
if "validate" in self.request.POST:
return reverse_lazy('treasury:manage_soge_credit', args=(self.get_object().pk,))
return reverse_lazy('treasury:soge_credits')

View File

@ -3,6 +3,7 @@
import json
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -312,3 +313,14 @@ class WEIMembership(Membership):
)
transaction._force_save = True
transaction.save(force_insert=True)
if self.registration.soge_credit and "treasury" in settings.INSTALLED_APPS:
# If the soge pays, then the transaction is unvalidated in a first time, then submitted for control
# to treasurers.
transaction.refresh_from_db()
from treasury.models import SogeCredit
soge_credit = SogeCredit.objects.get_or_create(user=self.user)[0]
soge_credit.refresh_from_db()
transaction.save()
soge_credit.transactions.add(transaction)
soge_credit.save()

View File

@ -73,7 +73,8 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
club = context["club"]
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\
.order_by('-created_at', '-id')
history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
context['history_list'] = history_table
@ -742,9 +743,10 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.club.pk})
class WEISurveyView(BaseFormView, DetailView):
class WEISurveyView(LoginRequiredMixin, BaseFormView, DetailView):
"""
Display the survey for the WEI for first
Display the survey for the WEI for first year members.
Warning: this page is accessible for anyone that is connected, the view doesn't extend ProtectQuerySetMixin.
"""
model = WEIRegistration
template_name = "wei/survey.html"
@ -800,7 +802,7 @@ class WEISurveyView(BaseFormView, DetailView):
return reverse_lazy('wei:wei_survey', args=(self.get_object().pk,))
class WEISurveyEndView(TemplateView):
class WEISurveyEndView(LoginRequiredMixin, TemplateView):
template_name = "wei/survey_end.html"
def get_context_data(self, **kwargs):
@ -810,7 +812,7 @@ class WEISurveyEndView(TemplateView):
return context
class WEIClosedView(TemplateView):
class WEIClosedView(LoginRequiredMixin, TemplateView):
template_name = "wei/survey_closed.html"
def get_context_data(self, **kwargs):