158 lines
4.5 KiB
Python
Raw Permalink Normal View History

"""
Pension account API endpoints.
"""
from typing import List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from app.core.database import get_db
from app.api.deps import CurrentUser
from app.models.pension import PensionAccount, PensionHolding
from app.schemas.pension import (
PensionAccountCreate,
PensionAccountUpdate,
PensionAccountResponse,
AllocationResult,
RecommendationResult,
)
from app.services.pension_allocation import calculate_allocation, get_recommendation
router = APIRouter(prefix="/api/pension", tags=["pension"])
@router.post("/accounts", response_model=PensionAccountResponse, status_code=201)
async def create_account(
data: PensionAccountCreate,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
account = PensionAccount(
user_id=current_user.id,
**data.model_dump(),
)
db.add(account)
db.commit()
db.refresh(account)
return account
@router.get("/accounts", response_model=List[PensionAccountResponse])
async def list_accounts(
current_user: CurrentUser,
db: Session = Depends(get_db),
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
):
accounts = (
db.query(PensionAccount)
.options(joinedload(PensionAccount.holdings))
.filter(PensionAccount.user_id == current_user.id)
.order_by(PensionAccount.created_at.desc())
.offset(skip)
.limit(limit)
.all()
)
return accounts
@router.get("/accounts/{account_id}", response_model=PensionAccountResponse)
async def get_account(
account_id: int,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
account = (
db.query(PensionAccount)
.options(joinedload(PensionAccount.holdings))
.filter(PensionAccount.id == account_id, PensionAccount.user_id == current_user.id)
.first()
)
if not account:
raise HTTPException(status_code=404, detail="Pension account not found")
return account
@router.put("/accounts/{account_id}", response_model=PensionAccountResponse)
async def update_account(
account_id: int,
data: PensionAccountUpdate,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
account = (
db.query(PensionAccount)
.options(joinedload(PensionAccount.holdings))
.filter(PensionAccount.id == account_id, PensionAccount.user_id == current_user.id)
.first()
)
if not account:
raise HTTPException(status_code=404, detail="Pension account not found")
update_data = data.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(account, field, value)
db.commit()
db.refresh(account)
return account
@router.post("/accounts/{account_id}/allocate", response_model=AllocationResult)
async def allocate_assets(
account_id: int,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
account = (
db.query(PensionAccount)
.filter(PensionAccount.id == account_id, PensionAccount.user_id == current_user.id)
.first()
)
if not account:
raise HTTPException(status_code=404, detail="Pension account not found")
result = calculate_allocation(
account_id=account.id,
account_type=account.account_type.value,
total_amount=account.total_amount,
birth_year=account.birth_year,
target_retirement_age=account.target_retirement_age,
)
# Save allocation as holdings
db.query(PensionHolding).filter(PensionHolding.account_id == account_id).delete()
for alloc in result.allocations:
holding = PensionHolding(
account_id=account_id,
asset_name=alloc.asset_name,
asset_type=alloc.asset_type,
amount=alloc.amount,
ratio=alloc.ratio,
)
db.add(holding)
db.commit()
return result
@router.get("/accounts/{account_id}/recommendation", response_model=RecommendationResult)
async def get_account_recommendation(
account_id: int,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
account = (
db.query(PensionAccount)
.filter(PensionAccount.id == account_id, PensionAccount.user_id == current_user.id)
.first()
)
if not account:
raise HTTPException(status_code=404, detail="Pension account not found")
return get_recommendation(
account_id=account.id,
birth_year=account.birth_year,
target_retirement_age=account.target_retirement_age,
)