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$
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.
+ +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:
+Ao abrir o link /shared/document/:token, o paciente vê:
Ao clicar Assinar:
+sign_document_by_token passando o hashdocument_signatures: timestamp, hash, IP, UA, status='assinado'Cada assinatura grava:
+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.
+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.
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).
max(signatários × 3, 5) — gerar 1 signatário dá 5 usos disponíveis (margem de erro / reload / multi-device).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.
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.
+ +sign_document_by_signature_id (paciente logado no portal), sign_document_by_token (link público), get_signable_document_by_token (resolve token → doc + signature_request), list_my_signatures (lista do paciente, cruza por signatario_id, signatario_email e patient.user_id)DocumentSignatures.service.js com wrappers signByPortal, signByToken, getSignableDocumentByToken, listMySignatures, hashDocument, refuseSignature, createSignatureRequests, createShareLink, buildShareUrluseDocumentSignatures (Tipo A blueprint)DocumentSignatureDialog.vue (component)PortalDocumentos.vue (portal logado) + SharedDocumentPage.vue (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": "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\nNo preview de um documento (na aba Documentos do prontuário), clique no botão Assinar na sidebar de ações. O DocumentSignatureDialog abre com:
\nAo abrir o link /shared/document/:token, o paciente vê:
Ao clicar Assinar:
\nsign_document_by_token passando o hashdocument_signatures: timestamp, hash, IP, UA, status='assinado'Cada assinatura grava:
\ninet_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.\nO 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.
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).
max(signatários × 3, 5) — gerar 1 signatário dá 5 usos disponíveis (margem de erro / reload / multi-device).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.
A assinatura eletrônica registrada pelo sistema atende:
\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\nsign_document_by_signature_id (paciente logado no portal), sign_document_by_token (link público), get_signable_document_by_token (resolve token → doc + signature_request), list_my_signatures (lista do paciente, cruza por signatario_id, signatario_email e patient.user_id)DocumentSignatures.service.js com wrappers signByPortal, signByToken, getSignableDocumentByToken, listMySignatures, hashDocument, refuseSignature, createSignatureRequests, createShareLink, buildShareUrluseDocumentSignatures (Tipo A blueprint)DocumentSignatureDialog.vue (component)PortalDocumentos.vue (portal logado) + SharedDocumentPage.vue (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
+ }
+ ]
+}