From 3a42b0696d1d2ae80c2c2bafcc184139e76e4973 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Fri, 22 May 2026 19:26:32 -0300 Subject: [PATCH] saas-docs: doc da Assinatura eletronica de documentos (Fase 3 #7) Doc 05 cobrindo o fluxo end-to-end de assinatura eletronica: - Visao geral (terapeuta cria solicitacao -> link publico -> paciente abre sem login -> aceite LGPD -> assinatura registrada server-side) - Lado terapeuta: DocumentSignatureDialog (signatarios, toggle link publico, validade 24h/3d/7d/30d, URL copyavel) - Lado paciente publico: SharedDocumentPage com /shared/document/:token (preview + painel LGPD + checkbox aceite + assinar/recusar + SHA-256 computado client-side) - Audit trail: hash + timestamp server + IP/UA via inet_client_addr() e current_setting (anti-spoof) - Portal logado: PortalDocumentos lista pendencias com KPIs + filtro - Expiracao de link, multiplos signatarios, validade legal LGPD/CFP/ MP2200-2 (limite ICP-Brasil) - Notas dev: RPCs, service, composable, components, pendencia notificacao automatica via Modulo 6 12 FAQs cobrindo: como pedir assinatura, paciente sem login, audit registrado, terapeuta assinar tambem, validade do link, recusar, portal do paciente, envio manual hoje, validade legal, integridade do conteudo, cancelar solicitacao, multiplos signatarios. categoria='Documentos', pagina_path='/melissa/paciente', ordem=5. SQL import em database-novo/tmp/import-doc-assinatura-eletronica.sql. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../tmp/import-doc-assinatura-eletronica.sql | 166 ++++++++++++++++++ .../05-assinatura-eletronica-melissa.json | 87 +++++++++ 2 files changed, 253 insertions(+) create mode 100644 database-novo/tmp/import-doc-assinatura-eletronica.sql create mode 100644 development/saas-docs/05-assinatura-eletronica-melissa.json diff --git a/database-novo/tmp/import-doc-assinatura-eletronica.sql b/database-novo/tmp/import-doc-assinatura-eletronica.sql new file mode 100644 index 0000000..be89201 --- /dev/null +++ b/database-novo/tmp/import-doc-assinatura-eletronica.sql @@ -0,0 +1,166 @@ +-- 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. +
  3. Adiciona os signatários (nome + email) e ativa "Gerar link público para assinatura"
  4. +
  5. Sistema cria signature requests + um link público temporário com token
  6. +
  7. Terapeuta copia a URL e envia pro paciente (WhatsApp, email, SMS — manual por enquanto)
  8. +
  9. Paciente abre o link em qualquer navegador, lê o documento, marca o checkbox de aceite LGPD e clica Assinar
  10. +
  11. Sistema computa SHA-256 do PDF baixado, registra assinatura via RPC server-side (IP/UA capturados pelo banco)
  12. +
  13. Terapeuta vê o status atualizado no documento (pendente → assinado)
  14. +
+ +

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. +
  3. Chama a RPC sign_document_by_token passando o hash
  4. +
  5. RPC captura IP via inet_client_addr() e user-agent via current_setting('request.headers') — server-side, à prova de spoof client-side
  6. +
  7. Registra em document_signatures: timestamp, hash, IP, UA, status='assinado'
  8. +
  9. Mostra tela de confirmação "Documento assinado com sucesso"
  10. +
+ +

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; diff --git a/development/saas-docs/05-assinatura-eletronica-melissa.json b/development/saas-docs/05-assinatura-eletronica-melissa.json new file mode 100644 index 0000000..920e51e --- /dev/null +++ b/development/saas-docs/05-assinatura-eletronica-melissa.json @@ -0,0 +1,87 @@ +{ + "titulo": "Assinatura eletrônica de documentos", + "conteudo": "

Assinatura eletrônica de documentos

\n\n

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.

\n\n

1. Visão geral do fluxo

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

2. Lado terapeuta — criar solicitação

\n

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:

\n\n\n

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

\n

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

\n\n\n

Ao clicar Assinar:

\n
    \n
  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. \n
  3. Chama a RPC sign_document_by_token passando o hash
  4. \n
  5. RPC captura IP via inet_client_addr() e user-agent via current_setting('request.headers') — server-side, à prova de spoof client-side
  6. \n
  7. Registra em document_signatures: timestamp, hash, IP, UA, status='assinado'
  8. \n
  9. Mostra tela de confirmação \"Documento assinado com sucesso\"
  10. \n
\n\n

4. Audit trail registrado

\n

Cada assinatura grava:

\n\n\n
\n 🛡️ 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.\n
\n\n

5. Recusar a assinatura

\n

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.

\n\n

6. Portal do paciente — lista de pendências

\n

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

\n\n

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).

\n\n

7. Expiração e múltiplos usos

\n\n\n

8. Múltiplos signatários

\n

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.

\n\n

9. Validade legal

\n

A assinatura eletrônica registrada pelo sistema atende:

\n\n

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

\n\n

⚠️ Notas pro desenvolvedor

\n", + "categoria": "Documentos", + "exibir_no_faq": true, + "tipo_acesso": "usuario", + "pagina_path": "/melissa/paciente", + "ordem": 5, + "ativo": true, + "medias": [ + { "tipo": "imagem", "url": "" } + ], + "_faq_itens": [ + { + "pergunta": "Como peço pra um paciente assinar um documento (TCLE, contrato, autorização)?", + "resposta": "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.", + "ordem": 0, + "ativo": true + }, + { + "pergunta": "Como o paciente assina sem ter login no sistema?", + "resposta": "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.", + "ordem": 1, + "ativo": true + }, + { + "pergunta": "Que informação fica registrada quando ele assina?", + "resposta": "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.", + "ordem": 2, + "ativo": true + }, + { + "pergunta": "O terapeuta também precisa assinar o documento?", + "resposta": "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.", + "ordem": 3, + "ativo": true + }, + { + "pergunta": "O link tem validade? E se expirar?", + "resposta": "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.", + "ordem": 4, + "ativo": true + }, + { + "pergunta": "E se o paciente recusar a assinatura?", + "resposta": "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.", + "ordem": 5, + "ativo": true + }, + { + "pergunta": "O paciente pode assinar depois pelo Portal sem precisar do link?", + "resposta": "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ó.", + "ordem": 6, + "ativo": true + }, + { + "pergunta": "Como compartilho o link com o paciente — tem envio automático?", + "resposta": "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.", + "ordem": 7, + "ativo": true + }, + { + "pergunta": "A assinatura tem validade legal mesmo sem certificado ICP-Brasil?", + "resposta": "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.", + "ordem": 8, + "ativo": true + }, + { + "pergunta": "O paciente consegue editar o documento antes de assinar?", + "resposta": "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.", + "ordem": 9, + "ativo": true + }, + { + "pergunta": "Como cancelo uma solicitação de assinatura?", + "resposta": "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.", + "ordem": 10, + "ativo": true + }, + { + "pergunta": "Posso pedir mais de uma pessoa pra assinar o mesmo documento?", + "resposta": "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.", + "ordem": 11, + "ativo": true + } + ] +}