From c384ee02ebfe24e817655f316f901351739e4b40 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 31 Mar 2020 01:03:30 +0200 Subject: [PATCH 01/19] Implement a new type of note (see #45) --- apps/member/forms.py | 28 +++++++- apps/member/urls.py | 16 ++++- apps/member/views.py | 94 ++++++++++++++++++++++--- apps/note/admin.py | 12 +++- apps/note/api/serializers.py | 21 +++++- apps/note/models/notes.py | 36 ++++++++++ apps/note/tables.py | 20 +++++- static/js/base.js | 2 +- templates/member/club_detail.html | 9 +++ templates/member/club_info.html | 7 +- templates/member/noteowner_detail.html | 2 +- templates/note/noteactivity_detail.html | 57 +++++++++++++++ templates/note/noteactivity_form.html | 16 +++++ templates/note/noteactivity_list.html | 27 +++++++ 14 files changed, 326 insertions(+), 21 deletions(-) create mode 100644 templates/note/noteactivity_detail.html create mode 100644 templates/note/noteactivity_form.html create mode 100644 templates/note/noteactivity_list.html diff --git a/apps/member/forms.py b/apps/member/forms.py index 20f0acfe..d731c10c 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -7,7 +7,8 @@ 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 +from note.models.notes import NoteActivity +from note_kfet.inputs import Autocomplete, AmountInput from permission.models import PermissionMask from .models import Profile, Club, Membership @@ -47,6 +48,31 @@ class ClubForm(forms.ModelForm): class Meta: model = Club fields = '__all__' + widgets = { + "membership_fee": AmountInput() + } + + +class NoteActivityForm(forms.ModelForm): + class Meta: + model = NoteActivity + fields = ('note_name', 'club', 'controller', ) + widgets = { + "club": Autocomplete( + Club, + attrs={ + 'api_url': '/api/members/club/', + } + ), + "controller": Autocomplete( + User, + attrs={ + 'api_url': '/api/user/', + 'name_field': 'username', + 'placeholder': 'Nom ...', + } + ) + } class AddMembersForm(forms.Form): diff --git a/apps/member/urls.py b/apps/member/urls.py index 085a3fec..f1a5c2bd 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -8,13 +8,23 @@ from . import views app_name = 'member' urlpatterns = [ path('signup/', views.UserCreateView.as_view(), name="signup"), + path('club/', views.ClubListView.as_view(), name="club_list"), path('club//', views.ClubDetailView.as_view(), name="club_detail"), path('club//add_member/', views.ClubAddMemberView.as_view(), name="club_add_member"), path('club/create/', views.ClubCreateView.as_view(), name="club_create"), - 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"), + 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"), + path('club//linked_notes/', views.ClubLinkedNotesView.as_view(), + name="club_linked_note_list"), + path('club//linked_notes/create/', views.ClubLinkedNoteCreateView.as_view(), + name="club_linked_note_create"), + path('club//linked_notes//', views.ClubLinkedNoteDetailView.as_view(), + name="club_linked_note_detail"), + path('club//linked_notes//update/', views.ClubLinkedNoteUpdateView.as_view(), + name="club_linked_note_update"), + path('user/', views.UserListView.as_view(), name="user_list"), path('user/', views.UserDetailView.as_view(), name="user_detail"), path('user//update', views.UserUpdateView.as_view(), name="user_update_profile"), diff --git a/apps/member/views.py b/apps/member/views.py index 8145b5e9..e8bde67d 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -18,13 +18,14 @@ from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token from note.forms import ImageForm from note.models import Alias, NoteUser +from note.models.notes import NoteActivity from note.models.transactions import Transaction -from note.tables import HistoryTable, AliasTable +from note.tables import HistoryTable, AliasTable, NoteActivityTable from permission.backends import PermissionBackend from .filters import UserFilter, UserFilterFormHelper from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper, \ - CustomAuthenticationForm + CustomAuthenticationForm, NoteActivityForm from .models import Club, Membership from .tables import ClubTable, UserTable @@ -134,7 +135,8 @@ class UserDetailView(LoginRequiredMixin, DetailView): context = super().get_context_data(**kwargs) user = context['user_object'] history_list = \ - Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id") + 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).only("club") @@ -179,8 +181,8 @@ class ProfileAliasView(LoginRequiredMixin, DetailView): class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): form_class = ImageForm - def get_context_data(self, *args, **kwargs): - context = super().get_context_data(*args, **kwargs) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) context['form'] = self.form_class(self.request.POST, self.request.FILES) return context @@ -290,8 +292,8 @@ class ClubDetailView(LoginRequiredMixin, DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) club = context["club"] - club_transactions = \ - Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) + 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.all().filter(club=club) @@ -317,7 +319,9 @@ class ClubUpdateView(LoginRequiredMixin, UpdateView): context_object_name = "club" form_class = ClubForm template_name = "member/club_form.html" - success_url = reverse_lazy("member:club_detail") + + def get_success_url(self): + return reverse_lazy("member:club_detail", kwargs={"pk": self.object.pk}) class ClubPictureUpdateView(PictureUpdateView): @@ -361,3 +365,77 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView): def form_valid(self, formset): formset.save() return super().form_valid(formset) + + +class ClubLinkedNotesView(LoginRequiredMixin, SingleTableView): + model = NoteActivity + table_class = NoteActivityTable + + def get_queryset(self): + return super().get_queryset().filter(club=self.get_object())\ + .filter(PermissionBackend.filter_queryset(self.request.user, NoteActivity, "view")) + + def get_object(self): + if hasattr(self, 'object'): + return self.object + self.object = Club.objects.get(pk=int(self.kwargs["pk"])) + return self.object + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + ctx["object"] = ctx["club"] = self.get_object() + + return ctx + + +class ClubLinkedNoteCreateView(LoginRequiredMixin, CreateView): + model = NoteActivity + form_class = NoteActivityForm + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + club = Club.objects.get(pk=self.kwargs["club_pk"]) + ctx["object"] = ctx["club"] = club + ctx["form"].fields["club"].initial = club + + return ctx + + def get_success_url(self): + self.object.refresh_from_db() + return reverse_lazy('member:club_linked_note_detail', + kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) + + +class ClubLinkedNoteUpdateView(LoginRequiredMixin, UpdateView): + model = NoteActivity + form_class = NoteActivityForm + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + ctx["club"] = Club.objects.get(pk=self.kwargs["club_pk"]) + + return ctx + + def get_success_url(self): + return reverse_lazy('member:club_linked_note_detail', + kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) + + +class ClubLinkedNoteDetailView(LoginRequiredMixin, DetailView): + model = NoteActivity + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + + note = NoteActivity.objects.get(pk=self.kwargs["pk"]) + + transactions = Transaction.objects.all().filter(Q(source=note) | Q(destination=note))\ + .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by("-id") + ctx['history_list'] = HistoryTable(transactions) + ctx["note"] = note + ctx["club"] = note.club + + return ctx diff --git a/apps/note/admin.py b/apps/note/admin.py index 702d3350..f0dede17 100644 --- a/apps/note/admin.py +++ b/apps/note/admin.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from polymorphic.admin import PolymorphicChildModelAdmin, \ PolymorphicChildModelFilter, PolymorphicParentModelAdmin -from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser +from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, NoteActivity from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \ RecurrentTransaction, MembershipTransaction @@ -24,7 +24,7 @@ class NoteAdmin(PolymorphicParentModelAdmin): """ Parent regrouping all note types as children """ - child_models = (NoteClub, NoteSpecial, NoteUser) + child_models = (NoteClub, NoteSpecial, NoteUser, NoteActivity) list_filter = ( PolymorphicChildModelFilter, 'is_active', @@ -74,6 +74,14 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin): readonly_fields = ('balance',) +@admin.register(NoteActivity) +class NoteActivityAdmin(PolymorphicChildModelAdmin): + """ + Child for a special note, see NoteAdmin + """ + readonly_fields = ('balance',) + + @admin.register(NoteUser) class NoteUserAdmin(PolymorphicChildModelAdmin): """ diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index fbd12038..a445fef9 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer -from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias +from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias, NoteActivity from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \ RecurrentTransaction, SpecialTransaction @@ -69,6 +69,22 @@ class NoteUserSerializer(serializers.ModelSerializer): return str(obj) +class NoteActivitySerializer(serializers.ModelSerializer): + """ + REST API Serializer for User's notes. + The djangorestframework plugin will analyse the model `NoteActivity` and parse all fields in the API. + """ + name = serializers.SerializerMethodField() + + class Meta: + model = NoteActivity + fields = '__all__' + read_only_fields = ('note', 'user', ) + + def get_name(self, obj): + return str(obj) + + class AliasSerializer(serializers.ModelSerializer): """ REST API Serializer for Aliases. @@ -90,7 +106,8 @@ class NotePolymorphicSerializer(PolymorphicSerializer): Note: NoteSerializer, NoteUser: NoteUserSerializer, NoteClub: NoteClubSerializer, - NoteSpecial: NoteSpecialSerializer + NoteSpecial: NoteSpecialSerializer, + NoteActivity: NoteActivitySerializer, } class Meta: diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index 9282bde9..64ec524f 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -4,11 +4,13 @@ import unicodedata from django.conf import settings +from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.db import models from django.utils.translation import gettext_lazy as _ from polymorphic.models import PolymorphicModel +from member.models import Club """ Defines each note types @@ -174,6 +176,40 @@ class NoteSpecial(Note): return self.special_type +class NoteActivity(Note): + """ + A :model:`note.Note` for accounts that are not attached to a user neither to a club, + that only need to store and transfer money (notes for activities, departments, ...) + """ + + note_name = models.CharField( + verbose_name=_('name'), + max_length=255, + unique=True, + ) + + club = models.ForeignKey( + Club, + on_delete=models.PROTECT, + related_name="linked_notes", + verbose_name=_("club"), + ) + + controller = models.ForeignKey( + User, + on_delete=models.PROTECT, + related_name="+", + verbose_name=_("controller"), + ) + + class Meta: + verbose_name = _("common note") + verbose_name_plural = _("common notes") + + def __str__(self): + return self.note_name + + class Alias(models.Model): """ points toward a :model:`note.NoteUser` or :model;`note.NoteClub` instance. diff --git a/apps/note/tables.py b/apps/note/tables.py index 0d83e3cc..2aba4684 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -9,7 +9,7 @@ from django.utils.html import format_html from django_tables2.utils import A from django.utils.translation import gettext_lazy as _ -from .models.notes import Alias +from .models.notes import Alias, NoteActivity from .models.transactions import Transaction, TransactionTemplate from .templatetags.pretty_money import pretty_money @@ -121,6 +121,24 @@ class AliasTable(tables.Table): attrs={'td': {'class': 'col-sm-1'}}) +class NoteActivityTable(tables.Table): + note_name = tables.LinkColumn( + "member:club_linked_note_detail", + args=[A("club.pk"), A("pk")], + ) + + def render_balance(self, value): + return pretty_money(value) + + class Meta: + attrs = { + 'class': 'table table-condensed table-striped table-hover' + } + model = NoteActivity + fields = ('note_name', 'balance',) + template_name = 'django_tables2/bootstrap4.html' + + class ButtonTable(tables.Table): class Meta: attrs = { diff --git a/static/js/base.js b/static/js/base.js index 22d1366a..7febd3d6 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -70,7 +70,7 @@ function refreshBalance() { * @param fun For each found note with the matched alias `alias`, fun(note, alias) is called. */ function getMatchedNotes(pattern, fun) { - $.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club&ordering=normalized_name", fun); + $.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club|activity&ordering=normalized_name", fun); } /** diff --git a/templates/member/club_detail.html b/templates/member/club_detail.html index 979c0897..3ad29901 100644 --- a/templates/member/club_detail.html +++ b/templates/member/club_detail.html @@ -7,3 +7,12 @@ {% block profile_content %} {% include "member/club_tables.html" %} {% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/templates/member/club_info.html b/templates/member/club_info.html index 1c8e8661..907914be 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -34,7 +34,10 @@
{{ object.note.alias_set.all|join:", " }}
{% trans 'email'|capfirst %}
-
{{ club.email}}
+
{{ club.email }}
+ +
{% trans 'linked notes'|capfirst %}
+
{{ club.linked_notes.all|join:", " }}
diff --git a/templates/member/noteowner_detail.html b/templates/member/noteowner_detail.html index ad329aee..fc781549 100644 --- a/templates/member/noteowner_detail.html +++ b/templates/member/noteowner_detail.html @@ -19,7 +19,7 @@ {% block extrajavascript %} +{% endblock %} diff --git a/templates/note/noteactivity_form.html b/templates/note/noteactivity_form.html new file mode 100644 index 00000000..5088c790 --- /dev/null +++ b/templates/note/noteactivity_form.html @@ -0,0 +1,16 @@ +{% extends "member/noteowner_detail.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block profile_info %} +{% include "member/club_info.html" %} +{% endblock %} + +{% block profile_content %} +
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock %} diff --git a/templates/note/noteactivity_list.html b/templates/note/noteactivity_list.html new file mode 100644 index 00000000..b4d69536 --- /dev/null +++ b/templates/note/noteactivity_list.html @@ -0,0 +1,27 @@ +{% extends "member/noteowner_detail.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block profile_info %} +{% include "member/club_info.html" %} +{% endblock %} + +{% block profile_content %} +
+
+
+
+
{% trans "linked notes of club"|capfirst %} {{ club.name }}
+
+
+ {% render_table table %} +
+
+ + + + +
+
+{% endblock %} From 1aae18e6a66f0bd2993dec89fdc97ff2724be7ce Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 31 Mar 2020 04:16:30 +0200 Subject: [PATCH 02/19] Improved permissions, 404 and 403 errors will be more frequent (when we type an invalid URL) --- apps/activity/views.py | 24 +++-- apps/member/forms.py | 8 +- apps/member/views.py | 83 +++++++-------- apps/note/models/__init__.py | 4 +- apps/note/views.py | 30 +++--- apps/permission/backends.py | 5 +- apps/permission/fixtures/initial.json | 134 ++++++++++++++++++++++-- apps/permission/models.py | 17 ++- apps/permission/views.py | 11 ++ apps/treasury/forms.py | 4 +- apps/treasury/views.py | 40 ++++--- templates/note/noteactivity_detail.html | 9 +- templates/note/noteactivity_list.html | 8 +- 13 files changed, 272 insertions(+), 105 deletions(-) create mode 100644 apps/permission/views.py diff --git a/apps/activity/views.py b/apps/activity/views.py index feb7591d..51e2ebf5 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -1,5 +1,6 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + from datetime import datetime, timezone from django.contrib.auth.mixins import LoginRequiredMixin @@ -11,13 +12,14 @@ from django.utils.translation import gettext_lazy as _ from django_tables2.views import SingleTableView from note.models import NoteUser, Alias, NoteSpecial from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin from .forms import ActivityForm, GuestForm from .models import Activity, Guest, Entry from .tables import ActivityTable, GuestTable, EntryTable -class ActivityCreateView(LoginRequiredMixin, CreateView): +class ActivityCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): model = Activity form_class = ActivityForm @@ -30,13 +32,12 @@ class ActivityCreateView(LoginRequiredMixin, CreateView): return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk}) -class ActivityListView(LoginRequiredMixin, SingleTableView): +class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): model = Activity table_class = ActivityTable def get_queryset(self): - return super().get_queryset()\ - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).reverse() + return super().get_queryset().reverse() def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) @@ -50,7 +51,7 @@ class ActivityListView(LoginRequiredMixin, SingleTableView): return ctx -class ActivityDetailView(LoginRequiredMixin, DetailView): +class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): model = Activity context_object_name = "activity" @@ -66,7 +67,7 @@ class ActivityDetailView(LoginRequiredMixin, DetailView): return ctx -class ActivityUpdateView(LoginRequiredMixin, UpdateView): +class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = Activity form_class = ActivityForm @@ -74,18 +75,20 @@ class ActivityUpdateView(LoginRequiredMixin, UpdateView): return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]}) -class ActivityInviteView(LoginRequiredMixin, CreateView): +class ActivityInviteView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): model = Guest form_class = GuestForm template_name = "activity/activity_invite.html" def get_form(self, form_class=None): form = super().get_form(form_class) - form.activity = Activity.objects.get(pk=self.kwargs["pk"]) + form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .get(pk=self.kwargs["pk"]) return form def form_valid(self, form): - form.instance.activity = Activity.objects.get(pk=self.kwargs["pk"]) + form.instance.activity = Activity.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"]) return super().form_valid(form) def get_success_url(self, **kwargs): @@ -98,7 +101,8 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - activity = Activity.objects.get(pk=self.kwargs["pk"]) + activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\ + .get(pk=self.kwargs["pk"]) ctx["activity"] = activity matched = [] diff --git a/apps/member/forms.py b/apps/member/forms.py index d731c10c..9c25f2c2 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -49,7 +49,13 @@ class ClubForm(forms.ModelForm): model = Club fields = '__all__' widgets = { - "membership_fee": AmountInput() + "membership_fee": AmountInput(), + "parent_club": Autocomplete( + Club, + attrs={ + 'api_url': '/api/members/club/', + } + ), } diff --git a/apps/member/views.py b/apps/member/views.py index e8bde67d..932899ff 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -22,6 +22,7 @@ from note.models.notes import NoteActivity from note.models.transactions import Transaction from note.tables import HistoryTable, AliasTable, NoteActivityTable from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin from .filters import UserFilter, UserFilterFormHelper from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper, \ @@ -64,7 +65,7 @@ class UserCreateView(CreateView): return super().form_valid(form) -class UserUpdateView(LoginRequiredMixin, UpdateView): +class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = User fields = ['first_name', 'last_name', 'username', 'email'] template_name = 'member/profile_update.html' @@ -98,7 +99,8 @@ class UserUpdateView(LoginRequiredMixin, UpdateView): if form.is_valid() and profile_form.is_valid(): new_username = form.data['username'] alias = Alias.objects.filter(name=new_username) - # Si le nouveau pseudo n'est pas un de nos alias, on supprime éventuellement un alias similaire pour le remplacer + # Si le nouveau pseudo n'est pas un de nos alias, + # on supprime éventuellement un alias similaire pour le remplacer if not alias.exists(): similar = Alias.objects.filter( normalized_name=Alias.normalize(new_username)) @@ -120,7 +122,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView): return reverse_lazy('member:user_detail', args=(self.object.id,)) -class UserDetailView(LoginRequiredMixin, DetailView): +class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ Affiche les informations sur un utilisateur, sa note, ses clubs... """ @@ -128,9 +130,6 @@ class UserDetailView(LoginRequiredMixin, DetailView): context_object_name = "user_object" template_name = "member/profile_detail.html" - def get_queryset(self, **kwargs): - return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, User, "view")) - def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = context['user_object'] @@ -138,13 +137,13 @@ class UserDetailView(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).only("club") + club_list = Membership.objects.all().filter(user=user)\ + .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).only("club") context['club_list'] = ClubTable(club_list) return context -class UserListView(LoginRequiredMixin, SingleTableView): +class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ Affiche la liste des utilisateurs, avec une fonction de recherche statique """ @@ -155,7 +154,7 @@ class UserListView(LoginRequiredMixin, SingleTableView): formhelper_class = UserFilterFormHelper def get_queryset(self, **kwargs): - qs = super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, User, "view")) + qs = super().get_queryset() self.filter = self.filter_class(self.request.GET, queryset=qs) self.filter.form.helper = self.formhelper_class() return self.filter.qs @@ -166,7 +165,7 @@ class UserListView(LoginRequiredMixin, SingleTableView): return context -class ProfileAliasView(LoginRequiredMixin, DetailView): +class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): model = User template_name = 'member/profile_alias.html' context_object_name = 'user_object' @@ -178,7 +177,7 @@ class ProfileAliasView(LoginRequiredMixin, DetailView): return context -class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): +class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView): form_class = ImageForm def get_context_data(self, **kwargs): @@ -239,8 +238,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView): template_name = "member/manage_auth_tokens.html" def get(self, request, *args, **kwargs): - if 'regenerate' in request.GET and Token.objects.filter( - user=request.user).exists(): + if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists(): Token.objects.get(user=self.request.user).delete() return redirect(reverse_lazy('member:auth_token') + "?show", permanent=True) @@ -249,8 +247,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['token'] = Token.objects.get_or_create( - user=self.request.user)[0] + context['token'] = Token.objects.get_or_create(user=self.request.user)[0] return context @@ -259,7 +256,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView): # ******************************* # -class ClubCreateView(LoginRequiredMixin, CreateView): +class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): """ Create Club """ @@ -271,38 +268,32 @@ class ClubCreateView(LoginRequiredMixin, CreateView): return super().form_valid(form) -class ClubListView(LoginRequiredMixin, SingleTableView): +class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ List existing Clubs """ model = Club table_class = ClubTable - def get_queryset(self, **kwargs): - return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) - -class ClubDetailView(LoginRequiredMixin, DetailView): +class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): model = Club context_object_name = "club" - def get_queryset(self, **kwargs): - return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) - def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) club = context["club"] 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.all().filter(club=club) + club_member = Membership.objects.filter(club=club)\ + .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).all() # TODO: consider only valid Membership context['member_list'] = club_member return context -class ClubAliasView(LoginRequiredMixin, DetailView): +class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): model = Club template_name = 'member/club_alias.html' context_object_name = 'club' @@ -314,7 +305,7 @@ class ClubAliasView(LoginRequiredMixin, DetailView): return context -class ClubUpdateView(LoginRequiredMixin, UpdateView): +class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = Club context_object_name = "club" form_class = ClubForm @@ -333,7 +324,7 @@ class ClubPictureUpdateView(PictureUpdateView): return reverse_lazy('member:club_detail', kwargs={'pk': self.object.id}) -class ClubAddMemberView(LoginRequiredMixin, CreateView): +class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): model = Membership form_class = MembershipForm template_name = 'member/add_members.html' @@ -344,7 +335,8 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView): "change")) def get_context_data(self, **kwargs): - club = Club.objects.get(pk=self.kwargs["pk"]) + 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() @@ -367,36 +359,40 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView): return super().form_valid(formset) -class ClubLinkedNotesView(LoginRequiredMixin, SingleTableView): +class ClubLinkedNotesView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): model = NoteActivity table_class = NoteActivityTable def get_queryset(self): - return super().get_queryset().filter(club=self.get_object())\ - .filter(PermissionBackend.filter_queryset(self.request.user, NoteActivity, "view")) + return super().get_queryset().filter(club=self.get_object()) def get_object(self): if hasattr(self, 'object'): return self.object - self.object = Club.objects.get(pk=int(self.kwargs["pk"])) + self.object = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ + .get(pk=int(self.kwargs["pk"])) return self.object def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["object"] = ctx["club"] = self.get_object() + club = ctx["object"] = ctx["club"] = self.get_object() + + empty_note = NoteActivity(note_name="", club=club, controller=self.request.user) + ctx["can_create"] = PermissionBackend().has_perm(self.request.user, "note.add_noteactivity", empty_note) return ctx -class ClubLinkedNoteCreateView(LoginRequiredMixin, CreateView): +class ClubLinkedNoteCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): model = NoteActivity form_class = NoteActivityForm def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - club = Club.objects.get(pk=self.kwargs["club_pk"]) + club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ + .get(pk=self.kwargs["club_pk"]) ctx["object"] = ctx["club"] = club ctx["form"].fields["club"].initial = club @@ -408,14 +404,15 @@ class ClubLinkedNoteCreateView(LoginRequiredMixin, CreateView): kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) -class ClubLinkedNoteUpdateView(LoginRequiredMixin, UpdateView): +class ClubLinkedNoteUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = NoteActivity form_class = NoteActivityForm def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["club"] = Club.objects.get(pk=self.kwargs["club_pk"]) + ctx["club"] = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ + .get(pk=self.kwargs["club_pk"]) return ctx @@ -424,15 +421,15 @@ class ClubLinkedNoteUpdateView(LoginRequiredMixin, UpdateView): kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) -class ClubLinkedNoteDetailView(LoginRequiredMixin, DetailView): +class ClubLinkedNoteDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): model = NoteActivity def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - note = NoteActivity.objects.get(pk=self.kwargs["pk"]) + note = self.get_queryset().filter(pk=self.kwargs["pk"]).get() - transactions = Transaction.objects.all().filter(Q(source=note) | Q(destination=note))\ + transactions = Transaction.objects.filter(Q(source=note) | Q(destination=note))\ .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by("-id") ctx['history_list'] = HistoryTable(transactions) ctx["note"] = note diff --git a/apps/note/models/__init__.py b/apps/note/models/__init__.py index e9c8a0a9..dd024963 100644 --- a/apps/note/models/__init__.py +++ b/apps/note/models/__init__.py @@ -1,13 +1,13 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser +from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, NoteActivity from .transactions import MembershipTransaction, Transaction, \ TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction __all__ = [ # Notes - 'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser', + 'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser', 'NoteActivity', # Transactions 'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate', 'RecurrentTransaction', 'SpecialTransaction', diff --git a/apps/note/views.py b/apps/note/views.py index 25279281..ac9b3e40 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -9,6 +9,7 @@ from django_tables2 import SingleTableView from django.urls import reverse_lazy from note_kfet.inputs import AmountInput from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin from .forms import TransactionTemplateForm from .models import Transaction, TransactionTemplate, RecurrentTransaction, NoteSpecial @@ -16,7 +17,7 @@ from .models.transactions import SpecialTransaction from .tables import HistoryTable, ButtonTable -class TransactionCreateView(LoginRequiredMixin, SingleTableView): +class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ View for the creation of Transaction between two note which are not :models:`transactions.RecurrentTransaction`. e.g. for donation/transfer between people and clubs or for credit/debit with :models:`note.NoteSpecial` @@ -26,12 +27,9 @@ class TransactionCreateView(LoginRequiredMixin, SingleTableView): model = Transaction # Transaction history table table_class = HistoryTable - table_pagination = {"per_page": 50} - def get_queryset(self): - return Transaction.objects.filter(PermissionBackend.filter_queryset( - self.request.user, Transaction, "view") - ).order_by("-id").all()[:50] + def get_queryset(self, **kwargs): + return super().get_queryset(**kwargs).order_by("-id").all()[:50] def get_context_data(self, **kwargs): """ @@ -42,12 +40,14 @@ class TransactionCreateView(LoginRequiredMixin, SingleTableView): context['amount_widget'] = AmountInput(attrs={"id": "amount"}) context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk - context['special_types'] = NoteSpecial.objects.order_by("special_type").all() + context['special_types'] = NoteSpecial.objects\ + .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\ + .order_by("special_type").all() return context -class TransactionTemplateCreateView(LoginRequiredMixin, CreateView): +class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): """ Create TransactionTemplate """ @@ -56,7 +56,7 @@ class TransactionTemplateCreateView(LoginRequiredMixin, CreateView): success_url = reverse_lazy('note:template_list') -class TransactionTemplateListView(LoginRequiredMixin, SingleTableView): +class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ List TransactionsTemplates """ @@ -64,7 +64,7 @@ class TransactionTemplateListView(LoginRequiredMixin, SingleTableView): table_class = ButtonTable -class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView): +class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ """ model = TransactionTemplate @@ -72,21 +72,19 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView): success_url = reverse_lazy('note:template_list') -class ConsoView(LoginRequiredMixin, SingleTableView): +class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ The Magic View that make people pay their beer and burgers. (Most of the magic happens in the dark world of Javascript see consos.js) """ + model = Transaction template_name = "note/conso_form.html" # Transaction history table table_class = HistoryTable - table_pagination = {"per_page": 50} - def get_queryset(self): - return Transaction.objects.filter( - PermissionBackend.filter_queryset(self.request.user, Transaction, "view") - ).order_by("-id").all()[:50] + def get_queryset(self, **kwargs): + return super().get_queryset(**kwargs).order_by("-id").all()[:50] def get_context_data(self, **kwargs): """ diff --git a/apps/permission/backends.py b/apps/permission/backends.py index e61b0719..f838fc1f 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -5,7 +5,7 @@ from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.db.models import Q, F -from note.models import Note, NoteUser, NoteClub, NoteSpecial +from note.models import Note, NoteUser, NoteClub, NoteSpecial, NoteActivity from note_kfet.middlewares import get_current_session from member.models import Membership, Club @@ -35,7 +35,7 @@ class PermissionBackend(ModelBackend): model__app_label=model.app_label, # For polymorphic models, we don't filter on model type type=type, ).all(): - if not isinstance(model, permission.model.__class__): + if not isinstance(model, permission.model.__class__) or not permission.club: continue club = Club.objects.get(pk=permission.club) @@ -49,6 +49,7 @@ class PermissionBackend(ModelBackend): NoteUser=NoteUser, NoteClub=NoteClub, NoteSpecial=NoteSpecial, + NoteActivity=NoteActivity, F=F, Q=Q ) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 31b59069..e0430530 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -176,7 +176,7 @@ "note", "alias" ], - "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}]", + "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}, {\"note__in\": [\"NoteActivity\", \"objects\", [\"all\"]]}]", "type": "view", "mask": 1, "field": "", @@ -386,7 +386,7 @@ "note", "transaction" ], - "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}]", + "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]", "type": "add", "mask": 2, "field": "", @@ -783,6 +783,111 @@ "description": "Validate invitation transactions" } }, + { + "model": "permission.permission", + "pk": 47, + "fields": { + "model": [ + "member", + "club" + ], + "query": "{\"pk\": [\"club\", \"pk\"]}", + "type": "change", + "mask": 1, + "field": "", + "description": "Update club" + } + }, + { + "model": "permission.permission", + "pk": 48, + "fields": { + "model": [ + "note", + "noteactivity" + ], + "query": "{\"club\": [\"club\"]}", + "type": "change", + "mask": 1, + "field": "", + "description": "Manage notes that are linked to a club" + } + }, + { + "model": "permission.permission", + "pk": 49, + "fields": { + "model": [ + "note", + "noteactivity" + ], + "query": "{\"club\": [\"club\"]}", + "type": "view", + "mask": 1, + "field": "", + "description": "View notes that are linked to a club" + } + }, + { + "model": "permission.permission", + "pk": 50, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]", + "type": "add", + "mask": 2, + "field": "", + "description": "Add transactions linked to a noteactivity" + } + }, + { + "model": "permission.permission", + "pk": 51, + "fields": { + "model": [ + "note", + "transaction" + ], + "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}]]", + "type": "view", + "mask": 1, + "field": "", + "description": "View transactions linked to a noteactivity" + } + }, + { + "model": "permission.permission", + "pk": 52, + "fields": { + "model": [ + "note", + "note" + ], + "query": "{\"noteactivity__controller\": [\"user\"]}", + "type": "view", + "mask": 1, + "field": "", + "description": "View note activity" + } + }, + { + "model": "permission.permission", + "pk": 53, + "fields": { + "model": [ + "note", + "noteactivity" + ], + "query": "{\"controller\": [\"user\"]}", + "type": "view", + "mask": 1, + "field": "", + "description": "View note activity" + } + }, { "model": "permission.rolepermissions", "pk": 1, @@ -810,7 +915,6 @@ 3, 4, 5, - 6, 7, 8, 9, @@ -827,7 +931,12 @@ 35, 36, 39, - 40 + 40, + 6, + 52, + 53, + 51, + 50 ] } }, @@ -838,9 +947,9 @@ "role": 8, "permissions": [ 19, - 20, 21, - 22 + 22, + 20 ] } }, @@ -880,5 +989,18 @@ 46 ] } + }, + { + "model": "permission.rolepermissions", + "pk": 6, + "fields": { + "role": 7, + "permissions": [ + 22, + 47, + 48, + 49 + ] + } } ] diff --git a/apps/permission/models.py b/apps/permission/models.py index 205f5b41..d1b55090 100644 --- a/apps/permission/models.py +++ b/apps/permission/models.py @@ -38,20 +38,29 @@ class InstancedPermission: if permission_type == self.type: self.update_query() - # Don't increase indexes - obj.pk = 0 + # Don't increase indexes, if the primary key is an AutoField + if not hasattr(obj, "pk") or not obj.pk: + obj.pk = 0 + oldpk = None + else: + oldpk = obj.pk + # Ensure previous models are deleted + self.model.model_class().objects.filter(pk=obj.pk).delete() # Force insertion, no data verification, no trigger Model.save(obj, force_insert=True) - ret = obj in self.model.model_class().objects.filter(self.query).all() + ret = self.model.model_class().objects.filter(self.query & Q(pk=obj.pk)).exists() # Delete testing object Model.delete(obj) + + # If the primary key was specified, we restore it + obj.pk = oldpk return ret if permission_type == self.type: if self.field and field_name != self.field: return False self.update_query() - return obj in self.model.model_class().objects.filter(self.query).all() + return self.model.model_class().objects.filter(self.query & Q(pk=obj.pk)).exists() else: return False diff --git a/apps/permission/views.py b/apps/permission/views.py new file mode 100644 index 00000000..bbd9872f --- /dev/null +++ b/apps/permission/views.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from permission.backends import PermissionBackend + + +class ProtectQuerysetMixin: + def get_queryset(self, **kwargs): + qs = super().get_queryset(**kwargs) + + return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view")) diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py index 7fe7de4c..ad479e14 100644 --- a/apps/treasury/forms.py +++ b/apps/treasury/forms.py @@ -8,6 +8,7 @@ from crispy_forms.layout import Submit from django import forms from django.utils.translation import gettext_lazy as _ from note_kfet.inputs import DatePickerInput, AmountInput +from permission.backends import PermissionBackend from .models import Invoice, Product, Remittance, SpecialTransactionProxy @@ -131,7 +132,8 @@ class LinkTransactionToRemittanceForm(forms.ModelForm): # Add submit button self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'})) - self.fields["remittance"].queryset = Remittance.objects.filter(closed=False) + self.fields["remittance"].queryset = Remittance.objects.filter(closed=False)\ + .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) def clean_last_name(self): """ diff --git a/apps/treasury/views.py b/apps/treasury/views.py index c374ced1..adf38aaa 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -19,13 +19,15 @@ from django.views.generic.base import View, TemplateView from django_tables2 import SingleTableView from note.models import SpecialTransaction, NoteSpecial from note_kfet.settings.base import BASE_DIR +from permission.backends import PermissionBackend +from permission.views import ProtectQuerysetMixin from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm from .models import Invoice, Product, Remittance, SpecialTransactionProxy from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable -class InvoiceCreateView(LoginRequiredMixin, CreateView): +class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): """ Create Invoice """ @@ -67,7 +69,7 @@ class InvoiceCreateView(LoginRequiredMixin, CreateView): return reverse_lazy('treasury:invoice_list') -class InvoiceListView(LoginRequiredMixin, SingleTableView): +class InvoiceListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ List existing Invoices """ @@ -75,7 +77,7 @@ class InvoiceListView(LoginRequiredMixin, SingleTableView): table_class = InvoiceTable -class InvoiceUpdateView(LoginRequiredMixin, UpdateView): +class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ Create Invoice """ @@ -130,7 +132,7 @@ class InvoiceRenderView(LoginRequiredMixin, View): def get(self, request, **kwargs): pk = kwargs["pk"] - invoice = Invoice.objects.get(pk=pk) + invoice = Invoice.objects.filter(PermissionBackend.filter_queryset(request.user, Invoice, "view")).get(pk=pk) products = Product.objects.filter(invoice=invoice).all() # Informations of the BDE. Should be updated when the school will move. @@ -188,7 +190,7 @@ class InvoiceRenderView(LoginRequiredMixin, View): return response -class RemittanceCreateView(LoginRequiredMixin, CreateView): +class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): """ Create Remittance """ @@ -201,7 +203,9 @@ class RemittanceCreateView(LoginRequiredMixin, CreateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["table"] = RemittanceTable(data=Remittance.objects.all()) + ctx["table"] = RemittanceTable(data=Remittance.objects + .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) + .all()) ctx["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) return ctx @@ -216,22 +220,28 @@ class RemittanceListView(LoginRequiredMixin, TemplateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["opened_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=False).all()) - ctx["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all()) + ctx["opened_remittances"] = RemittanceTable( + data=Remittance.objects.filter(closed=False).filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) + ctx["closed_remittances"] = RemittanceTable( + data=Remittance.objects.filter(closed=True).filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).reverse().all()) ctx["special_transactions_no_remittance"] = SpecialTransactionTable( data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), - specialtransactionproxy__remittance=None).all(), + specialtransactionproxy__remittance=None).filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), exclude=('remittance_remove', )) ctx["special_transactions_with_remittance"] = SpecialTransactionTable( data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)), - specialtransactionproxy__remittance__closed=False).all(), + specialtransactionproxy__remittance__closed=False).filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(), exclude=('remittance_add', )) return ctx -class RemittanceUpdateView(LoginRequiredMixin, UpdateView): +class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ Update Remittance """ @@ -244,8 +254,10 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["table"] = RemittanceTable(data=Remittance.objects.all()) - data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all() + ctx["table"] = RemittanceTable(data=Remittance.objects.filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) + data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all() ctx["special_transactions"] = SpecialTransactionTable( data=data, exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', )) @@ -253,7 +265,7 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView): return ctx -class LinkTransactionToRemittanceView(LoginRequiredMixin, UpdateView): +class LinkTransactionToRemittanceView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ Attach a special transaction to a remittance """ diff --git a/templates/note/noteactivity_detail.html b/templates/note/noteactivity_detail.html index 731707f6..aab9e055 100644 --- a/templates/note/noteactivity_detail.html +++ b/templates/note/noteactivity_detail.html @@ -3,6 +3,7 @@ {% load i18n %} {% load render_table from django_tables2 %} {% load pretty_money %} +{% load perms %} {% block profile_info %} {% include "member/club_info.html" %} @@ -25,9 +26,11 @@
{{ note.balance|pretty_money }}
- + {% if "change_"|has_perm:note %} + + {% endif %} diff --git a/templates/note/noteactivity_list.html b/templates/note/noteactivity_list.html index b4d69536..3cd5b786 100644 --- a/templates/note/noteactivity_list.html +++ b/templates/note/noteactivity_list.html @@ -19,9 +19,11 @@ - - - + {% if can_create %} + + + + {% endif %} {% endblock %} From 12945945bc57308e568f05a49459285b985d3917 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 31 Mar 2020 14:10:30 +0200 Subject: [PATCH 03/19] Update translations --- apps/note/models/notes.py | 4 +- apps/treasury/views.py | 4 +- locale/de/LC_MESSAGES/django.po | 162 +++++++++++++++++++------------ locale/fr/LC_MESSAGES/django.po | 165 ++++++++++++++++++++------------ 4 files changed, 206 insertions(+), 129 deletions(-) diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index 64ec524f..1228ca00 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -203,8 +203,8 @@ class NoteActivity(Note): ) class Meta: - verbose_name = _("common note") - verbose_name_plural = _("common notes") + verbose_name = _("activity note") + verbose_name_plural = _("activity notes") def __str__(self): return self.note_name diff --git a/apps/treasury/views.py b/apps/treasury/views.py index adf38aaa..f564ccb2 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -255,9 +255,9 @@ class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView) ctx = super().get_context_data(**kwargs) ctx["table"] = RemittanceTable(data=Remittance.objects.filter( - PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter( - PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all() + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all() ctx["special_transactions"] = SpecialTransactionTable( data=data, exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', )) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index e2c8075e..c83bf321 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-03-30 17:31+0200\n" +"POT-Creation-Date: 2020-03-31 04:16+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -45,9 +45,10 @@ 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: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 +#: 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 msgid "name" msgstr "" @@ -68,18 +69,18 @@ msgid "activity types" msgstr "" #: apps/activity/models.py:53 apps/note/models/transactions.py:69 -#: apps/permission/models.py:90 templates/activity/activity_detail.html:16 +#: apps/permission/models.py:99 templates/activity/activity_detail.html:16 msgid "description" msgstr "" -#: apps/activity/models.py:60 apps/note/models/notes.py:164 +#: apps/activity/models.py:60 apps/note/models/notes.py:166 #: 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:117 +#: apps/note/models/notes.py:119 msgid "user" msgstr "" @@ -88,7 +89,7 @@ msgid "organizer" msgstr "" #: apps/activity/models.py:82 apps/activity/models.py:131 apps/note/apps.py:14 -#: apps/note/models/notes.py:58 +#: apps/note/models/notes.py:60 msgid "note" msgstr "" @@ -169,16 +170,16 @@ msgstr "" msgid "Type" msgstr "" -#: apps/activity/tables.py:77 apps/treasury/forms.py:120 +#: apps/activity/tables.py:77 apps/treasury/forms.py:121 msgid "Last name" msgstr "" -#: apps/activity/tables.py:79 apps/treasury/forms.py:122 +#: apps/activity/tables.py:79 apps/treasury/forms.py:123 #: templates/note/transaction_form.html:92 msgid "First name" msgstr "" -#: apps/activity/tables.py:81 apps/note/models/notes.py:67 +#: apps/activity/tables.py:81 apps/note/models/notes.py:69 msgid "Note" msgstr "" @@ -186,11 +187,11 @@ msgstr "" msgid "Balance" msgstr "" -#: apps/activity/views.py:44 templates/base.html:94 +#: apps/activity/views.py:45 templates/base.html:94 msgid "Activities" msgstr "" -#: apps/activity/views.py:149 +#: apps/activity/views.py:153 msgid "Entry for activity \"{}\"" msgstr "" @@ -226,12 +227,12 @@ msgstr "" msgid "create" msgstr "" -#: apps/logs/models.py:61 apps/note/tables.py:142 +#: apps/logs/models.py:61 apps/note/tables.py:160 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:164 msgid "delete" msgstr "" @@ -313,7 +314,8 @@ msgid "" "members can renew their membership." msgstr "" -#: apps/member/models.py:104 apps/note/models/notes.py:139 +#: apps/member/models.py:104 apps/note/models/notes.py:141 +#: apps/note/models/notes.py:195 msgid "club" msgstr "" @@ -321,7 +323,7 @@ msgstr "" msgid "clubs" msgstr "" -#: apps/member/models.py:128 apps/permission/models.py:275 +#: apps/member/models.py:128 apps/permission/models.py:284 msgid "role" msgstr "" @@ -353,19 +355,19 @@ msgstr "" msgid "memberships" msgstr "" -#: apps/member/views.py:76 templates/member/profile_info.html:45 +#: apps/member/views.py:78 templates/member/profile_info.html:45 msgid "Update Profile" msgstr "" -#: apps/member/views.py:89 +#: apps/member/views.py:91 msgid "An alias with a similar name already exists." msgstr "" -#: apps/note/admin.py:120 apps/note/models/transactions.py:94 +#: apps/note/admin.py:128 apps/note/models/transactions.py:94 msgid "source" msgstr "" -#: apps/note/admin.py:128 apps/note/admin.py:156 +#: apps/note/admin.py:136 apps/note/admin.py:164 #: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107 msgid "destination" msgstr "" @@ -378,104 +380,116 @@ msgstr "" msgid "Maximal size: 2MB" msgstr "" -#: apps/note/models/notes.py:27 +#: apps/note/models/notes.py:29 msgid "account balance" msgstr "" -#: apps/note/models/notes.py:28 +#: apps/note/models/notes.py:30 msgid "in centimes, money credited for this instance" msgstr "" -#: apps/note/models/notes.py:32 +#: apps/note/models/notes.py:34 msgid "last negative date" msgstr "" -#: apps/note/models/notes.py:33 +#: apps/note/models/notes.py:35 msgid "last time the balance was negative" msgstr "" -#: apps/note/models/notes.py:38 +#: apps/note/models/notes.py:40 msgid "active" msgstr "" -#: apps/note/models/notes.py:41 +#: apps/note/models/notes.py:43 msgid "" "Designates whether this note should be treated as active. Unselect this " "instead of deleting notes." msgstr "" -#: apps/note/models/notes.py:45 +#: apps/note/models/notes.py:47 msgid "display image" msgstr "" -#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117 +#: apps/note/models/notes.py:55 apps/note/models/transactions.py:117 msgid "created at" msgstr "" -#: apps/note/models/notes.py:59 +#: apps/note/models/notes.py:61 msgid "notes" msgstr "" -#: apps/note/models/notes.py:77 apps/note/models/notes.py:101 +#: apps/note/models/notes.py:79 apps/note/models/notes.py:103 msgid "This alias is already taken." msgstr "" -#: apps/note/models/notes.py:121 +#: apps/note/models/notes.py:123 msgid "one's note" msgstr "" -#: apps/note/models/notes.py:122 +#: apps/note/models/notes.py:124 msgid "users note" msgstr "" -#: apps/note/models/notes.py:128 +#: apps/note/models/notes.py:130 #, python-format msgid "%(user)s's note" msgstr "" -#: apps/note/models/notes.py:143 +#: apps/note/models/notes.py:145 msgid "club note" msgstr "" -#: apps/note/models/notes.py:144 +#: apps/note/models/notes.py:146 msgid "clubs notes" msgstr "" -#: apps/note/models/notes.py:150 +#: apps/note/models/notes.py:152 #, python-format msgid "Note of %(club)s club" msgstr "" -#: apps/note/models/notes.py:170 +#: apps/note/models/notes.py:172 msgid "special note" msgstr "" -#: apps/note/models/notes.py:171 +#: apps/note/models/notes.py:173 msgid "special notes" msgstr "" -#: apps/note/models/notes.py:194 +#: apps/note/models/notes.py:202 templates/note/noteactivity_detail.html:22 +msgid "controller" +msgstr "" + +#: apps/note/models/notes.py:206 +msgid "activity note" +msgstr "" + +#: apps/note/models/notes.py:207 +msgid "activity notes" +msgstr "" + +#: apps/note/models/notes.py:230 msgid "Invalid alias" msgstr "" -#: apps/note/models/notes.py:210 +#: apps/note/models/notes.py:246 msgid "alias" msgstr "" -#: apps/note/models/notes.py:211 templates/member/club_info.html:33 +#: apps/note/models/notes.py:247 templates/member/club_info.html:33 #: templates/member/profile_info.html:36 msgid "aliases" msgstr "" -#: apps/note/models/notes.py:233 +#: apps/note/models/notes.py:269 msgid "Alias is too long." msgstr "" -#: apps/note/models/notes.py:238 +#: apps/note/models/notes.py:274 msgid "An alias with a similar name already exists: {} " msgstr "" -#: apps/note/models/notes.py:251 +#: apps/note/models/notes.py:287 msgid "You can't delete your main alias." msgstr "" @@ -578,29 +592,29 @@ msgstr "" msgid "No reason specified" msgstr "" -#: apps/note/views.py:41 +#: apps/note/views.py:39 msgid "Transfer money" msgstr "" -#: apps/note/views.py:102 templates/base.html:79 +#: apps/note/views.py:100 templates/base.html:79 msgid "Consumptions" msgstr "" -#: apps/permission/models.py:69 apps/permission/models.py:262 +#: apps/permission/models.py:78 apps/permission/models.py:271 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" msgstr "" -#: apps/permission/models.py:71 apps/permission/models.py:264 +#: apps/permission/models.py:80 apps/permission/models.py:273 #, python-brace-format msgid "Can {type} {model} in {query}" msgstr "" -#: apps/permission/models.py:84 +#: apps/permission/models.py:93 msgid "rank" msgstr "" -#: apps/permission/models.py:147 +#: apps/permission/models.py:156 msgid "Specifying field applies only to view and change permission types." msgstr "" @@ -608,31 +622,32 @@ msgstr "" msgid "Treasury" msgstr "" -#: apps/treasury/forms.py:84 apps/treasury/forms.py:132 +#: apps/treasury/forms.py:85 apps/treasury/forms.py:133 #: 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/treasury/invoice_form.html:46 +#: templates/member/club_form.html:9 templates/note/noteactivity_form.html:14 +#: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "" -#: apps/treasury/forms.py:86 +#: apps/treasury/forms.py:87 msgid "Close" msgstr "" -#: apps/treasury/forms.py:95 +#: apps/treasury/forms.py:96 msgid "Remittance is already closed." msgstr "" -#: apps/treasury/forms.py:100 +#: apps/treasury/forms.py:101 msgid "You can't change the type of the remittance." msgstr "" -#: apps/treasury/forms.py:124 templates/note/transaction_form.html:98 +#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98 msgid "Bank" msgstr "" -#: apps/treasury/forms.py:126 apps/treasury/tables.py:47 +#: apps/treasury/forms.py:127 apps/treasury/tables.py:47 #: templates/note/transaction_form.html:128 #: templates/treasury/remittance_form.html:18 msgid "Amount" @@ -879,19 +894,24 @@ msgstr "" msgid "Club Parent" msgstr "" -#: templates/member/club_info.html:41 +#: templates/member/club_info.html:39 +msgid "linked notes" +msgstr "" + +#: templates/member/club_info.html:44 msgid "Add member" msgstr "" -#: templates/member/club_info.html:42 templates/note/conso_form.html:121 +#: templates/member/club_info.html:45 templates/note/conso_form.html:121 +#: templates/note/noteactivity_detail.html:31 msgid "Edit" msgstr "" -#: templates/member/club_info.html:43 +#: templates/member/club_info.html:46 msgid "Add roles" msgstr "" -#: templates/member/club_info.html:46 templates/member/profile_info.html:48 +#: templates/member/club_info.html:49 templates/member/profile_info.html:48 msgid "View Profile" msgstr "" @@ -912,6 +932,7 @@ msgid "Member of the Club" msgstr "" #: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 +#: templates/note/noteactivity_detail.html:42 msgid "Transaction history" msgstr "" @@ -944,6 +965,7 @@ msgid "Change password" msgstr "" #: templates/member/profile_info.html:33 +#: templates/note/noteactivity_detail.html:25 msgid "balance" msgstr "" @@ -992,6 +1014,22 @@ msgstr "" msgid "Recent transactions history" msgstr "" +#: templates/note/noteactivity_detail.html:15 +msgid "Linked note:" +msgstr "" + +#: templates/note/noteactivity_detail.html:19 +msgid "attached club" +msgstr "" + +#: templates/note/noteactivity_list.html:15 +msgid "linked notes of club" +msgstr "" + +#: templates/note/noteactivity_list.html:24 +msgid "Add new note" +msgstr "" + #: templates/note/transaction_form.html:15 msgid "Gift" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 08c1b174..cd01499d 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-03-30 17:31+0200\n" +"POT-Creation-Date: 2020-03-31 04:16+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -20,7 +20,8 @@ msgstr "activité" #: apps/activity/forms.py:45 apps/activity/models.py:217 msgid "You can't invite someone once the activity is started." -msgstr "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." +msgstr "" +"Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." #: apps/activity/forms.py:48 apps/activity/models.py:220 msgid "This activity is not validated yet." @@ -40,9 +41,10 @@ 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: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 +#: 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 msgid "name" msgstr "nom" @@ -63,18 +65,18 @@ msgid "activity types" msgstr "types d'activité" #: apps/activity/models.py:53 apps/note/models/transactions.py:69 -#: apps/permission/models.py:90 templates/activity/activity_detail.html:16 +#: apps/permission/models.py:99 templates/activity/activity_detail.html:16 msgid "description" msgstr "description" -#: apps/activity/models.py:60 apps/note/models/notes.py:164 +#: apps/activity/models.py:60 apps/note/models/notes.py:166 #: 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:117 +#: apps/note/models/notes.py:119 msgid "user" msgstr "utilisateur" @@ -83,7 +85,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:58 +#: apps/note/models/notes.py:60 msgid "note" msgstr "note" @@ -164,16 +166,16 @@ msgstr "supprimer" msgid "Type" msgstr "Type" -#: apps/activity/tables.py:77 apps/treasury/forms.py:120 +#: apps/activity/tables.py:77 apps/treasury/forms.py:121 msgid "Last name" msgstr "Nom de famille" -#: apps/activity/tables.py:79 apps/treasury/forms.py:122 +#: apps/activity/tables.py:79 apps/treasury/forms.py:123 #: templates/note/transaction_form.html:92 msgid "First name" msgstr "Prénom" -#: apps/activity/tables.py:81 apps/note/models/notes.py:67 +#: apps/activity/tables.py:81 apps/note/models/notes.py:69 msgid "Note" msgstr "Note" @@ -181,11 +183,11 @@ msgstr "Note" msgid "Balance" msgstr "Solde du compte" -#: apps/activity/views.py:44 templates/base.html:94 +#: apps/activity/views.py:45 templates/base.html:94 msgid "Activities" msgstr "Activités" -#: apps/activity/views.py:149 +#: apps/activity/views.py:153 msgid "Entry for activity \"{}\"" msgstr "Entrées pour l'activité « {} »" @@ -221,12 +223,12 @@ msgstr "Nouvelles données" msgid "create" msgstr "Créer" -#: apps/logs/models.py:61 apps/note/tables.py:142 +#: apps/logs/models.py:61 apps/note/tables.py:160 #: templates/activity/activity_detail.html:67 msgid "edit" msgstr "Modifier" -#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:146 +#: apps/logs/models.py:62 apps/note/tables.py:120 apps/note/tables.py:164 msgid "delete" msgstr "Supprimer" @@ -312,7 +314,8 @@ 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:139 +#: apps/member/models.py:104 apps/note/models/notes.py:141 +#: apps/note/models/notes.py:195 msgid "club" msgstr "club" @@ -320,7 +323,7 @@ msgstr "club" msgid "clubs" msgstr "clubs" -#: apps/member/models.py:128 apps/permission/models.py:275 +#: apps/member/models.py:128 apps/permission/models.py:284 msgid "role" msgstr "rôle" @@ -352,19 +355,19 @@ msgstr "adhésion" msgid "memberships" msgstr "adhésions" -#: apps/member/views.py:76 templates/member/profile_info.html:45 +#: apps/member/views.py:78 templates/member/profile_info.html:45 msgid "Update Profile" msgstr "Modifier le profil" -#: apps/member/views.py:89 +#: apps/member/views.py:91 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." -#: apps/note/admin.py:120 apps/note/models/transactions.py:94 +#: apps/note/admin.py:128 apps/note/models/transactions.py:94 msgid "source" msgstr "source" -#: apps/note/admin.py:128 apps/note/admin.py:156 +#: apps/note/admin.py:136 apps/note/admin.py:164 #: apps/note/models/transactions.py:53 apps/note/models/transactions.py:107 msgid "destination" msgstr "destination" @@ -377,105 +380,117 @@ msgstr "Choisissez une image" msgid "Maximal size: 2MB" msgstr "Taille maximale : 2 Mo" -#: apps/note/models/notes.py:27 +#: apps/note/models/notes.py:29 msgid "account balance" msgstr "solde du compte" -#: apps/note/models/notes.py:28 +#: apps/note/models/notes.py:30 msgid "in centimes, money credited for this instance" msgstr "en centimes, argent crédité pour cette instance" -#: apps/note/models/notes.py:32 +#: apps/note/models/notes.py:34 msgid "last negative date" msgstr "dernier date de négatif" -#: apps/note/models/notes.py:33 +#: apps/note/models/notes.py:35 msgid "last time the balance was negative" msgstr "dernier instant où la note était en négatif" -#: apps/note/models/notes.py:38 +#: apps/note/models/notes.py:40 msgid "active" msgstr "actif" -#: apps/note/models/notes.py:41 +#: apps/note/models/notes.py:43 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:45 +#: apps/note/models/notes.py:47 msgid "display image" msgstr "image affichée" -#: apps/note/models/notes.py:53 apps/note/models/transactions.py:117 +#: apps/note/models/notes.py:55 apps/note/models/transactions.py:117 msgid "created at" msgstr "créée le" -#: apps/note/models/notes.py:59 +#: apps/note/models/notes.py:61 msgid "notes" msgstr "notes" -#: apps/note/models/notes.py:77 apps/note/models/notes.py:101 +#: apps/note/models/notes.py:79 apps/note/models/notes.py:103 msgid "This alias is already taken." msgstr "Cet alias est déjà pris." -#: apps/note/models/notes.py:121 +#: apps/note/models/notes.py:123 msgid "one's note" msgstr "note d'un utilisateur" -#: apps/note/models/notes.py:122 +#: apps/note/models/notes.py:124 msgid "users note" msgstr "notes des utilisateurs" -#: apps/note/models/notes.py:128 +#: apps/note/models/notes.py:130 #, python-format msgid "%(user)s's note" msgstr "Note de %(user)s" -#: apps/note/models/notes.py:143 +#: apps/note/models/notes.py:145 msgid "club note" msgstr "note d'un club" -#: apps/note/models/notes.py:144 +#: apps/note/models/notes.py:146 msgid "clubs notes" msgstr "notes des clubs" -#: apps/note/models/notes.py:150 +#: apps/note/models/notes.py:152 #, python-format msgid "Note of %(club)s club" msgstr "Note du club %(club)s" -#: apps/note/models/notes.py:170 +#: apps/note/models/notes.py:172 msgid "special note" msgstr "note spéciale" -#: apps/note/models/notes.py:171 +#: apps/note/models/notes.py:173 msgid "special notes" msgstr "notes spéciales" -#: apps/note/models/notes.py:194 +#: apps/note/models/notes.py:202 templates/note/noteactivity_detail.html:22 +msgid "controller" +msgstr "contrôleur" + +#: apps/note/models/notes.py:206 +msgid "activity note" +msgstr "note d'activité" + +#: apps/note/models/notes.py:207 +msgid "activity notes" +msgstr "notes d'activité" + +#: apps/note/models/notes.py:230 msgid "Invalid alias" msgstr "Alias invalide" -#: apps/note/models/notes.py:210 +#: apps/note/models/notes.py:246 msgid "alias" msgstr "alias" -#: apps/note/models/notes.py:211 templates/member/club_info.html:33 +#: apps/note/models/notes.py:247 templates/member/club_info.html:33 #: templates/member/profile_info.html:36 msgid "aliases" msgstr "alias" -#: apps/note/models/notes.py:233 +#: apps/note/models/notes.py:269 msgid "Alias is too long." msgstr "L'alias est trop long." -#: apps/note/models/notes.py:238 +#: apps/note/models/notes.py:274 msgid "An alias with a similar name already exists: {} " msgstr "Un alias avec un nom similaire existe déjà : {}" -#: apps/note/models/notes.py:251 +#: apps/note/models/notes.py:287 msgid "You can't delete your main alias." msgstr "Vous ne pouvez pas supprimer votre alias principal." @@ -578,29 +593,29 @@ msgstr "Cliquez pour valider" msgid "No reason specified" msgstr "Pas de motif spécifié" -#: apps/note/views.py:41 +#: apps/note/views.py:39 msgid "Transfer money" msgstr "Transférer de l'argent" -#: apps/note/views.py:102 templates/base.html:79 +#: apps/note/views.py:100 templates/base.html:79 msgid "Consumptions" msgstr "Consommations" -#: apps/permission/models.py:69 apps/permission/models.py:262 +#: apps/permission/models.py:78 apps/permission/models.py:271 #, python-brace-format msgid "Can {type} {model}.{field} in {query}" msgstr "" -#: apps/permission/models.py:71 apps/permission/models.py:264 +#: apps/permission/models.py:80 apps/permission/models.py:273 #, python-brace-format msgid "Can {type} {model} in {query}" msgstr "" -#: apps/permission/models.py:84 +#: apps/permission/models.py:93 msgid "rank" msgstr "Rang" -#: apps/permission/models.py:147 +#: apps/permission/models.py:156 msgid "Specifying field applies only to view and change permission types." msgstr "" @@ -608,31 +623,32 @@ msgstr "" msgid "Treasury" msgstr "Trésorerie" -#: apps/treasury/forms.py:84 apps/treasury/forms.py:132 +#: apps/treasury/forms.py:85 apps/treasury/forms.py:133 #: 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/treasury/invoice_form.html:46 +#: templates/member/club_form.html:9 templates/note/noteactivity_form.html:14 +#: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "Envoyer" -#: apps/treasury/forms.py:86 +#: apps/treasury/forms.py:87 msgid "Close" msgstr "Fermer" -#: apps/treasury/forms.py:95 +#: apps/treasury/forms.py:96 msgid "Remittance is already closed." msgstr "La remise est déjà fermée." -#: apps/treasury/forms.py:100 +#: apps/treasury/forms.py:101 msgid "You can't change the type of the remittance." msgstr "Vous ne pouvez pas changer le type de la remise." -#: apps/treasury/forms.py:124 templates/note/transaction_form.html:98 +#: apps/treasury/forms.py:125 templates/note/transaction_form.html:98 msgid "Bank" msgstr "Banque" -#: apps/treasury/forms.py:126 apps/treasury/tables.py:47 +#: apps/treasury/forms.py:127 apps/treasury/tables.py:47 #: templates/note/transaction_form.html:128 #: templates/treasury/remittance_form.html:18 msgid "Amount" @@ -881,19 +897,24 @@ msgstr "Ajouter un alias" msgid "Club Parent" msgstr "Club parent" -#: templates/member/club_info.html:41 +#: templates/member/club_info.html:39 +msgid "linked notes" +msgstr "notes liées" + +#: templates/member/club_info.html:44 msgid "Add member" msgstr "Ajouter un membre" -#: templates/member/club_info.html:42 templates/note/conso_form.html:121 +#: templates/member/club_info.html:45 templates/note/conso_form.html:121 +#: templates/note/noteactivity_detail.html:31 msgid "Edit" msgstr "Éditer" -#: templates/member/club_info.html:43 +#: templates/member/club_info.html:46 msgid "Add roles" msgstr "Ajouter des rôles" -#: templates/member/club_info.html:46 templates/member/profile_info.html:48 +#: templates/member/club_info.html:49 templates/member/profile_info.html:48 msgid "View Profile" msgstr "Voir le profil" @@ -914,6 +935,7 @@ msgid "Member of the Club" msgstr "Membre du club" #: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 +#: templates/note/noteactivity_detail.html:42 msgid "Transaction history" msgstr "Historique des transactions" @@ -946,6 +968,7 @@ msgid "Change password" msgstr "Changer le mot de passe" #: templates/member/profile_info.html:33 +#: templates/note/noteactivity_detail.html:25 msgid "balance" msgstr "solde du compte" @@ -994,6 +1017,22 @@ msgstr "Consommations doubles" msgid "Recent transactions history" msgstr "Historique des transactions récentes" +#: templates/note/noteactivity_detail.html:15 +msgid "Linked note:" +msgstr "note attachée :" + +#: templates/note/noteactivity_detail.html:19 +msgid "attached club" +msgstr "club lié" + +#: templates/note/noteactivity_list.html:15 +msgid "linked notes of club" +msgstr "Notes liées au club" + +#: templates/note/noteactivity_list.html:24 +msgid "Add new note" +msgstr "Ajouter une note" + #: templates/note/transaction_form.html:15 msgid "Gift" msgstr "Don" From dd3b7bd7e5ab6a5c598e8f1afdece81516bfc14e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 31 Mar 2020 14:57:44 +0200 Subject: [PATCH 04/19] Remove note activities --- apps/member/forms.py | 23 ----- apps/member/urls.py | 8 -- apps/member/views.py | 84 +------------------ apps/note/admin.py | 12 +-- apps/note/api/serializers.py | 19 +---- apps/note/models/__init__.py | 4 +- apps/note/models/notes.py | 36 -------- apps/note/tables.py | 20 +---- apps/permission/backends.py | 3 +- apps/permission/fixtures/initial.json | 106 ++---------------------- locale/de/LC_MESSAGES/django.po | 33 +------- locale/fr/LC_MESSAGES/django.po | 33 +------- templates/note/noteactivity_detail.html | 60 -------------- templates/note/noteactivity_form.html | 16 ---- templates/note/noteactivity_list.html | 29 ------- 15 files changed, 16 insertions(+), 470 deletions(-) delete mode 100644 templates/note/noteactivity_detail.html delete mode 100644 templates/note/noteactivity_form.html delete mode 100644 templates/note/noteactivity_list.html diff --git a/apps/member/forms.py b/apps/member/forms.py index 9c25f2c2..80c2b49e 100644 --- a/apps/member/forms.py +++ b/apps/member/forms.py @@ -7,7 +7,6 @@ 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.models.notes import NoteActivity from note_kfet.inputs import Autocomplete, AmountInput from permission.models import PermissionMask @@ -59,28 +58,6 @@ class ClubForm(forms.ModelForm): } -class NoteActivityForm(forms.ModelForm): - class Meta: - model = NoteActivity - fields = ('note_name', 'club', 'controller', ) - widgets = { - "club": Autocomplete( - Club, - attrs={ - 'api_url': '/api/members/club/', - } - ), - "controller": Autocomplete( - User, - attrs={ - 'api_url': '/api/user/', - 'name_field': 'username', - 'placeholder': 'Nom ...', - } - ) - } - - class AddMembersForm(forms.Form): class Meta: fields = ('',) diff --git a/apps/member/urls.py b/apps/member/urls.py index f1a5c2bd..7f4bf0d5 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -16,14 +16,6 @@ urlpatterns = [ 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"), - path('club//linked_notes/', views.ClubLinkedNotesView.as_view(), - name="club_linked_note_list"), - path('club//linked_notes/create/', views.ClubLinkedNoteCreateView.as_view(), - name="club_linked_note_create"), - path('club//linked_notes//', views.ClubLinkedNoteDetailView.as_view(), - name="club_linked_note_detail"), - path('club//linked_notes//update/', views.ClubLinkedNoteUpdateView.as_view(), - name="club_linked_note_update"), path('user/', views.UserListView.as_view(), name="user_list"), path('user/', views.UserDetailView.as_view(), name="user_detail"), diff --git a/apps/member/views.py b/apps/member/views.py index 932899ff..9ca7d76a 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -18,15 +18,14 @@ from django_tables2.views import SingleTableView from rest_framework.authtoken.models import Token from note.forms import ImageForm from note.models import Alias, NoteUser -from note.models.notes import NoteActivity from note.models.transactions import Transaction -from note.tables import HistoryTable, AliasTable, NoteActivityTable +from note.tables import HistoryTable, AliasTable 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, NoteActivityForm + CustomAuthenticationForm from .models import Club, Membership from .tables import ClubTable, UserTable @@ -357,82 +356,3 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): def form_valid(self, formset): formset.save() return super().form_valid(formset) - - -class ClubLinkedNotesView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): - model = NoteActivity - table_class = NoteActivityTable - - def get_queryset(self): - return super().get_queryset().filter(club=self.get_object()) - - def get_object(self): - if hasattr(self, 'object'): - return self.object - self.object = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ - .get(pk=int(self.kwargs["pk"])) - return self.object - - def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) - - club = ctx["object"] = ctx["club"] = self.get_object() - - empty_note = NoteActivity(note_name="", club=club, controller=self.request.user) - ctx["can_create"] = PermissionBackend().has_perm(self.request.user, "note.add_noteactivity", empty_note) - - return ctx - - -class ClubLinkedNoteCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): - model = NoteActivity - form_class = NoteActivityForm - - def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) - - club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ - .get(pk=self.kwargs["club_pk"]) - ctx["object"] = ctx["club"] = club - ctx["form"].fields["club"].initial = club - - return ctx - - def get_success_url(self): - self.object.refresh_from_db() - return reverse_lazy('member:club_linked_note_detail', - kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) - - -class ClubLinkedNoteUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): - model = NoteActivity - form_class = NoteActivityForm - - def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) - - ctx["club"] = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ - .get(pk=self.kwargs["club_pk"]) - - return ctx - - def get_success_url(self): - return reverse_lazy('member:club_linked_note_detail', - kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk}) - - -class ClubLinkedNoteDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): - model = NoteActivity - - def get_context_data(self, **kwargs): - ctx = super().get_context_data(**kwargs) - - note = self.get_queryset().filter(pk=self.kwargs["pk"]).get() - - transactions = Transaction.objects.filter(Q(source=note) | Q(destination=note))\ - .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by("-id") - ctx['history_list'] = HistoryTable(transactions) - ctx["note"] = note - ctx["club"] = note.club - - return ctx diff --git a/apps/note/admin.py b/apps/note/admin.py index f0dede17..702d3350 100644 --- a/apps/note/admin.py +++ b/apps/note/admin.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from polymorphic.admin import PolymorphicChildModelAdmin, \ PolymorphicChildModelFilter, PolymorphicParentModelAdmin -from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, NoteActivity +from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \ RecurrentTransaction, MembershipTransaction @@ -24,7 +24,7 @@ class NoteAdmin(PolymorphicParentModelAdmin): """ Parent regrouping all note types as children """ - child_models = (NoteClub, NoteSpecial, NoteUser, NoteActivity) + child_models = (NoteClub, NoteSpecial, NoteUser) list_filter = ( PolymorphicChildModelFilter, 'is_active', @@ -74,14 +74,6 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin): readonly_fields = ('balance',) -@admin.register(NoteActivity) -class NoteActivityAdmin(PolymorphicChildModelAdmin): - """ - Child for a special note, see NoteAdmin - """ - readonly_fields = ('balance',) - - @admin.register(NoteUser) class NoteUserAdmin(PolymorphicChildModelAdmin): """ diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index a445fef9..5625e5b5 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer -from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias, NoteActivity +from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \ RecurrentTransaction, SpecialTransaction @@ -69,22 +69,6 @@ class NoteUserSerializer(serializers.ModelSerializer): return str(obj) -class NoteActivitySerializer(serializers.ModelSerializer): - """ - REST API Serializer for User's notes. - The djangorestframework plugin will analyse the model `NoteActivity` and parse all fields in the API. - """ - name = serializers.SerializerMethodField() - - class Meta: - model = NoteActivity - fields = '__all__' - read_only_fields = ('note', 'user', ) - - def get_name(self, obj): - return str(obj) - - class AliasSerializer(serializers.ModelSerializer): """ REST API Serializer for Aliases. @@ -107,7 +91,6 @@ class NotePolymorphicSerializer(PolymorphicSerializer): NoteUser: NoteUserSerializer, NoteClub: NoteClubSerializer, NoteSpecial: NoteSpecialSerializer, - NoteActivity: NoteActivitySerializer, } class Meta: diff --git a/apps/note/models/__init__.py b/apps/note/models/__init__.py index dd024963..e9c8a0a9 100644 --- a/apps/note/models/__init__.py +++ b/apps/note/models/__init__.py @@ -1,13 +1,13 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, NoteActivity +from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser from .transactions import MembershipTransaction, Transaction, \ TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction __all__ = [ # Notes - 'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser', 'NoteActivity', + 'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser', # Transactions 'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate', 'RecurrentTransaction', 'SpecialTransaction', diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index 1228ca00..9282bde9 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -4,13 +4,11 @@ import unicodedata from django.conf import settings -from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.db import models from django.utils.translation import gettext_lazy as _ from polymorphic.models import PolymorphicModel -from member.models import Club """ Defines each note types @@ -176,40 +174,6 @@ class NoteSpecial(Note): return self.special_type -class NoteActivity(Note): - """ - A :model:`note.Note` for accounts that are not attached to a user neither to a club, - that only need to store and transfer money (notes for activities, departments, ...) - """ - - note_name = models.CharField( - verbose_name=_('name'), - max_length=255, - unique=True, - ) - - club = models.ForeignKey( - Club, - on_delete=models.PROTECT, - related_name="linked_notes", - verbose_name=_("club"), - ) - - controller = models.ForeignKey( - User, - on_delete=models.PROTECT, - related_name="+", - verbose_name=_("controller"), - ) - - class Meta: - verbose_name = _("activity note") - verbose_name_plural = _("activity notes") - - def __str__(self): - return self.note_name - - class Alias(models.Model): """ points toward a :model:`note.NoteUser` or :model;`note.NoteClub` instance. diff --git a/apps/note/tables.py b/apps/note/tables.py index 2aba4684..0d83e3cc 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -9,7 +9,7 @@ from django.utils.html import format_html from django_tables2.utils import A from django.utils.translation import gettext_lazy as _ -from .models.notes import Alias, NoteActivity +from .models.notes import Alias from .models.transactions import Transaction, TransactionTemplate from .templatetags.pretty_money import pretty_money @@ -121,24 +121,6 @@ class AliasTable(tables.Table): attrs={'td': {'class': 'col-sm-1'}}) -class NoteActivityTable(tables.Table): - note_name = tables.LinkColumn( - "member:club_linked_note_detail", - args=[A("club.pk"), A("pk")], - ) - - def render_balance(self, value): - return pretty_money(value) - - class Meta: - attrs = { - 'class': 'table table-condensed table-striped table-hover' - } - model = NoteActivity - fields = ('note_name', 'balance',) - template_name = 'django_tables2/bootstrap4.html' - - class ButtonTable(tables.Table): class Meta: attrs = { diff --git a/apps/permission/backends.py b/apps/permission/backends.py index f838fc1f..30a273cc 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -5,7 +5,7 @@ from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.db.models import Q, F -from note.models import Note, NoteUser, NoteClub, NoteSpecial, NoteActivity +from note.models import Note, NoteUser, NoteClub, NoteSpecial from note_kfet.middlewares import get_current_session from member.models import Membership, Club @@ -49,7 +49,6 @@ class PermissionBackend(ModelBackend): NoteUser=NoteUser, NoteClub=NoteClub, NoteSpecial=NoteSpecial, - NoteActivity=NoteActivity, F=F, Q=Q ) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index e0430530..04920cfb 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -176,7 +176,7 @@ "note", "alias" ], - "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}, {\"note__in\": [\"NoteActivity\", \"objects\", [\"all\"]]}]", + "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}]", "type": "view", "mask": 1, "field": "", @@ -798,96 +798,6 @@ "description": "Update club" } }, - { - "model": "permission.permission", - "pk": 48, - "fields": { - "model": [ - "note", - "noteactivity" - ], - "query": "{\"club\": [\"club\"]}", - "type": "change", - "mask": 1, - "field": "", - "description": "Manage notes that are linked to a club" - } - }, - { - "model": "permission.permission", - "pk": 49, - "fields": { - "model": [ - "note", - "noteactivity" - ], - "query": "{\"club\": [\"club\"]}", - "type": "view", - "mask": 1, - "field": "", - "description": "View notes that are linked to a club" - } - }, - { - "model": "permission.permission", - "pk": 50, - "fields": { - "model": [ - "note", - "transaction" - ], - "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]", - "type": "add", - "mask": 2, - "field": "", - "description": "Add transactions linked to a noteactivity" - } - }, - { - "model": "permission.permission", - "pk": 51, - "fields": { - "model": [ - "note", - "transaction" - ], - "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}]]", - "type": "view", - "mask": 1, - "field": "", - "description": "View transactions linked to a noteactivity" - } - }, - { - "model": "permission.permission", - "pk": 52, - "fields": { - "model": [ - "note", - "note" - ], - "query": "{\"noteactivity__controller\": [\"user\"]}", - "type": "view", - "mask": 1, - "field": "", - "description": "View note activity" - } - }, - { - "model": "permission.permission", - "pk": 53, - "fields": { - "model": [ - "note", - "noteactivity" - ], - "query": "{\"controller\": [\"user\"]}", - "type": "view", - "mask": 1, - "field": "", - "description": "View note activity" - } - }, { "model": "permission.rolepermissions", "pk": 1, @@ -915,6 +825,7 @@ 3, 4, 5, + 6, 7, 8, 9, @@ -932,11 +843,6 @@ 36, 39, 40, - 6, - 52, - 53, - 51, - 50 ] } }, @@ -947,9 +853,9 @@ "role": 8, "permissions": [ 19, + 20, 21, - 22, - 20 + 22 ] } }, @@ -997,9 +903,7 @@ "role": 7, "permissions": [ 22, - 47, - 48, - 49 + 47 ] } } diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index c83bf321..695bdf95 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -456,18 +456,6 @@ msgstr "" msgid "special notes" msgstr "" -#: apps/note/models/notes.py:202 templates/note/noteactivity_detail.html:22 -msgid "controller" -msgstr "" - -#: apps/note/models/notes.py:206 -msgid "activity note" -msgstr "" - -#: apps/note/models/notes.py:207 -msgid "activity notes" -msgstr "" - #: apps/note/models/notes.py:230 msgid "Invalid alias" msgstr "" @@ -626,7 +614,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/note/noteactivity_form.html:14 +#: templates/member/club_form.html:9 #: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "" @@ -903,7 +891,6 @@ msgid "Add member" msgstr "" #: templates/member/club_info.html:45 templates/note/conso_form.html:121 -#: templates/note/noteactivity_detail.html:31 msgid "Edit" msgstr "" @@ -932,7 +919,6 @@ msgid "Member of the Club" msgstr "" #: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 -#: templates/note/noteactivity_detail.html:42 msgid "Transaction history" msgstr "" @@ -965,7 +951,6 @@ msgid "Change password" msgstr "" #: templates/member/profile_info.html:33 -#: templates/note/noteactivity_detail.html:25 msgid "balance" msgstr "" @@ -1014,22 +999,6 @@ msgstr "" msgid "Recent transactions history" msgstr "" -#: templates/note/noteactivity_detail.html:15 -msgid "Linked note:" -msgstr "" - -#: templates/note/noteactivity_detail.html:19 -msgid "attached club" -msgstr "" - -#: templates/note/noteactivity_list.html:15 -msgid "linked notes of club" -msgstr "" - -#: templates/note/noteactivity_list.html:24 -msgid "Add new note" -msgstr "" - #: templates/note/transaction_form.html:15 msgid "Gift" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index cd01499d..d2a78d06 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -457,18 +457,6 @@ msgstr "note spéciale" msgid "special notes" msgstr "notes spéciales" -#: apps/note/models/notes.py:202 templates/note/noteactivity_detail.html:22 -msgid "controller" -msgstr "contrôleur" - -#: apps/note/models/notes.py:206 -msgid "activity note" -msgstr "note d'activité" - -#: apps/note/models/notes.py:207 -msgid "activity notes" -msgstr "notes d'activité" - #: apps/note/models/notes.py:230 msgid "Invalid alias" msgstr "Alias invalide" @@ -627,7 +615,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/note/noteactivity_form.html:14 +#: templates/member/club_form.html:9 #: templates/treasury/invoice_form.html:46 msgid "Submit" msgstr "Envoyer" @@ -906,7 +894,6 @@ msgid "Add member" msgstr "Ajouter un membre" #: templates/member/club_info.html:45 templates/note/conso_form.html:121 -#: templates/note/noteactivity_detail.html:31 msgid "Edit" msgstr "Éditer" @@ -935,7 +922,6 @@ msgid "Member of the Club" msgstr "Membre du club" #: templates/member/club_tables.html:22 templates/member/profile_tables.html:22 -#: templates/note/noteactivity_detail.html:42 msgid "Transaction history" msgstr "Historique des transactions" @@ -968,7 +954,6 @@ msgid "Change password" msgstr "Changer le mot de passe" #: templates/member/profile_info.html:33 -#: templates/note/noteactivity_detail.html:25 msgid "balance" msgstr "solde du compte" @@ -1017,22 +1002,6 @@ msgstr "Consommations doubles" msgid "Recent transactions history" msgstr "Historique des transactions récentes" -#: templates/note/noteactivity_detail.html:15 -msgid "Linked note:" -msgstr "note attachée :" - -#: templates/note/noteactivity_detail.html:19 -msgid "attached club" -msgstr "club lié" - -#: templates/note/noteactivity_list.html:15 -msgid "linked notes of club" -msgstr "Notes liées au club" - -#: templates/note/noteactivity_list.html:24 -msgid "Add new note" -msgstr "Ajouter une note" - #: templates/note/transaction_form.html:15 msgid "Gift" msgstr "Don" diff --git a/templates/note/noteactivity_detail.html b/templates/note/noteactivity_detail.html deleted file mode 100644 index aab9e055..00000000 --- a/templates/note/noteactivity_detail.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "member/noteowner_detail.html" %} - -{% load i18n %} -{% load render_table from django_tables2 %} -{% load pretty_money %} -{% load perms %} - -{% block profile_info %} -{% include "member/club_info.html" %} -{% endblock %} - -{% block profile_content %} -
-
-

{% trans "Linked note:" %} {{ note.note_name }}

-
-
-
-
{% trans 'attached club'|capfirst %}
-
{{ club }}
- -
{% trans 'controller'|capfirst %}
-
{{ note.controller }}
- -
{% trans 'balance'|capfirst %}
-
{{ note.balance|pretty_money }}
-
- - {% if "change_"|has_perm:note %} - - {% endif %} -
- - -
- -
-
- {% render_table history_list %} -
-
-
-{% endblock %} - -{% block extrajavascript %} - -{% endblock %} diff --git a/templates/note/noteactivity_form.html b/templates/note/noteactivity_form.html deleted file mode 100644 index 5088c790..00000000 --- a/templates/note/noteactivity_form.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "member/noteowner_detail.html" %} - -{% load i18n %} -{% load crispy_forms_tags %} - -{% block profile_info %} -{% include "member/club_info.html" %} -{% endblock %} - -{% block profile_content %} -
- {% csrf_token %} - {{ form|crispy }} - -
-{% endblock %} diff --git a/templates/note/noteactivity_list.html b/templates/note/noteactivity_list.html deleted file mode 100644 index 3cd5b786..00000000 --- a/templates/note/noteactivity_list.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "member/noteowner_detail.html" %} - -{% load i18n %} -{% load render_table from django_tables2 %} - -{% block profile_info %} -{% include "member/club_info.html" %} -{% endblock %} - -{% block profile_content %} -
-
-
-
-
{% trans "linked notes of club"|capfirst %} {{ club.name }}
-
-
- {% render_table table %} -
-
- - {% if can_create %} - - - - {% endif %} -
-
-{% endblock %} From e98693b21403d32f0e2d91a379f964ab8ef4f6f7 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 31 Mar 2020 16:22:11 +0200 Subject: [PATCH 05/19] Memberships are optional for clubs --- apps/member/fixtures/initial.json | 2 ++ apps/member/models.py | 21 +++++++++++++++++++++ apps/permission/fixtures/initial.json | 2 +- templates/member/club_form.html | 20 ++++++++++++++++++++ templates/member/club_info.html | 21 ++++++++++----------- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/apps/member/fixtures/initial.json b/apps/member/fixtures/initial.json index bba1e7ac..649cbbc1 100644 --- a/apps/member/fixtures/initial.json +++ b/apps/member/fixtures/initial.json @@ -5,6 +5,7 @@ "fields": { "name": "BDE", "email": "tresorerie.bde@example.com", + "require_memberships": true, "membership_fee": 500, "membership_duration": "396 00:00:00", "membership_start": "213 00:00:00", @@ -17,6 +18,7 @@ "fields": { "name": "Kfet", "email": "tresorerie.bde@example.com", + "require_memberships": true, "membership_fee": 3500, "membership_duration": "396 00:00:00", "membership_start": "213 00:00:00", diff --git a/apps/member/models.py b/apps/member/models.py index d0051e59..377cc010 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -77,22 +77,34 @@ class Club(models.Model): ) # Memberships + + # When set to False, the membership system won't be used. + # Useful to create notes for activities or departments. + require_memberships = models.BooleanField( + default=True, + verbose_name=_("require memberships"), + ) + membership_fee = models.PositiveIntegerField( + default=0, verbose_name=_('membership fee'), ) membership_duration = models.DurationField( + blank=True, null=True, verbose_name=_('membership duration'), help_text=_('The longest time a membership can last ' '(NULL = infinite).'), ) membership_start = models.DurationField( + 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( + blank=True, null=True, verbose_name=_('membership end'), help_text=_('How long the membership can last after January 1st ' @@ -100,6 +112,15 @@ class Club(models.Model): 'membership.'), ) + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + if not self.require_memberships: + self.membership_fee = 0 + self.membership_duration = None + self.membership_start = None + self.membership_end = None + super().save(force_insert, force_update, update_fields) + class Meta: verbose_name = _("club") verbose_name_plural = _("clubs") diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 04920cfb..5bf0d77d 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -842,7 +842,7 @@ 35, 36, 39, - 40, + 40 ] } }, diff --git a/templates/member/club_form.html b/templates/member/club_form.html index 99c254e3..7abe9db9 100644 --- a/templates/member/club_form.html +++ b/templates/member/club_form.html @@ -9,3 +9,23 @@ {% endblock %} + +{% block extrajavascript %} + +{% endblock %} diff --git a/templates/member/club_info.html b/templates/member/club_info.html index 907914be..039583c5 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -18,26 +18,25 @@
{{ club.parent_club.name}}
{% endif %} -
{% trans 'membership start'|capfirst %}
-
{{ club.membership_start }}
+ {% if club.require_memberships %} +
{% trans 'membership start'|capfirst %}
+
{{ club.membership_start }}
-
{% trans 'membership end'|capfirst %}
-
{{ club.membership_end }}
+
{% trans 'membership end'|capfirst %}
+
{{ club.membership_end }}
-
{% trans 'membership duration'|capfirst %}
-
{{ club.membership_duration }}
+
{% trans 'membership duration'|capfirst %}
+
{{ club.membership_duration }}
-
{% trans 'membership fee'|capfirst %}
-
{{ club.membership_fee|pretty_money }}
+
{% trans 'membership fee'|capfirst %}
+
{{ club.membership_fee|pretty_money }}
+ {% endif %}
{% trans 'aliases'|capfirst %}
{{ object.note.alias_set.all|join:", " }}
{% trans 'email'|capfirst %}
{{ club.email }}
- -
{% trans 'linked notes'|capfirst %}
-
{{ club.linked_notes.all|join:", " }}