svthalia/concrexit

View on GitHub
website/payments/api/v2/views.py

Summary

Maintainability
A
0 mins
Test Coverage
from django.apps import apps
from django.utils.translation import gettext_lazy as _

import rest_framework.filters as framework_filters
from rest_framework import serializers, status
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.generics import ListAPIView, RetrieveAPIView, get_object_or_404
from rest_framework.response import Response
from rest_framework.settings import api_settings

from payments import services
from payments.api.v2 import filters
from payments.api.v2.serializers import PaymentSerializer
from payments.api.v2.serializers.payable_detail import PayableSerializer
from payments.api.v2.serializers.payment_user import PaymentUserSerializer
from payments.exceptions import PaymentError
from payments.models import Payment, PaymentUser
from payments.payables import NotRegistered, payables
from thaliawebsite.api.v2.permissions import IsAuthenticatedOrTokenHasScopeForMethod
from thaliawebsite.api.v2.serializers import EmptySerializer


class PaymentListView(ListAPIView):
    """Returns an overview of all an authenticated user's payments."""

    queryset = Payment.objects.all()
    serializer_class = PaymentSerializer

    permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
    required_scopes_per_method = {"GET": ["payments:read"]}
    filter_backends = (
        framework_filters.OrderingFilter,
        filters.CreatedAtFilter,
        filters.PaymentTypeFilter,
        filters.PaymentSettledFilter,
    )
    ordering_fields = ("created_at",)

    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(paid_by=PaymentUser.objects.get(pk=self.request.member.pk))
        )


class PaymentDetailView(RetrieveAPIView):
    """Returns a single payment made by the authenticated user."""

    queryset = Payment.objects.all()
    serializer_class = PaymentSerializer
    permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
    required_scopes_per_method = {"GET": ["payments:read"]}

    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(paid_by=PaymentUser.objects.get(pk=self.request.member.pk))
        )


class PayableDetailView(RetrieveAPIView):
    """Allow you to get information about a payable and process it."""

    permission_classes = [IsAuthenticatedOrTokenHasScopeForMethod]
    required_scopes_per_method = {
        "GET": ["payments:read"],
        "PATCH": ["payments:write"],
    }

    def get_serializer_class(self, *args, **kwargs):
        if self.request.method.lower() == "patch":
            return EmptySerializer
        return PayableSerializer

    def get(self, request, *args, **kwargs):
        serializer = self.get_serializer(self.get_payable())
        return Response(serializer.data, status=status.HTTP_200_OK)

    def get_payable(self):
        app_label = self.kwargs["app_label"]
        model_name = self.kwargs["model_name"]
        payable_pk = self.kwargs["payable_pk"]

        try:
            payable_model = apps.get_model(app_label=app_label, model_name=model_name)
            payable = payables.get_payable(
                get_object_or_404(payable_model, pk=payable_pk)
            )
        except (LookupError, NotRegistered) as e:
            raise serializers.ValidationError(
                {api_settings.NON_FIELD_ERRORS_KEY: [_("Payable model not found.")]}
            ) from e

        if (
            not payable.payment_payer
            or not payable.tpay_allowed
            or payable.payment_payer != PaymentUser.objects.get(pk=self.request.user.pk)
        ):
            raise PermissionDenied(
                detail=_("You do not have permission to perform this action.")
            )

        return payable

    def patch(self, request, *args, **kwargs):
        payable = self.get_payable()

        if payable.payment:
            return Response(
                data={"detail": _("This object has already been paid for.")},
                status=status.HTTP_409_CONFLICT,
            )

        try:
            services.create_payment(
                payable,
                PaymentUser.objects.get(pk=request.user.pk),
                Payment.TPAY,
            )
        except PaymentError as e:
            raise ValidationError(
                detail={api_settings.NON_FIELD_ERRORS_KEY: [str(e)]}
            ) from e

        payable.model.refresh_from_db()
        return Response(
            PayableSerializer(payable, context=self.get_serializer_context()).data,
            status=status.HTTP_201_CREATED,
        )


class PaymentUserCurrentView(RetrieveAPIView):
    """Returns details of the authenticated member."""

    queryset = PaymentUser
    serializer_class = PaymentUserSerializer
    permission_classes = [
        IsAuthenticatedOrTokenHasScopeForMethod,
    ]

    required_scopes_per_method = {"GET": ["payments:read"]}

    def get_object(self):
        return get_object_or_404(PaymentUser, pk=self.request.user.pk)