-- NPI Product Tracker Database Schema -- All tables for tracking product lifecycle through NPI gates CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Product category enum CREATE TYPE category AS ENUM ('I', 'IB', 'II', 'III'); -- Project status enum CREATE TYPE project_status AS ENUM ('active', 'on_hold', 'completed', 'cancelled'); -- Gate status enum CREATE TYPE gate_status AS ENUM ('not_started', 'in_progress', 'blocked', 'passed', 'skipped', 'failed'); -- RACI type enum CREATE TYPE raci_type AS ENUM ('R', 'A', 'C', 'I'); -- Update type enum CREATE TYPE update_type AS ENUM ('progress', 'blocker', 'decision', 'note'); -- PPPP status enum CREATE TYPE pppp_status AS ENUM ('draft', 'in_review', 'approved', 'final'); -- Notification type enum CREATE TYPE notification_type AS ENUM ( 'overdue_update', 'gate_review', 'weekly_digest', 'assignment', 'gate_passed', 'blocker' ); -- ============================================================ -- REFERENCE TABLES (seeded from Excel, rarely changed) -- ============================================================ -- 13 gate templates (G0-G12) CREATE TABLE gates ( id SERIAL PRIMARY KEY, gate_number INTEGER NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, description TEXT, sort_order INTEGER NOT NULL ); -- Which gates apply to which categories CREATE TABLE gate_applicability ( id SERIAL PRIMARY KEY, gate_id INTEGER NOT NULL REFERENCES gates(id) ON DELETE CASCADE, category category NOT NULL, is_required BOOLEAN DEFAULT true, is_trigger_based BOOLEAN DEFAULT false, UNIQUE(gate_id, category) ); -- Steps within each gate per category (from Excel) CREATE TABLE gate_steps ( id SERIAL PRIMARY KEY, gate_id INTEGER NOT NULL REFERENCES gates(id) ON DELETE CASCADE, category category NOT NULL, step_name VARCHAR(500) NOT NULL, description TEXT, evidence_required TEXT, common_failure_modes TEXT, sort_order INTEGER NOT NULL DEFAULT 0 ); -- RACI assignment per step per role (reference) CREATE TABLE gate_step_raci ( id SERIAL PRIMARY KEY, gate_step_id INTEGER NOT NULL REFERENCES gate_steps(id) ON DELETE CASCADE, role_name VARCHAR(100) NOT NULL, raci raci_type NOT NULL, UNIQUE(gate_step_id, role_name, raci) ); -- ============================================================ -- PROJECT TABLES (instance data) -- ============================================================ -- Each product being tracked through NPI CREATE TABLE projects ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(255) NOT NULL, description TEXT, category category NOT NULL, current_gate INTEGER DEFAULT 0, status project_status DEFAULT 'active', -- Key owners stored as text (no users table yet) requestor_name VARCHAR(255), requestor_email VARCHAR(255), sponsor_name VARCHAR(255), sponsor_email VARCHAR(255), product_owner_name VARCHAR(255), product_owner_email VARCHAR(255), tech_lead_name VARCHAR(255), tech_lead_email VARCHAR(255), commercial_owner_name VARCHAR(255), commercial_owner_email VARCHAR(255), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Instance: a gate for a specific project CREATE TABLE project_gates ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, gate_id INTEGER NOT NULL REFERENCES gates(id) ON DELETE CASCADE, status gate_status DEFAULT 'not_started', target_start_date DATE, target_end_date DATE, actual_start_date DATE, actual_end_date DATE, approved_by VARCHAR(255), approved_at TIMESTAMPTZ, notes TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(project_id, gate_id) ); -- Instance: checklist items per project gate CREATE TABLE project_gate_checklist ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_gate_id UUID NOT NULL REFERENCES project_gates(id) ON DELETE CASCADE, gate_step_id INTEGER REFERENCES gate_steps(id), step_name VARCHAR(500) NOT NULL, is_completed BOOLEAN DEFAULT false, completed_by VARCHAR(255), completed_at TIMESTAMPTZ, evidence_link TEXT, evidence_notes TEXT, is_applicable BOOLEAN DEFAULT true, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- RACI assignments per project (who fills each role) CREATE TABLE project_raci_assignments ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, role_name VARCHAR(100) NOT NULL, assignee_name VARCHAR(255), assignee_email VARCHAR(255), created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(project_id, role_name) ); -- Trigger flags driving gate tailoring CREATE TABLE project_triggers ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, trigger_name VARCHAR(255) NOT NULL, is_triggered BOOLEAN DEFAULT false, notes TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(project_id, trigger_name) ); -- Status updates / progress notes CREATE TABLE status_updates ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, project_gate_id UUID REFERENCES project_gates(id) ON DELETE SET NULL, author_name VARCHAR(255) NOT NULL, author_email VARCHAR(255), content TEXT NOT NULL, update_type update_type DEFAULT 'progress', created_at TIMESTAMPTZ DEFAULT NOW() ); -- PPPP tracking (Category III commercial) CREATE TABLE pppp_tracking ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE, positioning TEXT, pricing TEXT, packaging TEXT, proposition TEXT, status pppp_status DEFAULT 'draft', version INTEGER DEFAULT 1, updated_by VARCHAR(255), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- In-app + email notifications CREATE TABLE notifications ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), recipient_email VARCHAR(255) NOT NULL, recipient_name VARCHAR(255), project_id UUID REFERENCES projects(id) ON DELETE CASCADE, type notification_type NOT NULL, subject VARCHAR(500), body TEXT, is_read BOOLEAN DEFAULT false, email_sent BOOLEAN DEFAULT false, email_sent_at TIMESTAMPTZ, mailgun_message_id VARCHAR(255), created_at TIMESTAMPTZ DEFAULT NOW() ); -- Audit log CREATE TABLE audit_log ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), actor_name VARCHAR(255), actor_email VARCHAR(255), project_id UUID REFERENCES projects(id) ON DELETE SET NULL, action VARCHAR(255) NOT NULL, entity_type VARCHAR(100), entity_id UUID, details JSONB, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================================================ -- INDEXES -- ============================================================ CREATE INDEX idx_project_gates_project ON project_gates(project_id); CREATE INDEX idx_project_gates_status ON project_gates(status); CREATE INDEX idx_checklist_project_gate ON project_gate_checklist(project_gate_id); CREATE INDEX idx_checklist_completed ON project_gate_checklist(is_completed) WHERE is_applicable = true; CREATE INDEX idx_raci_project ON project_raci_assignments(project_id); CREATE INDEX idx_triggers_project ON project_triggers(project_id); CREATE INDEX idx_updates_project ON status_updates(project_id, created_at DESC); CREATE INDEX idx_notifications_recipient ON notifications(recipient_email, is_read); CREATE INDEX idx_notifications_project ON notifications(project_id); CREATE INDEX idx_audit_project ON audit_log(project_id, created_at DESC); CREATE INDEX idx_projects_status ON projects(status); CREATE INDEX idx_projects_type ON projects(category); CREATE INDEX idx_projects_gate ON projects(current_gate); -- ============================================================ -- TRIGGERS (auto-update updated_at) -- ============================================================ CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_projects_updated_at BEFORE UPDATE ON projects FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_project_gates_updated_at BEFORE UPDATE ON project_gates FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_checklist_updated_at BEFORE UPDATE ON project_gate_checklist FOR EACH ROW EXECUTE FUNCTION update_updated_at(); CREATE TRIGGER trg_pppp_updated_at BEFORE UPDATE ON pppp_tracking FOR EACH ROW EXECUTE FUNCTION update_updated_at();