@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/cert/CertRunner.js
CHANGED
|
@@ -43,6 +43,7 @@ const Simulacion = require('./Simulacion');
|
|
|
43
43
|
const IntercambioCert = require('./IntercambioCert');
|
|
44
44
|
|
|
45
45
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
46
|
+
const { STEPS, emitProgress } = require('../utils/progress');
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* @typedef {Object} CertConfig
|
|
@@ -136,6 +137,7 @@ class CertRunner {
|
|
|
136
137
|
pfxPassword: this.config.certificado.password,
|
|
137
138
|
rutEmpresa: rut,
|
|
138
139
|
dvEmpresa: dv,
|
|
140
|
+
sessionPath: this.sessionPath,
|
|
139
141
|
});
|
|
140
142
|
}
|
|
141
143
|
return this._siiCert;
|
|
@@ -210,7 +212,8 @@ class CertRunner {
|
|
|
210
212
|
this.folioHelper.usedFolios.clear();
|
|
211
213
|
|
|
212
214
|
for (const [tipoDte, cantidad] of Object.entries(cafRequired)) {
|
|
213
|
-
|
|
215
|
+
emitProgress(STEPS.CAF_REQUESTING, { tipo: Number(tipoDte) });
|
|
216
|
+
console.log(` Tipo ${tipoDte}: ${cantidad} folios...`);
|
|
214
217
|
|
|
215
218
|
// Usar solicitarCafConFallback que solicita y retorna el path
|
|
216
219
|
const cafPath = await this.folioService.solicitarCafConFallback({
|
|
@@ -223,7 +226,8 @@ class CertRunner {
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
cafs[tipoDte] = cafPath;
|
|
226
|
-
|
|
229
|
+
emitProgress(STEPS.CAF_OK, { tipo: Number(tipoDte) });
|
|
230
|
+
console.log(` ✓ CAF tipo ${tipoDte}`);
|
|
227
231
|
}
|
|
228
232
|
|
|
229
233
|
return cafs;
|
|
@@ -248,8 +252,6 @@ class CertRunner {
|
|
|
248
252
|
// Guardar envío consolidado
|
|
249
253
|
const envioPath = path.join(setsDir, `envio-set-${setName}.xml`);
|
|
250
254
|
fs.writeFileSync(envioPath, envio.xml, 'utf8');
|
|
251
|
-
console.log(` 📄 XML guardado: ${envioPath}`);
|
|
252
|
-
|
|
253
255
|
// Guardar DTEs individuales
|
|
254
256
|
if (envio.dtes && envio.dtes.length > 0) {
|
|
255
257
|
const dtesDir = path.join(setsDir, 'dtes');
|
|
@@ -312,20 +314,36 @@ class CertRunner {
|
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
async ejecutarSetBasico(casos) {
|
|
315
|
-
|
|
317
|
+
emitProgress(STEPS.SET_START, { set: 'basico' });
|
|
318
|
+
const r = await this._ejecutarSet(SetBasico, 'setBasico', 'basico', { 33: 4, 56: 1, 61: 3 }, 'basico', casos);
|
|
319
|
+
if (r.success) emitProgress(STEPS.SET_OK, { set: 'basico', trackId: r.trackId });
|
|
320
|
+
else emitProgress(STEPS.SET_ERROR, { set: 'basico', error: r.error });
|
|
321
|
+
return r;
|
|
316
322
|
}
|
|
317
323
|
|
|
318
324
|
async ejecutarSetGuia(casos) {
|
|
319
|
-
|
|
325
|
+
emitProgress(STEPS.SET_START, { set: 'guia' });
|
|
326
|
+
const r = await this._ejecutarSet(SetGuia, 'setGuiaDespacho', 'guia',
|
|
320
327
|
(setData) => ({ 52: setData.casos?.length || 1 }), 'guia', casos);
|
|
328
|
+
if (r.success) emitProgress(STEPS.SET_OK, { set: 'guia', trackId: r.trackId });
|
|
329
|
+
else emitProgress(STEPS.SET_ERROR, { set: 'guia', error: r.error });
|
|
330
|
+
return r;
|
|
321
331
|
}
|
|
322
332
|
|
|
323
333
|
async ejecutarSetExenta(casos) {
|
|
324
|
-
|
|
334
|
+
emitProgress(STEPS.SET_START, { set: 'exenta' });
|
|
335
|
+
const r = await this._ejecutarSet(SetExenta, 'setFacturaExenta', 'exenta', { 34: 3, 56: 1, 61: 4 }, 'exenta', casos);
|
|
336
|
+
if (r.success) emitProgress(STEPS.SET_OK, { set: 'exenta', trackId: r.trackId });
|
|
337
|
+
else emitProgress(STEPS.SET_ERROR, { set: 'exenta', error: r.error });
|
|
338
|
+
return r;
|
|
325
339
|
}
|
|
326
340
|
|
|
327
341
|
async ejecutarSetCompra(casos) {
|
|
328
|
-
|
|
342
|
+
emitProgress(STEPS.SET_START, { set: 'compra' });
|
|
343
|
+
const r = await this._ejecutarSet(SetCompra, 'setFacturaCompra', 'compra', { 46: 1, 56: 1, 61: 1 }, 'compra', casos);
|
|
344
|
+
if (r.success) emitProgress(STEPS.SET_OK, { set: 'compra', trackId: r.trackId });
|
|
345
|
+
else emitProgress(STEPS.SET_ERROR, { set: 'compra', error: r.error });
|
|
346
|
+
return r;
|
|
329
347
|
}
|
|
330
348
|
|
|
331
349
|
/**
|
|
@@ -338,12 +356,13 @@ class CertRunner {
|
|
|
338
356
|
async _declararConReintentos(sets, debugPrefix, options = {}) {
|
|
339
357
|
const { maxIntentos = 10, intervalo = 5000, label = 'avance' } = options;
|
|
340
358
|
|
|
341
|
-
console.log(`
|
|
359
|
+
console.log(` Esperando 10s para que SII procese los envios...`);
|
|
342
360
|
await sleep(10000);
|
|
343
361
|
|
|
344
362
|
let lastResult = null;
|
|
345
363
|
for (let intento = 1; intento <= maxIntentos; intento++) {
|
|
346
|
-
|
|
364
|
+
emitProgress(STEPS.POLLING, { intento, max: maxIntentos, label });
|
|
365
|
+
console.log(` Declarando ${label} (intento ${intento}/${maxIntentos})...`);
|
|
347
366
|
|
|
348
367
|
const result = await this.siiCert.declararAvance({ sets });
|
|
349
368
|
lastResult = result;
|
|
@@ -378,15 +397,19 @@ class CertRunner {
|
|
|
378
397
|
}
|
|
379
398
|
|
|
380
399
|
if (noProcessedError && intento < maxIntentos) {
|
|
381
|
-
console.log(`
|
|
400
|
+
console.log(` [...] SII aún procesando, reintentando en ${intervalo / 1000}s...`);
|
|
382
401
|
await sleep(intervalo);
|
|
402
|
+
} else if (result.allRejected) {
|
|
403
|
+
// SII rechazó todos los sets/libros — período incorrecto, no tiene sentido reintentar
|
|
404
|
+
console.log(` [ERR] SII rechazó todos los envíos (campos vacíos en portal) — período incorrecto. Corregir período y reenviar.`);
|
|
405
|
+
break;
|
|
383
406
|
} else if (result.verificado === false && intento < maxIntentos) {
|
|
384
407
|
// Verificación post-declaración falló: los campos quedaron vacíos en el portal
|
|
385
|
-
console.log(`
|
|
386
|
-
console.log(`
|
|
408
|
+
console.log(` [!] Verificación fallida: ${result.error}`);
|
|
409
|
+
console.log(` [...] Reintentando declaración en ${intervalo / 1000}s...`);
|
|
387
410
|
await sleep(intervalo);
|
|
388
411
|
} else if (!result.success) {
|
|
389
|
-
console.log(`
|
|
412
|
+
console.log(` [!] Error declarando ${label}: ${result.error || 'desconocido'}`);
|
|
390
413
|
break;
|
|
391
414
|
}
|
|
392
415
|
}
|
|
@@ -434,8 +457,9 @@ class CertRunner {
|
|
|
434
457
|
return { success: false, error: 'No hay sets para declarar' };
|
|
435
458
|
}
|
|
436
459
|
|
|
460
|
+
emitProgress(STEPS.SETS_DECLARING);
|
|
437
461
|
const result = await this._declararConReintentos(sets, 'declaracion-response', { maxIntentos, intervalo, label: 'avance de sets' });
|
|
438
|
-
if (result?.success) console.log('
|
|
462
|
+
if (result?.success) { emitProgress(STEPS.SETS_DECLARED); console.log(' ✓ Declaracion de sets enviada'); }
|
|
439
463
|
return result;
|
|
440
464
|
}
|
|
441
465
|
|
|
@@ -480,13 +504,19 @@ class CertRunner {
|
|
|
480
504
|
*/
|
|
481
505
|
async ejecutarFase4Libros(options = {}) {
|
|
482
506
|
// NOTA: Ya NO decrementamos aquí - cada libro decrementa su propio período
|
|
507
|
+
emitProgress(STEPS.BOOKS_START);
|
|
483
508
|
console.log('\n' + '═'.repeat(60));
|
|
484
|
-
console.log(
|
|
509
|
+
console.log('FASE 4: LIBROS (todos usan el mismo periodo)');
|
|
485
510
|
console.log('═'.repeat(60) + '\n');
|
|
486
511
|
|
|
487
512
|
const resultados = {};
|
|
488
513
|
const errores = [];
|
|
489
514
|
|
|
515
|
+
// Decrementar período UNA VEZ para todos los libros (todos usan el mismo período)
|
|
516
|
+
this._decrementarPeriodoLibros();
|
|
517
|
+
const _periodoComunLibros = this._getPeriodoLibros();
|
|
518
|
+
console.log(` Período para todos los libros: ${_periodoComunLibros}`);
|
|
519
|
+
|
|
490
520
|
// Verificar cuáles libros ya están REVISADO CONFORME en el portal (no re-enviar)
|
|
491
521
|
let _estadoActual = {};
|
|
492
522
|
try {
|
|
@@ -496,7 +526,7 @@ class CertRunner {
|
|
|
496
526
|
.filter(([k, v]) => k.toUpperCase().includes('LIBRO') && v === 'REVISADO CONFORME')
|
|
497
527
|
.map(([k]) => k);
|
|
498
528
|
if (_yaConformes.length) {
|
|
499
|
-
console.log(`
|
|
529
|
+
console.log(` Ya en REVISADO CONFORME (se omitirán): ${_yaConformes.join(', ')}`);
|
|
500
530
|
}
|
|
501
531
|
} catch (_e) { /* ignorar error de consulta previa */ }
|
|
502
532
|
|
|
@@ -521,13 +551,18 @@ class CertRunner {
|
|
|
521
551
|
try {
|
|
522
552
|
// 1. Libro de Compras (usa datos del SII)
|
|
523
553
|
if (_estaConforme('LIBRO DE COMPRAS')) {
|
|
524
|
-
|
|
554
|
+
emitProgress(STEPS.BOOK_SKIPPED, { book: 'libroCompras' });
|
|
555
|
+
resultados.libroCompras = { success: true, conforme: true };
|
|
556
|
+
console.log('\n[OK] Libro de Compras ya esta REVISADO CONFORME — omitiendo');
|
|
525
557
|
} else {
|
|
526
|
-
|
|
527
|
-
|
|
558
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroCompras' });
|
|
559
|
+
console.log('\nEnviando Libro de Compras...');
|
|
560
|
+
resultados.libroCompras = await this.ejecutarLibroCompras({ ...options, periodo: _periodoComunLibros });
|
|
528
561
|
if (!resultados.libroCompras.success) {
|
|
562
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroCompras', error: resultados.libroCompras.error });
|
|
529
563
|
errores.push(`Libro Compras: ${resultados.libroCompras.error}`);
|
|
530
564
|
} else {
|
|
565
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroCompras', trackId: resultados.libroCompras.trackId });
|
|
531
566
|
_guardarResultadosParciales();
|
|
532
567
|
}
|
|
533
568
|
}
|
|
@@ -538,13 +573,18 @@ class CertRunner {
|
|
|
538
573
|
try {
|
|
539
574
|
// 2. Libro de Ventas (usa SetBasico)
|
|
540
575
|
if (_estaConforme('LIBRO DE VENTAS')) {
|
|
541
|
-
|
|
576
|
+
emitProgress(STEPS.BOOK_SKIPPED, { book: 'libroVentas' });
|
|
577
|
+
resultados.libroVentas = { success: true, conforme: true };
|
|
578
|
+
console.log('\n[OK] Libro de Ventas ya esta REVISADO CONFORME — omitiendo');
|
|
542
579
|
} else {
|
|
543
|
-
|
|
544
|
-
|
|
580
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroVentas' });
|
|
581
|
+
console.log('\nEnviando Libro de Ventas...');
|
|
582
|
+
resultados.libroVentas = await this.ejecutarLibroVentas({ ...options, periodo: _periodoComunLibros });
|
|
545
583
|
if (!resultados.libroVentas.success) {
|
|
584
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroVentas', error: resultados.libroVentas.error });
|
|
546
585
|
errores.push(`Libro Ventas: ${resultados.libroVentas.error}`);
|
|
547
586
|
} else {
|
|
587
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroVentas', trackId: resultados.libroVentas.trackId });
|
|
548
588
|
_guardarResultadosParciales();
|
|
549
589
|
}
|
|
550
590
|
}
|
|
@@ -555,13 +595,18 @@ class CertRunner {
|
|
|
555
595
|
try {
|
|
556
596
|
// 3. Libro de Guías (usa SetGuia)
|
|
557
597
|
if (_estaConforme('LIBRO DE GUIAS')) {
|
|
558
|
-
|
|
598
|
+
emitProgress(STEPS.BOOK_SKIPPED, { book: 'libroGuias' });
|
|
599
|
+
resultados.libroGuias = { success: true, conforme: true };
|
|
600
|
+
console.log('\n[OK] Libro de Guias ya esta REVISADO CONFORME — omitiendo');
|
|
559
601
|
} else {
|
|
560
|
-
|
|
561
|
-
|
|
602
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroGuias' });
|
|
603
|
+
console.log('\nEnviando Libro de Guias...');
|
|
604
|
+
resultados.libroGuias = await this.ejecutarLibroGuias({ ...options, periodo: _periodoComunLibros });
|
|
562
605
|
if (!resultados.libroGuias.success) {
|
|
563
|
-
|
|
606
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroGuias', error: resultados.libroGuias.error });
|
|
607
|
+
errores.push(`Libro Guias: ${resultados.libroGuias.error}`);
|
|
564
608
|
} else {
|
|
609
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroGuias', trackId: resultados.libroGuias.trackId });
|
|
565
610
|
_guardarResultadosParciales();
|
|
566
611
|
}
|
|
567
612
|
}
|
|
@@ -573,13 +618,18 @@ class CertRunner {
|
|
|
573
618
|
if (this._estructuras?.libroComprasExentos) {
|
|
574
619
|
try {
|
|
575
620
|
if (_estaConforme('LIBRO DE COMPRAS PARA EXENTOS')) {
|
|
576
|
-
|
|
621
|
+
emitProgress(STEPS.BOOK_SKIPPED, { book: 'libroComprasExentos' });
|
|
622
|
+
resultados.libroComprasExentos = { success: true, conforme: true };
|
|
623
|
+
console.log('\n[OK] Libro Compras Exentos ya esta REVISADO CONFORME — omitiendo');
|
|
577
624
|
} else {
|
|
578
|
-
|
|
579
|
-
|
|
625
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroComprasExentos' });
|
|
626
|
+
console.log('\nEnviando Libro de Compras para Exentos...');
|
|
627
|
+
resultados.libroComprasExentos = await this.ejecutarLibroComprasExentos({ ...options, periodo: _periodoComunLibros });
|
|
580
628
|
if (!resultados.libroComprasExentos.success) {
|
|
629
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroComprasExentos', error: resultados.libroComprasExentos.error });
|
|
581
630
|
errores.push(`Libro Compras Exentos: ${resultados.libroComprasExentos.error}`);
|
|
582
631
|
} else {
|
|
632
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroComprasExentos', trackId: resultados.libroComprasExentos.trackId });
|
|
583
633
|
_guardarResultadosParciales();
|
|
584
634
|
}
|
|
585
635
|
}
|
|
@@ -599,64 +649,193 @@ class CertRunner {
|
|
|
599
649
|
}).length;
|
|
600
650
|
|
|
601
651
|
if (librosEnviados === 3) {
|
|
602
|
-
//
|
|
603
|
-
|
|
652
|
+
// Mapeo entre nombre SII y clave interna
|
|
653
|
+
const _SII_NOMBRE_A_KEY = {
|
|
654
|
+
'LIBRO DE VENTAS': 'libroVentas',
|
|
655
|
+
'LIBRO DE COMPRAS': 'libroCompras',
|
|
656
|
+
'LIBRO DE GUIAS': 'libroGuias',
|
|
657
|
+
'LIBRO DE COMPRAS PARA EXENTOS': 'libroComprasExentos',
|
|
658
|
+
};
|
|
659
|
+
const _KEY_A_SII_NOMBRE = Object.fromEntries(Object.entries(_SII_NOMBRE_A_KEY).map(([n, k]) => [k, n]));
|
|
660
|
+
|
|
661
|
+
// Busca el entry de _ss para un nombre SII (COMPRAS sin EXENTOS, etc.)
|
|
662
|
+
const _findEntry = (ss, nombre) => Object.entries(ss).find(([k]) => {
|
|
663
|
+
const ku = k.toUpperCase();
|
|
664
|
+
if (nombre === 'LIBRO DE COMPRAS') return ku.includes('LIBRO DE COMPRAS') && !ku.includes('EXENTOS');
|
|
665
|
+
return ku.includes(nombre);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// Retorna claves internas que están en S21 en ss, de entre los especificados
|
|
669
|
+
const _getS21Keys = (ss, nombres) =>
|
|
670
|
+
nombres
|
|
671
|
+
.filter(n => { const e = _findEntry(ss, n); return e && e[1] === 'S21'; })
|
|
672
|
+
.map(n => _SII_NOMBRE_A_KEY[n])
|
|
673
|
+
.filter(Boolean);
|
|
674
|
+
|
|
675
|
+
// Helper para re-enviar los libros no conformes con un nuevo período
|
|
676
|
+
// keysAReenviar: Set opcional — si se pasa, solo re-envía las claves del Set
|
|
677
|
+
const _reenviarLibros = async (nuevoPeriodo, keysAReenviar) => {
|
|
678
|
+
const _orden = [
|
|
679
|
+
{ key: 'libroCompras', fn: (p) => this.ejecutarLibroCompras({ ...options, periodo: p }) },
|
|
680
|
+
{ key: 'libroVentas', fn: (p) => this.ejecutarLibroVentas({ ...options, periodo: p }) },
|
|
681
|
+
{ key: 'libroGuias', fn: (p) => this.ejecutarLibroGuias({ ...options, periodo: p }) },
|
|
682
|
+
{ key: 'libroComprasExentos', fn: (p) => this.ejecutarLibroComprasExentos({ ...options, periodo: p }) },
|
|
683
|
+
];
|
|
684
|
+
for (const { key, fn } of _orden) {
|
|
685
|
+
if (resultados[key]?.conforme) continue; // ya conforme en SII
|
|
686
|
+
if (keysAReenviar && !keysAReenviar.has(key)) continue; // filtro por S21
|
|
687
|
+
emitProgress(STEPS.BOOK_SENDING, { book: key });
|
|
688
|
+
try {
|
|
689
|
+
resultados[key] = await fn(nuevoPeriodo);
|
|
690
|
+
if (!resultados[key].success) {
|
|
691
|
+
emitProgress(STEPS.BOOK_ERROR, { book: key, error: resultados[key].error });
|
|
692
|
+
} else {
|
|
693
|
+
emitProgress(STEPS.BOOK_OK, { book: key, trackId: resultados[key].trackId });
|
|
694
|
+
}
|
|
695
|
+
} catch (e) {
|
|
696
|
+
resultados[key] = { success: false, error: e.message };
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// Polling de aprobación (reutilizable)
|
|
702
|
+
// librosAVerificar: array de nombres SII a esperar. Si se omite, usa todos los no-conformes.
|
|
703
|
+
// Devuelve { ok, estadosFinal }
|
|
704
|
+
// Bail-out anticipado: si todos los pendientes llevan 5 polls consecutivos en S21 → período incorrecto
|
|
705
|
+
const _esperarAprobacion = async (librosAVerificar) => {
|
|
706
|
+
const _todosCandidatos = ['LIBRO DE VENTAS', 'LIBRO DE COMPRAS', 'LIBRO DE GUIAS'];
|
|
707
|
+
if (this._estructuras?.libroComprasExentos) _todosCandidatos.push('LIBRO DE COMPRAS PARA EXENTOS');
|
|
708
|
+
const _librosAVerif = librosAVerificar || _todosCandidatos.filter(n => !_estaConforme(n));
|
|
709
|
+
console.log(`\nEsperando aprobacion del SII para: ${_librosAVerif.join(', ')}`);
|
|
710
|
+
let _ss = {};
|
|
711
|
+
let _consecutivosS21 = 0;
|
|
712
|
+
for (let _i = 0; _i < 20; _i++) {
|
|
713
|
+
await sleep(15000);
|
|
714
|
+
emitProgress(STEPS.POLLING, { intento: _i + 1, max: 20, label: 'libros' });
|
|
715
|
+
const _poll = await this.siiCert.consultarEstadoSets();
|
|
716
|
+
if (!_poll.success) continue;
|
|
717
|
+
_ss = _poll.estadoSets || {};
|
|
718
|
+
const _info = Object.entries(_ss).filter(([k]) => k.toUpperCase().includes('LIBRO')).map(([k, v]) => `${k.trim()}: ${v}`);
|
|
719
|
+
if (_info.length) console.log(` [...] Intento ${_i + 1}/20: ${_info.join(' | ')}`);
|
|
720
|
+
const _librosObs = ['LIBRO DE VENTAS', 'LIBRO DE COMPRAS', 'LIBRO DE GUIAS'];
|
|
721
|
+
const _todosObligatoriosOk = _librosObs.every(n => {
|
|
722
|
+
const e = _findEntry(_ss, n);
|
|
723
|
+
return e && (e[1] === 'REVISADO CONFORME' || e[1] === 'S25');
|
|
724
|
+
});
|
|
725
|
+
const _todosOk = _librosAVerif.every(n => {
|
|
726
|
+
const e = _findEntry(_ss, n);
|
|
727
|
+
return e && (e[1] === 'REVISADO CONFORME' || e[1] === 'S25');
|
|
728
|
+
});
|
|
729
|
+
const _algunError = _librosAVerif.some(n => {
|
|
730
|
+
const e = _findEntry(_ss, n);
|
|
731
|
+
return e && (e[1] === 'LNC' || e[1] === 'LRH' || e[1].includes('RECHAZADO') || e[1].includes('ERROR'));
|
|
732
|
+
});
|
|
733
|
+
if (_todosOk) {
|
|
734
|
+
emitProgress(STEPS.BOOKS_DONE);
|
|
735
|
+
console.log('\n[OK] LIBROS APROBADOS POR EL SII!');
|
|
736
|
+
return { ok: true, estadosFinal: _ss };
|
|
737
|
+
}
|
|
738
|
+
if (_algunError) {
|
|
739
|
+
console.log('\n[ERR] Hay libros rechazados. Revisar emails del SII.');
|
|
740
|
+
return { ok: false, estadosFinal: _ss };
|
|
741
|
+
}
|
|
742
|
+
// Bail-out anticipado: todos los pendientes llevan N polls en S21 → período incorrecto
|
|
743
|
+
const _pendientesAun = _librosAVerif.filter(n => {
|
|
744
|
+
const e = _findEntry(_ss, n);
|
|
745
|
+
return !e || (e[1] !== 'REVISADO CONFORME' && e[1] !== 'S25');
|
|
746
|
+
});
|
|
747
|
+
const _todosS21 = _pendientesAun.length > 0 && _pendientesAun.every(n => {
|
|
748
|
+
const e = _findEntry(_ss, n);
|
|
749
|
+
return e && e[1] === 'S21';
|
|
750
|
+
});
|
|
751
|
+
if (_todosS21) {
|
|
752
|
+
_consecutivosS21++;
|
|
753
|
+
if (_consecutivosS21 >= 5) {
|
|
754
|
+
console.log(`\n[!] ${_pendientesAun.join(', ')} llevan ${_consecutivosS21} polls en S21 — período incorrecto.`);
|
|
755
|
+
return { ok: false, estadosFinal: _ss, stuckS21: true };
|
|
756
|
+
}
|
|
757
|
+
} else {
|
|
758
|
+
_consecutivosS21 = 0;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
console.log('\n[!] Timeout (5 min). El SII aún no responde. Verifica con --avance más tarde.');
|
|
762
|
+
return { ok: false, estadosFinal: _ss };
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
// 4. Declarar + retry automático:
|
|
766
|
+
// a) si allRejected al declarar → decrementar período y re-enviar todos
|
|
767
|
+
// b) si libros quedan en S21 tras polling → decrementar y re-enviar solo los S21
|
|
768
|
+
emitProgress(STEPS.BOOKS_DECLARING);
|
|
769
|
+
console.log('\nDeclarando libros...');
|
|
770
|
+
const MAX_PERIOD_RETRIES = 120;
|
|
604
771
|
try {
|
|
605
|
-
|
|
772
|
+
let declaracion = await this.declararLibros({ ...resultados, ...(options.setsResultados || {}) });
|
|
606
773
|
resultados.declaracion = declaracion;
|
|
607
|
-
|
|
774
|
+
|
|
775
|
+
// Fase a: allRejected al declarar (período rechazado en pe_avance3)
|
|
776
|
+
for (let _pRetry = 0; _pRetry < MAX_PERIOD_RETRIES && declaracion.allRejected; _pRetry++) {
|
|
777
|
+
this._decrementarPeriodoLibros();
|
|
778
|
+
const _nuevoPeriodo = this._getPeriodoLibros();
|
|
779
|
+
console.log(`\n[!] Período rechazado por SII. Reintentando con ${_nuevoPeriodo} (${_pRetry + 1}/${MAX_PERIOD_RETRIES})...`);
|
|
780
|
+
await _reenviarLibros(_nuevoPeriodo);
|
|
781
|
+
declaracion = await this.declararLibros({ ...resultados, ...(options.setsResultados || {}) });
|
|
782
|
+
resultados.declaracion = declaracion;
|
|
783
|
+
}
|
|
784
|
+
|
|
608
785
|
if (declaracion.success) {
|
|
609
|
-
console.log('\n
|
|
610
|
-
|
|
611
|
-
//
|
|
612
|
-
const
|
|
613
|
-
if (this._estructuras?.libroComprasExentos)
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
for (let
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const e = Object.entries(_ss).find(([k]) => k.toUpperCase().includes(nombre));
|
|
635
|
-
return e && (e[1] === 'LNC' || e[1] === 'LRH' || e[1].includes('RECHAZADO') || e[1].includes('ERROR'));
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
if (_todosOk) {
|
|
639
|
-
console.log('\n🎉 ¡LIBROS APROBADOS POR EL SII!');
|
|
640
|
-
_aprobados = true;
|
|
786
|
+
console.log('\n[OK] Libros declarados — esperando revisión del SII...');
|
|
787
|
+
|
|
788
|
+
// Construir la lista inicial de libros a verificar
|
|
789
|
+
const _todosLibrosNombres = ['LIBRO DE VENTAS', 'LIBRO DE COMPRAS', 'LIBRO DE GUIAS'];
|
|
790
|
+
if (this._estructuras?.libroComprasExentos) _todosLibrosNombres.push('LIBRO DE COMPRAS PARA EXENTOS');
|
|
791
|
+
let _librosAVerificar = _todosLibrosNombres.filter(n => !_estaConforme(n));
|
|
792
|
+
|
|
793
|
+
let { ok, estadosFinal } = await _esperarAprobacion(_librosAVerificar);
|
|
794
|
+
|
|
795
|
+
// Fase b: algunos libros quedaron en S21 → re-enviar solo esos con período decrementado
|
|
796
|
+
for (let _pRetry = 0; !ok && _pRetry < MAX_PERIOD_RETRIES; _pRetry++) {
|
|
797
|
+
const _s21Keys = _getS21Keys(estadosFinal, _librosAVerificar);
|
|
798
|
+
if (_s21Keys.length === 0) break; // errores reales (LNC/LRH), no de período
|
|
799
|
+
|
|
800
|
+
this._decrementarPeriodoLibros();
|
|
801
|
+
const _nuevoPeriodo = this._getPeriodoLibros();
|
|
802
|
+
const _s21Nombres = _s21Keys.map(k => _KEY_A_SII_NOMBRE[k]).filter(Boolean);
|
|
803
|
+
console.log(`\n[!] ${_s21Nombres.join(', ')} bloqueados en S21. Reintentando con período ${_nuevoPeriodo} (${_pRetry + 1}/${MAX_PERIOD_RETRIES})...`);
|
|
804
|
+
|
|
805
|
+
await _reenviarLibros(_nuevoPeriodo, new Set(_s21Keys));
|
|
806
|
+
declaracion = await this.declararLibros({ ...resultados, ...(options.setsResultados || {}) });
|
|
807
|
+
resultados.declaracion = declaracion;
|
|
808
|
+
|
|
809
|
+
if (!declaracion.success && !declaracion.allRejected) {
|
|
810
|
+
console.log(`\n[ERR] Declaración fallida: ${declaracion.error}`);
|
|
641
811
|
break;
|
|
642
812
|
}
|
|
643
|
-
if (
|
|
644
|
-
|
|
645
|
-
|
|
813
|
+
if (declaracion.success) {
|
|
814
|
+
// Solo verificar los libros que acabamos de re-enviar
|
|
815
|
+
_librosAVerificar = _s21Nombres;
|
|
816
|
+
;({ ok, estadosFinal } = await _esperarAprobacion(_librosAVerificar));
|
|
646
817
|
}
|
|
818
|
+
// si allRejected → continuar loop (decrementar de nuevo)
|
|
647
819
|
}
|
|
648
|
-
|
|
649
|
-
|
|
820
|
+
|
|
821
|
+
if (!ok) {
|
|
822
|
+
console.log('\n[!] No se pudo obtener aprobación del SII para todos los libros.');
|
|
650
823
|
}
|
|
651
824
|
} else {
|
|
652
|
-
|
|
825
|
+
const _errorDeclaracion = declaracion.error || 'Declaración rechazada por SII';
|
|
826
|
+
console.log(`\n[ERR] Declaración de libros fallida: ${_errorDeclaracion}`);
|
|
827
|
+
for (const k of ['libroVentas', 'libroCompras', 'libroGuias', 'libroComprasExentos']) {
|
|
828
|
+
if (resultados[k]?.success && !resultados[k]?.conforme) {
|
|
829
|
+
resultados[k] = { ...resultados[k], success: false, error: _errorDeclaracion };
|
|
830
|
+
}
|
|
831
|
+
}
|
|
653
832
|
}
|
|
654
833
|
} catch (e) {
|
|
655
|
-
console.log(`\n
|
|
834
|
+
console.log(`\n[!] Error declarando libros: ${e.message}`);
|
|
656
835
|
resultados.declaracion = { success: false, error: e.message };
|
|
657
836
|
}
|
|
658
837
|
} else {
|
|
659
|
-
console.log(`\n
|
|
838
|
+
console.log(`\n[!] Solo ${librosEnviados}/3 libros enviados. Errores: ${errores.join('; ')}`);
|
|
660
839
|
}
|
|
661
840
|
|
|
662
841
|
return {
|
|
@@ -706,7 +885,7 @@ class CertRunner {
|
|
|
706
885
|
const result = await this._declararConReintentos(sets, 'declaracion-libros-response', { maxIntentos, intervalo, label: 'libros' });
|
|
707
886
|
if (result?.success) {
|
|
708
887
|
const declarados = result.setsDeclarados || [];
|
|
709
|
-
console.log(`
|
|
888
|
+
console.log(` [OK] Libros declarados: ${declarados.join(', ')}`);
|
|
710
889
|
}
|
|
711
890
|
return result;
|
|
712
891
|
}
|
|
@@ -774,9 +953,9 @@ class CertRunner {
|
|
|
774
953
|
|
|
775
954
|
try {
|
|
776
955
|
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
777
|
-
console.log(`
|
|
956
|
+
console.log(` Período decrementado: ${currentPeriodo} → ${newPeriodo}`);
|
|
778
957
|
} catch (e) {
|
|
779
|
-
console.warn(`
|
|
958
|
+
console.warn(` [!] No se pudo guardar período: ${e.message}`);
|
|
780
959
|
}
|
|
781
960
|
|
|
782
961
|
return newPeriodo;
|
|
@@ -792,9 +971,9 @@ class CertRunner {
|
|
|
792
971
|
|
|
793
972
|
try {
|
|
794
973
|
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
795
|
-
console.log(`
|
|
974
|
+
console.log(` Período reseteado a: ${periodo}`);
|
|
796
975
|
} catch (e) {
|
|
797
|
-
console.warn(`
|
|
976
|
+
console.warn(` [!] No se pudo guardar período: ${e.message}`);
|
|
798
977
|
}
|
|
799
978
|
}
|
|
800
979
|
|
|
@@ -820,10 +999,9 @@ class CertRunner {
|
|
|
820
999
|
throw new Error('No hay resultado del SetBasico. Ejecutar ejecutarSetBasico() primero.');
|
|
821
1000
|
}
|
|
822
1001
|
|
|
823
|
-
//
|
|
824
|
-
this._decrementarPeriodoLibros();
|
|
825
|
-
|
|
826
|
-
console.log(` 📚 Generando Libro de Ventas para período ${periodo}...`);
|
|
1002
|
+
// Usar período pasado por opción (fase4 lo decrementa una vez para todos) o decrementar individualmente
|
|
1003
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
1004
|
+
console.log(` Generando Libro de Ventas para período ${periodo}...`);
|
|
827
1005
|
|
|
828
1006
|
const libroVentas = new LibroVentas({
|
|
829
1007
|
emisor: this.config.emisor,
|
|
@@ -838,7 +1016,6 @@ class CertRunner {
|
|
|
838
1016
|
// Guardar XML de debug
|
|
839
1017
|
const outPath = path.join(this.debugDir, 'libro-ventas.xml');
|
|
840
1018
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
841
|
-
console.log(` XML guardado: ${outPath}`);
|
|
842
1019
|
|
|
843
1020
|
// Enviar al SII
|
|
844
1021
|
const enviador = this._createLibroEnviador();
|
|
@@ -855,9 +1032,9 @@ class CertRunner {
|
|
|
855
1032
|
this.resultados.libroVentas = result;
|
|
856
1033
|
|
|
857
1034
|
if (result.success) {
|
|
858
|
-
console.log(`
|
|
1035
|
+
console.log(` [OK] Libro de Ventas enviado - TrackId: ${result.trackId}`);
|
|
859
1036
|
} else {
|
|
860
|
-
console.log(`
|
|
1037
|
+
console.log(` [ERR] Error enviando Libro de Ventas: ${result.error}`);
|
|
861
1038
|
}
|
|
862
1039
|
|
|
863
1040
|
return result;
|
|
@@ -872,9 +1049,7 @@ class CertRunner {
|
|
|
872
1049
|
async ejecutarLibroCompras(options = {}) {
|
|
873
1050
|
const libroComprasData = options.libroComprasData || this._estructuras?.libroCompras;
|
|
874
1051
|
|
|
875
|
-
|
|
876
|
-
this._decrementarPeriodoLibros();
|
|
877
|
-
const periodo = this._getPeriodoLibros();
|
|
1052
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
878
1053
|
|
|
879
1054
|
const libroCompras = new LibroCompras({
|
|
880
1055
|
emisor: this.config.emisor,
|
|
@@ -886,13 +1061,12 @@ class CertRunner {
|
|
|
886
1061
|
throw new Error('No hay datos del libro de compras. El SII no entregó el set LIBRO_COMPRAS al obtener las estructuras.');
|
|
887
1062
|
}
|
|
888
1063
|
|
|
889
|
-
console.log(`
|
|
1064
|
+
console.log(` Generando Libro de Compras para período ${periodo} (${libroComprasData.detalle.length} documentos del SII)...`);
|
|
890
1065
|
const { libro, xml, detalle, resumen } = libroCompras.generarDesdeEstructuras(libroComprasData, periodo);
|
|
891
1066
|
|
|
892
1067
|
// Guardar XML de debug
|
|
893
1068
|
const outPath = path.join(this.debugDir, 'libro-compras.xml');
|
|
894
1069
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
895
|
-
console.log(` XML guardado: ${outPath}`);
|
|
896
1070
|
|
|
897
1071
|
// Enviar al SII
|
|
898
1072
|
const enviador = this._createLibroEnviador();
|
|
@@ -909,9 +1083,9 @@ class CertRunner {
|
|
|
909
1083
|
this.resultados.libroCompras = result;
|
|
910
1084
|
|
|
911
1085
|
if (result.success) {
|
|
912
|
-
console.log(`
|
|
1086
|
+
console.log(` [OK] Libro de Compras enviado - TrackId: ${result.trackId}`);
|
|
913
1087
|
} else {
|
|
914
|
-
console.log(`
|
|
1088
|
+
console.log(` [ERR] Error enviando Libro de Compras: ${result.error}`);
|
|
915
1089
|
}
|
|
916
1090
|
|
|
917
1091
|
return result;
|
|
@@ -928,8 +1102,7 @@ class CertRunner {
|
|
|
928
1102
|
throw new Error('No hay datos del libro de compras para exentos. El SII no entregó el set LIBRO_COMPRAS_EXENTOS.');
|
|
929
1103
|
}
|
|
930
1104
|
|
|
931
|
-
this._decrementarPeriodoLibros();
|
|
932
|
-
const periodo = this._getPeriodoLibros();
|
|
1105
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
933
1106
|
|
|
934
1107
|
const libroCompras = new LibroCompras({
|
|
935
1108
|
emisor: this.config.emisor,
|
|
@@ -937,12 +1110,11 @@ class CertRunner {
|
|
|
937
1110
|
certificado: this.certificado,
|
|
938
1111
|
});
|
|
939
1112
|
|
|
940
|
-
console.log(`
|
|
1113
|
+
console.log(` Generando Libro de Compras para Exentos para período ${periodo} (${libroData.detalle.length} documentos del SII)...`);
|
|
941
1114
|
const { libro, xml, detalle } = libroCompras.generarDesdeEstructuras(libroData, periodo);
|
|
942
1115
|
|
|
943
1116
|
const outPath = path.join(this.debugDir, 'libro-compras-exentos.xml');
|
|
944
1117
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
945
|
-
console.log(` XML guardado: ${outPath}`);
|
|
946
1118
|
|
|
947
1119
|
const enviador = this._createLibroEnviador();
|
|
948
1120
|
const resultado = await enviador.enviarLibro(libro, 'LibroCVExentos.xml');
|
|
@@ -958,9 +1130,9 @@ class CertRunner {
|
|
|
958
1130
|
this.resultados.libroComprasExentos = result;
|
|
959
1131
|
|
|
960
1132
|
if (result.success) {
|
|
961
|
-
console.log(`
|
|
1133
|
+
console.log(` [OK] Libro de Compras para Exentos enviado - TrackId: ${result.trackId}`);
|
|
962
1134
|
} else {
|
|
963
|
-
console.log(`
|
|
1135
|
+
console.log(` [ERR] Error enviando Libro de Compras para Exentos: ${result.error}`);
|
|
964
1136
|
}
|
|
965
1137
|
|
|
966
1138
|
return result;
|
|
@@ -980,10 +1152,8 @@ class CertRunner {
|
|
|
980
1152
|
throw new Error('No hay resultado del SetGuia. Ejecutar ejecutarSetGuia() primero.');
|
|
981
1153
|
}
|
|
982
1154
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const periodo = this._getPeriodoLibros();
|
|
986
|
-
console.log(` 📚 Generando Libro de Guías para período ${periodo}...`);
|
|
1155
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
1156
|
+
console.log(` Generando Libro de Guías para período ${periodo}...`);
|
|
987
1157
|
|
|
988
1158
|
const libroGuias = new LibroGuias({
|
|
989
1159
|
emisor: this.config.emisor,
|
|
@@ -1000,7 +1170,6 @@ class CertRunner {
|
|
|
1000
1170
|
// Guardar XML de debug
|
|
1001
1171
|
const outPath = path.join(this.debugDir, 'libro-guias.xml');
|
|
1002
1172
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
1003
|
-
console.log(` XML guardado: ${outPath}`);
|
|
1004
1173
|
|
|
1005
1174
|
// Enviar al SII
|
|
1006
1175
|
const enviador = this._createLibroEnviador();
|
|
@@ -1017,9 +1186,9 @@ class CertRunner {
|
|
|
1017
1186
|
this.resultados.libroGuias = result;
|
|
1018
1187
|
|
|
1019
1188
|
if (result.success) {
|
|
1020
|
-
console.log(`
|
|
1189
|
+
console.log(` [OK] Libro de Guías enviado - TrackId: ${result.trackId}`);
|
|
1021
1190
|
} else {
|
|
1022
|
-
console.log(`
|
|
1191
|
+
console.log(` [ERR] Error enviando Libro de Guías: ${result.error}`);
|
|
1023
1192
|
}
|
|
1024
1193
|
|
|
1025
1194
|
return result;
|
|
@@ -1035,11 +1204,11 @@ class CertRunner {
|
|
|
1035
1204
|
*/
|
|
1036
1205
|
async avanzarSiguientePaso() {
|
|
1037
1206
|
console.log('\n' + '═'.repeat(60));
|
|
1038
|
-
console.log('
|
|
1207
|
+
console.log('AVANZAR SIGUIENTE PASO');
|
|
1039
1208
|
console.log('═'.repeat(60) + '\n');
|
|
1040
1209
|
|
|
1041
1210
|
try {
|
|
1042
|
-
console.log('
|
|
1211
|
+
console.log(' Enviando solicitud de avance...');
|
|
1043
1212
|
const result = await this.siiCert.avanzarSiguientePaso();
|
|
1044
1213
|
|
|
1045
1214
|
if (result.rawHtml) {
|
|
@@ -1048,20 +1217,20 @@ class CertRunner {
|
|
|
1048
1217
|
result.rawHtml,
|
|
1049
1218
|
'utf8'
|
|
1050
1219
|
);
|
|
1051
|
-
console.log(`
|
|
1220
|
+
console.log(` Respuesta guardada en: ${path.join(this.debugDir, 'avanzar-siguiente-paso-response.html')}`);
|
|
1052
1221
|
}
|
|
1053
1222
|
|
|
1054
1223
|
if (result.success) {
|
|
1055
|
-
console.log('
|
|
1224
|
+
console.log(' [OK] Avance al siguiente paso exitoso');
|
|
1056
1225
|
this.resultados.avanceSiguientePaso = { success: true };
|
|
1057
1226
|
} else {
|
|
1058
|
-
console.log(`
|
|
1227
|
+
console.log(` [ERR] Error en avance: ${result.error || 'Error desconocido'}`);
|
|
1059
1228
|
this.resultados.avanceSiguientePaso = { success: false, error: result.error };
|
|
1060
1229
|
}
|
|
1061
1230
|
|
|
1062
1231
|
return result;
|
|
1063
1232
|
} catch (error) {
|
|
1064
|
-
console.log(`
|
|
1233
|
+
console.log(` [ERR] Error: ${error.message}`);
|
|
1065
1234
|
this.resultados.avanceSiguientePaso = { success: false, error: error.message };
|
|
1066
1235
|
return { success: false, error: error.message };
|
|
1067
1236
|
}
|
|
@@ -1075,15 +1244,15 @@ class CertRunner {
|
|
|
1075
1244
|
async esperarLibrosYAvanzar(options = {}) {
|
|
1076
1245
|
const { maxIntentos = 30, intervalo = 10000 } = options;
|
|
1077
1246
|
|
|
1078
|
-
console.log('\n
|
|
1247
|
+
console.log('\n[...] Esperando aprobación de libros...');
|
|
1079
1248
|
|
|
1080
1249
|
for (let i = 1; i <= maxIntentos; i++) {
|
|
1081
|
-
console.log(`\n
|
|
1250
|
+
console.log(`\n [...] Intento ${i}/${maxIntentos}...`);
|
|
1082
1251
|
|
|
1083
1252
|
const avance = await this.siiCert.verAvanceParsed();
|
|
1084
1253
|
|
|
1085
1254
|
if (!avance.success) {
|
|
1086
|
-
console.log(`
|
|
1255
|
+
console.log(` [!] Error consultando avance: ${avance.error}`);
|
|
1087
1256
|
await sleep(intervalo);
|
|
1088
1257
|
continue;
|
|
1089
1258
|
}
|
|
@@ -1103,30 +1272,30 @@ class CertRunner {
|
|
|
1103
1272
|
estado.estado?.toUpperCase().includes('REPARO');
|
|
1104
1273
|
|
|
1105
1274
|
if (esAprobado) {
|
|
1106
|
-
console.log(`
|
|
1275
|
+
console.log(` [OK] ${libro}: REVISADO CONFORME`);
|
|
1107
1276
|
} else if (esRechazado) {
|
|
1108
|
-
console.log(`
|
|
1277
|
+
console.log(` [ERR] ${libro}: ${estado.estado}`);
|
|
1109
1278
|
hayRechazados = true;
|
|
1110
1279
|
} else {
|
|
1111
|
-
console.log(`
|
|
1280
|
+
console.log(` [...] ${libro}: ${estado.estado || 'EN REVISION'}`);
|
|
1112
1281
|
todosAprobados = false;
|
|
1113
1282
|
}
|
|
1114
1283
|
}
|
|
1115
1284
|
|
|
1116
1285
|
if (hayRechazados) {
|
|
1117
|
-
console.log('\n
|
|
1286
|
+
console.log('\n [ERR] Hay libros rechazados. No se puede avanzar.');
|
|
1118
1287
|
return { success: false, error: 'Hay libros rechazados' };
|
|
1119
1288
|
}
|
|
1120
1289
|
|
|
1121
1290
|
if (todosAprobados) {
|
|
1122
|
-
console.log('\n
|
|
1291
|
+
console.log('\n ¡Todos los libros aprobados!');
|
|
1123
1292
|
return await this.avanzarSiguientePaso();
|
|
1124
1293
|
}
|
|
1125
1294
|
|
|
1126
1295
|
await sleep(intervalo);
|
|
1127
1296
|
}
|
|
1128
1297
|
|
|
1129
|
-
console.log('\n
|
|
1298
|
+
console.log('\n [!] Timeout esperando aprobación de libros');
|
|
1130
1299
|
return { success: false, error: 'Timeout esperando aprobación' };
|
|
1131
1300
|
}
|
|
1132
1301
|
|
|
@@ -1148,12 +1317,12 @@ class CertRunner {
|
|
|
1148
1317
|
}
|
|
1149
1318
|
|
|
1150
1319
|
console.log('\n' + '═'.repeat(60));
|
|
1151
|
-
console.log('
|
|
1320
|
+
console.log('FASE 6: SIMULACIÓN');
|
|
1152
1321
|
console.log('═'.repeat(60) + '\n');
|
|
1153
1322
|
|
|
1154
1323
|
// Calcular CAFs necesarios
|
|
1155
1324
|
const cafRequired = this._calcularCafsSimulacion(estructuras);
|
|
1156
|
-
console.log('
|
|
1325
|
+
console.log(' Solicitando CAFs para simulación...');
|
|
1157
1326
|
|
|
1158
1327
|
// Solicitar CAFs frescos
|
|
1159
1328
|
const cafs = await this.solicitarCafs(cafRequired);
|
|
@@ -1178,22 +1347,21 @@ class CertRunner {
|
|
|
1178
1347
|
});
|
|
1179
1348
|
|
|
1180
1349
|
// Generar
|
|
1181
|
-
console.log('
|
|
1350
|
+
console.log(' Generando DTEs de simulación...');
|
|
1182
1351
|
const { envioDte, dtes, xml, plan, tiposUsados } = simulacion.generar(
|
|
1183
1352
|
estructuras,
|
|
1184
1353
|
cafObjects,
|
|
1185
1354
|
this.folioHelper,
|
|
1186
1355
|
);
|
|
1187
1356
|
|
|
1188
|
-
console.log(`
|
|
1189
|
-
console.log(`
|
|
1357
|
+
console.log(` Plan de simulación: ${plan.length} documentos`);
|
|
1358
|
+
console.log(` Tipos usados: ${tiposUsados.join(', ')}`);
|
|
1190
1359
|
|
|
1191
1360
|
// Guardar XML de debug
|
|
1192
1361
|
const runDir = path.join(this.debugDir, 'simulacion');
|
|
1193
1362
|
fs.mkdirSync(runDir, { recursive: true });
|
|
1194
1363
|
const outPath = path.join(runDir, 'envio-simulacion.xml');
|
|
1195
1364
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
1196
|
-
console.log(` XML guardado: ${outPath}`);
|
|
1197
1365
|
|
|
1198
1366
|
// Guardar DTEs individuales
|
|
1199
1367
|
const dtesDir = path.join(runDir, 'dtes');
|
|
@@ -1204,7 +1372,7 @@ class CertRunner {
|
|
|
1204
1372
|
});
|
|
1205
1373
|
|
|
1206
1374
|
// Enviar al SII
|
|
1207
|
-
console.log('\n
|
|
1375
|
+
console.log('\n Enviando al SII...');
|
|
1208
1376
|
const enviador = this._createEnviador();
|
|
1209
1377
|
const resultado = await enviador.enviar(envioDte);
|
|
1210
1378
|
|
|
@@ -1226,9 +1394,9 @@ class CertRunner {
|
|
|
1226
1394
|
}, null, 2), 'utf8');
|
|
1227
1395
|
|
|
1228
1396
|
if (result.success) {
|
|
1229
|
-
console.log(`\n
|
|
1397
|
+
console.log(`\n[OK] Simulación enviada - TrackId: ${result.trackId}`);
|
|
1230
1398
|
} else {
|
|
1231
|
-
console.log(`\n
|
|
1399
|
+
console.log(`\n[ERR] Error en simulación: ${result.error}`);
|
|
1232
1400
|
}
|
|
1233
1401
|
|
|
1234
1402
|
return result;
|
|
@@ -1293,10 +1461,10 @@ class CertRunner {
|
|
|
1293
1461
|
}
|
|
1294
1462
|
|
|
1295
1463
|
// Verificar si ya pasamos a INTERCAMBIO (simulación ya aprobada)
|
|
1296
|
-
console.log('
|
|
1464
|
+
console.log(' Verificando etapa actual...');
|
|
1297
1465
|
const avance = await this.siiCert.verAvanceParsed();
|
|
1298
1466
|
if (avance.rawHtml && /paso\s*<b>\s*INTERCAMBIO/i.test(avance.rawHtml)) {
|
|
1299
|
-
console.log('
|
|
1467
|
+
console.log(' [OK] Simulación ya aprobada - empresa en etapa INTERCAMBIO');
|
|
1300
1468
|
return { success: true, skipped: true, message: 'Ya en etapa INTERCAMBIO' };
|
|
1301
1469
|
}
|
|
1302
1470
|
|
|
@@ -1309,7 +1477,7 @@ class CertRunner {
|
|
|
1309
1477
|
};
|
|
1310
1478
|
|
|
1311
1479
|
const result = await this._declararConReintentos(sets, 'declaracion-simulacion-response', { maxIntentos, intervalo, label: 'simulación' });
|
|
1312
|
-
if (result?.success) console.log('
|
|
1480
|
+
if (result?.success) console.log(' [OK] Simulación declarada exitosamente');
|
|
1313
1481
|
return result;
|
|
1314
1482
|
}
|
|
1315
1483
|
|
|
@@ -1321,41 +1489,41 @@ class CertRunner {
|
|
|
1321
1489
|
async esperarSimulacionAprobada(options = {}) {
|
|
1322
1490
|
const { maxIntentos = 30, intervalo = 10000 } = options;
|
|
1323
1491
|
|
|
1324
|
-
console.log('\n
|
|
1492
|
+
console.log('\n[...] Esperando aprobación de simulación...');
|
|
1325
1493
|
|
|
1326
1494
|
for (let i = 1; i <= maxIntentos; i++) {
|
|
1327
|
-
console.log(`\n
|
|
1495
|
+
console.log(`\n [...] Intento ${i}/${maxIntentos}...`);
|
|
1328
1496
|
|
|
1329
1497
|
const avance = await this.siiCert.verAvanceParsed();
|
|
1330
1498
|
|
|
1331
1499
|
if (!avance.success) {
|
|
1332
|
-
console.log(`
|
|
1500
|
+
console.log(` [!] Error consultando avance: ${avance.error}`);
|
|
1333
1501
|
await sleep(intervalo);
|
|
1334
1502
|
continue;
|
|
1335
1503
|
}
|
|
1336
1504
|
|
|
1337
|
-
//
|
|
1505
|
+
// [OK] PRIMERO: Verificar si ya pasó a INTERCAMBIO (significa que simulación fue aprobada)
|
|
1338
1506
|
if (avance.etapaActual && avance.etapaActual.includes('INTERCAMBIO')) {
|
|
1339
|
-
console.log(`
|
|
1340
|
-
console.log('\n
|
|
1507
|
+
console.log(` [OK] Etapa actual: ${avance.etapaActual}`);
|
|
1508
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Empresa pasó a etapa INTERCAMBIO.');
|
|
1341
1509
|
return { success: true, etapa: 'INTERCAMBIO' };
|
|
1342
1510
|
}
|
|
1343
1511
|
|
|
1344
|
-
//
|
|
1512
|
+
// [OK] TAMBIÉN: Etapas que vienen DESPUÉS de INTERCAMBIO (simulación + intercambio ya completos)
|
|
1345
1513
|
const ETAPAS_POST_INTERCAMBIO = ['DOCUMENTOS IMPRESOS', 'MUESTRAS IMPRESAS', 'BOLETA', 'AUTORIZADO', 'COMPLETADO'];
|
|
1346
1514
|
if (avance.etapaActual && ETAPAS_POST_INTERCAMBIO.some(e => avance.etapaActual.toUpperCase().includes(e))) {
|
|
1347
|
-
console.log(`
|
|
1348
|
-
console.log('\n
|
|
1515
|
+
console.log(` Etapa actual: ${avance.etapaActual}`);
|
|
1516
|
+
console.log('\n ¡SIMULACIÓN + INTERCAMBIO COMPLETADOS! Empresa en etapa: ' + avance.etapaActual);
|
|
1349
1517
|
return { success: true, etapa: avance.etapaActual, postIntercambio: true };
|
|
1350
1518
|
}
|
|
1351
1519
|
|
|
1352
|
-
//
|
|
1520
|
+
// [OK] SEGUNDO: Verificar indicador de formulario de confirmación (simulación aprobada pendiente confirmar)
|
|
1353
1521
|
if (avance.simulacionAprobadaIndicador) {
|
|
1354
|
-
console.log(`
|
|
1522
|
+
console.log(` [OK] Formulario de confirmación detectado`);
|
|
1355
1523
|
|
|
1356
1524
|
// Confirmar automáticamente la simulación
|
|
1357
1525
|
if (this.resultados.simulacion?.trackId) {
|
|
1358
|
-
console.log(`\n
|
|
1526
|
+
console.log(`\n Confirmando revisión de simulación (TrackId: ${this.resultados.simulacion.trackId})...`);
|
|
1359
1527
|
|
|
1360
1528
|
const fecha = this._getFechaHoy();
|
|
1361
1529
|
const confirmResult = await this.siiCert.declararAvance({
|
|
@@ -1368,7 +1536,7 @@ class CertRunner {
|
|
|
1368
1536
|
});
|
|
1369
1537
|
|
|
1370
1538
|
if (confirmResult.success) {
|
|
1371
|
-
console.log('
|
|
1539
|
+
console.log(' [OK] Confirmación enviada exitosamente');
|
|
1372
1540
|
|
|
1373
1541
|
// Revalidar contra SII para evitar falso positivo de confirmación
|
|
1374
1542
|
const verificacion = await this.siiCert.verAvanceParsed();
|
|
@@ -1378,19 +1546,19 @@ class CertRunner {
|
|
|
1378
1546
|
const simConforme = Boolean(estadoSim?.esConforme || estadoSim?.estado?.toUpperCase()?.includes('REVISADO CONFORME'));
|
|
1379
1547
|
|
|
1380
1548
|
if (yaIntercambio || simConforme || !sigueFormulario) {
|
|
1381
|
-
console.log('\n
|
|
1549
|
+
console.log('\n ¡SIMULACIÓN CONFIRMADA! Certificación completa.');
|
|
1382
1550
|
return { success: true, confirmada: true };
|
|
1383
1551
|
}
|
|
1384
1552
|
|
|
1385
|
-
console.log('
|
|
1553
|
+
console.log(' [!] SII aún mantiene formulario de simulación pendiente; se reintentará...');
|
|
1386
1554
|
await sleep(intervalo);
|
|
1387
1555
|
continue;
|
|
1388
1556
|
} else {
|
|
1389
|
-
console.log(`
|
|
1557
|
+
console.log(` [!] Error en confirmación: ${confirmResult.error}`);
|
|
1390
1558
|
// Continuar el loop para reintentar
|
|
1391
1559
|
}
|
|
1392
1560
|
} else {
|
|
1393
|
-
console.log('\n
|
|
1561
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Lista para confirmar revisión.');
|
|
1394
1562
|
return { success: true, pendienteConfirmar: true };
|
|
1395
1563
|
}
|
|
1396
1564
|
}
|
|
@@ -1411,28 +1579,28 @@ class CertRunner {
|
|
|
1411
1579
|
simEstado.estado?.toUpperCase().includes('REPARO');
|
|
1412
1580
|
|
|
1413
1581
|
if (esAprobado) {
|
|
1414
|
-
console.log(`
|
|
1415
|
-
console.log('\n
|
|
1582
|
+
console.log(` [OK] SIMULACIÓN: REVISADO CONFORME`);
|
|
1583
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Certificación completa.');
|
|
1416
1584
|
return { success: true };
|
|
1417
1585
|
} else if (esRechazado) {
|
|
1418
|
-
console.log(`
|
|
1586
|
+
console.log(` [ERR] SIMULACIÓN: ${simEstado.estado}`);
|
|
1419
1587
|
return { success: false, error: 'Simulación rechazada' };
|
|
1420
1588
|
} else {
|
|
1421
|
-
console.log(`
|
|
1589
|
+
console.log(` [...] SIMULACIÓN: ${simEstado.estado || 'EN REVISION'}`);
|
|
1422
1590
|
}
|
|
1423
1591
|
} else {
|
|
1424
1592
|
// No hay estado de simulación, pero verificar etapa actual
|
|
1425
1593
|
if (avance.etapaActual) {
|
|
1426
|
-
console.log(`
|
|
1594
|
+
console.log(` Etapa actual: ${avance.etapaActual}`);
|
|
1427
1595
|
} else {
|
|
1428
|
-
console.log('
|
|
1596
|
+
console.log(' [...] Simulación aún no registrada...');
|
|
1429
1597
|
}
|
|
1430
1598
|
}
|
|
1431
1599
|
|
|
1432
1600
|
await sleep(intervalo);
|
|
1433
1601
|
}
|
|
1434
1602
|
|
|
1435
|
-
console.log('\n
|
|
1603
|
+
console.log('\n [!] Timeout esperando aprobación de simulación');
|
|
1436
1604
|
return { success: false, error: 'Timeout esperando aprobación' };
|
|
1437
1605
|
}
|
|
1438
1606
|
|
|
@@ -1463,7 +1631,7 @@ class CertRunner {
|
|
|
1463
1631
|
fs.mkdirSync(intercambioDir, { recursive: true });
|
|
1464
1632
|
|
|
1465
1633
|
console.log('\n' + '═'.repeat(60));
|
|
1466
|
-
console.log('
|
|
1634
|
+
console.log('FASE 7: INTERCAMBIO DE INFORMACIÓN');
|
|
1467
1635
|
console.log('═'.repeat(60));
|
|
1468
1636
|
|
|
1469
1637
|
// ── PASO 1: Obtener el SET XML ─────────────────────────────
|
|
@@ -1476,35 +1644,35 @@ class CertRunner {
|
|
|
1476
1644
|
const setDownloadPath = path.join(intercambioDir, 'set-intercambio.xml');
|
|
1477
1645
|
|
|
1478
1646
|
if (setInputPath && fs.existsSync(setInputPath)) {
|
|
1479
|
-
console.log(`\
|
|
1647
|
+
console.log(`\nLeyendo SET desde: ${setInputPath}`);
|
|
1480
1648
|
setXml = fs.readFileSync(setInputPath, 'utf8');
|
|
1481
|
-
console.log(`
|
|
1649
|
+
console.log(` ✓ ${setXml.length} bytes`);
|
|
1482
1650
|
} else if (fs.existsSync(setDownloadPath)) {
|
|
1483
|
-
console.log(`\
|
|
1651
|
+
console.log(`\nLeyendo SET guardado: ${setDownloadPath}`);
|
|
1484
1652
|
setXml = fs.readFileSync(setDownloadPath, 'utf8');
|
|
1485
|
-
console.log(`
|
|
1653
|
+
console.log(` ✓ ${setXml.length} bytes`);
|
|
1486
1654
|
} else {
|
|
1487
|
-
console.log('\
|
|
1655
|
+
console.log('\nDescargando SET desde www4.sii.cl/pfeInternet...');
|
|
1488
1656
|
const dl = await this._descargarSetPfeInternet(intercambioDir);
|
|
1489
1657
|
if (dl.success) {
|
|
1490
1658
|
setXml = dl.xml;
|
|
1491
1659
|
fs.writeFileSync(setDownloadPath, setXml, 'utf8');
|
|
1492
|
-
console.log(`
|
|
1660
|
+
console.log(` [OK] SET descargado (${setXml.length} bytes) → ${setDownloadPath}`);
|
|
1493
1661
|
} else {
|
|
1494
|
-
console.log(`
|
|
1662
|
+
console.log(` [!] No se pudo descargar: ${dl.error}`);
|
|
1495
1663
|
console.log('\n' + '─'.repeat(60));
|
|
1496
|
-
console.log('
|
|
1497
|
-
console.log('
|
|
1498
|
-
console.log('
|
|
1499
|
-
console.log(`
|
|
1500
|
-
console.log('
|
|
1664
|
+
console.log('DESCARGA MANUAL REQUERIDA:');
|
|
1665
|
+
console.log(' 1. Si aparece error de sesiones: ingresa a https://www4.sii.cl/ → Cerrar Sesión');
|
|
1666
|
+
console.log(' 2. Ir a: https://www4.sii.cl/pfeInternet/ y descargar el SET XML');
|
|
1667
|
+
console.log(` 3. Guardarlo en: ${setDownloadPath}`);
|
|
1668
|
+
console.log(' 4. Volver a ejecutar el runner');
|
|
1501
1669
|
console.log('─'.repeat(60));
|
|
1502
1670
|
return { success: false, error: 'SET no disponible - descarga manual requerida', requiresManual: true, manualPath: setInputPath };
|
|
1503
1671
|
}
|
|
1504
1672
|
}
|
|
1505
1673
|
|
|
1506
1674
|
// ── PASO 2: Generar XMLs de respuesta ─────────────────────
|
|
1507
|
-
console.log('\
|
|
1675
|
+
console.log('\nGenerando respuestas firmadas...');
|
|
1508
1676
|
const intercambioCert = new IntercambioCert({
|
|
1509
1677
|
certificado: this.certificado,
|
|
1510
1678
|
emisor: {
|
|
@@ -1521,7 +1689,7 @@ class CertRunner {
|
|
|
1521
1689
|
}
|
|
1522
1690
|
|
|
1523
1691
|
// ── PASO 3: Subir respuestas ───────────────────────────────
|
|
1524
|
-
console.log('\
|
|
1692
|
+
console.log('\nSubiendo respuestas a www4.sii.cl/pfeInternet...');
|
|
1525
1693
|
const uploadResult = await this._subirRespuestasPfeInternet({
|
|
1526
1694
|
recepcionXml: fs.readFileSync(genResult.files.recepcion, 'utf8'),
|
|
1527
1695
|
aprobacionXml: fs.readFileSync(genResult.files.aprobacion, 'utf8'),
|
|
@@ -1531,17 +1699,17 @@ class CertRunner {
|
|
|
1531
1699
|
|
|
1532
1700
|
if (uploadResult.success) {
|
|
1533
1701
|
console.log('\n' + '═'.repeat(60));
|
|
1534
|
-
console.log('
|
|
1702
|
+
console.log('[OK] INTERCAMBIO COMPLETADO');
|
|
1535
1703
|
console.log('═'.repeat(60));
|
|
1536
|
-
if (uploadResult.resultado) console.log(`
|
|
1704
|
+
if (uploadResult.resultado) console.log(` Resultado SII: ${uploadResult.resultado}`);
|
|
1537
1705
|
} else {
|
|
1538
|
-
console.log(`
|
|
1706
|
+
console.log(` [!] No se pudo subir automáticamente: ${uploadResult.error}`);
|
|
1539
1707
|
console.log('\n' + '─'.repeat(60));
|
|
1540
|
-
console.log('
|
|
1541
|
-
console.log('
|
|
1542
|
-
console.log(`
|
|
1543
|
-
console.log(`
|
|
1544
|
-
console.log(`
|
|
1708
|
+
console.log('SUBIDA MANUAL REQUERIDA:');
|
|
1709
|
+
console.log(' 1. Ir a: https://www4.sii.cl/pfeInternet/ → "Subir archivos"');
|
|
1710
|
+
console.log(` 2. Subir: ${genResult.files.recepcion}`);
|
|
1711
|
+
console.log(` 3. Subir: ${genResult.files.aprobacion}`);
|
|
1712
|
+
console.log(` 4. Subir: ${genResult.files.recibos}`);
|
|
1545
1713
|
console.log('─'.repeat(60));
|
|
1546
1714
|
}
|
|
1547
1715
|
|
|
@@ -1563,7 +1731,7 @@ class CertRunner {
|
|
|
1563
1731
|
*/
|
|
1564
1732
|
async _obtenerCookiesSII() {
|
|
1565
1733
|
if (this._siiCookieJar) {
|
|
1566
|
-
console.log('[SII Auth]
|
|
1734
|
+
console.log('[SII Auth] Reutilizando sesión SII en memoria');
|
|
1567
1735
|
return this._siiCookieJar;
|
|
1568
1736
|
}
|
|
1569
1737
|
const SiiPortalAuth = require('../SiiPortalAuth');
|
|
@@ -1572,7 +1740,7 @@ class CertRunner {
|
|
|
1572
1740
|
const siiAuth = new SiiPortalAuth({ pfxBuffer, pfxPassword: password });
|
|
1573
1741
|
this._siiCookieJar = await siiAuth.autenticar();
|
|
1574
1742
|
const nSession = Object.keys(this._siiCookieJar).filter(k => k.startsWith('NETSCAPE')).length;
|
|
1575
|
-
console.log(`[SII Auth]
|
|
1743
|
+
console.log(`[SII Auth] [OK] Sesión SII activa (cookies NETSCAPE: ${nSession})`);
|
|
1576
1744
|
return this._siiCookieJar;
|
|
1577
1745
|
}
|
|
1578
1746
|
|
|
@@ -1671,7 +1839,7 @@ class CertRunner {
|
|
|
1671
1839
|
const boundary = `----WebKitFormBoundary${Date.now()}`;
|
|
1672
1840
|
const emptyMultipartBody = `--${boundary}--\r\n`;
|
|
1673
1841
|
|
|
1674
|
-
console.log(`
|
|
1842
|
+
console.log(` → Descargando SET desde pfeInternet/downloadFile (RUT ${rutNum}-${dv})...`);
|
|
1675
1843
|
|
|
1676
1844
|
const r = await makeReq(
|
|
1677
1845
|
`https://www4.sii.cl/pfeInternet/downloadFile?re=${rutNum}&dve=${dv}`,
|
|
@@ -1695,12 +1863,12 @@ class CertRunner {
|
|
|
1695
1863
|
r.body.includes('<SetDTE') ||
|
|
1696
1864
|
r.body.includes('<?xml')
|
|
1697
1865
|
)) {
|
|
1698
|
-
console.log(`
|
|
1866
|
+
console.log(` ✓ SET descargado correctamente (${r.body.length} bytes)`);
|
|
1699
1867
|
return { success: true, xml: r.body };
|
|
1700
1868
|
}
|
|
1701
1869
|
|
|
1702
1870
|
const errMsg = `pfeInternet/downloadFile respondió HTTP ${r.status} sin XML válido`;
|
|
1703
|
-
console.log(`
|
|
1871
|
+
console.log(` [ERR] ${errMsg}`);
|
|
1704
1872
|
fs.writeFileSync(path.join(debugDir, `pfe-download-error-${Date.now()}.html`), r.body, 'utf8');
|
|
1705
1873
|
return { success: false, error: errMsg };
|
|
1706
1874
|
} catch (err) {
|
|
@@ -1723,8 +1891,8 @@ class CertRunner {
|
|
|
1723
1891
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
1724
1892
|
// Los labels deben coincidir con el texto del portal GWT (Archivo N: ...)
|
|
1725
1893
|
const archivos = [
|
|
1726
|
-
{ label: 'Respuesta de Intercambio',
|
|
1727
|
-
{ label: 'Recibo de Mercaderias',
|
|
1894
|
+
{ label: 'Respuesta de Intercambio', filename: 'respuesta-recepcion-envio.xml', content: recepcionXml, uploadN: 1 },
|
|
1895
|
+
{ label: 'Recibo de Mercaderias', filename: 'envio-recibos.xml', content: recibosXml, uploadN: 2 },
|
|
1728
1896
|
{ label: 'Resultado Aprobaci\u00f3n Comercial de Documento', filename: 'respuesta-aprobacion-comercial.xml', content: aprobacionXml, uploadN: 3 },
|
|
1729
1897
|
];
|
|
1730
1898
|
for (const a of archivos) {
|
|
@@ -1751,7 +1919,7 @@ class CertRunner {
|
|
|
1751
1919
|
await page.setCookie(...puppeteerCookies);
|
|
1752
1920
|
|
|
1753
1921
|
// Navegar al portal pfeInternet
|
|
1754
|
-
console.log('
|
|
1922
|
+
console.log(' → Cargando portal pfeInternet...');
|
|
1755
1923
|
await page.goto('https://www4.sii.cl/pfeInternet/', {
|
|
1756
1924
|
waitUntil: 'networkidle2',
|
|
1757
1925
|
timeout: 60000,
|
|
@@ -1762,7 +1930,7 @@ class CertRunner {
|
|
|
1762
1930
|
|
|
1763
1931
|
// Hacer click en el enlace "Subir archivos XML de respuesta de Intercambio"
|
|
1764
1932
|
// El href es javascript:openForm('opt-ingresoEmpresaUp') — necesita click real para GWT
|
|
1765
|
-
console.log('
|
|
1933
|
+
console.log(' → Clickeando "Subir archivos XML de respuesta de Intercambio"...');
|
|
1766
1934
|
const linkClicked = await page.click('a[href*="ingresoEmpresaUp"]').then(() => true).catch(() => false);
|
|
1767
1935
|
if (!linkClicked) {
|
|
1768
1936
|
// Fallback: evaluar click con dispatchEvent
|
|
@@ -1781,7 +1949,7 @@ class CertRunner {
|
|
|
1781
1949
|
if (rutInput) {
|
|
1782
1950
|
const [rutNum, dv] = this.config.emisor.rut.split('-');
|
|
1783
1951
|
const rutConDv = `${rutNum}-${dv}`;
|
|
1784
|
-
console.log(`
|
|
1952
|
+
console.log(` → Ingresando RUT empresa: ${rutConDv}`);
|
|
1785
1953
|
await rutInput.click({ clickCount: 3 }); // seleccionar todo
|
|
1786
1954
|
await rutInput.type(rutConDv);
|
|
1787
1955
|
|
|
@@ -1792,7 +1960,7 @@ class CertRunner {
|
|
|
1792
1960
|
});
|
|
1793
1961
|
if (confirmBtn) {
|
|
1794
1962
|
await confirmBtn.asElement().click();
|
|
1795
|
-
console.log('
|
|
1963
|
+
console.log(' → Click "Confirmar Empresa", esperando formulario de upload...');
|
|
1796
1964
|
await page.waitForNetworkIdle({ timeout: 15000, idleTime: 1000 }).catch(() => {});
|
|
1797
1965
|
}
|
|
1798
1966
|
}
|
|
@@ -1810,7 +1978,7 @@ class CertRunner {
|
|
|
1810
1978
|
}
|
|
1811
1979
|
throw new Error('pfeInternet no mostró formulario de upload tras openForm — ver pfeInternet-error.png/.html');
|
|
1812
1980
|
}
|
|
1813
|
-
console.log('
|
|
1981
|
+
console.log(' → Formulario de upload listo');
|
|
1814
1982
|
|
|
1815
1983
|
// ── DEBUG: screenshot del formulario con los inputs listos ──
|
|
1816
1984
|
if (debugDir) {
|
|
@@ -1846,11 +2014,11 @@ class CertRunner {
|
|
|
1846
2014
|
}, archivo.label);
|
|
1847
2015
|
|
|
1848
2016
|
if (yaProcessado) {
|
|
1849
|
-
console.log(`
|
|
2017
|
+
console.log(` → ${archivo.filename}: ya procesado anteriormente, saltando...`);
|
|
1850
2018
|
continue;
|
|
1851
2019
|
}
|
|
1852
2020
|
|
|
1853
|
-
console.log(`
|
|
2021
|
+
console.log(` → Subiendo ${archivo.filename}...`);
|
|
1854
2022
|
|
|
1855
2023
|
// Cada archivo tiene su propio form con action uploadFile1/2/3
|
|
1856
2024
|
// Usamos el selector específico para no confundir entre los 3 inputs que pueden
|
|
@@ -1869,7 +2037,7 @@ class CertRunner {
|
|
|
1869
2037
|
// Esperar el diálogo GWT de confirmación
|
|
1870
2038
|
await page.waitForSelector('.gwt-DialogBox .msgeDialogBox', { timeout: 30000 });
|
|
1871
2039
|
const msgText = await page.$eval('.gwt-DialogBox .msgeDialogBox', el => el.textContent.trim());
|
|
1872
|
-
console.log(`
|
|
2040
|
+
console.log(` ✓ ${msgText}`);
|
|
1873
2041
|
|
|
1874
2042
|
if (debugDir) {
|
|
1875
2043
|
fs.writeFileSync(
|
|
@@ -1965,16 +2133,16 @@ class CertRunner {
|
|
|
1965
2133
|
const t = (document.body.textContent || '').toUpperCase();
|
|
1966
2134
|
return t.includes('ESTADO DE LA REVISI') ||
|
|
1967
2135
|
t.includes('POR REVISAR') || t.includes('APROBADO') ||
|
|
1968
|
-
t.includes('EN REVISI')
|
|
2136
|
+
t.includes('EN REVISI') || t.includes('RECHAZADO');
|
|
1969
2137
|
}, { timeout: 8000, polling: 500 }).catch(() => {});
|
|
1970
2138
|
|
|
1971
2139
|
const estado = await page.evaluate(() => {
|
|
1972
2140
|
const t = (document.body.textContent || '').toUpperCase();
|
|
1973
|
-
if (t.includes('APROBADO'))
|
|
1974
|
-
if (t.includes('POR REVISAR'))
|
|
1975
|
-
if (t.includes('EN REVISI'))
|
|
1976
|
-
if (t.includes('RECHAZADO'))
|
|
1977
|
-
if (t.includes('ENVIADO AL SII'))
|
|
2141
|
+
if (t.includes('APROBADO')) return 'APROBADO';
|
|
2142
|
+
if (t.includes('POR REVISAR')) return 'POR REVISAR';
|
|
2143
|
+
if (t.includes('EN REVISI')) return 'EN REVISIÓN';
|
|
2144
|
+
if (t.includes('RECHAZADO')) return 'RECHAZADO';
|
|
2145
|
+
if (t.includes('ENVIADO AL SII')) return 'ENVIADO AL SII';
|
|
1978
2146
|
return null;
|
|
1979
2147
|
}).catch(() => null);
|
|
1980
2148
|
|
|
@@ -2005,7 +2173,7 @@ class CertRunner {
|
|
|
2005
2173
|
if (!pdfPaths.length) throw new Error(`No se encontraron PDFs en: ${pdfDir}`);
|
|
2006
2174
|
|
|
2007
2175
|
console.log('\n' + '═'.repeat(60));
|
|
2008
|
-
console.log(
|
|
2176
|
+
console.log(`FASE 8: MUESTRAS IMPRESAS (${pdfPaths.length} PDFs)`);
|
|
2009
2177
|
console.log('═'.repeat(60));
|
|
2010
2178
|
|
|
2011
2179
|
return this._subirMuestrasImpresasPortal({ pdfPaths, debugDir: pdfDir });
|
|
@@ -2046,7 +2214,7 @@ class CertRunner {
|
|
|
2046
2214
|
await page.setCookie(...puppeteerCookies);
|
|
2047
2215
|
|
|
2048
2216
|
// Navegar directamente a www4.sii.cl/pdfdteInternet/ con las cookies de sesión SII
|
|
2049
|
-
console.log('
|
|
2217
|
+
console.log(' → Cargando portal pdfdteInternet...');
|
|
2050
2218
|
await page.goto('https://www4.sii.cl/pdfdteInternet/', {
|
|
2051
2219
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2052
2220
|
});
|
|
@@ -2063,7 +2231,7 @@ class CertRunner {
|
|
|
2063
2231
|
}
|
|
2064
2232
|
throw new Error('pdfdteInternet: no se encontraron campos de RUT (¿sesión expirada?)');
|
|
2065
2233
|
}
|
|
2066
|
-
console.log(`
|
|
2234
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
2067
2235
|
await rutInputs[0].click({ clickCount: 3 }); await rutInputs[0].type(rutNum);
|
|
2068
2236
|
await dvInputs[0].click({ clickCount: 3 }); await dvInputs[0].type(dvChar);
|
|
2069
2237
|
await clickBoton(page, 'Rut');
|
|
@@ -2076,7 +2244,7 @@ class CertRunner {
|
|
|
2076
2244
|
return !!(dlg && dlg.offsetParent !== null);
|
|
2077
2245
|
});
|
|
2078
2246
|
if (hayDialog) {
|
|
2079
|
-
console.log('
|
|
2247
|
+
console.log(' → Diálogo de revisión existente → haciendo click en "Sí"');
|
|
2080
2248
|
const clicked = await page.evaluate(() => {
|
|
2081
2249
|
const si = Array.from(document.querySelectorAll('button.x-btn-text'))
|
|
2082
2250
|
.find(b => /^s[ií]$/i.test(b.textContent.trim()));
|
|
@@ -2097,7 +2265,7 @@ class CertRunner {
|
|
|
2097
2265
|
const dvNow = await page.$$('input[name="dv"]');
|
|
2098
2266
|
const pRut = rutNow.length >= 2 ? rutNow[1] : rutNow[0];
|
|
2099
2267
|
const pDv = dvNow.length >= 2 ? dvNow[1] : dvNow[0];
|
|
2100
|
-
console.log(`
|
|
2268
|
+
console.log(` → Ingresando RUT proveedor: ${rutNum}-${dvChar}`);
|
|
2101
2269
|
await pRut.click({ clickCount: 3 }); await pRut.type(rutNum);
|
|
2102
2270
|
await pDv.click({ clickCount: 3 }); await pDv.type(dvChar);
|
|
2103
2271
|
await clickBoton(page, 'Consultar');
|
|
@@ -2107,20 +2275,20 @@ class CertRunner {
|
|
|
2107
2275
|
// ── Re-ejecución: detectar estado terminal antes de proceder ──
|
|
2108
2276
|
const _estadoYaSubido = await page.evaluate(() => {
|
|
2109
2277
|
const t = (document.body.textContent || '').toUpperCase();
|
|
2110
|
-
if (t.includes('APROBADO'))
|
|
2111
|
-
if (t.includes('POR REVISAR'))
|
|
2112
|
-
if (t.includes('EN REVISI'))
|
|
2113
|
-
if (t.includes('RECHAZADO'))
|
|
2278
|
+
if (t.includes('APROBADO')) return 'APROBADO';
|
|
2279
|
+
if (t.includes('POR REVISAR')) return 'POR REVISAR';
|
|
2280
|
+
if (t.includes('EN REVISI')) return 'EN REVISIÓN';
|
|
2281
|
+
if (t.includes('RECHAZADO')) return 'RECHAZADO';
|
|
2114
2282
|
if (t.includes('ENVIADO AL SII')) return 'ENVIADO AL SII';
|
|
2115
2283
|
return null;
|
|
2116
2284
|
}).catch(() => null);
|
|
2117
2285
|
if (_estadoYaSubido) {
|
|
2118
|
-
console.log(`
|
|
2286
|
+
console.log(` [OK] Portal ya muestra estado "${_estadoYaSubido}" — muestras subidas previamente. Proceso completado.`);
|
|
2119
2287
|
return { success: true, alreadyCompleted: true, estado: _estadoYaSubido };
|
|
2120
2288
|
}
|
|
2121
2289
|
|
|
2122
2290
|
// Paso 4: "Crear" → habilita el input de archivo
|
|
2123
|
-
console.log('
|
|
2291
|
+
console.log(' → Click "Crear"...');
|
|
2124
2292
|
await clickBoton(page, 'Crear');
|
|
2125
2293
|
await new Promise(r => setTimeout(r, 2500));
|
|
2126
2294
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-04-after-crear.png'), fullPage: true }).catch(() => {});
|
|
@@ -2180,7 +2348,7 @@ class CertRunner {
|
|
|
2180
2348
|
// drop → por cada file: submit form al iframe → respuesta → leeImpresoById → tick verde
|
|
2181
2349
|
// Esto evita la re-navegación entre archivos y garantiza que la validación
|
|
2182
2350
|
// (Timbre/CAF/TED) ocurra antes de salir de la página.
|
|
2183
|
-
console.log(`
|
|
2351
|
+
console.log(` → Cargando ${pdfPaths.length} PDFs para drop en el portal...`);
|
|
2184
2352
|
const _fileDataList = pdfPaths.map(p => ({
|
|
2185
2353
|
name: path.basename(p),
|
|
2186
2354
|
b64: fs.readFileSync(p).toString('base64'),
|
|
@@ -2188,7 +2356,7 @@ class CertRunner {
|
|
|
2188
2356
|
|
|
2189
2357
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-04b-antes-drop.png'), fullPage: true }).catch(() => {});
|
|
2190
2358
|
|
|
2191
|
-
console.log(`
|
|
2359
|
+
console.log(` → Ejecutando drop de ${pdfPaths.length} PDFs sobre el portal...`);
|
|
2192
2360
|
const _dropped = await page.evaluate((files) => {
|
|
2193
2361
|
const dt = new DataTransfer();
|
|
2194
2362
|
for (const f of files) {
|
|
@@ -2206,7 +2374,7 @@ class CertRunner {
|
|
|
2206
2374
|
}, _fileDataList);
|
|
2207
2375
|
|
|
2208
2376
|
if (_dropped === 0) throw new Error('pdfdteInternet: drop zone no encontrado (.dropFilesLabel)');
|
|
2209
|
-
console.log(`
|
|
2377
|
+
console.log(` → Drop ejecutado (${_dropped} archivos). Esperando procesamiento...`);
|
|
2210
2378
|
|
|
2211
2379
|
// ── Fase 1: esperar hasta 45s por primera señal de progreso o estado terminal ──
|
|
2212
2380
|
// Si el portal ya está en "POR REVISAR" (re-ejecución), lo detectamos aquí inmediatamente.
|
|
@@ -2230,21 +2398,21 @@ class CertRunner {
|
|
|
2230
2398
|
}
|
|
2231
2399
|
const t = (document.body.textContent || '').toUpperCase();
|
|
2232
2400
|
let estado = null;
|
|
2233
|
-
if (t.includes('APROBADO'))
|
|
2401
|
+
if (t.includes('APROBADO')) estado = 'APROBADO';
|
|
2234
2402
|
else if (t.includes('POR REVISAR')) estado = 'POR REVISAR';
|
|
2235
|
-
else if (t.includes('EN REVISI'))
|
|
2236
|
-
else if (t.includes('RECHAZADO'))
|
|
2403
|
+
else if (t.includes('EN REVISI')) estado = 'EN REVISIÓN';
|
|
2404
|
+
else if (t.includes('RECHAZADO')) estado = 'RECHAZADO';
|
|
2237
2405
|
return { procesados, estado };
|
|
2238
2406
|
}).catch(() => ({ procesados: 0, estado: null }));
|
|
2239
2407
|
|
|
2240
2408
|
if (_fase1.estado) {
|
|
2241
|
-
console.log(`
|
|
2409
|
+
console.log(` [OK] Portal en estado "${_fase1.estado}" — muestras ya procesadas previamente.`);
|
|
2242
2410
|
return { success: true, alreadyCompleted: true, estado: _fase1.estado };
|
|
2243
2411
|
}
|
|
2244
2412
|
|
|
2245
2413
|
if (_fase1.procesados === 0) {
|
|
2246
2414
|
// Sin progreso y sin estado terminal: el portal puede no estar procesando
|
|
2247
|
-
console.warn('
|
|
2415
|
+
console.warn(' [!] Sin progreso en 45s y sin estado terminal. Continuando al paso siguiente...');
|
|
2248
2416
|
} else {
|
|
2249
2417
|
// ── Fase 2: progreso iniciado — esperar al total ──
|
|
2250
2418
|
await page.waitForFunction((total) => {
|
|
@@ -2261,7 +2429,7 @@ class CertRunner {
|
|
|
2261
2429
|
}
|
|
2262
2430
|
return 0;
|
|
2263
2431
|
}).catch(() => 0);
|
|
2264
|
-
console.warn(`
|
|
2432
|
+
console.warn(` [!] Timeout: solo se procesaron ${procesados}/${pdfPaths.length} antes del timeout`);
|
|
2265
2433
|
});
|
|
2266
2434
|
}
|
|
2267
2435
|
|
|
@@ -2272,7 +2440,7 @@ class CertRunner {
|
|
|
2272
2440
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-05-archivos-listos.png'), fullPage: true }).catch(() => {});
|
|
2273
2441
|
|
|
2274
2442
|
// Paso 6: re-navegar al estado limpio y Enviar al SII
|
|
2275
|
-
console.log('
|
|
2443
|
+
console.log(' → Re-navegando para "Enviar al SII"...');
|
|
2276
2444
|
await navegarAlFormulario();
|
|
2277
2445
|
|
|
2278
2446
|
// Esperar a que el botón esté habilitado (aria-disabled="false")
|
|
@@ -2284,7 +2452,7 @@ class CertRunner {
|
|
|
2284
2452
|
|
|
2285
2453
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-05b-antes-enviar.png'), fullPage: true }).catch(() => {});
|
|
2286
2454
|
|
|
2287
|
-
console.log('
|
|
2455
|
+
console.log(' → Click "Enviar al SII"...');
|
|
2288
2456
|
const enviado = await clickBoton(page, 'Enviar al SII');
|
|
2289
2457
|
if (!enviado) throw new Error('pdfdteInternet: botón "Enviar al SII" no disponible o deshabilitado');
|
|
2290
2458
|
|
|
@@ -2297,7 +2465,7 @@ class CertRunner {
|
|
|
2297
2465
|
|
|
2298
2466
|
const pageText = await page.$eval('body', el => el.textContent).catch(() => '');
|
|
2299
2467
|
const exitoso = /revision.*creada|solicitud.*enviada|documentos.*enviados|fue.*enviado|[eé]xito/i.test(pageText);
|
|
2300
|
-
console.log(`
|
|
2468
|
+
console.log(` → Resultado: ${exitoso ? '[OK] enviado correctamente' : '[!] sin confirmación explícita'}`);
|
|
2301
2469
|
return { success: exitoso, pageText: pageText.substring(0, 800) };
|
|
2302
2470
|
|
|
2303
2471
|
} catch (err) {
|
|
@@ -2340,9 +2508,9 @@ class CertRunner {
|
|
|
2340
2508
|
});
|
|
2341
2509
|
const page = await browser.newPage();
|
|
2342
2510
|
await page.setCookie(...puppeteerCookies);
|
|
2343
|
-
page.on('dialog', async dlg => { console.log(`
|
|
2511
|
+
page.on('dialog', async dlg => { console.log(` → [dialog SET=1] ${dlg.message()}`); await dlg.accept(); });
|
|
2344
2512
|
|
|
2345
|
-
console.log('
|
|
2513
|
+
console.log(' → Cargando portal certBolElectDteInternet (SET=1)...');
|
|
2346
2514
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/?SET=1', {
|
|
2347
2515
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2348
2516
|
});
|
|
@@ -2352,7 +2520,7 @@ class CertRunner {
|
|
|
2352
2520
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2353
2521
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2354
2522
|
if (!rutInput) throw new Error('certBolElectDteInternet/?SET=1: no se encontró campo RUT');
|
|
2355
|
-
console.log(`
|
|
2523
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
2356
2524
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutNum);
|
|
2357
2525
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(dvChar);
|
|
2358
2526
|
await page.evaluate(() => {
|
|
@@ -2360,7 +2528,7 @@ class CertRunner {
|
|
|
2360
2528
|
.find(b => /confirmar/i.test(b.textContent));
|
|
2361
2529
|
if (btn) btn.click();
|
|
2362
2530
|
});
|
|
2363
|
-
console.log('
|
|
2531
|
+
console.log(' → Click "Confirmar Empresa"');
|
|
2364
2532
|
|
|
2365
2533
|
// Esperar checkboxes — GWT dispara ~8-10 POST /facade en paralelo
|
|
2366
2534
|
await page.waitForNetworkIdle({ idleTime: 800, timeout: 40000 }).catch(() => {});
|
|
@@ -2375,7 +2543,7 @@ class CertRunner {
|
|
|
2375
2543
|
cbs.forEach(cb => { if (!cb.checked) cb.click(); });
|
|
2376
2544
|
return cbs.length;
|
|
2377
2545
|
});
|
|
2378
|
-
console.log(`
|
|
2546
|
+
console.log(` → ${nCbs} checkbox(es) marcados`);
|
|
2379
2547
|
await new Promise(r => setTimeout(r, 300));
|
|
2380
2548
|
|
|
2381
2549
|
// Paso 3: Rellenar correo proveedor
|
|
@@ -2390,9 +2558,9 @@ class CertRunner {
|
|
|
2390
2558
|
if (emailInput) {
|
|
2391
2559
|
await emailInput.click({ clickCount: 3 });
|
|
2392
2560
|
await emailInput.type(correoSet);
|
|
2393
|
-
console.log(`
|
|
2561
|
+
console.log(` → Correo proveedor: ${correoSet}`);
|
|
2394
2562
|
} else {
|
|
2395
|
-
console.log('
|
|
2563
|
+
console.log(' [!] No se encontró campo de correo — continuando sin él');
|
|
2396
2564
|
}
|
|
2397
2565
|
|
|
2398
2566
|
// Paso 4: Click "Bajar Nuevo Set" — esperar POST /facade (GWT RPC) y luego
|
|
@@ -2402,7 +2570,7 @@ class CertRunner {
|
|
|
2402
2570
|
// donde rutRepre/dvRepre vienen de las cookies NETSCAPE_LIVEWIRE.rut / .dv
|
|
2403
2571
|
|
|
2404
2572
|
const rutRepreNum = cookieJar['NETSCAPE_LIVEWIRE.rut'] || cookieJar['RUT_NS'] || '';
|
|
2405
|
-
const dvRepreChar = cookieJar['NETSCAPE_LIVEWIRE.dv']
|
|
2573
|
+
const dvRepreChar = cookieJar['NETSCAPE_LIVEWIRE.dv'] || cookieJar['DV_NS'] || '';
|
|
2406
2574
|
if (!rutRepreNum) throw new Error('No se pudo obtener rutRepre de las cookies SII (NETSCAPE_LIVEWIRE.rut)');
|
|
2407
2575
|
|
|
2408
2576
|
// Registrar listener de facade ANTES de hacer click
|
|
@@ -2411,7 +2579,7 @@ class CertRunner {
|
|
|
2411
2579
|
{ timeout: 20000 }
|
|
2412
2580
|
).catch(() => null);
|
|
2413
2581
|
|
|
2414
|
-
console.log('
|
|
2582
|
+
console.log(' → Click "Bajar Nuevo Set" — esperando GWT facade...');
|
|
2415
2583
|
await page.evaluate(() => {
|
|
2416
2584
|
const btn = Array.from(document.querySelectorAll('button'))
|
|
2417
2585
|
.find(b => /bajar/i.test(b.textContent));
|
|
@@ -2430,7 +2598,7 @@ class CertRunner {
|
|
|
2430
2598
|
`&rutRepre=${rutRepreNum}&dvRepre=${dvRepreChar}` +
|
|
2431
2599
|
`&mailProvSw=${encodeURIComponent(correoSet)}`;
|
|
2432
2600
|
|
|
2433
|
-
console.log(`
|
|
2601
|
+
console.log(` → Descargando set directamente: DownloadFileServlet?rutEmpresa=${rutNum}&dvEmpresa=${dvChar}&rutRepre=${rutRepreNum}&dvRepre=${dvRepreChar}&mailProvSw=${correoSet}`);
|
|
2434
2602
|
|
|
2435
2603
|
const setText = await new Promise((resolve, reject) => {
|
|
2436
2604
|
const req = https.get(downloadUrl, {
|
|
@@ -2465,10 +2633,10 @@ class CertRunner {
|
|
|
2465
2633
|
const nodePath = require('path');
|
|
2466
2634
|
fs.mkdirSync(nodePath.dirname(setPath), { recursive: true });
|
|
2467
2635
|
fs.writeFileSync(setPath, setText, 'utf-8');
|
|
2468
|
-
console.log(`
|
|
2636
|
+
console.log(` ✓ Set guardado en: ${setPath}`);
|
|
2469
2637
|
}
|
|
2470
2638
|
|
|
2471
|
-
console.log(`
|
|
2639
|
+
console.log(` ✓ Set de pruebas obtenido (${setText.length} chars)`);
|
|
2472
2640
|
return { success: true, setText };
|
|
2473
2641
|
} catch (err) {
|
|
2474
2642
|
return { success: false, error: err.message };
|
|
@@ -2506,9 +2674,9 @@ class CertRunner {
|
|
|
2506
2674
|
});
|
|
2507
2675
|
const page = await browser.newPage();
|
|
2508
2676
|
await page.setCookie(...puppeteerCookies);
|
|
2509
|
-
page.on('dialog', async dlg => { console.log(`
|
|
2677
|
+
page.on('dialog', async dlg => { console.log(` → [dialog SET=2] ${dlg.message()}`); await dlg.accept(); });
|
|
2510
2678
|
|
|
2511
|
-
console.log('
|
|
2679
|
+
console.log(' → Cargando portal certBolElectDteInternet (SET=2)...');
|
|
2512
2680
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/?SET=2', {
|
|
2513
2681
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2514
2682
|
});
|
|
@@ -2518,7 +2686,7 @@ class CertRunner {
|
|
|
2518
2686
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2519
2687
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2520
2688
|
if (!rutInput) throw new Error('certBolElectDteInternet/?SET=2: no se encontró campo RUT');
|
|
2521
|
-
console.log(`
|
|
2689
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
2522
2690
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutNum);
|
|
2523
2691
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(dvChar);
|
|
2524
2692
|
await page.evaluate(() => {
|
|
@@ -2526,7 +2694,7 @@ class CertRunner {
|
|
|
2526
2694
|
.find(b => /confirmar/i.test(b.textContent));
|
|
2527
2695
|
if (btn) btn.click();
|
|
2528
2696
|
});
|
|
2529
|
-
console.log('
|
|
2697
|
+
console.log(' → Click "Confirmar Empresa"');
|
|
2530
2698
|
|
|
2531
2699
|
// Esperar que aparezca el campo "Identificador de Envio" — GWT dispara ~8-10 POST /facade en paralelo
|
|
2532
2700
|
await page.waitForNetworkIdle({ idleTime: 800, timeout: 40000 }).catch(() => {});
|
|
@@ -2538,7 +2706,7 @@ class CertRunner {
|
|
|
2538
2706
|
// Paso 2: Ingresar TrackId
|
|
2539
2707
|
const trackInput = await page.$('input[maxlength="15"]');
|
|
2540
2708
|
if (!trackInput) throw new Error('No se encontró campo "Identificador de Envio" en certBolElectDteInternet/?SET=2');
|
|
2541
|
-
console.log(`
|
|
2709
|
+
console.log(` → Ingresando TrackId: ${trackId}`);
|
|
2542
2710
|
await trackInput.click({ clickCount: 3 });
|
|
2543
2711
|
await trackInput.type(String(trackId));
|
|
2544
2712
|
|
|
@@ -2548,7 +2716,7 @@ class CertRunner {
|
|
|
2548
2716
|
.find(b => /solicitar/i.test(b.textContent));
|
|
2549
2717
|
if (btn) btn.click();
|
|
2550
2718
|
});
|
|
2551
|
-
console.log('
|
|
2719
|
+
console.log(' → Click "Solicitar validación" — esperando respuesta...');
|
|
2552
2720
|
|
|
2553
2721
|
// Esperar respuesta del portal (puede ser confirmación o error)
|
|
2554
2722
|
await page.waitForFunction(() => {
|
|
@@ -2558,7 +2726,7 @@ class CertRunner {
|
|
|
2558
2726
|
}, { timeout: 15000, polling: 500 }).catch(() => {});
|
|
2559
2727
|
|
|
2560
2728
|
const respuesta = await page.evaluate(() => (document.body.innerText || '').trim().substring(0, 500));
|
|
2561
|
-
console.log(`
|
|
2729
|
+
console.log(` ✓ Validación solicitada. Respuesta: ${respuesta.substring(0, 120)}`);
|
|
2562
2730
|
|
|
2563
2731
|
return { success: true, respuesta };
|
|
2564
2732
|
} catch (err) {
|
|
@@ -2610,7 +2778,7 @@ class CertRunner {
|
|
|
2610
2778
|
let dialogMsg = null;
|
|
2611
2779
|
page.on('dialog', async dlg => {
|
|
2612
2780
|
dialogMsg = dlg.message();
|
|
2613
|
-
console.log(`
|
|
2781
|
+
console.log(` → [dialog] ${dialogMsg}`);
|
|
2614
2782
|
await dlg.accept();
|
|
2615
2783
|
});
|
|
2616
2784
|
|
|
@@ -2623,7 +2791,7 @@ class CertRunner {
|
|
|
2623
2791
|
for (let intento = 1; intento <= MAX_INTENTOS; intento++) {
|
|
2624
2792
|
dialogMsg = null; // resetear entre intentos
|
|
2625
2793
|
|
|
2626
|
-
console.log(`
|
|
2794
|
+
console.log(` → Navegando a certBolElectDteInternet (declaración) [intento ${intento}/${MAX_INTENTOS}]...`);
|
|
2627
2795
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/', {
|
|
2628
2796
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2629
2797
|
});
|
|
@@ -2633,7 +2801,7 @@ class CertRunner {
|
|
|
2633
2801
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2634
2802
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2635
2803
|
if (!rutInput) throw new Error('certBolElectDteInternet/: no se encontró campo RUT empresa');
|
|
2636
|
-
console.log(`
|
|
2804
|
+
console.log(` → Ingresando RUT empresa: ${rutEmpresaRaw}`);
|
|
2637
2805
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutEmpNum);
|
|
2638
2806
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(rutEmpDv);
|
|
2639
2807
|
|
|
@@ -2642,7 +2810,7 @@ class CertRunner {
|
|
|
2642
2810
|
.find(b => /confirmar empresa/i.test(b.textContent));
|
|
2643
2811
|
if (btn) btn.click();
|
|
2644
2812
|
});
|
|
2645
|
-
console.log('
|
|
2813
|
+
console.log(' → Click "Confirmar Empresa"...');
|
|
2646
2814
|
|
|
2647
2815
|
// GWT dispara ~8-10 POST /facade EN PARALELO al confirmar.
|
|
2648
2816
|
// Esperar red inactiva y luego DOM con checkboxes.
|
|
@@ -2654,9 +2822,9 @@ class CertRunner {
|
|
|
2654
2822
|
if (dialogMsg) {
|
|
2655
2823
|
// Portal lanzó alert — puede ser transitorio. Guardar y reintentar.
|
|
2656
2824
|
lastDialogMsg = dialogMsg;
|
|
2657
|
-
console.log(`
|
|
2825
|
+
console.log(` [!] Portal respondió con alerta en intento ${intento}: ${dialogMsg.substring(0, 100)}`);
|
|
2658
2826
|
if (intento < MAX_INTENTOS) {
|
|
2659
|
-
console.log('
|
|
2827
|
+
console.log(' → Recargando y reintentando en 3s...');
|
|
2660
2828
|
await new Promise(r => setTimeout(r, 3000));
|
|
2661
2829
|
continue;
|
|
2662
2830
|
}
|
|
@@ -2669,7 +2837,7 @@ class CertRunner {
|
|
|
2669
2837
|
break;
|
|
2670
2838
|
}
|
|
2671
2839
|
|
|
2672
|
-
console.log(`
|
|
2840
|
+
console.log(` [!] Formulario no cargó en intento ${intento}${intento < MAX_INTENTOS ? ` — reintentando...` : ''}`);
|
|
2673
2841
|
await new Promise(r => setTimeout(r, 2000));
|
|
2674
2842
|
}
|
|
2675
2843
|
|
|
@@ -2682,7 +2850,7 @@ class CertRunner {
|
|
|
2682
2850
|
todos.forEach(cb => { if (!cb.checked) cb.click(); });
|
|
2683
2851
|
return todos.length;
|
|
2684
2852
|
});
|
|
2685
|
-
console.log(`
|
|
2853
|
+
console.log(` → ${totalCbs} checkbox(es) marcados`);
|
|
2686
2854
|
await new Promise(r => setTimeout(r, 500));
|
|
2687
2855
|
|
|
2688
2856
|
// ── PASO 3: Rellenar campos proveedor software ────────────────
|
|
@@ -2753,7 +2921,7 @@ class CertRunner {
|
|
|
2753
2921
|
return false;
|
|
2754
2922
|
});
|
|
2755
2923
|
if (!submitOk) throw new Error('No se encontró botón "Grabar Declaración" en certBolElectDteInternet/');
|
|
2756
|
-
console.log('
|
|
2924
|
+
console.log(' → Click "Grabar Declaración"...');
|
|
2757
2925
|
|
|
2758
2926
|
// Esperar confirmación del SII
|
|
2759
2927
|
await page.waitForFunction(() => {
|
|
@@ -2763,7 +2931,7 @@ class CertRunner {
|
|
|
2763
2931
|
}, { timeout: 20000, polling: 1000 }).catch(() => {});
|
|
2764
2932
|
|
|
2765
2933
|
const msgFinal = await page.evaluate(() => (document.body.textContent || '').trim().substring(0, 300));
|
|
2766
|
-
console.log(`
|
|
2934
|
+
console.log(` ✓ Declaración completada. Respuesta: ${msgFinal.substring(0, 150)}`);
|
|
2767
2935
|
|
|
2768
2936
|
if (this.config.debugDir) {
|
|
2769
2937
|
await page.screenshot({ path: path.join(this.config.debugDir, 'boleta-declaracion-post-submit.png'), fullPage: true }).catch(() => {});
|