@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
@@ -1,15 +1,23 @@
1
1
  /**
2
- * Montagem da NF-e: entrada do domínio (`NfeInput`) para o objeto tipado do `@sinete/schemas` no PL vigente, com os
2
+ * Montagem da NF-e: entrada do domínio (`DadosNfe`) para o objeto tipado do `@sinete/schemas` no PL vigente, com os
3
3
  * derivados e os totais em decimal exato, a chave de acesso e as regras conferidas antes de qualquer serialização. A
4
- * saída é a string canônica de `<NFe>` ainda sem assinatura, já validada contra o schema; `signNfe` insere a
4
+ * saída é a string canônica de `<NFe>` ainda sem assinatura, já validada contra o schema; `assinarNfe` insere a
5
5
  * `Signature` por splice (ADR 0003) e nada mais toca nela.
6
6
  */
7
7
 
8
- import type { Ambiente, Signer, TimeContext, Uf, ValidationIssue } from '@sinete/core';
9
- import { ConfigError, fixedClock, formatarVerProc, isUf, tpAmbOf, ufBySigla, ValidationError } from '@sinete/core';
10
- import { base64Encode, firstChild, parseXml, signXml } from '@sinete/core/xml';
11
- import type { ComplexType, VigenciaEntry } from '@sinete/schemas';
12
- import { SerializeError, serialize, validate } from '@sinete/schemas';
8
+ import type { Ambiente, Assinador, ContextoDeTempo, Ocorrencia, Uf } from '@sinete/core';
9
+ import {
10
+ ErroDeConfiguracao,
11
+ ErroDeValidacao,
12
+ ehUf,
13
+ formatarVerProc,
14
+ relogioFixo,
15
+ tpAmbDoAmbiente,
16
+ ufPorSigla,
17
+ } from '@sinete/core';
18
+ import { assinarXml, codificarBase64, lerXml, primeiroFilho } from '@sinete/core/xml';
19
+ import type { ComplexType, EntradaDeVigencia } from '@sinete/schemas';
20
+ import { ErroSerializacao, serializar, validar } from '@sinete/schemas';
13
21
  import type {
14
22
  TNFe_infNFe,
15
23
  TNFe_infNFe_det,
@@ -21,7 +29,7 @@ import type {
21
29
  TTribNFe,
22
30
  } from '@sinete/schemas/nfe/PL_010f';
23
31
  import { TIS as TISCt, TNFe_infNFeSupl, TTribNFe as TTribNFeCt } from '@sinete/schemas/nfe/PL_010f';
24
- import { buildChaveAcesso, parseChaveAcesso, parseCnpj, parseCpf, parseIe } from '@sinete/validators';
32
+ import { lerChaveAcesso, lerCnpj, lerCpf, lerIe, montarChaveAcesso } from '@sinete/validators';
25
33
  import arredondamento from '../data/arredondamento.json' with { type: 'json' };
26
34
  import produtorRural from '../data/produtor-rural.json' with { type: 'json' };
27
35
  import reforma from '../data/reforma.json' with { type: 'json' };
@@ -31,6 +39,7 @@ import { Decimal } from '../decimal.ts';
31
39
  import { D0302, D0302A04, D1104V, D1110V, D1203, D1302, D1302_OPC } from '../format.ts';
32
40
  import { Issues } from '../issues.ts';
33
41
  import type {
42
+ DadosNfe,
34
43
  Destinatario,
35
44
  DocumentoPessoa,
36
45
  Emitente,
@@ -38,14 +47,13 @@ import type {
38
47
  EnderecoExterior,
39
48
  Item,
40
49
  Local,
41
- NfeInput,
42
50
  Referenciada,
43
51
  ResponsavelTecnico,
44
52
  } from '../model.ts';
45
- import type { IbsCbsCalculator, IbsCbsItemRequest } from '../ports.ts';
46
- import { ibsCbsCalculator } from '../rtc.ts';
53
+ import type { CalculadoraIbsCbs, PedidoIbsCbsItem } from '../ports.ts';
54
+ import { calculadoraIbsCbs } from '../rtc.ts';
47
55
  import type { Instante } from '../time.ts';
48
- import { formatDh, offsetDaUf } from '../time.ts';
56
+ import { deslocamentoDaUf, formatarDh } from '../time.ts';
49
57
  import { VERSAO_PACOTE } from '../versao-gerada.ts';
50
58
  import { conferirDestinatario } from './destinatario.ts';
51
59
  import { decimaisInvalidos, grupoInvalido, ibsCbsDoItem, totalIbsCbs } from './ibscbs.ts';
@@ -66,11 +74,11 @@ import { buildIcmsUfDest, buildIi, buildIpi, buildIssqn, buildPisCofins, buildPi
66
74
  import type { Familia } from './values.ts';
67
75
  import { Ctx, clean, digits, TOLERANCIA } from './values.ts';
68
76
 
69
- let padrao: IbsCbsCalculator | undefined;
77
+ let padrao: CalculadoraIbsCbs | undefined;
70
78
 
71
- /** A calculadora de IBS/CBS usada quando `options.ibsCbs` não vem: o motor do sinete, criada uma vez por processo. */
72
- function calculadoraPadrao(): IbsCbsCalculator {
73
- padrao ??= ibsCbsCalculator();
79
+ /** A calculadora de IBS/CBS usada quando `opcoes.ibsCbs` não vem: o motor do sinete, criada uma vez por processo. */
80
+ function calculadoraPadrao(): CalculadoraIbsCbs {
81
+ padrao ??= calculadoraIbsCbs();
74
82
  return padrao;
75
83
  }
76
84
 
@@ -84,18 +92,18 @@ export const XPROD_HOMOLOGACAO_NFCE = 'NOTA FISCAL EMITIDA EM AMBIENTE DE HOMOLO
84
92
 
85
93
  export type ExigenciaRespTec = 'obrigatorio' | 'opcional';
86
94
 
87
- export interface BuildNfeOptions {
95
+ export interface MontarNfeOpcoes {
88
96
  readonly ambiente: Ambiente;
89
97
  /** Relógio de emissão (dhEmi, PL vigente, CSRT) e de fato gerador (IBS/CBS). */
90
- readonly time: TimeContext;
98
+ readonly tempo: ContextoDeTempo;
91
99
  /**
92
- * Calculadora de IBS/CBS dos itens com `ibsCbs.classificacao`. Padrão: `ibsCbsCalculator()`, o motor do sinete com o
100
+ * Calculadora de IBS/CBS dos itens com `ibsCbs.classificacao`. Padrão: `calculadoraIbsCbs()`, o motor do sinete com o
93
101
  * dataset embarcado (importado na primeira nota que precisar dele) e as alíquotas oficiais. Informe outra para trocar
94
102
  * dataset, alíquotas, base ou regras, ou para calcular fora do sinete.
95
103
  */
96
- readonly ibsCbs?: IbsCbsCalculator;
104
+ readonly ibsCbs?: CalculadoraIbsCbs;
97
105
  /** Fuso do emitente em minutos; padrão pela UF (`data/fusos.json`). */
98
- readonly offsetMinutes?: number;
106
+ readonly deslocamentoMin?: number;
99
107
  /** Versão do aplicativo emissor (`verProc`); padrão `sinete <versão do @sinete/nfe>` (`formatarVerProc`). */
100
108
  readonly verProc?: string;
101
109
  /** Sobrepõe o modo de arredondamento de uma família (`data/arredondamento.json`). */
@@ -105,7 +113,7 @@ export interface BuildNfeOptions {
105
113
  /** Sobrepõe a exigência de infRespTec e CSRT da tabela por UF (`data/resp-tec.json`). */
106
114
  readonly exigencias?: { readonly infRespTec?: ExigenciaRespTec; readonly csrt?: ExigenciaRespTec };
107
115
  /** Fonte de aleatoriedade para o cNF; padrão `crypto.getRandomValues`. */
108
- readonly random?: (bytes: Uint8Array) => Uint8Array;
116
+ readonly aleatorio?: (bytes: Uint8Array) => Uint8Array;
109
117
  /**
110
118
  * O `vPag` do único `detPag` passa a ser o `vNF` calculado na mesma montagem, e o valor informado nele é ignorado.
111
119
  * Serve a quem recebe à vista o total da nota e não quer calcular o `vNF` antes de montar. Mais de um `detPag`,
@@ -128,7 +136,7 @@ export interface BuildNfeOptions {
128
136
  readonly urlChave?: string;
129
137
  }
130
138
 
131
- export interface BuiltNfe {
139
+ export interface NfeMontada {
132
140
  /** Chave de acesso (44 posições). */
133
141
  readonly chave: string;
134
142
  /** `Id` do `infNFe` (`NFe` + chave): é o que a assinatura referencia. */
@@ -140,21 +148,21 @@ export interface BuiltNfe {
140
148
  readonly tpEmis: string;
141
149
  readonly dhEmi: string;
142
150
  /**
143
- * Só NFC-e: o `infNFeSupl` a acrescentar (`comQrCode`, `signNfe`). Na versão 3 em contingência off-line, falta a
151
+ * Só NFC-e: o `infNFeSupl` a acrescentar (`comQrCode`, `assinarNfe`). Na versão 3 em contingência off-line, falta a
144
152
  * assinatura dos parâmetros, que só o certificado faz (`assinaturaQrCode`).
145
153
  */
146
154
  readonly nfce?: NfceSupl;
147
155
  /** Pacote de liberação usado (escolhido pela vigência). */
148
- readonly pl: VigenciaEntry;
156
+ readonly pl: EntradaDeVigencia;
149
157
  /** O objeto tipado, na forma do PL_010f (o mais novo que o pacote conhece). */
150
158
  readonly infNFe: TNFe_infNFe;
151
159
  /** `<NFe xmlns="...">` com o `infNFe` canônico, sem assinatura, já validado contra o schema. */
152
160
  readonly xml: string;
153
161
  }
154
162
 
155
- export type BuildNfeResult =
156
- | { readonly ok: true; readonly value: BuiltNfe }
157
- | { readonly ok: false; readonly issues: readonly ValidationIssue[] };
163
+ export type ResultadoMontagemNfe =
164
+ | { readonly ok: true; readonly valor: NfeMontada }
165
+ | { readonly ok: false; readonly ocorrencias: readonly Ocorrencia[] };
158
166
 
159
167
  const MODES: Record<Familia, RoundingMode> = Object.fromEntries(
160
168
  Object.entries(arredondamento.familias).map(([k, v]) => [k, v.modo as RoundingMode]),
@@ -164,7 +172,7 @@ const SEM_GTIN = 'SEM GTIN';
164
172
  const BRASILIA = -180;
165
173
 
166
174
  function diaBrasilia(d: Instante): string {
167
- return formatDh(d, BRASILIA).slice(0, 10);
175
+ return formatarDh(d, BRASILIA).slice(0, 10);
168
176
  }
169
177
 
170
178
  /** Exigência de infRespTec ou CSRT para a UF no ambiente e na data (tabela `data/resp-tec.json`). */
@@ -192,29 +200,29 @@ export function exigenciaRespTec(
192
200
  /** `hashCSRT`: Base64(SHA-1(CSRT + chave de acesso)) (NT 2018.005, campo ZD09). */
193
201
  export async function hashCsrt(csrt: string, chave: string): Promise<string> {
194
202
  const d = await globalThis.crypto.subtle.digest('SHA-1', new TextEncoder().encode(csrt + chave));
195
- return base64Encode(new Uint8Array(d));
203
+ return codificarBase64(new Uint8Array(d));
196
204
  }
197
205
 
198
206
  /** Normaliza CNPJ ou CPF, apontando a ocorrência do validador. */
199
207
  function documento(doc: DocumentoPessoa, path: string, issues: Issues): { CNPJ: string } | { CPF: string } {
200
208
  if (doc.CNPJ !== undefined) {
201
- const r = parseCnpj(doc.CNPJ, { path: `${path}.CNPJ` });
202
- if (!r.ok) issues.list.push(r.error);
203
- return { CNPJ: r.ok ? r.value : doc.CNPJ };
209
+ const r = lerCnpj(doc.CNPJ, { caminho: `${path}.CNPJ` });
210
+ if (!r.ok) issues.list.push(r.erro);
211
+ return { CNPJ: r.ok ? r.valor : doc.CNPJ };
204
212
  }
205
- const r = parseCpf(doc.CPF ?? '', { path: `${path}.CPF` });
206
- if (!r.ok) issues.list.push(r.error);
207
- return { CPF: r.ok ? r.value : (doc.CPF ?? '') };
213
+ const r = lerCpf(doc.CPF ?? '', { caminho: `${path}.CPF` });
214
+ if (!r.ok) issues.list.push(r.erro);
215
+ return { CPF: r.ok ? r.valor : (doc.CPF ?? '') };
208
216
  }
209
217
 
210
218
  function ie(value: string | undefined, uf: Uf, path: string, issues: Issues, allowIsento: boolean): string | undefined {
211
219
  if (value === undefined) return undefined;
212
- const r = parseIe(value, uf, { path, allowIsento });
220
+ const r = lerIe(value, uf, { caminho: path, aceitarIsento: allowIsento });
213
221
  if (!r.ok) {
214
- issues.list.push(r.error);
222
+ issues.list.push(r.erro);
215
223
  return value;
216
224
  }
217
- return r.value.value;
225
+ return r.valor.valor;
218
226
  }
219
227
 
220
228
  function endereco(e: Endereco): Record<string, string | undefined> {
@@ -242,10 +250,10 @@ function gerarChave(
242
250
  random: (b: Uint8Array) => Uint8Array,
243
251
  issues: Issues,
244
252
  ): { chave: string; cNF: string } | undefined {
245
- const tentar = (cNF: string): { chave: string; ok: boolean; issue?: ValidationIssue } => {
246
- const chave = buildChaveAcesso({ ...parts, cNF });
247
- const r = parseChaveAcesso(chave, { emissao: true, path: 'chave' });
248
- return r.ok ? { chave, ok: true } : { chave, ok: false, issue: r.error };
253
+ const tentar = (cNF: string): { chave: string; ok: boolean; issue?: Ocorrencia } => {
254
+ const chave = montarChaveAcesso({ ...parts, cNF });
255
+ const r = lerChaveAcesso(chave, { emissao: true, caminho: 'chave' });
256
+ return r.ok ? { chave, ok: true } : { chave, ok: false, issue: r.erro };
249
257
  };
250
258
  if (cNFInformado !== undefined) {
251
259
  if (!/^\d{8}$/.test(cNFInformado)) {
@@ -259,9 +267,9 @@ function gerarChave(
259
267
  const doCnf = r.issue.code === 'chave_cnf_invalido';
260
268
  issues.list.push({
261
269
  ...r.issue,
262
- path: doCnf ? 'cNF' : r.issue.path,
270
+ caminho: doCnf ? 'cNF' : r.issue.caminho,
263
271
  code: 'chave_invalida',
264
- message: `${r.issue.message} (${r.issue.code})`,
272
+ mensagem: `${r.issue.mensagem} (${r.issue.code})`,
265
273
  origem: doCnf ? 'entrada' : 'montagem',
266
274
  });
267
275
  }
@@ -279,7 +287,7 @@ function gerarChave(
279
287
  issues.list.push({
280
288
  ...r.issue,
281
289
  code: 'chave_invalida',
282
- message: `${r.issue.message} (${r.issue.code})`,
290
+ mensagem: `${r.issue.mensagem} (${r.issue.code})`,
283
291
  origem: 'montagem',
284
292
  });
285
293
  return undefined;
@@ -291,9 +299,9 @@ function gerarChave(
291
299
 
292
300
  function referencia(ref: Referenciada, path: string, issues: Issues): TNFe_infNFe_ide_NFref {
293
301
  const chave = (c: string, p: string): string => {
294
- const r = parseChaveAcesso(c, { path: p });
295
- if (!r.ok) issues.list.push(r.error);
296
- return r.ok ? r.value.chave : c;
302
+ const r = lerChaveAcesso(c, { caminho: p });
303
+ if (!r.ok) issues.list.push(r.erro);
304
+ return r.ok ? r.valor.chave : c;
297
305
  };
298
306
  if ('refNFe' in ref) return { refNFe: chave(ref.refNFe, `${path}.refNFe`) };
299
307
  if ('refNFeSig' in ref) {
@@ -312,7 +320,7 @@ function referencia(ref: Referenciada, path: string, issues: Issues): TNFe_infNF
312
320
  // Nota de produtor em papel (modelo 04) extinta: só referência a nota emitida antes do fim na UF dela.
313
321
  if (nfp.mod === '04') {
314
322
  const porUf = produtorRural.refNFP.ufs as Readonly<Record<string, { readonly vedadaDesde: string }>>;
315
- const cUfSigla = Object.keys(porUf).find((k) => ufBySigla(k)?.cUF === nfp.cUF);
323
+ const cUfSigla = Object.keys(porUf).find((k) => ufPorSigla(k)?.cUF === nfp.cUF);
316
324
  const vedadaDesde = cUfSigla ? (porUf[cUfSigla]?.vedadaDesde ?? '') : produtorRural.refNFP.modelo04.vedadaDesde;
317
325
  if (nfp.AAMM >= vedadaDesde) {
318
326
  issues.add(
@@ -576,10 +584,10 @@ function montarItem(ctx: Ctx, item: Item, n: number, normal: boolean): ItemMonta
576
584
  if (item.infAdProd !== undefined) det.infAdProd = item.infAdProd;
577
585
  if (item.obsItem !== undefined) det.obsItem = item.obsItem;
578
586
  if (item.DFeReferenciado !== undefined) {
579
- const r = parseChaveAcesso(item.DFeReferenciado.chaveAcesso, { path: `${path}.DFeReferenciado.chaveAcesso` });
580
- if (!r.ok) ctx.issues.list.push(r.error);
587
+ const r = lerChaveAcesso(item.DFeReferenciado.chaveAcesso, { caminho: `${path}.DFeReferenciado.chaveAcesso` });
588
+ if (!r.ok) ctx.issues.list.push(r.erro);
581
589
  det.DFeReferenciado = clean({
582
- chaveAcesso: r.ok ? r.value.chave : item.DFeReferenciado.chaveAcesso,
590
+ chaveAcesso: r.ok ? r.valor.chave : item.DFeReferenciado.chaveAcesso,
583
591
  nItem: item.DFeReferenciado.nItem === undefined ? undefined : String(item.DFeReferenciado.nItem),
584
592
  });
585
593
  }
@@ -616,44 +624,44 @@ function composicao(m: ItemMontado, incluirProd: boolean): Decimal {
616
624
  }
617
625
 
618
626
  /** Monta, calcula e valida. Nunca lança por dado do chamador: devolve as ocorrências. */
619
- export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promise<BuildNfeResult> {
627
+ export async function montarNfe(entrada: DadosNfe, opcoes: MontarNfeOpcoes): Promise<ResultadoMontagemNfe> {
620
628
  const issues = new Issues();
621
- const finNFeIn = input.finNFe ?? '1';
622
- const mod = input.modelo ?? '55';
629
+ const finNFeIn = entrada.finNFe ?? '1';
630
+ const mod = entrada.modelo ?? '55';
623
631
  if (mod !== '55' && mod !== '65') {
624
632
  issues.add('modelo', 'modelo_nao_suportado', `modelo ${String(mod)}: o builder monta NF-e (55) e NFC-e (65)`);
625
- return { ok: false, issues: issues.classificadas };
633
+ return { ok: false, ocorrencias: issues.classificadas };
626
634
  }
627
635
  const nfce = mod === '65';
628
636
  // Padrões da identificação resolvidos uma vez: o mesmo valor vai para o ide e para a calculadora de IBS/CBS. A NFC-e
629
637
  // é presencial e para consumidor final (MOC 7.0 Anexo I, B25a-10 e B25b-20).
630
- const indPres = input.indPres ?? (nfce ? '1' : finNFeIn === '2' || finNFeIn === '3' ? '0' : '9');
631
- const indFinal = input.indFinal ?? (nfce || input.destinatario?.indIEDest === '9' ? '1' : '0');
632
- const ctx = new Ctx(issues, { ...MODES, ...options.arredondamento }, finNFeIn !== '2' && finNFeIn !== '3');
633
- const pRedutorGov = input.gCompraGov && ctx.req(input.gCompraGov.pRedutor, 'gCompraGov.pRedutor', D0302A04);
634
- const emitUf = input.emitente.endereco.UF;
635
- if (!isUf(emitUf)) {
638
+ const indPres = entrada.indPres ?? (nfce ? '1' : finNFeIn === '2' || finNFeIn === '3' ? '0' : '9');
639
+ const indFinal = entrada.indFinal ?? (nfce || entrada.destinatario?.indIEDest === '9' ? '1' : '0');
640
+ const ctx = new Ctx(issues, { ...MODES, ...opcoes.arredondamento }, finNFeIn !== '2' && finNFeIn !== '3');
641
+ const pRedutorGov = entrada.gCompraGov && ctx.req(entrada.gCompraGov.pRedutor, 'gCompraGov.pRedutor', D0302A04);
642
+ const emitUf = entrada.emitente.endereco.UF;
643
+ if (!ehUf(emitUf)) {
636
644
  issues.add('emitente.endereco.UF', 'campo_invalido', 'UF do emitente inválida');
637
- return { ok: false, issues: issues.classificadas };
645
+ return { ok: false, ocorrencias: issues.classificadas };
638
646
  }
639
- const cUF = ufBySigla(emitUf)?.cUF ?? '';
640
- const offset = options.offsetMinutes ?? offsetDaUf(emitUf);
641
- const agora = options.time.emissao.now();
647
+ const cUF = ufPorSigla(emitUf)?.cUF ?? '';
648
+ const offset = opcoes.deslocamentoMin ?? deslocamentoDaUf(emitUf);
649
+ const agora = opcoes.tempo.emissao.agora();
642
650
  // Um instante de fato gerador por montagem: o mesmo vai para a calculadora e para a regra de composição do vItem.
643
- const fatoGerador = options.time.fatoGerador.now();
644
- const dhEmi = formatDh(agora, offset);
651
+ const fatoGerador = opcoes.tempo.fatoGerador.agora();
652
+ const dhEmi = formatarDh(agora, offset);
645
653
  const aamm = dhEmi.slice(2, 4) + dhEmi.slice(5, 7);
646
- const tpAmb = tpAmbOf(options.ambiente);
654
+ const tpAmb = tpAmbDoAmbiente(opcoes.ambiente);
647
655
 
648
- // PL vigente (VigenciaError do schemas propaga: data fora de toda vigência é erro de configuração, não de dado).
656
+ // PL vigente (ErroVigencia do schemas propaga: data fora de toda vigência é erro de configuração, não de dado).
649
657
  // O PL sai do mesmo instante do dhEmi: reler o relógio numa virada de vigência escolheria outro PL.
650
- const pl = escolherPl(options.ambiente, fixedClock(agora));
658
+ const pl = escolherPl(opcoes.ambiente, relogioFixo(agora));
651
659
 
652
660
  // Emitente (grupo C)
653
- const e: Emitente = input.emitente;
661
+ const e: Emitente = entrada.emitente;
654
662
  const emitDoc = documento(e, 'emitente', issues);
655
- const serie = Number(input.serie);
656
- const nNF = Number(input.nNF);
663
+ const serie = Number(entrada.serie);
664
+ const nNF = Number(entrada.nNF);
657
665
  if (!Number.isInteger(serie) || serie < 0 || serie > 999) issues.add('serie', 'serie_invalida', 'série de 0 a 999');
658
666
  if (!Number.isInteger(nNF) || nNF < 1 || nNF > 999_999_999)
659
667
  issues.add('nNF', 'campo_invalido', 'nNF de 1 a 999999999');
@@ -687,7 +695,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
687
695
 
688
696
  // Destinatário (grupo E): obrigatório na NF-e (RV E01-10); na NFC-e, só na entrega a domicílio (E01-20, conferida
689
697
  // com as demais regras da NFC-e).
690
- const d: Destinatario | undefined = input.destinatario;
698
+ const d: Destinatario | undefined = entrada.destinatario;
691
699
  let dest: Record<string, unknown> | undefined;
692
700
  let destUf: Uf | 'EX' | undefined;
693
701
  if (d === undefined) {
@@ -723,7 +731,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
723
731
  : d.IE;
724
732
  dest = clean({
725
733
  ...docDest,
726
- xNome: options.ambiente === 'homologacao' ? XNOME_HOMOLOGACAO : d.xNome,
734
+ xNome: opcoes.ambiente === 'homologacao' ? XNOME_HOMOLOGACAO : d.xNome,
727
735
  enderDest,
728
736
  indIEDest: d.indIEDest,
729
737
  IE: destIe,
@@ -734,7 +742,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
734
742
  }
735
743
 
736
744
  // Identificação: contingência e forma de emissão
737
- const cont = input.contingencia;
745
+ const cont = entrada.contingencia;
738
746
  const tpEmis = cont?.tpEmis ?? '1';
739
747
  if (cont !== undefined) {
740
748
  if (cont.xJust.length < 15 || cont.xJust.length > 256) {
@@ -766,25 +774,25 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
766
774
  // Identificação que as regras do modelo conferem antes de montar.
767
775
  // Na NFC-e a operação é sempre interna (B11a-10, rejeição 707): o endereço do consumidor não a torna interestadual.
768
776
  const idDest =
769
- input.idDest ?? (nfce ? '1' : destUf === 'EX' ? '3' : destUf !== undefined && destUf !== emitUf ? '2' : '1');
770
- const tpImp = input.tpImp ?? (nfce ? '4' : '1');
777
+ entrada.idDest ?? (nfce ? '1' : destUf === 'EX' ? '3' : destUf !== undefined && destUf !== emitUf ? '2' : '1');
778
+ const tpImp = entrada.tpImp ?? (nfce ? '4' : '1');
771
779
  conferirDestinatario(
772
- input,
780
+ entrada,
773
781
  {
774
782
  mod,
775
- tpNF: input.tpNF,
783
+ tpNF: entrada.tpNF,
776
784
  idDest,
777
- idDestInformado: input.idDest !== undefined,
785
+ idDestInformado: entrada.idDest !== undefined,
778
786
  indFinal,
779
- producao: options.ambiente === 'producao',
787
+ producao: opcoes.ambiente === 'producao',
780
788
  },
781
789
  issues,
782
790
  );
783
791
  if (nfce) {
784
792
  conferirNfce(
785
- input,
793
+ entrada,
786
794
  {
787
- tpNF: input.tpNF,
795
+ tpNF: entrada.tpNF,
788
796
  idDest,
789
797
  tpImp,
790
798
  finNFe: finNFeIn,
@@ -796,7 +804,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
796
804
  } else if (tpImp === '4' || tpImp === '5') {
797
805
  issues.add('tpImp', 'campo_invalido', 'tpImp 4 e 5 (DANFC-e) são da NFC-e (B21-20, rejeição 710)');
798
806
  }
799
- const qr: QrCodeNfceOpcoes = options.qrCode ?? { versao: '3' };
807
+ const qr: QrCodeNfceOpcoes = opcoes.qrCode ?? { versao: '3' };
800
808
  if (nfce && qr.versao === '2') {
801
809
  // O CSC e o idCSC são opções do montador, não da nota: as ocorrências são da montagem (ADR 0011).
802
810
  if (!/^\d{1,6}$/.test(qr.idCSC) || Number(qr.idCSC) === 0) {
@@ -824,12 +832,13 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
824
832
 
825
833
  // Chave de acesso e cNF
826
834
  const random =
827
- options.random ?? ((b: Uint8Array): Uint8Array => globalThis.crypto.getRandomValues(b as Uint8Array<ArrayBuffer>));
835
+ opcoes.aleatorio ??
836
+ ((b: Uint8Array): Uint8Array => globalThis.crypto.getRandomValues(b as Uint8Array<ArrayBuffer>));
828
837
  const emitente14 = 'CNPJ' in emitDoc ? emitDoc.CNPJ : emitDoc.CPF;
829
838
  const gerada = issues.empty
830
839
  ? gerarChave(
831
840
  { cUF, aamm, emitente: emitente14, mod, serie: String(serie), nNF: String(nNF), tpEmis },
832
- input.cNF,
841
+ entrada.cNF,
833
842
  random,
834
843
  issues,
835
844
  )
@@ -839,24 +848,24 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
839
848
  const cDV = chave.slice(43);
840
849
 
841
850
  // Itens
842
- if (input.itens.length === 0 || input.itens.length > 990) {
851
+ if (entrada.itens.length === 0 || entrada.itens.length > 990) {
843
852
  issues.add('itens', 'itens_limite', 'a NF-e tem de 1 a 990 itens');
844
853
  }
845
- const itens = input.itens.map((it, n) => montarItem(ctx, it, n, finNFeIn === '1'));
854
+ const itens = entrada.itens.map((it, n) => montarItem(ctx, it, n, finNFeIn === '1'));
846
855
  // Na NFC-e em homologação, a descrição do primeiro item é a literal da RV I04-10, como o nome do destinatário da
847
856
  // E04-20: o builder a põe, e a descrição informada fica fora do XML de teste.
848
857
  const primeiro = itens[0];
849
- if (nfce && options.ambiente === 'homologacao' && primeiro !== undefined) {
858
+ if (nfce && opcoes.ambiente === 'homologacao' && primeiro !== undefined) {
850
859
  (primeiro.det.prod as unknown as Record<string, unknown>).xProd = XPROD_HOMOLOGACAO_NFCE;
851
860
  }
852
861
 
853
862
  // IBS/CBS pela calculadora das opções, ou pelo motor do sinete
854
- const classificados = input.itens
863
+ const classificados = entrada.itens
855
864
  .map((it, n) => ({ it, n }))
856
865
  .filter(({ it }) => it.impostos.ibsCbs?.classificacao !== undefined);
857
866
  if (classificados.length > 0) {
858
- const calculadora = options.ibsCbs ?? calculadoraPadrao();
859
- const reqs: IbsCbsItemRequest[] = classificados.map(({ it, n }) => {
867
+ const calculadora = opcoes.ibsCbs ?? calculadoraPadrao();
868
+ const reqs: PedidoIbsCbsItem[] = classificados.map(({ it, n }) => {
860
869
  const c = it.impostos.ibsCbs?.classificacao as NonNullable<
861
870
  NonNullable<typeof it.impostos.ibsCbs>['classificacao']
862
871
  >;
@@ -894,29 +903,29 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
894
903
  };
895
904
  });
896
905
  const destino =
897
- input.entrega !== undefined
898
- ? { UF: input.entrega.UF, cMun: input.entrega.cMun }
906
+ entrada.entrega !== undefined
907
+ ? { UF: entrada.entrega.UF, cMun: entrada.entrega.cMun }
899
908
  : d?.endereco !== undefined
900
909
  ? isExterior(d.endereco)
901
910
  ? { UF: 'EX' as const, cMun: '9999999' }
902
911
  : { UF: d.endereco.UF, cMun: d.endereco.cMun }
903
912
  : undefined;
904
- const compra = input.gCompraGov;
913
+ const compra = entrada.gCompraGov;
905
914
  const resp = await calculadora.calcular({
906
915
  nota: {
907
916
  fatoGerador,
908
917
  emissao: agora,
909
- ambiente: options.ambiente,
918
+ ambiente: opcoes.ambiente,
910
919
  mod,
911
- tpNF: input.tpNF,
920
+ tpNF: entrada.tpNF,
912
921
  finNFe: finNFeIn,
913
- ...(input.tpNFDebito ? { tpNFDebito: input.tpNFDebito } : {}),
914
- ...(input.tpNFCredito ? { tpNFCredito: input.tpNFCredito } : {}),
922
+ ...(entrada.tpNFDebito ? { tpNFDebito: entrada.tpNFDebito } : {}),
923
+ ...(entrada.tpNFCredito ? { tpNFCredito: entrada.tpNFCredito } : {}),
915
924
  indFinal,
916
925
  indPres,
917
926
  emitente: { UF: emitUf, cMun: e.endereco.cMun, CRT: e.CRT },
918
927
  ...(destino ? { destino } : {}),
919
- ...(input.cMunFGIBS ? { cMunFGIBS: input.cMunFGIBS } : {}),
928
+ ...(entrada.cMunFGIBS ? { cMunFGIBS: entrada.cMunFGIBS } : {}),
920
929
  ...(compra && pRedutorGov
921
930
  ? {
922
931
  compraGov: {
@@ -929,7 +938,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
929
938
  },
930
939
  itens: reqs,
931
940
  });
932
- issues.addAll(resp.issues ?? [], 'montagem');
941
+ issues.addAll(resp.ocorrencias ?? [], 'montagem');
933
942
  for (const { n } of classificados) {
934
943
  const g = resp.itens.find((x) => x.nItem === n + 1);
935
944
  if (g === undefined) {
@@ -951,7 +960,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
951
960
  const ib = `itens[${n}].impostos.ibsCbs`;
952
961
  const is = `itens[${n}].impostos.is`;
953
962
  // O IBSCBS pronto (ibsCbs.grupo) é da entrada; o que a calculadora devolveu é da montagem (ADR 0011).
954
- const ibsDaCalculadora = input.itens[n]?.impostos.ibsCbs?.grupo === undefined;
963
+ const ibsDaCalculadora = entrada.itens[n]?.impostos.ibsCbs?.grupo === undefined;
955
964
  const antes = issues.list.length;
956
965
  decimaisInvalidos(imp.IBSCBS, ib, issues);
957
966
  if (ibsDaCalculadora) issues.reclassificarDesde(antes, 'montagem');
@@ -962,7 +971,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
962
971
  grupoInvalido(TISCt as ComplexType, 'IS', imp.IS, is, issues);
963
972
  }
964
973
  });
965
- if (!issues.empty) return { ok: false, issues: issues.classificadas };
974
+ if (!issues.empty) return { ok: false, ocorrencias: issues.classificadas };
966
975
 
967
976
  // Totais (grupo W)
968
977
  const gruposIbsCbs = itens
@@ -1096,43 +1105,43 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1096
1105
  const ide = clean({
1097
1106
  cUF,
1098
1107
  cNF,
1099
- natOp: input.natOp,
1108
+ natOp: entrada.natOp,
1100
1109
  mod,
1101
1110
  serie: String(serie),
1102
1111
  nNF: String(nNF),
1103
1112
  dhEmi,
1104
- dhSaiEnt: input.dhSaiEnt === undefined ? undefined : formatDh(input.dhSaiEnt, offset),
1105
- dPrevEntrega: input.dPrevEntrega,
1106
- tpNF: input.tpNF,
1113
+ dhSaiEnt: entrada.dhSaiEnt === undefined ? undefined : formatarDh(entrada.dhSaiEnt, offset),
1114
+ dPrevEntrega: entrada.dPrevEntrega,
1115
+ tpNF: entrada.tpNF,
1107
1116
  idDest,
1108
- cMunFG: input.cMunFG ?? e.endereco.cMun,
1109
- cMunFGIBS: input.cMunFGIBS,
1117
+ cMunFG: entrada.cMunFG ?? e.endereco.cMun,
1118
+ cMunFGIBS: entrada.cMunFGIBS,
1110
1119
  tpImp,
1111
1120
  tpEmis,
1112
1121
  cDV,
1113
1122
  tpAmb,
1114
1123
  finNFe,
1115
- tpNFDebito: input.tpNFDebito,
1116
- tpNFCredito: input.tpNFCredito,
1124
+ tpNFDebito: entrada.tpNFDebito,
1125
+ tpNFCredito: entrada.tpNFCredito,
1117
1126
  indFinal,
1118
1127
  indPres,
1119
1128
  // NT 2020.006: indIntermed obrigatório com indPres 2, 3, 4 e 9.
1120
- indIntermed: input.indIntermed ?? (['2', '3', '4', '9'].includes(indPres) ? '0' : undefined),
1129
+ indIntermed: entrada.indIntermed ?? (['2', '3', '4', '9'].includes(indPres) ? '0' : undefined),
1121
1130
  procEmi: '0',
1122
- verProc: options.verProc ?? formatarVerProc('sinete', VERSAO_PACOTE),
1123
- dhCont: cont === undefined ? undefined : formatDh(cont.dhCont, offset),
1131
+ verProc: opcoes.verProc ?? formatarVerProc('sinete', VERSAO_PACOTE),
1132
+ dhCont: cont === undefined ? undefined : formatarDh(cont.dhCont, offset),
1124
1133
  xJust: cont?.xJust,
1125
- NFref: input.referenciadas?.map((r, n) => referencia(r, `referenciadas[${n}]`, issues)),
1134
+ NFref: entrada.referenciadas?.map((r, n) => referencia(r, `referenciadas[${n}]`, issues)),
1126
1135
  gCompraGov:
1127
- input.gCompraGov === undefined
1136
+ entrada.gCompraGov === undefined
1128
1137
  ? undefined
1129
1138
  : clean({
1130
- tpEnteGov: input.gCompraGov.tpEnteGov,
1139
+ tpEnteGov: entrada.gCompraGov.tpEnteGov,
1131
1140
  pRedutor: ctx.s(pRedutorGov ?? Decimal.ZERO, D0302A04),
1132
- tpOperGov: input.gCompraGov.tpOperGov,
1133
- refDFeAnt: input.gCompraGov.refDFeAnt as string[] | undefined,
1141
+ tpOperGov: entrada.gCompraGov.tpOperGov,
1142
+ refDFeAnt: entrada.gCompraGov.refDFeAnt as string[] | undefined,
1134
1143
  }),
1135
- gPagAntecipado: input.gPagAntecipado === undefined ? undefined : { refNFe: [...input.gPagAntecipado] },
1144
+ gPagAntecipado: entrada.gPagAntecipado === undefined ? undefined : { refNFe: [...entrada.gPagAntecipado] },
1136
1145
  });
1137
1146
 
1138
1147
  // Locais, autorizados, transporte, cobrança, pagamento, informações adicionais
@@ -1148,10 +1157,10 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1148
1157
  email: l.email,
1149
1158
  IE: l.IE,
1150
1159
  });
1151
- if ((input.autXML?.length ?? 0) > 10) issues.add('autXML', 'campo_invalido', 'no máximo 10 autorizados');
1152
- const autXML = input.autXML?.map((a, n) => documento(a, `autXML[${n}]`, issues));
1160
+ if ((entrada.autXML?.length ?? 0) > 10) issues.add('autXML', 'campo_invalido', 'no máximo 10 autorizados');
1161
+ const autXML = entrada.autXML?.map((a, n) => documento(a, `autXML[${n}]`, issues));
1153
1162
 
1154
- const tr = input.transporte;
1163
+ const tr = entrada.transporte;
1155
1164
  const transp: Record<string, unknown> = { modFrete: tr?.modFrete ?? '9' };
1156
1165
  if (tr?.transportador !== undefined) {
1157
1166
  const t = tr.transportador;
@@ -1194,8 +1203,8 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1194
1203
  }
1195
1204
 
1196
1205
  let cobr: Record<string, unknown> | undefined;
1197
- if (input.cobranca !== undefined) {
1198
- const c = input.cobranca;
1206
+ if (entrada.cobranca !== undefined) {
1207
+ const c = entrada.cobranca;
1199
1208
  cobr = {};
1200
1209
  if (c.fatura !== undefined) {
1201
1210
  const f = c.fatura;
@@ -1225,10 +1234,10 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1225
1234
  }
1226
1235
  }
1227
1236
 
1228
- const pagIn = input.pagamento ?? { detPag: [{ tPag: '90' as const, vPag: '0' }] };
1229
- const igualTotal = options.pagamentoIgualTotal === true;
1237
+ const pagIn = entrada.pagamento ?? { detPag: [{ tPag: '90' as const, vPag: '0' }] };
1238
+ const igualTotal = opcoes.pagamentoIgualTotal === true;
1230
1239
  if (igualTotal) {
1231
- const unico = input.pagamento?.detPag.length === 1 ? input.pagamento.detPag[0] : undefined;
1240
+ const unico = entrada.pagamento?.detPag.length === 1 ? entrada.pagamento.detPag[0] : undefined;
1232
1241
  if (unico === undefined) {
1233
1242
  issues.add('pagamento.detPag', 'pagamento_igual_total', 'pagamentoIgualTotal pede exatamente um detPag');
1234
1243
  } else if (unico.tPag === '90') {
@@ -1253,7 +1262,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1253
1262
  }) as unknown as PagMontado;
1254
1263
  // Pagamentos e troco contra o vNF (W16), não o vNFTot: é o total que a YA03-10 (NT 2025.001 v1.03) e a YA09-10 (MOC 7.0
1255
1264
  // Anexo I) citam, e a NT 2025.002 (até a v1.51) não mudou essas regras nem o W16.
1256
- if (nfce && input.pagamento !== undefined && input.pagamento.detPag.length > 0 && issues.empty) {
1265
+ if (nfce && entrada.pagamento !== undefined && entrada.pagamento.detPag.length > 0 && issues.empty) {
1257
1266
  pagamentoNfce(pag, vNF, issues);
1258
1267
  }
1259
1268
  // W16-40 (rejeição 750): acima do limite, a NFC-e identifica o destinatário por CNPJ, CPF ou idEstrangeiro.
@@ -1264,7 +1273,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1264
1273
  `NFC-e acima de R$ ${NFCE_LIMITE_SEM_DESTINATARIO} identifica o destinatário (W16-40, rejeição 750)`,
1265
1274
  );
1266
1275
  }
1267
- const ia = input.informacoesAdicionais;
1276
+ const ia = entrada.informacoesAdicionais;
1268
1277
  const infAdic =
1269
1278
  ia === undefined || Object.values(ia).every((x) => x === undefined)
1270
1279
  ? undefined
@@ -1277,18 +1286,18 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1277
1286
  });
1278
1287
 
1279
1288
  // Responsável técnico e CSRT (NT 2018.005), com a exigência por UF como dado
1280
- const exig = { ...exigenciaRespTec(emitUf, options.ambiente, agora), ...options.exigencias };
1281
- const rt = input.respTec ?? options.respTec;
1289
+ const exig = { ...exigenciaRespTec(emitUf, opcoes.ambiente, agora), ...opcoes.exigencias };
1290
+ const rt = entrada.respTec ?? opcoes.respTec;
1282
1291
  let infRespTec: Record<string, unknown> | undefined;
1283
1292
  if (rt === undefined) {
1284
1293
  if (exig.infRespTec === 'obrigatorio') {
1285
1294
  issues.add('respTec', 'resp_tec_obrigatorio', `a UF ${emitUf} exige o responsável técnico (rejeição 972)`);
1286
1295
  }
1287
1296
  } else {
1288
- const cnpjRt = parseCnpj(rt.CNPJ, { path: 'respTec.CNPJ' });
1289
- if (!cnpjRt.ok) issues.list.push(cnpjRt.error);
1297
+ const cnpjRt = lerCnpj(rt.CNPJ, { caminho: 'respTec.CNPJ' });
1298
+ if (!cnpjRt.ok) issues.list.push(cnpjRt.erro);
1290
1299
  infRespTec = {
1291
- CNPJ: cnpjRt.ok ? cnpjRt.value : rt.CNPJ,
1300
+ CNPJ: cnpjRt.ok ? cnpjRt.valor : rt.CNPJ,
1292
1301
  xContato: rt.xContato,
1293
1302
  email: rt.email,
1294
1303
  fone: digits(rt.fone),
@@ -1307,51 +1316,54 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1307
1316
  ide,
1308
1317
  emit,
1309
1318
  dest,
1310
- retirada: local(input.retirada, 'retirada'),
1311
- entrega: local(input.entrega, 'entrega'),
1319
+ retirada: local(entrada.retirada, 'retirada'),
1320
+ entrega: local(entrada.entrega, 'entrega'),
1312
1321
  autXML,
1313
1322
  det: itens.map((m) => m.det),
1314
1323
  total,
1315
1324
  transp,
1316
1325
  cobr,
1317
1326
  pag,
1318
- infIntermed: input.infIntermed,
1327
+ infIntermed: entrada.infIntermed,
1319
1328
  infAdic,
1320
- exporta: input.exporta,
1321
- compra: input.compra,
1322
- cana: input.cana,
1329
+ exporta: entrada.exporta,
1330
+ compra: entrada.compra,
1331
+ cana: entrada.cana,
1323
1332
  infRespTec,
1324
- agropecuario: input.agropecuario,
1333
+ agropecuario: entrada.agropecuario,
1325
1334
  }) as unknown as TNFe_infNFe;
1326
1335
 
1327
1336
  // Regras da SEFAZ que só dependem do documento (ADR 0012).
1328
- cstComIsento(input, idDest, issues);
1329
- vencimentos(input, { dhEmi, instante: agora }, issues);
1337
+ cstComIsento(entrada, idDest, issues);
1338
+ vencimentos(entrada, { dhEmi, instante: agora }, issues);
1330
1339
 
1331
1340
  camposForaDoPl(pl.infNFe, inf, 'infNFe', pl.vigencia.pl, issues);
1332
1341
  textosForaDoXml(inf, 'infNFe', issues);
1333
- if (!issues.empty) return { ok: false, issues: issues.classificadas };
1342
+ if (!issues.empty) return { ok: false, ocorrencias: issues.classificadas };
1334
1343
 
1335
1344
  // Serialização canônica e validação estrita contra o schema do PL vigente, antes de qualquer assinatura.
1336
- // O serializer recusa com SerializeError o que não cabe no modelo (escolhas exclusivas informadas juntas, por
1345
+ // O serializer recusa com ErroSerializacao o que não cabe no modelo (escolhas exclusivas informadas juntas, por
1337
1346
  // exemplo vagao e balsa): vira ocorrência com o caminho do XML, nunca exceção.
1338
1347
  let xml: string;
1339
1348
  try {
1340
- xml = `<NFe xmlns="${NFE_NS}">${serialize(pl.infNFe, 'infNFe', inf, NFE_NS)}</NFe>`;
1349
+ xml = `<NFe xmlns="${NFE_NS}">${serializar(pl.infNFe, 'infNFe', inf, NFE_NS)}</NFe>`;
1341
1350
  } catch (e) {
1342
- if (!(e instanceof SerializeError)) throw e;
1343
- return { ok: false, issues: [{ path: e.path, code: 'schema', message: e.message, origem: 'montagem' }] };
1351
+ if (!(e instanceof ErroSerializacao)) throw e;
1352
+ return {
1353
+ ok: false,
1354
+ ocorrencias: [{ caminho: e.caminho, code: 'schema', mensagem: e.message, origem: 'montagem' }],
1355
+ };
1344
1356
  }
1345
- const doc = parseXml(xml);
1346
- const infEl = firstChild(doc.root, 'infNFe', NFE_NS);
1347
- const schemaIssues = infEl === undefined ? [] : validate(pl.infNFe, infEl);
1357
+ const doc = lerXml(xml);
1358
+ const infEl = primeiroFilho(doc.raiz, 'infNFe', NFE_NS);
1359
+ const schemaIssues = infEl === undefined ? [] : validar(pl.infNFe, infEl);
1348
1360
  if (schemaIssues.length > 0) {
1349
1361
  return {
1350
1362
  ok: false,
1351
- issues: schemaIssues.map((i) => ({
1352
- path: i.path,
1363
+ ocorrencias: schemaIssues.map((i) => ({
1364
+ caminho: i.caminho,
1353
1365
  code: 'schema',
1354
- message: `${i.code}: ${i.message}`,
1366
+ mensagem: `${i.code}: ${i.mensagem}`,
1355
1367
  origem: 'montagem',
1356
1368
  })),
1357
1369
  };
@@ -1359,14 +1371,14 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1359
1371
  let supl: NfceSupl | undefined;
1360
1372
  if (nfce) {
1361
1373
  // infNFeSupl (grupo ZX, NT 2025.001 item 04): endereços da UF no dia da emissão, pelo horário de Brasília.
1362
- const tabela = urlsNfce(emitUf, options.ambiente, diaBrasilia(agora));
1363
- const urlQr = options.urlQrCode ?? tabela.qrCode;
1364
- const urlChave = options.urlChave ?? tabela.urlChave;
1374
+ const tabela = urlsNfce(emitUf, opcoes.ambiente, diaBrasilia(agora));
1375
+ const urlQr = opcoes.urlQrCode ?? tabela.qrCode;
1376
+ const urlChave = opcoes.urlChave ?? tabela.urlChave;
1365
1377
  if (urlQr === undefined || !/^https?:\/\//i.test(urlQr)) {
1366
1378
  issues.montagem(
1367
1379
  'urlQrCode',
1368
1380
  'qrcode_invalido',
1369
- `sem endereço completo do QR Code da NFC-e para ${emitUf} em ${options.ambiente}: informe urlQrCode (data/nfce-urls.json)`,
1381
+ `sem endereço completo do QR Code da NFC-e para ${emitUf} em ${opcoes.ambiente}: informe urlQrCode (data/nfce-urls.json)`,
1370
1382
  );
1371
1383
  }
1372
1384
  if (urlChave === undefined) {
@@ -1377,11 +1389,11 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1377
1389
  );
1378
1390
  }
1379
1391
  if (urlQr === undefined || urlChave === undefined || !issues.empty)
1380
- return { ok: false, issues: issues.classificadas };
1392
+ return { ok: false, ocorrencias: issues.classificadas };
1381
1393
  const p = await parametrosQrCode(
1382
1394
  {
1383
1395
  chave,
1384
- ambiente: options.ambiente,
1396
+ ambiente: opcoes.ambiente,
1385
1397
  tpEmis,
1386
1398
  dhEmi,
1387
1399
  vNF: (inf.total as { ICMSTot: { vNF: string } }).ICMSTot.vNF,
@@ -1395,13 +1407,13 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1395
1407
  // off-line, com uma assinatura do tamanho da de uma chave RSA de 2048 bits no lugar da que o certificado ainda vai
1396
1408
  // fazer; o `comQrCode` confere de novo com a assinatura verdadeira.
1397
1409
  const suplIssues = conferirSupl(
1398
- inserirSupl({ xml, nfce: supl } as BuiltNfe, supl.assinar ? ASSINATURA_2048 : undefined),
1410
+ inserirSupl({ xml, nfce: supl } as NfeMontada, supl.assinar ? ASSINATURA_2048 : undefined),
1399
1411
  );
1400
- if (suplIssues.length > 0) return { ok: false, issues: suplIssues };
1412
+ if (suplIssues.length > 0) return { ok: false, ocorrencias: suplIssues };
1401
1413
  }
1402
1414
  return {
1403
1415
  ok: true,
1404
- value: {
1416
+ valor: {
1405
1417
  chave,
1406
1418
  id: `NFe${chave}`,
1407
1419
  cNF,
@@ -1418,13 +1430,18 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1418
1430
  }
1419
1431
 
1420
1432
  /** Base64 de uma assinatura RSA de 2048 bits (256 bytes): a forma da assinatura do QR Code off-line na montagem. */
1421
- const ASSINATURA_2048 = base64Encode(new Uint8Array(256));
1433
+ const ASSINATURA_2048 = codificarBase64(new Uint8Array(256));
1422
1434
 
1423
1435
  /** Ocorrências do `infNFeSupl` do XML contra o schema (`TNFe_infNFeSupl`), como de montagem. */
1424
- function conferirSupl(xml: string): ValidationIssue[] {
1425
- const el = firstChild(parseXml(xml).root, 'infNFeSupl', NFE_NS);
1426
- const erros = el === undefined ? [] : validate(TNFe_infNFeSupl as ComplexType, el);
1427
- return erros.map((i) => ({ path: i.path, code: 'schema', message: `${i.code}: ${i.message}`, origem: 'montagem' }));
1436
+ function conferirSupl(xml: string): Ocorrencia[] {
1437
+ const el = primeiroFilho(lerXml(xml).raiz, 'infNFeSupl', NFE_NS);
1438
+ const erros = el === undefined ? [] : validar(TNFe_infNFeSupl as ComplexType, el);
1439
+ return erros.map((i) => ({
1440
+ caminho: i.caminho,
1441
+ code: 'schema',
1442
+ mensagem: `${i.code}: ${i.mensagem}`,
1443
+ origem: 'montagem',
1444
+ }));
1428
1445
  }
1429
1446
 
1430
1447
  const escapeXml = (s: string): string => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -1434,43 +1451,43 @@ const escapeXml = (s: string): string => s.replace(/&/g, '&amp;').replace(/</g,
1434
1451
  * certificado que assina a nota; Manual do DANFE NFC-e 6.0, 4.4.2). `undefined` quando o QR Code não leva assinatura
1435
1452
  * (NF-e, emissão normal, versão 2).
1436
1453
  */
1437
- export async function assinaturaQrCode(built: BuiltNfe, signer: Signer): Promise<string | undefined> {
1438
- return built.nfce?.assinar === true ? assinarParametros(built.nfce.parametros, signer) : undefined;
1454
+ export async function assinaturaQrCode(nota: NfeMontada, assinador: Assinador): Promise<string | undefined> {
1455
+ return nota.nfce?.assinar === true ? assinarParametros(nota.nfce.parametros, assinador) : undefined;
1439
1456
  }
1440
1457
 
1441
1458
  /**
1442
1459
  * A NFC-e montada com o `infNFeSupl` (QR Code e `urlChave`) inserido por splice antes do fechamento de `NFe`, pronta
1443
1460
  * para a assinatura. Na versão 3 em contingência off-line, informe a `assinatura` (`assinaturaQrCode`); nos demais
1444
1461
  * casos ela não existe (ZX02-330, rejeição 445; ZX02-334, rejeição 474). Para assinar em três fases (A3, HSM), passe
1445
- * este texto ao `prepareSignature` do `@sinete/core/xml` com o `id` da nota. Na NF-e (modelo 55), devolve o XML como
1462
+ * este texto ao `prepararAssinatura` do `@sinete/core/xml` com o `id` da nota. Na NF-e (modelo 55), devolve o XML como
1446
1463
  * veio: ela não tem `infNFeSupl` (ZX01-10, rejeição 393).
1447
1464
  */
1448
- export function comQrCode(built: BuiltNfe, assinatura?: string): string {
1449
- const xml = inserirSupl(built, assinatura);
1465
+ export function comQrCode(nota: NfeMontada, assinatura?: string): string {
1466
+ const xml = inserirSupl(nota, assinatura);
1450
1467
  // Com a assinatura verdadeira, o qrCode pode passar do tamanho que a montagem conferiu (chave maior que 2048 bits).
1451
1468
  if (assinatura !== undefined) {
1452
1469
  const issues = conferirSupl(xml);
1453
- if (issues.length > 0) throw new ValidationError('o QR Code da NFC-e não passou no schema', issues);
1470
+ if (issues.length > 0) throw new ErroDeValidacao('o QR Code da NFC-e não passou no schema', issues);
1454
1471
  }
1455
1472
  return xml;
1456
1473
  }
1457
1474
 
1458
- function inserirSupl(built: BuiltNfe, assinatura: string | undefined): string {
1475
+ function inserirSupl(built: NfeMontada, assinatura: string | undefined): string {
1459
1476
  const s = built.nfce;
1460
1477
  if (s === undefined) {
1461
- if (assinatura !== undefined) throw new ConfigError('a NF-e (modelo 55) não tem QR Code');
1478
+ if (assinatura !== undefined) throw new ErroDeConfiguracao('a NF-e (modelo 55) não tem QR Code');
1462
1479
  return built.xml;
1463
1480
  }
1464
1481
  if (s.assinar && assinatura === undefined) {
1465
- throw new ConfigError(
1482
+ throw new ErroDeConfiguracao(
1466
1483
  'NFC-e em contingência off-line com QR Code versão 3 precisa da assinatura (ZX02-334, rejeição 474)',
1467
1484
  );
1468
1485
  }
1469
1486
  if (!s.assinar && assinatura !== undefined) {
1470
- throw new ConfigError('este QR Code não leva assinatura (ZX02-330, rejeição 445)');
1487
+ throw new ErroDeConfiguracao('este QR Code não leva assinatura (ZX02-330, rejeição 445)');
1471
1488
  }
1472
1489
  const fim = '</NFe>';
1473
- if (!built.xml.endsWith(fim)) throw new ConfigError('NFC-e montada fora da forma esperada');
1490
+ if (!built.xml.endsWith(fim)) throw new ErroDeConfiguracao('NFC-e montada fora da forma esperada');
1474
1491
  const qrCode = `${s.base}${s.parametros}${assinatura === undefined ? '' : `|${assinatura}`}`;
1475
1492
  const supl = `<infNFeSupl><qrCode>${escapeXml(qrCode)}</qrCode><urlChave>${escapeXml(s.urlChave)}</urlChave></infNFeSupl>`;
1476
1493
  return built.xml.slice(0, -fim.length) + supl + fim;
@@ -1481,6 +1498,6 @@ function inserirSupl(built: BuiltNfe, assinatura: string | undefined): string {
1481
1498
  * contingência off-line pede); depois, a `Signature` como último filho de `NFe`, tudo por splice, e devolve a string
1482
1499
  * final. É essa string que vai para a SEFAZ e para o banco; nada depois deve reparseá-la para reescrever.
1483
1500
  */
1484
- export async function signNfe(built: BuiltNfe, signer: Signer): Promise<string> {
1485
- return signXml(comQrCode(built, await assinaturaQrCode(built, signer)), { id: built.id }, signer);
1501
+ export async function assinarNfe(nota: NfeMontada, assinador: Assinador): Promise<string> {
1502
+ return assinarXml(comQrCode(nota, await assinaturaQrCode(nota, assinador)), { id: nota.id }, assinador);
1486
1503
  }