@devlas/dte-sii 2.13.1 → 2.13.3
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/CafSolicitor.js +7 -2
- package/DTE.js +6 -2
- package/SiiCertificacion.js +9 -1
- package/SiiSession.js +10 -1
- package/package.json +1 -1
- package/utils/sanitize.js +12 -4
package/CafSolicitor.js
CHANGED
|
@@ -421,9 +421,14 @@ class CafSolicitor {
|
|
|
421
421
|
|
|
422
422
|
this._saveDebug(debugDir, 'step1-submit.html', response.body || '');
|
|
423
423
|
|
|
424
|
-
// Guardar sesión para reutilización
|
|
424
|
+
// Guardar sesión para reutilización. Best-effort: si falla, se pierde el
|
|
425
|
+
// reuso pero NO se aborta la solicitud de folios ya en curso.
|
|
425
426
|
if (this.sessionPath) {
|
|
426
|
-
|
|
427
|
+
try {
|
|
428
|
+
this.session.saveSession(this.sessionPath);
|
|
429
|
+
} catch (e) {
|
|
430
|
+
console.warn(`[CafSolicitor] No se pudo guardar la sesión: ${e.message}`);
|
|
431
|
+
}
|
|
427
432
|
}
|
|
428
433
|
|
|
429
434
|
// Rechazo duro por falta de Verificación de Actividades: debe detectarse ANTES de
|
package/DTE.js
CHANGED
|
@@ -270,8 +270,12 @@ class DTE {
|
|
|
270
270
|
// va firmado, el SII valida la firma sobre lo que él leyó → "TED - Firma invalida".
|
|
271
271
|
// Se aplica ANTES de firmar, así la firma cubre exactamente los bytes del barcode.
|
|
272
272
|
// El DTE conserva el texto real con acentos; esto solo afecta al timbre.
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
// ⚠️ Truncar DESPUÉS de plegar, no antes: el plegado puede ALARGAR el texto, porque
|
|
274
|
+
// algunos caracteres se expanden de 1 a 2 (ß→ss, Æ→AE, Þ→TH, fi→fi). Si el corte de 40
|
|
275
|
+
// caía justo en uno de esos, el resultado quedaba en 41-42 caracteres y desbordaba el
|
|
276
|
+
// límite de <RSR> e <IT1> del SII, con riesgo de rechazo del timbre.
|
|
277
|
+
const rznRecepRaw = sanitizeTedText(enc.Receptor.RznSocRecep || '').substring(0, 40);
|
|
278
|
+
const it1Raw = sanitizeTedText(primerItem.NmbItem || 'Producto').substring(0, 40);
|
|
275
279
|
const rznRecepXml = this._escapeXmlText(rznRecepRaw);
|
|
276
280
|
const it1Xml = this._escapeXmlText(it1Raw);
|
|
277
281
|
|
package/SiiCertificacion.js
CHANGED
|
@@ -107,7 +107,15 @@ class SiiCertificacion {
|
|
|
107
107
|
const _sp = options.sessionPath;
|
|
108
108
|
this.session.ensureSession = async function(targetPath) {
|
|
109
109
|
const result = await _origEnsure(targetPath);
|
|
110
|
-
|
|
110
|
+
// Best-effort: este hook corre en CADA establecimiento de sesión, así que
|
|
111
|
+
// una falla al escribir el cache (disco lleno, permisos, volumen no montado)
|
|
112
|
+
// tumbaría toda operación contra el SII. Persistir la sesión es una
|
|
113
|
+
// optimización; la operación en curso ya tiene la sesión viva en memoria.
|
|
114
|
+
try {
|
|
115
|
+
_sess.saveSession(_sp);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
console.warn(`[SiiCertificacion] No se pudo guardar la sesión en ${_sp}: ${e.message}`);
|
|
118
|
+
}
|
|
111
119
|
return result;
|
|
112
120
|
};
|
|
113
121
|
}
|
package/SiiSession.js
CHANGED
|
@@ -538,17 +538,26 @@ class SiiSession {
|
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
/**
|
|
541
|
-
* Guarda la sesión actual en un archivo JSON
|
|
541
|
+
* Guarda la sesión actual en un archivo JSON.
|
|
542
|
+
*
|
|
543
|
+
* Crea el directorio contenedor si no existe: `filePath` suele apuntar a un
|
|
544
|
+
* volumen persistente o a una carpeta de debug que en el primer arranque
|
|
545
|
+
* todavía no está creada, y sin esto `writeFileSync` lanza ENOENT. Perder la
|
|
546
|
+
* sesión obliga a re-loguearse contra el SII en cada operación, que es el
|
|
547
|
+
* camino directo al bloqueo por "máximo de sesiones autenticadas" del RUT.
|
|
548
|
+
*
|
|
542
549
|
* @param {string} filePath - Ruta del archivo donde guardar
|
|
543
550
|
*/
|
|
544
551
|
saveSession(filePath) {
|
|
545
552
|
const fs = require('fs');
|
|
553
|
+
const path = require('path');
|
|
546
554
|
const sessionData = {
|
|
547
555
|
cookieJar: this.cookieJar,
|
|
548
556
|
baseHost: this.baseHost,
|
|
549
557
|
savedAt: Date.now(),
|
|
550
558
|
expiresAt: Date.now() + (90 * 60 * 1000), // 90 minutos de validez
|
|
551
559
|
};
|
|
560
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
552
561
|
fs.writeFileSync(filePath, JSON.stringify(sessionData, null, 2), 'utf8');
|
|
553
562
|
}
|
|
554
563
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devlas/dte-sii",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.3",
|
|
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",
|
package/utils/sanitize.js
CHANGED
|
@@ -72,19 +72,27 @@ function sanitizeSiiText(text) {
|
|
|
72
72
|
* @returns {string} - Texto sin caracteres fuera de ASCII
|
|
73
73
|
*/
|
|
74
74
|
function sanitizeTedText(text) {
|
|
75
|
-
|
|
75
|
+
// ⚠️ El plegado va ANTES de `sanitizeSiiText`, no después.
|
|
76
|
+
//
|
|
77
|
+
// `sanitizeSiiText` descarta lo que no reconoce, así que si corre primero se lleva las
|
|
78
|
+
// ligaduras (fi, ff, Œ…) y el mapeo de más abajo nunca llega a verlas: quedaba como código
|
|
79
|
+
// muerto y esos caracteres desaparecían del timbre en vez de expandirse.
|
|
80
|
+
return String(text ?? '')
|
|
76
81
|
// Descompone en letra base + diacrítico y descarta el diacrítico.
|
|
77
82
|
.normalize('NFD')
|
|
78
83
|
.replace(/[\u0300-\u036f]/g, '')
|
|
79
84
|
// Lo que NFD no separa (ligaduras y símbolos con forma propia).
|
|
80
|
-
.replace(/[
|
|
85
|
+
.replace(/[fffiflffifflŒœ]/g, m => ({
|
|
86
|
+
'ff': 'ff', 'fi': 'fi', 'fl': 'fl', 'ffi': 'ffi', 'ffl': 'ffl', 'Œ': 'OE', 'œ': 'oe',
|
|
87
|
+
})[m])
|
|
81
88
|
.replace(/Æ/g, 'AE').replace(/æ/g, 'ae')
|
|
82
89
|
.replace(/Ø/g, 'O').replace(/ø/g, 'o')
|
|
83
90
|
.replace(/Ð/g, 'D').replace(/ð/g, 'd')
|
|
84
91
|
.replace(/Þ/g, 'TH').replace(/þ/g, 'th')
|
|
85
92
|
.replace(/ß/g, 'ss')
|
|
86
|
-
//
|
|
87
|
-
.replace(/[^\x20-\x7E]/g, '')
|
|
93
|
+
// Recién ahora las reglas del SII, sobre texto ya plegado a ASCII.
|
|
94
|
+
.replace(/[^\x20-\x7E]/g, '')
|
|
95
|
+
.trim();
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
/**
|