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>
474 lines
19 KiB
Python
474 lines
19 KiB
Python
"""Sizer — compute vendor obligations per order.
|
||
|
||
Called by ``crypto_payment_worker`` when transitioning received → sizing.
|
||
Returns the list of vendor_obligations rows to insert plus the total USD
|
||
that needs to land at RelayFi (``needed_usd_cents``).
|
||
|
||
Two categories of obligation:
|
||
1. filing_fee — paid via Relay debit card via Playwright flow.
|
||
Includes a +10% buffer for card-decline retries + slippage.
|
||
2. commission — paid via Relay ACH 14 days post-delivery. Exact amount.
|
||
|
||
The sizer reads order data from the relevant table (compliance_orders,
|
||
formation_orders, canada_crtc_orders, bundle_orders) + existing
|
||
config tables (state_filing_fees, sales_agents) + a small static map
|
||
for vendors that don't live in a table (NECA OCN, BC Registry, etc.).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import logging
|
||
import os
|
||
from dataclasses import dataclass, field
|
||
from datetime import datetime, timedelta
|
||
from typing import Optional
|
||
|
||
import psycopg2
|
||
import psycopg2.extras
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# ── Static vendor fee map (single source of truth for non-table vendors) ──
|
||
#
|
||
# These are vendors whose fees are fixed + don't live in a queryable
|
||
# table. Keep in sync with the COMPLIANCE_SERVICES catalog in
|
||
# api/src/routes/compliance-orders.ts when their pricing changes.
|
||
STATIC_VENDOR_FEES_CENTS: dict[str, int] = {
|
||
"nwra": 12500, # Northwest Registered Agent $125/yr
|
||
"neca_ocn": 55000, # NECA OCN registration $550 standard
|
||
"neca_ocn_expedited": 67500, # NECA OCN expedited $675
|
||
"bc_registry": 27700, # BC Registry incorporation $277 CAD ≈ rounded
|
||
"cores_frn": 0, # FCC CORES FRN — no filing fee
|
||
"usac_499_initial": 0, # USAC 499 Initial — no filing fee
|
||
"usac_499a": 0, # USAC 499-A — no filing fee (contributions separate)
|
||
"fcc_cpni": 0, # FCC CPNI cert — no filing fee
|
||
"fcc_rmd": 10000, # FCC RMD — $100 filing fee (effective 2025)
|
||
"fcc_bdc": 0, # FCC BDC — no filing fee
|
||
"fcc_stir_shaken": 0, # FCC STIR/SHAKEN — no filing fee
|
||
"fcc_63_11": 0, # FCC Foreign Carrier Affiliation — no filing fee
|
||
"fcc_calea_ssi": 0, # CALEA SSI — no filing fee (internal plan)
|
||
"dc_agent": 0, # DC Agent — our DC agent service has no external fee
|
||
}
|
||
|
||
# Default filing-fee buffer in basis points (1000 bps = 10%)
|
||
DEFAULT_FILING_BUFFER_BPS = 1000
|
||
|
||
# Commission payout happens 14 days post-delivery per commission_ledger
|
||
# lifecycle (migration 011); reserve the cash at offramp time.
|
||
COMMISSION_DUE_DAYS = 14
|
||
|
||
|
||
@dataclass
|
||
class VendorObligation:
|
||
"""Matches the shape inserted into vendor_obligations."""
|
||
order_id: str
|
||
order_type: str
|
||
obligation_kind: str # 'filing_fee' | 'commission'
|
||
vendor: str
|
||
vendor_detail: Optional[str]
|
||
amount_usd_cents: int
|
||
fx_buffer_bps: int
|
||
due_by: Optional[datetime] = None
|
||
commission_ledger_id: Optional[int] = None
|
||
|
||
|
||
@dataclass
|
||
class SizingResult:
|
||
obligations: list[VendorObligation]
|
||
filing_total_cents: int # raw filing-fee sum, pre-buffer
|
||
filing_total_with_buffer_cents: int
|
||
commission_total_cents: int
|
||
needed_usd_cents: int # what we need at Relay
|
||
notes: list[str] = field(default_factory=list)
|
||
|
||
|
||
def _db_connect():
|
||
return psycopg2.connect(os.environ.get("DATABASE_URL", ""))
|
||
|
||
|
||
def compute_obligations(
|
||
*,
|
||
order_id: str,
|
||
order_type: str,
|
||
conn=None,
|
||
) -> SizingResult:
|
||
"""Compute vendor obligations for the given order.
|
||
|
||
``order_type`` is one of: 'compliance' | 'formation' | 'canada_crtc' | 'bundle'.
|
||
"""
|
||
close_after = False
|
||
if conn is None:
|
||
conn = _db_connect()
|
||
close_after = True
|
||
try:
|
||
if order_type == "compliance":
|
||
return _size_compliance(order_id, conn)
|
||
if order_type == "formation":
|
||
return _size_formation(order_id, conn)
|
||
if order_type == "canada_crtc":
|
||
return _size_canada_crtc(order_id, conn)
|
||
if order_type == "bundle":
|
||
return _size_bundle(order_id, conn)
|
||
logger.warning("sizer: unknown order_type=%s for %s", order_type, order_id)
|
||
return SizingResult([], 0, 0, 0, 0, ["unknown order_type — zero obligations"])
|
||
finally:
|
||
if close_after:
|
||
conn.close()
|
||
|
||
|
||
# ── Compliance orders (FCC filings + OCN + DC agent + bundles) ──────────
|
||
|
||
def _size_compliance(order_id: str, conn) -> SizingResult:
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute(
|
||
"""
|
||
SELECT co.*, te.id AS entity_id,
|
||
(co.intake_data->>'expedited')::boolean AS expedited
|
||
FROM compliance_orders co
|
||
LEFT JOIN telecom_entities te ON te.id = co.telecom_entity_id
|
||
WHERE co.order_number = %s
|
||
""",
|
||
(order_id,),
|
||
)
|
||
order = cur.fetchone()
|
||
if not order:
|
||
return SizingResult([], 0, 0, 0, 0, [f"compliance order {order_id} not found"])
|
||
|
||
slug = order.get("service_slug") or ""
|
||
obligations: list[VendorObligation] = []
|
||
|
||
# OCN registration has real money to NECA; optional expedited upcharge.
|
||
if slug == "ocn-registration":
|
||
fee_key = "neca_ocn_expedited" if order.get("expedited") else "neca_ocn"
|
||
obligations.append(_filing_vo(
|
||
order_id, "compliance", "neca_ocn", None,
|
||
STATIC_VENDOR_FEES_CENTS[fee_key],
|
||
))
|
||
|
||
# Any service that includes NRA registered-agent coverage in its bundle.
|
||
# Customer opts in via intake_data.include_nwra_ra or similar; read the flag.
|
||
if (order.get("intake_data") or {}).get("include_nwra_ra"):
|
||
obligations.append(_filing_vo(
|
||
order_id, "compliance", "nwra", None,
|
||
STATIC_VENDOR_FEES_CENTS["nwra"],
|
||
))
|
||
|
||
# FCC 499-family, CPNI, RMD, BDC, STIR/SHAKEN, 63-11, CALEA SSI, DC agent,
|
||
# compliance checkup, CDR analysis, CORES FRN — no vendor filing fee.
|
||
# (Our service fee is pure markup; nothing to reserve for vendors.)
|
||
|
||
# Commission reserve if a sales agent is attached
|
||
commission = _compute_commission_obligation(
|
||
order_id, "compliance", order, slug, conn,
|
||
)
|
||
if commission:
|
||
obligations.append(commission)
|
||
|
||
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
|
||
comms = [o for o in obligations if o.obligation_kind == "commission"]
|
||
filing_total = sum(o.amount_usd_cents for o in filing)
|
||
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
|
||
comm_total = sum(o.amount_usd_cents for o in comms)
|
||
|
||
return SizingResult(
|
||
obligations=obligations,
|
||
filing_total_cents=filing_total,
|
||
filing_total_with_buffer_cents=filing_buf,
|
||
commission_total_cents=comm_total,
|
||
needed_usd_cents=filing_buf + comm_total,
|
||
)
|
||
|
||
|
||
# ── Formation orders (LLC / Corp state filings) ─────────────────────────
|
||
|
||
def _size_formation(order_id: str, conn) -> SizingResult:
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute("SELECT * FROM formation_orders WHERE order_number = %s", (order_id,))
|
||
order = cur.fetchone()
|
||
if not order:
|
||
return SizingResult([], 0, 0, 0, 0, [f"formation order {order_id} not found"])
|
||
|
||
state = order.get("state_code") or order.get("state")
|
||
entity_type = (order.get("entity_type") or "llc").lower()
|
||
|
||
obligations: list[VendorObligation] = []
|
||
if state:
|
||
# state_filing_fees UNIT CONVENTIONS per column (confirmed by
|
||
# spot-checking 15+ states against real SOS fee schedules):
|
||
# llc_formation_fee / corp_formation_fee — CENTS (e.g., 7000 = $70 CA LLC)
|
||
# foreign_llc_fee / foreign_corp_fee — CENTS
|
||
# expedited_fee — DOLLARS × 10000
|
||
# (e.g., 7500000 = $750 CA same-day)
|
||
# llc_annual_fee / corp_annual_fee — mixed (some rows are franchise-tax
|
||
# amounts; verify per-state before trust)
|
||
# name_reservation_fee, franchise_tax_min — DOLLARS × 10000 in most rows
|
||
#
|
||
# This sizer only reads formation + foreign + expedited and
|
||
# normalizes expedited to cents.
|
||
formation_col = "corp_formation_fee" if entity_type.startswith("corp") else "llc_formation_fee"
|
||
foreign_col = "foreign_corp_fee" if entity_type.startswith("corp") else "foreign_llc_fee"
|
||
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute(
|
||
f"""
|
||
SELECT {formation_col} AS formation_cents,
|
||
{foreign_col} AS foreign_cents,
|
||
expedited_fee AS expedited_raw
|
||
FROM state_filing_fees
|
||
WHERE state_code = %s
|
||
LIMIT 1
|
||
""",
|
||
(state,),
|
||
)
|
||
fee = cur.fetchone()
|
||
if fee:
|
||
is_foreign = bool(order.get("foreign_filing") or order.get("is_foreign_entity"))
|
||
base_cents = int((fee.get("foreign_cents") or 0) if is_foreign else (fee.get("formation_cents") or 0))
|
||
expedited_cents = 0
|
||
if order.get("expedited") and fee.get("expedited_raw"):
|
||
# Divide by 100 to convert (dollars × 10000) → cents
|
||
expedited_cents = int(fee["expedited_raw"]) // 100
|
||
total = base_cents + expedited_cents
|
||
if total > 0:
|
||
obligations.append(_filing_vo(
|
||
order_id, "formation", "state_sos", state, total,
|
||
))
|
||
|
||
if order.get("include_ra_service") or order.get("include_registered_agent"):
|
||
obligations.append(_filing_vo(
|
||
order_id, "formation", "nwra", state,
|
||
STATIC_VENDOR_FEES_CENTS["nwra"],
|
||
))
|
||
|
||
commission = _compute_commission_obligation(
|
||
order_id, "formation", order, f"formation-{entity_type}", conn,
|
||
)
|
||
if commission:
|
||
obligations.append(commission)
|
||
|
||
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
|
||
comms = [o for o in obligations if o.obligation_kind == "commission"]
|
||
filing_total = sum(o.amount_usd_cents for o in filing)
|
||
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
|
||
comm_total = sum(o.amount_usd_cents for o in comms)
|
||
|
||
return SizingResult(
|
||
obligations=obligations,
|
||
filing_total_cents=filing_total,
|
||
filing_total_with_buffer_cents=filing_buf,
|
||
commission_total_cents=comm_total,
|
||
needed_usd_cents=filing_buf + comm_total,
|
||
)
|
||
|
||
|
||
# ── Canada CRTC orders ──────────────────────────────────────────────────
|
||
|
||
def _size_canada_crtc(order_id: str, conn) -> SizingResult:
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute("SELECT * FROM canada_crtc_orders WHERE order_number = %s", (order_id,))
|
||
order = cur.fetchone()
|
||
if not order:
|
||
return SizingResult([], 0, 0, 0, 0, [f"canada_crtc order {order_id} not found"])
|
||
|
||
obligations: list[VendorObligation] = []
|
||
obligations.append(_filing_vo(
|
||
order_id, "canada_crtc", "bc_registry", "BC",
|
||
STATIC_VENDOR_FEES_CENTS["bc_registry"],
|
||
))
|
||
if order.get("amb_annual_price_cents"):
|
||
obligations.append(_filing_vo(
|
||
order_id, "canada_crtc", "amb_mailbox", None,
|
||
int(order["amb_annual_price_cents"]),
|
||
))
|
||
|
||
commission = _compute_commission_obligation(
|
||
order_id, "canada_crtc", order, "canada_crtc", conn,
|
||
)
|
||
if commission:
|
||
obligations.append(commission)
|
||
|
||
filing = [o for o in obligations if o.obligation_kind == "filing_fee"]
|
||
comms = [o for o in obligations if o.obligation_kind == "commission"]
|
||
filing_total = sum(o.amount_usd_cents for o in filing)
|
||
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
|
||
comm_total = sum(o.amount_usd_cents for o in comms)
|
||
|
||
return SizingResult(
|
||
obligations=obligations,
|
||
filing_total_cents=filing_total,
|
||
filing_total_with_buffer_cents=filing_buf,
|
||
commission_total_cents=comm_total,
|
||
needed_usd_cents=filing_buf + comm_total,
|
||
)
|
||
|
||
|
||
# ── Bundle orders (sum of sub-orders) ───────────────────────────────────
|
||
|
||
def _size_bundle(order_id: str, conn) -> SizingResult:
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute("SELECT * FROM bundle_orders WHERE order_number = %s", (order_id,))
|
||
order = cur.fetchone()
|
||
if not order:
|
||
return SizingResult([], 0, 0, 0, 0, [f"bundle order {order_id} not found"])
|
||
|
||
# bundle_orders.child_order_numbers is a TEXT[] of the sub-order numbers
|
||
# (verify schema during implementation — may be JSONB with types).
|
||
sub_ids = order.get("child_order_numbers") or []
|
||
sub_types = order.get("child_order_types") or []
|
||
|
||
all_obligations: list[VendorObligation] = []
|
||
for sub_id, sub_type in zip(sub_ids, sub_types):
|
||
sub_res = compute_obligations(order_id=sub_id, order_type=sub_type, conn=conn)
|
||
all_obligations.extend(sub_res.obligations)
|
||
|
||
# Bundle-level commission (override the per-sub commissions if present)
|
||
commission = _compute_commission_obligation(
|
||
order_id, "bundle", order, "bundle", conn,
|
||
)
|
||
if commission:
|
||
# Drop sub-level commissions; keep only the bundle-level one
|
||
all_obligations = [o for o in all_obligations if o.obligation_kind != "commission"]
|
||
all_obligations.append(commission)
|
||
|
||
filing = [o for o in all_obligations if o.obligation_kind == "filing_fee"]
|
||
comms = [o for o in all_obligations if o.obligation_kind == "commission"]
|
||
filing_total = sum(o.amount_usd_cents for o in filing)
|
||
filing_buf = int(filing_total * (1 + DEFAULT_FILING_BUFFER_BPS / 10000))
|
||
comm_total = sum(o.amount_usd_cents for o in comms)
|
||
|
||
return SizingResult(
|
||
obligations=all_obligations,
|
||
filing_total_cents=filing_total,
|
||
filing_total_with_buffer_cents=filing_buf,
|
||
commission_total_cents=comm_total,
|
||
needed_usd_cents=filing_buf + comm_total,
|
||
)
|
||
|
||
|
||
# ── Commission obligation (shared across all order types) ───────────────
|
||
|
||
def _compute_commission_obligation(
|
||
order_id: str, order_type: str, order: dict, slug_for_override: str, conn,
|
||
) -> Optional[VendorObligation]:
|
||
"""If the order is attributed to a sales agent, reserve their commission."""
|
||
agent_id = order.get("sales_agent_id") or order.get("referral_agent_id")
|
||
if not agent_id:
|
||
return None
|
||
|
||
with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
|
||
cur.execute(
|
||
"""
|
||
SELECT id, commission_type, commission_default_cents,
|
||
commission_pct, commission_overrides, active
|
||
FROM sales_agents
|
||
WHERE id = %s
|
||
""",
|
||
(agent_id,),
|
||
)
|
||
agent = cur.fetchone()
|
||
if not agent or not agent.get("active"):
|
||
return None
|
||
|
||
overrides = agent.get("commission_overrides") or {}
|
||
if isinstance(overrides, str):
|
||
try: overrides = json.loads(overrides)
|
||
except (TypeError, ValueError): overrides = {}
|
||
override = overrides.get(slug_for_override) or {}
|
||
ctype = override.get("type") or agent.get("commission_type", "flat")
|
||
|
||
amount_cents = 0
|
||
if ctype == "flat":
|
||
amount_cents = int(
|
||
override.get("flat_cents")
|
||
or agent.get("commission_default_cents")
|
||
or 0
|
||
)
|
||
elif ctype == "percent":
|
||
pct = float(override.get("pct") or agent.get("commission_pct") or 0)
|
||
# Use subtotal (pre-surcharge) as the basis; fall back to service_fee_cents
|
||
subtotal = int(
|
||
order.get("subtotal_cents")
|
||
or order.get("service_fee_cents")
|
||
or order.get("total_cents")
|
||
or 0
|
||
)
|
||
amount_cents = int(subtotal * pct / 100)
|
||
|
||
if amount_cents <= 0:
|
||
return None
|
||
|
||
# Due 14 days post-delivery. If we don't know delivery date, estimate
|
||
# from created_at + 7 days (typical service turnaround) + 14.
|
||
created = order.get("created_at") or datetime.utcnow()
|
||
due_by = created + timedelta(days=7 + COMMISSION_DUE_DAYS)
|
||
|
||
return VendorObligation(
|
||
order_id=order_id,
|
||
order_type=order_type,
|
||
obligation_kind="commission",
|
||
vendor="agent_commission",
|
||
vendor_detail=str(agent_id),
|
||
amount_usd_cents=amount_cents,
|
||
fx_buffer_bps=0, # commissions are exact, no buffer
|
||
due_by=due_by,
|
||
)
|
||
|
||
|
||
# ── Constructors ────────────────────────────────────────────────────────
|
||
|
||
def _filing_vo(
|
||
order_id: str, order_type: str, vendor: str,
|
||
vendor_detail: Optional[str], amount_cents: int,
|
||
) -> VendorObligation:
|
||
return VendorObligation(
|
||
order_id=order_id,
|
||
order_type=order_type,
|
||
obligation_kind="filing_fee",
|
||
vendor=vendor,
|
||
vendor_detail=vendor_detail,
|
||
amount_usd_cents=amount_cents,
|
||
fx_buffer_bps=DEFAULT_FILING_BUFFER_BPS,
|
||
)
|
||
|
||
|
||
# ── Persistence helpers ─────────────────────────────────────────────────
|
||
|
||
def persist_obligations(order_id: str, result: SizingResult, conn=None) -> None:
|
||
"""Upsert the SizingResult into vendor_obligations.
|
||
|
||
Idempotent: deletes existing pending rows for this order then re-inserts.
|
||
We never overwrite rows already marked reserved/paid/waived.
|
||
"""
|
||
close_after = False
|
||
if conn is None:
|
||
conn = _db_connect()
|
||
close_after = True
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(
|
||
"DELETE FROM vendor_obligations "
|
||
"WHERE order_id = %s AND status = 'pending'",
|
||
(order_id,),
|
||
)
|
||
for o in result.obligations:
|
||
cur.execute(
|
||
"""
|
||
INSERT INTO vendor_obligations (
|
||
order_id, order_type, obligation_kind, vendor, vendor_detail,
|
||
amount_usd_cents, fx_buffer_bps, due_by, status,
|
||
commission_ledger_id
|
||
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,'pending',%s)
|
||
""",
|
||
(
|
||
o.order_id, o.order_type, o.obligation_kind,
|
||
o.vendor, o.vendor_detail,
|
||
o.amount_usd_cents, o.fx_buffer_bps, o.due_by,
|
||
o.commission_ledger_id,
|
||
),
|
||
)
|
||
conn.commit()
|
||
finally:
|
||
if close_after:
|
||
conn.close()
|