lead claims: outreach v1, decided by Mike

Who picked a lead up and when, so nothing sits waiting unnoticed. No
outcomes, no end states, nothing on join_leads changes: the lead stays the
record of what the family said and the claim is only who has it. One row
per claim in lead_claims; the current claim is the latest unreleased.
Taking over is release then claim, never a silent overwrite: 409 names
the holder. The holder or an owner may release. Every claim and release
lands in the action log; the summary carries an unclaimed count.

tests/smoke_admin.py 112 -> 122.
This commit is contained in:
2026-09-04 18:34:01 -04:00
parent aaa77be14d
commit 59559ad909
3 changed files with 153 additions and 0 deletions
+38
View File
@@ -175,6 +175,44 @@ def get_one(request: Request, record_id: str, x_admin_token: str = Header(None))
return rec
@router.post("/leads/{record_id}/claim")
def claim_lead(request: Request, record_id: str, x_admin_token: str = Header(None)):
"""Take a lead so it is not sitting unclaimed. Outreach v1 is exactly
this: who picked it up and when. No outcomes. 409 if someone else has
it - taking over is release, then claim, never a silent overwrite."""
_auth(request, x_admin_token, "leads:read")
person = _person(request)
if not person:
raise HTTPException(403, "claiming needs a signed-in person")
try:
cur = store.claim_lead(record_id, person["id"], person["email"])
except store.ClaimRejected as e:
raise _reject(e)
if not cur:
raise HTTPException(404, "no such lead")
_log(request, "lead.claimed", record_id)
return {"claim": {"email": cur["email"], "claimed_at": cur["claimed_at"]}}
@router.post("/leads/{record_id}/release")
def release_lead(request: Request, record_id: str, x_admin_token: str = Header(None)):
"""Let a lead go. The holder may; so may an owner (people:manage),
which is how a lead gets reassigned when someone steps back."""
_auth(request, x_admin_token, "leads:read")
person = _person(request)
if not person:
raise HTTPException(403, "releasing needs a signed-in person")
try:
cur = store.release_lead(record_id, person["id"], person["email"],
can_release_any=identity.can(person, "people:manage"))
except store.ClaimRejected as e:
raise _reject(e)
if not cur:
raise HTTPException(404, "no such lead")
_log(request, "lead.released", "%s (was %s)" % (record_id, cur["email"]))
return {"claim": None}
@router.get("/mirrors/failed")
def failed(request: Request, target: str = "google_sheet", x_admin_token: str = Header(None)):
_auth(request, x_admin_token, "leads:read")