-- Migration 1001: Alert Notes System -- Description: Contextual popup/info system for customers and contacts -- Date: 2026-02-17 -- Alert Notes main table CREATE TABLE IF NOT EXISTS alert_notes ( id SERIAL PRIMARY KEY, entity_type VARCHAR(20) NOT NULL CHECK (entity_type IN ('customer', 'contact')), entity_id INTEGER NOT NULL, title VARCHAR(255) NOT NULL, message TEXT NOT NULL, severity VARCHAR(20) NOT NULL DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')), requires_acknowledgement BOOLEAN DEFAULT TRUE, active BOOLEAN DEFAULT TRUE, created_by_user_id INTEGER REFERENCES users(user_id) ON DELETE SET NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Alert Note Restrictions (who is allowed to handle the customer/contact) -- Supports both group-based and user-based restrictions CREATE TABLE IF NOT EXISTS alert_note_restrictions ( id SERIAL PRIMARY KEY, alert_note_id INTEGER NOT NULL REFERENCES alert_notes(id) ON DELETE CASCADE, restriction_type VARCHAR(20) NOT NULL CHECK (restriction_type IN ('group', 'user')), restriction_id INTEGER NOT NULL, -- restriction_id references either groups.id or users.user_id depending on restriction_type created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(alert_note_id, restriction_type, restriction_id) ); -- Alert Note Acknowledgements (tracking who has seen and acknowledged alerts) CREATE TABLE IF NOT EXISTS alert_note_acknowledgements ( id SERIAL PRIMARY KEY, alert_note_id INTEGER NOT NULL REFERENCES alert_notes(id) ON DELETE CASCADE, user_id INTEGER NOT NULL REFERENCES users(user_id) ON DELETE CASCADE, acknowledged_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(alert_note_id, user_id) ); -- Create indexes for performance CREATE INDEX IF NOT EXISTS idx_alert_notes_entity ON alert_notes(entity_type, entity_id); CREATE INDEX IF NOT EXISTS idx_alert_notes_active ON alert_notes(active) WHERE active = TRUE; CREATE INDEX IF NOT EXISTS idx_alert_notes_severity ON alert_notes(severity); CREATE INDEX IF NOT EXISTS idx_alert_notes_created_by ON alert_notes(created_by_user_id); CREATE INDEX IF NOT EXISTS idx_alert_note_restrictions_alert ON alert_note_restrictions(alert_note_id); CREATE INDEX IF NOT EXISTS idx_alert_note_acknowledgements_alert ON alert_note_acknowledgements(alert_note_id); CREATE INDEX IF NOT EXISTS idx_alert_note_acknowledgements_user ON alert_note_acknowledgements(user_id); -- Trigger to update updated_at timestamp CREATE OR REPLACE FUNCTION update_alert_notes_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_update_alert_notes_updated_at BEFORE UPDATE ON alert_notes FOR EACH ROW EXECUTE FUNCTION update_alert_notes_updated_at(); -- Add alert_notes permissions INSERT INTO permissions (code, description, category) VALUES ('alert_notes.view', 'View alert notes', 'alert_notes'), ('alert_notes.create', 'Create alert notes', 'alert_notes'), ('alert_notes.edit', 'Edit alert notes', 'alert_notes'), ('alert_notes.delete', 'Delete alert notes', 'alert_notes'), ('alert_notes.manage', 'Full management of alert notes', 'alert_notes') ON CONFLICT (code) DO NOTHING; -- Assign all alert_notes permissions to Administrators group INSERT INTO group_permissions (group_id, permission_id) SELECT g.id, p.id FROM groups g CROSS JOIN permissions p WHERE g.name = 'Administrators' AND p.category = 'alert_notes' ON CONFLICT DO NOTHING; -- Assign create, edit permissions to Managers group INSERT INTO group_permissions (group_id, permission_id) SELECT g.id, p.id FROM groups g CROSS JOIN permissions p WHERE g.name = 'Managers' AND p.code IN ( 'alert_notes.view', 'alert_notes.create', 'alert_notes.edit' ) ON CONFLICT DO NOTHING; -- Assign view permission to all other groups (Technicians, Viewers) INSERT INTO group_permissions (group_id, permission_id) SELECT g.id, p.id FROM groups g CROSS JOIN permissions p WHERE g.name IN ('Technicians', 'Viewers') AND p.code = 'alert_notes.view' ON CONFLICT DO NOTHING; -- Verify migration SELECT 'Alert Notes system created successfully' AS status, (SELECT COUNT(*) FROM alert_notes) AS alert_notes_count, (SELECT COUNT(*) FROM permissions WHERE category = 'alert_notes') AS permissions_count;