Commit 8cc0d054 authored by Hamza Dely's avatar Hamza Dely

[consos/api] Implémentation de l'API de gestion des notes

parent fd0f4908
"""
Vues de l'API de l'application « Comptes »
"""
from rest_framework import routers
from consos.views import NoteViewSet
router = routers.DefaultRouter()
router.register(r'notes', NoteViewSet)
#router.register(r'adherents', AdherentViewSet)
urlpatterns = router.urls
"""
Sérialiseurs de l'application « Consos »
"""
from rest_framework import serializers
from note_kfet.serializers import mixins
from consos.models import Note
class NoteRechercheSerializer(serializers.Serializer):
"""
Sérialiseur spécifique au renvoi des informations de notes lors d'une recherche
de notes par alias.
"""
alias = serializers.CharField(read_only=True)
adherent_id = serializers.IntegerField(source='proprietaire.pk', read_only=True)
pseudo = serializers.CharField(source='proprietaire.pseudo', read_only=True)
solde = serializers.DecimalField(source='proprietaire.note.solde', max_digits=14, decimal_places=2, read_only=True)
class NoteSerializer(mixins.DynamicFieldsMixin, serializers.ModelSerializer):
"""
Sérialiseur pour les objets Note
"""
#aliases = serializers.SlugRelatedField(slug_field="adherent__aliases__alias", many=True, read_only=True)
class Meta:
model = Note
fields = [
'adherent', 'solde', 'releve_freq',
'dernier_releve', 'soft_lock', 'hard_lock',
]
read_only_fields = ['adherent']
default_empty = False
......@@ -13,11 +13,20 @@ from django.views.generic.edit import FormView, CreateView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import detail_route
from note_kfet.droits import D, Acl
from note_kfet.views.mixins import NoteMixin
from consos.models import Note, Transaction, Bouton
from consos.forms import BoutonConsoForm, CreditConsoForm, RetraitConsoForm, TransfertConsoForm
from consos.serializers import NoteRechercheSerializer, NoteSerializer
####################################################################################################
## Vues de l'interface Web ##
####################################################################################################
class NoteOuvrirView(PermissionRequiredMixin, LoginRequiredMixin, RedirectView):
"""
......@@ -289,3 +298,160 @@ class BoutonSupprimerView(PermissionRequiredMixin, LoginRequiredMixin, RedirectV
bouton = get_object_or_404(Bouton, pk=kwargs.pop('pk'))
bouton.delete()
return super().get_redirect_url(*args, **kwargs)
####################################################################################################
## Vues de l'API ##
####################################################################################################
class NoteViewSet(viewsets.GenericViewSet):
"""
Ensemble de vues concernant la gestion des notes
"""
queryset = Note.objects.all()
serializer_class = NoteSerializer
def list(self, request):
"""
Renvoie la liste de toutes les notes actives
Les données doivent :
- être envoyées avec une requête GET
- contenir un paramètre 'search' contenant une expression régulière
à rechercher
"""
if not request.user.has_perm("comptes.adherent_chercher", Acl.BASIQUE):
return Response({}, status=status.HTTP_403_FORBIDDEN)
search = request.query_params.get('search', None)
if search is None:
return Response(
{
"detail" : "Paramètre 'search' absent",
},
status=status.HTTP_400_BAD_REQUEST,
)
lookup_args = {}
if not request.user.has_perm("comptes.adherent_chercher", Acl.TOTAL):
lookup_args.update(**{'adherent__supprime' : False})
if not request.user.has_perm("comptes.adherent_chercher", Acl.ETENDU):
lookup_args.update(**{'adherent__is_active' : False})
note_qs = self.get_queryset().filter(**lookup_args)
alias_class = ContentType.objects.get(model="alias").model_class()
qs = alias_class.objects.filter(alias__iregex="^%s" % search, proprietaire__note__in=note_qs).order_by('alias')
serializer = NoteRechercheSerializer(qs, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def retrieve(self, request, pk=None):
"""
Renvoie les informations sur la note demandée
Les données doivent :
- être envoyées via une requête GET
"""
if not request.user.has_perm("comptes.adherent_detail", Acl.LIMITE):
return Response({}, status=status.HTTP_403_FORBIDDEN)
if (request.user.pk != pk
and not request.user.has_perm("comptes.adherent_detail", Acl.BASIQUE)):
return Response(
{
"detail" : "Vous ne pouvez visualiser que votre propre note",
},
status=status.HTTP_403_FORBIDDEN,
)
note = self.get_object()
serializer = self.get_serializer(note)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
"""
Ouvre la note d'un adhérent donné
Les données doivent :
- être envoyées via une requête POST
- contenir un paramètre 'adherent' contenant l'identifiant de l'adhérent
dont il faut ouvrir la note
"""
if not request.user.has_perm("consos.note_ouvrir", Acl.BASIQUE):
return Response({}, status=status.HTTP_403_FORBIDDEN)
adh_id = request.data.get('pk', None)
if adh_id is None:
return Response({"detail" : "Identifiant de l'adhérent manquant"}, status=status.HTTP_400_BAD_REQUEST)
adh_class = ContentType.objects.get(model="adherent").model_class()
if get_object_or_404(adh_class, id=adh_id).type == adh_class.DEBIT:
return Response(
{
"detail" : "Impossible d'ouvrir une note pour un compte de débit",
},
status=status.HTTP_400_BAD_REQUEST,
)
_, created = Note.objects.get_or_create(adherent_id=adh_id)
if not created:
return Response({"detail" : "L'adhérent dispose déjà d'une note"}, status=status.HTTP_304_NOT_MODIFIED)
else:
return Response({}, status=status.HTTP_201_CREATED)
@detail_route(methods=['patch'])
def protect(self, request, pk=None):
"""
Protège une note, i.e. empêche qu'une transaction ne puisse être effectuée dessus
Les données doivent :
- être envoyées via une requête PATCH
- contenir un paramètre 'protect' pouvant valoir 'true' ou 'false'
"""
if request.user.has_perm("consos.note_soft_lock", Acl.LIMITE):
return Response({}, status=status.HTTP_403_FORBIDDEN)
protect = request.data.get('protect', None)
if protect is None:
return Response({"detail" : "Paramètre 'protect' manquant"}, status=status.HTTP_400_BAD_REQUEST)
elif protect not in ['true', 'false']:
return Response({"detail" : "Paramètre 'protect' invalide"}, status=status.HTTP_400_BAD_REQUEST)
if protect == 'true':
data = {'soft_lock' : True}
else:
data = {'soft_lock' : False}
note = self.get_object()
serializer = self.get_serializer(note, data=data, partial=True)
if note.adherent != request.user and not request.user.has_perm("consos.note_soft_lock", Acl.TOTAL):
return Response({"detail" : "Vous ne pouvez protéger que vôtre propre note"}, status=status.HTTP_403_FORBIDDEN)
serializer.save()
return Response({}, status=status.HTTP_200_OK)
@detail_route(methods=['patch'])
def block(self, request, pk=None):
"""
Bloque une note, i.e. empêche que la note ne puisse être utilisée.
Le blocage est posé par un membre de l'association et ne peut être retiré par d'adhérent.
Les données doivent :
- être envoyées via une requête PATCH
- contenir un paramètre 'block' pouvant valoir 'true' ou 'false'
"""
if request.user.has_perm("consos.note_hard_lock", Acl.BASIQUE):
return Response({}, status=status.HTTP_403_FORBIDDEN)
block = request.data.get('block', None)
if block is None:
return Response({"detail" : "Paramètre 'block' manquant"}, status=status.HTTP_400_BAD_REQUEST)
elif block not in ['true', 'false']:
return Response({"detail" : "Paramètre 'block' invalide"}, status=status.HTTP_400_BAD_REQUEST)
if block == 'true':
data = {'hard_lock' : True}
else:
data = {'hard_lock' : False}
note = self.get_object()
serializer = self.get_serializer(note, data=data, partial=True)
if note.adherent.type != note.adherent.PERSONNE and not request.user.has_perm("consos.note_hard_lock", Acl.TOTAL):
return Response({"detail" : "Vous ne pouvez bloquer que les adhérents de type personne"}, status=status.HTTP_403_FORBIDDEN)
serializer.save()
return Response({}, status=status.HTTP_200_OK)
......@@ -10,7 +10,7 @@ from rest_framework.reverse import reverse_lazy
from rest_framework.response import Response
# Dictionnaire contenant les apps disposant d'une API
APPS_API = ['comptes', 'activites']
APPS_API = ['comptes', 'activites', 'consos']
urlpatterns = [
url('^%s/' % app, include('%s.api' % app, namespace=app)) for app in APPS_API
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment