Epic 2: take validates status, reject adds comment, assign validates member
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s

This commit is contained in:
Markov 2026-02-23 20:37:59 +01:00
parent 6bbddad7c2
commit dbd20ff550

View File

@ -10,7 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload, joinedload from sqlalchemy.orm import selectinload, joinedload
from tracker.database import get_db from tracker.database import get_db
from tracker.models import Task, Step, Project from tracker.models import Task, Step, Project, Member, Message
router = APIRouter(tags=["tasks"]) router = APIRouter(tags=["tasks"])
@ -257,10 +257,12 @@ async def delete_task(task_id: str, db: AsyncSession = Depends(get_db)):
@router.post("/tasks/{task_id}/take", response_model=TaskOut) @router.post("/tasks/{task_id}/take", response_model=TaskOut)
async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Depends(get_db)): async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Depends(get_db)):
"""Atomically take a task — only if not already assigned.""" """Atomically take a task — only if unassigned and in backlog/todo."""
task = await _get_task(task_id, db) task = await _get_task(task_id, db)
if task.assignee_slug: if task.assignee_slug:
raise HTTPException(409, f"Task already assigned to {task.assignee_slug}") raise HTTPException(409, f"Task already assigned to {task.assignee_slug}")
if task.status not in ("backlog", "todo"):
raise HTTPException(409, f"Task status is {task.status}, expected backlog or todo")
task.assignee_slug = slug task.assignee_slug = slug
task.status = "in_progress" task.status = "in_progress"
# Add to watchers # Add to watchers
@ -288,11 +290,21 @@ async def reject_task(task_id: str, req: RejectRequest, db: AsyncSession = Depen
task = await _get_task(task_id, db) task = await _get_task(task_id, db)
old_assignee = task.assignee_slug old_assignee = task.assignee_slug
task.assignee_slug = None task.assignee_slug = None
task.status = "todo" task.status = "backlog"
# Add rejection comment
if old_assignee:
comment = Message(
task_id=task.id,
author_type="system",
author_slug=old_assignee,
content=f"Задача отклонена: {req.reason}",
mentions=[],
)
db.add(comment)
await db.commit() await db.commit()
from tracker.ws.manager import manager from tracker.ws.manager import manager
await manager.broadcast_task_event(str(task.project_id), "task.updated", { await manager.broadcast_task_event(str(task.project_id), "task.updated", {
"id": str(task.id), "status": "todo", "assignee_slug": None, "id": str(task.id), "status": "backlog", "assignee_slug": None,
}) })
return {"ok": True, "reason": req.reason, "old_assignee": old_assignee} return {"ok": True, "reason": req.reason, "old_assignee": old_assignee}
@ -300,6 +312,10 @@ async def reject_task(task_id: str, req: RejectRequest, db: AsyncSession = Depen
@router.post("/tasks/{task_id}/assign", response_model=TaskOut) @router.post("/tasks/{task_id}/assign", response_model=TaskOut)
async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depends(get_db)): async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depends(get_db)):
"""Assign task to a member.""" """Assign task to a member."""
# Verify assignee exists
result = await db.execute(select(Member).where(Member.slug == req.assignee_slug))
if not result.scalar_one_or_none():
raise HTTPException(404, f"Member {req.assignee_slug} not found")
task = await _get_task(task_id, db) task = await _get_task(task_id, db)
task.assignee_slug = req.assignee_slug task.assignee_slug = req.assignee_slug
if req.assignee_slug not in (task.watchers or []): if req.assignee_slug not in (task.watchers or []):