@bitize/bitmde 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +252 -2
  3. package/dist/apis/distribuicaoNFSe-api.d.ts +176 -0
  4. package/dist/apis/manifestacaoNFSe-api.d.ts +130 -0
  5. package/dist/controllers/distribuicaoNFSe-controller.d.ts +50 -0
  6. package/dist/controllers/index.d.ts +76 -0
  7. package/dist/controllers/manifestacaoNFSe-controller.d.ts +38 -0
  8. package/dist/env/index.d.ts +4 -1
  9. package/dist/env/nfse.d.ts +32 -0
  10. package/dist/helpers/distribuicaoNFSe-helper.d.ts +107 -0
  11. package/dist/helpers/index.d.ts +146 -0
  12. package/dist/helpers/manifestacaoNFSe-helper.d.ts +80 -0
  13. package/dist/helpers/retornoNfse-helper.d.ts +44 -0
  14. package/dist/index.d.ts +7 -1
  15. package/dist/schemas/index.d.ts +17 -0
  16. package/dist/schemas/manifestacaoNFSe-schema.d.ts +33 -0
  17. package/dist/util/assinatura.d.ts +3 -1
  18. package/dist/util/gzip.d.ts +7 -0
  19. package/dist/util/index.d.ts +2 -1
  20. package/dist/validators/chaveNfse-validator.d.ts +14 -0
  21. package/dist/validators/cnpj-validator.d.ts +19 -0
  22. package/dist/validators/index.d.ts +50 -0
  23. package/dist/validators/motivoRejeicao-validator.d.ts +23 -0
  24. package/dist/validators/nsuNfse-validator.d.ts +20 -0
  25. package/lib/apis/distribuicaoNFSe-api.js +59 -0
  26. package/lib/apis/manifestacaoNFSe-api.js +69 -0
  27. package/lib/controllers/distribuicaoNFSe-controller.js +16 -0
  28. package/lib/controllers/index.js +3 -1
  29. package/lib/controllers/manifestacaoNFSe-controller.js +25 -0
  30. package/lib/env/index.js +8 -1
  31. package/lib/env/nfse.js +24 -0
  32. package/lib/env/version.js +1 -1
  33. package/lib/helpers/distribuicaoNFSe-helper.js +128 -0
  34. package/lib/helpers/index.js +5 -2
  35. package/lib/helpers/manifestacaoNFSe-helper.js +105 -0
  36. package/lib/helpers/retornoNfse-helper.js +31 -0
  37. package/lib/index.js +7 -1
  38. package/lib/schemas/index.js +2 -1
  39. package/lib/schemas/manifestacaoNFSe-schema.js +19 -0
  40. package/lib/util/assinatura.js +2 -2
  41. package/lib/util/gzip.js +8 -0
  42. package/lib/validators/chaveNfse-validator.js +16 -0
  43. package/lib/validators/cnpj-validator.js +21 -0
  44. package/lib/validators/index.js +5 -1
  45. package/lib/validators/motivoRejeicao-validator.js +28 -0
  46. package/lib/validators/nsuNfse-validator.js +18 -0
  47. package/package.json +6 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## [Não publicado]
4
4
 
5
+ ## [0.20.0] / 2026-10-16
6
+
7
+ > Acrescenta a NFS-e Nacional: distribuição pelo ADN e manifestação do tomador na SEFIN. Nada muda nas APIs de NF-e.
8
+
9
+ ### Adicionado
10
+
11
+ - **`DistribuicaoNFSe`: distribuição de NFS-e Nacional pelo Ambiente de Dados Nacional (ADN)**, exportada nas três formas (`module.exports`, `.default`, `.mde`). Usa o mesmo certificado A1 da NF-e e consulta as NFS-e e os eventos em que o CNPJ figura:
12
+ - `consultaUltNSU(ultNSU)` — até 50 documentos depois do NSU informado, começando em `'0'`; fila vazia é HTTP 404 sem `error`
13
+ - `consultaNSU(nsu)` — o documento daquele NSU
14
+ - `consultaChNFSe(chNFSe)` — a NFS-e da chave de 50 dígitos e os eventos dela
15
+ - Campo `papel` em cada NFS-e distribuída: `'tomador'`, `'prestador'` ou `'intermediario'`, conforme o CNPJ consultado apareça como `toma`, `prest` ou `interm` da DPS, nessa ordem; `''` se não aparecer e em eventos. A biblioteca marca, não filtra
16
+ - Retorno próprio da NFS-e, `{ data, resBody, status, error?, transportError? }`: `resBody` é o corpo cru do ADN, e `status`, `error` e `transportError` têm o mesmo significado das APIs de NF-e. Ver [ADR 0016](https://github.com/bitize/bitmde/blob/main/.docs/arquitetura/decisoes/0016-retorno-proprio-da-nfse.md)
17
+ - Hosts do ADN, de produção e produção restrita, no `test/tls-sefaz.test.js` e com isso no workflow semanal `cadeia-tls.yml`
18
+ - **`ManifestacaoNFSe`: manifestação do tomador da NFS-e Nacional na SEFIN Nacional**, exportada nas três formas. Com o mesmo certificado A1, registra um evento por chamada:
19
+ - `confirmar(chNFSe)` — Confirmação do Tomador (`e203202`)
20
+ - `rejeitar(chNFSe, { cMotivo, xMotivo? })` — Rejeição do Tomador (`e203206`), com `cMotivo` `'1'` a `'5'` ou `'9'`; `xMotivo` de 15 a 255 caracteres, obrigatório no motivo `'9'`
21
+ - Retorno da manifestação `{ data, reqXml, resBody, status, error?, transportError? }`, no formato da ADR 0016: `reqXml` é o `pedRegEvento` assinado e `data.evento` traz o evento registrado, em `xml` e `json`
22
+ - Hosts da SEFIN Nacional, de produção e produção restrita, no `test/tls-sefaz.test.js` e no `cadeia-tls.yml`
23
+
24
+ Nada muda em `DistribuicaoDFe` nem em `RecepcaoEvento`: o XML assinado da recepção de evento sai byte a byte igual, conferido por teste.
25
+
26
+ ### Modificado
27
+
28
+ - Workflow `testes.yml` deixa de rodar em push e PR que só mexem em documentação (`**/*.md`, `.docs/**`, `.claude/**`, `LICENSE`). O `qualidade.yml`, com o Prettier que valida o Markdown, continua rodando sempre, e o `publicar.yml` roda a suíte antes de todo release
29
+
5
30
  ## [0.19.0] / 2026-10-03
6
31
 
7
32
  > Muda o comportamento padrão de transporte e deixa de suportar o Node 20; leia [Compatibilidade](#compatibilidade) antes de atualizar.
package/README.md CHANGED
@@ -5,9 +5,9 @@
5
5
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bitize/bitmde/blob/main/LICENSE)
6
6
  [![Mantido pelo BitERP](https://img.shields.io/badge/mantido%20por-BitERP-0b5fff.svg)](https://www.biterp.ai)
7
7
 
8
- Biblioteca para consumo dos Web Services da Sefaz de Distribuição de DF-e e Evento de Manifestação do Destinatário.
8
+ Biblioteca para consumo dos Web Services da Sefaz de Distribuição de DF-e e Evento de Manifestação do Destinatário, e da NFS-e Nacional: distribuição pelo Ambiente de Dados Nacional (ADN) e manifestação do tomador na SEFIN Nacional.
9
9
 
10
- Essa biblioteca permite consultar a relação das notas fiscais emitidas contra um determinado CNPJ/CPF e realizar o envio do evento de manifestação, podendo assim, baixar o XML da NF-e.
10
+ Essa biblioteca permite consultar a relação das notas fiscais emitidas contra um determinado CNPJ/CPF e realizar o envio do evento de manifestação, podendo assim, baixar o XML da NF-e. Com o mesmo certificado, consulta as NFS-e em que o CNPJ é tomador, prestador ou intermediário, e registra a Confirmação ou a Rejeição do tomador.
11
11
 
12
12
  Desenvolvida e mantida pela equipe do **[BitERP](https://www.biterp.ai)**, onde é usada em produção para a automação da manifestação do destinatário e do download de XML de NF-e de entrada.
13
13
 
@@ -37,6 +37,10 @@ npm i @bitize/bitmde
37
37
  - Retorna o Documento Fiscal referente ao NSU informado, podendo ser um `Resumo`, uma `NF-e` ou um `Evento`
38
38
  - Envio de evento
39
39
  - Registra o evento de manifestação na nota informada (`Confirmação da Operação`, `Ciência da Operação`, `Desconhecimento da Operação` ou `Operação não Realizada`)
40
+ - Distribuição de NFS-e Nacional
41
+ - Consulta por último NSU, por NSU e por chave de acesso no ADN, com o papel do CNPJ (`tomador`, `prestador` ou `intermediario`) em cada NFS-e
42
+ - Manifestação do tomador da NFS-e Nacional
43
+ - Registra a `Confirmação do Tomador` ou a `Rejeição do Tomador` na SEFIN Nacional
40
44
 
41
45
  ## Distribuição de DF-e
42
46
 
@@ -348,6 +352,250 @@ console.log(manifestacao)
348
352
  // }
349
353
  ```
350
354
 
355
+ ## Distribuição de NFS-e Nacional
356
+
357
+ Consulta, no Ambiente de Dados Nacional (ADN), as NFS-e e os eventos em que o CNPJ figura. A fila traz o histórico inteiro do CNPJ desde a primeira nota no ADN e mistura notas recebidas, notas emitidas e eventos de terceiros sobre umas e outras. A biblioteca **não filtra**: cada NFS-e vem com o `papel` do CNPJ consultado.
358
+
359
+ > **Cobertura.** Só aparece a NFS-e que chegou ao ADN: as emitidas no Emissor Nacional e as de município cujo sistema próprio compartilha com o ADN. Nota de município que ainda não compartilha não está na distribuição, mesmo com a obrigatoriedade da LC 214/2025.
360
+
361
+ ### Construtor
362
+
363
+ ```js
364
+ new DistribuicaoNFSe(config)
365
+ ```
366
+
367
+ - `config` `<Object>`
368
+ - `pfx` `<Buffer>` - [OPCIONAL] - Arquivo **.pfx**. Se o `pfx` não for informado, as propriedades `cert` e `key` passam a ser obrigatórias.
369
+ - `passphrase` `<String>` - [OPCIONAL] - Senha do arquivo **.pfx**.
370
+ - `cert` `<Buffer | String>` - [OPCIONAL] - Conteúdo do _cert.pem_. Essa propriedade fica obrigatória se o `pfx` não for informado.
371
+ - `key` `<Buffer | String>` - [OPCIONAL] - Conteúdo do _key.pem_. Essa propriedade fica obrigatória se o `pfx` não for informado.
372
+ - `cnpj` `<String>` - [OBRIGATÓRIO] - CNPJ consultado, com 14 caracteres e sem pontuação (aceita o alfanumérico). Pode ser o do certificado ou o de outro estabelecimento com a mesma raiz.
373
+ - `tpAmb` `<String>` - [OBRIGATÓRIO] - Informar `'1'` para **Produção** ou `'2'` para **Produção restrita**.
374
+ - `options` `<Object>` - [OPCIONAL]
375
+ - `requestOptions` `<AxiosRequestConfig>` - [OPCIONAL]
376
+ - `httpsOptions` `<AgentOptions>` - [OPCIONAL] - Opções do `https.Agent`, mescladas por cima do padrão `{ ca: CA_PADRAO, rejectUnauthorized: true }`. Ver [Conexão com a SEFAZ](#conexão-com-a-sefaz).
377
+
378
+ ### Consultas
379
+
380
+ | Método | Argumento | Devolve |
381
+ | :----------------------- | :--------------------------------- | :--------------------------------------------- |
382
+ | `consultaUltNSU(ultNSU)` | NSU, 1-15 dígitos; começa em `'0'` | Até 50 documentos **depois** do NSU informado |
383
+ | `consultaNSU(nsu)` | NSU, 1-15 dígitos, maior que zero | O documento daquele NSU |
384
+ | `consultaChNFSe(chNFSe)` | Chave de acesso, 50 dígitos | A NFS-e da chave e os eventos vinculados a ela |
385
+
386
+ No ADN o NSU é um inteiro, sem zeros à esquerda: `'00051'` e `51` viram `'51'`.
387
+
388
+ #### Exemplo: drenar a fila
389
+
390
+ ```js
391
+ const { DistribuicaoNFSe } = require('@bitize/bitmde')
392
+ const fs = require('fs')
393
+
394
+ const distribuicao = new DistribuicaoNFSe({
395
+ pfx: fs.readFileSync('./certificado.pfx'),
396
+ passphrase: 'senha',
397
+ cnpj: '12345678000190',
398
+ tpAmb: '1',
399
+ })
400
+
401
+ let ultNSU = '0' // ou o último NSU guardado
402
+
403
+ while (true) {
404
+ const consulta = await distribuicao.consultaUltNSU(ultNSU)
405
+
406
+ if (consulta.error) {
407
+ throw new Error(consulta.error)
408
+ }
409
+
410
+ const { documentos, documentosErros } = consulta.data
411
+
412
+ // Fim da fila: HTTP 404, NENHUM_DOCUMENTO_LOCALIZADO, sem error.
413
+ if (documentos.length === 0 && documentosErros.length === 0) {
414
+ break
415
+ }
416
+
417
+ for (const doc of documentos) {
418
+ if (doc.tipoDocumento === 'NFSE' && doc.papel === 'tomador') {
419
+ // NFS-e recebida: doc.xml e doc.json
420
+ }
421
+ }
422
+
423
+ ultNSU = [...documentos, ...documentosErros]
424
+ .map((doc) => doc.nsu)
425
+ .sort((a, b) => Number(a) - Number(b))
426
+ .pop()
427
+ }
428
+ ```
429
+
430
+ O próximo pedido usa o **último NSU recebido**, sem somar 1. O ADN não bloqueia quem volta a um NSU anterior, como faz a distribuição de NF-e, mas guardar o ponteiro e escolher o intervalo entre as consultas continua sendo de quem consome.
431
+
432
+ #### Retorno
433
+
434
+ ```js
435
+ console.log(consulta)
436
+ // {
437
+ // data: {
438
+ // statusProcessamento: 'DOCUMENTOS_LOCALIZADOS',
439
+ // tipoAmbiente: 'PRODUCAO',
440
+ // versaoAplicativo: '1.0.0.0',
441
+ // dataHoraProcessamento: '2026-10-15T10:35:14.5687887-03:00',
442
+ // alertas: [],
443
+ // erros: [],
444
+ // documentos: [
445
+ // {
446
+ // nsu: '1',
447
+ // chaveAcesso: '41000000000000000000000000000000000000000000000001',
448
+ // tipoDocumento: 'NFSE',
449
+ // tipoEvento: '',
450
+ // dataHoraGeracao: '2026-10-01T09:12:00',
451
+ // papel: 'tomador',
452
+ // xml: '<?xml version="1.0" encoding="utf-8"?><NFSe versao="1.01" ... </NFSe>',
453
+ // json: { NFSe: { ... } },
454
+ // },
455
+ // {
456
+ // nsu: '2',
457
+ // chaveAcesso: '41000000000000000000000000000000000000000000000001',
458
+ // tipoDocumento: 'EVENTO',
459
+ // tipoEvento: 'CANCELAMENTO',
460
+ // dataHoraGeracao: '2026-10-02T15:40:00',
461
+ // papel: '',
462
+ // xml: '<?xml version="1.0" encoding="utf-8"?><evento versao="1.01" ... </evento>',
463
+ // json: { evento: { ... } },
464
+ // },
465
+ // ],
466
+ // documentosErros: [],
467
+ // },
468
+ // resBody: '{"StatusProcessamento":"DOCUMENTOS_LOCALIZADOS", ... }',
469
+ // status: 200,
470
+ // }
471
+ ```
472
+
473
+ - `papel` vale `'tomador'`, `'prestador'` ou `'intermediario'` quando o `cnpj` da config é o `toma`, o `prest` ou o `interm` da DPS, testados nessa ordem; `''` quando não é nenhum, e sempre `''` em eventos. Para ficar só com as recebidas, filtre por `tipoDocumento === 'NFSE' && papel === 'tomador'`.
474
+ - `resBody` é o corpo cru da resposta do ADN, como veio. Não há `reqXml` nem `resXml`: a consulta é um `GET` sem corpo e a resposta é JSON.
475
+ - `documentosErros` traz, com `nsu`, `chaveAcesso`, `tipoDocumento` e `error`, os itens cujo XML não abriu: `ArquivoXml sem conteúdo.`, `Falha ao descompactar o ArquivoXml: <motivo>` ou `Falha ao interpretar o XML do ArquivoXml: <motivo>`. Eles não derrubam o lote nem preenchem o `error` de topo.
476
+
477
+ #### Quando há `error`
478
+
479
+ | Situação | `status` | `data` | `error` |
480
+ | :------------------------------------------------ | :------: | :--------------------------- | :------------------------------------------------- |
481
+ | Fila vazia, chave ou NSU sem documento | 404 | preenchido, `documentos: []` | — |
482
+ | Rejeição do ADN (ex.: `cnpj` de outra raiz) | 400 | preenchido, com `erros` | `E2243: O CNPJ informado na consulta deve ter ...` |
483
+ | Resposta que não é do ADN (ex.: HTML de um proxy) | real | `{}` | `Resposta inesperada do ADN (HTTP 502).` |
484
+ | Sem resposta HTTP | 0 | `{}` | `Falha de transporte (<code>): <message>` |
485
+
486
+ Na rejeição, `data` **não** é esvaziado: `data.erros` traz o diagnóstico em `{ codigo, descricao, complemento }`. `error` e `transportError` têm o mesmo significado da distribuição de DF-e — ver [Erro de transporte](#erro-de-transporte).
487
+
488
+ ## Manifestação do tomador da NFS-e Nacional
489
+
490
+ Registra, na SEFIN Nacional, a Confirmação (`e203202`) ou a Rejeição (`e203206`) do tomador sobre uma NFS-e que exista no ADN. Cada chamada é **um** pedido de registro de evento, assinado com o A1 da config — não há lote.
491
+
492
+ > **Evento fiscal não se desfaz.** Em produção (`tpAmb: '1'`), cada chamada aceita registra um evento vinculado à nota. Depois da Confirmação, o prestador não consegue mais cancelar a NFS-e, só substituí-la; a Rejeição só é anulada pelo município de incidência. Teste em produção restrita (`tpAmb: '2'`).
493
+
494
+ ### Construtor
495
+
496
+ ```js
497
+ new ManifestacaoNFSe(config)
498
+ ```
499
+
500
+ - `config` `<Object>`
501
+ - `pfx` `<Buffer>` - [OPCIONAL] - Arquivo **.pfx**. Se o `pfx` não for informado, as propriedades `cert` e `key` passam a ser obrigatórias.
502
+ - `passphrase` `<String>` - [OPCIONAL] - Senha do arquivo **.pfx**.
503
+ - `cert` `<Buffer | String>` - [OPCIONAL] - Conteúdo do _cert.pem_. Essa propriedade fica obrigatória se o `pfx` não for informado.
504
+ - `key` `<Buffer | String>` - [OPCIONAL] - Conteúdo do _key.pem_. Essa propriedade fica obrigatória se o `pfx` não for informado.
505
+ - `cnpj` `<String>` - [OBRIGATÓRIO] - CNPJ do tomador, enviado como `CNPJAutor`, com 14 caracteres e sem pontuação. Precisa ter a mesma raiz do CNPJ do certificado.
506
+ - `tpAmb` `<String>` - [OBRIGATÓRIO] - Informar `'1'` para **Produção** ou `'2'` para **Produção restrita**.
507
+ - `timezone` `<String>` - [OPCIONAL] - Fuso do `dhEvento`. Padrão `'America/Sao_Paulo'`. Ver [Lista de Timezones](#lista-de-timezones).
508
+ - `options` `<Object>` - [OPCIONAL]
509
+ - `requestOptions` `<AxiosRequestConfig>` - [OPCIONAL]
510
+ - `httpsOptions` `<AgentOptions>` - [OPCIONAL] - Opções do `https.Agent`, mescladas por cima do padrão `{ ca: CA_PADRAO, rejectUnauthorized: true }`. Ver [Conexão com a SEFAZ](#conexão-com-a-sefaz).
511
+
512
+ ### Métodos
513
+
514
+ | Método | Argumentos | Evento |
515
+ | :------------------------- | :------------------------------------------------- | :--------------------------------- |
516
+ | `confirmar(chNFSe)` | Chave de acesso, 50 dígitos | `e203202` — Confirmação do Tomador |
517
+ | `rejeitar(chNFSe, motivo)` | Chave de acesso e `motivo` `{ cMotivo, xMotivo? }` | `e203206` — Rejeição do Tomador |
518
+
519
+ Códigos de `cMotivo`:
520
+
521
+ | `cMotivo` | Motivo |
522
+ | :-------: | :-------------------------------------------------------------------------------------------- |
523
+ | `'1'` | NFS-e em duplicidade |
524
+ | `'2'` | NFS-e já emitida pelo tomador |
525
+ | `'3'` | Não ocorrência do fato gerador |
526
+ | `'4'` | Erro quanto à responsabilidade tributária |
527
+ | `'5'` | Erro quanto ao valor do serviço, valor das deduções, serviço prestado ou data do fato gerador |
528
+ | `'9'` | Outros — **exige `xMotivo`** |
529
+
530
+ `xMotivo` é opcional nos demais motivos e, quando informado, tem de 15 a 255 caracteres.
531
+
532
+ Cada NFS-e aceita uma Confirmação e uma Rejeição do tomador. A Rejeição é aceita depois da Confirmação, e a Confirmação depois da Rejeição; repetir o mesmo evento é rejeitado (`E0861` e `E0866`).
533
+
534
+ #### Exemplo
535
+
536
+ ```js
537
+ const { ManifestacaoNFSe } = require('@bitize/bitmde')
538
+ const fs = require('fs')
539
+
540
+ const manifestacao = new ManifestacaoNFSe({
541
+ pfx: fs.readFileSync('./certificado.pfx'),
542
+ passphrase: 'senha',
543
+ cnpj: '12345678000190',
544
+ tpAmb: '2',
545
+ })
546
+
547
+ const confirmacao = await manifestacao.confirmar(
548
+ '41000000000000000000000000000000000000000000000001'
549
+ )
550
+
551
+ const rejeicao = await manifestacao.rejeitar(
552
+ '41000000000000000000000000000000000000000000000002',
553
+ { cMotivo: '9', xMotivo: 'Serviço não foi prestado.' }
554
+ )
555
+
556
+ if (rejeicao.error) {
557
+ console.error(rejeicao.error) // ex.: 'E0866: ...'
558
+ }
559
+ ```
560
+
561
+ #### Retorno
562
+
563
+ ```js
564
+ console.log(confirmacao)
565
+ // {
566
+ // data: {
567
+ // tipoAmbiente: '2',
568
+ // versaoAplicativo: 'SefinNacional_1.6.0',
569
+ // dataHoraProcessamento: '2026-10-16T09:26:06-03:00',
570
+ // alertas: [],
571
+ // erros: [],
572
+ // evento: {
573
+ // xml: '<evento versao="1.01" ... </evento>',
574
+ // json: { evento: { ... } },
575
+ // },
576
+ // },
577
+ // reqXml: '<?xml version="1.0" encoding="UTF-8"?><pedRegEvento xmlns="http://www.sped.fazenda.gov.br/nfse" versao="1.01"> ... </pedRegEvento>',
578
+ // resBody: '{"tipoAmbiente":2, ... }',
579
+ // status: 201,
580
+ // }
581
+ ```
582
+
583
+ - `reqXml` é o `pedRegEvento` assinado, antes do gzip — é o que se audita numa rejeição.
584
+ - `data.evento` traz o evento registrado pela SEFIN, em `xml` e `json`.
585
+ - `status`, `error` e `transportError` têm o mesmo significado das outras APIs.
586
+
587
+ #### Quando há `error`
588
+
589
+ | Situação | `status` | `data` | `error` |
590
+ | :-------------------------------------------------- | :------: | :----------------------- | :----------------------------------------------------- |
591
+ | Evento registrado e devolvido | 201 | preenchido, com `evento` | — |
592
+ | Evento registrado, mas o retorno não abre | 201 | preenchido, `evento: {}` | `Falha ao abrir o evento retornado: <motivo>` |
593
+ | Rejeição da SEFIN (ex.: nota fora do ADN) | 400 | preenchido, com `erros` | `E1831: O evento não pode ser recepcionado pois a ...` |
594
+ | Resposta que não é da SEFIN (ex.: HTML de um proxy) | real | `{}` | `Resposta inesperada da SEFIN (HTTP 502).` |
595
+ | Sem resposta HTTP | 0 | `{}` | `Falha de transporte (<code>): <message>` |
596
+
597
+ Na segunda linha o evento **foi registrado**: não repita o pedido; consulte a nota pela [Distribuição de NFS-e Nacional](#distribuição-de-nfs-e-nacional). `reqXml` vem em todos os casos.
598
+
351
599
  ## Conexão com a SEFAZ
352
600
 
353
601
  ### Validação do certificado do servidor
@@ -402,6 +650,8 @@ Quando não há resposta HTTP (falha na cadeia TLS, DNS, conexão recusada, time
402
650
 
403
651
  Uma resposta HTTP de erro da SEFAZ, como um `502` ou um `503`, chega com o status real, o corpo em `resXml` e **sem** `transportError`. Então `status: 0` significa sempre "a SEFAZ não respondeu".
404
652
 
653
+ Na `DistribuicaoNFSe` o erro de transporte tem a mesma forma, com `resBody: ''` no lugar de `reqXml` e `resXml`. As mesmas regras de `CA_PADRAO` e `httpsOptions` valem para os hosts do ADN, que hoje servem certificado Let's Encrypt.
654
+
405
655
  ## Sobre o BitERP
406
656
 
407
657
  O `@bitize/bitmde` é mantido pela equipe do **[BitERP](https://www.biterp.ai)**, um ERP online que automatiza a rotina fiscal, financeira e comercial de empresas brasileiras.
@@ -0,0 +1,176 @@
1
+ export = DistribuicaoNFSe;
2
+ declare class DistribuicaoNFSe {
3
+ /**
4
+ * Distribuição de NFS-e Nacional pelo Ambiente de Dados Nacional (ADN).
5
+ *
6
+ * @param {Object} config
7
+ * @param {Buffer} [config.pfx]
8
+ * @param {string} [config.passphrase]
9
+ * @param {Buffer | string} [config.cert]
10
+ * @param {Buffer | string} [config.key]
11
+ * @param {string} config.cnpj CNPJ consultado, enviado como `cnpjConsulta`. Precisa ter a mesma raiz do CNPJ do certificado.
12
+ * @param {'1' | '2'} config.tpAmb `'1'` produção, `'2'` produção restrita.
13
+ * @param {Object} [config.options]
14
+ * @param {import('axios').AxiosRequestConfig} [config.options.requestOptions]
15
+ * @param {import('https').AgentOptions} [config.options.httpsOptions] Mesclado por cima do padrão `{ ca: CA_PADRAO, rejectUnauthorized: true }`. Informar `ca` substitui a cadeia inteira; para somar uma AC, use `[...CA_PADRAO, minhaCa]`.
16
+ */
17
+ constructor(config: {
18
+ pfx?: Buffer;
19
+ passphrase?: string;
20
+ cert?: Buffer | string;
21
+ key?: Buffer | string;
22
+ cnpj: string;
23
+ tpAmb: "1" | "2";
24
+ options?: {
25
+ requestOptions?: import("axios").AxiosRequestConfig;
26
+ httpsOptions?: import("https").AgentOptions;
27
+ };
28
+ });
29
+ config: Readonly<{
30
+ cnpj: string;
31
+ tpAmb: "1" | "2";
32
+ cert: string;
33
+ key: string;
34
+ requestOptions: Readonly<import("axios").AxiosRequestConfig<any, any>>;
35
+ httpsOptions: Readonly<import("node:https").AgentOptions>;
36
+ }>;
37
+ /**
38
+ * A NFS-e da chave e os eventos vinculados a ela.
39
+ *
40
+ * @param {string} chNFSe Chave de acesso de 50 dígitos.
41
+ */
42
+ consultaChNFSe(chNFSe: string): Promise<{
43
+ data: {
44
+ statusProcessamento: string;
45
+ tipoAmbiente: string;
46
+ versaoAplicativo: string;
47
+ dataHoraProcessamento: string;
48
+ alertas: Array<{
49
+ codigo: string;
50
+ descricao: string;
51
+ complemento: string;
52
+ }>;
53
+ erros: Array<{
54
+ codigo: string;
55
+ descricao: string;
56
+ complemento: string;
57
+ }>;
58
+ documentos: Array<{
59
+ nsu: string;
60
+ chaveAcesso: string;
61
+ tipoDocumento: string;
62
+ tipoEvento: string;
63
+ dataHoraGeracao: string;
64
+ papel: "" | "tomador" | "prestador" | "intermediario";
65
+ xml: string;
66
+ json: any;
67
+ }>;
68
+ documentosErros: Array<{
69
+ nsu: string;
70
+ chaveAcesso: string;
71
+ tipoDocumento: string;
72
+ error: string;
73
+ }>;
74
+ };
75
+ error?: string;
76
+ transportError?: {
77
+ code: string;
78
+ message: string;
79
+ };
80
+ resBody: string;
81
+ status: number;
82
+ }>;
83
+ /**
84
+ * O documento daquele NSU.
85
+ *
86
+ * @param {string | number} nsu Maior que zero.
87
+ */
88
+ consultaNSU(nsu: string | number): Promise<{
89
+ data: {
90
+ statusProcessamento: string;
91
+ tipoAmbiente: string;
92
+ versaoAplicativo: string;
93
+ dataHoraProcessamento: string;
94
+ alertas: Array<{
95
+ codigo: string;
96
+ descricao: string;
97
+ complemento: string;
98
+ }>;
99
+ erros: Array<{
100
+ codigo: string;
101
+ descricao: string;
102
+ complemento: string;
103
+ }>;
104
+ documentos: Array<{
105
+ nsu: string;
106
+ chaveAcesso: string;
107
+ tipoDocumento: string;
108
+ tipoEvento: string;
109
+ dataHoraGeracao: string;
110
+ papel: "" | "tomador" | "prestador" | "intermediario";
111
+ xml: string;
112
+ json: any;
113
+ }>;
114
+ documentosErros: Array<{
115
+ nsu: string;
116
+ chaveAcesso: string;
117
+ tipoDocumento: string;
118
+ error: string;
119
+ }>;
120
+ };
121
+ error?: string;
122
+ transportError?: {
123
+ code: string;
124
+ message: string;
125
+ };
126
+ resBody: string;
127
+ status: number;
128
+ }>;
129
+ /**
130
+ * Até 50 documentos depois do NSU informado. Começa em `'0'`; o pedido
131
+ * seguinte usa o último NSU recebido. Fila vazia é `status` 404, sem `error`.
132
+ *
133
+ * @param {string | number} ultNSU
134
+ */
135
+ consultaUltNSU(ultNSU: string | number): Promise<{
136
+ data: {
137
+ statusProcessamento: string;
138
+ tipoAmbiente: string;
139
+ versaoAplicativo: string;
140
+ dataHoraProcessamento: string;
141
+ alertas: Array<{
142
+ codigo: string;
143
+ descricao: string;
144
+ complemento: string;
145
+ }>;
146
+ erros: Array<{
147
+ codigo: string;
148
+ descricao: string;
149
+ complemento: string;
150
+ }>;
151
+ documentos: Array<{
152
+ nsu: string;
153
+ chaveAcesso: string;
154
+ tipoDocumento: string;
155
+ tipoEvento: string;
156
+ dataHoraGeracao: string;
157
+ papel: "" | "tomador" | "prestador" | "intermediario";
158
+ xml: string;
159
+ json: any;
160
+ }>;
161
+ documentosErros: Array<{
162
+ nsu: string;
163
+ chaveAcesso: string;
164
+ tipoDocumento: string;
165
+ error: string;
166
+ }>;
167
+ };
168
+ error?: string;
169
+ transportError?: {
170
+ code: string;
171
+ message: string;
172
+ };
173
+ resBody: string;
174
+ status: number;
175
+ }>;
176
+ }
@@ -0,0 +1,130 @@
1
+ export = ManifestacaoNFSe;
2
+ declare class ManifestacaoNFSe {
3
+ /**
4
+ * Manifestação do tomador da NFS-e Nacional (Confirmação e Rejeição), na
5
+ * SEFIN Nacional. Cada chamada registra um evento fiscal: em produção
6
+ * (`tpAmb` `'1'`), ele não se desfaz.
7
+ *
8
+ * @param {Object} config
9
+ * @param {Buffer} [config.pfx]
10
+ * @param {string} [config.passphrase]
11
+ * @param {Buffer | string} [config.cert]
12
+ * @param {Buffer | string} [config.key]
13
+ * @param {string} config.cnpj CNPJ do tomador, enviado como `CNPJAutor`. Precisa ter a mesma raiz do CNPJ do certificado.
14
+ * @param {'1' | '2'} config.tpAmb `'1'` produção, `'2'` produção restrita.
15
+ * @param {'America/Araguaina' | 'America/Bahia' | 'America/Belem' | 'America/Boa_Vista' | 'America/Campo_Grande' | 'America/Cuiaba' | 'America/Fortaleza' | 'America/Maceio' | 'America/Manaus' | 'America/Noronha' | 'America/Porto_Velho' | 'America/Recife' | 'America/Rio_Branco' | 'America/Sao_Paulo'} [config.timezone = 'America/Sao_Paulo']
16
+ * @param {Object} [config.options]
17
+ * @param {import('axios').AxiosRequestConfig} [config.options.requestOptions]
18
+ * @param {import('https').AgentOptions} [config.options.httpsOptions] Mesclado por cima do padrão `{ ca: CA_PADRAO, rejectUnauthorized: true }`. Informar `ca` substitui a cadeia inteira; para somar uma AC, use `[...CA_PADRAO, minhaCa]`.
19
+ */
20
+ constructor(config: {
21
+ pfx?: Buffer;
22
+ passphrase?: string;
23
+ cert?: Buffer | string;
24
+ key?: Buffer | string;
25
+ cnpj: string;
26
+ tpAmb: "1" | "2";
27
+ timezone?: "America/Araguaina" | "America/Bahia" | "America/Belem" | "America/Boa_Vista" | "America/Campo_Grande" | "America/Cuiaba" | "America/Fortaleza" | "America/Maceio" | "America/Manaus" | "America/Noronha" | "America/Porto_Velho" | "America/Recife" | "America/Rio_Branco" | "America/Sao_Paulo";
28
+ options?: {
29
+ requestOptions?: import("axios").AxiosRequestConfig;
30
+ httpsOptions?: import("https").AgentOptions;
31
+ };
32
+ });
33
+ config: Readonly<{
34
+ cnpj: string;
35
+ tpAmb: "1" | "2";
36
+ timezone: "America/Araguaina" | "America/Bahia" | "America/Belem" | "America/Boa_Vista" | "America/Campo_Grande" | "America/Cuiaba" | "America/Fortaleza" | "America/Maceio" | "America/Manaus" | "America/Noronha" | "America/Porto_Velho" | "America/Recife" | "America/Rio_Branco" | "America/Sao_Paulo";
37
+ cert: string;
38
+ key: string;
39
+ requestOptions: Readonly<import("axios").AxiosRequestConfig<any, any>>;
40
+ httpsOptions: Readonly<import("node:https").AgentOptions>;
41
+ }>;
42
+ /**
43
+ * Confirmação do tomador (`e203202`). Depois dela, o prestador não
44
+ * consegue mais cancelar a NFS-e, só substituí-la.
45
+ *
46
+ * @param {string} chNFSe Chave de acesso de 50 dígitos.
47
+ */
48
+ confirmar(chNFSe: string): Promise<{
49
+ data: {
50
+ tipoAmbiente: string;
51
+ versaoAplicativo: string;
52
+ dataHoraProcessamento: string;
53
+ alertas: Array<{
54
+ codigo: string;
55
+ descricao: string;
56
+ complemento: string;
57
+ }>;
58
+ erros: Array<{
59
+ codigo: string;
60
+ descricao: string;
61
+ complemento: string;
62
+ }>;
63
+ evento: {
64
+ xml?: string;
65
+ json?: any;
66
+ };
67
+ };
68
+ error?: string;
69
+ transportError?: {
70
+ code: string;
71
+ message: string;
72
+ };
73
+ reqXml: string;
74
+ resBody: string;
75
+ status: number;
76
+ }>;
77
+ /**
78
+ * Rejeição do tomador (`e203206`).
79
+ *
80
+ * @param {string} chNFSe Chave de acesso de 50 dígitos.
81
+ * @param {Object} motivo
82
+ * @param {MotivoRejeicaoNFSe} motivo.cMotivo
83
+ * @param {string} [motivo.xMotivo] De 15 a 255 caracteres; obrigatório com `cMotivo` `'9'`.
84
+ */
85
+ rejeitar(chNFSe: string, motivo: {
86
+ cMotivo: MotivoRejeicaoNFSe;
87
+ xMotivo?: string;
88
+ }): Promise<{
89
+ data: {
90
+ tipoAmbiente: string;
91
+ versaoAplicativo: string;
92
+ dataHoraProcessamento: string;
93
+ alertas: Array<{
94
+ codigo: string;
95
+ descricao: string;
96
+ complemento: string;
97
+ }>;
98
+ erros: Array<{
99
+ codigo: string;
100
+ descricao: string;
101
+ complemento: string;
102
+ }>;
103
+ evento: {
104
+ xml?: string;
105
+ json?: any;
106
+ };
107
+ };
108
+ error?: string;
109
+ transportError?: {
110
+ code: string;
111
+ message: string;
112
+ };
113
+ reqXml: string;
114
+ resBody: string;
115
+ status: number;
116
+ }>;
117
+ }
118
+ declare namespace ManifestacaoNFSe {
119
+ export { MotivoRejeicaoNFSe };
120
+ }
121
+ /**
122
+ * Motivo da Rejeição do tomador (`cMotivo`):
123
+ * - `'1'` NFS-e em duplicidade;
124
+ * - `'2'` NFS-e já emitida pelo tomador;
125
+ * - `'3'` não ocorrência do fato gerador;
126
+ * - `'4'` erro quanto à responsabilidade tributária;
127
+ * - `'5'` erro quanto ao valor do serviço, valor das deduções, serviço prestado ou data do fato gerador;
128
+ * - `'9'` outros — exige `xMotivo`.
129
+ */
130
+ type MotivoRejeicaoNFSe = "1" | "2" | "3" | "4" | "5" | "9";
@@ -0,0 +1,50 @@
1
+ declare const _exports: typeof DistribuicaoNFSeController;
2
+ export = _exports;
3
+ declare class DistribuicaoNFSeController {
4
+ /**
5
+ *
6
+ * @param {Object} opts
7
+ * @returns {Promise<{data:{statusProcessamento: string,tipoAmbiente: string,versaoAplicativo: string,dataHoraProcessamento: string, alertas: Array<{codigo: string,descricao: string,complemento: string}>, erros: Array<{codigo: string,descricao: string,complemento: string}>, documentos: Array<{nsu: string,chaveAcesso: string,tipoDocumento: string,tipoEvento: string,dataHoraGeracao: string,papel: '' | 'tomador' | 'prestador' | 'intermediario',xml: string,json: Object}>, documentosErros: Array<{nsu: string,chaveAcesso: string,tipoDocumento: string,error: string}>}, error?: string, transportError?: {code: string, message: string}, resBody: string, status: number}>}
8
+ */
9
+ static enviar(opts: any): Promise<{
10
+ data: {
11
+ statusProcessamento: string;
12
+ tipoAmbiente: string;
13
+ versaoAplicativo: string;
14
+ dataHoraProcessamento: string;
15
+ alertas: Array<{
16
+ codigo: string;
17
+ descricao: string;
18
+ complemento: string;
19
+ }>;
20
+ erros: Array<{
21
+ codigo: string;
22
+ descricao: string;
23
+ complemento: string;
24
+ }>;
25
+ documentos: Array<{
26
+ nsu: string;
27
+ chaveAcesso: string;
28
+ tipoDocumento: string;
29
+ tipoEvento: string;
30
+ dataHoraGeracao: string;
31
+ papel: "" | "tomador" | "prestador" | "intermediario";
32
+ xml: string;
33
+ json: any;
34
+ }>;
35
+ documentosErros: Array<{
36
+ nsu: string;
37
+ chaveAcesso: string;
38
+ tipoDocumento: string;
39
+ error: string;
40
+ }>;
41
+ };
42
+ error?: string;
43
+ transportError?: {
44
+ code: string;
45
+ message: string;
46
+ };
47
+ resBody: string;
48
+ status: number;
49
+ }>;
50
+ }