@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.
@@ -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
- console.log(` Tipo ${tipoDte}: ${cantidad} folios...`);
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
- console.log(` ✓ CAF tipo ${tipoDte}`);
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
- return this._ejecutarSet(SetBasico, 'setBasico', 'basico', { 33: 4, 56: 1, 61: 3 }, 'basico', casos);
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
- return this._ejecutarSet(SetGuia, 'setGuiaDespacho', 'guia',
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
- return this._ejecutarSet(SetExenta, 'setFacturaExenta', 'exenta', { 34: 3, 56: 1, 61: 4 }, 'exenta', casos);
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
- return this._ejecutarSet(SetCompra, 'setFacturaCompra', 'compra', { 46: 1, 56: 1, 61: 1 }, 'compra', casos);
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(` Esperando 10s para que SII procese los envíos...`);
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
- console.log(` 🔄 Declarando ${label} (intento ${intento}/${maxIntentos})...`);
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(` SII aún procesando, reintentando en ${intervalo / 1000}s...`);
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(` ⚠️ Verificación fallida: ${result.error}`);
386
- console.log(` 🔄 Reintentando declaración en ${intervalo / 1000}s...`);
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(` ⚠️ Error declarando ${label}: ${result.error || 'desconocido'}`);
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(' Declaración de sets enviada');
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(`📚 FASE 4: LIBROS (cada libro usará período diferente)`);
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(` ℹ️ Ya en REVISADO CONFORME (se omitirán): ${_yaConformes.join(', ')}`);
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
- console.log('\n✅ Libro de Compras ya está REVISADO CONFORME — omitiendo');
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
- console.log('\n📖 Enviando Libro de Compras...');
527
- resultados.libroCompras = await this.ejecutarLibroCompras(options);
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
- console.log('\n✅ Libro de Ventas ya está REVISADO CONFORME — omitiendo');
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
- console.log('\n📖 Enviando Libro de Ventas...');
544
- resultados.libroVentas = await this.ejecutarLibroVentas(options);
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
- console.log('\n✅ Libro de Guías ya está REVISADO CONFORME — omitiendo');
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
- console.log('\n📖 Enviando Libro de Guías...');
561
- resultados.libroGuias = await this.ejecutarLibroGuias(options);
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
- errores.push(`Libro Guías: ${resultados.libroGuias.error}`);
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
- console.log('\n✅ Libro Compras Exentos ya está REVISADO CONFORME — omitiendo');
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
- console.log('\n📖 Enviando Libro de Compras para Exentos...');
579
- resultados.libroComprasExentos = await this.ejecutarLibroComprasExentos(options);
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
- // 4. Declarar los libros (incluyendo sets para evitar que pe_avance3 los resetee)
603
- console.log('\n📝 Declarando libros...');
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
- const declaracion = await this.declararLibros({ ...resultados, ...(options.setsResultados || {}) });
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 Libros declarados — esperando revisión del SII...');
610
-
611
- // Polling real: esperar hasta REVISADO CONFORME o S25 (máx 20 intentos x 15s = 5 min)
612
- const _librosAEsperar = ['LIBRO DE VENTAS', 'LIBRO DE COMPRAS', 'LIBRO DE GUIAS'];
613
- if (this._estructuras?.libroComprasExentos) _librosAEsperar.push('LIBRO DE COMPRAS PARA EXENTOS');
614
- // Solo esperar los libros que realmente enviamos (no los que ya eran REVISADO CONFORME)
615
- const _librosEnviados = _librosAEsperar.filter(n => !_estaConforme(n));
616
-
617
- console.log(`\n⏳ Esperando aprobación del SII para: ${_librosEnviados.join(', ')}`);
618
- let _aprobados = false;
619
- for (let _i = 0; _i < 20; _i++) {
620
- await sleep(15000);
621
- const _poll = await this.siiCert.consultarEstadoSets();
622
- if (!_poll.success) continue;
623
- const _ss = _poll.estadoSets || {};
624
- const _info = Object.entries(_ss)
625
- .filter(([k]) => k.toUpperCase().includes('LIBRO'))
626
- .map(([k, v]) => `${k.trim()}: ${v}`);
627
- if (_info.length) console.log(` 🔄 Intento ${_i + 1}/20: ${_info.join(' | ')}`);
628
-
629
- const _todosOk = _librosEnviados.every(nombre => {
630
- const e = Object.entries(_ss).find(([k]) => k.toUpperCase().includes(nombre));
631
- return e && (e[1] === 'REVISADO CONFORME' || e[1] === 'S25');
632
- });
633
- const _algunError = _librosEnviados.some(nombre => {
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 (_algunError) {
644
- console.log('\n❌ Hay libros rechazados. Revisar emails del SII.');
645
- break;
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
- if (!_aprobados) {
649
- console.log('\n⚠️ Timeout (5 min). El SII aún no responde. Verifica con --avance más tarde.');
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
- console.log(`\n⚠️ Libros enviados pero declaración con error: ${declaracion.error}`);
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⚠️ Error declarando libros: ${e.message}`);
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⚠️ Solo ${librosEnviados}/3 libros enviados. Errores: ${errores.join('; ')}`);
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(` Libros declarados: ${declarados.join(', ')}`);
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(` 📅 Período decrementado: ${currentPeriodo} → ${newPeriodo}`);
956
+ console.log(` Período decrementado: ${currentPeriodo} → ${newPeriodo}`);
778
957
  } catch (e) {
779
- console.warn(` ⚠️ No se pudo guardar período: ${e.message}`);
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(` 📅 Período reseteado a: ${periodo}`);
974
+ console.log(` Período reseteado a: ${periodo}`);
796
975
  } catch (e) {
797
- console.warn(` ⚠️ No se pudo guardar período: ${e.message}`);
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
- // IMPORTANTE: Decrementar período ANTES de usar para evitar "LNC - Libro Cerrado"
824
- this._decrementarPeriodoLibros();
825
- const periodo = this._getPeriodoLibros();
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(` Libro de Ventas enviado - TrackId: ${result.trackId}`);
1035
+ console.log(` [OK] Libro de Ventas enviado - TrackId: ${result.trackId}`);
859
1036
  } else {
860
- console.log(` Error enviando Libro de Ventas: ${result.error}`);
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
- // IMPORTANTE: Decrementar período ANTES de usar para evitar "LNC - Libro Cerrado"
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(` 📚 Generando Libro de Compras para período ${periodo} (${libroComprasData.detalle.length} documentos del SII)...`);
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(` Libro de Compras enviado - TrackId: ${result.trackId}`);
1086
+ console.log(` [OK] Libro de Compras enviado - TrackId: ${result.trackId}`);
913
1087
  } else {
914
- console.log(` Error enviando Libro de Compras: ${result.error}`);
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(` 📚 Generando Libro de Compras para Exentos para período ${periodo} (${libroData.detalle.length} documentos del SII)...`);
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(` Libro de Compras para Exentos enviado - TrackId: ${result.trackId}`);
1133
+ console.log(` [OK] Libro de Compras para Exentos enviado - TrackId: ${result.trackId}`);
962
1134
  } else {
963
- console.log(` Error enviando Libro de Compras para Exentos: ${result.error}`);
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
- // IMPORTANTE: Decrementar período ANTES de usar para evitar "LNC - Libro Cerrado"
984
- this._decrementarPeriodoLibros();
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(` Libro de Guías enviado - TrackId: ${result.trackId}`);
1189
+ console.log(` [OK] Libro de Guías enviado - TrackId: ${result.trackId}`);
1021
1190
  } else {
1022
- console.log(` Error enviando Libro de Guías: ${result.error}`);
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('🚀 AVANZAR SIGUIENTE PASO');
1207
+ console.log('AVANZAR SIGUIENTE PASO');
1039
1208
  console.log('═'.repeat(60) + '\n');
1040
1209
 
1041
1210
  try {
1042
- console.log(' 📋 Enviando solicitud de avance...');
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(` 📄 Respuesta guardada en: ${path.join(this.debugDir, 'avanzar-siguiente-paso-response.html')}`);
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(' Avance al siguiente paso exitoso');
1224
+ console.log(' [OK] Avance al siguiente paso exitoso');
1056
1225
  this.resultados.avanceSiguientePaso = { success: true };
1057
1226
  } else {
1058
- console.log(` Error en avance: ${result.error || 'Error desconocido'}`);
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(` Error: ${error.message}`);
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 Esperando aprobación de libros...');
1247
+ console.log('\n[...] Esperando aprobación de libros...');
1079
1248
 
1080
1249
  for (let i = 1; i <= maxIntentos; i++) {
1081
- console.log(`\n 🔄 Intento ${i}/${maxIntentos}...`);
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(` ⚠️ Error consultando avance: ${avance.error}`);
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(` ${libro}: REVISADO CONFORME`);
1275
+ console.log(` [OK] ${libro}: REVISADO CONFORME`);
1107
1276
  } else if (esRechazado) {
1108
- console.log(` ${libro}: ${estado.estado}`);
1277
+ console.log(` [ERR] ${libro}: ${estado.estado}`);
1109
1278
  hayRechazados = true;
1110
1279
  } else {
1111
- console.log(` 🔄 ${libro}: ${estado.estado || 'EN REVISION'}`);
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 Hay libros rechazados. No se puede avanzar.');
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 🎉 ¡Todos los libros aprobados!');
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 ⚠️ Timeout esperando aprobación de libros');
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('🧪 FASE 6: SIMULACIÓN');
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(' Solicitando CAFs para simulación...');
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(' Generando DTEs de simulación...');
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(` 📦 Plan de simulación: ${plan.length} documentos`);
1189
- console.log(` 📄 Tipos usados: ${tiposUsados.join(', ')}`);
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 📤 Enviando al SII...');
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 Simulación enviada - TrackId: ${result.trackId}`);
1397
+ console.log(`\n[OK] Simulación enviada - TrackId: ${result.trackId}`);
1230
1398
  } else {
1231
- console.log(`\n Error en simulación: ${result.error}`);
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(' 🔍 Verificando etapa actual...');
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(' Simulación ya aprobada - empresa en etapa INTERCAMBIO');
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(' Simulación declarada exitosamente');
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 Esperando aprobación de simulació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 🔄 Intento ${i}/${maxIntentos}...`);
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(` ⚠️ Error consultando avance: ${avance.error}`);
1500
+ console.log(` [!] Error consultando avance: ${avance.error}`);
1333
1501
  await sleep(intervalo);
1334
1502
  continue;
1335
1503
  }
1336
1504
 
1337
- // PRIMERO: Verificar si ya pasó a INTERCAMBIO (significa que simulación fue aprobada)
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(` Etapa actual: ${avance.etapaActual}`);
1340
- console.log('\n 🎉 ¡SIMULACIÓN APROBADA! Empresa pasó a etapa INTERCAMBIO.');
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
- // TAMBIÉN: Etapas que vienen DESPUÉS de INTERCAMBIO (simulación + intercambio ya completos)
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(` 📍 Etapa actual: ${avance.etapaActual}`);
1348
- console.log('\n 🎉 ¡SIMULACIÓN + INTERCAMBIO COMPLETADOS! Empresa en etapa: ' + avance.etapaActual);
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
- // SEGUNDO: Verificar indicador de formulario de confirmación (simulación aprobada pendiente confirmar)
1520
+ // [OK] SEGUNDO: Verificar indicador de formulario de confirmación (simulación aprobada pendiente confirmar)
1353
1521
  if (avance.simulacionAprobadaIndicador) {
1354
- console.log(` Formulario de confirmación detectado`);
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 📝 Confirmando revisión de simulación (TrackId: ${this.resultados.simulacion.trackId})...`);
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(' Confirmación enviada exitosamente');
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 🎉 ¡SIMULACIÓN CONFIRMADA! Certificación completa.');
1549
+ console.log('\n ¡SIMULACIÓN CONFIRMADA! Certificación completa.');
1382
1550
  return { success: true, confirmada: true };
1383
1551
  }
1384
1552
 
1385
- console.log(' ⚠️ SII aún mantiene formulario de simulación pendiente; se reintentará...');
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(` ⚠️ Error en confirmación: ${confirmResult.error}`);
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 🎉 ¡SIMULACIÓN APROBADA! Lista para confirmar revisió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(` SIMULACIÓN: REVISADO CONFORME`);
1415
- console.log('\n 🎉 ¡SIMULACIÓN APROBADA! Certificación completa.');
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(` SIMULACIÓN: ${simEstado.estado}`);
1586
+ console.log(` [ERR] SIMULACIÓN: ${simEstado.estado}`);
1419
1587
  return { success: false, error: 'Simulación rechazada' };
1420
1588
  } else {
1421
- console.log(` 🔄 SIMULACIÓN: ${simEstado.estado || 'EN REVISION'}`);
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(` 📍 Etapa actual: ${avance.etapaActual}`);
1594
+ console.log(` Etapa actual: ${avance.etapaActual}`);
1427
1595
  } else {
1428
- console.log(' Simulación aún no registrada...');
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 ⚠️ Timeout esperando aprobación de simulació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('📬 FASE 7: INTERCAMBIO DE INFORMACIÓN');
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(`\n📂 Leyendo SET desde: ${setInputPath}`);
1647
+ console.log(`\nLeyendo SET desde: ${setInputPath}`);
1480
1648
  setXml = fs.readFileSync(setInputPath, 'utf8');
1481
- console.log(` ✓ ${setXml.length} bytes`);
1649
+ console.log(` ✓ ${setXml.length} bytes`);
1482
1650
  } else if (fs.existsSync(setDownloadPath)) {
1483
- console.log(`\n📂 Leyendo SET guardado: ${setDownloadPath}`);
1651
+ console.log(`\nLeyendo SET guardado: ${setDownloadPath}`);
1484
1652
  setXml = fs.readFileSync(setDownloadPath, 'utf8');
1485
- console.log(` ✓ ${setXml.length} bytes`);
1653
+ console.log(` ✓ ${setXml.length} bytes`);
1486
1654
  } else {
1487
- console.log('\n📡 Descargando SET desde www4.sii.cl/pfeInternet...');
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(` SET descargado (${setXml.length} bytes) → ${setDownloadPath}`);
1660
+ console.log(` [OK] SET descargado (${setXml.length} bytes) → ${setDownloadPath}`);
1493
1661
  } else {
1494
- console.log(` ⚠️ No se pudo descargar: ${dl.error}`);
1662
+ console.log(` [!] No se pudo descargar: ${dl.error}`);
1495
1663
  console.log('\n' + '─'.repeat(60));
1496
- console.log('📋 DESCARGA MANUAL REQUERIDA:');
1497
- console.log(' 1. Si aparece error de sesiones: ingresa a https://www4.sii.cl/ → Cerrar Sesión');
1498
- console.log(' 2. Ir a: https://www4.sii.cl/pfeInternet/ y descargar el SET XML');
1499
- console.log(` 3. Guardarlo en: ${setDownloadPath}`);
1500
- console.log(' 4. Volver a ejecutar el runner');
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('\n📝 Generando respuestas firmadas...');
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('\n📤 Subiendo respuestas a www4.sii.cl/pfeInternet...');
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(' INTERCAMBIO COMPLETADO');
1702
+ console.log('[OK] INTERCAMBIO COMPLETADO');
1535
1703
  console.log('═'.repeat(60));
1536
- if (uploadResult.resultado) console.log(` Resultado SII: ${uploadResult.resultado}`);
1704
+ if (uploadResult.resultado) console.log(` Resultado SII: ${uploadResult.resultado}`);
1537
1705
  } else {
1538
- console.log(` ⚠️ No se pudo subir automáticamente: ${uploadResult.error}`);
1706
+ console.log(` [!] No se pudo subir automáticamente: ${uploadResult.error}`);
1539
1707
  console.log('\n' + '─'.repeat(60));
1540
- console.log('📋 SUBIDA MANUAL REQUERIDA:');
1541
- console.log(' 1. Ir a: https://www4.sii.cl/pfeInternet/ → "Subir archivos"');
1542
- console.log(` 2. Subir: ${genResult.files.recepcion}`);
1543
- console.log(` 3. Subir: ${genResult.files.aprobacion}`);
1544
- console.log(` 4. Subir: ${genResult.files.recibos}`);
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] ♻️ Reutilizando sesión SII en memoria');
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] Sesión SII activa (cookies NETSCAPE: ${nSession})`);
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(` → Descargando SET desde pfeInternet/downloadFile (RUT ${rutNum}-${dv})...`);
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(` ✓ SET descargado correctamente (${r.body.length} bytes)`);
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(` ${errMsg}`);
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', filename: 'respuesta-recepcion-envio.xml', content: recepcionXml, uploadN: 1 },
1727
- { label: 'Recibo de Mercaderias', filename: 'envio-recibos.xml', content: recibosXml, uploadN: 2 },
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(' → Cargando portal pfeInternet...');
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(' → Clickeando "Subir archivos XML de respuesta de Intercambio"...');
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(` → Ingresando RUT empresa: ${rutConDv}`);
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(' → Click "Confirmar Empresa", esperando formulario de upload...');
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(' → Formulario de upload listo');
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(` → ${archivo.filename}: ya procesado anteriormente, saltando...`);
2017
+ console.log(` → ${archivo.filename}: ya procesado anteriormente, saltando...`);
1850
2018
  continue;
1851
2019
  }
1852
2020
 
1853
- console.log(` → Subiendo ${archivo.filename}...`);
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(` ✓ ${msgText}`);
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') || t.includes('RECHAZADO');
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')) return 'APROBADO';
1974
- if (t.includes('POR REVISAR')) return 'POR REVISAR';
1975
- if (t.includes('EN REVISI')) return 'EN REVISIÓN';
1976
- if (t.includes('RECHAZADO')) return 'RECHAZADO';
1977
- if (t.includes('ENVIADO AL SII')) return '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(`📄 FASE 8: MUESTRAS IMPRESAS (${pdfPaths.length} PDFs)`);
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(' → Cargando portal pdfdteInternet...');
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(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
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(' → Diálogo de revisión existente → haciendo click en "Sí"');
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(` → Ingresando RUT proveedor: ${rutNum}-${dvChar}`);
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')) return 'APROBADO';
2111
- if (t.includes('POR REVISAR')) return 'POR REVISAR';
2112
- if (t.includes('EN REVISI')) return 'EN REVISIÓN';
2113
- if (t.includes('RECHAZADO')) return '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(` Portal ya muestra estado "${_estadoYaSubido}" — muestras subidas previamente. Proceso completado.`);
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(' → Click "Crear"...');
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(` → Cargando ${pdfPaths.length} PDFs para drop en el portal...`);
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(` → Ejecutando drop de ${pdfPaths.length} PDFs sobre el portal...`);
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(` → Drop ejecutado (${_dropped} archivos). Esperando procesamiento...`);
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')) estado = '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')) estado = 'EN REVISIÓN';
2236
- else if (t.includes('RECHAZADO')) estado = '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(` Portal en estado "${_fase1.estado}" — muestras ya procesadas previamente.`);
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(' Sin progreso en 45s y sin estado terminal. Continuando al paso siguiente...');
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(` Timeout: solo se procesaron ${procesados}/${pdfPaths.length} antes del timeout`);
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(' → Re-navegando para "Enviar al SII"...');
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(' → Click "Enviar al SII"...');
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(` → Resultado: ${exitoso ? ' enviado correctamente' : '⚠️ sin confirmación explícita'}`);
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(` → [dialog SET=1] ${dlg.message()}`); await dlg.accept(); });
2511
+ page.on('dialog', async dlg => { console.log(` → [dialog SET=1] ${dlg.message()}`); await dlg.accept(); });
2344
2512
 
2345
- console.log(' → Cargando portal certBolElectDteInternet (SET=1)...');
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(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
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(' → Click "Confirmar Empresa"');
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(` → ${nCbs} checkbox(es) marcados`);
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(` → Correo proveedor: ${correoSet}`);
2561
+ console.log(` → Correo proveedor: ${correoSet}`);
2394
2562
  } else {
2395
- console.log(' ⚠️ No se encontró campo de correo — continuando sin él');
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'] || cookieJar['DV_NS'] || '';
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(' → Click "Bajar Nuevo Set" — esperando GWT facade...');
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(` → Descargando set directamente: DownloadFileServlet?rutEmpresa=${rutNum}&dvEmpresa=${dvChar}&rutRepre=${rutRepreNum}&dvRepre=${dvRepreChar}&mailProvSw=${correoSet}`);
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(` ✓ Set guardado en: ${setPath}`);
2636
+ console.log(` ✓ Set guardado en: ${setPath}`);
2469
2637
  }
2470
2638
 
2471
- console.log(` ✓ Set de pruebas obtenido (${setText.length} chars)`);
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(` → [dialog SET=2] ${dlg.message()}`); await dlg.accept(); });
2677
+ page.on('dialog', async dlg => { console.log(` → [dialog SET=2] ${dlg.message()}`); await dlg.accept(); });
2510
2678
 
2511
- console.log(' → Cargando portal certBolElectDteInternet (SET=2)...');
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(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
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(' → Click "Confirmar Empresa"');
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(` → Ingresando TrackId: ${trackId}`);
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(' → Click "Solicitar validación" — esperando respuesta...');
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(` ✓ Validación solicitada. Respuesta: ${respuesta.substring(0, 120)}`);
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(` → [dialog] ${dialogMsg}`);
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(` → Navegando a certBolElectDteInternet (declaración) [intento ${intento}/${MAX_INTENTOS}]...`);
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(` → Ingresando RUT empresa: ${rutEmpresaRaw}`);
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(' → Click "Confirmar Empresa"...');
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(` ⚠️ Portal respondió con alerta en intento ${intento}: ${dialogMsg.substring(0, 100)}`);
2825
+ console.log(` [!] Portal respondió con alerta en intento ${intento}: ${dialogMsg.substring(0, 100)}`);
2658
2826
  if (intento < MAX_INTENTOS) {
2659
- console.log(' → Recargando y reintentando en 3s...');
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(` ⚠️ Formulario no cargó en intento ${intento}${intento < MAX_INTENTOS ? ` — reintentando...` : ''}`);
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(` → ${totalCbs} checkbox(es) marcados`);
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(' → Click "Grabar Declaración"...');
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(` ✓ Declaración completada. Respuesta: ${msgFinal.substring(0, 150)}`);
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(() => {});