diff --git a/apps/activity/models.py b/apps/activity/models.py
index 86c30656..89a51d73 100644
--- a/apps/activity/models.py
+++ b/apps/activity/models.py
@@ -124,7 +124,7 @@ class Activity(models.Model):
Update the activity wiki page each time the activity is updated (validation, change description, ...)
"""
ret = super().save(*args, **kwargs)
- if self.pk and "scripts" in settings.INSTALLED_APPS:
+ if settings.DEBUG and self.pk and "scripts" in settings.INSTALLED_APPS:
def refresh_activities():
from scripts.management.commands.refresh_activities import Command as RefreshActivitiesCommand
RefreshActivitiesCommand.refresh_human_readable_wiki_page("Modification de l'activité " + self.name)
diff --git a/apps/activity/views.py b/apps/activity/views.py
index 170f8003..3d596a3f 100644
--- a/apps/activity/views.py
+++ b/apps/activity/views.py
@@ -4,26 +4,39 @@
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied
from django.db.models import F, Q
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
-from django.views.generic import CreateView, DetailView, TemplateView, UpdateView
+from django.views.generic import DetailView, TemplateView, UpdateView
from django_tables2.views import SingleTableView
from note.models import Alias, NoteSpecial, NoteUser
from permission.backends import PermissionBackend
-from permission.views import ProtectQuerysetMixin
+from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .forms import ActivityForm, GuestForm
from .models import Activity, Entry, Guest
from .tables import ActivityTable, EntryTable, GuestTable
-class ActivityCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class ActivityCreateView(LoginRequiredMixin, ProtectedCreateView):
model = Activity
form_class = ActivityForm
extra_context = {"title": _("Create new activity")}
+ def get_sample_object(self):
+ return Activity(
+ name="",
+ description="",
+ creater=self.request.user,
+ activity_type_id=1,
+ organizer_id=1,
+ attendees_club_id=1,
+ date_start=timezone.now(),
+ date_end=timezone.now(),
+ )
+
def form_valid(self, form):
form.instance.creater = self.request.user
return super().form_valid(form)
@@ -85,11 +98,20 @@ class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]})
-class ActivityInviteView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class ActivityInviteView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
model = Guest
form_class = GuestForm
template_name = "activity/activity_invite.html"
+ def get_sample_object(self):
+ activity = Activity.objects.get(pk=self.kwargs["pk"])
+ return Guest(
+ activity=activity,
+ first_name="",
+ last_name="",
+ inviter=self.request.user.note,
+ )
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
activity = context["form"].activity
@@ -114,6 +136,24 @@ class ActivityInviteView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
class ActivityEntryView(LoginRequiredMixin, TemplateView):
template_name = "activity/activity_entry.html"
+ def dispatch(self, request, *args, **kwargs):
+ """
+ Don't display the entry interface if the user has no right to see it (no right to add an entry for itself),
+ it is closed or doesn't manage entries.
+ """
+ activity = Activity.objects.get(pk=self.kwargs["pk"])
+
+ sample_entry = Entry(activity=activity, note=self.request.user.note)
+ if not PermissionBackend.check_perm(self.request.user, "activity.add_entry", sample_entry):
+ raise PermissionDenied(_("You are not allowed to display the entry interface for this activity."))
+
+ if not activity.activity_type.manage_entries:
+ raise PermissionDenied(_("This activity does not support activity entries."))
+
+ if not activity.open:
+ raise PermissionDenied(_("This activity is closed."))
+ return super().dispatch(request, *args, **kwargs)
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
diff --git a/apps/member/templates/member/club_list.html b/apps/member/templates/member/club_list.html
index 9b06e924..0ae4d5b8 100644
--- a/apps/member/templates/member/club_list.html
+++ b/apps/member/templates/member/club_list.html
@@ -1,12 +1,15 @@
{% extends "base.html" %}
{% load render_table from django_tables2 %}
{% load i18n %}
+
{% block content %}
diff --git a/apps/member/tests/test_login.py b/apps/member/tests/test_login.py
index 6b712687..51a4ab94 100644
--- a/apps/member/tests/test_login.py
+++ b/apps/member/tests/test_login.py
@@ -3,6 +3,7 @@
from django.contrib.auth.models import User
from django.test import TestCase
+from note.models import TransactionTemplate, TemplateCategory
"""
Test that login page still works
@@ -16,6 +17,8 @@ class TemplateLoggedOutTests(TestCase):
class TemplateLoggedInTests(TestCase):
+ fixtures = ('initial', )
+
def setUp(self):
self.user = User.objects.create_superuser(
username="admin",
@@ -48,5 +51,12 @@ class TemplateLoggedInTests(TestCase):
self.assertEqual(response.status_code, 200)
def test_consos_page(self):
+ # Create one button and ensure that it is visible
+ cat = TemplateCategory.objects.create()
+ TransactionTemplate.objects.create(
+ destination_id=5,
+ category=cat,
+ amount=0,
+ )
response = self.client.get('/note/consos/')
self.assertEqual(response.status_code, 200)
diff --git a/apps/member/views.py b/apps/member/views.py
index ebcd9d7b..332f0047 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -15,7 +15,7 @@ from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
-from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
+from django.views.generic import DetailView, UpdateView, TemplateView
from django.views.generic.edit import FormMixin
from django_tables2.views import SingleTableView
from rest_framework.authtoken.models import Token
@@ -26,7 +26,7 @@ from note.tables import HistoryTable, AliasTable
from note_kfet.middlewares import _set_current_user_and_ip
from permission.backends import PermissionBackend
from permission.models import Role
-from permission.views import ProtectQuerysetMixin
+from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm, UserForm, MembershipRolesForm
from .models import Club, Membership
@@ -295,7 +295,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
# ******************************* #
-class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create Club
"""
@@ -304,6 +304,12 @@ class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
success_url = reverse_lazy('member:club_list')
extra_context = {"title": _("Create new club")}
+ def get_sample_object(self):
+ return Club(
+ name="",
+ email="",
+ )
+
def form_valid(self, form):
return super().form_valid(form)
@@ -332,6 +338,14 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return qs
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context["can_add_club"] = PermissionBackend.check_perm(self.request.user, "member.add_club", Club(
+ name="",
+ email="club@example.com",
+ ))
+ return context
+
class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
@@ -432,7 +446,7 @@ class ClubPictureUpdateView(PictureUpdateView):
return reverse_lazy('member:club_detail', kwargs={'pk': self.object.id})
-class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Add a membership to a club.
"""
@@ -441,6 +455,19 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
template_name = 'member/add_members.html'
extra_context = {"title": _("Add new member to the club")}
+ def get_sample_object(self):
+ if "club_pk" in self.kwargs:
+ club = Club.objects.get(pk=self.kwargs["club_pk"])
+ else:
+ club = Membership.objects.get(pk=self.kwargs["pk"]).club
+ return Membership(
+ user=self.request.user,
+ club=club,
+ fee=0,
+ date_start=timezone.now(),
+ date_end=timezone.now() + timedelta(days=1),
+ )
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = context['form']
diff --git a/apps/note/views.py b/apps/note/views.py
index 169920e9..01fa9048 100644
--- a/apps/note/views.py
+++ b/apps/note/views.py
@@ -1,10 +1,12 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
+
import json
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied
from django.db.models import Q, F
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView, DetailView
@@ -145,6 +147,14 @@ class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
# Transaction history table
table_class = HistoryTable
+ def dispatch(self, request, *args, **kwargs):
+ templates = TransactionTemplate.objects.filter(
+ PermissionBackend().filter_queryset(self.request.user, TransactionTemplate, "view")
+ )
+ if not templates.exists():
+ raise PermissionDenied(_("You can't see any button."))
+ return super().dispatch(request, *args, **kwargs)
+
def get_queryset(self, **kwargs):
return Transaction.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
diff --git a/apps/permission/signals.py b/apps/permission/signals.py
index ef21c36e..112247eb 100644
--- a/apps/permission/signals.py
+++ b/apps/permission/signals.py
@@ -70,7 +70,7 @@ def pre_save_object(sender, instance, **kwargs):
if not has_perm:
raise PermissionDenied(
- _("You don't have the permission to add this instance of model {app_label}.{model_name}.")
+ _("You don't have the permission to add an instance of model {app_label}.{model_name}.")
.format(app_label=app_label, model_name=model_name, ))
diff --git a/apps/permission/tests/__init__.py b/apps/permission/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/permission/tests/test_permission_denied.py b/apps/permission/tests/test_permission_denied.py
new file mode 100644
index 00000000..80bf4129
--- /dev/null
+++ b/apps/permission/tests/test_permission_denied.py
@@ -0,0 +1,150 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from datetime import timedelta
+
+from django.contrib.auth.models import User
+from django.test import TestCase
+from django.urls import reverse
+from django.utils import timezone
+from activity.models import Activity
+from member.models import Club, Membership
+from note.models import NoteUser
+from wei.models import WEIClub, Bus, WEIRegistration
+
+
+class TestPermissionDenied(TestCase):
+ """
+ Load some protected pages and check that we have 403 errors.
+ """
+ fixtures = ('initial',)
+
+ def setUp(self) -> None:
+ # Create sample user with no rights
+ self.user = User.objects.create(
+ username="toto",
+ )
+ NoteUser.objects.create(user=self.user)
+ self.client.force_login(self.user)
+
+ def test_consos(self):
+ response = self.client.get(reverse("note:consos"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_activity(self):
+ response = self.client.get(reverse("activity:activity_create"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_activity_entries(self):
+ activity = Activity.objects.create(
+ name="",
+ description="",
+ creater=self.user,
+ activity_type_id=1,
+ organizer_id=1,
+ attendees_club_id=1,
+ date_start=timezone.now(),
+ date_end=timezone.now(),
+ )
+ response = self.client.get(reverse("activity:activity_entry", kwargs=dict(pk=activity.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_invite_activity(self):
+ activity = Activity.objects.create(
+ name="",
+ description="",
+ creater=self.user,
+ activity_type_id=1,
+ organizer_id=1,
+ attendees_club_id=1,
+ date_start=timezone.now(),
+ date_end=timezone.now(),
+ )
+ response = self.client.get(reverse("activity:activity_invite", kwargs=dict(pk=activity.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_club(self):
+ response = self.client.get(reverse("member:club_create"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_add_member_club(self):
+ club = Club.objects.create()
+ response = self.client.get(reverse("member:club_add_member", kwargs=dict(club_pk=club.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_renew_membership(self):
+ club = Club.objects.create()
+ membership = Membership.objects.create(user=self.user, club=club)
+ response = self.client.get(reverse("member:club_renew_membership", kwargs=dict(pk=membership.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_weiclub(self):
+ response = self.client.get(reverse("wei:wei_create"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_wei_bus(self):
+ wei = WEIClub.objects.create(
+ membership_start=timezone.now().date(),
+ date_start=timezone.now().date() + timedelta(days=1),
+ date_end=timezone.now().date() + timedelta(days=1),
+ )
+ response = self.client.get(reverse("wei:add_bus", kwargs=dict(pk=wei.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_wei_team(self):
+ wei = WEIClub.objects.create(
+ membership_start=timezone.now().date(),
+ date_start=timezone.now().date() + timedelta(days=1),
+ date_end=timezone.now().date() + timedelta(days=1),
+ )
+ bus = Bus.objects.create(wei=wei)
+ response = self.client.get(reverse("wei:add_team", kwargs=dict(pk=bus.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_1a_weiregistration(self):
+ wei = WEIClub.objects.create(
+ membership_start=timezone.now().date(),
+ date_start=timezone.now().date() + timedelta(days=1),
+ date_end=timezone.now().date() + timedelta(days=1),
+ )
+ response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=wei.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_old_weiregistration(self):
+ wei = WEIClub.objects.create(
+ membership_start=timezone.now().date(),
+ date_start=timezone.now().date() + timedelta(days=1),
+ date_end=timezone.now().date() + timedelta(days=1),
+ )
+ response = self.client.get(reverse("wei:wei_register_2A", kwargs=dict(wei_pk=wei.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_validate_weiregistration(self):
+ wei = WEIClub.objects.create(
+ membership_start=timezone.now().date(),
+ date_start=timezone.now().date() + timedelta(days=1),
+ date_end=timezone.now().date() + timedelta(days=1),
+ )
+ registration = WEIRegistration.objects.create(wei=wei, user=self.user, birth_date="2000-01-01")
+ response = self.client.get(reverse("wei:validate_registration", kwargs=dict(pk=registration.pk)))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_invoice(self):
+ response = self.client.get(reverse("treasury:invoice_create"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_list_invoices(self):
+ response = self.client.get(reverse("treasury:invoice_list"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_create_remittance(self):
+ response = self.client.get(reverse("treasury:remittance_create"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_list_remittance(self):
+ response = self.client.get(reverse("treasury:remittance_list"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_list_soge_credits(self):
+ response = self.client.get(reverse("treasury:soge_credits"))
+ self.assertEqual(response.status_code, 403)
diff --git a/apps/permission/test.py b/apps/permission/tests/test_permission_queries.py
similarity index 98%
rename from apps/permission/test.py
rename to apps/permission/tests/test_permission_queries.py
index e8452e8a..913893e7 100644
--- a/apps/permission/test.py
+++ b/apps/permission/tests/test_permission_queries.py
@@ -10,7 +10,7 @@ from member.models import Club, Membership
from note.models import NoteUser, Note, NoteClub, NoteSpecial
from wei.models import WEIMembership, WEIRegistration, WEIClub, Bus, BusTeam
-from .models import Permission
+from ..models import Permission
class PermissionQueryTestCase(TestCase):
diff --git a/apps/permission/views.py b/apps/permission/views.py
index 113f0443..4b59204b 100644
--- a/apps/permission/views.py
+++ b/apps/permission/views.py
@@ -1,12 +1,14 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
+
from datetime import date
+from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.forms import HiddenInput
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
-from django.views.generic import UpdateView, TemplateView
+from django.views.generic import UpdateView, TemplateView, CreateView
from member.models import Membership
from .backends import PermissionBackend
@@ -42,6 +44,30 @@ class ProtectQuerysetMixin:
return form
+class ProtectedCreateView(CreateView):
+ """
+ Extends a CreateView to check is the user has the right to create a sample instance of the given Model.
+ If not, a 403 error is displayed.
+ """
+
+ def get_sample_object(self):
+ """
+ return a sample instance of the Model.
+ It should be valid (can be stored properly in database), but must not collide with existing data.
+ """
+ raise NotImplementedError
+
+ def dispatch(self, request, *args, **kwargs):
+ model_class = self.model
+ # noinspection PyProtectedMember
+ app_label, model_name = model_class._meta.app_label, model_class._meta.model_name.lower()
+ perm = app_label + ".add_" + model_name
+ if not PermissionBackend.check_perm(request.user, perm, self.get_sample_object()):
+ raise PermissionDenied(_("You don't have the permission to add an instance of model "
+ "{app_label}.{model_name}.").format(app_label=app_label, model_name=model_name))
+ return super().dispatch(request, *args, **kwargs)
+
+
class RightsView(TemplateView):
template_name = "permission/all_rights.html"
extra_context = {"title": _("Rights")}
diff --git a/apps/treasury/views.py b/apps/treasury/views.py
index b95ce074..351253bf 100644
--- a/apps/treasury/views.py
+++ b/apps/treasury/views.py
@@ -8,28 +8,28 @@ from tempfile import mkdtemp
from crispy_forms.helper import FormHelper
from django.contrib.auth.mixins import LoginRequiredMixin
-from django.core.exceptions import ValidationError
+from django.core.exceptions import ValidationError, PermissionDenied
from django.db.models import Q
from django.forms import Form
from django.http import HttpResponse
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, UpdateView, DetailView
+from django.views.generic import UpdateView, DetailView
from django.views.generic.base import View, TemplateView
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView
from note.models import SpecialTransaction, NoteSpecial, Alias
from note_kfet.settings.base import BASE_DIR
from permission.backends import PermissionBackend
-from permission.views import ProtectQuerysetMixin
+from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm
from .models import Invoice, Product, Remittance, SpecialTransactionProxy, SogeCredit
from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable, SogeCreditTable
-class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create Invoice
"""
@@ -37,6 +37,15 @@ class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = InvoiceForm
extra_context = {"title": _("Create new invoice")}
+ def get_sample_object(self):
+ return Invoice(
+ id=0,
+ object="",
+ description="",
+ name="",
+ address="",
+ )
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -72,7 +81,7 @@ class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
return reverse_lazy('treasury:invoice_list')
-class InvoiceListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
+class InvoiceListView(LoginRequiredMixin, SingleTableView):
"""
List existing Invoices
"""
@@ -80,6 +89,18 @@ class InvoiceListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView)
table_class = InvoiceTable
extra_context = {"title": _("Invoices list")}
+ def dispatch(self, request, *args, **kwargs):
+ sample_invoice = Invoice(
+ id=0,
+ object="",
+ description="",
+ name="",
+ address="",
+ )
+ if not PermissionBackend.check_perm(self.request.user, "treasury.add_invoice", sample_invoice):
+ raise PermissionDenied(_("You are not able to see the treasury interface."))
+ return super().dispatch(request, *args, **kwargs)
+
class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
@@ -194,7 +215,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
return response
-class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create Remittance
"""
@@ -202,6 +223,12 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView)
form_class = RemittanceForm
extra_context = {"title": _("Create a new remittance")}
+ def get_sample_object(self):
+ return Remittance(
+ remittance_type_id=1,
+ comment="",
+ )
+
def get_success_url(self):
return reverse_lazy('treasury:remittance_list')
@@ -223,6 +250,15 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
template_name = "treasury/remittance_list.html"
extra_context = {"title": _("Remittances list")}
+ def dispatch(self, request, *args, **kwargs):
+ sample_remittance = Remittance(
+ remittance_type_id=1,
+ comment="",
+ )
+ if not PermissionBackend.check_perm(self.request.user, "treasury.add_remittance", sample_remittance):
+ raise PermissionDenied(_("You are not able to see the treasury interface."))
+ return super().dispatch(request, *args, **kwargs)
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -340,6 +376,11 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi
table_class = SogeCreditTable
extra_context = {"title": _("List of credits from the Société générale")}
+ def dispatch(self, request, *args, **kwargs):
+ if not self.get_queryset().exists():
+ raise PermissionDenied(_("You are not able to see the treasury interface."))
+ return super().dispatch(request, *args, **kwargs)
+
def get_queryset(self, **kwargs):
"""
Filter the table with the given parameter.
diff --git a/apps/wei/views.py b/apps/wei/views.py
index 696fece9..5b16fe3a 100644
--- a/apps/wei/views.py
+++ b/apps/wei/views.py
@@ -4,7 +4,7 @@
import os
import shutil
import subprocess
-from datetime import datetime, date
+from datetime import datetime, date, timedelta
from tempfile import mkdtemp
from django.contrib.auth.mixins import LoginRequiredMixin
@@ -19,7 +19,7 @@ from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils import timezone
from django.views import View
-from django.views.generic import DetailView, UpdateView, CreateView, RedirectView, TemplateView
+from django.views.generic import DetailView, UpdateView, RedirectView, TemplateView
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView
@@ -28,7 +28,7 @@ from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSp
from note.tables import HistoryTable
from note_kfet.settings import BASE_DIR
from permission.backends import PermissionBackend
-from permission.views import ProtectQuerysetMixin
+from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .forms.registration import WEIChooseBusForm
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
@@ -58,6 +58,8 @@ class WEIListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["can_create_wei"] = PermissionBackend.check_perm(self.request.user, "wei.add_weiclub", WEIClub(
+ name="",
+ email="weiclub@example.com",
year=0,
date_start=timezone.now().date(),
date_end=timezone.now().date(),
@@ -65,14 +67,24 @@ class WEIListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return context
-class WEICreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class WEICreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create WEI
"""
+
model = WEIClub
form_class = WEIForm
extra_context = {"title": _("Create WEI")}
+ def get_sample_object(self):
+ return WEIClub(
+ name="",
+ email="weiclub@example.com",
+ year=0,
+ date_start=timezone.now().date(),
+ date_end=timezone.now().date(),
+ )
+
def form_valid(self, form):
form.instance.requires_membership = True
form.instance.parent_club = Club.objects.get(name="Kfet")
@@ -274,7 +286,7 @@ class WEIUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.pk})
-class BusCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class BusCreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create Bus
"""
@@ -282,6 +294,13 @@ class BusCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = BusForm
extra_context = {"title": _("Create new bus")}
+ def get_sample_object(self):
+ wei = WEIClub.objects.get(pk=self.kwargs["pk"])
+ return Bus(
+ wei=wei,
+ name="",
+ )
+
def dispatch(self, request, *args, **kwargs):
wei = WEIClub.objects.get(pk=self.kwargs["pk"])
today = date.today()
@@ -362,7 +381,7 @@ class BusManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
return context
-class BusTeamCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class BusTeamCreateView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Create BusTeam
"""
@@ -370,6 +389,14 @@ class BusTeamCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = BusTeamForm
extra_context = {"title": _("Create new team")}
+ def get_sample_object(self):
+ bus = Bus.objects.get(pk=self.kwargs["pk"])
+ return BusTeam(
+ name="",
+ bus=bus,
+ color=0,
+ )
+
def dispatch(self, request, *args, **kwargs):
wei = WEIClub.objects.get(buses__pk=self.kwargs["pk"])
today = date.today()
@@ -447,7 +474,7 @@ class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
return context
-class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Register a new user to the WEI
"""
@@ -455,6 +482,18 @@ class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = WEIRegistrationForm
extra_context = {"title": _("Register first year student to the WEI")}
+ def get_sample_object(self):
+ wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
+ return WEIRegistration(
+ wei=wei,
+ user=self.request.user,
+ first_year=True,
+ birth_date="1970-01-01",
+ gender="No",
+ emergency_contact_name="No",
+ emergency_contact_phone="No",
+ )
+
def dispatch(self, request, *args, **kwargs):
wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
today = date.today()
@@ -502,7 +541,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
-class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Register an old user to the WEI
"""
@@ -510,6 +549,18 @@ class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form_class = WEIRegistrationForm
extra_context = {"title": _("Register old student to the WEI")}
+ def get_sample_object(self):
+ wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
+ return WEIRegistration(
+ wei=wei,
+ user=self.request.user,
+ first_year=True,
+ birth_date="1970-01-01",
+ gender="No",
+ emergency_contact_name="No",
+ emergency_contact_phone="No",
+ )
+
def dispatch(self, request, *args, **kwargs):
wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
today = date.today()
@@ -713,7 +764,7 @@ class WEIDeleteRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Delete
return reverse_lazy('wei:wei_detail', args=(self.object.wei.pk,))
-class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
+class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, ProtectedCreateView):
"""
Validate WEI Registration
"""
@@ -721,6 +772,17 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Crea
form_class = WEIMembershipForm
extra_context = {"title": _("Validate WEI registration")}
+ def get_sample_object(self):
+ registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
+ return WEIMembership(
+ club=registration.wei,
+ user=registration.user,
+ date_start=timezone.now().date(),
+ date_end=timezone.now().date() + timedelta(days=1),
+ fee=0,
+ registration=registration,
+ )
+
def dispatch(self, request, *args, **kwargs):
wei = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
today = date.today()
diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po
index 4486c987..59a41d73 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-08-10 19:56+0200\n"
+"POT-Creation-Date: 2020-08-13 15:15+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -327,27 +327,39 @@ msgstr ""
msgid "All activities"
msgstr ""
-#: apps/activity/views.py:25
+#: apps/activity/views.py:26
msgid "Create new activity"
msgstr ""
-#: apps/activity/views.py:40 note_kfet/templates/base.html:128
+#: apps/activity/views.py:53 note_kfet/templates/base.html:128
msgid "Activities"
msgstr ""
-#: apps/activity/views.py:65
+#: apps/activity/views.py:78
msgid "Activity detail"
msgstr ""
-#: apps/activity/views.py:82
+#: apps/activity/views.py:95
msgid "Update activity"
msgstr ""
-#: apps/activity/views.py:96
+#: apps/activity/views.py:118
msgid "Invite guest to the activity \"{}\""
msgstr ""
-#: apps/activity/views.py:197
+#: apps/activity/views.py:148
+msgid "You are not allowed to display the entry interface for this activity."
+msgstr ""
+
+#: apps/activity/views.py:151
+msgid "This activity does not support activity entries."
+msgstr ""
+
+#: apps/activity/views.py:154
+msgid "This activity is closed."
+msgstr ""
+
+#: apps/activity/views.py:237
msgid "Entry for activity \"{}\""
msgstr ""
@@ -682,7 +694,7 @@ msgstr ""
msgid "The role {role} does not apply to the club {club}."
msgstr ""
-#: apps/member/models.py:362 apps/member/views.py:592
+#: apps/member/models.py:362 apps/member/views.py:619
msgid "User is already a member of the club"
msgstr ""
@@ -774,11 +786,11 @@ msgstr ""
msgid "View Profile"
msgstr ""
-#: apps/member/templates/member/club_list.html:9
+#: apps/member/templates/member/club_list.html:11
msgid "Create club"
msgstr ""
-#: apps/member/templates/member/club_list.html:16
+#: apps/member/templates/member/club_list.html:19
msgid "Club listing"
msgstr ""
@@ -890,7 +902,7 @@ msgstr ""
msgid "Search user"
msgstr ""
-#: apps/member/views.py:205 apps/member/views.py:391
+#: apps/member/views.py:205 apps/member/views.py:405
msgid "Note aliases"
msgstr ""
@@ -902,47 +914,47 @@ msgstr ""
msgid "Create new club"
msgstr ""
-#: apps/member/views.py:317
+#: apps/member/views.py:323
msgid "Search club"
msgstr ""
-#: apps/member/views.py:342
+#: apps/member/views.py:356
msgid "Club detail"
msgstr ""
-#: apps/member/views.py:408
+#: apps/member/views.py:422
msgid "Update club"
msgstr ""
-#: apps/member/views.py:442
+#: apps/member/views.py:456
msgid "Add new member to the club"
msgstr ""
-#: apps/member/views.py:583 apps/wei/views.py:861
+#: apps/member/views.py:610 apps/wei/views.py:926
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
msgstr ""
-#: apps/member/views.py:596
+#: apps/member/views.py:623
msgid "The membership must start after {:%m-%d-%Y}."
msgstr ""
-#: apps/member/views.py:601
+#: apps/member/views.py:628
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr ""
-#: apps/member/views.py:618 apps/member/views.py:620 apps/member/views.py:622
+#: apps/member/views.py:645 apps/member/views.py:647 apps/member/views.py:649
#: apps/registration/views.py:292 apps/registration/views.py:294
-#: apps/registration/views.py:296 apps/wei/views.py:866 apps/wei/views.py:870
+#: apps/registration/views.py:296 apps/wei/views.py:931 apps/wei/views.py:935
msgid "This field is required."
msgstr ""
-#: apps/member/views.py:706
+#: apps/member/views.py:733
msgid "Manage roles of an user in the club"
msgstr ""
-#: apps/member/views.py:731
+#: apps/member/views.py:758
msgid "Members of the club"
msgstr ""
@@ -1223,7 +1235,7 @@ msgstr ""
#: apps/note/tables.py:138 apps/note/tables.py:172 apps/treasury/tables.py:39
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:28
#: apps/treasury/templates/treasury/sogecredit_detail.html:59
-#: apps/wei/tables.py:75 apps/wei/tables.py:101
+#: apps/wei/tables.py:75 apps/wei/tables.py:102
#: apps/wei/templates/wei/weiregistration_confirm_delete.html:32
msgid "Delete"
msgstr ""
@@ -1340,27 +1352,31 @@ msgstr ""
msgid "Unable to delete button "
msgstr ""
-#: apps/note/views.py:34
+#: apps/note/views.py:36
msgid "Transfer money"
msgstr ""
-#: apps/note/views.py:74
+#: apps/note/views.py:76
msgid "Create new button"
msgstr ""
-#: apps/note/views.py:83
+#: apps/note/views.py:85
msgid "Search button"
msgstr ""
-#: apps/note/views.py:106
+#: apps/note/views.py:108
msgid "Update button"
msgstr ""
-#: apps/note/views.py:143 note_kfet/templates/base.html:108
+#: apps/note/views.py:145 note_kfet/templates/base.html:108
msgid "Consumptions"
msgstr ""
-#: apps/note/views.py:179
+#: apps/note/views.py:155
+msgid "You can't see any button."
+msgstr ""
+
+#: apps/note/views.py:189
msgid "Search transactions"
msgstr ""
@@ -1448,10 +1464,10 @@ msgid ""
"of model {app_label}.{model_name}."
msgstr ""
-#: apps/permission/signals.py:73
+#: apps/permission/signals.py:73 apps/permission/views.py:66
#, python-brace-format
msgid ""
-"You don't have the permission to add this instance of model {app_label}."
+"You don't have the permission to add an instance of model {app_label}."
"{model_name}."
msgstr ""
@@ -1490,11 +1506,11 @@ msgstr ""
msgid "No associated permission"
msgstr ""
-#: apps/permission/views.py:47 note_kfet/templates/base.html:143
+#: apps/permission/views.py:73 note_kfet/templates/base.html:143
msgid "Rights"
msgstr ""
-#: apps/permission/views.py:52
+#: apps/permission/views.py:78
msgid "All rights"
msgstr ""
@@ -1864,7 +1880,7 @@ msgid "No"
msgstr ""
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:8
-#: apps/treasury/views.py:143
+#: apps/treasury/views.py:164
msgid "Delete invoice"
msgstr ""
@@ -1882,7 +1898,7 @@ msgid "Return to invoices list"
msgstr ""
#: apps/treasury/templates/treasury/invoice_form.html:6
-#: apps/treasury/views.py:81
+#: apps/treasury/views.py:90
msgid "Invoices list"
msgstr ""
@@ -1916,7 +1932,7 @@ msgstr ""
#: apps/treasury/templates/treasury/remittance_form.html:9
#: apps/treasury/templates/treasury/specialtransactionproxy_form.html:7
-#: apps/treasury/views.py:224
+#: apps/treasury/views.py:251
msgid "Remittances list"
msgstr ""
@@ -2009,7 +2025,7 @@ msgid "Please ask the user to credit its note before deleting this credit."
msgstr ""
#: apps/treasury/templates/treasury/sogecredit_detail.html:57
-#: apps/wei/tables.py:58 apps/wei/tables.py:59 apps/wei/tables.py:96
+#: apps/wei/tables.py:58 apps/wei/tables.py:59 apps/wei/tables.py:98
msgid "Validate"
msgstr ""
@@ -2029,27 +2045,32 @@ msgstr ""
msgid "Create new invoice"
msgstr ""
-#: apps/treasury/views.py:90
+#: apps/treasury/views.py:101 apps/treasury/views.py:259
+#: apps/treasury/views.py:381
+msgid "You are not able to see the treasury interface."
+msgstr ""
+
+#: apps/treasury/views.py:111
msgid "Update an invoice"
msgstr ""
-#: apps/treasury/views.py:203
+#: apps/treasury/views.py:224
msgid "Create a new remittance"
msgstr ""
-#: apps/treasury/views.py:274
+#: apps/treasury/views.py:310
msgid "Update a remittance"
msgstr ""
-#: apps/treasury/views.py:297
+#: apps/treasury/views.py:333
msgid "Attach a transaction to a remittance"
msgstr ""
-#: apps/treasury/views.py:341
+#: apps/treasury/views.py:377
msgid "List of credits from the Société générale"
msgstr ""
-#: apps/treasury/views.py:375
+#: apps/treasury/views.py:416
msgid "Manage credits from the Société générale"
msgstr ""
@@ -2287,8 +2308,8 @@ msgstr ""
#: apps/wei/templates/wei/survey.html:12
#: apps/wei/templates/wei/survey_closed.html:12
-#: apps/wei/templates/wei/survey_end.html:12 apps/wei/views.py:917
-#: apps/wei/views.py:971 apps/wei/views.py:981
+#: apps/wei/templates/wei/survey_end.html:12 apps/wei/views.py:982
+#: apps/wei/views.py:1036 apps/wei/views.py:1046
msgid "Survey WEI"
msgstr ""
@@ -2320,11 +2341,11 @@ msgstr ""
msgid "WEI list"
msgstr ""
-#: apps/wei/templates/wei/weiclub_info.html:62 apps/wei/views.py:468
+#: apps/wei/templates/wei/weiclub_info.html:62 apps/wei/views.py:507
msgid "Register 1A"
msgstr ""
-#: apps/wei/templates/wei/weiclub_info.html:65 apps/wei/views.py:523
+#: apps/wei/templates/wei/weiclub_info.html:65 apps/wei/views.py:574
msgid "Register 2A+"
msgstr ""
@@ -2336,7 +2357,7 @@ msgstr ""
msgid "View WEI"
msgstr ""
-#: apps/wei/templates/wei/weiclub_list.html:10 apps/wei/views.py:74
+#: apps/wei/templates/wei/weiclub_list.html:10 apps/wei/views.py:77
msgid "Create WEI"
msgstr ""
@@ -2489,93 +2510,93 @@ msgstr ""
msgid "Search WEI"
msgstr ""
-#: apps/wei/views.py:94
+#: apps/wei/views.py:106
msgid "WEI Detail"
msgstr ""
-#: apps/wei/views.py:189
+#: apps/wei/views.py:201
msgid "View members of the WEI"
msgstr ""
-#: apps/wei/views.py:217
+#: apps/wei/views.py:229
msgid "Find WEI Membership"
msgstr ""
-#: apps/wei/views.py:227
+#: apps/wei/views.py:239
msgid "View registrations to the WEI"
msgstr ""
-#: apps/wei/views.py:251
+#: apps/wei/views.py:263
msgid "Find WEI Registration"
msgstr ""
-#: apps/wei/views.py:262
+#: apps/wei/views.py:274
msgid "Update the WEI"
msgstr ""
-#: apps/wei/views.py:283
+#: apps/wei/views.py:295
msgid "Create new bus"
msgstr ""
-#: apps/wei/views.py:314
+#: apps/wei/views.py:333
msgid "Update bus"
msgstr ""
-#: apps/wei/views.py:344
+#: apps/wei/views.py:363
msgid "Manage bus"
msgstr ""
-#: apps/wei/views.py:371
+#: apps/wei/views.py:390
msgid "Create new team"
msgstr ""
-#: apps/wei/views.py:403
+#: apps/wei/views.py:430
msgid "Update team"
msgstr ""
-#: apps/wei/views.py:434
+#: apps/wei/views.py:461
msgid "Manage WEI team"
msgstr ""
-#: apps/wei/views.py:456
+#: apps/wei/views.py:483
msgid "Register first year student to the WEI"
msgstr ""
-#: apps/wei/views.py:489 apps/wei/views.py:560
+#: apps/wei/views.py:528 apps/wei/views.py:611
msgid "This user is already registered to this WEI."
msgstr ""
-#: apps/wei/views.py:494
+#: apps/wei/views.py:533
msgid ""
-"This user can't be in her/his first year since he/she has already participated "
-"to a WEI."
+"This user can't be in her/his first year since he/she has already "
+"participated to a WEI."
msgstr ""
-#: apps/wei/views.py:511
+#: apps/wei/views.py:550
msgid "Register old student to the WEI"
msgstr ""
-#: apps/wei/views.py:542 apps/wei/views.py:627
+#: apps/wei/views.py:593 apps/wei/views.py:684
msgid "You already opened an account in the Société générale."
msgstr ""
-#: apps/wei/views.py:590
+#: apps/wei/views.py:641
msgid "Update WEI Registration"
msgstr ""
-#: apps/wei/views.py:686
+#: apps/wei/views.py:743
msgid "Delete WEI registration"
msgstr ""
-#: apps/wei/views.py:697
+#: apps/wei/views.py:754
msgid "You don't have the right to delete this WEI registration."
msgstr ""
-#: apps/wei/views.py:716
+#: apps/wei/views.py:773
msgid "Validate WEI registration"
msgstr ""
-#: apps/wei/views.py:855
+#: apps/wei/views.py:920
msgid "This user didn't give her/his caution check."
msgstr ""
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 527f3c37..9556dd6e 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/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-08-10 19:56+0200\n"
+"POT-Creation-Date: 2020-08-13 15:15+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -328,27 +328,39 @@ msgstr "Nouvelle activité"
msgid "All activities"
msgstr "Toutes les activités"
-#: apps/activity/views.py:25
+#: apps/activity/views.py:26
msgid "Create new activity"
msgstr "Créer une nouvelle activité"
-#: apps/activity/views.py:40 note_kfet/templates/base.html:128
+#: apps/activity/views.py:53 note_kfet/templates/base.html:128
msgid "Activities"
msgstr "Activités"
-#: apps/activity/views.py:65
+#: apps/activity/views.py:78
msgid "Activity detail"
msgstr "Détails de l'activité"
-#: apps/activity/views.py:82
+#: apps/activity/views.py:95
msgid "Update activity"
msgstr "Modifier l'activité"
-#: apps/activity/views.py:96
+#: apps/activity/views.py:118
msgid "Invite guest to the activity \"{}\""
msgstr "Invitation pour l'activité « {} »"
-#: apps/activity/views.py:197
+#: apps/activity/views.py:148
+msgid "You are not allowed to display the entry interface for this activity."
+msgstr "Vous n'êtes pas autorisé à afficher l'interface des entrées pour cette activité."
+
+#: apps/activity/views.py:151
+msgid "This activity does not support activity entries."
+msgstr "Cette activité ne requiert pas d'entrées."
+
+#: apps/activity/views.py:154
+msgid "This activity is closed."
+msgstr "Cette activité est fermée."
+
+#: apps/activity/views.py:237
msgid "Entry for activity \"{}\""
msgstr "Entrées pour l'activité « {} »"
@@ -686,7 +698,7 @@ msgstr "l'adhésion finit le"
msgid "The role {role} does not apply to the club {club}."
msgstr "Le rôle {role} ne s'applique pas au club {club}."
-#: apps/member/models.py:362 apps/member/views.py:592
+#: apps/member/models.py:362 apps/member/views.py:619
msgid "User is already a member of the club"
msgstr "L'utilisateur est déjà membre du club"
@@ -783,11 +795,11 @@ msgstr "Éditer"
msgid "View Profile"
msgstr "Voir le profil"
-#: apps/member/templates/member/club_list.html:9
+#: apps/member/templates/member/club_list.html:11
msgid "Create club"
msgstr "Créer un club"
-#: apps/member/templates/member/club_list.html:16
+#: apps/member/templates/member/club_list.html:19
msgid "Club listing"
msgstr "Liste des clubs"
@@ -899,7 +911,7 @@ msgstr "Détails de l'utilisateur"
msgid "Search user"
msgstr "Chercher un utilisateur"
-#: apps/member/views.py:205 apps/member/views.py:391
+#: apps/member/views.py:205 apps/member/views.py:405
msgid "Note aliases"
msgstr "Alias de la note"
@@ -911,23 +923,23 @@ msgstr "Modifier la photo de la note"
msgid "Create new club"
msgstr "Créer un nouveau club"
-#: apps/member/views.py:317
+#: apps/member/views.py:323
msgid "Search club"
msgstr "Chercher un club"
-#: apps/member/views.py:342
+#: apps/member/views.py:356
msgid "Club detail"
msgstr "Détails du club"
-#: apps/member/views.py:408
+#: apps/member/views.py:422
msgid "Update club"
msgstr "Modifier le club"
-#: apps/member/views.py:442
+#: apps/member/views.py:456
msgid "Add new member to the club"
msgstr "Ajouter un nouveau membre au club"
-#: apps/member/views.py:583 apps/wei/views.py:861
+#: apps/member/views.py:610 apps/wei/views.py:926
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@@ -935,25 +947,25 @@ 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:596
+#: apps/member/views.py:623
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:601
+#: apps/member/views.py:628
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
-#: apps/member/views.py:618 apps/member/views.py:620 apps/member/views.py:622
+#: apps/member/views.py:645 apps/member/views.py:647 apps/member/views.py:649
#: apps/registration/views.py:292 apps/registration/views.py:294
-#: apps/registration/views.py:296 apps/wei/views.py:866 apps/wei/views.py:870
+#: apps/registration/views.py:296 apps/wei/views.py:931 apps/wei/views.py:935
msgid "This field is required."
msgstr "Ce champ est requis."
-#: apps/member/views.py:706
+#: apps/member/views.py:733
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:731
+#: apps/member/views.py:758
msgid "Members of the club"
msgstr "Membres du club"
@@ -1241,7 +1253,7 @@ msgstr "Pas de motif spécifié"
#: apps/note/tables.py:138 apps/note/tables.py:172 apps/treasury/tables.py:39
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:28
#: apps/treasury/templates/treasury/sogecredit_detail.html:59
-#: apps/wei/tables.py:75 apps/wei/tables.py:101
+#: apps/wei/tables.py:75 apps/wei/tables.py:102
#: apps/wei/templates/wei/weiregistration_confirm_delete.html:32
msgid "Delete"
msgstr "Supprimer"
@@ -1358,27 +1370,31 @@ msgstr "Le bouton a bien été supprimé"
msgid "Unable to delete button "
msgstr "Impossible de supprimer le bouton "
-#: apps/note/views.py:34
+#: apps/note/views.py:36
msgid "Transfer money"
msgstr "Transférer de l'argent"
-#: apps/note/views.py:74
+#: apps/note/views.py:76
msgid "Create new button"
msgstr "Créer un nouveau bouton"
-#: apps/note/views.py:83
+#: apps/note/views.py:85
msgid "Search button"
msgstr "Chercher un bouton"
-#: apps/note/views.py:106
+#: apps/note/views.py:108
msgid "Update button"
msgstr "Modifier le bouton"
-#: apps/note/views.py:143 note_kfet/templates/base.html:108
+#: apps/note/views.py:145 note_kfet/templates/base.html:108
msgid "Consumptions"
msgstr "Consommations"
-#: apps/note/views.py:179
+#: apps/note/views.py:155
+msgid "You can't see any button."
+msgstr "Vous ne pouvez pas voir le moindre bouton."
+
+#: apps/note/views.py:189
msgid "Search transactions"
msgstr "Rechercher des transactions"
@@ -1472,13 +1488,13 @@ 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:73
+#: apps/permission/signals.py:73 apps/permission/views.py:66
#, python-brace-format
msgid ""
-"You don't have the permission to add this instance of model {app_label}."
+"You don't have the permission to add an instance of model {app_label}."
"{model_name}."
msgstr ""
-"Vous n'avez pas la permission d'ajouter cette instance du modèle {app_label}."
+"Vous n'avez pas la permission d'ajouter une instance du modèle {app_label}."
"{model_name}."
#: apps/permission/signals.py:101
@@ -1518,11 +1534,11 @@ msgstr "Requête :"
msgid "No associated permission"
msgstr "Pas de permission associée"
-#: apps/permission/views.py:47 note_kfet/templates/base.html:143
+#: apps/permission/views.py:73 note_kfet/templates/base.html:143
msgid "Rights"
msgstr "Droits"
-#: apps/permission/views.py:52
+#: apps/permission/views.py:78
msgid "All rights"
msgstr "Tous les droits"
@@ -1912,7 +1928,7 @@ msgid "No"
msgstr "Non"
#: apps/treasury/templates/treasury/invoice_confirm_delete.html:8
-#: apps/treasury/views.py:143
+#: apps/treasury/views.py:164
msgid "Delete invoice"
msgstr "Supprimer la facture"
@@ -1932,7 +1948,7 @@ msgid "Return to invoices list"
msgstr "Retour à la liste des factures"
#: apps/treasury/templates/treasury/invoice_form.html:6
-#: apps/treasury/views.py:81
+#: apps/treasury/views.py:90
msgid "Invoices list"
msgstr "Liste des factures"
@@ -1969,7 +1985,7 @@ msgstr "Remise n°"
#: apps/treasury/templates/treasury/remittance_form.html:9
#: apps/treasury/templates/treasury/specialtransactionproxy_form.html:7
-#: apps/treasury/views.py:224
+#: apps/treasury/views.py:251
msgid "Remittances list"
msgstr "Liste des remises"
@@ -2073,7 +2089,7 @@ msgstr ""
"demande de crédit."
#: apps/treasury/templates/treasury/sogecredit_detail.html:57
-#: apps/wei/tables.py:58 apps/wei/tables.py:59 apps/wei/tables.py:96
+#: apps/wei/tables.py:58 apps/wei/tables.py:59 apps/wei/tables.py:98
msgid "Validate"
msgstr "Valider"
@@ -2095,27 +2111,32 @@ msgstr ""
msgid "Create new invoice"
msgstr "Créer une nouvelle facture"
-#: apps/treasury/views.py:90
+#: apps/treasury/views.py:101 apps/treasury/views.py:259
+#: apps/treasury/views.py:381
+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:111
msgid "Update an invoice"
msgstr "Modifier la facture"
-#: apps/treasury/views.py:203
+#: apps/treasury/views.py:224
msgid "Create a new remittance"
msgstr "Créer une nouvelle remise"
-#: apps/treasury/views.py:274
+#: apps/treasury/views.py:310
msgid "Update a remittance"
msgstr "Modifier la remise"
-#: apps/treasury/views.py:297
+#: apps/treasury/views.py:333
msgid "Attach a transaction to a remittance"
msgstr "Joindre une transaction à une remise"
-#: apps/treasury/views.py:341
+#: apps/treasury/views.py:377
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:375
+#: apps/treasury/views.py:416
msgid "Manage credits from the Société générale"
msgstr "Gérer les crédits de la Société générale"
@@ -2368,8 +2389,8 @@ msgstr "Télécharger au format PDF"
#: apps/wei/templates/wei/survey.html:12
#: apps/wei/templates/wei/survey_closed.html:12
-#: apps/wei/templates/wei/survey_end.html:12 apps/wei/views.py:917
-#: apps/wei/views.py:971 apps/wei/views.py:981
+#: apps/wei/templates/wei/survey_end.html:12 apps/wei/views.py:982
+#: apps/wei/views.py:1036 apps/wei/views.py:1046
msgid "Survey WEI"
msgstr "Questionnaire WEI"
@@ -2402,11 +2423,11 @@ msgstr "Prix du WEI / incluant l'adhésion BDE/Kfet (étudiants)"
msgid "WEI list"
msgstr "Liste des WEI"
-#: apps/wei/templates/wei/weiclub_info.html:62 apps/wei/views.py:468
+#: apps/wei/templates/wei/weiclub_info.html:62 apps/wei/views.py:507
msgid "Register 1A"
msgstr "Inscrire un 1A"
-#: apps/wei/templates/wei/weiclub_info.html:65 apps/wei/views.py:523
+#: apps/wei/templates/wei/weiclub_info.html:65 apps/wei/views.py:574
msgid "Register 2A+"
msgstr "Inscrire un 2A+"
@@ -2418,7 +2439,7 @@ msgstr "Ajouter un bus"
msgid "View WEI"
msgstr "Voir le WEI"
-#: apps/wei/templates/wei/weiclub_list.html:10 apps/wei/views.py:74
+#: apps/wei/templates/wei/weiclub_list.html:10 apps/wei/views.py:77
msgid "Create WEI"
msgstr "Créer un WEI"
@@ -2584,95 +2605,95 @@ msgstr "Voir les adhésions validées ..."
msgid "Search WEI"
msgstr "Chercher un WEI"
-#: apps/wei/views.py:94
+#: apps/wei/views.py:106
msgid "WEI Detail"
msgstr "Détails du WEI"
-#: apps/wei/views.py:189
+#: apps/wei/views.py:201
msgid "View members of the WEI"
msgstr "Voir les membres du WEI"
-#: apps/wei/views.py:217
+#: apps/wei/views.py:229
msgid "Find WEI Membership"
msgstr "Trouver une adhésion au WEI"
-#: apps/wei/views.py:227
+#: apps/wei/views.py:239
msgid "View registrations to the WEI"
msgstr "Voir les inscriptions au WEI"
-#: apps/wei/views.py:251
+#: apps/wei/views.py:263
msgid "Find WEI Registration"
msgstr "Trouver une inscription au WEI"
-#: apps/wei/views.py:262
+#: apps/wei/views.py:274
msgid "Update the WEI"
msgstr "Modifier le WEI"
-#: apps/wei/views.py:283
+#: apps/wei/views.py:295
msgid "Create new bus"
msgstr "Ajouter un nouveau bus"
-#: apps/wei/views.py:314
+#: apps/wei/views.py:333
msgid "Update bus"
msgstr "Modifier le bus"
-#: apps/wei/views.py:344
+#: apps/wei/views.py:363
msgid "Manage bus"
msgstr "Gérer le bus"
-#: apps/wei/views.py:371
+#: apps/wei/views.py:390
msgid "Create new team"
msgstr "Créer une nouvelle équipe"
-#: apps/wei/views.py:403
+#: apps/wei/views.py:430
msgid "Update team"
msgstr "Modifier l'équipe"
-#: apps/wei/views.py:434
+#: apps/wei/views.py:461
msgid "Manage WEI team"
msgstr "Gérer l'équipe WEI"
-#: apps/wei/views.py:456
+#: apps/wei/views.py:483
msgid "Register first year student to the WEI"
msgstr "Inscrire un 1A au WEI"
-#: apps/wei/views.py:489 apps/wei/views.py:560
+#: apps/wei/views.py:528 apps/wei/views.py:611
msgid "This user is already registered to this WEI."
msgstr "Cette personne est déjà inscrite au WEI."
-#: apps/wei/views.py:494
+#: apps/wei/views.py:533
msgid ""
-"This user can't be in her/his first year since he/she has already participated "
-"to a WEI."
+"This user can't be in her/his first year since he/she has already "
+"participated to a WEI."
msgstr ""
"Cet utilisateur ne peut pas être en première année puisqu'iel a déjà "
"participé à un WEI."
-#: apps/wei/views.py:511
+#: apps/wei/views.py:550
msgid "Register old student to the WEI"
msgstr "Inscrire un 2A+ au WEI"
-#: apps/wei/views.py:542 apps/wei/views.py:627
+#: apps/wei/views.py:593 apps/wei/views.py:684
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:590
+#: apps/wei/views.py:641
msgid "Update WEI Registration"
msgstr "Modifier l'inscription WEI"
-#: apps/wei/views.py:686
+#: apps/wei/views.py:743
msgid "Delete WEI registration"
msgstr "Supprimer l'inscription WEI"
-#: apps/wei/views.py:697
+#: apps/wei/views.py:754
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:716
+#: apps/wei/views.py:773
msgid "Validate WEI registration"
msgstr "Valider l'inscription WEI"
-#: apps/wei/views.py:855
+#: apps/wei/views.py:920
msgid "This user didn't give her/his caution check."
msgstr "Cet utilisateur n'a pas donné son chèque de caution."