Dependency edges can now require the prerequisite be ACTIVE at FMCSA, not
just our handler completed. mc-authority/ucr/d&a now wait for an active
USDOT; BOC-3 stays parallel-OK (can file while authority pending). Adds
_prerequisite_active() polling FMCSA QC API, a waiting_on_activation hold
state with next-check timestamp, and a 21-day authority vetting estimate
for customer comms. Branch logic unit-tested.
_get_authority_state() returns structured FMCSA authority state; handle()
branches on active/pending/revoked/none:
- active: file/refresh BOC-3 (current behavior)
- pending: file BOC-3 + insurance/21-day-vetting reminder
- revoked: file + recommend reinstatement (mc-authority, never auto-charge)
- none (USDOT only): flag MC authority needed first, do not file blindly
recommended_followups + authority_state persisted in admin todo for
upsell-approve on the order timeline.
Cross-references every DOT/state/hazmat slug across COMPLIANCE_SERVICES,
REQUIRED_FIELDS, SERVICE_META, INTAKE_MANIFEST, and SERVICE_HANDLERS, and
verifies every required field is collectible by its assigned intake steps.
Caught + fixed missing usdot-reactivation SERVICE_META entry. 24/24 pass.
Adds Tawk_API.onLoad mobile guard (max-width 768px -> hideWidget) in shared
footer snippet and current built pages so mobile browsers no longer get the
proactive text bubble covering content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Forms that legally require the client's signature were not being captured
correctly:
- MCS-150 handler created a perjury e-sign record but then submitted to FMCSA
anyway, before the client signed. Now it gates submission: request the
signature, hold, and only file when handle_esign_completed re-dispatches with
client_approved=True.
- MCS-150 e-sign links were signed with JWT_SECRET/ADMIN_JWT_SECRET, but the
portal verifies with CUSTOMER_JWT_SECRET, so every link returned "Invalid
portal link." New shared dot_esign helper signs with CUSTOMER_JWT_SECRET.
- carrier-closeout (final MCS-150 Out of Business) and entity-dissolution
(Articles of Dissolution + no-lawsuits/liens/judgments attestation) captured
no signature at all. Both now request a signed attestation before the
workflow proceeds.
- mc-authority / emergency-temporary-authority now get a correctly labeled
OP-1 applicant certification instead of an "MCS-150" record.
Also fixes a latent dispatcher bug: order["service_slug"] was never set, so
handlers sharing a class fell back to their default SERVICE_SLUG. This made
entity-dissolution run the carrier-closeout branch and mc-authority/etc. look
like mcs150-update. Now the resolved slug is injected into order_data.
Portal e-sign page now renders the document-specific certification text from
metadata.perjury_text (so the dissolution no-liabilities attestation and OP-1
cert are actually shown to the signer), not just a generic perjury line.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ROOT CAUSE of orders never fulfilling: the batch Sales Order used the service
SLUG as item_code (e.g. 'mcs150-update') but ERPNext items use the catalog
erpnext_item codes ('MCS150-UPDATE'), so SO creation threw 'Item not found' ->
no SO -> no portal -> no fulfillment. Now maps slug -> erpnext_item (falls back
to COMPLIANCE-SERVICE). DOT ERPNext items were also missing — created them.
Notification: show Subtotal / Discount / Card surcharge / Total so totals like
$35.54 (= $34.50 + $1.04 surcharge) are transparent instead of looking wrong.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Federal DOT services (MCS-150, BOC-3, UCR, authority, D&A, audit, full-compliance,
reactivation, ETA, closeout) now have customer intake pages, so they get an
intake-form link like FCC services instead of the old 'admin-assisted / we're
working on it' message. Only form-less state-level filings stay admin-assisted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Was reading only updated.rows[0] -> reported a single line item's net as the
'Total' and showed just one service for multi-service batches (e.g. Paul Wilson's
3-service $218 PayPal batch showed as 'mcs150-update $34.50'). Now sums
service_fee - discount + surcharge + gov_fee across all rows and lists every
service.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gap: dot-registration (new USDOT=MCS-150) routed through intake but never asked
for photo ID; usdot-reactivation, emergency-temporary-authority, carrier-closeout
(final MCS-150 + authority revocation), entity-dissolution, and entity-upgrade-
bundle weren't wired to collect it at all.
- intake_manifest: route usdot-reactivation, ETA, carrier-closeout,
entity-dissolution through the dot-intake step
- DOTIntakeStep DOT_SECTIONS: add dot-sec-photo-id for dot-registration and all
the above (operations + photo ID)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Auto-uncheck conflicting services: closing-down (carrier-closeout, entity-
dissolution) vs any operational filing; new USDOT vs reactivation; new USDOT
vs MCS-150 update.
- Hero: removed 'this is all we do' (we also do telecom); 4-col grid.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- send_test no longer overwrites {{ UnsubscribeURL }} with a dead static URL;
Listmonk renders it into a working per-subscriber unsubscribe link.
- dot-compliance hero grid: 4 columns (minmax 150px, max-width 920px) instead
of 3 to reduce vertical space.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- import_subscribers: was POSTing wrong bulk shape AND fallback used 'list_ids'
(ignored by Listmonk) instead of 'lists' -> subscribers never attached to the
list -> real sends would go to an empty list. Now single-adds with 'lists',
handles already-exists, returns a count, logs if 0 added.
- send_test: passed base['lists'] (objects) instead of IDs -> test send rejected.
Now extracts list IDs.
- create_and_schedule_campaign: add schedule= flag (preview makes drafts).
- --preview: 1 sample carrier/campaign, only owner email, drafts not scheduled,
test sends immediately, never marks real carriers sent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each of the 8 daily campaigns gets a test send immediately after creation
using the first row's real carrier data as the sample. TEST_EMAIL env var
overrides the default (carrierone@gmx.com).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4-card dark hero: specialized in trucking compliance, fast turnaround (1-2 days),
attention to detail (verified against current FMCSA reqs), real people/support.
Trust bar updated: No Login.gov required + Klarna added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These were missing — the ETA button in email 188 linked to the order page
with services=emergency-temporary-authority but no matching checkbox existed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The original CREATE INDEX (non-concurrent) on a 2M-row table held a SHARE lock
for ~33 minutes, blocking all 25+ DOT checker queries and causing 'Failed to
fetch' for real users. CONCURRENTLY builds the index without a table lock.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AbortSignal.timeout() requires Node 17.3+. The API container likely runs an
older Node version, so timeouts never fired -> fetch hung forever when FMCSA
API is down -> nginx proxy timeout -> 'Failed to fetch' in the browser.
Fix: use AbortController + manual setTimeout() which works on all Node versions.
All 3 external fetch points (fmcsaFetch x2, SOS x2) now actually abort at 5s.
Also: guard final res.json() with !res.headersSent so the 12s deadline fallback
and the normal response path can't double-send.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If FMCSA live API is slow (can take 2x 10s = 20s when down), the route would
hang until nginx proxy killed the connection -> 'Failed to fetch'. Now:
- fmcsaFetch timeout: 10s -> 5s (two calls max 10s total)
- SOS entity-status timeout: already reduced to 5s
- 12s hard deadline: if any live API hangs past 12s, immediately return
census-only data with a 'partial=true' flag so the user gets something
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The bare catch{} introduced a control-flow issue. fmcsaFetch() already returns
null on all errors and never throws, so the try/catch wrapping was unnecessary.
Keep only the SOS timeout reduction (20s->5s) as the actual fix for the nginx
proxy timeout that caused 'Failed to fetch' on slow DOT lookups.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two WORKER_URL/entity-status calls both had 20s timeouts; worst case 40s total
response time exceeds nginx proxy_read_timeout, dropping the connection and
causing the browser to show 'Failed to fetch'. Also wraps fmcsaFetch calls
explicitly so FMCSA API failure still returns full local census data.
- AbortSignal.timeout(20000) -> 5000 on both SOS entity-status calls
- fmcsaFetch carrier + authority calls wrapped in individual try/catch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- build_trucking_campaigns.py: nightly script that creates 8 Listmonk campaigns
per day (4 TZ x 2 types: MCS-150 overdue 2k/TZ, inactive USDOT 1k/TZ)
at 4AM ET / 5AM ET (CT) / 6AM ET (MT) / 7AM ET (PT). Deduplicates via
listmonk_sent_at column.
- migration 083: add listmonk_sent_at + listmonk_campaign_type to fmcsa_carriers
- email_verifier.py: bump max_workers from 5 to 20 for 4x faster throughput
- cron: daily pw-trucking-campaigns at 08:00 UTC (3 AM EST)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- order/dot-compliance: add carrier-closeout ($199) + entity-dissolution ($49)
cards so the checker's wrap-up CTA actually resolves (was a dead-end: no
matching data-slug checkbox to pre-select)
- new-carrier flag: drop the 'not tax advice / we'll confirm' hedge, reframe
confidently as a fee/cost point (not taxes); only show the Wyoming-LLC caveat
when Wyoming is actually selected
- checker: fix malformed inline style on the sell-truck textarea (missing ;)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When forming or operating in CA (gross-receipts fee >$250k), TN ($300 vs $20
report), or NY (LLC publication + filing fee), show an advisory with the reason
and a one-click 'Use a corporation'. Keyed off wiz.entityState/baseState.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Checker closing mode now pitches a done-for-you 'Trucking Wrap-Up' ($199)
with a buy button to /order/dot-compliance?services=carrier-closeout, instead
of a lead form. DIY checklist replaced by what's-included list.
- Entity dissolution offered as a paid add-on with the lawsuits/liens/judgments
warning before dissolving.
- New catalog services: carrier-closeout ($199), entity-dissolution ($199).
- CarrierCloseoutHandler orchestrates the sequential shutdown workflow
(final MCS-150 out-of-business, MC revoke, UCR cancel, IFTA/IRP + state
closures; dissolution branch for the add-on) as admin-tracked tasks.
- Sell-your-trucks: single shared form with quick-cash / marketplace / both;
name field is now a real first+last name (no corp-name prefill).
- tickets categories: add truck_sale_both, drop business_closeout (now an order).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These lead-capture categories were posted by the DOT checker but missing from
VALID_CATEGORIES, so the API rejected them with 400 (insurance_lead too — it
was referenced in the Telegram code but never allowlisted).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 10-code jokes are email-only now; revert checker/new-carrier CTAs to plain
- Sell-trucks quick-cash flow no longer names ByeTruck or links out; lead still
routes via the truck_sale_quickcash ticket (internal routing stays generic)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- DOT checker: '10-4' on Fix My DOT Filings / Get These Handled; '10-7'
(out of service) on the business close-out CTA
- New-carrier (LLC) page: add a free Compliance Check button in the hero
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Intent toggle: 'staying in business' (compliance) vs 'closing my business'
- Closing mode shows a green, personalized wind-down checklist from FMCSA data
(final MCS-150/USDOT deactivation, MC revoke, UCR, IFTA/IRP, state permits,
insurance, entity dissolution) + 'let us handle the shutdown' lead capture
- Sell-your-trucks box: quick cash -> ByeTruck referral lead + handoff;
marketplace -> email capture for follow-up guide
- Deep link via ?intent=closing for the email CTAs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>