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
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)
|