feat(campaigns): submit/approve/reject router endpoints
This commit is contained in:
@@ -91,6 +91,16 @@ from app.services.campaign_crud_service import (
|
||||
activate_campaign as crud_activate,
|
||||
)
|
||||
|
||||
from app.services.campaign_crud_service import (
|
||||
submit_campaign_for_approval as crud_submit,
|
||||
)
|
||||
from app.services.campaign_crud_service import (
|
||||
approve_campaign as crud_approve,
|
||||
)
|
||||
from app.services.campaign_crud_service import (
|
||||
reject_campaign as crud_reject,
|
||||
)
|
||||
|
||||
# Import log_action from app.services.audit_service
|
||||
from app.services.audit_service import log_action
|
||||
|
||||
@@ -172,6 +182,18 @@ class SchedulePayload(BaseModel):
|
||||
next_run_at: Optional[str] = None
|
||||
|
||||
|
||||
class ApprovePayload(BaseModel):
|
||||
"""Payload for a manager approving a pending campaign."""
|
||||
|
||||
start_date: str # ISO date/datetime — required, campaign won't activate without it
|
||||
|
||||
|
||||
class RejectPayload(BaseModel):
|
||||
"""Payload for a manager rejecting a pending campaign."""
|
||||
|
||||
reason: str
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /campaigns — List campaigns with filters
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -385,6 +407,98 @@ def update_campaign(
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST /campaigns/{id}/submit — Submit draft for manager approval
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.post("/{campaign_id}/submit")
|
||||
def submit_campaign(
|
||||
campaign_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_any_role("red_lead", "blue_lead")),
|
||||
) -> dict:
|
||||
"""Submit a draft campaign into the manager's approval queue."""
|
||||
with UnitOfWork(db) as uow:
|
||||
campaign = crud_submit(
|
||||
db, campaign_id,
|
||||
submitter_id=current_user.id,
|
||||
submitter_role=current_user.role,
|
||||
)
|
||||
log_action(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
action="submit_campaign_for_approval",
|
||||
entity_type="campaign",
|
||||
entity_id=campaign.id,
|
||||
details={"name": campaign.name},
|
||||
)
|
||||
uow.commit()
|
||||
db.refresh(campaign)
|
||||
return serialize_campaign(db, campaign)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST /campaigns/{id}/approve — Manager approves a pending campaign
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.post("/{campaign_id}/approve")
|
||||
def approve_campaign_endpoint(
|
||||
campaign_id: str,
|
||||
payload: ApprovePayload,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_any_role("manager")),
|
||||
) -> dict:
|
||||
"""Manager approves a pending campaign, fixing its start date and activating it."""
|
||||
with UnitOfWork(db) as uow:
|
||||
campaign = crud_approve(
|
||||
db, campaign_id,
|
||||
approver_id=current_user.id,
|
||||
start_date=payload.start_date,
|
||||
)
|
||||
log_action(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
action="approve_campaign",
|
||||
entity_type="campaign",
|
||||
entity_id=campaign.id,
|
||||
details={"start_date": payload.start_date},
|
||||
)
|
||||
uow.commit()
|
||||
db.refresh(campaign)
|
||||
return serialize_campaign(db, campaign)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST /campaigns/{id}/reject — Manager rejects a pending campaign
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.post("/{campaign_id}/reject")
|
||||
def reject_campaign_endpoint(
|
||||
campaign_id: str,
|
||||
payload: RejectPayload,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(require_any_role("manager")),
|
||||
) -> dict:
|
||||
"""Manager rejects a pending campaign, returning it to draft with a reason."""
|
||||
with UnitOfWork(db) as uow:
|
||||
campaign = crud_reject(
|
||||
db, campaign_id,
|
||||
rejecter_id=current_user.id,
|
||||
reason=payload.reason,
|
||||
)
|
||||
log_action(
|
||||
db,
|
||||
user_id=current_user.id,
|
||||
action="reject_campaign",
|
||||
entity_type="campaign",
|
||||
entity_id=campaign.id,
|
||||
details={"reason": payload.reason},
|
||||
)
|
||||
uow.commit()
|
||||
db.refresh(campaign)
|
||||
return serialize_campaign(db, campaign)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DELETE /campaigns/{id} — Delete campaign
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user