← back to Ventura Corridor
db/migrations/008_pitches.sql
42 lines
-- 008_pitches.sql · DW pitch pipeline
-- Each row = one outreach target. Lifecycle status walks left-to-right.
CREATE TABLE IF NOT EXISTS pitches (
id BIGSERIAL PRIMARY KEY,
business_id BIGINT NOT NULL REFERENCES businesses(id) ON DELETE CASCADE,
pitch_type TEXT NOT NULL, -- 'trade-partner' | 'trade-client' | 'cross-sell' | 'showroom-partner' | 'retail-counter' | 'skip-misfile' | 'unknown'
priority SMALLINT NOT NULL DEFAULT 5, -- 1=DW peer, 2=interior firm, 3=design, 4=home-furnish, 5=furniture, 9=skip
status TEXT NOT NULL DEFAULT 'draft',-- 'draft'|'researched'|'scrubbed'|'approved'|'sent'|'replied'|'won'|'lost'|'skip'
subject TEXT,
body TEXT,
observation TEXT, -- "what we observed" notes filled in by hand
why_dw_fits TEXT,
research_links JSONB DEFAULT '{}'::jsonb, -- {google, gmaps, yelp, linkedin, ig}
pitch_md_path TEXT, -- relative path to data/pitches/<id>-<slug>.md
-- Lifecycle timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
scrubbed_at TIMESTAMPTZ,
approved_at TIMESTAMPTZ,
sent_at TIMESTAMPTZ,
replied_at TIMESTAMPTZ,
closed_at TIMESTAMPTZ,
-- Outcome
outcome TEXT, -- 'sale'|'meeting'|'unsubscribe'|'ignored'|'wrong_target'|null
notes TEXT,
UNIQUE (business_id)
);
CREATE INDEX IF NOT EXISTS idx_pitches_status ON pitches (status);
CREATE INDEX IF NOT EXISTS idx_pitches_priority ON pitches (priority);
CREATE INDEX IF NOT EXISTS idx_pitches_type ON pitches (pitch_type);
CREATE INDEX IF NOT EXISTS idx_pitches_outcome ON pitches (outcome) WHERE outcome IS NOT NULL;
-- update_updated_at trigger
CREATE OR REPLACE FUNCTION pitches_touch_updated_at() RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
$$;
DROP TRIGGER IF EXISTS pitches_updated_at ON pitches;
CREATE TRIGGER pitches_updated_at BEFORE UPDATE ON pitches
FOR EACH ROW EXECUTE FUNCTION pitches_touch_updated_at();