mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-07-20 08:01:26 +02:00
Merge branch 'main' into family
This commit is contained in:
@ -145,7 +145,7 @@ class AddIngredientForms(forms.ModelForm):
|
||||
polymorphic_ctype__model="transformedfood",
|
||||
is_ready=False,
|
||||
end_of_life='',
|
||||
).filter(PermissionBackend.filter_queryset(get_current_request(), TransformedFood, "change")).exclude(pk=pk)
|
||||
).filter(PermissionBackend.filter_queryset(get_current_request(), Food, "change")).exclude(pk=pk)
|
||||
|
||||
class Meta:
|
||||
model = TransformedFood
|
||||
|
@ -12,18 +12,21 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<ul>
|
||||
{% if QR_code %}
|
||||
<li> {{QR_code}} </li>
|
||||
{% endif %}
|
||||
{% for field, value in fields %}
|
||||
<li> {{ field }} : {{ value }}</li>
|
||||
{% endfor %}
|
||||
{% if meals %}
|
||||
<li> {% trans "Contained in" %} :
|
||||
<li> {% trans "Contained in" %} :
|
||||
{% for meal in meals %}
|
||||
<a href="{% url "food:transformedfood_view" pk=meal.pk %}">{{ meal.name }}</a>{% if not forloop.last %},{% endif %}
|
||||
<a href="{% url "food:transformedfood_view" pk=meal.pk %}">{{ meal.name }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if foods %}
|
||||
<li> {% trans "Contain" %} :
|
||||
<li> {% trans "Contain" %} :
|
||||
{% for food in foods %}
|
||||
<a href="{% url "food:food_view" pk=food.pk %}">{{ food.name }}</a>{% if not forloop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
@ -31,23 +34,23 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% if update %}
|
||||
<a class="btn btn-sm btn-secondary" href="{% url "food:food_update" pk=food.pk %}">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-secondary" href="{% url "food:food_update" pk=food.pk %}">
|
||||
{% trans "Update" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if add_ingredient %}
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:add_ingredient" pk=food.pk %}">
|
||||
{% trans "Add to a meal" %}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:add_ingredient" pk=food.pk %}">
|
||||
{% trans "Add to a meal" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if manage_ingredients %}
|
||||
<a class="btn btn-sm btn-secondary" href="{% url "food:manage_ingredients" pk=food.pk %}">
|
||||
{% trans "Manage ingredients" %}
|
||||
</a>
|
||||
{% trans "Manage ingredients" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
|
||||
{% trans "Return to the food list" %}
|
||||
</a>
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
|
||||
{% trans "Return to the food list" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
@ -7,7 +7,52 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{{ block.super }}
|
||||
<div class="card bg-light">
|
||||
<h3 class="card-header text-center">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<style>
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type=number] {
|
||||
appearance: textfield;
|
||||
padding: 6px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
<div class="d-flex align-items-center" style="max-width: 300px;">
|
||||
<form method="get" action="{% url 'food:redirect_view' %}" class="d-flex w-100">
|
||||
<input type="number" name="slug" placeholder="QR-code" required class="form-control form-control-sm" style="max-width: 120px;">
|
||||
<button type="submit" class="btn btn-sm btn-primary">{% trans "View food" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input id="searchbar" type="text" class="form-control"
|
||||
placeholder="{% trans "Search by attribute such as name..." %}">
|
||||
</div>
|
||||
|
||||
{% block extra_inside_card %}
|
||||
{% endblock %}
|
||||
|
||||
<div id="dynamic-table">
|
||||
{% if table.data %}
|
||||
{% render_table table %}
|
||||
{% else %}
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
{% trans "There is no results." %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="card bg-light mb-3">
|
||||
<h3 class="card-header text-center">
|
||||
@ -68,4 +113,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('goButton').addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
const slug = document.getElementById('slugInput').value;
|
||||
if (slug && !isNaN(slug)) {
|
||||
window.location.href = `/food/${slug}/`;
|
||||
} else {
|
||||
alert("Veuillez entrer un nombre valide.");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -18,4 +18,5 @@ urlpatterns = [
|
||||
path('detail/basic/<int:pk>', views.BasicFoodDetailView.as_view(), name='basicfood_view'),
|
||||
path('detail/transformed/<int:pk>', views.TransformedFoodDetailView.as_view(), name='transformedfood_view'),
|
||||
path('add/ingredient/<int:pk>', views.AddIngredientView.as_view(), name='add_ingredient'),
|
||||
path('redirect/', views.QRCodeRedirectView.as_view(), name='redirect_view'),
|
||||
]
|
||||
|
@ -10,6 +10,7 @@ from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect, Http404
|
||||
from django.views.generic import DetailView, UpdateView, CreateView
|
||||
from django.views.generic.list import ListView
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -63,7 +64,8 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
valid_regex = is_regex(pattern)
|
||||
suffix = '__iregex' if valid_regex else '__istartswith'
|
||||
prefix = '^' if valid_regex else ''
|
||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern}))
|
||||
qs = qs.filter(Q(**{f'name{suffix}': prefix + pattern})
|
||||
| Q(**{f'owner__name{suffix}': prefix + pattern}))
|
||||
else:
|
||||
qs = qs.none()
|
||||
search_table = qs.filter(PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||
@ -453,6 +455,8 @@ class FoodDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||
context["fields"] = [(
|
||||
Food._meta.get_field(field).verbose_name.capitalize(),
|
||||
value) for field, value in fields.items()]
|
||||
if self.object.QR_code.exists():
|
||||
context["QR_code"] = self.object.QR_code.first()
|
||||
context["meals"] = self.object.transformed_ingredient_inv.all()
|
||||
context["update"] = PermissionBackend.check_perm(self.request, "food.change_food")
|
||||
context["add_ingredient"] = (self.object.end_of_life == '' and PermissionBackend.check_perm(self.request, "food.change_transformedfood"))
|
||||
@ -506,3 +510,14 @@ class TransformedFoodDetailView(FoodDetailView):
|
||||
if Food.objects.filter(pk=kwargs['pk']).count() == 1:
|
||||
kwargs['stop_redirect'] = (Food.objects.get(pk=kwargs['pk']).polymorphic_ctype.model == 'transformedfood')
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
|
||||
class QRCodeRedirectView(RedirectView):
|
||||
"""
|
||||
Redirects to the QR code creation page from Food List
|
||||
"""
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
slug = self.request.GET.get('slug')
|
||||
if slug:
|
||||
return reverse_lazy('food:qrcode_create', kwargs={'slug': slug})
|
||||
return reverse_lazy('food:list')
|
||||
|
@ -438,8 +438,6 @@ class Membership(models.Model):
|
||||
)
|
||||
if hasattr(self, '_force_renew_parent') and self._force_renew_parent:
|
||||
new_membership._force_renew_parent = True
|
||||
if hasattr(self, '_soge') and self._soge:
|
||||
new_membership._soge = True
|
||||
if hasattr(self, '_force_save') and self._force_save:
|
||||
new_membership._force_save = True
|
||||
new_membership.save()
|
||||
@ -458,8 +456,6 @@ class Membership(models.Model):
|
||||
# Renew the previous membership of the parent club
|
||||
parent_membership = parent_membership.first()
|
||||
parent_membership._force_renew_parent = True
|
||||
if hasattr(self, '_soge'):
|
||||
parent_membership._soge = True
|
||||
if hasattr(self, '_force_save'):
|
||||
parent_membership._force_save = True
|
||||
parent_membership.renew()
|
||||
@ -471,8 +467,6 @@ class Membership(models.Model):
|
||||
date_start=self.date_start,
|
||||
)
|
||||
parent_membership._force_renew_parent = True
|
||||
if hasattr(self, '_soge'):
|
||||
parent_membership._soge = True
|
||||
if hasattr(self, '_force_save'):
|
||||
parent_membership._force_save = True
|
||||
parent_membership.save()
|
||||
|
@ -44,7 +44,7 @@ class TemplateLoggedInTests(TestCase):
|
||||
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, 302, 302)
|
||||
|
||||
def test_logout(self):
|
||||
response = self.client.get(reverse("logout"))
|
||||
response = self.client.post(reverse("logout"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_admin_index(self):
|
||||
|
@ -13,7 +13,7 @@ def register_note_urls(router, path):
|
||||
router.register(path + '/note', NotePolymorphicViewSet)
|
||||
router.register(path + '/alias', AliasViewSet)
|
||||
router.register(path + '/trust', TrustViewSet)
|
||||
router.register(path + '/consumer', ConsumerViewSet)
|
||||
router.register(path + '/consumer', ConsumerViewSet, basename='alias2')
|
||||
|
||||
router.register(path + '/transaction/category', TemplateCategoryViewSet)
|
||||
router.register(path + '/transaction/transaction', TransactionViewSet)
|
||||
|
@ -4810,18 +4810,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"for_club": null,
|
||||
"name": "\u00c9lectron libre (avec perm)",
|
||||
"permissions": [
|
||||
22,
|
||||
84
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 17,
|
||||
@ -5093,11 +5081,6 @@
|
||||
"pk": 15,
|
||||
"fields": {}
|
||||
},
|
||||
{
|
||||
"model": "wei.weirole",
|
||||
"pk": 16,
|
||||
"fields": {}
|
||||
},
|
||||
{
|
||||
"model": "wei.weirole",
|
||||
"pk": 17,
|
||||
|
@ -1,8 +1,10 @@
|
||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from oauth2_provider.oauth2_validators import OAuth2Validator
|
||||
from oauth2_provider.scopes import BaseScopes
|
||||
from member.models import Club
|
||||
from note.models import Alias
|
||||
from note_kfet.middlewares import get_current_request
|
||||
|
||||
from .backends import PermissionBackend
|
||||
@ -16,26 +18,58 @@ class PermissionScopes(BaseScopes):
|
||||
and can be useful to make queries through the API with limited privileges.
|
||||
"""
|
||||
|
||||
def get_all_scopes(self):
|
||||
return {f"{p.id}_{club.id}": f"{p.description} (club {club.name})"
|
||||
for p in Permission.objects.all() for club in Club.objects.all()}
|
||||
def get_all_scopes(self, **kwargs):
|
||||
scopes = {}
|
||||
if 'scopes' in kwargs:
|
||||
for scope in kwargs['scopes']:
|
||||
if scope == 'openid':
|
||||
scopes['openid'] = "OpenID Connect"
|
||||
else:
|
||||
p = Permission.objects.get(id=scope.split('_')[0])
|
||||
club = Club.objects.get(id=scope.split('_')[1])
|
||||
scopes[scope] = f"{p.description} (club {club.name})"
|
||||
return scopes
|
||||
|
||||
scopes = {f"{p.id}_{club.id}": f"{p.description} (club {club.name})"
|
||||
for p in Permission.objects.all() for club in Club.objects.all()}
|
||||
scopes['openid'] = "OpenID Connect"
|
||||
return scopes
|
||||
|
||||
def get_available_scopes(self, application=None, request=None, *args, **kwargs):
|
||||
if not application:
|
||||
return []
|
||||
return [f"{p.id}_{p.membership.club.id}"
|
||||
for t in Permission.PERMISSION_TYPES
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), t[0])]
|
||||
scopes = [f"{p.id}_{p.membership.club.id}"
|
||||
for t in Permission.PERMISSION_TYPES
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), t[0])]
|
||||
scopes.append('openid')
|
||||
return scopes
|
||||
|
||||
def get_default_scopes(self, application=None, request=None, *args, **kwargs):
|
||||
if not application:
|
||||
return []
|
||||
return [f"{p.id}_{p.membership.club.id}"
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), 'view')]
|
||||
scopes = [f"{p.id}_{p.membership.club.id}"
|
||||
for p in PermissionBackend.get_raw_permissions(get_current_request(), 'view')]
|
||||
scopes.append('openid')
|
||||
return scopes
|
||||
|
||||
|
||||
class PermissionOAuth2Validator(OAuth2Validator):
|
||||
oidc_claim_scope = None # fix breaking change of django-oauth-toolkit 2.0.0
|
||||
oidc_claim_scope = OAuth2Validator.oidc_claim_scope
|
||||
oidc_claim_scope.update({"name": 'openid',
|
||||
"normalized_name": 'openid',
|
||||
"email": 'openid',
|
||||
})
|
||||
|
||||
def get_additional_claims(self, request):
|
||||
return {
|
||||
"name": request.user.username,
|
||||
"normalized_name": Alias.normalize(request.user.username),
|
||||
"email": request.user.email,
|
||||
}
|
||||
|
||||
def get_discovery_claims(self, request):
|
||||
claims = super().get_discovery_claims(self)
|
||||
return claims + ["name", "normalized_name", "email"]
|
||||
|
||||
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
|
||||
"""
|
||||
@ -54,6 +88,8 @@ class PermissionOAuth2Validator(OAuth2Validator):
|
||||
if scope in scopes:
|
||||
valid_scopes.add(scope)
|
||||
|
||||
request.scopes = valid_scopes
|
||||
if 'openid' in scopes:
|
||||
valid_scopes.add('openid')
|
||||
|
||||
request.scopes = valid_scopes
|
||||
return valid_scopes
|
||||
|
@ -13,12 +13,14 @@ EXCLUDED = [
|
||||
'cas_server.serviceticket',
|
||||
'cas_server.user',
|
||||
'cas_server.userattributes',
|
||||
'constance.constance',
|
||||
'contenttypes.contenttype',
|
||||
'logs.changelog',
|
||||
'migrations.migration',
|
||||
'oauth2_provider.accesstoken',
|
||||
'oauth2_provider.grant',
|
||||
'oauth2_provider.refreshtoken',
|
||||
'oauth2_provider.idtoken',
|
||||
'sessions.session',
|
||||
]
|
||||
|
||||
|
@ -164,14 +164,24 @@ class ScopesView(LoginRequiredMixin, TemplateView):
|
||||
from oauth2_provider.models import Application
|
||||
from .scopes import PermissionScopes
|
||||
|
||||
scopes = PermissionScopes()
|
||||
oidc = False
|
||||
context["scopes"] = {}
|
||||
all_scopes = scopes.get_all_scopes()
|
||||
for app in Application.objects.filter(user=self.request.user).all():
|
||||
available_scopes = scopes.get_available_scopes(app)
|
||||
available_scopes = PermissionScopes().get_available_scopes(app)
|
||||
context["scopes"][app] = OrderedDict()
|
||||
items = [(k, v) for (k, v) in all_scopes.items() if k in available_scopes]
|
||||
all_scopes = PermissionScopes().get_all_scopes(scopes=available_scopes)
|
||||
scopes = {}
|
||||
for scope in available_scopes:
|
||||
scopes[scope] = all_scopes[scope]
|
||||
# remove OIDC scope for sort
|
||||
if 'openid' in scopes:
|
||||
del scopes['openid']
|
||||
oidc = True
|
||||
items = [(k, v) for (k, v) in scopes.items()]
|
||||
items.sort(key=lambda x: (int(x[0].split("_")[1]), int(x[0].split("_")[0])))
|
||||
# add oidc if necessary
|
||||
if oidc:
|
||||
items.append(('openid', PermissionScopes().get_all_scopes(scopes=['openid'])['openid']))
|
||||
for k, v in items:
|
||||
context["scopes"][app][k] = v
|
||||
|
||||
|
@ -353,7 +353,7 @@ class SogeCredit(models.Model):
|
||||
def amount(self):
|
||||
if self.valid:
|
||||
return self.credit_transaction.total
|
||||
amount = sum(transaction.total for transaction in self.transactions.all())
|
||||
amount = sum(max(transaction.total - 2000, 0) for transaction in self.transactions.all())
|
||||
if 'wei' in settings.INSTALLED_APPS:
|
||||
from wei.models import WEIMembership
|
||||
if not WEIMembership.objects\
|
||||
@ -441,7 +441,7 @@ class SogeCredit(models.Model):
|
||||
With Great Power Comes Great Responsibility...
|
||||
"""
|
||||
|
||||
total_fee = sum(transaction.total for transaction in self.transactions.all() if not transaction.valid)
|
||||
total_fee = sum(max(transaction.total - 2000, 0) for transaction in self.transactions.all() if not transaction.valid)
|
||||
if self.user.note.balance < total_fee:
|
||||
raise ValidationError(_("This user doesn't have enough money to pay the memberships with its note. "
|
||||
"Please ask her/him to credit the note before invalidating this credit."))
|
||||
|
@ -77,7 +77,7 @@ class WEIRegistrationViewSet(ReadProtectedModelViewSet):
|
||||
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
|
||||
filterset_fields = ['user', 'user__username', 'user__first_name', 'user__last_name', 'user__email',
|
||||
'user__note__alias__name', 'user__note__alias__normalized_name', 'wei', 'wei__name',
|
||||
'wei__email', 'wei__year', 'soge_credit', 'caution_check', 'birth_date', 'gender',
|
||||
'wei__email', 'wei__year', 'soge_credit', 'deposit_check', 'birth_date', 'gender',
|
||||
'clothing_cut', 'clothing_size', 'first_year', 'emergency_contact_name',
|
||||
'emergency_contact_phone', ]
|
||||
search_fields = ['$user__username', '$user__first_name', '$user__last_name', '$user__email',
|
||||
|
@ -5,7 +5,7 @@ from bootstrap_datepicker_plus.widgets import DatePickerInput
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.forms import CheckboxSelectMultiple, RadioSelect
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note.models import NoteSpecial, NoteUser
|
||||
from note_kfet.inputs import AmountInput, Autocomplete, ColorWidget
|
||||
@ -24,7 +24,8 @@ class WEIForm(forms.ModelForm):
|
||||
"membership_end": DatePickerInput(),
|
||||
"date_start": DatePickerInput(),
|
||||
"date_end": DatePickerInput(),
|
||||
"caution_amount": AmountInput(),
|
||||
"deposit_amount": AmountInput(),
|
||||
"fee_soge_credit": AmountInput(),
|
||||
}
|
||||
|
||||
|
||||
@ -43,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', 'caution_check'
|
||||
'first_year', 'information_json', 'deposit_check'
|
||||
]
|
||||
widgets = {
|
||||
"user": Autocomplete(
|
||||
@ -58,7 +59,7 @@ class WEIRegistrationForm(forms.ModelForm):
|
||||
'minDate': '1900-01-01',
|
||||
'maxDate': '2100-01-01'
|
||||
}),
|
||||
"caution_check": forms.BooleanField(
|
||||
"deposit_check": forms.BooleanField(
|
||||
required=False,
|
||||
),
|
||||
}
|
||||
@ -66,10 +67,10 @@ class WEIRegistrationForm(forms.ModelForm):
|
||||
|
||||
class WEIRegistration2AForm(WEIRegistrationForm):
|
||||
class Meta(WEIRegistrationForm.Meta):
|
||||
fields = WEIRegistrationForm.Meta.fields + ['caution_type']
|
||||
fields = WEIRegistrationForm.Meta.fields + ['deposit_type']
|
||||
widgets = WEIRegistrationForm.Meta.widgets.copy()
|
||||
widgets.update({
|
||||
"caution_type": forms.RadioSelect(),
|
||||
"deposit_type": forms.RadioSelect(),
|
||||
})
|
||||
|
||||
|
||||
@ -99,7 +100,7 @@ class WEIChooseBusForm(forms.Form):
|
||||
queryset=WEIRole.objects.filter(~Q(name="1A") & ~Q(name="GC WEI")),
|
||||
label=_("WEI Roles"),
|
||||
help_text=_("Select the roles that you are interested in."),
|
||||
initial=WEIRole.objects.filter(name="Adhérent⋅e WEI").all(),
|
||||
initial=WEIRole.objects.filter(Q(name="Adhérent⋅e WEI") | Q(name="\u00c9lectron libre")).all(),
|
||||
widget=CheckboxSelectMultiple(),
|
||||
)
|
||||
|
||||
@ -140,6 +141,19 @@ class WEIMembershipForm(forms.ModelForm):
|
||||
required=False,
|
||||
)
|
||||
|
||||
def __init__(self, *args, wei=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if 'bus' in self.fields:
|
||||
if wei is not None:
|
||||
self.fields['bus'].queryset = Bus.objects.filter(wei=wei)
|
||||
else:
|
||||
self.fields['bus'].queryset = Bus.objects.none()
|
||||
if 'team' in self.fields:
|
||||
if wei is not None:
|
||||
self.fields['team'].queryset = BusTeam.objects.filter(bus__wei=wei)
|
||||
else:
|
||||
self.fields['team'].queryset = BusTeam.objects.none()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
if 'team' in cleaned_data and cleaned_data["team"] is not None \
|
||||
@ -151,21 +165,8 @@ class WEIMembershipForm(forms.ModelForm):
|
||||
model = WEIMembership
|
||||
fields = ('roles', 'bus', 'team',)
|
||||
widgets = {
|
||||
"bus": Autocomplete(
|
||||
Bus,
|
||||
attrs={
|
||||
'api_url': '/api/wei/bus/',
|
||||
'placeholder': 'Bus ...',
|
||||
}
|
||||
),
|
||||
"team": Autocomplete(
|
||||
BusTeam,
|
||||
attrs={
|
||||
'api_url': '/api/wei/team/',
|
||||
'placeholder': 'Équipe ...',
|
||||
},
|
||||
resetable=True,
|
||||
),
|
||||
"bus": RadioSelect(),
|
||||
"team": RadioSelect(),
|
||||
}
|
||||
|
||||
|
||||
@ -173,7 +174,7 @@ class WEIMembership1AForm(WEIMembershipForm):
|
||||
"""
|
||||
Used to confirm registrations of first year members without choosing a bus now.
|
||||
"""
|
||||
caution_check = None
|
||||
deposit_check = None
|
||||
roles = None
|
||||
|
||||
def clean(self):
|
||||
|
18
apps/wei/migrations/0014_weiclub_fee_soge_credit.py
Normal file
18
apps/wei/migrations/0014_weiclub_fee_soge_credit.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.23 on 2025-07-15 14:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0013_weiclub_caution_amount_weiregistration_caution_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='weiclub',
|
||||
name='fee_soge_credit',
|
||||
field=models.PositiveIntegerField(default=2000, verbose_name='fee soge credit'),
|
||||
),
|
||||
]
|
@ -0,0 +1,40 @@
|
||||
# Generated by Django 4.2.23 on 2025-07-15 16:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wei', '0014_weiclub_fee_soge_credit'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='weiclub',
|
||||
name='caution_amount',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='weiregistration',
|
||||
name='caution_check',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='weiregistration',
|
||||
name='caution_type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='weiclub',
|
||||
name='deposit_amount',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='deposit amount'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='weiregistration',
|
||||
name='deposit_check',
|
||||
field=models.BooleanField(default=False, verbose_name='Deposit check given'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='weiregistration',
|
||||
name='deposit_type',
|
||||
field=models.CharField(choices=[('check', 'Check'), ('note', 'Note transaction')], default='check', max_length=16, verbose_name='deposit type'),
|
||||
),
|
||||
]
|
@ -33,11 +33,16 @@ class WEIClub(Club):
|
||||
verbose_name=_("date end"),
|
||||
)
|
||||
|
||||
caution_amount = models.PositiveIntegerField(
|
||||
verbose_name=_("caution amount"),
|
||||
deposit_amount = models.PositiveIntegerField(
|
||||
verbose_name=_("deposit amount"),
|
||||
default=0,
|
||||
)
|
||||
|
||||
fee_soge_credit = models.PositiveIntegerField(
|
||||
verbose_name=_("membership fee (soge credit)"),
|
||||
default=2000,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("WEI")
|
||||
verbose_name_plural = _("WEI")
|
||||
@ -197,19 +202,19 @@ class WEIRegistration(models.Model):
|
||||
verbose_name=_("Credit from Société générale"),
|
||||
)
|
||||
|
||||
caution_check = models.BooleanField(
|
||||
deposit_check = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("Caution check given")
|
||||
verbose_name=_("Deposit check given")
|
||||
)
|
||||
|
||||
caution_type = models.CharField(
|
||||
deposit_type = models.CharField(
|
||||
max_length=16,
|
||||
choices=(
|
||||
('check', _("Check")),
|
||||
('note', _("Note transaction")),
|
||||
),
|
||||
default='check',
|
||||
verbose_name=_("caution type"),
|
||||
verbose_name=_("deposit type"),
|
||||
)
|
||||
|
||||
birth_date = models.DateField(
|
||||
@ -319,7 +324,8 @@ class WEIRegistration(models.Model):
|
||||
date_start__gte=bde.membership_start,
|
||||
).exists()
|
||||
|
||||
fee = self.wei.membership_fee_paid if self.user.profile.paid \
|
||||
fee = self.wei.fee_soge_credit if self.soge_credit \
|
||||
else self.wei.membership_fee_paid if self.user.profile.paid \
|
||||
else self.wei.membership_fee_unpaid
|
||||
if not kfet_member:
|
||||
fee += kfet.membership_fee_paid if self.user.profile.paid \
|
||||
|
@ -123,7 +123,7 @@ class WEIRegistrationTable(tables.Table):
|
||||
}
|
||||
model = WEIRegistration
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'caution_check',
|
||||
fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_check',
|
||||
'edit', 'validate', 'delete',)
|
||||
row_attrs = {
|
||||
'class': 'table-row',
|
||||
@ -163,7 +163,7 @@ class WEIMembershipTable(tables.Table):
|
||||
model = WEIMembership
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
fields = ('user', 'user__last_name', 'user__first_name', 'registration__gender', 'user__profile__department',
|
||||
'year', 'bus', 'team', 'registration__caution_check', )
|
||||
'year', 'bus', 'team', 'registration__deposit_check', )
|
||||
row_attrs = {
|
||||
'class': 'table-row',
|
||||
'id': lambda record: "row-" + str(record.pk),
|
||||
|
@ -49,9 +49,9 @@ 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>
|
||||
{% if club.deposit_amount > 0 %}
|
||||
<dt class="col-xl-6">{% trans 'Deposit amount'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ club.deposit_amount|pretty_money }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if "note.view_note"|has_perm:club.note %}
|
||||
|
@ -95,8 +95,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<dd class="col-xl-6"><em>{% trans "The algorithm didn't run." %}</em></dd>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<dt class="col-xl-6">{% trans 'caution check given'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ registration.caution_check|yesno }}</dd>
|
||||
<dt class="col-xl-6">{% trans 'Deposit check given'|capfirst %}</dt>
|
||||
<dd class="col-xl-6">{{ registration.deposit_check|yesno }}</dd>
|
||||
|
||||
{% with information=registration.information %}
|
||||
<dt class="col-xl-6">{% trans 'preferred bus'|capfirst %}</dt>
|
||||
@ -137,41 +137,37 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% if registration.soge_credit %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
The WEI will be paid by Société générale. The membership will be created even if the bank didn't pay the BDE yet.
|
||||
The WEI will partially be paid by Société générale. The membership will be created even if the bank didn't pay the BDE yet.
|
||||
The membership transaction will be created but will be invalid. You will have to validate it once the bank
|
||||
validated the creation of the account, or to change the payment method.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% else %}
|
||||
<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 %}
|
||||
<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.deposit_type == 'note' %}
|
||||
<li>{% blocktrans trimmed with amount=club.deposit_amount|pretty_money %}
|
||||
Deposit (by Note transaction): {{ amount }}
|
||||
{% endblocktrans %}</li>
|
||||
{% else %}
|
||||
<li>{% blocktrans trimmed with amount=club.deposit_amount|pretty_money %}
|
||||
Deposit (by check): {{ amount }}
|
||||
{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
<li><strong>{% blocktrans trimmed with total=total_needed|pretty_money %}
|
||||
Total needed: {{ total }}
|
||||
{% endblocktrans %}</strong></li>
|
||||
</ul>
|
||||
<p>{% blocktrans trimmed with balance=registration.user.note.balance|pretty_money %}
|
||||
Current balance: {{ balance }}
|
||||
{% endblocktrans %}</p>
|
||||
</div>
|
||||
|
||||
{% if not registration.caution_check and not registration.first_year and registration.caution_type == 'check' %}
|
||||
{% if not registration.deposit_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>
|
||||
@ -210,4 +206,27 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
function refreshTeams() {
|
||||
let buses = [];
|
||||
$("input[name='bus']:checked").each(function (ignored) {
|
||||
buses.push($(this).parent().text().trim());
|
||||
});
|
||||
console.log(buses);
|
||||
$("input[name='team']").each(function () {
|
||||
let label = $(this).parent();
|
||||
$(this).parent().addClass('d-none');
|
||||
buses.forEach(function (bus) {
|
||||
if (label.text().includes(bus))
|
||||
label.removeClass('d-none');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$("input[name='bus']").change(refreshTeams);
|
||||
|
||||
refreshTeams();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -101,7 +101,7 @@ class TestWEIRegistration(TestCase):
|
||||
user_id=self.user.id,
|
||||
wei_id=self.wei.id,
|
||||
soge_credit=True,
|
||||
caution_check=True,
|
||||
deposit_check=True,
|
||||
birth_date=date(2000, 1, 1),
|
||||
gender="nonbinary",
|
||||
clothing_cut="male",
|
||||
@ -121,12 +121,13 @@ class TestWEIRegistration(TestCase):
|
||||
email="gc.wei@example.com",
|
||||
membership_fee_paid=12500,
|
||||
membership_fee_unpaid=5500,
|
||||
fee_soge_credit=2000,
|
||||
membership_start=str(self.year + 1) + "-08-01",
|
||||
membership_end=str(self.year + 1) + "-09-30",
|
||||
year=self.year + 1,
|
||||
date_start=str(self.year + 1) + "-09-01",
|
||||
date_end=str(self.year + 1) + "-09-03",
|
||||
caution_amount=12000,
|
||||
deposit_amount=12000,
|
||||
))
|
||||
qs = WEIClub.objects.filter(name="Create WEI Test", year=self.year + 1)
|
||||
self.assertTrue(qs.exists())
|
||||
@ -157,11 +158,12 @@ class TestWEIRegistration(TestCase):
|
||||
email="wei-updated@example.com",
|
||||
membership_fee_paid=0,
|
||||
membership_fee_unpaid=0,
|
||||
fee_soge_credit=0,
|
||||
membership_start="2000-08-01",
|
||||
membership_end="2000-09-30",
|
||||
date_start="2000-09-01",
|
||||
date_end="2000-09-03",
|
||||
caution_amount=12000,
|
||||
deposit_amount=12000,
|
||||
))
|
||||
qs = WEIClub.objects.filter(name="Update WEI Test", id=self.wei.id)
|
||||
self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=self.wei.pk)), 302, 200)
|
||||
@ -320,7 +322,7 @@ class TestWEIRegistration(TestCase):
|
||||
bus=[],
|
||||
team=[],
|
||||
roles=[],
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(response.context["membership_form"].is_valid())
|
||||
@ -338,7 +340,7 @@ class TestWEIRegistration(TestCase):
|
||||
bus=[self.bus.id],
|
||||
team=[self.team.id],
|
||||
roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A") & ~Q(name="GC WEI")).all()],
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
))
|
||||
qs = WEIRegistration.objects.filter(user_id=user.id)
|
||||
self.assertTrue(qs.exists())
|
||||
@ -358,7 +360,7 @@ class TestWEIRegistration(TestCase):
|
||||
bus=[self.bus.id],
|
||||
team=[self.team.id],
|
||||
roles=[role.id for role in WEIRole.objects.filter(~Q(name="1A")).all()],
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue("This user is already registered to this WEI." in str(response.context["form"].errors))
|
||||
@ -511,7 +513,7 @@ class TestWEIRegistration(TestCase):
|
||||
team=[self.team.id],
|
||||
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()],
|
||||
information_json=self.registration.information_json,
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
)
|
||||
)
|
||||
qs = WEIRegistration.objects.filter(user_id=self.user.id, soge_credit=False, clothing_size="M")
|
||||
@ -566,7 +568,7 @@ class TestWEIRegistration(TestCase):
|
||||
team=[self.team.id],
|
||||
roles=[role.id for role in WEIRole.objects.filter(name="Adhérent⋅e WEI").all()],
|
||||
information_json=self.registration.information_json,
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
)
|
||||
)
|
||||
qs = WEIRegistration.objects.filter(user_id=self.user.id, clothing_size="L")
|
||||
@ -590,7 +592,7 @@ class TestWEIRegistration(TestCase):
|
||||
team=[],
|
||||
roles=[],
|
||||
information_json=self.registration.information_json,
|
||||
caution_type='check'
|
||||
deposit_type='check'
|
||||
)
|
||||
)
|
||||
self.assertFalse(response.context["membership_form"].is_valid())
|
||||
@ -640,7 +642,7 @@ class TestWEIRegistration(TestCase):
|
||||
last_name="admin",
|
||||
first_name="admin",
|
||||
bank="Société générale",
|
||||
caution_check=True,
|
||||
deposit_check=True,
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(response.context["form"].is_valid())
|
||||
@ -655,7 +657,7 @@ class TestWEIRegistration(TestCase):
|
||||
last_name="admin",
|
||||
first_name="admin",
|
||||
bank="Société générale",
|
||||
caution_check=True,
|
||||
deposit_check=True,
|
||||
))
|
||||
self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200)
|
||||
|
||||
@ -678,11 +680,7 @@ class TestWEIRegistration(TestCase):
|
||||
self.assertTrue(soge_credit.exists())
|
||||
soge_credit = soge_credit.get()
|
||||
self.assertTrue(membership.transaction in soge_credit.transactions.all())
|
||||
self.assertTrue(kfet_membership.transaction in soge_credit.transactions.all())
|
||||
self.assertTrue(bde_membership.transaction in soge_credit.transactions.all())
|
||||
self.assertFalse(membership.transaction.valid)
|
||||
self.assertFalse(kfet_membership.transaction.valid)
|
||||
self.assertFalse(bde_membership.transaction.valid)
|
||||
|
||||
# Check that if the WEI is started, we can't update a wei
|
||||
self.wei.date_start = date(2000, 1, 1)
|
||||
@ -815,7 +813,7 @@ class TestWeiAPI(TestAPI):
|
||||
user_id=self.user.id,
|
||||
wei_id=self.wei.id,
|
||||
soge_credit=True,
|
||||
caution_check=True,
|
||||
deposit_check=True,
|
||||
birth_date=date(2000, 1, 1),
|
||||
gender="nonbinary",
|
||||
clothing_cut="male",
|
||||
|
@ -27,7 +27,7 @@ from django.views.generic.edit import BaseFormView, DeleteView
|
||||
from django_tables2 import SingleTableView, MultiTableMixin
|
||||
from api.viewsets import is_regex
|
||||
from member.models import Membership, Club
|
||||
from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSpecial
|
||||
from note.models import Transaction, NoteClub, Alias, SpecialTransaction
|
||||
from note.tables import HistoryTable
|
||||
from note_kfet.settings import BASE_DIR
|
||||
from permission.backends import PermissionBackend
|
||||
@ -560,13 +560,15 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
# Cacher les champs pendant l'inscription initiale
|
||||
if "first_year" in form.fields:
|
||||
del form.fields["first_year"]
|
||||
if "caution_check" in form.fields:
|
||||
del form.fields["caution_check"]
|
||||
if "deposit_check" in form.fields:
|
||||
del form.fields["deposit_check"]
|
||||
if "information_json" in form.fields:
|
||||
del form.fields["information_json"]
|
||||
if "caution_type" in form.fields:
|
||||
del form.fields["caution_type"]
|
||||
if "deposit_type" in form.fields:
|
||||
del form.fields["deposit_type"]
|
||||
|
||||
if "soge_credit" in form.fields:
|
||||
form.fields["soge_credit"].help_text = _('Check if you will open a Société Générale account')
|
||||
return form
|
||||
|
||||
@transaction.atomic
|
||||
@ -658,6 +660,9 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["user"].initial = self.request.user
|
||||
if "soge_credit" in form.fields:
|
||||
form.fields["soge_credit"].help_text = _('Check if you will open a Société Générale account')
|
||||
|
||||
if "myself" in self.request.path and self.request.user.profile.soge:
|
||||
form.fields["soge_credit"].disabled = True
|
||||
form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.")
|
||||
@ -665,16 +670,16 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
# Cacher les champs pendant l'inscription initiale
|
||||
if "first_year" in form.fields:
|
||||
del form.fields["first_year"]
|
||||
if "caution_check" in form.fields:
|
||||
del form.fields["caution_check"]
|
||||
if "deposit_check" in form.fields:
|
||||
del form.fields["deposit_check"]
|
||||
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)
|
||||
# S'assurer que le champ deposit_type est obligatoire
|
||||
if "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
|
||||
|
||||
@ -703,7 +708,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
form.instance.information = information
|
||||
|
||||
# Sauvegarder le type de caution
|
||||
form.instance.caution_type = form.cleaned_data["caution_type"]
|
||||
form.instance.deposit_type = form.cleaned_data["deposit_type"]
|
||||
form.instance.save()
|
||||
|
||||
if 'treasury' in settings.INSTALLED_APPS:
|
||||
@ -773,22 +778,23 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
form = super().get_form(form_class)
|
||||
form.fields["user"].disabled = True
|
||||
# The auto-json-format may cause issues with the default field remove
|
||||
if not PermissionBackend.check_perm(self.request, 'wei.change_weiregistration_information_json', self.object):
|
||||
if "information_json" in form.fields:
|
||||
del form.fields["information_json"]
|
||||
# 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"]
|
||||
# Masquer le champ deposit_check pour tout le monde dans le formulaire de modification
|
||||
if "deposit_check" in form.fields:
|
||||
del form.fields["deposit_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)
|
||||
# S'assurer que le champ deposit_type est obligatoire pour les 2A+
|
||||
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
|
||||
|
||||
def get_membership_form(self, data=None, instance=None):
|
||||
membership_form = WEIMembershipForm(data if data else None, instance=instance)
|
||||
registration = self.get_object()
|
||||
membership_form = WEIMembershipForm(data if data else None, instance=instance, wei=registration.wei)
|
||||
del membership_form.fields["credit_type"]
|
||||
del membership_form.fields["credit_amount"]
|
||||
del membership_form.fields["first_name"]
|
||||
@ -844,8 +850,8 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
||||
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"]
|
||||
if "deposit_type" in form.cleaned_data:
|
||||
form.instance.deposit_type = form.cleaned_data["deposit_type"]
|
||||
form.instance.save()
|
||||
|
||||
return super().form_valid(form)
|
||||
@ -951,8 +957,8 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
|
||||
# Calculer le montant total nécessaire (frais + caution si transaction)
|
||||
total_needed = fee
|
||||
if registration.caution_type == 'note':
|
||||
total_needed += registration.wei.caution_amount
|
||||
if registration.deposit_type == 'note':
|
||||
total_needed += registration.wei.deposit_amount
|
||||
context["total_needed"] = total_needed
|
||||
|
||||
form = context["form"]
|
||||
@ -969,40 +975,38 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
return WEIMembership1AForm
|
||||
return WEIMembershipForm
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
|
||||
wei = registration.wei
|
||||
kwargs['wei'] = wei
|
||||
return kwargs
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
|
||||
form.fields["last_name"].initial = registration.user.last_name
|
||||
form.fields["first_name"].initial = registration.user.first_name
|
||||
|
||||
# Ajouter le champ caution_check uniquement pour les non-première année et le rendre obligatoire
|
||||
# Ajouter le champ deposit_check uniquement pour les non-première année et le rendre obligatoire
|
||||
if not registration.first_year:
|
||||
if registration.caution_type == 'check':
|
||||
form.fields["caution_check"] = forms.BooleanField(
|
||||
if registration.deposit_type == 'check':
|
||||
form.fields["deposit_check"] = forms.BooleanField(
|
||||
required=True,
|
||||
initial=registration.caution_check,
|
||||
label=_("Caution check given"),
|
||||
initial=registration.deposit_check,
|
||||
label=_("Deposit check given"),
|
||||
help_text=_("Please make sure the check is given before validating the registration")
|
||||
)
|
||||
else:
|
||||
form.fields["caution_check"] = forms.BooleanField(
|
||||
form.fields["deposit_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
|
||||
'amount': registration.wei.deposit_amount / 100
|
||||
}
|
||||
)
|
||||
|
||||
if registration.soge_credit:
|
||||
form.fields["credit_type"].disabled = True
|
||||
form.fields["credit_type"].initial = NoteSpecial.objects.get(special_type="Virement bancaire")
|
||||
form.fields["credit_amount"].disabled = True
|
||||
form.fields["last_name"].disabled = True
|
||||
form.fields["first_name"].disabled = True
|
||||
form.fields["bank"].disabled = True
|
||||
form.fields["bank"].initial = "Société générale"
|
||||
|
||||
if 'bus' in form.fields:
|
||||
# For 2A+ and hardcoded 1A
|
||||
form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk)
|
||||
@ -1035,8 +1039,8 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
club = registration.wei
|
||||
user = registration.user
|
||||
|
||||
if "caution_check" in form.data:
|
||||
registration.caution_check = form.data["caution_check"] == "on"
|
||||
if "deposit_check" in form.data:
|
||||
registration.deposit_check = form.data["deposit_check"] == "on"
|
||||
registration.save()
|
||||
membership = form.instance
|
||||
membership.user = user
|
||||
@ -1047,6 +1051,8 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
membership._force_renew_parent = True
|
||||
|
||||
fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
|
||||
if registration.soge_credit:
|
||||
fee = 2000
|
||||
|
||||
kfet = club.parent_club
|
||||
bde = kfet.parent_club
|
||||
@ -1073,16 +1079,16 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
first_name = form.cleaned_data["first_name"]
|
||||
bank = form.cleaned_data["bank"]
|
||||
|
||||
if credit_type is None or registration.soge_credit:
|
||||
if credit_type is None:
|
||||
credit_amount = 0
|
||||
|
||||
# Calculer le montant total nécessaire (frais + caution si transaction)
|
||||
total_needed = fee
|
||||
if registration.caution_type == 'note':
|
||||
total_needed += club.caution_amount
|
||||
if registration.deposit_type == 'note':
|
||||
total_needed += club.deposit_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:
|
||||
if user.note.balance + credit_amount < total_needed:
|
||||
form.add_error('credit_type',
|
||||
_("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€") % {
|
||||
@ -1130,14 +1136,14 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
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':
|
||||
if registration.deposit_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},
|
||||
amount=club.deposit_amount,
|
||||
reason=_("Deposit %(name)s") % {'name': club.name},
|
||||
valid=True,
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user