From 565da12c9d9e56968b49c718f0d06c1e2823c569 Mon Sep 17 00:00:00 2001 From: Matheus Cardoso Soares Date: Fri, 14 Aug 2026 00:12:24 -0300 Subject: [PATCH] fix(cliente): listarEventos busca no ADN, nao no SEFIN Nacional --- src/cliente/indice.teste.ts | 44 +++++++++++++++++++++++++++++++++++++ src/cliente/indice.ts | 14 ++++++++---- src/danfse/desenho.ts | 8 ++++++- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/cliente/indice.teste.ts b/src/cliente/indice.teste.ts index 7720e38..73a1812 100644 --- a/src/cliente/indice.teste.ts +++ b/src/cliente/indice.teste.ts @@ -204,3 +204,47 @@ test('consultarConvenio monta o caminho com o codigo do municipio', async () => await fechar(); } }); + +// listarEventos vive no ADN (nao no SEFIN Nacional): a rota POST +// /nfse/{chave}/eventos so serve pra registrar evento, GET nela devolve 405. +// A leitura por chave e um endpoint separado do ADN Contribuinte. +test('listarEventos busca no ADN, nao no SEFIN Nacional', async () => { + const { urlBase, fechar } = await subirServidorDeTeste((req, _corpo, res) => { + assert.equal(req.method, 'GET'); + assert.equal(req.url, '/contribuintes/NFSe/' + '1'.repeat(50) + '/Eventos'); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + StatusProcessamento: 'DOCUMENTOS_LOCALIZADOS', + LoteDFe: [ + { + NSU: 238, + ChaveAcesso: '1'.repeat(50), + TipoDocumento: 'EVENTO', + TipoEvento: 'CANCELAMENTO', + ArquivoXml: compactarGZipBase64('cancelamento'), + DataHoraGeracao: '2026-07-01T23:00:02.04', + }, + ], + Alertas: [], + Erros: [], + TipoAmbiente: 'HOMOLOGACAO', + DataHoraProcessamento: '2026-07-01T23:01:00-03:00', + }) + ); + }); + + try { + const cliente = criarClienteSefin({ + ambiente: 'homologacao', + certificado: clienteDeTeste, + urlBaseAdn: urlBase, + agenteOpcoes: { rejectUnauthorized: false }, + }); + const resultado = await cliente.listarEventos('1'.repeat(50)); + assert.equal(resultado.documentos[0]?.tipoDocumento, 'EVENTO'); + assert.equal(resultado.documentos[0]?.tipoEvento, 'CANCELAMENTO'); + } finally { + await fechar(); + } +}); diff --git a/src/cliente/indice.ts b/src/cliente/indice.ts index 910365d..0799df7 100644 --- a/src/cliente/indice.ts +++ b/src/cliente/indice.ts @@ -62,8 +62,13 @@ export interface ClienteSefin { consultarDps(idDps: string): Promise; /** POST /nfse/{chave}/eventos - registra um evento (ex.: cancelamento) já assinado. */ registrarEvento(chaveAcesso: string, pedRegXmlAssinado: string): Promise; - /** GET /nfse/{chave}/eventos - lista os eventos registrados para a NFS-e. */ - listarEventos(chaveAcesso: string): Promise; + /** + * GET /contribuintes/NFSe/{chave}/Eventos no ADN - lista os eventos (ex.: + * cancelamento, substituição) registrados para a chave de acesso. Não + * confundir com `registrarEvento`: aquele é POST no SEFIN Nacional (só + * aceita escrita, devolve 405 em GET); este é leitura, e vive no ADN. + */ + listarEventos(chaveAcesso: string): Promise; /** * GET /contribuintes/DFe/{nsu} no ADN - baixa o próximo lote de documentos * fiscais (até 50) a partir do NSU informado. Use `0` para sincronizar @@ -185,8 +190,9 @@ export function criarClienteSefin(opcoes: OpcoesClienteSefin): ClienteSefin { }); }, - listarEventos(chaveAcesso) { - return requisitar('GET', urlBase, `nfse/${chaveAcesso}/eventos`); + async listarEventos(chaveAcesso) { + const { corpo } = await requisitar('GET', urlAdn, `contribuintes/NFSe/${chaveAcesso}/Eventos`); + return normalizarLoteDistribuicao(corpo); }, async baixarDfe(nsu, opcoesConsulta) { diff --git a/src/danfse/desenho.ts b/src/danfse/desenho.ts index de4897b..29cc9ed 100644 --- a/src/danfse/desenho.ts +++ b/src/danfse/desenho.ts @@ -76,7 +76,13 @@ const ALTURA_FAIXA = 3.4 * MM; export type ResolvedorMunicipio = (codigoIbge: string) => { nome: string; uf: string } | undefined; export interface OpcoesGerarDanfse { - /** Aplica marca d'água diagonal, conforme NT 008/2026 §2.5. */ + /** + * Aplica marca d'água diagonal, conforme NT 008/2026 §2.5. Precisa ser + * informado explicitamente porque cancelamento/substituição nunca ficam no + * XML da própria NFS-e (o `cStat` só tem códigos de geração) - são sempre + * um documento de EVENTO separado. Quem chama essa função é responsável + * por checar os eventos (`listarEventos`/`baixarDfe`) antes de decidir. + */ situacaoEspecial?: 'Cancelada' | 'Substituida'; resolverMunicipio?: ResolvedorMunicipio; /** PNG/JPEG da logomarca oficial da NFS-e (item 2.4.3). Sem isso, o cabeçalho usa só texto. */