diff --git a/scripts/workers/services/__init__.py b/scripts/workers/services/__init__.py
index 26445f1..2b5c803 100644
--- a/scripts/workers/services/__init__.py
+++ b/scripts/workers/services/__init__.py
@@ -18,6 +18,8 @@ from .fcc_compliance_checkup import FCCComplianceCheckupHandler
from .rmd_filing import RMDFilingHandler
from .cpni_certification import CPNIFilingHandler
from .form_499a import Form499AHandler, Form499ABundleHandler
+from .form_499q import Form499QHandler
+from .form_499a_discontinuance import Form499ADiscontinuanceHandler
from .stir_shaken import StirShakenHandler
from .bdc_filing import BDCFilingHandler
from .fcc_full_compliance import FullComplianceHandler
@@ -63,6 +65,8 @@ SERVICE_HANDLERS: dict[str, type] = {
"fcc-499a": Form499AHandler,
"fcc-499a-zero": Form499AHandler, # same handler, zero_revenue flag set from slug
"fcc-499a-499q": Form499ABundleHandler,
+ "fcc-499q": Form499QHandler,
+ "fcc-499a-discontinuance": Form499ADiscontinuanceHandler,
"stir-shaken": StirShakenHandler,
# BDC triple — same handler, mode auto-resolved from slug
"bdc-filing": BDCFilingHandler, # legacy alias (both)
@@ -100,6 +104,7 @@ FCC_SERVICE_SLUGS: frozenset[str] = frozenset({
"fcc-499a-zero",
"fcc-499a-499q",
"fcc-499q",
+ "fcc-499a-discontinuance",
"stir-shaken",
"bdc-filing",
"fcc-full-compliance",
diff --git a/scripts/workers/services/form_499a_discontinuance.py b/scripts/workers/services/form_499a_discontinuance.py
new file mode 100644
index 0000000..4347872
--- /dev/null
+++ b/scripts/workers/services/form_499a_discontinuance.py
@@ -0,0 +1,130 @@
+"""FCC Form 499-A Discontinuance Filing Handler.
+
+For carriers who no longer provide telecommunications services and need
+to close out their USAC 499-A filing obligations. Files a final 499-A
+with zero revenue and requests discontinuance status from USAC.
+
+This is typically for:
+- Pure broadband resale ISPs who were incorrectly filing 499-A
+- Carriers who have ceased operations
+- Companies that were acquired and the FRN is being retired
+"""
+from __future__ import annotations
+
+import logging
+import os
+from datetime import datetime
+
+from .base_handler import BaseComplianceHandler
+
+logger = logging.getLogger("workers.services.form_499a_discontinuance")
+
+
+class Form499ADiscontinuanceHandler(BaseComplianceHandler):
+ SERVICE_SLUG = "fcc-499a-discontinuance"
+ SERVICE_NAME = "Form 499-A Discontinuance Filing"
+
+ async def process(self, order_data: dict) -> dict | None:
+ order_number = order_data.get("order_number", "")
+ entity = order_data.get("entity", {})
+ intake_data = order_data.get("intake_data", {})
+
+ filer_id = intake_data.get("filer_id_499") or entity.get("filer_id_499", "")
+ frn = intake_data.get("frn") or entity.get("frn", "")
+ legal_name = entity.get("legal_name") or intake_data.get("entity_name", "")
+
+ logger.info(
+ "Form499ADiscontinuanceHandler: %s for %s (FRN: %s, Filer ID: %s)",
+ order_number, legal_name, frn, filer_id,
+ )
+
+ discontinuance_reason = intake_data.get("discontinuance_reason", "Ceased providing telecommunications services")
+ last_service_date = intake_data.get("last_service_date", "")
+
+ # Create admin todo with discontinuance instructions
+ # (USAC E-File discontinuance is a manual process — file zero-revenue 499-A
+ # then submit discontinuance request via USAC contact form)
+ self._create_admin_todo(
+ order_number,
+ f"FILE 499-A DISCONTINUANCE for {legal_name}\n\n"
+ f"FRN: {frn}\n"
+ f"Filer ID: {filer_id}\n"
+ f"Reason: {discontinuance_reason}\n"
+ f"Last service date: {last_service_date or 'Not specified'}\n\n"
+ f"Steps:\n"
+ f"1. Log in to USAC E-File (https://forms.universalservice.org/)\n"
+ f"2. File a final 499-A with $0 revenue for the current year\n"
+ f"3. In the comments/notes section, state: "
+ f"'This is a final filing. {legal_name} has discontinued all "
+ f"telecommunications services as of {last_service_date or 'current date'}. "
+ f"Please close this filer account.'\n"
+ f"4. Contact USAC at (888) 641-8722 or usac@usac.org to confirm "
+ f"discontinuance and request removal of future filing obligations\n"
+ f"5. Confirm that CPNI, RMD, and other FCC filings are also discontinued\n\n"
+ f"Client email: {entity.get('contact_email') or order_data.get('customer_email', '')}",
+ )
+
+ # Send confirmation to client
+ self._send_confirmation(
+ to=entity.get("contact_email") or order_data.get("customer_email", ""),
+ entity_name=legal_name,
+ order_number=order_number,
+ filer_id=filer_id,
+ )
+
+ return {"status": "submitted_for_processing"}
+
+ def _send_confirmation(
+ self, to: str, entity_name: str, order_number: str, filer_id: str,
+ ) -> None:
+ if not to:
+ return
+ try:
+ import smtplib
+ from email.mime.multipart import MIMEMultipart
+ from email.mime.text import MIMEText
+
+ subject = f"Form 499-A Discontinuance Filed — {entity_name}"
+ html = f"""
+
+
+
Form 499-A Discontinuance
+
+
+
We've received your request to discontinue the FCC Form 499-A filing
+ obligation for {entity_name} (Filer ID: {filer_id}).
+
+
We will:
+
+ - File a final Form 499-A with zero revenue
+ - Request USAC to close your filer account
+ - Confirm discontinuance of related obligations (CPNI, RMD)
+
+
+
You'll receive a confirmation email once the discontinuance is processed
+ by USAC. This typically takes 2-4 weeks.
+
+
+ Order: {order_number}
+ Questions? Reply to this email or contact
+ ops@performancewest.net.
+
+
+
+ """
+ msg = MIMEMultipart("alternative")
+ msg["From"] = os.environ.get("SMTP_FROM", "Performance West ")
+ msg["To"] = to
+ msg["Subject"] = subject
+ msg.attach(MIMEText(html, "html"))
+
+ with smtplib.SMTP(
+ os.environ.get("SMTP_HOST", "co.carrierone.com"),
+ int(os.environ.get("SMTP_PORT", "587")),
+ timeout=30,
+ ) as s:
+ s.starttls()
+ s.login(os.environ.get("SMTP_USER", ""), os.environ.get("SMTP_PASS", ""))
+ s.send_message(msg)
+ except Exception as exc:
+ logger.warning("Discontinuance confirmation email failed: %s", exc)
diff --git a/scripts/workers/services/form_499q.py b/scripts/workers/services/form_499q.py
new file mode 100644
index 0000000..b0da0e4
--- /dev/null
+++ b/scripts/workers/services/form_499q.py
@@ -0,0 +1,130 @@
+"""FCC Form 499-Q Quarterly Filing Handler.
+
+Simplified filing: the client submits quarterly revenue via the intake page,
+and this handler files it at USAC E-File. Revenue calculations are minimal
+compared to the 499-A — just four revenue buckets projected for the quarter.
+
+The 499-Q determines quarterly USF contribution payments.
+"""
+from __future__ import annotations
+
+import logging
+import os
+from datetime import datetime
+
+from .base_handler import BaseComplianceHandler
+
+logger = logging.getLogger("workers.services.form_499q")
+
+
+class Form499QHandler(BaseComplianceHandler):
+ SERVICE_SLUG = "fcc-499q"
+ SERVICE_NAME = "FCC Form 499-Q Quarterly Filing"
+
+ async def process(self, order_data: dict) -> dict | None:
+ order_number = order_data.get("order_number", "")
+ entity = order_data.get("entity", {})
+ intake_data = order_data.get("intake_data", {})
+
+ if not intake_data.get("intake_completed"):
+ logger.info(
+ "Form499QHandler: %s intake not completed — waiting for client",
+ order_number,
+ )
+ self._create_admin_todo(
+ order_number,
+ f"499-Q {intake_data.get('quarter', '?')} for "
+ f"{entity.get('legal_name', '?')} — awaiting client intake. "
+ f"Due {intake_data.get('due_date', '?')}.",
+ )
+ return None
+
+ quarter = intake_data.get("quarter", "?")
+ revenue = intake_data.get("revenue", {})
+ filer_id = intake_data.get("filer_id_499") or entity.get("filer_id_499", "")
+ frn = intake_data.get("frn") or entity.get("frn", "")
+
+ logger.info(
+ "Form499QHandler: processing %s %s for %s (total: $%.2f)",
+ order_number, quarter,
+ entity.get("legal_name", "?"),
+ revenue.get("total", 0),
+ )
+
+ # Create admin todo with filing instructions
+ # (Full Playwright automation for USAC E-File 499-Q TBD)
+ self._create_admin_todo(
+ order_number,
+ f"FILE 499-Q {quarter} for {entity.get('legal_name', '?')} "
+ f"(FRN: {frn}, Filer ID: {filer_id})\n\n"
+ f"Revenue:\n"
+ f" Carrier's Carrier Interstate: ${revenue.get('carriers_carrier_interstate', 0):.2f}\n"
+ f" Carrier's Carrier Intrastate: ${revenue.get('carriers_carrier_intrastate', 0):.2f}\n"
+ f" End-User Interstate: ${revenue.get('end_user_interstate', 0):.2f}\n"
+ f" End-User Intrastate: ${revenue.get('end_user_intrastate', 0):.2f}\n"
+ f" Total: ${revenue.get('total', 0):.2f}\n\n"
+ f"Due: {intake_data.get('due_date', '?')}\n"
+ f"Parent 499-A: {intake_data.get('parent_499a_order', '?')}\n\n"
+ f"File at: https://forms.universalservice.org/",
+ )
+
+ # Send confirmation email to client
+ self._send_confirmation_email(
+ to=entity.get("contact_email") or order_data.get("customer_email", ""),
+ entity_name=entity.get("legal_name", ""),
+ order_number=order_number,
+ quarter=quarter,
+ due_date=intake_data.get("due_date", ""),
+ )
+
+ return {"status": "submitted_for_filing"}
+
+ def _send_confirmation_email(
+ self, to: str, entity_name: str, order_number: str,
+ quarter: str, due_date: str,
+ ) -> None:
+ if not to:
+ return
+ try:
+ import smtplib
+ from email.mime.multipart import MIMEMultipart
+ from email.mime.text import MIMEText
+
+ subject = f"499-Q {quarter} Filing Received — {entity_name}"
+ html = f"""
+
+
+
Form 499-Q {quarter} — Filing Received
+
+
+
Your FCC Form 499-Q quarterly revenue data for {entity_name}
+ ({quarter}, due {due_date}) has been received.
+
We'll file this with USAC E-File and send you a confirmation with your
+ filing reference number once complete.
+
+ Order: {order_number}
+ Questions? Reply to this email or contact
+ ops@performancewest.net.
+
+
+
+ """
+ msg = MIMEMultipart("alternative")
+ msg["From"] = os.environ.get("SMTP_FROM", "Performance West ")
+ msg["To"] = to
+ msg["Subject"] = subject
+ msg.attach(MIMEText(html, "html"))
+
+ with smtplib.SMTP(
+ os.environ.get("SMTP_HOST", "co.carrierone.com"),
+ int(os.environ.get("SMTP_PORT", "587")),
+ timeout=30,
+ ) as s:
+ s.starttls()
+ s.login(
+ os.environ.get("SMTP_USER", ""),
+ os.environ.get("SMTP_PASS", ""),
+ )
+ s.send_message(msg)
+ except Exception as exc:
+ logger.warning("499-Q confirmation email failed: %s", exc)
diff --git a/site/public/order/fcc-499q/index.html b/site/public/order/fcc-499q/index.html
new file mode 100644
index 0000000..4a9e4c8
--- /dev/null
+++ b/site/public/order/fcc-499q/index.html
@@ -0,0 +1,235 @@
+
+
+
+
+
+FCC Form 499-Q Quarterly Filing — Performance West Inc.
+
+
+
+
+
+
+
+
FCC Form 499-Q — Quarterly Filing
+
Report your projected quarterly USF contributions based on actual telecom revenue for the prior quarter.
+
+
Loading order details...
+
+
+
+
+
+
499-Q Filing Submitted
+
+
+
+
+
+
+
+
+
+
diff --git a/site/public/order/fcc-compliance/index.html b/site/public/order/fcc-compliance/index.html
index 961a8e1..e25c021 100644
--- a/site/public/order/fcc-compliance/index.html
+++ b/site/public/order/fcc-compliance/index.html
@@ -145,6 +145,7 @@ var CHECK_TO_SLUG = {
cpni:"cpni-certification", cpni_certification:"cpni-certification",
"499a":"fcc-499a", form_499a:"fcc-499a",
form_499a_zero:"fcc-499a-zero", "499a-zero":"fcc-499a-zero",
+ form_499a_disc:"fcc-499a-discontinuance", "499a-disc":"fcc-499a-discontinuance",
"499q":"fcc-499a-499q", form_499q:"fcc-499a-499q",
rmd:"rmd-filing", rmd_filing:"rmd-filing",
stir:"stir-shaken", stir_shaken:"stir-shaken",
@@ -161,6 +162,7 @@ var SLUG_META = {
"cpni-certification": {label:"CPNI Annual Certification", price:14900, desc:"47 CFR § 64.2009 annual CPNI cert filed to FCC ECFS", order:"/order/cpni-certification"},
"fcc-499a": {label:"FCC Form 499-A Filing", price:49900, desc:"Annual USF contribution filing at USAC E-File", order:"/order/fcc-499a"},
"fcc-499a-zero": {label:"FCC Form 499-A (Zero Revenue)", price:17900, desc:"For carriers with no telecom revenue", order:"/order/fcc-499a"},
+ "fcc-499a-discontinuance":{label:"Form 499-A Discontinuance", price:29900, desc:"Close out USAC filer registration", order:"/order/fcc-499a"},
"fcc-499a-499q": {label:"FCC 499-A + 499-Q Bundle", price:59900, desc:"Annual + quarterly USF projections", order:"/order/fcc-499a-499q"},
"rmd-filing": {label:"RMD Registration / Recertification", price:21900, gov_fee:10000, gov_fee_label:"FCC RMD filing fee", desc:"Robocall Mitigation Database filing + $100 FCC filing fee", order:"/order/rmd-filing"},
"stir-shaken": {label:"STIR/SHAKEN Implementation", price:49900, desc:"STIR/SHAKEN certificate + attestation setup", order:"/order/stir-shaken"},
diff --git a/site/src/pages/tools/fcc-compliance-check.astro b/site/src/pages/tools/fcc-compliance-check.astro
index be0d2fb..771f4f6 100644
--- a/site/src/pages/tools/fcc-compliance-check.astro
+++ b/site/src/pages/tools/fcc-compliance-check.astro
@@ -682,6 +682,7 @@ import Base from "../../layouts/Base.astro";
if (check.id === "form_499a" && (status === "red" || status === "yellow")) {
function resetF499() {
check.status = status;
+ check._499aVariant = null;
const cr = colorMap[status];
card.className = `${cr.bg} ${cr.border} border rounded-xl p-4 flex items-start gap-3`;
card.innerHTML = inner;
@@ -718,14 +719,16 @@ import Base from "../../layouts/Base.astro";
card.querySelector(".flex-1")?.appendChild(followUp);
followUp.querySelector(".f499-keep")?.addEventListener("click", () => {
+ check._499aVariant = "zero";
showF499Result(
`${eName} had no telecom revenue but wants to keep the filer registration active. A zero-revenue 499-A must be filed for each year with an active filer ID. We can file these for you.`,
"yellow"
);
});
followUp.querySelector(".f499-deregister")?.addEventListener("click", () => {
+ check._499aVariant = "discontinuance";
showF499Result(
- `${eName} wants to cancel the USAC filer registration. All outstanding 499-A filings must be current first (including zero-revenue returns). We can handle the catch-up filings and submit the cancellation request to USAC on your behalf.`,
+ `${eName} wants to cancel the USAC filer registration. We'll file any outstanding zero-revenue 499-A returns and submit the discontinuance request to USAC on your behalf.`,
"yellow"
);
});
@@ -777,8 +780,14 @@ import Base from "../../layouts/Base.astro";
services.push({ id: "stir_shaken", name: "STIR/SHAKEN Implementation", desc: "", price: 499 });
break;
case "form_499a":
- services.push({ id: "form_499a", name: "Form 499-A Filing", desc: "with revenue", price: 499 });
- services.push({ id: "form_499a_zero", name: "Form 499-A (Zero Revenue)", desc: "no telecom revenue", price: 179, altOf: "form_499a" });
+ if (check._499aVariant === "discontinuance") {
+ services.push({ id: "form_499a_disc", name: "Form 499-A Discontinuance", desc: "close filer account", price: 299 });
+ } else if (check._499aVariant === "zero") {
+ services.push({ id: "form_499a_zero", name: "Form 499-A (Zero Revenue)", desc: "no telecom revenue", price: 179 });
+ } else {
+ services.push({ id: "form_499a", name: "Form 499-A Filing", desc: "with revenue", price: 499 });
+ services.push({ id: "form_499a_zero", name: "Form 499-A (Zero Revenue)", desc: "no telecom revenue", price: 179, altOf: "form_499a" });
+ }
if (s === "red") {
services.push({ id: "dc_agent", name: "D.C. Registered Agent", desc: "", price: 99, priceLabel: "$99/yr" });
}