← back to StudentLoanTracker

migrations/001_initial_schema.sql

124 lines

-- StudentLoanTracker Initial Schema Migration
-- Created: 2026-03-25
-- Database: dw_unified, Schema: slt

CREATE SCHEMA IF NOT EXISTS slt;

-- Users (synced from Supabase Auth or local)
CREATE TABLE IF NOT EXISTS slt.users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT,
  phone TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  sms_consent BOOLEAN DEFAULT FALSE,
  sms_consent_at TIMESTAMPTZ,
  preferences JSONB DEFAULT '{}'
);

-- Loan records (manual entry or parsed from TXT)
CREATE TABLE IF NOT EXISTS slt.loans (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES slt.users(id),
  session_id TEXT,
  loan_type TEXT,
  servicer TEXT,
  original_balance NUMERIC(12,2),
  current_balance NUMERIC(12,2),
  interest_rate NUMERIC(5,3),
  disbursement_date DATE,
  status TEXT,
  repayment_plan TEXT,
  raw_data JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Calculator results (snapshots)
CREATE TABLE IF NOT EXISTS slt.calc_results (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES slt.users(id),
  session_id TEXT,
  calc_type TEXT,
  inputs JSONB,
  outputs JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- PSLF tracking
CREATE TABLE IF NOT EXISTS slt.pslf_progress (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES slt.users(id),
  qualifying_payments INT DEFAULT 0,
  employer_type TEXT,
  employer_name TEXT,
  current_plan TEXT,
  start_date DATE,
  notes TEXT,
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Document uploads (metadata only)
CREATE TABLE IF NOT EXISTS slt.documents (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES slt.users(id),
  session_id TEXT,
  filename TEXT,
  mime_type TEXT,
  classification TEXT,
  confidence NUMERIC(3,2),
  extracted_data JSONB,
  retained BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Servicer directory (admin-managed)
CREATE TABLE IF NOT EXISTS slt.servicers (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  phone TEXT,
  website TEXT,
  official BOOLEAN DEFAULT TRUE,
  notes TEXT,
  last_verified DATE,
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Seed official servicers
INSERT INTO slt.servicers (name, phone, website, official, last_verified, notes) VALUES
  ('Edfinancial', '1-855-337-6884', 'https://edfinancial.com', true, '2026-03-25', 'Official federal servicer'),
  ('MOHELA', '1-888-866-4352', 'https://www.mohela.com', true, '2026-03-25', 'Official federal servicer, commonly handles PSLF'),
  ('Aidvantage', '1-800-722-1300', 'https://aidvantage.com', true, '2026-03-25', 'Official federal servicer'),
  ('Nelnet', '1-888-486-4722', 'https://www.nelnet.com', true, '2026-03-25', 'Official federal servicer')
ON CONFLICT DO NOTHING;

-- SMS/email communications log
CREATE TABLE IF NOT EXISTS slt.comms_log (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES slt.users(id),
  channel TEXT,
  direction TEXT,
  content_summary TEXT,
  status TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Admin audit log
CREATE TABLE IF NOT EXISTS slt.audit_log (
  id SERIAL PRIMARY KEY,
  admin_user TEXT,
  action TEXT,
  target_table TEXT,
  target_id TEXT,
  details JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes
CREATE INDEX IF NOT EXISTS idx_loans_user ON slt.loans(user_id);
CREATE INDEX IF NOT EXISTS idx_loans_session ON slt.loans(session_id);
CREATE INDEX IF NOT EXISTS idx_calc_results_user ON slt.calc_results(user_id);
CREATE INDEX IF NOT EXISTS idx_calc_results_session ON slt.calc_results(session_id);
CREATE INDEX IF NOT EXISTS idx_pslf_user ON slt.pslf_progress(user_id);
CREATE INDEX IF NOT EXISTS idx_docs_user ON slt.documents(user_id);
CREATE INDEX IF NOT EXISTS idx_comms_user ON slt.comms_log(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_created ON slt.audit_log(created_at);