@devlas/dte-sii 2.5.13 → 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 +43 -20
- package/SiiPortalAuth.js +33 -9
- package/SiiSession.js +4 -4
- package/cert/BoletaCert.js +44 -44
- package/cert/CertRunner.js +421 -253
- package/cert/ConfigLoader.js +1 -1
- package/cert/IntercambioCert.js +19 -19
- 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/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/SiiCertificacion.js
CHANGED
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
const SiiSession = require('./SiiSession.js');
|
|
19
|
+
const { STEPS, emitProgress } = require('./utils/progress');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
* Etapas de
|
|
22
|
+
* Etapas de certificacion DTE
|
|
22
23
|
*/
|
|
23
24
|
const ETAPAS_CERTIFICACION = {
|
|
24
25
|
SET_BASICO: 'SET_BASICO',
|
|
@@ -71,6 +72,20 @@ class SiiCertificacion {
|
|
|
71
72
|
pfxPassword: options.pfxPassword,
|
|
72
73
|
ambiente: 'certificacion',
|
|
73
74
|
});
|
|
75
|
+
|
|
76
|
+
// Reutilización de sesión: cargar desde archivo y guardar automáticamente tras cada login
|
|
77
|
+
if (options.sessionPath) {
|
|
78
|
+
this._sessionPath = options.sessionPath;
|
|
79
|
+
this.session.loadSession(options.sessionPath);
|
|
80
|
+
const _origEnsure = this.session.ensureSession.bind(this.session);
|
|
81
|
+
const _sess = this.session;
|
|
82
|
+
const _sp = options.sessionPath;
|
|
83
|
+
this.session.ensureSession = async function(targetPath) {
|
|
84
|
+
const result = await _origEnsure(targetPath);
|
|
85
|
+
_sess.saveSession(_sp);
|
|
86
|
+
return result;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
74
89
|
}
|
|
75
90
|
|
|
76
91
|
/**
|
|
@@ -518,7 +533,7 @@ class SiiCertificacion {
|
|
|
518
533
|
}
|
|
519
534
|
|
|
520
535
|
if (process.env.DEBUG_SII) {
|
|
521
|
-
console.log('
|
|
536
|
+
console.log(' [DEBUG] Campos hidden encontrados:', Object.keys(hiddenFields).join(', '));
|
|
522
537
|
}
|
|
523
538
|
|
|
524
539
|
// Si no hay formulario para declarar (página de estado/reparos)
|
|
@@ -559,7 +574,7 @@ class SiiCertificacion {
|
|
|
559
574
|
}
|
|
560
575
|
const debugPath = path.join(debugDir, 'pe_avance2_form.html');
|
|
561
576
|
fs.writeFileSync(debugPath, formHtml, 'utf8');
|
|
562
|
-
|
|
577
|
+
|
|
563
578
|
}
|
|
564
579
|
|
|
565
580
|
// Extraer todas las filas <tr>...</tr> del formulario
|
|
@@ -596,7 +611,7 @@ class SiiCertificacion {
|
|
|
596
611
|
// Solo agregar si tiene NUM_ENV (no si tiene REVISADO CONFORME o EN REVISION)
|
|
597
612
|
fieldMapping[pattern.name] = parseInt(numEnvMatch[1]);
|
|
598
613
|
if (process.env.DEBUG_SII) {
|
|
599
|
-
console.log(`
|
|
614
|
+
console.log(` [DEBUG] ${pattern.name} → NUM_ENV${numEnvMatch[1]}`);
|
|
600
615
|
}
|
|
601
616
|
}
|
|
602
617
|
break; // Ya encontramos la fila para este pattern
|
|
@@ -605,7 +620,7 @@ class SiiCertificacion {
|
|
|
605
620
|
}
|
|
606
621
|
|
|
607
622
|
if (process.env.DEBUG_SII) {
|
|
608
|
-
console.log('
|
|
623
|
+
console.log(' [DEBUG] Mapeo dinámico de índices:', fieldMapping);
|
|
609
624
|
}
|
|
610
625
|
|
|
611
626
|
if (sets.setSimulacion && !fieldMapping.setSimulacion) {
|
|
@@ -646,7 +661,7 @@ class SiiCertificacion {
|
|
|
646
661
|
numEnvValues[index] = cleanTrackId;
|
|
647
662
|
fecEnvValues[index] = formatDate(fecha);
|
|
648
663
|
if (String(trackId) !== cleanTrackId) {
|
|
649
|
-
|
|
664
|
+
|
|
650
665
|
}
|
|
651
666
|
}
|
|
652
667
|
}
|
|
@@ -677,16 +692,16 @@ class SiiCertificacion {
|
|
|
677
692
|
|
|
678
693
|
// Debug: mostrar datos del formulario
|
|
679
694
|
if (process.env.DEBUG_SII) {
|
|
680
|
-
console.log('
|
|
695
|
+
console.log(' [DEBUG] Datos del formulario a enviar:');
|
|
681
696
|
for (const [k, v] of Object.entries(formData)) {
|
|
682
|
-
console.log(`
|
|
697
|
+
console.log(` ${k}: ${v || '(vacío)'}`);
|
|
683
698
|
}
|
|
684
699
|
|
|
685
700
|
// Mostrar el body codificado
|
|
686
701
|
const SiiSession = require('./SiiSession');
|
|
687
702
|
const encodedBody = SiiSession.formEncode(formData);
|
|
688
|
-
console.log('
|
|
689
|
-
console.log(`
|
|
703
|
+
console.log(' [DEBUG] Body codificado (primeros 500 chars):');
|
|
704
|
+
console.log(` ${encodedBody.substring(0, 500)}`);
|
|
690
705
|
}
|
|
691
706
|
|
|
692
707
|
// 5. Enviar formulario de declaración
|
|
@@ -706,7 +721,7 @@ class SiiCertificacion {
|
|
|
706
721
|
|
|
707
722
|
// Debug - guardar respuesta
|
|
708
723
|
if (process.env.DEBUG_SII) {
|
|
709
|
-
console.log(`
|
|
724
|
+
console.log(` [DEBUG] Respuesta: ${body.length} bytes, status: ${declareResponse.status}`);
|
|
710
725
|
// Guardar respuesta de pe_avance3 para debug
|
|
711
726
|
const fs = require('fs');
|
|
712
727
|
const path = require('path');
|
|
@@ -716,7 +731,7 @@ class SiiCertificacion {
|
|
|
716
731
|
}
|
|
717
732
|
const debugPath = path.join(debugDir, 'pe_avance3_response.html');
|
|
718
733
|
fs.writeFileSync(debugPath, body);
|
|
719
|
-
console.log(`
|
|
734
|
+
console.log(` [DEBUG] Respuesta guardada en: ${debugPath}`);
|
|
720
735
|
}
|
|
721
736
|
|
|
722
737
|
const bodyLower = body.toLowerCase();
|
|
@@ -767,7 +782,7 @@ class SiiCertificacion {
|
|
|
767
782
|
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug/cert-v2');
|
|
768
783
|
if (!fs.existsSync(debugDir)) fs.mkdirSync(debugDir, { recursive: true });
|
|
769
784
|
fs.writeFileSync(path.join(debugDir, 'pe_avance3_response.html'), body, 'utf8');
|
|
770
|
-
|
|
785
|
+
|
|
771
786
|
}
|
|
772
787
|
|
|
773
788
|
// 7. VERIFICACIÓN POST-DECLARACIÓN: Re-leer pe_avance2 y confirmar que los TrackIDs se guardaron
|
|
@@ -776,6 +791,7 @@ class SiiCertificacion {
|
|
|
776
791
|
let verificado = true;
|
|
777
792
|
let verificacionError = '';
|
|
778
793
|
let enRevision = false;
|
|
794
|
+
let camposVacios = [];
|
|
779
795
|
try {
|
|
780
796
|
const verifyResponse = await this.session.submitForm(
|
|
781
797
|
'/cvc_cgi/dte/pe_avance2',
|
|
@@ -791,12 +807,11 @@ class SiiCertificacion {
|
|
|
791
807
|
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug/cert-v2');
|
|
792
808
|
if (!fs.existsSync(debugDir)) fs.mkdirSync(debugDir, { recursive: true });
|
|
793
809
|
fs.writeFileSync(path.join(debugDir, 'pe_avance2_verify.html'), verifyHtml, 'utf8');
|
|
794
|
-
|
|
810
|
+
|
|
795
811
|
}
|
|
796
812
|
|
|
797
813
|
// Para cada set que declaramos, verificar estado
|
|
798
814
|
const verifyRows = verifyHtml.split(/<\/tr>/i);
|
|
799
|
-
const camposVacios = [];
|
|
800
815
|
const camposEnRevision = [];
|
|
801
816
|
for (const [setName, index] of Object.entries(fieldMapping)) {
|
|
802
817
|
if (!sets[setName]) continue;
|
|
@@ -832,23 +847,28 @@ class SiiCertificacion {
|
|
|
832
847
|
|
|
833
848
|
if (camposEnRevision.length > 0) {
|
|
834
849
|
enRevision = true;
|
|
835
|
-
console.log(`
|
|
850
|
+
console.log(` [...] EN REVISION: ${camposEnRevision.join(', ')} — declaración aceptada, SII procesando`);
|
|
836
851
|
}
|
|
837
852
|
|
|
838
853
|
if (camposVacios.length > 0 && !enRevision) {
|
|
839
854
|
verificado = false;
|
|
840
855
|
verificacionError = `Declaración NO se guardó en el portal SII. Campos vacíos para: ${camposVacios.join(', ')}. Posible error de sesión o TrackID no reconocido.`;
|
|
841
|
-
console.log(`
|
|
856
|
+
console.log(` [!] ${verificacionError}`);
|
|
842
857
|
}
|
|
843
858
|
} catch (verifyErr) {
|
|
844
|
-
console.log(`
|
|
859
|
+
console.log(` [!] No se pudo verificar la declaración: ${verifyErr.message}`);
|
|
845
860
|
}
|
|
846
861
|
|
|
862
|
+
// allRejected: SII rechazó todos los sets declarados — período incorrecto, no tiene sentido reintentar
|
|
863
|
+
const _declaredCount = Object.keys(fieldMapping).filter(k => sets[k]).length;
|
|
864
|
+
const allRejected = !enRevision && camposVacios.length > 0 && camposVacios.length >= _declaredCount && _declaredCount > 0;
|
|
865
|
+
|
|
847
866
|
return {
|
|
848
867
|
success: verificado || enRevision,
|
|
849
868
|
error: verificacionError || errorMsg || undefined,
|
|
850
869
|
verificado,
|
|
851
870
|
enRevision,
|
|
871
|
+
allRejected,
|
|
852
872
|
status: declareResponse.status,
|
|
853
873
|
rawHtml: body,
|
|
854
874
|
formHtml,
|
|
@@ -906,7 +926,7 @@ class SiiCertificacion {
|
|
|
906
926
|
}
|
|
907
927
|
const debugPath = path.join(debugDir, 'pe_avance2_avanzar.html');
|
|
908
928
|
fs.writeFileSync(debugPath, formHtml, 'utf8');
|
|
909
|
-
console.log('
|
|
929
|
+
console.log(' [DEBUG] HTML avanzar guardado en:', debugPath);
|
|
910
930
|
}
|
|
911
931
|
|
|
912
932
|
let avanceResponse = await this.session.submitForm(
|
|
@@ -932,7 +952,7 @@ class SiiCertificacion {
|
|
|
932
952
|
}
|
|
933
953
|
const debugPath = path.join(debugDir, 'pe_avance3_avanzar_response.html');
|
|
934
954
|
fs.writeFileSync(debugPath, body);
|
|
935
|
-
console.log('
|
|
955
|
+
console.log(' [DEBUG] Respuesta avanzar guardada en:', debugPath);
|
|
936
956
|
}
|
|
937
957
|
|
|
938
958
|
const hasError = body.toLowerCase().includes('error') && !body.includes('Error de Sesión');
|
|
@@ -1269,6 +1289,7 @@ class SiiCertificacion {
|
|
|
1269
1289
|
if (onProgress) {
|
|
1270
1290
|
onProgress({ intento, maxIntentos, estado: 'polling' });
|
|
1271
1291
|
}
|
|
1292
|
+
emitProgress(STEPS.POLLING, { intento, max: maxIntentos, label: 'sets' });
|
|
1272
1293
|
|
|
1273
1294
|
const result = await this.verAvanceParsed();
|
|
1274
1295
|
if (!result.success) continue;
|
|
@@ -1288,10 +1309,12 @@ class SiiCertificacion {
|
|
|
1288
1309
|
}
|
|
1289
1310
|
|
|
1290
1311
|
if (todosConformes) {
|
|
1312
|
+
emitProgress(STEPS.SETS_APPROVED);
|
|
1291
1313
|
return { success: true, estados: estadosRelevantes };
|
|
1292
1314
|
}
|
|
1293
1315
|
|
|
1294
1316
|
if (algunoRechazado) {
|
|
1317
|
+
emitProgress(STEPS.SETS_REJECTED);
|
|
1295
1318
|
return { success: false, error: 'Sets rechazados', estados: estadosRelevantes };
|
|
1296
1319
|
}
|
|
1297
1320
|
}
|
package/SiiPortalAuth.js
CHANGED
|
@@ -173,10 +173,10 @@ class SiiPortalAuth {
|
|
|
173
173
|
if (cached) {
|
|
174
174
|
const valida = await this._validarSesion(cached);
|
|
175
175
|
if (valida) {
|
|
176
|
-
console.log('[SiiPortalAuth]
|
|
176
|
+
console.log('[SiiPortalAuth] Reutilizando sesión SII cacheada');
|
|
177
177
|
return cached;
|
|
178
178
|
}
|
|
179
|
-
console.
|
|
179
|
+
console.warn('[SiiPortalAuth] Sesión cacheada expirada, re-autenticando...');
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// ── 2. Nueva autenticación ────────────────────────────────────────────────
|
|
@@ -187,6 +187,7 @@ class SiiPortalAuth {
|
|
|
187
187
|
{ cookieJar }
|
|
188
188
|
);
|
|
189
189
|
|
|
190
|
+
console.log('[SiiPortalAuth] Autenticando con certificado en herculesr.sii.cl...');
|
|
190
191
|
const r2 = await this._request(
|
|
191
192
|
`https://herculesr.sii.cl/cgi_AUT2000/CAutInicio.cgi?${TARGET}`,
|
|
192
193
|
{
|
|
@@ -196,6 +197,7 @@ class SiiPortalAuth {
|
|
|
196
197
|
usarCert: true,
|
|
197
198
|
}
|
|
198
199
|
);
|
|
200
|
+
console.log(`[SiiPortalAuth] Respuesta herculesr: status=${r2.status}`);
|
|
199
201
|
|
|
200
202
|
// Verificar mensaje de límite de sesiones
|
|
201
203
|
if (r2.body.includes('m\u00e1ximo de sesiones') || r2.body.includes('maximo de sesiones') ||
|
|
@@ -222,19 +224,25 @@ class SiiPortalAuth {
|
|
|
222
224
|
*/
|
|
223
225
|
async _validarSesion(cookieJar) {
|
|
224
226
|
try {
|
|
227
|
+
console.log('[SiiPortalAuth] Validando sesión cacheada en SII...');
|
|
225
228
|
const res = await this._request(
|
|
226
229
|
'https://maullin.sii.cl/cvc_cgi/dte/ad_empresa1',
|
|
227
230
|
{ cookieJar: { ...cookieJar } } // copia para no contaminar
|
|
228
231
|
);
|
|
229
232
|
// Si redirige al login SII → expirada
|
|
230
233
|
const loc = res.headers['location'] || '';
|
|
231
|
-
if (loc.includes('InicioAutenticacion') || loc.includes('IngresoRutClave'))
|
|
234
|
+
if (loc.includes('InicioAutenticacion') || loc.includes('IngresoRutClave')) {
|
|
235
|
+
console.warn('[SiiPortalAuth] Validación: SII redirigió al login → sesión expirada');
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
232
238
|
// Si el body contiene formulario de empresa → válida
|
|
233
239
|
const valida = res.status === 200 && (res.body.includes('RUT_EMP') || res.body.includes('ad_empresa'));
|
|
240
|
+
console.log(`[SiiPortalAuth] Validación: status=${res.status} → sesión ${valida ? 'VÁLIDA ✓' : 'INVÁLIDA ✗'}`);
|
|
234
241
|
// Refrescar timestamp del caché para extender TTL mientras la sesión se usa activamente
|
|
235
242
|
if (valida) SiiPortalAuth._guardarSesionCache(this._certHash, cookieJar);
|
|
236
243
|
return valida;
|
|
237
|
-
} catch {
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.warn('[SiiPortalAuth] Validación: error de red →', err.message);
|
|
238
246
|
return false;
|
|
239
247
|
}
|
|
240
248
|
}
|
|
@@ -242,13 +250,25 @@ class SiiPortalAuth {
|
|
|
242
250
|
/** Lee sesión cacheada del disco para el cert dado. @private */
|
|
243
251
|
static _cargarSesionCache(certHash) {
|
|
244
252
|
try {
|
|
245
|
-
|
|
253
|
+
if (!fs.existsSync(SESSION_CACHE_PATH)) {
|
|
254
|
+
console.log('[SiiPortalAuth] Cache: archivo no existe →', SESSION_CACHE_PATH);
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
246
257
|
const data = JSON.parse(fs.readFileSync(SESSION_CACHE_PATH, 'utf8'));
|
|
247
|
-
if (data.certHash !== certHash)
|
|
258
|
+
if (data.certHash !== certHash) {
|
|
259
|
+
console.log(`[SiiPortalAuth] Cache: cert no coincide (guardado=${data.certHash} actual=${certHash})`);
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const edadMin = Math.round((Date.now() - data.ts) / 60000);
|
|
248
263
|
// TTL: 90 minutos (SII permite ~2h de inactividad; se refresca en cada validación)
|
|
249
|
-
if (Date.now() - data.ts > 90 * 60 * 1000)
|
|
264
|
+
if (Date.now() - data.ts > 90 * 60 * 1000) {
|
|
265
|
+
console.log(`[SiiPortalAuth] Cache: sesión expirada (edad=${edadMin} min, TTL=90 min)`);
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
console.log(`[SiiPortalAuth] Cache: sesión encontrada`);
|
|
250
269
|
return data.cookies;
|
|
251
|
-
} catch {
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.warn('[SiiPortalAuth] Cache: error leyendo caché →', err.message);
|
|
252
272
|
return null;
|
|
253
273
|
}
|
|
254
274
|
}
|
|
@@ -262,7 +282,11 @@ class SiiPortalAuth {
|
|
|
262
282
|
ts: Date.now(),
|
|
263
283
|
cookies: cookieJar,
|
|
264
284
|
}), 'utf8');
|
|
265
|
-
|
|
285
|
+
const cookieKeys = Object.keys(cookieJar);
|
|
286
|
+
console.log(`[SiiPortalAuth] Cache: sesión guardada`);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
console.warn('[SiiPortalAuth] Cache: error guardando caché →', err.message);
|
|
289
|
+
}
|
|
266
290
|
}
|
|
267
291
|
|
|
268
292
|
/** Borra la sesión cacheada (útil para forzar re-login). */
|
package/SiiSession.js
CHANGED
|
@@ -279,7 +279,7 @@ class SiiSession {
|
|
|
279
279
|
// Detectar bloqueo por demasiadas sesiones
|
|
280
280
|
if (response.body && response.body.includes('superado el m')) {
|
|
281
281
|
const errorMsg = 'SII: Demasiadas sesiones abiertas. Espera ~30 min o cierra sesión manualmente en el portal SII.';
|
|
282
|
-
console.error(`\n
|
|
282
|
+
console.error(`\n[ERR] ${errorMsg}\n`);
|
|
283
283
|
throw new Error(errorMsg);
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -292,7 +292,7 @@ class SiiSession {
|
|
|
292
292
|
// Verificar si el login resultó en bloqueo por sesiones
|
|
293
293
|
if (response.body && response.body.includes('superado el m')) {
|
|
294
294
|
const errorMsg = 'SII: Demasiadas sesiones abiertas. Espera ~30 min o cierra sesión manualmente en el portal SII.';
|
|
295
|
-
console.error(`\n
|
|
295
|
+
console.error(`\n[ERR] ${errorMsg}\n`);
|
|
296
296
|
throw new Error(errorMsg);
|
|
297
297
|
}
|
|
298
298
|
}
|
|
@@ -386,10 +386,10 @@ class SiiSession {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
this.cookieJar = data.cookieJar || '';
|
|
389
|
-
console.log('Sesión SII cargada desde archivo');
|
|
389
|
+
console.log('[SessionSii] Sesión SII cargada desde archivo');
|
|
390
390
|
return true;
|
|
391
391
|
} catch (err) {
|
|
392
|
-
console.log('Error cargando sesión SII:', err.message);
|
|
392
|
+
console.log('[SessionSii] Error cargando sesión SII:', err.message);
|
|
393
393
|
return false;
|
|
394
394
|
}
|
|
395
395
|
}
|
package/cert/BoletaCert.js
CHANGED
|
@@ -302,11 +302,11 @@ class BoletaCert {
|
|
|
302
302
|
const { DOMParser: XMLParser } = require('@xmldom/xmldom');
|
|
303
303
|
|
|
304
304
|
console.log('\n' + '═'.repeat(60));
|
|
305
|
-
console.log('
|
|
305
|
+
console.log('REENVÍO RCOF (ConsumoFolio)');
|
|
306
306
|
console.log('═'.repeat(60));
|
|
307
307
|
|
|
308
308
|
// 1. Parsear el EnvioBOLETA existente para extraer info
|
|
309
|
-
console.log('\
|
|
309
|
+
console.log('\nLeyendo EnvioBOLETA...');
|
|
310
310
|
const envioBOLETAXml = fs.readFileSync(options.envioBOLETAPath, 'utf-8');
|
|
311
311
|
const parser = new XMLParser();
|
|
312
312
|
const doc = parser.parseFromString(envioBOLETAXml, 'text/xml');
|
|
@@ -333,11 +333,11 @@ class BoletaCert {
|
|
|
333
333
|
Totales: { MntNeto: mntNeto, MntExe: mntExe, IVA: iva, MntTotal: mntTotal }
|
|
334
334
|
}
|
|
335
335
|
});
|
|
336
|
-
console.log(`
|
|
336
|
+
console.log(` - Folio ${folio}: Neto=${mntNeto}, Exento=${mntExe}, IVA=${iva}, Total=${mntTotal}`);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
// 2. Crear RCOF
|
|
340
|
-
console.log(`\
|
|
340
|
+
console.log(`\nGenerando RCOF con SecEnvio=${options.secEnvio}...`);
|
|
341
341
|
const { ConsumoFolio } = this._lib();
|
|
342
342
|
const consumoFolio = new ConsumoFolio(this.certificado);
|
|
343
343
|
|
|
@@ -354,26 +354,26 @@ class BoletaCert {
|
|
|
354
354
|
});
|
|
355
355
|
|
|
356
356
|
consumoFolio.generar();
|
|
357
|
-
console.log(`
|
|
357
|
+
console.log(` ✓ XML generado: ${consumoFolio.xml.length} bytes`);
|
|
358
358
|
|
|
359
359
|
// Guardar debug
|
|
360
360
|
if (this.debugDir) {
|
|
361
361
|
const debugPath = path.join(this.debugDir, 'boleta-cert');
|
|
362
362
|
fs.mkdirSync(debugPath, { recursive: true });
|
|
363
363
|
fs.writeFileSync(path.join(debugPath, `ConsumoFolio-sec${options.secEnvio}.xml`), consumoFolio.xml, 'utf-8');
|
|
364
|
-
console.log(`
|
|
364
|
+
console.log(` Guardado en: ${path.join(debugPath, `ConsumoFolio-sec${options.secEnvio}.xml`)}`);
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
// 3. Enviar RCOF
|
|
368
|
-
console.log('\
|
|
368
|
+
console.log('\nEnviando RCOF al SII...');
|
|
369
369
|
const enviador = new EnviadorSII(this.certificado, this.ambiente);
|
|
370
370
|
const resultadoRCOF = await enviador.enviarConsumoFolios(consumoFolio);
|
|
371
371
|
|
|
372
372
|
if (!resultadoRCOF.ok) {
|
|
373
|
-
console.log(`
|
|
373
|
+
console.log(` [ERR] Error: ${resultadoRCOF.error}`);
|
|
374
374
|
return { success: false, error: resultadoRCOF.error };
|
|
375
375
|
}
|
|
376
|
-
console.log(`
|
|
376
|
+
console.log(` [OK] Enviado - TrackId: ${resultadoRCOF.trackId}`);
|
|
377
377
|
|
|
378
378
|
return { success: true, trackIdRCOF: resultadoRCOF.trackId };
|
|
379
379
|
}
|
|
@@ -390,71 +390,71 @@ class BoletaCert {
|
|
|
390
390
|
const { EnviadorSII } = this._lib();
|
|
391
391
|
|
|
392
392
|
console.log('\n' + '═'.repeat(60));
|
|
393
|
-
console.log('
|
|
393
|
+
console.log('CERTIFICACIÓN BOLETAS ELECTRÓNICAS');
|
|
394
394
|
console.log('═'.repeat(60));
|
|
395
395
|
|
|
396
396
|
// 1. Parsear set de pruebas
|
|
397
|
-
console.log('\
|
|
397
|
+
console.log('\nParseando set de pruebas...');
|
|
398
398
|
const casos = this.parseSetPruebas(options.setPath);
|
|
399
|
-
console.log(`
|
|
399
|
+
console.log(` ✓ ${casos.length} casos encontrados`);
|
|
400
400
|
|
|
401
401
|
// 2. Generar boletas
|
|
402
|
-
console.log('\
|
|
402
|
+
console.log('\nGenerando boletas...');
|
|
403
403
|
const { boletas, foliosUsados, folioInicial, folioFinal } = await this.generarBoletasSet(
|
|
404
404
|
casos,
|
|
405
405
|
options.cafBoleta,
|
|
406
406
|
options.folioInicial
|
|
407
407
|
);
|
|
408
|
-
console.log(`
|
|
408
|
+
console.log(` ✓ ${boletas.length} boletas generadas (folios ${folioInicial}-${folioFinal})`);
|
|
409
409
|
|
|
410
410
|
for (const b of boletas) {
|
|
411
411
|
const monto = b.dte.montoTotal || 0;
|
|
412
|
-
console.log(`
|
|
412
|
+
console.log(` - CASO-${b.caso}: Folio ${b.folio} - $${monto.toLocaleString('es-CL')}`);
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
// 3. Generar EnvioBOLETA
|
|
416
|
-
console.log('\
|
|
416
|
+
console.log('\nGenerando EnvioBOLETA...');
|
|
417
417
|
const envioBoleta = this.generarEnvioBoleta(boletas);
|
|
418
|
-
console.log(`
|
|
418
|
+
console.log(` ✓ XML generado: ${envioBoleta.xml.length} bytes`);
|
|
419
419
|
|
|
420
420
|
// Guardar debug
|
|
421
421
|
if (this.debugDir) {
|
|
422
422
|
const debugPath = path.join(this.debugDir, 'boleta-cert');
|
|
423
423
|
fs.mkdirSync(debugPath, { recursive: true });
|
|
424
424
|
fs.writeFileSync(path.join(debugPath, 'EnvioBOLETA.xml'), envioBoleta.xml, 'utf-8');
|
|
425
|
-
console.log(`
|
|
425
|
+
console.log(` Guardado en: ${path.join(debugPath, 'EnvioBOLETA.xml')}`);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// 4. Enviar EnvioBOLETA
|
|
429
|
-
console.log('\
|
|
429
|
+
console.log('\nEnviando EnvioBOLETA al SII...');
|
|
430
430
|
const enviador = new EnviadorSII(this.certificado, this.ambiente);
|
|
431
431
|
const resultadoBoleta = await enviador.enviarBoletaSoap(envioBoleta);
|
|
432
432
|
|
|
433
433
|
if (!resultadoBoleta.ok) {
|
|
434
|
-
console.log(`
|
|
434
|
+
console.log(` [ERR] Error: ${resultadoBoleta.error}`);
|
|
435
435
|
return { success: false, error: resultadoBoleta.error, fase: 'EnvioBOLETA' };
|
|
436
436
|
}
|
|
437
|
-
console.log(`
|
|
437
|
+
console.log(` [OK] Enviado - TrackId: ${resultadoBoleta.trackId}`);
|
|
438
438
|
|
|
439
439
|
// 5. Generar RCOF
|
|
440
|
-
console.log('\
|
|
440
|
+
console.log('\nGenerando RCOF (ConsumoFolio)...');
|
|
441
441
|
const consumoFolio = this.generarConsumoFolio(envioBoleta);
|
|
442
442
|
consumoFolio.generar(); // generar() ya incluye firmar() internamente
|
|
443
|
-
console.log(`
|
|
443
|
+
console.log(` ✓ XML generado: ${consumoFolio.xml.length} bytes`);
|
|
444
444
|
|
|
445
445
|
// Guardar debug
|
|
446
446
|
if (this.debugDir) {
|
|
447
447
|
const debugPath = path.join(this.debugDir, 'boleta-cert');
|
|
448
448
|
fs.writeFileSync(path.join(debugPath, 'ConsumoFolio.xml'), consumoFolio.xml, 'utf-8');
|
|
449
|
-
console.log(`
|
|
449
|
+
console.log(` Guardado en: ${path.join(debugPath, 'ConsumoFolio.xml')}`);
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
// 6. Enviar RCOF
|
|
453
|
-
console.log('\
|
|
453
|
+
console.log('\nEnviando RCOF al SII...');
|
|
454
454
|
const resultadoRCOF = await enviador.enviarConsumoFolios(consumoFolio);
|
|
455
455
|
|
|
456
456
|
if (!resultadoRCOF.ok) {
|
|
457
|
-
console.log(`
|
|
457
|
+
console.log(` [ERR] Error: ${resultadoRCOF.error}`);
|
|
458
458
|
return {
|
|
459
459
|
success: false,
|
|
460
460
|
error: resultadoRCOF.error,
|
|
@@ -462,16 +462,16 @@ class BoletaCert {
|
|
|
462
462
|
trackIdBoleta: resultadoBoleta.trackId
|
|
463
463
|
};
|
|
464
464
|
}
|
|
465
|
-
console.log(`
|
|
465
|
+
console.log(` [OK] Enviado - TrackId: ${resultadoRCOF.trackId}`);
|
|
466
466
|
|
|
467
467
|
// Resumen
|
|
468
468
|
console.log('\n' + '═'.repeat(60));
|
|
469
|
-
console.log('
|
|
469
|
+
console.log('[OK] CERTIFICACIÓN BOLETAS COMPLETADA');
|
|
470
470
|
console.log('═'.repeat(60));
|
|
471
|
-
console.log(`
|
|
472
|
-
console.log(`
|
|
473
|
-
console.log(`
|
|
474
|
-
console.log(`
|
|
471
|
+
console.log(` EnvioBOLETA: ${resultadoBoleta.trackId}`);
|
|
472
|
+
console.log(` RCOF: ${resultadoRCOF.trackId}`);
|
|
473
|
+
console.log(` Boletas: ${boletas.length}`);
|
|
474
|
+
console.log(` Folios: ${folioInicial} - ${folioFinal}`);
|
|
475
475
|
|
|
476
476
|
return {
|
|
477
477
|
success: true,
|
|
@@ -494,9 +494,9 @@ class BoletaCert {
|
|
|
494
494
|
const forge = require('node-forge');
|
|
495
495
|
|
|
496
496
|
console.log('\n' + '═'.repeat(60));
|
|
497
|
-
console.log('
|
|
497
|
+
console.log('DECLARAR AVANCE BOLETAS');
|
|
498
498
|
console.log('═'.repeat(60));
|
|
499
|
-
console.log(`
|
|
499
|
+
console.log(` TrackId: ${trackId}`);
|
|
500
500
|
|
|
501
501
|
const host = this.ambiente === 'produccion' ? 'palena.sii.cl' : 'maullin.sii.cl';
|
|
502
502
|
const endpoint = '/cgi_dte/UPL/DTEauth?3';
|
|
@@ -548,7 +548,7 @@ class BoletaCert {
|
|
|
548
548
|
req.end();
|
|
549
549
|
});
|
|
550
550
|
|
|
551
|
-
console.log(`
|
|
551
|
+
console.log(` URL: ${url}`);
|
|
552
552
|
|
|
553
553
|
try {
|
|
554
554
|
const response = await requestWithCert({
|
|
@@ -561,7 +561,7 @@ class BoletaCert {
|
|
|
561
561
|
},
|
|
562
562
|
}, body);
|
|
563
563
|
|
|
564
|
-
console.log(`
|
|
564
|
+
console.log(` Status: ${response.status}`);
|
|
565
565
|
|
|
566
566
|
// Guardar respuesta para debug
|
|
567
567
|
if (this.debugDir) {
|
|
@@ -583,24 +583,24 @@ class BoletaCert {
|
|
|
583
583
|
const enRevision = /EN REVISION|PROCESANDO/i.test(html);
|
|
584
584
|
|
|
585
585
|
if (esAprobado) {
|
|
586
|
-
console.log('
|
|
586
|
+
console.log(' [OK] BOLETAS APROBADAS');
|
|
587
587
|
return { success: true, estado: 'APROBADO', html };
|
|
588
588
|
} else if (esRechazado) {
|
|
589
|
-
console.log('
|
|
589
|
+
console.log(' [ERR] BOLETAS RECHAZADAS');
|
|
590
590
|
// Extraer mensaje de error si existe
|
|
591
591
|
const errorMatch = html.match(/<font[^>]*color[^>]*red[^>]*>([^<]+)</i);
|
|
592
592
|
const errorMsg = errorMatch ? errorMatch[1].trim() : 'Error desconocido';
|
|
593
593
|
return { success: false, estado: 'RECHAZADO', error: errorMsg, html };
|
|
594
594
|
} else if (enRevision) {
|
|
595
|
-
console.log('
|
|
595
|
+
console.log(' [...] EN REVISIÓN');
|
|
596
596
|
return { success: true, estado: 'EN_REVISION', html };
|
|
597
597
|
} else {
|
|
598
|
-
console.log('
|
|
598
|
+
console.log(' Respuesta recibida (verificar manualmente)');
|
|
599
599
|
return { success: true, estado: 'DESCONOCIDO', html };
|
|
600
600
|
}
|
|
601
601
|
|
|
602
602
|
} catch (error) {
|
|
603
|
-
console.error(`
|
|
603
|
+
console.error(` [ERR] Error: ${error.message}`);
|
|
604
604
|
return { success: false, error: error.message };
|
|
605
605
|
}
|
|
606
606
|
}
|
|
@@ -614,8 +614,8 @@ class BoletaCert {
|
|
|
614
614
|
const https = require('https');
|
|
615
615
|
const forge = require('node-forge');
|
|
616
616
|
|
|
617
|
-
console.log(`\n
|
|
618
|
-
console.log(`
|
|
617
|
+
console.log(`\n Consultando estado del set...`);
|
|
618
|
+
console.log(` TrackId: ${trackId}`);
|
|
619
619
|
|
|
620
620
|
const host = this.ambiente === 'produccion' ? 'www4.sii.cl' : 'www4.sii.cl';
|
|
621
621
|
|
|
@@ -718,7 +718,7 @@ class BoletaCert {
|
|
|
718
718
|
}
|
|
719
719
|
|
|
720
720
|
} catch (error) {
|
|
721
|
-
console.error(`
|
|
721
|
+
console.error(` [!] Error consultando estado: ${error.message}`);
|
|
722
722
|
return {
|
|
723
723
|
success: true,
|
|
724
724
|
estado: 'DESCONOCIDO',
|