@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 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
- this.session.saveSession(this.sessionPath);
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
- const rznRecepRaw = sanitizeTedText((enc.Receptor.RznSocRecep || '').substring(0, 40));
274
- const it1Raw = sanitizeTedText((primerItem.NmbItem || 'Producto').substring(0, 40));
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
 
@@ -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
- _sess.saveSession(_sp);
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.1",
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
- return sanitizeSiiText(text)
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(/[fffifl]/g, m => ({ 'ff': 'ff', 'fi': 'fi', 'fl': 'fl' })[m])
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
- // Red de seguridad: cualquier resto no-ASCII se elimina antes del barcode.
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
  /**