feat(quotes): anexo de conformidade ANATEL/RGC no PDF de oferta
Porta o anexo regulatório do legado (Resolução Anatel 765/2023, RGC, recorte STFC) com fidelidade total ao conteúdo legal, incluindo a seção de tarifação — em vez do html2canvas+jsPDF client-side do legado, usa o Chromium server-side já construído para Documents. Schema: products ganha service_type/metered/tariff_rates/has_ldi (migration 1788525270647) — campos que a migration original de products tinha deliberadamente deferido. Snapshot no item da oferta na criação (nunca referência viva ao produto). Novo pacote compartilhado packages/domain-shared/src/telecom para o catálogo de tipos e legendas de tarifação. Endpoint GET /v1/quotes/:id/anatel-pdf (400 sem item STFC), sempre renderizado ao vivo (nunca cacheado, já que o conteúdo deriva de updated_at). Frontend: campos de produto/oferta necessários + "Baixar PDF ANATEL" na tela de detalhe da oferta. Inclui também o botão de download de PDF em Documentos (endpoint já existia, faltava o link na UI). Verificado ao vivo através do container real: PDF gerado com Chromium de verdade, texto extraído confere byte a byte com os valores computados (multa, datas, tarifas, resumo financeiro). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
* Client for `apps/api` `/v1/documents/templates` and `/v1/documents/generations`
|
||||
* — the "templates + generation" half of Documents (eden.md módulo 4, §1.1-1.3).
|
||||
*
|
||||
* This leva has NO real visual editor and NO real PDF: `editorContent` is a
|
||||
* simplified JSONB with a single root-level `plainText` string carrying
|
||||
* `{{variavel}}` placeholders (no rich node tree), and "generation" just
|
||||
* merges variables into `renderedHtml` — `pdf_s3_key`/`pdf_sha256`/
|
||||
* `pdf_size_bytes` are always `null` here. Don't build UI implying more.
|
||||
* This leva has NO real visual editor: `editorContent` is a simplified
|
||||
* JSONB with a single root-level `plainText` string carrying
|
||||
* `{{variavel}}` placeholders (no rich node tree). PDF generation itself
|
||||
* IS real (Chromium via packages/integrations/src/pdf/renderer.ts) —
|
||||
* `pdf_s3_key`/`pdf_sha256`/`pdf_size_bytes` are populated on every
|
||||
* `status: 'final'` generation.
|
||||
*
|
||||
* Like Contracts/Resellers, template/version/generation responses are
|
||||
* snake_case straight off the DB row — except the two computed fields on a
|
||||
@@ -14,7 +15,7 @@
|
||||
* camelCase exactly as documented in the task brief; mirrored as-is below,
|
||||
* not "fixed" to snake_case.
|
||||
*/
|
||||
import { apiRequest } from "./http.js";
|
||||
import { API_BASE_URL, apiRequest } from "./http.js";
|
||||
|
||||
export type DocGroup = "cliente" | "revenda";
|
||||
export type DocumentVersionStatus = "draft" | "published";
|
||||
@@ -184,6 +185,29 @@ export function createGeneration(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the generated PDF and triggers a browser download. Not routed
|
||||
* through `apiRequest` (JSON-only) — this needs the raw binary response,
|
||||
* but still needs the bearer token, so a plain `<a href>` won't work
|
||||
* either (the API route is behind JwtAuthGuard). Standard SPA pattern:
|
||||
* fetch as a Blob, hand the browser a temporary object URL.
|
||||
*/
|
||||
export async function downloadGenerationPdf(id: string, accessToken: string, filename: string): Promise<void> {
|
||||
const response = await fetch(`${API_BASE_URL}/documents/generations/${id}/pdf`, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Não foi possível baixar o PDF.");
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/** Extracts `{{variavel}}` references from a plain-text template body — used to auto-build the generation context form. */
|
||||
export function extractTemplateVariables(plainText: string): string[] {
|
||||
const found = new Set<string>();
|
||||
|
||||
Reference in New Issue
Block a user