Backend Aug 22-25: AI analysis, welcome-packet templating, LetterStream, DocuSeal, staff RBAC + tier gate
- analysis.py: deterministic claim scorer + /analyze /approve /letter /advance-tier endpoints (auto-runs on intake) - packet.py + packet_fields.json: welcome-packet templating engine (6 onboarding docs, field catalog) - letterstream.py + letters.py: certified-mail send pipeline + letter lifecycle (webhook verified) - docuseal.py: DocuSeal signing integration - staff.py/models.py/schema.sql/auth.py: approval actor from staff key, tier gate (APPROVED+ACTIVE+onboarding docs), onboarding_docs table - frontend/: dependency-free static portal (intake, magic-link login/verify, dashboard) - landing-mockups/: 4 design-stance mockups + favicons - legal/: aup/privacy/sms-terms/terms HTML - docs/: letter-queue scope, letterstream API contract, 6 welcome-packet templates - review-dre-landing-2026-08-21.md: 3-variant landing feedback sprint - compliance/DRE_Compliance_Manual.md: updated Source synced from deployed /opt/dre-portal/app/ (was 4 days ahead of git).
This commit is contained in:
+16
-4
@@ -18,6 +18,12 @@ def _env(name: str, default: str = "") -> str:
|
||||
return os.environ.get(name, default)
|
||||
|
||||
|
||||
def _team_recipients() -> list[str]:
|
||||
"""Fan-out list from DRE_TEAM_NOTIFY (comma-separated). Defaults to dre@."""
|
||||
raw = _env("DRE_TEAM_NOTIFY", "dre@debtrecoveryexperts.com")
|
||||
return [a.strip() for a in raw.split(",") if a.strip()]
|
||||
|
||||
|
||||
def send_email(to_addr: str, subject: str, body_text: str, html: str | None = None) -> bool:
|
||||
"""Send an email via the configured SMTP relay. Returns True on success, False on failure.
|
||||
Never raises — caller proceeds regardless."""
|
||||
@@ -47,7 +53,7 @@ def send_email(to_addr: str, subject: str, body_text: str, html: str | None = No
|
||||
|
||||
def notify_team_intake(claim_number: str, client_number: str, company_name: str,
|
||||
amount_cents: int, debtor_name: str) -> bool:
|
||||
team = _env("DRE_TEAM_NOTIFY", "dre@debtrecoveryexperts.com")
|
||||
team = _team_recipients()
|
||||
base = _env("DRE_BASE_URL", "https://my.debtrecoveryexperts.com")
|
||||
dollars = amount_cents / 100.0
|
||||
body = (
|
||||
@@ -66,7 +72,10 @@ def notify_team_intake(claim_number: str, client_number: str, company_name: str,
|
||||
f"<b>Amount:</b> ${dollars:,.2f}</p>"
|
||||
f"<p><a href=\"{base}/\">Review in portal</a></p>"
|
||||
)
|
||||
return send_email(team, f"New DRE Claim: {claim_number}", body, html)
|
||||
ok = True
|
||||
for addr in team:
|
||||
ok = send_email(addr, f"New DRE Claim: {claim_number}", body, html) and ok
|
||||
return ok
|
||||
|
||||
|
||||
def send_magic_link(to_addr: str, raw_token: str, client_number: str) -> bool:
|
||||
@@ -92,11 +101,14 @@ def send_magic_link(to_addr: str, raw_token: str, client_number: str) -> bool:
|
||||
|
||||
def notify_team_message(claim_number: str, subject: str, content: str,
|
||||
author: str) -> bool:
|
||||
team = _env("DRE_TEAM_NOTIFY", "dre@debtrecoveryexperts.com")
|
||||
team = _team_recipients()
|
||||
body = (
|
||||
f"New client message on claim {claim_number}.\n\n"
|
||||
f"From: {author}\n"
|
||||
f"Subject: {subject}\n\n"
|
||||
f"{content}\n"
|
||||
)
|
||||
return send_email(team, f"Client message on {claim_number}: {subject}", body)
|
||||
ok = True
|
||||
for addr in team:
|
||||
ok = send_email(addr, f"Client message on {claim_number}: {subject}", body) and ok
|
||||
return ok
|
||||
|
||||
Reference in New Issue
Block a user