feat(boc3): authority-aware filing with upsell-approve follow-ups

_get_authority_state() returns structured FMCSA authority state; handle()
branches on active/pending/revoked/none:
- active: file/refresh BOC-3 (current behavior)
- pending: file BOC-3 + insurance/21-day-vetting reminder
- revoked: file + recommend reinstatement (mc-authority, never auto-charge)
- none (USDOT only): flag MC authority needed first, do not file blindly
recommended_followups + authority_state persisted in admin todo for
upsell-approve on the order timeline.
This commit is contained in:
justin 2026-06-02 03:31:17 -05:00
parent cadff79bd6
commit bbbfeaeaa1

View file

@ -136,8 +136,59 @@ class BOC3FilingHandler:
LOG.error("[%s] Missing DOT number", order_number)
return []
# Check current BOC-3 status
boc3_status = self._check_boc3_status(dot_number)
# Check current authority/BOC-3 status (structured) and branch on it.
auth = self._get_authority_state(dot_number)
boc3_status = auth["summary"]
branch = auth["branch"]
# Branch-specific follow-ups. These are surfaced for upsell-approve on the
# order timeline (customer/admin confirms + pays) — NEVER auto-charged.
recommended_followups: list[dict] = []
branch_steps: list[str] = []
if branch == "active":
# Default behavior: just file/refresh the BOC-3.
branch_steps = ["Authority is ACTIVE — file/refresh BOC-3 only."]
elif branch == "pending":
branch_steps = [
"Authority is PENDING — BOC-3 can be filed now (parallel OK).",
"Authority will NOT activate until active insurance (BMC-91/BMC-34) is on file"
" AND the ~21-day vetting/protest window passes.",
]
recommended_followups.append({
"type": "insurance_reminder",
"title": "Confirm active insurance is on file",
"reason": "Pending authority needs insurance + the 21-day vetting "
"window before it activates.",
"service_slug": None,
})
elif branch == "revoked":
branch_steps = [
"Authority is REVOKED/INACTIVE — BOC-3 alone does NOT reinstate.",
"Recommend reinstatement (OP-1 reinstatement + $80 gov fee).",
]
recommended_followups.append({
"type": "upsell",
"title": "Reinstate operating authority",
"reason": "Authority is revoked; a BOC-3 cannot activate revoked "
"authority. Reinstatement (OP-1 + $80 FMCSA fee) is required.",
"service_slug": "mc-authority",
})
elif branch == "none":
branch_steps = [
"NO operating authority on file (USDOT only) — BOC-3 has nothing to attach to.",
"MC operating authority is likely needed FIRST; do NOT file BOC-3 in isolation.",
]
recommended_followups.append({
"type": "upsell",
"title": "Apply for MC operating authority first",
"reason": "A BOC-3 designates process agents for an operating "
"authority. With USDOT only, authority must be obtained first.",
"service_slug": "mc-authority",
})
else:
branch_steps = [
f"Authority status UNKNOWN ({boc3_status}) — verify manually before filing.",
]
# Build the designation request
designation = {
@ -163,7 +214,9 @@ class BOC3FilingHandler:
"service": self.SERVICE_NAME,
"designation": designation,
"current_boc3_status": boc3_status,
"steps": [
"authority_state": auth,
"recommended_followups": recommended_followups,
"steps": branch_steps + [
"1. Go to https://www.processagent.com/order",
"2. Submit BOC-3 order ($25) with carrier's DOT#, MC#, legal name, address",
f" Partner: {PROCESS_AGENT_PARTNER['name']}",
@ -192,8 +245,13 @@ class BOC3FilingHandler:
f"DOT: {dot_number}\n"
f"MC/Docket: {docket_number}\n"
f"Type: {entity_type}\n"
f"Authority status: {boc3_status}\n"
f"Customer: {customer_email}\n\n"
f"Submit to process agent partner for electronic filing with FMCSA.",
+ ("Recommended follow-ups (upsell-approve, not auto-charged):\n"
+ "\n".join(f" - {f['title']}: {f['reason']}"
for f in recommended_followups) + "\n\n"
if recommended_followups else "")
+ f"Submit to process agent partner for electronic filing with FMCSA.",
json.dumps(todo_data),
))
conn.commit()
@ -208,12 +266,31 @@ class BOC3FilingHandler:
return []
def _check_boc3_status(self, dot_number: str) -> str:
"""Check if carrier has a BOC-3 on file via FMCSA API."""
"""Human-readable summary string (kept for backward compat / emails)."""
auth = self._get_authority_state(dot_number)
return auth.get("summary", "Could not determine authority status")
def _get_authority_state(self, dot_number: str) -> dict:
"""
Return structured authority state from FMCSA QC API.
Keys: common/contract/broker (raw status codes A/I/P/N/None),
any_active (bool), any_pending (bool), any_revoked (bool),
has_any_authority (bool), branch (str), summary (str).
Branch is one of: active | pending | revoked | none | unknown.
"""
result = {
"common": None, "contract": None, "broker": None,
"any_active": False, "any_pending": False, "any_revoked": False,
"has_any_authority": False, "branch": "unknown",
"summary": "Could not determine authority status",
}
try:
import urllib.request
api_key = os.environ.get("FMCSA_API_KEY", "")
if not api_key:
return "API key not configured"
result["summary"] = "API key not configured"
return result
url = (
f"https://mobile.fmcsa.dot.gov/qc/services/carriers/"
@ -224,18 +301,33 @@ class BOC3FilingHandler:
data = json.loads(resp.read())
carrier = data.get("content", {}).get("carrier", {})
# BOC-3 status isn't directly in the API, but we can check
# if authority is active (requires BOC-3 + insurance on file)
common = carrier.get("commonAuthorityStatus", "N")
contract = carrier.get("contractAuthorityStatus", "N")
broker = carrier.get("brokerAuthorityStatus", "N")
common = carrier.get("commonAuthorityStatus")
contract = carrier.get("contractAuthorityStatus")
broker = carrier.get("brokerAuthorityStatus")
statuses = [s for s in (common, contract, broker) if s]
if common == "A" or contract == "A" or broker == "A":
return "Authority active (BOC-3 likely on file)"
result.update(common=common, contract=contract, broker=broker)
# FMCSA codes: A=active, I=inactive, P=pending, N=none/not authorized.
result["any_active"] = any(s == "A" for s in statuses)
result["any_pending"] = any(s == "P" for s in statuses)
result["any_revoked"] = any(s == "I" for s in statuses)
result["has_any_authority"] = any(s in ("A", "I", "P") for s in statuses)
if result["any_active"]:
result["branch"] = "active"
result["summary"] = "Authority active (BOC-3 likely on file)"
elif result["any_pending"]:
result["branch"] = "pending"
result["summary"] = "Authority pending (needs BOC-3 + insurance to activate)"
elif result["any_revoked"]:
result["branch"] = "revoked"
result["summary"] = "Authority revoked/inactive (reinstatement likely needed)"
else:
return "No active authority (BOC-3 may be needed)"
result["branch"] = "none"
result["summary"] = "No operating authority on file (USDOT only)"
except Exception as exc:
return f"Could not check: {exc}"
result["summary"] = f"Could not check: {exc}"
return result
def _send_status_email(self, order_number, entity_name, dot_number, customer_email):
"""Send client an email that we're working on their BOC-3."""