@devlas/dte-sii 2.18.0 → 2.18.2

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 (2) hide show
  1. package/cert/CertRunner.js +58 -7
  2. package/package.json +2 -2
@@ -3842,9 +3842,25 @@ class CertRunner {
3842
3842
  };
3843
3843
  }
3844
3844
 
3845
- // Si ya hay una revisión activa (no rechazada ni aprobada), no se puede re-subir.
3845
+ // ⚠️ `INGRESO` NO es "ya enviadas": es una revisión creada y **sin someter**.
3846
+ //
3847
+ // El portal deja la revisión en ese estado mientras se suben los PDFs, y solo pasa a
3848
+ // POR REVISAR cuando se llama a `solicitaRevisionSII`. Si la subida se corta a mitad
3849
+ // —el SII devuelve 503 de forma intermitente— queda una revisión con parte de los
3850
+ // archivos que el SII no va a mirar nunca.
3851
+ //
3852
+ // Tratarla como completada fue un bug real (24/08/2026): la subida murió en el archivo
3853
+ // 47 de 64 con un 503, el reintento vio `INGRESO`, devolvió `alreadyCompleted`, la etapa
3854
+ // se marcó cumplida y el pipeline avanzó al Cierre, donde el SII contestó que la empresa
3855
+ // seguía en DOCUMENTOS IMPRESOS. La corrida quedó trabada dando la subida por hecha.
3856
+ //
3857
+ // Ahora se retoma esa misma revisión: se saltea `creaLista` y se sube sobre su ID.
3858
+ const reanudarRevision = estadoActual === 'INGRESO';
3859
+
3860
+ // Si ya hay una revisión activa Y SOMETIDA (no rechazada ni aprobada), no se re-sube.
3846
3861
  // El SII sólo permite crear nueva revisión cuando el estado es RECHAZADO o APROBADO.
3847
- if (estadoActual && estadoActual !== 'RECHAZADO' && estadoActual !== 'APROBADO') {
3862
+ if (estadoActual && !reanudarRevision
3863
+ && estadoActual !== 'RECHAZADO' && estadoActual !== 'APROBADO') {
3848
3864
  // Extraer detalles adicionales de la respuesta leeImpreso para diagnóstico.
3849
3865
  //
3850
3866
  // ⚠️ NO se intenta sacar de acá la cantidad de documentos. La tabla de strings trae
@@ -3869,9 +3885,27 @@ class CertRunner {
3869
3885
  console.log(` → No hay revisión previa — creando primera revisión`);
3870
3886
  }
3871
3887
 
3872
- // ── Paso 2: crear nueva lista/revisión (creaLista) ────────────────────────
3888
+ // ── Paso 2: crear la revisión, o retomar la que quedó a medias ────────────
3889
+ let revId = null;
3890
+ let revIdEncoded = null;
3891
+
3892
+ if (reanudarRevision) {
3893
+ // El ID viaja como Long GWT en la respuesta de `leeImpreso`.
3894
+ for (const m of leeResp.body.matchAll(/'([A-Za-z0-9$_]{3,7})'/g)) {
3895
+ const n = decodeGwtLong(m[1]);
3896
+ if (n >= 10000 && n <= 9999999) { revId = n; revIdEncoded = m[1]; break; }
3897
+ }
3898
+ if (revId) {
3899
+ console.log(` → Revisión ${revId} quedó en INGRESO (creada, sin someter) — se retoma y se completa`);
3900
+ } else {
3901
+ console.log(' → Estado INGRESO pero no se pudo leer el ID de revisión — se crea una nueva');
3902
+ }
3903
+ }
3904
+
3905
+ // ── Paso 2b: crear nueva lista/revisión (creaLista) ───────────────────────
3873
3906
  // Params (6): amb=Z, rutNum, dv, proveedor=rutNum, provDv=dv, provNomre=""
3874
3907
  // String table (9): module, policy, svc, method, String type, Z, rutNum, dv, ""
3908
+ if (!revId) {
3875
3909
  const creaBody =
3876
3910
  `7|0|9|${GWT_BASE}|${policyHash}|${GWT_SVC}|creaLista|` +
3877
3911
  `java.lang.String/2004016611|Z|${rutNum}|${dvChar}||` +
@@ -3899,8 +3933,6 @@ class CertRunner {
3899
3933
  }
3900
3934
 
3901
3935
  // Extraer ID de revisión (Long codificado en GWT base64, ej: 'BAqo' = 264872)
3902
- let revId = null;
3903
- let revIdEncoded = null;
3904
3936
  for (const m of creaResp.body.matchAll(/'([A-Za-z0-9$_]{3,7})'/g)) {
3905
3937
  const n = decodeGwtLong(m[1]);
3906
3938
  if (n >= 10000 && n <= 9999999) { revId = n; revIdEncoded = m[1]; break; }
@@ -3911,8 +3943,10 @@ class CertRunner {
3911
3943
  if (debugDir) {
3912
3944
  fs.writeFileSync(path.join(debugDir, `pdfte-crea-lista-resp.txt`), creaResp.body, 'utf8');
3913
3945
  }
3946
+ } // fin del bloque creaLista (se saltea al retomar una revisión en INGRESO)
3914
3947
 
3915
3948
  // ── Paso 3: subir cada PDF ────────────────────────────────────────────────
3949
+ const SUBIDA_MAX_INTENTOS = 4; // 1 intento + 3 reintentos ante 5xx del portal
3916
3950
  const uploadUrl = `${GWT_BASE}upload`;
3917
3951
  let uploadedCount = 0;
3918
3952
  for (const pdfPath of pdfPaths) {
@@ -3931,7 +3965,7 @@ class CertRunner {
3931
3965
  const closing = Buffer.from(`--${boundary}--\r\n`);
3932
3966
  const formBody = Buffer.concat([partFile, partId, partAmb, closing]);
3933
3967
 
3934
- const upResp = await makeReq(uploadUrl, {
3968
+ const peticionSubida = {
3935
3969
  method: 'POST',
3936
3970
  body: formBody,
3937
3971
  headers: {
@@ -3942,7 +3976,24 @@ class CertRunner {
3942
3976
  'Pragma': 'no-cache',
3943
3977
  },
3944
3978
  cookies,
3945
- });
3979
+ };
3980
+ let upResp = await makeReq(uploadUrl, peticionSubida);
3981
+
3982
+ // ⚠️ El portal del SII devuelve 5xx de forma intermitente, y cortar la subida a mitad
3983
+ // deja la revisión con parte de los archivos y sin someter. Pasó el 24/08/2026: un 503
3984
+ // en el archivo 47 de 64 dejó la revisión en INGRESO y la corrida trabada.
3985
+ //
3986
+ // Un 5xx no dice nada del archivo, dice que el servicio no está: se reintenta el mismo
3987
+ // POST. Un 4xx sí es del archivo o de la sesión, y ahí cortar es lo correcto.
3988
+ let intentoSubida = 1;
3989
+ while (upResp.status >= 500 && intentoSubida < SUBIDA_MAX_INTENTOS) {
3990
+ const espera = 2000 * intentoSubida;
3991
+ console.log(` [!] ${filename} → HTTP ${upResp.status}, reintentando en ${espera / 1000}s ` +
3992
+ `(${intentoSubida}/${SUBIDA_MAX_INTENTOS - 1})`);
3993
+ await sleep(espera);
3994
+ intentoSubida++;
3995
+ upResp = await makeReq(uploadUrl, peticionSubida);
3996
+ }
3946
3997
 
3947
3998
  if (upResp.status !== 200) {
3948
3999
  throw new Error(`pdfdteInternet upload ${filename} → HTTP ${upResp.status}: ${upResp.body.substring(0, 150)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devlas/dte-sii",
3
- "version": "2.18.0",
3
+ "version": "2.18.2",
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",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@xmldom/xmldom": "^0.8.11",
36
- "bwip-js": "^4.8.0",
36
+ "bwip-js": "^4.11.4",
37
37
  "dotenv": "^17.3.1",
38
38
  "fast-xml-parser": "^5.3.3",
39
39
  "got": "^11.8.6",