diff --git a/apps/note/api/views.py b/apps/note/api/views.py index f230a646..fc4a0e8f 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -5,6 +5,7 @@ from django.db.models import Q from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter, SearchFilter from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet +from rest_framework import viewsets from .serializers import NotePolymorphicSerializer, AliasSerializer, TemplateCategorySerializer, \ TransactionTemplateSerializer, TransactionPolymorphicSerializer @@ -81,7 +82,7 @@ class TemplateCategoryViewSet(ReadProtectedModelViewSet): search_fields = ['$name', ] -class TransactionTemplateViewSet(ReadProtectedModelViewSet): +class TransactionTemplateViewSet(viewsets.ModelViewSet): """ REST API View set. The djangorestframework plugin will get all `TransactionTemplate` objects, serialize it to JSON with the given serializer, @@ -89,8 +90,9 @@ class TransactionTemplateViewSet(ReadProtectedModelViewSet): """ queryset = TransactionTemplate.objects.all() serializer_class = TransactionTemplateSerializer - filter_backends = [DjangoFilterBackend] + filter_backends = [SearchFilter, DjangoFilterBackend] filterset_fields = ['name', 'amount', 'display', 'category', ] + search_fields = ['$name', ] class TransactionViewSet(ReadProtectedModelViewSet): diff --git a/apps/note/tables.py b/apps/note/tables.py index b9dac051..20054d2c 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -9,7 +9,7 @@ from django_tables2.utils import A from django.utils.translation import gettext_lazy as _ from .models.notes import Alias -from .models.transactions import Transaction +from .models.transactions import Transaction, TransactionTemplate from .templatetags.pretty_money import pretty_money @@ -57,6 +57,12 @@ class HistoryTable(tables.Table): return "✔" if value else "✖" +# function delete_button(id) provided in template file +DELETE_TEMPLATE = """ + +""" + + class AliasTable(tables.Table): class Meta: attrs = { @@ -69,9 +75,41 @@ class AliasTable(tables.Table): show_header = False name = tables.Column(attrs={'td': {'class': 'text-center'}}) + # delete = tables.TemplateColumn(template_code=delete_template, + # attrs={'td':{'class': 'col-sm-1'}}) + delete = tables.LinkColumn('member:user_alias_delete', args=[A('pk')], attrs={ 'td': {'class': 'col-sm-2'}, 'a': {'class': 'btn btn-danger'}}, text='delete', accessor='pk') + + +class ButtonTable(tables.Table): + class Meta: + attrs = { + 'class': + 'table table-bordered condensed table-hover' + } + row_attrs = { + 'class': lambda record: 'table-row ' + 'table-success' if record.display else 'table-danger', + 'id': lambda record: "row-" + str(record.pk), + 'data-href': lambda record: record.pk + } + + model = TransactionTemplate + + edit = tables.LinkColumn('note:template_update', + args=[A('pk')], + attrs={'td': {'class': 'col-sm-1'}, + 'a': {'class': 'btn btn-primary'}}, + text=_('edit'), + accessor='pk') + + delete = tables.TemplateColumn(template_code=DELETE_TEMPLATE, + extra_context={"delete_trans": _('delete')}, + attrs={'td': {'class': 'col-sm-1'}}) + + def render_amount(self, value): + return pretty_money(value) diff --git a/apps/note/urls.py b/apps/note/urls.py index fea911f6..59316069 100644 --- a/apps/note/urls.py +++ b/apps/note/urls.py @@ -8,7 +8,7 @@ from .models import Note app_name = 'note' urlpatterns = [ - path('transfer/', views.TransactionCreate.as_view(), name='transfer'), + path('transfer/', views.TransactionCreateView.as_view(), name='transfer'), path('buttons/create/', views.TransactionTemplateCreateView.as_view(), name='template_create'), path('buttons/update//', views.TransactionTemplateUpdateView.as_view(), name='template_update'), path('buttons/', views.TransactionTemplateListView.as_view(), name='template_list'), diff --git a/apps/note/views.py b/apps/note/views.py index 84df2bd7..ddf5ee6f 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -6,22 +6,25 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.translation import gettext_lazy as _ -from django.views.generic import CreateView, ListView, UpdateView +from django.views.generic import CreateView, UpdateView from django_tables2 import SingleTableView +from django.urls import reverse_lazy from permission.backends import PermissionBackend from .forms import TransactionTemplateForm from .models import Transaction, TransactionTemplate, Alias, RecurrentTransaction, NoteSpecial from .models.transactions import SpecialTransaction -from .tables import HistoryTable +from .tables import HistoryTable, ButtonTable -class TransactionCreate(LoginRequiredMixin, SingleTableView): +class TransactionCreateView(LoginRequiredMixin, SingleTableView): """ - Show transfer page + 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` """ template_name = "note/transaction_form.html" + model = Transaction # Transaction history table table_class = HistoryTable table_pagination = {"per_page": 50} @@ -46,13 +49,14 @@ class TransactionCreate(LoginRequiredMixin, SingleTableView): class NoteAutocomplete(autocomplete.Select2QuerySetView): """ - Auto complete note by aliases + Auto complete note by aliases. Used in every search field for note + ex: :view:`ConsoView`, :view:`TransactionCreateView` """ def get_queryset(self): """ - Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion. - Cette fonction récupère la requête, et renvoie la liste filtrée des aliases. + When someone look for an :models:`note.Alias`, a query is sent to the dedicated API. + This function handles the result and return a filtered list of aliases. """ # Un utilisateur non connecté n'a accès à aucune information if not self.request.user.is_authenticated: @@ -81,6 +85,10 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView): return qs def get_result_label(self, result): + """ + Show the selected alias and the username associated + (aka. ) + """ # Gère l'affichage de l'alias dans la recherche res = result.name note_name = str(result.note) @@ -89,7 +97,9 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView): return res def get_result_value(self, result): - # Le résultat renvoyé doit être l'identifiant de la note, et non de l'alias + """ + The value used for the transactions will be the id of the Note. + """ return str(result.note.pk) @@ -99,14 +109,15 @@ class TransactionTemplateCreateView(LoginRequiredMixin, CreateView): """ model = TransactionTemplate form_class = TransactionTemplateForm + success_url = reverse_lazy('note:template_list') -class TransactionTemplateListView(LoginRequiredMixin, ListView): +class TransactionTemplateListView(LoginRequiredMixin, SingleTableView): """ List TransactionsTemplates """ model = TransactionTemplate - form_class = TransactionTemplateForm + table_class = ButtonTable class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView): @@ -114,11 +125,13 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView): """ model = TransactionTemplate form_class = TransactionTemplateForm + success_url = reverse_lazy('note:template_list') class ConsoView(LoginRequiredMixin, SingleTableView): """ - Consume + 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) """ template_name = "note/conso_form.html" diff --git a/apps/permission/permissions.py b/apps/permission/permissions.py index d9816a63..9f6d8cd2 100644 --- a/apps/permission/permissions.py +++ b/apps/permission/permissions.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from rest_framework.permissions import DjangoObjectPermissions +from .backends import PermissionBackend SAFE_METHODS = ('HEAD', 'OPTIONS', ) @@ -41,8 +42,8 @@ class StrongDjangoObjectPermissions(DjangoObjectPermissions): user = request.user perms = self.get_required_object_permissions(request.method, model_cls) - - if not user.has_perms(perms, obj): + # if not user.has_perms(perms, obj): + if not all(PermissionBackend().has_perm(user, perm, obj) for perm in perms): # If the user does not have permissions we need to determine if # they have read permissions to see 403, or not, and simply see # a 404 response. diff --git a/templates/base.html b/templates/base.html index fae86443..2f07a6cc 100644 --- a/templates/base.html +++ b/templates/base.html @@ -79,6 +79,9 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Consumptions' %} {% endif %} + {% if "member.club"|not_empty_model_list %} {% endif %} - {% if "note.transactiontemplate"|not_empty_model_change_list %} - - {% endif %} -