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>
142 lines
6.6 KiB
SQL
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';
|