-- Importacao da doc Assinatura eletronica de documentos (Fase 3 #7) -- Gerado a partir de development/saas-docs/05-assinatura-eletronica-melissa.json BEGIN; DO $IMPORT$ DECLARE v_doc_id uuid; BEGIN INSERT INTO public.saas_docs ( titulo, conteudo, categoria, exibir_no_faq, tipo_acesso, pagina_path, ordem, ativo, medias ) VALUES ( 'Assinatura eletrônica de documentos', $HTML$

Assinatura eletrônica de documentos

O sistema permite enviar documentos clínicos (TCLE, contratos, autorizações, laudos) pro paciente assinar sem que ele precise ter login. O fluxo registra a assinatura com hash do conteúdo, IP, user-agent e timestamp — gerando um audit trail compliance LGPD/CFP.

1. Visão geral do fluxo

  1. Terapeuta abre o documento no prontuário e clica em Assinar
  2. Adiciona os signatários (nome + email) e ativa "Gerar link público para assinatura"
  3. Sistema cria signature requests + um link público temporário com token
  4. Terapeuta copia a URL e envia pro paciente (WhatsApp, email, SMS — manual por enquanto)
  5. Paciente abre o link em qualquer navegador, lê o documento, marca o checkbox de aceite LGPD e clica Assinar
  6. Sistema computa SHA-256 do PDF baixado, registra assinatura via RPC server-side (IP/UA capturados pelo banco)
  7. Terapeuta vê o status atualizado no documento (pendente → assinado)

2. Lado terapeuta — criar solicitação

No preview de um documento (na aba Documentos do prontuário), clique no botão Assinar na sidebar de ações. O DocumentSignatureDialog abre com:

3. Lado paciente — link público (sem login)

Ao abrir o link /shared/document/:token, o paciente vê:

Ao clicar Assinar:

  1. Sistema baixa o PDF e computa SHA-256 client-side (proof of integrity — se o doc foi alterado depois, hash não bate)
  2. Chama a RPC sign_document_by_token passando o hash
  3. RPC captura IP via inet_client_addr() e user-agent via current_setting('request.headers') — server-side, à prova de spoof client-side
  4. Registra em document_signatures: timestamp, hash, IP, UA, status='assinado'
  5. Mostra tela de confirmação "Documento assinado com sucesso"

4. Audit trail registrado

Cada assinatura grava:

🛡️ Por que server-side? Capturar IP/UA no banco via inet_client_addr() é anti-spoof: o cliente não consegue forjar valores arbitrários. Garante que o audit trail reflete a sessão HTTP real, não um POST manipulado.

5. Recusar a assinatura

O paciente pode recusar em vez de assinar — útil se ele não concorda com o conteúdo. Click em Recusar abre um confirm; ao confirmar, o sistema registra a recusa (com timestamp + IP/UA da mesma forma) e marca a request como status='recusado'. O terapeuta vê isso na lista de signature requests e pode entrar em contato pra ajustar o documento.

6. Portal do paciente — lista de pendências

Pacientes logados no portal (/portal/documentos) veem uma lista de TODOS os documentos solicitados pra eles, com KPIs no topo:

Filtro por status (todos / pendentes / assinados) + lista. Click em Assinar agora num item pendente leva pro /shared/document/:token (mesma página pública, mas com auth já garantida via portal).

7. Expiração e múltiplos usos

8. Múltiplos signatários

Documentos como termo de autorização de menor podem precisar de 2+ assinaturas (responsável legal + paciente menor, ou os dois pais). O dialog aceita N signatários; cada um recebe sua própria entry em document_signatures. O link público é o mesmo — quando o paciente abre, escolhe qual signatário ele é no select e assina apenas a sua entry.

9. Validade legal

A assinatura eletrônica registrada pelo sistema atende:

⚠️ Pra documentos que exigem ICP-Brasil (notarial, procuração com poderes especiais), use uma plataforma externa de assinatura qualificada — esse fluxo não substitui.

⚠️ Notas pro desenvolvedor

$HTML$, 'Documentos', true, 'usuario', '/melissa/paciente', 5, true, '[{"tipo": "imagem", "url": ""}]'::jsonb ) RETURNING id INTO v_doc_id; INSERT INTO public.saas_faq_itens (doc_id, pergunta, resposta, ordem, ativo) VALUES (v_doc_id, 'Como peço pra um paciente assinar um documento (TCLE, contrato, autorização)?', $FAQ$Na aba Documentos do prontuário, clique no doc → no preview, clique em Assinar (sidebar de ações). O dialog abre. Adicione o paciente como signatário (nome + email), mantenha "Gerar link público para assinatura" marcado, escolha validade (7 dias é o default) e clique em Solicitar. O sistema cria a request e mostra uma URL pra copiar. Envie pro paciente via WhatsApp, email, SMS — como preferir.$FAQ$, 0, true), (v_doc_id, 'Como o paciente assina sem ter login no sistema?', $FAQ$Ele abre o link público (/shared/document/:token) em qualquer navegador. Vê o PDF inline, lê o aviso LGPD/CFP, marca o checkbox "Li o documento e concordo", e clica Assinar. O sistema computa hash SHA-256 do PDF, chama a RPC server-side que captura IP/User-Agent e registra a assinatura. Nada de cadastro, nada de senha.$FAQ$, 1, true), (v_doc_id, 'Que informação fica registrada quando ele assina?', $FAQ$Tudo que precisa pra audit compliance: nome e email do signatário (do cadastro), timestamp server-side (não do relógio do cliente), hash SHA-256 do PDF no momento da assinatura (qualquer alteração posterior invalida a integridade), IP e User-Agent capturados pelo banco via inet_client_addr() e current_setting('request.headers') — anti-spoof. Tudo fica em public.document_signatures.$FAQ$, 2, true), (v_doc_id, 'O terapeuta também precisa assinar o documento?', $FAQ$Depende do tipo. Pra atestados, laudos e declarações, geralmente sim — você gera o PDF a partir do template (que já contém seu nome + registro profissional + assinatura digitalizada se você incluiu no rodapé). Pra contratos e termos com paciente como contraparte, você adiciona você mesmo como segundo signatário no dialog antes de enviar. Cada um abre o link e assina sua entry separadamente.$FAQ$, 3, true), (v_doc_id, 'O link tem validade? E se expirar?', $FAQ$Tem. Você escolhe 24h, 3 dias, 7 dias ou 30 dias na hora de criar (default 7d). Depois disso o link retorna erro 410 Gone. Se o paciente não assinou a tempo, gere um novo link: no preview do doc, clique em Compartilhar ou abra o dialog de assinatura novamente — vai criar outro token. Limite de usos do link: ~5 (margem pra reload/multi-device), depois também expira.$FAQ$, 4, true), (v_doc_id, 'E se o paciente recusar a assinatura?', $FAQ$Tem botão Recusar ao lado do Assinar. Clique pede confirmação; ao confirmar, a request fica com status='recusado' com timestamp e IP/UA registrados igual à assinatura. Você vê o status na lista de pendências do doc e na aba do prontuário. Geralmente: ajuste o conteúdo do documento e envie nova solicitação.$FAQ$, 5, true), (v_doc_id, 'O paciente pode assinar depois pelo Portal sem precisar do link?', $FAQ$Sim, se ele tem conta de portal. Em /portal/documentos aparece a lista de tudo que está pendente pra ele assinar, com KPIs (total, pendentes, assinados, recusados) e botão Assinar agora que leva pra mesma página de assinatura. Útil pra pacientes que perderam o link no WhatsApp — eles loga e acha tudo num lugar só.$FAQ$, 6, true), (v_doc_id, 'Como compartilho o link com o paciente — tem envio automático?', $FAQ$Hoje o envio é manual: o dialog gera a URL, você copia e cola onde quiser (WhatsApp, email, SMS, AirDrop, QR code, link em conversa direta). Envio automático (notificação por WA/email quando signature é criada) está no roadmap, depende do Módulo 6 (notifications factory channel) que ainda não foi implementado.$FAQ$, 7, true), (v_doc_id, 'A assinatura tem validade legal mesmo sem certificado ICP-Brasil?', $FAQ$Pra documentos clínicos comuns (TCLE, contrato de prestação, autorizações, declarações entre terapeuta-paciente), sim. A assinatura simples com timestamp + hash + IP + UA atende LGPD (Art. 7º I — consentimento explícito) e o Código de Ética do CFP. Pra documentos que exigem certificado ICP-Brasil (notarial, procuração com poderes especiais), use uma plataforma externa de assinatura qualificada — esse fluxo não substitui.$FAQ$, 8, true), (v_doc_id, 'O paciente consegue editar o documento antes de assinar?', $FAQ$Não. O paciente só visualiza — o PDF é renderizado em iframe e a integridade é garantida pelo hash SHA-256 computado no momento da assinatura. Se o conteúdo precisar mudar, é você que ajusta o documento (editar via template ou regenerar) e envia nova solicitação. A assinatura antiga (se houve) fica registrada com o hash do conteúdo antigo — o doc atual tem hash diferente, mostrando que mudou.$FAQ$, 9, true), (v_doc_id, 'Como cancelo uma solicitação de assinatura?', $FAQ$Hoje não há um botão "cancelar" direto na UI. O caminho é: ignore (deixa expirar pelo prazo do link) ou peça pro admin marcar como status='expirado' no banco. Em versões futuras teremos botão de cancelar na lista de pendências do doc.$FAQ$, 10, true), (v_doc_id, 'Posso pedir mais de uma pessoa pra assinar o mesmo documento?', $FAQ$Sim. Pra termos com múltiplos signatários (autorização de atendimento de menor com 2 pais, contrato com responsável legal + paciente), adicione cada um como signatário separado no dialog. Cada um vira uma entry em document_signatures. O link público é o mesmo — quando o signatário abre, escolhe quem ele é no select acima dos botões e assina apenas a entry dele. Útil também pra você incluir si mesmo (terapeuta) + paciente num contrato bilateral.$FAQ$, 11, true); RAISE NOTICE 'Doc criada: id=%, faq_itens=12', v_doc_id; END; $IMPORT$; COMMIT;