-- Template migration for each organization schema. -- Replace {schema} with the real schema name, e.g. company_. create table if not exists {schema}.communications ( id uuid primary key, communication_type text not null, direction text not null, subject_ciphertext bytea not null, subject_nonce bytea not null, subject_key_id text not null, body_ciphertext bytea, body_nonce bytea, body_key_id text, status text not null default 'open', occurred_at timestamptz, created_by_user_id uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint communications_type_valid check ( communication_type in ('email', 'phone', 'letter', 'meeting', 'internal_note') ), constraint communications_direction_valid check ( direction in ('inbound', 'outbound', 'internal') ), constraint communications_status_valid check ( status in ('open', 'done', 'archived') ), constraint communications_body_encryption_complete check ( ( body_ciphertext is null and body_nonce is null and body_key_id is null ) or ( body_ciphertext is not null and body_nonce is not null and body_key_id is not null ) ) ); create index if not exists idx_communications_type_status on {schema}.communications (communication_type, status, occurred_at desc); create table if not exists {schema}.communication_links ( communication_id uuid not null references {schema}.communications(id) on delete cascade, entity_type text not null, entity_id uuid not null, created_at timestamptz not null default now(), primary key (communication_id, entity_type, entity_id), constraint communication_links_entity_type_valid check ( entity_type in ( 'customer', 'supplier', 'activity', 'quote', 'outgoing_invoice', 'incoming_invoice', 'item', 'document' ) ) ); create index if not exists idx_communication_links_entity on {schema}.communication_links (entity_type, entity_id); create table if not exists {schema}.documents ( id uuid primary key, title_ciphertext bytea not null, title_nonce bytea not null, title_key_id text not null, description_ciphertext bytea, description_nonce bytea, description_key_id text, status text not null default 'active', created_by_user_id uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint documents_status_valid check (status in ('active', 'archived', 'deleted')), constraint documents_description_encryption_complete check ( ( description_ciphertext is null and description_nonce is null and description_key_id is null ) or ( description_ciphertext is not null and description_nonce is not null and description_key_id is not null ) ) ); create table if not exists {schema}.document_versions ( id uuid primary key, document_id uuid not null references {schema}.documents(id) on delete cascade, version_no integer not null, file_name_ciphertext bytea not null, file_name_nonce bytea not null, file_name_key_id text not null, content_type_ciphertext bytea not null, content_type_nonce bytea not null, content_type_key_id text not null, file_size bigint not null, storage_path text not null, checksum_sha256 text not null, uploaded_by_user_id uuid, created_at timestamptz not null default now(), unique (document_id, version_no), constraint document_versions_file_size_valid check (file_size >= 0) ); create index if not exists idx_document_versions_document_version on {schema}.document_versions (document_id, version_no desc); create table if not exists {schema}.document_links ( document_id uuid not null references {schema}.documents(id) on delete cascade, entity_type text not null, entity_id uuid not null, created_at timestamptz not null default now(), primary key (document_id, entity_type, entity_id), constraint document_links_entity_type_valid check ( entity_type in ( 'customer', 'supplier', 'activity', 'communication', 'quote', 'outgoing_invoice', 'incoming_invoice', 'item' ) ) ); create index if not exists idx_document_links_entity on {schema}.document_links (entity_type, entity_id); create table if not exists {schema}.document_audit_log ( id uuid primary key, document_id uuid not null references {schema}.documents(id) on delete cascade, version_id uuid references {schema}.document_versions(id) on delete set null, action text not null, user_id uuid, created_at timestamptz not null default now(), constraint document_audit_log_action_valid check ( action in ('upload', 'download', 'archive') ) ); create index if not exists idx_document_audit_log_document on {schema}.document_audit_log (document_id, created_at desc);