Sprint 7: PartnerMeCheckInView — JWT-authenticated ticket check-in
- admin_api/views.py: PartnerMeCheckInView — validates ticket belongs to partner's event, marks is_checked_in=True, returns name/ticket/event/ alreadyCheckedIn; uses IsAuthenticated (Bearer JWT, not body token) - admin_api/urls.py: wire partners/me/check-in/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,8 @@ urlpatterns = [
|
|||||||
# Partner-Me: staff CRUD (Sprint 6)
|
# Partner-Me: staff CRUD (Sprint 6)
|
||||||
path('partners/me/staff/', views.PartnerMeStaffListView.as_view(), name='partner-me-staff-list'),
|
path('partners/me/staff/', views.PartnerMeStaffListView.as_view(), name='partner-me-staff-list'),
|
||||||
path('partners/me/staff/<int:pk>/', views.PartnerMeStaffDetailView.as_view(), name='partner-me-staff-detail'),
|
path('partners/me/staff/<int:pk>/', views.PartnerMeStaffDetailView.as_view(), name='partner-me-staff-detail'),
|
||||||
|
# Partner-Me: check-in (Sprint 7)
|
||||||
|
path('partners/me/check-in/', views.PartnerMeCheckInView.as_view(), name='partner-me-check-in'),
|
||||||
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'),
|
||||||
|
|||||||
@@ -4223,3 +4223,62 @@ class PartnerMeStaffDetailView(APIView):
|
|||||||
staff_user.is_active = False
|
staff_user.is_active = False
|
||||||
staff_user.save(update_fields=['is_active'])
|
staff_user.save(update_fields=['is_active'])
|
||||||
return Response({'status': 'revoked'}, status=204)
|
return Response({'status': 'revoked'}, status=204)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Sprint 7 — Partner Check-in (JWT-authenticated)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
class PartnerMeCheckInView(APIView):
|
||||||
|
"""
|
||||||
|
POST /api/v1/partners/me/check-in/
|
||||||
|
Body: { "ticket_id": "<ticket_id>" }
|
||||||
|
Validates the ticket belongs to a partner-owned event, marks checked-in.
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
from bookings.models import Ticket
|
||||||
|
|
||||||
|
partner, err = _require_partner(request)
|
||||||
|
if err:
|
||||||
|
return err
|
||||||
|
|
||||||
|
ticket_id = (request.data.get('ticket_id') or '').strip()
|
||||||
|
if not ticket_id:
|
||||||
|
return Response({'valid': False, 'error': 'ticket_id is required'}, status=400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ticket = Ticket.objects.select_related(
|
||||||
|
'booking__user',
|
||||||
|
'booking__ticket_meta__event',
|
||||||
|
'booking__ticket_type',
|
||||||
|
).get(ticket_id=ticket_id)
|
||||||
|
except Ticket.DoesNotExist:
|
||||||
|
return Response({'valid': False, 'error': 'Ticket not found'}, status=404)
|
||||||
|
|
||||||
|
# Verify the ticket's event belongs to this partner
|
||||||
|
event = ticket.booking.ticket_meta.event if ticket.booking.ticket_meta_id else None
|
||||||
|
if not event or event.partner_id != partner.id:
|
||||||
|
return Response({'valid': False, 'error': 'Ticket not found'}, status=404)
|
||||||
|
|
||||||
|
user = ticket.booking.user
|
||||||
|
customer_name = f"{user.first_name} {user.last_name}".strip() or user.username
|
||||||
|
ticket_type = ticket.booking.ticket_type.ticket_type if ticket.booking.ticket_type_id else '—'
|
||||||
|
|
||||||
|
already_checked_in = ticket.is_checked_in
|
||||||
|
|
||||||
|
if not already_checked_in:
|
||||||
|
from datetime import datetime, timezone as _tz
|
||||||
|
ticket.is_checked_in = True
|
||||||
|
ticket.checked_in_date_time = datetime.now(_tz.utc)
|
||||||
|
ticket.save(update_fields=['is_checked_in', 'checked_in_date_time'])
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'valid': True,
|
||||||
|
'alreadyCheckedIn': already_checked_in,
|
||||||
|
'name': customer_name,
|
||||||
|
'ticket': ticket_type,
|
||||||
|
'event': event.title if event else '—',
|
||||||
|
'ticketId': ticket.ticket_id,
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user