Files
breakpilot-compliance/ai-compliance-sdk/migrations/009_whistleblower_schema.sql
Benjamin Boenisch 504dd3591b feat: Add Academy, Whistleblower, Incidents, Vendor, DSB, SSO, Reporting, Multi-Tenant and Industry backends
Go handlers, models, stores and migrations for all SDK modules.
Updates developer portal navigation and BYOEH page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 21:11:27 +01:00

142 lines
6.6 KiB
SQL

-- ============================================================================
-- Migration 009: Whistleblower / Hinweisgebersystem (HinSchG)
-- Implements the German Whistleblower Protection Act (Hinweisgeberschutzgesetz)
-- ============================================================================
-- Whistleblower reports table
CREATE TABLE IF NOT EXISTS whistleblower_reports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
-- Identification
reference_number VARCHAR(20) NOT NULL UNIQUE, -- e.g. "WB-2026-0001"
access_key VARCHAR(50) NOT NULL UNIQUE, -- for anonymous reporter access
-- Report content
category VARCHAR(50) NOT NULL, -- corruption, fraud, data_protection, discrimination, environment, competition, product_safety, tax_evasion, other
status VARCHAR(50) NOT NULL DEFAULT 'new', -- new, acknowledged, under_review, investigation, measures_taken, closed, rejected
title VARCHAR(500) NOT NULL,
description TEXT NOT NULL,
-- Reporter info (nullable for anonymous reports)
is_anonymous BOOLEAN NOT NULL DEFAULT TRUE,
reporter_name VARCHAR(255),
reporter_email VARCHAR(255),
reporter_phone VARCHAR(100),
-- HinSchG deadlines
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deadline_acknowledgment TIMESTAMPTZ NOT NULL, -- 7 days from received_at per HinSchG
deadline_feedback TIMESTAMPTZ NOT NULL, -- 3 months from received_at per HinSchG
-- Status timestamps
acknowledged_at TIMESTAMPTZ,
closed_at TIMESTAMPTZ,
-- Assignment
assigned_to UUID, -- user responsible for handling
-- Audit trail (JSONB array of {timestamp, action, user_id, details})
audit_trail JSONB NOT NULL DEFAULT '[]',
-- Resolution
resolution TEXT,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Whistleblower messages table (anonymous communication channel)
CREATE TABLE IF NOT EXISTS whistleblower_messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
report_id UUID NOT NULL REFERENCES whistleblower_reports(id) ON DELETE CASCADE,
-- Message
direction VARCHAR(30) NOT NULL, -- reporter_to_admin, admin_to_reporter
content TEXT NOT NULL,
sent_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
read_at TIMESTAMPTZ
);
-- Whistleblower measures table (corrective measures)
CREATE TABLE IF NOT EXISTS whistleblower_measures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
report_id UUID NOT NULL REFERENCES whistleblower_reports(id) ON DELETE CASCADE,
-- Measure details
title VARCHAR(500) NOT NULL,
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'planned', -- planned, in_progress, completed
responsible VARCHAR(255),
due_date TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Sequence table for reference number generation
CREATE TABLE IF NOT EXISTS whistleblower_sequences (
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id) ON DELETE CASCADE,
year INT NOT NULL,
last_sequence INT NOT NULL DEFAULT 0,
PRIMARY KEY (tenant_id, year)
);
-- ============================================================================
-- Indexes
-- ============================================================================
-- Report indexes
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_tenant ON whistleblower_reports(tenant_id);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_access_key ON whistleblower_reports(access_key);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_reference ON whistleblower_reports(reference_number);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_status ON whistleblower_reports(tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_category ON whistleblower_reports(tenant_id, category);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_received ON whistleblower_reports(tenant_id, received_at);
CREATE INDEX IF NOT EXISTS idx_whistleblower_reports_deadlines ON whistleblower_reports(deadline_acknowledgment, deadline_feedback)
WHERE acknowledged_at IS NULL OR closed_at IS NULL;
-- Message indexes
CREATE INDEX IF NOT EXISTS idx_whistleblower_messages_report ON whistleblower_messages(report_id);
CREATE INDEX IF NOT EXISTS idx_whistleblower_messages_sent ON whistleblower_messages(report_id, sent_at);
-- Measure indexes
CREATE INDEX IF NOT EXISTS idx_whistleblower_measures_report ON whistleblower_measures(report_id);
CREATE INDEX IF NOT EXISTS idx_whistleblower_measures_status ON whistleblower_measures(report_id, status);
-- ============================================================================
-- Triggers
-- ============================================================================
-- Reuse existing update_updated_at_column function
-- Reports trigger
DROP TRIGGER IF EXISTS update_whistleblower_reports_updated_at ON whistleblower_reports;
CREATE TRIGGER update_whistleblower_reports_updated_at
BEFORE UPDATE ON whistleblower_reports
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Measures trigger
DROP TRIGGER IF EXISTS update_whistleblower_measures_updated_at ON whistleblower_measures;
CREATE TRIGGER update_whistleblower_measures_updated_at
BEFORE UPDATE ON whistleblower_measures
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- ============================================================================
-- Comments
-- ============================================================================
COMMENT ON TABLE whistleblower_reports IS 'Whistleblower reports per HinSchG (Hinweisgeberschutzgesetz)';
COMMENT ON TABLE whistleblower_messages IS 'Anonymous communication channel between reporter and admin';
COMMENT ON TABLE whistleblower_measures IS 'Corrective measures taken for whistleblower reports';
COMMENT ON TABLE whistleblower_sequences IS 'Sequence numbers for reference number generation per tenant and year';
COMMENT ON COLUMN whistleblower_reports.reference_number IS 'Human-readable reference number (e.g. WB-2026-0001)';
COMMENT ON COLUMN whistleblower_reports.access_key IS 'Secret key for anonymous reporter to access their report';
COMMENT ON COLUMN whistleblower_reports.deadline_acknowledgment IS '7-day deadline per HinSchG §17 Abs. 1';
COMMENT ON COLUMN whistleblower_reports.deadline_feedback IS '3-month deadline per HinSchG §17 Abs. 2';
COMMENT ON COLUMN whistleblower_reports.audit_trail IS 'JSON array of audit entries tracking all actions on the report';