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>
176 lines
9.3 KiB
PL/PgSQL
176 lines
9.3 KiB
PL/PgSQL
-- ============================================================================
|
|
-- Migration 013: DSB-as-a-Service Portal Schema
|
|
-- Datenschutzbeauftragter (Data Protection Officer) Portal
|
|
--
|
|
-- Provides a portal for external DSBs to manage multiple client tenants,
|
|
-- track hours, manage tasks, and communicate.
|
|
--
|
|
-- Depends on: 001_rbac_schema.sql (compliance_tenants)
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- DSB Assignments: which DSB is assigned to which tenant
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS dsb_assignments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
dsb_user_id UUID NOT NULL, -- the DSB user
|
|
tenant_id UUID NOT NULL REFERENCES compliance_tenants(id),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active, paused, terminated
|
|
contract_start DATE NOT NULL,
|
|
contract_end DATE,
|
|
monthly_hours_budget DECIMAL(5,1) DEFAULT 0,
|
|
notes TEXT DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(dsb_user_id, tenant_id)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- DSB Time Tracking
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS dsb_hours (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
|
date DATE NOT NULL,
|
|
hours DECIMAL(4,1) NOT NULL,
|
|
category VARCHAR(50) NOT NULL, -- 'dsfa_review', 'consultation', 'audit', 'training', 'incident_response', 'documentation', 'meeting', 'other'
|
|
description TEXT NOT NULL,
|
|
billable BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- DSB Tasks / Queue
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS dsb_tasks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT DEFAULT '',
|
|
category VARCHAR(50) NOT NULL, -- 'dsfa_review', 'dsr_response', 'incident_review', 'audit_preparation', 'policy_review', 'training', 'consultation', 'other'
|
|
priority VARCHAR(20) NOT NULL DEFAULT 'medium', -- low, medium, high, urgent
|
|
status VARCHAR(20) NOT NULL DEFAULT 'open', -- open, in_progress, waiting, completed, cancelled
|
|
due_date DATE,
|
|
completed_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- DSB Communication Log
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS dsb_communications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
assignment_id UUID NOT NULL REFERENCES dsb_assignments(id) ON DELETE CASCADE,
|
|
direction VARCHAR(10) NOT NULL, -- 'inbound', 'outbound'
|
|
channel VARCHAR(20) NOT NULL, -- 'email', 'phone', 'meeting', 'portal', 'letter'
|
|
subject VARCHAR(255) NOT NULL,
|
|
content TEXT DEFAULT '',
|
|
participants TEXT DEFAULT '',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- Indexes: DSB Assignments
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_dsb_user_id ON dsb_assignments(dsb_user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_tenant_id ON dsb_assignments(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_assignments_status ON dsb_assignments(status);
|
|
|
|
-- ============================================================================
|
|
-- Indexes: DSB Hours
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_hours_assignment_id ON dsb_hours(assignment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_hours_date ON dsb_hours(date);
|
|
|
|
-- ============================================================================
|
|
-- Indexes: DSB Tasks
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_assignment_id ON dsb_tasks(assignment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_status ON dsb_tasks(status);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_priority ON dsb_tasks(priority);
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_tasks_due_date ON dsb_tasks(due_date);
|
|
|
|
-- ============================================================================
|
|
-- Indexes: DSB Communications
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_dsb_communications_assignment_id ON dsb_communications(assignment_id);
|
|
|
|
-- ============================================================================
|
|
-- Triggers
|
|
-- ============================================================================
|
|
|
|
-- Ensure update_updated_at_column() function exists (created in earlier migrations)
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- DSB Assignments trigger
|
|
DROP TRIGGER IF EXISTS update_dsb_assignments_updated_at ON dsb_assignments;
|
|
CREATE TRIGGER update_dsb_assignments_updated_at
|
|
BEFORE UPDATE ON dsb_assignments
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- DSB Tasks trigger
|
|
DROP TRIGGER IF EXISTS update_dsb_tasks_updated_at ON dsb_tasks;
|
|
CREATE TRIGGER update_dsb_tasks_updated_at
|
|
BEFORE UPDATE ON dsb_tasks
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- ============================================================================
|
|
-- Comments
|
|
-- ============================================================================
|
|
|
|
-- Table comments
|
|
COMMENT ON TABLE dsb_assignments IS 'DSB-as-a-Service: Maps external Data Protection Officers (DSBs) to client tenants with contract details and hour budgets';
|
|
COMMENT ON TABLE dsb_hours IS 'DSB-as-a-Service: Time tracking entries for DSB work on assigned tenants, categorized and billable';
|
|
COMMENT ON TABLE dsb_tasks IS 'DSB-as-a-Service: Task queue for DSB work items per tenant assignment with priority and status tracking';
|
|
COMMENT ON TABLE dsb_communications IS 'DSB-as-a-Service: Communication log between DSB and client tenant, tracking direction, channel, and content';
|
|
|
|
-- DSB Assignments column comments
|
|
COMMENT ON COLUMN dsb_assignments.dsb_user_id IS 'UUID of the Data Protection Officer user account';
|
|
COMMENT ON COLUMN dsb_assignments.tenant_id IS 'UUID of the client tenant this DSB is assigned to';
|
|
COMMENT ON COLUMN dsb_assignments.status IS 'Assignment status: active, paused, or terminated';
|
|
COMMENT ON COLUMN dsb_assignments.contract_start IS 'Start date of the DSB service contract';
|
|
COMMENT ON COLUMN dsb_assignments.contract_end IS 'End date of the DSB service contract (NULL for open-ended)';
|
|
COMMENT ON COLUMN dsb_assignments.monthly_hours_budget IS 'Monthly hour budget allocated for this tenant';
|
|
COMMENT ON COLUMN dsb_assignments.notes IS 'Internal notes about the assignment';
|
|
|
|
-- DSB Hours column comments
|
|
COMMENT ON COLUMN dsb_hours.assignment_id IS 'Reference to the DSB assignment this time entry belongs to';
|
|
COMMENT ON COLUMN dsb_hours.date IS 'Date the work was performed';
|
|
COMMENT ON COLUMN dsb_hours.hours IS 'Number of hours worked (e.g. 1.5)';
|
|
COMMENT ON COLUMN dsb_hours.category IS 'Work category: dsfa_review, consultation, audit, training, incident_response, documentation, meeting, other';
|
|
COMMENT ON COLUMN dsb_hours.description IS 'Description of work performed';
|
|
COMMENT ON COLUMN dsb_hours.billable IS 'Whether this time entry is billable to the client';
|
|
|
|
-- DSB Tasks column comments
|
|
COMMENT ON COLUMN dsb_tasks.assignment_id IS 'Reference to the DSB assignment this task belongs to';
|
|
COMMENT ON COLUMN dsb_tasks.title IS 'Short title describing the task';
|
|
COMMENT ON COLUMN dsb_tasks.description IS 'Detailed task description';
|
|
COMMENT ON COLUMN dsb_tasks.category IS 'Task category: dsfa_review, dsr_response, incident_review, audit_preparation, policy_review, training, consultation, other';
|
|
COMMENT ON COLUMN dsb_tasks.priority IS 'Task priority: low, medium, high, urgent';
|
|
COMMENT ON COLUMN dsb_tasks.status IS 'Task status: open, in_progress, waiting, completed, cancelled';
|
|
COMMENT ON COLUMN dsb_tasks.due_date IS 'Due date for the task (NULL if no deadline)';
|
|
COMMENT ON COLUMN dsb_tasks.completed_at IS 'Timestamp when the task was completed';
|
|
|
|
-- DSB Communications column comments
|
|
COMMENT ON COLUMN dsb_communications.assignment_id IS 'Reference to the DSB assignment this communication belongs to';
|
|
COMMENT ON COLUMN dsb_communications.direction IS 'Communication direction: inbound (from client) or outbound (from DSB)';
|
|
COMMENT ON COLUMN dsb_communications.channel IS 'Communication channel: email, phone, meeting, portal, letter';
|
|
COMMENT ON COLUMN dsb_communications.subject IS 'Subject line or topic of the communication';
|
|
COMMENT ON COLUMN dsb_communications.content IS 'Full content or summary of the communication';
|
|
COMMENT ON COLUMN dsb_communications.participants IS 'Comma-separated list of participants';
|