Production readiness fixes: 3 critical + 2 high-priority

Critical #1 — CRTC: Fix undefined 'province' variable (canada_crtc.py:1322)
  Crashes every order at Step 6 document generation. Replaced with
  order_data.get("custom_incorporation_province", "BC").

Critical #2 — FCC Carrier Reg: Add State PUC state picker
  The order page collected "1/few/nationwide" but API expected an array
  of state codes. Added a multi-state checkbox grid that appears when
  State PUC add-on is checked. Sends puc_states: ["CA","NY",...] in
  service_wizard. Price updates per-state ($399 × count).

Critical #3 — Compliance: Add REQUIRED_FIELDS for fcc-499q and
  fcc-499a-discontinuance. Without these, intake validation was
  completely skipped — invalid data accepted silently.

High #4 — FCC Carrier Reg: Don't mark D.C. Agent complete
  prematurely. Was calling _update_step() right after creating the
  admin todo. Now waits for admin to confirm NW order is placed.

High #5 — Compliance: Add fcc-499q and fcc-499a-discontinuance to
  REQUIRES_ENTITY_FRN set. Both require FRN for USAC filing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-04 05:28:13 -05:00
parent 78c04b8bc3
commit 47ca1bf10f
4 changed files with 49 additions and 3 deletions

View file

@ -343,6 +343,8 @@ const REQUIRED_FIELDS: Record<string, FieldSpec> = {
"fcc-63-11-notification": { required: ["foreign_carrier.foreign_carrier_legal_name", "foreign_carrier.country", "foreign_carrier.ownership_pct", "foreign_carrier.affected_routes", "foreign_carrier.affiliation_date"], soft: ["foreign_carrier.notification_type"] },
"ocn-registration": { required: ["service_category", "operating_states"], soft: ["expedited"] },
"dc-agent": { required: [], soft: [] },
"fcc-499q": { required: ["quarter", "revenue"], soft: [] },
"fcc-499a-discontinuance": { required: ["filer_id_499", "discontinuance_reason"], soft: ["last_service_date", "successor_entity"] },
"cdr-analysis": { required: ["reporting_year"], soft: ["reporting_period"] },
"fcc-full-compliance": {
required: [
@ -364,6 +366,7 @@ const REQUIRED_FIELDS: Record<string, FieldSpec> = {
// service can run"). Checked against the linked telecom_entity.
const REQUIRES_ENTITY_FRN: ReadonlySet<string> = new Set([
"rmd-filing", "cpni-certification", "fcc-499a", "fcc-499a-zero", "fcc-499a-499q",
"fcc-499q", "fcc-499a-discontinuance",
"fcc-499-initial", "stir-shaken", "bdc-filing", "bdc-broadband",
"bdc-voice", "calea-ssi", "fcc-63-11-notification", "fcc-full-compliance",
]);

View file

@ -1319,7 +1319,7 @@ class CanadaCRTCHandler(BaseServiceHandler):
name=director.get("name", ""),
address=director.get("address", ""),
city=director.get("city", ""),
state=director.get("province", province),
state=director.get("province", order_data.get("custom_incorporation_province", "BC")),
zip_code=director.get("postal_code", ""),
title="Director",
is_organizer=director.get("is_incorporator", False),

View file

@ -146,7 +146,7 @@ class FCCCarrierRegistrationHandler:
f" UPDATE fcc_carrier_registrations SET dc_agent_completed_at = NOW() "
f"WHERE order_number = '{order_number}'",
)
self._update_step(order_number, "dc_agent_completed_at")
# Don't mark complete — admin must confirm NW order is placed first
# ── Step 5: State PUC Registrations ───────────────────────────────
puc_states = order.get("state_puc_states") or []

View file

@ -398,6 +398,8 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
// Contact
contactName: '', contactEmail: '', contactPhone: '', contactTitle: 'Chief Executive Officer',
addrStreet: '', addrCity: '', addrState: '', addrZip: '',
// State PUC
pucStates: [],
// Pricing
baseFee: 129900, formationFee: 0, stateFee: 0, pucFee: 0, addonFee: 0,
};
@ -581,6 +583,46 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
addonRow('International Section 214 Authorization', 149900, 'intl_214', wizard.needs214);
addonRow('State PUC Registration (per state)', 39900, 'state_puc', false);
// State PUC picker — appears when state_puc checkbox is checked
var pucPicker = document.createElement('div');
pucPicker.id = 'puc-state-picker';
pucPicker.className = 'hidden';
pucPicker.style.cssText = 'margin:0.5rem 0 0.5rem 1.5rem;padding:0.5rem 0.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px';
pucPicker.innerHTML = '<p style="font-size:.8rem;font-weight:600;color:#374151;margin-bottom:.35rem">Select states where you will have customers:</p>' +
'<div id="puc-state-grid" style="display:grid;grid-template-columns:repeat(4,1fr);gap:2px;max-height:200px;overflow-y:auto"></div>' +
'<p id="puc-count" style="font-size:.78rem;color:#6b7280;margin-top:.3rem">0 states selected ($0)</p>';
addons.appendChild(pucPicker);
var stateGrid = document.getElementById('puc-state-grid');
STATES.forEach(function(s) {
var lbl = document.createElement('label');
lbl.style.cssText = 'display:flex;align-items:center;gap:3px;font-size:.78rem;cursor:pointer;padding:2px 4px;border-radius:4px';
lbl.innerHTML = '<input type="checkbox" data-puc-state="' + s[0] + '" style="accent-color:#1e3a5f"> ' + s[0];
lbl.title = s[1];
stateGrid.appendChild(lbl);
});
// Toggle picker visibility when state_puc checkbox changes
var pucCheckbox = addons.querySelector('[data-reg="state_puc"]');
if (pucCheckbox) {
pucCheckbox.addEventListener('change', function() {
pucPicker.classList.toggle('hidden', !this.checked);
if (!this.checked) {
// Uncheck all states
document.querySelectorAll('[data-puc-state]').forEach(function(cb) { cb.checked = false; });
wizard.pucStates = [];
}
updatePrice();
});
}
// Update count + price when states are checked
stateGrid.addEventListener('change', function() {
wizard.pucStates = Array.from(document.querySelectorAll('[data-puc-state]:checked')).map(function(cb) { return cb.dataset.pucState; });
document.getElementById('puc-count').textContent = wizard.pucStates.length + ' state' + (wizard.pucStates.length !== 1 ? 's' : '') + ' selected ($' + (wizard.pucStates.length * 399).toLocaleString() + ')';
updatePrice();
});
updatePrice();
}
@ -595,7 +637,7 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
if (key === 'stir_shaken') { total += 49900; details.push('STIR/SHAKEN: +$499'); }
if (key === 'ocn') { total += 265000; details.push('OCN: +$2,650'); }
if (key === 'intl_214') { total += 149900; details.push('Intl 214: +$1,499'); }
if (key === 'state_puc') { total += 39900; details.push('State PUC: +$399/state'); }
if (key === 'state_puc') { var n = wizard.pucStates.length || 1; total += 39900 * n; details.push('State PUC: +$' + (399 * n).toLocaleString() + ' (' + n + ' state' + (n > 1 ? 's' : '') + ')'); }
});
// Formation fees (if new entity)
@ -803,6 +845,7 @@ select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px
infra_needs: wizard.infraNeeds,
broadband_type: wizard.broadbandType,
operating_states: wizard.operatingStates,
puc_states: wizard.pucStates,
},
services: services,
engagement_accepted: true,