Initial commit — Performance West telecom compliance platform

Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-04-27 06:54:22 -05:00
commit f8cd37ac8c
1823 changed files with 145167 additions and 0 deletions

View file

@ -0,0 +1,97 @@
// FCC Constants — safe harbor %s, de minimis factors, Line 105 ticking rules
//
// The authoritative source of these values is USAC's annual Form 499-A
// Instructions (released each November). Kept here for client-side use in
// the intake wizard (display defaults, validation). The server-side
// lookup goes against fcc_deminimis_factors + fcc_safe_harbor_percentages
// DB tables — so this file mirrors those and must stay in sync.
//
// Authority: 2026 Form 499-A Instructions
// docs/fcc-references/2026-FCC-Form-499A-Form-Instructions.pdf
export const US_STATES_AND_TERRITORIES: string[] = [
"AL","AK","AZ","AR","CA","CO","CT","DE","DC","FL","GA","HI","ID","IL","IN",
"IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH",
"NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT",
"VT","VA","WA","WV","WI","WY",
// US territories (Line 227 requires these as well)
"PR","GU","VI","AS","MP","UM", // Puerto Rico, Guam, USVI, Am. Samoa, N. Mariana, US Minor Outlying
];
// ── De minimis factor by form year ──────────────────────────────────────
// Per 2026 Form 499-A Appendix A Line 10 = 0.256. The factor changes
// annually; keep in sync with fcc_deminimis_factors table.
export const DE_MINIMIS_FACTOR_BY_YEAR: Record<number, number> = {
2026: 0.2560,
};
// De minimis exemption threshold ($)
export const DE_MINIMIS_THRESHOLD_USD = 10000;
// ── Safe harbor percentages by form year + category ─────────────────────
// Per 2026 Form 499-A Section IV.C.5.g. Non-interconnected VoIP has NO
// safe harbor — must use traffic study or actual data.
export const SAFE_HARBOR_PCT_BY_YEAR: Record<
number,
Record<string, number>
> = {
2026: {
voip_interconnected: 64.9,
cellular_pcs: 37.1,
paging: 12.0,
smr_dispatch: 1.0,
},
};
// Categories for which safe-harbor election is DISALLOWED (must use traffic study or actual data)
export const SAFE_HARBOR_DISALLOWED_CATEGORIES: Set<string> = new Set([
"voip_non_interconnected",
]);
// ── Officer-definition help text (Lines 221/223/225) ────────────────────
// Verbatim from 2026 instructions, paraphrased slightly for clarity.
export const OFFICER_DEFINITION_HELP_TEXT =
"An officer is an occupant of a position listed in the articles of "
+ "incorporation, articles of formation, or other similar legal document. "
+ "For corporations and LLCs, list the CEO/President/Chairman (and if "
+ "the same person holds multiple, the next-most-senior officers). For "
+ "sole proprietorships, list the owner only. For partnerships, list "
+ "the managing partner plus the two partners with the greatest "
+ "financial interest.";
// ── Form 499-A filing types (Line 612) ──────────────────────────────────
export type FilingType =
| "original_april_1"
| "registration_new_filer"
| "revised_registration"
| "revised_revenue";
export const FILING_TYPE_LABELS: Record<FilingType, string> = {
original_april_1: "Original Filing (April 1)",
registration_new_filer: "Registration — New Filer",
revised_registration: "Revised Filing (Registration Info)",
revised_revenue: "Revised Filing (Revenue Info)",
};
// ── Reseller certification — sample language (FCC Section IV.C.4) ───────
// Near-verbatim from 2026 instructions. Used as default text in
// ResellerCertStep.astro for the signed certification body.
export const RESELLER_CERTIFICATION_SAMPLE_TEXT =
"I certify under penalty of perjury that the company is purchasing "
+ "service(s) for resale, at least in part, and that the company is "
+ "incorporating the purchased services into its own offerings which are, "
+ "at least in part, assessable U.S. telecommunications or interconnected "
+ "Voice over Internet Protocol services. I also certify under penalty "
+ "of perjury that the company either directly contributes or has a "
+ "reasonable expectation that another entity in the downstream chain "
+ "of resellers directly contributes to the federal universal service "
+ "support mechanisms on the assessable portion of revenues from "
+ "offerings that incorporate the purchased services.";
// ── Nondisclosure (Line 605) certification text ─────────────────────────
export const NONDISCLOSURE_CERT_TEXT =
"By checking this box, the officer of the company signing the Worksheet "
+ "certifies that the information contained on the Worksheet is "
+ "privileged or confidential commercial or financial information and "
+ "that disclosure of such information would likely cause substantial "
+ "harm to the competitive position of the company filing the Worksheet.";

View file

@ -0,0 +1,143 @@
/**
* Per-service intake step manifest.
*
* Each service slug maps to the ordered list of wizard steps. The Wizard
* component inserts category-gated dynamic steps (wireless, earth_station,
* audio_bridging, icc_import, reseller_cert) at runtime based on the
* user's Line 105 selection, between the declared step markers.
*
* Keep in sync with:
* - api/src/routes/compliance-orders.ts REQUIRED_FIELDS
* - scripts/workers/services/__init__.py SERVICE_HANDLERS registry
*/
export type IntakeStep =
| "entity"
| "category" // NEW: Line 105 ranked multi-select
| "officer"
| "jurisdiction"
| "history" // NEW: Line 228 first-service date
| "wireless" // NEW: category-gated — inserted when wireless selected
| "earth_station" // NEW: category-gated — satellite + private_line
| "audio_bridging" // NEW: category-gated — audio_bridging
| "revenue"
| "bundled_service" // NEW: local+toll bundle allocation
| "icc_import" // NEW: ICC revenue file uploads
| "reseller_cert" // NEW: Line 303 reseller certifications
| "lnpa_region" // NEW: Block 5 Lines 503-510 LNPA regions
| "block6_cert" // NEW: exemptions + nondisclosure + filing type
| "bdc_data"
| "stir_shaken"
| "calea"
| "foreign_carrier"
| "foreign_qual" // NEW: multi-state COA picker
| "dc_agent" // NEW: DC registered agent choice
| "cpni_questions" // NEW: CPNI certification questionnaire (sec 5-9)
| "cdr_period"
| "ocn"
| "review"
| "payment";
export const INTAKE_MANIFEST: Record<string, IntakeStep[]> = {
// Diagnostic — lightweight; just needs the entity
"fcc-compliance-checkup": ["entity", "review", "payment"],
// Entry-point filings
"cores-frn-registration": ["entity", "officer", "review", "payment"],
"fcc-499-initial": [
"entity", "classification", "category", "officer", "jurisdiction", "history",
"dc_agent", "block6_cert", "review", "payment",
],
// Annual + quarterly revenue filings — the full fidelity flow
"fcc-499a": [
"entity", "classification", "category", "officer", "jurisdiction", "history",
"dc_agent", "revenue", "bundled_service", "icc_import",
"reseller_cert", "lnpa_region",
"block6_cert", "review", "payment",
],
"fcc-499a-499q": [
"entity", "classification", "category", "officer", "jurisdiction",
"revenue", "icc_import",
"block6_cert", "review", "payment",
],
// RMD + CPNI + STIR/SHAKEN
"rmd-filing": ["entity", "officer", "stir_shaken", "review", "payment"],
"cpni-certification": ["entity", "officer", "cpni_questions", "review", "payment"],
"stir-shaken": ["entity", "stir_shaken", "review", "payment"],
// BDC tiers (formerly Form 477)
"bdc-broadband": ["entity", "bdc_data", "review", "payment"],
"bdc-voice": ["entity", "bdc_data", "review", "payment"],
"bdc-filing": ["entity", "bdc_data", "review", "payment"],
// CALEA + foreign-affiliation
"calea-ssi": ["entity", "category", "calea", "review", "payment"],
"fcc-63-11-notification": ["entity", "foreign_carrier", "review", "payment"],
// Supporting services
"ocn-registration": ["entity", "ocn", "review", "payment"],
"dc-agent": ["entity", "review", "payment"],
"cdr-analysis": ["entity", "cdr_period", "review", "payment"],
// Foreign qualification — Certificate of Authority across US states
"foreign-qualification-single": [
"entity", "foreign_qual", "review", "payment",
],
"foreign-qualification-multi": [
"entity", "foreign_qual", "review", "payment",
],
// Bundles
"fcc-full-compliance": [
"entity", "category", "officer", "jurisdiction", "history",
"revenue", "stir_shaken", "icc_import",
"reseller_cert", "lnpa_region", "block6_cert",
"review", "payment",
],
"new-carrier-bundle": [
"entity", "category", "officer", "jurisdiction", "history",
"dc_agent", "calea", "review", "payment",
],
};
// Category-gated dynamic steps. The Wizard inserts these after the `category`
// step when the corresponding Line 105 category is in line_105_categories.
export const CATEGORY_GATED_STEPS: Record<string, IntakeStep[]> = {
wireless: ["wireless"],
satellite: ["earth_station"],
mobile_satellite:["earth_station"],
private_line: ["earth_station"],
audio_bridging: ["audio_bridging"],
};
// ── Pricing + human name lookup (mirrors COMPLIANCE_SERVICES server-side)
export const SERVICE_META: Record<string, { name: string; price_cents: number }> = {
"fcc-compliance-checkup": { name: "FCC Carrier Compliance Checkup", price_cents: 79900 },
"fcc-499a": { name: "FCC Form 499-A Filing", price_cents: 49900 },
"fcc-499a-499q": { name: "FCC Form 499-A + 499-Q Bundle", price_cents: 59900 },
"fcc-full-compliance": { name: "FCC Full Compliance Bundle", price_cents: 149900 },
"cpni-certification": { name: "CPNI Annual Certification", price_cents: 14900 },
"rmd-filing": { name: "RMD Registration / Recertification", price_cents: 21900 }, // + $100 FCC filing fee passthrough
"stir-shaken": { name: "STIR/SHAKEN Implementation Assistance", price_cents: 49900 },
"dc-agent": { name: "D.C. Registered Agent (Annual)", price_cents: 9900 },
"bdc-broadband": { name: "BDC Broadband Deployment Filing", price_cents: 19900 },
"bdc-voice": { name: "BDC Voice Subscription (formerly Form 477)", price_cents: 14900 },
"bdc-filing": { name: "BDC Filing (Broadband + Voice)", price_cents: 29900 },
"ocn-registration": { name: "NECA OCN / Company Code Registration", price_cents: 65000 },
"cdr-analysis": { name: "CDR Traffic Study (Annual)", price_cents: 49900 },
"cores-frn-registration": { name: "CORES / FRN Registration", price_cents: 9900 },
"fcc-499-initial": { name: "Form 499 Initial Registration", price_cents: 29900 },
"calea-ssi": { name: "CALEA SSI Plan", price_cents: 29900 },
"fcc-63-11-notification": { name: "Foreign Carrier Affiliation Notification", price_cents: 34900 },
"new-carrier-bundle": { name: "New Carrier Onboarding Bundle", price_cents: 179900 },
"foreign-qualification-single": { name: "Foreign Qualification (One State)", price_cents: 14900 },
"foreign-qualification-multi": { name: "Foreign Qualification (Multi-State)", price_cents: 9900 },
};
export function formatUSD(cents: number): string {
return `$${(cents / 100).toLocaleString("en-US", {
minimumFractionDigits: 2, maximumFractionDigits: 2,
})}`;
}

View file

@ -0,0 +1,302 @@
// Line 105 Category Catalog — FCC Form 499-A Appendix B (2026 edition)
//
// The 2026 instructions enumerate 22 Line 105 categories, grouped in this
// file by how much intake flow each gets:
// * first_class — dedicated intake step, CPNI/CALEA variant, special
// revenue-line logic
// * subtype_of — not a standalone category; marks a CLEC/IXC/Wireless
// with infra_type=reseller/mvno, or Satellite with a
// subservice flag. form_499a.py still ticks the box on
// the form; handler behavior mirrors the parent.
// * flag_only — we tick the Line 105 box correctly, nothing else
// special (revenue flows through standard lines)
// * deprecated — admin ToDo only; we don't support new flows for
// these categories, too rare to prioritize
//
// The catalog drives CategoryStep.astro (ranked multi-select UI) and the
// handler's form-filling logic.
export type Line105Status =
| "first_class"
| "subtype_of"
| "flag_only"
| "deprecated";
export type Line105Grouping =
| "common" // shown expanded by default
| "carrier_grade" // collapsed; "expand to see more carrier types"
| "specialized" // collapsed; satellite / audio-bridging / MSS
| "rare"; // collapsed; "handled manually" warning
export interface Line105Entry {
id: string; // matches form_499a.py handler enum
label: string; // UI label
fcc_box_number: number; // the numbered box on the paper form
status: Line105Status;
grouping: Line105Grouping;
parent?: string; // when status=subtype_of, the parent category id
tooltip: string; // short plain-English help
reseller_offered?: boolean; // show "Do you own facilities or resell?" sub-question
tdm_offered?: boolean; // show "TDM or packet?" under reseller
mvno_offered?: boolean; // show "Do you own spectrum or are you an MVNO?"
}
export const LINE_105_CATALOG: Line105Entry[] = [
// ── COMMON (expanded by default) ───────────────────────────────────────
{
id: "voip_interconnected",
label: "Interconnected VoIP provider",
fcc_box_number: 1,
status: "first_class",
grouping: "common",
tooltip:
"VoIP service that interconnects with the PSTN and allows customers "
+ "to make and receive calls to/from ordinary phone numbers. Safe "
+ "harbor of 64.9% interstate available.",
},
{
id: "voip_non_interconnected",
label: "Non-interconnected (one-way) VoIP",
fcc_box_number: 2,
status: "first_class",
grouping: "common",
tooltip:
"VoIP that only makes OR only receives calls (not both). No safe "
+ "harbor — requires traffic study or actual billing data.",
},
{
id: "clec",
label: "Competitive Local Exchange Carrier (CLEC)",
fcc_box_number: 3,
status: "first_class",
grouping: "common",
tooltip:
"Competitive local carrier offering wireline telephone service in "
+ "direct competition with the incumbent (ILEC).",
reseller_offered: true,
tdm_offered: true,
},
{
id: "ixc",
label: "Interexchange Carrier (IXC)",
fcc_box_number: 7,
status: "first_class",
grouping: "common",
tooltip:
"Long-distance (toll) carrier. Access-charge collection matters here "
+ "— originating access and 8YY revenue belong on Line 404.x.",
reseller_offered: true,
tdm_offered: true,
},
{
id: "wireless",
label: "Wireless / Cellular / PCS (CMRS)",
fcc_box_number: 8,
status: "first_class",
grouping: "common",
tooltip:
"Commercial Mobile Radio Service — cellular, PCS. Safe harbor of "
+ "37.1% interstate. MVNOs are a subtype below.",
mvno_offered: true,
},
// ── CARRIER_GRADE (collapsed) ──────────────────────────────────────────
{
id: "ilec",
label: "Incumbent LEC (ILEC)",
fcc_box_number: 4,
status: "flag_only",
grouping: "carrier_grade",
tooltip:
"Historical local exchange carrier. We don't onboard ILECs — select "
+ "this and our team will reach out.",
},
{
id: "local_reseller",
label: "Local Reseller (TDM era)",
fcc_box_number: 5,
status: "subtype_of",
parent: "clec",
grouping: "carrier_grade",
tooltip:
"Historical category for carriers that buy wholesale from an ILEC "
+ "and rebrand. If this describes you, check CLEC above and set "
+ "infrastructure = reseller.",
},
{
id: "toll_reseller",
label: "Toll Reseller",
fcc_box_number: 6,
status: "subtype_of",
parent: "ixc",
grouping: "carrier_grade",
tooltip:
"Long-distance reseller buying wholesale minutes. Check IXC above "
+ "and set infrastructure = reseller.",
},
{
id: "private_line",
label: "Private Line / Business Data Service",
fcc_box_number: 11,
status: "first_class",
grouping: "carrier_grade",
tooltip:
"Dedicated point-to-point circuits (DS-1/DS-3/Ethernet). Includes "
+ "special-access and BDS services.",
},
{
id: "toll_free",
label: "Toll-Free (8YY) Service",
fcc_box_number: 17,
status: "flag_only",
grouping: "carrier_grade",
tooltip:
"Customer-paid-calling (8YY) service provider. 8YY originating "
+ "access revenue flows through ICC imports.",
},
// ── SPECIALIZED (collapsed) ─────────────────────────────────────────────
{
id: "mvno",
label: "Mobile Virtual Network Operator (MVNO)",
fcc_box_number: 9,
status: "subtype_of",
parent: "wireless",
grouping: "specialized",
tooltip:
"Wireless service that uses another carrier's network (e.g., buying "
+ "from T-Mobile / Verizon / AT&T). Check Wireless above and set "
+ "infrastructure = MVNO.",
},
{
id: "satellite",
label: "Satellite Service",
fcc_box_number: 12,
status: "first_class",
grouping: "specialized",
tooltip:
"Earth-station based satellite service (FSS / BSS). Requires FCC "
+ "Form 312 earth station license.",
},
{
id: "mobile_satellite",
label: "Mobile Satellite Service (MSS)",
fcc_box_number: 21,
status: "subtype_of",
parent: "satellite",
grouping: "specialized",
tooltip:
"Mobile satellite (Iridium, Globalstar, Starlink Commercial mobile). "
+ "Check Satellite above and set service_type=MSS.",
},
{
id: "audio_bridging",
label: "Audio Bridging / Conferencing Service",
fcc_box_number: 16,
status: "first_class",
grouping: "specialized",
tooltip:
"Multi-party audio conferencing provider. FCC clarified in 2021 "
+ "that bridging in interstate commerce is telecom service.",
},
{
id: "fixed_wireless",
label: "Fixed Wireless (non-mobile)",
fcc_box_number: 20,
status: "subtype_of",
parent: "clec",
grouping: "specialized",
tooltip:
"Fixed wireless last-mile local service. Check CLEC above and set "
+ "infrastructure = facilities + wireless_metadata.",
},
{
id: "prepaid_calling_card",
label: "Prepaid Calling Card",
fcc_box_number: 10,
status: "flag_only",
grouping: "specialized",
tooltip:
"Prepaid long-distance cards. Revenue goes on Line 411 with special "
+ "safe-harbor rules.",
},
// ── RARE / DEPRECATED (collapsed with warning) ─────────────────────────
{
id: "payphone",
label: "Payphone Service Provider (PSP)",
fcc_box_number: 13,
status: "deprecated",
grouping: "rare",
tooltip:
"Rare in 2026 — if this describes you, our team will handle manually.",
},
{
id: "osp",
label: "Operator Service Provider (OSP)",
fcc_box_number: 14,
status: "flag_only",
grouping: "rare",
tooltip: "Operator-assisted call service provider.",
},
{
id: "shared_tenant",
label: "Shared Tenant Services",
fcc_box_number: 15,
status: "flag_only",
grouping: "rare",
tooltip: "Shared tenant telecommunications (e.g., building-wide PBX resale).",
},
{
id: "paging",
label: "Paging / Messaging",
fcc_box_number: 18,
status: "deprecated",
grouping: "rare",
tooltip: "Standalone paging — rare in 2026. Our team will handle manually.",
},
{
id: "smr",
label: "Specialized Mobile Radio (SMR)",
fcc_box_number: 19,
status: "deprecated",
grouping: "rare",
tooltip: "Specialized mobile radio dispatch. Rare — our team will handle manually.",
},
{
id: "other",
label: "Other (describe separately)",
fcc_box_number: 22,
status: "flag_only",
grouping: "rare",
tooltip:
"Any category not listed above. Our team will follow up for a "
+ "free-text description required on the form.",
},
];
// Convenience lookups
export const LINE_105_BY_ID: Record<string, Line105Entry> = Object.fromEntries(
LINE_105_CATALOG.map((c) => [c.id, c]),
);
export const LINE_105_GROUPS: Record<Line105Grouping, Line105Entry[]> =
LINE_105_CATALOG.reduce((acc, entry) => {
(acc[entry.grouping] ||= []).push(entry);
return acc;
}, {} as Record<Line105Grouping, Line105Entry[]>);
// Map a category id → the extra Line 105 box that gets auto-ticked because
// of the infra_type/mvno flag. form_499a.py mirrors this logic.
export function derivedLine105Boxes(
categoryId: string,
infra_type?: string,
): number[] {
const boxes: number[] = [];
if (infra_type === "reseller") {
if (categoryId === "clec") boxes.push(5); // local_reseller
if (categoryId === "ixc") boxes.push(6); // toll_reseller
}
if (infra_type === "mvno" && categoryId === "wireless") boxes.push(9);
return boxes;
}

View file

@ -0,0 +1,113 @@
// LNPA Region Catalog — 10 regions for Form 499-A Block 5 Lines 503-510
//
// The NANPA/LNPA regions as defined by iconectiv NPAC. Every filer with
// telecom revenue must allocate Block 3 (carrier's carrier) and Block 4
// (end user) revenues across these regions as percentages summing to 100%
// per column.
//
// Used by LNPARegionStep.astro to render the 10-region grid.
export interface LnpaRegion {
code: string;
label: string;
states: string[]; // postal codes of states in the region
description: string;
}
export const LNPA_REGIONS: LnpaRegion[] = [
{
code: "NE",
label: "Northeast",
states: ["CT", "MA", "ME", "NH", "NY", "RI", "VT"],
description: "Connecticut, Massachusetts, Maine, New Hampshire, New York, Rhode Island, Vermont",
},
{
code: "MA",
label: "Mid-Atlantic",
states: ["DC", "DE", "MD", "NJ", "PA", "VA", "WV"],
description: "DC, Delaware, Maryland, New Jersey, Pennsylvania, Virginia, West Virginia",
},
{
code: "SE",
label: "Southeast",
states: ["AL", "FL", "GA", "KY", "MS", "NC", "SC", "TN"],
description: "Alabama, Florida, Georgia, Kentucky, Mississippi, North Carolina, South Carolina, Tennessee",
},
{
code: "SC",
label: "Southern / South Central",
states: ["AR", "LA", "NM", "OK"],
description: "Arkansas, Louisiana, New Mexico, Oklahoma",
},
{
code: "TX",
label: "Texas",
states: ["TX"],
description: "Texas",
},
{
code: "MW",
label: "Midwest",
states: ["IL", "IN", "MI", "OH", "WI"],
description: "Illinois, Indiana, Michigan, Ohio, Wisconsin",
},
{
code: "IA",
label: "Iowa / Plains",
states: ["IA", "KS", "MN", "MO", "ND", "NE", "SD"],
description: "Iowa, Kansas, Minnesota, Missouri, North Dakota, Nebraska, South Dakota",
},
{
code: "RM",
label: "Rocky Mountain",
states: ["CO", "ID", "MT", "UT", "WY"],
description: "Colorado, Idaho, Montana, Utah, Wyoming",
},
{
code: "NW",
label: "Northwest",
states: ["AK", "OR", "WA"],
description: "Alaska, Oregon, Washington",
},
{
code: "WC",
label: "West Coast",
states: ["AZ", "CA", "HI", "NV"],
description: "Arizona, California, Hawaii, Nevada",
},
];
export const LNPA_REGION_BY_CODE: Record<string, LnpaRegion> = Object.fromEntries(
LNPA_REGIONS.map((r) => [r.code, r]),
);
// Given a state postal code, return the LNPA region that contains it.
export function stateToRegion(state: string): string | null {
const upper = state.toUpperCase();
for (const r of LNPA_REGIONS) {
if (r.states.includes(upper)) return r.code;
}
return null;
}
// Convert a state → revenue map (e.g., from CDR billing state distribution)
// into a region → percentage map. Used by the "Paste from billing
// distribution" helper in LNPARegionStep.
export function stateDistributionToRegionPct(
stateRevenue: Record<string, number>,
): Record<string, number> {
const regionTotals: Record<string, number> = {};
let grandTotal = 0;
for (const [state, revenue] of Object.entries(stateRevenue)) {
const region = stateToRegion(state);
if (!region) continue;
regionTotals[region] = (regionTotals[region] || 0) + revenue;
grandTotal += revenue;
}
if (grandTotal === 0) return {};
const regionPct: Record<string, number> = {};
for (const r of LNPA_REGIONS) {
regionPct[r.code] = Math.round((regionTotals[r.code] || 0) / grandTotal * 10000) / 100;
}
return regionPct;
}

View file

@ -0,0 +1,79 @@
// Safe harbor recommender — client-side wrapper around
// GET /api/v1/fcc/safe-harbor-recommendation.
//
// Consumed by RevenueStep to display a contextual recommendation next
// to the method dropdown. The server does the actual comparison against
// the customer's CDR traffic study — this is just a thin fetch wrapper.
export type Recommendation =
| "safe_harbor_okay"
| "use_actual_saves"
| "use_actual_required"
| "no_actual_data"
| "not_allowed"
| "no_safe_harbor_for_category";
export interface SafeHarborRecommendation {
recommendation: Recommendation;
category: string;
year: number;
safe_harbor_pct: number | null;
actual_interstate_pct: number | null;
delta_percentage_points?: number;
estimated_usf_delta_cents?: number;
total_revenue_cents?: number;
study_methodology?: string | null;
total_classified_calls?: number;
message: string;
}
export async function fetchSafeHarborRecommendation(opts: {
profile_id?: number | null;
year: number;
category: string;
total_revenue_cents?: number;
}): Promise<SafeHarborRecommendation | null> {
const params = new URLSearchParams({
year: String(opts.year),
category: opts.category,
});
if (opts.profile_id) params.set("profile_id", String(opts.profile_id));
if (opts.total_revenue_cents) {
params.set("total_revenue_cents", String(opts.total_revenue_cents));
}
try {
const r = await fetch(`/api/v1/fcc/safe-harbor-recommendation?${params.toString()}`);
if (!r.ok) return null;
return await r.json();
} catch {
return null;
}
}
/** UI hint: banner color + preferred method for a recommendation. */
export function recommendationBadge(rec: Recommendation): {
tone: "ok" | "info" | "warn" | "danger";
suggest: "safe_harbor" | "actual_data" | "traffic_study" | null;
headline: string;
} {
switch (rec) {
case "safe_harbor_okay":
return { tone: "ok", suggest: "safe_harbor",
headline: "Safe harbor is a good choice here." };
case "use_actual_saves":
return { tone: "info", suggest: "actual_data",
headline: "Using actual data would save you money." };
case "use_actual_required":
return { tone: "warn", suggest: "traffic_study",
headline: "Safe harbor would under-report — switch to traffic study." };
case "no_actual_data":
return { tone: "info", suggest: "safe_harbor",
headline: "No traffic study — safe harbor is the sensible default." };
case "not_allowed":
return { tone: "danger", suggest: "traffic_study",
headline: "Safe harbor not allowed for this category." };
case "no_safe_harbor_for_category":
return { tone: "danger", suggest: "actual_data",
headline: "No safe harbor defined for this category / year." };
}
}