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:
parent
6171c64b90
commit
cbfb8d6091
29 changed files with 602 additions and 52 deletions
12
api/migrations/074_engagement_columns.sql
Normal file
12
api/migrations/074_engagement_columns.sql
Normal 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;
|
||||||
|
|
@ -1462,7 +1462,11 @@ export async function handlePaymentComplete(
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Send order confirmation email ──────────────────────────────────────
|
// ── 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({
|
await sendOrderConfirmationEmail({
|
||||||
order_id,
|
order_id,
|
||||||
order_type,
|
order_type,
|
||||||
|
|
|
||||||
|
|
@ -701,6 +701,8 @@ router.post("/api/v1/compliance-orders", async (req, res) => {
|
||||||
// Multi-year catch-up (migration 060) — array of reporting years.
|
// Multi-year catch-up (migration 060) — array of reporting years.
|
||||||
// 2+ years gets the 15% multi-year discount.
|
// 2+ years gets the 15% multi-year discount.
|
||||||
multi_year_filings,
|
multi_year_filings,
|
||||||
|
// Engagement authorization (migration 074)
|
||||||
|
engagement_accepted,
|
||||||
} = req.body ?? {};
|
} = req.body ?? {};
|
||||||
|
|
||||||
if (!service_slug || !customer_email || !customer_name) {
|
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,
|
discount_code, discount_cents, notes, intake_data,
|
||||||
filing_mode, form_year_override, revises_order_number, revised_reason,
|
filing_mode, form_year_override, revises_order_number, revised_reason,
|
||||||
waive_deminimis_exemption, waive_deminimis_reason,
|
waive_deminimis_exemption, waive_deminimis_reason,
|
||||||
multi_year_filings, multi_year_discount_pct
|
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)
|
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 *`,
|
RETURNING *`,
|
||||||
[
|
[
|
||||||
order_number,
|
order_number,
|
||||||
|
|
@ -870,6 +873,9 @@ router.post("/api/v1/compliance-orders", async (req, res) => {
|
||||||
waive_deminimis_reason || null,
|
waive_deminimis_reason || null,
|
||||||
myf && myf.length > 0 ? myf : null,
|
myf && myf.length > 0 ? myf : null,
|
||||||
multi_year_discount_pct || 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,
|
customer_phone,
|
||||||
discount_code,
|
discount_code,
|
||||||
intake_data,
|
intake_data,
|
||||||
|
engagement_accepted,
|
||||||
} = req.body ?? {};
|
} = req.body ?? {};
|
||||||
|
|
||||||
if (!rawServices || !Array.isArray(rawServices) || rawServices.length === 0) {
|
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,
|
order_number, batch_id, service_slug, service_name, service_fee_cents,
|
||||||
gov_fee_cents, gov_fee_label,
|
gov_fee_cents, gov_fee_label,
|
||||||
customer_email, customer_name, customer_phone,
|
customer_email, customer_name, customer_phone,
|
||||||
discount_code, discount_cents, intake_data
|
discount_code, discount_cents, intake_data,
|
||||||
) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
|
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 *`,
|
RETURNING *`,
|
||||||
[
|
[
|
||||||
orderNumber,
|
orderNumber,
|
||||||
|
|
@ -1014,6 +1022,9 @@ router.post("/api/v1/compliance-orders/batch", async (req, res) => {
|
||||||
discount_code || null,
|
discount_code || null,
|
||||||
svcDiscount,
|
svcDiscount,
|
||||||
intake_data ? JSON.stringify(intake_data) : "{}",
|
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]);
|
orders.push(result.rows[0]);
|
||||||
|
|
|
||||||
179
docs/usac-past-due-fee-negotiation.md
Normal file
179
docs/usac-past-due-fee-negotiation.md
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
# USAC Past-Due Filing Fee Negotiation & Contingency Fee Service
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Carriers with past-due USAC filing obligations (Form 499-A late filings) face compounding penalties. This document covers how the fees work, viable methods to reduce them, whether we can charge on a contingency basis, and when the client needs a lawyer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. How USAC Late Filing Fees Work
|
||||||
|
|
||||||
|
### Fee Calculation
|
||||||
|
Late filing fees are assessed when a carrier fails to file Form 499-A or 499-Q within **30 days** of the due date. The fee is the **greater** of:
|
||||||
|
- **$100/month flat**, OR
|
||||||
|
- **Monthly USF obligation × (U.S. prime rate + 3.5%) / 365 × days late**
|
||||||
|
|
||||||
|
Fees accrue monthly until the form is submitted. For a carrier with meaningful revenue, this compounds quickly.
|
||||||
|
|
||||||
|
### Additional Consequences
|
||||||
|
- **Interest**: Accrues daily at prime + 3.5% from delinquency date
|
||||||
|
- **91+ days past due**: Additional 6% annual penalty applied retroactively
|
||||||
|
- **Red Light status**: Triggered after 1 day delinquent — all USF disbursements withheld, TIN-linked entities affected
|
||||||
|
- **DCIA referral**: Debt referred to U.S. Treasury for collection after 90+ days
|
||||||
|
- **FCC enforcement**: Additional collection costs under 31 U.S.C. § 3717
|
||||||
|
|
||||||
|
### Key Policy
|
||||||
|
> "Late payment fees will not be reversed unless USAC has made an error."
|
||||||
|
|
||||||
|
USAC's official position is that penalties are only reversed for USAC billing errors, not for carrier negligence or hardship.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Viable Methods to Reduce Past-Due Fees
|
||||||
|
|
||||||
|
### Method 1: USAC Billing Dispute (Email)
|
||||||
|
- **How**: Email CustomerSupport@usac.org with subject "Billing Inquiry or Dispute"
|
||||||
|
- **Include**: Requestor name, Filer ID, invoice date, statement IDs, dispute description
|
||||||
|
- **Response time**: 24 hours
|
||||||
|
- **Success rate**: LOW — only reverses fees for documented USAC errors
|
||||||
|
- **Best for**: Carriers who believe USAC miscalculated their obligation, billed the wrong entity, or double-counted contributions
|
||||||
|
|
||||||
|
### Method 2: Payment Plan (Installment Agreement)
|
||||||
|
- **How**: Submit written request to USAC at 700 12th Street NW, Suite 900, Washington DC 20005
|
||||||
|
- **Requirement**: Must demonstrate financial inability to pay in one lump sum (verified per 31 CFR § 901.8)
|
||||||
|
- **Terms**: Promissory note, additional interest, admin fees, possible audit obligations
|
||||||
|
- **Invoices**: Separate "PMTP" invoices, due by 15th of following month
|
||||||
|
- **Effect**: Stops Red Light status, prevents Treasury referral, allows structured payoff
|
||||||
|
- **Does NOT reduce the total amount** — just spreads it out
|
||||||
|
|
||||||
|
### Method 3: USAC Appeal (Contributor Division)
|
||||||
|
- **How**: Email ContributorAppeals@usac.org within 60 days of USAC decision
|
||||||
|
- **Include**: Filer ID, USAC decision letter, supporting docs, explanation of relief sought
|
||||||
|
- **Timeline**: Written acknowledgment upon receipt, then written decision
|
||||||
|
- **Best for**: Challenging the underlying assessment (revenue classification errors, incorrect contribution base, de minimis eligibility disputes)
|
||||||
|
- **Pending appeals protect disputed debt from Treasury referral and Red Light status**
|
||||||
|
|
||||||
|
### Method 4: FCC Appeal / Waiver Request
|
||||||
|
- **How**: File with FCC via ECFS (Electronic Comment Filing System), docket WC 06-122
|
||||||
|
- **Deadline**: 60 days after USAC decision on appeal
|
||||||
|
- **Standard**: Must demonstrate "good cause" and "special circumstances" warranting deviation from the general rule, and that the deviation serves the public interest
|
||||||
|
- **Reality**: FCC has maintained an **extremely strict standard**. In February 2026, the FCC denied **every single one** of 20+ waiver requests for FY 2023-2024 regulatory fee penalties. The FCC cited that "relief is reserved for rare, extraordinary circumstances outside a payer's control." Examples of denied reasons: administrative oversight, delayed invoice awareness, CORES navigation difficulties, bank transaction limits, contractor mistakes.
|
||||||
|
- **Historical precedent for grants**: Post-9/11 relief is cited as the benchmark. Only truly extraordinary external events have qualified.
|
||||||
|
- **Requires an attorney** — 47 CFR § 1.23 limits practice before the FCC to bar-admitted attorneys
|
||||||
|
|
||||||
|
### Method 5: Revenue Reclassification / Revised Filing
|
||||||
|
- **How**: File revised 499-A forms that correctly classify revenue
|
||||||
|
- **Effect**: If original filings overcounted contributable revenue (e.g., classified broadband revenue as telecom, included exempt revenue), the revised filing reduces the contribution base, which reduces the USF obligation, which reduces the late fees proportionally
|
||||||
|
- **This is the most practical path for many carriers** — filing accurate revised forms that reduce the assessment base
|
||||||
|
- **We can do this** — it's form preparation, not legal representation
|
||||||
|
|
||||||
|
### Method 6: De Minimis Exemption / Discontinuance
|
||||||
|
- **How**: If carrier qualifies as de minimis (< $10,000 in contributable telecom revenue), file for de minimis status retroactively or file 499-A Discontinuance
|
||||||
|
- **Effect**: Eliminates or dramatically reduces the contribution obligation, and therefore the late fees on that obligation
|
||||||
|
- **We already offer this**: 499-A Discontinuance service ($299)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Contingency Fee Legality
|
||||||
|
|
||||||
|
### The Core Question
|
||||||
|
Can Performance West charge 15% of savings from reducing a client's USAC bill?
|
||||||
|
|
||||||
|
### Answer: YES for form preparation / consulting; NO for legal representation
|
||||||
|
|
||||||
|
**What we CAN do (not practicing law):**
|
||||||
|
- Prepare and file revised 499-A forms with correct revenue classifications
|
||||||
|
- Calculate de minimis eligibility and file discontinuance
|
||||||
|
- Identify revenue reclassification opportunities (broadband vs. telecom allocation)
|
||||||
|
- Prepare billing dispute letters for USAC (factual, not legal arguments)
|
||||||
|
- Advise on the USAC payment plan process
|
||||||
|
- Charge a flat fee, hourly rate, or percentage of savings for these consulting services
|
||||||
|
|
||||||
|
**What we CANNOT do (practicing law):**
|
||||||
|
- File appeals with the FCC on behalf of the client (47 CFR § 1.23 — attorneys only)
|
||||||
|
- Negotiate settlements directly with USAC on legal grounds
|
||||||
|
- Provide legal opinions on whether penalties are lawfully assessed
|
||||||
|
- Represent the client in any FCC proceeding or hearing
|
||||||
|
|
||||||
|
### Contingency Fee Structure — Safe Harbor
|
||||||
|
A **percentage-of-savings** model is legally permissible as a **consulting fee** when:
|
||||||
|
1. We are performing form preparation, revenue analysis, and classification work (not legal advocacy)
|
||||||
|
2. We do not hold ourselves out as attorneys or provide legal advice
|
||||||
|
3. The fee is tied to the result of our consulting work (revised filings that reduce the assessment), not to the outcome of a legal proceeding
|
||||||
|
4. We have a clear engagement letter stating we are compliance consultants, not lawyers
|
||||||
|
|
||||||
|
This is analogous to how tax preparation firms charge based on refund amounts, or how energy consultants charge a percentage of utility bill reductions. The key is that we're doing the analytical/filing work, not the legal representation.
|
||||||
|
|
||||||
|
### When They Need a Lawyer
|
||||||
|
- Filing an appeal with the FCC (mandatory — 47 CFR § 1.23)
|
||||||
|
- Challenging the constitutionality or legality of the USF assessment
|
||||||
|
- Responding to FCC enforcement actions or forfeiture proceedings
|
||||||
|
- Contesting Treasury collection actions (DCIA referral)
|
||||||
|
- Any proceeding before an administrative law judge
|
||||||
|
|
||||||
|
**Recommended telecom law firms**: CommLaw Group, iCommLaw, Kelley Drye & Warren LLP (telecom practice)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Recommended Service Offering
|
||||||
|
|
||||||
|
### "USAC Past-Due Assessment Review" — Consulting Service
|
||||||
|
|
||||||
|
**What we deliver:**
|
||||||
|
1. **Revenue audit**: Review the carrier's past 499-A filings for revenue misclassification. Many small carriers over-report because they include broadband revenue, non-telecom revenue, or exempt services in their contribution base.
|
||||||
|
2. **Revised filing preparation**: File corrected 499-A forms for the delinquent years with proper revenue classification.
|
||||||
|
3. **De minimis / discontinuance evaluation**: Determine if the carrier qualifies for de minimis exemption or should file a discontinuance.
|
||||||
|
4. **Payment plan assistance**: Help the carrier prepare the financial documentation needed for a USAC installment agreement.
|
||||||
|
5. **Billing dispute letter**: If USAC made a calculation error, prepare the dispute email.
|
||||||
|
|
||||||
|
**Pricing model options:**
|
||||||
|
- **Option A**: Flat fee per year of remediation ($499/year)
|
||||||
|
- **Option B**: 15% of documented reduction in USF obligation (contingency)
|
||||||
|
- **Option C**: Hybrid — $299 minimum + 10% of savings above $2,000
|
||||||
|
|
||||||
|
**Engagement letter must clearly state:**
|
||||||
|
- We are compliance consultants, not attorneys
|
||||||
|
- We prepare forms and provide regulatory compliance consulting
|
||||||
|
- If the client needs legal representation before the FCC, we will refer to a telecom attorney
|
||||||
|
- The fee is for consulting services (form preparation, revenue analysis, classification)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Public Examples / Precedent
|
||||||
|
|
||||||
|
### FCC Waiver Denials (DA-26-184, February 2026)
|
||||||
|
The FCC denied 20+ requests to waive the 25% late penalty on FY 2023/2024 regulatory fees. Every request was denied. The FCC emphasized that waivers require "extraordinary circumstances outside the payer's control" — not administrative oversight, not bank issues, not contractor errors. This sets a very high bar for any FCC-level appeal.
|
||||||
|
|
||||||
|
### Fifth Circuit USF Challenge
|
||||||
|
In 2024, the Fifth Circuit ruled the USF contribution scheme violates the legislative vesting clause (non-delegation doctrine). While this is being appealed and doesn't directly help with past-due fees, it creates uncertainty about the long-term viability of the USF contribution mechanism and could be cited in waiver requests as a mitigating factor.
|
||||||
|
|
||||||
|
### deltathree, Inc. (DA-16-432)
|
||||||
|
The FCC granted deltathree's request for review of revised quarterly filings submitted 45+ days late. This is a rare example of the FCC allowing late revised filings, but the circumstances were unusual.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Summary — What to Tell Clients
|
||||||
|
|
||||||
|
| Approach | Who Does It | Success Rate | Cost to Client |
|
||||||
|
|----------|-------------|-------------|----------------|
|
||||||
|
| Revenue reclassification + revised 499-A | **Us** | HIGH (if revenue was misclassified) | $499/year or 15% of savings |
|
||||||
|
| De minimis / discontinuance filing | **Us** | HIGH (if eligible) | $299 |
|
||||||
|
| USAC billing dispute | **Us** (prepare letter) | LOW (only for USAC errors) | Included |
|
||||||
|
| USAC payment plan | **Us** (prepare docs) | MEDIUM (restructures, doesn't reduce) | Included |
|
||||||
|
| USAC appeal (47 CFR 54.719) | **Us** (prepare) + attorney (file) | MEDIUM | Attorney fees |
|
||||||
|
| FCC waiver request | **Attorney only** | VERY LOW (nearly all denied) | $5,000-15,000+ |
|
||||||
|
|
||||||
|
**Bottom line**: The most effective path is NOT trying to negotiate the penalties away — it's reducing the underlying assessment through accurate revenue reclassification, which proportionally reduces all associated late fees and interest. This is consulting work we can do legally on a contingency basis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
- [USAC Late Filing Fees](https://www.usac.org/service-providers/making-payments/invoices/late-filing-sanction/)
|
||||||
|
- [USAC Billing Disputes](https://www.usac.org/service-providers/making-payments/billing-disputes/)
|
||||||
|
- [USAC Payment Plans](https://www.usac.org/service-providers/making-payments/how-to-pay/payment-plans/)
|
||||||
|
- [USAC Appeals Process](https://www.usac.org/about/appeals-audits/appeals/)
|
||||||
|
- [USAC Late Payments / DCIA / Red Light](https://www.usac.org/service-providers/making-payments/late-payments-dcia-red-light/)
|
||||||
|
- [47 CFR § 1.23 — Practice Before the FCC](https://www.law.cornell.edu/cfr/text/47/1.23)
|
||||||
|
- [FCC Contribution Methodology](https://www.fcc.gov/general/contribution-methodology-administrative-filings)
|
||||||
|
- [FCC Regulatory Fee Penalty Denials (2026)](https://radioink.com/2026/02/26/fcc-holds-line-on-25-regulatory-fee-penalty-for-late-payments/)
|
||||||
|
- [USF Due Process Violations (CGO)](https://www.thecgo.org/research/an-alphabet-soup-of-due-process-violations/)
|
||||||
193
scripts/document_gen/templates/engagement_letter_499a.py
Normal file
193
scripts/document_gen/templates/engagement_letter_499a.py
Normal 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
|
||||||
|
|
@ -1032,6 +1032,8 @@ def handle_process_compliance_service(payload: dict) -> dict:
|
||||||
order["customer_name"] = row.get("customer_name")
|
order["customer_name"] = row.get("customer_name")
|
||||||
order["customer_phone"] = row.get("customer_phone")
|
order["customer_phone"] = row.get("customer_phone")
|
||||||
order["batch_id"] = row.get("batch_id")
|
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["filing_mode"] = row.get("filing_mode")
|
||||||
order["form_year_override"] = row.get("form_year_override")
|
order["form_year_override"] = row.get("form_year_override")
|
||||||
order["revises_order_number"] = row.get("revises_order_number")
|
order["revises_order_number"] = row.get("revises_order_number")
|
||||||
|
|
|
||||||
|
|
@ -170,11 +170,36 @@ class Form499AHandler(BaseServiceHandler):
|
||||||
date_str = datetime.now().strftime("%Y%m%d")
|
date_str = datetime.now().strftime("%Y%m%d")
|
||||||
generated: list[str] = []
|
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+
|
# Multi-year mode (migration 060): when multi_year_filings has 2+
|
||||||
# years, run this handler once per year with each year pinned as
|
# years, run this handler once per year with each year pinned as
|
||||||
# form_year_override. Persist per-year confirmations to
|
# form_year_override. Persist per-year confirmations to
|
||||||
# compliance_orders.multi_year_confirmations.
|
# compliance_orders.multi_year_confirmations.
|
||||||
multi_year = order_data.get("multi_year_filings") or []
|
|
||||||
if multi_year and len(multi_year) >= 2:
|
if multi_year and len(multi_year) >= 2:
|
||||||
all_generated: list[str] = []
|
all_generated: list[str] = []
|
||||||
year_conf_records: list[dict] = []
|
year_conf_records: list[dict] = []
|
||||||
|
|
@ -1410,6 +1435,150 @@ class Form499AHandler(BaseServiceHandler):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Could not create admin ToDo: %s", 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]:
|
def _box_to_category_ids(box_num: int) -> set[str]:
|
||||||
"""Given a Line 105 box number, return the category ids that tick it.
|
"""Given a Line 105 box number, return the category ids that tick it.
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,7 @@ function renderServices() {
|
||||||
'<label class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 cursor-pointer hover:border-pw-400 has-[:checked]:border-pw-500 has-[:checked]:bg-pw-50"><input type="radio" name="pw_pay" value="paypal"><span class="text-sm font-medium text-gray-800">PayPal</span><span class="ml-auto text-xs text-gray-400">+3%</span></label>' +
|
'<label class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 cursor-pointer hover:border-pw-400 has-[:checked]:border-pw-500 has-[:checked]:bg-pw-50"><input type="radio" name="pw_pay" value="paypal"><span class="text-sm font-medium text-gray-800">PayPal</span><span class="ml-auto text-xs text-gray-400">+3%</span></label>' +
|
||||||
'<label class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 cursor-pointer hover:border-pw-400 has-[:checked]:border-pw-500 has-[:checked]:bg-pw-50"><input type="radio" name="pw_pay" value="crypto"><span class="text-sm font-medium text-gray-800">Cryptocurrency</span><span class="ml-auto text-xs text-gray-400">No fee</span></label>' +
|
'<label class="flex items-center gap-3 p-3 rounded-lg border border-gray-200 cursor-pointer hover:border-pw-400 has-[:checked]:border-pw-500 has-[:checked]:bg-pw-50"><input type="radio" name="pw_pay" value="crypto"><span class="text-sm font-medium text-gray-800">Cryptocurrency</span><span class="ml-auto text-xs text-gray-400">No fee</span></label>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
'<label class="flex items-start gap-2 p-3 mt-3 rounded-lg border border-gray-200 cursor-pointer text-xs text-gray-600 leading-relaxed"><input type="checkbox" id="pw-engage" required class="mt-0.5 rounded border-gray-300 text-pw-600 focus:ring-pw-500"><span>I authorize Performance West Inc. to prepare and submit regulatory filings on my behalf as described above. I understand Performance West provides compliance consulting services, not legal advice or legal representation. I confirm the information I provide is accurate to the best of my knowledge. <a href="/terms" target="_blank" class="text-pw-600 underline">Terms of Service</a></span></label>' +
|
||||||
'<button type="submit" id="pw-submit" class="w-full py-3 rounded-lg bg-green-600 text-white font-bold text-base hover:bg-green-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed">Continue to Payment</button>' +
|
'<button type="submit" id="pw-submit" class="w-full py-3 rounded-lg bg-green-600 text-white font-bold text-base hover:bg-green-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed">Continue to Payment</button>' +
|
||||||
'<p id="pw-err" class="text-sm text-red-700 mt-1" hidden></p>' +
|
'<p id="pw-err" class="text-sm text-red-700 mt-1" hidden></p>' +
|
||||||
'</form>';
|
'</form>';
|
||||||
|
|
@ -367,6 +368,8 @@ function renderServices() {
|
||||||
document.getElementById("pw-batch-form").addEventListener("submit", function(e){
|
document.getElementById("pw-batch-form").addEventListener("submit", function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if(selectedSlugs.length === 0){ alert("Please select at least one service."); return; }
|
if(selectedSlugs.length === 0){ alert("Please select at least one service."); return; }
|
||||||
|
var engageBox=document.getElementById("pw-engage");
|
||||||
|
if(engageBox && !engageBox.checked){ alert("Please accept the authorization terms to continue."); return; }
|
||||||
var errEl=document.getElementById("pw-err");errEl.hidden=true;
|
var errEl=document.getElementById("pw-err");errEl.hidden=true;
|
||||||
var btn=document.getElementById("pw-submit");btn.disabled=true;btn.textContent="Creating order...";
|
var btn=document.getElementById("pw-submit");btn.disabled=true;btn.textContent="Creating order...";
|
||||||
var payMethod=document.querySelector("input[name=pw_pay]:checked").value;
|
var payMethod=document.querySelector("input[name=pw_pay]:checked").value;
|
||||||
|
|
@ -376,7 +379,8 @@ function renderServices() {
|
||||||
customer_email:document.getElementById("pw-email").value.trim(),
|
customer_email:document.getElementById("pw-email").value.trim(),
|
||||||
customer_phone:document.getElementById("pw-phone").value.trim()||undefined,
|
customer_phone:document.getElementById("pw-phone").value.trim()||undefined,
|
||||||
discount_code:document.getElementById("pw-promo").value.trim()||undefined,
|
discount_code:document.getElementById("pw-promo").value.trim()||undefined,
|
||||||
intake_data:{frn:frn,source:"compliance-check-remediation"}
|
intake_data:{frn:frn,source:"compliance-check-remediation"},
|
||||||
|
engagement_accepted:true
|
||||||
};
|
};
|
||||||
fetch(API+"/api/v1/compliance-orders/batch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)})
|
fetch(API+"/api/v1/compliance-orders/batch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)})
|
||||||
.then(function(r){return r.json().then(function(d){if(!r.ok)throw new Error(d.error||"Order failed");return d})})
|
.then(function(r){return r.json().then(function(d){if(!r.ok)throw new Error(d.error||"Order failed");return d})})
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,11 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label style="display:flex;align-items:flex-start;gap:.5rem;padding:.65rem;margin-top:.75rem;border:1px solid #e5e7eb;border-radius:8px;cursor:pointer;font-size:.75rem;color:#6b7280;line-height:1.5">
|
||||||
|
<input type="checkbox" id="engage-check" required style="margin-top:2px;accent-color:#1e3a5f">
|
||||||
|
<span>I authorize Performance West Inc. to prepare and submit regulatory filings on my behalf as described above. I understand Performance West provides compliance consulting services, not legal advice or legal representation. I confirm the information I provide is accurate to the best of my knowledge. <a href="/terms" target="_blank" style="color:#1e3a5f;text-decoration:underline">Terms of Service</a></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div style="margin-top:1rem;text-align:right">
|
<div style="margin-top:1rem;text-align:right">
|
||||||
<button class="btn btn-primary" id="btn-pay">Continue to Payment</button>
|
<button class="btn btn-primary" id="btn-pay">Continue to Payment</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,6 +234,11 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
|
||||||
var contactEmail = document.getElementById('contact-email').value.trim();
|
var contactEmail = document.getElementById('contact-email').value.trim();
|
||||||
var contactPhone = document.getElementById('contact-phone').value.trim();
|
var contactPhone = document.getElementById('contact-phone').value.trim();
|
||||||
|
|
||||||
|
if (!document.getElementById('engage-check').checked) {
|
||||||
|
errEl.textContent = 'Please accept the authorization terms to continue.';
|
||||||
|
errEl.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!entityName || !contactName || !contactEmail || !contactPhone) {
|
if (!entityName || !contactName || !contactEmail || !contactPhone) {
|
||||||
errEl.textContent = 'Please fill in all required fields (entity name, contact name, email, phone).';
|
errEl.textContent = 'Please fill in all required fields (entity name, contact name, email, phone).';
|
||||||
errEl.style.display = 'block';
|
errEl.style.display = 'block';
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,11 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
|
||||||
<input type="text" id="promo-code" placeholder="SAVE20" style="margin-bottom:.75rem">
|
<input type="text" id="promo-code" placeholder="SAVE20" style="margin-bottom:.75rem">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label style="display:flex;align-items:flex-start;gap:.5rem;padding:.65rem;margin-top:.75rem;border:1px solid #e5e7eb;border-radius:8px;cursor:pointer;font-size:.75rem;color:#6b7280;line-height:1.5">
|
||||||
|
<input type="checkbox" id="engage-check" required style="margin-top:2px;accent-color:#1e3a5f">
|
||||||
|
<span>I authorize Performance West Inc. to prepare and submit regulatory filings on my behalf as described above. I understand Performance West provides compliance consulting services, not legal advice or legal representation. I confirm the information I provide is accurate to the best of my knowledge. <a href="/terms" target="_blank" style="color:#1e3a5f;text-decoration:underline">Terms of Service</a></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
|
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
|
||||||
<button class="btn" style="background:#e5e7eb;color:#374151" id="btn-back-3">← Back</button>
|
<button class="btn" style="background:#e5e7eb;color:#374151" id="btn-back-3">← Back</button>
|
||||||
<button class="btn btn-primary" id="btn-pay" disabled>Continue to Payment</button>
|
<button class="btn btn-primary" id="btn-pay" disabled>Continue to Payment</button>
|
||||||
|
|
@ -410,6 +415,12 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
|
||||||
const errEl = document.getElementById('checkout-error');
|
const errEl = document.getElementById('checkout-error');
|
||||||
errEl.style.display = 'none';
|
errEl.style.display = 'none';
|
||||||
|
|
||||||
|
if (!document.getElementById('engage-check').checked) {
|
||||||
|
errEl.textContent = 'Please accept the authorization terms to continue.';
|
||||||
|
errEl.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const customerName = document.getElementById('customer-name').value.trim();
|
const customerName = document.getElementById('customer-name').value.trim();
|
||||||
const customerEmail = document.getElementById('customer-email').value.trim();
|
const customerEmail = document.getElementById('customer-email').value.trim();
|
||||||
if (!customerName || !customerEmail) {
|
if (!customerName || !customerEmail) {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Availability data only (no voice subscription). For broadba
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Both BDC blocks in one order — broadband deployment + voi
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Voice subscriber counts only — the part of the legacy For
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "System Security and Integrity plan required of every common
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Classified traffic study from your CDRs — feeds the 499-A
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Register your carrier in FCC CORES and obtain your FRN. Req
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "47 CFR § 64.2009 annual CPNI certification filed at FCC EC
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Your required D.C. registered agent for service of process
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "New carrier registration with USAC — obtain your Filer ID
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Annual 499-A plus the four quarterly 499-Q filings — one
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Annual Telecommunications Reporting Worksheet. Due April 1
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "47 CFR § 63.11 notification filed with the FCC Internation
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Diagnostic check — CORES, RMD, STIR/SHAKEN, CPNI, 499-A s
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "RMD + CPNI + STIR/SHAKEN + 499-A + 499-Q in one order. Ever
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,6 @@ const description =
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<p class="pw-price" id="pw-price">
|
|
||||||
Service fee: {formatUSD(meta?.price_cents ?? 0)}
|
|
||||||
{isMulti ? " per state" : ""}
|
|
||||||
<span class="pw-price-note"> + state filing fees + registered agent</span>
|
|
||||||
</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
|
|
||||||
{!isMulti && (
|
{!isMulti && (
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Start-to-finish for a brand-new VoIP carrier: FRN + 499 Ini
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Obtain an Operating Company Number from NECA. Required for
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,7 @@ const description = "Robocall Mitigation Database filing. Annual recertification
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)} <span style="font-size:0.85rem;font-weight:400;color:#64748b;">+ $100 FCC filing fee</span></p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
<p style="font-size:0.82rem;color:#92400e;background:#fefce8;border-left:3px solid #f59e0b;padding:0.5rem 0.75rem;border-radius:0 4px 4px 0;margin-top:0.5rem;max-width:48rem;">
|
|
||||||
The FCC charges a $100 filing fee for RMD registrations and recertifications (effective 2025). This fee is passed through at cost and paid directly to the FCC during submission.
|
|
||||||
</p>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Wizard service_slug={slug} steps={steps ?? ["entity", "review", "payment"]} title={meta?.name ?? slug} />
|
<Wizard service_slug={slug} steps={steps ?? ["entity", "review", "payment"]} title={meta?.name ?? slug} />
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ const description = "Posture update + RMD refresh + STI-CA vendor coordination.
|
||||||
<main>
|
<main>
|
||||||
<section class="pw-order-intro">
|
<section class="pw-order-intro">
|
||||||
<h1>{meta?.name}</h1>
|
<h1>{meta?.name}</h1>
|
||||||
<p class="pw-price" id="pw-price">{formatUSD(meta?.price_cents ?? 0)}</p>
|
|
||||||
<div id="pw-tax-notice"><TaxDeductibilityNotice /></div>
|
|
||||||
<p class="pw-desc">{description}</p>
|
<p class="pw-desc">{description}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue