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:
+49
-4
@@ -103,13 +103,15 @@ class IntakeRequest(BaseModel):
|
||||
debtor: IntakeDebtor
|
||||
claim: IntakeClaim
|
||||
tos_accepted: bool = True
|
||||
no_other_agency_accepted: bool
|
||||
no_prior_action_accepted: bool
|
||||
turnstile_token: str | None = None
|
||||
|
||||
@field_validator("tos_accepted")
|
||||
@field_validator("tos_accepted", "no_other_agency_accepted", "no_prior_action_accepted")
|
||||
@classmethod
|
||||
def _tos(cls, v):
|
||||
def _bool_req(cls, v):
|
||||
if v is not True:
|
||||
raise ValueError("tos_accepted must be true")
|
||||
raise ValueError("must be true")
|
||||
return v
|
||||
|
||||
|
||||
@@ -175,7 +177,7 @@ class StaffNoteCreate(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
content: str = Field(..., min_length=1, max_length=10000)
|
||||
visibility: str = "INTERNAL"
|
||||
author_name: str = Field(..., min_length=1, max_length=200)
|
||||
author_name: str | None = Field(None, max_length=200)
|
||||
|
||||
@field_validator("visibility")
|
||||
@classmethod
|
||||
@@ -188,3 +190,46 @@ class StaffNoteCreate(BaseModel):
|
||||
@classmethod
|
||||
def _v(cls, v):
|
||||
return _scan_pii(v)
|
||||
|
||||
|
||||
class StaffApproval(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
decision: str
|
||||
staff_name: str | None = Field(None, max_length=200)
|
||||
reason: str | None = Field(None, max_length=500)
|
||||
|
||||
@field_validator("decision")
|
||||
@classmethod
|
||||
def _dec(cls, v):
|
||||
v = v.upper()
|
||||
if v not in ("APPROVE", "REJECT"):
|
||||
raise ValueError("decision must be APPROVE or REJECT")
|
||||
return v
|
||||
|
||||
@field_validator("staff_name", "reason")
|
||||
@classmethod
|
||||
def _v(cls, v):
|
||||
return _scan_pii(v)
|
||||
|
||||
|
||||
class StaffLetterUpdate(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
subject: str = Field(..., min_length=1, max_length=200)
|
||||
body: str = Field(..., min_length=1, max_length=20000)
|
||||
staff_name: str | None = Field(None, max_length=200)
|
||||
|
||||
@field_validator("subject", "body", "staff_name")
|
||||
@classmethod
|
||||
def _v(cls, v):
|
||||
return _scan_pii(v)
|
||||
|
||||
|
||||
class StaffOnboardingUpdate(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
received: bool
|
||||
received_by: str | None = Field(None, max_length=200)
|
||||
|
||||
@field_validator("received_by")
|
||||
@classmethod
|
||||
def _v(cls, v):
|
||||
return _scan_pii(v)
|
||||
|
||||
Reference in New Issue
Block a user