mcs150: census-prefilled intake-completion flow + completeness gate

Closes the data gap for orders that bypass the full intake (e.g. the DOT
compliance-remediation pipeline) and for all MCS-150 variants:

- Worker intake-completeness gate (mcs150_update): before filling, check the
  customer-required operational fields the FMCSA census cannot supply
  (operation classification, cargo, CURRENT annual mileage, email; plus
  signer/address for new-registration/reactivation, and states-of-operation
  for 150B hazmat). If missing, email the customer a census-pre-filled intake
  link and hold the order at fulfillment_status='awaiting_intake' with an admin
  todo, instead of fabricating a blank filing. The existing intake PUT endpoint
  already re-dispatches the worker on submit, so filing auto-resumes.
- Intake wizard (Wizard.astro): when resuming ?order=CO-xxx for a DOT/MCS order,
  seed still-empty fields from the FMCSA census (name/address/fleet/interstate)
  so the customer only confirms the operational details.
- /api/v1/dot/census now also returns total_drivers + a normalized
  carrier_operation_code for the prefill.
- MCS150Step.astro extended to collect every field the filler needs across all
  variants: mailing address, cdl_drivers, primary_vehicle_type,
  reason_for_filing, usdot_revoked, cell/fax, hazmat-safety-permit block
  (needs_hmsp, operating states, security plan), and intermodal-equipment
  provider counts; all prefill from intake_data.

verify_mcs150_variants.py covers 150/150B/150C end-to-end (ALL PASS).
This commit is contained in:
justin 2026-06-10 14:03:28 -05:00
parent 38739e023c
commit a3aeedd716
4 changed files with 439 additions and 2 deletions

View file

@ -124,6 +124,23 @@ class MCS150UpdateHandler:
LOG.info("[%s] Backfilled signer_name from signed certification: %s",
order_number, signed_name)
# INTAKE-COMPLETENESS GATE. The FMCSA census gives us the carrier's
# registered name/address/fleet, but it cannot tell us the operational
# details the MCS-150 requires the filer to confirm/update: the
# operation classification (Q23), cargo types (Q24), current annual
# mileage (Q21 -- must be CURRENT), and a contact email. If those are
# missing we must NOT fabricate a filing; instead we ask the customer to
# complete a short, census-pre-filled intake and hold the order until
# they do. (New-registration / reactivation flows that have not yet
# signed also route through here.)
missing = self._missing_intake_fields(slug, intake)
if missing and not client_approved and not admin_approved:
self._request_intake_completion(
order_number, entity_name, customer_email, dot_number, missing)
LOG.info("[%s] Held for customer intake completion; missing=%s",
order_number, missing)
return []
# Step 1: Fill the official MCS-150 PDF
pdf_path = None
try:
@ -355,6 +372,117 @@ class MCS150UpdateHandler:
return [minio_path] if minio_path else []
def _missing_intake_fields(self, slug: str, intake: dict) -> list:
"""Return the customer-required intake fields still missing for this
service. These are the operational details the FMCSA census cannot
supply and that the MCS-150 requires the filer to confirm/update.
"""
# Per-variant required operational fields.
base_required = ["carrier_operation", "interstate_intrastate",
"annual_miles", "email"]
# Cargo only applies to carriers (not pure intermodal-equipment providers).
if not intake.get("is_intermodal_equipment_provider"):
base_required.append("cargo_types")
# New registrations / reactivations also need the signer + address.
if slug in ("dot-registration", "usdot-reactivation", "dot-full-compliance"):
base_required += ["signer_name", "signer_title", "address_street",
"address_city", "address_state", "address_zip"]
# Hazmat safety-permit (150B) needs states of operation.
if intake.get("hazmat") == "yes" and intake.get("needs_hmsp"):
base_required.append("operating_states")
missing = []
for f in base_required:
v = intake.get(f)
if v in (None, "", [], {}):
missing.append(f)
return missing
def _request_intake_completion(self, order_number, entity_name,
customer_email, dot_number, missing):
"""Email the customer a census-pre-filled intake link and create a
low-priority admin todo noting we're waiting on intake completion."""
domain = os.environ.get("PUBLIC_SITE_URL", "https://performancewest.net").rstrip("/")
intake_url = f"{domain}/order/dot-compliance?order={order_number}"
# Customer email (no paper/mail mechanics; public form names are fine).
try:
from scripts.workers.worker_email import send_worker_email
label = {
"carrier_operation": "how your company operates (for-hire, private, etc.)",
"interstate_intrastate": "interstate vs intrastate operation",
"annual_miles": "your current annual mileage",
"cargo_types": "the types of cargo you haul",
"email": "a contact email address",
"operating_states": "the states you operate in",
"signer_name": "the name of the company officer signing",
"signer_title": "the officer's title",
}
needed = ", ".join(label.get(m, m.replace("_", " ")) for m in missing)
subject = f"Action needed: confirm your MCS-150 details (DOT {dot_number})"
text = (
f"Hi,\n\n"
f"We're ready to prepare the MCS-150 update for {entity_name} "
f"(USDOT {dot_number}). We've pre-filled everything we can from "
f"your current FMCSA record. To finish, we just need you to "
f"confirm a few current details: {needed}.\n\n"
f"Please review and confirm here (it takes about 2 minutes):\n"
f"{intake_url}\n\n"
f"Once you submit, we'll prepare your filing for your signature "
f"and handle the rest.\n\n"
f"Thank you,\nPerformance West"
)
html = (
f"<p>Hi,</p>"
f"<p>We're ready to prepare the MCS-150 update for "
f"<strong>{entity_name}</strong> (USDOT {dot_number}). We've "
f"pre-filled everything we can from your current FMCSA record. "
f"To finish, we just need you to confirm a few current details: "
f"<strong>{needed}</strong>.</p>"
f"<p><a href=\"{intake_url}\">Review and confirm your details</a> "
f"(about 2 minutes).</p>"
f"<p>Once you submit, we'll prepare your filing for your "
f"signature and handle the rest.</p>"
f"<p>Thank you,<br>Performance West</p>"
)
if customer_email:
send_worker_email(customer_email, subject, html, text=text,
cc="justin@performancewest.net")
except Exception as exc:
LOG.warning("[%s] Could not send intake-completion email: %s",
order_number, exc)
# Hold the order + admin todo.
self._set_fulfillment_status(order_number, "awaiting_intake")
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"Awaiting MCS-150 intake -- {entity_name} (DOT {dot_number})",
"intake", "normal", order_number, self.SERVICE_SLUG,
(f"Order {order_number} is missing customer-required intake "
f"fields: {', '.join(missing)}.\n"
f"A census-pre-filled intake link was emailed to "
f"{customer_email or 'the customer'}.\n"
f"Intake link: {intake_url}\n"
f"Filing auto-resumes once the customer submits."),
json.dumps({"order_number": order_number, "dot_number": dot_number,
"entity_name": entity_name, "awaiting_intake": True,
"missing_fields": missing}),
),
)
conn.commit()
conn.close()
except Exception as exc:
LOG.warning("[%s] Could not create intake-pending todo: %s",
order_number, exc)
def _restamp_signed_form(self, order_number: str, document_key: str) -> None:
"""Point the signed esign record at ``document_key`` (the freshly filled
form) and re-stamp the signature onto it, so the signed PDF an admin