Sprint 4: PartnerBookingListView for partner-scoped booking list

- admin_api/views.py: PartnerBookingListView — filters Booking rows by
  partner via ticket_meta__event__partner, supports search (booking_id,
  user email/name), payment_status filter, event_id filter, pagination
  with page_size bound [1,200]
- admin_api/urls.py: wire partners/me/bookings/ endpoint

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 11:34:57 +05:30
parent 611d653938
commit 4669907a02
2 changed files with 90 additions and 0 deletions

View File

@@ -33,6 +33,8 @@ urlpatterns = [
# Partner-Me: ticket tiers (Sprint 3) # Partner-Me: ticket tiers (Sprint 3)
path('partners/me/events/<int:event_pk>/tiers/', views.PartnerMeEventTiersView.as_view(), name='partner-me-event-tiers'), path('partners/me/events/<int:event_pk>/tiers/', views.PartnerMeEventTiersView.as_view(), name='partner-me-event-tiers'),
path('partners/me/events/<int:event_pk>/tiers/<int:tier_pk>/', views.PartnerMeEventTierDetailView.as_view(), name='partner-me-event-tier-detail'), path('partners/me/events/<int:event_pk>/tiers/<int:tier_pk>/', views.PartnerMeEventTierDetailView.as_view(), name='partner-me-event-tier-detail'),
# Partner-Me: bookings (Sprint 4)
path('partners/me/bookings/', views.PartnerBookingListView.as_view(), name='partner-me-bookings'),
path('users/metrics/', views.UserMetricsView.as_view(), name='user-metrics'), path('users/metrics/', views.UserMetricsView.as_view(), name='user-metrics'),
path('users/', views.UserListView.as_view(), name='user-list'), path('users/', views.UserListView.as_view(), name='user-list'),
path('users/<int:pk>/', views.UserDetailView.as_view(), name='user-detail'), path('users/<int:pk>/', views.UserDetailView.as_view(), name='user-detail'),

View File

@@ -3897,3 +3897,91 @@ class PartnerMeEventTierDetailView(APIView):
return err return err
tt.delete() tt.delete()
return Response({'status': 'deleted'}, status=204) return Response({'status': 'deleted'}, status=204)
# ============================================================
# Sprint 4 — Partner Bookings
# ============================================================
class PartnerBookingListView(APIView):
"""
GET /api/v1/partners/me/bookings/
Query params: search, status (payment_status), event_id, page, page_size
"""
permission_classes = [IsAuthenticated]
def get(self, request):
from bookings.models import Booking
from django.db.models import Q
partner, err = _require_partner(request)
if err:
return err
qs = Booking.objects.filter(
ticket_meta__event__partner=partner
).select_related(
'user', 'ticket_meta__event', 'ticket_type'
).order_by('-created_date', '-id')
# Search: booking_id, user email, user first/last name
search = request.query_params.get('search', '').strip()
if search:
qs = qs.filter(
Q(booking_id__icontains=search) |
Q(user__email__icontains=search) |
Q(user__first_name__icontains=search) |
Q(user__last_name__icontains=search)
)
# Filter by payment_status
status = request.query_params.get('status', '').strip()
if status:
qs = qs.filter(payment_status=status)
# Filter by event_id
event_id = request.query_params.get('event_id', '').strip()
if event_id:
qs = qs.filter(ticket_meta__event_id=event_id)
# Pagination
try:
page_size = max(1, min(int(request.query_params.get('page_size', 20)), 200))
except (ValueError, TypeError):
page_size = 20
try:
page = max(1, int(request.query_params.get('page', 1)))
except (ValueError, TypeError):
page = 1
total = qs.count()
start = (page - 1) * page_size
bookings = qs[start:start + page_size]
results = []
for b in bookings:
user = b.user
customer_name = f"{user.first_name} {user.last_name}".strip() or user.username
event = b.ticket_meta.event if b.ticket_meta_id else None
results.append({
'id': str(b.id),
'bookingId': b.booking_id or f'BKG-{b.id}',
'customerName': customer_name,
'customerEmail': user.email,
'eventTitle': event.title if event else '',
'eventId': str(event.id) if event else None,
'ticketType': b.ticket_type.ticket_type if b.ticket_type_id else '',
'quantity': b.quantity,
'amount': str(b.price),
'totalAmount': str(b.price * b.quantity),
'paymentStatus': b.payment_status,
'transactionId': b.transaction_id or '',
'createdDate': b.created_date.isoformat() if b.created_date else None,
})
return Response({
'count': total,
'page': page,
'pageSize': page_size,
'results': results,
})