@devlas/dte-sii 2.14.0 → 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.
- package/cert/CertRunner.js +104 -6
- package/package.json +1 -1
package/cert/CertRunner.js
CHANGED
|
@@ -4051,7 +4051,13 @@ class CertRunner {
|
|
|
4051
4051
|
const cookieJar = await this._obtenerCookiesSII();
|
|
4052
4052
|
const cookieStr = Object.entries(cookieJar).map(([k, v]) => `${k}=${v}`).join('; ');
|
|
4053
4053
|
|
|
4054
|
+
// ⚠️ Este era el ÚNICO cliente HTTP de la librería que no dejaba rastro, y es
|
|
4055
|
+
// precisamente el que responde "¿el SII ya habilitó el timbraje?": consulta
|
|
4056
|
+
// of_solicita_folios_dcto en palena y mira si el tipo 39 aparece en el select.
|
|
4057
|
+
// Cuando la respuesta es "todavía no", sin la captura no queda forma de saber si el
|
|
4058
|
+
// SII contestó eso de verdad o si la consulta salió mal.
|
|
4054
4059
|
const fetchHtml = (url) => new Promise((resolve, reject) => {
|
|
4060
|
+
const _t0 = Date.now();
|
|
4055
4061
|
const req = https.get(url, {
|
|
4056
4062
|
headers: {
|
|
4057
4063
|
'Cookie': cookieStr,
|
|
@@ -4062,12 +4068,54 @@ class CertRunner {
|
|
|
4062
4068
|
}, (res) => {
|
|
4063
4069
|
const chunks = [];
|
|
4064
4070
|
res.on('data', c => chunks.push(c));
|
|
4065
|
-
res.on('end', () =>
|
|
4071
|
+
res.on('end', () => {
|
|
4072
|
+
const _body = Buffer.concat(chunks).toString('utf-8');
|
|
4073
|
+
registrarHttpDebug({
|
|
4074
|
+
url, method: 'GET', status: res.statusCode, headers: res.headers,
|
|
4075
|
+
body: _body, ms: Date.now() - _t0, cliente: 'verificarAutorizacionBoleta',
|
|
4076
|
+
});
|
|
4077
|
+
resolve(_body);
|
|
4078
|
+
});
|
|
4066
4079
|
});
|
|
4067
4080
|
req.on('error', reject);
|
|
4068
4081
|
req.setTimeout(20000, () => { req.destroy(); reject(new Error('Timeout')); });
|
|
4069
4082
|
});
|
|
4070
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
|
+
|
|
4071
4119
|
const result = {
|
|
4072
4120
|
autorizadaProduccion: false,
|
|
4073
4121
|
autorizadaCertificacion: false,
|
|
@@ -4077,8 +4125,37 @@ class CertRunner {
|
|
|
4077
4125
|
};
|
|
4078
4126
|
|
|
4079
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.
|
|
4080
4144
|
try {
|
|
4081
|
-
const
|
|
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);
|
|
4082
4159
|
if (this.config.debugDir) {
|
|
4083
4160
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
4084
4161
|
fs.writeFileSync(
|
|
@@ -4086,10 +4163,31 @@ class CertRunner {
|
|
|
4086
4163
|
htmlProd, 'utf-8'
|
|
4087
4164
|
);
|
|
4088
4165
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
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
|
+
}
|
|
4093
4191
|
} catch (e) {
|
|
4094
4192
|
console.log(` [!] No se pudo verificar producción: ${e.message}`);
|
|
4095
4193
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devlas/dte-sii",
|
|
3
|
-
"version": "2.14.
|
|
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",
|