billing admin account 1
This commit is contained in:
@@ -8,6 +8,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db import models
|
||||
|
||||
from .models import Invoice, Payment, CreditPackage, PaymentMethodConfig, CreditTransaction
|
||||
from .services.invoice_service import InvoiceService
|
||||
@@ -126,16 +127,21 @@ class PaymentViewSet(viewsets.ViewSet):
|
||||
"""Get available payment methods for current account"""
|
||||
account = request.user.account
|
||||
methods = PaymentService.get_available_payment_methods(account)
|
||||
method_list = methods.pop('methods', [])
|
||||
|
||||
return Response(methods)
|
||||
return Response({
|
||||
'results': method_list,
|
||||
'count': len(method_list),
|
||||
**methods
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['post'])
|
||||
@action(detail=False, methods=['post'], url_path='manual')
|
||||
def create_manual_payment(self, request):
|
||||
"""Submit manual payment for approval"""
|
||||
account = request.user.account
|
||||
invoice_id = request.data.get('invoice_id')
|
||||
payment_method = request.data.get('payment_method') # 'bank_transfer' or 'local_wallet'
|
||||
transaction_reference = request.data.get('transaction_reference')
|
||||
transaction_reference = request.data.get('transaction_reference') or request.data.get('reference')
|
||||
notes = request.data.get('notes')
|
||||
|
||||
if not all([invoice_id, payment_method, transaction_reference]):
|
||||
@@ -261,22 +267,32 @@ class CreditTransactionViewSet(viewsets.ViewSet):
|
||||
for txn in transactions
|
||||
],
|
||||
'count': transactions.count(),
|
||||
'current_balance': account.credit_balance
|
||||
'current_balance': account.credits
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def balance(self, request):
|
||||
"""Get current credit balance"""
|
||||
account = request.user.account
|
||||
|
||||
# Get subscription details
|
||||
active_subscription = account.subscriptions.filter(status='active').first()
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
now = timezone.now()
|
||||
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
used_this_month = abs(
|
||||
CreditTransaction.objects.filter(
|
||||
account=account,
|
||||
created_at__gte=month_start,
|
||||
amount__lt=0
|
||||
).aggregate(total=models.Sum('amount'))['total'] or 0
|
||||
)
|
||||
plan = getattr(account, 'plan', None)
|
||||
included = plan.included_credits if plan else 0
|
||||
|
||||
return Response({
|
||||
'balance': account.credit_balance,
|
||||
'subscription_plan': active_subscription.plan.name if active_subscription else 'None',
|
||||
'monthly_credits': active_subscription.plan.monthly_credits if active_subscription else 0,
|
||||
'subscription_status': active_subscription.status if active_subscription else None
|
||||
'credits': account.credits,
|
||||
'plan_credits_per_month': included,
|
||||
'credits_used_this_month': used_this_month,
|
||||
'credits_remaining': max(account.credits, 0),
|
||||
})
|
||||
|
||||
|
||||
@@ -284,15 +300,95 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
"""Admin billing management"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def pending_payments(self, request):
|
||||
"""List payments pending approval"""
|
||||
# Check admin permission
|
||||
if not request.user.is_staff:
|
||||
def _require_admin(self, request):
|
||||
if not request.user.is_staff and not getattr(request.user, 'is_superuser', False):
|
||||
return Response(
|
||||
{'error': 'Admin access required'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
return None
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def invoices(self, request):
|
||||
"""List invoices across all accounts (admin)"""
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
status_filter = request.query_params.get('status')
|
||||
account_id = request.query_params.get('account_id')
|
||||
qs = Invoice.objects.all().select_related('account').order_by('-created_at')
|
||||
if status_filter:
|
||||
qs = qs.filter(status=status_filter)
|
||||
if account_id:
|
||||
qs = qs.filter(account_id=account_id)
|
||||
|
||||
invoices = qs[:200]
|
||||
return Response({
|
||||
'results': [
|
||||
{
|
||||
'id': inv.id,
|
||||
'invoice_number': inv.invoice_number,
|
||||
'status': inv.status,
|
||||
'total_amount': str(getattr(inv, 'total_amount', inv.total)),
|
||||
'subtotal': str(inv.subtotal),
|
||||
'tax_amount': str(getattr(inv, 'tax_amount', inv.tax)),
|
||||
'currency': inv.currency,
|
||||
'created_at': inv.created_at.isoformat(),
|
||||
'paid_at': inv.paid_at.isoformat() if inv.paid_at else None,
|
||||
'due_date': inv.due_date.isoformat() if inv.due_date else None,
|
||||
'line_items': inv.line_items,
|
||||
'account_name': inv.account.name if inv.account else None,
|
||||
}
|
||||
for inv in invoices
|
||||
],
|
||||
'count': qs.count(),
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def payments(self, request):
|
||||
"""List payments across all accounts (admin)"""
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
status_filter = request.query_params.get('status')
|
||||
account_id = request.query_params.get('account_id')
|
||||
payment_method = request.query_params.get('payment_method')
|
||||
qs = Payment.objects.all().select_related('account', 'invoice').order_by('-created_at')
|
||||
if status_filter:
|
||||
qs = qs.filter(status=status_filter)
|
||||
if account_id:
|
||||
qs = qs.filter(account_id=account_id)
|
||||
if payment_method:
|
||||
qs = qs.filter(payment_method=payment_method)
|
||||
|
||||
payments = qs[:200]
|
||||
return Response({
|
||||
'results': [
|
||||
{
|
||||
'id': pay.id,
|
||||
'account_name': pay.account.name if pay.account else None,
|
||||
'amount': str(pay.amount),
|
||||
'currency': pay.currency,
|
||||
'status': pay.status,
|
||||
'payment_method': pay.payment_method,
|
||||
'created_at': pay.created_at.isoformat(),
|
||||
'invoice_id': pay.invoice_id,
|
||||
'invoice_number': pay.invoice.invoice_number if pay.invoice else None,
|
||||
'transaction_reference': pay.transaction_reference,
|
||||
}
|
||||
for pay in payments
|
||||
],
|
||||
'count': qs.count(),
|
||||
})
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def pending_payments(self, request):
|
||||
"""List payments pending approval"""
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
payments = PaymentService.get_pending_approvals()
|
||||
|
||||
@@ -317,11 +413,9 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
@action(detail=True, methods=['post'])
|
||||
def approve_payment(self, request, pk=None):
|
||||
"""Approve a manual payment"""
|
||||
if not request.user.is_staff:
|
||||
return Response(
|
||||
{'error': 'Admin access required'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
payment = get_object_or_404(Payment, id=pk)
|
||||
admin_notes = request.data.get('notes')
|
||||
@@ -347,11 +441,9 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
@action(detail=True, methods=['post'])
|
||||
def reject_payment(self, request, pk=None):
|
||||
"""Reject a manual payment"""
|
||||
if not request.user.is_staff:
|
||||
return Response(
|
||||
{'error': 'Admin access required'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
payment = get_object_or_404(Payment, id=pk)
|
||||
rejection_reason = request.data.get('reason', 'No reason provided')
|
||||
@@ -377,11 +469,9 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
@action(detail=False, methods=['get'])
|
||||
def stats(self, request):
|
||||
"""System billing stats"""
|
||||
if not request.user.is_staff:
|
||||
return Response(
|
||||
{'error': 'Admin access required'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
error = self._require_admin(request)
|
||||
if error:
|
||||
return error
|
||||
|
||||
from django.db.models import Sum, Count
|
||||
from ...auth.models import Account
|
||||
@@ -407,12 +497,12 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
|
||||
# Revenue stats
|
||||
total_revenue = Payment.objects.filter(
|
||||
status='completed',
|
||||
status__in=['completed', 'succeeded'],
|
||||
amount__gt=0
|
||||
).aggregate(total=Sum('amount'))['total'] or 0
|
||||
|
||||
revenue_this_month = Payment.objects.filter(
|
||||
status='completed',
|
||||
status__in=['completed', 'succeeded'],
|
||||
processed_at__gte=this_month_start,
|
||||
amount__gt=0
|
||||
).aggregate(total=Sum('amount'))['total'] or 0
|
||||
@@ -430,9 +520,7 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
).aggregate(total=Sum('amount'))['total'] or 0)
|
||||
|
||||
# Payment/Invoice stats
|
||||
pending_approvals = Payment.objects.filter(
|
||||
status='pending_approval'
|
||||
).count()
|
||||
pending_approvals = Payment.objects.filter(status='pending_approval').count()
|
||||
|
||||
invoices_pending = Invoice.objects.filter(status='pending').count()
|
||||
invoices_overdue = Invoice.objects.filter(
|
||||
@@ -442,7 +530,7 @@ class AdminBillingViewSet(viewsets.ViewSet):
|
||||
|
||||
# Recent activity
|
||||
recent_payments = Payment.objects.filter(
|
||||
status='completed'
|
||||
status__in=['completed', 'succeeded']
|
||||
).order_by('-processed_at')[:5]
|
||||
|
||||
recent_activity = [
|
||||
|
||||
Reference in New Issue
Block a user