trucking: pass-through fee disclosure + state fulfillment status machine
Item 2 of the trucking state-authorization plan. - compliance-orders.ts: populate gov_fee_label for every state-trucking service so the variable, billed-at-cost government charges (apportioned IRP, IFTA decals, NY HUT, CT HUF, weight-distance, CA MCP+CARB, OS/OW permits, bundle) are disclosed at checkout. price_cents stays the flat service fee; gov fees pass through at cost. - migration 086: compliance_orders.fulfillment_status state machine (authorization_required -> authorization_signed -> awaiting_customer_ delegation -> awaiting_secure_credentials -> awaiting_government_fee_ approval -> awaiting_insurance_filing -> ready_to_file -> filed_waiting_state -> completed) + fulfillment_status_at - state_trucking.py: FULFILLMENT_* constants + _set_fulfillment_status(); gate sets authorization_required on pause, authorization_signed on resume, ready_to_file once the filing todo is queued - TruckingValueNotice.astro: 'What's included & what's billed at cost' disclosure with the authorization/delegation explanation
This commit is contained in:
parent
7ed06780bb
commit
29ad0908ee
4 changed files with 136 additions and 0 deletions
46
api/migrations/086_state_trucking_fulfillment_status.sql
Normal file
46
api/migrations/086_state_trucking_fulfillment_status.sql
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
-- 086: State motor carrier fulfillment status machine.
|
||||||
|
--
|
||||||
|
-- State trucking filings (IRP, IFTA, NY HUT, CT HUF, weight-distance, CA MCP,
|
||||||
|
-- etc.) have a multi-step fulfillment lifecycle that goes beyond payment: we
|
||||||
|
-- must collect the customer's signed authorization, possibly get them to add us
|
||||||
|
-- as a delegate inside the state portal (or collect credentials), get approval
|
||||||
|
-- for variable government fees, and then file and wait on the state.
|
||||||
|
--
|
||||||
|
-- This adds a dedicated fulfillment_status column to compliance_orders so ops
|
||||||
|
-- and the customer portal can see exactly where a state filing is, independent
|
||||||
|
-- of payment_status. Non-state services simply leave it NULL.
|
||||||
|
--
|
||||||
|
-- Status lifecycle (see docs/trucking-state-authorization-plan.md):
|
||||||
|
-- authorization_required -> we emailed the customer the signing link
|
||||||
|
-- authorization_signed -> customer signed the Limited Authorization
|
||||||
|
-- awaiting_customer_delegation -> customer must add us as portal user/delegate
|
||||||
|
-- awaiting_secure_credentials -> delegation unsupported; collecting credentials
|
||||||
|
-- awaiting_government_fee_approval -> variable state fee quoted, awaiting OK to pay
|
||||||
|
-- awaiting_insurance_filing -> waiting on insurer to file proof (e.g. intrastate)
|
||||||
|
-- ready_to_file -> all blockers cleared; queued for filing
|
||||||
|
-- filed_waiting_state -> submitted; waiting on the state to issue
|
||||||
|
-- completed -> credentials/decals/account delivered
|
||||||
|
|
||||||
|
ALTER TABLE compliance_orders
|
||||||
|
ADD COLUMN IF NOT EXISTS fulfillment_status TEXT
|
||||||
|
CHECK (fulfillment_status IS NULL OR fulfillment_status IN (
|
||||||
|
'authorization_required',
|
||||||
|
'authorization_signed',
|
||||||
|
'awaiting_customer_delegation',
|
||||||
|
'awaiting_secure_credentials',
|
||||||
|
'awaiting_government_fee_approval',
|
||||||
|
'awaiting_insurance_filing',
|
||||||
|
'ready_to_file',
|
||||||
|
'filed_waiting_state',
|
||||||
|
'completed'
|
||||||
|
)),
|
||||||
|
ADD COLUMN IF NOT EXISTS fulfillment_status_at TIMESTAMPTZ;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_compliance_orders_fulfillment
|
||||||
|
ON compliance_orders(fulfillment_status)
|
||||||
|
WHERE fulfillment_status IS NOT NULL;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN compliance_orders.fulfillment_status IS
|
||||||
|
'State motor carrier filing lifecycle (NULL for services without a portal/authorization step). See migration 086.';
|
||||||
|
COMMENT ON COLUMN compliance_orders.fulfillment_status_at IS
|
||||||
|
'When fulfillment_status last changed.';
|
||||||
|
|
@ -295,81 +295,101 @@ const COMPLIANCE_SERVICES: Record<
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
// ── State-Level Trucking Compliance ──────────────────────────────────
|
// ── State-Level Trucking Compliance ──────────────────────────────────
|
||||||
|
// For these services the price_cents is our flat SERVICE fee only. The
|
||||||
|
// actual state government charges (apportioned IRP registration, IFTA decal
|
||||||
|
// fees, weight-distance/HUT account setup, permit/decal costs) vary by the
|
||||||
|
// carrier's fleet weight, mileage, and base state, so they are passed
|
||||||
|
// through AT COST and billed separately once the state issues the invoice —
|
||||||
|
// we never mark them up. The gov_fee_label is shown to the customer at
|
||||||
|
// checkout so the pass-through is disclosed up front.
|
||||||
"irp-registration": {
|
"irp-registration": {
|
||||||
name: "IRP Registration Assistance",
|
name: "IRP Registration Assistance",
|
||||||
price_cents: 10900, // + state fees (apportioned registration billed at cost)
|
price_cents: 10900, // + state fees (apportioned registration billed at cost)
|
||||||
|
gov_fee_label: "Apportioned IRP registration & plate fees (state, by jurisdictions + weight, billed at cost)",
|
||||||
erpnext_item: "IRP-REGISTRATION",
|
erpnext_item: "IRP-REGISTRATION",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ifta-application": {
|
"ifta-application": {
|
||||||
name: "IFTA Application + Decals",
|
name: "IFTA Application + Decals",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "IFTA license & decal fees (state, billed at cost)",
|
||||||
erpnext_item: "IFTA-APPLICATION",
|
erpnext_item: "IFTA-APPLICATION",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ifta-quarterly": {
|
"ifta-quarterly": {
|
||||||
name: "IFTA Quarterly Filing",
|
name: "IFTA Quarterly Filing",
|
||||||
price_cents: 10900,
|
price_cents: 10900,
|
||||||
|
gov_fee_label: "IFTA taxes due (remitted to the state at cost based on your mileage)",
|
||||||
erpnext_item: "IFTA-QUARTERLY",
|
erpnext_item: "IFTA-QUARTERLY",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"or-weight-mile-tax": {
|
"or-weight-mile-tax": {
|
||||||
name: "Oregon Weight-Mile Tax Setup",
|
name: "Oregon Weight-Mile Tax Setup",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "Oregon weight-mile tax account & bond/deposit (state, billed at cost)",
|
||||||
erpnext_item: "OR-WEIGHT-MILE-TAX",
|
erpnext_item: "OR-WEIGHT-MILE-TAX",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ny-hut-registration": {
|
"ny-hut-registration": {
|
||||||
name: "NY Highway Use Tax Registration",
|
name: "NY Highway Use Tax Registration",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "NY HUT certificate & decal fees (state, billed at cost)",
|
||||||
erpnext_item: "NY-HUT-REGISTRATION",
|
erpnext_item: "NY-HUT-REGISTRATION",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ky-kyu-registration": {
|
"ky-kyu-registration": {
|
||||||
name: "KY Weight-Distance Tax Setup",
|
name: "KY Weight-Distance Tax Setup",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "KYU weight-distance account fees (state, billed at cost)",
|
||||||
erpnext_item: "KY-KYU-REGISTRATION",
|
erpnext_item: "KY-KYU-REGISTRATION",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"nm-weight-distance": {
|
"nm-weight-distance": {
|
||||||
name: "NM Weight-Distance Tax Setup",
|
name: "NM Weight-Distance Tax Setup",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "NM weight-distance permit & account fees (state, billed at cost)",
|
||||||
erpnext_item: "NM-WEIGHT-DISTANCE",
|
erpnext_item: "NM-WEIGHT-DISTANCE",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ct-highway-use-fee": {
|
"ct-highway-use-fee": {
|
||||||
name: "CT Highway Use Fee Setup",
|
name: "CT Highway Use Fee Setup",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "CT Highway Use Fee registration (state, billed at cost)",
|
||||||
erpnext_item: "CT-HIGHWAY-USE-FEE",
|
erpnext_item: "CT-HIGHWAY-USE-FEE",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"ca-mcp-carb": {
|
"ca-mcp-carb": {
|
||||||
name: "California MCP + CARB Compliance",
|
name: "California MCP + CARB Compliance",
|
||||||
price_cents: 22900, // $229 + state fees (CA is more complex: MCP + CARB)
|
price_cents: 22900, // $229 + state fees (CA is more complex: MCP + CARB)
|
||||||
|
gov_fee_label: "CA MCP permit fee + CARB/Clean Truck Check fees (state, by fleet size, billed at cost)",
|
||||||
erpnext_item: "CA-MCP-CARB",
|
erpnext_item: "CA-MCP-CARB",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"state-dot-registration": {
|
"state-dot-registration": {
|
||||||
name: "State DOT Registration",
|
name: "State DOT Registration",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "State DOT/intrastate registration fee (state, billed at cost)",
|
||||||
erpnext_item: "STATE-DOT-REGISTRATION",
|
erpnext_item: "STATE-DOT-REGISTRATION",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"intrastate-authority": {
|
"intrastate-authority": {
|
||||||
name: "Intrastate Operating Authority",
|
name: "Intrastate Operating Authority",
|
||||||
price_cents: 10900, // + state fees
|
price_cents: 10900, // + state fees
|
||||||
|
gov_fee_label: "State intrastate authority filing fee (state, billed at cost)",
|
||||||
erpnext_item: "INTRASTATE-AUTHORITY",
|
erpnext_item: "INTRASTATE-AUTHORITY",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"osow-permit": {
|
"osow-permit": {
|
||||||
name: "Oversize/Overweight Permit",
|
name: "Oversize/Overweight Permit",
|
||||||
price_cents: 10900, // + state permit fees
|
price_cents: 10900, // + state permit fees
|
||||||
|
gov_fee_label: "State oversize/overweight permit fees (per trip/route, billed at cost)",
|
||||||
erpnext_item: "OSOW-PERMIT",
|
erpnext_item: "OSOW-PERMIT",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
"state-trucking-bundle": {
|
"state-trucking-bundle": {
|
||||||
name: "State Compliance Bundle",
|
name: "State Compliance Bundle",
|
||||||
price_cents: 49900, // $499 — IRP+IFTA+weight tax+intrastate ($796 individual)
|
price_cents: 49900, // $499 — IRP+IFTA+weight tax+intrastate ($796 individual)
|
||||||
|
gov_fee_label: "State registration, decal & tax fees for each included filing (billed at cost)",
|
||||||
erpnext_item: "STATE-TRUCKING-BUNDLE",
|
erpnext_item: "STATE-TRUCKING-BUNDLE",
|
||||||
discountable: true,
|
discountable: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,19 @@ from datetime import datetime
|
||||||
|
|
||||||
LOG = logging.getLogger("workers.services.state_trucking")
|
LOG = logging.getLogger("workers.services.state_trucking")
|
||||||
|
|
||||||
|
# State motor carrier fulfillment lifecycle (compliance_orders.fulfillment_status,
|
||||||
|
# migration 086). These let ops and the customer portal see exactly where a state
|
||||||
|
# filing is, independent of payment_status.
|
||||||
|
FULFILLMENT_AUTHORIZATION_REQUIRED = "authorization_required"
|
||||||
|
FULFILLMENT_AUTHORIZATION_SIGNED = "authorization_signed"
|
||||||
|
FULFILLMENT_AWAITING_DELEGATION = "awaiting_customer_delegation"
|
||||||
|
FULFILLMENT_AWAITING_CREDENTIALS = "awaiting_secure_credentials"
|
||||||
|
FULFILLMENT_AWAITING_FEE_APPROVAL = "awaiting_government_fee_approval"
|
||||||
|
FULFILLMENT_AWAITING_INSURANCE = "awaiting_insurance_filing"
|
||||||
|
FULFILLMENT_READY_TO_FILE = "ready_to_file"
|
||||||
|
FULFILLMENT_FILED_WAITING_STATE = "filed_waiting_state"
|
||||||
|
FULFILLMENT_COMPLETED = "completed"
|
||||||
|
|
||||||
# Map service slugs to human-readable names and filing instructions
|
# Map service slugs to human-readable names and filing instructions
|
||||||
SERVICE_INFO = {
|
SERVICE_INFO = {
|
||||||
"irp-registration": {
|
"irp-registration": {
|
||||||
|
|
@ -258,6 +271,7 @@ class StateTruckingHandler:
|
||||||
signer_title=str(intake.get("signer_title", "")),
|
signer_title=str(intake.get("signer_title", "")),
|
||||||
)
|
)
|
||||||
if requested:
|
if requested:
|
||||||
|
self._set_fulfillment_status(order_number, FULFILLMENT_AUTHORIZATION_REQUIRED)
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"[%s] Authorization requested — pipeline PAUSED pending signature",
|
"[%s] Authorization requested — pipeline PAUSED pending signature",
|
||||||
order_number,
|
order_number,
|
||||||
|
|
@ -271,6 +285,7 @@ class StateTruckingHandler:
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
signed_auth_key = self._signed_authorization_key(order_number)
|
signed_auth_key = self._signed_authorization_key(order_number)
|
||||||
|
self._set_fulfillment_status(order_number, FULFILLMENT_AUTHORIZATION_SIGNED)
|
||||||
LOG.info("[%s] Authorization signed (%s) — proceeding to filing", order_number, signed_auth_key)
|
LOG.info("[%s] Authorization signed (%s) — proceeding to filing", order_number, signed_auth_key)
|
||||||
|
|
||||||
# Slug-specific intake fields collected by StateTruckingIntakeStep.
|
# Slug-specific intake fields collected by StateTruckingIntakeStep.
|
||||||
|
|
@ -343,6 +358,8 @@ class StateTruckingHandler:
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
LOG.info("[%s] Admin todo created for %s", order_number, service_name)
|
LOG.info("[%s] Admin todo created for %s", order_number, service_name)
|
||||||
|
# Authorization is in hand and the filing task is queued for ops.
|
||||||
|
self._set_fulfillment_status(order_number, FULFILLMENT_READY_TO_FILE)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
LOG.error("[%s] Failed to create admin todo: %s", order_number, exc)
|
LOG.error("[%s] Failed to create admin todo: %s", order_number, exc)
|
||||||
|
|
||||||
|
|
@ -606,6 +623,30 @@ class StateTruckingHandler:
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _set_fulfillment_status(self, order_number: str, status: str) -> None:
|
||||||
|
"""Advance compliance_orders.fulfillment_status (best-effort).
|
||||||
|
|
||||||
|
``status`` must be one of the FULFILLMENT_* constants. Non-fatal: a
|
||||||
|
failure here never blocks the filing pipeline.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import psycopg2
|
||||||
|
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
|
||||||
|
try:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(
|
||||||
|
"""UPDATE compliance_orders
|
||||||
|
SET fulfillment_status = %s, fulfillment_status_at = NOW(),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE order_number = %s""",
|
||||||
|
(status, order_number),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
except Exception as exc:
|
||||||
|
LOG.warning("[%s] Could not set fulfillment_status=%s: %s", order_number, status, exc)
|
||||||
|
|
||||||
def _authorization_status(self, order_number: str) -> str | None:
|
def _authorization_status(self, order_number: str) -> str | None:
|
||||||
"""Return the current authorization esign status: 'signed', 'pending', or None."""
|
"""Return the current authorization esign status: 'signed', 'pending', or None."""
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,35 @@ const n = NOTICES[slug];
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details class="pw-tvalue-details">
|
||||||
|
<summary>What's included & what's billed at cost</summary>
|
||||||
|
<div class="pw-tvalue-body">
|
||||||
|
<p>
|
||||||
|
The price shown is <strong>our flat service fee</strong> to prepare and
|
||||||
|
submit this filing for you. Government charges are passed through
|
||||||
|
<strong>at cost</strong> — we never mark them up. Depending on your
|
||||||
|
fleet weight, mileage, and base state, the state may charge:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>government registration, account, decal, and permit fees;</li>
|
||||||
|
<li>weight-distance / highway-use taxes you owe to the state;</li>
|
||||||
|
<li>card processing fees, and any bond, deposit, or insurance filing cost.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Where these amounts are not known at checkout, we bill them separately at
|
||||||
|
cost once the state issues its invoice — with your approval before
|
||||||
|
we remit anything.
|
||||||
|
</p>
|
||||||
|
<p class="pw-tvalue-note">
|
||||||
|
To file on your behalf we will email you a short <strong>authorization to
|
||||||
|
sign</strong>. It lets Performance West prepare and submit your filing,
|
||||||
|
communicate with the agency, and remit the government fees you provide. If
|
||||||
|
the state requires it, we may ask you to add us as a portal user/delegate
|
||||||
|
or securely share account access.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</aside>
|
</aside>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue