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>
118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
"""Alabama — SOS portal automation."""
|
|
|
|
from __future__ import annotations
|
|
from scripts.formation.base import StatePortal, NameSearchResult, FormationOrder, FilingResult, FilingStatus
|
|
from .config import CONFIG
|
|
|
|
|
|
class ALPortal(StatePortal):
|
|
STATE_CODE = "AL"
|
|
STATE_NAME = "Alabama"
|
|
PORTAL_NAME = CONFIG["portal_name"]
|
|
PORTAL_URL = CONFIG["portal_url"]
|
|
NWRA_ADDRESS = CONFIG["nwra_address"]
|
|
NWRA_CITY = CONFIG["nwra_city"]
|
|
NWRA_STATE = CONFIG["nwra_state"]
|
|
NWRA_ZIP = CONFIG["nwra_zip"]
|
|
|
|
async def search_name(self, name: str) -> NameSearchResult:
|
|
"""Search Alabama business name availability."""
|
|
try:
|
|
page = await self.start_browser()
|
|
await page.goto(CONFIG["name_search_url"])
|
|
await self.human_delay()
|
|
|
|
# Type name into search field
|
|
sel = CONFIG["selectors"]
|
|
if sel["name_search_input"]:
|
|
await self.type_slowly(sel["name_search_input"], name)
|
|
await self.safe_click(sel["name_search_submit"])
|
|
await page.wait_for_load_state("networkidle")
|
|
|
|
content = await page.content()
|
|
available = CONFIG["selectors"]["name_unavailable_indicator"] not in content
|
|
|
|
return NameSearchResult(
|
|
available=available,
|
|
state_code=self.STATE_CODE,
|
|
searched_name=name,
|
|
raw_response=content[:2000],
|
|
)
|
|
|
|
return NameSearchResult(
|
|
available=False,
|
|
state_code=self.STATE_CODE,
|
|
searched_name=name,
|
|
raw_response="Selectors not yet configured for this state",
|
|
)
|
|
except Exception as e:
|
|
self.log.error("Name search failed: %s", e)
|
|
return NameSearchResult(
|
|
available=False,
|
|
state_code=self.STATE_CODE,
|
|
searched_name=name,
|
|
raw_response=str(e),
|
|
)
|
|
finally:
|
|
await self.close_browser()
|
|
|
|
async def file_llc(self, order: FormationOrder) -> FilingResult:
|
|
"""File LLC in Alabama."""
|
|
try:
|
|
page = await self.start_browser()
|
|
await page.goto(CONFIG["filing_url"])
|
|
await self.human_delay()
|
|
await self.screenshot("llc_start")
|
|
|
|
# TODO: Implement Alabama-specific LLC filing flow
|
|
# Each state's portal has different form fields, steps, and workflows.
|
|
# The selectors in config.py need to be populated by inspecting the portal.
|
|
|
|
return FilingResult(
|
|
success=False,
|
|
status=FilingStatus.ERROR,
|
|
state_code=self.STATE_CODE,
|
|
entity_name=order.entity_name,
|
|
error_message="LLC filing automation not yet implemented for Alabama",
|
|
screenshot_path=await self.screenshot("llc_not_implemented"),
|
|
)
|
|
except Exception as e:
|
|
self.log.error("LLC filing failed: %s", e)
|
|
return FilingResult(
|
|
success=False,
|
|
status=FilingStatus.ERROR,
|
|
state_code=self.STATE_CODE,
|
|
entity_name=order.entity_name,
|
|
error_message=str(e),
|
|
)
|
|
finally:
|
|
await self.close_browser()
|
|
|
|
async def file_corporation(self, order: FormationOrder) -> FilingResult:
|
|
"""File Corporation in Alabama."""
|
|
try:
|
|
page = await self.start_browser()
|
|
await page.goto(CONFIG["filing_url"])
|
|
await self.human_delay()
|
|
|
|
return FilingResult(
|
|
success=False,
|
|
status=FilingStatus.ERROR,
|
|
state_code=self.STATE_CODE,
|
|
entity_name=order.entity_name,
|
|
error_message="Corporation filing automation not yet implemented for Alabama",
|
|
)
|
|
except Exception as e:
|
|
return FilingResult(
|
|
success=False,
|
|
status=FilingStatus.ERROR,
|
|
state_code=self.STATE_CODE,
|
|
entity_name=order.entity_name,
|
|
error_message=str(e),
|
|
)
|
|
finally:
|
|
await self.close_browser()
|
|
|
|
|
|
def adapter() -> ALPortal:
|
|
return ALPortal()
|