Epic 2: take validates status, reject adds comment, assign validates member
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
This commit is contained in:
parent
6bbddad7c2
commit
dbd20ff550
@ -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 []):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user