@sinete/nfe 0.1.0 → 0.2.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 (87) hide show
  1. package/README.md +49 -48
  2. package/dist/build/build.d.ts +23 -23
  3. package/dist/build/build.d.ts.map +1 -1
  4. package/dist/build/destinatario.d.ts +2 -2
  5. package/dist/build/ibscbs.d.ts.map +1 -1
  6. package/dist/build/nfce.d.ts +5 -5
  7. package/dist/build/nfce.d.ts.map +1 -1
  8. package/dist/build/pl.d.ts +4 -4
  9. package/dist/build/pl.d.ts.map +1 -1
  10. package/dist/build/rejeicoes.d.ts +7 -7
  11. package/dist/build/rejeicoes.d.ts.map +1 -1
  12. package/dist/build/values.d.ts +8 -8
  13. package/dist/build/values.d.ts.map +1 -1
  14. package/dist/decimal.d.ts +2 -2
  15. package/dist/decimal.d.ts.map +1 -1
  16. package/dist/format.d.ts +19 -19
  17. package/dist/format.d.ts.map +1 -1
  18. package/dist/ibs-cbs.d.ts +5 -5
  19. package/dist/ibs-cbs.d.ts.map +1 -1
  20. package/dist/ibs-cbs.js +35 -35
  21. package/dist/ibs-cbs.js.map +2 -2
  22. package/dist/index.d.ts +18 -18
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +602 -573
  25. package/dist/index.js.map +25 -25
  26. package/dist/issues.d.ts +10 -10
  27. package/dist/issues.d.ts.map +1 -1
  28. package/dist/model.d.ts +4 -4
  29. package/dist/ports.d.ts +15 -15
  30. package/dist/ports.d.ts.map +1 -1
  31. package/dist/rotulo.d.ts +2 -2
  32. package/dist/rotulo.d.ts.map +1 -1
  33. package/dist/rtc.d.ts +23 -23
  34. package/dist/rtc.d.ts.map +1 -1
  35. package/dist/services/client.d.ts +44 -40
  36. package/dist/services/client.d.ts.map +1 -1
  37. package/dist/services/gzip.d.ts +1 -1
  38. package/dist/services/gzip.d.ts.map +1 -1
  39. package/dist/services/index.d.ts +4 -4
  40. package/dist/services/index.d.ts.map +1 -1
  41. package/dist/services/outcome.d.ts +3 -3
  42. package/dist/services/outcome.d.ts.map +1 -1
  43. package/dist/services/proc.d.ts +6 -6
  44. package/dist/services/proc.d.ts.map +1 -1
  45. package/dist/services/recuperar.d.ts +7 -7
  46. package/dist/services/recuperar.d.ts.map +1 -1
  47. package/dist/services/resolver.d.ts +10 -10
  48. package/dist/services/resolver.d.ts.map +1 -1
  49. package/dist/services/soap.d.ts +8 -8
  50. package/dist/services/soap.d.ts.map +1 -1
  51. package/dist/time.d.ts +4 -4
  52. package/dist/time.d.ts.map +1 -1
  53. package/dist/versao-gerada.d.ts +1 -1
  54. package/package.json +9 -9
  55. package/src/build/build.ts +202 -185
  56. package/src/build/destinatario.ts +8 -8
  57. package/src/build/ibscbs.ts +10 -6
  58. package/src/build/icms.ts +6 -6
  59. package/src/build/nfce.ts +19 -19
  60. package/src/build/pl.ts +12 -12
  61. package/src/build/rejeicoes.ts +13 -13
  62. package/src/build/values.ts +14 -14
  63. package/src/data/arredondamento.json +1 -1
  64. package/src/data/cstat.json +6 -6
  65. package/src/data/fusos.json +1 -1
  66. package/src/data/nfce-urls.json +2 -2
  67. package/src/data/produtor-rural.json +1 -1
  68. package/src/data/resp-tec.json +1 -1
  69. package/src/decimal.ts +10 -9
  70. package/src/format.ts +29 -28
  71. package/src/ibs-cbs.ts +74 -74
  72. package/src/index.ts +29 -27
  73. package/src/issues.ts +11 -11
  74. package/src/model.ts +4 -4
  75. package/src/ports.ts +15 -15
  76. package/src/rotulo.ts +4 -4
  77. package/src/rtc.ts +91 -91
  78. package/src/services/client.ts +248 -217
  79. package/src/services/gzip.ts +7 -7
  80. package/src/services/index.ts +12 -10
  81. package/src/services/outcome.ts +9 -9
  82. package/src/services/proc.ts +50 -49
  83. package/src/services/recuperar.ts +27 -25
  84. package/src/services/resolver.ts +22 -22
  85. package/src/services/soap.ts +35 -35
  86. package/src/time.ts +6 -6
  87. package/src/versao-gerada.ts +1 -1
@@ -5,11 +5,11 @@
5
5
  * critério da UF (E16a-30 e E16a-35, E14-10 e E14-20, facultativas). As regras só da NFC-e estão em `nfce.ts`.
6
6
  */
7
7
 
8
- import { ufBySigla } from '@sinete/core';
9
- import { parseCnpj } from '@sinete/validators';
8
+ import { ufPorSigla } from '@sinete/core';
9
+ import { lerCnpj } from '@sinete/validators';
10
10
  import suframa from '../data/suframa.json' with { type: 'json' };
11
11
  import type { Issues } from '../issues.ts';
12
- import type { NfeInput } from '../model.ts';
12
+ import type { DadosNfe } from '../model.ts';
13
13
 
14
14
  /** Identificação já resolvida pelo montador (com os padrões aplicados). */
15
15
  export interface IdeDestinatario {
@@ -26,7 +26,7 @@ const ID_ESTRANGEIRO = /^[0-9A-Za-z:.+\-/()]*$/;
26
26
 
27
27
  const digitos = (s: string): string => s.replace(/\D/g, '');
28
28
 
29
- export function conferirDestinatario(input: NfeInput, ide: IdeDestinatario, issues: Issues): void {
29
+ export function conferirDestinatario(input: DadosNfe, ide: IdeDestinatario, issues: Issues): void {
30
30
  const d = input.destinatario;
31
31
  if (d === undefined) return;
32
32
  const nfe = ide.mod === '55';
@@ -85,7 +85,7 @@ export function conferirDestinatario(input: NfeInput, ide: IdeDestinatario, issu
85
85
  // E10-20: o município do destinatário é da UF dele (as duas primeiras posições do código são o código da UF).
86
86
  if (end !== undefined && !exterior) {
87
87
  const e = end as { UF: string; cMun: string };
88
- const cUF = ufBySigla(e.UF)?.cUF;
88
+ const cUF = ufPorSigla(e.UF)?.cUF;
89
89
  if (cUF !== undefined && digitos(e.cMun).slice(0, 2) !== cUF) {
90
90
  issues.add(
91
91
  'destinatario.endereco.cMun',
@@ -108,11 +108,11 @@ export function conferirDestinatario(input: NfeInput, ide: IdeDestinatario, issu
108
108
  // Na saída a entrega vale contra o emitente e a retirada contra o destinatário; na entrada, ao contrário.
109
109
  const [ufEntrega, ufRetirada] = saida ? [emitUf, destUf] : [destUf, emitUf];
110
110
  if (ide.idDest === '2' && destUf === emitUf) {
111
- // CNPJ alfanumérico: as letras contam; só a máscara sai (a mesma normalização do parseCnpj).
111
+ // CNPJ alfanumérico: as letras contam; só a máscara sai (a mesma normalização do lerCnpj).
112
112
  const cnpj = (v: string | undefined): string | undefined => {
113
113
  if (v === undefined) return undefined;
114
- const r = parseCnpj(v);
115
- return r.ok ? r.value : undefined;
114
+ const r = lerCnpj(v);
115
+ return r.ok ? r.valor : undefined;
116
116
  };
117
117
  const cnpjDest = 'CNPJ' in d ? cnpj(d.CNPJ) : undefined;
118
118
  const mesmoCnpj = cnpjDest !== undefined && cnpjDest === cnpj(input.emitente.CNPJ);
@@ -3,9 +3,9 @@
3
3
  * `IBSCBSTot` (W34 a W59g) somando exatamente os valores dos itens.
4
4
  */
5
5
 
6
- import { firstChild, parseXml } from '@sinete/core/xml';
6
+ import { lerXml, primeiroFilho } from '@sinete/core/xml';
7
7
  import type { ComplexType } from '@sinete/schemas';
8
- import { serialize, validate } from '@sinete/schemas';
8
+ import { serializar, validar } from '@sinete/schemas';
9
9
  import type { TIBSCBSMonoTot, TTribNFe } from '@sinete/schemas/nfe/PL_010f';
10
10
  import { Decimal } from '../decimal.ts';
11
11
  import type { Issues } from '../issues.ts';
@@ -19,12 +19,16 @@ const NFE_NS = 'http://www.portalfiscal.inf.br/nfe';
19
19
  export function grupoInvalido(ct: ComplexType, nome: string, value: unknown, path: string, issues: Issues): boolean {
20
20
  if (value === undefined) return false;
21
21
  try {
22
- const doc = parseXml(`<w xmlns="${NFE_NS}">${serialize(ct, nome, value as never, NFE_NS)}</w>`);
23
- const el = firstChild(doc.root, nome, NFE_NS);
22
+ const doc = lerXml(`<w xmlns="${NFE_NS}">${serializar(ct, nome, value as never, NFE_NS)}</w>`);
23
+ const el = primeiroFilho(doc.raiz, nome, NFE_NS);
24
24
  const erros =
25
- el === undefined ? [{ path: '', code: 'modelo_de_conteudo', message: 'grupo vazio' }] : validate(ct, el);
25
+ el === undefined ? [{ caminho: '', code: 'modelo_de_conteudo', mensagem: 'grupo vazio' }] : validar(ct, el);
26
26
  for (const e of erros)
27
- issues.add(`${path}${e.path.replace(/^\/[^/]+/, '').replace(/\//g, '.')}`, 'schema', `${e.code}: ${e.message}`);
27
+ issues.add(
28
+ `${path}${e.caminho.replace(/^\/[^/]+/, '').replace(/\//g, '.')}`,
29
+ 'schema',
30
+ `${e.code}: ${e.mensagem}`,
31
+ );
28
32
  return erros.length > 0;
29
33
  } catch (e) {
30
34
  issues.add(path, 'schema', `grupo malformado: ${(e as Error).message}`);
package/src/build/icms.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  import type { TNFe_infNFe_det_imposto_ICMS } from '@sinete/schemas/nfe/PL_010f';
7
7
  import type { DecimalInput } from '../decimal.ts';
8
8
  import { Decimal } from '../decimal.ts';
9
- import type { DecimalFormat } from '../format.ts';
9
+ import type { FormatoDecimal } from '../format.ts';
10
10
  import { D0302A04, D0302A04_OPC, D1104V, D1302 } from '../format.ts';
11
11
  import type {
12
12
  Desoneracao,
@@ -74,9 +74,9 @@ export interface IcmsItemBase {
74
74
 
75
75
  type Out = Record<string, string | undefined>;
76
76
 
77
- const M: DecimalFormat = D1302;
78
- const P: DecimalFormat = D0302A04;
79
- const PO: DecimalFormat = D0302A04_OPC;
77
+ const M: FormatoDecimal = D1302;
78
+ const P: FormatoDecimal = D0302A04;
79
+ const PO: FormatoDecimal = D0302A04_OPC;
80
80
 
81
81
  function nonNeg(d: Decimal): Decimal {
82
82
  return d.isNegative() ? Decimal.ZERO : d;
@@ -221,7 +221,7 @@ export function buildIcms(
221
221
 
222
222
  /** Retido anteriormente (N26 a N26b, N27a a N27c, N34 a N37): informativo, só o FCP retido entra no total. */
223
223
  const retido = (i: IcmsStRetido): Out => {
224
- const o = (k: keyof IcmsStRetido, f: DecimalFormat): string | undefined =>
224
+ const o = (k: keyof IcmsStRetido, f: FormatoDecimal): string | undefined =>
225
225
  ctx.so(ctx.opt(i[k], `${path}.${k}`, f), f);
226
226
  const vFCPSTRet = ctx.opt(i.vFCPSTRet, `${path}.vFCPSTRet`, M);
227
227
  if (vFCPSTRet !== undefined) t.vFCPSTRet = t.vFCPSTRet.plus(vFCPSTRet);
@@ -259,7 +259,7 @@ export function buildIcms(
259
259
  vFCPDif?: DecimalInput;
260
260
  vFCPEfet?: DecimalInput;
261
261
  },
262
- redFormat: DecimalFormat,
262
+ redFormat: FormatoDecimal,
263
263
  triade: boolean,
264
264
  ): Out => {
265
265
  const pRedBC = ctx.opt(i.pRedBC, `${path}.pRedBC`, redFormat);
package/src/build/nfce.ts CHANGED
@@ -7,14 +7,14 @@
7
7
  * UF decide, e recusar localmente bloquearia a venda onde a regra não vale.
8
8
  */
9
9
 
10
- import type { Ambiente, Signer, Uf } from '@sinete/core';
11
- import { tpAmbOf } from '@sinete/core';
12
- import { base64Encode, c14n, firstChild, parseXml, SHA1_DIGEST_INFO_PREFIX } from '@sinete/core/xml';
13
- import { parseCnpj } from '@sinete/validators';
10
+ import type { Ambiente, Assinador, Uf } from '@sinete/core';
11
+ import { tpAmbDoAmbiente } from '@sinete/core';
12
+ import { c14n, codificarBase64, lerXml, PREFIXO_DIGEST_INFO_SHA1, primeiroFilho } from '@sinete/core/xml';
13
+ import { lerCnpj } from '@sinete/validators';
14
14
  import urls from '../data/nfce-urls.json' with { type: 'json' };
15
15
  import { Decimal } from '../decimal.ts';
16
16
  import type { Issues } from '../issues.ts';
17
- import type { NfeInput } from '../model.ts';
17
+ import type { DadosNfe } from '../model.ts';
18
18
 
19
19
  /**
20
20
  * Versão do QR Code da NFC-e. A 3 (padrão) dispensa o CSC: na emissão normal leva só chave, versão e ambiente, e na
@@ -60,7 +60,7 @@ function vigente(lista: readonly Entrada[] | undefined, dia: string): Entrada |
60
60
  /**
61
61
  * Endereços da NFC-e da UF no ambiente e no dia (`data/nfce-urls.json`, das tabelas do Portal Nacional da NFC-e). O
62
62
  * `qrCode` é `undefined` onde a tabela não traz o endereço completo (AM e MA publicam sem o protocolo): informe
63
- * `BuildNfeOptions.urlQrCode`.
63
+ * `MontarNfeOpcoes.urlQrCode`.
64
64
  */
65
65
  export function urlsNfce(
66
66
  uf: Uf,
@@ -87,10 +87,10 @@ const hex = (b: Uint8Array): string => Array.from(b, (x) => x.toString(16).padSt
87
87
  * `797a4759...`).
88
88
  */
89
89
  async function digValHex(xml: string): Promise<string> {
90
- const doc = parseXml(xml);
91
- const inf = firstChild(doc.root, 'infNFe', NFE_NS);
90
+ const doc = lerXml(xml);
91
+ const inf = primeiroFilho(doc.raiz, 'infNFe', NFE_NS);
92
92
  if (inf === undefined) throw new Error('NF-e montada sem infNFe');
93
- return hexDoDigestValue(base64Encode(await sha1(te.encode(c14n(inf)))));
93
+ return hexDoDigestValue(codificarBase64(await sha1(te.encode(c14n(inf)))));
94
94
  }
95
95
 
96
96
  /** O DigestValue em Base64 convertido para hexadecimal, caractere a caractere (Manual 6.0, 4.3.5, passo 1). */
@@ -128,7 +128,7 @@ export async function parametrosQrCode(
128
128
  d: DadosQrCode,
129
129
  qr: QrCodeNfceOpcoes,
130
130
  ): Promise<{ readonly parametros: string; readonly assinar: boolean }> {
131
- const tpAmb = tpAmbOf(d.ambiente);
131
+ const tpAmb = tpAmbDoAmbiente(d.ambiente);
132
132
  const offline = d.tpEmis === '9';
133
133
  const dia = d.dhEmi.slice(8, 10);
134
134
  if (qr.versao === '3') {
@@ -155,14 +155,14 @@ export async function parametrosQrCode(
155
155
  * Assinatura dos parâmetros 1 a 7 do QR Code versão 3 off-line: RSA com SHA-1 (PKCS#1 v1.5), em Base64, com o mesmo
156
156
  * certificado que assina a NFC-e (Manual 6.0, 4.4.2, parâmetro 8).
157
157
  */
158
- export async function assinarParametros(parametros: string, signer: Signer): Promise<string> {
158
+ export async function assinarParametros(parametros: string, signer: Assinador): Promise<string> {
159
159
  const bytes = te.encode(parametros);
160
- if (signer.kind === 'data') return base64Encode(await signer.sign(bytes, 'SHA-1'));
160
+ if (signer.tipo === 'dados') return codificarBase64(await signer.assinar(bytes, 'SHA-1'));
161
161
  const h = await sha1(bytes);
162
- const di = new Uint8Array(SHA1_DIGEST_INFO_PREFIX.length + h.length);
163
- di.set(SHA1_DIGEST_INFO_PREFIX);
164
- di.set(h, SHA1_DIGEST_INFO_PREFIX.length);
165
- return base64Encode(await signer.signDigestInfo(di));
162
+ const di = new Uint8Array(PREFIXO_DIGEST_INFO_SHA1.length + h.length);
163
+ di.set(PREFIXO_DIGEST_INFO_SHA1);
164
+ di.set(h, PREFIXO_DIGEST_INFO_SHA1.length);
165
+ return codificarBase64(await signer.assinarDigestInfo(di));
166
166
  }
167
167
 
168
168
  /** CFOP da prestação de serviço tributada pelo ISSQN na NFC-e (MOC 7.0 Anexo I, RV I08-150 a I08-170). */
@@ -192,7 +192,7 @@ export interface IdeNfce {
192
192
  * Regras da NFC-e sobre a entrada (origem `entrada`, ADR 0011), todas de aplicação obrigatória. Grupos que a NFC-e
193
193
  * não tem saem como `grupo_vedado` no caminho da entrada.
194
194
  */
195
- export function conferirNfce(input: NfeInput, ide: IdeNfce, issues: Issues): void {
195
+ export function conferirNfce(input: DadosNfe, ide: IdeNfce, issues: Issues): void {
196
196
  const vedado = (path: string, msg: string): void => issues.add(path, 'grupo_vedado', `NFC-e ${msg}`);
197
197
  const invalido = (path: string, msg: string): void => issues.add(path, 'campo_invalido', `NFC-e ${msg}`);
198
198
 
@@ -416,8 +416,8 @@ export function pagamentoNfce(pag: PagMontado, vNF: Decimal, issues: Issues): vo
416
416
  );
417
417
  }
418
418
  if (c.CNPJ !== undefined) {
419
- const r = parseCnpj(c.CNPJ, { path: `${path}.CNPJ` });
420
- if (!r.ok) issues.list.push(r.error);
419
+ const r = lerCnpj(c.CNPJ, { caminho: `${path}.CNPJ` });
420
+ if (!r.ok) issues.list.push(r.erro);
421
421
  }
422
422
  });
423
423
  }
package/src/build/pl.ts CHANGED
@@ -6,10 +6,10 @@
6
6
  * `camposForaDoPl` aponta cada campo que sumiria em silêncio.
7
7
  */
8
8
 
9
- import type { Ambiente, Clock } from '@sinete/core';
10
- import { UnsupportedError } from '@sinete/core';
11
- import type { ComplexType, Particle, VigenciaEntry } from '@sinete/schemas';
12
- import { isComplexType, isElementParticle, isWildcard, selecionarPl } from '@sinete/schemas';
9
+ import type { Ambiente, Relogio } from '@sinete/core';
10
+ import { ErroNaoSuportado } from '@sinete/core';
11
+ import type { ComplexType, EntradaDeVigencia, Particle } from '@sinete/schemas';
12
+ import { ehComplexType, ehElementParticle, ehWildcard, selecionarPl } from '@sinete/schemas';
13
13
  import { TNFe_infNFe as Inf010e } from '@sinete/schemas/nfe/PL_010e';
14
14
  import { TNFe_infNFe as Inf010f } from '@sinete/schemas/nfe/PL_010f';
15
15
  import type { Issues } from '../issues.ts';
@@ -21,32 +21,32 @@ const INF_NFE: Readonly<Record<string, ComplexType>> = {
21
21
  };
22
22
 
23
23
  export interface PlEscolhido {
24
- readonly vigencia: VigenciaEntry;
24
+ readonly vigencia: EntradaDeVigencia;
25
25
  readonly infNFe: ComplexType;
26
26
  }
27
27
 
28
28
  /** PL da NF-e vigente para o ambiente no relógio de emissão (ADR 0002, decisão 6). */
29
- export function escolherPl(ambiente: Ambiente, emissao: Clock): PlEscolhido {
29
+ export function escolherPl(ambiente: Ambiente, emissao: Relogio): PlEscolhido {
30
30
  const vigencia = selecionarPl('nfe', ambiente, emissao);
31
31
  const infNFe = INF_NFE[vigencia.modulo];
32
32
  if (infNFe === undefined) {
33
33
  // A tabela de vigências ganhou um PL que este pacote ainda não conhece: falha explícita, nunca tentativa.
34
- throw new UnsupportedError(`@sinete/nfe não conhece o módulo ${vigencia.modulo}; atualize o pacote`);
34
+ throw new ErroNaoSuportado(`@sinete/nfe não conhece o módulo ${vigencia.modulo}; atualize o pacote`);
35
35
  }
36
36
  return { vigencia, infNFe };
37
37
  }
38
38
 
39
39
  function memberNames(p: Particle, acc: Set<string>): Set<string> {
40
- if (isWildcard(p)) acc.add('$any');
41
- else if (isElementParticle(p)) acc.add(p.e);
40
+ if (ehWildcard(p)) acc.add('$any');
41
+ else if (ehElementParticle(p)) acc.add(p.e);
42
42
  else for (const i of p.i) memberNames(i, acc);
43
43
  return acc;
44
44
  }
45
45
 
46
46
  function elementTypes(p: Particle, acc: Map<string, ComplexType>): Map<string, ComplexType> {
47
- if (isWildcard(p)) return acc;
48
- if (isElementParticle(p)) {
49
- if (isComplexType(p.t)) acc.set(p.e, p.t);
47
+ if (ehWildcard(p)) return acc;
48
+ if (ehElementParticle(p)) {
49
+ if (ehComplexType(p.t)) acc.set(p.e, p.t);
50
50
  } else for (const i of p.i) elementTypes(i, acc);
51
51
  return acc;
52
52
  }
@@ -7,11 +7,11 @@
7
7
  * dúvida, a nota vai e a SEFAZ decide, porque recusar localmente uma nota que a SEFAZ aceitaria é pior que a rejeição.
8
8
  */
9
9
 
10
- import type { ValidationIssue } from '@sinete/core';
10
+ import type { Ocorrencia } from '@sinete/core';
11
11
  import type { Issues } from '../issues.ts';
12
- import type { NfeInput } from '../model.ts';
12
+ import type { DadosNfe } from '../model.ts';
13
13
  import type { Instante } from '../time.ts';
14
- import { formatDh } from '../time.ts';
14
+ import { formatarDh } from '../time.ts';
15
15
 
16
16
  /**
17
17
  * CFOP em que o CST 50 (suspensão) é aceito com destinatário contribuinte isento: conserto ou reparo e remessa para
@@ -40,7 +40,7 @@ const CFOP_CST50_ISENTO: ReadonlySet<string> = new Set([
40
40
  * recusado aqui, seja qual for o documento do destinatário. A exceção 2 (emissão antes de 01/07/2016) não alcança nota
41
41
  * montada agora.
42
42
  */
43
- export function cstComIsento(input: NfeInput, idDest: string, issues: Issues): void {
43
+ export function cstComIsento(input: DadosNfe, idDest: string, issues: Issues): void {
44
44
  const d = input.destinatario;
45
45
  if (d?.indIEDest !== '2') return;
46
46
  input.itens.forEach((it, n) => {
@@ -60,7 +60,7 @@ export function cstComIsento(input: NfeInput, idDest: string, issues: Issues): v
60
60
 
61
61
  /** Data (`AAAA-MM-DD`) do instante no deslocamento dado, em minutos. */
62
62
  const dataNoFuso = (instante: Instante, offsetMinutes: number): string =>
63
- formatDh(instante, offsetMinutes).slice(0, 10);
63
+ formatarDh(instante, offsetMinutes).slice(0, 10);
64
64
 
65
65
  /** Brasília (UTC-3), o fuso das SEFAZ, para a comparação de datas que o MOC faz "desconsiderando a hora". */
66
66
  const OFFSET_BRASILIA = -180;
@@ -78,7 +78,7 @@ const OFFSET_BRASILIA = -180;
78
78
  * "na data da emissão" só vale quando a data local do `dhEmi`, a de Brasília e a de UTC coincidem.
79
79
  */
80
80
  export function vencimentos(
81
- input: NfeInput,
81
+ input: DadosNfe,
82
82
  emissao: { readonly dhEmi: string; readonly instante: Instante },
83
83
  issues: Issues,
84
84
  ): void {
@@ -107,22 +107,22 @@ const soDocumento = (s: string): string => s.replace(/[^0-9A-Za-z]/g, '').toUppe
107
107
  * - e-CPF: o CPF do emitente é o do certificado, senão rejeição 227 (RV F03A).
108
108
  *
109
109
  * Só compara documento com documento do mesmo tipo: e-CNPJ assinando nota de emitente CPF (ou o contrário) fica para a
110
- * SEFAZ. O certificado da SEFAZ que a F03 dispensa só assina a nota avulsa, que o `buildNfe` não monta (procEmi 0).
110
+ * SEFAZ. O certificado da SEFAZ que a F03 dispensa só assina a nota avulsa, que o `montarNfe` não monta (procEmi 0).
111
111
  * Devolve as ocorrências (vazio quando confere), com o caminho da entrada.
112
112
  */
113
113
  export function conferirEmitenteDoCertificado(
114
- nfe: NfeInput,
114
+ nfe: DadosNfe,
115
115
  titular: { readonly cnpj?: string | undefined; readonly cpf?: string | undefined },
116
- ): readonly ValidationIssue[] {
116
+ ): readonly Ocorrencia[] {
117
117
  const e = nfe.emitente;
118
118
  if (e.CNPJ !== undefined && titular.cnpj !== undefined) {
119
119
  const base = soDocumento(e.CNPJ).slice(0, 8);
120
120
  if (base !== soDocumento(titular.cnpj).slice(0, 8)) {
121
121
  return [
122
122
  {
123
- path: 'emitente.CNPJ',
123
+ caminho: 'emitente.CNPJ',
124
124
  code: 'emitente_difere_do_certificado',
125
- message: `CNPJ-base ${base} do emitente difere do CNPJ-base do certificado (F03, rejeição 213)`,
125
+ mensagem: `CNPJ-base ${base} do emitente difere do CNPJ-base do certificado (F03, rejeição 213)`,
126
126
  origem: 'entrada',
127
127
  },
128
128
  ];
@@ -131,9 +131,9 @@ export function conferirEmitenteDoCertificado(
131
131
  if (e.CPF !== undefined && titular.cpf !== undefined && soDocumento(e.CPF) !== soDocumento(titular.cpf)) {
132
132
  return [
133
133
  {
134
- path: 'emitente.CPF',
134
+ caminho: 'emitente.CPF',
135
135
  code: 'emitente_difere_do_certificado',
136
- message: 'CPF do emitente difere do CPF do certificado (F03A, rejeição 227)',
136
+ mensagem: 'CPF do emitente difere do CPF do certificado (F03A, rejeição 227)',
137
137
  origem: 'entrada',
138
138
  },
139
139
  ];
@@ -6,8 +6,8 @@
6
6
 
7
7
  import type { DecimalInput, RoundingMode } from '../decimal.ts';
8
8
  import { Decimal } from '../decimal.ts';
9
- import type { DecimalFormat } from '../format.ts';
10
- import { formatDecimal, formatProblem } from '../format.ts';
9
+ import type { FormatoDecimal } from '../format.ts';
10
+ import { formatarDecimal, problemaDeFormato } from '../format.ts';
11
11
  import type { Issues } from '../issues.ts';
12
12
 
13
13
  /** Famílias de campo com modo de arredondamento próprio (`data/arredondamento.json`). */
@@ -32,23 +32,23 @@ export class Ctx {
32
32
  }
33
33
 
34
34
  /** Entrada opcional; inválida vira ocorrência e `undefined`. */
35
- opt(value: DecimalInput | undefined, path: string, format: DecimalFormat): Decimal | undefined {
35
+ opt(value: DecimalInput | undefined, path: string, format: FormatoDecimal): Decimal | undefined {
36
36
  if (value === undefined) return undefined;
37
37
  const d = Decimal.tryOf(value);
38
38
  if (d === undefined) {
39
39
  this.issues.add(path, 'decimal_invalido', 'número decimal inválido (use ponto como separador, sem milhar)');
40
40
  return undefined;
41
41
  }
42
- const problem = formatProblem(d, format);
42
+ const problem = problemaDeFormato(d, format);
43
43
  if (problem !== undefined) {
44
- this.issues.add(path, 'decimal_invalido', `${problem} (${format.name})`);
44
+ this.issues.add(path, 'decimal_invalido', `${problem} (${format.nome})`);
45
45
  return undefined;
46
46
  }
47
47
  return d;
48
48
  }
49
49
 
50
50
  /** Entrada obrigatória; ausente ou inválida vira ocorrência e zero, para que a montagem siga e junte tudo. */
51
- req(value: DecimalInput | undefined, path: string, format: DecimalFormat): Decimal {
51
+ req(value: DecimalInput | undefined, path: string, format: FormatoDecimal): Decimal {
52
52
  if (value === undefined) {
53
53
  this.issues.add(path, 'campo_obrigatorio', 'campo obrigatório');
54
54
  return Decimal.ZERO;
@@ -57,7 +57,7 @@ export class Ctx {
57
57
  }
58
58
 
59
59
  /** Arredonda um valor calculado no formato do campo pelo modo da família. */
60
- round(value: Decimal, format: DecimalFormat, familia: Familia): Decimal {
60
+ round(value: Decimal, format: FormatoDecimal, familia: Familia): Decimal {
61
61
  return value.round(format.max, this.modes[familia]);
62
62
  }
63
63
 
@@ -73,7 +73,7 @@ export class Ctx {
73
73
  given: DecimalInput | undefined,
74
74
  padrao: Decimal,
75
75
  path: string,
76
- format: DecimalFormat,
76
+ format: FormatoDecimal,
77
77
  familia: Familia,
78
78
  ): Decimal {
79
79
  return this.opt(given, path, format) ?? this.round(padrao, format, familia);
@@ -83,7 +83,7 @@ export class Ctx {
83
83
  given: DecimalInput | undefined,
84
84
  computed: Decimal,
85
85
  path: string,
86
- format: DecimalFormat,
86
+ format: FormatoDecimal,
87
87
  familia: Familia,
88
88
  ): Decimal {
89
89
  const r = this.round(computed, format, familia);
@@ -101,15 +101,15 @@ export class Ctx {
101
101
  }
102
102
 
103
103
  /** Texto no formato do campo. */
104
- s(value: Decimal, format: DecimalFormat): string {
105
- return formatDecimal(value, format);
104
+ s(value: Decimal, format: FormatoDecimal): string {
105
+ return formatarDecimal(value, format);
106
106
  }
107
107
 
108
108
  /** Texto opcional: ausente fica ausente, e zero some nos formatos que não aceitam zero. */
109
- so(value: Decimal | undefined, format: DecimalFormat): string | undefined {
109
+ so(value: Decimal | undefined, format: FormatoDecimal): string | undefined {
110
110
  if (value === undefined) return undefined;
111
- if (format.nonZero && value.isZero()) return undefined;
112
- return formatDecimal(value, format);
111
+ if (format.naoNulo && value.isZero()) return undefined;
112
+ return formatarDecimal(value, format);
113
113
  }
114
114
  }
115
115
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "descricao": "Modo de arredondamento dos valores calculados pelo builder, por família de campo. HALF_UP é o arredondamento comercial; HALF_EVEN é o bancário (NBR 5891). Quem emite pode sobrepor por família em BuildNfeOptions.arredondamento.",
2
+ "descricao": "Modo de arredondamento dos valores calculados pelo builder, por família de campo. HALF_UP é o arredondamento comercial; HALF_EVEN é o bancário (NBR 5891). Quem emite pode sobrepor por família em MontarNfeOpcoes.arredondamento.",
3
3
  "atualizadoEm": "2026-09-26",
4
4
  "familias": {
5
5
  "produto": {
@@ -23,14 +23,14 @@
23
23
  "consumoIndevido": ["656"],
24
24
  "dicas": {
25
25
  "656": {
26
- "probableCause": "Consumo indevido: consultas repetidas à Distribuição DF-e sem documento novo (ultNSU igual a maxNSU) ou acima do limite de requisições por hora.",
27
- "suggestedFix": "Aguardar uma hora antes de consultar de novo e só repetir a consulta quando ultNSU for menor que maxNSU; guardar o ultNSU devolvido e continuar dele.",
28
- "source": "NT 2014.002 v1.02 (Distribuição DF-e), regras de consumo indevido"
26
+ "causaProvavel": "Consumo indevido: consultas repetidas à Distribuição DF-e sem documento novo (ultNSU igual a maxNSU) ou acima do limite de requisições por hora.",
27
+ "comoCorrigir": "Aguardar uma hora antes de consultar de novo e só repetir a consulta quando ultNSU for menor que maxNSU; guardar o ultNSU devolvido e continuar dele.",
28
+ "fonte": "NT 2014.002 v1.02 (Distribuição DF-e), regras de consumo indevido"
29
29
  },
30
30
  "217": {
31
- "probableCause": "A chave consultada não consta na base do autorizador consultado: a nota não foi recebida, ou foi autorizada em outro ambiente ou autorizador (SVC).",
32
- "suggestedFix": "Se o envio ficou sem resposta, reenviar exatamente o mesmo XML assinado; se foi emitida em contingência SVC, consultar no SVC.",
33
- "source": "MOC 7.0 Anexo I, serviço de consulta protocolo"
31
+ "causaProvavel": "A chave consultada não consta na base do autorizador consultado: a nota não foi recebida, ou foi autorizada em outro ambiente ou autorizador (SVC).",
32
+ "comoCorrigir": "Se o envio ficou sem resposta, reenviar exatamente o mesmo XML assinado; se foi emitida em contingência SVC, consultar no SVC.",
33
+ "fonte": "MOC 7.0 Anexo I, serviço de consulta protocolo"
34
34
  }
35
35
  }
36
36
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "descricao": "Deslocamento do horário legal de cada UF em relação a UTC, em minutos, usado para formatar dhEmi, dhSaiEnt, dhCont e dhEvento quando quem emite não informa o fuso. Sem horário de verão desde 2019. UFs com mais de um fuso usam o da capital; o emitente de outro fuso informa offsetMinutes.",
2
+ "descricao": "Deslocamento do horário legal de cada UF em relação a UTC, em minutos, usado para formatar dhEmi, dhSaiEnt, dhCont e dhEvento quando quem emite não informa o fuso. Sem horário de verão desde 2019. UFs com mais de um fuso usam o da capital; o emitente de outro fuso informa deslocamentoMin.",
3
3
  "atualizadoEm": "2026-09-26",
4
4
  "fontes": [
5
5
  "Lei 11.662/2008 e Lei 12.876/2013 (fusos horários do Brasil; Acre em UTC-5)",
@@ -1,10 +1,10 @@
1
1
  {
2
- "descricao": "Endereços da NFC-e (modelo 65) por UF e ambiente: a consulta via QR Code (1ª parte da URL do infNFeSupl/qrCode, sem o '?p=' que o builder acrescenta) e a consulta por chave de acesso (infNFeSupl/urlChave, impressa no DANFC-e). Cada UF tem uma lista de endereços com a data de início ('desde', AAAA-MM-DD, 00:00 de Brasília); vale o mais recente cujo início não passou da data de emissão. As tabelas do ENCAT foram transcritas como publicadas, com três ajustes: o '?' final e o exemplo de parâmetros (AM: '?chNFe=1315....') foram retirados, porque o builder monta a query; o espaço dentro do endereço de homologação do MA ('homolog acao') foi juntado; e o endereço de MG por chave de acesso vem da página de web services da própria SEFAZ-MG, mais nova que a do ENCAT. Onde a tabela não dá o protocolo ('http://' ou 'https://'), a URL do QR Code fica null: o schema exige o protocolo e não há fonte para escolher um; informe BuildNfeOptions.urlQrCode. A regra ZX02-20 (rejeição 395) compara o endereço sem diferenciar maiúsculas e minúsculas; ZX03-20 (rejeição 878) é opcional por UF.",
2
+ "descricao": "Endereços da NFC-e (modelo 65) por UF e ambiente: a consulta via QR Code (1ª parte da URL do infNFeSupl/qrCode, sem o '?p=' que o builder acrescenta) e a consulta por chave de acesso (infNFeSupl/urlChave, impressa no DANFC-e). Cada UF tem uma lista de endereços com a data de início ('desde', AAAA-MM-DD, 00:00 de Brasília); vale o mais recente cujo início não passou da data de emissão. As tabelas do ENCAT foram transcritas como publicadas, com três ajustes: o '?' final e o exemplo de parâmetros (AM: '?chNFe=1315....') foram retirados, porque o builder monta a query; o espaço dentro do endereço de homologação do MA ('homolog acao') foi juntado; e o endereço de MG por chave de acesso vem da página de web services da própria SEFAZ-MG, mais nova que a do ENCAT. Onde a tabela não dá o protocolo ('http://' ou 'https://'), a URL do QR Code fica null: o schema exige o protocolo e não há fonte para escolher um; informe MontarNfeOpcoes.urlQrCode. A regra ZX02-20 (rejeição 395) compara o endereço sem diferenciar maiúsculas e minúsculas; ZX03-20 (rejeição 878) é opcional por UF.",
3
3
  "atualizadoEm": "2026-09-26",
4
4
  "fontes": [
5
5
  "Portal Nacional da NFC-e (ENCAT), URL por UF utilizada QR code: https://nfce.encat.org/desenvolvedor/qrcode/ (coletada em 26/09/2026)",
6
6
  "Portal Nacional da NFC-e (ENCAT), URL por UF utilizada para consulta chave, QR Code versão 2.0: https://nfce.encat.org/desenvolvedor/httpnfce-encat-orgconsulte-sua-nota-qr-code-versao-2-0/ (coletada em 26/09/2026)",
7
- "SEFAZ-MG, web services da NFC-e: https://portalsped.fazenda.mg.gov.br/spedmg/nfce/web-services/ (a mesma fonte do nfceConsultaUrls do @sinete/transport) (coletada em 26/09/2026)",
7
+ "SEFAZ-MG, web services da NFC-e: https://portalsped.fazenda.mg.gov.br/spedmg/nfce/web-services/ (a mesma fonte do urlsConsultaNfce do @sinete/transport) (coletada em 26/09/2026)",
8
8
  "MOC 7.0 Anexo I, regras ZX02-20 e ZX03-20; NT 2025.001 v1.03, grupo ZX (campos ZX02 qrCode e ZX03 urlChave)"
9
9
  ],
10
10
  "qrCode": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "descricao": "Regras do produtor rural que variam no tempo ou por UF. refNFP: a nota fiscal de produtor em papel (modelo 04) foi extinta; uma NF-e ainda pode referenciar uma nota modelo 04 emitida antes do fim da vigência na UF da nota referenciada. vedadaDesde é o primeiro AAMM em que a UF não aceita mais nota modelo 04 emitida; a referência a nota com AAMM igual ou posterior é recusada pelo builder. series: faixas de série por tipo de emitente na emissão pelo contribuinte (procEmi 0, a única que o buildNfe monta).",
2
+ "descricao": "Regras do produtor rural que variam no tempo ou por UF. refNFP: a nota fiscal de produtor em papel (modelo 04) foi extinta; uma NF-e ainda pode referenciar uma nota modelo 04 emitida antes do fim da vigência na UF da nota referenciada. vedadaDesde é o primeiro AAMM em que a UF não aceita mais nota modelo 04 emitida; a referência a nota com AAMM igual ou posterior é recusada pelo builder. series: faixas de série por tipo de emitente na emissão pelo contribuinte (procEmi 0, a única que o montarNfe monta).",
3
3
  "atualizadoEm": "2026-09-28",
4
4
  "refNFP": {
5
5
  "modelo04": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "descricao": "Exigência do grupo infRespTec e do CSRT por UF do emitente, com vigência por ambiente. infRespTec: 'obrigatorio' faz o builder recusar a nota sem responsável técnico; 'opcional' só o inclui quando informado. csrt: 'obrigatorio' exige idCSRT e hashCSRT (rejeição 975). Datas no formato AAAA-MM-DD, a partir de 00:00 de Brasília. UF ausente usa o padrão. A adesão é de cada UF (NT 2018.005); quem emite pode sobrepor em BuildNfeOptions.",
2
+ "descricao": "Exigência do grupo infRespTec e do CSRT por UF do emitente, com vigência por ambiente. infRespTec: 'obrigatorio' faz o builder recusar a nota sem responsável técnico; 'opcional' só o inclui quando informado. csrt: 'obrigatorio' exige idCSRT e hashCSRT (rejeição 975). Datas no formato AAAA-MM-DD, a partir de 00:00 de Brasília. UF ausente usa o padrão. A adesão é de cada UF (NT 2018.005); quem emite pode sobrepor em MontarNfeOpcoes.",
3
3
  "atualizadoEm": "2026-09-26",
4
4
  "fonte": "NT 2018.005 v1.50 (responsável técnico e CSRT, adesão a critério da UF)",
5
5
  "padrao": { "infRespTec": "opcional", "csrt": "opcional" },
package/src/decimal.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  * Qual modo vale para qual campo é dado (`data/arredondamento.json`), não regra espalhada no código.
13
13
  */
14
14
 
15
- import { ConfigError } from '@sinete/core';
15
+ import { ErroDeConfiguracao } from '@sinete/core';
16
16
 
17
17
  export type RoundingMode = 'HALF_EVEN' | 'HALF_UP' | 'DOWN';
18
18
 
@@ -44,7 +44,8 @@ export class Decimal {
44
44
 
45
45
  /** Constrói a partir do coeficiente e da escala (`fromParts(1234n, 2)` = 12.34). */
46
46
  static fromParts(coef: bigint, scale: number): Decimal {
47
- if (!Number.isInteger(scale) || scale < 0 || scale > MAX_SCALE) throw new ConfigError(`escala inválida: ${scale}`);
47
+ if (!Number.isInteger(scale) || scale < 0 || scale > MAX_SCALE)
48
+ throw new ErroDeConfiguracao(`escala inválida: ${scale}`);
48
49
  return new Decimal(coef, scale);
49
50
  }
50
51
 
@@ -54,7 +55,7 @@ export class Decimal {
54
55
  */
55
56
  static of(input: DecimalInput): Decimal {
56
57
  const d = Decimal.tryOf(input);
57
- if (d === undefined) throw new ConfigError(`número decimal inválido: ${JSON.stringify(String(input))}`);
58
+ if (d === undefined) throw new ErroDeConfiguracao(`número decimal inválido: ${JSON.stringify(String(input))}`);
58
59
  return d;
59
60
  }
60
61
 
@@ -114,7 +115,7 @@ export class Decimal {
114
115
  /** Divisão com o resultado arredondado em `scale` casas pelo modo dado. */
115
116
  dividedBy(other: DecimalInput, scale: number, mode: RoundingMode): Decimal {
116
117
  const o = Decimal.of(other);
117
- if (o.coef === 0n) throw new ConfigError('divisão por zero');
118
+ if (o.coef === 0n) throw new ErroDeConfiguracao('divisão por zero');
118
119
  // (a / 10^sa) / (b / 10^sb) = a * 10^(sb + scale + 1 - sa) / b, com uma casa de guarda para o arredondamento.
119
120
  const shift = o.scale + scale + 1 - this.scale;
120
121
  const num = shift >= 0 ? this.coef * pow10(shift) : this.coef;
@@ -140,7 +141,7 @@ export class Decimal {
140
141
 
141
142
  /** Arredonda para `scale` casas. Com `scale` maior que a atual, só completa com zeros. */
142
143
  round(scale: number, mode: RoundingMode): Decimal {
143
- if (!Number.isInteger(scale) || scale < 0) throw new ConfigError(`casas decimais inválidas: ${scale}`);
144
+ if (!Number.isInteger(scale) || scale < 0) throw new ErroDeConfiguracao(`casas decimais inválidas: ${scale}`);
144
145
  if (scale >= this.scale) return new Decimal(this.coef * pow10(scale - this.scale), scale);
145
146
  return roundCoef(this.coef, this.scale, scale, mode, false);
146
147
  }
@@ -219,13 +220,13 @@ function roundCoef(coef: bigint, from: number, to: number, mode: RoundingMode, s
219
220
  }
220
221
 
221
222
  /** Atalho para `Decimal.of`. */
222
- export function dec(input: DecimalInput): Decimal {
223
- return Decimal.of(input);
223
+ export function dec(valor: DecimalInput): Decimal {
224
+ return Decimal.of(valor);
224
225
  }
225
226
 
226
227
  /** Soma uma lista (vazia = zero). */
227
- export function sum(values: Iterable<Decimal>): Decimal {
228
+ export function sum(valores: Iterable<Decimal>): Decimal {
228
229
  let acc = Decimal.ZERO;
229
- for (const v of values) acc = acc.plus(v);
230
+ for (const v of valores) acc = acc.plus(v);
230
231
  return acc;
231
232
  }