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 %}