-- ========================================================================== -- 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 -- ==========================================================================