72 lines
2.7 KiB
Python
72 lines
2.7 KiB
Python
|
|
"""Endpoints для Milestone-платежей (Upwork-style)."""
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
from sqlalchemy import select
|
||
|
|
|
||
|
|
from app.core.database import get_db
|
||
|
|
from app.core.security import get_current_user
|
||
|
|
from app.models.project import Project
|
||
|
|
from app.models.milestone import Milestone
|
||
|
|
from app.models.escrow import EscrowTransaction
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/api/projects/{project_id}/milestones", tags=["milestones"])
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("", response_model=dict)
|
||
|
|
async def create_milestone(
|
||
|
|
project_id: str, data: dict, db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user)
|
||
|
|
):
|
||
|
|
"""Создать milestone для проекта."""
|
||
|
|
|
||
|
|
result = await db.execute(select(Project).where(Project.id == project_id))
|
||
|
|
project = result.scalar_one_or_none()
|
||
|
|
|
||
|
|
if not project or project.client_id != user["id"]:
|
||
|
|
raise HTTPException(status_code=403, detail="Только владелец проекта может создавать milestones")
|
||
|
|
|
||
|
|
milestone = Milestone(
|
||
|
|
project_id=project_id,
|
||
|
|
title=data.get("title", ""),
|
||
|
|
description=data.get("description"),
|
||
|
|
amount=float(data.get("amount")),
|
||
|
|
due_date=None, # ISO format string
|
||
|
|
)
|
||
|
|
db.add(milestone)
|
||
|
|
await db.commit()
|
||
|
|
await db.refresh(milestone)
|
||
|
|
|
||
|
|
return {"id": str(milestone.id), "status": milestone.status}
|
||
|
|
|
||
|
|
|
||
|
|
@router.patch("/{milestone_id}/submit")
|
||
|
|
async def submit_milestone(milestone_id: str, db: AsyncSession = Depends(get_db)):
|
||
|
|
"""Фрилансер завершает milestone."""
|
||
|
|
|
||
|
|
result = await db.execute(select(Milestone).where(Milestone.id == milestone_id))
|
||
|
|
milestone = result.scalar_one_or_none()
|
||
|
|
|
||
|
|
if not milestone or milestone.status != "funded":
|
||
|
|
raise HTTPException(status_code=400, detail="Milestone не может быть завершён")
|
||
|
|
|
||
|
|
milestone.status = "submitted"
|
||
|
|
await db.commit()
|
||
|
|
|
||
|
|
return {"status": "submitted", "milestone_id": str(milestone.id)}
|
||
|
|
|
||
|
|
|
||
|
|
@router.patch("/{milestone_id}/approve")
|
||
|
|
async def approve_milestone(milestone_id: str, user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||
|
|
"""Клиент одобряет milestone."""
|
||
|
|
|
||
|
|
result = await db.execute(select(Milestone).where(Milestone.id == milestone_id))
|
||
|
|
milestone = result.scalar_one_or_none()
|
||
|
|
|
||
|
|
if not milestone or milestone.status != "submitted":
|
||
|
|
raise HTTPException(status_code=400, detail="Milestone не может быть одобрен")
|
||
|
|
|
||
|
|
milestone.status = "approved"
|
||
|
|
await db.commit()
|
||
|
|
|
||
|
|
return {"status": "approved", "milestone_id": str(milestone.id)}
|