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:
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Adds the legacy telecom-billing specifics that 1788438907074_products.cjs
|
||||
* deliberately deferred ("service_type/metered/tariff_rates/ixc_product_code
|
||||
* — those are legacy telecom-billing specifics that belong to a later
|
||||
* phase, not invented here"). That later phase is now: the ANATEL/RGC
|
||||
* compliance annex (Resolução Anatel nº 765/2023) needs to know which
|
||||
* products are STFC (art. 21 §3º), whether they're metered per-minute,
|
||||
* and their LC/LDN/VC1/VC2/VC3/LDI tariff rates to print the required
|
||||
* disclosure. Column shapes and the 4-value service_type list are ported
|
||||
* with exact fidelity from the legacy's baseline-schema.js products table.
|
||||
*
|
||||
* `quotes.sales_rep_name` rides along here (small, single-column, same
|
||||
* ANATEL-annex consumer — printed in the document's signature block) for
|
||||
* the same reason can_approve_discount rode along in 1788445280742_quotes.
|
||||
*
|
||||
* service_type/metered/minutes_allowance/tariff_rates/has_ldi are snapshot
|
||||
* onto quotes.items (a JSONB blob, no schema change needed there) at
|
||||
* quote-creation time — see apps/api/src/quotes/quotes.service.ts
|
||||
* buildItems() — so a later product edit never retroactively changes an
|
||||
* already-issued quote's regulatory disclosure (ported from the legacy's
|
||||
* same snapshot behavior, NewQuote.jsx).
|
||||
*/
|
||||
|
||||
exports.shorthands = undefined;
|
||||
|
||||
exports.up = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE products
|
||||
ADD COLUMN service_type TEXT NOT NULL DEFAULT 'STFC'
|
||||
CHECK (service_type IN ('STFC', 'Serviço PABX', 'SVA (locação)', 'Serviço software')),
|
||||
ADD COLUMN metered BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN minutes_allowance NUMERIC(12,2),
|
||||
ADD COLUMN tariff_rates JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN has_ldi BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
COMMENT ON COLUMN products.service_type IS
|
||||
'Recorte regulatório RGC (Resolução Anatel 765/2023) — só estes 4 valores existem no legado, ported verbatim. Gate para a seção de conformidade ANATEL no PDF de oferta (só aparece se algum item da oferta for STFC).';
|
||||
COMMENT ON COLUMN products.metered IS
|
||||
'Cobrança por minuto (chamadas STFC) — gate para a tabela de tarifação (LC/LDN/VC1/VC2/VC3[/LDI]) no PDF de oferta.';
|
||||
COMMENT ON COLUMN products.tariff_rates IS
|
||||
'Tarifas por tipo de chamada, formato {TIPO: {normal, reduced}} — chaves possíveis LC/LDN/VC1/VC2/VC3/LDI (ver packages/domain-shared para o catálogo de tipos). LDI só é exibido na tabela quando has_ldi=true, mas a chave pode existir no JSON de qualquer forma.';
|
||||
COMMENT ON COLUMN products.has_ldi IS
|
||||
'Habilita a coluna/linha de legenda LDI (longa distância internacional) na tabela de tarifação.';
|
||||
|
||||
ALTER TABLE quotes ADD COLUMN sales_rep_name TEXT;
|
||||
COMMENT ON COLUMN quotes.sales_rep_name IS
|
||||
'Nome do responsável comercial impresso no bloco de assinatura do PDF de oferta — texto livre, opcional (cai para "Responsável Comercial" genérico quando ausente).';
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (pgm) => {
|
||||
pgm.sql(`
|
||||
ALTER TABLE quotes DROP COLUMN IF EXISTS sales_rep_name;
|
||||
ALTER TABLE products
|
||||
DROP COLUMN IF EXISTS service_type,
|
||||
DROP COLUMN IF EXISTS metered,
|
||||
DROP COLUMN IF EXISTS minutes_allowance,
|
||||
DROP COLUMN IF EXISTS tariff_rates,
|
||||
DROP COLUMN IF EXISTS has_ldi;
|
||||
`);
|
||||
};
|
||||
Reference in New Issue
Block a user