Fix flagged items: CRTC email submission, BITS todo, selector docs, stale plans

- CRTC letter now auto-emailed to secretary.general@crtc.gc.ca after eSign
- BITS admin todo updated to reference electronic + physical submission
- COLIN selectors.py: documented verification status per step
- BC config: added CRTC Secretary General email address
- plan.md: marked completed items (eSign, portal auth, CRTC email)
- go-live-todo.md: marked Compliance Calendar DocType as imported

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-04 11:33:45 -05:00
parent 05eec47528
commit 97dd08c821
8 changed files with 413 additions and 13 deletions

View file

@ -156,6 +156,7 @@ CONFIG = {
"postal_code": "J8X 4B1",
"country": "Canada",
"website": "https://crtc.gc.ca",
"email": "secretary.general@crtc.gc.ca",
"notification_required": True,
},

View file

@ -0,0 +1,273 @@
#!/usr/bin/env python3
"""Generate sample documents from every DOCX template and email them."""
import os
import smtplib
import tempfile
from datetime import datetime
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
WORK = tempfile.mkdtemp(prefix="pw_template_test_")
DATE_STR = datetime.now().strftime("%Y%m%d")
TODAY = datetime.now().strftime("%B %d, %Y")
generated = []
def try_gen(num, name, func):
try:
result = func()
if result:
generated.append((name, result))
print(f"{num}. {name}: {'OK' if result else 'FAIL'}")
except Exception as e:
print(f"{num}. {name}: ERROR - {str(e)[:150]}")
# ── Common kwargs for CPNI variants ────────────────────────────────
CPNI_COMMON = dict(
entity_name="Acme Telecom LLC", frn="0015341902",
filer_id_499="829999",
address_street="1712 Pioneer Ave, Suite 200",
address_city="Cheyenne", address_state="WY", address_zip="82001",
officer_name="John Smith", officer_title="CEO",
contact_email="john@acmetelecom.example",
contact_phone="(307) 555-0142",
reporting_year=2025, complaints_count=0,
)
# ── Common kwargs for CALEA variants ───────────────────────────────
CALEA_COMMON = dict(
entity_name="Acme Telecom LLC", frn="0015341902",
law_enforcement_contact={
"name": "Jane Doe",
"phone": "(307) 555-0199",
"email_24h": "compliance@acmetelecom.example",
},
signatory_name="John Smith", signatory_title="CEO",
)
# 1. RMD Letter
def gen1():
from scripts.document_gen.templates.rmd_letter_generator import generate_rmd_letter
return generate_rmd_letter(
entity_name="Acme Telecom LLC", frn="0015341902",
provider_classification="voice_service_provider",
carrier_category="interconnected_voip",
stir_shaken_status="partial_implementation",
upstream_provider_name="Bandwidth.com",
address_street="1712 Pioneer Ave, Suite 200",
address_city="Cheyenne", address_state="WY", address_zip="82001",
contact_name="John Smith", contact_title="CEO",
contact_email="john@acmetelecom.example", contact_phone="(307) 555-0142",
ceo_name="John Smith", ceo_title="CEO",
output_path=os.path.join(WORK, f"01_rmd_letter.docx"),
)
try_gen(1, "RMD Certification Letter", gen1)
# 2. RMD Exhibit A
def gen2():
from scripts.document_gen.templates.rmd_exhibit_a_generator import generate_exhibit_a
return generate_exhibit_a(
entity_name="Acme Telecom LLC", frn="0015341902",
carrier_role="ucaas", rmd_option="option2",
upstream_provider_name="Bandwidth.com",
contact_name="John Smith", contact_title="CEO",
contact_email="john@acmetelecom.example", contact_phone="(307) 555-0142",
address="1712 Pioneer Ave, Suite 200, Cheyenne, WY 82001",
principals=["John Smith (CEO)"],
output_path=os.path.join(WORK, f"02_rmd_exhibit_a.docx"),
)
try_gen(2, "RMD Exhibit A (Robocall Mitigation Plan)", gen2)
# 3. CPNI Certification Letter (generic)
def gen3():
from scripts.document_gen.templates.cpni_cert_letter_generator import generate_cpni_cert_letter
return generate_cpni_cert_letter(
entity_name="Acme Telecom LLC", frn="0015341902",
filer_id_499="829999",
address_street="1712 Pioneer Ave, Suite 200",
address_city="Cheyenne", address_state="WY", address_zip="82001",
officer_name="John Smith", officer_title="CEO",
contact_email="john@acmetelecom.example", contact_phone="(307) 555-0142",
reporting_year=2025, complaints_count=0,
output_path=os.path.join(WORK, f"03_cpni_cert_generic.docx"),
)
try_gen(3, "CPNI Certification Letter (Generic)", gen3)
# 4. CPNI Procedure Statement
def gen4():
from scripts.document_gen.templates.cpni_procedure_statement_generator import generate_cpni_procedure_statement
return generate_cpni_procedure_statement(
entity_name="Acme Telecom LLC",
signatory_name="John Smith", signatory_title="CEO",
support_email="support@acmetelecom.example",
output_path=os.path.join(WORK, f"04_cpni_procedures.docx"),
)
try_gen(4, "CPNI Procedure Statement", gen4)
# 5. CALEA SSI Plan (generic VoIP)
def gen5():
from scripts.document_gen.templates.calea_ssi_generator import generate_calea_ssi_plan
return generate_calea_ssi_plan(
**CALEA_COMMON,
output_path=os.path.join(WORK, f"05_calea_ssi_voip.docx"),
)
try_gen(5, "CALEA SSI Plan (VoIP)", gen5)
# 6. Discontinuance Letter
def gen6():
from scripts.document_gen.templates.form_499a_discontinuance_letter_generator import generate_discontinuance_letter
return generate_discontinuance_letter(
entity_name="Acme Telecom LLC", filer_id="829999", frn="0015341902",
ein="87-1234567", address="1712 Pioneer Ave, Suite 200, Cheyenne, WY 82001",
officer_name="John Smith", officer_title="CEO",
officer_email="john@acmetelecom.example", officer_phone="(307) 555-0142",
termination_date="April 30, 2026",
discontinuance_reason="Company no longer providing telecommunications services",
output_path=os.path.join(WORK, f"06_discontinuance.docx"),
)
try_gen(6, "USAC Discontinuance Letter", gen6)
# 7. OCN Request Form
def gen7():
from scripts.document_gen.templates.ocn_request_form_generator import generate_ocn_request_packet
return generate_ocn_request_packet(
entity_name="Acme Telecom LLC",
company_contact_name="John Smith",
company_contact_voice="(307) 555-0142",
company_contact_email="john@acmetelecom.example",
company_contact_address="1712 Pioneer Ave, Suite 200, Cheyenne, WY 82001",
service_category="IPES",
operating_states=["WY", "CO", "MT"],
output_path=os.path.join(WORK, f"07_ocn_request.docx"),
)
try_gen(7, "NECA OCN Request Form", gen7)
# 8. Reseller Certification
def gen8():
from scripts.document_gen.templates.reseller_cert_attestation_generator import generate_reseller_cert_attestation
return generate_reseller_cert_attestation(
output_path=os.path.join(WORK, f"08_reseller_cert.docx"),
filer_legal_name="Acme Telecom LLC",
filer_filer_id_499="829999",
reseller_legal_name="Upstream Carrier Inc",
reseller_filer_id_499="123456",
reporting_year=2025,
filer_contact_name="John Smith",
filer_contact_email="john@acmetelecom.example",
)
try_gen(8, "Reseller Certification Attestation", gen8)
# 9. CRTC Letter
def gen9():
from scripts.document_gen.templates.crtc_letter_generator import generate_crtc_letter
return generate_crtc_letter(
entity_name="1234567 B.C. Ltd.",
incorporation_number="BC1234567",
registered_office="123 W Georgia St, Vancouver, BC V6B 1J5",
services_description="Interconnected VoIP and UCaaS services",
director_name="John Smith",
ca_domain="acmetelecom.ca",
output_path=os.path.join(WORK, f"09_crtc_letter.docx"),
)
try_gen(9, "CRTC Registration Letter", gen9)
# 10-18. CPNI variants
CPNI_VARIANTS = [
("cpni_clec_generator", "generate_cpni_clec", "CPNI CLEC (Facilities)"),
("cpni_ixc_generator", "generate_cpni_ixc", "CPNI IXC (Facilities)"),
("cpni_wireless_generator", "generate_cpni_wireless", "CPNI Wireless (CMRS)"),
("cpni_clec_reseller_generator", "generate_cpni_clec_reseller", "CPNI CLEC Reseller"),
("cpni_ixc_reseller_generator", "generate_cpni_ixc_reseller", "CPNI IXC Reseller"),
("cpni_wireless_mvno_generator", "generate_cpni_wireless_mvno", "CPNI Wireless MVNO"),
("cpni_private_line_generator", "generate_cpni_private_line", "CPNI Private Line"),
("cpni_satellite_generator", "generate_cpni_satellite", "CPNI Satellite"),
("cpni_audio_bridge_generator", "generate_cpni_audio_bridge", "CPNI Audio Bridge"),
]
for i, (module, func_name, label) in enumerate(CPNI_VARIANTS, 10):
def make_gen(m=module, fn=func_name, n=i):
def gen():
mod = __import__(f"scripts.document_gen.templates.{m}", fromlist=[fn])
func = getattr(mod, fn)
return func(**CPNI_COMMON, output_path=os.path.join(WORK, f"{n:02d}_{m}.docx"))
return gen
try_gen(i, label, make_gen())
# 19-24. CALEA variants
CALEA_VARIANTS = [
("calea_wireless_generator", "generate_calea_wireless", "CALEA Wireless (CMRS)"),
("calea_ixc_ss7_generator", "generate_calea_ixc_ss7", "CALEA IXC SS7"),
("calea_clec_ss7_generator", "generate_calea_clec_ss7", "CALEA CLEC SS7"),
("calea_satellite_generator", "generate_calea_satellite", "CALEA Satellite"),
("calea_wireless_mvno_generator", "generate_calea_wireless_mvno", "CALEA Wireless MVNO"),
("calea_audio_bridge_generator", "generate_calea_audio_bridge", "CALEA Audio Bridge"),
]
for i, (module, func_name, label) in enumerate(CALEA_VARIANTS, 19):
def make_gen(m=module, fn=func_name, n=i):
def gen():
mod = __import__(f"scripts.document_gen.templates.{m}", fromlist=[fn])
func = getattr(mod, fn)
return func(**CALEA_COMMON, output_path=os.path.join(WORK, f"{n:02d}_{m}.docx"))
return gen
try_gen(i, label, make_gen())
# 25. Engagement Letter (499-A)
def gen25():
from scripts.document_gen.templates.engagement_letter_499a import generate_engagement_letter
return generate_engagement_letter(
entity_name="Acme Telecom LLC",
contact_name="John Smith",
contact_email="john@acmetelecom.example",
order_number="CO-TEST-ENG",
filing_years=[2023, 2024, 2025],
output_path=os.path.join(WORK, f"25_engagement_letter.docx"),
)
try_gen(25, "Engagement Letter (499-A Past-Due)", gen25)
print(f"\n=== Generated {len(generated)} documents ===")
# Email all
if generated:
msg = MIMEMultipart()
msg["From"] = "Performance West <noreply@performancewest.net>"
msg["To"] = "justin@performancewest.net"
msg["Subject"] = f"All Templates: {len(generated)} documents ({TODAY})"
msg["Reply-To"] = "info@performancewest.net"
body = f"{len(generated)} document templates generated with sample data.\n\n"
body += "Entity: Acme Telecom LLC (FRN: 0015341902)\n\n"
for name, path in generated:
size = os.path.getsize(path)
body += f" {name}: {os.path.basename(path)} ({size:,} bytes)\n"
msg.attach(MIMEText(body, "plain"))
for name, path in generated:
with open(path, "rb") as f:
part = MIMEBase("application", "octet-stream")
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", f'attachment; filename="{os.path.basename(path)}"')
msg.attach(part)
with smtplib.SMTP("email-smtp.us-east-2.amazonaws.com", 587, timeout=30) as s:
s.starttls()
s.login("AKIAYEWLMNWPHSHQWCRD", "BKrUBud+KjyaRA1RiA26FFu1R+hqR4cpFShwbZf7RUzG")
s.send_message(msg)
print(f"\nEmailed {len(generated)} documents to justin@performancewest.net")

View file

@ -1164,7 +1164,30 @@ class CanadaCRTCHandler(BaseServiceHandler):
from_password=regulatory_pw,
)
# 9b: Email print/ship instructions to admin
# 9b: Email CRTC notification letter to Secretary General
# Per CRTC Telecom Information Bulletin 2015-134, new entrants
# must notify the CRTC. Electronic submission is accepted.
crtc_letter_path = None
for fpath in generated_files:
if "crtc" in Path(fpath).name.lower() and fpath.endswith(".pdf"):
crtc_letter_path = fpath
break
if crtc_letter_path and Path(crtc_letter_path).exists():
try:
self._send_crtc_notification_email(
entity_name=formation_order.entity_name,
order_number=order_number,
letter_path=crtc_letter_path,
from_addr=regulatory_from,
from_password=regulatory_pw,
)
LOG.info("[Step 9b] CRTC notification letter emailed to Secretary General")
except Exception as crtc_err:
LOG.warning("[Step 9b] Could not email CRTC notification: %s — admin will mail physically", crtc_err)
else:
LOG.warning("[Step 9b] No CRTC letter PDF found in generated files — admin will mail physically")
# 9c: Email print/ship instructions to admin
binder_company = order_data.get("custom_own_ca_company") or ""
binder_attn = order_data.get("custom_own_ca_attn") or ""
if binder_path:
@ -1556,6 +1579,9 @@ class CanadaCRTCHandler(BaseServiceHandler):
f"**BITS Registration — {entity_name}** (Order: {order_number})\n\n"
f"The CRTC notification letter has been sent to the Secretary General "
f"via the signed letter included in the corporate binder.\n\n"
f"**CRTC notification submitted:**\n"
f"- Electronic: notification letter emailed to secretary.general@crtc.gc.ca\n"
f"- Physical: included in corporate binder (ship to address below)\n\n"
f"**Action required:**\n"
f"1. Confirm the binder was shipped to the CRTC address:\n"
f" {crtc_config.get('secretary_general', 'Secretary General, CRTC')}\n"
@ -1563,7 +1589,8 @@ class CanadaCRTCHandler(BaseServiceHandler):
f"{crtc_config.get('city', '')}, {crtc_config.get('province', '')} "
f"{crtc_config.get('postal_code', '')}\n"
f"2. Monitor for CRTC acknowledgement letter (30-60 days)\n"
f"3. File acknowledgement in ERPNext Sensitive ID when received"
f"3. File acknowledgement in ERPNext Sensitive ID when received\n"
f"4. After acknowledgement: request ATS activation code ({ats_config.get('activation_code_phone', '1-877-249-2782')})"
f"{reg_line}"
f"{gckey_status}"
)
@ -2292,6 +2319,55 @@ class CanadaCRTCHandler(BaseServiceHandler):
self._send_email(to_email=to_email, subject=subject, body=body)
def _send_crtc_notification_email(
self,
entity_name: str,
order_number: str,
letter_path: str,
from_addr: str | None = None,
from_password: str | None = None,
) -> None:
"""Email the signed CRTC notification letter to the Secretary General.
Sends FROM the customer's regulatory@domain.ca if available, otherwise
from Performance West. The CRTC accepts electronic notifications.
On dev, redirects to admin email.
"""
crtc_email = "secretary.general@crtc.gc.ca"
admin_email = os.environ.get("ADMIN_EMAIL", "ops@performancewest.net")
# Dev mode: redirect to admin
if os.environ.get("NODE_ENV") == "development":
crtc_email = admin_email
LOG.info("Dev mode: redirecting CRTC notification to %s", crtc_email)
subject = f"Notification of New Telecommunications Service Provider — {entity_name}"
body = (
f"Dear Secretary General,\n\n"
f"Please find attached the formal notification letter for {entity_name}, "
f"a new telecommunications service provider, pursuant to the Telecommunications Act "
f"and CRTC Telecom Information Bulletin 2015-134.\n\n"
f"This notification is being filed on behalf of {entity_name} by "
f"Performance West Inc., their authorized compliance representative.\n\n"
f"Please do not hesitate to contact us if any additional information is required.\n\n"
f"Respectfully,\n"
f"Performance West Inc.\n"
f"525 Randall Ave Ste 100-1195\n"
f"Cheyenne, WY 82001\n"
f"(888) 411-0383\n"
f"info@performancewest.net\n\n"
f"Order reference: {order_number}"
)
self._send_email(
to_email=crtc_email,
subject=subject,
body=body,
attachment_path=letter_path,
from_addr=from_addr,
from_password=from_password,
)
@staticmethod
def _send_email(
self,