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>
242 lines
12 KiB
Text
242 lines
12 KiB
Text
---
|
||
// Block6CertStep — Lines 603-605, 612 + de minimis election.
|
||
// Exemption certifications (USF/TRS/NANPA/LNP/ITSP), 501(c)/government,
|
||
// nondisclosure request, filing type, and the de minimis vs. regular
|
||
// election (with explainer).
|
||
import { NONDISCLOSURE_CERT_TEXT, FILING_TYPE_LABELS } from "../../../lib/fcc_constants";
|
||
import DeMinimisChoiceExplainer from "../DeMinimisChoiceExplainer.astro";
|
||
---
|
||
|
||
<div class="pw-step">
|
||
<h2>Certifications (Block 6)</h2>
|
||
|
||
<section class="pw-block" id="pw-demin-section">
|
||
<h3>De minimis filing election</h3>
|
||
<p class="pw-help">
|
||
Based on your revenue + safe-harbor interstate % and the current
|
||
year's de minimis factor, your estimated annual USF contribution is
|
||
<strong id="pw-demin-estimate">—</strong>. The exemption threshold
|
||
is $10,000 (Appendix A).
|
||
</p>
|
||
<div id="pw-demin-status-banner" class="pw-demin-banner" hidden></div>
|
||
|
||
<DeMinimisChoiceExplainer />
|
||
|
||
<label class="pw-field">How do you want to file?</label>
|
||
<select id="pw-demin-election" class="pw-input">
|
||
<option value="auto">Let the Appendix A calculation decide (standard)</option>
|
||
<option value="deminimis">File as de minimis — no USF contribution</option>
|
||
<option value="regular">File as regular contributor — pay USF directly (waive exemption)</option>
|
||
</select>
|
||
|
||
<div id="pw-waive-reason-wrap" hidden>
|
||
<label class="pw-field">Reason for filing regular (optional — shown to reviewers)</label>
|
||
<textarea id="pw-waive-reason" class="pw-input" rows="2"
|
||
placeholder="e.g., 'Wholesale SIP vendor charges us USF surcharge on trunking — filing regular so we can show Filer ID + reseller cert.'"></textarea>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="pw-block">
|
||
<h3>Line 603 — Claim exemption from contribution mechanisms</h3>
|
||
<p class="pw-help">
|
||
Check any mechanism you're exempt from. Exemptions require a written
|
||
explanation and evidence your legal team can produce on audit.
|
||
</p>
|
||
<div class="pw-cert-grid">
|
||
<label><input type="checkbox" id="pw-ex-usf" /> USF (Universal Service Fund)</label>
|
||
<label><input type="checkbox" id="pw-ex-trs" /> TRS (Telecom Relay Service)</label>
|
||
<label><input type="checkbox" id="pw-ex-nanpa" /> NANPA (numbering)</label>
|
||
<label><input type="checkbox" id="pw-ex-lnp" /> LNP (number portability)</label>
|
||
<label><input type="checkbox" id="pw-ex-itsp" /> ITSP (regulatory fees)</label>
|
||
</div>
|
||
<div id="pw-ex-expl-wrap" hidden>
|
||
<label class="pw-field">Explanation (required for any exemption)</label>
|
||
<textarea id="pw-ex-expl" class="pw-input" rows="3"></textarea>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="pw-block">
|
||
<h3>Line 604 — Organization type</h3>
|
||
<label><input type="checkbox" id="pw-gov" /> State or local government entity</label><br>
|
||
<label><input type="checkbox" id="pw-501c" /> 501(c) tax-exempt organization</label>
|
||
</section>
|
||
|
||
<section class="pw-block">
|
||
<h3>Line 605 — Confidential treatment of revenue data</h3>
|
||
<label>
|
||
<input type="checkbox" id="pw-nondisclosure" />
|
||
Request nondisclosure of revenue information
|
||
</label>
|
||
<p id="pw-nondisclosure-text" class="pw-cert-text" hidden></p>
|
||
</section>
|
||
|
||
<section class="pw-block">
|
||
<h3>Line 612 — Type of filing</h3>
|
||
<select id="pw-filing-type" class="pw-input">
|
||
<option value="original_april_1">Original Filing (April 1)</option>
|
||
<option value="registration_new_filer">Registration — New Filer</option>
|
||
<option value="revised_registration">Revised Filing (Registration Info)</option>
|
||
<option value="revised_revenue">Revised Filing (Revenue Info)</option>
|
||
</select>
|
||
</section>
|
||
|
||
<div id="pw-b6-err" class="pw-err" hidden></div>
|
||
</div>
|
||
|
||
<style>
|
||
.pw-step h2 { margin: 0 0 0.5rem; color: #1a2744; }
|
||
.pw-step h3 { margin: 0 0 0.4rem; color: #1a2744; font-size: 1rem; }
|
||
.pw-block { padding: 1rem; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 0.75rem; }
|
||
.pw-help { color: #64748b; font-size: 0.85rem; margin-bottom: 0.75rem; }
|
||
.pw-field { display: block; font-weight: 600; color: #1f2937; margin: 0.6rem 0 0.2rem; font-size: 0.88rem; }
|
||
.pw-input { width: 100%; padding: 0.5rem 0.7rem; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.93rem; font-family: inherit; }
|
||
.pw-demin-banner {
|
||
padding: 0.5rem 0.75rem; border-radius: 6px;
|
||
margin-top: 0.5rem; font-size: 0.88rem;
|
||
}
|
||
.pw-demin-banner.pw-exempt { background: #d1fae5; color: #065f46; border-left: 3px solid #059669; }
|
||
.pw-demin-banner.pw-no-exempt{ background: #fee2e2; color: #991b1b; border-left: 3px solid #dc2626; }
|
||
.pw-demin-banner.pw-near { background: #fef3c7; color: #92400e; border-left: 3px solid #d97706; }
|
||
.pw-cert-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.4rem; margin-bottom: 0.5rem; }
|
||
.pw-cert-grid label { font-size: 0.9rem; }
|
||
.pw-cert-text {
|
||
font-size: 0.82rem; color: #475569;
|
||
background: #f8fafc; padding: 0.5rem 0.75rem; border-radius: 6px;
|
||
margin-top: 0.5rem;
|
||
}
|
||
.pw-err { color: #b91c1c; margin-top: 0.75rem; font-size: 0.9rem; }
|
||
</style>
|
||
|
||
<script>
|
||
import { NONDISCLOSURE_CERT_TEXT } from "../../../lib/fcc_constants";
|
||
|
||
const exUsf = document.getElementById("pw-ex-usf") as HTMLInputElement;
|
||
const exTrs = document.getElementById("pw-ex-trs") as HTMLInputElement;
|
||
const exNanpa = document.getElementById("pw-ex-nanpa") as HTMLInputElement;
|
||
const exLnp = document.getElementById("pw-ex-lnp") as HTMLInputElement;
|
||
const exItsp = document.getElementById("pw-ex-itsp") as HTMLInputElement;
|
||
const exExplWrap = document.getElementById("pw-ex-expl-wrap") as HTMLElement;
|
||
const exExpl = document.getElementById("pw-ex-expl") as HTMLTextAreaElement;
|
||
const gov = document.getElementById("pw-gov") as HTMLInputElement;
|
||
const nfp = document.getElementById("pw-501c") as HTMLInputElement;
|
||
const nd = document.getElementById("pw-nondisclosure") as HTMLInputElement;
|
||
const ndText = document.getElementById("pw-nondisclosure-text") as HTMLElement;
|
||
const filingType = document.getElementById("pw-filing-type") as HTMLSelectElement;
|
||
const err = document.getElementById("pw-b6-err") as HTMLDivElement;
|
||
const deminElection = document.getElementById("pw-demin-election") as HTMLSelectElement;
|
||
const deminEstimate = document.getElementById("pw-demin-estimate") as HTMLElement;
|
||
const deminBanner = document.getElementById("pw-demin-status-banner") as HTMLElement;
|
||
const waiveReason = document.getElementById("pw-waive-reason") as HTMLTextAreaElement;
|
||
const waiveReasonWrap = document.getElementById("pw-waive-reason-wrap") as HTMLElement;
|
||
|
||
ndText.textContent = NONDISCLOSURE_CERT_TEXT;
|
||
|
||
async function refreshDeMinimisEstimate() {
|
||
const s = (window as any).PWIntake.get();
|
||
const year = Number(s.intake_data?.form_year) || new Date().getUTCFullYear() - 1;
|
||
const totalRev = Number(s.intake_data?.total_revenue_cents) || 0;
|
||
const interPct = Number(s.intake_data?.interstate_pct) || 0;
|
||
const intlPct = Number(s.intake_data?.international_pct) || 0;
|
||
if (!totalRev) {
|
||
deminEstimate.textContent = "— (enter revenue on the Revenue step first)";
|
||
deminBanner.hidden = true;
|
||
return;
|
||
}
|
||
// Quick estimate: contribution base × year factor. We fetch the
|
||
// factor via the late-filing-estimate endpoint (convenient wrapper).
|
||
try {
|
||
const r = await fetch(
|
||
`/api/v1/fcc/late-filing-estimate?year=${year}&total_revenue_cents=${totalRev}&interstate_pct=${interPct + intlPct}`,
|
||
);
|
||
if (!r.ok) throw new Error(`${r.status}`);
|
||
const data = await r.json();
|
||
const est = data.estimated_usf_cents;
|
||
const isExempt = est < 1000000; // $10,000 threshold
|
||
const nearThreshold = est < 1500000 && est >= 750000;
|
||
deminEstimate.textContent = `$${(est / 100).toLocaleString("en-US", { minimumFractionDigits: 2 })}`;
|
||
deminBanner.hidden = false;
|
||
if (isExempt) {
|
||
deminBanner.className = "pw-demin-banner pw-exempt";
|
||
deminBanner.textContent = `✓ You qualify as de minimis. Your estimated $${(est/100).toFixed(2)} annual contribution is below the $10,000 threshold.`;
|
||
} else if (nearThreshold) {
|
||
deminBanner.className = "pw-demin-banner pw-near";
|
||
deminBanner.textContent = `⚠ You're near the de minimis threshold. Small changes in interstate % could push you over — we recommend reviewing your traffic study carefully.`;
|
||
} else {
|
||
deminBanner.className = "pw-demin-banner pw-no-exempt";
|
||
deminBanner.textContent = `You do NOT qualify as de minimis. Estimated $${(est/100).toFixed(2)} exceeds the $10,000 threshold.`;
|
||
}
|
||
} catch {
|
||
deminEstimate.textContent = "— (could not reach server)";
|
||
}
|
||
}
|
||
|
||
deminElection.addEventListener("change", () => {
|
||
waiveReasonWrap.hidden = deminElection.value !== "regular";
|
||
});
|
||
|
||
function anyExempt() {
|
||
return exUsf.checked || exTrs.checked || exNanpa.checked || exLnp.checked || exItsp.checked;
|
||
}
|
||
function updateExplVisibility() {
|
||
exExplWrap.hidden = !anyExempt();
|
||
}
|
||
[exUsf, exTrs, exNanpa, exLnp, exItsp].forEach((cb) => cb.addEventListener("change", updateExplVisibility));
|
||
nd.addEventListener("change", () => { ndText.hidden = !nd.checked; });
|
||
|
||
window.addEventListener("pw:step-shown", (evt: any) => {
|
||
if (evt.detail.step !== "block6_cert") return;
|
||
const s = (window as any).PWIntake.get();
|
||
const d = s.intake_data || {};
|
||
exUsf.checked = !!d.exempt_usf;
|
||
exTrs.checked = !!d.exempt_trs;
|
||
exNanpa.checked = !!d.exempt_nanpa;
|
||
exLnp.checked = !!d.exempt_lnp;
|
||
exItsp.checked = !!d.exempt_itsp;
|
||
exExpl.value = d.exemption_explanation || "";
|
||
gov.checked = !!d.is_state_local_gov;
|
||
nfp.checked = !!d.is_tax_exempt_501c;
|
||
nd.checked = !!d.nondisclosure_requested;
|
||
filingType.value = d.filing_type || "original_april_1";
|
||
deminElection.value = d.deminimis_election || "auto";
|
||
waiveReasonWrap.hidden = deminElection.value !== "regular";
|
||
waiveReason.value = d.waive_deminimis_reason || "";
|
||
updateExplVisibility();
|
||
ndText.hidden = !nd.checked;
|
||
refreshDeMinimisEstimate();
|
||
});
|
||
|
||
window.addEventListener("pw:step-next", (evt: any) => {
|
||
const PW = (window as any).PWIntake;
|
||
if (PW.steps[PW.get().step_index] !== "block6_cert") return;
|
||
if (anyExempt() && !exExpl.value.trim()) {
|
||
err.hidden = false; err.textContent = "Exemption claims require a written explanation."; evt.preventDefault(); return;
|
||
}
|
||
err.hidden = true;
|
||
const election = deminElection.value;
|
||
PW.patchIntakeData({
|
||
exempt_usf: exUsf.checked,
|
||
exempt_trs: exTrs.checked,
|
||
exempt_nanpa: exNanpa.checked,
|
||
exempt_lnp: exLnp.checked,
|
||
exempt_itsp: exItsp.checked,
|
||
exemption_explanation: exExpl.value.trim() || null,
|
||
is_state_local_gov: gov.checked,
|
||
is_tax_exempt_501c: nfp.checked,
|
||
nondisclosure_requested: nd.checked,
|
||
filing_type: filingType.value,
|
||
deminimis_election: election,
|
||
// If the filer explicitly elected "regular", mark is_deminimis false
|
||
// and set the waive flag. If "deminimis", mark is_deminimis true.
|
||
// "auto" leaves is_deminimis to be set by the server-side calc.
|
||
is_deminimis: election === "regular" ? false : (election === "deminimis" ? true : undefined),
|
||
waive_deminimis_reason: election === "regular" ? (waiveReason.value.trim() || null) : null,
|
||
});
|
||
// Expose waive flag on the top-level state so order creation reads it.
|
||
const st = PW.get();
|
||
PW.set({
|
||
...st,
|
||
waive_deminimis_exemption: election === "regular",
|
||
waive_deminimis_reason: election === "regular" ? (waiveReason.value.trim() || null) : null,
|
||
});
|
||
});
|
||
</script>
|