@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.
- package/BoletaService.js +109 -0
- package/CAF.js +173 -0
- package/CafSolicitor.js +380 -0
- package/Certificado.js +123 -0
- package/ConsumoFolio.js +376 -0
- package/DTE.js +399 -0
- package/EnviadorSII.js +1304 -0
- package/Envio.js +196 -0
- package/FolioRegistry.js +553 -0
- package/FolioService.js +703 -0
- package/LICENSE +27 -0
- package/LibroBase.js +134 -0
- package/LibroCompraVenta.js +205 -0
- package/LibroGuia.js +225 -0
- package/README.md +239 -0
- package/Signer.js +94 -0
- package/SiiCertificacion.js +1189 -0
- package/SiiPortalAuth.js +460 -0
- package/SiiSession.js +499 -0
- package/cert/BoletaCert.js +731 -0
- package/cert/CertFolioHelper.js +185 -0
- package/cert/CertRunner.js +2658 -0
- package/cert/ConfigLoader.js +133 -0
- package/cert/IntercambioCert.js +429 -0
- package/cert/LibroCompras.js +359 -0
- package/cert/LibroGuias.js +171 -0
- package/cert/LibroVentas.js +153 -0
- package/cert/MuestrasImpresas.js +676 -0
- package/cert/SetBase.js +321 -0
- package/cert/SetBasico.js +413 -0
- package/cert/SetCompra.js +472 -0
- package/cert/SetExenta.js +490 -0
- package/cert/SetGuia.js +283 -0
- package/cert/SetParser.js +1184 -0
- package/cert/SetsProvider.js +499 -0
- package/cert/Simulacion.js +521 -0
- package/cert/comunaOficina.js +460 -0
- package/cert/index.js +124 -0
- package/cert/types.js +330 -0
- package/dte-sii.d.ts +458 -0
- package/index.js +428 -0
- package/package.json +48 -0
- package/utils/c14n.js +275 -0
- package/utils/calculo.js +396 -0
- package/utils/config.js +276 -0
- package/utils/constants.js +302 -0
- package/utils/emisor.js +174 -0
- package/utils/endpoints.js +225 -0
- package/utils/error.js +235 -0
- package/utils/index.js +339 -0
- package/utils/logger.js +239 -0
- package/utils/pfx.js +203 -0
- package/utils/receptor.js +218 -0
- package/utils/referencia.js +169 -0
- package/utils/resolucion.js +119 -0
- package/utils/rut.js +169 -0
- package/utils/sanitize.js +124 -0
- package/utils/tokenCache.js +214 -0
- package/utils/xml.js +358 -0
- package/utils.js +4 -0
package/utils/emisor.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
|
+
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
+
/**
|
|
4
|
+
* Utilidades de Emisor
|
|
5
|
+
*
|
|
6
|
+
* Funciones para construir y validar datos de emisor en DTEs
|
|
7
|
+
*
|
|
8
|
+
* @module dte-sii/utils/emisor
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { sanitizeSiiText } = require('./sanitize');
|
|
12
|
+
const { formatRutSii, validarRut } = require('./rut');
|
|
13
|
+
|
|
14
|
+
// ============================================
|
|
15
|
+
// CONSTRUCCIÓN DE EMISOR
|
|
16
|
+
// ============================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Construye objeto de Emisor para DTE (no boleta)
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} config - Configuración del emisor
|
|
22
|
+
* @param {string} config.rut - RUT del emisor
|
|
23
|
+
* @param {string} config.razonSocial - Razón social
|
|
24
|
+
* @param {string} config.giro - Giro comercial
|
|
25
|
+
* @param {string} config.direccion - Dirección
|
|
26
|
+
* @param {string} config.comuna - Comuna
|
|
27
|
+
* @param {string} [config.ciudad] - Ciudad (default: comuna)
|
|
28
|
+
* @param {string} [config.telefono] - Teléfono
|
|
29
|
+
* @param {string} [config.correo] - Correo electrónico
|
|
30
|
+
* @param {number|string} [config.acteco] - Código de actividad económica
|
|
31
|
+
* @returns {Object} Emisor formateado para DTE
|
|
32
|
+
*/
|
|
33
|
+
function buildEmisor(config) {
|
|
34
|
+
const {
|
|
35
|
+
rut,
|
|
36
|
+
razonSocial,
|
|
37
|
+
giro,
|
|
38
|
+
direccion,
|
|
39
|
+
comuna,
|
|
40
|
+
ciudad,
|
|
41
|
+
telefono,
|
|
42
|
+
correo,
|
|
43
|
+
acteco,
|
|
44
|
+
} = config;
|
|
45
|
+
|
|
46
|
+
const emisor = {
|
|
47
|
+
RUTEmisor: formatRutSii(rut),
|
|
48
|
+
RznSoc: sanitizeSiiText(razonSocial),
|
|
49
|
+
GiroEmis: sanitizeSiiText(giro),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (acteco) emisor.Acteco = acteco;
|
|
53
|
+
if (telefono) emisor.Telefono = telefono;
|
|
54
|
+
if (correo) emisor.CorreoEmisor = correo;
|
|
55
|
+
|
|
56
|
+
emisor.DirOrigen = sanitizeSiiText(direccion);
|
|
57
|
+
emisor.CmnaOrigen = comuna;
|
|
58
|
+
emisor.CiudadOrigen = ciudad || comuna || '';
|
|
59
|
+
|
|
60
|
+
return emisor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Construye objeto de Emisor para Boleta
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} config - Configuración del emisor
|
|
67
|
+
* @param {string} config.rut - RUT del emisor
|
|
68
|
+
* @param {string} config.razonSocial - Razón social
|
|
69
|
+
* @param {string} config.giro - Giro comercial
|
|
70
|
+
* @param {string} config.direccion - Dirección
|
|
71
|
+
* @param {string} config.comuna - Comuna
|
|
72
|
+
* @param {string} [config.ciudad] - Ciudad
|
|
73
|
+
* @returns {Object} Emisor formateado para Boleta
|
|
74
|
+
*/
|
|
75
|
+
function buildEmisorBoleta(config) {
|
|
76
|
+
const { rut, razonSocial, giro, direccion, comuna, ciudad } = config;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
RUTEmisor: formatRutSii(rut),
|
|
80
|
+
RznSocEmisor: sanitizeSiiText(razonSocial),
|
|
81
|
+
GiroEmisor: sanitizeSiiText(giro),
|
|
82
|
+
DirOrigen: sanitizeSiiText(direccion),
|
|
83
|
+
CmnaOrigen: comuna,
|
|
84
|
+
CiudadOrigen: ciudad || comuna || '',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Normaliza emisor desde diferentes formatos de entrada
|
|
90
|
+
* Detecta si viene en formato boleta o DTE y normaliza
|
|
91
|
+
*
|
|
92
|
+
* @param {Object} emisor - Emisor en cualquier formato
|
|
93
|
+
* @param {boolean} [esBoleta=false] - Si es formato boleta
|
|
94
|
+
* @returns {Object} Emisor normalizado
|
|
95
|
+
*/
|
|
96
|
+
function normalizeEmisor(emisor, esBoleta = false) {
|
|
97
|
+
if (esBoleta) {
|
|
98
|
+
return {
|
|
99
|
+
RUTEmisor: emisor.RUTEmisor,
|
|
100
|
+
RznSocEmisor: sanitizeSiiText(emisor.RznSocEmisor || emisor.RznSoc),
|
|
101
|
+
GiroEmisor: sanitizeSiiText(emisor.GiroEmisor || emisor.GiroEmis),
|
|
102
|
+
DirOrigen: sanitizeSiiText(emisor.DirOrigen),
|
|
103
|
+
CmnaOrigen: emisor.CmnaOrigen,
|
|
104
|
+
CiudadOrigen: emisor.CiudadOrigen || emisor.CmnaOrigen || '',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = {
|
|
109
|
+
RUTEmisor: emisor.RUTEmisor,
|
|
110
|
+
RznSoc: sanitizeSiiText(emisor.RznSoc || emisor.RznSocEmisor),
|
|
111
|
+
GiroEmis: sanitizeSiiText(emisor.GiroEmis || emisor.GiroEmisor),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
if (emisor.Acteco) result.Acteco = emisor.Acteco;
|
|
115
|
+
if (emisor.Telefono) result.Telefono = emisor.Telefono;
|
|
116
|
+
if (emisor.CorreoEmisor) result.CorreoEmisor = emisor.CorreoEmisor;
|
|
117
|
+
|
|
118
|
+
result.DirOrigen = sanitizeSiiText(emisor.DirOrigen);
|
|
119
|
+
result.CmnaOrigen = emisor.CmnaOrigen;
|
|
120
|
+
result.CiudadOrigen = emisor.CiudadOrigen || emisor.CmnaOrigen || '';
|
|
121
|
+
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Valida datos de emisor
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} emisor - Datos del emisor
|
|
129
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
130
|
+
*/
|
|
131
|
+
function validarEmisor(emisor) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
|
|
134
|
+
if (!emisor) {
|
|
135
|
+
return { valid: false, errors: ['Emisor no proporcionado'] };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!emisor.RUTEmisor) {
|
|
139
|
+
errors.push('RUT de emisor requerido');
|
|
140
|
+
} else if (!validarRut(emisor.RUTEmisor)) {
|
|
141
|
+
errors.push('RUT de emisor inválido');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const razonSocial = emisor.RznSoc || emisor.RznSocEmisor;
|
|
145
|
+
if (!razonSocial) {
|
|
146
|
+
errors.push('Razón social requerida');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const giro = emisor.GiroEmis || emisor.GiroEmisor;
|
|
150
|
+
if (!giro) {
|
|
151
|
+
errors.push('Giro requerido');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!emisor.DirOrigen) {
|
|
155
|
+
errors.push('Dirección de origen requerida');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!emisor.CmnaOrigen) {
|
|
159
|
+
errors.push('Comuna de origen requerida');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { valid: errors.length === 0, errors };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ============================================
|
|
166
|
+
// EXPORTS
|
|
167
|
+
// ============================================
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
buildEmisor,
|
|
171
|
+
buildEmisorBoleta,
|
|
172
|
+
normalizeEmisor,
|
|
173
|
+
validarEmisor,
|
|
174
|
+
};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
|
+
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
+
/**
|
|
4
|
+
* endpoints.js - URLs centralizadas del SII
|
|
5
|
+
*
|
|
6
|
+
* Centraliza todas las URLs de los servicios del SII para evitar
|
|
7
|
+
* duplicación y facilitar el mantenimiento.
|
|
8
|
+
*
|
|
9
|
+
* @module utils/endpoints
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hosts base por ambiente
|
|
14
|
+
*/
|
|
15
|
+
const HOSTS = {
|
|
16
|
+
certificacion: 'maullin.sii.cl',
|
|
17
|
+
produccion: 'palena.sii.cl',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hosts para API REST de Boletas
|
|
22
|
+
*/
|
|
23
|
+
const REST_HOSTS = {
|
|
24
|
+
certificacion: {
|
|
25
|
+
api: 'apicert.sii.cl',
|
|
26
|
+
envio: 'pangal.sii.cl',
|
|
27
|
+
},
|
|
28
|
+
produccion: {
|
|
29
|
+
api: 'api.sii.cl',
|
|
30
|
+
envio: 'rahue.sii.cl',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Endpoints SOAP para DTEUpload
|
|
36
|
+
*/
|
|
37
|
+
const SOAP_ENDPOINTS = {
|
|
38
|
+
certificacion: {
|
|
39
|
+
seed: 'https://maullin.sii.cl/DTEWS/CrSeed.jws',
|
|
40
|
+
token: 'https://maullin.sii.cl/DTEWS/GetTokenFromSeed.jws',
|
|
41
|
+
upload: 'https://maullin.sii.cl/cgi_dte/UPL/DTEUpload',
|
|
42
|
+
estado: 'https://maullin.sii.cl/DTEWS/QueryEstUp.jws',
|
|
43
|
+
estadoDte: 'https://maullin.sii.cl/DTEWS/QueryEstDte.jws',
|
|
44
|
+
},
|
|
45
|
+
produccion: {
|
|
46
|
+
seed: 'https://palena.sii.cl/DTEWS/CrSeed.jws',
|
|
47
|
+
token: 'https://palena.sii.cl/DTEWS/GetTokenFromSeed.jws',
|
|
48
|
+
upload: 'https://palena.sii.cl/cgi_dte/UPL/DTEUpload',
|
|
49
|
+
estado: 'https://palena.sii.cl/DTEWS/QueryEstUp.jws',
|
|
50
|
+
estadoDte: 'https://palena.sii.cl/DTEWS/QueryEstDte.jws',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Endpoints REST para Boletas Electrónicas
|
|
56
|
+
*/
|
|
57
|
+
const REST_ENDPOINTS = {
|
|
58
|
+
certificacion: {
|
|
59
|
+
semilla: 'https://apicert.sii.cl/recursos/v1/boleta.electronica.semilla',
|
|
60
|
+
token: 'https://apicert.sii.cl/recursos/v1/boleta.electronica.token',
|
|
61
|
+
envio: 'https://pangal.sii.cl/recursos/v1/boleta.electronica.envio',
|
|
62
|
+
estado: 'https://apicert.sii.cl/recursos/v1/boleta.electronica.envio/',
|
|
63
|
+
},
|
|
64
|
+
produccion: {
|
|
65
|
+
semilla: 'https://api.sii.cl/recursos/v1/boleta.electronica.semilla',
|
|
66
|
+
token: 'https://api.sii.cl/recursos/v1/boleta.electronica.token',
|
|
67
|
+
envio: 'https://rahue.sii.cl/recursos/v1/boleta.electronica.envio',
|
|
68
|
+
estado: 'https://api.sii.cl/recursos/v1/boleta.electronica.envio/',
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Endpoints de Certificación (portal pe_*)
|
|
74
|
+
*/
|
|
75
|
+
const CERT_ENDPOINTS = {
|
|
76
|
+
// Generación de sets
|
|
77
|
+
generar: '/cvc_cgi/dte/pe_generar',
|
|
78
|
+
generar1: '/cvc_cgi/dte/pe_generar1',
|
|
79
|
+
|
|
80
|
+
// Avance de certificación
|
|
81
|
+
avance1: '/cvc_cgi/dte/pe_avance1',
|
|
82
|
+
avance2: '/cvc_cgi/dte/pe_avance2',
|
|
83
|
+
avance5: '/cvc_cgi/dte/pe_avance5',
|
|
84
|
+
avance7: '/cvc_cgi/dte/pe_avance7',
|
|
85
|
+
|
|
86
|
+
// CAF
|
|
87
|
+
solicitarFolios: '/cvc_cgi/dte/of_solicita_folios',
|
|
88
|
+
estadoFolios: '/cgi_dte/UPL/DTEauth?2',
|
|
89
|
+
anularFolios: '/cvc_cgi/dte/of_anular_caf',
|
|
90
|
+
|
|
91
|
+
// Boletas Electrónicas
|
|
92
|
+
validarBoletaEnvio: '/cgi_dte/UPL/DTEauth?3',
|
|
93
|
+
|
|
94
|
+
// Autenticación
|
|
95
|
+
ingresoRut: '/cvc_cgi/dte/pe_ingrut',
|
|
96
|
+
enrolaUsuarios: '/cvc_cgi/dte/eu_enrola_usuarios',
|
|
97
|
+
construccionDte: '/cvc_cgi/dte/pe_construccion_dte',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Obtener host base para un ambiente
|
|
102
|
+
* @param {string} ambiente - 'certificacion' o 'produccion'
|
|
103
|
+
* @returns {string} Host base (ej: 'maullin.sii.cl')
|
|
104
|
+
*/
|
|
105
|
+
function getHost(ambiente) {
|
|
106
|
+
const amb = String(ambiente).toLowerCase();
|
|
107
|
+
if (!HOSTS[amb]) {
|
|
108
|
+
throw new Error(`Ambiente inválido: ${ambiente}`);
|
|
109
|
+
}
|
|
110
|
+
return HOSTS[amb];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Obtener URL completa para endpoint SOAP
|
|
115
|
+
* @param {string} ambiente - 'certificacion' o 'produccion'
|
|
116
|
+
* @param {string} endpoint - Nombre del endpoint (seed, token, upload, estado)
|
|
117
|
+
* @returns {string} URL completa
|
|
118
|
+
*/
|
|
119
|
+
function getSoapUrl(ambiente, endpoint) {
|
|
120
|
+
const amb = String(ambiente).toLowerCase();
|
|
121
|
+
const urls = SOAP_ENDPOINTS[amb];
|
|
122
|
+
if (!urls) {
|
|
123
|
+
throw new Error(`Ambiente inválido: ${ambiente}`);
|
|
124
|
+
}
|
|
125
|
+
if (!urls[endpoint]) {
|
|
126
|
+
throw new Error(`Endpoint SOAP inválido: ${endpoint}`);
|
|
127
|
+
}
|
|
128
|
+
return urls[endpoint];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Obtener URL completa para endpoint REST (Boletas)
|
|
133
|
+
* @param {string} ambiente - 'certificacion' o 'produccion'
|
|
134
|
+
* @param {string} endpoint - Nombre del endpoint (semilla, token, envio, estado)
|
|
135
|
+
* @returns {string} URL completa
|
|
136
|
+
*/
|
|
137
|
+
function getRestUrl(ambiente, endpoint) {
|
|
138
|
+
const amb = String(ambiente).toLowerCase();
|
|
139
|
+
const urls = REST_ENDPOINTS[amb];
|
|
140
|
+
if (!urls) {
|
|
141
|
+
throw new Error(`Ambiente inválido: ${ambiente}`);
|
|
142
|
+
}
|
|
143
|
+
if (!urls[endpoint]) {
|
|
144
|
+
throw new Error(`Endpoint REST inválido: ${endpoint}`);
|
|
145
|
+
}
|
|
146
|
+
return urls[endpoint];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Obtener URL completa para endpoint de certificación
|
|
151
|
+
* @param {string} ambiente - 'certificacion' o 'produccion'
|
|
152
|
+
* @param {string} endpoint - Nombre del endpoint (generar, avance1, etc)
|
|
153
|
+
* @returns {string} URL completa
|
|
154
|
+
*/
|
|
155
|
+
function getCertUrl(ambiente, endpoint) {
|
|
156
|
+
const host = getHost(ambiente);
|
|
157
|
+
const path = CERT_ENDPOINTS[endpoint];
|
|
158
|
+
if (!path) {
|
|
159
|
+
throw new Error(`Endpoint de certificación inválido: ${endpoint}`);
|
|
160
|
+
}
|
|
161
|
+
return `https://${host}${path}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Obtener path relativo de certificación
|
|
166
|
+
* @param {string} endpoint - Nombre del endpoint
|
|
167
|
+
* @returns {string} Path relativo
|
|
168
|
+
*/
|
|
169
|
+
function getCertPath(endpoint) {
|
|
170
|
+
const path = CERT_ENDPOINTS[endpoint];
|
|
171
|
+
if (!path) {
|
|
172
|
+
throw new Error(`Endpoint de certificación inválido: ${endpoint}`);
|
|
173
|
+
}
|
|
174
|
+
return path;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Obtener todas las URLs para un ambiente
|
|
179
|
+
* @param {string} ambiente - 'certificacion' o 'produccion'
|
|
180
|
+
* @returns {Object} Objeto con todas las URLs
|
|
181
|
+
*/
|
|
182
|
+
function getAllUrls(ambiente) {
|
|
183
|
+
const amb = String(ambiente).toLowerCase();
|
|
184
|
+
return {
|
|
185
|
+
host: HOSTS[amb],
|
|
186
|
+
soap: SOAP_ENDPOINTS[amb],
|
|
187
|
+
rest: REST_ENDPOINTS[amb],
|
|
188
|
+
cert: Object.entries(CERT_ENDPOINTS).reduce((acc, [key, path]) => {
|
|
189
|
+
acc[key] = `https://${HOSTS[amb]}${path}`;
|
|
190
|
+
return acc;
|
|
191
|
+
}, {}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Validar ambiente
|
|
197
|
+
* @param {string} ambiente - Ambiente a validar
|
|
198
|
+
* @returns {string} Ambiente normalizado ('certificacion' o 'produccion')
|
|
199
|
+
* @throws {Error} Si el ambiente es inválido
|
|
200
|
+
*/
|
|
201
|
+
function validateAmbiente(ambiente) {
|
|
202
|
+
const amb = String(ambiente).toLowerCase();
|
|
203
|
+
if (!['certificacion', 'produccion'].includes(amb)) {
|
|
204
|
+
throw new Error(`Ambiente inválido: "${ambiente}", debe ser 'certificacion' o 'produccion'`);
|
|
205
|
+
}
|
|
206
|
+
return amb;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = {
|
|
210
|
+
// Constantes
|
|
211
|
+
HOSTS,
|
|
212
|
+
REST_HOSTS,
|
|
213
|
+
SOAP_ENDPOINTS,
|
|
214
|
+
REST_ENDPOINTS,
|
|
215
|
+
CERT_ENDPOINTS,
|
|
216
|
+
|
|
217
|
+
// Funciones
|
|
218
|
+
getHost,
|
|
219
|
+
getSoapUrl,
|
|
220
|
+
getRestUrl,
|
|
221
|
+
getCertUrl,
|
|
222
|
+
getCertPath,
|
|
223
|
+
getAllUrls,
|
|
224
|
+
validateAmbiente,
|
|
225
|
+
};
|
package/utils/error.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
|
+
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
+
/**
|
|
4
|
+
* Clase de Error Personalizada
|
|
5
|
+
*
|
|
6
|
+
* Errores tipados para mejor manejo y debugging
|
|
7
|
+
*
|
|
8
|
+
* @module dte-sii/utils/error
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// CÓDIGOS DE ERROR
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
const ERROR_CODES = {
|
|
16
|
+
// Configuración
|
|
17
|
+
CONFIG_INVALID: 'CONFIG_INVALID',
|
|
18
|
+
CONFIG_MISSING: 'CONFIG_MISSING',
|
|
19
|
+
|
|
20
|
+
// Certificado
|
|
21
|
+
CERT_NOT_FOUND: 'CERT_NOT_FOUND',
|
|
22
|
+
CERT_INVALID: 'CERT_INVALID',
|
|
23
|
+
CERT_EXPIRED: 'CERT_EXPIRED',
|
|
24
|
+
CERT_PASSWORD_WRONG: 'CERT_PASSWORD_WRONG',
|
|
25
|
+
|
|
26
|
+
// CAF / Folios
|
|
27
|
+
CAF_NOT_FOUND: 'CAF_NOT_FOUND',
|
|
28
|
+
CAF_INVALID: 'CAF_INVALID',
|
|
29
|
+
CAF_EXPIRED: 'CAF_EXPIRED',
|
|
30
|
+
CAF_NO_FOLIOS: 'CAF_NO_FOLIOS',
|
|
31
|
+
FOLIO_OUT_OF_RANGE: 'FOLIO_OUT_OF_RANGE',
|
|
32
|
+
|
|
33
|
+
// DTE
|
|
34
|
+
DTE_INVALID: 'DTE_INVALID',
|
|
35
|
+
DTE_MISSING_FIELDS: 'DTE_MISSING_FIELDS',
|
|
36
|
+
DTE_VALIDATION_FAILED: 'DTE_VALIDATION_FAILED',
|
|
37
|
+
|
|
38
|
+
// Firma
|
|
39
|
+
SIGN_FAILED: 'SIGN_FAILED',
|
|
40
|
+
SIGN_VERIFY_FAILED: 'SIGN_VERIFY_FAILED',
|
|
41
|
+
|
|
42
|
+
// SII
|
|
43
|
+
SII_CONNECTION_FAILED: 'SII_CONNECTION_FAILED',
|
|
44
|
+
SII_AUTH_FAILED: 'SII_AUTH_FAILED',
|
|
45
|
+
SII_REJECTED: 'SII_REJECTED',
|
|
46
|
+
SII_TIMEOUT: 'SII_TIMEOUT',
|
|
47
|
+
SII_INVALID_RESPONSE: 'SII_INVALID_RESPONSE',
|
|
48
|
+
|
|
49
|
+
// XML
|
|
50
|
+
XML_PARSE_FAILED: 'XML_PARSE_FAILED',
|
|
51
|
+
XML_BUILD_FAILED: 'XML_BUILD_FAILED',
|
|
52
|
+
|
|
53
|
+
// General
|
|
54
|
+
UNKNOWN: 'UNKNOWN',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// ============================================
|
|
58
|
+
// CLASE DteSiiError
|
|
59
|
+
// ============================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Error personalizado del paquete
|
|
63
|
+
*/
|
|
64
|
+
class DteSiiError extends Error {
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} message - Mensaje de error
|
|
67
|
+
* @param {string} [code] - Código de error (de ERROR_CODES)
|
|
68
|
+
* @param {Object} [details] - Detalles adicionales
|
|
69
|
+
*/
|
|
70
|
+
constructor(message, code = ERROR_CODES.UNKNOWN, details = {}) {
|
|
71
|
+
super(message);
|
|
72
|
+
this.name = 'DteSiiError';
|
|
73
|
+
this.code = code;
|
|
74
|
+
this.details = details;
|
|
75
|
+
this.timestamp = new Date().toISOString();
|
|
76
|
+
|
|
77
|
+
// Mantener stack trace correcto
|
|
78
|
+
if (Error.captureStackTrace) {
|
|
79
|
+
Error.captureStackTrace(this, DteSiiError);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Convierte el error a objeto plano (para logging/serialización)
|
|
85
|
+
* @returns {Object}
|
|
86
|
+
*/
|
|
87
|
+
toJSON() {
|
|
88
|
+
return {
|
|
89
|
+
name: this.name,
|
|
90
|
+
code: this.code,
|
|
91
|
+
message: this.message,
|
|
92
|
+
details: this.details,
|
|
93
|
+
timestamp: this.timestamp,
|
|
94
|
+
stack: this.stack,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Representación string del error
|
|
100
|
+
* @returns {string}
|
|
101
|
+
*/
|
|
102
|
+
toString() {
|
|
103
|
+
return `[${this.code}] ${this.message}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Verifica si es un error de SII
|
|
108
|
+
* @returns {boolean}
|
|
109
|
+
*/
|
|
110
|
+
isSiiError() {
|
|
111
|
+
return this.code.startsWith('SII_');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Verifica si es un error recuperable (se puede reintentar)
|
|
116
|
+
* @returns {boolean}
|
|
117
|
+
*/
|
|
118
|
+
isRetryable() {
|
|
119
|
+
return [
|
|
120
|
+
ERROR_CODES.SII_CONNECTION_FAILED,
|
|
121
|
+
ERROR_CODES.SII_TIMEOUT,
|
|
122
|
+
].includes(this.code);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============================================
|
|
127
|
+
// FUNCIONES HELPER
|
|
128
|
+
// ============================================
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Crea error de configuración
|
|
132
|
+
*
|
|
133
|
+
* @param {string} message - Mensaje de error
|
|
134
|
+
* @param {Object} [details] - Detalles
|
|
135
|
+
* @returns {DteSiiError}
|
|
136
|
+
*/
|
|
137
|
+
function configError(message, details = {}) {
|
|
138
|
+
return new DteSiiError(message, ERROR_CODES.CONFIG_INVALID, details);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Crea error de certificado
|
|
143
|
+
*
|
|
144
|
+
* @param {string} message - Mensaje de error
|
|
145
|
+
* @param {string} [code] - Código específico de certificado
|
|
146
|
+
* @param {Object} [details] - Detalles
|
|
147
|
+
* @returns {DteSiiError}
|
|
148
|
+
*/
|
|
149
|
+
function certError(message, code = ERROR_CODES.CERT_INVALID, details = {}) {
|
|
150
|
+
return new DteSiiError(message, code, details);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Crea error de CAF/folios
|
|
155
|
+
*
|
|
156
|
+
* @param {string} message - Mensaje de error
|
|
157
|
+
* @param {string} [code] - Código específico de CAF
|
|
158
|
+
* @param {Object} [details] - Detalles
|
|
159
|
+
* @returns {DteSiiError}
|
|
160
|
+
*/
|
|
161
|
+
function cafError(message, code = ERROR_CODES.CAF_INVALID, details = {}) {
|
|
162
|
+
return new DteSiiError(message, code, details);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Crea error de DTE
|
|
167
|
+
*
|
|
168
|
+
* @param {string} message - Mensaje de error
|
|
169
|
+
* @param {Object} [details] - Detalles
|
|
170
|
+
* @returns {DteSiiError}
|
|
171
|
+
*/
|
|
172
|
+
function dteError(message, details = {}) {
|
|
173
|
+
return new DteSiiError(message, ERROR_CODES.DTE_INVALID, details);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Crea error de SII
|
|
178
|
+
*
|
|
179
|
+
* @param {string} message - Mensaje de error
|
|
180
|
+
* @param {string} [code] - Código específico de SII
|
|
181
|
+
* @param {Object} [details] - Detalles
|
|
182
|
+
* @returns {DteSiiError}
|
|
183
|
+
*/
|
|
184
|
+
function siiError(message, code = ERROR_CODES.SII_REJECTED, details = {}) {
|
|
185
|
+
return new DteSiiError(message, code, details);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Crea error de XML
|
|
190
|
+
*
|
|
191
|
+
* @param {string} message - Mensaje de error
|
|
192
|
+
* @param {Object} [details] - Detalles
|
|
193
|
+
* @returns {DteSiiError}
|
|
194
|
+
*/
|
|
195
|
+
function xmlError(message, details = {}) {
|
|
196
|
+
return new DteSiiError(message, ERROR_CODES.XML_PARSE_FAILED, details);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Envuelve un error nativo en DteSiiError
|
|
201
|
+
*
|
|
202
|
+
* @param {Error} error - Error original
|
|
203
|
+
* @param {string} [code] - Código a asignar
|
|
204
|
+
* @param {Object} [details] - Detalles adicionales
|
|
205
|
+
* @returns {DteSiiError}
|
|
206
|
+
*/
|
|
207
|
+
function wrapError(error, code = ERROR_CODES.UNKNOWN, details = {}) {
|
|
208
|
+
if (error instanceof DteSiiError) {
|
|
209
|
+
return error;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return new DteSiiError(
|
|
213
|
+
error.message || String(error),
|
|
214
|
+
code,
|
|
215
|
+
{ ...details, originalError: error.name, stack: error.stack }
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ============================================
|
|
220
|
+
// EXPORTS
|
|
221
|
+
// ============================================
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
DteSiiError,
|
|
225
|
+
ERROR_CODES,
|
|
226
|
+
|
|
227
|
+
// Helpers
|
|
228
|
+
configError,
|
|
229
|
+
certError,
|
|
230
|
+
cafError,
|
|
231
|
+
dteError,
|
|
232
|
+
siiError,
|
|
233
|
+
xmlError,
|
|
234
|
+
wrapError,
|
|
235
|
+
};
|