Restructurate memberships, closes #16

This commit is contained in:
Yohann D'ANELLO 2020-03-31 23:54:14 +02:00
parent e98693b214
commit bf9789bd9e
10 changed files with 231 additions and 224 deletions

View File

@ -7,9 +7,9 @@
"email": "tresorerie.bde@example.com",
"require_memberships": true,
"membership_fee": 500,
"membership_duration": "396 00:00:00",
"membership_start": "213 00:00:00",
"membership_end": "273 00:00:00"
"membership_duration": 396,
"membership_start": "2019-08-31",
"membership_end": "2020-09-30"
}
},
{
@ -20,9 +20,9 @@
"email": "tresorerie.bde@example.com",
"require_memberships": true,
"membership_fee": 3500,
"membership_duration": "396 00:00:00",
"membership_start": "213 00:00:00",
"membership_end": "273 00:00:00"
"membership_duration": 396,
"membership_start": "2019-08-31",
"membership_end": "2020-09-30"
}
}
]

View File

@ -1,13 +1,10 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from crispy_forms.bootstrap import Div
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth.models import User
from note_kfet.inputs import Autocomplete, AmountInput
from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
from permission.models import PermissionMask
from .models import Profile, Club, Membership
@ -55,6 +52,8 @@ class ClubForm(forms.ModelForm):
'api_url': '/api/members/club/',
}
),
"membership_start": DatePickerInput(),
"membership_end": DatePickerInput(),
}
@ -80,28 +79,5 @@ class MembershipForm(forms.ModelForm):
'placeholder': 'Nom ...',
},
),
'date_start': DatePickerInput(),
}
MemberFormSet = forms.modelformset_factory(
Membership,
form=MembershipForm,
extra=2,
can_delete=True,
)
class FormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_tag = False
self.form_method = 'POST'
self.form_class = 'form-inline'
# self.template = 'bootstrap/table_inline_formset.html'
self.layout = Layout(
Div(
Div('user', css_class='col-sm-2'),
Div('roles', css_class='col-sm-2'),
Div('date_start', css_class='col-sm-2'),
css_class="row formset-row",
))

View File

@ -83,27 +83,31 @@ class Club(models.Model):
require_memberships = models.BooleanField(
default=True,
verbose_name=_("require memberships"),
help_text=_("Uncheck if this club don't require memberships."),
)
membership_fee = models.PositiveIntegerField(
default=0,
verbose_name=_('membership fee'),
)
membership_duration = models.DurationField(
membership_duration = models.IntegerField(
blank=True,
null=True,
verbose_name=_('membership duration'),
help_text=_('The longest time a membership can last '
help_text=_('The longest time (in days) a membership can last '
'(NULL = infinite).'),
)
membership_start = models.DurationField(
membership_start = models.DateField(
blank=True,
null=True,
verbose_name=_('membership start'),
help_text=_('How long after January 1st the members can renew '
'their membership.'),
)
membership_end = models.DurationField(
membership_end = models.DateField(
blank=True,
null=True,
verbose_name=_('membership end'),
@ -112,6 +116,20 @@ class Club(models.Model):
'membership.'),
)
def update_membership_dates(self):
"""
This function is called each time the club detail view is displayed.
Update the year of the membership dates.
"""
today = datetime.date.today()
if (today - self.membership_start).days >= 365:
self.membership_start = datetime.date(self.membership_start.year + 1,
self.membership_start.month, self.membership_start.day)
self.membership_end = datetime.date(self.membership_end.year + 1,
self.membership_end.month, self.membership_end.day)
self.save(force_update=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.require_memberships:
@ -135,9 +153,6 @@ class Club(models.Model):
class Role(models.Model):
"""
Role that an :model:`auth.User` can have in a :model:`member.Club`
TODO: Integrate the right management, and create some standard Roles at the
creation of the club.
"""
name = models.CharField(
verbose_name=_('name'),
@ -162,21 +177,25 @@ class Membership(models.Model):
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
)
club = models.ForeignKey(
Club,
on_delete=models.PROTECT,
)
roles = models.ForeignKey(
roles = models.ManyToManyField(
Role,
on_delete=models.PROTECT,
)
date_start = models.DateField(
verbose_name=_('membership starts on'),
)
date_end = models.DateField(
verbose_name=_('membership ends on'),
null=True,
)
fee = models.PositiveIntegerField(
verbose_name=_('fee'),
)
@ -189,8 +208,16 @@ class Membership(models.Model):
def save(self, *args, **kwargs):
if self.club.parent_club is not None:
if not Membership.objects.filter(user=self.user, club=self.club.parent_club):
if not Membership.objects.filter(user=self.user, club=self.club.parent_club).exists():
raise ValidationError(_('User is not a member of the parent club'))
created = not self.pk
if created:
self.fee = self.club.membership_fee
self.date_end = self.date_start + datetime.timedelta(days=self.club.membership_duration)
if self.date_end > self.club.membership_end:
self.date_end = self.club.membership_end
super().save(*args, **kwargs)
class Meta:

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import io
from datetime import datetime
from PIL import Image
from django.conf import settings
@ -24,8 +25,7 @@ from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin
from .filters import UserFilter, UserFilterFormHelper
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper, \
CustomAuthenticationForm
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
from .models import Club, Membership
from .tables import ClubTable, UserTable
@ -281,13 +281,19 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = context["club"]
if PermissionBackend().has_perm(self.request.user, "member.change_club_membership_start", club):
club.update_membership_dates()
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
context['history_list'] = HistoryTable(club_transactions)
club_member = Membership.objects.filter(club=club)\
.filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).all()
# TODO: consider only valid Membership
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()
context['member_list'] = club_member
return context
@ -328,31 +334,20 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = MembershipForm
template_name = 'member/add_members.html'
def get_queryset(self, **kwargs):
return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")
| PermissionBackend.filter_queryset(self.request.user, Membership,
"change"))
def get_context_data(self, **kwargs):
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
.get(pk=self.kwargs["pk"])
context = super().get_context_data(**kwargs)
context['formset'] = MemberFormSet()
context['helper'] = FormSetHelper()
context['club'] = club
context['no_cache'] = True
return context
def post(self, request, *args, **kwargs):
return
# TODO: Implement POST
# formset = MembershipFormset(request.POST)
# if formset.is_valid():
# return self.form_valid(formset)
# else:
# return self.form_invalid(formset)
def form_valid(self, form):
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
.get(pk=self.kwargs["pk"])
form.instance.club = club
return super().form_valid(form)
def form_valid(self, formset):
formset.save()
return super().form_valid(formset)
def get_success_url(self):
return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})

View File

@ -32,6 +32,7 @@ class PermissionBackend(ModelBackend):
for permission in Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \
.filter(
rolepermissions__role__membership__user=user,
rolepermissions__role__membership__valid=True,
model__app_label=model.app_label, # For polymorphic models, we don't filter on model type
type=type,
).all():

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-31 04:16+0200\n"
"POT-Creation-Date: 2020-03-31 23:49+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -44,11 +44,10 @@ 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:64 apps/member/models.py:122
#: apps/note/models/notes.py:186 apps/note/models/notes.py:224
#: apps/note/models/transactions.py:24 apps/note/models/transactions.py:44
#: apps/note/models/transactions.py:231 templates/member/club_info.html:13
#: templates/member/profile_info.html:14
#: apps/member/models.py:64 apps/member/models.py:158
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:231
#: templates/member/club_info.html:13 templates/member/profile_info.html:14
msgid "name"
msgstr ""
@ -73,14 +72,14 @@ msgstr ""
msgid "description"
msgstr ""
#: apps/activity/models.py:60 apps/note/models/notes.py:166
#: apps/activity/models.py:60 apps/note/models/notes.py:164
#: apps/note/models/transactions.py:62
#: templates/activity/activity_detail.html:19
msgid "type"
msgstr ""
#: apps/activity/models.py:66 apps/logs/models.py:21
#: apps/note/models/notes.py:119
#: apps/note/models/notes.py:117
msgid "user"
msgstr ""
@ -89,7 +88,7 @@ msgid "organizer"
msgstr ""
#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14
#: apps/note/models/notes.py:60
#: apps/note/models/notes.py:58
msgid "note"
msgstr ""
@ -179,7 +178,7 @@ msgstr ""
msgid "First name"
msgstr ""
#: apps/activity/tables.py:81 apps/note/models/notes.py:69
#: apps/activity/tables.py:81 apps/note/models/notes.py:67
msgid "Note"
msgstr ""
@ -227,12 +226,12 @@ msgstr ""
msgid "create"
msgstr ""
#: apps/logs/models.py:61 apps/note/tables.py:160
#: apps/logs/models.py:61 apps/note/tables.py:142
#: templates/activity/activity_detail.html:67
msgid "edit"
msgstr ""
#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:164
#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146
msgid "delete"
msgstr ""
@ -276,7 +275,7 @@ msgstr ""
msgid "user profile"
msgstr ""
#: apps/member/models.py:69 templates/member/club_info.html:36
#: apps/member/models.py:69 templates/member/club_info.html:38
msgid "email"
msgstr ""
@ -284,90 +283,97 @@ msgstr ""
msgid "parent club"
msgstr ""
#: apps/member/models.py:81 templates/member/club_info.html:30
msgid "membership fee"
msgstr ""
#: apps/member/models.py:85 templates/member/club_info.html:27
msgid "membership duration"
#: apps/member/models.py:85
msgid "require memberships"
msgstr ""
#: apps/member/models.py:86
msgid "The longest time a membership can last (NULL = infinite)."
msgid "Uncheck if this club don't require memberships."
msgstr ""
#: apps/member/models.py:91 templates/member/club_info.html:21
msgid "membership start"
#: apps/member/models.py:91 templates/member/club_info.html:31
msgid "membership fee"
msgstr ""
#: apps/member/models.py:92
msgid "How long after January 1st the members can renew their membership."
msgstr ""
#: apps/member/models.py:97 templates/member/club_info.html:24
msgid "membership end"
#: apps/member/models.py:97 templates/member/club_info.html:28
msgid "membership duration"
msgstr ""
#: apps/member/models.py:98
msgid "The longest time (in days) a membership can last (NULL = infinite)."
msgstr ""
#: apps/member/models.py:105 templates/member/club_info.html:22
msgid "membership start"
msgstr ""
#: apps/member/models.py:106
msgid "How long after January 1st the members can renew their membership."
msgstr ""
#: apps/member/models.py:113 templates/member/club_info.html:25
msgid "membership end"
msgstr ""
#: apps/member/models.py:114
msgid ""
"How long the membership can last after January 1st of the next year after "
"members can renew their membership."
msgstr ""
#: apps/member/models.py:104 apps/note/models/notes.py:141
#: apps/note/models/notes.py:195
#: apps/member/models.py:143 apps/note/models/notes.py:139
msgid "club"
msgstr ""
#: apps/member/models.py:105
#: apps/member/models.py:144
msgid "clubs"
msgstr ""
#: apps/member/models.py:128 apps/permission/models.py:284
#: apps/member/models.py:164 apps/permission/models.py:284
msgid "role"
msgstr ""
#: apps/member/models.py:129
#: apps/member/models.py:165
msgid "roles"
msgstr ""
#: apps/member/models.py:153
#: apps/member/models.py:191
msgid "membership starts on"
msgstr ""
#: apps/member/models.py:156
#: apps/member/models.py:195
msgid "membership ends on"
msgstr ""
#: apps/member/models.py:160
#: apps/member/models.py:200
msgid "fee"
msgstr ""
#: apps/member/models.py:172
#: apps/member/models.py:212
msgid "User is not a member of the parent club"
msgstr ""
#: apps/member/models.py:176
#: apps/member/models.py:224
msgid "membership"
msgstr ""
#: apps/member/models.py:177
#: apps/member/models.py:225
msgid "memberships"
msgstr ""
#: apps/member/views.py:78 templates/member/profile_info.html:45
#: apps/member/views.py:77 templates/member/profile_info.html:45
msgid "Update Profile"
msgstr ""
#: apps/member/views.py:91
#: apps/member/views.py:90
msgid "An alias with a similar name already exists."
msgstr ""
#: apps/note/admin.py:128 apps/note/models/transactions.py:94
#: apps/note/admin.py:120 apps/note/models/transactions.py:94
msgid "source"
msgstr ""
#: apps/note/admin.py:136 apps/note/admin.py:164
#: apps/note/admin.py:128 apps/note/admin.py:156
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107
msgid "destination"
msgstr ""
@ -380,104 +386,104 @@ msgstr ""
msgid "Maximal size: 2MB"
msgstr ""
#: apps/note/models/notes.py:29
#: apps/note/models/notes.py:27
msgid "account balance"
msgstr ""
#: apps/note/models/notes.py:30
#: apps/note/models/notes.py:28
msgid "in centimes, money credited for this instance"
msgstr ""
#: apps/note/models/notes.py:34
#: apps/note/models/notes.py:32
msgid "last negative date"
msgstr ""
#: apps/note/models/notes.py:35
#: apps/note/models/notes.py:33
msgid "last time the balance was negative"
msgstr ""
#: apps/note/models/notes.py:40
#: apps/note/models/notes.py:38
msgid "active"
msgstr ""
#: apps/note/models/notes.py:43
#: apps/note/models/notes.py:41
msgid ""
"Designates whether this note should be treated as active. Unselect this "
"instead of deleting notes."
msgstr ""
#: apps/note/models/notes.py:47
#: apps/note/models/notes.py:45
msgid "display image"
msgstr ""
#: apps/note/models/notes.py:55 apps/note/models/transactions.py:117
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117
msgid "created at"
msgstr ""
#: apps/note/models/notes.py:61
#: apps/note/models/notes.py:59
msgid "notes"
msgstr ""
#: apps/note/models/notes.py:79 apps/note/models/notes.py:103
#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
msgid "This alias is already taken."
msgstr ""
#: apps/note/models/notes.py:123
#: apps/note/models/notes.py:121
msgid "one's note"
msgstr ""
#: apps/note/models/notes.py:124
#: apps/note/models/notes.py:122
msgid "users note"
msgstr ""
#: apps/note/models/notes.py:130
#: apps/note/models/notes.py:128
#, python-format
msgid "%(user)s's note"
msgstr ""
#: apps/note/models/notes.py:145
#: apps/note/models/notes.py:143
msgid "club note"
msgstr ""
#: apps/note/models/notes.py:146
#: apps/note/models/notes.py:144
msgid "clubs notes"
msgstr ""
#: apps/note/models/notes.py:152
#: apps/note/models/notes.py:150
#, python-format
msgid "Note of %(club)s club"
msgstr ""
#: apps/note/models/notes.py:172
#: apps/note/models/notes.py:170
msgid "special note"
msgstr ""
#: apps/note/models/notes.py:173
#: apps/note/models/notes.py:171
msgid "special notes"
msgstr ""
#: apps/note/models/notes.py:230
#: apps/note/models/notes.py:194
msgid "Invalid alias"
msgstr ""
#: apps/note/models/notes.py:246
#: apps/note/models/notes.py:210
msgid "alias"
msgstr ""
#: apps/note/models/notes.py:247 templates/member/club_info.html:33
#: apps/note/models/notes.py:211 templates/member/club_info.html:35
#: templates/member/profile_info.html:36
msgid "aliases"
msgstr ""
#: apps/note/models/notes.py:269
#: apps/note/models/notes.py:233
msgid "Alias is too long."
msgstr ""
#: apps/note/models/notes.py:274
#: apps/note/models/notes.py:238
msgid "An alias with a similar name already exists: {} "
msgstr ""
#: apps/note/models/notes.py:287
#: apps/note/models/notes.py:251
msgid "You can't delete your main alias."
msgstr ""
@ -614,7 +620,7 @@ msgstr ""
#: templates/activity/activity_form.html:9
#: templates/activity/activity_invite.html:8
#: templates/django_filters/rest_framework/form.html:5
#: templates/member/club_form.html:9
#: templates/member/add_members.html:14 templates/member/club_form.html:9
#: templates/treasury/invoice_form.html:46
msgid "Submit"
msgstr ""
@ -882,23 +888,23 @@ msgstr ""
msgid "Club Parent"
msgstr ""
#: templates/member/club_info.html:39
msgid "linked notes"
#: templates/member/club_info.html:29
msgid "days"
msgstr ""
#: templates/member/club_info.html:44
#: templates/member/club_info.html:43
msgid "Add member"
msgstr ""
#: templates/member/club_info.html:45 templates/note/conso_form.html:121
#: templates/member/club_info.html:44 templates/note/conso_form.html:121
msgid "Edit"
msgstr ""
#: templates/member/club_info.html:46
#: templates/member/club_info.html:45
msgid "Add roles"
msgstr ""
#: templates/member/club_info.html:49 templates/member/profile_info.html:48
#: templates/member/club_info.html:48 templates/member/profile_info.html:48
msgid "View Profile"
msgstr ""

View File

@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-31 04:16+0200\n"
"POT-Creation-Date: 2020-03-31 23:49+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -40,11 +40,10 @@ 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:64 apps/member/models.py:122
#: apps/note/models/notes.py:186 apps/note/models/notes.py:224
#: apps/note/models/transactions.py:24 apps/note/models/transactions.py:44
#: apps/note/models/transactions.py:231 templates/member/club_info.html:13
#: templates/member/profile_info.html:14
#: apps/member/models.py:64 apps/member/models.py:158
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:231
#: templates/member/club_info.html:13 templates/member/profile_info.html:14
msgid "name"
msgstr "nom"
@ -69,14 +68,14 @@ msgstr "types d'activité"
msgid "description"
msgstr "description"
#: apps/activity/models.py:60 apps/note/models/notes.py:166
#: apps/activity/models.py:60 apps/note/models/notes.py:164
#: apps/note/models/transactions.py:62
#: templates/activity/activity_detail.html:19
msgid "type"
msgstr "type"
#: apps/activity/models.py:66 apps/logs/models.py:21
#: apps/note/models/notes.py:119
#: apps/note/models/notes.py:117
msgid "user"
msgstr "utilisateur"
@ -85,7 +84,7 @@ msgid "organizer"
msgstr "organisateur"
#: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14
#: apps/note/models/notes.py:60
#: apps/note/models/notes.py:58
msgid "note"
msgstr "note"
@ -175,7 +174,7 @@ msgstr "Nom de famille"
msgid "First name"
msgstr "Prénom"
#: apps/activity/tables.py:81 apps/note/models/notes.py:69
#: apps/activity/tables.py:81 apps/note/models/notes.py:67
msgid "Note"
msgstr "Note"
@ -223,12 +222,12 @@ msgstr "Nouvelles données"
msgid "create"
msgstr "Créer"
#: apps/logs/models.py:61 apps/note/tables.py:160
#: apps/logs/models.py:61 apps/note/tables.py:142
#: templates/activity/activity_detail.html:67
msgid "edit"
msgstr "Modifier"
#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:164
#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146
msgid "delete"
msgstr "Supprimer"
@ -272,7 +271,7 @@ msgstr "payé"
msgid "user profile"
msgstr "profil utilisateur"
#: apps/member/models.py:69 templates/member/club_info.html:36
#: apps/member/models.py:69 templates/member/club_info.html:38
msgid "email"
msgstr "courriel"
@ -280,33 +279,41 @@ msgstr "courriel"
msgid "parent club"
msgstr "club parent"
#: apps/member/models.py:81 templates/member/club_info.html:30
#: apps/member/models.py:85
msgid "require memberships"
msgstr "nécessite des adhésions"
#: apps/member/models.py:86
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:91 templates/member/club_info.html:31
msgid "membership fee"
msgstr "cotisation pour adhérer"
#: apps/member/models.py:85 templates/member/club_info.html:27
#: apps/member/models.py:97 templates/member/club_info.html:28
msgid "membership duration"
msgstr "durée de l'adhésion"
#: apps/member/models.py:86
msgid "The longest time a membership can last (NULL = infinite)."
msgstr "La durée maximale d'une adhésion (NULL = infinie)."
#: apps/member/models.py:98
msgid "The longest time (in days) a membership can last (NULL = infinite)."
msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)."
#: apps/member/models.py:91 templates/member/club_info.html:21
#: apps/member/models.py:105 templates/member/club_info.html:22
msgid "membership start"
msgstr "début de l'adhésion"
#: apps/member/models.py:92
#: apps/member/models.py:106
msgid "How long after January 1st the members can renew their membership."
msgstr ""
"Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur "
"adhésion."
#: apps/member/models.py:97 templates/member/club_info.html:24
#: apps/member/models.py:113 templates/member/club_info.html:25
msgid "membership end"
msgstr "fin de l'adhésion"
#: apps/member/models.py:98
#: apps/member/models.py:114
msgid ""
"How long the membership can last after January 1st of the next year after "
"members can renew their membership."
@ -314,60 +321,59 @@ 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:104 apps/note/models/notes.py:141
#: apps/note/models/notes.py:195
#: apps/member/models.py:143 apps/note/models/notes.py:139
msgid "club"
msgstr "club"
#: apps/member/models.py:105
#: apps/member/models.py:144
msgid "clubs"
msgstr "clubs"
#: apps/member/models.py:128 apps/permission/models.py:284
#: apps/member/models.py:164 apps/permission/models.py:284
msgid "role"
msgstr "rôle"
#: apps/member/models.py:129
#: apps/member/models.py:165
msgid "roles"
msgstr "rôles"
#: apps/member/models.py:153
#: apps/member/models.py:191
msgid "membership starts on"
msgstr "l'adhésion commence le"
#: apps/member/models.py:156
#: apps/member/models.py:195
msgid "membership ends on"
msgstr "l'adhésion finie le"
#: apps/member/models.py:160
#: apps/member/models.py:200
msgid "fee"
msgstr "cotisation"
#: apps/member/models.py:172
#: apps/member/models.py:212
msgid "User is not a member of the parent club"
msgstr "L'utilisateur n'est pas membre du club parent"
#: apps/member/models.py:176
#: apps/member/models.py:224
msgid "membership"
msgstr "adhésion"
#: apps/member/models.py:177
#: apps/member/models.py:225
msgid "memberships"
msgstr "adhésions"
#: apps/member/views.py:78 templates/member/profile_info.html:45
#: apps/member/views.py:77 templates/member/profile_info.html:45
msgid "Update Profile"
msgstr "Modifier le profil"
#: apps/member/views.py:91
#: apps/member/views.py:90
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
#: apps/note/admin.py:128 apps/note/models/transactions.py:94
#: apps/note/admin.py:120 apps/note/models/transactions.py:94
msgid "source"
msgstr "source"
#: apps/note/admin.py:136 apps/note/admin.py:164
#: apps/note/admin.py:128 apps/note/admin.py:156
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107
msgid "destination"
msgstr "destination"
@ -380,105 +386,105 @@ msgstr "Choisissez une image"
msgid "Maximal size: 2MB"
msgstr "Taille maximale : 2 Mo"
#: apps/note/models/notes.py:29
#: apps/note/models/notes.py:27
msgid "account balance"
msgstr "solde du compte"
#: apps/note/models/notes.py:30
#: apps/note/models/notes.py:28
msgid "in centimes, money credited for this instance"
msgstr "en centimes, argent crédité pour cette instance"
#: apps/note/models/notes.py:34
#: apps/note/models/notes.py:32
msgid "last negative date"
msgstr "dernier date de négatif"
#: apps/note/models/notes.py:35
#: apps/note/models/notes.py:33
msgid "last time the balance was negative"
msgstr "dernier instant où la note était en négatif"
#: apps/note/models/notes.py:40
#: apps/note/models/notes.py:38
msgid "active"
msgstr "actif"
#: apps/note/models/notes.py:43
#: apps/note/models/notes.py:41
msgid ""
"Designates whether this note should be treated as active. Unselect this "
"instead of deleting notes."
msgstr ""
"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
#: apps/note/models/notes.py:47
#: apps/note/models/notes.py:45
msgid "display image"
msgstr "image affichée"
#: apps/note/models/notes.py:55 apps/note/models/transactions.py:117
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117
msgid "created at"
msgstr "créée le"
#: apps/note/models/notes.py:61
#: apps/note/models/notes.py:59
msgid "notes"
msgstr "notes"
#: apps/note/models/notes.py:79 apps/note/models/notes.py:103
#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
msgid "This alias is already taken."
msgstr "Cet alias est déjà pris."
#: apps/note/models/notes.py:123
#: apps/note/models/notes.py:121
msgid "one's note"
msgstr "note d'un utilisateur"
#: apps/note/models/notes.py:124
#: apps/note/models/notes.py:122
msgid "users note"
msgstr "notes des utilisateurs"
#: apps/note/models/notes.py:130
#: apps/note/models/notes.py:128
#, python-format
msgid "%(user)s's note"
msgstr "Note de %(user)s"
#: apps/note/models/notes.py:145
#: apps/note/models/notes.py:143
msgid "club note"
msgstr "note d'un club"
#: apps/note/models/notes.py:146
#: apps/note/models/notes.py:144
msgid "clubs notes"
msgstr "notes des clubs"
#: apps/note/models/notes.py:152
#: apps/note/models/notes.py:150
#, python-format
msgid "Note of %(club)s club"
msgstr "Note du club %(club)s"
#: apps/note/models/notes.py:172
#: apps/note/models/notes.py:170
msgid "special note"
msgstr "note spéciale"
#: apps/note/models/notes.py:173
#: apps/note/models/notes.py:171
msgid "special notes"
msgstr "notes spéciales"
#: apps/note/models/notes.py:230
#: apps/note/models/notes.py:194
msgid "Invalid alias"
msgstr "Alias invalide"
#: apps/note/models/notes.py:246
#: apps/note/models/notes.py:210
msgid "alias"
msgstr "alias"
#: apps/note/models/notes.py:247 templates/member/club_info.html:33
#: apps/note/models/notes.py:211 templates/member/club_info.html:35
#: templates/member/profile_info.html:36
msgid "aliases"
msgstr "alias"
#: apps/note/models/notes.py:269
#: apps/note/models/notes.py:233
msgid "Alias is too long."
msgstr "L'alias est trop long."
#: apps/note/models/notes.py:274
#: apps/note/models/notes.py:238
msgid "An alias with a similar name already exists: {} "
msgstr "Un alias avec un nom similaire existe déjà : {}"
#: apps/note/models/notes.py:287
#: apps/note/models/notes.py:251
msgid "You can't delete your main alias."
msgstr "Vous ne pouvez pas supprimer votre alias principal."
@ -615,7 +621,7 @@ msgstr "Trésorerie"
#: templates/activity/activity_form.html:9
#: templates/activity/activity_invite.html:8
#: templates/django_filters/rest_framework/form.html:5
#: templates/member/club_form.html:9
#: templates/member/add_members.html:14 templates/member/club_form.html:9
#: templates/treasury/invoice_form.html:46
msgid "Submit"
msgstr "Envoyer"
@ -885,23 +891,23 @@ msgstr "Ajouter un alias"
msgid "Club Parent"
msgstr "Club parent"
#: templates/member/club_info.html:39
msgid "linked notes"
msgstr "notes liées"
#: templates/member/club_info.html:29
msgid "days"
msgstr "jours"
#: templates/member/club_info.html:44
#: templates/member/club_info.html:43
msgid "Add member"
msgstr "Ajouter un membre"
#: templates/member/club_info.html:45 templates/note/conso_form.html:121
#: templates/member/club_info.html:44 templates/note/conso_form.html:121
msgid "Edit"
msgstr "Éditer"
#: templates/member/club_info.html:46
#: templates/member/club_info.html:45
msgid "Add roles"
msgstr "Ajouter des rôles"
#: templates/member/club_info.html:49 templates/member/profile_info.html:48
#: templates/member/club_info.html:48 templates/member/profile_info.html:48
msgid "View Profile"
msgstr "Voir le profil"
@ -1210,3 +1216,6 @@ msgstr "Il n'y a pas de transaction associée à une remise ouverte."
#: templates/treasury/remittance_list.html:54
msgid "Closed remittances"
msgstr "Remises fermées"
#~ msgid "linked notes"
#~ msgstr "notes liées"

View File

@ -1,6 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import datetime
from json import dumps as json_dumps
from django.forms.widgets import DateTimeBaseInput, NumberInput, TextInput

View File

@ -1,29 +1,21 @@
{% extends "member/noteowner_detail.html" %}
{% load crispy_forms_tags %}
{% load static %}
{% load i18n %}
{% block profile_info %}
{% include "member/club_info.html" %}
{% endblock %}
{% block profile_content %}
{% block profile_content %}
<form method="post" action="">
{% csrf_token %}
{% crispy formset helper %}
<div class="form-actions">
<input type="submit" name="submit" value="Add Members" class="btn btn-primary" id="submit-save">
</div>
{{ form|crispy }}
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
</form>
{% endblock %}
{% block extrajavascript %}
<script src="{% static 'js/dynamic-formset.js' %}"></script>
<script>
$('.formset-row').formset({
addText: 'add another', // Text for the add link
deleteText: 'remove', // Text for the delete link
addCssClass: 'btn btn-primary', // CSS class applied to the add link
deleteCssClass: 'btn btn-danger h-50 my-auto',
});
</script>
{% endblock %}

View File

@ -26,7 +26,7 @@
<dd class="col-xl-6">{{ club.membership_end }}</dd>
<dt class="col-xl-6">{% trans 'membership duration'|capfirst %}</dt>
<dd class="col-xl-6">{{ club.membership_duration }}</dd>
<dd class="col-xl-6">{{ club.membership_duration }} {% trans "days" %}</dd>
<dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt>
<dd class="col-xl-6">{{ club.membership_fee|pretty_money }}</dd>