mcs150: enrich intake from FMCSA carrier census before PDF fill
The MCS-150 biennial update re-confirms the carrier's existing FMCSA record. Previously the PDF filler only had whatever the intake form collected; rescued/sparse orders (or orders where the carrier's data lives in FMCSA, not the intake) produced near-empty forms. Now we pull the carrier census (legal name, address, EIN, fleet counts) from the FMCSA carrier API and merge it under any customer-provided intake values (customer edits win), so the form is pre-filled with the carrier's current registered data. Refactored the FMCSA fetch into a shared _fetch_fmcsa_carrier helper used by both enrichment and status check.
This commit is contained in:
parent
7e5946d65a
commit
d5e66786a2
8 changed files with 294 additions and 15 deletions
|
|
@ -97,6 +97,22 @@ class MCS150UpdateHandler:
|
|||
# Check current MCS-150 status via FMCSA API
|
||||
mcs150_status = self._check_current_status(dot_number)
|
||||
|
||||
# Enrich the intake with the carrier's CURRENT registered data from the
|
||||
# FMCSA carrier API. The MCS-150 biennial update re-confirms the carrier's
|
||||
# existing FMCSA record, so the authoritative source for the form is the
|
||||
# FMCSA census (legal name, address, EIN, fleet counts) -- the intake form
|
||||
# only collects the DOT number + any changes. Customer-provided intake
|
||||
# values take precedence over the census (so edits/changes win).
|
||||
census = self._fetch_carrier_record(dot_number)
|
||||
if census:
|
||||
merged = {**census, **{k: v for k, v in intake.items() if v not in (None, "")}}
|
||||
intake = merged
|
||||
LOG.info("[%s] Enriched intake from FMCSA census (legal_name=%s)",
|
||||
order_number, intake.get("legal_name"))
|
||||
else:
|
||||
LOG.warning("[%s] No FMCSA census data for DOT %s -- form may be sparse",
|
||||
order_number, dot_number)
|
||||
|
||||
# Step 1: Fill the official MCS-150 PDF
|
||||
pdf_path = None
|
||||
try:
|
||||
|
|
@ -321,30 +337,59 @@ class MCS150UpdateHandler:
|
|||
|
||||
return [minio_path] if minio_path else []
|
||||
|
||||
def _check_current_status(self, dot_number: str) -> str:
|
||||
"""Check current MCS-150 status via FMCSA API."""
|
||||
def _fetch_fmcsa_carrier(self, dot_number: str) -> dict:
|
||||
"""Fetch the raw FMCSA carrier census record for a DOT number."""
|
||||
try:
|
||||
import urllib.request
|
||||
api_key = os.environ.get("FMCSA_API_KEY", "")
|
||||
if not api_key:
|
||||
return "API key not configured"
|
||||
|
||||
return {}
|
||||
url = f"https://mobile.fmcsa.dot.gov/qc/services/carriers/{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", {})
|
||||
outdated = carrier.get("mcs150Outdated", "?")
|
||||
status = carrier.get("statusCode", "?")
|
||||
allowed = carrier.get("allowedToOperate", "?")
|
||||
|
||||
return (
|
||||
f"Status: {status}, Allowed: {allowed}, "
|
||||
f"MCS-150 Outdated: {outdated}"
|
||||
)
|
||||
return data.get("content", {}).get("carrier", {}) or {}
|
||||
except Exception as exc:
|
||||
return f"Could not check: {exc}"
|
||||
LOG.warning("FMCSA carrier fetch failed for %s: %s", dot_number, exc)
|
||||
return {}
|
||||
|
||||
def _fetch_carrier_record(self, dot_number: str) -> dict:
|
||||
"""Return the carrier's current FMCSA data mapped to the intake keys the
|
||||
PDF filler expects (so the biennial-update form is pre-filled with the
|
||||
carrier's existing registered data). Empty dict if unavailable.
|
||||
"""
|
||||
c = self._fetch_fmcsa_carrier(dot_number)
|
||||
if not c:
|
||||
return {}
|
||||
|
||||
def s(v):
|
||||
return "" if v is None else str(v)
|
||||
|
||||
out: dict = {
|
||||
"legal_name": s(c.get("legalName")),
|
||||
"dba_name": s(c.get("dbaName")),
|
||||
"dot_number": s(c.get("dotNumber") or dot_number),
|
||||
"ein": s(c.get("ein")),
|
||||
"address_street": s(c.get("phyStreet")),
|
||||
"address_city": s(c.get("phyCity")),
|
||||
"address_state": s(c.get("phyState")),
|
||||
"address_zip": s(c.get("phyZipcode")),
|
||||
"phone": s(c.get("telephone") or c.get("phone")),
|
||||
"power_units": s(c.get("totalPowerUnits")),
|
||||
"drivers": s(c.get("totalDrivers")),
|
||||
}
|
||||
return {k: v for k, v in out.items() if v}
|
||||
|
||||
def _check_current_status(self, dot_number: str) -> str:
|
||||
"""Check current MCS-150 status via FMCSA API."""
|
||||
c = self._fetch_fmcsa_carrier(dot_number)
|
||||
if not c:
|
||||
return "Could not check FMCSA status"
|
||||
return (
|
||||
f"Status: {c.get('statusCode', '?')}, "
|
||||
f"Allowed: {c.get('allowedToOperate', '?')}, "
|
||||
f"MCS-150 Outdated: {c.get('mcs150Outdated', '?')}"
|
||||
)
|
||||
|
||||
def _create_pending_signature_todo(self, order_number, entity_name, dot_number,
|
||||
slug, minio_path, customer_email):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue