Files
agenciapsilmno/database-novo/migrations/20260329000002_create_document_templates.sql

261 lines
11 KiB
SQL

-- ==========================================================================
-- Agencia PSI — Migracao: tabelas de Templates de Documentos
-- ==========================================================================
-- Criado por: Leonardo Nohama
-- Data: 2026-03-29 · Sao Carlos/SP — Brasil
--
-- Proposito:
-- Templates de documentos (declaracao, atestado, recibo, relatorio etc.)
-- e registro de cada documento gerado (instancia PDF).
--
-- Tabelas: document_templates, document_generated.
--
-- Relacionamentos:
-- document_templates.tenant_id → tenants(id)
-- document_templates.owner_id → auth.users(id)
-- document_generated.template_id → document_templates(id)
-- document_generated.patient_id → patients(id)
-- document_generated.tenant_id → tenants(id)
--
-- Templates globais: is_global = true, tenant_id = NULL.
-- Templates do tenant: is_global = false, tenant_id preenchido.
-- ==========================================================================
-- --------------------------------------------------------------------------
-- 1. Tabela: document_templates
-- --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.document_templates (
id uuid DEFAULT gen_random_uuid() NOT NULL,
-- Contexto
tenant_id uuid,
owner_id uuid,
-- Identificacao
nome_template text NOT NULL,
tipo text NOT NULL DEFAULT 'outro',
-- declaracao_comparecimento | atestado_psicologico
-- relatorio_acompanhamento | recibo_pagamento
-- termo_consentimento | encaminhamento | outro
descricao text,
-- Corpo do template
corpo_html text NOT NULL DEFAULT '',
cabecalho_html text,
rodape_html text,
-- Variaveis que o template utiliza
variaveis text[] DEFAULT '{}',
-- Ex: {paciente_nome, paciente_cpf, data_sessao, terapeuta_nome, ...}
-- Personalizacao visual
logo_url text,
-- Escopo
is_global boolean DEFAULT false NOT NULL,
-- true = template padrao do sistema (visivel para todos)
-- false = template criado pelo tenant/terapeuta
-- Controle
ativo boolean DEFAULT true NOT NULL,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
CONSTRAINT document_templates_pkey PRIMARY KEY (id),
CONSTRAINT dt_tipo_check CHECK (
tipo = ANY (ARRAY[
'declaracao_comparecimento', 'atestado_psicologico',
'relatorio_acompanhamento', 'recibo_pagamento',
'termo_consentimento', 'encaminhamento',
'contrato_servicos', 'tcle', 'autorizacao_menor',
'laudo_psicologico', 'parecer_psicologico',
'termo_sigilo', 'declaracao_inicio_tratamento',
'termo_alta', 'tcle_online', 'outro'
])
)
);
-- --------------------------------------------------------------------------
-- 2. Indices — document_templates
-- --------------------------------------------------------------------------
CREATE INDEX IF NOT EXISTS dt_tenant_idx
ON public.document_templates USING btree (tenant_id);
CREATE INDEX IF NOT EXISTS dt_owner_idx
ON public.document_templates USING btree (owner_id);
CREATE INDEX IF NOT EXISTS dt_global_idx
ON public.document_templates USING btree (is_global)
WHERE is_global = true;
CREATE INDEX IF NOT EXISTS dt_tipo_idx
ON public.document_templates USING btree (tipo);
CREATE INDEX IF NOT EXISTS dt_nome_trgm_idx
ON public.document_templates USING gin (nome_template gin_trgm_ops);
-- --------------------------------------------------------------------------
-- 3. Trigger updated_at
-- --------------------------------------------------------------------------
CREATE TRIGGER trg_dt_updated_at
BEFORE UPDATE ON public.document_templates
FOR EACH ROW
EXECUTE FUNCTION public.set_updated_at();
-- --------------------------------------------------------------------------
-- 4. RLS — document_templates
-- --------------------------------------------------------------------------
ALTER TABLE public.document_templates ENABLE ROW LEVEL SECURITY;
-- Templates globais: todos podem ler
CREATE POLICY "dt: global templates readable by all"
ON public.document_templates
FOR SELECT
USING (is_global = true);
-- Templates do tenant: membros do tenant podem ler
CREATE POLICY "dt: tenant members can select"
ON public.document_templates
FOR SELECT
USING (
is_global = false
AND tenant_id IN (
SELECT tm.tenant_id FROM public.tenant_members tm
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
)
);
-- Owner pode inserir/atualizar/deletar seus templates
CREATE POLICY "dt: owner can insert"
ON public.document_templates
FOR INSERT
WITH CHECK (owner_id = auth.uid() AND is_global = false);
CREATE POLICY "dt: owner can update"
ON public.document_templates
FOR UPDATE
USING (owner_id = auth.uid() AND is_global = false)
WITH CHECK (owner_id = auth.uid() AND is_global = false);
CREATE POLICY "dt: owner can delete"
ON public.document_templates
FOR DELETE
USING (owner_id = auth.uid() AND is_global = false);
-- SaaS admin pode gerenciar templates globais (usa funcao public.is_saas_admin())
CREATE POLICY "dt: saas admin can insert global"
ON public.document_templates
FOR INSERT
WITH CHECK (is_global = true AND public.is_saas_admin());
CREATE POLICY "dt: saas admin can update global"
ON public.document_templates
FOR UPDATE
USING (is_global = true AND public.is_saas_admin())
WITH CHECK (is_global = true AND public.is_saas_admin());
CREATE POLICY "dt: saas admin can delete global"
ON public.document_templates
FOR DELETE
USING (is_global = true AND public.is_saas_admin());
-- --------------------------------------------------------------------------
-- 5. Comentarios — document_templates
-- --------------------------------------------------------------------------
COMMENT ON TABLE public.document_templates IS 'Templates de documentos para geracao automatica (declaracao, atestado, recibo etc.).';
COMMENT ON COLUMN public.document_templates.nome_template IS 'Nome do template. Ex: Declaracao de Comparecimento.';
COMMENT ON COLUMN public.document_templates.tipo IS 'declaracao_comparecimento|atestado_psicologico|relatorio_acompanhamento|recibo_pagamento|termo_consentimento|encaminhamento|outro.';
COMMENT ON COLUMN public.document_templates.corpo_html IS 'Corpo do template em HTML com variaveis {{nome_variavel}}.';
COMMENT ON COLUMN public.document_templates.cabecalho_html IS 'HTML do cabecalho (logo, nome da clinica etc.).';
COMMENT ON COLUMN public.document_templates.rodape_html IS 'HTML do rodape (CRP, endereco, contato etc.).';
COMMENT ON COLUMN public.document_templates.variaveis IS 'Array com nomes das variaveis usadas no template. Ex: {paciente_nome, data_sessao}.';
COMMENT ON COLUMN public.document_templates.is_global IS 'true = template padrao do sistema visivel para todos. false = template do tenant.';
COMMENT ON COLUMN public.document_templates.logo_url IS 'URL do logo personalizado para o cabecalho do documento.';
-- ==========================================================================
-- 6. Tabela: document_generated (cada PDF gerado)
-- ==========================================================================
CREATE TABLE IF NOT EXISTS public.document_generated (
id uuid DEFAULT gen_random_uuid() NOT NULL,
-- Origem
template_id uuid NOT NULL REFERENCES public.document_templates(id) ON DELETE RESTRICT,
patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE,
tenant_id uuid NOT NULL,
-- Dados usados no preenchimento (snapshot — permite auditoria futura)
dados_preenchidos jsonb NOT NULL DEFAULT '{}',
-- PDF gerado
pdf_path text NOT NULL,
storage_bucket text NOT NULL DEFAULT 'generated-docs',
-- Vinculo opcional com documento pai (se o PDF gerado tambem for registrado em documents)
documento_id uuid REFERENCES public.documents(id) ON DELETE SET NULL,
-- Quem gerou
gerado_por uuid NOT NULL,
gerado_em timestamptz DEFAULT now() NOT NULL,
CONSTRAINT document_generated_pkey PRIMARY KEY (id)
);
-- --------------------------------------------------------------------------
-- 7. Indices — document_generated
-- --------------------------------------------------------------------------
CREATE INDEX IF NOT EXISTS dg_template_idx
ON public.document_generated USING btree (template_id);
CREATE INDEX IF NOT EXISTS dg_patient_idx
ON public.document_generated USING btree (patient_id, gerado_em DESC);
CREATE INDEX IF NOT EXISTS dg_tenant_idx
ON public.document_generated USING btree (tenant_id, gerado_em DESC);
CREATE INDEX IF NOT EXISTS dg_gerado_por_idx
ON public.document_generated USING btree (gerado_por, gerado_em DESC);
-- --------------------------------------------------------------------------
-- 8. RLS — document_generated
-- --------------------------------------------------------------------------
ALTER TABLE public.document_generated ENABLE ROW LEVEL SECURITY;
CREATE POLICY "dg: generator full access"
ON public.document_generated
USING (gerado_por = auth.uid())
WITH CHECK (gerado_por = auth.uid());
-- Membros do tenant podem visualizar
CREATE POLICY "dg: tenant members can select"
ON public.document_generated
FOR SELECT
USING (tenant_id IN (
SELECT tm.tenant_id FROM public.tenant_members tm
WHERE tm.user_id = auth.uid() AND tm.status = 'active'
));
-- --------------------------------------------------------------------------
-- 9. Comentarios — document_generated
-- --------------------------------------------------------------------------
COMMENT ON TABLE public.document_generated IS 'Registro de cada documento PDF gerado a partir de um template.';
COMMENT ON COLUMN public.document_generated.template_id IS 'Template usado para gerar o documento.';
COMMENT ON COLUMN public.document_generated.dados_preenchidos IS 'Snapshot JSON dos dados usados no preenchimento. Permite auditoria futura.';
COMMENT ON COLUMN public.document_generated.pdf_path IS 'Caminho do PDF gerado no Supabase Storage bucket.';
COMMENT ON COLUMN public.document_generated.documento_id IS 'FK opcional para documents — se o PDF gerado tambem foi registrado como documento do paciente.';
COMMENT ON COLUMN public.document_generated.gerado_por IS 'Usuario que gerou o documento (auth.uid()).';
-- ==========================================================================
-- FIM DA MIGRACAO 006
-- ==========================================================================