Files
agenciapsilmno/src/features/documents/composables/useDocumentGenerate.js
T
Leonardo 342defecde documents/generate: suporte a edicao in-place + linkage documento_id
document_generated.documento_id (FK pra documents) estava sempre NULL
no INSERT — sem isso nao da pra rastrear qual generated belongs to
qual documents row, impossibilitando re-edicao.

DocumentGenerate.service saveGeneratedDocument:
- Modo create (default): INSERT em documents PRIMEIRO pra capturar
  doc.id, depois INSERT em document_generated com documento_id setado.
- Modo edit (editingDocId param novo): UPDATE in-place — substitui
  PDF no Storage (novo path), atualiza bucket_path/tamanho/nome em
  documents (preserva id+audit), atualiza dados_preenchidos+pdf_path
  em document_generated. Se nao houver registro generated (doc legado),
  INSERT vinculando ao documents.id. Cleanup best-effort do PDF antigo.
- Nova fn loadGeneratedFromDocId(documentoId): busca template_id +
  dados_preenchidos pra pre-popular o dialog de edicao.

useDocumentGenerate.generateAndSave: ganha 2o param editingDocId que
passa pro service.

Backfill SQL pra docs antigos: match dg.pdf_path = d.bucket_path +
tenant/patient guard. 3 docs linkados no DB local, 5 ficaram orfaos
(paths que nao existem mais em documents — cleanup antigo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:42:24 -03:00

210 lines
7.0 KiB
JavaScript

/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/documents/composables/useDocumentGenerate.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import { ref, computed } from 'vue';
import {
loadAllVariables,
fillTemplate,
buildFullHtml,
generatePdfBlob,
generateAndDownloadPdf,
printDocument as printPdf,
saveGeneratedDocument,
listGeneratedDocuments
} from '@/services/DocumentGenerate.service';
import { getTemplate } from '@/services/DocumentTemplates.service';
// ── Composable ──────────────────────────────────────────────
export function useDocumentGenerate() {
const loading = ref(false);
const error = ref(null);
const generatedDocs = ref([]);
// Dados carregados para preenchimento
const variables = ref({});
const selectedTemplate = ref(null);
const previewHtml = ref('');
// ── Carregar variaveis do paciente/sessao ───────────────
async function loadVariables(patientId, agendaEventoId = null) {
loading.value = true;
error.value = null;
try {
variables.value = await loadAllVariables(patientId, agendaEventoId);
// Hint útil pra diagnostico: se vier objeto mas todos campos vazios,
// sinaliza que perfil/clínica/paciente provavelmente nao tem dados.
const filled = Object.values(variables.value).filter(v => String(v ?? '').trim() !== '').length;
if (filled === 0) {
error.value = 'Nenhum dado foi encontrado pra auto-preencher. Verifique o cadastro do paciente, perfil e clínica.';
}
} catch (e) {
console.error('[useDocumentGenerate.loadVariables] falha:', e);
error.value = e?.message || 'Erro ao carregar dados pra preenchimento.';
variables.value = {};
} finally {
loading.value = false;
}
}
// ── Selecionar template e gerar preview ─────────────────
async function selectTemplate(templateId) {
loading.value = true;
error.value = null;
try {
selectedTemplate.value = await getTemplate(templateId);
updatePreview();
} catch (e) {
error.value = e?.message || 'Erro ao carregar template.';
selectedTemplate.value = null;
previewHtml.value = '';
} finally {
loading.value = false;
}
}
// ── Atualizar preview ───────────────────────────────────
function updatePreview() {
if (!selectedTemplate.value) {
previewHtml.value = '';
return;
}
previewHtml.value = buildFullHtml(selectedTemplate.value, variables.value);
}
// ── Atualizar variavel individual ───────────────────────
function setVariable(key, value) {
variables.value[key] = value;
updatePreview();
}
// ── Gerar PDF (client-side) ────────────────────────────
/**
* Gera PDF blob, salva no Storage + banco.
* @param {string} patientId
* @param {string|null} editingDocId - se setado, UPDATE no doc existente
* (in-place replace de PDF + metadados, preserva documents.id e audit).
*/
async function generateAndSave(patientId, editingDocId = null) {
if (!selectedTemplate.value) throw new Error('Nenhum template selecionado.');
loading.value = true;
error.value = null;
try {
const templateNome = selectedTemplate.value.nome_template || 'documento';
// Gera PDF blob
const blob = await generatePdfBlob(selectedTemplate.value, variables.value);
// Salva no Storage + banco (generated-docs + documents)
const result = await saveGeneratedDocument({
templateId: selectedTemplate.value.id,
patientId,
dadosPreenchidos: { ...variables.value },
pdfBlob: blob,
templateNome,
templateTipo: selectedTemplate.value.tipo,
editingDocId
});
generatedDocs.value.unshift(result);
return result;
} catch (e) {
error.value = e?.message || 'Erro ao gerar documento.';
throw e;
} finally {
loading.value = false;
}
}
/**
* Gera somente o PDF e faz download, sem salvar no banco.
*/
async function downloadOnly() {
if (!selectedTemplate.value) return;
loading.value = true;
error.value = null;
try {
const templateNome = selectedTemplate.value?.nome_template || 'documento';
const filename = `${templateNome.replace(/\s+/g, '_')}_${Date.now()}.pdf`;
await generateAndDownloadPdf(selectedTemplate.value, variables.value, filename);
} catch (e) {
error.value = e?.message || 'Erro ao gerar PDF.';
throw e;
} finally {
loading.value = false;
}
}
/**
* Abre PDF em nova aba para impressao.
*/
function printDocument() {
if (!selectedTemplate.value) return;
printPdf(selectedTemplate.value, variables.value);
}
// ── Carregar historico de documentos gerados ────────────
async function fetchGeneratedDocs(patientId) {
loading.value = true;
error.value = null;
try {
generatedDocs.value = await listGeneratedDocuments(patientId);
} catch (e) {
error.value = e?.message || 'Erro ao carregar documentos gerados.';
generatedDocs.value = [];
} finally {
loading.value = false;
}
}
// ── Reset ───────────────────────────────────────────────
function reset() {
selectedTemplate.value = null;
variables.value = {};
previewHtml.value = '';
error.value = null;
}
return {
// State
loading,
error,
variables,
selectedTemplate,
previewHtml,
generatedDocs,
// Actions
loadVariables,
selectTemplate,
updatePreview,
setVariable,
generateAndSave,
downloadOnly,
printDocument,
fetchGeneratedDocs,
reset
};
}