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,193 @@
"""Generate a 499-A engagement letter for past-due/multi-year refiling.
Produces a DOCX engagement letter that the client must eSign before
we begin work on revised 499-A filings for prior years.
"""
from __future__ import annotations
import os
from datetime import date
from typing import Optional
def generate_engagement_letter(
*,
entity_name: str,
frn: str = "",
contact_name: str = "",
contact_email: str = "",
filing_years: list[int] | None = None,
fee_description: str = "",
order_number: str = "",
output_path: str,
) -> str:
"""Generate the engagement letter DOCX. Returns the output path."""
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
NAVY = RGBColor(0x1A, 0x27, 0x44)
GREEN = RGBColor(0x05, 0x96, 0x69)
today = date.today()
today_str = today.strftime("%B %d, %Y")
years_str = ", ".join(str(y) for y in (filing_years or [today.year]))
doc = Document()
for s in doc.sections:
s.top_margin = Inches(1)
s.bottom_margin = Inches(1)
s.left_margin = Inches(1.25)
s.right_margin = Inches(1.25)
# Header
tp = doc.add_paragraph()
tp.alignment = WD_ALIGN_PARAGRAPH.CENTER
tr = tp.add_run("Engagement Letter")
tr.font.size = Pt(16)
tr.bold = True
tr.font.color.rgb = NAVY
sp = doc.add_paragraph()
sp.alignment = WD_ALIGN_PARAGRAPH.CENTER
sr = sp.add_run("FCC Form 499-A Revenue Audit & Revised Filing")
sr.font.size = Pt(11)
sr.italic = True
sp.paragraph_format.space_after = Pt(16)
def _p(text: str, bold: bool = False, size: int = 11) -> None:
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(6)
r = p.add_run(text)
r.font.size = Pt(size)
r.bold = bold
def _h(text: str) -> None:
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(12)
p.paragraph_format.space_after = Pt(4)
r = p.add_run(text)
r.font.size = Pt(12)
r.bold = True
r.font.color.rgb = NAVY
_p(f"Date: {today_str}")
_p(f"Client: {entity_name}", bold=True)
if frn:
_p(f"FCC Registration Number (FRN): {frn}")
if contact_name:
_p(f"Contact: {contact_name}" + (f" ({contact_email})" if contact_email else ""))
if order_number:
_p(f"Order Reference: {order_number}")
_h("1. Scope of Services")
_p(
f"Performance West Inc. (\"PW\") will provide the following compliance "
f"consulting services for {entity_name} (\"Client\"):"
)
_p(
f" a) Revenue audit of Client's FCC Form 499-A filings for calendar "
f"year(s) {years_str} to identify potential revenue misclassifications "
f"affecting the Universal Service Fund (USF) contribution base."
)
_p(
f" b) Preparation and submission of revised FCC Form 499-A filings "
f"for the identified calendar year(s) with corrected revenue classifications."
)
_p(
f" c) Evaluation of Client's eligibility for de minimis exemption "
f"under 47 CFR \u00a7 54.708."
)
_p(
f" d) Assistance with USAC billing dispute documentation and "
f"payment plan applications, if applicable."
)
_h("2. Fee Structure")
if fee_description:
_p(fee_description)
else:
_p(
f"PW's fee for these services is $499 per calendar year of revised "
f"filing, totaling ${499 * len(filing_years or [today.year])} for "
f"{len(filing_years or [today.year])} year(s)."
)
_p(
"Payment is due upon engagement. This fee covers the revenue audit, "
"revised form preparation, and submission. Government filing fees, "
"if any, are the Client's responsibility and will be disclosed "
"before submission."
)
_h("3. Authorization")
_p(
f"Client hereby authorizes PW to prepare and submit revised FCC "
f"Form 499-A filings on Client's behalf for the calendar year(s) "
f"identified above. Client understands that PW will submit these "
f"forms to the Universal Service Administrative Company (USAC) "
f"using the Client's Filer ID and FRN."
)
_h("4. Not Legal Advice")
_p(
"PW is a compliance consulting firm, not a law firm. The services "
"described in this letter constitute regulatory form preparation "
"and compliance consulting, not legal advice or legal representation. "
"PW does not provide legal opinions, represent clients before the "
"FCC in adjudicatory proceedings, or create an attorney-client "
"relationship."
)
_p(
"If Client requires legal representation before the FCC (e.g., "
"appeals under 47 CFR \u00a7 54.719, waiver requests, or enforcement "
"proceedings), PW will refer Client to a qualified telecommunications "
"attorney."
)
_h("5. Client Responsibilities")
_p(
"Client certifies that all information provided to PW is accurate "
"and complete to the best of Client's knowledge. Client understands "
"that FCC Form 499-A filings carry a certification of accuracy and "
"that false statements may result in penalties under 18 U.S.C. \u00a7 1001."
)
_p(
"Client agrees to provide PW with access to revenue records, "
"financial statements, and other documentation reasonably necessary "
"to perform the revenue audit and prepare revised filings."
)
_h("6. Limitation of Liability")
_p(
"PW's liability under this engagement is limited to the fees paid "
"by Client for the services described herein. PW is not responsible "
"for penalties, interest, or other charges assessed by USAC or the "
"FCC, whether arising from prior filings, revised filings, or any "
"other cause."
)
_h("7. Term")
_p(
"This engagement begins upon signature below and continues until "
"the revised filings have been submitted to USAC, or until "
"terminated by either party with written notice."
)
# Signature block
doc.add_paragraph()
_p("AGREED AND ACCEPTED:", bold=True)
doc.add_paragraph()
_p(f"Client: {entity_name}")
_p("Signature: ____________________________________")
_p("Name: ____________________________________")
_p("Title: ____________________________________")
_p(f"Date: {today_str}")
doc.add_paragraph()
_p("Performance West Inc.")
_p("525 Randall Ave Ste 100-1195, Cheyenne, WY 82001")
_p("1-888-411-0383 | info@performancewest.net")
doc.save(output_path)
return output_path

View file

@ -1032,6 +1032,8 @@ def handle_process_compliance_service(payload: dict) -> dict:
order["customer_name"] = row.get("customer_name")
order["customer_phone"] = row.get("customer_phone")
order["batch_id"] = row.get("batch_id")
order["engagement_esign_signed_at"] = row.get("engagement_esign_signed_at")
order["engagement_esign_required"] = row.get("engagement_esign_required")
order["filing_mode"] = row.get("filing_mode")
order["form_year_override"] = row.get("form_year_override")
order["revises_order_number"] = row.get("revises_order_number")

View file

@ -170,11 +170,36 @@ class Form499AHandler(BaseServiceHandler):
date_str = datetime.now().strftime("%Y%m%d")
generated: list[str] = []
# Engagement letter gate: past-due or multi-year (2+) refiling orders
# require a signed engagement letter before we begin work.
filing_mode = order_data.get("filing_mode") or "current"
multi_year = order_data.get("multi_year_filings") or []
needs_engagement = (
filing_mode == "past_due"
or (multi_year and len(multi_year) >= 2)
)
if needs_engagement:
esign_signed = order_data.get("engagement_esign_signed_at")
if not esign_signed:
# Check if we already generated the letter (avoid re-sending on re-dispatch)
already_required = order_data.get("engagement_esign_required")
if not already_required:
logger.info(
"Form499AHandler: %s requires engagement letter eSign (mode=%s, years=%s) — generating + pausing",
order_number, filing_mode, multi_year,
)
self._generate_and_send_engagement_letter(order_data)
else:
logger.info(
"Form499AHandler: %s still waiting for engagement eSign — skipping",
order_number,
)
return []
# Multi-year mode (migration 060): when multi_year_filings has 2+
# years, run this handler once per year with each year pinned as
# form_year_override. Persist per-year confirmations to
# compliance_orders.multi_year_confirmations.
multi_year = order_data.get("multi_year_filings") or []
if multi_year and len(multi_year) >= 2:
all_generated: list[str] = []
year_conf_records: list[dict] = []
@ -1410,6 +1435,150 @@ class Form499AHandler(BaseServiceHandler):
except Exception as exc:
logger.error("Could not create admin ToDo: %s", exc)
def _generate_and_send_engagement_letter(self, order_data: dict) -> None:
"""Generate engagement letter PDF and email client a signing link."""
order_number = order_data["name"]
entity = order_data.get("entity", {}) or {}
intake = order_data.get("intake_data") or {}
multi_year = order_data.get("multi_year_filings") or []
customer_email = order_data.get("customer_email", "")
customer_name = order_data.get("customer_name", "")
import tempfile
work_dir = tempfile.mkdtemp(prefix="engagement_")
docx_path = os.path.join(work_dir, f"engagement_{order_number}.docx")
try:
from scripts.document_gen.templates.engagement_letter_499a import (
generate_engagement_letter,
)
generate_engagement_letter(
entity_name=entity.get("legal_name") or intake.get("entity_legal_name", ""),
frn=entity.get("frn") or intake.get("frn", ""),
contact_name=customer_name,
contact_email=customer_email,
filing_years=multi_year if multi_year else None,
order_number=order_number,
output_path=docx_path,
)
# Convert to PDF
pdf_path = self._convert_to_pdf(docx_path)
# Upload to MinIO
from scripts.document_gen import MinioStorage
storage = MinioStorage()
minio_key = f"engagement/{order_number}/engagement_letter.pdf"
storage.upload_file(pdf_path or docx_path, minio_key)
# Update order with engagement letter path + mark eSign required
try:
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
cur = conn.cursor()
cur.execute(
"""UPDATE compliance_orders
SET engagement_esign_required = TRUE,
engagement_letter_minio_key = %s,
payment_status = 'pending_esign'
WHERE order_number = %s""",
(minio_key, order_number),
)
conn.commit()
cur.close()
conn.close()
except Exception as exc:
logger.warning("Could not update engagement status: %s", exc)
# Email client the engagement signing link
if customer_email:
try:
try:
import jwt as pyjwt
except ImportError:
import PyJWT as pyjwt
secret = os.environ.get("CUSTOMER_JWT_SECRET", "changeme")
domain = os.environ.get("DOMAIN", "performancewest.net")
token = pyjwt.encode(
{"order_id": order_number, "order_type": "compliance", "email": customer_email},
secret, algorithm="HS256",
)
sign_url = f"https://{domain}/portal/engagement-sign?token={token}"
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
first_name = customer_name.split(" ")[0] if customer_name else "there"
years_str = ", ".join(str(y) for y in multi_year) if multi_year else "current year"
subject = f"Engagement Letter — 499-A Revenue Audit for {entity.get('legal_name', order_number)}"
body = (
f"<h2>Engagement Letter Ready for Signature</h2>"
f"<p>Hi {first_name},</p>"
f"<p>Before we begin your FCC Form 499-A revenue audit and revised filing "
f"for calendar year(s) <strong>{years_str}</strong>, we need your signature "
f"on the engagement letter.</p>"
f"<p>Please review and sign the letter by clicking below:</p>"
f"<p><a href='{sign_url}' style='display:inline-block;background:#1e3a5f;color:#fff;"
f"padding:12px 28px;border-radius:6px;text-decoration:none;font-weight:600;'>"
f"Review & Sign Engagement Letter</a></p>"
f"<p style='font-size:12px;color:#9ca3af;'>Order: {order_number}</p>"
f"<p style='font-size:11px;color:#9ca3af;margin-top:16px;'>"
f"Performance West Inc. | 525 Randall Ave Ste 100-1195, Cheyenne, WY 82001 | 1-888-411-0383</p>"
)
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = os.environ.get("SMTP_FROM", "Performance West <noreply@performancewest.net>")
msg["To"] = customer_email
msg.attach(MIMEText(body, "html"))
smtp_host = os.environ.get("SMTP_HOST", "co.carrierone.com")
smtp_port = int(os.environ.get("SMTP_PORT", "587"))
smtp_user = os.environ.get("SMTP_USER", "")
smtp_pass = os.environ.get("SMTP_PASS", "")
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
if smtp_user and smtp_pass:
server.login(smtp_user, smtp_pass)
server.send_message(msg)
logger.info("Engagement letter email sent to %s for %s", customer_email, order_number)
except Exception as exc:
logger.warning("Could not send engagement email for %s: %s", order_number, exc)
# Create admin todo
try:
from scripts.workers.erpnext_client import ERPNextClient
ERPNextClient().create_resource("ToDo", {
"description": (
f"[fcc-499a] {order_number}\n\n"
f"Engagement letter generated and sent for 499-A past-due/multi-year refiling.\n"
f"Entity: {entity.get('legal_name', '')}\n"
f"Years: {years_str}\n"
f"Waiting for client eSign before processing begins."
),
"priority": "Medium",
"role": "Accounting Advisor",
})
except Exception as exc:
logger.warning("Could not create engagement admin ToDo: %s", exc)
except Exception as exc:
logger.error("Engagement letter generation failed for %s: %s", order_number, exc)
# Create admin todo for manual follow-up
try:
from scripts.workers.erpnext_client import ERPNextClient
ERPNextClient().create_resource("ToDo", {
"description": (
f"[fcc-499a] {order_number}\n\n"
f"FAILED to generate engagement letter: {exc}\n"
f"Manual engagement letter needed before processing past-due 499-A."
),
"priority": "High",
"role": "Accounting Advisor",
})
except Exception:
pass
def _box_to_category_ids(box_num: int) -> set[str]:
"""Given a Line 105 box number, return the category ids that tick it.