from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import require_role from app.db.database import get_db from app.db.models import FundRecord, User from app.schemas.fund import FundRecordCreate, FundRecordUpdate, FundRecordOut, FundStatistics from app.schemas.common import PageResponse from app.services.fund_service import ( create_fund_record, update_fund_record, delete_fund_record, get_fund_record_by_id, list_fund_records, get_fund_statistics, ) router = APIRouter(prefix="/api/fund", tags=["fund"]) def record_to_out(record: FundRecord) -> FundRecordOut: return FundRecordOut( id=record.id, class_id=record.class_id, type=record.type, amount=record.amount, category=record.category, description=record.description, record_date=record.record_date, recorder_id=record.recorder_id, recorder_name=record.recorder.name if record.recorder else "Unknown", created_at=record.created_at, updated_at=record.updated_at, ) @router.get("/statistics", response_model=FundStatistics) async def get_statistics( class_id: int | None = None, user: User = Depends(require_role("super_admin", "class_admin", "student")), db: AsyncSession = Depends(get_db), ): effective_class_id = class_id if user.role == "super_admin" and class_id else user.class_id if effective_class_id is None: return FundStatistics( total_income=0, total_expense=0, balance=0, income_by_category=[], expense_by_category=[] ) return await get_fund_statistics(db, effective_class_id) @router.get("/", response_model=PageResponse[FundRecordOut]) async def get_fund_records( page: int = 1, page_size: int = 20, type: str | None = None, category: str | None = None, class_id: int | None = None, user: User = Depends(require_role("super_admin", "class_admin", "student")), db: AsyncSession = Depends(get_db), ): effective_class_id = class_id if user.role == "super_admin" and class_id else user.class_id if effective_class_id is None: return PageResponse(items=[], total=0, page=page, page_size=page_size, total_pages=0) records, total = await list_fund_records(db, effective_class_id, page, page_size, type, category) total_pages = (total + page_size - 1) // page_size items = [record_to_out(r) for r in records] return PageResponse(items=items, total=total, page=page, page_size=page_size, total_pages=total_pages) @router.post("/", response_model=FundRecordOut) async def create_new_record( data: FundRecordCreate, class_id: int | None = None, user: User = Depends(require_role("super_admin", "class_admin")), db: AsyncSession = Depends(get_db), ): effective_class_id = class_id if user.role == "super_admin" and class_id else user.class_id if effective_class_id is None: raise HTTPException(status_code=400, detail="No class specified") if data.type not in ("income", "expense"): raise HTTPException(status_code=400, detail="Type must be 'income' or 'expense'") if data.amount <= 0: raise HTTPException(status_code=400, detail="Amount must be positive") record = await create_fund_record(db, effective_class_id, user.id, data) # Reload with recorder relationship record = await get_fund_record_by_id(db, record.id) return record_to_out(record) @router.put("/{record_id}", response_model=FundRecordOut) async def update_existing_record( record_id: int, data: FundRecordUpdate, user: User = Depends(require_role("super_admin", "class_admin")), db: AsyncSession = Depends(get_db), ): record = await get_fund_record_by_id(db, record_id) if record is None: raise HTTPException(status_code=404, detail="Record not found") if user.role != "super_admin" and record.class_id != user.class_id: raise HTTPException(status_code=403, detail="Access denied") if data.type is not None and data.type not in ("income", "expense"): raise HTTPException(status_code=400, detail="Type must be 'income' or 'expense'") if data.amount is not None and data.amount <= 0: raise HTTPException(status_code=400, detail="Amount must be positive") updated = await update_fund_record(db, record, data) # Reload with recorder relationship updated = await get_fund_record_by_id(db, updated.id) return record_to_out(updated) @router.delete("/{record_id}") async def delete_existing_record( record_id: int, user: User = Depends(require_role("super_admin", "class_admin")), db: AsyncSession = Depends(get_db), ): record = await get_fund_record_by_id(db, record_id) if record is None: raise HTTPException(status_code=404, detail="Record not found") if user.role != "super_admin" and record.class_id != user.class_id: raise HTTPException(status_code=403, detail="Access denied") await delete_fund_record(db, record) return {"message": "Record deleted"}