from fastapi import APIRouter, Depends, HTTPException
from typing import List
from decimal import Decimal
from pydantic import BaseModel
from ..auth import get_current_user
from ..models import User
from ..billing.models import PricingConfig, Invoice, SubscriptionPlan
from ..billing.invoice_generator import InvoiceGenerator
def require_superuser(current_user: User = Depends(get_current_user)):
if not current_user.is_superuser:
raise HTTPException(status_code=403, detail="Superuser privileges required")
return current_user
router = APIRouter(
prefix="/api/admin/billing",
tags=["admin", "billing"],
dependencies=[Depends(require_superuser)]
)
class PricingConfigUpdate(BaseModel):
config_key: str
config_value: float
class PlanCreate(BaseModel):
name: str
display_name: str
description: str
storage_gb: int
bandwidth_gb: int
price_monthly: float
price_yearly: float = None
@router.get("/pricing")
async def get_all_pricing(current_user: User = Depends(require_superuser)):
configs = await PricingConfig.all()
return [
{
"id": c.id,
"config_key": c.config_key,
"config_value": float(c.config_value),
"description": c.description,
"unit": c.unit,
"updated_at": c.updated_at
}
for c in configs
]
@router.put("/pricing/{config_id}")
async def update_pricing(
config_id: int,
update: PricingConfigUpdate,
current_user: User = Depends(require_superuser)
):
config = await PricingConfig.get_or_none(id=config_id)
if not config:
raise HTTPException(status_code=404, detail="Config not found")
config.config_value = Decimal(str(update.config_value))
config.updated_by = current_user
await config.save()
return {"message": "Pricing updated successfully"}
@router.post("/generate-invoices/{year}/{month}")
async def generate_all_invoices(
year: int,
month: int,
current_user: User = Depends(require_superuser)
):
users = await User.filter(is_active=True).all()
generated = []
skipped = []
for user in users:
invoice = await InvoiceGenerator.generate_monthly_invoice(user, year, month)
if invoice:
generated.append({
"user_id": user.id,
"invoice_id": invoice.id,
"total": float(invoice.total)
})
else:
skipped.append(user.id)
return {
"generated": len(generated),
"skipped": len(skipped),
"invoices": generated
}
@router.post("/plans")
async def create_plan(
plan_data: PlanCreate,
current_user: User = Depends(require_superuser)
):
plan = await SubscriptionPlan.create(**plan_data.dict())
return {"id": plan.id, "message": "Plan created successfully"}
@router.get("/stats")
async def get_billing_stats(current_user: User = Depends(require_superuser)):
from tortoise.functions import Sum, Count
total_revenue = await Invoice.filter(status="paid").annotate(
total_sum=Sum("total")
).values("total_sum")
invoice_count = await Invoice.all().count()
pending_invoices = await Invoice.filter(status="open").count()
return {
"total_revenue": float(total_revenue[0]["total_sum"] or 0),
"total_invoices": invoice_count,
"pending_invoices": pending_invoices
}