← back to NationalPaperHangers

db/migrations/017_coi_requests.sql

83 lines

-- 017 · Live COI request flow (UX idea #5)
--
-- Today: designers calling installers, calling brokers, 3-day phone-tag
-- before a $4k/roll de Gournay install. Generic directories treat insurance
-- as a yes/no signal; luxury commercial + hospitality need an actual
-- designer-named-additional-insured cert PER JOB.
--
-- v0: capture structured insurance metadata on the installer side, give
-- designers a one-form COI request that emails the installer + their
-- broker with the designer-named additional-insured details. PDF
-- generation is intentionally NOT in v0 — the broker does that.
--
-- Reversible:
--   ALTER TABLE installers DROP COLUMN insurance;
--   DROP TABLE coi_requests;

BEGIN;

-- Structured insurance metadata. Existing boolean `insurance_on_file` +
-- `insurance_expires DATE` stay as the lightweight summary. JSONB carries
-- the rich data so we don't reshape the table later.
ALTER TABLE installers
  ADD COLUMN IF NOT EXISTS insurance JSONB;
-- Expected shape:
-- {
--   "carrier": "Hartford",
--   "policy_number": "GL-2024-12345",
--   "limits": { "general_aggregate_usd": 2000000, "per_occurrence_usd": 1000000 },
--   "expiry": "2026-12-31",
--   "broker_name": "Jane Broker",
--   "broker_email": "jane@broker.com",
--   "broker_phone": "+1-555-0100",
--   "auto_attach_to_email": true
-- }

CREATE TABLE IF NOT EXISTS coi_requests (
  id                          SERIAL PRIMARY KEY,
  installer_id                INTEGER NOT NULL REFERENCES installers(id) ON DELETE CASCADE,

  -- Designer / requester
  designer_name               TEXT NOT NULL,
  designer_company            TEXT,
  designer_email              TEXT NOT NULL,
  designer_phone              TEXT,

  -- Project / job
  project_name                TEXT,
  project_address             TEXT,
  project_start_date          DATE,
  project_value_usd           NUMERIC(12,2),

  -- Additional-insured party (often the design firm itself, sometimes the
  -- end client). The broker NEEDS this exact phrasing on the cert.
  additional_insured_name     TEXT NOT NULL,
  additional_insured_address  TEXT,

  notes                       TEXT,

  -- Workflow
  status                      TEXT NOT NULL DEFAULT 'pending',
                              -- pending | acknowledged | fulfilled | declined | expired
  installer_notified_at       TIMESTAMPTZ,
  broker_notified_at          TIMESTAMPTZ,
  fulfilled_at                TIMESTAMPTZ,
  fulfilled_pdf_url           TEXT,
  decline_reason              TEXT,

  source_ip                   TEXT,
  source_user_agent           TEXT,
  created_at                  TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at                  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_coi_requests_installer ON coi_requests(installer_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_coi_requests_status ON coi_requests(status, created_at DESC);

ALTER TABLE coi_requests
  DROP CONSTRAINT IF EXISTS coi_requests_status_check,
  ADD CONSTRAINT coi_requests_status_check
    CHECK (status IN ('pending','acknowledged','fulfilled','declined','expired'));

COMMIT;