feat(P3.1): milestone action endpoints — freeze/start/close + auto-complete hook
- New milestone_actions router with POST freeze/start/close endpoints - freeze: validates exactly 1 release maintenance task exists - start: validates all milestone/task dependencies completed, records started_at - close: allows from open/freeze/undergoing with reason - try_auto_complete_milestone helper: auto-completes milestone when sole release task finishes - Wired auto-complete into task transition and update endpoints - Added freeze enforcement: no new feature story tasks after freeze - Added started_at to milestone serializer - All actions write activity logs
This commit is contained in:
@@ -31,6 +31,7 @@ def _serialize_milestone(milestone):
|
||||
"depend_on_tasks": json.loads(milestone.depend_on_tasks) if milestone.depend_on_tasks else [],
|
||||
"project_id": milestone.project_id,
|
||||
"created_by_id": milestone.created_by_id,
|
||||
"started_at": milestone.started_at,
|
||||
"created_at": milestone.created_at,
|
||||
"updated_at": milestone.updated_at,
|
||||
}
|
||||
@@ -111,8 +112,14 @@ def create_milestone_task(project_id: int, milestone_id: int, task_data: schemas
|
||||
if not milestone:
|
||||
raise HTTPException(status_code=404, detail="Milestone not found")
|
||||
|
||||
if milestone.status and hasattr(milestone.status, 'value') and milestone.status.value == "undergoing":
|
||||
raise HTTPException(status_code=400, detail="Cannot add items to a milestone that is undergoing")
|
||||
ms_status = milestone.status.value if hasattr(milestone.status, 'value') else milestone.status
|
||||
if ms_status in ("undergoing", "completed", "closed"):
|
||||
raise HTTPException(status_code=400, detail=f"Cannot add items to a milestone that is '{ms_status}'")
|
||||
# P3.6 / §5: freeze prevents adding new feature story tasks
|
||||
task_type = task_data.model_dump(exclude_unset=True).get("task_type", "")
|
||||
task_subtype = task_data.model_dump(exclude_unset=True).get("task_subtype", "")
|
||||
if ms_status == "freeze" and task_type == "story" and task_subtype == "feature":
|
||||
raise HTTPException(status_code=400, detail="Cannot add feature story tasks after milestone is frozen")
|
||||
|
||||
# Generate task_code
|
||||
milestone_code = milestone.milestone_code or f"m{milestone.id}"
|
||||
|
||||
Reference in New Issue
Block a user