@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/SetParser.js
CHANGED
|
@@ -124,12 +124,12 @@ function mapearTipoDocLibro(tipo) {
|
|
|
124
124
|
if (t.includes('FACTURA EXENTA ELECTRONICA') || (t.includes('NO AFECTA') && t.includes('ELECTRONICA'))) return 34;
|
|
125
125
|
if (t.includes('FACTURA ELECTRONICA')) return 33;
|
|
126
126
|
if (t.includes('FACTURA EXENTA') || t.includes('FACTURA NO AFECTA')) return 30; // exenta papel
|
|
127
|
-
if (t === 'FACTURA') return 30;
|
|
127
|
+
if (t === 'FACTURA') return 30; // factura afecta papel → TpoDoc=30 (regular libro); exentos libro usa lógica especial
|
|
128
128
|
if (t.includes('NOTA DE CREDITO') && (t.includes('ELECTRONICA') || t.includes('ELECTRONICO'))) return 61;
|
|
129
129
|
if (t.includes('NOTA DE CREDITO')) return 60;
|
|
130
130
|
if (t.includes('NOTA DE DEBITO') && (t.includes('ELECTRONICA') || t.includes('ELECTRONICO'))) return 56;
|
|
131
131
|
if (t.includes('NOTA DE DEBITO')) return 55;
|
|
132
|
-
return
|
|
132
|
+
return null;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -991,14 +991,26 @@ function generarEstructuraSetExportacion(set) {
|
|
|
991
991
|
/**
|
|
992
992
|
* Genera estructura para LIBRO DE COMPRAS (IECV)
|
|
993
993
|
*/
|
|
994
|
-
function generarEstructuraLibroCompras(set) {
|
|
994
|
+
function generarEstructuraLibroCompras(set, opts = {}) {
|
|
995
|
+
const esExentos = opts.esExentos === true;
|
|
995
996
|
const detalle = [];
|
|
996
997
|
const resumen = {};
|
|
997
998
|
|
|
998
999
|
for (const doc of set.documentosLibro) {
|
|
999
|
-
|
|
1000
|
+
let tipoDoc = mapearTipoDocLibro(doc.tipoDocumento);
|
|
1000
1001
|
const tasaIva = 0.19;
|
|
1001
1002
|
|
|
1003
|
+
// En libro de compras EXENTOS, los TpoDoc correctos son:
|
|
1004
|
+
// FACTURA (papel afecta) → 30 (con IVANoRec)
|
|
1005
|
+
// FACTURA EXENTA (papel) → 32 (Fac. Venta B&S No Afectos/Exentos, papel)
|
|
1006
|
+
// FACTURA ELECTRONICA → 33 (con IVANoRec si tiene montoAfecto)
|
|
1007
|
+
// FACTURA EXENTA ELECTRONICA → 34 (puramente exenta)
|
|
1008
|
+
// NC/NCE/ND/NDE → 60/61/55/56
|
|
1009
|
+
// Total: 7 tipos distintos = 7 líneas de resumen
|
|
1010
|
+
const esFacturaExentaPapel = esExentos && tipoDoc === 30
|
|
1011
|
+
&& !doc.montoAfecto && !!doc.montoExento;
|
|
1012
|
+
if (esFacturaExentaPapel) tipoDoc = 32;
|
|
1013
|
+
|
|
1002
1014
|
const detalleDoc = {
|
|
1003
1015
|
TpoDoc: tipoDoc,
|
|
1004
1016
|
NroDoc: doc.folio,
|
|
@@ -1035,16 +1047,56 @@ function generarEstructuraLibroCompras(set) {
|
|
|
1035
1047
|
detalleDoc.IVARetTotal = Math.round((doc.montoAfecto || 0) * tasaIva);
|
|
1036
1048
|
}
|
|
1037
1049
|
|
|
1038
|
-
// Referencia para notas de crédito
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1050
|
+
// Referencia para notas de crédito/débito (TpoDoc 60/61/55/56)
|
|
1051
|
+
const esNota = (tipoDoc === 60 || tipoDoc === 61 || tipoDoc === 55 || tipoDoc === 56);
|
|
1052
|
+
if (esNota && doc.observacion) {
|
|
1053
|
+
// El folio referenciado es siempre el último número de la observación
|
|
1054
|
+
const matchFolio = doc.observacion.match(/(\d+)\s*$/i);
|
|
1055
|
+
if (matchFolio) {
|
|
1056
|
+
detalleDoc.FolioDocRef = parseInt(matchFolio[1]);
|
|
1057
|
+
if (doc.observacion.match(/FACTURA EXENTA ELECTRONICA/i)) {
|
|
1058
|
+
detalleDoc.TpoDocRef = 34;
|
|
1059
|
+
} else if (doc.observacion.match(/FACTURA EXENTA/i)) {
|
|
1060
|
+
detalleDoc.TpoDocRef = esExentos ? 32 : 30;
|
|
1061
|
+
} else if (doc.observacion.match(/FACTURA.*ELECTRONICA/i)) {
|
|
1062
|
+
detalleDoc.TpoDocRef = 33;
|
|
1063
|
+
} else if (doc.observacion.match(/FACTURA/i)) {
|
|
1064
|
+
detalleDoc.TpoDocRef = 30;
|
|
1065
|
+
}
|
|
1066
|
+
// Sin TpoDocRef si no se menciona FACTURA (ej. referencia a otra NC/ND)
|
|
1044
1067
|
}
|
|
1045
1068
|
}
|
|
1046
1069
|
|
|
1047
|
-
|
|
1070
|
+
// En libro de compras EXENTOS: lógica por tipo de doc
|
|
1071
|
+
// Docs con montoAfecto → IVANoRec (IVA no recuperable)
|
|
1072
|
+
// Docs puramente exentos (TpoDoc=32/34) → sin IVANoRec
|
|
1073
|
+
// TpoDoc=46 → usa IVARetTotal (no IVANoRec)
|
|
1074
|
+
const esPuramenteExento = !doc.montoAfecto && !!doc.montoExento;
|
|
1075
|
+
if (esExentos && tipoDoc !== 46 && !esPuramenteExento) {
|
|
1076
|
+
const mntIVA = detalleDoc.MntIVA || 0;
|
|
1077
|
+
detalleDoc.IVANoRec = { CodIVANoRec: 1, MntIVANoRec: mntIVA };
|
|
1078
|
+
detalleDoc.MntIVA = 0;
|
|
1079
|
+
if (detalleDoc.MntNeto === undefined || detalleDoc.MntNeto === null) {
|
|
1080
|
+
detalleDoc.MntNeto = 0;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
// TpoDoc=32 (Fac. Exenta papel): solo MntExe + MntTotal, sin TasaImp/MntNeto/MntIVA/IVANoRec
|
|
1084
|
+
if (esFacturaExentaPapel) {
|
|
1085
|
+
delete detalleDoc.TasaImp;
|
|
1086
|
+
delete detalleDoc.MntNeto;
|
|
1087
|
+
delete detalleDoc.MntIVA;
|
|
1088
|
+
delete detalleDoc.IVANoRec;
|
|
1089
|
+
}
|
|
1090
|
+
// Para docs puramente exentos que NO son TpoDoc=32: TasaImp=19 + MntNeto=0 + MntIVA=0
|
|
1091
|
+
if (esExentos && esPuramenteExento && !esFacturaExentaPapel) {
|
|
1092
|
+
detalleDoc.TasaImp = Math.round(tasaIva * 100); // TasaImp=19 requerido
|
|
1093
|
+
detalleDoc.MntNeto = 0;
|
|
1094
|
+
detalleDoc.MntIVA = 0;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// MntTotal incluye IVANoRec (IVA pagado pero no deducible)
|
|
1098
|
+
const mntIvaNoRecTotal = (esExentos && detalleDoc.IVANoRec) ? (detalleDoc.IVANoRec.MntIVANoRec || 0) : 0;
|
|
1099
|
+
detalleDoc.MntTotal = (detalleDoc.MntNeto || 0) + (detalleDoc.MntIVA || 0) + (detalleDoc.MntExe || 0) + mntIvaNoRecTotal;
|
|
1048
1100
|
if (doc.retencionTotal && tipoDoc === 46) {
|
|
1049
1101
|
detalleDoc.MntTotal = detalleDoc.MntNeto || 0; // Sin IVA pagado
|
|
1050
1102
|
}
|
|
@@ -1069,6 +1121,36 @@ function generarEstructuraLibroCompras(set) {
|
|
|
1069
1121
|
resumen[tipoDoc].TotMntTotal += detalleDoc.MntTotal || 0;
|
|
1070
1122
|
}
|
|
1071
1123
|
|
|
1124
|
+
// Post-proceso para libro de compras EXENTOS:
|
|
1125
|
+
// NC/NCE con sin montos declarados que anulan un doc con montos deben heredar esos montos
|
|
1126
|
+
if (esExentos) {
|
|
1127
|
+
for (const doc of detalle) {
|
|
1128
|
+
const esNotaAnulacion = (doc.TpoDoc === 60 || doc.TpoDoc === 61 || doc.TpoDoc === 55 || doc.TpoDoc === 56)
|
|
1129
|
+
&& !doc.MntNeto && !doc.MntExe && doc.FolioDocRef;
|
|
1130
|
+
if (esNotaAnulacion) {
|
|
1131
|
+
const refDoc = detalle.find(d => d.NroDoc === doc.FolioDocRef);
|
|
1132
|
+
if (refDoc) {
|
|
1133
|
+
if (refDoc.MntExe) doc.MntExe = refDoc.MntExe;
|
|
1134
|
+
if (refDoc.MntNeto) {
|
|
1135
|
+
doc.MntNeto = refDoc.MntNeto;
|
|
1136
|
+
// Heredar IVANoRec del doc referenciado
|
|
1137
|
+
if (refDoc.IVANoRec) {
|
|
1138
|
+
doc.IVANoRec = { CodIVANoRec: 1, MntIVANoRec: refDoc.IVANoRec.MntIVANoRec || 0 };
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
const mntIvaNoRecDoc = (doc.IVANoRec ? (doc.IVANoRec.MntIVANoRec || 0) : 0);
|
|
1142
|
+
doc.MntTotal = (doc.MntNeto || 0) + (doc.MntExe || 0) + mntIvaNoRecDoc;
|
|
1143
|
+
// Actualizar resumen para este TpoDoc
|
|
1144
|
+
if (resumen[doc.TpoDoc]) {
|
|
1145
|
+
resumen[doc.TpoDoc].TotMntExe += doc.MntExe || 0;
|
|
1146
|
+
resumen[doc.TpoDoc].TotMntNeto += doc.MntNeto || 0;
|
|
1147
|
+
resumen[doc.TpoDoc].TotMntTotal += doc.MntTotal;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1072
1154
|
return {
|
|
1073
1155
|
numeroAtencion: set.numeroAtencion,
|
|
1074
1156
|
factorProporcionalidad: set.factorProporcionalidad || 0.6,
|
|
@@ -1136,8 +1218,9 @@ function generarEstructurasParaScripts(datosExtraidos, receptorConfig = {}) {
|
|
|
1136
1218
|
estructuras.libroCompras = generarEstructuraLibroCompras(set);
|
|
1137
1219
|
break;
|
|
1138
1220
|
case 'LIBRO_COMPRAS_EXENTOS':
|
|
1139
|
-
//
|
|
1140
|
-
|
|
1221
|
+
// En el libro de exentos, FACTURA afecta papel usa TpoDoc=29 (Factura de Inicio)
|
|
1222
|
+
// para distinguirse de FACTURA EXENTA (TpoDoc=30) en el ResumenPeriodo
|
|
1223
|
+
estructuras.libroComprasExentos = generarEstructuraLibroCompras(set, { esExentos: true });
|
|
1141
1224
|
break;
|
|
1142
1225
|
case 'LIBRO_VENTAS':
|
|
1143
1226
|
// El libro de ventas se genera con los datos del set básico o exento
|
package/cert/SetsProvider.js
CHANGED
|
@@ -105,13 +105,13 @@ class SetsProvider {
|
|
|
105
105
|
* @returns {Promise<boolean>}
|
|
106
106
|
*/
|
|
107
107
|
async initSession() {
|
|
108
|
-
this.logger.log('[
|
|
108
|
+
this.logger.log('[SessionSii] Inicializando sesión SII...');
|
|
109
109
|
|
|
110
110
|
const siiCert = this._getSiiCert();
|
|
111
111
|
|
|
112
112
|
// Intentar cargar sesión existente
|
|
113
113
|
if (this.sessionPath && SiiCertificacion.isSessionValid(this.sessionPath)) {
|
|
114
|
-
this.logger.log('[
|
|
114
|
+
this.logger.log('[SessionSii] ✓ Sesión existente válida');
|
|
115
115
|
const loaded = siiCert.loadSession(this.sessionPath);
|
|
116
116
|
if (loaded) {
|
|
117
117
|
return true;
|
|
@@ -119,7 +119,7 @@ class SetsProvider {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// Crear nueva sesión
|
|
122
|
-
this.logger.log('[
|
|
122
|
+
this.logger.log('[SessionSii] Estableciendo nueva sesión...');
|
|
123
123
|
try {
|
|
124
124
|
// verAvance() fuerza el login
|
|
125
125
|
await siiCert.verAvance();
|
|
@@ -128,12 +128,12 @@ class SetsProvider {
|
|
|
128
128
|
if (this.sessionPath) {
|
|
129
129
|
this._ensureDir(path.dirname(this.sessionPath));
|
|
130
130
|
siiCert.saveSession(this.sessionPath);
|
|
131
|
-
this.logger.log('[
|
|
131
|
+
this.logger.log('[SessionSii] ✓ Sesión guardada');
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
return true;
|
|
135
135
|
} catch (error) {
|
|
136
|
-
this.logger.error(`[
|
|
136
|
+
this.logger.error(`[SessionSii] [ERR] Error: ${error.message}`);
|
|
137
137
|
return false;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -153,11 +153,11 @@ class SetsProvider {
|
|
|
153
153
|
setsOpcionales = DEFAULT_SETS_OPCIONALES,
|
|
154
154
|
} = options;
|
|
155
155
|
|
|
156
|
-
this.logger.log('\n[
|
|
156
|
+
this.logger.log('\n[SessionSii] Obteniendo sets de prueba...');
|
|
157
157
|
|
|
158
158
|
// Si hay cache y no se fuerza refresh, retornar cache
|
|
159
159
|
if (!forceRefresh && this._estructurasCache) {
|
|
160
|
-
this.logger.log('[SetsProvider]
|
|
160
|
+
this.logger.log('[SetsProvider] ✓ Usando cache de estructuras');
|
|
161
161
|
return {
|
|
162
162
|
success: true,
|
|
163
163
|
regenerated: false,
|
|
@@ -212,7 +212,7 @@ class SetsProvider {
|
|
|
212
212
|
// Detectar si el set fue regenerado
|
|
213
213
|
const regenerated = this._detectRegeneration(result);
|
|
214
214
|
if (regenerated) {
|
|
215
|
-
this.logger.log('[SetsProvider]
|
|
215
|
+
this.logger.log('[SetsProvider] [!] Set regenerado - reiniciar proceso');
|
|
216
216
|
// Invalidar cualquier cache anterior
|
|
217
217
|
this.invalidateCache();
|
|
218
218
|
}
|
|
@@ -228,13 +228,13 @@ class SetsProvider {
|
|
|
228
228
|
(htmlLower.includes('postulacion factura electronica') && !htmlLower.includes('caso'))) {
|
|
229
229
|
// Distinguir: ¿es "no inscrito" o expiró la sesión?
|
|
230
230
|
if (htmlLower.includes('no inscrito') || (htmlLower.includes('no est') && htmlLower.includes('inscrito'))) {
|
|
231
|
-
this.logger.log('[
|
|
231
|
+
this.logger.log('[SessionSii] pe_generar2: empresa no inscrita en Postulación (fase avanzada).');
|
|
232
232
|
return { success: false, error: 'El contribuyente no está inscrito en Postulación en el portal SII.' };
|
|
233
233
|
}
|
|
234
234
|
// Sesión expiró en el portal aunque el archivo no lo sabía → forzar re-autenticación
|
|
235
235
|
// Cookies vencidas server-side: limpiar sesión guardada y pedir a
|
|
236
236
|
// generarSetPruebas que re-autentique por sí mismo via pe_generar
|
|
237
|
-
this.logger.log('[
|
|
237
|
+
this.logger.log('[SessionSii] [!] Sesión rechazada por portal, limpiando y reintentando...');
|
|
238
238
|
if (this.sessionPath) {
|
|
239
239
|
const SiiSession = require('../SiiSession');
|
|
240
240
|
SiiSession.clearSession(this.sessionPath);
|
|
@@ -284,7 +284,7 @@ class SetsProvider {
|
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
this.logger.log('[SetsProvider]
|
|
287
|
+
this.logger.log('[SetsProvider] ✓ Sets obtenidos correctamente');
|
|
288
288
|
|
|
289
289
|
return {
|
|
290
290
|
success: true,
|
|
@@ -296,7 +296,7 @@ class SetsProvider {
|
|
|
296
296
|
};
|
|
297
297
|
|
|
298
298
|
} catch (error) {
|
|
299
|
-
this.logger.error(`[SetsProvider]
|
|
299
|
+
this.logger.error(`[SetsProvider] [ERR] Error: ${error.message}`);
|
|
300
300
|
return {
|
|
301
301
|
success: false,
|
|
302
302
|
error: error.message,
|
|
@@ -329,7 +329,7 @@ class SetsProvider {
|
|
|
329
329
|
if (result.setDescargado?.rawHtml && this._lastNumeroAtencion) {
|
|
330
330
|
const match = result.setDescargado.rawHtml.match(/NUMERO DE ATENCI[^:]*:\s*(\d+)/i);
|
|
331
331
|
if (match && match[1] !== this._lastNumeroAtencion) {
|
|
332
|
-
this.logger.log(`[SetsProvider]
|
|
332
|
+
this.logger.log(`[SetsProvider] Número de atención cambió: ${this._lastNumeroAtencion} → ${match[1]}`);
|
|
333
333
|
return true;
|
|
334
334
|
}
|
|
335
335
|
}
|
|
@@ -351,19 +351,16 @@ class SetsProvider {
|
|
|
351
351
|
if (this.debugDir) {
|
|
352
352
|
this._saveDebug('set-texto.txt', textoLimpio);
|
|
353
353
|
}
|
|
354
|
-
|
|
355
|
-
// Usar SetParser del core
|
|
356
|
-
this.logger.log('[SetsProvider] Usando SetParser del core...');
|
|
357
354
|
|
|
358
355
|
const datosExtraidos = SetParser.extraerCasosDelSet(textoLimpio);
|
|
359
356
|
|
|
360
357
|
if (datosExtraidos.sets.length === 0) {
|
|
361
|
-
this.logger.log('[SetsProvider]
|
|
358
|
+
this.logger.log('[SetsProvider] [!] No se encontraron sets en el contenido');
|
|
362
359
|
return { estructuras: null, datosExtraidos: null };
|
|
363
360
|
}
|
|
364
361
|
|
|
365
|
-
this.logger.log(`[SetsProvider]
|
|
366
|
-
this.logger.log(`[SetsProvider]
|
|
362
|
+
this.logger.log(`[SetsProvider] Sets encontrados: ${datosExtraidos.sets.length}`);
|
|
363
|
+
this.logger.log(`[SetsProvider] Total casos: ${datosExtraidos.totalCasos}`);
|
|
367
364
|
|
|
368
365
|
// Guardar número de atención para detectar regeneración
|
|
369
366
|
if (datosExtraidos.sets[0]?.numeroAtencion) {
|
package/cert/Simulacion.js
CHANGED
|
@@ -53,7 +53,7 @@ class Simulacion {
|
|
|
53
53
|
const docRefs = {};
|
|
54
54
|
|
|
55
55
|
if (plan.length < 10) {
|
|
56
|
-
console.warn('
|
|
56
|
+
console.warn('[!] Se recomienda mínimo 10 documentos para simulación.');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const envioDte = new EnvioDTE({ certificado: this.certificado });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devlas/dte-sii",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.14",
|
|
4
4
|
"description": "Facturación y boletas electrónicas para el SII de Chile. Genera, timbra, firma y envía DTEs, libros electrónicos y automatiza la certificación.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "dte-sii.d.ts",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@xmldom/xmldom": "^0.8.11",
|
|
33
33
|
"bwip-js": "^4.8.0",
|
|
34
|
+
"dotenv": "^17.3.1",
|
|
34
35
|
"fast-xml-parser": "^5.3.3",
|
|
35
36
|
"got": "^11.8.6",
|
|
36
37
|
"node-forge": "^1.3.3",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) 2026 Devlas SpA
|
|
2
|
+
// Sistema de progreso estructurado para el runner de certificacion SII.
|
|
3
|
+
// El runner y la libreria emiten lineas [PROGRESS]{...json...} que la API
|
|
4
|
+
// parsea sin regex frágiles sobre texto libre.
|
|
5
|
+
|
|
6
|
+
const STEPS = {
|
|
7
|
+
// Autenticacion
|
|
8
|
+
AUTH_INIT: 'AUTH_INIT',
|
|
9
|
+
AUTH_OK: 'AUTH_OK',
|
|
10
|
+
// Sets
|
|
11
|
+
SETS_DOWNLOADING: 'SETS_DOWNLOADING',
|
|
12
|
+
SETS_LOADED: 'SETS_LOADED',
|
|
13
|
+
// CAFs
|
|
14
|
+
CAF_REQUESTING: 'CAF_REQUESTING', // data: { tipo }
|
|
15
|
+
CAF_OK: 'CAF_OK', // data: { tipo }
|
|
16
|
+
// Ejecucion de sets
|
|
17
|
+
SET_START: 'SET_START', // data: { set }
|
|
18
|
+
SET_SIGNING: 'SET_SIGNING', // data: { set }
|
|
19
|
+
SET_SENDING: 'SET_SENDING', // data: { set }
|
|
20
|
+
SET_OK: 'SET_OK', // data: { set, trackId }
|
|
21
|
+
SET_ERROR: 'SET_ERROR', // data: { set, error }
|
|
22
|
+
// Declaracion
|
|
23
|
+
SETS_DECLARING: 'SETS_DECLARING',
|
|
24
|
+
SETS_DECLARED: 'SETS_DECLARED',
|
|
25
|
+
// Polling aprobacion sets
|
|
26
|
+
POLLING: 'POLLING', // data: { intento, max }
|
|
27
|
+
SET_APPROVED: 'SET_APPROVED', // data: { set }
|
|
28
|
+
SETS_APPROVED: 'SETS_APPROVED',
|
|
29
|
+
SETS_REJECTED: 'SETS_REJECTED',
|
|
30
|
+
// Libros (Fase 4)
|
|
31
|
+
BOOKS_START: 'BOOKS_START',
|
|
32
|
+
BOOK_SENDING: 'BOOK_SENDING', // data: { book }
|
|
33
|
+
BOOK_OK: 'BOOK_OK', // data: { book, trackId }
|
|
34
|
+
BOOK_SKIPPED: 'BOOK_SKIPPED', // data: { book }
|
|
35
|
+
BOOK_ERROR: 'BOOK_ERROR', // data: { book, error }
|
|
36
|
+
BOOKS_DECLARING: 'BOOKS_DECLARING',
|
|
37
|
+
BOOKS_DONE: 'BOOKS_DONE',
|
|
38
|
+
// Avance (Fase 5)
|
|
39
|
+
ADVANCE_WAITING: 'ADVANCE_WAITING',
|
|
40
|
+
ADVANCE_DONE: 'ADVANCE_DONE',
|
|
41
|
+
// Simulacion (Fase 6)
|
|
42
|
+
SIM_START: 'SIM_START',
|
|
43
|
+
SIM_SENDING: 'SIM_SENDING',
|
|
44
|
+
SIM_OK: 'SIM_OK', // data: { trackId }
|
|
45
|
+
SIM_DECLARING: 'SIM_DECLARING',
|
|
46
|
+
SIM_POLLING: 'SIM_POLLING', // data: { intento, max }
|
|
47
|
+
SIM_DONE: 'SIM_DONE',
|
|
48
|
+
// Intercambio (Fase 7)
|
|
49
|
+
INTERCAMBIO_START: 'INTERCAMBIO_START',
|
|
50
|
+
INTERCAMBIO_DONE: 'INTERCAMBIO_DONE',
|
|
51
|
+
// Muestras impresas (Fase 8)
|
|
52
|
+
MUESTRAS_START: 'MUESTRAS_START',
|
|
53
|
+
MUESTRAS_PDFS: 'MUESTRAS_PDFS',
|
|
54
|
+
MUESTRAS_UPLOADING: 'MUESTRAS_UPLOADING',
|
|
55
|
+
MUESTRAS_DONE: 'MUESTRAS_DONE',
|
|
56
|
+
// Boleta electronica
|
|
57
|
+
BOLETA_START: 'BOLETA_START',
|
|
58
|
+
BOLETA_SENDING: 'BOLETA_SENDING',
|
|
59
|
+
BOLETA_OK: 'BOLETA_OK',
|
|
60
|
+
BOLETA_DECLARING: 'BOLETA_DECLARING',
|
|
61
|
+
BOLETA_DONE: 'BOLETA_DONE',
|
|
62
|
+
// Fin
|
|
63
|
+
CERT_DONE: 'CERT_DONE',
|
|
64
|
+
CERT_ERROR: 'CERT_ERROR', // data: { error }
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Emite una linea de progreso estructurada a stdout.
|
|
69
|
+
* Formato: [PROGRESS]{"step":"...","clave":"valor",...}
|
|
70
|
+
*
|
|
71
|
+
* @param {string} step - Una de las constantes STEPS
|
|
72
|
+
* @param {Object} [data] - Datos adicionales opcionales
|
|
73
|
+
*/
|
|
74
|
+
function emitProgress(step, data = {}) {
|
|
75
|
+
process.stdout.write(`[PROGRESS]${JSON.stringify({ step, ...data })}\n`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { STEPS, emitProgress };
|
package/utils/xml.js
CHANGED
|
@@ -54,7 +54,7 @@ const prettyBuilder = new XMLBuilder({
|
|
|
54
54
|
ignoreAttributes: false,
|
|
55
55
|
attributeNamePrefix: '@_',
|
|
56
56
|
format: true,
|
|
57
|
-
indentBy: '
|
|
57
|
+
indentBy: ' ',
|
|
58
58
|
suppressEmptyNode: true,
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -326,7 +326,7 @@ function saveEnvioArtifacts({
|
|
|
326
326
|
fs.writeFileSync(path.join(debugDir, `respuesta-${debugPrefix}.xml`), responseText, 'utf-8');
|
|
327
327
|
}
|
|
328
328
|
} catch (saveError) {
|
|
329
|
-
console.warn('
|
|
329
|
+
console.warn('[!] No se pudo guardar histórico/debug:', saveError.message || saveError);
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|