From 881cd88f48367cae5e6365d7331f23df49bc72c1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 20:10:21 +0200 Subject: [PATCH 01/24] [WEI] Fix permission check for information json Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index cb8e3646..b3d2c3a4 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -704,7 +704,8 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update def get_form(self, form_class=None): form = super().get_form(form_class) form.fields["user"].disabled = True - if not self.object.first_year: + # 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): del form.fields["information_json"] return form From ba9ef0371a9aa4cb2b76df36fa392eb01d8e5551 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 20:36:17 +0200 Subject: [PATCH 02/24] [WEI] Run algorithm only on valid surveys Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/surveys/wei2021.py | 1 + apps/wei/management/commands/wei_algorithm.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/wei/forms/surveys/wei2021.py b/apps/wei/forms/surveys/wei2021.py index b0cfb0cb..a6a241cb 100644 --- a/apps/wei/forms/surveys/wei2021.py +++ b/apps/wei/forms/surveys/wei2021.py @@ -170,6 +170,7 @@ class WEISurveyAlgorithm2021(WEISurveyAlgorithm): We modify it to allow buses to have multiple "weddings". """ surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys + surveys = [s for s in surveys if s.is_complete()] free_surveys = [s for s in surveys if not s.information.valid] # Remaining surveys while free_surveys: # Some students are not affected survey = free_surveys[0] diff --git a/apps/wei/management/commands/wei_algorithm.py b/apps/wei/management/commands/wei_algorithm.py index 558dfae4..238bf13c 100644 --- a/apps/wei/management/commands/wei_algorithm.py +++ b/apps/wei/management/commands/wei_algorithm.py @@ -28,7 +28,8 @@ class Command(BaseCommand): output = options['output'] registrations = algorithm.get_registrations() - per_bus = {bus: [r for r in registrations if r.information['selected_bus_pk'] == bus.pk] + per_bus = {bus: [r for r in registrations if 'selected_bus_pk' in r.information + and r.information['selected_bus_pk'] == bus.pk] for bus in algorithm.get_buses()} for bus, members in per_bus.items(): output.write(bus.name + "\n") From b646f549d631e2b130b85b922310a36ab2e9c120 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Sep 2021 21:24:16 +0200 Subject: [PATCH 03/24] =?UTF-8?q?When=20creating=20a=20Sog=C3=A9=20credit,?= =?UTF-8?q?=20serch=20existing=20recent=20memberships=20and=20register=20t?= =?UTF-8?q?hem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/treasury/models.py | 37 +++++++++++++++++++++++++++++++++++++ apps/wei/models.py | 5 ++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 0b5948fd..39fb8523 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -11,7 +11,10 @@ from django.db.models import Q from django.template.loader import render_to_string from django.utils import timezone from django.utils.translation import gettext_lazy as _ + +from member.models import Club, Membership from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser +from wei.models import WEIClub class Invoice(models.Model): @@ -305,6 +308,40 @@ class SogeCredit(models.Model): return self.credit_transaction.total if self.valid \ else sum(transaction.total for transaction in self.transactions.all()) + def update_transactions(self): + """ + The Sogé credit may be created after the user already paid its memberships. + We query transactions and update the credit, if it is unvalid. + """ + if self.valid: + return + + bde = Club.objects.get(name="BDE") + kfet = Club.objects.get(name="Kfet") + wei = WEIClub.objects.order_by('-year').first() + bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start) + kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start) + wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start) + + if bde_qs.exists(): + m = bde_qs.get() + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) + + if kfet_qs.exists(): + m = kfet_qs.get() + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) + + if wei_qs.exists(): + m = wei_qs.get() + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) + + for tr in self.transactions.all(): + tr.valid = False + tr.save() + def invalidate(self): """ Invalidating a Société générale delete the transaction of the bank if it was already created. diff --git a/apps/wei/models.py b/apps/wei/models.py index b59a0dfd..9e99cd1c 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -364,8 +364,11 @@ class WEIMembership(Membership): # to treasurers. transaction.refresh_from_db() from treasury.models import SogeCredit - soge_credit = SogeCredit.objects.get_or_create(user=self.user)[0] + soge_credit, created = SogeCredit.objects.get_or_create(user=self.user) soge_credit.refresh_from_db() transaction.save() soge_credit.transactions.add(transaction) soge_credit.save() + + soge_credit.update_transactions() + soge_credit.save() From 4e1ba1447a37138d8caa2f2107d7cd3e0b0ecb24 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Sep 2021 00:47:11 +0200 Subject: [PATCH 04/24] =?UTF-8?q?Add=20option=20to=20add=20a=20posteriori?= =?UTF-8?q?=20a=20Sog=C3=A9=20credit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/treasury/api/serializers.py | 10 +- apps/treasury/forms.py | 21 +- apps/treasury/models.py | 6 +- .../templates/treasury/sogecredit_list.html | 75 ++++-- apps/treasury/views.py | 8 +- locale/fr/LC_MESSAGES/django.po | 219 +++++++++--------- 6 files changed, 213 insertions(+), 126 deletions(-) diff --git a/apps/treasury/api/serializers.py b/apps/treasury/api/serializers.py index bc15db88..5442fd06 100644 --- a/apps/treasury/api/serializers.py +++ b/apps/treasury/api/serializers.py @@ -1,6 +1,6 @@ # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later - +from django.db import transaction from rest_framework import serializers from note.api.serializers import SpecialTransactionSerializer @@ -68,6 +68,14 @@ class SogeCreditSerializer(serializers.ModelSerializer): The djangorestframework plugin will analyse the model `SogeCredit` and parse all fields in the API. """ + @transaction.atomic + def save(self, **kwargs): + # Update soge transactions after creating a credit + instance = super().save(**kwargs) + instance.update_transactions() + instance.save() + return instance + class Meta: model = SogeCredit fields = '__all__' diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py index 6c5bc353..02441189 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -4,11 +4,12 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit from django import forms +from django.contrib.auth.models import User from django.db import transaction from django.utils.translation import gettext_lazy as _ -from note_kfet.inputs import AmountInput +from note_kfet.inputs import AmountInput, Autocomplete -from .models import Invoice, Product, Remittance, SpecialTransactionProxy +from .models import Invoice, Product, Remittance, SpecialTransactionProxy, SogeCredit class InvoiceForm(forms.ModelForm): @@ -161,3 +162,19 @@ class LinkTransactionToRemittanceForm(forms.ModelForm): class Meta: model = SpecialTransactionProxy fields = ('remittance', ) + + +class SogeCreditForm(forms.ModelForm): + class Meta: + model = SogeCredit + fields = ('user', ) + widgets = { + "user": Autocomplete( + User, + attrs={ + 'api_url': '/api/user/', + 'name_field': 'username', + 'placeholder': 'Nom ...', + }, + ), + } diff --git a/apps/treasury/models.py b/apps/treasury/models.py index 39fb8523..c1089bb8 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -289,6 +289,7 @@ class SogeCredit(models.Model): transactions = models.ManyToManyField( MembershipTransaction, related_name="+", + blank=True, verbose_name=_("membership transactions"), ) @@ -313,7 +314,7 @@ class SogeCredit(models.Model): The Sogé credit may be created after the user already paid its memberships. We query transactions and update the credit, if it is unvalid. """ - if self.valid: + if self.valid or not self.pk: return bde = Club.objects.get(name="BDE") @@ -402,7 +403,8 @@ class SogeCredit(models.Model): self.credit_transaction.amount = self.amount self.credit_transaction._force_save = True self.credit_transaction.save() - super().save(*args, **kwargs) + + return super().save(*args, **kwargs) def delete(self, **kwargs): """ diff --git a/apps/treasury/templates/treasury/sogecredit_list.html b/apps/treasury/templates/treasury/sogecredit_list.html index 2bcf3155..4aecb8eb 100644 --- a/apps/treasury/templates/treasury/sogecredit_list.html +++ b/apps/treasury/templates/treasury/sogecredit_list.html @@ -3,6 +3,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} {% load render_table from django_tables2 %} +{% load crispy_forms_filters %} {% load i18n %} {% block content %} @@ -27,7 +28,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ title }}
- +
+ +
+ +
+
+ +{# Popup to add new Soge credits manually if needed #} + {% endblock %} {% block extrajavascript %} {% endblock %} \ No newline at end of file diff --git a/apps/treasury/views.py b/apps/treasury/views.py index b9a7fe7c..aee6ea04 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -25,7 +25,8 @@ from note_kfet.settings.base import BASE_DIR from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin, ProtectedCreateView -from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm +from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, \ + LinkTransactionToRemittanceForm, SogeCreditForm from .models import Invoice, Product, Remittance, SpecialTransactionProxy, SogeCredit from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable, SogeCreditTable @@ -433,6 +434,11 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi return qs + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['form'] = SogeCreditForm(self.request.POST or None) + return context + class SogeCreditManageView(LoginRequiredMixin, ProtectQuerysetMixin, BaseFormView, DetailView): """ diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 80ab332a..ff9f2541 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-06-15 21:17+0200\n" +"POT-Creation-Date: 2021-09-06 00:45+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: French \n" @@ -111,7 +111,7 @@ msgid "type" msgstr "type" #: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:305 -#: apps/note/models/notes.py:148 apps/treasury/models.py:283 +#: apps/note/models/notes.py:148 apps/treasury/models.py:286 #: apps/wei/models.py:165 apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "utilisateur" @@ -251,19 +251,19 @@ msgstr "Entré le " msgid "remove" msgstr "supprimer" -#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:197 +#: apps/activity/tables.py:80 apps/note/forms.py:68 apps/treasury/models.py:200 msgid "Type" msgstr "Type" #: apps/activity/tables.py:82 apps/member/forms.py:186 -#: apps/registration/forms.py:90 apps/treasury/forms.py:130 +#: apps/registration/forms.py:90 apps/treasury/forms.py:131 #: apps/wei/forms/registration.py:96 msgid "Last name" msgstr "Nom de famille" #: apps/activity/tables.py:84 apps/member/forms.py:191 #: apps/note/templates/note/transaction_form.html:134 -#: apps/registration/forms.py:95 apps/treasury/forms.py:132 +#: apps/registration/forms.py:95 apps/treasury/forms.py:133 #: apps/wei/forms/registration.py:101 msgid "First name" msgstr "Prénom" @@ -327,7 +327,7 @@ msgstr "Entrée effectuée !" #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 -#: apps/treasury/forms.py:88 apps/treasury/forms.py:142 +#: apps/treasury/forms.py:89 apps/treasury/forms.py:143 #: apps/treasury/templates/treasury/invoice_form.html:74 #: apps/wei/templates/wei/bus_form.html:17 #: apps/wei/templates/wei/busteam_form.html:17 @@ -540,8 +540,8 @@ msgstr "Taille maximale : 2 Mo" msgid "This image cannot be loaded." msgstr "Cette image ne peut pas être chargée." -#: apps/member/forms.py:141 apps/member/views.py:100 -#: apps/registration/forms.py:33 apps/registration/views.py:254 +#: apps/member/forms.py:141 apps/member/views.py:102 +#: apps/registration/forms.py:33 apps/registration/views.py:259 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." @@ -573,7 +573,7 @@ msgid "Credit amount" msgstr "Montant à créditer" #: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140 -#: apps/registration/forms.py:100 apps/treasury/forms.py:134 +#: apps/registration/forms.py:100 apps/treasury/forms.py:135 #: apps/wei/forms/registration.py:106 msgid "Bank" msgstr "Banque" @@ -835,7 +835,7 @@ msgstr "Le rôle {role} ne s'applique pas au club {club}." msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:443 apps/member/views.py:661 +#: apps/member/models.py:443 apps/member/views.py:660 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" @@ -944,7 +944,8 @@ msgstr "" "déverrouiller lui-même." #: apps/member/templates/member/base.html:110 -#: apps/member/templates/member/base.html:137 apps/treasury/forms.py:90 +#: apps/member/templates/member/base.html:137 apps/treasury/forms.py:91 +#: apps/treasury/templates/treasury/sogecredit_list.html:72 msgid "Close" msgstr "Fermer" @@ -968,6 +969,8 @@ msgstr "Alias de la note" #: apps/member/templates/member/club_alias.html:20 #: apps/member/templates/member/profile_alias.html:19 #: apps/treasury/tables.py:99 +#: apps/treasury/templates/treasury/sogecredit_list.html:34 +#: apps/treasury/templates/treasury/sogecredit_list.html:73 msgid "Add" msgstr "Ajouter" @@ -1133,7 +1136,7 @@ msgstr "Inscriptions" msgid "This address must be valid." msgstr "Cette adresse doit être valide." -#: apps/member/views.py:138 +#: apps/member/views.py:139 msgid "Profile detail" msgstr "Détails de l'utilisateur" @@ -1169,7 +1172,7 @@ msgstr "Modifier le club" msgid "Add new member to the club" msgstr "Ajouter un nouveau membre au club" -#: apps/member/views.py:642 apps/wei/views.py:917 +#: apps/member/views.py:642 apps/wei/views.py:932 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1177,19 +1180,19 @@ msgstr "" "Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas " "avoir un solde négatif." -#: apps/member/views.py:665 +#: apps/member/views.py:664 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:670 +#: apps/member/views.py:669 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:816 +#: apps/member/views.py:815 msgid "Manage roles of an user in the club" msgstr "Gérer les rôles d'un utilisateur dans le club" -#: apps/member/views.py:841 +#: apps/member/views.py:840 msgid "Members of the club" msgstr "Membres du club" @@ -1475,8 +1478,8 @@ msgstr "" "mode de paiement et un utilisateur ou un club" #: apps/note/models/transactions.py:355 apps/note/models/transactions.py:358 -#: apps/note/models/transactions.py:361 apps/wei/views.py:922 -#: apps/wei/views.py:926 +#: apps/note/models/transactions.py:361 apps/wei/views.py:937 +#: apps/wei/views.py:941 msgid "This field is required." msgstr "Ce champ est requis." @@ -1492,7 +1495,7 @@ msgstr "Transactions de crédit/retrait" msgid "membership transaction" msgstr "transaction d'adhésion" -#: apps/note/models/transactions.py:385 apps/treasury/models.py:289 +#: apps/note/models/transactions.py:385 apps/treasury/models.py:293 msgid "membership transactions" msgstr "transactions d'adhésion" @@ -1599,14 +1602,14 @@ msgid "Action" msgstr "Action" #: apps/note/templates/note/transaction_form.html:112 -#: apps/treasury/forms.py:136 apps/treasury/tables.py:67 +#: apps/treasury/forms.py:137 apps/treasury/tables.py:67 #: apps/treasury/tables.py:132 #: apps/treasury/templates/treasury/remittance_form.html:23 msgid "Amount" msgstr "Montant" #: apps/note/templates/note/transaction_form.html:128 -#: apps/treasury/models.py:52 +#: apps/treasury/models.py:55 msgid "Name" msgstr "Nom" @@ -2064,18 +2067,18 @@ msgstr "Utilisateurs en attente d'inscription" msgid "Registration detail" msgstr "Détails de l'inscription" -#: apps/registration/views.py:278 +#: apps/registration/views.py:279 msgid "You must join the BDE." msgstr "Vous devez adhérer au BDE." -#: apps/registration/views.py:302 +#: apps/registration/views.py:303 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Le montant crédité est trop faible pour adhérer, il doit être au minimum de " "{}" -#: apps/registration/views.py:383 +#: apps/registration/views.py:384 msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" @@ -2083,145 +2086,145 @@ msgstr "Invalider l'inscription" msgid "Treasury" msgstr "Trésorerie" -#: apps/treasury/forms.py:25 apps/treasury/models.py:91 +#: apps/treasury/forms.py:26 apps/treasury/models.py:94 #: apps/treasury/templates/treasury/invoice_form.html:22 msgid "This invoice is locked and can no longer be edited." msgstr "Cette facture est verrouillée et ne peut plus être éditée." -#: apps/treasury/forms.py:99 +#: apps/treasury/forms.py:100 msgid "Remittance is already closed." msgstr "La remise est déjà fermée." -#: apps/treasury/forms.py:104 +#: apps/treasury/forms.py:105 msgid "You can't change the type of the remittance." msgstr "Vous ne pouvez pas changer le type de la remise." -#: apps/treasury/forms.py:124 apps/treasury/models.py:265 +#: apps/treasury/forms.py:125 apps/treasury/models.py:268 #: apps/treasury/tables.py:97 apps/treasury/tables.py:105 #: apps/treasury/templates/treasury/invoice_list.html:16 #: apps/treasury/templates/treasury/remittance_list.html:16 -#: apps/treasury/templates/treasury/sogecredit_list.html:16 +#: apps/treasury/templates/treasury/sogecredit_list.html:17 msgid "Remittance" msgstr "Remise" -#: apps/treasury/forms.py:125 +#: apps/treasury/forms.py:126 msgid "No attached remittance" msgstr "Pas de remise associée" -#: apps/treasury/models.py:24 +#: apps/treasury/models.py:27 msgid "Invoice identifier" msgstr "Numéro de facture" -#: apps/treasury/models.py:38 +#: apps/treasury/models.py:41 msgid "BDE" msgstr "BDE" -#: apps/treasury/models.py:43 +#: apps/treasury/models.py:46 msgid "Object" msgstr "Objet" -#: apps/treasury/models.py:47 +#: apps/treasury/models.py:50 msgid "Description" msgstr "Description" -#: apps/treasury/models.py:56 +#: apps/treasury/models.py:59 msgid "Address" msgstr "Adresse" -#: apps/treasury/models.py:61 apps/treasury/models.py:191 +#: apps/treasury/models.py:64 apps/treasury/models.py:194 msgid "Date" msgstr "Date" -#: apps/treasury/models.py:65 +#: apps/treasury/models.py:68 msgid "Acquitted" msgstr "Acquittée" -#: apps/treasury/models.py:70 +#: apps/treasury/models.py:73 msgid "Locked" msgstr "Verrouillée" -#: apps/treasury/models.py:71 +#: apps/treasury/models.py:74 msgid "An invoice can't be edited when it is locked." msgstr "Une facture ne peut plus être modifiée si elle est verrouillée." -#: apps/treasury/models.py:77 +#: apps/treasury/models.py:80 msgid "tex source" msgstr "fichier TeX source" -#: apps/treasury/models.py:111 apps/treasury/models.py:127 +#: apps/treasury/models.py:114 apps/treasury/models.py:130 msgid "invoice" msgstr "facture" -#: apps/treasury/models.py:112 +#: apps/treasury/models.py:115 msgid "invoices" msgstr "factures" -#: apps/treasury/models.py:115 +#: apps/treasury/models.py:118 #, python-brace-format msgid "Invoice #{id}" msgstr "Facture n°{id}" -#: apps/treasury/models.py:132 +#: apps/treasury/models.py:135 msgid "Designation" msgstr "Désignation" -#: apps/treasury/models.py:138 +#: apps/treasury/models.py:141 msgid "Quantity" msgstr "Quantité" -#: apps/treasury/models.py:143 +#: apps/treasury/models.py:146 msgid "Unit price" msgstr "Prix unitaire" -#: apps/treasury/models.py:159 +#: apps/treasury/models.py:162 msgid "product" msgstr "produit" -#: apps/treasury/models.py:160 +#: apps/treasury/models.py:163 msgid "products" msgstr "produits" -#: apps/treasury/models.py:180 +#: apps/treasury/models.py:183 msgid "remittance type" msgstr "type de remise" -#: apps/treasury/models.py:181 +#: apps/treasury/models.py:184 msgid "remittance types" msgstr "types de remises" -#: apps/treasury/models.py:202 +#: apps/treasury/models.py:205 msgid "Comment" msgstr "Commentaire" -#: apps/treasury/models.py:207 +#: apps/treasury/models.py:210 msgid "Closed" msgstr "Fermée" -#: apps/treasury/models.py:211 +#: apps/treasury/models.py:214 msgid "remittance" msgstr "remise" -#: apps/treasury/models.py:212 +#: apps/treasury/models.py:215 msgid "remittances" msgstr "remises" -#: apps/treasury/models.py:245 +#: apps/treasury/models.py:248 msgid "Remittance #{:d}: {}" msgstr "Remise n°{:d} : {}" -#: apps/treasury/models.py:269 +#: apps/treasury/models.py:272 msgid "special transaction proxy" msgstr "proxy de transaction spéciale" -#: apps/treasury/models.py:270 +#: apps/treasury/models.py:273 msgid "special transaction proxies" msgstr "proxys de transactions spéciales" -#: apps/treasury/models.py:295 +#: apps/treasury/models.py:299 msgid "credit transaction" msgstr "transaction de crédit" -#: apps/treasury/models.py:379 +#: apps/treasury/models.py:418 msgid "" "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." @@ -2229,16 +2232,16 @@ msgstr "" "Cet utilisateur n'a pas assez d'argent pour payer les adhésions avec sa " "note. Merci de lui demander de recharger sa note avant d'invalider ce crédit." -#: apps/treasury/models.py:399 +#: apps/treasury/models.py:438 #: apps/treasury/templates/treasury/sogecredit_detail.html:10 msgid "Credit from the Société générale" msgstr "Crédit de la Société générale" -#: apps/treasury/models.py:400 +#: apps/treasury/models.py:439 msgid "Credits from the Société générale" msgstr "Crédits de la Société générale" -#: apps/treasury/models.py:403 +#: apps/treasury/models.py:442 #, python-brace-format msgid "Soge credit for {user}" msgstr "Crédit de la société générale pour l'utilisateur {user}" @@ -2250,7 +2253,7 @@ msgstr "Facture n°{:d}" #: apps/treasury/tables.py:25 #: apps/treasury/templates/treasury/invoice_list.html:13 #: apps/treasury/templates/treasury/remittance_list.html:13 -#: apps/treasury/templates/treasury/sogecredit_list.html:13 +#: apps/treasury/templates/treasury/sogecredit_list.html:14 msgid "Invoice" msgstr "Facture" @@ -2267,12 +2270,12 @@ msgid "Yes" msgstr "Oui" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:10 -#: apps/treasury/views.py:179 +#: apps/treasury/views.py:180 msgid "Delete invoice" msgstr "Supprimer la facture" #: apps/treasury/templates/treasury/invoice_confirm_delete.html:15 -#: apps/treasury/views.py:183 +#: apps/treasury/views.py:184 msgid "This invoice is locked and can't be deleted." msgstr "Cette facture est verrouillée et ne peut pas être supprimée." @@ -2306,7 +2309,7 @@ msgstr "Retirer produit" #: apps/treasury/templates/treasury/invoice_list.html:19 #: apps/treasury/templates/treasury/remittance_list.html:19 -#: apps/treasury/templates/treasury/sogecredit_list.html:19 +#: apps/treasury/templates/treasury/sogecredit_list.html:20 msgid "Société générale credits" msgstr "Crédits de la Société générale" @@ -2426,54 +2429,62 @@ msgstr "Valider" msgid "Return to credit list" msgstr "Retour à la liste des crédits" -#: apps/treasury/templates/treasury/sogecredit_list.html:34 +#: apps/treasury/templates/treasury/sogecredit_list.html:40 msgid "Filter with unvalidated credits only" msgstr "Filtrer avec uniquement les crédits non valides" -#: apps/treasury/templates/treasury/sogecredit_list.html:44 +#: apps/treasury/templates/treasury/sogecredit_list.html:50 msgid "There is no matched user that have asked for a Société générale credit." msgstr "" "Il n'y a pas d'utilisateur trouvé ayant demandé un crédit de la Société " "générale." -#: apps/treasury/views.py:39 +#: apps/treasury/templates/treasury/sogecredit_list.html:63 +msgid "Add credit from the Société générale" +msgstr "Ajouter un crédit de la Société générale" + +#: apps/treasury/templates/treasury/sogecredit_list.html:109 +msgid "Credit successfully registered" +msgstr "Le crédit a bien été enregistré" + +#: apps/treasury/views.py:40 msgid "Create new invoice" msgstr "Créer une nouvelle facture" -#: apps/treasury/views.py:96 +#: apps/treasury/views.py:97 msgid "Invoices list" msgstr "Liste des factures" -#: apps/treasury/views.py:111 apps/treasury/views.py:285 -#: apps/treasury/views.py:411 +#: apps/treasury/views.py:112 apps/treasury/views.py:286 +#: apps/treasury/views.py:412 msgid "You are not able to see the treasury interface." msgstr "Vous n'êtes pas autorisé à voir l'interface de trésorerie." -#: apps/treasury/views.py:121 +#: apps/treasury/views.py:122 msgid "Update an invoice" msgstr "Modifier la facture" -#: apps/treasury/views.py:246 +#: apps/treasury/views.py:247 msgid "Create a new remittance" msgstr "Créer une nouvelle remise" -#: apps/treasury/views.py:273 +#: apps/treasury/views.py:274 msgid "Remittances list" msgstr "Liste des remises" -#: apps/treasury/views.py:336 +#: apps/treasury/views.py:337 msgid "Update a remittance" msgstr "Modifier la remise" -#: apps/treasury/views.py:359 +#: apps/treasury/views.py:360 msgid "Attach a transaction to a remittance" msgstr "Joindre une transaction à une remise" -#: apps/treasury/views.py:403 +#: apps/treasury/views.py:404 msgid "List of credits from the Société générale" msgstr "Liste des crédits de la Société générale" -#: apps/treasury/views.py:443 +#: apps/treasury/views.py:449 msgid "Manage credits from the Société générale" msgstr "Gérer les crédits de la Société générale" @@ -2713,11 +2724,11 @@ msgstr "Prix du WEI (étudiants)" msgid "WEI list" msgstr "Liste des WEI" -#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:510 +#: apps/wei/templates/wei/base.html:81 apps/wei/views.py:517 msgid "Register 1A" msgstr "Inscrire un 1A" -#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:578 +#: apps/wei/templates/wei/base.html:85 apps/wei/views.py:592 msgid "Register 2A+" msgstr "Inscrire un 2A+" @@ -2746,8 +2757,8 @@ msgstr "Télécharger au format PDF" #: apps/wei/templates/wei/survey.html:11 #: apps/wei/templates/wei/survey_closed.html:11 -#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:973 -#: apps/wei/views.py:1028 apps/wei/views.py:1038 +#: apps/wei/templates/wei/survey_end.html:11 apps/wei/views.py:988 +#: apps/wei/views.py:1043 apps/wei/views.py:1053 msgid "Survey WEI" msgstr "Questionnaire WEI" @@ -2985,11 +2996,11 @@ msgstr "Gérer l'équipe WEI" msgid "Register first year student to the WEI" msgstr "Inscrire un 1A au WEI" -#: apps/wei/views.py:532 apps/wei/views.py:613 +#: apps/wei/views.py:539 apps/wei/views.py:627 msgid "This user is already registered to this WEI." msgstr "Cette personne est déjà inscrite au WEI." -#: apps/wei/views.py:537 +#: apps/wei/views.py:544 msgid "" "This user can't be in her/his first year since he/she has already " "participated to a WEI." @@ -2997,27 +3008,27 @@ msgstr "" "Cet utilisateur ne peut pas être en première année puisqu'il a déjà " "participé à un WEI." -#: apps/wei/views.py:554 +#: apps/wei/views.py:561 msgid "Register old student to the WEI" msgstr "Inscrire un 2A+ au WEI" -#: apps/wei/views.py:597 apps/wei/views.py:686 +#: apps/wei/views.py:611 apps/wei/views.py:700 msgid "You already opened an account in the Société générale." msgstr "Vous avez déjà ouvert un compte auprès de la société générale." -#: apps/wei/views.py:643 +#: apps/wei/views.py:657 msgid "Update WEI Registration" msgstr "Modifier l'inscription WEI" -#: apps/wei/views.py:746 +#: apps/wei/views.py:761 msgid "Delete WEI registration" msgstr "Supprimer l'inscription WEI" -#: apps/wei/views.py:757 +#: apps/wei/views.py:772 msgid "You don't have the right to delete this WEI registration." msgstr "Vous n'avez pas la permission de supprimer cette inscription au WEI." -#: apps/wei/views.py:776 +#: apps/wei/views.py:791 msgid "Validate WEI registration" msgstr "Valider l'inscription WEI" @@ -3141,13 +3152,7 @@ msgstr "" "Vous n'êtes plus adhérent BDE. Merci de réadhérer si vous voulez profiter de " "la note." -#: note_kfet/templates/base.html:164 -msgid "You are not a Kfet member, so you can't use your note account." -msgstr "" -"Vous n'êtes pas adhérent Kfet, vous ne pouvez par conséquent pas utiliser " -"votre compte note." - -#: note_kfet/templates/base.html:170 +#: note_kfet/templates/base.html:166 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3155,7 +3160,7 @@ msgstr "" "Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail " "et de cliquer sur le lien de validation." -#: note_kfet/templates/base.html:176 +#: note_kfet/templates/base.html:172 msgid "" "You declared that you opened a bank account in the Société générale. The " "bank did not validate the creation of the account to the BDE, so the " @@ -3170,7 +3175,7 @@ msgstr "" "durer quelques jours. Merci de vous assurer de bien aller au bout de vos " "démarches." -#: note_kfet/templates/base.html:199 +#: note_kfet/templates/base.html:195 msgid "Contact us" msgstr "Nous contacter" @@ -3218,9 +3223,10 @@ msgid "" "link templates and convert permissions to scope numbers with the permissions " "that you want to grant for your application." msgstr "" -"Vous pouvez aller ici pour générer des modèles " -"de liens d'autorisation et convertir des permissions en identifiants de " -"scopes avec les permissions que vous souhaitez attribuer à votre application." +"Vous pouvez aller ici pour générer des " +"modèles de liens d'autorisation et convertir des permissions en identifiants " +"de scopes avec les permissions que vous souhaitez attribuer à votre " +"application." #: note_kfet/templates/oauth2_provider/application_detail.html:37 #: note_kfet/templates/oauth2_provider/application_form.html:23 @@ -3400,3 +3406,8 @@ msgstr "" "vous connecter. Vous devez vous rendre à la Kfet et payer les frais " "d'adhésion. Vous devez également valider votre adresse email en suivant le " "lien que vous avez reçu." + +#~ msgid "You are not a Kfet member, so you can't use your note account." +#~ msgstr "" +#~ "Vous n'êtes pas adhérent Kfet, vous ne pouvez par conséquent pas utiliser " +#~ "votre compte note." From ad04e45992357506c32cf4bc6f36a2a52c1b8507 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Sep 2021 11:43:39 +0200 Subject: [PATCH 05/24] =?UTF-8?q?PC=20Kfet=20can=20create=20and=20update?= =?UTF-8?q?=20Sog=C3=A9=20credits=20(but=20not=20see=20them)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/permission/fixtures/initial.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 27df5b29..0bcb4aad 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3304,6 +3304,7 @@ 30, 31, 70, + 72, 143, 166, 167, @@ -3511,6 +3512,8 @@ 56, 57, 58, + 70, + 72, 135, 137, 143, From 391f3bde8fc06be67c35a57b5e5ab31c2f9709d3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Mon, 6 Sep 2021 11:56:56 +0200 Subject: [PATCH 06/24] Fix permission to see note balance when we can't see profile detail (e.g. for note account) Signed-off-by: Yohann D'ANELLO --- .../member/templates/member/includes/profile_info.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/member/templates/member/includes/profile_info.html b/apps/member/templates/member/includes/profile_info.html index e1941d23..378d54e2 100644 --- a/apps/member/templates/member/includes/profile_info.html +++ b/apps/member/templates/member/includes/profile_info.html @@ -39,13 +39,13 @@
{% trans 'address'|capfirst %}
{{ user_object.profile.address }}
- {% if user_object.note and "note.view_note"|has_perm:user_object.note %} -
{% trans 'balance'|capfirst %}
-
{{ user_object.note.balance | pretty_money }}
-
{% trans 'paid'|capfirst %}
{{ user_object.profile.paid|yesno }}
- {% endif %} + {% endif %} + + {% if user_object.note and "note.view_note"|has_perm:user_object.note %} +
{% trans 'balance'|capfirst %}
+
{{ user_object.note.balance | pretty_money }}
{% endif %} From fb6e3c3de08c25278ff7885bd6d451db21299db8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 7 Sep 2021 10:56:50 +0200 Subject: [PATCH 07/24] If connected and if we have the right, directly redirect to the validation page when registering someone Signed-off-by: Yohann D'ANELLO --- apps/registration/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/registration/views.py b/apps/registration/views.py index 9b385324..b256f591 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -85,6 +85,9 @@ class UserCreateView(CreateView): return super().form_valid(form) def get_success_url(self): + # Direct access to validation menu if we have the right to validate it + if PermissionBackend.check_perm(self.request, 'auth.view_user', self.object): + return reverse_lazy('registration:future_user_detail', args=(self.object.pk,)) return reverse_lazy('registration:email_validation_sent') From 4b03a78ad6f90b6ebc0d427042c0639472c47d67 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 7 Sep 2021 12:57:03 +0200 Subject: [PATCH 08/24] Fix password change form from unauthenticated users Signed-off-by: Yohann D'ANELLO --- apps/permission/signals.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/permission/signals.py b/apps/permission/signals.py index 78d0b8f9..6fb27392 100644 --- a/apps/permission/signals.py +++ b/apps/permission/signals.py @@ -61,6 +61,12 @@ def pre_save_object(sender, instance, **kwargs): # If the field wasn't modified, no need to check the permissions if old_value == new_value: continue + + if app_label == 'auth' and model_name == 'user' and field.name == 'password' and request.user.is_anonymous: + # We must ignore password changes from anonymous users since it can be done by people that forgot + # their password. We trust password change form. + continue + if not PermissionBackend.check_perm(request, app_label + ".change_" + model_name + "_" + field_name, instance): raise PermissionDenied( From da1e15c5e66cb69f6679833330a941acd0dc5b4d Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 7 Sep 2021 13:04:09 +0200 Subject: [PATCH 09/24] =?UTF-8?q?Update=20Sog=C3=A9=20credit=20amount=20wh?= =?UTF-8?q?en=20a=20transaction=20is=20added=20if=20the=20credit=20was=20a?= =?UTF-8?q?lready=20validated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yohann D'ANELLO --- apps/wei/models.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/wei/models.py b/apps/wei/models.py index 9e99cd1c..7ab56f57 100644 --- a/apps/wei/models.py +++ b/apps/wei/models.py @@ -372,3 +372,11 @@ class WEIMembership(Membership): soge_credit.update_transactions() soge_credit.save() + + if soge_credit.valid and \ + soge_credit.credit_transaction.total != sum(tr.total for tr in soge_credit.transactions.all()): + # The credit is already validated, but we add a new transaction (eg. for the WEI). + # Then we invalidate the transaction, update the credit transaction amount + # and re-validate the credit. + soge_credit.validate(True) + soge_credit.save() From b27341009e159279ec86b1bab35a7391ed77a0f0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 7 Sep 2021 15:11:15 +0200 Subject: [PATCH 10/24] [WEI] Update validation buttons for 1A Signed-off-by: Yohann D'ANELLO --- apps/wei/tables.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/wei/tables.py b/apps/wei/tables.py index b2e55508..0f862cc9 100644 --- a/apps/wei/tables.py +++ b/apps/wei/tables.py @@ -99,9 +99,12 @@ class WEIRegistrationTable(tables.Table): url = reverse_lazy('wei:validate_registration', args=(record.pk,)) text = _('Validate') - if record.fee > record.user.note.balance: + if record.fee > record.user.note.balance and not record.soge_credit: btn_class = 'btn-secondary' tooltip = _("The user does not have enough money.") + elif record.first_year and 'selected_bus_pk' not in record.information: + btn_class = 'btn-info' + tooltip = _("The user is in first year, and the repartition algorithm didn't run.") else: btn_class = 'btn-success' tooltip = _("The user has enough money, you can validate the registration.") From 048266ed61c9e3c37cadf6bda1bd01cae3e6aab3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 7 Sep 2021 22:09:00 +0200 Subject: [PATCH 11/24] [WEI] Fix unvalidated registrations table Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index b3d2c3a4..6ea0ddbb 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -132,7 +132,7 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): wei=club ) pre_registrations_table = WEIRegistrationTable(data=pre_registrations, prefix="pre-registration-") - pre_registrations_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1)) + pre_registrations_table.paginate(per_page=20, page=self.request.GET.get('pre-registration-page', 1)) context['pre_registrations'] = pre_registrations_table my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user) From d965732b656993ab164065f49d808a3783764c9e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 8 Sep 2021 14:52:39 +0200 Subject: [PATCH 12/24] Support multiple addresses for IP-based connection (useful when using IPv4/IPv6 and for ENS -> Crans transition) Signed-off-by: Yohann D'ANELLO --- note_kfet/middlewares.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/note_kfet/middlewares.py b/note_kfet/middlewares.py index e763a571..ed6d6acf 100644 --- a/note_kfet/middlewares.py +++ b/note_kfet/middlewares.py @@ -75,7 +75,7 @@ class LoginByIPMiddleware(object): else: ip = request.META.get('REMOTE_ADDR') - qs = User.objects.filter(password=f"ipbased${ip}") + qs = User.objects.filter(password__iregex=f"ipbased\\$.*\\^{ip}\\$.*") if qs.exists(): login(request, qs.get()) session = request.session From 03411ac9bd4cca1444c748ac2136ea2997cb20c1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 8 Sep 2021 16:59:44 +0200 Subject: [PATCH 13/24] Don't check permissions in a script Signed-off-by: Yohann D'ANELLO --- apps/permission/backends.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/permission/backends.py b/apps/permission/backends.py index af071455..f9c90d56 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -159,6 +159,10 @@ class PermissionBackend(ModelBackend): primary key, the result is not memoized. Moreover, the right could change (e.g. for a transaction, the balance of the user could change) """ + # Requested by a shell + if request is None: + return False + user_obj = request.user sess = request.session From 8fd5b6ee01ef885b5ba185969c16ef45625be0ce Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 8 Sep 2021 17:07:07 +0200 Subject: [PATCH 14/24] Fix safe summary for old passwords hashes from NK15 in Django Admin Signed-off-by: Yohann D'ANELLO --- apps/member/hashers.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/member/hashers.py b/apps/member/hashers.py index 69db24b0..a72ed94b 100644 --- a/apps/member/hashers.py +++ b/apps/member/hashers.py @@ -2,10 +2,12 @@ # SPDX-License-Identifier: GPL-3.0-or-later import hashlib +from collections import OrderedDict from django.conf import settings -from django.contrib.auth.hashers import PBKDF2PasswordHasher +from django.contrib.auth.hashers import PBKDF2PasswordHasher, mask_hash from django.utils.crypto import constant_time_compare +from django.utils.translation import gettext_lazy as _ from note_kfet.middlewares import get_current_request @@ -47,6 +49,18 @@ class CustomNK15Hasher(PBKDF2PasswordHasher): return constant_time_compare(hashlib.sha256((salt + password).encode("utf-8")).hexdigest(), db_hashed_pass) return super().verify(password, encoded) + def safe_summary(self, encoded): + # Displayed information in Django Admin. + if '|' in encoded: + salt, db_hashed_pass = encoded.split('$')[2].split('|') + return OrderedDict([ + (_('algorithm'), 'custom_nk15'), + (_('iterations'), '1'), + (_('salt'), mask_hash(salt)), + (_('hash'), mask_hash(db_hashed_pass)), + ]) + return super().safe_summary(encoded) + class DebugSuperuserBackdoor(PBKDF2PasswordHasher): """ From 7edd622755c5f26802b620c99dc3d4ddfecf2290 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 8 Sep 2021 18:35:36 +0200 Subject: [PATCH 15/24] BDE members can now use their note balance for personal transactions Signed-off-by: Yohann D'ANELLO --- apps/permission/fixtures/initial.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 0bcb4aad..1a55e991 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -627,7 +627,7 @@ "type": "view", "mask": 1, "field": "", - "permanent": false, + "permanent": true, "description": "Voir les personnes qu'on a invitées" } }, @@ -2883,6 +2883,7 @@ 3, 4, 5, + 6, 7, 8, 9, @@ -2890,6 +2891,10 @@ 11, 12, 13, + 14, + 15, + 16, + 17, 22, 48, 52, @@ -2907,11 +2912,6 @@ "for_club": 2, "name": "Adh\u00e9rent Kfet", "permissions": [ - 6, - 14, - 15, - 16, - 17, 22, 34, 36, From bd035744a4412a9214a053bcd855040ae8678d3f Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Wed, 8 Sep 2021 18:47:45 +0200 Subject: [PATCH 16/24] Don't create WEI registrations for unvalidated users Signed-off-by: Yohann D'ANELLO --- apps/wei/forms/registration.py | 11 +++- locale/fr/LC_MESSAGES/django.po | 102 ++++++++++++++++++++------------ 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/apps/wei/forms/registration.py b/apps/wei/forms/registration.py index 13e7b86b..474d83ee 100644 --- a/apps/wei/forms/registration.py +++ b/apps/wei/forms/registration.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import User from django.db.models import Q from django.forms import CheckboxSelectMultiple from django.utils.translation import gettext_lazy as _ -from note.models import NoteSpecial +from note.models import NoteSpecial, NoteUser from note_kfet.inputs import AmountInput, DatePickerInput, Autocomplete, ColorWidget from ..models import WEIClub, WEIRegistration, Bus, BusTeam, WEIMembership, WEIRole @@ -27,6 +27,15 @@ class WEIForm(forms.ModelForm): class WEIRegistrationForm(forms.ModelForm): + def clean(self): + cleaned_data = super().clean() + + if 'user' in cleaned_data: + if not NoteUser.objects.filter(user=cleaned_data['user']).exists(): + self.add_error('user', _("The selected user is not validated. Please validate its account first")) + + return cleaned_data + class Meta: model = WEIRegistration exclude = ('wei', ) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index ff9f2541..6416a03a 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-06 00:45+0200\n" +"POT-Creation-Date: 2021-09-08 18:46+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: Yohann D'ANELLO \n" "Language-Team: French \n" @@ -257,14 +257,14 @@ msgstr "Type" #: apps/activity/tables.py:82 apps/member/forms.py:186 #: apps/registration/forms.py:90 apps/treasury/forms.py:131 -#: apps/wei/forms/registration.py:96 +#: apps/wei/forms/registration.py:105 msgid "Last name" msgstr "Nom de famille" #: apps/activity/tables.py:84 apps/member/forms.py:191 #: apps/note/templates/note/transaction_form.html:134 #: apps/registration/forms.py:95 apps/treasury/forms.py:133 -#: apps/wei/forms/registration.py:101 +#: apps/wei/forms/registration.py:110 msgid "First name" msgstr "Prénom" @@ -508,7 +508,7 @@ msgstr "rôles" msgid "fee" msgstr "cotisation" -#: apps/member/apps.py:14 apps/wei/tables.py:193 apps/wei/tables.py:224 +#: apps/member/apps.py:14 apps/wei/tables.py:196 apps/wei/tables.py:227 msgid "member" msgstr "adhérent" @@ -541,7 +541,7 @@ msgid "This image cannot be loaded." msgstr "Cette image ne peut pas être chargée." #: apps/member/forms.py:141 apps/member/views.py:102 -#: apps/registration/forms.py:33 apps/registration/views.py:259 +#: apps/registration/forms.py:33 apps/registration/views.py:262 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." @@ -554,12 +554,12 @@ msgid "Check this case if the Société Générale paid the inscription." msgstr "Cochez cette case si la Société Générale a payé l'inscription." #: apps/member/forms.py:172 apps/registration/forms.py:77 -#: apps/wei/forms/registration.py:83 +#: apps/wei/forms/registration.py:92 msgid "Credit type" msgstr "Type de rechargement" #: apps/member/forms.py:173 apps/registration/forms.py:78 -#: apps/wei/forms/registration.py:84 +#: apps/wei/forms/registration.py:93 msgid "No credit" msgstr "Pas de rechargement" @@ -568,13 +568,13 @@ msgid "You can credit the note of the user." msgstr "Vous pouvez créditer la note de l'utilisateur avant l'adhésion." #: apps/member/forms.py:179 apps/registration/forms.py:83 -#: apps/wei/forms/registration.py:89 +#: apps/wei/forms/registration.py:98 msgid "Credit amount" msgstr "Montant à créditer" #: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140 #: apps/registration/forms.py:100 apps/treasury/forms.py:135 -#: apps/wei/forms/registration.py:106 +#: apps/wei/forms/registration.py:115 msgid "Bank" msgstr "Banque" @@ -586,6 +586,22 @@ msgstr "Utilisateur" msgid "Roles" msgstr "Rôles" +#: apps/member/hashers.py:57 +msgid "algorithm" +msgstr "algorithme" + +#: apps/member/hashers.py:58 +msgid "iterations" +msgstr "itérations" + +#: apps/member/hashers.py:59 +msgid "salt" +msgstr "salage" + +#: apps/member/hashers.py:60 +msgid "hash" +msgstr "haché" + #: apps/member/models.py:38 #: apps/member/templates/member/includes/profile_info.html:35 #: apps/registration/templates/registration/future_profile_detail.html:40 @@ -688,7 +704,7 @@ msgid "address" msgstr "adresse" #: apps/member/models.py:90 -#: apps/member/templates/member/includes/profile_info.html:46 +#: apps/member/templates/member/includes/profile_info.html:42 #: apps/registration/templates/registration/future_profile_detail.html:43 #: apps/wei/templates/wei/weimembership_form.html:47 msgid "paid" @@ -1020,7 +1036,7 @@ msgid "membership fee" msgstr "cotisation pour adhérer" #: apps/member/templates/member/includes/club_info.html:43 -#: apps/member/templates/member/includes/profile_info.html:43 +#: apps/member/templates/member/includes/profile_info.html:47 #: apps/treasury/templates/treasury/sogecredit_detail.html:24 #: apps/wei/templates/wei/base.html:60 msgid "balance" @@ -1514,7 +1530,7 @@ msgstr "Pas de motif spécifié" #: apps/note/tables.py:169 apps/note/tables.py:203 apps/treasury/tables.py:39 #: apps/treasury/templates/treasury/invoice_confirm_delete.html:30 #: apps/treasury/templates/treasury/sogecredit_detail.html:65 -#: apps/wei/tables.py:74 apps/wei/tables.py:114 +#: apps/wei/tables.py:74 apps/wei/tables.py:117 #: apps/wei/templates/wei/weiregistration_confirm_delete.html:31 #: note_kfet/templates/oauth2_provider/application_confirm_delete.html:18 #: note_kfet/templates/oauth2_provider/application_detail.html:39 @@ -1770,7 +1786,7 @@ msgstr "s'applique au club" msgid "role permissions" msgstr "permissions par rôles" -#: apps/permission/signals.py:67 +#: apps/permission/signals.py:73 #, python-brace-format msgid "" "You don't have the permission to change the field {field} on this instance " @@ -1779,7 +1795,7 @@ msgstr "" "Vous n'avez pas la permission de modifier le champ {field} sur l'instance du " "modèle {app_label}.{model_name}." -#: apps/permission/signals.py:77 apps/permission/views.py:105 +#: apps/permission/signals.py:83 apps/permission/views.py:105 #, python-brace-format msgid "" "You don't have the permission to add an instance of model {app_label}." @@ -1788,7 +1804,7 @@ msgstr "" "Vous n'avez pas la permission d'ajouter une instance du modèle {app_label}." "{model_name}." -#: apps/permission/signals.py:106 +#: apps/permission/signals.py:112 #, python-brace-format msgid "" "You don't have the permission to delete this instance of model {app_label}." @@ -2035,50 +2051,50 @@ msgstr "L'équipe de la Note Kfet." msgid "Register new user" msgstr "Enregistrer un nouvel utilisateur" -#: apps/registration/views.py:95 +#: apps/registration/views.py:98 msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:97 +#: apps/registration/views.py:100 msgid "Validate email" msgstr "Valider l'adresse e-mail" -#: apps/registration/views.py:141 +#: apps/registration/views.py:144 msgid "Email validation unsuccessful" msgstr "La validation de l'adresse mail a échoué" -#: apps/registration/views.py:152 +#: apps/registration/views.py:155 msgid "Email validation email sent" msgstr "L'email de vérification de l'adresse email a bien été envoyé" -#: apps/registration/views.py:160 +#: apps/registration/views.py:163 msgid "Resend email validation link" msgstr "Renvoyer le lien de validation" -#: apps/registration/views.py:178 +#: apps/registration/views.py:181 msgid "Pre-registered users list" msgstr "Liste des utilisateurs en attente d'inscription" -#: apps/registration/views.py:202 +#: apps/registration/views.py:205 msgid "Unregistered users" msgstr "Utilisateurs en attente d'inscription" -#: apps/registration/views.py:215 +#: apps/registration/views.py:218 msgid "Registration detail" msgstr "Détails de l'inscription" -#: apps/registration/views.py:279 +#: apps/registration/views.py:282 msgid "You must join the BDE." msgstr "Vous devez adhérer au BDE." -#: apps/registration/views.py:303 +#: apps/registration/views.py:306 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Le montant crédité est trop faible pour adhérer, il doit être au minimum de " "{}" -#: apps/registration/views.py:384 +#: apps/registration/views.py:387 msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" @@ -2494,12 +2510,18 @@ msgstr "Gérer les crédits de la Société générale" msgid "WEI" msgstr "WEI" -#: apps/wei/forms/registration.py:51 apps/wei/models.py:118 +#: apps/wei/forms/registration.py:35 +msgid "The selected user is not validated. Please validate its account first" +msgstr "" +"L'utilisateur sélectionné n'est pas validé. Merci de d'abord valider son " +"compte." + +#: apps/wei/forms/registration.py:60 apps/wei/models.py:118 #: apps/wei/models.py:315 msgid "bus" msgstr "bus" -#: apps/wei/forms/registration.py:52 +#: apps/wei/forms/registration.py:61 msgid "" "This choice is not definitive. The WEI organizers are free to attribute for " "you a bus and a team, in particular if you are a free eletron." @@ -2508,11 +2530,11 @@ msgstr "" "attribuer un bus et une équipe, en particulier si vous êtes un électron " "libre." -#: apps/wei/forms/registration.py:59 +#: apps/wei/forms/registration.py:68 msgid "Team" msgstr "Équipe" -#: apps/wei/forms/registration.py:61 +#: apps/wei/forms/registration.py:70 msgid "" "Leave this field empty if you won't be in a team (staff, bus chief, free " "electron)" @@ -2520,16 +2542,16 @@ msgstr "" "Laissez ce champ vide si vous ne serez pas dans une équipe (staff, chef de " "bus ou électron libre)" -#: apps/wei/forms/registration.py:67 apps/wei/forms/registration.py:77 +#: apps/wei/forms/registration.py:76 apps/wei/forms/registration.py:86 #: apps/wei/models.py:153 msgid "WEI Roles" msgstr "Rôles au WEI" -#: apps/wei/forms/registration.py:68 +#: apps/wei/forms/registration.py:77 msgid "Select the roles that you are interested in." msgstr "Sélectionnez les rôles qui vous intéressent." -#: apps/wei/forms/registration.py:113 +#: apps/wei/forms/registration.py:122 msgid "This team doesn't belong to the given bus." msgstr "Cette équipe n'appartient pas à ce bus." @@ -2688,23 +2710,29 @@ msgid "The user does not have enough money." msgstr "L'utilisateur n'a pas assez d'argent." #: apps/wei/tables.py:107 +msgid "The user is in first year, and the repartition algorithm didn't run." +msgstr "" +"L'utilisateur est en première année, et l'algorithme de répartition n'a pas " +"tourné." + +#: apps/wei/tables.py:110 msgid "The user has enough money, you can validate the registration." msgstr "L'utilisateur a assez d'argent, l'inscription est possible." -#: apps/wei/tables.py:139 +#: apps/wei/tables.py:142 msgid "Year" msgstr "Année" -#: apps/wei/tables.py:177 apps/wei/templates/wei/bus_detail.html:32 +#: apps/wei/tables.py:180 apps/wei/templates/wei/bus_detail.html:32 #: apps/wei/templates/wei/busteam_detail.html:50 msgid "Teams" msgstr "Équipes" -#: apps/wei/tables.py:186 apps/wei/tables.py:227 +#: apps/wei/tables.py:189 apps/wei/tables.py:230 msgid "Members count" msgstr "Nombre de membres" -#: apps/wei/tables.py:193 apps/wei/tables.py:224 +#: apps/wei/tables.py:196 apps/wei/tables.py:227 msgid "members" msgstr "adhérents" From 1ee40cb94e44fbc2a13d8485333b5bd04235af38 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 09:10:05 +0200 Subject: [PATCH 17/24] Fix chemistry department (warning: this may break the choices from members of the department) Signed-off-by: Yohann D'ANELLO --- apps/member/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/member/models.py b/apps/member/models.py index 2564190a..73b7c668 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -57,7 +57,7 @@ class Profile(models.Model): ('A1', _("Mathematics (A1)")), ('A2', _("Physics (A2)")), ("A'2", _("Applied physics (A'2)")), - ('A''2', _("Chemistry (A''2)")), + ("A''2", _("Chemistry (A''2)")), ('A3', _("Biology (A3)")), ('B1234', _("SAPHIRE (B1234)")), ('B1', _("Mechanics (B1)")), From a78f3b7caa0bafe55c0a36f38fa713ce768f3db8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 09:16:08 +0200 Subject: [PATCH 18/24] [WEI] Fix broken tests Signed-off-by: Yohann D'ANELLO --- apps/wei/tests/test_wei_registration.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/wei/tests/test_wei_registration.py b/apps/wei/tests/test_wei_registration.py index bcd755d8..0bc84013 100644 --- a/apps/wei/tests/test_wei_registration.py +++ b/apps/wei/tests/test_wei_registration.py @@ -12,7 +12,7 @@ from django.test import TestCase from django.urls import reverse from django.utils import timezone from member.models import Membership, Club -from note.models import NoteClub, SpecialTransaction +from note.models import NoteClub, SpecialTransaction, NoteUser from treasury.models import SogeCredit from ..api.views import BusViewSet, BusTeamViewSet, WEIClubViewSet, WEIMembershipViewSet, WEIRegistrationViewSet, \ @@ -65,6 +65,8 @@ class TestWEIRegistration(TestCase): sess["permission_mask"] = 42 sess.save() + NoteUser.objects.create(user=self.user) + self.year = timezone.now().year self.wei = WEIClub.objects.create( name="Test WEI", From 2c02c747f458066d1d499f354f7f0e3a1023a484 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 09:23:12 +0200 Subject: [PATCH 19/24] [WEI] Fix errors when a user go to the WEI registration form while it is already registered Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/wei/views.py b/apps/wei/views.py index 6ea0ddbb..83f40f3b 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -510,6 +510,10 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView): # We can't register someone once the WEI is started and before the membership start date if today >= wei.date_start or today < wei.membership_start: return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) + # Don't register twice + if 'myself' in self.request.path and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): + obj = WEIRegistration.objects.get(wei=wei, user=self.request.user) + return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.pk,))) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): @@ -585,6 +589,10 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView): # We can't register someone once the WEI is started and before the membership start date if today >= wei.date_start or today < wei.membership_start: return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) + # Don't register twice + if 'myself' in self.request.path and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists(): + obj = WEIRegistration.objects.get(wei=wei, user=self.request.user) + return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.pk,))) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): From 5793b83de772717e173cc677db13d207efc694f1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 09:27:15 +0200 Subject: [PATCH 20/24] [WEI] Fix error when validating sometimes a membership Signed-off-by: Yohann D'ANELLO --- apps/wei/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/wei/views.py b/apps/wei/views.py index 83f40f3b..c24b6c45 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -973,12 +973,11 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView): membership.roles.set(WEIRole.objects.filter(name="1A").all()) membership.save() - ret = super().form_valid(form) - + membership.save() membership.refresh_from_db() membership.roles.add(WEIRole.objects.get(name="Adhérent WEI")) - return ret + return super().form_valid(form) def get_success_url(self): self.object.refresh_from_db() From be6059eba6f856f0c876021450690e93200fa299 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 09:47:04 +0200 Subject: [PATCH 21/24] [WEI] Fix tests Signed-off-by: Yohann D'ANELLO --- apps/wei/tests/test_wei_registration.py | 27 +++++++++++++++++++------ apps/wei/views.py | 12 ++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/wei/tests/test_wei_registration.py b/apps/wei/tests/test_wei_registration.py index 0bc84013..65edd902 100644 --- a/apps/wei/tests/test_wei_registration.py +++ b/apps/wei/tests/test_wei_registration.py @@ -65,8 +65,6 @@ class TestWEIRegistration(TestCase): sess["permission_mask"] = 42 sess.save() - NoteUser.objects.create(user=self.user) - self.year = timezone.now().year self.wei = WEIClub.objects.create( name="Test WEI", @@ -304,6 +302,7 @@ class TestWEIRegistration(TestCase): self.assertEqual(response.status_code, 200) user = User.objects.create(username="toto", email="toto@example.com") + NoteUser.objects.create(user=user) # Try with an invalid form response = self.client.post(reverse("wei:wei_register_2A", kwargs=dict(wei_pk=self.wei.pk)), dict( @@ -370,7 +369,7 @@ class TestWEIRegistration(TestCase): last_name="toto", bank="Société générale", )) - response = self.client.get(reverse("wei:wei_register_2A_myself", kwargs=dict(wei_pk=self.wei.pk))) + response = self.client.get(reverse("wei:wei_register_2A", kwargs=dict(wei_pk=self.wei.pk))) self.assertEqual(response.status_code, 200) # Check that if the WEI is started, we can't register anyone @@ -386,10 +385,8 @@ class TestWEIRegistration(TestCase): response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk))) self.assertEqual(response.status_code, 200) - response = self.client.get(reverse("wei:wei_register_1A_myself", kwargs=dict(wei_pk=self.wei.pk))) - self.assertEqual(response.status_code, 200) - user = User.objects.create(username="toto", email="toto@example.com") + NoteUser.objects.create(user=user) response = self.client.post(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)), dict( user=user.id, soge_credit=True, @@ -469,6 +466,24 @@ class TestWEIRegistration(TestCase): response = self.client.get(reverse("wei:wei_survey", kwargs=dict(pk=registration.pk))) self.assertRedirects(response, reverse("wei:wei_closed", kwargs=dict(pk=self.wei.pk)), 302, 200) + def test_register_myself(self): + """ + Try to register myself to the WEI, and check redirections. + """ + response = self.client.get(reverse('wei:wei_register_1A_myself', args=(self.wei.pk,))) + self.assertRedirects(response, reverse('wei:wei_update_registration', args=(self.registration.pk,))) + + response = self.client.get(reverse('wei:wei_register_2A_myself', args=(self.wei.pk,))) + self.assertRedirects(response, reverse('wei:wei_update_registration', args=(self.registration.pk,))) + + self.registration.delete() + + response = self.client.get(reverse('wei:wei_register_1A_myself', args=(self.wei.pk,))) + self.assertEqual(response.status_code, 200) + + response = self.client.get(reverse('wei:wei_register_2A_myself', args=(self.wei.pk,))) + self.assertEqual(response.status_code, 200) + def test_wei_survey_ended(self): """ Test display the end page of a survey. diff --git a/apps/wei/views.py b/apps/wei/views.py index c24b6c45..20243c40 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -691,12 +691,14 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update context["membership_form"] = membership_form elif not self.object.first_year and PermissionBackend.check_perm( self.request, "wei.change_weiregistration_information_json", self.object): + information = self.object.information + d = dict( + bus=Bus.objects.filter(pk__in=information["preferred_bus_pk"]).all(), + team=BusTeam.objects.filter(pk__in=information["preferred_team_pk"]).all(), + roles=WEIRole.objects.filter(pk__in=information["preferred_roles_pk"]).all(), + ) if 'preferred_bus_pk' in information else dict() choose_bus_form = WEIChooseBusForm( - self.request.POST if self.request.POST else dict( - bus=Bus.objects.filter(pk__in=self.object.information["preferred_bus_pk"]).all(), - team=BusTeam.objects.filter(pk__in=self.object.information["preferred_team_pk"]).all(), - roles=WEIRole.objects.filter(pk__in=self.object.information["preferred_roles_pk"]).all(), - ) + self.request.POST if self.request.POST else d ) choose_bus_form.fields["bus"].queryset = Bus.objects.filter(wei=context["club"]) choose_bus_form.fields["team"].queryset = BusTeam.objects.filter(bus__wei=context["club"]) From af4be98b5b3d97b55e93dc0fb909262116c63f13 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 10:41:57 +0200 Subject: [PATCH 22/24] Fix consumer search with non-regex values (only for consumers, not for all search fields in API) Signed-off-by: Yohann D'ANELLO --- apps/note/api/views.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index d4021210..5096d238 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -1,5 +1,6 @@ # Copyright (C) 2018-2021 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +import re from django.conf import settings from django.db.models import Q @@ -133,23 +134,31 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): if settings.DATABASES[queryset.db]["ENGINE"] == 'django.db.backends.postgresql' else queryset alias = self.request.query_params.get("alias", None) + # Check if this is a valid regex. If not, we won't check regex + try: + re.compile(alias) + valid_regex = True + except re.error: + valid_regex = False + suffix = '__iregex' if valid_regex else '__istartswith' + alias_prefix = '^' if valid_regex else '' queryset = queryset.prefetch_related('note') if alias: # We match first an alias if it is matched without normalization, # then if the normalized pattern matches a normalized alias. queryset = queryset.filter( - name__iregex="^" + alias + **{f'name{suffix}': alias_prefix + alias} ).union( queryset.filter( - Q(normalized_name__iregex="^" + Alias.normalize(alias)) - & ~Q(name__iregex="^" + alias) + Q(**{f'normalized_name{suffix}': alias_prefix + Alias.normalize(alias)}) + & ~Q(**{f'name{suffix}': alias_prefix + alias}) ), all=True).union( queryset.filter( - Q(normalized_name__iregex="^" + alias.lower()) - & ~Q(normalized_name__iregex="^" + Alias.normalize(alias)) - & ~Q(name__iregex="^" + alias) + Q(**{f'normalized_name{suffix}': alias_prefix + alias.lower()}) + & ~Q(**{f'normalized_name{suffix}': alias_prefix + Alias.normalize(alias)}) + & ~Q(**{f'name{suffix}': alias_prefix + alias}) ), all=True) From 0dd3da5c0197ba985481b7fa308f636d309cb418 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 10:45:36 +0200 Subject: [PATCH 23/24] Linting Signed-off-by: Yohann D'ANELLO --- apps/member/hashers.py | 8 ++++---- apps/treasury/models.py | 17 +++++++++-------- apps/wei/views.py | 6 +++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/member/hashers.py b/apps/member/hashers.py index a72ed94b..32f8c63e 100644 --- a/apps/member/hashers.py +++ b/apps/member/hashers.py @@ -54,10 +54,10 @@ class CustomNK15Hasher(PBKDF2PasswordHasher): if '|' in encoded: salt, db_hashed_pass = encoded.split('$')[2].split('|') return OrderedDict([ - (_('algorithm'), 'custom_nk15'), - (_('iterations'), '1'), - (_('salt'), mask_hash(salt)), - (_('hash'), mask_hash(db_hashed_pass)), + (_('algorithm'), 'custom_nk15'), + (_('iterations'), '1'), + (_('salt'), mask_hash(salt)), + (_('hash'), mask_hash(db_hashed_pass)), ]) return super().safe_summary(encoded) diff --git a/apps/treasury/models.py b/apps/treasury/models.py index c1089bb8..7e4e1566 100644 --- a/apps/treasury/models.py +++ b/apps/treasury/models.py @@ -3,6 +3,7 @@ from datetime import date +from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator @@ -11,10 +12,8 @@ from django.db.models import Q from django.template.loader import render_to_string from django.utils import timezone from django.utils.translation import gettext_lazy as _ - from member.models import Club, Membership from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser -from wei.models import WEIClub class Invoice(models.Model): @@ -319,10 +318,8 @@ class SogeCredit(models.Model): bde = Club.objects.get(name="BDE") kfet = Club.objects.get(name="Kfet") - wei = WEIClub.objects.order_by('-year').first() bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start) kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start) - wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start) if bde_qs.exists(): m = bde_qs.get() @@ -334,10 +331,14 @@ class SogeCredit(models.Model): if m.transaction not in self.transactions.all(): self.transactions.add(m.transaction) - if wei_qs.exists(): - m = wei_qs.get() - if m.transaction not in self.transactions.all(): - self.transactions.add(m.transaction) + if 'wei' in settings.INSTALLED_APPS: + from wei.models import WEIClub + wei = WEIClub.objects.order_by('-year').first() + wei_qs = Membership.objects.filter(user=self.user, club=wei, date_start__gte=wei.membership_start) + if wei_qs.exists(): + m = wei_qs.get() + if m.transaction not in self.transactions.all(): + self.transactions.add(m.transaction) for tr in self.transactions.all(): tr.valid = False diff --git a/apps/wei/views.py b/apps/wei/views.py index 20243c40..348ac751 100644 --- a/apps/wei/views.py +++ b/apps/wei/views.py @@ -693,9 +693,9 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update self.request, "wei.change_weiregistration_information_json", self.object): information = self.object.information d = dict( - bus=Bus.objects.filter(pk__in=information["preferred_bus_pk"]).all(), - team=BusTeam.objects.filter(pk__in=information["preferred_team_pk"]).all(), - roles=WEIRole.objects.filter(pk__in=information["preferred_roles_pk"]).all(), + bus=Bus.objects.filter(pk__in=information["preferred_bus_pk"]).all(), + team=BusTeam.objects.filter(pk__in=information["preferred_team_pk"]).all(), + roles=WEIRole.objects.filter(pk__in=information["preferred_roles_pk"]).all(), ) if 'preferred_bus_pk' in information else dict() choose_bus_form = WEIChooseBusForm( self.request.POST if self.request.POST else d From fb98d9cd8bd41149bea58eaab8514590712c8f89 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 9 Sep 2021 10:53:40 +0200 Subject: [PATCH 24/24] Fix one more error in alias autocompletion Signed-off-by: Yohann D'ANELLO --- apps/note/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 5096d238..a228bdf6 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -138,7 +138,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): try: re.compile(alias) valid_regex = True - except re.error: + except (re.error, TypeError): valid_regex = False suffix = '__iregex' if valid_regex else '__istartswith' alias_prefix = '^' if valid_regex else ''