Add engagement authorization, remove price headers from intake pages, fix duplicate emails

- Add clickwrap authorization checkbox to fcc-compliance, state-puc, neca-ocn order pages
- Store engagement_accepted_at/ip/version in compliance_orders (migration 074)
- Add 499-A past-due/multi-year eSign engagement letter generator
- Gate 499-A handler on engagement signature for past-due/multi-year orders
- Remove price/tax/fee headers from all 19 intake pages (post-payment only)
- Fix duplicate confirmation email for compliance_batch orders
- Add USAC past-due fee negotiation research doc

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-28 02:50:02 -05:00
parent 6171c64b90
commit cbfb8d6091
29 changed files with 602 additions and 52 deletions

View file

@ -0,0 +1,12 @@
-- 074_engagement_columns.sql
-- Store client engagement authorization consent for compliance orders.
-- Part 1: clickwrap checkbox consent for all orders.
-- Part 2: eSign engagement letter for 499-A past-due/multi-year refiling.
ALTER TABLE compliance_orders
ADD COLUMN IF NOT EXISTS engagement_accepted_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS engagement_accepted_ip TEXT,
ADD COLUMN IF NOT EXISTS engagement_version TEXT,
ADD COLUMN IF NOT EXISTS engagement_esign_required BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS engagement_esign_signed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS engagement_letter_minio_key TEXT;

View file

@ -1462,7 +1462,11 @@ export async function handlePaymentComplete(
}
// ── Send order confirmation email ──────────────────────────────────────
try {
// Skip for compliance_batch — sendComplianceIntakeEmail already sent
// a combined confirmation + intake email above.
if (order_type === "compliance_batch") {
console.log(`[checkout] Skipping generic confirmation for ${order_id} — intake email already sent`);
} else try {
await sendOrderConfirmationEmail({
order_id,
order_type,

View file

@ -701,6 +701,8 @@ router.post("/api/v1/compliance-orders", async (req, res) => {
// Multi-year catch-up (migration 060) — array of reporting years.
// 2+ years gets the 15% multi-year discount.
multi_year_filings,
// Engagement authorization (migration 074)
engagement_accepted,
} = req.body ?? {};
if (!service_slug || !customer_email || !customer_name) {
@ -844,8 +846,9 @@ router.post("/api/v1/compliance-orders", async (req, res) => {
discount_code, discount_cents, notes, intake_data,
filing_mode, form_year_override, revises_order_number, revised_reason,
waive_deminimis_exemption, waive_deminimis_reason,
multi_year_filings, multi_year_discount_pct
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)
multi_year_filings, multi_year_discount_pct,
engagement_accepted_at, engagement_accepted_ip, engagement_version
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25)
RETURNING *`,
[
order_number,
@ -870,6 +873,9 @@ router.post("/api/v1/compliance-orders", async (req, res) => {
waive_deminimis_reason || null,
myf && myf.length > 0 ? myf : null,
multi_year_discount_pct || null,
engagement_accepted ? new Date().toISOString() : null,
engagement_accepted ? (req.ip || req.headers["x-forwarded-for"] || null) : null,
engagement_accepted ? "v1-2026-04" : null,
],
);
@ -898,6 +904,7 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
customer_phone,
discount_code,
intake_data,
engagement_accepted,
} = req.body ?? {};
if (!rawServices || !Array.isArray(rawServices) || rawServices.length === 0) {
@ -997,8 +1004,9 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
order_number, batch_id, service_slug, service_name, service_fee_cents,
gov_fee_cents, gov_fee_label,
customer_email, customer_name, customer_phone,
discount_code, discount_cents, intake_data
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
discount_code, discount_cents, intake_data,
engagement_accepted_at, engagement_accepted_ip, engagement_version
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
RETURNING *`,
[
orderNumber,
@ -1014,6 +1022,9 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
discount_code || null,
svcDiscount,
intake_data ? JSON.stringify(intake_data) : "{}",
engagement_accepted ? new Date().toISOString() : null,
engagement_accepted ? (req.ip || req.headers["x-forwarded-for"] || null) : null,
engagement_accepted ? "v1-2026-04" : null,
],
);
orders.push(result.rows[0]);