@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/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
|
/**
|
|
@@ -378,14 +393,22 @@ class SiiCertificacion {
|
|
|
378
393
|
// Parsear estado de cada set/libro
|
|
379
394
|
const estadoSets = {};
|
|
380
395
|
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
|
|
396
|
+
// Parsear estado de cada set/libro fila por fila para evitar falsos positivos
|
|
397
|
+
// (la regex global cruzaba filas y asignaba REVISADO CONFORME de SET CASO GENERAL a LIBRO DE VENTAS)
|
|
398
|
+
const rows = html.split(/<\/tr>/i);
|
|
399
|
+
for (const row of rows) {
|
|
400
|
+
const nameMatch = /(SET[^<\n\r]*|LIBRO[^<\n\r]*)<\/font><\/td>/i.exec(row);
|
|
401
|
+
if (!nameMatch) continue;
|
|
402
|
+
const nombre = nameMatch[1].trim();
|
|
403
|
+
// Estado explícito en <b>...</b>
|
|
404
|
+
const stateMatch = /<b>([^<]+)<\/b>/i.exec(row);
|
|
405
|
+
if (stateMatch) {
|
|
406
|
+
estadoSets[nombre] = stateMatch[1].trim().toUpperCase();
|
|
407
|
+
} else {
|
|
408
|
+
// Fila con campos input: leer valor EST oculto (S01 = sin declarar, S21 = declarado)
|
|
409
|
+
const estMatch = /name="EST\d+"[^>]*value="([^"]+)"/i.exec(row);
|
|
410
|
+
estadoSets[nombre] = estMatch ? estMatch[1] : 'S01';
|
|
411
|
+
}
|
|
389
412
|
}
|
|
390
413
|
|
|
391
414
|
// Verificar si todos los sets requeridos están REVISADO CONFORME
|
|
@@ -510,7 +533,7 @@ class SiiCertificacion {
|
|
|
510
533
|
}
|
|
511
534
|
|
|
512
535
|
if (process.env.DEBUG_SII) {
|
|
513
|
-
console.log('
|
|
536
|
+
console.log(' [DEBUG] Campos hidden encontrados:', Object.keys(hiddenFields).join(', '));
|
|
514
537
|
}
|
|
515
538
|
|
|
516
539
|
// Si no hay formulario para declarar (página de estado/reparos)
|
|
@@ -541,16 +564,17 @@ class SiiCertificacion {
|
|
|
541
564
|
// El SII cambia el orden según qué sets están aprobados
|
|
542
565
|
const fieldMapping = {};
|
|
543
566
|
|
|
544
|
-
|
|
567
|
+
// Guardar pe_avance2 para debug (siempre)
|
|
568
|
+
{
|
|
545
569
|
const fs = require('fs');
|
|
546
570
|
const path = require('path');
|
|
547
|
-
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug');
|
|
571
|
+
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug/cert-v2');
|
|
548
572
|
if (!fs.existsSync(debugDir)) {
|
|
549
573
|
fs.mkdirSync(debugDir, { recursive: true });
|
|
550
574
|
}
|
|
551
|
-
const debugPath = path.join(debugDir, '
|
|
575
|
+
const debugPath = path.join(debugDir, 'pe_avance2_form.html');
|
|
552
576
|
fs.writeFileSync(debugPath, formHtml, 'utf8');
|
|
553
|
-
|
|
577
|
+
|
|
554
578
|
}
|
|
555
579
|
|
|
556
580
|
// Extraer todas las filas <tr>...</tr> del formulario
|
|
@@ -587,7 +611,7 @@ class SiiCertificacion {
|
|
|
587
611
|
// Solo agregar si tiene NUM_ENV (no si tiene REVISADO CONFORME o EN REVISION)
|
|
588
612
|
fieldMapping[pattern.name] = parseInt(numEnvMatch[1]);
|
|
589
613
|
if (process.env.DEBUG_SII) {
|
|
590
|
-
console.log(`
|
|
614
|
+
console.log(` [DEBUG] ${pattern.name} → NUM_ENV${numEnvMatch[1]}`);
|
|
591
615
|
}
|
|
592
616
|
}
|
|
593
617
|
break; // Ya encontramos la fila para este pattern
|
|
@@ -596,7 +620,7 @@ class SiiCertificacion {
|
|
|
596
620
|
}
|
|
597
621
|
|
|
598
622
|
if (process.env.DEBUG_SII) {
|
|
599
|
-
console.log('
|
|
623
|
+
console.log(' [DEBUG] Mapeo dinámico de índices:', fieldMapping);
|
|
600
624
|
}
|
|
601
625
|
|
|
602
626
|
if (sets.setSimulacion && !fieldMapping.setSimulacion) {
|
|
@@ -637,7 +661,7 @@ class SiiCertificacion {
|
|
|
637
661
|
numEnvValues[index] = cleanTrackId;
|
|
638
662
|
fecEnvValues[index] = formatDate(fecha);
|
|
639
663
|
if (String(trackId) !== cleanTrackId) {
|
|
640
|
-
|
|
664
|
+
|
|
641
665
|
}
|
|
642
666
|
}
|
|
643
667
|
}
|
|
@@ -668,16 +692,16 @@ class SiiCertificacion {
|
|
|
668
692
|
|
|
669
693
|
// Debug: mostrar datos del formulario
|
|
670
694
|
if (process.env.DEBUG_SII) {
|
|
671
|
-
console.log('
|
|
695
|
+
console.log(' [DEBUG] Datos del formulario a enviar:');
|
|
672
696
|
for (const [k, v] of Object.entries(formData)) {
|
|
673
|
-
console.log(`
|
|
697
|
+
console.log(` ${k}: ${v || '(vacío)'}`);
|
|
674
698
|
}
|
|
675
699
|
|
|
676
700
|
// Mostrar el body codificado
|
|
677
701
|
const SiiSession = require('./SiiSession');
|
|
678
702
|
const encodedBody = SiiSession.formEncode(formData);
|
|
679
|
-
console.log('
|
|
680
|
-
console.log(`
|
|
703
|
+
console.log(' [DEBUG] Body codificado (primeros 500 chars):');
|
|
704
|
+
console.log(` ${encodedBody.substring(0, 500)}`);
|
|
681
705
|
}
|
|
682
706
|
|
|
683
707
|
// 5. Enviar formulario de declaración
|
|
@@ -697,7 +721,7 @@ class SiiCertificacion {
|
|
|
697
721
|
|
|
698
722
|
// Debug - guardar respuesta
|
|
699
723
|
if (process.env.DEBUG_SII) {
|
|
700
|
-
console.log(`
|
|
724
|
+
console.log(` [DEBUG] Respuesta: ${body.length} bytes, status: ${declareResponse.status}`);
|
|
701
725
|
// Guardar respuesta de pe_avance3 para debug
|
|
702
726
|
const fs = require('fs');
|
|
703
727
|
const path = require('path');
|
|
@@ -707,7 +731,7 @@ class SiiCertificacion {
|
|
|
707
731
|
}
|
|
708
732
|
const debugPath = path.join(debugDir, 'pe_avance3_response.html');
|
|
709
733
|
fs.writeFileSync(debugPath, body);
|
|
710
|
-
console.log(`
|
|
734
|
+
console.log(` [DEBUG] Respuesta guardada en: ${debugPath}`);
|
|
711
735
|
}
|
|
712
736
|
|
|
713
737
|
const bodyLower = body.toLowerCase();
|
|
@@ -737,13 +761,117 @@ class SiiCertificacion {
|
|
|
737
761
|
// Éxito si el form se envió sin errores de sesión/contenido.
|
|
738
762
|
// El estado del envío (ERRORES O REPAROS / EN REVISION / REVISADO CONFORME)
|
|
739
763
|
// NO determina el éxito de la declaración — eso se resuelve vía polling.
|
|
740
|
-
const
|
|
764
|
+
const postOk = (!hasError || hasSuccess) && !sesionExpirada && !contenidoNoCorresponde;
|
|
765
|
+
|
|
766
|
+
if (!postOk) {
|
|
767
|
+
return {
|
|
768
|
+
success: false,
|
|
769
|
+
error: errorMsg || undefined,
|
|
770
|
+
status: declareResponse.status,
|
|
771
|
+
rawHtml: body,
|
|
772
|
+
formHtml,
|
|
773
|
+
setsDeclarados: Object.keys(sets),
|
|
774
|
+
formDataSent: formData,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Guardar HTML de pe_avance3 (respuesta de declaración) siempre para debug
|
|
779
|
+
{
|
|
780
|
+
const fs = require('fs');
|
|
781
|
+
const path = require('path');
|
|
782
|
+
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug/cert-v2');
|
|
783
|
+
if (!fs.existsSync(debugDir)) fs.mkdirSync(debugDir, { recursive: true });
|
|
784
|
+
fs.writeFileSync(path.join(debugDir, 'pe_avance3_response.html'), body, 'utf8');
|
|
785
|
+
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// 7. VERIFICACIÓN POST-DECLARACIÓN: Re-leer pe_avance2 y confirmar que los TrackIDs se guardaron
|
|
789
|
+
// Si aparece "EN REVISION" → la declaración fue aceptada y el SII está procesando
|
|
790
|
+
// Si reaparecen inputs vacíos → la declaración no se guardó
|
|
791
|
+
let verificado = true;
|
|
792
|
+
let verificacionError = '';
|
|
793
|
+
let enRevision = false;
|
|
794
|
+
let camposVacios = [];
|
|
795
|
+
try {
|
|
796
|
+
const verifyResponse = await this.session.submitForm(
|
|
797
|
+
'/cvc_cgi/dte/pe_avance2',
|
|
798
|
+
{ RUT_EMP: this.rutEmpresa, DV_EMP: this.dvEmpresa, ACEPTAR: 'Continuar' },
|
|
799
|
+
'https://maullin.sii.cl/cvc_cgi/dte/pe_avance1'
|
|
800
|
+
);
|
|
801
|
+
const verifyHtml = verifyResponse.body || '';
|
|
802
|
+
|
|
803
|
+
// Guardar HTML de verificación pe_avance2 siempre
|
|
804
|
+
{
|
|
805
|
+
const fs = require('fs');
|
|
806
|
+
const path = require('path');
|
|
807
|
+
const debugDir = process.env.SII_DEBUG_DIR || path.join(__dirname, '../../debug/cert-v2');
|
|
808
|
+
if (!fs.existsSync(debugDir)) fs.mkdirSync(debugDir, { recursive: true });
|
|
809
|
+
fs.writeFileSync(path.join(debugDir, 'pe_avance2_verify.html'), verifyHtml, 'utf8');
|
|
810
|
+
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Para cada set que declaramos, verificar estado
|
|
814
|
+
const verifyRows = verifyHtml.split(/<\/tr>/i);
|
|
815
|
+
const camposEnRevision = [];
|
|
816
|
+
for (const [setName, index] of Object.entries(fieldMapping)) {
|
|
817
|
+
if (!sets[setName]) continue;
|
|
818
|
+
for (const row of verifyRows) {
|
|
819
|
+
const numEnvMatch = row.match(new RegExp(`NAME="NUM_ENV${index}"`, 'i'));
|
|
820
|
+
if (!numEnvMatch) {
|
|
821
|
+
// Si no hay input NUM_ENV, verificar si aparece REVISADO CONFORME o EN REVISION
|
|
822
|
+
const labelPattern = patterns.find(p => p.name === setName);
|
|
823
|
+
if (labelPattern && labelPattern.label.test(row)) {
|
|
824
|
+
if (/<b>[^<]*REVISADO CONFORME[^<]*<\/b>/i.test(row)) break;
|
|
825
|
+
if (/EN REVISION/i.test(row)) {
|
|
826
|
+
camposEnRevision.push(setName);
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
// Tiene input — verificar si tiene valor o está REVISADO CONFORME
|
|
833
|
+
const tieneConforme = /<b>[^<]*REVISADO CONFORME[^<]*<\/b>/i.test(row);
|
|
834
|
+
if (tieneConforme) break;
|
|
835
|
+
if (/EN REVISION/i.test(row)) {
|
|
836
|
+
camposEnRevision.push(setName);
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
const valueMatch = row.match(new RegExp(`NAME="NUM_ENV${index}"[^>]*value="([^"]*)"`, 'i'));
|
|
840
|
+
const tieneValor = valueMatch && valueMatch[1] && valueMatch[1].trim() !== '';
|
|
841
|
+
if (!tieneValor) {
|
|
842
|
+
camposVacios.push(setName);
|
|
843
|
+
}
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (camposEnRevision.length > 0) {
|
|
849
|
+
enRevision = true;
|
|
850
|
+
console.log(` [...] EN REVISION: ${camposEnRevision.join(', ')} — declaración aceptada, SII procesando`);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (camposVacios.length > 0 && !enRevision) {
|
|
854
|
+
verificado = false;
|
|
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.`;
|
|
856
|
+
console.log(` [!] ${verificacionError}`);
|
|
857
|
+
}
|
|
858
|
+
} catch (verifyErr) {
|
|
859
|
+
console.log(` [!] No se pudo verificar la declaración: ${verifyErr.message}`);
|
|
860
|
+
}
|
|
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;
|
|
741
865
|
|
|
742
866
|
return {
|
|
743
|
-
success,
|
|
744
|
-
error: errorMsg || undefined,
|
|
867
|
+
success: verificado || enRevision,
|
|
868
|
+
error: verificacionError || errorMsg || undefined,
|
|
869
|
+
verificado,
|
|
870
|
+
enRevision,
|
|
871
|
+
allRejected,
|
|
745
872
|
status: declareResponse.status,
|
|
746
873
|
rawHtml: body,
|
|
874
|
+
formHtml,
|
|
747
875
|
setsDeclarados: Object.keys(sets),
|
|
748
876
|
formDataSent: formData,
|
|
749
877
|
};
|
|
@@ -798,7 +926,7 @@ class SiiCertificacion {
|
|
|
798
926
|
}
|
|
799
927
|
const debugPath = path.join(debugDir, 'pe_avance2_avanzar.html');
|
|
800
928
|
fs.writeFileSync(debugPath, formHtml, 'utf8');
|
|
801
|
-
console.log('
|
|
929
|
+
console.log(' [DEBUG] HTML avanzar guardado en:', debugPath);
|
|
802
930
|
}
|
|
803
931
|
|
|
804
932
|
let avanceResponse = await this.session.submitForm(
|
|
@@ -824,7 +952,7 @@ class SiiCertificacion {
|
|
|
824
952
|
}
|
|
825
953
|
const debugPath = path.join(debugDir, 'pe_avance3_avanzar_response.html');
|
|
826
954
|
fs.writeFileSync(debugPath, body);
|
|
827
|
-
console.log('
|
|
955
|
+
console.log(' [DEBUG] Respuesta avanzar guardada en:', debugPath);
|
|
828
956
|
}
|
|
829
957
|
|
|
830
958
|
const hasError = body.toLowerCase().includes('error') && !body.includes('Error de Sesión');
|
|
@@ -1161,6 +1289,7 @@ class SiiCertificacion {
|
|
|
1161
1289
|
if (onProgress) {
|
|
1162
1290
|
onProgress({ intento, maxIntentos, estado: 'polling' });
|
|
1163
1291
|
}
|
|
1292
|
+
emitProgress(STEPS.POLLING, { intento, max: maxIntentos, label: 'sets' });
|
|
1164
1293
|
|
|
1165
1294
|
const result = await this.verAvanceParsed();
|
|
1166
1295
|
if (!result.success) continue;
|
|
@@ -1180,10 +1309,12 @@ class SiiCertificacion {
|
|
|
1180
1309
|
}
|
|
1181
1310
|
|
|
1182
1311
|
if (todosConformes) {
|
|
1312
|
+
emitProgress(STEPS.SETS_APPROVED);
|
|
1183
1313
|
return { success: true, estados: estadosRelevantes };
|
|
1184
1314
|
}
|
|
1185
1315
|
|
|
1186
1316
|
if (algunoRechazado) {
|
|
1317
|
+
emitProgress(STEPS.SETS_REJECTED);
|
|
1187
1318
|
return { success: false, error: 'Sets rechazados', estados: estadosRelevantes };
|
|
1188
1319
|
}
|
|
1189
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
|
}
|