@devlas/dte-sii 2.5.0

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.
Files changed (60) hide show
  1. package/BoletaService.js +109 -0
  2. package/CAF.js +173 -0
  3. package/CafSolicitor.js +380 -0
  4. package/Certificado.js +123 -0
  5. package/ConsumoFolio.js +376 -0
  6. package/DTE.js +399 -0
  7. package/EnviadorSII.js +1304 -0
  8. package/Envio.js +196 -0
  9. package/FolioRegistry.js +553 -0
  10. package/FolioService.js +703 -0
  11. package/LICENSE +27 -0
  12. package/LibroBase.js +134 -0
  13. package/LibroCompraVenta.js +205 -0
  14. package/LibroGuia.js +225 -0
  15. package/README.md +239 -0
  16. package/Signer.js +94 -0
  17. package/SiiCertificacion.js +1189 -0
  18. package/SiiPortalAuth.js +460 -0
  19. package/SiiSession.js +499 -0
  20. package/cert/BoletaCert.js +731 -0
  21. package/cert/CertFolioHelper.js +185 -0
  22. package/cert/CertRunner.js +2658 -0
  23. package/cert/ConfigLoader.js +133 -0
  24. package/cert/IntercambioCert.js +429 -0
  25. package/cert/LibroCompras.js +359 -0
  26. package/cert/LibroGuias.js +171 -0
  27. package/cert/LibroVentas.js +153 -0
  28. package/cert/MuestrasImpresas.js +676 -0
  29. package/cert/SetBase.js +321 -0
  30. package/cert/SetBasico.js +413 -0
  31. package/cert/SetCompra.js +472 -0
  32. package/cert/SetExenta.js +490 -0
  33. package/cert/SetGuia.js +283 -0
  34. package/cert/SetParser.js +1184 -0
  35. package/cert/SetsProvider.js +499 -0
  36. package/cert/Simulacion.js +521 -0
  37. package/cert/comunaOficina.js +460 -0
  38. package/cert/index.js +124 -0
  39. package/cert/types.js +330 -0
  40. package/dte-sii.d.ts +458 -0
  41. package/index.js +428 -0
  42. package/package.json +48 -0
  43. package/utils/c14n.js +275 -0
  44. package/utils/calculo.js +396 -0
  45. package/utils/config.js +276 -0
  46. package/utils/constants.js +302 -0
  47. package/utils/emisor.js +174 -0
  48. package/utils/endpoints.js +225 -0
  49. package/utils/error.js +235 -0
  50. package/utils/index.js +339 -0
  51. package/utils/logger.js +239 -0
  52. package/utils/pfx.js +203 -0
  53. package/utils/receptor.js +218 -0
  54. package/utils/referencia.js +169 -0
  55. package/utils/resolucion.js +119 -0
  56. package/utils/rut.js +169 -0
  57. package/utils/sanitize.js +124 -0
  58. package/utils/tokenCache.js +214 -0
  59. package/utils/xml.js +358 -0
  60. package/utils.js +4 -0
@@ -0,0 +1,185 @@
1
+ // Copyright (c) 2026 Devlas SpA — https://devlas.cl
2
+ // Licencia MIT. Ver archivo LICENSE para mas detalles.
3
+ /**
4
+ * CertFolioHelper
5
+ *
6
+ * Helper para gestión de folios en certificación.
7
+ * Centraliza la lógica de reserva local de folios que estaba duplicada en todos los tests.
8
+ *
9
+ * @module dte-sii/cert/CertFolioHelper
10
+ */
11
+
12
+ const crypto = require('crypto');
13
+
14
+ /**
15
+ * Clase helper para gestión de folios en certificación
16
+ * Reemplaza las funciones locales duplicadas en cada test
17
+ */
18
+ class CertFolioHelper {
19
+ /**
20
+ * @param {Object} [options] - Opciones de configuración
21
+ * @param {string} [options.ambiente='certificacion'] - Ambiente (certificacion/produccion)
22
+ */
23
+ constructor(options = {}) {
24
+ this.ambiente = options.ambiente || 'certificacion';
25
+ this.counters = new Map(); // Contadores de folios por CAF
26
+ this.usedFolios = new Map(); // Folios usados por tipo DTE
27
+ this.sentFolios = new Map(); // Folios marcados como enviados
28
+ }
29
+
30
+ /**
31
+ * Construye clave única para un CAF
32
+ * @private
33
+ */
34
+ _buildKey({ tipoDte, folioDesde, folioHasta }) {
35
+ return `${tipoDte}|${folioDesde}|${folioHasta}`;
36
+ }
37
+
38
+ /**
39
+ * Crea fingerprint de un CAF para identificación única
40
+ * @param {string} cafXml - XML del CAF
41
+ * @returns {string} Hash del CAF
42
+ */
43
+ createCafFingerprint(cafXml) {
44
+ if (!cafXml) return 'local';
45
+ return crypto.createHash('sha256').update(cafXml).digest('hex').slice(0, 12);
46
+ }
47
+
48
+ /**
49
+ * Reserva el siguiente folio disponible para un CAF
50
+ *
51
+ * @param {Object} params - Parámetros
52
+ * @param {number} params.tipoDte - Tipo de DTE
53
+ * @param {number} params.folioDesde - Folio inicial del CAF
54
+ * @param {number} params.folioHasta - Folio final del CAF
55
+ * @returns {number} Folio reservado
56
+ * @throws {Error} Si no hay folios disponibles
57
+ */
58
+ reserveNextFolio({ tipoDte, folioDesde, folioHasta }) {
59
+ const desde = Number(folioDesde);
60
+ const hasta = Number(folioHasta);
61
+
62
+ if (Number.isNaN(desde) || Number.isNaN(hasta)) {
63
+ throw new Error('Parámetros inválidos para reservar folio');
64
+ }
65
+
66
+ const key = this._buildKey({ tipoDte, folioDesde: desde, folioHasta: hasta });
67
+ const next = this.counters.has(key) ? this.counters.get(key) : desde;
68
+
69
+ if (next > hasta) {
70
+ throw new Error(`No hay más folios disponibles (${desde}-${hasta})`);
71
+ }
72
+
73
+ this.counters.set(key, next + 1);
74
+ return next;
75
+ }
76
+
77
+ /**
78
+ * Marca un folio como usado (para evitar reutilización)
79
+ *
80
+ * @param {number} tipoDte - Tipo de DTE
81
+ * @param {number} folio - Número de folio
82
+ */
83
+ markFolioUsed(tipoDte, folio) {
84
+ if (!this.usedFolios.has(tipoDte)) {
85
+ this.usedFolios.set(tipoDte, new Set());
86
+ }
87
+ this.usedFolios.get(tipoDte).add(folio);
88
+ }
89
+
90
+ /**
91
+ * Verifica si un folio ya fue usado
92
+ *
93
+ * @param {number} tipoDte - Tipo de DTE
94
+ * @param {number} folio - Número de folio
95
+ * @returns {boolean}
96
+ */
97
+ isFolioUsed(tipoDte, folio) {
98
+ return this.usedFolios.get(tipoDte)?.has(folio) || false;
99
+ }
100
+
101
+ /**
102
+ * Marca un folio como enviado al SII
103
+ *
104
+ * @param {Object} params - Parámetros
105
+ * @param {number} params.tipoDte - Tipo de DTE
106
+ * @param {number} params.folio - Número de folio
107
+ * @param {string} [params.trackId] - TrackId del envío
108
+ * @param {Object} [params.extra] - Datos adicionales
109
+ */
110
+ markFolioSent({ tipoDte, folio, trackId, extra }) {
111
+ const key = `${tipoDte}|${folio}`;
112
+ this.sentFolios.set(key, {
113
+ tipoDte,
114
+ folio,
115
+ trackId,
116
+ sentAt: new Date().toISOString(),
117
+ ...extra,
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Libera folios reservados (para cleanup en caso de error)
123
+ * Nota: En esta implementación simple no hace nada real
124
+ * ya que los counters son locales a la ejecución
125
+ *
126
+ * @param {Object} params - Parámetros
127
+ */
128
+ releaseFolios(params) {
129
+ // En certificación local, no hay necesidad de liberar
130
+ // Esta función existe para compatibilidad con la interfaz
131
+ }
132
+
133
+ /**
134
+ * Resetea contadores para un tipo de DTE específico
135
+ * Útil cuando se solicita un nuevo CAF
136
+ *
137
+ * @param {number} tipoDte - Tipo de DTE
138
+ */
139
+ resetCountersForTipo(tipoDte) {
140
+ for (const [key] of this.counters) {
141
+ if (key.startsWith(`${tipoDte}|`)) {
142
+ this.counters.delete(key);
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Obtiene estadísticas de folios usados
149
+ *
150
+ * @returns {Object} Estadísticas por tipo de DTE
151
+ */
152
+ getStats() {
153
+ const stats = {};
154
+ for (const [tipoDte, folios] of this.usedFolios) {
155
+ stats[tipoDte] = {
156
+ count: folios.size,
157
+ folios: Array.from(folios).sort((a, b) => a - b),
158
+ };
159
+ }
160
+ return stats;
161
+ }
162
+
163
+ /**
164
+ * Verifica que un CAF tenga folios nuevos disponibles
165
+ * (que no hayan sido usados en esta sesión)
166
+ *
167
+ * @param {number} tipoDte - Tipo de DTE
168
+ * @param {number} folioDesde - Folio inicial
169
+ * @param {number} folioHasta - Folio final
170
+ * @returns {boolean}
171
+ */
172
+ hasNewFolios(tipoDte, folioDesde, folioHasta) {
173
+ const usados = this.usedFolios.get(tipoDte);
174
+ if (!usados || usados.size === 0) return true;
175
+
176
+ for (let f = folioDesde; f <= folioHasta; f++) {
177
+ if (!usados.has(f)) {
178
+ return true;
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+ }
184
+
185
+ module.exports = CertFolioHelper;