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:
root
2026-08-26 02:26:33 -04:00
parent 7a5603b495
commit 7a62b0b340
46 changed files with 11004 additions and 35 deletions
+95
View File
@@ -50,6 +50,24 @@ CREATE TABLE IF NOT EXISTS claims (
invoice_date TEXT, -- ISO date
date_assigned TEXT, -- set when moved out of NEW
date_resolved TEXT, -- set on SETTLED/CLOSED/WRITE_OFF
-- Client signed representations at intake (added via db._migrate on existing DBs)
no_other_agency_at TEXT, -- confirmed no other agency/attorney engaged
no_prior_action_at TEXT, -- confirmed no prior litigation/judgment/bankruptcy
-- AI analysis + approval (added via db._migrate on existing DBs)
analysis_score INTEGER, -- 0-100, NULL until analyzed
analysis_summary TEXT, -- plain-English narrative
analysis_components TEXT, -- JSON breakdown of component scores
analysis_at TEXT, -- ISO timestamp of analysis
recommended_tier TEXT, -- AI-recommended starting tier
approval_status TEXT NOT NULL DEFAULT 'NONE'
CHECK (approval_status IN ('NONE','PENDING','APPROVED','REJECTED')),
approval_decision_by TEXT, -- staff name who approved/rejected
approval_decision_at TEXT,
-- AI-recommended letter (staff-customizable)
letter_subject TEXT,
letter_body TEXT,
letter_tier TEXT, -- tier the letter was generated for
letter_updated_at TEXT,
twentycrm_id TEXT, -- nullable
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
@@ -137,3 +155,80 @@ CREATE TABLE IF NOT EXISTS schema_migrations (
version TEXT PRIMARY KEY,
applied_at TEXT NOT NULL
);
-- Welcome-packet field overrides: client's final value per profile field,
-- merged over pre-populated submission values at render time.
CREATE TABLE IF NOT EXISTS packet_field_overrides (
id TEXT PRIMARY KEY,
claim_id TEXT NOT NULL REFERENCES claims(id) ON DELETE CASCADE,
profile_key TEXT NOT NULL,
value TEXT,
updated_by TEXT NOT NULL DEFAULT 'CLIENT' CHECK (updated_by IN ('CLIENT','STAFF')),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(claim_id, profile_key)
);
CREATE INDEX IF NOT EXISTS idx_packet_overrides_claim ON packet_field_overrides(claim_id);
-- Onboarding paperwork receipt tracking. One row per welcome-packet document
-- per claim; staff mark each doc "received" once the client returns it.
CREATE TABLE IF NOT EXISTS onboarding_docs (
id TEXT PRIMARY KEY,
claim_id TEXT NOT NULL REFERENCES claims(id) ON DELETE CASCADE,
doc_key TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING','RECEIVED')),
received_by TEXT,
received_at TEXT,
created_at TEXT NOT NULL,
UNIQUE(claim_id, doc_key)
);
CREATE INDEX IF NOT EXISTS idx_onboarding_claim ON onboarding_docs(claim_id);
-- Physical mail queue (LetterStream). Lifecycle: DRAFT -> APPROVED -> PREAUTH -> SENT
-- (+ REJECTED / CANCELLED / ERROR). One row per mailed letter.
CREATE TABLE IF NOT EXISTS letters (
id TEXT PRIMARY KEY, -- uuid4
claim_id TEXT NOT NULL REFERENCES claims(id) ON DELETE CASCADE,
letter_type TEXT NOT NULL DEFAULT 'demand',
subject TEXT,
body TEXT, -- rendered letter body snapshot
recipient_json TEXT NOT NULL, -- structured {name,name2,addr1,addr2,city,state,zip}
sender_json TEXT NOT NULL, -- structured {name,addr1,addr2,city,state,zip}
mailtype TEXT NOT NULL DEFAULT 'firstclass',
status TEXT NOT NULL DEFAULT 'DRAFT'
CHECK (status IN ('DRAFT','APPROVED','PREAUTH','SENT','REJECTED','CANCELLED','ERROR')),
job_id TEXT, -- LetterStream unique job name
batch_id TEXT, -- LetterStream batch id
doc_id TEXT, -- LetterStream doc id
tracking_no TEXT, -- USPS tracking (certified mail)
cost_cents INTEGER, -- pricing from preauth/response
authcode TEXT, -- preauth authcode (release via doauth)
pages INTEGER,
pdf_path TEXT, -- rendered PDF on disk
error TEXT,
note TEXT, -- reject/cancel reason (terminal states)
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
sent_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_letters_claim ON letters(claim_id);
CREATE INDEX IF NOT EXISTS idx_letters_status ON letters(status);
CREATE INDEX IF NOT EXISTS idx_letters_doc ON letters(doc_id);
-- LetterStream tracking scan events (pushed via callback every 4h).
CREATE TABLE IF NOT EXISTS letter_events (
id TEXT PRIMARY KEY,
letter_id TEXT NOT NULL REFERENCES letters(id) ON DELETE CASCADE,
scan_code TEXT,
scan_status TEXT,
scan_date TEXT,
scan_zip TEXT,
scan_facility TEXT,
tracking_id TEXT,
batch_id TEXT,
job_id TEXT,
doc_id TEXT,
raw_json TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_letter_events_letter ON letter_events(letter_id);