new-site/scripts/workers/services/boc3_filing.py
justin 33da00fd89 50-state trucking compliance: services, checker, order page, CA landing
- Migration 079: state_trucking_requirements table seeded for all 51 jurisdictions
  (IRP, IFTA, weight-distance taxes, MCP/CARB, intrastate authority, state DOT)
- Migration 080: carrier_operating_states tracking table
- 13 new state trucking services in catalog ($99-$599)
- StateTruckingHandler with state-specific admin todos
- DOT compliance checker: 7 new state-level checks (IRP, IFTA, weight tax,
  MCP/CARB, emissions, intrastate authority, state DOT number)
- New API endpoint: GET /api/v1/dot/state-requirements
- DOT order page: state compliance service cards with auto-preselect
- California trucking landing page (MCP + CARB + IRP + IFTA)
- Fix: DOT checker nav missing Trucking/DOT section
- Fix: All 8 DOT intake pages missing style block (dangling text)
- Fix: DOT confirmation email now says "Order Confirmed" not "Action Required"
- Fix: MCS150/BOC3/StateTrucking handlers missing async process() method
- Fix: StateTruckingHandler connection leak + slug resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 12:46:33 -05:00

272 lines
11 KiB
Python

"""
BOC-3 Process Agent Designation Service Handler.
The BOC-3 designates a process agent in every US state who can accept
legal documents on behalf of a motor carrier. The process agent (not
the carrier) files the form with FMCSA.
Model: Performance West partners with a blanket process agent service
(e.g., NWRA or similar) who covers all 48 contiguous states + DC.
We collect the carrier's info, submit the designation to our process
agent partner, they file the BOC-3 electronically with FMCSA.
Service slug: boc3-filing
Price: $149
Gov fee: $0
Intake data needed:
- DOT number
- MC/FF/MX number (docket number)
- Legal name
- DBA name (if any)
- Business address
- Phone number
- Email
- Entity type (carrier, broker, freight forwarder)
Filing flow:
1. Client orders BOC-3 filing
2. We collect intake data
3. We submit designation request to process agent partner
4. Process agent files BOC-3 electronically with FMCSA
5. We verify filing on FMCSA L&I system
6. We send confirmation to client
"""
from __future__ import annotations
import json
import logging
import os
from datetime import datetime
LOG = logging.getLogger("workers.services.boc3_filing")
# Process agent partner details
# NWRA (Northwest Registered Agent) confirmed they no longer offer BOC-3.
# Registered Agents Inc is the replacement vendor (contract pending).
# Wholesale cost: ~$25 or less. Manual fulfillment until API is available.
PROCESS_AGENT_PARTNER = {
"name": "Registered Agents Inc",
"contact_email": "", # Update when contract is executed
"api_endpoint": None, # Will be set when RAI API is available
}
class BOC3FilingHandler:
"""Handle BOC-3 process agent designation orders."""
SERVICE_SLUG = "boc3-filing"
SERVICE_NAME = "BOC-3 Process Agent Filing"
async def process(self, order_data: dict) -> list[str]:
"""Entry point called by job_server. Delegates to handle()."""
order_number = order_data.get("order_number", order_data.get("name", ""))
return self.handle(order_data, order_number)
def handle(self, order_data: dict, order_number: str) -> list[str]:
"""
Process a BOC-3 filing order.
Currently creates an admin todo. When process agent partner API
is available, this will automate the submission.
"""
LOG.info("[%s] Processing BOC-3 filing order", order_number)
intake = order_data.get("intake_data") or {}
if isinstance(intake, str):
intake = json.loads(intake)
dot_number = intake.get("dot_number", "")
docket_number = intake.get("docket_number", "") # MC-XXXXXX
entity_name = intake.get("entity_name", order_data.get("customer_name", ""))
customer_email = order_data.get("customer_email", "")
entity_type = intake.get("entity_type", "carrier") # carrier, broker, freight_forwarder
if not dot_number:
LOG.error("[%s] Missing DOT number", order_number)
return []
# Check current BOC-3 status
boc3_status = self._check_boc3_status(dot_number)
# Build the designation request
designation = {
"dot_number": dot_number,
"docket_number": docket_number,
"legal_name": entity_name,
"dba_name": intake.get("dba_name", ""),
"business_address": {
"street": intake.get("address_street", ""),
"city": intake.get("address_city", ""),
"state": intake.get("address_state", ""),
"zip": intake.get("address_zip", ""),
},
"phone": intake.get("phone", ""),
"email": customer_email,
"entity_type": entity_type,
"requested_at": datetime.utcnow().isoformat(),
}
# If we have a process agent API, submit directly
if PROCESS_AGENT_PARTNER.get("api_endpoint"):
success = self._submit_to_process_agent(designation)
if success:
self._send_confirmation_email(order_number, entity_name, dot_number, customer_email)
return []
# Otherwise create admin todo
todo_data = {
"order_number": order_number,
"service": self.SERVICE_NAME,
"designation": designation,
"current_boc3_status": boc3_status,
"steps": [
"1. Submit BOC-3 designation request to process agent partner",
f" Partner: {PROCESS_AGENT_PARTNER['name']}",
f" Email: {PROCESS_AGENT_PARTNER.get('contact_email', 'TBD')}",
"2. Include: DOT#, MC#, legal name, address for all 48 states + DC",
"3. Process agent files BOC-3 electronically with FMCSA",
"4. Verify filing at https://li-public.fmcsa.dot.gov/LIVIEW/pkg_carrquery.prc_carrlist",
"5. Send confirmation to client",
],
}
try:
import psycopg2
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
with conn.cursor() as cur:
cur.execute("""
INSERT INTO admin_todos (
title, category, priority, order_number, service_slug,
description, data, status
) VALUES (%s, %s, %s, %s, %s, %s, %s, 'pending')
""", (
f"BOC-3 Filing — {entity_name} (DOT {dot_number})",
"filing",
"high",
order_number,
self.SERVICE_SLUG,
f"File BOC-3 process agent designation for {entity_name}.\n"
f"DOT: {dot_number}\n"
f"MC/Docket: {docket_number}\n"
f"Type: {entity_type}\n"
f"Customer: {customer_email}\n\n"
f"Submit to process agent partner for electronic filing with FMCSA.",
json.dumps(todo_data),
))
conn.commit()
conn.close()
LOG.info("[%s] Admin todo created for BOC-3 filing", order_number)
except Exception as exc:
LOG.error("[%s] Failed to create admin todo: %s", order_number, exc)
# Send status email
self._send_status_email(order_number, entity_name, dot_number, customer_email)
return []
def _check_boc3_status(self, dot_number: str) -> str:
"""Check if carrier has a BOC-3 on file via FMCSA API."""
try:
import urllib.request
api_key = os.environ.get("FMCSA_API_KEY", "")
if not api_key:
return "API key not configured"
url = (
f"https://mobile.fmcsa.dot.gov/qc/services/carriers/"
f"{dot_number}?webKey={api_key}"
)
req = urllib.request.Request(url, headers={"Accept": "application/json"})
with urllib.request.urlopen(req, timeout=10) as resp:
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")
if common == "A" or contract == "A" or broker == "A":
return "Authority active (BOC-3 likely on file)"
else:
return "No active authority (BOC-3 may be needed)"
except Exception as exc:
return f"Could not check: {exc}"
def _submit_to_process_agent(self, designation: dict) -> bool:
"""Submit designation to process agent partner via API."""
# TODO: Implement when partner API is available
LOG.warning("Process agent API not configured — falling back to admin todo")
return False
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."""
if not customer_email:
return
try:
import smtplib
from email.mime.text import MIMEText
body = (
f"Hi,\n\n"
f"We've received your BOC-3 process agent designation order for "
f"{entity_name} (DOT# {dot_number}).\n\n"
f"Order: {order_number}\n\n"
f"We're submitting your designation to our blanket process agent "
f"who covers all 48 contiguous states plus DC. Once filed with "
f"FMCSA, your operating authority will reflect the active BOC-3.\n\n"
f"This is typically completed within 1-2 business days.\n\n"
f"Questions? Reply to this email or call (888) 411-0383.\n\n"
f"Performance West Inc.\n"
f"DOT Compliance Services\n"
)
msg = MIMEText(body)
msg["Subject"] = f"BOC-3 Filing In Progress — {entity_name} (DOT {dot_number})"
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Status email sent to %s", order_number, customer_email)
except Exception as exc:
LOG.warning("[%s] Failed to send status email: %s", order_number, exc)
def _send_confirmation_email(self, order_number, entity_name, dot_number, customer_email):
"""Send confirmation that BOC-3 has been filed."""
if not customer_email:
return
try:
import smtplib
from email.mime.text import MIMEText
body = (
f"Hi,\n\n"
f"Your BOC-3 process agent designation has been filed with FMCSA "
f"for {entity_name} (DOT# {dot_number}).\n\n"
f"Order: {order_number}\n\n"
f"Your process agent is now designated in all 48 contiguous states "
f"plus the District of Columbia. This designation remains active "
f"as long as your carrier account is maintained.\n\n"
f"You can verify your BOC-3 status at:\n"
f"https://li-public.fmcsa.dot.gov/LIVIEW/pkg_carrquery.prc_carrlist\n\n"
f"Questions? Reply to this email or call (888) 411-0383.\n\n"
f"Performance West Inc.\n"
f"DOT Compliance Services\n"
)
msg = MIMEText(body)
msg["Subject"] = f"BOC-3 Filed — {entity_name} (DOT {dot_number})"
msg["From"] = "noreply@performancewest.net"
msg["To"] = customer_email
with smtplib.SMTP("localhost", 25) as s:
s.sendmail(msg["From"], [customer_email], msg.as_string())
LOG.info("[%s] Confirmation email sent to %s", order_number, customer_email)
except Exception as exc:
LOG.warning("[%s] Failed to send confirmation email: %s", order_number, exc)