@devlas/dte-sii 2.14.1 → 2.14.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 +90 -5
  2. package/package.json +1 -1
@@ -4081,6 +4081,41 @@ class CertRunner {
4081
4081
  req.setTimeout(20000, () => { req.destroy(); reject(new Error('Timeout')); });
4082
4082
  });
4083
4083
 
4084
+ const [rutNum, dvChar] = String(this.config.emisor.rut).replace(/\./g, '').split('-');
4085
+
4086
+ /** POST de formulario, para el segundo paso del timbraje. Se captura igual que el GET. */
4087
+ const postForm = (url, campos) => new Promise((resolve, reject) => {
4088
+ const cuerpo = Object.entries(campos)
4089
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v ?? '')}`).join('&');
4090
+ const u = new URL(url);
4091
+ const _t0 = Date.now();
4092
+ const req = https.request({
4093
+ hostname: u.hostname, path: u.pathname + u.search, method: 'POST',
4094
+ headers: {
4095
+ 'Cookie': cookieStr,
4096
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
4097
+ 'Content-Type': 'application/x-www-form-urlencoded',
4098
+ 'Content-Length': Buffer.byteLength(cuerpo),
4099
+ },
4100
+ rejectUnauthorized: false,
4101
+ }, (res) => {
4102
+ const chunks = [];
4103
+ res.on('data', c => chunks.push(c));
4104
+ res.on('end', () => {
4105
+ const _body = Buffer.concat(chunks).toString('utf-8');
4106
+ registrarHttpDebug({
4107
+ url, method: 'POST', status: res.statusCode, headers: res.headers,
4108
+ body: _body, reqBody: cuerpo, ms: Date.now() - _t0,
4109
+ cliente: 'verificarAutorizacionBoleta',
4110
+ });
4111
+ resolve(_body);
4112
+ });
4113
+ });
4114
+ req.on('error', reject);
4115
+ req.setTimeout(20000, () => { req.destroy(); reject(new Error('Timeout')); });
4116
+ req.write(cuerpo); req.end();
4117
+ });
4118
+
4084
4119
  const result = {
4085
4120
  autorizadaProduccion: false,
4086
4121
  autorizadaCertificacion: false,
@@ -4090,8 +4125,37 @@ class CertRunner {
4090
4125
  };
4091
4126
 
4092
4127
  // ── 1. Verificar producción: of_solicita_folios_dcto en palena ─────────
4128
+ //
4129
+ // ⚠️ Son DOS pasos, no uno. `of_solicita_folios_dcto` es la segunda pantalla del
4130
+ // formulario de timbraje: entrar con un GET pelado, sin el POST previo que lleva
4131
+ // RUT_EMP/DV_EMP, hace que el SII responda 200 con una página de error genérica
4132
+ // ("No ha sido posible completar su solicitud... LIBRUD-OFSF-DTE-3-1-02") que no
4133
+ // trae ningún <select>.
4134
+ //
4135
+ // Como el parser buscaba <option value="39">, esa página vacía se leía como "no
4136
+ // autorizada". Es la secuencia que ya usa CafSolicitor._processMultiStepFlow() para
4137
+ // pedir folios de verdad, y por eso ESE camino sí funciona.
4138
+ //
4139
+ // Caso real (17/08/2026, RUT 78441936-3): el flujo reportaba "Boleta NO autorizada"
4140
+ // mientras el SII tenía a la empresa como FACTURADOR ELECTRONICO con BOLETA
4141
+ // ELECTRONICA autorizada hasta el folio 103009 desde el 12/07/2026, y palena le
4142
+ // entregaba folios reales de factura por el camino bien armado. Un comercio ya
4143
+ // habilitado quedaba viendo "esperando al SII" para siempre.
4093
4144
  try {
4094
- const htmlProd = await fetchHtml('https://palena.sii.cl/cvc_cgi/dte/of_solicita_folios_dcto');
4145
+ const SiiSession = require('../SiiSession');
4146
+ const htmlPaso1 = await fetchHtml('https://palena.sii.cl/cvc_cgi/dte/of_solicita_folios');
4147
+ const accion = SiiSession.extractFormAction(htmlPaso1) || '/cvc_cgi/dte/of_solicita_folios_dcto';
4148
+ const campos = {
4149
+ ...SiiSession.extractInputValues(htmlPaso1),
4150
+ RUT_EMP: rutNum,
4151
+ DV_EMP: dvChar,
4152
+ };
4153
+ // El `action` del form puede venir absoluto, con barra inicial, o relativo.
4154
+ const BASE_PALENA = 'https://palena.sii.cl';
4155
+ const urlPaso2 = accion.startsWith('http') ? accion
4156
+ : accion.startsWith('/') ? `${BASE_PALENA}${accion}`
4157
+ : `${BASE_PALENA}/cvc_cgi/dte/${accion}`;
4158
+ const htmlProd = await postForm(urlPaso2, campos);
4095
4159
  if (this.config.debugDir) {
4096
4160
  const ts = new Date().toISOString().replace(/[:.]/g, '-');
4097
4161
  fs.writeFileSync(
@@ -4099,10 +4163,31 @@ class CertRunner {
4099
4163
  htmlProd, 'utf-8'
4100
4164
  );
4101
4165
  }
4102
- const matches = [...htmlProd.matchAll(/<option[^>]+value="(\d+)"[^>]*>/gi)];
4103
- result.tiposDisponiblesProduccion = matches.map(m => parseInt(m[1], 10)).filter(n => n > 0);
4104
- result.autorizadaProduccion = result.tiposDisponiblesProduccion.includes(39);
4105
- console.log(` Producción tipos disponibles: [${result.tiposDisponiblesProduccion.join(', ')}] | Boleta 39: ${result.autorizadaProduccion ? '✅ SÍ' : '❌ NO'}`);
4166
+ // ⚠️ "No hay <option> con el 39" NO significa "no está autorizada". El SII también
4167
+ // responde 200 con una página de error que no trae ningún select, y ahí el parser
4168
+ // veía cero tipos y concluía "no autorizada" — un dato inventado, indistinguible
4169
+ // de la respuesta real.
4170
+ //
4171
+ // Visto el 17/08/2026 (RUT 78441936-3): palena devolvió 3377 bytes con
4172
+ // "No ha sido posible completar su solicitud... código LIBRUD-OFSF-DTE-3-1-02" y
4173
+ // el flujo venía reportando "certificación incompleta" desde hacía días.
4174
+ //
4175
+ // Se distingue por la ausencia de select, no por el texto del error: cualquier
4176
+ // fallo del portal que no pinte el formulario cae acá.
4177
+ const hayFormulario = /<select[^>]*>/i.test(htmlProd);
4178
+ if (!hayFormulario) {
4179
+ const codigo = (htmlProd.match(/([A-Z]{4,}-[A-Z]{3,}-[A-Z0-9-]{4,})/) || [])[1] || null;
4180
+ result.consultaFallida = true;
4181
+ result.errorConsultaProduccion = codigo
4182
+ ? `El portal del SII no entregó el formulario de timbraje (código ${codigo})`
4183
+ : 'El portal del SII no entregó el formulario de timbraje';
4184
+ console.log(` [!] Producción — ${result.errorConsultaProduccion}. NO se puede afirmar si está autorizada.`);
4185
+ } else {
4186
+ const matches = [...htmlProd.matchAll(/<option[^>]+value="(\d+)"[^>]*>/gi)];
4187
+ result.tiposDisponiblesProduccion = matches.map(m => parseInt(m[1], 10)).filter(n => n > 0);
4188
+ result.autorizadaProduccion = result.tiposDisponiblesProduccion.includes(39);
4189
+ console.log(` ✓ Producción — tipos disponibles: [${result.tiposDisponiblesProduccion.join(', ')}] | Boleta 39: ${result.autorizadaProduccion ? '✅ SÍ' : '❌ NO'}`);
4190
+ }
4106
4191
  } catch (e) {
4107
4192
  console.log(` [!] No se pudo verificar producción: ${e.message}`);
4108
4193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devlas/dte-sii",
3
- "version": "2.14.1",
3
+ "version": "2.14.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",