@sinete/nfe 0.1.0 → 0.3.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 (90) hide show
  1. package/README.md +55 -50
  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/textos.d.ts +26 -0
  13. package/dist/build/textos.d.ts.map +1 -0
  14. package/dist/build/values.d.ts +8 -8
  15. package/dist/build/values.d.ts.map +1 -1
  16. package/dist/decimal.d.ts +2 -2
  17. package/dist/decimal.d.ts.map +1 -1
  18. package/dist/format.d.ts +19 -19
  19. package/dist/format.d.ts.map +1 -1
  20. package/dist/ibs-cbs.d.ts +10 -5
  21. package/dist/ibs-cbs.d.ts.map +1 -1
  22. package/dist/ibs-cbs.js +35 -35
  23. package/dist/ibs-cbs.js.map +3 -3
  24. package/dist/index.d.ts +18 -18
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +796 -588
  27. package/dist/index.js.map +27 -26
  28. package/dist/issues.d.ts +10 -10
  29. package/dist/issues.d.ts.map +1 -1
  30. package/dist/model.d.ts +4 -4
  31. package/dist/ports.d.ts +15 -15
  32. package/dist/ports.d.ts.map +1 -1
  33. package/dist/rotulo.d.ts +2 -2
  34. package/dist/rotulo.d.ts.map +1 -1
  35. package/dist/rtc.d.ts +23 -23
  36. package/dist/rtc.d.ts.map +1 -1
  37. package/dist/services/client.d.ts +44 -40
  38. package/dist/services/client.d.ts.map +1 -1
  39. package/dist/services/gzip.d.ts +1 -1
  40. package/dist/services/gzip.d.ts.map +1 -1
  41. package/dist/services/index.d.ts +4 -4
  42. package/dist/services/index.d.ts.map +1 -1
  43. package/dist/services/outcome.d.ts +3 -3
  44. package/dist/services/outcome.d.ts.map +1 -1
  45. package/dist/services/proc.d.ts +6 -6
  46. package/dist/services/proc.d.ts.map +1 -1
  47. package/dist/services/recuperar.d.ts +8 -7
  48. package/dist/services/recuperar.d.ts.map +1 -1
  49. package/dist/services/resolver.d.ts +12 -11
  50. package/dist/services/resolver.d.ts.map +1 -1
  51. package/dist/services/soap.d.ts +8 -8
  52. package/dist/services/soap.d.ts.map +1 -1
  53. package/dist/time.d.ts +4 -4
  54. package/dist/time.d.ts.map +1 -1
  55. package/dist/versao-gerada.d.ts +1 -1
  56. package/package.json +9 -9
  57. package/src/build/build.ts +213 -203
  58. package/src/build/destinatario.ts +8 -8
  59. package/src/build/ibscbs.ts +10 -6
  60. package/src/build/icms.ts +6 -6
  61. package/src/build/nfce.ts +19 -19
  62. package/src/build/pl.ts +12 -12
  63. package/src/build/rejeicoes.ts +13 -13
  64. package/src/build/textos.ts +228 -0
  65. package/src/build/values.ts +14 -14
  66. package/src/data/arredondamento.json +1 -1
  67. package/src/data/cstat.json +6 -6
  68. package/src/data/fusos.json +1 -1
  69. package/src/data/nfce-urls.json +2 -2
  70. package/src/data/produtor-rural.json +1 -1
  71. package/src/data/resp-tec.json +1 -1
  72. package/src/decimal.ts +10 -9
  73. package/src/format.ts +29 -28
  74. package/src/ibs-cbs.ts +79 -74
  75. package/src/index.ts +29 -27
  76. package/src/issues.ts +11 -11
  77. package/src/model.ts +4 -4
  78. package/src/ports.ts +15 -15
  79. package/src/rotulo.ts +4 -4
  80. package/src/rtc.ts +91 -91
  81. package/src/services/client.ts +248 -217
  82. package/src/services/gzip.ts +7 -7
  83. package/src/services/index.ts +12 -10
  84. package/src/services/outcome.ts +9 -9
  85. package/src/services/proc.ts +50 -49
  86. package/src/services/recuperar.ts +29 -25
  87. package/src/services/resolver.ts +25 -23
  88. package/src/services/soap.ts +35 -35
  89. package/src/time.ts +6 -6
  90. 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';
@@ -62,15 +70,16 @@ import {
62
70
  } from './nfce.ts';
63
71
  import { camposForaDoPl, escolherPl } from './pl.ts';
64
72
  import { cstComIsento, vencimentos } from './rejeicoes.ts';
73
+ import { conferirTextosDaEntrada, textoXmlValido } from './textos.ts';
65
74
  import { buildIcmsUfDest, buildIi, buildIpi, buildIssqn, buildPisCofins, buildPisCofinsSt } from './tributos.ts';
66
75
  import type { Familia } from './values.ts';
67
76
  import { Ctx, clean, digits, TOLERANCIA } from './values.ts';
68
77
 
69
- let padrao: IbsCbsCalculator | undefined;
78
+ let padrao: CalculadoraIbsCbs | undefined;
70
79
 
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();
80
+ /** A calculadora de IBS/CBS usada quando `opcoes.ibsCbs` não vem: o motor do sinete, criada uma vez por processo. */
81
+ function calculadoraPadrao(): CalculadoraIbsCbs {
82
+ padrao ??= calculadoraIbsCbs();
74
83
  return padrao;
75
84
  }
76
85
 
@@ -84,18 +93,18 @@ export const XPROD_HOMOLOGACAO_NFCE = 'NOTA FISCAL EMITIDA EM AMBIENTE DE HOMOLO
84
93
 
85
94
  export type ExigenciaRespTec = 'obrigatorio' | 'opcional';
86
95
 
87
- export interface BuildNfeOptions {
96
+ export interface MontarNfeOpcoes {
88
97
  readonly ambiente: Ambiente;
89
98
  /** Relógio de emissão (dhEmi, PL vigente, CSRT) e de fato gerador (IBS/CBS). */
90
- readonly time: TimeContext;
99
+ readonly tempo: ContextoDeTempo;
91
100
  /**
92
- * Calculadora de IBS/CBS dos itens com `ibsCbs.classificacao`. Padrão: `ibsCbsCalculator()`, o motor do sinete com o
101
+ * Calculadora de IBS/CBS dos itens com `ibsCbs.classificacao`. Padrão: `calculadoraIbsCbs()`, o motor do sinete com o
93
102
  * dataset embarcado (importado na primeira nota que precisar dele) e as alíquotas oficiais. Informe outra para trocar
94
103
  * dataset, alíquotas, base ou regras, ou para calcular fora do sinete.
95
104
  */
96
- readonly ibsCbs?: IbsCbsCalculator;
105
+ readonly ibsCbs?: CalculadoraIbsCbs;
97
106
  /** Fuso do emitente em minutos; padrão pela UF (`data/fusos.json`). */
98
- readonly offsetMinutes?: number;
107
+ readonly deslocamentoMin?: number;
99
108
  /** Versão do aplicativo emissor (`verProc`); padrão `sinete <versão do @sinete/nfe>` (`formatarVerProc`). */
100
109
  readonly verProc?: string;
101
110
  /** Sobrepõe o modo de arredondamento de uma família (`data/arredondamento.json`). */
@@ -105,7 +114,7 @@ export interface BuildNfeOptions {
105
114
  /** Sobrepõe a exigência de infRespTec e CSRT da tabela por UF (`data/resp-tec.json`). */
106
115
  readonly exigencias?: { readonly infRespTec?: ExigenciaRespTec; readonly csrt?: ExigenciaRespTec };
107
116
  /** Fonte de aleatoriedade para o cNF; padrão `crypto.getRandomValues`. */
108
- readonly random?: (bytes: Uint8Array) => Uint8Array;
117
+ readonly aleatorio?: (bytes: Uint8Array) => Uint8Array;
109
118
  /**
110
119
  * O `vPag` do único `detPag` passa a ser o `vNF` calculado na mesma montagem, e o valor informado nele é ignorado.
111
120
  * 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 +137,7 @@ export interface BuildNfeOptions {
128
137
  readonly urlChave?: string;
129
138
  }
130
139
 
131
- export interface BuiltNfe {
140
+ export interface NfeMontada {
132
141
  /** Chave de acesso (44 posições). */
133
142
  readonly chave: string;
134
143
  /** `Id` do `infNFe` (`NFe` + chave): é o que a assinatura referencia. */
@@ -140,21 +149,21 @@ export interface BuiltNfe {
140
149
  readonly tpEmis: string;
141
150
  readonly dhEmi: string;
142
151
  /**
143
- * Só NFC-e: o `infNFeSupl` a acrescentar (`comQrCode`, `signNfe`). Na versão 3 em contingência off-line, falta a
152
+ * Só NFC-e: o `infNFeSupl` a acrescentar (`comQrCode`, `assinarNfe`). Na versão 3 em contingência off-line, falta a
144
153
  * assinatura dos parâmetros, que só o certificado faz (`assinaturaQrCode`).
145
154
  */
146
155
  readonly nfce?: NfceSupl;
147
156
  /** Pacote de liberação usado (escolhido pela vigência). */
148
- readonly pl: VigenciaEntry;
157
+ readonly pl: EntradaDeVigencia;
149
158
  /** O objeto tipado, na forma do PL_010f (o mais novo que o pacote conhece). */
150
159
  readonly infNFe: TNFe_infNFe;
151
160
  /** `<NFe xmlns="...">` com o `infNFe` canônico, sem assinatura, já validado contra o schema. */
152
161
  readonly xml: string;
153
162
  }
154
163
 
155
- export type BuildNfeResult =
156
- | { readonly ok: true; readonly value: BuiltNfe }
157
- | { readonly ok: false; readonly issues: readonly ValidationIssue[] };
164
+ export type ResultadoMontagemNfe =
165
+ | { readonly ok: true; readonly valor: NfeMontada }
166
+ | { readonly ok: false; readonly ocorrencias: readonly Ocorrencia[] };
158
167
 
159
168
  const MODES: Record<Familia, RoundingMode> = Object.fromEntries(
160
169
  Object.entries(arredondamento.familias).map(([k, v]) => [k, v.modo as RoundingMode]),
@@ -164,7 +173,7 @@ const SEM_GTIN = 'SEM GTIN';
164
173
  const BRASILIA = -180;
165
174
 
166
175
  function diaBrasilia(d: Instante): string {
167
- return formatDh(d, BRASILIA).slice(0, 10);
176
+ return formatarDh(d, BRASILIA).slice(0, 10);
168
177
  }
169
178
 
170
179
  /** Exigência de infRespTec ou CSRT para a UF no ambiente e na data (tabela `data/resp-tec.json`). */
@@ -192,29 +201,29 @@ export function exigenciaRespTec(
192
201
  /** `hashCSRT`: Base64(SHA-1(CSRT + chave de acesso)) (NT 2018.005, campo ZD09). */
193
202
  export async function hashCsrt(csrt: string, chave: string): Promise<string> {
194
203
  const d = await globalThis.crypto.subtle.digest('SHA-1', new TextEncoder().encode(csrt + chave));
195
- return base64Encode(new Uint8Array(d));
204
+ return codificarBase64(new Uint8Array(d));
196
205
  }
197
206
 
198
207
  /** Normaliza CNPJ ou CPF, apontando a ocorrência do validador. */
199
208
  function documento(doc: DocumentoPessoa, path: string, issues: Issues): { CNPJ: string } | { CPF: string } {
200
209
  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 };
210
+ const r = lerCnpj(doc.CNPJ, { caminho: `${path}.CNPJ` });
211
+ if (!r.ok) issues.list.push(r.erro);
212
+ return { CNPJ: r.ok ? r.valor : doc.CNPJ };
204
213
  }
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 ?? '') };
214
+ const r = lerCpf(doc.CPF ?? '', { caminho: `${path}.CPF` });
215
+ if (!r.ok) issues.list.push(r.erro);
216
+ return { CPF: r.ok ? r.valor : (doc.CPF ?? '') };
208
217
  }
209
218
 
210
219
  function ie(value: string | undefined, uf: Uf, path: string, issues: Issues, allowIsento: boolean): string | undefined {
211
220
  if (value === undefined) return undefined;
212
- const r = parseIe(value, uf, { path, allowIsento });
221
+ const r = lerIe(value, uf, { caminho: path, aceitarIsento: allowIsento });
213
222
  if (!r.ok) {
214
- issues.list.push(r.error);
223
+ issues.list.push(r.erro);
215
224
  return value;
216
225
  }
217
- return r.value.value;
226
+ return r.valor.valor;
218
227
  }
219
228
 
220
229
  function endereco(e: Endereco): Record<string, string | undefined> {
@@ -242,10 +251,10 @@ function gerarChave(
242
251
  random: (b: Uint8Array) => Uint8Array,
243
252
  issues: Issues,
244
253
  ): { 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 };
254
+ const tentar = (cNF: string): { chave: string; ok: boolean; issue?: Ocorrencia } => {
255
+ const chave = montarChaveAcesso({ ...parts, cNF });
256
+ const r = lerChaveAcesso(chave, { emissao: true, caminho: 'chave' });
257
+ return r.ok ? { chave, ok: true } : { chave, ok: false, issue: r.erro };
249
258
  };
250
259
  if (cNFInformado !== undefined) {
251
260
  if (!/^\d{8}$/.test(cNFInformado)) {
@@ -259,9 +268,9 @@ function gerarChave(
259
268
  const doCnf = r.issue.code === 'chave_cnf_invalido';
260
269
  issues.list.push({
261
270
  ...r.issue,
262
- path: doCnf ? 'cNF' : r.issue.path,
271
+ caminho: doCnf ? 'cNF' : r.issue.caminho,
263
272
  code: 'chave_invalida',
264
- message: `${r.issue.message} (${r.issue.code})`,
273
+ mensagem: `${r.issue.mensagem} (${r.issue.code})`,
265
274
  origem: doCnf ? 'entrada' : 'montagem',
266
275
  });
267
276
  }
@@ -279,7 +288,7 @@ function gerarChave(
279
288
  issues.list.push({
280
289
  ...r.issue,
281
290
  code: 'chave_invalida',
282
- message: `${r.issue.message} (${r.issue.code})`,
291
+ mensagem: `${r.issue.mensagem} (${r.issue.code})`,
283
292
  origem: 'montagem',
284
293
  });
285
294
  return undefined;
@@ -291,9 +300,9 @@ function gerarChave(
291
300
 
292
301
  function referencia(ref: Referenciada, path: string, issues: Issues): TNFe_infNFe_ide_NFref {
293
302
  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;
303
+ const r = lerChaveAcesso(c, { caminho: p });
304
+ if (!r.ok) issues.list.push(r.erro);
305
+ return r.ok ? r.valor.chave : c;
297
306
  };
298
307
  if ('refNFe' in ref) return { refNFe: chave(ref.refNFe, `${path}.refNFe`) };
299
308
  if ('refNFeSig' in ref) {
@@ -312,7 +321,7 @@ function referencia(ref: Referenciada, path: string, issues: Issues): TNFe_infNF
312
321
  // Nota de produtor em papel (modelo 04) extinta: só referência a nota emitida antes do fim na UF dela.
313
322
  if (nfp.mod === '04') {
314
323
  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);
324
+ const cUfSigla = Object.keys(porUf).find((k) => ufPorSigla(k)?.cUF === nfp.cUF);
316
325
  const vedadaDesde = cUfSigla ? (porUf[cUfSigla]?.vedadaDesde ?? '') : produtorRural.refNFP.modelo04.vedadaDesde;
317
326
  if (nfp.AAMM >= vedadaDesde) {
318
327
  issues.add(
@@ -336,25 +345,10 @@ function referencia(ref: Referenciada, path: string, issues: Issues): TNFe_infNF
336
345
  } as TNFe_infNFe_ide_NFref;
337
346
  }
338
347
 
339
- /** Produção `Char` do XML 1.0: tab, LF, CR, U+0020 a U+D7FF, U+E000 a U+FFFD e U+10000 a U+10FFFF. */
340
- function textoXmlValido(texto: string): boolean {
341
- for (const ch of texto) {
342
- const c = ch.codePointAt(0) ?? 0;
343
- const ok =
344
- c === 0x9 ||
345
- c === 0xa ||
346
- c === 0xd ||
347
- (c >= 0x20 && c <= 0xd7ff) ||
348
- (c >= 0xe000 && c <= 0xfffd) ||
349
- (c >= 0x10000 && c <= 0x10ffff);
350
- if (!ok) return false;
351
- }
352
- return true;
353
- }
354
-
355
348
  /**
356
- * Texto com caractere que o XML não representa (vindo, por exemplo, de uma descrição importada) vira ocorrência com o
357
- * caminho do campo, em vez de falhar no parse do documento montado.
349
+ * Texto com caractere que o XML não representa vira ocorrência com o caminho do campo, em vez de falhar no parse do
350
+ * documento montado. Os textos da entrada já foram conferidos com o caminho dela (`conferirTextosDaEntrada`): aqui só
351
+ * sobra o que veio das opções da montagem.
358
352
  */
359
353
  function textosForaDoXml(value: unknown, path: string, issues: Issues): void {
360
354
  if (typeof value === 'string') {
@@ -576,10 +570,10 @@ function montarItem(ctx: Ctx, item: Item, n: number, normal: boolean): ItemMonta
576
570
  if (item.infAdProd !== undefined) det.infAdProd = item.infAdProd;
577
571
  if (item.obsItem !== undefined) det.obsItem = item.obsItem;
578
572
  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);
573
+ const r = lerChaveAcesso(item.DFeReferenciado.chaveAcesso, { caminho: `${path}.DFeReferenciado.chaveAcesso` });
574
+ if (!r.ok) ctx.issues.list.push(r.erro);
581
575
  det.DFeReferenciado = clean({
582
- chaveAcesso: r.ok ? r.value.chave : item.DFeReferenciado.chaveAcesso,
576
+ chaveAcesso: r.ok ? r.valor.chave : item.DFeReferenciado.chaveAcesso,
583
577
  nItem: item.DFeReferenciado.nItem === undefined ? undefined : String(item.DFeReferenciado.nItem),
584
578
  });
585
579
  }
@@ -616,44 +610,44 @@ function composicao(m: ItemMontado, incluirProd: boolean): Decimal {
616
610
  }
617
611
 
618
612
  /** 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> {
613
+ export async function montarNfe(entrada: DadosNfe, opcoes: MontarNfeOpcoes): Promise<ResultadoMontagemNfe> {
620
614
  const issues = new Issues();
621
- const finNFeIn = input.finNFe ?? '1';
622
- const mod = input.modelo ?? '55';
615
+ const finNFeIn = entrada.finNFe ?? '1';
616
+ const mod = entrada.modelo ?? '55';
623
617
  if (mod !== '55' && mod !== '65') {
624
618
  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 };
619
+ return { ok: false, ocorrencias: issues.classificadas };
626
620
  }
627
621
  const nfce = mod === '65';
628
622
  // 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
623
  // é 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)) {
624
+ const indPres = entrada.indPres ?? (nfce ? '1' : finNFeIn === '2' || finNFeIn === '3' ? '0' : '9');
625
+ const indFinal = entrada.indFinal ?? (nfce || entrada.destinatario?.indIEDest === '9' ? '1' : '0');
626
+ const ctx = new Ctx(issues, { ...MODES, ...opcoes.arredondamento }, finNFeIn !== '2' && finNFeIn !== '3');
627
+ const pRedutorGov = entrada.gCompraGov && ctx.req(entrada.gCompraGov.pRedutor, 'gCompraGov.pRedutor', D0302A04);
628
+ const emitUf = entrada.emitente.endereco.UF;
629
+ if (!ehUf(emitUf)) {
636
630
  issues.add('emitente.endereco.UF', 'campo_invalido', 'UF do emitente inválida');
637
- return { ok: false, issues: issues.classificadas };
631
+ return { ok: false, ocorrencias: issues.classificadas };
638
632
  }
639
- const cUF = ufBySigla(emitUf)?.cUF ?? '';
640
- const offset = options.offsetMinutes ?? offsetDaUf(emitUf);
641
- const agora = options.time.emissao.now();
633
+ const cUF = ufPorSigla(emitUf)?.cUF ?? '';
634
+ const offset = opcoes.deslocamentoMin ?? deslocamentoDaUf(emitUf);
635
+ const agora = opcoes.tempo.emissao.agora();
642
636
  // 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);
637
+ const fatoGerador = opcoes.tempo.fatoGerador.agora();
638
+ const dhEmi = formatarDh(agora, offset);
645
639
  const aamm = dhEmi.slice(2, 4) + dhEmi.slice(5, 7);
646
- const tpAmb = tpAmbOf(options.ambiente);
640
+ const tpAmb = tpAmbDoAmbiente(opcoes.ambiente);
647
641
 
648
- // PL vigente (VigenciaError do schemas propaga: data fora de toda vigência é erro de configuração, não de dado).
642
+ // PL vigente (ErroVigencia do schemas propaga: data fora de toda vigência é erro de configuração, não de dado).
649
643
  // 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));
644
+ const pl = escolherPl(opcoes.ambiente, relogioFixo(agora));
651
645
 
652
646
  // Emitente (grupo C)
653
- const e: Emitente = input.emitente;
647
+ const e: Emitente = entrada.emitente;
654
648
  const emitDoc = documento(e, 'emitente', issues);
655
- const serie = Number(input.serie);
656
- const nNF = Number(input.nNF);
649
+ const serie = Number(entrada.serie);
650
+ const nNF = Number(entrada.nNF);
657
651
  if (!Number.isInteger(serie) || serie < 0 || serie > 999) issues.add('serie', 'serie_invalida', 'série de 0 a 999');
658
652
  if (!Number.isInteger(nNF) || nNF < 1 || nNF > 999_999_999)
659
653
  issues.add('nNF', 'campo_invalido', 'nNF de 1 a 999999999');
@@ -687,7 +681,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
687
681
 
688
682
  // 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
683
  // com as demais regras da NFC-e).
690
- const d: Destinatario | undefined = input.destinatario;
684
+ const d: Destinatario | undefined = entrada.destinatario;
691
685
  let dest: Record<string, unknown> | undefined;
692
686
  let destUf: Uf | 'EX' | undefined;
693
687
  if (d === undefined) {
@@ -723,7 +717,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
723
717
  : d.IE;
724
718
  dest = clean({
725
719
  ...docDest,
726
- xNome: options.ambiente === 'homologacao' ? XNOME_HOMOLOGACAO : d.xNome,
720
+ xNome: opcoes.ambiente === 'homologacao' ? XNOME_HOMOLOGACAO : d.xNome,
727
721
  enderDest,
728
722
  indIEDest: d.indIEDest,
729
723
  IE: destIe,
@@ -734,7 +728,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
734
728
  }
735
729
 
736
730
  // Identificação: contingência e forma de emissão
737
- const cont = input.contingencia;
731
+ const cont = entrada.contingencia;
738
732
  const tpEmis = cont?.tpEmis ?? '1';
739
733
  if (cont !== undefined) {
740
734
  if (cont.xJust.length < 15 || cont.xJust.length > 256) {
@@ -766,25 +760,25 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
766
760
  // Identificação que as regras do modelo conferem antes de montar.
767
761
  // Na NFC-e a operação é sempre interna (B11a-10, rejeição 707): o endereço do consumidor não a torna interestadual.
768
762
  const idDest =
769
- input.idDest ?? (nfce ? '1' : destUf === 'EX' ? '3' : destUf !== undefined && destUf !== emitUf ? '2' : '1');
770
- const tpImp = input.tpImp ?? (nfce ? '4' : '1');
763
+ entrada.idDest ?? (nfce ? '1' : destUf === 'EX' ? '3' : destUf !== undefined && destUf !== emitUf ? '2' : '1');
764
+ const tpImp = entrada.tpImp ?? (nfce ? '4' : '1');
771
765
  conferirDestinatario(
772
- input,
766
+ entrada,
773
767
  {
774
768
  mod,
775
- tpNF: input.tpNF,
769
+ tpNF: entrada.tpNF,
776
770
  idDest,
777
- idDestInformado: input.idDest !== undefined,
771
+ idDestInformado: entrada.idDest !== undefined,
778
772
  indFinal,
779
- producao: options.ambiente === 'producao',
773
+ producao: opcoes.ambiente === 'producao',
780
774
  },
781
775
  issues,
782
776
  );
783
777
  if (nfce) {
784
778
  conferirNfce(
785
- input,
779
+ entrada,
786
780
  {
787
- tpNF: input.tpNF,
781
+ tpNF: entrada.tpNF,
788
782
  idDest,
789
783
  tpImp,
790
784
  finNFe: finNFeIn,
@@ -796,7 +790,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
796
790
  } else if (tpImp === '4' || tpImp === '5') {
797
791
  issues.add('tpImp', 'campo_invalido', 'tpImp 4 e 5 (DANFC-e) são da NFC-e (B21-20, rejeição 710)');
798
792
  }
799
- const qr: QrCodeNfceOpcoes = options.qrCode ?? { versao: '3' };
793
+ const qr: QrCodeNfceOpcoes = opcoes.qrCode ?? { versao: '3' };
800
794
  if (nfce && qr.versao === '2') {
801
795
  // 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
796
  if (!/^\d{1,6}$/.test(qr.idCSC) || Number(qr.idCSC) === 0) {
@@ -824,12 +818,13 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
824
818
 
825
819
  // Chave de acesso e cNF
826
820
  const random =
827
- options.random ?? ((b: Uint8Array): Uint8Array => globalThis.crypto.getRandomValues(b as Uint8Array<ArrayBuffer>));
821
+ opcoes.aleatorio ??
822
+ ((b: Uint8Array): Uint8Array => globalThis.crypto.getRandomValues(b as Uint8Array<ArrayBuffer>));
828
823
  const emitente14 = 'CNPJ' in emitDoc ? emitDoc.CNPJ : emitDoc.CPF;
829
824
  const gerada = issues.empty
830
825
  ? gerarChave(
831
826
  { cUF, aamm, emitente: emitente14, mod, serie: String(serie), nNF: String(nNF), tpEmis },
832
- input.cNF,
827
+ entrada.cNF,
833
828
  random,
834
829
  issues,
835
830
  )
@@ -839,24 +834,24 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
839
834
  const cDV = chave.slice(43);
840
835
 
841
836
  // Itens
842
- if (input.itens.length === 0 || input.itens.length > 990) {
837
+ if (entrada.itens.length === 0 || entrada.itens.length > 990) {
843
838
  issues.add('itens', 'itens_limite', 'a NF-e tem de 1 a 990 itens');
844
839
  }
845
- const itens = input.itens.map((it, n) => montarItem(ctx, it, n, finNFeIn === '1'));
840
+ const itens = entrada.itens.map((it, n) => montarItem(ctx, it, n, finNFeIn === '1'));
846
841
  // 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
842
  // E04-20: o builder a põe, e a descrição informada fica fora do XML de teste.
848
843
  const primeiro = itens[0];
849
- if (nfce && options.ambiente === 'homologacao' && primeiro !== undefined) {
844
+ if (nfce && opcoes.ambiente === 'homologacao' && primeiro !== undefined) {
850
845
  (primeiro.det.prod as unknown as Record<string, unknown>).xProd = XPROD_HOMOLOGACAO_NFCE;
851
846
  }
852
847
 
853
848
  // IBS/CBS pela calculadora das opções, ou pelo motor do sinete
854
- const classificados = input.itens
849
+ const classificados = entrada.itens
855
850
  .map((it, n) => ({ it, n }))
856
851
  .filter(({ it }) => it.impostos.ibsCbs?.classificacao !== undefined);
857
852
  if (classificados.length > 0) {
858
- const calculadora = options.ibsCbs ?? calculadoraPadrao();
859
- const reqs: IbsCbsItemRequest[] = classificados.map(({ it, n }) => {
853
+ const calculadora = opcoes.ibsCbs ?? calculadoraPadrao();
854
+ const reqs: PedidoIbsCbsItem[] = classificados.map(({ it, n }) => {
860
855
  const c = it.impostos.ibsCbs?.classificacao as NonNullable<
861
856
  NonNullable<typeof it.impostos.ibsCbs>['classificacao']
862
857
  >;
@@ -894,29 +889,29 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
894
889
  };
895
890
  });
896
891
  const destino =
897
- input.entrega !== undefined
898
- ? { UF: input.entrega.UF, cMun: input.entrega.cMun }
892
+ entrada.entrega !== undefined
893
+ ? { UF: entrada.entrega.UF, cMun: entrada.entrega.cMun }
899
894
  : d?.endereco !== undefined
900
895
  ? isExterior(d.endereco)
901
896
  ? { UF: 'EX' as const, cMun: '9999999' }
902
897
  : { UF: d.endereco.UF, cMun: d.endereco.cMun }
903
898
  : undefined;
904
- const compra = input.gCompraGov;
899
+ const compra = entrada.gCompraGov;
905
900
  const resp = await calculadora.calcular({
906
901
  nota: {
907
902
  fatoGerador,
908
903
  emissao: agora,
909
- ambiente: options.ambiente,
904
+ ambiente: opcoes.ambiente,
910
905
  mod,
911
- tpNF: input.tpNF,
906
+ tpNF: entrada.tpNF,
912
907
  finNFe: finNFeIn,
913
- ...(input.tpNFDebito ? { tpNFDebito: input.tpNFDebito } : {}),
914
- ...(input.tpNFCredito ? { tpNFCredito: input.tpNFCredito } : {}),
908
+ ...(entrada.tpNFDebito ? { tpNFDebito: entrada.tpNFDebito } : {}),
909
+ ...(entrada.tpNFCredito ? { tpNFCredito: entrada.tpNFCredito } : {}),
915
910
  indFinal,
916
911
  indPres,
917
912
  emitente: { UF: emitUf, cMun: e.endereco.cMun, CRT: e.CRT },
918
913
  ...(destino ? { destino } : {}),
919
- ...(input.cMunFGIBS ? { cMunFGIBS: input.cMunFGIBS } : {}),
914
+ ...(entrada.cMunFGIBS ? { cMunFGIBS: entrada.cMunFGIBS } : {}),
920
915
  ...(compra && pRedutorGov
921
916
  ? {
922
917
  compraGov: {
@@ -929,7 +924,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
929
924
  },
930
925
  itens: reqs,
931
926
  });
932
- issues.addAll(resp.issues ?? [], 'montagem');
927
+ issues.addAll(resp.ocorrencias ?? [], 'montagem');
933
928
  for (const { n } of classificados) {
934
929
  const g = resp.itens.find((x) => x.nItem === n + 1);
935
930
  if (g === undefined) {
@@ -951,7 +946,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
951
946
  const ib = `itens[${n}].impostos.ibsCbs`;
952
947
  const is = `itens[${n}].impostos.is`;
953
948
  // 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;
949
+ const ibsDaCalculadora = entrada.itens[n]?.impostos.ibsCbs?.grupo === undefined;
955
950
  const antes = issues.list.length;
956
951
  decimaisInvalidos(imp.IBSCBS, ib, issues);
957
952
  if (ibsDaCalculadora) issues.reclassificarDesde(antes, 'montagem');
@@ -962,7 +957,14 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
962
957
  grupoInvalido(TISCt as ComplexType, 'IS', imp.IS, is, issues);
963
958
  }
964
959
  });
965
- if (!issues.empty) return { ok: false, issues: issues.classificadas };
960
+ // Texto e tamanho dos campos de texto pelo tipo do PL, com o caminho da entrada (ADR 0011); o campo que outra
961
+ // conferência já recusou fica só com a ocorrência dela.
962
+ // Em homologação, o nome do destinatário e, na NFC-e, a descrição do primeiro item são as literais da E04-20 e da I04-10:
963
+ // o texto informado não vai ao XML.
964
+ const substituidos =
965
+ opcoes.ambiente === 'homologacao' ? ['destinatario.xNome', ...(nfce ? ['itens[0].produto.xProd'] : [])] : [];
966
+ conferirTextosDaEntrada(entrada, pl.infNFe as ComplexType, issues, new Set(substituidos));
967
+ if (!issues.empty) return { ok: false, ocorrencias: issues.classificadas };
966
968
 
967
969
  // Totais (grupo W)
968
970
  const gruposIbsCbs = itens
@@ -1096,43 +1098,43 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1096
1098
  const ide = clean({
1097
1099
  cUF,
1098
1100
  cNF,
1099
- natOp: input.natOp,
1101
+ natOp: entrada.natOp,
1100
1102
  mod,
1101
1103
  serie: String(serie),
1102
1104
  nNF: String(nNF),
1103
1105
  dhEmi,
1104
- dhSaiEnt: input.dhSaiEnt === undefined ? undefined : formatDh(input.dhSaiEnt, offset),
1105
- dPrevEntrega: input.dPrevEntrega,
1106
- tpNF: input.tpNF,
1106
+ dhSaiEnt: entrada.dhSaiEnt === undefined ? undefined : formatarDh(entrada.dhSaiEnt, offset),
1107
+ dPrevEntrega: entrada.dPrevEntrega,
1108
+ tpNF: entrada.tpNF,
1107
1109
  idDest,
1108
- cMunFG: input.cMunFG ?? e.endereco.cMun,
1109
- cMunFGIBS: input.cMunFGIBS,
1110
+ cMunFG: entrada.cMunFG ?? e.endereco.cMun,
1111
+ cMunFGIBS: entrada.cMunFGIBS,
1110
1112
  tpImp,
1111
1113
  tpEmis,
1112
1114
  cDV,
1113
1115
  tpAmb,
1114
1116
  finNFe,
1115
- tpNFDebito: input.tpNFDebito,
1116
- tpNFCredito: input.tpNFCredito,
1117
+ tpNFDebito: entrada.tpNFDebito,
1118
+ tpNFCredito: entrada.tpNFCredito,
1117
1119
  indFinal,
1118
1120
  indPres,
1119
1121
  // 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),
1122
+ indIntermed: entrada.indIntermed ?? (['2', '3', '4', '9'].includes(indPres) ? '0' : undefined),
1121
1123
  procEmi: '0',
1122
- verProc: options.verProc ?? formatarVerProc('sinete', VERSAO_PACOTE),
1123
- dhCont: cont === undefined ? undefined : formatDh(cont.dhCont, offset),
1124
+ verProc: opcoes.verProc ?? formatarVerProc('sinete', VERSAO_PACOTE),
1125
+ dhCont: cont === undefined ? undefined : formatarDh(cont.dhCont, offset),
1124
1126
  xJust: cont?.xJust,
1125
- NFref: input.referenciadas?.map((r, n) => referencia(r, `referenciadas[${n}]`, issues)),
1127
+ NFref: entrada.referenciadas?.map((r, n) => referencia(r, `referenciadas[${n}]`, issues)),
1126
1128
  gCompraGov:
1127
- input.gCompraGov === undefined
1129
+ entrada.gCompraGov === undefined
1128
1130
  ? undefined
1129
1131
  : clean({
1130
- tpEnteGov: input.gCompraGov.tpEnteGov,
1132
+ tpEnteGov: entrada.gCompraGov.tpEnteGov,
1131
1133
  pRedutor: ctx.s(pRedutorGov ?? Decimal.ZERO, D0302A04),
1132
- tpOperGov: input.gCompraGov.tpOperGov,
1133
- refDFeAnt: input.gCompraGov.refDFeAnt as string[] | undefined,
1134
+ tpOperGov: entrada.gCompraGov.tpOperGov,
1135
+ refDFeAnt: entrada.gCompraGov.refDFeAnt as string[] | undefined,
1134
1136
  }),
1135
- gPagAntecipado: input.gPagAntecipado === undefined ? undefined : { refNFe: [...input.gPagAntecipado] },
1137
+ gPagAntecipado: entrada.gPagAntecipado === undefined ? undefined : { refNFe: [...entrada.gPagAntecipado] },
1136
1138
  });
1137
1139
 
1138
1140
  // Locais, autorizados, transporte, cobrança, pagamento, informações adicionais
@@ -1148,10 +1150,10 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1148
1150
  email: l.email,
1149
1151
  IE: l.IE,
1150
1152
  });
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));
1153
+ if ((entrada.autXML?.length ?? 0) > 10) issues.add('autXML', 'campo_invalido', 'no máximo 10 autorizados');
1154
+ const autXML = entrada.autXML?.map((a, n) => documento(a, `autXML[${n}]`, issues));
1153
1155
 
1154
- const tr = input.transporte;
1156
+ const tr = entrada.transporte;
1155
1157
  const transp: Record<string, unknown> = { modFrete: tr?.modFrete ?? '9' };
1156
1158
  if (tr?.transportador !== undefined) {
1157
1159
  const t = tr.transportador;
@@ -1194,8 +1196,8 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1194
1196
  }
1195
1197
 
1196
1198
  let cobr: Record<string, unknown> | undefined;
1197
- if (input.cobranca !== undefined) {
1198
- const c = input.cobranca;
1199
+ if (entrada.cobranca !== undefined) {
1200
+ const c = entrada.cobranca;
1199
1201
  cobr = {};
1200
1202
  if (c.fatura !== undefined) {
1201
1203
  const f = c.fatura;
@@ -1225,10 +1227,10 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1225
1227
  }
1226
1228
  }
1227
1229
 
1228
- const pagIn = input.pagamento ?? { detPag: [{ tPag: '90' as const, vPag: '0' }] };
1229
- const igualTotal = options.pagamentoIgualTotal === true;
1230
+ const pagIn = entrada.pagamento ?? { detPag: [{ tPag: '90' as const, vPag: '0' }] };
1231
+ const igualTotal = opcoes.pagamentoIgualTotal === true;
1230
1232
  if (igualTotal) {
1231
- const unico = input.pagamento?.detPag.length === 1 ? input.pagamento.detPag[0] : undefined;
1233
+ const unico = entrada.pagamento?.detPag.length === 1 ? entrada.pagamento.detPag[0] : undefined;
1232
1234
  if (unico === undefined) {
1233
1235
  issues.add('pagamento.detPag', 'pagamento_igual_total', 'pagamentoIgualTotal pede exatamente um detPag');
1234
1236
  } else if (unico.tPag === '90') {
@@ -1253,7 +1255,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1253
1255
  }) as unknown as PagMontado;
1254
1256
  // 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
1257
  // 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) {
1258
+ if (nfce && entrada.pagamento !== undefined && entrada.pagamento.detPag.length > 0 && issues.empty) {
1257
1259
  pagamentoNfce(pag, vNF, issues);
1258
1260
  }
1259
1261
  // W16-40 (rejeição 750): acima do limite, a NFC-e identifica o destinatário por CNPJ, CPF ou idEstrangeiro.
@@ -1264,7 +1266,7 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1264
1266
  `NFC-e acima de R$ ${NFCE_LIMITE_SEM_DESTINATARIO} identifica o destinatário (W16-40, rejeição 750)`,
1265
1267
  );
1266
1268
  }
1267
- const ia = input.informacoesAdicionais;
1269
+ const ia = entrada.informacoesAdicionais;
1268
1270
  const infAdic =
1269
1271
  ia === undefined || Object.values(ia).every((x) => x === undefined)
1270
1272
  ? undefined
@@ -1277,18 +1279,18 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1277
1279
  });
1278
1280
 
1279
1281
  // 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;
1282
+ const exig = { ...exigenciaRespTec(emitUf, opcoes.ambiente, agora), ...opcoes.exigencias };
1283
+ const rt = entrada.respTec ?? opcoes.respTec;
1282
1284
  let infRespTec: Record<string, unknown> | undefined;
1283
1285
  if (rt === undefined) {
1284
1286
  if (exig.infRespTec === 'obrigatorio') {
1285
1287
  issues.add('respTec', 'resp_tec_obrigatorio', `a UF ${emitUf} exige o responsável técnico (rejeição 972)`);
1286
1288
  }
1287
1289
  } else {
1288
- const cnpjRt = parseCnpj(rt.CNPJ, { path: 'respTec.CNPJ' });
1289
- if (!cnpjRt.ok) issues.list.push(cnpjRt.error);
1290
+ const cnpjRt = lerCnpj(rt.CNPJ, { caminho: 'respTec.CNPJ' });
1291
+ if (!cnpjRt.ok) issues.list.push(cnpjRt.erro);
1290
1292
  infRespTec = {
1291
- CNPJ: cnpjRt.ok ? cnpjRt.value : rt.CNPJ,
1293
+ CNPJ: cnpjRt.ok ? cnpjRt.valor : rt.CNPJ,
1292
1294
  xContato: rt.xContato,
1293
1295
  email: rt.email,
1294
1296
  fone: digits(rt.fone),
@@ -1307,51 +1309,54 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1307
1309
  ide,
1308
1310
  emit,
1309
1311
  dest,
1310
- retirada: local(input.retirada, 'retirada'),
1311
- entrega: local(input.entrega, 'entrega'),
1312
+ retirada: local(entrada.retirada, 'retirada'),
1313
+ entrega: local(entrada.entrega, 'entrega'),
1312
1314
  autXML,
1313
1315
  det: itens.map((m) => m.det),
1314
1316
  total,
1315
1317
  transp,
1316
1318
  cobr,
1317
1319
  pag,
1318
- infIntermed: input.infIntermed,
1320
+ infIntermed: entrada.infIntermed,
1319
1321
  infAdic,
1320
- exporta: input.exporta,
1321
- compra: input.compra,
1322
- cana: input.cana,
1322
+ exporta: entrada.exporta,
1323
+ compra: entrada.compra,
1324
+ cana: entrada.cana,
1323
1325
  infRespTec,
1324
- agropecuario: input.agropecuario,
1326
+ agropecuario: entrada.agropecuario,
1325
1327
  }) as unknown as TNFe_infNFe;
1326
1328
 
1327
1329
  // Regras da SEFAZ que só dependem do documento (ADR 0012).
1328
- cstComIsento(input, idDest, issues);
1329
- vencimentos(input, { dhEmi, instante: agora }, issues);
1330
+ cstComIsento(entrada, idDest, issues);
1331
+ vencimentos(entrada, { dhEmi, instante: agora }, issues);
1330
1332
 
1331
1333
  camposForaDoPl(pl.infNFe, inf, 'infNFe', pl.vigencia.pl, issues);
1332
1334
  textosForaDoXml(inf, 'infNFe', issues);
1333
- if (!issues.empty) return { ok: false, issues: issues.classificadas };
1335
+ if (!issues.empty) return { ok: false, ocorrencias: issues.classificadas };
1334
1336
 
1335
1337
  // 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
1338
+ // O serializer recusa com ErroSerializacao o que não cabe no modelo (escolhas exclusivas informadas juntas, por
1337
1339
  // exemplo vagao e balsa): vira ocorrência com o caminho do XML, nunca exceção.
1338
1340
  let xml: string;
1339
1341
  try {
1340
- xml = `<NFe xmlns="${NFE_NS}">${serialize(pl.infNFe, 'infNFe', inf, NFE_NS)}</NFe>`;
1342
+ xml = `<NFe xmlns="${NFE_NS}">${serializar(pl.infNFe, 'infNFe', inf, NFE_NS)}</NFe>`;
1341
1343
  } catch (e) {
1342
- if (!(e instanceof SerializeError)) throw e;
1343
- return { ok: false, issues: [{ path: e.path, code: 'schema', message: e.message, origem: 'montagem' }] };
1344
+ if (!(e instanceof ErroSerializacao)) throw e;
1345
+ return {
1346
+ ok: false,
1347
+ ocorrencias: [{ caminho: e.caminho, code: 'schema', mensagem: e.message, origem: 'montagem' }],
1348
+ };
1344
1349
  }
1345
- const doc = parseXml(xml);
1346
- const infEl = firstChild(doc.root, 'infNFe', NFE_NS);
1347
- const schemaIssues = infEl === undefined ? [] : validate(pl.infNFe, infEl);
1350
+ const doc = lerXml(xml);
1351
+ const infEl = primeiroFilho(doc.raiz, 'infNFe', NFE_NS);
1352
+ const schemaIssues = infEl === undefined ? [] : validar(pl.infNFe, infEl);
1348
1353
  if (schemaIssues.length > 0) {
1349
1354
  return {
1350
1355
  ok: false,
1351
- issues: schemaIssues.map((i) => ({
1352
- path: i.path,
1356
+ ocorrencias: schemaIssues.map((i) => ({
1357
+ caminho: i.caminho,
1353
1358
  code: 'schema',
1354
- message: `${i.code}: ${i.message}`,
1359
+ mensagem: `${i.code}: ${i.mensagem}`,
1355
1360
  origem: 'montagem',
1356
1361
  })),
1357
1362
  };
@@ -1359,14 +1364,14 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1359
1364
  let supl: NfceSupl | undefined;
1360
1365
  if (nfce) {
1361
1366
  // 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;
1367
+ const tabela = urlsNfce(emitUf, opcoes.ambiente, diaBrasilia(agora));
1368
+ const urlQr = opcoes.urlQrCode ?? tabela.qrCode;
1369
+ const urlChave = opcoes.urlChave ?? tabela.urlChave;
1365
1370
  if (urlQr === undefined || !/^https?:\/\//i.test(urlQr)) {
1366
1371
  issues.montagem(
1367
1372
  'urlQrCode',
1368
1373
  'qrcode_invalido',
1369
- `sem endereço completo do QR Code da NFC-e para ${emitUf} em ${options.ambiente}: informe urlQrCode (data/nfce-urls.json)`,
1374
+ `sem endereço completo do QR Code da NFC-e para ${emitUf} em ${opcoes.ambiente}: informe urlQrCode (data/nfce-urls.json)`,
1370
1375
  );
1371
1376
  }
1372
1377
  if (urlChave === undefined) {
@@ -1377,11 +1382,11 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1377
1382
  );
1378
1383
  }
1379
1384
  if (urlQr === undefined || urlChave === undefined || !issues.empty)
1380
- return { ok: false, issues: issues.classificadas };
1385
+ return { ok: false, ocorrencias: issues.classificadas };
1381
1386
  const p = await parametrosQrCode(
1382
1387
  {
1383
1388
  chave,
1384
- ambiente: options.ambiente,
1389
+ ambiente: opcoes.ambiente,
1385
1390
  tpEmis,
1386
1391
  dhEmi,
1387
1392
  vNF: (inf.total as { ICMSTot: { vNF: string } }).ICMSTot.vNF,
@@ -1395,13 +1400,13 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1395
1400
  // off-line, com uma assinatura do tamanho da de uma chave RSA de 2048 bits no lugar da que o certificado ainda vai
1396
1401
  // fazer; o `comQrCode` confere de novo com a assinatura verdadeira.
1397
1402
  const suplIssues = conferirSupl(
1398
- inserirSupl({ xml, nfce: supl } as BuiltNfe, supl.assinar ? ASSINATURA_2048 : undefined),
1403
+ inserirSupl({ xml, nfce: supl } as NfeMontada, supl.assinar ? ASSINATURA_2048 : undefined),
1399
1404
  );
1400
- if (suplIssues.length > 0) return { ok: false, issues: suplIssues };
1405
+ if (suplIssues.length > 0) return { ok: false, ocorrencias: suplIssues };
1401
1406
  }
1402
1407
  return {
1403
1408
  ok: true,
1404
- value: {
1409
+ valor: {
1405
1410
  chave,
1406
1411
  id: `NFe${chave}`,
1407
1412
  cNF,
@@ -1418,13 +1423,18 @@ export async function buildNfe(input: NfeInput, options: BuildNfeOptions): Promi
1418
1423
  }
1419
1424
 
1420
1425
  /** 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));
1426
+ const ASSINATURA_2048 = codificarBase64(new Uint8Array(256));
1422
1427
 
1423
1428
  /** 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' }));
1429
+ function conferirSupl(xml: string): Ocorrencia[] {
1430
+ const el = primeiroFilho(lerXml(xml).raiz, 'infNFeSupl', NFE_NS);
1431
+ const erros = el === undefined ? [] : validar(TNFe_infNFeSupl as ComplexType, el);
1432
+ return erros.map((i) => ({
1433
+ caminho: i.caminho,
1434
+ code: 'schema',
1435
+ mensagem: `${i.code}: ${i.mensagem}`,
1436
+ origem: 'montagem',
1437
+ }));
1428
1438
  }
1429
1439
 
1430
1440
  const escapeXml = (s: string): string => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
@@ -1434,43 +1444,43 @@ const escapeXml = (s: string): string => s.replace(/&/g, '&amp;').replace(/</g,
1434
1444
  * 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
1445
  * (NF-e, emissão normal, versão 2).
1436
1446
  */
1437
- export async function assinaturaQrCode(built: BuiltNfe, signer: Signer): Promise<string | undefined> {
1438
- return built.nfce?.assinar === true ? assinarParametros(built.nfce.parametros, signer) : undefined;
1447
+ export async function assinaturaQrCode(nota: NfeMontada, assinador: Assinador): Promise<string | undefined> {
1448
+ return nota.nfce?.assinar === true ? assinarParametros(nota.nfce.parametros, assinador) : undefined;
1439
1449
  }
1440
1450
 
1441
1451
  /**
1442
1452
  * A NFC-e montada com o `infNFeSupl` (QR Code e `urlChave`) inserido por splice antes do fechamento de `NFe`, pronta
1443
1453
  * para a assinatura. Na versão 3 em contingência off-line, informe a `assinatura` (`assinaturaQrCode`); nos demais
1444
1454
  * 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
1455
+ * este texto ao `prepararAssinatura` do `@sinete/core/xml` com o `id` da nota. Na NF-e (modelo 55), devolve o XML como
1446
1456
  * veio: ela não tem `infNFeSupl` (ZX01-10, rejeição 393).
1447
1457
  */
1448
- export function comQrCode(built: BuiltNfe, assinatura?: string): string {
1449
- const xml = inserirSupl(built, assinatura);
1458
+ export function comQrCode(nota: NfeMontada, assinatura?: string): string {
1459
+ const xml = inserirSupl(nota, assinatura);
1450
1460
  // Com a assinatura verdadeira, o qrCode pode passar do tamanho que a montagem conferiu (chave maior que 2048 bits).
1451
1461
  if (assinatura !== undefined) {
1452
1462
  const issues = conferirSupl(xml);
1453
- if (issues.length > 0) throw new ValidationError('o QR Code da NFC-e não passou no schema', issues);
1463
+ if (issues.length > 0) throw new ErroDeValidacao('o QR Code da NFC-e não passou no schema', issues);
1454
1464
  }
1455
1465
  return xml;
1456
1466
  }
1457
1467
 
1458
- function inserirSupl(built: BuiltNfe, assinatura: string | undefined): string {
1468
+ function inserirSupl(built: NfeMontada, assinatura: string | undefined): string {
1459
1469
  const s = built.nfce;
1460
1470
  if (s === undefined) {
1461
- if (assinatura !== undefined) throw new ConfigError('a NF-e (modelo 55) não tem QR Code');
1471
+ if (assinatura !== undefined) throw new ErroDeConfiguracao('a NF-e (modelo 55) não tem QR Code');
1462
1472
  return built.xml;
1463
1473
  }
1464
1474
  if (s.assinar && assinatura === undefined) {
1465
- throw new ConfigError(
1475
+ throw new ErroDeConfiguracao(
1466
1476
  'NFC-e em contingência off-line com QR Code versão 3 precisa da assinatura (ZX02-334, rejeição 474)',
1467
1477
  );
1468
1478
  }
1469
1479
  if (!s.assinar && assinatura !== undefined) {
1470
- throw new ConfigError('este QR Code não leva assinatura (ZX02-330, rejeição 445)');
1480
+ throw new ErroDeConfiguracao('este QR Code não leva assinatura (ZX02-330, rejeição 445)');
1471
1481
  }
1472
1482
  const fim = '</NFe>';
1473
- if (!built.xml.endsWith(fim)) throw new ConfigError('NFC-e montada fora da forma esperada');
1483
+ if (!built.xml.endsWith(fim)) throw new ErroDeConfiguracao('NFC-e montada fora da forma esperada');
1474
1484
  const qrCode = `${s.base}${s.parametros}${assinatura === undefined ? '' : `|${assinatura}`}`;
1475
1485
  const supl = `<infNFeSupl><qrCode>${escapeXml(qrCode)}</qrCode><urlChave>${escapeXml(s.urlChave)}</urlChave></infNFeSupl>`;
1476
1486
  return built.xml.slice(0, -fim.length) + supl + fim;
@@ -1481,6 +1491,6 @@ function inserirSupl(built: BuiltNfe, assinatura: string | undefined): string {
1481
1491
  * contingência off-line pede); depois, a `Signature` como último filho de `NFe`, tudo por splice, e devolve a string
1482
1492
  * final. É essa string que vai para a SEFAZ e para o banco; nada depois deve reparseá-la para reescrever.
1483
1493
  */
1484
- export async function signNfe(built: BuiltNfe, signer: Signer): Promise<string> {
1485
- return signXml(comQrCode(built, await assinaturaQrCode(built, signer)), { id: built.id }, signer);
1494
+ export async function assinarNfe(nota: NfeMontada, assinador: Assinador): Promise<string> {
1495
+ return assinarXml(comQrCode(nota, await assinaturaQrCode(nota, assinador)), { id: nota.id }, assinador);
1486
1496
  }