diff --git a/apps/member/models.py b/apps/member/models.py index 0af52736..693854af 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -187,11 +187,13 @@ class Membership(models.Model): user = models.ForeignKey( User, on_delete=models.PROTECT, + verbose_name=_("user"), ) club = models.ForeignKey( Club, on_delete=models.PROTECT, + verbose_name=_("club"), ) roles = models.ManyToManyField( @@ -237,6 +239,7 @@ class Membership(models.Model): self.fee = self.club.membership_fee_paid else: self.fee = self.club.membership_fee_unpaid + if self.club.membership_duration is not None: self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration) else: diff --git a/apps/member/tables.py b/apps/member/tables.py index ec21c712..7365320d 100644 --- a/apps/member/tables.py +++ b/apps/member/tables.py @@ -1,8 +1,10 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from datetime import datetime import django_tables2 as tables from django.contrib.auth.models import User +from django.utils.translation import gettext_lazy as _ from django.urls import reverse_lazy from django.utils.html import format_html from note.templatetags.pretty_money import pretty_money @@ -49,8 +51,39 @@ class MembershipTable(tables.Table): } ) - def render_fee(self, value): - return pretty_money(value) + def render_club(self, value): + s = value.name + if PermissionBackend().has_perm(get_current_authenticated_user(), "member.view_club", value): + s = format_html("{name}", + url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s) + + return s + + def render_fee(self, value, record): + t = pretty_money(value) + + # If it is required and if the user has the right, the renew button is displayed. + if record.club.membership_start is not None: + if record.date_start < record.club.membership_start: # If the renew is available + if not Membership.objects.filter( + club=record.club, + user=record.user, + date_start__gte=record.club.membership_start, + date_end__lte=record.club.membership_end, + ).exists(): # If the renew is not yet performed + empty_membership = Membership( + club=record.club, + user=record.user, + date_start=datetime.now().date(), + date_end=datetime.now().date(), + fee=0, + ) + if PermissionBackend().has_perm(get_current_authenticated_user(), + "member:add_membership", empty_membership): # If the user has right + t = format_html(t + ' {text}', + url=reverse_lazy('member:club_renew_membership', + kwargs={"pk": record.pk}), text=_("Renew")) + return t def render_roles(self, record): roles = record.roles.all() diff --git a/apps/member/urls.py b/apps/member/urls.py index 2000a6e4..1214f024 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -10,10 +10,11 @@ urlpatterns = [ path('signup/', views.UserCreateView.as_view(), name="signup"), path('club/', views.ClubListView.as_view(), name="club_list"), + path('club/create/', views.ClubCreateView.as_view(), name="club_create"), path('club//', views.ClubDetailView.as_view(), name="club_detail"), path('club//add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"), path('club/manage_roles//', views.ClubManageRolesView.as_view(), name="club_manage_roles"), - path('club/create/', views.ClubCreateView.as_view(), name="club_create"), + path('club/renew_membership//', views.ClubRenewMembershipView.as_view(), name="club_renew_membership"), path('club//update/', views.ClubUpdateView.as_view(), name="club_update"), path('club//update_pic/', views.ClubPictureUpdateView.as_view(), name="club_update_pic"), path('club//aliases/', views.ClubAliasView.as_view(), name="club_alias"), diff --git a/apps/member/views.py b/apps/member/views.py index d3a1848f..c3f0425c 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -2,19 +2,21 @@ # SPDX-License-Identifier: GPL-3.0-or-later import io -from datetime import datetime +from datetime import datetime, timedelta from PIL import Image from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.contrib.auth.views import LoginView +from django.core.exceptions import ValidationError from django.db.models import Q from django.forms import HiddenInput from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, UpdateView, TemplateView +from django.views.generic.base import View from django.views.generic.edit import FormMixin from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token @@ -137,8 +139,8 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\ .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) context['history_list'] = HistoryTable(history_list) - club_list = Membership.objects.all().filter(user=user)\ - .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).only("club") + club_list = Membership.objects.filter(user=user, date_end__gte=datetime.today())\ + .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")) context['club_list'] = MembershipTable(data=club_list) return context @@ -292,9 +294,8 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): context['history_list'] = HistoryTable(club_transactions) club_member = Membership.objects.filter( club=club, - date_start__lte=datetime.now().date(), - date_end__gte=datetime.now().date(), - ).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).all() + date_end__gte=datetime.today(), + ).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")) context['member_list'] = MembershipTable(data=club_member) @@ -366,7 +367,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): else: fee = club.membership_fee_unpaid if user.note.balance < fee and not Membership.objects.filter( - club=2, + club__name="Kfet", user=user, date_start__lte=datetime.now().date(), date_end__gte=datetime.now().date(), @@ -437,3 +438,28 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): def get_success_url(self): return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id}) + + +class ClubRenewMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, View): + def get(self, *args, **kwargs): + user = self.request.user + membership = Membership.objects.filter(PermissionBackend.filter_queryset(user, Membership, "change"))\ + .filter(pk=self.kwargs["pk"]).get() + + if Membership.objects.filter( + club=membership.club, + user=membership.user, + date_start__gte=membership.club.membership_start, + date_end__lte=membership.club.membership_end, + ).exists(): + raise ValidationError(_("This membership is already renewed")) + + new_membership = Membership.objects.create( + user=user, + club=membership.club, + date_start=membership.date_end + timedelta(days=1), + ) + new_membership.roles.set(membership.roles.all()) + new_membership.save() + + return redirect(reverse_lazy('member:club_detail', kwargs={'pk': membership.club.pk})) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 39f5818c..69cd883e 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-01 04:12+0200\n" +"POT-Creation-Date: 2020-04-01 18:39+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,7 +44,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "" #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:66 apps/member/models.py:166 +#: apps/member/models.py:66 apps/member/models.py:169 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 @@ -78,7 +78,7 @@ msgstr "" msgid "type" msgstr "" -#: apps/activity/models.py:66 apps/logs/models.py:21 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190 #: apps/note/models/notes.py:117 msgid "user" msgstr "" @@ -275,7 +275,7 @@ msgstr "" msgid "user profile" msgstr "" -#: apps/member/models.py:71 templates/member/club_info.html:41 +#: apps/member/models.py:71 templates/member/club_info.html:46 msgid "email" msgstr "" @@ -291,11 +291,11 @@ msgstr "" msgid "Uncheck if this club don't require memberships." msgstr "" -#: apps/member/models.py:93 templates/member/club_info.html:31 +#: apps/member/models.py:93 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "" -#: apps/member/models.py:98 templates/member/club_info.html:34 +#: apps/member/models.py:98 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "" @@ -325,71 +325,86 @@ msgid "" "members can renew their membership." msgstr "" -#: apps/member/models.py:151 apps/note/models/notes.py:139 +#: apps/member/models.py:154 apps/member/models.py:196 +#: apps/note/models/notes.py:139 msgid "club" msgstr "" -#: apps/member/models.py:152 +#: apps/member/models.py:155 msgid "clubs" msgstr "" -#: apps/member/models.py:172 apps/permission/models.py:288 +#: apps/member/models.py:175 apps/permission/models.py:288 msgid "role" msgstr "" -#: apps/member/models.py:173 apps/member/models.py:196 +#: apps/member/models.py:176 apps/member/models.py:201 msgid "roles" msgstr "" -#: apps/member/models.py:200 +#: apps/member/models.py:205 msgid "membership starts on" msgstr "" -#: apps/member/models.py:204 +#: apps/member/models.py:209 msgid "membership ends on" msgstr "" -#: apps/member/models.py:209 +#: apps/member/models.py:214 msgid "fee" msgstr "" -#: apps/member/models.py:221 apps/member/views.py:365 +#: apps/member/models.py:226 apps/member/views.py:383 msgid "User is not a member of the parent club" msgstr "" -#: apps/member/models.py:231 apps/member/views.py:374 +#: apps/member/models.py:236 apps/member/views.py:392 msgid "User is already a member of the club" msgstr "" -#: apps/member/models.py:265 +#: apps/member/models.py:271 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "" -#: apps/member/models.py:268 +#: apps/member/models.py:274 msgid "membership" msgstr "" -#: apps/member/models.py:269 +#: apps/member/models.py:275 msgid "memberships" msgstr "" -#: apps/member/views.py:78 templates/member/profile_info.html:45 +#: apps/member/tables.py:73 +msgid "Renew" +msgstr "" + +#: apps/member/views.py:80 templates/member/profile_info.html:45 msgid "Update Profile" msgstr "" -#: apps/member/views.py:91 +#: apps/member/views.py:93 msgid "An alias with a similar name already exists." msgstr "" -#: apps/member/views.py:378 apps/member/views.py:410 +#: apps/member/views.py:379 +msgid "" +"This user don't have enough money to join this club, and can't have a " +"negative balance." +msgstr "" + +#: apps/member/views.py:396 apps/member/views.py:428 msgid "The membership must start after {:%m-%d-%Y}." msgstr "" -#: apps/member/views.py:383 apps/member/views.py:415 +#: apps/member/views.py:401 apps/member/views.py:433 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "" +#: apps/member/views.py:455 +msgid "This membership is already renewed" +msgstr "" + #: apps/note/admin.py:120 apps/note/models/transactions.py:94 msgid "source" msgstr "" @@ -491,7 +506,7 @@ msgstr "" msgid "alias" msgstr "" -#: apps/note/models/notes.py:211 templates/member/club_info.html:38 +#: apps/note/models/notes.py:211 templates/member/club_info.html:43 #: templates/member/profile_info.html:36 msgid "aliases" msgstr "" @@ -913,15 +928,19 @@ msgstr "" msgid "days" msgstr "" -#: templates/member/club_info.html:47 +#: templates/member/club_info.html:32 +msgid "membership fee" +msgstr "" + +#: templates/member/club_info.html:52 msgid "Add member" msgstr "" -#: templates/member/club_info.html:50 templates/note/conso_form.html:121 +#: templates/member/club_info.html:55 templates/note/conso_form.html:121 msgid "Edit" msgstr "" -#: templates/member/club_info.html:54 templates/member/profile_info.html:48 +#: templates/member/club_info.html:59 templates/member/profile_info.html:48 msgid "View Profile" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 24f7bf8a..76bc90f6 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-01 04:12+0200\n" +"POT-Creation-Date: 2020-04-01 18:39+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -40,7 +40,7 @@ msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." #: apps/activity/models.py:23 apps/activity/models.py:48 -#: apps/member/models.py:66 apps/member/models.py:166 +#: apps/member/models.py:66 apps/member/models.py:169 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/transactions.py:44 apps/note/models/transactions.py:232 #: templates/member/club_info.html:13 templates/member/profile_info.html:14 @@ -74,7 +74,7 @@ msgstr "description" msgid "type" msgstr "type" -#: apps/activity/models.py:66 apps/logs/models.py:21 +#: apps/activity/models.py:66 apps/logs/models.py:21 apps/member/models.py:190 #: apps/note/models/notes.py:117 msgid "user" msgstr "utilisateur" @@ -271,7 +271,7 @@ msgstr "payé" msgid "user profile" msgstr "profil utilisateur" -#: apps/member/models.py:71 templates/member/club_info.html:41 +#: apps/member/models.py:71 templates/member/club_info.html:46 msgid "email" msgstr "courriel" @@ -287,11 +287,11 @@ msgstr "nécessite des adhésions" msgid "Uncheck if this club don't require memberships." msgstr "Décochez si ce club n'utilise pas d'adhésions." -#: apps/member/models.py:93 templates/member/club_info.html:31 +#: apps/member/models.py:93 templates/member/club_info.html:35 msgid "membership fee (paid students)" msgstr "cotisation pour adhérer (normalien élève)" -#: apps/member/models.py:98 templates/member/club_info.html:34 +#: apps/member/models.py:98 templates/member/club_info.html:38 msgid "membership fee (unpaid students)" msgstr "cotisation pour adhérer (normalien étudiant)" @@ -325,71 +325,86 @@ msgstr "" "Combien de temps l'adhésion peut durer après le 1er Janvier de l'année " "suivante avant que les adhérents peuvent renouveler leur adhésion." -#: apps/member/models.py:151 apps/note/models/notes.py:139 +#: apps/member/models.py:154 apps/member/models.py:196 +#: apps/note/models/notes.py:139 msgid "club" msgstr "club" -#: apps/member/models.py:152 +#: apps/member/models.py:155 msgid "clubs" msgstr "clubs" -#: apps/member/models.py:172 apps/permission/models.py:288 +#: apps/member/models.py:175 apps/permission/models.py:288 msgid "role" msgstr "rôle" -#: apps/member/models.py:173 apps/member/models.py:196 +#: apps/member/models.py:176 apps/member/models.py:201 msgid "roles" msgstr "rôles" -#: apps/member/models.py:200 +#: apps/member/models.py:205 msgid "membership starts on" msgstr "l'adhésion commence le" -#: apps/member/models.py:204 +#: apps/member/models.py:209 msgid "membership ends on" msgstr "l'adhésion finit le" -#: apps/member/models.py:209 +#: apps/member/models.py:214 msgid "fee" msgstr "cotisation" -#: apps/member/models.py:221 apps/member/views.py:365 +#: apps/member/models.py:226 apps/member/views.py:383 msgid "User is not a member of the parent club" msgstr "L'utilisateur n'est pas membre du club parent" -#: apps/member/models.py:231 apps/member/views.py:374 +#: apps/member/models.py:236 apps/member/views.py:392 msgid "User is already a member of the club" msgstr "L'utilisateur est déjà membre du club" -#: apps/member/models.py:265 +#: apps/member/models.py:271 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Adhésion de {user} pour le club {club}" -#: apps/member/models.py:268 +#: apps/member/models.py:274 msgid "membership" msgstr "adhésion" -#: apps/member/models.py:269 +#: apps/member/models.py:275 msgid "memberships" msgstr "adhésions" -#: apps/member/views.py:78 templates/member/profile_info.html:45 +#: apps/member/tables.py:73 +msgid "Renew" +msgstr "" + +#: apps/member/views.py:80 templates/member/profile_info.html:45 msgid "Update Profile" msgstr "Modifier le profil" -#: apps/member/views.py:91 +#: apps/member/views.py:93 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/member/views.py:378 apps/member/views.py:410 +#: apps/member/views.py:379 +msgid "" +"This user don't have enough money to join this club, and can't have a " +"negative balance." +msgstr "" + +#: apps/member/views.py:396 apps/member/views.py:428 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:383 apps/member/views.py:415 +#: apps/member/views.py:401 apps/member/views.py:433 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." +#: apps/member/views.py:455 +msgid "This membership is already renewed" +msgstr "Cette adhésion est déjà renouvelée" + #: apps/note/admin.py:120 apps/note/models/transactions.py:94 msgid "source" msgstr "source" @@ -492,7 +507,7 @@ msgstr "Alias invalide" msgid "alias" msgstr "alias" -#: apps/note/models/notes.py:211 templates/member/club_info.html:38 +#: apps/note/models/notes.py:211 templates/member/club_info.html:43 #: templates/member/profile_info.html:36 msgid "aliases" msgstr "alias" @@ -916,15 +931,19 @@ msgstr "Club parent" msgid "days" msgstr "jours" -#: templates/member/club_info.html:47 +#: templates/member/club_info.html:32 +msgid "membership fee" +msgstr "cotisation pour adhérer" + +#: templates/member/club_info.html:52 msgid "Add member" msgstr "Ajouter un membre" -#: templates/member/club_info.html:50 templates/note/conso_form.html:121 +#: templates/member/club_info.html:55 templates/note/conso_form.html:121 msgid "Edit" msgstr "Éditer" -#: templates/member/club_info.html:54 templates/member/profile_info.html:48 +#: templates/member/club_info.html:59 templates/member/profile_info.html:48 msgid "View Profile" msgstr "Voir le profil" diff --git a/templates/member/club_info.html b/templates/member/club_info.html index ef5c59f1..a781bea8 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -28,11 +28,16 @@
{% trans 'membership duration'|capfirst %}
{{ club.membership_duration }} {% trans "days" %}
-
{% trans 'membership fee (paid students)'|capfirst %}
-
{{ club.membership_fee_paid|pretty_money }}
+ {% if club.membership_fee_paid == club.membership_fee_unpaid %} +
{% trans 'membership fee'|capfirst %}
+
{{ club.membership_fee_paid|pretty_money }}
+ {% else %} +
{% trans 'membership fee (paid students)'|capfirst %}
+
{{ club.membership_fee_paid|pretty_money }}
-
{% trans 'membership fee (unpaid students)'|capfirst %}
-
{{ club.membership_fee_unpaid|pretty_money }}
+
{% trans 'membership fee (unpaid students)'|capfirst %}
+
{{ club.membership_fee_unpaid|pretty_money }}
+ {% endif %} {% endif %}
{% trans 'aliases'|capfirst %}