← back to NationalPaperHangers

db/migrations/011_installer_credentials.sql

64 lines

-- 011 · Brand-trained credentials (UX idea #2)
--
-- "Verified" is meaningless to a designer specifying $4K/roll de Gournay.
-- They want: "Has this installer been TRAINED by de Gournay?" We capture
-- structured per-brand credentials with optional cert scan + dates.
--
-- Existing `installers.accreditations TEXT[]` stays as the lightweight
-- catch-all (e.g. "WIA Certified Installer"). Brand-specific training lives
-- in the new structured table so each badge can carry a verifiable cert.
--
-- Reversible: DROP TABLE installer_credentials;

BEGIN;

CREATE TABLE IF NOT EXISTS installer_credentials (
  id                 SERIAL PRIMARY KEY,
  installer_id       INTEGER NOT NULL REFERENCES installers(id) ON DELETE CASCADE,
  brand              TEXT NOT NULL,
  credential_type    TEXT NOT NULL DEFAULT 'brand_trained',
                     -- brand_trained | brand_certified | brand_approved | manufacturer_partner | trade_member
  year_issued        INTEGER,
  year_expires       INTEGER,
  certificate_url    TEXT,
                     -- HTTPS URL of the cert scan. Validated http(s) only at write time.
  notes              TEXT,
  ops_verified       BOOLEAN NOT NULL DEFAULT false,
  ops_verified_at    TIMESTAMPTZ,
  ops_verified_by    TEXT,
  display_order      INTEGER DEFAULT 0,
  created_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at         TIMESTAMPTZ NOT NULL DEFAULT now(),

  CONSTRAINT installer_credentials_type_check
    CHECK (credential_type IN ('brand_trained','brand_certified','brand_approved','manufacturer_partner','trade_member')),
  CONSTRAINT installer_credentials_year_check
    CHECK (year_issued IS NULL OR (year_issued >= 1900 AND year_issued <= 2100)),
  CONSTRAINT installer_credentials_expiry_check
    CHECK (year_expires IS NULL OR (year_expires >= 1900 AND year_expires <= 2200))
);

-- Per-installer view + ordering. Most installers will have 1-5 credentials.
CREATE INDEX IF NOT EXISTS idx_installer_credentials_installer
  ON installer_credentials (installer_id, display_order, year_issued DESC NULLS LAST);

-- Verified-only filter for the lead-side directory ("show me only de Gournay-trained installers").
CREATE INDEX IF NOT EXISTS idx_installer_credentials_brand
  ON installer_credentials (LOWER(brand))
  WHERE ops_verified = true;

-- Updated-at touch trigger — uses the same pattern as bookings/installers.
DROP TRIGGER IF EXISTS trg_installer_credentials_updated ON installer_credentials;
CREATE OR REPLACE FUNCTION installer_credentials_touch_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_installer_credentials_updated
  BEFORE UPDATE ON installer_credentials
  FOR EACH ROW EXECUTE FUNCTION installer_credentials_touch_updated_at();

COMMIT;