feat(sc-coc): SC intrastate Certificate of Compliance flow (insurance gate -> $25 fee -> file)

Routes SC intrastate-authority orders to the real SCDMV COC product instead of a
PSC certificate (which doesn't apply to property carriers):

  - sc_coc_filing.py: emails the carrier a one-click yes/no — does your insurer
    have / can they file a Form E (SC intrastate liability, $750k or $300k by
    GVWR) with SCDMV? Records the answer; builds the filled COC package.
  - state_trucking._handle_sc_coc_gate: SC intrastate gate —
      no answer  -> email the question once, HOLD
      answered no -> broker referral opened, HOLD (ops todo)
      answered yes-> proceed to bill the exact $25 SCDMV COC fee (at cost) + file
  - API POST /compliance-orders/:id/sc-insurance: records yes/no in intake_data
    (no schema change); NO opens an insurance_lead broker-referral ticket +
    Telegram; YES re-dispatches the worker to bill the $25 + file.
  - site/order/sc-insurance: customer one-click yes/no page (auto-submits when
    the email links straight to ?have=yes|no).

Non-SC intrastate still uses the PSC/PUC email path or a manual todo.
This commit is contained in:
justin 2026-06-16 09:15:55 -05:00
parent dae9603808
commit c46efe5730
4 changed files with 526 additions and 30 deletions

View file

@ -0,0 +1,164 @@
"""South Carolina intrastate Certificate of Compliance (COC) flow.
For-hire PROPERTY carriers based in / operating intrastate in SC register via the
SCDMV Certificate of Compliance (COC) NOT a PSC certificate (which only covers
passenger, household-goods, and hazardous-waste-for-disposal carriers).
Compliance steps we automate for an SC intrastate property carrier:
1. Confirm the carrier's liability insurance is (or will be) filed with SCDMV
on a Form E by their INSURANCE COMPANY (SCDMV does not accept an ACORD cert).
We email a simple yes/no question with a one-click response page.
- YES -> proceed to bill the $25 COC fee + file the COC application.
- NO -> open a broker-referral ticket so we connect them with an insurer
that writes SC intrastate liability and can file the Form E.
2. Bill the exact $25 SCDMV COC new-application fee at cost (gov-fee child),
reusing the standard payment-link flow.
3. Fill + submit the SCDMV Form COC (mail w/ $25 check to Blythewood, or fax),
and confirm the Form E is on record.
Coverage class (drives whether cargo insurance / Form H is needed, and is the
basis for the state fee determination):
- E-L : low-value commodities (scrap metal, dump-truck aggregates) Form E
liability only, no cargo insurance.
- E-LC : property properly insured for any cargo Form E + Form H.
Insurance minimums (intrastate, non-hazmat liability):
- GVWR >= 10,000 lbs: $750,000
- GVWR < 10,000 lbs: $300,000
"""
from __future__ import annotations
import json
import logging
import os
import smtplib
from email.mime.text import MIMEText
LOG = logging.getLogger("workers.services.sc_coc_filing")
SITE = os.getenv("PUBLIC_SITE_URL", "https://performancewest.net").rstrip("/")
SMTP_HOST = os.getenv("SMTP_HOST", "co.carrierone.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASS = os.getenv("SMTP_PASS", "")
SMTP_FROM = os.getenv("SMTP_FROM", "Performance West <noreply@performancewest.net>")
FROM_ADDR = "noreply@performancewest.net"
# SCDMV COC submission coordinates (from SCDMV Form COC, rev 11/2025).
SCDMV_COC = {
"mail": "SCDMV, P.O. Box 1498, Blythewood, SC 29016-0027",
"fax": "(803) 896-2698",
"phone": "(803) 896-3870",
"new_fee_usd": 25,
}
def liability_minimum_usd(intake: dict) -> int:
"""SC intrastate non-hazmat liability minimum by GVWR bracket."""
bracket = (intake.get("gross_weight_bracket") or "").lower()
# Treat unknown / 26k / 80k brackets as the >=10k bucket (the common case).
if bracket in ("under_10k", "lt_10k"):
return 300_000
return 750_000
def insurance_response_url(order_number: str, answer: str) -> str:
"""One-click yes/no link the customer clicks from the email."""
return f"{SITE}/order/sc-insurance?order={order_number}&have={answer}"
def send_insurance_question_email(order_number: str, customer_email: str,
customer_name: str, entity_name: str,
intake: dict) -> bool:
"""Email the carrier a simple yes/no: do they have SC intrastate liability
insurance their insurer can file on a Form E with SCDMV?"""
if not customer_email:
return False
minimum = liability_minimum_usd(intake)
yes_url = insurance_response_url(order_number, "yes")
no_url = insurance_response_url(order_number, "no")
body = (
f"Hi {customer_name or 'there'},\n\n"
f"We're getting your South Carolina intrastate authority set up for "
f"{entity_name}. In SC, a for-hire carrier like yours registers with the "
f"SCDMV (Certificate of Compliance) — and the one thing the state requires "
f"from your insurance company is a liability filing called a \"Form E.\"\n\n"
f"Quick question so we know how to proceed:\n\n"
f"Does your current insurance company have (or can they file) a Form E "
f"showing at least ${minimum:,} in liability coverage for South Carolina "
f"intrastate operation?\n\n"
f"(Note: this must be a Form E filed by your insurance COMPANY directly "
f"with SCDMV — a regular ACORD certificate of insurance is not accepted.)\n\n"
f"✅ YES — my insurer can file the Form E:\n{yes_url}\n\n"
f"❌ NO / NOT SURE — I need help getting the right insurance:\n{no_url}\n\n"
f"If you click NO, no problem — we'll connect you with a broker who writes "
f"SC intrastate trucking liability and can file the Form E for you.\n\n"
f"Once your Form E is on file, we complete your SCDMV Certificate of "
f"Compliance and you're cleared to operate.\n\n"
f"Order: {order_number}\n"
f"Questions? Just reply here or call (888) 411-0383.\n\n"
f"Performance West Inc.\nDOT / State Motor Carrier Compliance\n"
)
try:
msg = MIMEText(body)
msg["Subject"] = f"Quick question about your SC insurance — {entity_name}"
msg["From"] = SMTP_FROM
msg["To"] = customer_email
with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30) as s:
s.starttls()
if SMTP_USER and SMTP_PASS:
s.login(SMTP_USER, SMTP_PASS)
s.sendmail(FROM_ADDR, [customer_email], msg.as_string())
LOG.info("[%s] SC insurance question emailed to %s", order_number, customer_email)
return True
except Exception as exc: # noqa: BLE001
LOG.error("[%s] Failed to send SC insurance question: %s", order_number, exc)
return False
def record_insurance_answer(order_number: str, have_insurance: bool) -> bool:
"""Persist the carrier's yes/no into intake_data.sc_coc_insurance. Returns
True on success. Idempotent."""
try:
import psycopg2
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
with conn.cursor() as cur:
cur.execute(
"SELECT intake_data FROM compliance_orders WHERE order_number = %s",
(order_number,),
)
row = cur.fetchone()
if not row:
conn.close()
return False
intake = row[0] or {}
if isinstance(intake, str):
intake = json.loads(intake)
intake["sc_coc_insurance"] = "yes" if have_insurance else "no"
from datetime import datetime, timezone
intake["sc_coc_insurance_at"] = datetime.now(timezone.utc).isoformat()
cur.execute(
"UPDATE compliance_orders SET intake_data = %s, updated_at = now() "
"WHERE order_number = %s",
(json.dumps(intake), order_number),
)
conn.commit()
conn.close()
LOG.info("[%s] Recorded SC COC insurance answer: %s", order_number,
"yes" if have_insurance else "no")
return True
except Exception as exc: # noqa: BLE001
LOG.error("[%s] Failed to record insurance answer: %s", order_number, exc)
return False
def build_coc_package(order_number: str, intake: dict,
coverage_class: str | None = None) -> str | None:
"""Fill the SCDMV Form COC for this order. Returns the filled PDF path."""
try:
from scripts.document_gen.templates.sc_coc_pdf_filler import fill_sc_coc
return fill_sc_coc(intake, order_number=order_number, coverage_class=coverage_class)
except Exception as exc: # noqa: BLE001
LOG.error("[%s] Failed to build COC package: %s", order_number, exc)
return None

View file

@ -713,6 +713,94 @@ class StateTruckingHandler:
LOG.warning("[%s] Could not check gov-fee settlement: %s", order_number, exc)
return False
def _handle_sc_coc_gate(self, order_number, entity_name, customer_email,
customer_phone, intake) -> bool:
"""SC intrastate Certificate of Compliance insurance gate.
SC for-hire property carriers register via the SCDMV COC, which requires
their INSURER to file a Form E (liability). We ask the carrier a one-click
yes/no first:
- no answer yet -> email the question, HOLD (return True)
- answered 'no' -> broker referral is in progress, HOLD (return True)
- answered 'yes' -> insurance confirmed, PROCEED to bill the $25 COC
fee (return False so the caller falls through to fee billing)
"""
answer = str(intake.get("sc_coc_insurance", "")).lower()
if answer == "yes":
LOG.info("[%s] SC COC: insurance confirmed — proceeding to $25 COC fee", order_number)
return False # proceed to bill the COC fee
if answer == "no":
# Broker referral opened (by the response endpoint). Hold until ops
# gets them covered, then they re-answer yes.
LOG.info("[%s] SC COC: carrier needs insurance — broker referral, holding", order_number)
try:
notify_fulfillment_todo(
title=f"SC COC — carrier needs insurance broker — {entity_name}",
order_number=order_number,
service_slug="intrastate-authority",
priority="normal",
description=(f"{entity_name} answered NO to the SC intrastate Form E "
f"insurance question. A broker-referral ticket was opened.\n"
f"Connect them with an SC intrastate liability insurer who can "
f"file a Form E with SCDMV, then they re-confirm and we file the "
f"$25 Certificate of Compliance.\nCustomer: {customer_email}"),
)
except Exception:
pass
return True # hold
# No answer yet: send the yes/no insurance question (once).
try:
from scripts.workers.services.sc_coc_filing import send_insurance_question_email
except Exception as exc: # noqa: BLE001
LOG.error("[%s] sc_coc_filing import failed: %s", order_number, exc)
return True # hold safely rather than mis-bill
already = str(intake.get("sc_coc_insurance_asked", "")).lower() in ("1", "true", "yes")
if not already:
send_insurance_question_email(order_number, customer_email,
intake.get("customer_name", ""), entity_name, intake)
self._mark_intake_flag(order_number, "sc_coc_insurance_asked", "true")
try:
notify_fulfillment_todo(
title=f"SC COC — insurance question sent — {entity_name}",
order_number=order_number,
service_slug="intrastate-authority",
priority="low",
description=(f"Asked {entity_name} whether their insurer can file a Form E "
f"for SC intrastate. When they confirm YES we bill the $25 SCDMV "
f"Certificate of Compliance fee and file; if NO we open a broker "
f"referral.\nCustomer: {customer_email}"),
)
except Exception:
pass
return True # hold pending their answer
def _mark_intake_flag(self, order_number: str, key: str, value: str) -> None:
"""Best-effort set intake_data[key]=value on the order (no schema change)."""
try:
import json as _json
import psycopg2
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
with conn.cursor() as cur:
cur.execute("SELECT intake_data FROM compliance_orders WHERE order_number = %s",
(order_number,))
row = cur.fetchone()
if not row:
conn.close()
return
data = row[0] or {}
if isinstance(data, str):
data = _json.loads(data)
data[key] = value
cur.execute("UPDATE compliance_orders SET intake_data = %s, updated_at = now() "
"WHERE order_number = %s", (_json.dumps(data), order_number))
conn.commit()
conn.close()
except Exception as exc: # noqa: BLE001
LOG.warning("[%s] Could not set intake flag %s: %s", order_number, key, exc)
def _request_gov_fee_payment(self, order_number, service_slug, service_name,
entity_name, customer_email, customer_phone, intake,
signed_auth_key=None) -> bool:
@ -751,39 +839,53 @@ class StateTruckingHandler:
pass
return sent
# ── Intrastate authority: email the PSC/PUC, wait for the fee invoice ──
# (Falls through to manual todo when the state has no email submission
# path — handled by returning False below.)
# ── Intrastate authority ──────────────────────────────────────────────
# Per-state: figure out what the carrier actually needs and route to it.
# SC for-hire PROPERTY carriers register via the SCDMV Certificate of
# Compliance (COC) — gated on a Form E liability filing by their insurer.
# We email a yes/no insurance question first; once they confirm (or we've
# referred them to a broker) we bill the exact $25 COC fee + file. Other
# states fall through to the PSC/PUC email path or a manual todo.
if service_slug == "intrastate-authority":
try:
from scripts.workers.services.intrastate_filing import (
send_intrastate_submission, state_isa_contact,
)
except Exception as exc: # noqa: BLE001
LOG.error("[%s] intrastate_filing import failed: %s", order_number, exc)
return False
base_state = (intake.get("base_state") or intake.get("address_state") or "").upper()
if not state_isa_contact(base_state):
LOG.info("[%s] No intrastate email contact for %s — manual todo", order_number, base_state)
return False
sent = send_intrastate_submission(order_number, entity_name,
intake.get("dot_number", ""), base_state, intake,
signed_auth_key=signed_auth_key)
if sent:
if base_state == "SC":
if self._handle_sc_coc_gate(order_number, entity_name, customer_email,
customer_phone, intake):
return True # hold pending insurance answer / broker referral
# else: insurance confirmed -> fall through to bill the $25 COC fee
else:
# Non-SC: email the state PSC/PUC if we have a submission contact;
# otherwise fall back to a manual todo.
try:
notify_fulfillment_todo(
title=f"Intrastate authority submitted to {base_state} PSC — awaiting fee — {entity_name}",
order_number=order_number,
service_slug=service_slug,
priority="normal",
description=(f"Intrastate authority application emailed to the {base_state} "
f"PSC/PUC with the signed POA.\nWaiting on their requirements + "
f"fee invoice; when it arrives we auto-bill the customer the exact "
f"amount and you'll get a Telegram alert.\nCustomer: {customer_email}"),
from scripts.workers.services.intrastate_filing import (
send_intrastate_submission, state_isa_contact,
)
except Exception:
pass
return sent
except Exception as exc: # noqa: BLE001
LOG.error("[%s] intrastate_filing import failed: %s", order_number, exc)
return False
if not state_isa_contact(base_state):
LOG.info("[%s] No intrastate email contact for %s — manual todo", order_number, base_state)
return False
sent = send_intrastate_submission(order_number, entity_name,
intake.get("dot_number", ""), base_state, intake,
signed_auth_key=signed_auth_key)
if sent:
try:
notify_fulfillment_todo(
title=f"Intrastate authority submitted to {base_state} PSC — awaiting fee — {entity_name}",
order_number=order_number,
service_slug=service_slug,
priority="normal",
description=(f"Intrastate authority application emailed to the {base_state} "
f"PSC/PUC with the signed POA.\nWaiting on their requirements + "
f"fee invoice; when it arrives we auto-bill the customer the exact "
f"amount and you'll get a Telegram alert.\nCustomer: {customer_email}"),
)
except Exception:
pass
return sent
# ── IFTA / intrastate: published fee, bill the estimate now ───────────
try:
from scripts.workers.services.gov_fee import (