new-site/scripts/workers/services/foreign_qualification.py
justin f8cd37ac8c 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>
2026-04-27 06:54:22 -05:00

329 lines
13 KiB
Python

"""Foreign Qualification (Certificate of Authority) handler.
Processes per-state COA filings for entities expanding to additional
states. Supports two modes:
- `foreign-qualification-single`: one target state per order.
- `foreign-qualification-multi`: N target states, one order. The
handler fans out one `foreign_qualification_registrations` row per
state and processes them sequentially.
FCC carriers ordering multi-state registration get the same flow —
their entity is identified by telecom_entity_id, the handler reads
the selected target states from intake_data.target_states.
Filing steps per state (high-level):
1. Obtain Certificate of Good Standing from home state (if required
by target state's `foreign_qual_requires_coa` flag).
2. Provision NW Registered Agent address in target state (if the
order includes RA service).
3. Submit the Certificate of Authority / Application for Registration
via the target state's SOS portal (Playwright adapter).
4. Capture confirmation, persist COA document.
5. Mark `foreign_qualification_registrations.status = 'approved'`.
"""
from __future__ import annotations
import logging
import os
from datetime import datetime
from typing import Optional
import psycopg2
from .base_handler import BaseServiceHandler
logger = logging.getLogger(__name__)
DATABASE_URL = os.environ.get("DATABASE_URL", "")
class ForeignQualificationHandler(BaseServiceHandler):
SERVICE_SLUG = "foreign-qualification"
SERVICE_NAME = "Foreign Qualification"
REQUIRES_LLM = False
async def process(self, order_data: dict) -> list[str]:
order_number = order_data["name"]
entity = order_data.get("entity", {}) or {}
intake = order_data.get("intake_data", {}) or {}
home_state = (
intake.get("home_state_code")
or entity.get("state_of_formation")
or entity.get("address_state")
or ""
).upper()
entity_type = (
intake.get("entity_type")
or entity.get("entity_type")
or "llc"
).lower()
target_states = intake.get("target_states") or []
if isinstance(target_states, str):
target_states = [s.strip().upper() for s in target_states.split(",") if s.strip()]
else:
target_states = [str(s).strip().upper() for s in target_states]
if not target_states:
self._create_admin_todo(
order_number,
f"{self.SERVICE_NAME}: no target states specified in intake_data. "
"Admin should add target_states and re-dispatch.",
)
return []
entity_name = (
intake.get("entity_legal_name")
or entity.get("legal_name")
or ""
)
if not entity_name:
self._create_admin_todo(
order_number,
f"{self.SERVICE_NAME}: missing entity legal name. "
"Admin should set entity_legal_name in intake_data.",
)
return []
# ── Fan out: one registration row per target state ──────────────
conn = psycopg2.connect(DATABASE_URL)
try:
with conn.cursor() as cur:
for state_code in target_states:
# Fetch state fee from state_filing_fees.
fee_col = (
"foreign_llc_fee"
if entity_type in ("llc", "pllc")
else "foreign_corp_fee"
)
cur.execute(
f"SELECT {fee_col} AS fee FROM state_filing_fees WHERE state_code = %s",
(state_code,),
)
fee_row = cur.fetchone()
state_fee = int(fee_row[0]) if fee_row and fee_row[0] else 0
# Check for existing active registration to avoid dupes.
cur.execute(
"""
SELECT id FROM foreign_qualification_registrations
WHERE order_number = %s
AND target_state_code = %s
AND status NOT IN ('cancelled','rejected')
LIMIT 1
""",
(order_number, state_code),
)
if cur.fetchone():
logger.info(
"ForeignQualHandler: %s already has active registration "
"for %s — skipping",
order_number, state_code,
)
continue
# Compliance order ID (nullable).
co_id = order_data.get("compliance_order_id") or order_data.get("id")
cur.execute(
"""
INSERT INTO foreign_qualification_registrations (
compliance_order_id, order_number,
telecom_entity_id,
home_country, home_state_code,
target_state_code,
entity_legal_name, entity_type,
formed_on, home_state_filing_number, ein,
principal_address_json,
include_ra_service,
state_fee_cents, expedited,
status
) VALUES (
%s, %s,
%s,
%s, %s,
%s,
%s, %s,
%s, %s, %s,
%s::jsonb,
%s,
%s, %s,
'received'
)
""",
(
co_id, order_number,
entity.get("id"),
intake.get("home_country", "US"), home_state,
state_code,
entity_name, entity_type,
entity.get("formed_on"), entity.get("state_filing_number"),
entity.get("ein"),
_json_or_null(intake.get("principal_address")),
intake.get("include_ra_each", True),
state_fee,
bool(intake.get("expedited", False)),
),
)
conn.commit()
logger.info(
"ForeignQualHandler: created %d registration(s) for %s",
len(target_states), order_number,
)
except Exception as exc:
logger.exception(
"ForeignQualHandler: DB error creating registrations for %s: %s",
order_number, exc,
)
conn.rollback()
self._create_admin_todo(
order_number,
f"{self.SERVICE_NAME}: failed to create registration rows — {exc}",
)
return []
finally:
conn.close()
# ── Per-state filing dispatch ───────────────────────────────────
# For v1, each state filing needs admin review because we haven't
# built the state-specific Playwright adapters for foreign qual yet.
# The handler creates a ToDo per state so the admin can file manually
# or trigger the adapter once it's written.
filed_count = 0
for state_code in target_states:
try:
filed = await self._process_one_state(
order_number=order_number,
entity=entity,
intake=intake,
entity_name=entity_name,
entity_type=entity_type,
home_state=home_state,
target_state=state_code,
)
if filed:
filed_count += 1
except Exception as exc:
logger.error(
"ForeignQualHandler: error filing %s in %s: %s",
order_number, state_code, exc,
)
self._update_registration_status(
order_number, state_code, "admin_review",
last_error=str(exc),
)
if filed_count == 0:
logger.info(
"ForeignQualHandler: no states auto-filed for %s"
"all set to admin_review",
order_number,
)
return [] # artifacts come from per-state filings, not the parent
async def _process_one_state(
self,
*,
order_number: str,
entity: dict,
intake: dict,
entity_name: str,
entity_type: str,
home_state: str,
target_state: str,
) -> bool:
"""Attempt to file the COA in one target state. Returns True if
the adapter succeeded, False if we parked in admin_review."""
from scripts.formation.jurisdictions import get_jurisdiction
jc = get_jurisdiction(target_state)
if not jc.has_adapter():
logger.info(
"ForeignQualHandler: no adapter for %s — parking for admin",
target_state,
)
self._update_registration_status(
order_number, target_state, "admin_review",
last_error=f"No Playwright adapter for {target_state} foreign-qual",
)
self._create_admin_todo(
order_number,
f"Foreign qualification in {jc.name} ({target_state}) needs "
f"manual filing — no adapter. Entity: {entity_name} ({entity_type}) "
f"from {home_state}.",
)
return False
# TODO: dispatch to adapter.file_foreign_qualification() once
# per-state adapters implement the method. For now, all states
# park at admin_review.
self._update_registration_status(
order_number, target_state, "admin_review",
last_error="Foreign qual adapter not yet implemented",
)
self._create_admin_todo(
order_number,
f"Foreign qualification in {jc.name} ({target_state}): entity "
f"{entity_name} ({entity_type}) from {home_state}. "
f"State fee: ${jc.foreign_qualification_fee_cents(entity_type) or 0 / 100:.0f}. "
f"RA: {'NWRA requested' if intake.get('include_ra_each', True) else 'not requested'}. "
f"File via {jc.portal_name or 'portal'} at {jc.portal_url or 'N/A'}.",
)
return False
def _update_registration_status(
self,
order_number: str,
target_state: str,
status: str,
*,
last_error: Optional[str] = None,
) -> None:
try:
conn = psycopg2.connect(DATABASE_URL)
with conn.cursor() as cur:
cur.execute(
"""
UPDATE foreign_qualification_registrations
SET status = %s,
last_error = %s,
attempt_count = attempt_count + 1
WHERE order_number = %s
AND target_state_code = %s
""",
(status, last_error, order_number, target_state),
)
conn.commit()
conn.close()
except Exception as exc:
logger.error(
"ForeignQualHandler: failed to update status for %s/%s: %s",
order_number, target_state, exc,
)
def _create_admin_todo(self, order_number: str, description: str) -> None:
try:
from scripts.workers.erpnext_client import ERPNextClient
ERPNextClient().create_resource(
"ToDo",
{
"description": (
f"[{self.SERVICE_SLUG}] {order_number}\n\n{description}"
),
"priority": "Medium",
"role": "Accounting Advisor",
},
)
except Exception as exc:
logger.error("Could not create admin ToDo: %s", exc)
def _json_or_null(v) -> Optional[str]:
if v is None:
return None
import json
return json.dumps(v) if isinstance(v, dict) else str(v)