@devlas/dte-sii 2.14.3 → 2.15.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.
@@ -447,7 +447,11 @@ class BoletaCert {
447
447
  // 4. Enviar EnvioBOLETA
448
448
  console.log('\nEnviando EnvioBOLETA al SII...');
449
449
  const enviador = new EnviadorSII(this.certificado, this.ambiente);
450
- const resultadoBoleta = await enviador.enviarBoletaSoap(envioBoleta);
450
+ // `let`, no `const`: la rama de duplicado más abajo lo reasigna. Con `const` eso lanzaba
451
+ // `TypeError: Assignment to constant variable` justo en el path que pretende recuperarse.
452
+ // Sin disparador confirmado: el duplicado real de EnvioBOLETA vuelve STATUS 99 (no 7), y
453
+ // los STATUS 7 observados son cvc-* de Libros, que EnviadorSII rutea a duplicado:false.
454
+ let resultadoBoleta = await enviador.enviarBoletaSoap(envioBoleta);
451
455
 
452
456
  if (!resultadoBoleta.ok) {
453
457
  if (resultadoBoleta.duplicado) {
@@ -58,6 +58,32 @@ const { STEPS, emitProgress } = require('../utils/progress');
58
58
  * @property {string} [sessionPath]
59
59
  */
60
60
 
61
+ /**
62
+ * CAF que consume cada set de certificación. **Fuente única de verdad**: la usan tanto los
63
+ * `ejecutarSet*` (como fallback cuando el set no trae `cafRequired`) como
64
+ * `precargarPlanDeCorrida()`. Antes estos números vivían inline en cada `ejecutarSet*` y los
65
+ * consumidores los duplicaban por su cuenta, así que un cambio acá los dejaba desincronizados
66
+ * y timbrando mal.
67
+ *
68
+ * El valor de `guia` es una función porque depende de cuántos casos traiga el set.
69
+ * El default de 3 sale de tráfico real: en 17 corridas registradas el set de guía vino
70
+ * siempre con `cafRequired: { 52: 3 }` y 3 casos. Antes la lib usaba 1, que timbraba de menos.
71
+ */
72
+ const CAF_POR_SET = {
73
+ basico: { 33: 4, 56: 1, 61: 3 },
74
+ guia: (setData) => ({ 52: setData?.casos?.length || 3 }),
75
+ exenta: { 34: 3, 56: 1, 61: 4 },
76
+ compra: { 46: 1, 56: 1, 61: 1 },
77
+ };
78
+
79
+ /** Nombre de la estructura y su plan de CAF, en el orden en que se ejecutan los sets. */
80
+ const SETS_DE_CORRIDA = [
81
+ ['basico', 'setBasico', CAF_POR_SET.basico],
82
+ ['guia', 'setGuiaDespacho', CAF_POR_SET.guia],
83
+ ['exenta', 'setFacturaExenta', CAF_POR_SET.exenta],
84
+ ['compra', 'setFacturaCompra', CAF_POR_SET.compra],
85
+ ];
86
+
61
87
  class CertRunner {
62
88
  /**
63
89
  * @param {CertConfig} config - Configuración del runner
@@ -302,6 +328,45 @@ class CertRunner {
302
328
  return this._cafsPrecargados;
303
329
  }
304
330
 
331
+ /**
332
+ * Timbra por adelantado el total de folios de la corrida, armando el plan desde los sets ya
333
+ * cargados en `_estructuras`. Azúcar sobre `precargarCafsDeSets()` para que el consumidor no
334
+ * tenga que duplicar los `cafRequired` de cada set (ver `CAF_POR_SET`).
335
+ *
336
+ * Llamar después de `obtenerSets()` y antes del primer `ejecutarSet*`.
337
+ *
338
+ * ⚠️ **Hay que pasar los sets que realmente se van a ejecutar.** `_estructuras` puede traer
339
+ * los cuatro aunque la corrida ejecute uno solo, porque se rehidrata desde disco. Precargar
340
+ * todo en ese caso timbraría folios de sets que nadie va a emitir, y quedarían autorizados
341
+ * sin usar: exactamente el gatillo del racionamiento del SII que este método viene a evitar.
342
+ *
343
+ * @param {Array<'basico'|'guia'|'exenta'|'compra'>} [sets] - Sets de esta corrida.
344
+ * @returns {Promise<Object>} { 33: cafPath, 56: cafPath, ... }
345
+ */
346
+ async precargarPlanDeCorrida(sets = ['basico', 'guia', 'exenta', 'compra']) {
347
+ if (!this._estructuras) {
348
+ throw new Error('Ejecutar obtenerSets() antes de precargarPlanDeCorrida().');
349
+ }
350
+
351
+ const pedidos = new Set(sets);
352
+ const planes = [];
353
+ for (const [nombre, claveEstructura, fallback] of SETS_DE_CORRIDA) {
354
+ if (!pedidos.has(nombre)) continue;
355
+ const setData = this._estructuras[claveEstructura];
356
+ if (!setData) continue; // set opcional ausente en esta corrida
357
+ planes.push(
358
+ setData.cafRequired || (typeof fallback === 'function' ? fallback(setData) : fallback)
359
+ );
360
+ }
361
+
362
+ if (!planes.length) {
363
+ console.log('\n Nada que precargar: ninguno de los sets pedidos está presente.');
364
+ return {};
365
+ }
366
+ return this.precargarCafsDeSets(planes);
367
+ }
368
+
369
+
305
370
  /**
306
371
  * Devuelve los CAF precargados que le tocan a un set.
307
372
  *
@@ -803,7 +868,7 @@ class CertRunner {
803
868
 
804
869
  async ejecutarSetBasico(casos) {
805
870
  emitProgress(STEPS.SET_START, { set: 'basico' });
806
- const r = await this._ejecutarSet(SetBasico, 'setBasico', 'basico', { 33: 4, 56: 1, 61: 3 }, 'basico', casos);
871
+ const r = await this._ejecutarSet(SetBasico, 'setBasico', 'basico', CAF_POR_SET.basico, 'basico', casos);
807
872
  if (r.success) emitProgress(STEPS.SET_OK, { set: 'basico', trackId: r.trackId });
808
873
  else emitProgress(STEPS.SET_ERROR, { set: 'basico', error: r.error });
809
874
  return r;
@@ -812,7 +877,7 @@ class CertRunner {
812
877
  async ejecutarSetGuia(casos) {
813
878
  emitProgress(STEPS.SET_START, { set: 'guia' });
814
879
  const r = await this._ejecutarSet(SetGuia, 'setGuiaDespacho', 'guia',
815
- (setData) => ({ 52: setData.casos?.length || 1 }), 'guia', casos);
880
+ CAF_POR_SET.guia, 'guia', casos);
816
881
  if (r.success) emitProgress(STEPS.SET_OK, { set: 'guia', trackId: r.trackId });
817
882
  else emitProgress(STEPS.SET_ERROR, { set: 'guia', error: r.error });
818
883
  return r;
@@ -820,7 +885,7 @@ class CertRunner {
820
885
 
821
886
  async ejecutarSetExenta(casos) {
822
887
  emitProgress(STEPS.SET_START, { set: 'exenta' });
823
- const r = await this._ejecutarSet(SetExenta, 'setFacturaExenta', 'exenta', { 34: 3, 56: 1, 61: 4 }, 'exenta', casos);
888
+ const r = await this._ejecutarSet(SetExenta, 'setFacturaExenta', 'exenta', CAF_POR_SET.exenta, 'exenta', casos);
824
889
  if (r.success) emitProgress(STEPS.SET_OK, { set: 'exenta', trackId: r.trackId });
825
890
  else emitProgress(STEPS.SET_ERROR, { set: 'exenta', error: r.error });
826
891
  return r;
@@ -828,7 +893,7 @@ class CertRunner {
828
893
 
829
894
  async ejecutarSetCompra(casos) {
830
895
  emitProgress(STEPS.SET_START, { set: 'compra' });
831
- const r = await this._ejecutarSet(SetCompra, 'setFacturaCompra', 'compra', { 46: 1, 56: 1, 61: 1 }, 'compra', casos);
896
+ const r = await this._ejecutarSet(SetCompra, 'setFacturaCompra', 'compra', CAF_POR_SET.compra, 'compra', casos);
832
897
  if (r.success) emitProgress(STEPS.SET_OK, { set: 'compra', trackId: r.trackId });
833
898
  else emitProgress(STEPS.SET_ERROR, { set: 'compra', error: r.error });
834
899
  return r;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devlas/dte-sii",
3
- "version": "2.14.3",
3
+ "version": "2.15.0",
4
4
  "description": "Facturación y boletas electrónicas para el SII de Chile. Genera, timbra, firma y envía DTEs, libros electrónicos y automatiza la certificación.",
5
5
  "main": "index.js",
6
6
  "types": "dte-sii.d.ts",
@@ -8,7 +8,7 @@
8
8
  "author": "Devlas SpA <ti@devlas.cl> (https://devlas.cl)",
9
9
  "homepage": "https://github.com/devlas-cl/dte-sii",
10
10
  "scripts": {
11
- "test": "node test/pfx.test.js && node test/folios-consolidados.test.js && node test/c14n-apostrofe.test.js"
11
+ "test": "node test/pfx.test.js && node test/folios-consolidados.test.js && node test/c14n-apostrofe.test.js && node test/precargar-plan-corrida.test.js"
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",