mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	| @@ -230,6 +230,7 @@ class Membership(models.Model): | ||||
|     ) | ||||
|  | ||||
|     date_start = models.DateField( | ||||
|         default=datetime.date.today, | ||||
|         verbose_name=_('membership starts on'), | ||||
|     ) | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ from polymorphic.admin import PolymorphicChildModelAdmin, \ | ||||
|  | ||||
| from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser | ||||
| from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \ | ||||
|     RecurrentTransaction, MembershipTransaction | ||||
|     RecurrentTransaction, MembershipTransaction, SpecialTransaction | ||||
|  | ||||
|  | ||||
| class AliasInlines(admin.TabularInline): | ||||
| @@ -102,7 +102,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin): | ||||
|     """ | ||||
|     Admin customisation for Transaction | ||||
|     """ | ||||
|     child_models = (RecurrentTransaction, MembershipTransaction) | ||||
|     child_models = (RecurrentTransaction, MembershipTransaction, SpecialTransaction) | ||||
|     list_display = ('created_at', 'poly_source', 'poly_destination', | ||||
|                     'quantity', 'amount', 'valid') | ||||
|     list_filter = ('valid',) | ||||
| @@ -141,7 +141,14 @@ class TransactionAdmin(PolymorphicParentModelAdmin): | ||||
| @admin.register(MembershipTransaction) | ||||
| class MembershipTransactionAdmin(PolymorphicChildModelAdmin): | ||||
|     """ | ||||
|     Admin customisation for Transaction | ||||
|     Admin customisation for MembershipTransaction | ||||
|     """ | ||||
|  | ||||
|  | ||||
| @admin.register(SpecialTransaction) | ||||
| class SpecialTransactionAdmin(PolymorphicChildModelAdmin): | ||||
|     """ | ||||
|     Admin customisation for SpecialTransaction | ||||
|     """ | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| from django import forms | ||||
| from django.contrib.auth.forms import UserCreationForm | ||||
| from django.contrib.auth.models import User | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| from note.models import NoteSpecial | ||||
| from note_kfet.inputs import AmountInput | ||||
|  | ||||
|  | ||||
| class SignUpForm(UserCreationForm): | ||||
| @@ -19,3 +22,46 @@ class SignUpForm(UserCreationForm): | ||||
|     class Meta: | ||||
|         model = User | ||||
|         fields = ('first_name', 'last_name', 'username', 'email', ) | ||||
|  | ||||
|  | ||||
| class ValidationForm(forms.Form): | ||||
|     credit_type = forms.ModelChoiceField( | ||||
|         queryset=NoteSpecial.objects, | ||||
|         label=_("Credit type"), | ||||
|         empty_label=_("No credit"), | ||||
|         required=False, | ||||
|     ) | ||||
|  | ||||
|     credit_amount = forms.IntegerField( | ||||
|         label=_("Credit amount"), | ||||
|         required=False, | ||||
|         initial=0, | ||||
|         widget=AmountInput(), | ||||
|     ) | ||||
|  | ||||
|     last_name = forms.CharField( | ||||
|         label=_("Last name"), | ||||
|         required=False, | ||||
|     ) | ||||
|  | ||||
|     first_name = forms.CharField( | ||||
|         label=_("First name"), | ||||
|         required=False, | ||||
|     ) | ||||
|  | ||||
|     bank = forms.CharField( | ||||
|         label=_("Bank"), | ||||
|         required=False, | ||||
|     ) | ||||
|  | ||||
|     join_BDE = forms.BooleanField( | ||||
|         label=_("Join BDE"), | ||||
|         required=False, | ||||
|         initial=True, | ||||
|     ) | ||||
|  | ||||
|     join_Kfet = forms.BooleanField( | ||||
|         label=_("Join Kfet"), | ||||
|         required=False, | ||||
|         initial=True, | ||||
|     ) | ||||
|   | ||||
| @@ -12,14 +12,16 @@ from django.utils.http import urlsafe_base64_decode | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| from django.views import View | ||||
| from django.views.decorators.csrf import csrf_protect | ||||
| from django.views.generic import CreateView, TemplateView, DetailView | ||||
| from django.views.generic import CreateView, TemplateView, DetailView, FormView | ||||
| from django_tables2 import SingleTableView | ||||
| from member.forms import ProfileForm | ||||
| from member.models import Profile | ||||
| from member.models import Membership, Club | ||||
| from note.models import SpecialTransaction | ||||
| from note.templatetags.pretty_money import pretty_money | ||||
| from permission.backends import PermissionBackend | ||||
| from permission.views import ProtectQuerysetMixin | ||||
|  | ||||
| from .forms import SignUpForm | ||||
| from .forms import SignUpForm, ValidationForm | ||||
| from .tables import FutureUserTable | ||||
| from .tokens import account_activation_token | ||||
|  | ||||
| @@ -138,11 +140,12 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi | ||||
|         return context | ||||
|  | ||||
|  | ||||
| class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): | ||||
| class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView): | ||||
|     """ | ||||
|     Affiche les informations sur un utilisateur, sa note, ses clubs... | ||||
|     """ | ||||
|     model = User | ||||
|     form_class = ValidationForm | ||||
|     context_object_name = "user_object" | ||||
|     template_name = "registration/future_profile_detail.html" | ||||
|  | ||||
| @@ -152,6 +155,92 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView) | ||||
|         """ | ||||
|         return super().get_queryset().filter(profile__registration_valid=False) | ||||
|  | ||||
|     def get_form(self, form_class=None): | ||||
|         form = super().get_form(form_class) | ||||
|         user = self.get_object() | ||||
|         form.fields["last_name"].initial = user.last_name | ||||
|         form.fields["first_name"].initial = user.first_name | ||||
|         return form | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         user = self.object = self.get_object() | ||||
|  | ||||
|         print(form.cleaned_data) | ||||
|         credit_type = form.cleaned_data["credit_type"] | ||||
|         credit_amount = form.cleaned_data["credit_amount"] | ||||
|         last_name = form.cleaned_data["last_name"] | ||||
|         first_name = form.cleaned_data["first_name"] | ||||
|         bank = form.cleaned_data["bank"] | ||||
|         join_BDE = form.cleaned_data["join_BDE"] | ||||
|         join_Kfet = form.cleaned_data["join_Kfet"] | ||||
|  | ||||
|         fee = 0 | ||||
|         bde = Club.objects.get(name="BDE") | ||||
|         bde_fee = bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid | ||||
|         if join_BDE: | ||||
|             fee += bde_fee | ||||
|         kfet = Club.objects.get(name="Kfet") | ||||
|         kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid | ||||
|         if join_Kfet: | ||||
|             fee += kfet_fee | ||||
|  | ||||
|         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: | ||||
|             form.add_error('credit_type', | ||||
|                            _("The entered amount is not enough for the memberships, should be at least {}") | ||||
|                            .format(pretty_money(fee))) | ||||
|             return self.form_invalid(form) | ||||
|  | ||||
|         if credit_type is not None and credit_amount > 0: | ||||
|             if not last_name or not first_name or not bank: | ||||
|                 if not last_name: | ||||
|                     form.add_error('last_name', _("This field is required.")) | ||||
|                 if not first_name: | ||||
|                     form.add_error('first_name', _("This field is required.")) | ||||
|                 if not bank: | ||||
|                     form.add_error('bank', _("This field is required.")) | ||||
|                 return self.form_invalid(form) | ||||
|  | ||||
|         ret = super().form_valid(form) | ||||
|         user.is_active = True | ||||
|         user.profile.registration_valid = True | ||||
|         user.save() | ||||
|         user.profile.save() | ||||
|  | ||||
|         if credit_type is not None and credit_amount > 0: | ||||
|             SpecialTransaction.objects.create( | ||||
|                 source=credit_type, | ||||
|                 destination=user.note, | ||||
|                 quantity=1, | ||||
|                 amount=credit_amount, | ||||
|                 reason="Crédit " + credit_type.special_type + " (Inscription)", | ||||
|                 last_name=last_name, | ||||
|                 first_name=first_name, | ||||
|                 bank=bank, | ||||
|                 valid=True, | ||||
|             ) | ||||
|  | ||||
|         if join_BDE: | ||||
|             Membership.objects.create( | ||||
|                 club=bde, | ||||
|                 user=user, | ||||
|                 fee=bde_fee, | ||||
|             ) | ||||
|  | ||||
|         if join_Kfet: | ||||
|             Membership.objects.create( | ||||
|                 club=kfet, | ||||
|                 user=user, | ||||
|                 fee=kfet_fee, | ||||
|             ) | ||||
|  | ||||
|         return ret | ||||
|  | ||||
|     def get_success_url(self): | ||||
|         return reverse_lazy('member:user_detail', args=(self.get_object().pk, )) | ||||
|  | ||||
|  | ||||
| class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View): | ||||
|     """ | ||||
|   | ||||
| @@ -13,7 +13,7 @@ class AmountInput(NumberInput): | ||||
|     template_name = "note/amount_input.html" | ||||
|  | ||||
|     def format_value(self, value): | ||||
|         return None if value is None or value == "" else "{:.02f}".format(value / 100, ) | ||||
|         return None if value is None or value == "" else "{:.02f}".format(int(value) / 100, ) | ||||
|  | ||||
|     def value_from_datadict(self, data, files, name): | ||||
|         val = super().value_from_datadict(data, files, name) | ||||
|   | ||||
| @@ -1,54 +1,65 @@ | ||||
| {% extends "base.html" %} | ||||
| {% load static %} | ||||
| {% load i18n %} | ||||
| {% load render_table from django_tables2 %} | ||||
| {% load crispy_forms_tags %} | ||||
| {% load pretty_money %} | ||||
|  | ||||
| {% block content %} | ||||
|     <div class="card bg-light shadow"> | ||||
|         <div class="card-header text-center" > | ||||
|             <h4> {% trans "Account #" %}  {{ object.pk }}</h4> | ||||
|         </div> | ||||
|         <div class="card-body" id="profile_infos"> | ||||
|             <dl class="row"> | ||||
|                 <dt class="col-xl-6">{% trans 'name'|capfirst %}, {% trans 'first name' %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.last_name }} {{ object.first_name }}</dd> | ||||
|  | ||||
| <div class="card bg-light shadow"> | ||||
|     <div class="card-header text-center" > | ||||
|         <h4> {% trans "Account #" %}  {{ object.pk }}</h4> | ||||
|                 <dt class="col-xl-6">{% trans 'username'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.username }}</dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'email'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6"><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'password'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6"> | ||||
|                     <a class="small" href="{% url 'password_change' %}"> | ||||
|                         {% trans 'Change password' %} | ||||
|                     </a> | ||||
|                 </dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'section'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.profile.section }}</dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'address'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.profile.address }}</dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.profile.phone_number }}</dd> | ||||
|  | ||||
|                 <dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt> | ||||
|                 <dd class="col-xl-6">{{ object.profile.paid|yesno }}</dd> | ||||
|             </dl> | ||||
|         </div> | ||||
|         <div class="card-footer text-center"> | ||||
|             <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' object.pk %}">{% trans 'Update Profile' %}</a> | ||||
|             <a class="btn btn-danger btn-sm" href="{% url 'registration:future_user_invalidate' object.pk %}">{% trans 'Delete registration' %}</a> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="card-body" id="profile_infos"> | ||||
|         <dl class="row"> | ||||
|             <dt class="col-xl-6">{% trans 'name'|capfirst %}, {% trans 'first name' %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.last_name }} {{ object.first_name }}</dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'username'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.username }}</dd> | ||||
|     <hr> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'email'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6"><a href="mailto:{{ object.email }}">{{ object.email }}</a></dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'password'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6"> | ||||
|                 <a class="small" href="{% url 'password_change' %}"> | ||||
|                     {% trans 'Change password' %} | ||||
|                 </a> | ||||
|             </dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'section'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.profile.section }}</dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'address'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.profile.address }}</dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.profile.phone_number }}</dd> | ||||
|  | ||||
|             <dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt> | ||||
|             <dd class="col-xl-6">{{ object.profile.paid|yesno }}</dd> | ||||
|         </dl> | ||||
|  | ||||
|         {% if object.pk == user.pk %} | ||||
|         <a class="small" href="{% url 'member:auth_token' %}">{% trans 'Manage auth token' %}</a> | ||||
|         {% endif %} | ||||
|     <div class="card bg-light shadow"> | ||||
|         <form method="post"> | ||||
|             <div class="card-header text-center" > | ||||
|                 <h4> {% trans "Validate account" %}</h4> | ||||
|             </div> | ||||
|             <div class="card-body" id="profile_infos"> | ||||
|                 {% csrf_token %} | ||||
|                 {{ form|crispy }} | ||||
|             </div> | ||||
|             <div class="card-footer text-center"> | ||||
|                 <button class="btn btn-success btn-sm">{% trans 'Validate registration' %}</button> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
|     <div class="card-footer text-center"> | ||||
|         <a class="btn btn-primary btn-sm" href="{% url 'member:user_update_profile' object.pk %}">{% trans 'Update Profile' %}</a> | ||||
|         <a class="btn btn-danger btn-sm" href="{% url 'registration:future_user_invalidate' object.pk %}">{% trans 'Delete registration' %}</a> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| {% endblock %} | ||||
		Reference in New Issue
	
	Block a user