mirror of https://gitlab.crans.org/bde/nk20
The BDE offers 80 € to each new member that registers to the Société générale
This commit is contained in:
parent
dc6a5f56f6
commit
fa3c723140
|
@ -27,8 +27,8 @@ def create_bde_and_kfet(apps, schema_editor):
|
||||||
parent_club_id=1,
|
parent_club_id=1,
|
||||||
email="tresorerie.bde@example.com",
|
email="tresorerie.bde@example.com",
|
||||||
require_memberships=True,
|
require_memberships=True,
|
||||||
membership_fee_paid=500,
|
membership_fee_paid=3500,
|
||||||
membership_fee_unpaid=500,
|
membership_fee_unpaid=3500,
|
||||||
membership_duration=396,
|
membership_duration=396,
|
||||||
membership_start="2020-08-01",
|
membership_start="2020-08-01",
|
||||||
membership_end="2021-09-30",
|
membership_end="2021-09-30",
|
||||||
|
|
|
@ -138,7 +138,7 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
We can't display information of a not registered user.
|
We can't display information of a not registered user.
|
||||||
"""
|
"""
|
||||||
return super().get_queryset().filter(profile__registration_valid=True)
|
return super().get_queryset(**kwargs).filter(profile__registration_valid=True)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -175,7 +175,7 @@ class Transaction(PolymorphicModel):
|
||||||
|
|
||||||
created = self.pk is None
|
created = self.pk is None
|
||||||
to_transfer = self.amount * self.quantity
|
to_transfer = self.amount * self.quantity
|
||||||
if not created:
|
if not created and not self.valid and not hasattr(self, "_force_save"):
|
||||||
# Revert old transaction
|
# Revert old transaction
|
||||||
old_transaction = Transaction.objects.get(pk=self.pk)
|
old_transaction = Transaction.objects.get(pk=self.pk)
|
||||||
# Check that nothing important changed
|
# Check that nothing important changed
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import HiddenInput
|
from django.forms import HiddenInput
|
||||||
|
from django.http import Http404
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import UpdateView, TemplateView, CreateView
|
from django.views.generic import UpdateView, TemplateView, CreateView
|
||||||
from member.models import Membership
|
from member.models import Membership
|
||||||
|
@ -24,9 +25,20 @@ class ProtectQuerysetMixin:
|
||||||
Display 404 error if the user can't see an object, remove the fields the user can't
|
Display 404 error if the user can't see an object, remove the fields the user can't
|
||||||
update on an update form (useful if the user can't change only specified fields).
|
update on an update form (useful if the user can't change only specified fields).
|
||||||
"""
|
"""
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, filter_permissions=True, **kwargs):
|
||||||
qs = super().get_queryset(**kwargs)
|
qs = super().get_queryset(**kwargs)
|
||||||
return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view")).distinct()
|
return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view")).distinct()\
|
||||||
|
if filter_permissions else qs
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
try:
|
||||||
|
return super().get_object(queryset)
|
||||||
|
except Http404 as e:
|
||||||
|
try:
|
||||||
|
super().get_object(self.get_queryset(filter_permissions=False))
|
||||||
|
raise PermissionDenied()
|
||||||
|
except Http404:
|
||||||
|
raise e
|
||||||
|
|
||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
form = super().get_form(form_class)
|
form = super().get_form(form_class)
|
||||||
|
|
|
@ -364,7 +364,7 @@ class TestValidateRegistration(TestCase):
|
||||||
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
||||||
self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
|
self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
|
||||||
self.assertEqual(Transaction.objects.filter(
|
self.assertEqual(Transaction.objects.filter(
|
||||||
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2)
|
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
||||||
self.assertFalse(Transaction.objects.filter(valid=True).exists())
|
self.assertFalse(Transaction.objects.filter(valid=True).exists())
|
||||||
|
|
||||||
response = self.client.get(self.user.profile.get_absolute_url())
|
response = self.client.get(self.user.profile.get_absolute_url())
|
||||||
|
|
|
@ -21,6 +21,7 @@ from note.templatetags.pretty_money import pretty_money
|
||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from permission.models import Role
|
from permission.models import Role
|
||||||
from permission.views import ProtectQuerysetMixin
|
from permission.views import ProtectQuerysetMixin
|
||||||
|
from treasury.models import SogeCredit
|
||||||
|
|
||||||
from .forms import SignUpForm, ValidationForm
|
from .forms import SignUpForm, ValidationForm
|
||||||
from .tables import FutureUserTable
|
from .tables import FutureUserTable
|
||||||
|
@ -219,6 +220,9 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||||
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
||||||
kfet = Club.objects.get(name="Kfet")
|
kfet = Club.objects.get(name="Kfet")
|
||||||
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||||
|
# In 2020, for COVID-19 reasons, the BDE offered 80 € to each new member that opens a Sogé account,
|
||||||
|
# since there is no WEI.
|
||||||
|
fee += 8000
|
||||||
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
@ -342,6 +346,11 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
||||||
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
|
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
|
||||||
membership.save()
|
membership.save()
|
||||||
|
|
||||||
|
if soge:
|
||||||
|
soge_credit = SogeCredit.objects.get(user=user)
|
||||||
|
# Update the credit transaction amount
|
||||||
|
soge_credit.save()
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
|
|
@ -291,11 +291,11 @@ class SogeCredit(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def valid(self):
|
def valid(self):
|
||||||
return self.credit_transaction is not None
|
return self.credit_transaction.valid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amount(self):
|
def amount(self):
|
||||||
return sum(transaction.total for transaction in self.transactions.all())
|
return sum(transaction.total for transaction in self.transactions.all()) + 8000
|
||||||
|
|
||||||
def invalidate(self):
|
def invalidate(self):
|
||||||
"""
|
"""
|
||||||
|
@ -304,11 +304,7 @@ class SogeCredit(models.Model):
|
||||||
"""
|
"""
|
||||||
if self.valid:
|
if self.valid:
|
||||||
self.credit_transaction.valid = False
|
self.credit_transaction.valid = False
|
||||||
self.credit_transaction._force_save = True
|
|
||||||
self.credit_transaction.save()
|
self.credit_transaction.save()
|
||||||
self.credit_transaction._force_delete = True
|
|
||||||
self.credit_transaction.delete()
|
|
||||||
self.credit_transaction = None
|
|
||||||
for transaction in self.transactions.all():
|
for transaction in self.transactions.all():
|
||||||
transaction.valid = False
|
transaction.valid = False
|
||||||
transaction._force_save = True
|
transaction._force_save = True
|
||||||
|
@ -321,17 +317,10 @@ class SogeCredit(models.Model):
|
||||||
|
|
||||||
# First invalidate all transaction and delete the credit if already did (and force mode)
|
# First invalidate all transaction and delete the credit if already did (and force mode)
|
||||||
self.invalidate()
|
self.invalidate()
|
||||||
self.credit_transaction = SpecialTransaction.objects.create(
|
# Refresh credit amount
|
||||||
source=NoteSpecial.objects.get(special_type="Virement bancaire"),
|
self.save()
|
||||||
destination=self.user.note,
|
self.credit_transaction.valid = True
|
||||||
quantity=1,
|
self.credit_transaction.save()
|
||||||
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",
|
|
||||||
created_at=self.transactions.order_by("-created_at").first().created_at,
|
|
||||||
)
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
for transaction in self.transactions.all():
|
for transaction in self.transactions.all():
|
||||||
|
@ -340,6 +329,25 @@ class SogeCredit(models.Model):
|
||||||
transaction.created_at = timezone.now()
|
transaction.created_at = timezone.now()
|
||||||
transaction.save()
|
transaction.save()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.credit_transaction:
|
||||||
|
self.credit_transaction = SpecialTransaction.objects.create(
|
||||||
|
source=NoteSpecial.objects.get(special_type="Virement bancaire"),
|
||||||
|
destination=self.user.note,
|
||||||
|
quantity=1,
|
||||||
|
amount=0,
|
||||||
|
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",
|
||||||
|
valid=False,
|
||||||
|
)
|
||||||
|
elif not self.valid:
|
||||||
|
self.credit_transaction.amount = self.amount
|
||||||
|
self.credit_transaction._force_save = True
|
||||||
|
self.credit_transaction.save()
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def delete(self, **kwargs):
|
def delete(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Deleting a SogeCredit is equivalent to say that the Société générale didn't pay.
|
Deleting a SogeCredit is equivalent to say that the Société générale didn't pay.
|
||||||
|
@ -358,6 +366,9 @@ class SogeCredit(models.Model):
|
||||||
transaction.valid = True
|
transaction.valid = True
|
||||||
transaction.created_at = timezone.now()
|
transaction.created_at = timezone.now()
|
||||||
transaction.save()
|
transaction.save()
|
||||||
|
self.credit_transaction.valid = False
|
||||||
|
self.credit_transaction.reason += " (invalide)"
|
||||||
|
self.credit_transaction.save()
|
||||||
super().delete(**kwargs)
|
super().delete(**kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -30,7 +30,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
<input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note ...">
|
<input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note ...">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<label for="invalid_only" class="form-check-label">
|
<label for="invalid_only" class="form-check-label">
|
||||||
<input id="invalid_only" name="invalid_only" type="checkbox" class="checkboxinput form-check-input">
|
<input id="invalid_only" name="invalid_only" type="checkbox" class="checkboxinput form-check-input" checked>
|
||||||
{% trans "Filter with unvalidated credits only" %}
|
{% trans "Filter with unvalidated credits only" %}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -353,7 +353,6 @@ class TestSogeCredits(TestCase):
|
||||||
soge_credit.refresh_from_db()
|
soge_credit.refresh_from_db()
|
||||||
self.assertTrue(soge_credit.valid)
|
self.assertTrue(soge_credit.valid)
|
||||||
self.user.note.refresh_from_db()
|
self.user.note.refresh_from_db()
|
||||||
self.assertEqual(self.user.note.balance, 0)
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
||||||
self.assertTrue(self.user.profile.soge)
|
self.assertTrue(self.user.profile.soge)
|
||||||
|
@ -391,7 +390,7 @@ class TestSogeCredits(TestCase):
|
||||||
self.user.note.refresh_from_db()
|
self.user.note.refresh_from_db()
|
||||||
self.assertEqual(self.user.note.balance, 0)
|
self.assertEqual(self.user.note.balance, 0)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 4)
|
||||||
self.assertFalse(self.user.profile.soge)
|
self.assertFalse(self.user.profile.soge)
|
||||||
|
|
||||||
def test_invoice_api(self):
|
def test_invoice_api(self):
|
||||||
|
|
|
@ -425,11 +425,8 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi
|
||||||
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
|
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
|
||||||
)
|
)
|
||||||
|
|
||||||
if "valid" in self.request.GET:
|
if "valid" not in self.request.GET or not self.request.GET["valid"]:
|
||||||
q = Q(credit_transaction=None)
|
qs = qs.filter(credit_transaction__valid=False)
|
||||||
if not self.request.GET["valid"]:
|
|
||||||
q = ~q
|
|
||||||
qs = qs.filter(q)
|
|
||||||
|
|
||||||
return qs[:20]
|
return qs[:20]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue