You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

141 lines
5.8 KiB

import logging
from decimal import Decimal
from django.db.models import Q, F
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiResponse, OpenApiParameter
from apps.donations.models import DonationCampaign, DonationTransaction, PaymentStatus
from apps.donations.serializers import (
DonationCampaignListSerializer,
DonationCampaignDetailSerializer,
DonationTransactionSerializer,
DonationContributeRequestSerializer,
)
logger = logging.getLogger(__name__)
def get_campaign_by_pk_or_slug(pk_or_slug, queryset=None):
if queryset is None:
queryset = DonationCampaign.objects.all()
if str(pk_or_slug).isdigit():
return get_object_or_404(queryset, pk=int(pk_or_slug))
return get_object_or_404(queryset, slug=pk_or_slug)
class DonationCampaignListView(GenericAPIView):
permission_classes = [AllowAny]
serializer_class = DonationCampaignListSerializer
queryset = DonationCampaign.objects.filter(is_active=True)
@extend_schema(
summary="List active donation appeals & campaigns",
description="Retrieves charitable campaigns with target, raised amounts, progress percentages, and filters.",
parameters=[
OpenApiParameter('category', str, description="Filter by campaign category"),
OpenApiParameter('priority', str, description="Filter by priority (urgent, high, medium)"),
OpenApiParameter('search', str, description="Search in campaign title or description"),
OpenApiParameter('featured', bool, description="Filter featured campaigns"),
],
responses={200: DonationCampaignListSerializer(many=True)},
tags=["Donations & Financial Campaigns"],
)
def get(self, request, *args, **kwargs):
qs = DonationCampaign.objects.all()
if not (request.user and request.user.is_authenticated and request.user.is_staff):
qs = qs.filter(is_active=True)
category = request.query_params.get('category')
if category:
qs = qs.filter(category=category)
priority = request.query_params.get('priority')
if priority:
qs = qs.filter(priority=priority)
featured = request.query_params.get('featured')
if featured is not None:
qs = qs.filter(is_featured=featured.lower() in ('true', '1'))
search = request.query_params.get('search')
if search:
qs = qs.filter(Q(title__icontains=search) | Q(description__icontains=search))
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
class DonationCampaignDetailView(GenericAPIView):
permission_classes = [AllowAny]
serializer_class = DonationCampaignDetailSerializer
queryset = DonationCampaign.objects.all()
@extend_schema(
summary="Retrieve campaign details & recent contributions",
description="Returns detailed campaign goal, beneficiary center, progress rate, and recent donors.",
responses={
200: DonationCampaignDetailSerializer,
404: OpenApiResponse(description="Campaign not found"),
},
tags=["Donations & Financial Campaigns"],
)
def get(self, request, pk_or_slug, *args, **kwargs):
campaign = get_campaign_by_pk_or_slug(pk_or_slug, self.get_queryset())
serializer = self.get_serializer(campaign)
return Response(serializer.data, status=status.HTTP_200_OK)
class DonationContributeView(GenericAPIView):
permission_classes = [AllowAny]
serializer_class = DonationContributeRequestSerializer
queryset = DonationCampaign.objects.all()
@extend_schema(
summary="Make a charitable contribution to campaign",
description="Processes a donation contribution, persists transaction, and increments campaign accumulated funds.",
request=DonationContributeRequestSerializer,
responses={
201: DonationTransactionSerializer,
400: OpenApiResponse(description="Validation error"),
404: OpenApiResponse(description="Campaign not found"),
},
tags=["Donations & Financial Campaigns"],
)
def post(self, request, pk_or_slug, *args, **kwargs):
campaign = get_campaign_by_pk_or_slug(pk_or_slug, self.get_queryset())
if not campaign.is_active:
return Response({'error': _("This campaign is currently inactive or closed.")}, status=status.HTTP_400_BAD_REQUEST)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
amount = serializer.validated_data['amount']
donor_name = serializer.validated_data.get('donor_name') or 'Anonymous Donor'
donor_email = serializer.validated_data.get('donor_email')
user = request.user if (request.user and request.user.is_authenticated) else None
if user and not serializer.validated_data.get('donor_name'):
donor_name = user.get_full_name() or user.email
transaction = DonationTransaction.objects.create(
campaign=campaign,
donor_user=user,
donor_name=donor_name,
donor_email=donor_email or (user.email if user else None),
amount=amount,
currency=campaign.currency,
payment_status=PaymentStatus.COMPLETED,
)
# Increment campaign raised amount
campaign.raised_amount = (campaign.raised_amount or Decimal('0.00')) + amount
campaign.save()
return Response(DonationTransactionSerializer(transaction).data, status=status.HTTP_201_CREATED)