Phase 6: Financials & Payouts — 4 new financial endpoints (metrics, transactions, settlements, release)

This commit is contained in:
2026-03-24 19:05:33 +00:00
parent bc0a9ad5c8
commit 3103eff949
2 changed files with 128 additions and 0 deletions

View File

@@ -27,4 +27,8 @@ urlpatterns = [
path('events/', views.EventListView.as_view(), name='event-list'),
path('events/<int:pk>/', views.EventDetailView.as_view(), name='event-detail'),
path('events/<int:pk>/moderate/', views.EventModerationView.as_view(), name='event-moderate'),
path('financials/metrics/', views.FinancialMetricsView.as_view(), name='financial-metrics'),
path('financials/transactions/', views.TransactionListView.as_view(), name='transaction-list'),
path('financials/settlements/', views.SettlementListView.as_view(), name='settlement-list'),
path('financials/settlements/<int:pk>/release/', views.SettlementReleaseView.as_view(), name='settlement-release'),
]

View File

@@ -717,3 +717,127 @@ class EventModerationView(APIView):
return Response({'error': 'Invalid action'}, status=400)
e.refresh_from_db()
return Response(_serialize_event(e))
# ---------------------------------------------------------------------------
# Phase 6: Financials & Payouts
# ---------------------------------------------------------------------------
_TX_STATUS_MAP = {
'captured': 'Completed',
'created': 'Pending',
'failed': 'Failed',
'refunded': 'Failed',
}
def _serialize_transaction(t):
ts = t.captured_at or t.created_at
order_ref = getattr(t, 'razorpay_order_id', None) or getattr(t, 'transaction_id', None)
return {
'id': str(t.id),
'title': f'Payment {order_ref}' if order_ref else f'Transaction #{t.id}',
'partner': '',
'amount': t.amount / 100,
'date': ts.isoformat() if ts else '',
'type': 'in',
'method': 'Razorpay',
'fees': 0,
'net': t.amount / 100,
'status': _TX_STATUS_MAP.get(t.status, 'Pending'),
}
_SETTLEMENT_STATUS_MAP = {
'pending': 'Ready',
'failed': 'Overdue',
'cancelled': 'Overdue',
'completed': 'On Hold',
'refunded': 'On Hold',
}
def _serialize_settlement(p):
return {
'id': str(p.id),
'partnerName': '',
'eventName': '',
'amount': float(p.payment_transaction_amount),
'dueDate': p.payment_transaction_date.isoformat() if p.payment_transaction_date else '',
'status': _SETTLEMENT_STATUS_MAP.get(p.payment_transaction_status, 'On Hold'),
}
class FinancialMetricsView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
from ledger.models import RazorpayTransaction
from banking_operations.models import PaymentTransaction
from django.db.models import Sum
total_paise = (
RazorpayTransaction.objects.filter(status='captured')
.aggregate(t=Sum('amount'))['t'] or 0
)
total_revenue = total_paise / 100
total_payouts = float(
PaymentTransaction.objects
.filter(payment_type='debit', payment_transaction_status='completed')
.aggregate(t=Sum('payment_transaction_amount'))['t'] or 0
)
platform_earnings = round(total_revenue * 0.12, 2)
pending_count = PaymentTransaction.objects.filter(
payment_type='debit', payment_transaction_status='pending'
).count()
pending_amount = float(
PaymentTransaction.objects
.filter(payment_type='debit', payment_transaction_status='pending')
.aggregate(t=Sum('payment_transaction_amount'))['t'] or 0
)
return Response({
'totalRevenue': total_revenue,
'totalPayouts': total_payouts,
'platformEarnings': platform_earnings,
'pendingPayouts': pending_count,
'pendingPayoutAmount': pending_amount,
})
class TransactionListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
from ledger.models import RazorpayTransaction
try:
page = max(1, int(request.GET.get('page', 1)))
page_size = min(100, int(request.GET.get('page_size', 20)))
except (ValueError, TypeError):
page, page_size = 1, 20
qs = RazorpayTransaction.objects.order_by('-id')
total = qs.count()
txs = qs[(page - 1) * page_size: page * page_size]
return Response({'count': total, 'results': [_serialize_transaction(t) for t in txs]})
class SettlementListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
from banking_operations.models import PaymentTransaction
qs = PaymentTransaction.objects.filter(
payment_type='debit'
).order_by('-id')[:50]
return Response([_serialize_settlement(p) for p in qs])
class SettlementReleaseView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
from banking_operations.models import PaymentTransaction
from django.shortcuts import get_object_or_404
p = get_object_or_404(PaymentTransaction, pk=pk, payment_type='debit')
p.payment_transaction_status = 'completed'
p.save(update_fields=['payment_transaction_status'])
return Response(_serialize_settlement(p))