1446 lines
55 KiB
Python
1446 lines
55 KiB
Python
from decimal import Decimal
|
|
from urllib.parse import quote_plus
|
|
|
|
from django.http import JsonResponse
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.forms.models import model_to_dict
|
|
from django.contrib.auth import get_user_model
|
|
from django.db import models as django_models
|
|
from django.db.models import Sum, F, Max, Min, DecimalField
|
|
from django.utils import timezone
|
|
from datetime import timedelta, datetime, date
|
|
|
|
from rest_framework.views import APIView
|
|
|
|
from partner.models import (
|
|
Partner,
|
|
PARTNER_TYPE_CHOICES,
|
|
STATUS_CHOICES,
|
|
KYC_DOCUMENT_TYPE_CHOICES,
|
|
)
|
|
from events.models import Event
|
|
from bookings.models import TicketMeta, Booking, TicketType
|
|
from mobile_api.utils import validate_token_and_get_user
|
|
from eventify_logger.services import log
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
def _partner_to_dict(partner, request=None):
|
|
"""Serialize Partner for JSON."""
|
|
data = model_to_dict(
|
|
partner,
|
|
fields=[
|
|
"id",
|
|
"name",
|
|
"partner_type",
|
|
"primary_contact_person_name",
|
|
"primary_contact_person_email",
|
|
"primary_contact_person_phone",
|
|
"status",
|
|
"address",
|
|
"city",
|
|
"state",
|
|
"country",
|
|
"website_url",
|
|
"pincode",
|
|
"latitude",
|
|
"longitude",
|
|
"is_kyc_compliant",
|
|
"kyc_compliance_status",
|
|
"kyc_compliance_reason",
|
|
"kyc_compliance_document_type",
|
|
"kyc_compliance_document_other_type",
|
|
"kyc_compliance_document_number",
|
|
],
|
|
)
|
|
# Add document file URL if exists
|
|
if partner.kyc_compliance_document_file:
|
|
if request:
|
|
data["kyc_compliance_document_file"] = request.build_absolute_uri(partner.kyc_compliance_document_file.url)
|
|
else:
|
|
data["kyc_compliance_document_file"] = partner.kyc_compliance_document_file.url
|
|
else:
|
|
data["kyc_compliance_document_file"] = None
|
|
return data
|
|
|
|
|
|
def _iso_z_from_date(d):
|
|
if not d:
|
|
return None
|
|
if isinstance(d, datetime):
|
|
if timezone.is_naive(d):
|
|
d = timezone.make_aware(d, timezone.get_current_timezone())
|
|
return d.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
|
if isinstance(d, date):
|
|
return f"{d.isoformat()}T00:00:00.000Z"
|
|
return None
|
|
|
|
|
|
def _format_event_time(t):
|
|
if not t:
|
|
return None
|
|
return t.strftime("%H:%M")
|
|
|
|
|
|
def _partner_type_display(partner):
|
|
return dict(PARTNER_TYPE_CHOICES).get(partner.partner_type, partner.partner_type)
|
|
|
|
|
|
def _partner_status_display(partner):
|
|
return dict(STATUS_CHOICES).get(partner.status, partner.status)
|
|
|
|
|
|
def _partner_logo_url(partner):
|
|
return (
|
|
"https://ui-avatars.com/api/?name="
|
|
f"{quote_plus(partner.name)}&background=0D8ABC&color=fff"
|
|
)
|
|
|
|
|
|
def _company_address_line(partner):
|
|
parts = [
|
|
partner.address,
|
|
partner.city,
|
|
partner.state,
|
|
partner.pincode,
|
|
partner.country,
|
|
]
|
|
return ", ".join(p.strip() for p in parts if p and str(p).strip()) or None
|
|
|
|
|
|
def _verification_status_label(partner):
|
|
if partner.is_kyc_compliant and partner.kyc_compliance_status == "approved":
|
|
return "Verified"
|
|
if partner.kyc_compliance_status == "rejected":
|
|
return "Rejected"
|
|
if partner.kyc_compliance_status == "pending":
|
|
return "Pending"
|
|
return partner.get_kyc_compliance_status_display()
|
|
|
|
|
|
def _risk_score(partner):
|
|
scores = {
|
|
"high_risk": 78,
|
|
"medium_risk": 45,
|
|
"low_risk": 12,
|
|
"approved": 12,
|
|
"pending": 50,
|
|
"rejected": 95,
|
|
}
|
|
return scores.get(partner.kyc_compliance_status, 50)
|
|
|
|
|
|
def _kyc_status_upper(partner):
|
|
mapping = {
|
|
"approved": "APPROVED",
|
|
"pending": "PENDING",
|
|
"rejected": "REJECTED",
|
|
"high_risk": "HIGH_RISK",
|
|
"low_risk": "LOW_RISK",
|
|
"medium_risk": "MEDIUM_RISK",
|
|
}
|
|
return mapping.get(partner.kyc_compliance_status, str(partner.kyc_compliance_status).upper())
|
|
|
|
|
|
def _kyc_document_type_label(partner):
|
|
if partner.kyc_compliance_document_type == "other" and partner.kyc_compliance_document_other_type:
|
|
return partner.kyc_compliance_document_other_type
|
|
if partner.kyc_compliance_document_type:
|
|
return dict(KYC_DOCUMENT_TYPE_CHOICES).get(
|
|
partner.kyc_compliance_document_type,
|
|
partner.kyc_compliance_document_type,
|
|
)
|
|
return "Document"
|
|
|
|
|
|
def _build_kyc_documents(partner, request):
|
|
"""Single KYC row from Partner model fields (no separate KYC table)."""
|
|
if not (
|
|
partner.kyc_compliance_document_file
|
|
or partner.kyc_compliance_document_type
|
|
or partner.kyc_compliance_document_number
|
|
):
|
|
return []
|
|
|
|
type_label = _kyc_document_type_label(partner)
|
|
name = f"{type_label} - {partner.name}"
|
|
if partner.kyc_compliance_document_file:
|
|
if request:
|
|
url = request.build_absolute_uri(partner.kyc_compliance_document_file.url)
|
|
else:
|
|
url = partner.kyc_compliance_document_file.url
|
|
else:
|
|
url = None
|
|
|
|
return [
|
|
{
|
|
"id": f"kyc-{partner.id}",
|
|
"partnerId": f"p{partner.id}",
|
|
"type": type_label.upper() if type_label else "DOCUMENT",
|
|
"name": name,
|
|
"url": url,
|
|
"status": _kyc_status_upper(partner),
|
|
"mandatory": True,
|
|
"reviewedBy": "Admin" if partner.kyc_compliance_status == "approved" else None,
|
|
"reviewedAt": None,
|
|
"uploadedBy": partner.primary_contact_person_name,
|
|
"uploadedAt": None,
|
|
}
|
|
]
|
|
|
|
|
|
def _build_partner_detail_payload(partner, request):
|
|
"""Shape partner block for PartnerDetailWithEventsAPI."""
|
|
ptype = _partner_type_display(partner)
|
|
events_qs = Event.objects.filter(partner=partner)
|
|
events_count = events_qs.count()
|
|
active_deals = events_qs.filter(event_status__in=["live", "published"]).count()
|
|
|
|
booking_agg = (
|
|
Booking.objects.filter(ticket_meta__event__partner=partner)
|
|
.aggregate(
|
|
total_revenue=Sum(
|
|
F("quantity") * F("price"),
|
|
output_field=DecimalField(max_digits=16, decimal_places=2),
|
|
),
|
|
last_booking=Max("updated_date"),
|
|
)
|
|
)
|
|
total_revenue = booking_agg["total_revenue"] or Decimal("0")
|
|
last_booking_date = booking_agg["last_booking"]
|
|
|
|
last_event_date = events_qs.aggregate(m=Max("created_date"))["m"]
|
|
last_activity = None
|
|
for candidate in (last_booking_date, last_event_date):
|
|
if candidate is None:
|
|
continue
|
|
if last_activity is None or candidate > last_activity:
|
|
last_activity = candidate
|
|
|
|
tags = [ptype]
|
|
if partner.is_kyc_compliant:
|
|
tags.append("KYC Verified")
|
|
|
|
return {
|
|
"id": f"p{partner.id}",
|
|
"name": partner.name,
|
|
"type": ptype,
|
|
"status": _partner_status_display(partner),
|
|
"logo": _partner_logo_url(partner),
|
|
"primaryContact": {
|
|
"name": partner.primary_contact_person_name,
|
|
"email": partner.primary_contact_person_email,
|
|
"phone": partner.primary_contact_person_phone,
|
|
"role": f"{ptype} Manager",
|
|
},
|
|
"companyDetails": {
|
|
"legalName": partner.name,
|
|
"taxId": partner.kyc_compliance_document_number or None,
|
|
"website": partner.website_url or None,
|
|
"address": _company_address_line(partner),
|
|
},
|
|
"metrics": {
|
|
"activeDeals": active_deals,
|
|
"totalRevenue": float(total_revenue),
|
|
"openBalance": 0,
|
|
"lastActivity": _iso_z_from_date(last_activity),
|
|
"eventsCount": events_count,
|
|
},
|
|
"tags": tags,
|
|
"notes": partner.kyc_compliance_reason or None,
|
|
"joinedAt": None,
|
|
"verificationStatus": _verification_status_label(partner),
|
|
"riskScore": _risk_score(partner),
|
|
}
|
|
|
|
|
|
def _build_partner_events_payload(partner, events, request):
|
|
"""partnerEvents[] from Event + TicketMeta / Booking aggregates."""
|
|
if not events:
|
|
return []
|
|
|
|
event_ids = [e.id for e in events]
|
|
|
|
cap_by_event = dict(
|
|
TicketMeta.objects.filter(event_id__in=event_ids)
|
|
.values("event_id")
|
|
.annotate(total=Sum("maximum_quantity"))
|
|
.values_list("event_id", "total")
|
|
)
|
|
|
|
booking_rows = (
|
|
Booking.objects.filter(ticket_meta__event_id__in=event_ids)
|
|
.values("ticket_meta__event_id")
|
|
.annotate(
|
|
sold=Sum("quantity"),
|
|
revenue=Sum(
|
|
F("quantity") * F("price"),
|
|
output_field=DecimalField(max_digits=16, decimal_places=2),
|
|
),
|
|
)
|
|
)
|
|
booking_by_event = {row["ticket_meta__event_id"]: row for row in booking_rows}
|
|
|
|
min_price_rows = (
|
|
TicketType.objects.filter(ticket_meta__event_id__in=event_ids, is_active=True)
|
|
.values("ticket_meta__event_id")
|
|
.annotate(mp=Min("price"))
|
|
)
|
|
min_price_by_event = {row["ticket_meta__event_id"]: row["mp"] for row in min_price_rows}
|
|
|
|
out = []
|
|
for e in events:
|
|
row = booking_by_event.get(e.id, {})
|
|
sold = row.get("sold") or 0
|
|
revenue = row.get("revenue") or Decimal("0")
|
|
mp = min_price_by_event.get(e.id)
|
|
ticket_price = float(mp) if mp is not None else 0.0
|
|
total_tickets = cap_by_event.get(e.id) or 0
|
|
|
|
title = (e.title or "").strip() or e.name
|
|
venue = (e.venue_name or "").strip() or e.place
|
|
category = e.event_type.event_type if e.event_type_id else ""
|
|
|
|
out.append(
|
|
{
|
|
"id": f"evt-{e.id}",
|
|
"partnerId": f"p{partner.id}",
|
|
"title": title,
|
|
"description": e.description or None,
|
|
"date": _iso_z_from_date(e.start_date),
|
|
"time": _format_event_time(e.start_time),
|
|
"venue": venue,
|
|
"category": category,
|
|
"ticketPrice": ticket_price,
|
|
"totalTickets": int(total_tickets) if total_tickets else 0,
|
|
"ticketsSold": int(sold),
|
|
"revenue": float(revenue),
|
|
"status": e.event_status.upper() if e.event_status else None,
|
|
"submittedAt": None,
|
|
"createdAt": _iso_z_from_date(e.created_date),
|
|
}
|
|
)
|
|
return out
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerCreateAPI(APIView):
|
|
"""
|
|
Create a new Partner.
|
|
Body: token, username, name, partner_type, primary_contact_person_name,
|
|
primary_contact_person_email, primary_contact_person_phone (required);
|
|
address, city, state, country, website_url, pincode, latitude, longitude,
|
|
is_kyc_compliant, kyc_compliance_status, kyc_compliance_reason,
|
|
kyc_compliance_document_type, kyc_compliance_document_other_type,
|
|
kyc_compliance_document_number (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
name = data.get("name")
|
|
partner_type = data.get("partner_type")
|
|
primary_contact_person_name = data.get("primary_contact_person_name")
|
|
primary_contact_person_email = data.get("primary_contact_person_email")
|
|
primary_contact_person_phone = data.get("primary_contact_person_phone")
|
|
|
|
if not all([name, partner_type, primary_contact_person_name, primary_contact_person_email, primary_contact_person_phone]):
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": "name, partner_type, primary_contact_person_name, primary_contact_person_email, and primary_contact_person_phone are required.",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
# Validate partner_type
|
|
valid_partner_types = [choice[1] for choice in Partner._meta.get_field("partner_type").choices]
|
|
print(valid_partner_types)
|
|
if partner_type not in valid_partner_types:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid partner_type. Must be one of: {', '.join(valid_partner_types)}",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
partner = Partner.objects.create(
|
|
name=name,
|
|
partner_type=partner_type,
|
|
primary_contact_person_name=primary_contact_person_name,
|
|
primary_contact_person_email=primary_contact_person_email,
|
|
primary_contact_person_phone=primary_contact_person_phone,
|
|
address=data.get("address"),
|
|
city=data.get("city"),
|
|
state=data.get("state"),
|
|
country=data.get("country"),
|
|
website_url=data.get("website_url"),
|
|
pincode=data.get("pincode"),
|
|
latitude=data.get("latitude"),
|
|
longitude=data.get("longitude"),
|
|
is_kyc_compliant=data.get("is_kyc_compliant", False),
|
|
kyc_compliance_status=data.get("kyc_compliance_status", "pending"),
|
|
kyc_compliance_reason=data.get("kyc_compliance_reason"),
|
|
kyc_compliance_document_type=data.get("kyc_compliance_document_type"),
|
|
kyc_compliance_document_other_type=data.get("kyc_compliance_document_other_type"),
|
|
kyc_compliance_document_number=data.get("kyc_compliance_document_number"),
|
|
)
|
|
|
|
# Handle file upload if provided
|
|
if "kyc_compliance_document_file" in request.FILES:
|
|
partner.kyc_compliance_document_file = request.FILES["kyc_compliance_document_file"]
|
|
partner.save()
|
|
|
|
log("info", "Partner created", request=request, user=user, logger_data={"partner_id": partner.id, "partner_name": name})
|
|
return JsonResponse(
|
|
{"status": "success", "partner": _partner_to_dict(partner, request)},
|
|
status=201,
|
|
)
|
|
except Exception as e:
|
|
log("error", "Partner create exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListAPI(APIView):
|
|
"""
|
|
List Partners, optionally filtered by partner_type or kyc_compliance_status.
|
|
Body: token, username, partner_type (optional), kyc_compliance_status (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
qs = Partner.objects.all().order_by("-id")
|
|
|
|
partner_type = data.get("partner_type")
|
|
if partner_type:
|
|
qs = qs.filter(partner_type=partner_type)
|
|
|
|
kyc_compliance_status = data.get("kyc_compliance_status")
|
|
if kyc_compliance_status:
|
|
qs = qs.filter(kyc_compliance_status=kyc_compliance_status)
|
|
|
|
partners = [_partner_to_dict(p, request) for p in qs]
|
|
return JsonResponse({"status": "success", "partners": partners}, status=200)
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerDetailWithEventsAPI(APIView):
|
|
"""
|
|
Get full partner detail for UI: partner, kycDocuments, dealTerms, ledger, partnerEvents.
|
|
Body: token, username, partner_id (required).
|
|
dealTerms and ledger are placeholders ([]) until dedicated models exist.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
events = list(
|
|
Event.objects.filter(partner=partner)
|
|
.select_related("event_type")
|
|
.order_by("-created_date")
|
|
)
|
|
|
|
payload = {
|
|
"status": "success",
|
|
"partner": _build_partner_detail_payload(partner, request),
|
|
"kycDocuments": _build_kyc_documents(partner, request),
|
|
"dealTerms": [],
|
|
"ledger": [],
|
|
"partnerEvents": _build_partner_events_payload(partner, events, request),
|
|
}
|
|
|
|
log(
|
|
"info",
|
|
"Partner detail with events",
|
|
request=request,
|
|
user=user,
|
|
logger_data={"partner_id": partner.id},
|
|
)
|
|
return JsonResponse(payload, status=200)
|
|
except Exception as e:
|
|
log("error", "Partner detail with events exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerUpdateAPI(APIView):
|
|
"""
|
|
Update an existing Partner.
|
|
Body: token, username, partner_id (required);
|
|
name, partner_type, primary_contact_person_name, primary_contact_person_email,
|
|
primary_contact_person_phone, address, city, state, country, website_url,
|
|
pincode, latitude, longitude, is_kyc_compliant, kyc_compliance_status,
|
|
kyc_compliance_reason, kyc_compliance_document_type,
|
|
kyc_compliance_document_other_type, kyc_compliance_document_number (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Update fields if provided
|
|
if data.get("name") is not None:
|
|
partner.name = data["name"]
|
|
if data.get("partner_type") is not None:
|
|
valid_partner_types = [choice[0] for choice in Partner._meta.get_field("partner_type").choices]
|
|
if data["partner_type"] not in valid_partner_types:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid partner_type. Must be one of: {', '.join(valid_partner_types)}",
|
|
},
|
|
status=400,
|
|
)
|
|
partner.partner_type = data["partner_type"]
|
|
if data.get("primary_contact_person_name") is not None:
|
|
partner.primary_contact_person_name = data["primary_contact_person_name"]
|
|
if data.get("primary_contact_person_email") is not None:
|
|
partner.primary_contact_person_email = data["primary_contact_person_email"]
|
|
if data.get("primary_contact_person_phone") is not None:
|
|
partner.primary_contact_person_phone = data["primary_contact_person_phone"]
|
|
if "address" in data:
|
|
partner.address = data["address"]
|
|
if "city" in data:
|
|
partner.city = data["city"]
|
|
if "state" in data:
|
|
partner.state = data["state"]
|
|
if "country" in data:
|
|
partner.country = data["country"]
|
|
if "website_url" in data:
|
|
partner.website_url = data["website_url"]
|
|
if "pincode" in data:
|
|
partner.pincode = data["pincode"]
|
|
if data.get("latitude") is not None:
|
|
try:
|
|
partner.latitude = float(data["latitude"])
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be numeric."},
|
|
status=400,
|
|
)
|
|
if data.get("longitude") is not None:
|
|
try:
|
|
partner.longitude = float(data["longitude"])
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be numeric."},
|
|
status=400,
|
|
)
|
|
if data.get("is_kyc_compliant") is not None:
|
|
partner.is_kyc_compliant = bool(data["is_kyc_compliant"])
|
|
if data.get("kyc_compliance_status") is not None:
|
|
valid_statuses = [choice[0] for choice in Partner._meta.get_field("kyc_compliance_status").choices]
|
|
if data["kyc_compliance_status"] not in valid_statuses:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid kyc_compliance_status. Must be one of: {', '.join(valid_statuses)}",
|
|
},
|
|
status=400,
|
|
)
|
|
partner.kyc_compliance_status = data["kyc_compliance_status"]
|
|
if "kyc_compliance_reason" in data:
|
|
partner.kyc_compliance_reason = data["kyc_compliance_reason"]
|
|
if "kyc_compliance_document_type" in data:
|
|
partner.kyc_compliance_document_type = data["kyc_compliance_document_type"]
|
|
if "kyc_compliance_document_other_type" in data:
|
|
partner.kyc_compliance_document_other_type = data["kyc_compliance_document_other_type"]
|
|
if "kyc_compliance_document_number" in data:
|
|
partner.kyc_compliance_document_number = data["kyc_compliance_document_number"]
|
|
|
|
# Handle file upload if provided
|
|
if "kyc_compliance_document_file" in request.FILES:
|
|
partner.kyc_compliance_document_file = request.FILES["kyc_compliance_document_file"]
|
|
|
|
partner.save()
|
|
log("info", "Partner updated", request=request, user=user, logger_data={"partner_id": partner_id})
|
|
return JsonResponse(
|
|
{"status": "success", "partner": _partner_to_dict(partner, request)},
|
|
status=200,
|
|
)
|
|
except Exception as e:
|
|
log("error", "Partner update exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerDeleteAPI(APIView):
|
|
"""
|
|
Delete an existing Partner.
|
|
Body: token, username, partner_id.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
partner_name = partner.name
|
|
partner.delete()
|
|
log("info", "Partner deleted", request=request, user=user, logger_data={"partner_id": partner_id, "partner_name": partner_name})
|
|
return JsonResponse(
|
|
{"status": "success", "message": "Partner deleted successfully."},
|
|
status=200,
|
|
)
|
|
except Exception as e:
|
|
log("error", "Partner delete exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerUpdateKYCDocumentsAPI(APIView):
|
|
"""
|
|
Update KYC documents for an existing Partner.
|
|
Body: token, username, partner_id (required);
|
|
kyc_compliance_document_type, kyc_compliance_document_other_type,
|
|
kyc_compliance_document_number (optional);
|
|
kyc_compliance_document_file (file upload, optional);
|
|
is_kyc_compliant, kyc_compliance_status, kyc_compliance_reason (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Update KYC document fields
|
|
if "kyc_compliance_document_type" in data:
|
|
document_type = data["kyc_compliance_document_type"]
|
|
if document_type: # Allow empty string to clear
|
|
valid_types = [choice[0] for choice in Partner._meta.get_field("kyc_compliance_document_type").choices]
|
|
if document_type not in valid_types:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid kyc_compliance_document_type. Must be one of: {', '.join(valid_types)}",
|
|
},
|
|
status=400,
|
|
)
|
|
partner.kyc_compliance_document_type = document_type if document_type else None
|
|
|
|
if "kyc_compliance_document_other_type" in data:
|
|
partner.kyc_compliance_document_other_type = data["kyc_compliance_document_other_type"] or None
|
|
|
|
if "kyc_compliance_document_number" in data:
|
|
partner.kyc_compliance_document_number = data["kyc_compliance_document_number"] or None
|
|
|
|
# Handle file upload
|
|
if "kyc_compliance_document_file" in request.FILES:
|
|
partner.kyc_compliance_document_file = request.FILES["kyc_compliance_document_file"]
|
|
|
|
# Optionally update compliance status fields
|
|
if data.get("is_kyc_compliant") is not None:
|
|
partner.is_kyc_compliant = bool(data["is_kyc_compliant"])
|
|
|
|
if data.get("kyc_compliance_status") is not None:
|
|
valid_statuses = [choice[0] for choice in Partner._meta.get_field("kyc_compliance_status").choices]
|
|
if data["kyc_compliance_status"] not in valid_statuses:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid kyc_compliance_status. Must be one of: {', '.join(valid_statuses)}",
|
|
},
|
|
status=400,
|
|
)
|
|
partner.kyc_compliance_status = data["kyc_compliance_status"]
|
|
|
|
if "kyc_compliance_reason" in data:
|
|
partner.kyc_compliance_reason = data["kyc_compliance_reason"] or None
|
|
|
|
partner.save()
|
|
return JsonResponse(
|
|
{
|
|
"status": "success",
|
|
"message": "KYC documents updated successfully.",
|
|
"partner": _partner_to_dict(partner, request),
|
|
},
|
|
status=200,
|
|
)
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerUpdateAddressLocationAPI(APIView):
|
|
"""
|
|
Update address and location fields for an existing Partner.
|
|
Body: token, username, partner_id (required);
|
|
address, city, state, country, website_url, pincode, latitude, longitude (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Update address fields
|
|
if "address" in data:
|
|
partner.address = data["address"] or None
|
|
if "city" in data:
|
|
partner.city = data["city"] or None
|
|
if "state" in data:
|
|
partner.state = data["state"] or None
|
|
if "country" in data:
|
|
partner.country = data["country"] or None
|
|
if "website_url" in data:
|
|
partner.website_url = data["website_url"] or None
|
|
if "pincode" in data:
|
|
partner.pincode = data["pincode"] or None
|
|
|
|
# Update location coordinates with validation
|
|
if data.get("latitude") is not None:
|
|
try:
|
|
latitude = float(data["latitude"])
|
|
# Validate latitude range (-90 to 90)
|
|
if latitude < -90 or latitude > 90:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be between -90 and 90."},
|
|
status=400,
|
|
)
|
|
partner.latitude = latitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
if data.get("longitude") is not None:
|
|
try:
|
|
longitude = float(data["longitude"])
|
|
# Validate longitude range (-180 to 180)
|
|
if longitude < -180 or longitude > 180:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be between -180 and 180."},
|
|
status=400,
|
|
)
|
|
partner.longitude = longitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
partner.save()
|
|
return JsonResponse(
|
|
{
|
|
"status": "success",
|
|
"message": "Address and location updated successfully.",
|
|
"partner": _partner_to_dict(partner, request),
|
|
},
|
|
status=200,
|
|
)
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
def _user_to_dict(user, request=None):
|
|
"""Serialize User for JSON."""
|
|
data = model_to_dict(
|
|
user,
|
|
fields=[
|
|
"id",
|
|
"username",
|
|
"email",
|
|
"phone_number",
|
|
"role",
|
|
"is_staff",
|
|
"is_customer",
|
|
"is_user",
|
|
"pincode",
|
|
"district",
|
|
"state",
|
|
"country",
|
|
"place",
|
|
"latitude",
|
|
"longitude",
|
|
],
|
|
)
|
|
# Add profile picture URL if exists
|
|
if user.profile_picture:
|
|
if request:
|
|
data["profile_picture"] = request.build_absolute_uri(user.profile_picture.url)
|
|
else:
|
|
data["profile_picture"] = user.profile_picture.url
|
|
else:
|
|
data["profile_picture"] = None
|
|
return data
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerCreateUserAPI(APIView):
|
|
"""
|
|
Create a user with partner-related role (partner, partner_manager, partner_staff, partner_customer).
|
|
Body: token, username, email, password, role (required);
|
|
phone_number, partner_id, pincode, district, state, country, place, latitude, longitude (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
username = data.get("username")
|
|
email = data.get("email")
|
|
password = data.get("password")
|
|
role = data.get("role")
|
|
partner_id = data.get("partner_id")
|
|
|
|
if not all([username, email, password, role]):
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": "username, email, password, and role are required.",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
# Validate role - must be one of the partner-related roles
|
|
valid_partner_roles = ["partner", "partner_manager", "partner_staff", "partner_customer"]
|
|
if role not in valid_partner_roles:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid role. Must be one of: {', '.join(valid_partner_roles)}",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
# Check if username already exists
|
|
if User.objects.filter(username=username).exists():
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Username already exists."},
|
|
status=400,
|
|
)
|
|
|
|
# Check if email already exists
|
|
if User.objects.filter(email=email).exists():
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Email already exists."},
|
|
status=400,
|
|
)
|
|
|
|
# Validate partner_id if provided
|
|
partner = None
|
|
if partner_id:
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Create user
|
|
new_user = User.objects.create_user(
|
|
username=username,
|
|
email=email,
|
|
password=password,
|
|
role=role,
|
|
phone_number=data.get("phone_number"),
|
|
pincode=data.get("pincode"),
|
|
district=data.get("district"),
|
|
state=data.get("state"),
|
|
country=data.get("country"),
|
|
place=data.get("place"),
|
|
)
|
|
|
|
# Set location coordinates if provided
|
|
if data.get("latitude") is not None:
|
|
try:
|
|
latitude = float(data["latitude"])
|
|
if latitude < -90 or latitude > 90:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be between -90 and 90."},
|
|
status=400,
|
|
)
|
|
new_user.latitude = latitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
if data.get("longitude") is not None:
|
|
try:
|
|
longitude = float(data["longitude"])
|
|
if longitude < -180 or longitude > 180:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be between -180 and 180."},
|
|
status=400,
|
|
)
|
|
new_user.longitude = longitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
# Handle profile picture upload if provided
|
|
if "profile_picture" in request.FILES:
|
|
new_user.profile_picture = request.FILES["profile_picture"]
|
|
|
|
new_user.save()
|
|
|
|
response_data = {
|
|
"status": "success",
|
|
"message": f"User created successfully with role: {role}.",
|
|
"user": _user_to_dict(new_user, request),
|
|
}
|
|
|
|
# Include partner info if linked
|
|
if partner:
|
|
response_data["partner"] = {
|
|
"id": partner.id,
|
|
"name": partner.name,
|
|
"partner_type": partner.partner_type,
|
|
}
|
|
|
|
log("info", "Partner user created", request=request, user=user, logger_data={"new_user_id": new_user.id, "username": new_user.username, "role": role})
|
|
return JsonResponse(response_data, status=201)
|
|
|
|
except Exception as e:
|
|
log("error", "Partner create user exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListUsersAPI(APIView):
|
|
"""
|
|
List users associated with a partner (users with partner-related roles).
|
|
Body: token, username, partner_id (required);
|
|
role (optional filter: partner, partner_manager, partner_staff, partner_customer).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partner_id = data.get("partner_id")
|
|
if not partner_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "partner_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
# Validate partner exists
|
|
try:
|
|
partner = Partner.objects.get(id=partner_id)
|
|
except Partner.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Partner not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Filter users by partner-related roles
|
|
partner_roles = ["partner", "partner_manager", "partner_staff", "partner_customer"]
|
|
qs = User.objects.filter(role__in=partner_roles).order_by("-id")
|
|
|
|
# Optional role filter
|
|
role_filter = data.get("role")
|
|
if role_filter:
|
|
if role_filter not in partner_roles:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid role filter. Must be one of: {', '.join(partner_roles)}",
|
|
},
|
|
status=400,
|
|
)
|
|
qs = qs.filter(role=role_filter)
|
|
|
|
# Optionally filter by matching contact email or phone (if partner contact info matches user)
|
|
# This is a heuristic approach since there's no direct FK relationship
|
|
# You can enhance this logic based on your business requirements
|
|
match_by_contact = data.get("match_by_contact", False)
|
|
if match_by_contact:
|
|
qs = qs.filter(
|
|
django_models.Q(email=partner.primary_contact_person_email)
|
|
| django_models.Q(phone_number=partner.primary_contact_person_phone)
|
|
)
|
|
|
|
users = [_user_to_dict(u, request) for u in qs]
|
|
|
|
response_data = {
|
|
"status": "success",
|
|
"partner": {
|
|
"id": partner.id,
|
|
"name": partner.name,
|
|
"partner_type": partner.partner_type,
|
|
},
|
|
"users": users,
|
|
"total_count": len(users),
|
|
}
|
|
|
|
return JsonResponse(response_data, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerUpdateUserAPI(APIView):
|
|
"""
|
|
Update a partner user (user with partner-related role).
|
|
Body: token, username, user_id (required);
|
|
email, phone_number, role (must be partner-related), pincode, district, state,
|
|
country, place, latitude, longitude, profile_picture (optional).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
user_id = data.get("user_id")
|
|
if not user_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "user_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
target_user = User.objects.get(id=user_id)
|
|
except User.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "User not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Validate that the user has a partner-related role
|
|
partner_roles = ["partner", "partner_manager", "partner_staff", "partner_customer"]
|
|
if target_user.role not in partner_roles:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": "User is not a partner-related user. Only users with partner roles can be updated.",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
# Update fields if provided
|
|
if data.get("email") is not None:
|
|
new_email = data["email"]
|
|
# Check if email already exists for another user
|
|
if User.objects.filter(email=new_email).exclude(id=user_id).exists():
|
|
return JsonResponse(
|
|
{"status": "error", "message": "Email already exists for another user."},
|
|
status=400,
|
|
)
|
|
target_user.email = new_email
|
|
|
|
if data.get("phone_number") is not None:
|
|
target_user.phone_number = data["phone_number"] or None
|
|
|
|
if data.get("role") is not None:
|
|
new_role = data["role"]
|
|
if new_role not in partner_roles:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": f"Invalid role. Must be one of: {', '.join(partner_roles)}",
|
|
},
|
|
status=400,
|
|
)
|
|
target_user.role = new_role
|
|
|
|
if "pincode" in data:
|
|
target_user.pincode = data["pincode"] or None
|
|
if "district" in data:
|
|
target_user.district = data["district"] or None
|
|
if "state" in data:
|
|
target_user.state = data["state"] or None
|
|
if "country" in data:
|
|
target_user.country = data["country"] or None
|
|
if "place" in data:
|
|
target_user.place = data["place"] or None
|
|
|
|
if data.get("latitude") is not None:
|
|
try:
|
|
latitude = float(data["latitude"])
|
|
if latitude < -90 or latitude > 90:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be between -90 and 90."},
|
|
status=400,
|
|
)
|
|
target_user.latitude = latitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "latitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
if data.get("longitude") is not None:
|
|
try:
|
|
longitude = float(data["longitude"])
|
|
if longitude < -180 or longitude > 180:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be between -180 and 180."},
|
|
status=400,
|
|
)
|
|
target_user.longitude = longitude
|
|
except (TypeError, ValueError):
|
|
return JsonResponse(
|
|
{"status": "error", "message": "longitude must be numeric."},
|
|
status=400,
|
|
)
|
|
|
|
# Handle profile picture upload if provided
|
|
if "profile_picture" in request.FILES:
|
|
target_user.profile_picture = request.FILES["profile_picture"]
|
|
|
|
# Handle password update if provided
|
|
if data.get("password"):
|
|
target_user.set_password(data["password"])
|
|
|
|
target_user.save()
|
|
|
|
log("info", "Partner user updated", request=request, user=user, logger_data={"user_id": user_id})
|
|
return JsonResponse(
|
|
{
|
|
"status": "success",
|
|
"message": "Partner user updated successfully.",
|
|
"user": _user_to_dict(target_user, request),
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
except Exception as e:
|
|
log("error", "Partner update user exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerDeleteUserAPI(APIView):
|
|
"""
|
|
Delete a partner user (user with partner-related role).
|
|
Body: token, username, user_id (required).
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request)
|
|
if error_response:
|
|
return error_response
|
|
|
|
user_id = data.get("user_id")
|
|
if not user_id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "user_id is required."},
|
|
status=400,
|
|
)
|
|
|
|
try:
|
|
target_user = User.objects.get(id=user_id)
|
|
except User.DoesNotExist:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "User not found."},
|
|
status=404,
|
|
)
|
|
|
|
# Validate that the user has a partner-related role
|
|
partner_roles = ["partner", "partner_manager", "partner_staff", "partner_customer"]
|
|
if target_user.role not in partner_roles:
|
|
return JsonResponse(
|
|
{
|
|
"status": "error",
|
|
"message": "User is not a partner-related user. Only users with partner roles can be deleted.",
|
|
},
|
|
status=400,
|
|
)
|
|
|
|
# Prevent deleting yourself
|
|
if target_user.id == user.id:
|
|
return JsonResponse(
|
|
{"status": "error", "message": "You cannot delete your own account."},
|
|
status=400,
|
|
)
|
|
|
|
username = target_user.username
|
|
target_user.delete()
|
|
|
|
log("info", "Partner user deleted", request=request, user=user, logger_data={"user_id": user_id, "username": username})
|
|
return JsonResponse(
|
|
{
|
|
"status": "success",
|
|
"message": f"Partner user '{username}' deleted successfully.",
|
|
},
|
|
status=200,
|
|
)
|
|
|
|
except Exception as e:
|
|
log("error", "Partner delete user exception", request=request, logger_data={"error": str(e)})
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
# ============================================================================
|
|
# Partner List APIs (Filtered by Criteria)
|
|
# ============================================================================
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListAllAPI(APIView):
|
|
"""
|
|
List All Partners API.
|
|
Body: token, username (required).
|
|
Returns: list of all partners.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partners = Partner.objects.all().order_by("-id")
|
|
partners_list = [_partner_to_dict(p, request) for p in partners]
|
|
|
|
return JsonResponse({
|
|
"status": "success",
|
|
"partners": partners_list,
|
|
"total_count": len(partners_list)
|
|
}, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListKYCCompliantAPI(APIView):
|
|
"""
|
|
List KYC Compliant Partners API.
|
|
Body: token, username (required).
|
|
Returns: list of partners where is_kyc_compliant=True and kyc_compliance_status='approved'.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partners = Partner.objects.filter(
|
|
is_kyc_compliant=True,
|
|
kyc_compliance_status='approved'
|
|
).order_by("-id")
|
|
partners_list = [_partner_to_dict(p, request) for p in partners]
|
|
|
|
return JsonResponse({
|
|
"status": "success",
|
|
"partners": partners_list,
|
|
"total_count": len(partners_list)
|
|
}, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListKYCPendingAPI(APIView):
|
|
"""
|
|
List KYC Pending Partners API.
|
|
Body: token, username (required).
|
|
Returns: list of partners where kyc_compliance_status='pending'.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partners = Partner.objects.filter(
|
|
kyc_compliance_status='pending'
|
|
).order_by("-id")
|
|
partners_list = [_partner_to_dict(p, request) for p in partners]
|
|
|
|
return JsonResponse({
|
|
"status": "success",
|
|
"partners": partners_list,
|
|
"total_count": len(partners_list)
|
|
}, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListHighRiskAPI(APIView):
|
|
"""
|
|
List High Risk Partners API.
|
|
Body: token, username (required).
|
|
Returns: list of partners where kyc_compliance_status='high_risk'.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
|
if error_response:
|
|
return error_response
|
|
|
|
partners = Partner.objects.filter(
|
|
kyc_compliance_status='high_risk'
|
|
).order_by("-id")
|
|
partners_list = [_partner_to_dict(p, request) for p in partners]
|
|
|
|
return JsonResponse({
|
|
"status": "success",
|
|
"partners": partners_list,
|
|
"total_count": len(partners_list)
|
|
}, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name="dispatch")
|
|
class PartnerListJoinedThisWeekAPI(APIView):
|
|
"""
|
|
List Partners Joined This Week API.
|
|
Body: token, username (required).
|
|
Returns: list of partners created in the last 7 days.
|
|
|
|
Note: This API uses id-based filtering as a proxy for creation date.
|
|
For accurate results, consider adding a created_date field to the Partner model.
|
|
"""
|
|
|
|
def post(self, request):
|
|
try:
|
|
user, token, data, error_response = validate_token_and_get_user(request, error_status_code=True)
|
|
if error_response:
|
|
return error_response
|
|
|
|
# Calculate date range for this week
|
|
today = timezone.now().date()
|
|
week_start = today - timedelta(days=7)
|
|
|
|
# Since Partner model doesn't have created_date, we'll use id as a proxy
|
|
# Get the highest id to approximate recent partners
|
|
# This is a workaround - ideally Partner model should have created_date field
|
|
latest_partner = Partner.objects.order_by('-id').first()
|
|
|
|
if latest_partner:
|
|
# Get partners created in the last week based on id approximation
|
|
# This assumes sequential id assignment
|
|
# For accurate results, add created_date field to Partner model
|
|
partners = Partner.objects.filter(
|
|
id__gte=latest_partner.id - 1000 # Approximate filter
|
|
).order_by("-id")
|
|
|
|
# Alternative: If you want to add created_date field, use:
|
|
# partners = Partner.objects.filter(
|
|
# created_date__gte=week_start
|
|
# ).order_by("-created_date")
|
|
else:
|
|
partners = Partner.objects.none()
|
|
|
|
partners_list = [_partner_to_dict(p, request) for p in partners]
|
|
|
|
return JsonResponse({
|
|
"status": "success",
|
|
"partners": partners_list,
|
|
"total_count": len(partners_list),
|
|
"note": "This uses id-based approximation. Consider adding created_date field for accurate results."
|
|
}, status=200)
|
|
|
|
except Exception as e:
|
|
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|