@devlas/dte-sii 2.5.12 → 2.5.14
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/BoletaService.js +1 -1
- package/CafSolicitor.js +11 -12
- package/Certificado.js +2 -2
- package/ConsumoFolio.js +3 -3
- package/EnviadorSII.js +72 -99
- package/FolioService.js +1 -1
- package/SiiCertificacion.js +159 -28
- package/SiiPortalAuth.js +33 -9
- package/SiiSession.js +4 -4
- package/cert/BoletaCert.js +44 -44
- package/cert/CertRunner.js +511 -219
- package/cert/ConfigLoader.js +1 -1
- package/cert/IntercambioCert.js +19 -19
- package/cert/LibroCompras.js +45 -5
- package/cert/MuestrasImpresas.js +18 -18
- package/cert/SetBase.js +16 -16
- package/cert/SetBasico.js +6 -6
- package/cert/SetCompra.js +6 -6
- package/cert/SetExenta.js +6 -6
- package/cert/SetGuia.js +2 -2
- package/cert/SetParser.js +96 -13
- package/cert/SetsProvider.js +16 -19
- package/cert/Simulacion.js +1 -1
- package/package.json +2 -1
- package/utils/progress.js +78 -0
- package/utils/xml.js +2 -2
package/BoletaService.js
CHANGED
|
@@ -93,7 +93,7 @@ class BoletaService {
|
|
|
93
93
|
dte.timbrar(this.caf);
|
|
94
94
|
dte.firmar(this.certificado);
|
|
95
95
|
|
|
96
|
-
log.log(
|
|
96
|
+
log.log(`[OK] Boleta creada: Folio ${folio}, Monto $${totales.MntTotal}`);
|
|
97
97
|
|
|
98
98
|
return {
|
|
99
99
|
ok: true,
|
package/CafSolicitor.js
CHANGED
|
@@ -85,7 +85,6 @@ class CafSolicitor {
|
|
|
85
85
|
_saveDebug(debugDir, filename, content) {
|
|
86
86
|
const filePath = path.join(debugDir, filename);
|
|
87
87
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
88
|
-
console.log(`${filename}`);
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
/**
|
|
@@ -124,8 +123,8 @@ class CafSolicitor {
|
|
|
124
123
|
const cafPath = path.join(cafDir, cafFileName);
|
|
125
124
|
fs.writeFileSync(cafPath, xml, 'utf-8');
|
|
126
125
|
|
|
127
|
-
console.log(
|
|
128
|
-
console.log(`
|
|
126
|
+
console.log(`[OK] CAF guardado: ${cafFileName}`);
|
|
127
|
+
console.log(` Ruta: ${cafPath}`);
|
|
129
128
|
|
|
130
129
|
return cafPath;
|
|
131
130
|
}
|
|
@@ -158,7 +157,7 @@ class CafSolicitor {
|
|
|
158
157
|
|
|
159
158
|
console.log('─'.repeat(60));
|
|
160
159
|
console.log(`[CafSolicitor] Solicitando CAF tipo ${tipoDte} x${cantidad}`);
|
|
161
|
-
console.log(`
|
|
160
|
+
console.log(` RUT: ${this.rutEmisor} | Ambiente: ${this.ambiente}`);
|
|
162
161
|
|
|
163
162
|
try {
|
|
164
163
|
// Paso 1: POST inicial a of_solicita_folios
|
|
@@ -189,7 +188,7 @@ class CafSolicitor {
|
|
|
189
188
|
response = await this._processMultiStepFlow(response, rut, dv, tipoDte, cantidad, debugDir);
|
|
190
189
|
|
|
191
190
|
// Guardar respuesta final
|
|
192
|
-
this._saveDebug(debugDir,
|
|
191
|
+
this._saveDebug(debugDir, 'caf-final.html', response.body || '');
|
|
193
192
|
|
|
194
193
|
// Verificar si obtuvimos el CAF
|
|
195
194
|
if (response.body && response.body.includes('<AUTORIZACION')) {
|
|
@@ -229,7 +228,7 @@ class CafSolicitor {
|
|
|
229
228
|
|
|
230
229
|
response = await this.session.submitForm(formAction, step2Fields);
|
|
231
230
|
currentHtml = response.body || '';
|
|
232
|
-
this._saveDebug(debugDir,
|
|
231
|
+
this._saveDebug(debugDir, 'step2.html', currentHtml);
|
|
233
232
|
|
|
234
233
|
// Selección de tipo de documento
|
|
235
234
|
if (currentHtml.includes('COD_DOCTO')) {
|
|
@@ -243,7 +242,7 @@ class CafSolicitor {
|
|
|
243
242
|
|
|
244
243
|
response = await this.session.submitForm('/cvc_cgi/dte/of_solicita_folios_dcto', selectFields);
|
|
245
244
|
currentHtml = response.body || '';
|
|
246
|
-
this._saveDebug(debugDir,
|
|
245
|
+
this._saveDebug(debugDir, 'select.html', currentHtml);
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
// Paso 3: Solicitar numeración
|
|
@@ -274,7 +273,7 @@ class CafSolicitor {
|
|
|
274
273
|
|
|
275
274
|
response = await this.session.submitForm(formAction3, step3Fields);
|
|
276
275
|
currentHtml = response.body || '';
|
|
277
|
-
this._saveDebug(debugDir,
|
|
276
|
+
this._saveDebug(debugDir, 'step3.html', currentHtml);
|
|
278
277
|
|
|
279
278
|
// Confirmar folio inicial
|
|
280
279
|
if (currentHtml.includes('of_confirma_folio')) {
|
|
@@ -304,7 +303,7 @@ class CafSolicitor {
|
|
|
304
303
|
|
|
305
304
|
response = await this.session.submitForm(formAction, fields);
|
|
306
305
|
currentHtml = response.body || '';
|
|
307
|
-
this._saveDebug(debugDir,
|
|
306
|
+
this._saveDebug(debugDir, 'confirm.html', currentHtml);
|
|
308
307
|
|
|
309
308
|
if (currentHtml.includes('of_genera_folio')) {
|
|
310
309
|
response = await this._processGeneraFolio(response, debugDir);
|
|
@@ -330,7 +329,7 @@ class CafSolicitor {
|
|
|
330
329
|
|
|
331
330
|
response = await this.session.submitForm(formAction, fields);
|
|
332
331
|
currentHtml = response.body || '';
|
|
333
|
-
this._saveDebug(debugDir,
|
|
332
|
+
this._saveDebug(debugDir, 'genera.html', currentHtml);
|
|
334
333
|
|
|
335
334
|
// Paso final: of_genera_archivo
|
|
336
335
|
if (!currentHtml.includes('<AUTORIZACION') && currentHtml.includes('of_genera_archivo')) {
|
|
@@ -357,7 +356,7 @@ class CafSolicitor {
|
|
|
357
356
|
|
|
358
357
|
response = await this.session.submitForm(formAction, fields);
|
|
359
358
|
currentHtml = response.body || '';
|
|
360
|
-
this._saveDebug(debugDir,
|
|
359
|
+
this._saveDebug(debugDir, 'archivo.xml', currentHtml);
|
|
361
360
|
|
|
362
361
|
// A veces hay un paso extra
|
|
363
362
|
if (!currentHtml.includes('<AUTORIZACION') && currentHtml.includes('of_genera_archivo')) {
|
|
@@ -370,7 +369,7 @@ class CafSolicitor {
|
|
|
370
369
|
};
|
|
371
370
|
|
|
372
371
|
response = await this.session.submitForm(formAction2, fields2);
|
|
373
|
-
this._saveDebug(debugDir,
|
|
372
|
+
this._saveDebug(debugDir, 'archivo2.xml', response.body || '');
|
|
374
373
|
}
|
|
375
374
|
|
|
376
375
|
return response;
|
package/Certificado.js
CHANGED
|
@@ -51,14 +51,14 @@ class Certificado {
|
|
|
51
51
|
// Advertir si está próximo a expirar
|
|
52
52
|
const daysUntilExpiry = getDaysUntilExpiry(pfxData.notAfter);
|
|
53
53
|
if (daysUntilExpiry <= 30) {
|
|
54
|
-
log.warn(
|
|
54
|
+
log.warn(`[!] Certificado expira en ${daysUntilExpiry} días`);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// Usar datos extraídos por la utilidad centralizada
|
|
58
58
|
this.rut = pfxData.rut;
|
|
59
59
|
this.nombre = pfxData.cn;
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
getPrivateKeyPem() {
|
package/ConsumoFolio.js
CHANGED
|
@@ -251,12 +251,12 @@ class ConsumoFolio {
|
|
|
251
251
|
// Calcular C14N manual (incluye namespace heredado como PHP)
|
|
252
252
|
const c14nDocumento = this._c14nDocumentoConsumoFolios(documentoConsumoFolios, nsDefault);
|
|
253
253
|
|
|
254
|
-
console.log('
|
|
255
|
-
console.log('
|
|
254
|
+
console.log('C14N Length:', c14nDocumento.length);
|
|
255
|
+
console.log('C14N primeros 300 chars:', c14nDocumento.substring(0, 300));
|
|
256
256
|
|
|
257
257
|
// Calcular DigestValue = base64(sha1(C14N))
|
|
258
258
|
const digest = crypto.createHash('sha1').update(c14nDocumento).digest('base64');
|
|
259
|
-
console.log('
|
|
259
|
+
console.log('DigestValue:', digest);
|
|
260
260
|
|
|
261
261
|
// Construir SignedInfo (con tags expandidos para firmar)
|
|
262
262
|
// PHP incluye xmlns:xsi en SignedInfo para ConsumoFolio
|
package/EnviadorSII.js
CHANGED
|
@@ -94,8 +94,6 @@ class EnviadorSII {
|
|
|
94
94
|
async getSemilla() {
|
|
95
95
|
const url = this.urls[this.ambiente].semilla;
|
|
96
96
|
|
|
97
|
-
log.log(' Solicitando semilla a:', url);
|
|
98
|
-
|
|
99
97
|
const response = await fetch(url, {
|
|
100
98
|
method: 'GET',
|
|
101
99
|
headers: {
|
|
@@ -108,8 +106,6 @@ class EnviadorSII {
|
|
|
108
106
|
}
|
|
109
107
|
|
|
110
108
|
const xml = await response.text();
|
|
111
|
-
log.log(' XML semilla recibido:', xml.substring(0, 500));
|
|
112
|
-
|
|
113
109
|
// Usar parser centralizado
|
|
114
110
|
const data = parseXml(xml);
|
|
115
111
|
|
|
@@ -131,7 +127,7 @@ class EnviadorSII {
|
|
|
131
127
|
return data.getToken.item.Semilla.toString();
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
log.debug('
|
|
130
|
+
log.debug(' Estructura XML parseada:', JSON.stringify(data, null, 2));
|
|
135
131
|
throw new Error('No se pudo obtener semilla del SII');
|
|
136
132
|
}
|
|
137
133
|
|
|
@@ -140,8 +136,6 @@ class EnviadorSII {
|
|
|
140
136
|
*/
|
|
141
137
|
async getToken() {
|
|
142
138
|
const semilla = await this.getSemilla();
|
|
143
|
-
log.log('Semilla obtenida:', semilla);
|
|
144
|
-
|
|
145
139
|
const xmlSemilla = this._crearXMLSemilla(semilla);
|
|
146
140
|
|
|
147
141
|
const url = this.urls[this.ambiente].token;
|
|
@@ -161,8 +155,6 @@ class EnviadorSII {
|
|
|
161
155
|
}
|
|
162
156
|
|
|
163
157
|
const xml = await response.text();
|
|
164
|
-
log.log('Respuesta token:', xml);
|
|
165
|
-
|
|
166
158
|
// Usar parser centralizado con namespaces removidos
|
|
167
159
|
const data = parseXmlNoNs(xml);
|
|
168
160
|
|
|
@@ -171,7 +163,6 @@ class EnviadorSII {
|
|
|
171
163
|
const respBody = respuesta.RESP_BODY || respuesta['SII:RESP_BODY'];
|
|
172
164
|
if (respBody && respBody.TOKEN) {
|
|
173
165
|
this.token = respBody.TOKEN;
|
|
174
|
-
log.log('✅ Token obtenido:', this.token);
|
|
175
166
|
return this.token;
|
|
176
167
|
}
|
|
177
168
|
}
|
|
@@ -189,7 +180,6 @@ class EnviadorSII {
|
|
|
189
180
|
*/
|
|
190
181
|
async getSemillaSoap() {
|
|
191
182
|
const url = this.urls[this.ambiente].semillaSoap;
|
|
192
|
-
log.log(' Solicitando semilla SOAP a:', url);
|
|
193
183
|
const retryConfig = getConfigSection('retry');
|
|
194
184
|
const maxRetries = retryConfig?.maxRetries || 6;
|
|
195
185
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -204,7 +194,7 @@ class EnviadorSII {
|
|
|
204
194
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
205
195
|
try {
|
|
206
196
|
if (attempt > 1) {
|
|
207
|
-
log.log(`
|
|
197
|
+
log.log(` [...] Reintento semilla SOAP ${attempt}/${maxRetries}...`);
|
|
208
198
|
await wait(attempt * 1000);
|
|
209
199
|
}
|
|
210
200
|
|
|
@@ -219,22 +209,19 @@ class EnviadorSII {
|
|
|
219
209
|
|
|
220
210
|
if (!response.ok) {
|
|
221
211
|
if (isRetryableStatus(response.status) && attempt < maxRetries) {
|
|
222
|
-
log.log(`
|
|
212
|
+
log.log(` [!] Error semilla SOAP (${response.status}), reintentando...`);
|
|
223
213
|
continue;
|
|
224
214
|
}
|
|
225
215
|
throw siiError(`Error obteniendo semilla SOAP: ${response.status}`, ERROR_CODES.SII_CONNECTION_FAILED);
|
|
226
216
|
}
|
|
227
217
|
|
|
228
218
|
const xml = await response.text();
|
|
229
|
-
log.log(' Respuesta semilla SOAP (primeros 400 chars):', xml.substring(0, 400));
|
|
230
|
-
|
|
231
219
|
// Usar utilidad centralizada para decodificar entidades
|
|
232
220
|
const decodedXml = decodeXmlEntities(xml);
|
|
233
221
|
|
|
234
222
|
// Usar utilidad centralizada para extraer contenido de etiqueta
|
|
235
223
|
const semilla = extractTagContent(decodedXml, 'SEMILLA');
|
|
236
224
|
if (semilla) {
|
|
237
|
-
log.log(' Semilla extraída:', semilla);
|
|
238
225
|
return semilla;
|
|
239
226
|
}
|
|
240
227
|
|
|
@@ -246,7 +233,7 @@ class EnviadorSII {
|
|
|
246
233
|
throw siiError('No se pudo extraer semilla de la respuesta SOAP', ERROR_CODES.SII_INVALID_RESPONSE);
|
|
247
234
|
} catch (error) {
|
|
248
235
|
if (isRetryableError(error) && attempt < maxRetries) {
|
|
249
|
-
log.log(`
|
|
236
|
+
log.log(` [!] Error de conexión semilla SOAP (${error.cause?.code || 'socket'}), reintentando...`);
|
|
250
237
|
continue;
|
|
251
238
|
}
|
|
252
239
|
throw error;
|
|
@@ -265,7 +252,7 @@ class EnviadorSII {
|
|
|
265
252
|
if (this.useTokenCache) {
|
|
266
253
|
const cached = getCachedToken(this.ambiente, 'soap', this.rutCert);
|
|
267
254
|
if (cached) {
|
|
268
|
-
log.log('
|
|
255
|
+
log.log(' [OK] Token SOAP desde cache');
|
|
269
256
|
this.tokenSoap = cached;
|
|
270
257
|
return cached;
|
|
271
258
|
}
|
|
@@ -279,15 +266,12 @@ class EnviadorSII {
|
|
|
279
266
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
280
267
|
try {
|
|
281
268
|
if (attempt > 1) {
|
|
282
|
-
log.log(`
|
|
269
|
+
log.log(` [...] Reintento token SOAP ${attempt}/${maxRetries}...`);
|
|
283
270
|
await wait(attempt * 1000);
|
|
284
271
|
}
|
|
285
272
|
|
|
286
273
|
const semilla = await this.getSemillaSoap();
|
|
287
|
-
log.log(' Semilla SOAP obtenida:', semilla);
|
|
288
|
-
|
|
289
274
|
const xmlSemilla = this._crearXMLSemilla(semilla);
|
|
290
|
-
log.log(' Obteniendo token SOAP de:', url);
|
|
291
275
|
|
|
292
276
|
const soapEnvelope = `<?xml version="1.0" encoding="UTF-8"?>
|
|
293
277
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
|
@@ -310,15 +294,13 @@ class EnviadorSII {
|
|
|
310
294
|
if (!response.ok) {
|
|
311
295
|
const errorText = await response.text();
|
|
312
296
|
if (isRetryableStatus(response.status) && attempt < maxRetries) {
|
|
313
|
-
log.log(`
|
|
297
|
+
log.log(` [!] Error token SOAP (${response.status}), reintentando...`);
|
|
314
298
|
continue;
|
|
315
299
|
}
|
|
316
300
|
throw siiError(`Error obteniendo token SOAP: ${response.status} - ${errorText}`, ERROR_CODES.SII_CONNECTION_FAILED);
|
|
317
301
|
}
|
|
318
302
|
|
|
319
303
|
const xml = await response.text();
|
|
320
|
-
log.log(' Respuesta token SOAP (primeros 600 chars):', xml.substring(0, 600));
|
|
321
|
-
|
|
322
304
|
const decodedXml = xml
|
|
323
305
|
.replace(/</g, '<')
|
|
324
306
|
.replace(/>/g, '>')
|
|
@@ -334,7 +316,7 @@ class EnviadorSII {
|
|
|
334
316
|
setCachedToken(this.ambiente, 'soap', this.rutCert, this.tokenSoap);
|
|
335
317
|
}
|
|
336
318
|
|
|
337
|
-
log.log('
|
|
319
|
+
log.log(' [OK] Token SOAP obtenido');
|
|
338
320
|
return this.tokenSoap;
|
|
339
321
|
}
|
|
340
322
|
|
|
@@ -347,7 +329,7 @@ class EnviadorSII {
|
|
|
347
329
|
throw siiError('No se pudo obtener token SOAP del SII', ERROR_CODES.SII_INVALID_RESPONSE);
|
|
348
330
|
} catch (error) {
|
|
349
331
|
if (isRetryableError(error) && attempt < maxRetries) {
|
|
350
|
-
log.log(`
|
|
332
|
+
log.log(` [!] Error de conexión token SOAP (${error.cause?.code || 'socket'}), reintentando...`);
|
|
351
333
|
continue;
|
|
352
334
|
}
|
|
353
335
|
throw error;
|
|
@@ -394,13 +376,9 @@ class EnviadorSII {
|
|
|
394
376
|
const exponent = this.certificado.getExponent();
|
|
395
377
|
const cert = this._wordwrap(this.certificado.getCertificateBase64(), 64);
|
|
396
378
|
|
|
397
|
-
log.log('SignedInfo para firmar (length):', signedInfoParaFirmar.length);
|
|
398
|
-
|
|
399
379
|
const xmlFirmado = `<?xml version="1.0" encoding="UTF-8"?>
|
|
400
380
|
<getToken><item><Semilla>${semilla}</Semilla></item><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>${digestValue}</DigestValue></Reference></SignedInfo><SignatureValue>${signatureValue}</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>${modulus}</Modulus><Exponent>${exponent}</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>${cert}</X509Certificate></X509Data></KeyInfo></Signature></getToken>`;
|
|
401
381
|
|
|
402
|
-
log.log('DigestValue:', digestValue);
|
|
403
|
-
|
|
404
382
|
return xmlFirmado;
|
|
405
383
|
}
|
|
406
384
|
|
|
@@ -620,7 +598,7 @@ class EnviadorSII {
|
|
|
620
598
|
ok: true,
|
|
621
599
|
status: 0,
|
|
622
600
|
trackId: json.trackid.toString(),
|
|
623
|
-
mensaje:
|
|
601
|
+
mensaje: `[OK] Enviado al SII - TrackID: ${json.trackid}`,
|
|
624
602
|
respuesta: json,
|
|
625
603
|
};
|
|
626
604
|
}
|
|
@@ -654,7 +632,7 @@ class EnviadorSII {
|
|
|
654
632
|
ok: true,
|
|
655
633
|
status: status,
|
|
656
634
|
trackId: trackId,
|
|
657
|
-
mensaje:
|
|
635
|
+
mensaje: `[OK] Enviado al SII - TrackID: ${trackId}`,
|
|
658
636
|
};
|
|
659
637
|
}
|
|
660
638
|
|
|
@@ -710,19 +688,19 @@ class EnviadorSII {
|
|
|
710
688
|
|
|
711
689
|
let mensaje = '';
|
|
712
690
|
const estado = json.estado;
|
|
713
|
-
if (estado === 'REC') mensaje = '
|
|
714
|
-
else if (estado === 'SOK') mensaje = '
|
|
715
|
-
else if (estado === 'FOK') mensaje = '
|
|
716
|
-
else if (estado === 'PRD') mensaje = '
|
|
717
|
-
else if (estado === 'CRT') mensaje = '
|
|
718
|
-
else if (estado === 'EPR') mensaje = '
|
|
719
|
-
else if (estado === 'RPT') mensaje = '
|
|
720
|
-
else if (estado === 'RFR') mensaje = '
|
|
721
|
-
else if (estado === 'VOF') mensaje = '
|
|
722
|
-
else if (estado === 'RCT') mensaje = '
|
|
723
|
-
else if (estado === 'RPR') mensaje = '
|
|
724
|
-
else if (estado === 'RLV') mensaje = '
|
|
725
|
-
else if (estado === 'RCH') mensaje =
|
|
691
|
+
if (estado === 'REC') mensaje = 'Envío recibido';
|
|
692
|
+
else if (estado === 'SOK') mensaje = '[OK] Esquema validado';
|
|
693
|
+
else if (estado === 'FOK') mensaje = '[OK] Firma de envío validada';
|
|
694
|
+
else if (estado === 'PRD') mensaje = '[...] Envío en proceso';
|
|
695
|
+
else if (estado === 'CRT') mensaje = '[OK] Carátula OK';
|
|
696
|
+
else if (estado === 'EPR') mensaje = '[OK] Envío procesado';
|
|
697
|
+
else if (estado === 'RPT') mensaje = '[ERR] Rechazado por schema';
|
|
698
|
+
else if (estado === 'RFR') mensaje = '[ERR] Rechazado por error en firma';
|
|
699
|
+
else if (estado === 'VOF') mensaje = '[ERR] Error interno en SII';
|
|
700
|
+
else if (estado === 'RCT') mensaje = '[ERR] Rechazado por error en carátula';
|
|
701
|
+
else if (estado === 'RPR') mensaje = '[!] Aceptado con reparos';
|
|
702
|
+
else if (estado === 'RLV') mensaje = '[OK] Aceptado - Documento(s) válido(s)';
|
|
703
|
+
else if (estado === 'RCH') mensaje = `[ERR] Rechazado: ${json.glosa || json.descripcion || 'Sin detalle'}`;
|
|
726
704
|
else mensaje = `Estado: ${estado}`;
|
|
727
705
|
|
|
728
706
|
return {
|
|
@@ -768,8 +746,8 @@ class EnviadorSII {
|
|
|
768
746
|
</soapenv:Body>
|
|
769
747
|
</soapenv:Envelope>`;
|
|
770
748
|
|
|
771
|
-
log.log('
|
|
772
|
-
log.log('
|
|
749
|
+
log.log('Consultando estado SOAP:', urlQueryEstUp);
|
|
750
|
+
log.log(' TrackID:', trackId, 'RUT:', rutEmisor);
|
|
773
751
|
|
|
774
752
|
const response = await fetch(urlQueryEstUp, {
|
|
775
753
|
method: 'POST',
|
|
@@ -785,7 +763,7 @@ class EnviadorSII {
|
|
|
785
763
|
// Usar decodeXmlEntities centralizado
|
|
786
764
|
const decoded = decodeXmlEntities(text).replace(/
/g, '\n');
|
|
787
765
|
|
|
788
|
-
|
|
766
|
+
|
|
789
767
|
|
|
790
768
|
const estadoMatch = decoded.match(/<ESTADO>([^<]+)<\/ESTADO>/i);
|
|
791
769
|
const glosaMatch = decoded.match(/<GLOSA>([^<]+)<\/GLOSA>/i);
|
|
@@ -818,130 +796,130 @@ class EnviadorSII {
|
|
|
818
796
|
|
|
819
797
|
switch (estado) {
|
|
820
798
|
case 'EPR':
|
|
821
|
-
mensaje = '
|
|
799
|
+
mensaje = '[OK] Envío Procesado';
|
|
822
800
|
esExitoso = true;
|
|
823
801
|
break;
|
|
824
802
|
case 'RPR':
|
|
825
|
-
mensaje = '
|
|
803
|
+
mensaje = '[!] Aceptado con Reparos';
|
|
826
804
|
esExitoso = true;
|
|
827
805
|
break;
|
|
828
806
|
case 'REC':
|
|
829
|
-
mensaje = '
|
|
807
|
+
mensaje = '[...] Envío Recibido - Esperando validación';
|
|
830
808
|
esIntermedio = true;
|
|
831
809
|
break;
|
|
832
810
|
case 'SOK':
|
|
833
|
-
mensaje = '
|
|
811
|
+
mensaje = '[...] Schema OK - Validando firma...';
|
|
834
812
|
esIntermedio = true;
|
|
835
813
|
break;
|
|
836
814
|
case 'FOK':
|
|
837
|
-
mensaje = '
|
|
815
|
+
mensaje = '[...] Firma Validada - Procesando envío...';
|
|
838
816
|
esIntermedio = true;
|
|
839
817
|
break;
|
|
840
818
|
case 'PRD':
|
|
841
|
-
mensaje = '
|
|
819
|
+
mensaje = '[...] Envío en Proceso - Validando carátula...';
|
|
842
820
|
esIntermedio = true;
|
|
843
821
|
break;
|
|
844
822
|
case 'CRT':
|
|
845
|
-
mensaje = '
|
|
823
|
+
mensaje = '[...] Carátula OK - Finalizando proceso...';
|
|
846
824
|
esIntermedio = true;
|
|
847
825
|
break;
|
|
848
826
|
case 'DNK':
|
|
849
|
-
mensaje = '
|
|
827
|
+
mensaje = '[...] En proceso de revisión';
|
|
850
828
|
esIntermedio = true;
|
|
851
829
|
break;
|
|
852
830
|
case 'RPT':
|
|
853
|
-
mensaje =
|
|
831
|
+
mensaje = `[ERR] Rechazado por Schema: ${glosa || 'Error en estructura XML'}`;
|
|
854
832
|
esRechazado = true;
|
|
855
833
|
break;
|
|
856
834
|
case 'RFR':
|
|
857
|
-
mensaje =
|
|
835
|
+
mensaje = `[ERR] Rechazado por Firma: ${glosa || 'Error en firma digital'}`;
|
|
858
836
|
esRechazado = true;
|
|
859
837
|
break;
|
|
860
838
|
case 'VOF':
|
|
861
|
-
mensaje =
|
|
839
|
+
mensaje = `[ERR] Error Interno del SII: ${glosa || 'Reintentar más tarde'}`;
|
|
862
840
|
esRechazado = true;
|
|
863
841
|
break;
|
|
864
842
|
case 'RCT':
|
|
865
|
-
mensaje =
|
|
843
|
+
mensaje = `[ERR] Rechazado por Error en Carátula: ${glosa || 'Verificar datos del emisor'}`;
|
|
866
844
|
esRechazado = true;
|
|
867
845
|
break;
|
|
868
846
|
case 'RCH':
|
|
869
|
-
mensaje =
|
|
847
|
+
mensaje = `[ERR] Rechazado: ${glosa || 'Sin detalle'}`;
|
|
870
848
|
esRechazado = true;
|
|
871
849
|
break;
|
|
872
850
|
case 'RLV':
|
|
873
|
-
mensaje =
|
|
851
|
+
mensaje = `[ERR] Rechazado: ${glosa || 'Error en documento'}`;
|
|
874
852
|
esRechazado = true;
|
|
875
853
|
break;
|
|
876
854
|
// Códigos de schema/firma rechazados
|
|
877
855
|
case 'RSC':
|
|
878
|
-
mensaje =
|
|
856
|
+
mensaje = `[ERR] Rechazado por Error en Schema: ${glosa || 'XML no cumple XSD del SII'}`;
|
|
879
857
|
esRechazado = true;
|
|
880
858
|
break;
|
|
881
859
|
case 'PDR':
|
|
882
|
-
mensaje = '
|
|
860
|
+
mensaje = '[...] Envío en Proceso - Validando...';
|
|
883
861
|
esIntermedio = true;
|
|
884
862
|
break;
|
|
885
863
|
// Códigos numéricos negativos = errores del servicio de consulta SII (NO rechazo del documento)
|
|
886
864
|
// Según doc SII: son errores del sistema de consulta, el documento puede estar OK
|
|
887
865
|
case '-11':
|
|
888
|
-
mensaje =
|
|
866
|
+
mensaje = `[...] Error de consulta SII (ERR/SQL/SRV_CODE) - Reintente más tarde`;
|
|
889
867
|
esIntermedio = true;
|
|
890
868
|
break;
|
|
891
869
|
case '-12':
|
|
892
|
-
mensaje =
|
|
870
|
+
mensaje = `[...] Error retorno consulta SII - Reintente más tarde`;
|
|
893
871
|
esIntermedio = true;
|
|
894
872
|
break;
|
|
895
873
|
case '-13':
|
|
896
|
-
mensaje =
|
|
874
|
+
mensaje = `[...] Error: RUT usuario nulo - Verificar autenticación`;
|
|
897
875
|
esIntermedio = true;
|
|
898
876
|
break;
|
|
899
877
|
case '-14':
|
|
900
|
-
mensaje =
|
|
878
|
+
mensaje = `[...] Error XML retorno datos SII - Reintente más tarde`;
|
|
901
879
|
esIntermedio = true;
|
|
902
880
|
break;
|
|
903
881
|
case '-10':
|
|
904
|
-
mensaje =
|
|
882
|
+
mensaje = `[...] Error validación RUT usuario - Verificar credenciales`;
|
|
905
883
|
esIntermedio = true;
|
|
906
884
|
break;
|
|
907
885
|
case '-9':
|
|
908
|
-
mensaje =
|
|
886
|
+
mensaje = `[...] Error retorno datos SII - Reintente más tarde`;
|
|
909
887
|
esIntermedio = true;
|
|
910
888
|
break;
|
|
911
889
|
case '-8':
|
|
912
|
-
mensaje =
|
|
890
|
+
mensaje = `[...] Error retorno datos SII - Reintente más tarde`;
|
|
913
891
|
esIntermedio = true;
|
|
914
892
|
break;
|
|
915
893
|
case '-7':
|
|
916
|
-
mensaje =
|
|
894
|
+
mensaje = `[...] Error retorno datos SII - Reintente más tarde`;
|
|
917
895
|
esIntermedio = true;
|
|
918
896
|
break;
|
|
919
897
|
case '-6':
|
|
920
|
-
mensaje =
|
|
898
|
+
mensaje = `[...] Error: Usuario no autorizado para consultar - Verificar credenciales`;
|
|
921
899
|
esIntermedio = true;
|
|
922
900
|
break;
|
|
923
901
|
case '-5':
|
|
924
|
-
mensaje =
|
|
902
|
+
mensaje = `[...] Error retorno datos SII - Reintente más tarde`;
|
|
925
903
|
esIntermedio = true;
|
|
926
904
|
break;
|
|
927
905
|
case '-4':
|
|
928
|
-
mensaje =
|
|
906
|
+
mensaje = `[...] Error obtención de datos SII - Reintente más tarde`;
|
|
929
907
|
esIntermedio = true;
|
|
930
908
|
break;
|
|
931
909
|
case '-3':
|
|
932
|
-
mensaje =
|
|
910
|
+
mensaje = `[...] Error: RUT usuario no existe en SII - Verificar autenticación`;
|
|
933
911
|
esIntermedio = true;
|
|
934
912
|
break;
|
|
935
913
|
case '-2':
|
|
936
|
-
mensaje =
|
|
914
|
+
mensaje = `[...] Error retorno SII - Reintente más tarde`;
|
|
937
915
|
esIntermedio = true;
|
|
938
916
|
break;
|
|
939
917
|
case '-1':
|
|
940
|
-
mensaje =
|
|
918
|
+
mensaje = `[...] Error: Campo estado no retornado por SII - Reintente más tarde`;
|
|
941
919
|
esIntermedio = true;
|
|
942
920
|
break;
|
|
943
921
|
case '0':
|
|
944
|
-
mensaje =
|
|
922
|
+
mensaje = `[...] Enviado al SII, pendiente de validación`;
|
|
945
923
|
esIntermedio = true;
|
|
946
924
|
break;
|
|
947
925
|
default:
|
|
@@ -973,7 +951,7 @@ class EnviadorSII {
|
|
|
973
951
|
const { DOMParser } = require('@xmldom/xmldom');
|
|
974
952
|
|
|
975
953
|
if (!this.tokenSoap) {
|
|
976
|
-
log.log('
|
|
954
|
+
log.log('Obteniendo token SOAP para DTEUpload...');
|
|
977
955
|
await this.getTokenSoap();
|
|
978
956
|
}
|
|
979
957
|
|
|
@@ -993,9 +971,9 @@ class EnviadorSII {
|
|
|
993
971
|
|
|
994
972
|
const url = this.urls[this.ambiente].rcof;
|
|
995
973
|
|
|
996
|
-
log.log('
|
|
997
|
-
log.log('
|
|
998
|
-
log.log('
|
|
974
|
+
log.log('Enviando EnvioBOLETA via SOAP/DTEUpload...');
|
|
975
|
+
log.log(' URL:', url);
|
|
976
|
+
log.log(' XML Length:', xml.length, 'bytes');
|
|
999
977
|
|
|
1000
978
|
const [rutNum, dv] = rutEmisor.split('-');
|
|
1001
979
|
const [rutEnviaNum, dvEnvia] = rutEnvia.split('-');
|
|
@@ -1038,7 +1016,7 @@ class EnviadorSII {
|
|
|
1038
1016
|
const { DOMParser } = require('@xmldom/xmldom');
|
|
1039
1017
|
|
|
1040
1018
|
if (!this.tokenSoap) {
|
|
1041
|
-
log.log('
|
|
1019
|
+
log.log('Obteniendo token SOAP para DTEUpload...');
|
|
1042
1020
|
await this.getTokenSoap();
|
|
1043
1021
|
}
|
|
1044
1022
|
|
|
@@ -1060,9 +1038,9 @@ class EnviadorSII {
|
|
|
1060
1038
|
|
|
1061
1039
|
const xmlBuffer = Buffer.from(xml, 'latin1');
|
|
1062
1040
|
|
|
1063
|
-
log.log('
|
|
1064
|
-
log.log('
|
|
1065
|
-
log.log('
|
|
1041
|
+
log.log('Enviando EnvioDTE via SOAP/DTEUpload...');
|
|
1042
|
+
log.log(' URL:', url);
|
|
1043
|
+
log.log(' XML Length:', xmlBuffer.length, 'bytes');
|
|
1066
1044
|
|
|
1067
1045
|
const [rutNum, dv] = rutEmisor.split('-');
|
|
1068
1046
|
const [rutEnviaNum, dvEnvia] = rutEnvia.split('-');
|
|
@@ -1104,7 +1082,7 @@ class EnviadorSII {
|
|
|
1104
1082
|
const { DOMParser } = require('@xmldom/xmldom');
|
|
1105
1083
|
|
|
1106
1084
|
if (!this.tokenSoap) {
|
|
1107
|
-
log.log('
|
|
1085
|
+
log.log('Obteniendo token SOAP para DTEUpload...');
|
|
1108
1086
|
await this.getTokenSoap();
|
|
1109
1087
|
}
|
|
1110
1088
|
|
|
@@ -1164,7 +1142,7 @@ class EnviadorSII {
|
|
|
1164
1142
|
const { DOMParser } = require('@xmldom/xmldom');
|
|
1165
1143
|
|
|
1166
1144
|
if (!this.tokenSoap) {
|
|
1167
|
-
log.log('
|
|
1145
|
+
log.log('Obteniendo token SOAP para DTEUpload...');
|
|
1168
1146
|
await this.getTokenSoap();
|
|
1169
1147
|
}
|
|
1170
1148
|
|
|
@@ -1185,10 +1163,6 @@ class EnviadorSII {
|
|
|
1185
1163
|
|
|
1186
1164
|
const url = this.urls[this.ambiente].rcof;
|
|
1187
1165
|
|
|
1188
|
-
log.log('📤 Enviando Libro via SOAP/DTEUpload...');
|
|
1189
|
-
log.log(' URL:', url);
|
|
1190
|
-
log.log(' XML Length:', xml.length, 'bytes');
|
|
1191
|
-
|
|
1192
1166
|
const [rutNum, dv] = rutEmisor.split('-');
|
|
1193
1167
|
const [rutEnviaNum, dvEnvia] = rutEnvia.split('-');
|
|
1194
1168
|
|
|
@@ -1238,7 +1212,7 @@ class EnviadorSII {
|
|
|
1238
1212
|
*/
|
|
1239
1213
|
async _enviarMultipart(url, body, boundary, xml, tipoEnvio) {
|
|
1240
1214
|
const retryConfig = getConfigSection('retry');
|
|
1241
|
-
const maxRetries = retryConfig?.maxRetries ||
|
|
1215
|
+
const maxRetries = retryConfig?.maxRetries || 12;
|
|
1242
1216
|
const initialDelay = retryConfig?.initialDelay || 2000;
|
|
1243
1217
|
const backoffMultiplier = retryConfig?.backoffMultiplier || 1.8;
|
|
1244
1218
|
let lastError = null;
|
|
@@ -1255,7 +1229,7 @@ class EnviadorSII {
|
|
|
1255
1229
|
try {
|
|
1256
1230
|
if (attempt > 1) {
|
|
1257
1231
|
const delay = Math.min(initialDelay * Math.pow(backoffMultiplier, attempt - 2), 15000);
|
|
1258
|
-
log.log(`
|
|
1232
|
+
log.log(` [...] Reintento ${tipoEnvio} ${attempt}/${maxRetries} (delay: ${Math.round(delay/1000)}s)...`);
|
|
1259
1233
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1260
1234
|
}
|
|
1261
1235
|
|
|
@@ -1275,7 +1249,6 @@ class EnviadorSII {
|
|
|
1275
1249
|
clearTimeout(timeout);
|
|
1276
1250
|
|
|
1277
1251
|
const responseText = await response.text();
|
|
1278
|
-
log.log(` Respuesta ${tipoEnvio}:`, responseText);
|
|
1279
1252
|
|
|
1280
1253
|
const result = this._parsearRespuestaSoap(responseText, response.ok, response.status, tipoEnvio);
|
|
1281
1254
|
|
|
@@ -1296,7 +1269,7 @@ class EnviadorSII {
|
|
|
1296
1269
|
|
|
1297
1270
|
// Usar isRetryableError centralizado de utils
|
|
1298
1271
|
if (isRetryableError(fetchError) && attempt < maxRetries) {
|
|
1299
|
-
log.log(`
|
|
1272
|
+
log.log(` [!] Error de conexión ${tipoEnvio} (${fetchError.cause?.code || 'socket'}), reintentando...`);
|
|
1300
1273
|
continue;
|
|
1301
1274
|
}
|
|
1302
1275
|
|
|
@@ -1353,7 +1326,7 @@ class EnviadorSII {
|
|
|
1353
1326
|
status: status,
|
|
1354
1327
|
trackId: trackId,
|
|
1355
1328
|
archivo: fileName,
|
|
1356
|
-
mensaje:
|
|
1329
|
+
mensaje: `[OK] ${tipoEnvio} Enviado - TrackID: ${trackId}`,
|
|
1357
1330
|
respuesta: responseText,
|
|
1358
1331
|
};
|
|
1359
1332
|
}
|
package/FolioService.js
CHANGED
|
@@ -399,7 +399,7 @@ class FolioService {
|
|
|
399
399
|
// Log de progreso
|
|
400
400
|
foliosAnulados += 1;
|
|
401
401
|
const pct = Math.round((foliosAnulados / totalFolios) * 100);
|
|
402
|
-
process.stdout.write(`\r
|
|
402
|
+
process.stdout.write(`\r Anulando folio ${folio} (${foliosAnulados}/${totalFolios} - ${pct}%)`);
|
|
403
403
|
|
|
404
404
|
return { folio, ...this._parseAnulacionResult(resultRes.body || '') };
|
|
405
405
|
};
|