@devlas/dte-sii 2.14.3 → 2.16.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/CafSolicitor.js CHANGED
@@ -37,7 +37,7 @@ class CafSolicitor {
37
37
  /**
38
38
  * @param {Object} options - Opciones de configuración
39
39
  * @param {string} options.ambiente - 'certificacion' o 'produccion'
40
- * @param {string} options.rutEmisor - RUT del emisor (ej: 76192083-9)
40
+ * @param {string} options.rutEmisor - RUT del emisor (ej: 76543210-K)
41
41
  * @param {string} options.pfxPath - Ruta absoluta al certificado PFX
42
42
  * @param {string} options.pfxPassword - Contraseña del certificado
43
43
  * @param {string} [options.baseDir] - Directorio base para guardar archivos
@@ -194,7 +194,7 @@ class CafSolicitor {
194
194
  * Detecta la página de bloqueo duro de timbraje del SII.
195
195
  *
196
196
  * El SII usa al menos DOS redacciones para la misma página de rechazo:
197
- * - "NO SE AUTORIZA TIMBRAJE ELECTRÓNICO" (la documentada en PLAN-MEJORAS-CAF.md)
197
+ * - "NO SE AUTORIZA TIMBRAJE ELECTRÓNICO" (ver el CHANGELOG)
198
198
  * - "NO AUTORIZA TIMBRAJE ELECTRÓNICA" (observada 2026-07-22 en tipo 56)
199
199
  *
200
200
  * Detectar solo la primera hacía que la segunda cayera al genérico
@@ -247,7 +247,7 @@ class CafSolicitor {
247
247
  * Verificación de Actividades sale explícito ("no registra Verificación de
248
248
  * Actividades..."), en producción el SII solo dice esto — mismo texto para tipo 39
249
249
  * (boleta) y 33 (factura), sin indicar la causa real. Verificado 2026-07-24 contra
250
- * 78441936-3, en ambos tipos, con el mismo request real repetido dos veces.
250
+ * 79555666-7, en ambos tipos, con el mismo request real repetido dos veces.
251
251
  *
252
252
  * Sin esta detección caía en el genérico `UNKNOWN: No se obtuvo CAF en la
253
253
  * respuesta`, que no distingue este caso (probable Verificación de Actividades,
@@ -281,7 +281,7 @@ class CafSolicitor {
281
281
  * usuarios ni timbrar. Sin este mensaje el usuario ve un error genérico y vuelve a
282
282
  * intentar sin saber que lo único que corresponde es ir al SII.
283
283
  *
284
- * Caso real (12/08/2026, RUT 78441936-3): Verificación de Actividades aprobada,
284
+ * Caso real (12/08/2026, RUT 79555666-7): Verificación de Actividades aprobada,
285
285
  * usuario enrolado en maullin y certificación de boleta enviada — y aun así palena
286
286
  * rechazaba todo, porque la inscripción por internet estaba bloqueada de origen.
287
287
  */
@@ -300,7 +300,7 @@ class CafSolicitor {
300
300
  * autorizado como emisor electrónico en producción — no hay nada que arreglar del lado
301
301
  * del enrolamiento, porque el portal ni siquiera deja abrir la mantención de usuarios.
302
302
  *
303
- * Caso real (12/08/2026, RUT 78441936-3): el flujo de enrolamiento siguió de largo con
303
+ * Caso real (12/08/2026, RUT 79555666-7): el flujo de enrolamiento siguió de largo con
304
304
  * páginas vacías —sin formulario ni hidden `key`— hasta reventar con un 500 en
305
305
  * `eu_graba_usuario`. El 500 era el síntoma; esta frase, en el PRIMER paso, la causa.
306
306
  */
@@ -313,7 +313,7 @@ class CafSolicitor {
313
313
  * ¿Este HTML es un rechazo que hace inútil seguir el flujo?
314
314
  *
315
315
  * ⚠️ Los rechazos del SII NO llegan siempre en la última respuesta. Este caso real
316
- * (12/08/2026, RUT 78441936-3, tipo 39 en palena) llegó en el PASO 2
316
+ * (12/08/2026, RUT 79555666-7, tipo 39 en palena) llegó en el PASO 2
317
317
  * (`of_solicita_folios_dcto`), donde solo se miraba `esBloqueoTimbraje`. El flujo
318
318
  * siguió de largo y terminó devolviendo el genérico `UNKNOWN: No se obtuvo CAF`,
319
319
  * escondiendo un mensaje que la librería ya sabía interpretar desde julio.
@@ -381,7 +381,7 @@ class CafSolicitor {
381
381
  * anulado completamente... los documentos que el Servicio reciba con dichos folios
382
382
  * serán rechazados". Por eso hay que abrir cada rango para saber si sirve, y por eso
383
383
  * este método hace un request por rango. Verificado en maullin el 14/08/2026 con el
384
- * RUT 77967443-6: de 6 rangos listados en el tipo 56, solo 2 eran usables.
384
+ * RUT 76543210-K: de 6 rangos listados en el tipo 56, solo 2 eran usables.
385
385
  *
386
386
  * @param {number} tipoDte
387
387
  * @returns {Promise<Array<{ campos: Object, folioDesde: number, folioHasta: number,
package/EnviadorSII.js CHANGED
@@ -1195,7 +1195,7 @@ class EnviadorSII {
1195
1195
 
1196
1196
  const [rutNum, dv] = rutEmisor.split('-');
1197
1197
  // Fallback a rutEmisor si el certificado no expone RUT (ver
1198
- // docs/EXTRACCION_RUT_CERTIFICADO.md) — evita un TypeError por .split
1198
+ // ver el JSDoc de Certificado) — evita un TypeError por .split
1199
1199
  // sobre null cuando el PFX no tiene el RUT en ningún campo conocido.
1200
1200
  const rutEnvia = this.certificado.rut || rutEmisor;
1201
1201
  const [rutEnviaNum, dvEnvia] = rutEnvia.split('-');
package/FolioService.js CHANGED
@@ -167,7 +167,7 @@ class FolioService {
167
167
  // tipo entraba como candidato válido.
168
168
  //
169
169
  // Pasó de verdad (14/08/2026): al reusar CAF previos, los tipos 56 y 61 del
170
- // RUT 77967443-6 resolvieron a CAF de 78206276-K. El timbre quedó firmado con
170
+ // RUT 76543210-K resolvieron a CAF de 77111222-3. El timbre quedó firmado con
171
171
  // la llave de otro contribuyente y el SII devolvió `RFR - Rechazado por Error
172
172
  // en Firma` para todo el envío, lo que a su vez trababa la declaración de
173
173
  // simulación ("no debe contener documentos con reparos o rechazos").
@@ -761,7 +761,7 @@ class FolioService {
761
761
  // negativas: son resultados esperados (ese rango ya estaba anulado, o sus folios ya
762
762
  // se usaron) y aparecen mezclados con anulaciones exitosas.
763
763
  //
764
- // Contarlos rompía la limpieza (medido el 13/08/2026, RUT 77967443-6, tipo 33):
764
+ // Contarlos rompía la limpieza (medido el 13/08/2026, RUT 76543210-K, tipo 33):
765
765
  // 20 rangos a procesar
766
766
  // ✓ 47-49 anulado ✓ 43-46 anulado
767
767
  // ✗ 31-34 ya anulado ✗ 27-30 ya anulado → CORTE, 8 rangos sin intentar
package/README.md CHANGED
@@ -25,6 +25,7 @@ npm install @devlas/dte-sii
25
25
  - [Libros electrónicos y RCOF](#libros-electrónicos-y-rcof)
26
26
  - [Gestión de folios](#gestión-de-folios)
27
27
  - [Sesión y autenticación con el SII](#sesión-y-autenticación-con-el-sii)
28
+ - [Descargar el XML completo desde el portal (Respaldo MIPYME)](#descargar-el-xml-completo-desde-el-portal-respaldo-mipyme)
28
29
  - [Aceptación y reclamo de DTE (WsReclamo)](#aceptación-y-reclamo-de-dte-wsreclamo)
29
30
  - [Estados SII: Interpretación de respuestas](#estados-sii-interpretación-de-respuestas)
30
31
  - [Manejo de errores](#manejo-de-errores)
@@ -69,7 +70,7 @@ const caf = new CAF(fs.readFileSync('caf_33.xml', 'utf8'))
69
70
  const dte = new DTE({
70
71
  Encabezado: {
71
72
  IdDoc: { TipoDTE: 33, Folio: 1 },
72
- Emisor: { RUTEmisor: '76354771-K', RznSoc: 'Mi Empresa SpA', GiroEmis: 'Software', DirOrigen: 'Av. Ejemplo 123', CmnaOrigen: 'Santiago', Acteco: 620200 },
73
+ Emisor: { RUTEmisor: '76543210-K', RznSoc: 'Mi Empresa SpA', GiroEmis: 'Software', DirOrigen: 'Av. Ejemplo 123', CmnaOrigen: 'Santiago', Acteco: 620200 },
73
74
  Receptor: { RUTRecep: '12345678-9', RznSocRecep: 'Cliente SA', GiroRecep: 'Comercio', DirRecep: 'Calle 456', CmnaRecep: 'Providencia' },
74
75
  },
75
76
  Detalle: [
@@ -81,7 +82,7 @@ dte.generarXML().timbrar(caf).firmar(cert)
81
82
 
82
83
  const envio = new EnvioDTE({ certificado: cert })
83
84
  envio.agregar(dte)
84
- envio.setCaratula({ RutEmisor: '76354771-K', RutReceptor: '60803000-K', FchResol: '2024-01-15', NroResol: 123 })
85
+ envio.setCaratula({ RutEmisor: '76543210-K', RutReceptor: '60803000-K', FchResol: '2024-01-15', NroResol: 123 })
85
86
  envio.generar()
86
87
 
87
88
  const enviador = new EnviadorSII(cert, 'produccion') // o 'certificacion'
@@ -121,7 +122,7 @@ const dte = new DTE({
121
122
  tipo: 33,
122
123
  folio: 1,
123
124
  emisor: {
124
- rut: '76354771-K', razonSocial: 'Mi Empresa SpA',
125
+ rut: '76543210-K', razonSocial: 'Mi Empresa SpA',
125
126
  giro: 'Desarrollo de software', direccion: 'Av. Ejemplo 123',
126
127
  comuna: 'Santiago', actividadEconomica: 620200,
127
128
  },
@@ -158,7 +159,7 @@ const { EnvioDTE, EnviadorSII } = require('@devlas/dte-sii')
158
159
  const envio = new EnvioDTE({ certificado: cert })
159
160
  envio.agregar(dte)
160
161
  envio.setCaratula({
161
- RutEmisor: '76354771-K',
162
+ RutEmisor: '76543210-K',
162
163
  RutReceptor: '60803000-K', // RUT del SII para envíos propios
163
164
  FchResol: '2024-01-15',
164
165
  NroResol: 123,
@@ -178,14 +179,14 @@ const resultado = await enviador.enviarDteSoap(envio)
178
179
  // Estado del sobre (EnvioDTE)
179
180
  const estadoSobre = await enviador.consultarEstado({
180
181
  trackId: resultado.trackId,
181
- rutEmisor: '76354771-K',
182
+ rutEmisor: '76543210-K',
182
183
  })
183
184
  // estadoSobre.esExitoso / esIntermedio / esRechazado
184
185
  // estadoSobre.codigo → 'EPR', 'RPR', 'RSC', etc.
185
186
 
186
187
  // Estado de un DTE individual
187
188
  const estadoDte = await enviador.consultarEstadoDte({
188
- rutEmisor: '76354771-K',
189
+ rutEmisor: '76543210-K',
189
190
  rutReceptor: '12345678-9',
190
191
  tipoDte: 33,
191
192
  folio: 1,
@@ -244,13 +245,13 @@ const fs = require('fs')
244
245
  const cert = new Certificado(fs.readFileSync('empresa_cert.pfx'), 'clave')
245
246
  const caf = new CAF(fs.readFileSync('caf_39_cert.xml', 'utf8'))
246
247
 
247
- const dte = new DTE({ tipo: 39, folio: 1, emisor: { rut: '76354771-K', ... }, items: [ ... ] })
248
+ const dte = new DTE({ tipo: 39, folio: 1, emisor: { rut: '76543210-K', ... }, items: [ ... ] })
248
249
  dte.generarXML().timbrar(caf).firmar(cert)
249
250
 
250
251
  const envio = new EnvioBOLETA({ certificado: cert })
251
252
  envio.agregar(dte)
252
253
  envio.setCaratula({
253
- RutEmisor: '76354771-K',
254
+ RutEmisor: '76543210-K',
254
255
  FchResol: '2019-10-18', // fecha de resolución de certificación (entregada por el SII)
255
256
  NroResol: 0, // siempre 0 en certificación
256
257
  })
@@ -271,13 +272,13 @@ const fs = require('fs')
271
272
  const cert = new Certificado(fs.readFileSync('empresa_prod.pfx'), 'clave')
272
273
  const caf = new CAF(fs.readFileSync('caf_39_prod.xml', 'utf8'))
273
274
 
274
- const dte = new DTE({ tipo: 39, folio: 1, emisor: { rut: '76354771-K', ... }, items: [ ... ] })
275
+ const dte = new DTE({ tipo: 39, folio: 1, emisor: { rut: '76543210-K', ... }, items: [ ... ] })
275
276
  dte.generarXML().timbrar(caf).firmar(cert)
276
277
 
277
278
  const envio = new EnvioBOLETA({ certificado: cert })
278
279
  envio.agregar(dte)
279
280
  envio.setCaratula({
280
- RutEmisor: '76354771-K',
281
+ RutEmisor: '76543210-K',
281
282
  FchResol: '2024-01-15', // fecha real de resolución SII de la empresa
282
283
  NroResol: 123, // número real de resolución SII de la empresa
283
284
  })
@@ -303,7 +304,7 @@ service.cargarCAF(fs.readFileSync('caf_39.xml', 'utf8'))
303
304
 
304
305
  const boleta = await service.crearBoleta({
305
306
  folio: 1,
306
- emisor: { rut: '76354771-K', razonSocial: 'Mi Empresa', giro: 'Software', ... },
307
+ emisor: { rut: '76543210-K', razonSocial: 'Mi Empresa', giro: 'Software', ... },
307
308
  items: [{ nombre: 'Producto', cantidad: 1, precioConIva: 10000 }],
308
309
  resolucion: {
309
310
  fecha: process.env.SII_AMBIENTE === 'certificacion' ? '2019-10-18' : '2024-01-15',
@@ -323,8 +324,8 @@ const { LibroCompraVenta, Certificado } = require('@devlas/dte-sii')
323
324
 
324
325
  const libro = new LibroCompraVenta()
325
326
  libro.setCaratula({
326
- RutEmisorLibro: '76354771-K',
327
- RutEnvia: '76354771-K',
327
+ RutEmisorLibro: '76543210-K',
328
+ RutEnvia: '76543210-K',
328
329
  PeriodoTributario: '2024-06',
329
330
  FchResol: '2024-01-15', NroResol: 123,
330
331
  TipoOperacion: 'VENTA', // o 'COMPRA'
@@ -349,7 +350,7 @@ const { ConsumoFolio, CAF, Certificado } = require('@devlas/dte-sii')
349
350
 
350
351
  const rcof = new ConsumoFolio()
351
352
  rcof.setCaratula({
352
- RutEmisor: '76354771-K',
353
+ RutEmisor: '76543210-K',
353
354
  FchResol: '2024-01-15',
354
355
  NroResol: 0,
355
356
  FchInicio: '2024-06-15',
@@ -395,7 +396,7 @@ const fingerprint = createCafFingerprint(cafXml) // hash único del CA
395
396
 
396
397
  // Reservar el siguiente folio disponible del rango del CAF
397
398
  const folio = registry.reserveNextFolio({
398
- rutEmisor: '76354771-K',
399
+ rutEmisor: '76543210-K',
399
400
  tipoDte: caf.getTipoDTE(),
400
401
  folioDesde: caf.getFolioDesde(),
401
402
  folioHasta: caf.getFolioHasta(),
@@ -407,7 +408,7 @@ const folio = registry.reserveNextFolio({
407
408
 
408
409
  // Marcar folio como enviado al recibir trackId del SII
409
410
  registry.markFolioSent({
410
- rutEmisor: '76354771-K', tipoDte: 33, folio,
411
+ rutEmisor: '76543210-K', tipoDte: 33, folio,
411
412
  folioDesde: caf.getFolioDesde(), folioHasta: caf.getFolioHasta(),
412
413
  ambiente: 'produccion', cafFingerprint: fingerprint,
413
414
  trackId: '0245283324',
@@ -421,7 +422,7 @@ const { resolveCafPath } = require('@devlas/dte-sii')
421
422
 
422
423
  const cafPath = resolveCafPath({
423
424
  tipoDte: 33,
424
- rutEmisor: '76354771-K',
425
+ rutEmisor: '76543210-K',
425
426
  requiredCount: 1, // necesito al menos 1 folio disponible
426
427
  ambiente: 'produccion',
427
428
  })
@@ -439,7 +440,7 @@ const { FolioService, Certificado } = require('@devlas/dte-sii')
439
440
 
440
441
  const service = new FolioService({
441
442
  ambiente: 'produccion',
442
- rutEmisor: '76354771-K',
443
+ rutEmisor: '76543210-K',
443
444
  certificado: new Certificado(fs.readFileSync('empresa.pfx'), 'clave'),
444
445
  })
445
446
 
@@ -493,7 +494,7 @@ const fs = require('fs')
493
494
  const path = require('path')
494
495
 
495
496
  const CAF_DIR = path.join(__dirname, 'cafs')
496
- const RUT = '76354771-K'
497
+ const RUT = '76543210-K'
497
498
  const AMBIENTE = 'produccion'
498
499
  const TIPO_DTE = 33
499
500
  const UMBRAL = 10 // solicitar nuevo CAF cuando queden menos de N folios
@@ -568,6 +569,41 @@ const datos = await auth.obtenerDatosEmpresa()
568
569
  const cookies = await SiiPortalAuth.getCookieStringForPfx(cert)
569
570
  ```
570
571
 
572
+ #### Caché de sesión: un mapa por certificado
573
+
574
+ Un login contra `zeusr.sii.cl` es un handshake con certificado, **caro y contado por el SII**,
575
+ que bloquea el RUT por *"máximo de sesiones autenticadas"*. Por eso las cookies se cachean en
576
+ disco, en `$DATADIR/sii_session_cache.json`, con un TTL de 90 minutos.
577
+
578
+ Desde **2.16.0 el caché es un mapa por huella de certificado**. Antes guardaba una sola sesión,
579
+ así que en un servidor multi-tenant cada certificado invalidaba al anterior y **todos**
580
+ re-autenticaban en cada pasada: el costo crecía lineal con la base de clientes.
581
+
582
+ - El formato viejo se **migra**, no se descarta.
583
+ - Poda automática: expiradas primero, y tope de 200 entradas.
584
+ - Escritura atómica y relectura previa, para que dos réplicas sobre el mismo volumen no se
585
+ borren las sesiones entre sí.
586
+
587
+ ```javascript
588
+ SiiPortalAuth.limpiarSesionCache(certHash) // borra una
589
+ SiiPortalAuth.limpiarSesionCache() // borra todas
590
+ ```
591
+
592
+ > ⚠️ **En un servidor, apunta `DATADIR` a un volumen persistente.** Sin eso el caché vive en el
593
+ > filesystem del contenedor y se pierde en cada redeploy, forzando un re-login de toda la base.
594
+
595
+ #### Reintento ante fallas de red/TLS
596
+
597
+ `autenticar()` reintenta 3 veces con espera progresiva (1s, 2s, 4s) ante errores de transporte.
598
+
599
+ > ⚠️ El SII devuelve **`EPROTO` de forma intermitente** al abrir la conexión TLS con certificado
600
+ > (`rsa_pss ... last octet invalid`), y reintentando con el **mismo** certificado funciona. **No
601
+ > es señal de certificado vencido ni no habilitado**, aunque lo parezca. Interpretarlo así marca
602
+ > como rotos certificados que están sanos.
603
+
604
+ El **límite de sesiones nunca se reintenta** (cada intento empeora el bloqueo) y se distingue por
605
+ `err.code === 'SII_LIMITE_SESIONES'`.
606
+
571
607
  ### SiiSession: sesiones HTTP autenticadas
572
608
 
573
609
  ```javascript
@@ -580,6 +616,91 @@ const resp = await session.request('GET', 'https://herculesr.sii.cl/...')
580
616
 
581
617
  ---
582
618
 
619
+ ## Descargar el XML completo desde el portal (Respaldo MIPYME)
620
+
621
+ `descargarRespaldoMipyme()` baja el **XML firmado completo** de los DTE emitidos o recibidos
622
+ desde el "Respaldo de archivos MIPYME" del portal (`www1.sii.cl/cgi-bin/Portal001`).
623
+
624
+ Es la única vía que entrega el **documento entero**: detalle línea por línea, `CdgItem` del
625
+ proveedor, referencias y TED. `obtenerDetalleDtes()` solo trae metadatos y
626
+ `obtenerResumenRegistro()` solo totales mensuales.
627
+
628
+ **Requiere únicamente el certificado digital**, no estar certificado como emisor.
629
+
630
+ > 🔴 **Existe SOLO en producción: no hay ambiente de certificación.** Medido sobre
631
+ > `/cgi-bin/Portal001/lista_documentos.cgi`: `www1.sii.cl` responde 200, `maullin.sii.cl`
632
+ > redirige a `Error404` y `www4c.sii.cl` da 404 (para contrastar, una ruta real de maullin
633
+ > redirige al login de certificación, no a un 404).
634
+ >
635
+ > Consecuencia para cualquier consumidor: con el resto del sistema apuntando a maullin, este
636
+ > método **igual lee documentos reales del contribuyente**. Es de solo lectura contra el SII,
637
+ > pero escribe facturas reales en la base del entorno que lo llame. Un entorno de desarrollo
638
+ > necesita una puerta explícita; no alcanza con mirar la variable de ambiente del DTE, porque
639
+ > esta función no tiene ambientes.
640
+
641
+ ```javascript
642
+ const auth = new SiiPortalAuth({ pfxBuffer, pfxPassword })
643
+
644
+ const { total, tramos } = await auth.descargarRespaldoMipyme('76543210', '6', {
645
+ origen: 'RCP', // 'ENV' emitidos | 'RCP' recibidos
646
+ desde: '2026-01-01',
647
+ hasta: '2026-08-18',
648
+ tipoDoc: '', // vacío = todos
649
+ reintentos: 3,
650
+ })
651
+ // tramos: [{ desde, hasta, total, xml }, ...] — un XML por tramo
652
+ ```
653
+
654
+ ### Modo streaming (`onTramo`) — obligatorio para históricos grandes
655
+
656
+ Sin `onTramo` **todos los XML quedan en memoria hasta el final**: ~6,4 KB por documento, o sea
657
+ unos 7 MB para 1.100 documentos, y crece lineal.
658
+
659
+ ```javascript
660
+ await auth.descargarRespaldoMipyme(rut, dv, {
661
+ origen: 'RCP', desde, hasta,
662
+ onTramo: async (t) => { await guardar(t.xml) }, // se persiste y se suelta
663
+ })
664
+ // con onTramo, los tramos del resultado vienen SIN `xml`
665
+ ```
666
+
667
+ ### Qué hay que saber del portal
668
+
669
+ | | |
670
+ |---|---|
671
+ | **Tope de 20 por descarga** | Es del servidor, no cosmético. Con 21 devuelve **HTML de error**, no un XML recortado. El método trocea el rango solo, del **más reciente al más viejo**. |
672
+ | **Un día con más de 20** | No se puede partir más por fecha: se lanza error explícito. Tiene salida cortando por `TPO_DOC` (un tipo de DTE por consulta). ⚠️ **No** usar `FOLIO`/`FOLIOHASTA`: borran `FEC_HASTA` en silencio y devuelven otro conjunto. |
673
+ | **Encoding** | El XML viene en **ISO-8859-1**. Leerlo como utf8 rompe los acentos. |
674
+ | **Captcha** | Hoy va vacío, pero el SII puede encenderlo sin avisar → `RESPALDO_CAPTCHA`. |
675
+ | **Alcance** | Solo lo registrado en el sistema de facturación **gratuito** del SII. Un comercio que ya migró a otro sistema no encuentra ahí sus documentos nuevos. |
676
+
677
+ > 🔴 **El portal viejo comunica sus rechazos por `alert()` de JavaScript, con HTTP 200** — no en
678
+ > el HTML visible ni en el `<title>`, que dice otra cosa. Y la página **válida** trae además un
679
+ > `//alert(...)` **comentado**. Clasificar por título, limpiar los `<script>` antes de parsear, o
680
+ > creerle al alert comentado: las tres cosas producen diagnósticos falsos.
681
+
682
+ Los errores traen **`err.mensajePortal`** con el texto exacto del SII. **Mostrar ese texto, no
683
+ una traducción propia.**
684
+
685
+ ⚠️ **`SIN_DATOS` no es lo mismo que `INDETERMINADO`.** El primero es un veredicto definitivo
686
+ ("acá no hay nada") y el consumidor puede cerrar ese período para siempre. Los otros dos
687
+ significan "no se pudo concluir" y "no pudimos preguntar": tratarlos igual cerró en falso un
688
+ período que ya tenía 24 documentos bajados.
689
+
690
+ | código | qué pasó | ¿reintentar? |
691
+ |---|---|---|
692
+ | `RESPALDO_SIN_DATOS` | el RUT no tiene información en MIPYME | no |
693
+ | `RESPALDO_CAPTCHA` | el SII encendió el captcha | no |
694
+ | `RESPALDO_RECHAZADO` | rechazo con un texto que no conocemos; llega literal | no |
695
+ | `RESPALDO_INDETERMINADO` | **varios** mensajes del template, sin veredicto único; llegan en `err.mensajesPortal` | sí, más tarde |
696
+ | `RESPALDO_SIN_EMPRESA` | página de ingreso sin ningún alert; causa no determinada | no |
697
+
698
+ Contrato completo, respuestas reales y los errores que cuestan tiempo (es **POST** no GET;
699
+ `ORIGEN=ENV` no `EMI`; el listado es obligatorio antes de la descarga) en
700
+ la sección de arriba.
701
+
702
+ ---
703
+
583
704
  ## Aceptación y reclamo de DTE (WsReclamo)
584
705
 
585
706
  `WsReclamo` implementa el web service `WSRECLAMO` del SII (v1.2) para registrar eventos de aceptación/rechazo de DTE por parte del receptor.
@@ -594,7 +715,7 @@ const ws = new WsReclamo(new Certificado(fs.readFileSync('empresa.pfx'), 'clave'
594
715
 
595
716
  // Consultar historial de eventos de un DTE
596
717
  const eventos = await ws.listarEventosHistDoc({
597
- rutEmisor: '76354771-K',
718
+ rutEmisor: '76543210-K',
598
719
  tipoDTE: 33,
599
720
  folio: 1,
600
721
  rutReceptor: '12345678-9',
@@ -605,7 +726,7 @@ const estado = await ws.consultarEstadoReceptor({ ... })
605
726
 
606
727
  // Registrar aceptación (ACD) o reclamo (RCD)
607
728
  await ws.ingresarAceptacion({
608
- rutEmisor: '76354771-K', tipoDTE: 33, folio: 1,
729
+ rutEmisor: '76543210-K', tipoDTE: 33, folio: 1,
609
730
  accion: 'ACD', // ACD=Aceptado, RCD=Reclamado, ERM=Otorga Mercaderías
610
731
  })
611
732
  ```
@@ -676,7 +797,7 @@ const { configure, configureRetry } = require('@devlas/dte-sii')
676
797
  // Configuración global (aplicar al inicio de la app)
677
798
  configure({
678
799
  ambiente: 'produccion', // 'produccion' | 'certificacion'
679
- defaultRutEmisor: '76354771-K',
800
+ defaultRutEmisor: '76543210-K',
680
801
  tokenCacheTtlMs: 300_000, // 5 minutos (default)
681
802
  })
682
803
 
@@ -758,7 +879,7 @@ const require = createRequire(import.meta.url)
758
879
  const { Certificado, CAF, DTE, EnviadorSII } = require('@devlas/dte-sii')
759
880
  ```
760
881
 
761
- **TypeScript con ESM** (patrón usado en `devlas-cloud-api-node`):
882
+ **TypeScript con ESM** :
762
883
 
763
884
  ```typescript
764
885
  import { createRequire } from 'module'
@@ -814,7 +935,7 @@ import type {
814
935
  | `FolioService` | `FolioService.js` | Consulta, solicita y anula folios ante el SII |
815
936
  | `CafSolicitor` | `CafSolicitor.js` | Solicitud automatizada de CAF al SII |
816
937
  | `SiiSession` | `SiiSession.js` | Sesiones HTTP autenticadas con certificado (cookie jar) |
817
- | `SiiPortalAuth` | `SiiPortalAuth.js` | Autenticación al portal SII; obtiene datos de empresa; Singleton por cert |
938
+ | `SiiPortalAuth` | `SiiPortalAuth.js` | Autenticación al portal SII; datos de empresa; RCV; **respaldo MIPYME (XML completo)**; caché de sesión por certificado |
818
939
 
819
940
  ### Libros y reportes
820
941
 
@@ -880,7 +1001,7 @@ El directorio `cert/` contiene los helpers necesarios para ejecutar el proceso d
880
1001
  - Generación de muestras impresas
881
1002
 
882
1003
  ```javascript
883
- // Uso desde devlas-cloud-api-node
1004
+ // Uso desde un proyecto ESM
884
1005
  const { CertFolioHelper } = require('@devlas/dte-sii')
885
1006
  ```
886
1007