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>
This commit is contained in:
141
ai-compliance-sdk/migrations/009_whistleblower_schema.sql
Normal file
141
ai-compliance-sdk/migrations/009_whistleblower_schema.sql
Normal file
@@ -0,0 +1,141 @@
|
||||
-- ============================================================================
|
||||
-- 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';
|
||||
Reference in New Issue
Block a user