new-site/scripts/document_gen/templates/mcs150_form_generator.py
justin e82aa0b8c2 Fix PayPal capture for compliance orders + MCS-150 form generator
PayPal capture was defaulting to canada_crtc_orders table for all
non-formation orders. Now properly routes compliance_batch orders
to compliance_orders table with batch_id lookup. Also infers
order type from ID prefix (CB-=batch, CO-=compliance, FO-=formation).

MCS-150 form generator: produces DOCX with fax cover sheet + filled
MCS-150 form for faxing to FMCSA at 202-366-3477.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 11:34:16 -05:00

412 lines
14 KiB
Python

"""MCS-150 Biennial Update Form Generator.
Generates a filled MCS-150 form as DOCX (converted to PDF) for faxing
to FMCSA at 202-366-3477.
The generated document mirrors the official FMCSA Form MCS-150 layout
with all fields populated from intake data. Includes a cover sheet
with fax instructions.
Usage:
from scripts.document_gen.templates.mcs150_form_generator import generate_mcs150
paths = generate_mcs150(intake_data, order_number="CO-12345")
"""
from __future__ import annotations
import logging
import os
import tempfile
from datetime import datetime
from pathlib import Path
LOG = logging.getLogger("document_gen.mcs150_form")
try:
from docx import Document
from docx.shared import Pt, Inches, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
except ImportError:
LOG.warning("python-docx not installed")
Document = None
NAVY = RGBColor(0x1A, 0x27, 0x44) if Document else None
ORANGE = RGBColor(0xF9, 0x73, 0x16) if Document else None
GRAY = RGBColor(0x64, 0x74, 0x8B) if Document else None
FMCSA_FAX = "202-366-3477"
FMCSA_ADDRESS = (
"Federal Motor Carrier Safety Administration\n"
"Office of Registration and Safety Information\n"
"1200 New Jersey Avenue SE, Room W65-206\n"
"Washington, DC 20590"
)
CARGO_LABELS = {
"general": "General Freight",
"household": "Household Goods",
"metal": "Metal/Sheets/Coils",
"motor_vehicles": "Motor Vehicles",
"drivetow": "Drive/Tow Away",
"logs": "Logs/Poles/Lumber",
"building_materials": "Building Materials",
"mobile_homes": "Mobile Homes",
"machinery": "Machinery/Large Objects",
"fresh_produce": "Fresh Produce",
"liquids": "Liquids/Gases",
"intermodal": "Intermodal Containers",
"passengers": "Passengers",
"oilfield": "Oilfield Equipment",
"livestock": "Livestock",
"grain": "Grain/Feed/Hay",
"coal": "Coal/Coke",
"meat": "Meat",
"garbage": "Garbage/Refuse",
"chemicals": "Chemicals",
"commodities_dry": "Commodities Dry Bulk",
"refrigerated": "Refrigerated Food",
"beverages": "Beverages",
"paper": "Paper Products",
"utilities": "Utility",
"farm_supplies": "Farm Supplies",
"construction": "Construction",
"water_well": "Water Well",
"other": "Other",
}
ENTITY_TYPE_LABELS = {
"sole_proprietorship": "Sole Proprietorship",
"partnership": "Partnership",
"corporation": "Corporation",
"llc": "Limited Liability Company (LLC)",
"other": "Other",
}
CARRIER_OP_LABELS = {
"authorized_for_hire": "Authorized For-Hire",
"exempt_for_hire": "Exempt For-Hire",
"private_property": "Private (Property)",
"private_passengers": "Private (Passengers)",
"us_mail": "U.S. Mail",
"federal_government": "Federal Government",
"state_government": "State Government",
"local_government": "Local Government",
"indian_tribe": "Indian Tribe",
}
INTERSTATE_LABELS = {
"interstate": "Interstate",
"intrastate_hazmat": "Intrastate — Hazmat",
"intrastate_non_hazmat": "Intrastate — Non-Hazmat",
}
def _set_cell(cell, text, bold=False, size=10, color=None):
"""Set cell text with formatting."""
cell.text = ""
p = cell.paragraphs[0]
run = p.add_run(str(text))
run.font.size = Pt(size)
run.font.name = "Calibri"
if bold:
run.font.bold = True
if color:
run.font.color.rgb = color
def _add_field_row(table, label, value, bold_value=False):
"""Add a label-value row to a table."""
row = table.add_row()
_set_cell(row.cells[0], label, bold=True, size=9, color=GRAY)
_set_cell(row.cells[1], value or "", bold=bold_value, size=10)
return row
def generate_mcs150(intake: dict, order_number: str = "") -> list[str]:
"""Generate MCS-150 form document.
Args:
intake: Dict with all MCS-150 fields from the intake form.
order_number: Order number for reference.
Returns:
List of generated file paths (DOCX).
"""
if Document is None:
LOG.error("python-docx not installed")
return []
doc = Document()
# Page setup
section = doc.sections[0]
section.page_width = Inches(8.5)
section.page_height = Inches(11)
section.top_margin = Inches(0.75)
section.bottom_margin = Inches(0.5)
section.left_margin = Inches(0.75)
section.right_margin = Inches(0.75)
# ── Cover Sheet ──────────────────────────────────────────────────
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run("FAX COVER SHEET")
run.font.size = Pt(18)
run.font.bold = True
run.font.color.rgb = NAVY
p2 = doc.add_paragraph()
p2.alignment = WD_ALIGN_PARAGRAPH.CENTER
run2 = p2.add_run("MCS-150 BIENNIAL UPDATE")
run2.font.size = Pt(14)
run2.font.bold = True
run2.font.color.rgb = ORANGE
doc.add_paragraph("")
# Fax details table
cover_table = doc.add_table(rows=0, cols=2)
cover_table.columns[0].width = Inches(1.5)
cover_table.columns[1].width = Inches(5.5)
for label, value in [
("TO:", "FMCSA — Office of Registration and Safety Information"),
("FAX:", FMCSA_FAX),
("FROM:", "Performance West Inc."),
("DATE:", datetime.now().strftime("%B %d, %Y")),
("RE:", f"MCS-150 Biennial Update — {intake.get('legal_name', '')} (DOT# {intake.get('dot_number', '')})"),
("ORDER:", order_number),
("PAGES:", "2 (including cover)"),
]:
row = cover_table.add_row()
_set_cell(row.cells[0], label, bold=True, size=11)
_set_cell(row.cells[1], value, size=11)
doc.add_paragraph("")
note = doc.add_paragraph()
run_note = note.add_run(
"Please process the attached MCS-150 Biennial Update for the carrier identified above. "
"This filing is submitted on behalf of the carrier by Performance West Inc., "
"an authorized compliance consulting firm. "
"The carrier has authorized this filing as part of their compliance service agreement."
)
run_note.font.size = Pt(10)
run_note.font.color.rgb = GRAY
doc.add_paragraph("")
contact = doc.add_paragraph()
run_c = contact.add_run(
"Performance West Inc.\n"
"525 Randall Ave Ste 100-1195, Cheyenne, WY 82001\n"
"Phone: (888) 411-0383 | Fax: (888) 411-0383\n"
"info@performancewest.net"
)
run_c.font.size = Pt(9)
run_c.font.color.rgb = GRAY
# Page break for the actual form
doc.add_page_break()
# ── MCS-150 Form ─────────────────────────────────────────────────
header = doc.add_paragraph()
header.alignment = WD_ALIGN_PARAGRAPH.CENTER
h_run = header.add_run("FORM MCS-150 — MOTOR CARRIER IDENTIFICATION REPORT")
h_run.font.size = Pt(14)
h_run.font.bold = True
h_run.font.color.rgb = NAVY
sub = doc.add_paragraph()
sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
s_run = sub.add_run("BIENNIAL UPDATE")
s_run.font.size = Pt(11)
s_run.font.color.rgb = ORANGE
s_run.font.bold = True
doc.add_paragraph("")
# Section A: Entity Information
sec_a = doc.add_paragraph()
run_a = sec_a.add_run("SECTION A: ENTITY INFORMATION")
run_a.font.size = Pt(11)
run_a.font.bold = True
run_a.font.color.rgb = NAVY
table_a = doc.add_table(rows=0, cols=2)
table_a.columns[0].width = Inches(2.5)
table_a.columns[1].width = Inches(4.5)
_add_field_row(table_a, "1. Legal Entity Name", intake.get("legal_name", ""), bold_value=True)
_add_field_row(table_a, "2. DBA / Trade Name", intake.get("dba_name", ""))
_add_field_row(table_a, "USDOT Number", intake.get("dot_number", ""), bold_value=True)
_add_field_row(table_a, "MC/MX/FF Number", intake.get("mc_number", ""))
address = f"{intake.get('address_street', '')}"
city_state_zip = f"{intake.get('address_city', '')}, {intake.get('address_state', '')} {intake.get('address_zip', '')}"
_add_field_row(table_a, "3-6. Principal Address", address)
_add_field_row(table_a, "", city_state_zip)
_add_field_row(table_a, "Phone", intake.get("phone", ""))
doc.add_paragraph("")
# Section B: Entity Type & Operations
sec_b = doc.add_paragraph()
run_b = sec_b.add_run("SECTION B: ENTITY TYPE & OPERATIONS")
run_b.font.size = Pt(11)
run_b.font.bold = True
run_b.font.color.rgb = NAVY
table_b = doc.add_table(rows=0, cols=2)
table_b.columns[0].width = Inches(2.5)
table_b.columns[1].width = Inches(4.5)
entity_type = ENTITY_TYPE_LABELS.get(intake.get("entity_type", ""), intake.get("entity_type", ""))
carrier_op = CARRIER_OP_LABELS.get(intake.get("carrier_operation", ""), intake.get("carrier_operation", ""))
interstate = INTERSTATE_LABELS.get(intake.get("interstate_intrastate", ""), intake.get("interstate_intrastate", ""))
hazmat = "Yes" if intake.get("hazmat") == "yes" else "No"
_add_field_row(table_b, "Entity Type", entity_type)
_add_field_row(table_b, "Carrier Operation", carrier_op)
_add_field_row(table_b, "Interstate/Intrastate", interstate)
_add_field_row(table_b, "Hazardous Materials", hazmat, bold_value=(hazmat == "Yes"))
doc.add_paragraph("")
# Section C: Fleet Information
sec_c = doc.add_paragraph()
run_c2 = sec_c.add_run("SECTION C: FLEET INFORMATION")
run_c2.font.size = Pt(11)
run_c2.font.bold = True
run_c2.font.color.rgb = NAVY
table_c = doc.add_table(rows=0, cols=2)
table_c.columns[0].width = Inches(2.5)
table_c.columns[1].width = Inches(4.5)
power_units = intake.get("power_units", "")
drivers = intake.get("drivers", "")
miles = intake.get("annual_miles", "")
if miles:
try:
miles = f"{int(miles):,}"
except (ValueError, TypeError):
pass
_add_field_row(table_c, "Number of Power Units", str(power_units), bold_value=True)
_add_field_row(table_c, "Number of Drivers", str(drivers), bold_value=True)
_add_field_row(table_c, "Annual Miles (last 12 mo.)", str(miles) if miles else "")
doc.add_paragraph("")
# Section D: Cargo Types
sec_d = doc.add_paragraph()
run_d = sec_d.add_run("SECTION D: CARGO CLASSIFICATIONS")
run_d.font.size = Pt(11)
run_d.font.bold = True
run_d.font.color.rgb = NAVY
cargo_types = intake.get("cargo_types", [])
if cargo_types:
cargo_labels = [CARGO_LABELS.get(c, c.replace("_", " ").title()) for c in cargo_types]
cargo_p = doc.add_paragraph()
cargo_run = cargo_p.add_run(", ".join(cargo_labels))
cargo_run.font.size = Pt(10)
else:
cargo_p = doc.add_paragraph()
cargo_run = cargo_p.add_run("None specified")
cargo_run.font.size = Pt(10)
cargo_run.font.color.rgb = GRAY
doc.add_paragraph("")
# Section E: Certification
sec_e = doc.add_paragraph()
run_e = sec_e.add_run("SECTION E: CERTIFICATION")
run_e.font.size = Pt(11)
run_e.font.bold = True
run_e.font.color.rgb = NAVY
cert_text = doc.add_paragraph()
cert_run = cert_text.add_run(
"I certify that the information contained in this report is accurate and complete "
"to the best of my knowledge and belief. I understand that making a false statement "
"or misrepresentation on this form may subject the filer to criminal penalties under "
"18 U.S.C. § 1001."
)
cert_run.font.size = Pt(9)
cert_run.font.italic = True
cert_run.font.color.rgb = GRAY
doc.add_paragraph("")
table_e = doc.add_table(rows=0, cols=2)
table_e.columns[0].width = Inches(2.5)
table_e.columns[1].width = Inches(4.5)
signer_name = intake.get("signer_name", "")
signer_title = intake.get("signer_title", "")
_add_field_row(table_e, "Authorized Signer", signer_name, bold_value=True)
_add_field_row(table_e, "Title", signer_title)
_add_field_row(table_e, "Date", datetime.now().strftime("%B %d, %Y"))
doc.add_paragraph("")
# Signature line
sig = doc.add_paragraph()
sig_run = sig.add_run("_" * 50)
sig_run.font.size = Pt(10)
sig2 = doc.add_paragraph()
sig2_run = sig2.add_run("Signature of Authorized Official")
sig2_run.font.size = Pt(9)
sig2_run.font.color.rgb = GRAY
# Footer
doc.add_paragraph("")
footer = doc.add_paragraph()
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
f_run = footer.add_run(
f"Prepared by Performance West Inc. | Order {order_number} | {datetime.now().strftime('%Y-%m-%d')}"
)
f_run.font.size = Pt(8)
f_run.font.color.rgb = GRAY
# Save
work_dir = tempfile.mkdtemp(prefix="pw_mcs150_")
date_str = datetime.now().strftime("%Y%m%d")
dot = intake.get("dot_number", "unknown")
filename = f"MCS150_DOT{dot}_{date_str}.docx"
filepath = os.path.join(work_dir, filename)
doc.save(filepath)
LOG.info("Generated MCS-150 form: %s", filepath)
return [filepath]
if __name__ == "__main__":
# Test with sample data
test_intake = {
"legal_name": "ADAMS LUMBER INC",
"dba_name": "Adams Trucking",
"dot_number": "1157913",
"mc_number": "MC-456789",
"address_street": "123 Timber Lane",
"address_city": "Portland",
"address_state": "OR",
"address_zip": "97201",
"phone": "(503) 555-1234",
"entity_type": "corporation",
"carrier_operation": "authorized_for_hire",
"interstate_intrastate": "interstate",
"hazmat": "no",
"power_units": "5",
"drivers": "6",
"annual_miles": "250000",
"cargo_types": ["general", "building_materials", "logs"],
"signer_name": "Mark Adams",
"signer_title": "President",
}
paths = generate_mcs150(test_intake, order_number="CO-TEST123")
print(f"Generated: {paths}")