"""成本管理路由""" from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from typing import List, Optional from datetime import date from database import get_db from models import ( User, UserRole, AIToolCost, AIToolCostAllocation, OutsourceCost, CostOverride, SubscriptionPeriod, CostAllocationType, OutsourceType ) from schemas import ( AIToolCostCreate, AIToolCostOut, OutsourceCostCreate, OutsourceCostOut, CostOverrideCreate ) from auth import get_current_user, require_role router = APIRouter(prefix="/api/costs", tags=["成本管理"]) # ──────────────────── AI 工具成本 ──────────────────── @router.get("/ai-tools", response_model=List[AIToolCostOut]) def list_ai_tool_costs( db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR, UserRole.LEADER)) ): costs = db.query(AIToolCost).order_by(AIToolCost.record_date.desc()).all() return [ AIToolCostOut( id=c.id, tool_name=c.tool_name, subscription_period=c.subscription_period.value if hasattr(c.subscription_period, 'value') else c.subscription_period, amount=c.amount, allocation_type=c.allocation_type.value if hasattr(c.allocation_type, 'value') else c.allocation_type, project_id=c.project_id, recorded_by=c.recorded_by, record_date=c.record_date, created_at=c.created_at, ) for c in costs ] @router.post("/ai-tools", response_model=AIToolCostOut) def create_ai_tool_cost( req: AIToolCostCreate, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR, UserRole.LEADER)) ): cost = AIToolCost( tool_name=req.tool_name, subscription_period=SubscriptionPeriod(req.subscription_period), amount=req.amount, allocation_type=CostAllocationType(req.allocation_type), project_id=req.project_id, recorded_by=current_user.id, record_date=req.record_date, ) db.add(cost) db.flush() # 处理手动分摊 if req.allocation_type == "手动分摊" and req.allocations: for alloc in req.allocations: db.add(AIToolCostAllocation( ai_tool_cost_id=cost.id, project_id=alloc["project_id"], percentage=alloc["percentage"], )) db.commit() db.refresh(cost) return AIToolCostOut( id=cost.id, tool_name=cost.tool_name, subscription_period=cost.subscription_period.value, amount=cost.amount, allocation_type=cost.allocation_type.value, project_id=cost.project_id, recorded_by=cost.recorded_by, record_date=cost.record_date, created_at=cost.created_at, ) @router.delete("/ai-tools/{cost_id}") def delete_ai_tool_cost( cost_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER)) ): cost = db.query(AIToolCost).filter(AIToolCost.id == cost_id).first() if not cost: raise HTTPException(status_code=404, detail="记录不存在") db.query(AIToolCostAllocation).filter(AIToolCostAllocation.ai_tool_cost_id == cost_id).delete() db.delete(cost) db.commit() return {"message": "已删除"} # ──────────────────── 外包成本 ──────────────────── @router.get("/outsource", response_model=List[OutsourceCostOut]) def list_outsource_costs( project_id: Optional[int] = Query(None), db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR, UserRole.LEADER)) ): q = db.query(OutsourceCost) if project_id: q = q.filter(OutsourceCost.project_id == project_id) costs = q.order_by(OutsourceCost.record_date.desc()).all() return [ OutsourceCostOut( id=c.id, project_id=c.project_id, outsource_type=c.outsource_type.value if hasattr(c.outsource_type, 'value') else c.outsource_type, episode_start=c.episode_start, episode_end=c.episode_end, amount=c.amount, recorded_by=c.recorded_by, record_date=c.record_date, created_at=c.created_at, ) for c in costs ] @router.post("/outsource", response_model=OutsourceCostOut) def create_outsource_cost( req: OutsourceCostCreate, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR, UserRole.LEADER)) ): cost = OutsourceCost( project_id=req.project_id, outsource_type=OutsourceType(req.outsource_type), episode_start=req.episode_start, episode_end=req.episode_end, amount=req.amount, recorded_by=current_user.id, record_date=req.record_date, ) db.add(cost) db.commit() db.refresh(cost) return OutsourceCostOut( id=cost.id, project_id=cost.project_id, outsource_type=cost.outsource_type.value, episode_start=cost.episode_start, episode_end=cost.episode_end, amount=cost.amount, recorded_by=cost.recorded_by, record_date=cost.record_date, created_at=cost.created_at, ) @router.delete("/outsource/{cost_id}") def delete_outsource_cost( cost_id: int, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER)) ): cost = db.query(OutsourceCost).filter(OutsourceCost.id == cost_id).first() if not cost: raise HTTPException(status_code=404, detail="记录不存在") db.delete(cost) db.commit() return {"message": "已删除"} # ──────────────────── 人力成本手动调整 ──────────────────── @router.post("/overrides") def create_cost_override( req: CostOverrideCreate, db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR)) ): override = CostOverride( user_id=req.user_id, date=req.date, project_id=req.project_id, override_amount=req.override_amount, adjusted_by=current_user.id, reason=req.reason, ) db.add(override) db.commit() return {"message": "已保存成本调整"} @router.get("/overrides") def list_cost_overrides( user_id: Optional[int] = Query(None), project_id: Optional[int] = Query(None), db: Session = Depends(get_db), current_user: User = Depends(require_role(UserRole.OWNER, UserRole.SUPERVISOR)) ): q = db.query(CostOverride) if user_id: q = q.filter(CostOverride.user_id == user_id) if project_id: q = q.filter(CostOverride.project_id == project_id) records = q.order_by(CostOverride.date.desc()).all() return [ { "id": r.id, "user_id": r.user_id, "date": r.date, "project_id": r.project_id, "override_amount": r.override_amount, "adjusted_by": r.adjusted_by, "reason": r.reason, "created_at": r.created_at, } for r in records ]