@sinete/nfe 0.2.0 → 0.4.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.
package/src/rtc.ts CHANGED
@@ -28,7 +28,13 @@ import { calcular, ErroClassificacao, ErroRegimeNaoSuportado } from '@sinete/ibs
28
28
  import type { Regra } from '@sinete/ibs-cbs/validar';
29
29
  import { documentoDoRoc, validar } from '@sinete/ibs-cbs/validar';
30
30
  import type { DatasetIbsCbs } from '@sinete/ibs-cbs-dados';
31
- import type { CalculadoraIbsCbs, PedidoIbsCbsItem, PedidoIbsCbsNota, RespostaIbsCbs } from './ports.ts';
31
+ import type {
32
+ AliquotaIbsCbsInformada,
33
+ CalculadoraIbsCbs,
34
+ PedidoIbsCbsItem,
35
+ PedidoIbsCbsNota,
36
+ RespostaIbsCbs,
37
+ } from './ports.ts';
32
38
 
33
39
  /** Grupo `IBSCBS` do item, como o `@sinete/nfe` o recebe. */
34
40
  export type GrupoIbsCbs = RespostaIbsCbs['itens'][number]['IBSCBS'];
@@ -109,15 +115,42 @@ function grupoDoLeiaute(g: IBSCBS, indDoacao: '1' | undefined): GrupoIbsCbs {
109
115
  } as GrupoIbsCbs;
110
116
  }
111
117
 
112
- /** Erro do motor ou das alíquotas como ocorrência no item (ou na nota, quando o erro não diz o item). */
113
- function ocorrenciaDoMotor(e: unknown, itens: readonly PedidoIbsCbsItem[]): Ocorrencia | undefined {
118
+ /**
119
+ * Erro do motor ou das alíquotas como uma ocorrência no caminho do item (o primeiro, quando o erro não diz o item).
120
+ * `semAliquota` acha o item que pediu a alíquota desconhecida: o erro do provedor diz o tributo e a data, não o item.
121
+ */
122
+ function ocorrenciaDoMotor(
123
+ e: unknown,
124
+ itens: readonly PedidoIbsCbsItem[],
125
+ semAliquota: () => number | undefined,
126
+ ): Ocorrencia | undefined {
114
127
  if (e instanceof ErroClassificacao || e instanceof ErroRegimeNaoSuportado) {
115
128
  const path = e.item !== undefined ? caminho(e.item) : caminho(itens[0]?.nItem ?? 1);
116
129
  return { caminho: path, code: e.code, mensagem: e.message, origem: 'entrada' };
117
130
  }
118
131
  // Alíquota que o sinete não conhece para a data: falta de dado do pacote, não da nota.
119
- if (e instanceof ErroAliquotaDesconhecida)
120
- return { caminho: 'impostos.ibsCbs', code: e.code, mensagem: e.message, origem: 'montagem' };
132
+ if (e instanceof ErroAliquotaDesconhecida) {
133
+ const n = semAliquota() ?? itens[0]?.nItem ?? 1;
134
+ return { caminho: caminho(n), code: e.code, mensagem: e.message, origem: 'montagem' };
135
+ }
136
+ return undefined;
137
+ }
138
+
139
+ /**
140
+ * O primeiro item (pela ordem de `nItem`, a do motor) cujo cálculo sozinho pede uma alíquota desconhecida. Só roda na
141
+ * recusa, para pôr a ocorrência no item em vez de na nota.
142
+ */
143
+ function primeiroItemSemAliquota(
144
+ op: OperacaoClassificada,
145
+ calc: (op: OperacaoClassificada) => unknown,
146
+ ): number | undefined {
147
+ for (const it of [...op.itens].sort((a, b) => a.n - b.n)) {
148
+ try {
149
+ calc({ ...op, itens: [it] });
150
+ } catch (e) {
151
+ if (e instanceof ErroAliquotaDesconhecida) return it.n;
152
+ }
153
+ }
121
154
  return undefined;
122
155
  }
123
156
 
@@ -234,16 +267,18 @@ function calcularCom(
234
267
  itens: classificados,
235
268
  };
236
269
  const time = contextoDeTempo({ emissao: relogioFixo(nota.emissao), fatoGerador: relogioFixo(nota.fatoGerador) });
237
- let roc: Roc;
238
- try {
239
- roc = calcular(op, {
270
+ const calc = (o: OperacaoClassificada): Roc =>
271
+ calcular(o, {
240
272
  dataset,
241
273
  aliquotas: rates,
242
274
  tempo: time,
243
275
  ...(options.deslocamentoMin === undefined ? {} : { deslocamentoMin: options.deslocamentoMin }),
244
276
  });
277
+ let roc: Roc;
278
+ try {
279
+ roc = calc(op);
245
280
  } catch (e) {
246
- const issue = ocorrenciaDoMotor(e, itens);
281
+ const issue = ocorrenciaDoMotor(e, itens, () => primeiroItemSemAliquota(op, calc));
247
282
  if (issue === undefined) throw e;
248
283
  return { itens: [], ocorrencias: [issue] };
249
284
  }
@@ -295,11 +330,17 @@ function calcularCom(
295
330
  }
296
331
 
297
332
  const porItem = new Map(itens.map((it) => [it.nItem, it]));
333
+ const informadas: AliquotaIbsCbsInformada[] = roc.itens.flatMap((r) =>
334
+ r.aliquotas
335
+ .filter((a) => a.situacao === 'informada')
336
+ .map((a) => ({ nItem: r.nItem, tributo: a.tributo, valor: a.valor, motivo: a.motivo ?? '' })),
337
+ );
298
338
  return {
299
339
  itens: roc.itens.map((r) => ({
300
340
  nItem: r.nItem,
301
341
  IBSCBS: grupoDoLeiaute(r.IBSCBS, porItem.get(r.nItem)?.indDoacao),
302
342
  })),
343
+ ...(informadas.length === 0 ? {} : { aliquotasInformadas: informadas }),
303
344
  ...(issues.length === 0 ? {} : { ocorrencias: issues }),
304
345
  };
305
346
  }
@@ -11,7 +11,7 @@
11
11
 
12
12
  import type { DocumentoXml, ElementoXml } from '@sinete/core/xml';
13
13
  import { lerXml, primeiroFilho, textoDe } from '@sinete/core/xml';
14
- import type { ClienteNfe, EventoRegistrado, ResultadoConsulta } from './client.ts';
14
+ import type { ClienteNfe, EnvioOpcoes, EventoRegistrado, ResultadoConsulta } from './client.ts';
15
15
  import { cstatEm } from './outcome.ts';
16
16
  import { NFE_NS, recortarElemento } from './proc.ts';
17
17
 
@@ -73,14 +73,16 @@ function lerProcEvento(xml: string): EventoRegistrado | undefined {
73
73
  * vários, como na CC-e; com `nSeqEvento`, só o dessa sequência). Serve depois de um pedido de evento sem resposta ou
74
74
  * respondido com 573 ou 580: nunca conclua que o evento existe só pelo `cStat` do pedido. `registrado: false` quer
75
75
  * dizer que a consulta não mostrou o evento (ou não decidiu: veja `consulta`); não quer dizer que o evento não existe.
76
+ * `opcoes.signal` cancela a consulta.
76
77
  */
77
78
  export async function recuperarEventoRegistrado(
78
79
  cliente: ClienteNfe,
79
80
  chave: string,
80
81
  tpEvento: string,
81
82
  nSeqEvento?: number,
83
+ opcoes?: EnvioOpcoes,
82
84
  ): Promise<RecuperacaoEvento> {
83
- const consulta = await cliente.consultar(chave);
85
+ const consulta = await cliente.consultar(chave, undefined, opcoes);
84
86
  if (consulta.tipo !== 'autorizado' && consulta.tipo !== 'denegado') return { registrado: false, consulta };
85
87
  let achado: EventoRegistrado | undefined;
86
88
  for (const xml of consulta.valor.eventos) {
@@ -28,7 +28,7 @@
28
28
 
29
29
  import type { Recusado } from '@sinete/core';
30
30
  import { criarAutorizado, criarDenegado } from '@sinete/core';
31
- import type { ClienteNfe, ProtocoloNfe, ResultadoAutorizacao, ResultadoConsulta } from './client.ts';
31
+ import type { ClienteNfe, EnvioOpcoes, ProtocoloNfe, ResultadoAutorizacao, ResultadoConsulta } from './client.ts';
32
32
  import { cstatEm } from './outcome.ts';
33
33
  import { documentoAssinado } from './proc.ts';
34
34
 
@@ -85,12 +85,14 @@ export function chaveDaDuplicidade(xMotivo: string): string | undefined {
85
85
 
86
86
  /**
87
87
  * Resolve um envio de autorização sem resposta, ou cuja resposta foi 204 ou 539, consultando a chave da NF-e
88
- * assinada. `anterior` é o desfecho do envio, quando houve um.
88
+ * assinada. `anterior` é o desfecho do envio, quando houve um. `opcoes.signal` cancela a consulta (lança o
89
+ * `ErroTransporte` com `code: 'cancelado'`).
89
90
  */
90
91
  export async function resolverEnvioSemResposta(
91
92
  cliente: ClienteNfe,
92
93
  nfeAssinada: string,
93
94
  anterior?: ResultadoAutorizacao,
95
+ opcoes?: EnvioOpcoes,
94
96
  ): Promise<ResolucaoEnvio> {
95
97
  const a = documentoAssinado(nfeAssinada, 'NFe', 'infNFe');
96
98
  const chave = a.id.slice(3);
@@ -98,7 +100,7 @@ export async function resolverEnvioSemResposta(
98
100
  const outra = chaveDaDuplicidade(anterior.xMotivo);
99
101
  return { acao: 'divergente', motivo: anterior, ...(outra === undefined ? {} : { chNFe: outra }) };
100
102
  }
101
- const consulta = await cliente.consultar(chave, nfeAssinada);
103
+ const consulta = await cliente.consultar(chave, nfeAssinada, opcoes);
102
104
  if (consulta.tipo === 'recusado') {
103
105
  if (cstatEm(consulta.cStat, 'naoConsta')) return { acao: 'reenviar', nfeAssinada: a.xml };
104
106
  // 561, 562 e 613 na consulta: a chave local não consta, mas a numeração dela tem outra NF-e (outro mês, outro cNF
@@ -1,3 +1,3 @@
1
1
  // GERADO por scripts/versao-gerada.ts a partir do package.json. Não edite: rode `bun scripts/versao-gerada.ts`.
2
2
  /** Versão de `@sinete/nfe`, para o padrão de `verProc`/`verAplic` (`formatarVerProc` do @sinete/core). */
3
- export const VERSAO_PACOTE = '0.2.0';
3
+ export const VERSAO_PACOTE = '0.4.0';