@devlas/dte-sii 2.5.12 → 2.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BoletaService.js +1 -1
- package/CafSolicitor.js +11 -12
- package/Certificado.js +2 -2
- package/ConsumoFolio.js +3 -3
- package/EnviadorSII.js +72 -99
- package/FolioService.js +1 -1
- package/SiiCertificacion.js +159 -28
- package/SiiPortalAuth.js +33 -9
- package/SiiSession.js +4 -4
- package/cert/BoletaCert.js +44 -44
- package/cert/CertRunner.js +511 -219
- package/cert/ConfigLoader.js +1 -1
- package/cert/IntercambioCert.js +19 -19
- package/cert/LibroCompras.js +45 -5
- package/cert/MuestrasImpresas.js +18 -18
- package/cert/SetBase.js +16 -16
- package/cert/SetBasico.js +6 -6
- package/cert/SetCompra.js +6 -6
- package/cert/SetExenta.js +6 -6
- package/cert/SetGuia.js +2 -2
- package/cert/SetParser.js +96 -13
- package/cert/SetsProvider.js +16 -19
- package/cert/Simulacion.js +1 -1
- package/package.json +2 -1
- package/utils/progress.js +78 -0
- package/utils/xml.js +2 -2
package/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,16 +356,27 @@ 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;
|
|
350
369
|
|
|
370
|
+
// Guardar el form pe_avance2 (antes del POST) para debug
|
|
371
|
+
if (result.formHtml) {
|
|
372
|
+
fs.writeFileSync(
|
|
373
|
+
path.join(this.debugDir, `${debugPrefix}-pe_avance2-${intento}.html`),
|
|
374
|
+
result.formHtml,
|
|
375
|
+
'utf8'
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Guardar la respuesta pe_avance3 (despues del POST) para debug
|
|
351
380
|
if (result.rawHtml) {
|
|
352
381
|
fs.writeFileSync(
|
|
353
382
|
path.join(this.debugDir, `${debugPrefix}-${intento}.html`),
|
|
@@ -368,10 +397,19 @@ class CertRunner {
|
|
|
368
397
|
}
|
|
369
398
|
|
|
370
399
|
if (noProcessedError && intento < maxIntentos) {
|
|
371
|
-
console.log(`
|
|
400
|
+
console.log(` [...] SII aún procesando, reintentando en ${intervalo / 1000}s...`);
|
|
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;
|
|
406
|
+
} else if (result.verificado === false && intento < maxIntentos) {
|
|
407
|
+
// Verificación post-declaración falló: los campos quedaron vacíos en el portal
|
|
408
|
+
console.log(` [!] Verificación fallida: ${result.error}`);
|
|
409
|
+
console.log(` [...] Reintentando declaración en ${intervalo / 1000}s...`);
|
|
372
410
|
await sleep(intervalo);
|
|
373
411
|
} else if (!result.success) {
|
|
374
|
-
console.log(`
|
|
412
|
+
console.log(` [!] Error declarando ${label}: ${result.error || 'desconocido'}`);
|
|
375
413
|
break;
|
|
376
414
|
}
|
|
377
415
|
}
|
|
@@ -419,8 +457,9 @@ class CertRunner {
|
|
|
419
457
|
return { success: false, error: 'No hay sets para declarar' };
|
|
420
458
|
}
|
|
421
459
|
|
|
460
|
+
emitProgress(STEPS.SETS_DECLARING);
|
|
422
461
|
const result = await this._declararConReintentos(sets, 'declaracion-response', { maxIntentos, intervalo, label: 'avance de sets' });
|
|
423
|
-
if (result?.success) console.log('
|
|
462
|
+
if (result?.success) { emitProgress(STEPS.SETS_DECLARED); console.log(' ✓ Declaracion de sets enviada'); }
|
|
424
463
|
return result;
|
|
425
464
|
}
|
|
426
465
|
|
|
@@ -460,23 +499,72 @@ class CertRunner {
|
|
|
460
499
|
* @param {Object} [options] - Opciones
|
|
461
500
|
* @param {Object} [options.setBasicoResult] - Resultado del SetBasico
|
|
462
501
|
* @param {Object} [options.setGuiaResult] - Resultado del SetGuia
|
|
502
|
+
* @param {Object} [options.setsResultados] - Track IDs de los sets (basico/guia/exenta/compra) para incluirlos en la declaración conjunta
|
|
463
503
|
* @returns {Promise<Object>} Resultado con todos los libros
|
|
464
504
|
*/
|
|
465
505
|
async ejecutarFase4Libros(options = {}) {
|
|
466
506
|
// NOTA: Ya NO decrementamos aquí - cada libro decrementa su propio período
|
|
507
|
+
emitProgress(STEPS.BOOKS_START);
|
|
467
508
|
console.log('\n' + '═'.repeat(60));
|
|
468
|
-
console.log(
|
|
509
|
+
console.log('FASE 4: LIBROS (todos usan el mismo periodo)');
|
|
469
510
|
console.log('═'.repeat(60) + '\n');
|
|
470
511
|
|
|
471
512
|
const resultados = {};
|
|
472
513
|
const errores = [];
|
|
473
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
|
+
|
|
520
|
+
// Verificar cuáles libros ya están REVISADO CONFORME en el portal (no re-enviar)
|
|
521
|
+
let _estadoActual = {};
|
|
522
|
+
try {
|
|
523
|
+
const _consultaPrevia = await this.siiCert.consultarEstadoSets();
|
|
524
|
+
if (_consultaPrevia.success) _estadoActual = _consultaPrevia.estadoSets || {};
|
|
525
|
+
const _yaConformes = Object.entries(_estadoActual)
|
|
526
|
+
.filter(([k, v]) => k.toUpperCase().includes('LIBRO') && v === 'REVISADO CONFORME')
|
|
527
|
+
.map(([k]) => k);
|
|
528
|
+
if (_yaConformes.length) {
|
|
529
|
+
console.log(` Ya en REVISADO CONFORME (se omitirán): ${_yaConformes.join(', ')}`);
|
|
530
|
+
}
|
|
531
|
+
} catch (_e) { /* ignorar error de consulta previa */ }
|
|
532
|
+
|
|
533
|
+
const _estaConforme = (nombre) => {
|
|
534
|
+
const nombreUpper = nombre.toUpperCase();
|
|
535
|
+
const e = Object.entries(_estadoActual).find(([k]) => {
|
|
536
|
+
const ku = k.toUpperCase();
|
|
537
|
+
if (nombreUpper === 'LIBRO DE COMPRAS') return ku.includes('LIBRO DE COMPRAS') && !ku.includes('EXENTOS');
|
|
538
|
+
return ku.includes(nombreUpper);
|
|
539
|
+
});
|
|
540
|
+
return e && e[1] === 'REVISADO CONFORME';
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
// Helper: guardar resultados parciales a disco tras cada envío exitoso
|
|
544
|
+
const _resultadosLibrosPath = path.join(this.debugDir, 'resultados-libros.json');
|
|
545
|
+
const _guardarResultadosParciales = () => {
|
|
546
|
+
try {
|
|
547
|
+
fs.writeFileSync(_resultadosLibrosPath, JSON.stringify(resultados, null, 2));
|
|
548
|
+
} catch (_e) { /* ignorar */ }
|
|
549
|
+
};
|
|
550
|
+
|
|
474
551
|
try {
|
|
475
552
|
// 1. Libro de Compras (usa datos del SII)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
553
|
+
if (_estaConforme('LIBRO DE COMPRAS')) {
|
|
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');
|
|
557
|
+
} else {
|
|
558
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroCompras' });
|
|
559
|
+
console.log('\nEnviando Libro de Compras...');
|
|
560
|
+
resultados.libroCompras = await this.ejecutarLibroCompras({ ...options, periodo: _periodoComunLibros });
|
|
561
|
+
if (!resultados.libroCompras.success) {
|
|
562
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroCompras', error: resultados.libroCompras.error });
|
|
563
|
+
errores.push(`Libro Compras: ${resultados.libroCompras.error}`);
|
|
564
|
+
} else {
|
|
565
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroCompras', trackId: resultados.libroCompras.trackId });
|
|
566
|
+
_guardarResultadosParciales();
|
|
567
|
+
}
|
|
480
568
|
}
|
|
481
569
|
} catch (e) {
|
|
482
570
|
errores.push(`Libro Compras: ${e.message}`);
|
|
@@ -484,10 +572,21 @@ class CertRunner {
|
|
|
484
572
|
|
|
485
573
|
try {
|
|
486
574
|
// 2. Libro de Ventas (usa SetBasico)
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
575
|
+
if (_estaConforme('LIBRO DE VENTAS')) {
|
|
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');
|
|
579
|
+
} else {
|
|
580
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroVentas' });
|
|
581
|
+
console.log('\nEnviando Libro de Ventas...');
|
|
582
|
+
resultados.libroVentas = await this.ejecutarLibroVentas({ ...options, periodo: _periodoComunLibros });
|
|
583
|
+
if (!resultados.libroVentas.success) {
|
|
584
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroVentas', error: resultados.libroVentas.error });
|
|
585
|
+
errores.push(`Libro Ventas: ${resultados.libroVentas.error}`);
|
|
586
|
+
} else {
|
|
587
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroVentas', trackId: resultados.libroVentas.trackId });
|
|
588
|
+
_guardarResultadosParciales();
|
|
589
|
+
}
|
|
491
590
|
}
|
|
492
591
|
} catch (e) {
|
|
493
592
|
errores.push(`Libro Ventas: ${e.message}`);
|
|
@@ -495,22 +594,44 @@ class CertRunner {
|
|
|
495
594
|
|
|
496
595
|
try {
|
|
497
596
|
// 3. Libro de Guías (usa SetGuia)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
597
|
+
if (_estaConforme('LIBRO DE GUIAS')) {
|
|
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');
|
|
601
|
+
} else {
|
|
602
|
+
emitProgress(STEPS.BOOK_SENDING, { book: 'libroGuias' });
|
|
603
|
+
console.log('\nEnviando Libro de Guias...');
|
|
604
|
+
resultados.libroGuias = await this.ejecutarLibroGuias({ ...options, periodo: _periodoComunLibros });
|
|
605
|
+
if (!resultados.libroGuias.success) {
|
|
606
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroGuias', error: resultados.libroGuias.error });
|
|
607
|
+
errores.push(`Libro Guias: ${resultados.libroGuias.error}`);
|
|
608
|
+
} else {
|
|
609
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroGuias', trackId: resultados.libroGuias.trackId });
|
|
610
|
+
_guardarResultadosParciales();
|
|
611
|
+
}
|
|
502
612
|
}
|
|
503
613
|
} catch (e) {
|
|
504
614
|
errores.push(`Libro Guías: ${e.message}`);
|
|
505
615
|
}
|
|
506
616
|
|
|
507
|
-
// 4. Libro de Compras para Exentos (solo si el SII lo entregó)
|
|
617
|
+
// 4. Libro de Compras para Exentos (solo si el SII lo entregó y no está ya aprobado)
|
|
508
618
|
if (this._estructuras?.libroComprasExentos) {
|
|
509
619
|
try {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
620
|
+
if (_estaConforme('LIBRO DE COMPRAS PARA EXENTOS')) {
|
|
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');
|
|
624
|
+
} else {
|
|
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 });
|
|
628
|
+
if (!resultados.libroComprasExentos.success) {
|
|
629
|
+
emitProgress(STEPS.BOOK_ERROR, { book: 'libroComprasExentos', error: resultados.libroComprasExentos.error });
|
|
630
|
+
errores.push(`Libro Compras Exentos: ${resultados.libroComprasExentos.error}`);
|
|
631
|
+
} else {
|
|
632
|
+
emitProgress(STEPS.BOOK_OK, { book: 'libroComprasExentos', trackId: resultados.libroComprasExentos.trackId });
|
|
633
|
+
_guardarResultadosParciales();
|
|
634
|
+
}
|
|
514
635
|
}
|
|
515
636
|
} catch (e) {
|
|
516
637
|
errores.push(`Libro Compras Exentos: ${e.message}`);
|
|
@@ -518,27 +639,203 @@ class CertRunner {
|
|
|
518
639
|
}
|
|
519
640
|
|
|
520
641
|
// Contar libros obligatorios (ventas + compras + guías)
|
|
642
|
+
// Un libro cuenta como OK si fue enviado exitosamente O si ya era REVISADO CONFORME (omitido)
|
|
521
643
|
const librosObligatorios = ['libroVentas', 'libroCompras', 'libroGuias'];
|
|
522
|
-
const librosEnviados = librosObligatorios.filter(k =>
|
|
644
|
+
const librosEnviados = librosObligatorios.filter(k => {
|
|
645
|
+
if (resultados[k]?.success) return true;
|
|
646
|
+
// Mapeo clave→nombre para consultar _estaConforme
|
|
647
|
+
const nombreMap = { libroVentas: 'LIBRO DE VENTAS', libroCompras: 'LIBRO DE COMPRAS', libroGuias: 'LIBRO DE GUIAS' };
|
|
648
|
+
return _estaConforme(nombreMap[k]);
|
|
649
|
+
}).length;
|
|
523
650
|
|
|
524
651
|
if (librosEnviados === 3) {
|
|
525
|
-
//
|
|
526
|
-
|
|
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;
|
|
527
771
|
try {
|
|
528
|
-
|
|
772
|
+
let declaracion = await this.declararLibros({ ...resultados, ...(options.setsResultados || {}) });
|
|
529
773
|
resultados.declaracion = declaracion;
|
|
530
|
-
|
|
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
|
+
|
|
531
785
|
if (declaracion.success) {
|
|
532
|
-
console.log('\n
|
|
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}`);
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
if (declaracion.success) {
|
|
814
|
+
// Solo verificar los libros que acabamos de re-enviar
|
|
815
|
+
_librosAVerificar = _s21Nombres;
|
|
816
|
+
;({ ok, estadosFinal } = await _esperarAprobacion(_librosAVerificar));
|
|
817
|
+
}
|
|
818
|
+
// si allRejected → continuar loop (decrementar de nuevo)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (!ok) {
|
|
822
|
+
console.log('\n[!] No se pudo obtener aprobación del SII para todos los libros.');
|
|
823
|
+
}
|
|
533
824
|
} else {
|
|
534
|
-
|
|
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
|
+
}
|
|
535
832
|
}
|
|
536
833
|
} catch (e) {
|
|
537
|
-
console.log(`\n
|
|
834
|
+
console.log(`\n[!] Error declarando libros: ${e.message}`);
|
|
538
835
|
resultados.declaracion = { success: false, error: e.message };
|
|
539
836
|
}
|
|
540
837
|
} else {
|
|
541
|
-
console.log(`\n
|
|
838
|
+
console.log(`\n[!] Solo ${librosEnviados}/3 libros enviados. Errores: ${errores.join('; ')}`);
|
|
542
839
|
}
|
|
543
840
|
|
|
544
841
|
return {
|
|
@@ -563,6 +860,12 @@ class CertRunner {
|
|
|
563
860
|
const sets = {};
|
|
564
861
|
|
|
565
862
|
const mapping = {
|
|
863
|
+
// Sets — incluirlos para que pe_avance3 no los resetee a S01
|
|
864
|
+
basico: 'setBasico',
|
|
865
|
+
guia: 'setGuiaDespacho',
|
|
866
|
+
exenta: 'setFacturaExenta',
|
|
867
|
+
compra: 'setFacturaCompra',
|
|
868
|
+
// Libros
|
|
566
869
|
libroVentas: 'libroVentas',
|
|
567
870
|
libroCompras: 'libroCompras',
|
|
568
871
|
libroGuias: 'libroGuias',
|
|
@@ -582,7 +885,7 @@ class CertRunner {
|
|
|
582
885
|
const result = await this._declararConReintentos(sets, 'declaracion-libros-response', { maxIntentos, intervalo, label: 'libros' });
|
|
583
886
|
if (result?.success) {
|
|
584
887
|
const declarados = result.setsDeclarados || [];
|
|
585
|
-
console.log(`
|
|
888
|
+
console.log(` [OK] Libros declarados: ${declarados.join(', ')}`);
|
|
586
889
|
}
|
|
587
890
|
return result;
|
|
588
891
|
}
|
|
@@ -650,9 +953,9 @@ class CertRunner {
|
|
|
650
953
|
|
|
651
954
|
try {
|
|
652
955
|
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
653
|
-
console.log(`
|
|
956
|
+
console.log(` Período decrementado: ${currentPeriodo} → ${newPeriodo}`);
|
|
654
957
|
} catch (e) {
|
|
655
|
-
console.warn(`
|
|
958
|
+
console.warn(` [!] No se pudo guardar período: ${e.message}`);
|
|
656
959
|
}
|
|
657
960
|
|
|
658
961
|
return newPeriodo;
|
|
@@ -668,9 +971,9 @@ class CertRunner {
|
|
|
668
971
|
|
|
669
972
|
try {
|
|
670
973
|
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
671
|
-
console.log(`
|
|
974
|
+
console.log(` Período reseteado a: ${periodo}`);
|
|
672
975
|
} catch (e) {
|
|
673
|
-
console.warn(`
|
|
976
|
+
console.warn(` [!] No se pudo guardar período: ${e.message}`);
|
|
674
977
|
}
|
|
675
978
|
}
|
|
676
979
|
|
|
@@ -696,10 +999,9 @@ class CertRunner {
|
|
|
696
999
|
throw new Error('No hay resultado del SetBasico. Ejecutar ejecutarSetBasico() primero.');
|
|
697
1000
|
}
|
|
698
1001
|
|
|
699
|
-
//
|
|
700
|
-
this._decrementarPeriodoLibros();
|
|
701
|
-
|
|
702
|
-
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}...`);
|
|
703
1005
|
|
|
704
1006
|
const libroVentas = new LibroVentas({
|
|
705
1007
|
emisor: this.config.emisor,
|
|
@@ -714,7 +1016,6 @@ class CertRunner {
|
|
|
714
1016
|
// Guardar XML de debug
|
|
715
1017
|
const outPath = path.join(this.debugDir, 'libro-ventas.xml');
|
|
716
1018
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
717
|
-
console.log(` XML guardado: ${outPath}`);
|
|
718
1019
|
|
|
719
1020
|
// Enviar al SII
|
|
720
1021
|
const enviador = this._createLibroEnviador();
|
|
@@ -731,9 +1032,9 @@ class CertRunner {
|
|
|
731
1032
|
this.resultados.libroVentas = result;
|
|
732
1033
|
|
|
733
1034
|
if (result.success) {
|
|
734
|
-
console.log(`
|
|
1035
|
+
console.log(` [OK] Libro de Ventas enviado - TrackId: ${result.trackId}`);
|
|
735
1036
|
} else {
|
|
736
|
-
console.log(`
|
|
1037
|
+
console.log(` [ERR] Error enviando Libro de Ventas: ${result.error}`);
|
|
737
1038
|
}
|
|
738
1039
|
|
|
739
1040
|
return result;
|
|
@@ -748,9 +1049,7 @@ class CertRunner {
|
|
|
748
1049
|
async ejecutarLibroCompras(options = {}) {
|
|
749
1050
|
const libroComprasData = options.libroComprasData || this._estructuras?.libroCompras;
|
|
750
1051
|
|
|
751
|
-
|
|
752
|
-
this._decrementarPeriodoLibros();
|
|
753
|
-
const periodo = this._getPeriodoLibros();
|
|
1052
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
754
1053
|
|
|
755
1054
|
const libroCompras = new LibroCompras({
|
|
756
1055
|
emisor: this.config.emisor,
|
|
@@ -762,13 +1061,12 @@ class CertRunner {
|
|
|
762
1061
|
throw new Error('No hay datos del libro de compras. El SII no entregó el set LIBRO_COMPRAS al obtener las estructuras.');
|
|
763
1062
|
}
|
|
764
1063
|
|
|
765
|
-
console.log(`
|
|
1064
|
+
console.log(` Generando Libro de Compras para período ${periodo} (${libroComprasData.detalle.length} documentos del SII)...`);
|
|
766
1065
|
const { libro, xml, detalle, resumen } = libroCompras.generarDesdeEstructuras(libroComprasData, periodo);
|
|
767
1066
|
|
|
768
1067
|
// Guardar XML de debug
|
|
769
1068
|
const outPath = path.join(this.debugDir, 'libro-compras.xml');
|
|
770
1069
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
771
|
-
console.log(` XML guardado: ${outPath}`);
|
|
772
1070
|
|
|
773
1071
|
// Enviar al SII
|
|
774
1072
|
const enviador = this._createLibroEnviador();
|
|
@@ -785,9 +1083,9 @@ class CertRunner {
|
|
|
785
1083
|
this.resultados.libroCompras = result;
|
|
786
1084
|
|
|
787
1085
|
if (result.success) {
|
|
788
|
-
console.log(`
|
|
1086
|
+
console.log(` [OK] Libro de Compras enviado - TrackId: ${result.trackId}`);
|
|
789
1087
|
} else {
|
|
790
|
-
console.log(`
|
|
1088
|
+
console.log(` [ERR] Error enviando Libro de Compras: ${result.error}`);
|
|
791
1089
|
}
|
|
792
1090
|
|
|
793
1091
|
return result;
|
|
@@ -804,8 +1102,7 @@ class CertRunner {
|
|
|
804
1102
|
throw new Error('No hay datos del libro de compras para exentos. El SII no entregó el set LIBRO_COMPRAS_EXENTOS.');
|
|
805
1103
|
}
|
|
806
1104
|
|
|
807
|
-
this._decrementarPeriodoLibros();
|
|
808
|
-
const periodo = this._getPeriodoLibros();
|
|
1105
|
+
const periodo = options.periodo || (this._decrementarPeriodoLibros(), this._getPeriodoLibros());
|
|
809
1106
|
|
|
810
1107
|
const libroCompras = new LibroCompras({
|
|
811
1108
|
emisor: this.config.emisor,
|
|
@@ -813,12 +1110,11 @@ class CertRunner {
|
|
|
813
1110
|
certificado: this.certificado,
|
|
814
1111
|
});
|
|
815
1112
|
|
|
816
|
-
console.log(`
|
|
1113
|
+
console.log(` Generando Libro de Compras para Exentos para período ${periodo} (${libroData.detalle.length} documentos del SII)...`);
|
|
817
1114
|
const { libro, xml, detalle } = libroCompras.generarDesdeEstructuras(libroData, periodo);
|
|
818
1115
|
|
|
819
1116
|
const outPath = path.join(this.debugDir, 'libro-compras-exentos.xml');
|
|
820
1117
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
821
|
-
console.log(` XML guardado: ${outPath}`);
|
|
822
1118
|
|
|
823
1119
|
const enviador = this._createLibroEnviador();
|
|
824
1120
|
const resultado = await enviador.enviarLibro(libro, 'LibroCVExentos.xml');
|
|
@@ -834,9 +1130,9 @@ class CertRunner {
|
|
|
834
1130
|
this.resultados.libroComprasExentos = result;
|
|
835
1131
|
|
|
836
1132
|
if (result.success) {
|
|
837
|
-
console.log(`
|
|
1133
|
+
console.log(` [OK] Libro de Compras para Exentos enviado - TrackId: ${result.trackId}`);
|
|
838
1134
|
} else {
|
|
839
|
-
console.log(`
|
|
1135
|
+
console.log(` [ERR] Error enviando Libro de Compras para Exentos: ${result.error}`);
|
|
840
1136
|
}
|
|
841
1137
|
|
|
842
1138
|
return result;
|
|
@@ -856,10 +1152,8 @@ class CertRunner {
|
|
|
856
1152
|
throw new Error('No hay resultado del SetGuia. Ejecutar ejecutarSetGuia() primero.');
|
|
857
1153
|
}
|
|
858
1154
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const periodo = this._getPeriodoLibros();
|
|
862
|
-
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}...`);
|
|
863
1157
|
|
|
864
1158
|
const libroGuias = new LibroGuias({
|
|
865
1159
|
emisor: this.config.emisor,
|
|
@@ -876,7 +1170,6 @@ class CertRunner {
|
|
|
876
1170
|
// Guardar XML de debug
|
|
877
1171
|
const outPath = path.join(this.debugDir, 'libro-guias.xml');
|
|
878
1172
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
879
|
-
console.log(` XML guardado: ${outPath}`);
|
|
880
1173
|
|
|
881
1174
|
// Enviar al SII
|
|
882
1175
|
const enviador = this._createLibroEnviador();
|
|
@@ -893,9 +1186,9 @@ class CertRunner {
|
|
|
893
1186
|
this.resultados.libroGuias = result;
|
|
894
1187
|
|
|
895
1188
|
if (result.success) {
|
|
896
|
-
console.log(`
|
|
1189
|
+
console.log(` [OK] Libro de Guías enviado - TrackId: ${result.trackId}`);
|
|
897
1190
|
} else {
|
|
898
|
-
console.log(`
|
|
1191
|
+
console.log(` [ERR] Error enviando Libro de Guías: ${result.error}`);
|
|
899
1192
|
}
|
|
900
1193
|
|
|
901
1194
|
return result;
|
|
@@ -911,11 +1204,11 @@ class CertRunner {
|
|
|
911
1204
|
*/
|
|
912
1205
|
async avanzarSiguientePaso() {
|
|
913
1206
|
console.log('\n' + '═'.repeat(60));
|
|
914
|
-
console.log('
|
|
1207
|
+
console.log('AVANZAR SIGUIENTE PASO');
|
|
915
1208
|
console.log('═'.repeat(60) + '\n');
|
|
916
1209
|
|
|
917
1210
|
try {
|
|
918
|
-
console.log('
|
|
1211
|
+
console.log(' Enviando solicitud de avance...');
|
|
919
1212
|
const result = await this.siiCert.avanzarSiguientePaso();
|
|
920
1213
|
|
|
921
1214
|
if (result.rawHtml) {
|
|
@@ -924,20 +1217,20 @@ class CertRunner {
|
|
|
924
1217
|
result.rawHtml,
|
|
925
1218
|
'utf8'
|
|
926
1219
|
);
|
|
927
|
-
console.log(`
|
|
1220
|
+
console.log(` Respuesta guardada en: ${path.join(this.debugDir, 'avanzar-siguiente-paso-response.html')}`);
|
|
928
1221
|
}
|
|
929
1222
|
|
|
930
1223
|
if (result.success) {
|
|
931
|
-
console.log('
|
|
1224
|
+
console.log(' [OK] Avance al siguiente paso exitoso');
|
|
932
1225
|
this.resultados.avanceSiguientePaso = { success: true };
|
|
933
1226
|
} else {
|
|
934
|
-
console.log(`
|
|
1227
|
+
console.log(` [ERR] Error en avance: ${result.error || 'Error desconocido'}`);
|
|
935
1228
|
this.resultados.avanceSiguientePaso = { success: false, error: result.error };
|
|
936
1229
|
}
|
|
937
1230
|
|
|
938
1231
|
return result;
|
|
939
1232
|
} catch (error) {
|
|
940
|
-
console.log(`
|
|
1233
|
+
console.log(` [ERR] Error: ${error.message}`);
|
|
941
1234
|
this.resultados.avanceSiguientePaso = { success: false, error: error.message };
|
|
942
1235
|
return { success: false, error: error.message };
|
|
943
1236
|
}
|
|
@@ -951,15 +1244,15 @@ class CertRunner {
|
|
|
951
1244
|
async esperarLibrosYAvanzar(options = {}) {
|
|
952
1245
|
const { maxIntentos = 30, intervalo = 10000 } = options;
|
|
953
1246
|
|
|
954
|
-
console.log('\n
|
|
1247
|
+
console.log('\n[...] Esperando aprobación de libros...');
|
|
955
1248
|
|
|
956
1249
|
for (let i = 1; i <= maxIntentos; i++) {
|
|
957
|
-
console.log(`\n
|
|
1250
|
+
console.log(`\n [...] Intento ${i}/${maxIntentos}...`);
|
|
958
1251
|
|
|
959
1252
|
const avance = await this.siiCert.verAvanceParsed();
|
|
960
1253
|
|
|
961
1254
|
if (!avance.success) {
|
|
962
|
-
console.log(`
|
|
1255
|
+
console.log(` [!] Error consultando avance: ${avance.error}`);
|
|
963
1256
|
await sleep(intervalo);
|
|
964
1257
|
continue;
|
|
965
1258
|
}
|
|
@@ -979,30 +1272,30 @@ class CertRunner {
|
|
|
979
1272
|
estado.estado?.toUpperCase().includes('REPARO');
|
|
980
1273
|
|
|
981
1274
|
if (esAprobado) {
|
|
982
|
-
console.log(`
|
|
1275
|
+
console.log(` [OK] ${libro}: REVISADO CONFORME`);
|
|
983
1276
|
} else if (esRechazado) {
|
|
984
|
-
console.log(`
|
|
1277
|
+
console.log(` [ERR] ${libro}: ${estado.estado}`);
|
|
985
1278
|
hayRechazados = true;
|
|
986
1279
|
} else {
|
|
987
|
-
console.log(`
|
|
1280
|
+
console.log(` [...] ${libro}: ${estado.estado || 'EN REVISION'}`);
|
|
988
1281
|
todosAprobados = false;
|
|
989
1282
|
}
|
|
990
1283
|
}
|
|
991
1284
|
|
|
992
1285
|
if (hayRechazados) {
|
|
993
|
-
console.log('\n
|
|
1286
|
+
console.log('\n [ERR] Hay libros rechazados. No se puede avanzar.');
|
|
994
1287
|
return { success: false, error: 'Hay libros rechazados' };
|
|
995
1288
|
}
|
|
996
1289
|
|
|
997
1290
|
if (todosAprobados) {
|
|
998
|
-
console.log('\n
|
|
1291
|
+
console.log('\n ¡Todos los libros aprobados!');
|
|
999
1292
|
return await this.avanzarSiguientePaso();
|
|
1000
1293
|
}
|
|
1001
1294
|
|
|
1002
1295
|
await sleep(intervalo);
|
|
1003
1296
|
}
|
|
1004
1297
|
|
|
1005
|
-
console.log('\n
|
|
1298
|
+
console.log('\n [!] Timeout esperando aprobación de libros');
|
|
1006
1299
|
return { success: false, error: 'Timeout esperando aprobación' };
|
|
1007
1300
|
}
|
|
1008
1301
|
|
|
@@ -1024,12 +1317,12 @@ class CertRunner {
|
|
|
1024
1317
|
}
|
|
1025
1318
|
|
|
1026
1319
|
console.log('\n' + '═'.repeat(60));
|
|
1027
|
-
console.log('
|
|
1320
|
+
console.log('FASE 6: SIMULACIÓN');
|
|
1028
1321
|
console.log('═'.repeat(60) + '\n');
|
|
1029
1322
|
|
|
1030
1323
|
// Calcular CAFs necesarios
|
|
1031
1324
|
const cafRequired = this._calcularCafsSimulacion(estructuras);
|
|
1032
|
-
console.log('
|
|
1325
|
+
console.log(' Solicitando CAFs para simulación...');
|
|
1033
1326
|
|
|
1034
1327
|
// Solicitar CAFs frescos
|
|
1035
1328
|
const cafs = await this.solicitarCafs(cafRequired);
|
|
@@ -1054,22 +1347,21 @@ class CertRunner {
|
|
|
1054
1347
|
});
|
|
1055
1348
|
|
|
1056
1349
|
// Generar
|
|
1057
|
-
console.log('
|
|
1350
|
+
console.log(' Generando DTEs de simulación...');
|
|
1058
1351
|
const { envioDte, dtes, xml, plan, tiposUsados } = simulacion.generar(
|
|
1059
1352
|
estructuras,
|
|
1060
1353
|
cafObjects,
|
|
1061
1354
|
this.folioHelper,
|
|
1062
1355
|
);
|
|
1063
1356
|
|
|
1064
|
-
console.log(`
|
|
1065
|
-
console.log(`
|
|
1357
|
+
console.log(` Plan de simulación: ${plan.length} documentos`);
|
|
1358
|
+
console.log(` Tipos usados: ${tiposUsados.join(', ')}`);
|
|
1066
1359
|
|
|
1067
1360
|
// Guardar XML de debug
|
|
1068
1361
|
const runDir = path.join(this.debugDir, 'simulacion');
|
|
1069
1362
|
fs.mkdirSync(runDir, { recursive: true });
|
|
1070
1363
|
const outPath = path.join(runDir, 'envio-simulacion.xml');
|
|
1071
1364
|
fs.writeFileSync(outPath, xml, 'utf-8');
|
|
1072
|
-
console.log(` XML guardado: ${outPath}`);
|
|
1073
1365
|
|
|
1074
1366
|
// Guardar DTEs individuales
|
|
1075
1367
|
const dtesDir = path.join(runDir, 'dtes');
|
|
@@ -1080,7 +1372,7 @@ class CertRunner {
|
|
|
1080
1372
|
});
|
|
1081
1373
|
|
|
1082
1374
|
// Enviar al SII
|
|
1083
|
-
console.log('\n
|
|
1375
|
+
console.log('\n Enviando al SII...');
|
|
1084
1376
|
const enviador = this._createEnviador();
|
|
1085
1377
|
const resultado = await enviador.enviar(envioDte);
|
|
1086
1378
|
|
|
@@ -1102,9 +1394,9 @@ class CertRunner {
|
|
|
1102
1394
|
}, null, 2), 'utf8');
|
|
1103
1395
|
|
|
1104
1396
|
if (result.success) {
|
|
1105
|
-
console.log(`\n
|
|
1397
|
+
console.log(`\n[OK] Simulación enviada - TrackId: ${result.trackId}`);
|
|
1106
1398
|
} else {
|
|
1107
|
-
console.log(`\n
|
|
1399
|
+
console.log(`\n[ERR] Error en simulación: ${result.error}`);
|
|
1108
1400
|
}
|
|
1109
1401
|
|
|
1110
1402
|
return result;
|
|
@@ -1169,10 +1461,10 @@ class CertRunner {
|
|
|
1169
1461
|
}
|
|
1170
1462
|
|
|
1171
1463
|
// Verificar si ya pasamos a INTERCAMBIO (simulación ya aprobada)
|
|
1172
|
-
console.log('
|
|
1464
|
+
console.log(' Verificando etapa actual...');
|
|
1173
1465
|
const avance = await this.siiCert.verAvanceParsed();
|
|
1174
1466
|
if (avance.rawHtml && /paso\s*<b>\s*INTERCAMBIO/i.test(avance.rawHtml)) {
|
|
1175
|
-
console.log('
|
|
1467
|
+
console.log(' [OK] Simulación ya aprobada - empresa en etapa INTERCAMBIO');
|
|
1176
1468
|
return { success: true, skipped: true, message: 'Ya en etapa INTERCAMBIO' };
|
|
1177
1469
|
}
|
|
1178
1470
|
|
|
@@ -1185,7 +1477,7 @@ class CertRunner {
|
|
|
1185
1477
|
};
|
|
1186
1478
|
|
|
1187
1479
|
const result = await this._declararConReintentos(sets, 'declaracion-simulacion-response', { maxIntentos, intervalo, label: 'simulación' });
|
|
1188
|
-
if (result?.success) console.log('
|
|
1480
|
+
if (result?.success) console.log(' [OK] Simulación declarada exitosamente');
|
|
1189
1481
|
return result;
|
|
1190
1482
|
}
|
|
1191
1483
|
|
|
@@ -1197,41 +1489,41 @@ class CertRunner {
|
|
|
1197
1489
|
async esperarSimulacionAprobada(options = {}) {
|
|
1198
1490
|
const { maxIntentos = 30, intervalo = 10000 } = options;
|
|
1199
1491
|
|
|
1200
|
-
console.log('\n
|
|
1492
|
+
console.log('\n[...] Esperando aprobación de simulación...');
|
|
1201
1493
|
|
|
1202
1494
|
for (let i = 1; i <= maxIntentos; i++) {
|
|
1203
|
-
console.log(`\n
|
|
1495
|
+
console.log(`\n [...] Intento ${i}/${maxIntentos}...`);
|
|
1204
1496
|
|
|
1205
1497
|
const avance = await this.siiCert.verAvanceParsed();
|
|
1206
1498
|
|
|
1207
1499
|
if (!avance.success) {
|
|
1208
|
-
console.log(`
|
|
1500
|
+
console.log(` [!] Error consultando avance: ${avance.error}`);
|
|
1209
1501
|
await sleep(intervalo);
|
|
1210
1502
|
continue;
|
|
1211
1503
|
}
|
|
1212
1504
|
|
|
1213
|
-
//
|
|
1505
|
+
// [OK] PRIMERO: Verificar si ya pasó a INTERCAMBIO (significa que simulación fue aprobada)
|
|
1214
1506
|
if (avance.etapaActual && avance.etapaActual.includes('INTERCAMBIO')) {
|
|
1215
|
-
console.log(`
|
|
1216
|
-
console.log('\n
|
|
1507
|
+
console.log(` [OK] Etapa actual: ${avance.etapaActual}`);
|
|
1508
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Empresa pasó a etapa INTERCAMBIO.');
|
|
1217
1509
|
return { success: true, etapa: 'INTERCAMBIO' };
|
|
1218
1510
|
}
|
|
1219
1511
|
|
|
1220
|
-
//
|
|
1512
|
+
// [OK] TAMBIÉN: Etapas que vienen DESPUÉS de INTERCAMBIO (simulación + intercambio ya completos)
|
|
1221
1513
|
const ETAPAS_POST_INTERCAMBIO = ['DOCUMENTOS IMPRESOS', 'MUESTRAS IMPRESAS', 'BOLETA', 'AUTORIZADO', 'COMPLETADO'];
|
|
1222
1514
|
if (avance.etapaActual && ETAPAS_POST_INTERCAMBIO.some(e => avance.etapaActual.toUpperCase().includes(e))) {
|
|
1223
|
-
console.log(`
|
|
1224
|
-
console.log('\n
|
|
1515
|
+
console.log(` Etapa actual: ${avance.etapaActual}`);
|
|
1516
|
+
console.log('\n ¡SIMULACIÓN + INTERCAMBIO COMPLETADOS! Empresa en etapa: ' + avance.etapaActual);
|
|
1225
1517
|
return { success: true, etapa: avance.etapaActual, postIntercambio: true };
|
|
1226
1518
|
}
|
|
1227
1519
|
|
|
1228
|
-
//
|
|
1520
|
+
// [OK] SEGUNDO: Verificar indicador de formulario de confirmación (simulación aprobada pendiente confirmar)
|
|
1229
1521
|
if (avance.simulacionAprobadaIndicador) {
|
|
1230
|
-
console.log(`
|
|
1522
|
+
console.log(` [OK] Formulario de confirmación detectado`);
|
|
1231
1523
|
|
|
1232
1524
|
// Confirmar automáticamente la simulación
|
|
1233
1525
|
if (this.resultados.simulacion?.trackId) {
|
|
1234
|
-
console.log(`\n
|
|
1526
|
+
console.log(`\n Confirmando revisión de simulación (TrackId: ${this.resultados.simulacion.trackId})...`);
|
|
1235
1527
|
|
|
1236
1528
|
const fecha = this._getFechaHoy();
|
|
1237
1529
|
const confirmResult = await this.siiCert.declararAvance({
|
|
@@ -1244,7 +1536,7 @@ class CertRunner {
|
|
|
1244
1536
|
});
|
|
1245
1537
|
|
|
1246
1538
|
if (confirmResult.success) {
|
|
1247
|
-
console.log('
|
|
1539
|
+
console.log(' [OK] Confirmación enviada exitosamente');
|
|
1248
1540
|
|
|
1249
1541
|
// Revalidar contra SII para evitar falso positivo de confirmación
|
|
1250
1542
|
const verificacion = await this.siiCert.verAvanceParsed();
|
|
@@ -1254,19 +1546,19 @@ class CertRunner {
|
|
|
1254
1546
|
const simConforme = Boolean(estadoSim?.esConforme || estadoSim?.estado?.toUpperCase()?.includes('REVISADO CONFORME'));
|
|
1255
1547
|
|
|
1256
1548
|
if (yaIntercambio || simConforme || !sigueFormulario) {
|
|
1257
|
-
console.log('\n
|
|
1549
|
+
console.log('\n ¡SIMULACIÓN CONFIRMADA! Certificación completa.');
|
|
1258
1550
|
return { success: true, confirmada: true };
|
|
1259
1551
|
}
|
|
1260
1552
|
|
|
1261
|
-
console.log('
|
|
1553
|
+
console.log(' [!] SII aún mantiene formulario de simulación pendiente; se reintentará...');
|
|
1262
1554
|
await sleep(intervalo);
|
|
1263
1555
|
continue;
|
|
1264
1556
|
} else {
|
|
1265
|
-
console.log(`
|
|
1557
|
+
console.log(` [!] Error en confirmación: ${confirmResult.error}`);
|
|
1266
1558
|
// Continuar el loop para reintentar
|
|
1267
1559
|
}
|
|
1268
1560
|
} else {
|
|
1269
|
-
console.log('\n
|
|
1561
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Lista para confirmar revisión.');
|
|
1270
1562
|
return { success: true, pendienteConfirmar: true };
|
|
1271
1563
|
}
|
|
1272
1564
|
}
|
|
@@ -1287,28 +1579,28 @@ class CertRunner {
|
|
|
1287
1579
|
simEstado.estado?.toUpperCase().includes('REPARO');
|
|
1288
1580
|
|
|
1289
1581
|
if (esAprobado) {
|
|
1290
|
-
console.log(`
|
|
1291
|
-
console.log('\n
|
|
1582
|
+
console.log(` [OK] SIMULACIÓN: REVISADO CONFORME`);
|
|
1583
|
+
console.log('\n ¡SIMULACIÓN APROBADA! Certificación completa.');
|
|
1292
1584
|
return { success: true };
|
|
1293
1585
|
} else if (esRechazado) {
|
|
1294
|
-
console.log(`
|
|
1586
|
+
console.log(` [ERR] SIMULACIÓN: ${simEstado.estado}`);
|
|
1295
1587
|
return { success: false, error: 'Simulación rechazada' };
|
|
1296
1588
|
} else {
|
|
1297
|
-
console.log(`
|
|
1589
|
+
console.log(` [...] SIMULACIÓN: ${simEstado.estado || 'EN REVISION'}`);
|
|
1298
1590
|
}
|
|
1299
1591
|
} else {
|
|
1300
1592
|
// No hay estado de simulación, pero verificar etapa actual
|
|
1301
1593
|
if (avance.etapaActual) {
|
|
1302
|
-
console.log(`
|
|
1594
|
+
console.log(` Etapa actual: ${avance.etapaActual}`);
|
|
1303
1595
|
} else {
|
|
1304
|
-
console.log('
|
|
1596
|
+
console.log(' [...] Simulación aún no registrada...');
|
|
1305
1597
|
}
|
|
1306
1598
|
}
|
|
1307
1599
|
|
|
1308
1600
|
await sleep(intervalo);
|
|
1309
1601
|
}
|
|
1310
1602
|
|
|
1311
|
-
console.log('\n
|
|
1603
|
+
console.log('\n [!] Timeout esperando aprobación de simulación');
|
|
1312
1604
|
return { success: false, error: 'Timeout esperando aprobación' };
|
|
1313
1605
|
}
|
|
1314
1606
|
|
|
@@ -1339,7 +1631,7 @@ class CertRunner {
|
|
|
1339
1631
|
fs.mkdirSync(intercambioDir, { recursive: true });
|
|
1340
1632
|
|
|
1341
1633
|
console.log('\n' + '═'.repeat(60));
|
|
1342
|
-
console.log('
|
|
1634
|
+
console.log('FASE 7: INTERCAMBIO DE INFORMACIÓN');
|
|
1343
1635
|
console.log('═'.repeat(60));
|
|
1344
1636
|
|
|
1345
1637
|
// ── PASO 1: Obtener el SET XML ─────────────────────────────
|
|
@@ -1352,35 +1644,35 @@ class CertRunner {
|
|
|
1352
1644
|
const setDownloadPath = path.join(intercambioDir, 'set-intercambio.xml');
|
|
1353
1645
|
|
|
1354
1646
|
if (setInputPath && fs.existsSync(setInputPath)) {
|
|
1355
|
-
console.log(`\
|
|
1647
|
+
console.log(`\nLeyendo SET desde: ${setInputPath}`);
|
|
1356
1648
|
setXml = fs.readFileSync(setInputPath, 'utf8');
|
|
1357
|
-
console.log(`
|
|
1649
|
+
console.log(` ✓ ${setXml.length} bytes`);
|
|
1358
1650
|
} else if (fs.existsSync(setDownloadPath)) {
|
|
1359
|
-
console.log(`\
|
|
1651
|
+
console.log(`\nLeyendo SET guardado: ${setDownloadPath}`);
|
|
1360
1652
|
setXml = fs.readFileSync(setDownloadPath, 'utf8');
|
|
1361
|
-
console.log(`
|
|
1653
|
+
console.log(` ✓ ${setXml.length} bytes`);
|
|
1362
1654
|
} else {
|
|
1363
|
-
console.log('\
|
|
1655
|
+
console.log('\nDescargando SET desde www4.sii.cl/pfeInternet...');
|
|
1364
1656
|
const dl = await this._descargarSetPfeInternet(intercambioDir);
|
|
1365
1657
|
if (dl.success) {
|
|
1366
1658
|
setXml = dl.xml;
|
|
1367
1659
|
fs.writeFileSync(setDownloadPath, setXml, 'utf8');
|
|
1368
|
-
console.log(`
|
|
1660
|
+
console.log(` [OK] SET descargado (${setXml.length} bytes) → ${setDownloadPath}`);
|
|
1369
1661
|
} else {
|
|
1370
|
-
console.log(`
|
|
1662
|
+
console.log(` [!] No se pudo descargar: ${dl.error}`);
|
|
1371
1663
|
console.log('\n' + '─'.repeat(60));
|
|
1372
|
-
console.log('
|
|
1373
|
-
console.log('
|
|
1374
|
-
console.log('
|
|
1375
|
-
console.log(`
|
|
1376
|
-
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');
|
|
1377
1669
|
console.log('─'.repeat(60));
|
|
1378
1670
|
return { success: false, error: 'SET no disponible - descarga manual requerida', requiresManual: true, manualPath: setInputPath };
|
|
1379
1671
|
}
|
|
1380
1672
|
}
|
|
1381
1673
|
|
|
1382
1674
|
// ── PASO 2: Generar XMLs de respuesta ─────────────────────
|
|
1383
|
-
console.log('\
|
|
1675
|
+
console.log('\nGenerando respuestas firmadas...');
|
|
1384
1676
|
const intercambioCert = new IntercambioCert({
|
|
1385
1677
|
certificado: this.certificado,
|
|
1386
1678
|
emisor: {
|
|
@@ -1397,7 +1689,7 @@ class CertRunner {
|
|
|
1397
1689
|
}
|
|
1398
1690
|
|
|
1399
1691
|
// ── PASO 3: Subir respuestas ───────────────────────────────
|
|
1400
|
-
console.log('\
|
|
1692
|
+
console.log('\nSubiendo respuestas a www4.sii.cl/pfeInternet...');
|
|
1401
1693
|
const uploadResult = await this._subirRespuestasPfeInternet({
|
|
1402
1694
|
recepcionXml: fs.readFileSync(genResult.files.recepcion, 'utf8'),
|
|
1403
1695
|
aprobacionXml: fs.readFileSync(genResult.files.aprobacion, 'utf8'),
|
|
@@ -1407,17 +1699,17 @@ class CertRunner {
|
|
|
1407
1699
|
|
|
1408
1700
|
if (uploadResult.success) {
|
|
1409
1701
|
console.log('\n' + '═'.repeat(60));
|
|
1410
|
-
console.log('
|
|
1702
|
+
console.log('[OK] INTERCAMBIO COMPLETADO');
|
|
1411
1703
|
console.log('═'.repeat(60));
|
|
1412
|
-
if (uploadResult.resultado) console.log(`
|
|
1704
|
+
if (uploadResult.resultado) console.log(` Resultado SII: ${uploadResult.resultado}`);
|
|
1413
1705
|
} else {
|
|
1414
|
-
console.log(`
|
|
1706
|
+
console.log(` [!] No se pudo subir automáticamente: ${uploadResult.error}`);
|
|
1415
1707
|
console.log('\n' + '─'.repeat(60));
|
|
1416
|
-
console.log('
|
|
1417
|
-
console.log('
|
|
1418
|
-
console.log(`
|
|
1419
|
-
console.log(`
|
|
1420
|
-
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}`);
|
|
1421
1713
|
console.log('─'.repeat(60));
|
|
1422
1714
|
}
|
|
1423
1715
|
|
|
@@ -1439,7 +1731,7 @@ class CertRunner {
|
|
|
1439
1731
|
*/
|
|
1440
1732
|
async _obtenerCookiesSII() {
|
|
1441
1733
|
if (this._siiCookieJar) {
|
|
1442
|
-
console.log('[SII Auth]
|
|
1734
|
+
console.log('[SII Auth] Reutilizando sesión SII en memoria');
|
|
1443
1735
|
return this._siiCookieJar;
|
|
1444
1736
|
}
|
|
1445
1737
|
const SiiPortalAuth = require('../SiiPortalAuth');
|
|
@@ -1448,7 +1740,7 @@ class CertRunner {
|
|
|
1448
1740
|
const siiAuth = new SiiPortalAuth({ pfxBuffer, pfxPassword: password });
|
|
1449
1741
|
this._siiCookieJar = await siiAuth.autenticar();
|
|
1450
1742
|
const nSession = Object.keys(this._siiCookieJar).filter(k => k.startsWith('NETSCAPE')).length;
|
|
1451
|
-
console.log(`[SII Auth]
|
|
1743
|
+
console.log(`[SII Auth] [OK] Sesión SII activa (cookies NETSCAPE: ${nSession})`);
|
|
1452
1744
|
return this._siiCookieJar;
|
|
1453
1745
|
}
|
|
1454
1746
|
|
|
@@ -1547,7 +1839,7 @@ class CertRunner {
|
|
|
1547
1839
|
const boundary = `----WebKitFormBoundary${Date.now()}`;
|
|
1548
1840
|
const emptyMultipartBody = `--${boundary}--\r\n`;
|
|
1549
1841
|
|
|
1550
|
-
console.log(`
|
|
1842
|
+
console.log(` → Descargando SET desde pfeInternet/downloadFile (RUT ${rutNum}-${dv})...`);
|
|
1551
1843
|
|
|
1552
1844
|
const r = await makeReq(
|
|
1553
1845
|
`https://www4.sii.cl/pfeInternet/downloadFile?re=${rutNum}&dve=${dv}`,
|
|
@@ -1571,12 +1863,12 @@ class CertRunner {
|
|
|
1571
1863
|
r.body.includes('<SetDTE') ||
|
|
1572
1864
|
r.body.includes('<?xml')
|
|
1573
1865
|
)) {
|
|
1574
|
-
console.log(`
|
|
1866
|
+
console.log(` ✓ SET descargado correctamente (${r.body.length} bytes)`);
|
|
1575
1867
|
return { success: true, xml: r.body };
|
|
1576
1868
|
}
|
|
1577
1869
|
|
|
1578
1870
|
const errMsg = `pfeInternet/downloadFile respondió HTTP ${r.status} sin XML válido`;
|
|
1579
|
-
console.log(`
|
|
1871
|
+
console.log(` [ERR] ${errMsg}`);
|
|
1580
1872
|
fs.writeFileSync(path.join(debugDir, `pfe-download-error-${Date.now()}.html`), r.body, 'utf8');
|
|
1581
1873
|
return { success: false, error: errMsg };
|
|
1582
1874
|
} catch (err) {
|
|
@@ -1599,8 +1891,8 @@ class CertRunner {
|
|
|
1599
1891
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
1600
1892
|
// Los labels deben coincidir con el texto del portal GWT (Archivo N: ...)
|
|
1601
1893
|
const archivos = [
|
|
1602
|
-
{ label: 'Respuesta de Intercambio',
|
|
1603
|
-
{ 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 },
|
|
1604
1896
|
{ label: 'Resultado Aprobaci\u00f3n Comercial de Documento', filename: 'respuesta-aprobacion-comercial.xml', content: aprobacionXml, uploadN: 3 },
|
|
1605
1897
|
];
|
|
1606
1898
|
for (const a of archivos) {
|
|
@@ -1627,7 +1919,7 @@ class CertRunner {
|
|
|
1627
1919
|
await page.setCookie(...puppeteerCookies);
|
|
1628
1920
|
|
|
1629
1921
|
// Navegar al portal pfeInternet
|
|
1630
|
-
console.log('
|
|
1922
|
+
console.log(' → Cargando portal pfeInternet...');
|
|
1631
1923
|
await page.goto('https://www4.sii.cl/pfeInternet/', {
|
|
1632
1924
|
waitUntil: 'networkidle2',
|
|
1633
1925
|
timeout: 60000,
|
|
@@ -1638,7 +1930,7 @@ class CertRunner {
|
|
|
1638
1930
|
|
|
1639
1931
|
// Hacer click en el enlace "Subir archivos XML de respuesta de Intercambio"
|
|
1640
1932
|
// El href es javascript:openForm('opt-ingresoEmpresaUp') — necesita click real para GWT
|
|
1641
|
-
console.log('
|
|
1933
|
+
console.log(' → Clickeando "Subir archivos XML de respuesta de Intercambio"...');
|
|
1642
1934
|
const linkClicked = await page.click('a[href*="ingresoEmpresaUp"]').then(() => true).catch(() => false);
|
|
1643
1935
|
if (!linkClicked) {
|
|
1644
1936
|
// Fallback: evaluar click con dispatchEvent
|
|
@@ -1657,7 +1949,7 @@ class CertRunner {
|
|
|
1657
1949
|
if (rutInput) {
|
|
1658
1950
|
const [rutNum, dv] = this.config.emisor.rut.split('-');
|
|
1659
1951
|
const rutConDv = `${rutNum}-${dv}`;
|
|
1660
|
-
console.log(`
|
|
1952
|
+
console.log(` → Ingresando RUT empresa: ${rutConDv}`);
|
|
1661
1953
|
await rutInput.click({ clickCount: 3 }); // seleccionar todo
|
|
1662
1954
|
await rutInput.type(rutConDv);
|
|
1663
1955
|
|
|
@@ -1668,7 +1960,7 @@ class CertRunner {
|
|
|
1668
1960
|
});
|
|
1669
1961
|
if (confirmBtn) {
|
|
1670
1962
|
await confirmBtn.asElement().click();
|
|
1671
|
-
console.log('
|
|
1963
|
+
console.log(' → Click "Confirmar Empresa", esperando formulario de upload...');
|
|
1672
1964
|
await page.waitForNetworkIdle({ timeout: 15000, idleTime: 1000 }).catch(() => {});
|
|
1673
1965
|
}
|
|
1674
1966
|
}
|
|
@@ -1686,7 +1978,7 @@ class CertRunner {
|
|
|
1686
1978
|
}
|
|
1687
1979
|
throw new Error('pfeInternet no mostró formulario de upload tras openForm — ver pfeInternet-error.png/.html');
|
|
1688
1980
|
}
|
|
1689
|
-
console.log('
|
|
1981
|
+
console.log(' → Formulario de upload listo');
|
|
1690
1982
|
|
|
1691
1983
|
// ── DEBUG: screenshot del formulario con los inputs listos ──
|
|
1692
1984
|
if (debugDir) {
|
|
@@ -1722,11 +2014,11 @@ class CertRunner {
|
|
|
1722
2014
|
}, archivo.label);
|
|
1723
2015
|
|
|
1724
2016
|
if (yaProcessado) {
|
|
1725
|
-
console.log(`
|
|
2017
|
+
console.log(` → ${archivo.filename}: ya procesado anteriormente, saltando...`);
|
|
1726
2018
|
continue;
|
|
1727
2019
|
}
|
|
1728
2020
|
|
|
1729
|
-
console.log(`
|
|
2021
|
+
console.log(` → Subiendo ${archivo.filename}...`);
|
|
1730
2022
|
|
|
1731
2023
|
// Cada archivo tiene su propio form con action uploadFile1/2/3
|
|
1732
2024
|
// Usamos el selector específico para no confundir entre los 3 inputs que pueden
|
|
@@ -1745,7 +2037,7 @@ class CertRunner {
|
|
|
1745
2037
|
// Esperar el diálogo GWT de confirmación
|
|
1746
2038
|
await page.waitForSelector('.gwt-DialogBox .msgeDialogBox', { timeout: 30000 });
|
|
1747
2039
|
const msgText = await page.$eval('.gwt-DialogBox .msgeDialogBox', el => el.textContent.trim());
|
|
1748
|
-
console.log(`
|
|
2040
|
+
console.log(` ✓ ${msgText}`);
|
|
1749
2041
|
|
|
1750
2042
|
if (debugDir) {
|
|
1751
2043
|
fs.writeFileSync(
|
|
@@ -1841,16 +2133,16 @@ class CertRunner {
|
|
|
1841
2133
|
const t = (document.body.textContent || '').toUpperCase();
|
|
1842
2134
|
return t.includes('ESTADO DE LA REVISI') ||
|
|
1843
2135
|
t.includes('POR REVISAR') || t.includes('APROBADO') ||
|
|
1844
|
-
t.includes('EN REVISI')
|
|
2136
|
+
t.includes('EN REVISI') || t.includes('RECHAZADO');
|
|
1845
2137
|
}, { timeout: 8000, polling: 500 }).catch(() => {});
|
|
1846
2138
|
|
|
1847
2139
|
const estado = await page.evaluate(() => {
|
|
1848
2140
|
const t = (document.body.textContent || '').toUpperCase();
|
|
1849
|
-
if (t.includes('APROBADO'))
|
|
1850
|
-
if (t.includes('POR REVISAR'))
|
|
1851
|
-
if (t.includes('EN REVISI'))
|
|
1852
|
-
if (t.includes('RECHAZADO'))
|
|
1853
|
-
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';
|
|
1854
2146
|
return null;
|
|
1855
2147
|
}).catch(() => null);
|
|
1856
2148
|
|
|
@@ -1881,7 +2173,7 @@ class CertRunner {
|
|
|
1881
2173
|
if (!pdfPaths.length) throw new Error(`No se encontraron PDFs en: ${pdfDir}`);
|
|
1882
2174
|
|
|
1883
2175
|
console.log('\n' + '═'.repeat(60));
|
|
1884
|
-
console.log(
|
|
2176
|
+
console.log(`FASE 8: MUESTRAS IMPRESAS (${pdfPaths.length} PDFs)`);
|
|
1885
2177
|
console.log('═'.repeat(60));
|
|
1886
2178
|
|
|
1887
2179
|
return this._subirMuestrasImpresasPortal({ pdfPaths, debugDir: pdfDir });
|
|
@@ -1922,7 +2214,7 @@ class CertRunner {
|
|
|
1922
2214
|
await page.setCookie(...puppeteerCookies);
|
|
1923
2215
|
|
|
1924
2216
|
// Navegar directamente a www4.sii.cl/pdfdteInternet/ con las cookies de sesión SII
|
|
1925
|
-
console.log('
|
|
2217
|
+
console.log(' → Cargando portal pdfdteInternet...');
|
|
1926
2218
|
await page.goto('https://www4.sii.cl/pdfdteInternet/', {
|
|
1927
2219
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
1928
2220
|
});
|
|
@@ -1939,7 +2231,7 @@ class CertRunner {
|
|
|
1939
2231
|
}
|
|
1940
2232
|
throw new Error('pdfdteInternet: no se encontraron campos de RUT (¿sesión expirada?)');
|
|
1941
2233
|
}
|
|
1942
|
-
console.log(`
|
|
2234
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
1943
2235
|
await rutInputs[0].click({ clickCount: 3 }); await rutInputs[0].type(rutNum);
|
|
1944
2236
|
await dvInputs[0].click({ clickCount: 3 }); await dvInputs[0].type(dvChar);
|
|
1945
2237
|
await clickBoton(page, 'Rut');
|
|
@@ -1952,7 +2244,7 @@ class CertRunner {
|
|
|
1952
2244
|
return !!(dlg && dlg.offsetParent !== null);
|
|
1953
2245
|
});
|
|
1954
2246
|
if (hayDialog) {
|
|
1955
|
-
console.log('
|
|
2247
|
+
console.log(' → Diálogo de revisión existente → haciendo click en "Sí"');
|
|
1956
2248
|
const clicked = await page.evaluate(() => {
|
|
1957
2249
|
const si = Array.from(document.querySelectorAll('button.x-btn-text'))
|
|
1958
2250
|
.find(b => /^s[ií]$/i.test(b.textContent.trim()));
|
|
@@ -1973,7 +2265,7 @@ class CertRunner {
|
|
|
1973
2265
|
const dvNow = await page.$$('input[name="dv"]');
|
|
1974
2266
|
const pRut = rutNow.length >= 2 ? rutNow[1] : rutNow[0];
|
|
1975
2267
|
const pDv = dvNow.length >= 2 ? dvNow[1] : dvNow[0];
|
|
1976
|
-
console.log(`
|
|
2268
|
+
console.log(` → Ingresando RUT proveedor: ${rutNum}-${dvChar}`);
|
|
1977
2269
|
await pRut.click({ clickCount: 3 }); await pRut.type(rutNum);
|
|
1978
2270
|
await pDv.click({ clickCount: 3 }); await pDv.type(dvChar);
|
|
1979
2271
|
await clickBoton(page, 'Consultar');
|
|
@@ -1983,20 +2275,20 @@ class CertRunner {
|
|
|
1983
2275
|
// ── Re-ejecución: detectar estado terminal antes de proceder ──
|
|
1984
2276
|
const _estadoYaSubido = await page.evaluate(() => {
|
|
1985
2277
|
const t = (document.body.textContent || '').toUpperCase();
|
|
1986
|
-
if (t.includes('APROBADO'))
|
|
1987
|
-
if (t.includes('POR REVISAR'))
|
|
1988
|
-
if (t.includes('EN REVISI'))
|
|
1989
|
-
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';
|
|
1990
2282
|
if (t.includes('ENVIADO AL SII')) return 'ENVIADO AL SII';
|
|
1991
2283
|
return null;
|
|
1992
2284
|
}).catch(() => null);
|
|
1993
2285
|
if (_estadoYaSubido) {
|
|
1994
|
-
console.log(`
|
|
2286
|
+
console.log(` [OK] Portal ya muestra estado "${_estadoYaSubido}" — muestras subidas previamente. Proceso completado.`);
|
|
1995
2287
|
return { success: true, alreadyCompleted: true, estado: _estadoYaSubido };
|
|
1996
2288
|
}
|
|
1997
2289
|
|
|
1998
2290
|
// Paso 4: "Crear" → habilita el input de archivo
|
|
1999
|
-
console.log('
|
|
2291
|
+
console.log(' → Click "Crear"...');
|
|
2000
2292
|
await clickBoton(page, 'Crear');
|
|
2001
2293
|
await new Promise(r => setTimeout(r, 2500));
|
|
2002
2294
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-04-after-crear.png'), fullPage: true }).catch(() => {});
|
|
@@ -2056,7 +2348,7 @@ class CertRunner {
|
|
|
2056
2348
|
// drop → por cada file: submit form al iframe → respuesta → leeImpresoById → tick verde
|
|
2057
2349
|
// Esto evita la re-navegación entre archivos y garantiza que la validación
|
|
2058
2350
|
// (Timbre/CAF/TED) ocurra antes de salir de la página.
|
|
2059
|
-
console.log(`
|
|
2351
|
+
console.log(` → Cargando ${pdfPaths.length} PDFs para drop en el portal...`);
|
|
2060
2352
|
const _fileDataList = pdfPaths.map(p => ({
|
|
2061
2353
|
name: path.basename(p),
|
|
2062
2354
|
b64: fs.readFileSync(p).toString('base64'),
|
|
@@ -2064,7 +2356,7 @@ class CertRunner {
|
|
|
2064
2356
|
|
|
2065
2357
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-04b-antes-drop.png'), fullPage: true }).catch(() => {});
|
|
2066
2358
|
|
|
2067
|
-
console.log(`
|
|
2359
|
+
console.log(` → Ejecutando drop de ${pdfPaths.length} PDFs sobre el portal...`);
|
|
2068
2360
|
const _dropped = await page.evaluate((files) => {
|
|
2069
2361
|
const dt = new DataTransfer();
|
|
2070
2362
|
for (const f of files) {
|
|
@@ -2082,7 +2374,7 @@ class CertRunner {
|
|
|
2082
2374
|
}, _fileDataList);
|
|
2083
2375
|
|
|
2084
2376
|
if (_dropped === 0) throw new Error('pdfdteInternet: drop zone no encontrado (.dropFilesLabel)');
|
|
2085
|
-
console.log(`
|
|
2377
|
+
console.log(` → Drop ejecutado (${_dropped} archivos). Esperando procesamiento...`);
|
|
2086
2378
|
|
|
2087
2379
|
// ── Fase 1: esperar hasta 45s por primera señal de progreso o estado terminal ──
|
|
2088
2380
|
// Si el portal ya está en "POR REVISAR" (re-ejecución), lo detectamos aquí inmediatamente.
|
|
@@ -2106,21 +2398,21 @@ class CertRunner {
|
|
|
2106
2398
|
}
|
|
2107
2399
|
const t = (document.body.textContent || '').toUpperCase();
|
|
2108
2400
|
let estado = null;
|
|
2109
|
-
if (t.includes('APROBADO'))
|
|
2401
|
+
if (t.includes('APROBADO')) estado = 'APROBADO';
|
|
2110
2402
|
else if (t.includes('POR REVISAR')) estado = 'POR REVISAR';
|
|
2111
|
-
else if (t.includes('EN REVISI'))
|
|
2112
|
-
else if (t.includes('RECHAZADO'))
|
|
2403
|
+
else if (t.includes('EN REVISI')) estado = 'EN REVISIÓN';
|
|
2404
|
+
else if (t.includes('RECHAZADO')) estado = 'RECHAZADO';
|
|
2113
2405
|
return { procesados, estado };
|
|
2114
2406
|
}).catch(() => ({ procesados: 0, estado: null }));
|
|
2115
2407
|
|
|
2116
2408
|
if (_fase1.estado) {
|
|
2117
|
-
console.log(`
|
|
2409
|
+
console.log(` [OK] Portal en estado "${_fase1.estado}" — muestras ya procesadas previamente.`);
|
|
2118
2410
|
return { success: true, alreadyCompleted: true, estado: _fase1.estado };
|
|
2119
2411
|
}
|
|
2120
2412
|
|
|
2121
2413
|
if (_fase1.procesados === 0) {
|
|
2122
2414
|
// Sin progreso y sin estado terminal: el portal puede no estar procesando
|
|
2123
|
-
console.warn('
|
|
2415
|
+
console.warn(' [!] Sin progreso en 45s y sin estado terminal. Continuando al paso siguiente...');
|
|
2124
2416
|
} else {
|
|
2125
2417
|
// ── Fase 2: progreso iniciado — esperar al total ──
|
|
2126
2418
|
await page.waitForFunction((total) => {
|
|
@@ -2137,7 +2429,7 @@ class CertRunner {
|
|
|
2137
2429
|
}
|
|
2138
2430
|
return 0;
|
|
2139
2431
|
}).catch(() => 0);
|
|
2140
|
-
console.warn(`
|
|
2432
|
+
console.warn(` [!] Timeout: solo se procesaron ${procesados}/${pdfPaths.length} antes del timeout`);
|
|
2141
2433
|
});
|
|
2142
2434
|
}
|
|
2143
2435
|
|
|
@@ -2148,7 +2440,7 @@ class CertRunner {
|
|
|
2148
2440
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-05-archivos-listos.png'), fullPage: true }).catch(() => {});
|
|
2149
2441
|
|
|
2150
2442
|
// Paso 6: re-navegar al estado limpio y Enviar al SII
|
|
2151
|
-
console.log('
|
|
2443
|
+
console.log(' → Re-navegando para "Enviar al SII"...');
|
|
2152
2444
|
await navegarAlFormulario();
|
|
2153
2445
|
|
|
2154
2446
|
// Esperar a que el botón esté habilitado (aria-disabled="false")
|
|
@@ -2160,7 +2452,7 @@ class CertRunner {
|
|
|
2160
2452
|
|
|
2161
2453
|
if (debugDir) await page.screenshot({ path: path.join(debugDir, 'pdfte-05b-antes-enviar.png'), fullPage: true }).catch(() => {});
|
|
2162
2454
|
|
|
2163
|
-
console.log('
|
|
2455
|
+
console.log(' → Click "Enviar al SII"...');
|
|
2164
2456
|
const enviado = await clickBoton(page, 'Enviar al SII');
|
|
2165
2457
|
if (!enviado) throw new Error('pdfdteInternet: botón "Enviar al SII" no disponible o deshabilitado');
|
|
2166
2458
|
|
|
@@ -2173,7 +2465,7 @@ class CertRunner {
|
|
|
2173
2465
|
|
|
2174
2466
|
const pageText = await page.$eval('body', el => el.textContent).catch(() => '');
|
|
2175
2467
|
const exitoso = /revision.*creada|solicitud.*enviada|documentos.*enviados|fue.*enviado|[eé]xito/i.test(pageText);
|
|
2176
|
-
console.log(`
|
|
2468
|
+
console.log(` → Resultado: ${exitoso ? '[OK] enviado correctamente' : '[!] sin confirmación explícita'}`);
|
|
2177
2469
|
return { success: exitoso, pageText: pageText.substring(0, 800) };
|
|
2178
2470
|
|
|
2179
2471
|
} catch (err) {
|
|
@@ -2216,9 +2508,9 @@ class CertRunner {
|
|
|
2216
2508
|
});
|
|
2217
2509
|
const page = await browser.newPage();
|
|
2218
2510
|
await page.setCookie(...puppeteerCookies);
|
|
2219
|
-
page.on('dialog', async dlg => { console.log(`
|
|
2511
|
+
page.on('dialog', async dlg => { console.log(` → [dialog SET=1] ${dlg.message()}`); await dlg.accept(); });
|
|
2220
2512
|
|
|
2221
|
-
console.log('
|
|
2513
|
+
console.log(' → Cargando portal certBolElectDteInternet (SET=1)...');
|
|
2222
2514
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/?SET=1', {
|
|
2223
2515
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2224
2516
|
});
|
|
@@ -2228,7 +2520,7 @@ class CertRunner {
|
|
|
2228
2520
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2229
2521
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2230
2522
|
if (!rutInput) throw new Error('certBolElectDteInternet/?SET=1: no se encontró campo RUT');
|
|
2231
|
-
console.log(`
|
|
2523
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
2232
2524
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutNum);
|
|
2233
2525
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(dvChar);
|
|
2234
2526
|
await page.evaluate(() => {
|
|
@@ -2236,7 +2528,7 @@ class CertRunner {
|
|
|
2236
2528
|
.find(b => /confirmar/i.test(b.textContent));
|
|
2237
2529
|
if (btn) btn.click();
|
|
2238
2530
|
});
|
|
2239
|
-
console.log('
|
|
2531
|
+
console.log(' → Click "Confirmar Empresa"');
|
|
2240
2532
|
|
|
2241
2533
|
// Esperar checkboxes — GWT dispara ~8-10 POST /facade en paralelo
|
|
2242
2534
|
await page.waitForNetworkIdle({ idleTime: 800, timeout: 40000 }).catch(() => {});
|
|
@@ -2251,7 +2543,7 @@ class CertRunner {
|
|
|
2251
2543
|
cbs.forEach(cb => { if (!cb.checked) cb.click(); });
|
|
2252
2544
|
return cbs.length;
|
|
2253
2545
|
});
|
|
2254
|
-
console.log(`
|
|
2546
|
+
console.log(` → ${nCbs} checkbox(es) marcados`);
|
|
2255
2547
|
await new Promise(r => setTimeout(r, 300));
|
|
2256
2548
|
|
|
2257
2549
|
// Paso 3: Rellenar correo proveedor
|
|
@@ -2266,9 +2558,9 @@ class CertRunner {
|
|
|
2266
2558
|
if (emailInput) {
|
|
2267
2559
|
await emailInput.click({ clickCount: 3 });
|
|
2268
2560
|
await emailInput.type(correoSet);
|
|
2269
|
-
console.log(`
|
|
2561
|
+
console.log(` → Correo proveedor: ${correoSet}`);
|
|
2270
2562
|
} else {
|
|
2271
|
-
console.log('
|
|
2563
|
+
console.log(' [!] No se encontró campo de correo — continuando sin él');
|
|
2272
2564
|
}
|
|
2273
2565
|
|
|
2274
2566
|
// Paso 4: Click "Bajar Nuevo Set" — esperar POST /facade (GWT RPC) y luego
|
|
@@ -2278,7 +2570,7 @@ class CertRunner {
|
|
|
2278
2570
|
// donde rutRepre/dvRepre vienen de las cookies NETSCAPE_LIVEWIRE.rut / .dv
|
|
2279
2571
|
|
|
2280
2572
|
const rutRepreNum = cookieJar['NETSCAPE_LIVEWIRE.rut'] || cookieJar['RUT_NS'] || '';
|
|
2281
|
-
const dvRepreChar = cookieJar['NETSCAPE_LIVEWIRE.dv']
|
|
2573
|
+
const dvRepreChar = cookieJar['NETSCAPE_LIVEWIRE.dv'] || cookieJar['DV_NS'] || '';
|
|
2282
2574
|
if (!rutRepreNum) throw new Error('No se pudo obtener rutRepre de las cookies SII (NETSCAPE_LIVEWIRE.rut)');
|
|
2283
2575
|
|
|
2284
2576
|
// Registrar listener de facade ANTES de hacer click
|
|
@@ -2287,7 +2579,7 @@ class CertRunner {
|
|
|
2287
2579
|
{ timeout: 20000 }
|
|
2288
2580
|
).catch(() => null);
|
|
2289
2581
|
|
|
2290
|
-
console.log('
|
|
2582
|
+
console.log(' → Click "Bajar Nuevo Set" — esperando GWT facade...');
|
|
2291
2583
|
await page.evaluate(() => {
|
|
2292
2584
|
const btn = Array.from(document.querySelectorAll('button'))
|
|
2293
2585
|
.find(b => /bajar/i.test(b.textContent));
|
|
@@ -2306,7 +2598,7 @@ class CertRunner {
|
|
|
2306
2598
|
`&rutRepre=${rutRepreNum}&dvRepre=${dvRepreChar}` +
|
|
2307
2599
|
`&mailProvSw=${encodeURIComponent(correoSet)}`;
|
|
2308
2600
|
|
|
2309
|
-
console.log(`
|
|
2601
|
+
console.log(` → Descargando set directamente: DownloadFileServlet?rutEmpresa=${rutNum}&dvEmpresa=${dvChar}&rutRepre=${rutRepreNum}&dvRepre=${dvRepreChar}&mailProvSw=${correoSet}`);
|
|
2310
2602
|
|
|
2311
2603
|
const setText = await new Promise((resolve, reject) => {
|
|
2312
2604
|
const req = https.get(downloadUrl, {
|
|
@@ -2341,10 +2633,10 @@ class CertRunner {
|
|
|
2341
2633
|
const nodePath = require('path');
|
|
2342
2634
|
fs.mkdirSync(nodePath.dirname(setPath), { recursive: true });
|
|
2343
2635
|
fs.writeFileSync(setPath, setText, 'utf-8');
|
|
2344
|
-
console.log(`
|
|
2636
|
+
console.log(` ✓ Set guardado en: ${setPath}`);
|
|
2345
2637
|
}
|
|
2346
2638
|
|
|
2347
|
-
console.log(`
|
|
2639
|
+
console.log(` ✓ Set de pruebas obtenido (${setText.length} chars)`);
|
|
2348
2640
|
return { success: true, setText };
|
|
2349
2641
|
} catch (err) {
|
|
2350
2642
|
return { success: false, error: err.message };
|
|
@@ -2382,9 +2674,9 @@ class CertRunner {
|
|
|
2382
2674
|
});
|
|
2383
2675
|
const page = await browser.newPage();
|
|
2384
2676
|
await page.setCookie(...puppeteerCookies);
|
|
2385
|
-
page.on('dialog', async dlg => { console.log(`
|
|
2677
|
+
page.on('dialog', async dlg => { console.log(` → [dialog SET=2] ${dlg.message()}`); await dlg.accept(); });
|
|
2386
2678
|
|
|
2387
|
-
console.log('
|
|
2679
|
+
console.log(' → Cargando portal certBolElectDteInternet (SET=2)...');
|
|
2388
2680
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/?SET=2', {
|
|
2389
2681
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2390
2682
|
});
|
|
@@ -2394,7 +2686,7 @@ class CertRunner {
|
|
|
2394
2686
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2395
2687
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2396
2688
|
if (!rutInput) throw new Error('certBolElectDteInternet/?SET=2: no se encontró campo RUT');
|
|
2397
|
-
console.log(`
|
|
2689
|
+
console.log(` → Ingresando RUT empresa: ${rutNum}-${dvChar}`);
|
|
2398
2690
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutNum);
|
|
2399
2691
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(dvChar);
|
|
2400
2692
|
await page.evaluate(() => {
|
|
@@ -2402,7 +2694,7 @@ class CertRunner {
|
|
|
2402
2694
|
.find(b => /confirmar/i.test(b.textContent));
|
|
2403
2695
|
if (btn) btn.click();
|
|
2404
2696
|
});
|
|
2405
|
-
console.log('
|
|
2697
|
+
console.log(' → Click "Confirmar Empresa"');
|
|
2406
2698
|
|
|
2407
2699
|
// Esperar que aparezca el campo "Identificador de Envio" — GWT dispara ~8-10 POST /facade en paralelo
|
|
2408
2700
|
await page.waitForNetworkIdle({ idleTime: 800, timeout: 40000 }).catch(() => {});
|
|
@@ -2414,7 +2706,7 @@ class CertRunner {
|
|
|
2414
2706
|
// Paso 2: Ingresar TrackId
|
|
2415
2707
|
const trackInput = await page.$('input[maxlength="15"]');
|
|
2416
2708
|
if (!trackInput) throw new Error('No se encontró campo "Identificador de Envio" en certBolElectDteInternet/?SET=2');
|
|
2417
|
-
console.log(`
|
|
2709
|
+
console.log(` → Ingresando TrackId: ${trackId}`);
|
|
2418
2710
|
await trackInput.click({ clickCount: 3 });
|
|
2419
2711
|
await trackInput.type(String(trackId));
|
|
2420
2712
|
|
|
@@ -2424,7 +2716,7 @@ class CertRunner {
|
|
|
2424
2716
|
.find(b => /solicitar/i.test(b.textContent));
|
|
2425
2717
|
if (btn) btn.click();
|
|
2426
2718
|
});
|
|
2427
|
-
console.log('
|
|
2719
|
+
console.log(' → Click "Solicitar validación" — esperando respuesta...');
|
|
2428
2720
|
|
|
2429
2721
|
// Esperar respuesta del portal (puede ser confirmación o error)
|
|
2430
2722
|
await page.waitForFunction(() => {
|
|
@@ -2434,7 +2726,7 @@ class CertRunner {
|
|
|
2434
2726
|
}, { timeout: 15000, polling: 500 }).catch(() => {});
|
|
2435
2727
|
|
|
2436
2728
|
const respuesta = await page.evaluate(() => (document.body.innerText || '').trim().substring(0, 500));
|
|
2437
|
-
console.log(`
|
|
2729
|
+
console.log(` ✓ Validación solicitada. Respuesta: ${respuesta.substring(0, 120)}`);
|
|
2438
2730
|
|
|
2439
2731
|
return { success: true, respuesta };
|
|
2440
2732
|
} catch (err) {
|
|
@@ -2486,7 +2778,7 @@ class CertRunner {
|
|
|
2486
2778
|
let dialogMsg = null;
|
|
2487
2779
|
page.on('dialog', async dlg => {
|
|
2488
2780
|
dialogMsg = dlg.message();
|
|
2489
|
-
console.log(`
|
|
2781
|
+
console.log(` → [dialog] ${dialogMsg}`);
|
|
2490
2782
|
await dlg.accept();
|
|
2491
2783
|
});
|
|
2492
2784
|
|
|
@@ -2499,7 +2791,7 @@ class CertRunner {
|
|
|
2499
2791
|
for (let intento = 1; intento <= MAX_INTENTOS; intento++) {
|
|
2500
2792
|
dialogMsg = null; // resetear entre intentos
|
|
2501
2793
|
|
|
2502
|
-
console.log(`
|
|
2794
|
+
console.log(` → Navegando a certBolElectDteInternet (declaración) [intento ${intento}/${MAX_INTENTOS}]...`);
|
|
2503
2795
|
await page.goto('https://www4.sii.cl/certBolElectDteInternet/', {
|
|
2504
2796
|
waitUntil: 'networkidle2', timeout: 60000,
|
|
2505
2797
|
});
|
|
@@ -2509,7 +2801,7 @@ class CertRunner {
|
|
|
2509
2801
|
const rutInput = await page.$('input[maxlength="8"]');
|
|
2510
2802
|
const dvInput = await page.$('input[maxlength="1"]');
|
|
2511
2803
|
if (!rutInput) throw new Error('certBolElectDteInternet/: no se encontró campo RUT empresa');
|
|
2512
|
-
console.log(`
|
|
2804
|
+
console.log(` → Ingresando RUT empresa: ${rutEmpresaRaw}`);
|
|
2513
2805
|
await rutInput.click({ clickCount: 3 }); await rutInput.type(rutEmpNum);
|
|
2514
2806
|
await dvInput.click({ clickCount: 3 }); await dvInput.type(rutEmpDv);
|
|
2515
2807
|
|
|
@@ -2518,7 +2810,7 @@ class CertRunner {
|
|
|
2518
2810
|
.find(b => /confirmar empresa/i.test(b.textContent));
|
|
2519
2811
|
if (btn) btn.click();
|
|
2520
2812
|
});
|
|
2521
|
-
console.log('
|
|
2813
|
+
console.log(' → Click "Confirmar Empresa"...');
|
|
2522
2814
|
|
|
2523
2815
|
// GWT dispara ~8-10 POST /facade EN PARALELO al confirmar.
|
|
2524
2816
|
// Esperar red inactiva y luego DOM con checkboxes.
|
|
@@ -2530,9 +2822,9 @@ class CertRunner {
|
|
|
2530
2822
|
if (dialogMsg) {
|
|
2531
2823
|
// Portal lanzó alert — puede ser transitorio. Guardar y reintentar.
|
|
2532
2824
|
lastDialogMsg = dialogMsg;
|
|
2533
|
-
console.log(`
|
|
2825
|
+
console.log(` [!] Portal respondió con alerta en intento ${intento}: ${dialogMsg.substring(0, 100)}`);
|
|
2534
2826
|
if (intento < MAX_INTENTOS) {
|
|
2535
|
-
console.log('
|
|
2827
|
+
console.log(' → Recargando y reintentando en 3s...');
|
|
2536
2828
|
await new Promise(r => setTimeout(r, 3000));
|
|
2537
2829
|
continue;
|
|
2538
2830
|
}
|
|
@@ -2545,7 +2837,7 @@ class CertRunner {
|
|
|
2545
2837
|
break;
|
|
2546
2838
|
}
|
|
2547
2839
|
|
|
2548
|
-
console.log(`
|
|
2840
|
+
console.log(` [!] Formulario no cargó en intento ${intento}${intento < MAX_INTENTOS ? ` — reintentando...` : ''}`);
|
|
2549
2841
|
await new Promise(r => setTimeout(r, 2000));
|
|
2550
2842
|
}
|
|
2551
2843
|
|
|
@@ -2558,7 +2850,7 @@ class CertRunner {
|
|
|
2558
2850
|
todos.forEach(cb => { if (!cb.checked) cb.click(); });
|
|
2559
2851
|
return todos.length;
|
|
2560
2852
|
});
|
|
2561
|
-
console.log(`
|
|
2853
|
+
console.log(` → ${totalCbs} checkbox(es) marcados`);
|
|
2562
2854
|
await new Promise(r => setTimeout(r, 500));
|
|
2563
2855
|
|
|
2564
2856
|
// ── PASO 3: Rellenar campos proveedor software ────────────────
|
|
@@ -2629,7 +2921,7 @@ class CertRunner {
|
|
|
2629
2921
|
return false;
|
|
2630
2922
|
});
|
|
2631
2923
|
if (!submitOk) throw new Error('No se encontró botón "Grabar Declaración" en certBolElectDteInternet/');
|
|
2632
|
-
console.log('
|
|
2924
|
+
console.log(' → Click "Grabar Declaración"...');
|
|
2633
2925
|
|
|
2634
2926
|
// Esperar confirmación del SII
|
|
2635
2927
|
await page.waitForFunction(() => {
|
|
@@ -2639,7 +2931,7 @@ class CertRunner {
|
|
|
2639
2931
|
}, { timeout: 20000, polling: 1000 }).catch(() => {});
|
|
2640
2932
|
|
|
2641
2933
|
const msgFinal = await page.evaluate(() => (document.body.textContent || '').trim().substring(0, 300));
|
|
2642
|
-
console.log(`
|
|
2934
|
+
console.log(` ✓ Declaración completada. Respuesta: ${msgFinal.substring(0, 150)}`);
|
|
2643
2935
|
|
|
2644
2936
|
if (this.config.debugDir) {
|
|
2645
2937
|
await page.screenshot({ path: path.join(this.config.debugDir, 'boleta-declaracion-post-submit.png'), fullPage: true }).catch(() => {});
|