@devlas/dte-sii 2.5.11 → 2.5.12
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 +378 -378
- package/ConsumoFolio.js +376 -376
- package/Envio.js +196 -196
- package/LICENSE +27 -27
- package/README.md +273 -273
- package/SiiCertificacion.js +14 -7
- package/SiiPortalAuth.js +474 -474
- package/cert/CertRunner.js +8 -0
- package/cert/ConfigLoader.js +131 -131
- package/cert/IntercambioCert.js +427 -427
- package/cert/LibroCompras.js +357 -357
- package/cert/LibroGuias.js +169 -169
- package/cert/LibroVentas.js +151 -151
- package/cert/MuestrasImpresas.js +676 -676
- package/cert/SetBase.js +319 -319
- package/cert/SetBasico.js +411 -411
- package/cert/SetCompra.js +470 -470
- package/cert/SetExenta.js +488 -488
- package/cert/SetGuia.js +281 -281
- package/cert/SetParser.js +1182 -1182
- package/cert/SetsProvider.js +497 -497
- package/cert/Simulacion.js +519 -519
- package/cert/comunaOficina.js +458 -458
- package/cert/index.js +122 -122
- package/cert/types.js +328 -328
- package/package.json +49 -49
package/cert/LibroGuias.js
CHANGED
|
@@ -1,171 +1,171 @@
|
|
|
1
1
|
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
2
|
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
-
/**
|
|
4
|
-
* LibroGuias.js - Generador de Libro de Guías para Certificación SII
|
|
5
|
-
*
|
|
6
|
-
* Construye el libro de guías a partir de los resultados del SetGuia.
|
|
7
|
-
*
|
|
8
|
-
* Documentos incluidos:
|
|
9
|
-
* - Guías de Despacho (52)
|
|
10
|
-
*
|
|
11
|
-
* El resumen (ResumenPeriodo) se genera automáticamente por LibroGuia._buildResumenPeriodo()
|
|
12
|
-
*
|
|
13
|
-
* Tipos de Operación (TpoOper):
|
|
14
|
-
* - 1: Venta (incluye datos de receptor y montos)
|
|
15
|
-
* - 2-9: Traslados varios (sin datos comerciales)
|
|
16
|
-
*
|
|
17
|
-
* Estados especiales:
|
|
18
|
-
* - Anulado=1: Folio anulado (TotFolAnulado)
|
|
19
|
-
* - Anulado=2: Guía anulada (TotGuiaAnulada)
|
|
20
|
-
*
|
|
21
|
-
* @module dte-sii/cert/LibroGuias
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const { LibroGuia } = require('../index');
|
|
25
|
-
|
|
26
|
-
// Valor por defecto para certificación
|
|
27
|
-
const FOLIO_NOTIFICACION_DEFAULT = 3;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {Object} LibroGuiasConfig
|
|
31
|
-
* @property {Object} emisor - Datos del emisor
|
|
32
|
-
* @property {Object} receptor - Datos del receptor
|
|
33
|
-
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
34
|
-
* @property {Object} certificado - Instancia de Certificado
|
|
35
|
-
* @property {number} [folioNotificacion=3] - Folio de notificación SII
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
class LibroGuias {
|
|
39
|
-
/**
|
|
40
|
-
* @param {LibroGuiasConfig} config
|
|
41
|
-
*/
|
|
42
|
-
constructor(config) {
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.emisor = config.emisor;
|
|
45
|
-
this.receptor = config.receptor;
|
|
46
|
-
this.periodo = config.periodo;
|
|
47
|
-
this.certificado = config.certificado;
|
|
48
|
-
this.folioNotificacion = config.folioNotificacion || FOLIO_NOTIFICACION_DEFAULT;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Genera el libro de guías desde los resultados del SetGuia
|
|
53
|
-
* @param {Object} setGuiaResult - Resultado de SetGuia.ejecutar()
|
|
54
|
-
* @param {Object} [options] - Opciones adicionales
|
|
55
|
-
* @param {Object} [options.casosLibro] - Configuración especial por caso (anulado, operacion)
|
|
56
|
-
* @returns {Object} { libro, xml, detalle }
|
|
57
|
-
*/
|
|
58
|
-
generar(setGuiaResult, options = {}) {
|
|
59
|
-
const { documentos } = setGuiaResult;
|
|
60
|
-
|
|
61
|
-
if (!documentos || documentos.length === 0) {
|
|
62
|
-
throw new Error('LibroGuias: No hay documentos del SetGuia para generar libro');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const casosLibro = options.casosLibro || this._getDefaultCasosLibro(documentos.length);
|
|
66
|
-
const detalles = [];
|
|
67
|
-
|
|
68
|
-
// Procesar cada documento del set guía
|
|
69
|
-
for (let i = 0; i < documentos.length; i++) {
|
|
70
|
-
const doc = documentos[i];
|
|
71
|
-
const casoConfig = casosLibro[i] || {};
|
|
72
|
-
const detalle = this._buildDetalle(doc, casoConfig);
|
|
73
|
-
detalles.push(detalle);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Crear libro (el resumen se genera automáticamente en LibroGuia._buildResumenPeriodo)
|
|
77
|
-
const libro = new LibroGuia(this.certificado);
|
|
78
|
-
libro.setCaratula({
|
|
79
|
-
RutEmisorLibro: this.emisor.rut,
|
|
80
|
-
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
81
|
-
PeriodoTributario: this.periodo,
|
|
82
|
-
FchResol: this.emisor.fch_resol,
|
|
83
|
-
NroResol: this.emisor.nro_resol,
|
|
84
|
-
TipoLibro: 'ESPECIAL',
|
|
85
|
-
TipoEnvio: 'TOTAL',
|
|
86
|
-
FolioNotificacion: this.folioNotificacion,
|
|
87
|
-
});
|
|
88
|
-
libro.setDetalle(detalles);
|
|
89
|
-
libro.generar();
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
libro,
|
|
93
|
-
xml: libro.getXML(),
|
|
94
|
-
detalle: detalles,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Configuración por defecto para casos de certificación:
|
|
100
|
-
* - Caso 1 (índice 0): Normal
|
|
101
|
-
* - Caso 2 (índice 1): Operacion=1 (modificación)
|
|
102
|
-
* - Caso 3 (índice 2): Anulado=2 (guía anulada)
|
|
103
|
-
* @private
|
|
104
|
-
*/
|
|
105
|
-
_getDefaultCasosLibro(count) {
|
|
106
|
-
const casos = [];
|
|
107
|
-
for (let i = 0; i < count; i++) {
|
|
108
|
-
const caso = {};
|
|
109
|
-
if (i === 1) caso.operacion = 1; // Segundo caso: modificación
|
|
110
|
-
if (i === 2) caso.anulado = 2; // Tercer caso: anulado
|
|
111
|
-
casos.push(caso);
|
|
112
|
-
}
|
|
113
|
-
return casos;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Construye un registro de detalle desde un documento
|
|
118
|
-
* @private
|
|
119
|
-
*/
|
|
120
|
-
_buildDetalle(doc, casoConfig) {
|
|
121
|
-
const totales = doc.totales || {};
|
|
122
|
-
const fechaDoc = doc.fecha || `${this.periodo}-15`;
|
|
123
|
-
|
|
124
|
-
// Determinar tipo de operación desde el documento o caso
|
|
125
|
-
// IndTraslado=1 (venta) => TpoOper=1
|
|
126
|
-
const indTraslado = doc.indTraslado || 1;
|
|
127
|
-
const tpoOper = indTraslado === 1 ? 1 : indTraslado;
|
|
128
|
-
|
|
129
|
-
const detalle = {
|
|
130
|
-
Folio: doc.folio,
|
|
131
|
-
TpoOper: tpoOper,
|
|
132
|
-
FchDoc: fechaDoc,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Anulado (1=folio anulado, 2=guía anulada)
|
|
136
|
-
if (casoConfig.anulado) {
|
|
137
|
-
detalle.Anulado = casoConfig.anulado;
|
|
138
|
-
detalle.MntTotal = 0;
|
|
139
|
-
// No incluir otros campos en guías anuladas
|
|
140
|
-
return detalle;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Operación (1=modificación de texto)
|
|
144
|
-
if (casoConfig.operacion) {
|
|
145
|
-
detalle.Operacion = casoConfig.operacion;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Para ventas (TpoOper=1), incluir datos comerciales
|
|
149
|
-
if (tpoOper === 1) {
|
|
150
|
-
if (this.receptor.rut) detalle.RUTDoc = this.receptor.rut;
|
|
151
|
-
if (this.receptor.razon_social) detalle.RznSoc = this.receptor.razon_social;
|
|
152
|
-
|
|
153
|
-
const mntNeto = Number(totales.MntNeto || 0);
|
|
154
|
-
const tasaIva = Number(totales.TasaIVA || 19);
|
|
155
|
-
const iva = Number(totales.IVA || 0);
|
|
156
|
-
|
|
157
|
-
if (mntNeto > 0) {
|
|
158
|
-
detalle.MntNeto = mntNeto;
|
|
159
|
-
detalle.TasaImp = tasaIva;
|
|
160
|
-
detalle.IVA = iva > 0 ? iva : Math.round(mntNeto * (tasaIva / 100));
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Monto Total
|
|
165
|
-
detalle.MntTotal = Number(totales.MntTotal || 0);
|
|
166
|
-
|
|
167
|
-
return detalle;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
module.exports = LibroGuias;
|
|
3
|
+
/**
|
|
4
|
+
* LibroGuias.js - Generador de Libro de Guías para Certificación SII
|
|
5
|
+
*
|
|
6
|
+
* Construye el libro de guías a partir de los resultados del SetGuia.
|
|
7
|
+
*
|
|
8
|
+
* Documentos incluidos:
|
|
9
|
+
* - Guías de Despacho (52)
|
|
10
|
+
*
|
|
11
|
+
* El resumen (ResumenPeriodo) se genera automáticamente por LibroGuia._buildResumenPeriodo()
|
|
12
|
+
*
|
|
13
|
+
* Tipos de Operación (TpoOper):
|
|
14
|
+
* - 1: Venta (incluye datos de receptor y montos)
|
|
15
|
+
* - 2-9: Traslados varios (sin datos comerciales)
|
|
16
|
+
*
|
|
17
|
+
* Estados especiales:
|
|
18
|
+
* - Anulado=1: Folio anulado (TotFolAnulado)
|
|
19
|
+
* - Anulado=2: Guía anulada (TotGuiaAnulada)
|
|
20
|
+
*
|
|
21
|
+
* @module dte-sii/cert/LibroGuias
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const { LibroGuia } = require('../index');
|
|
25
|
+
|
|
26
|
+
// Valor por defecto para certificación
|
|
27
|
+
const FOLIO_NOTIFICACION_DEFAULT = 3;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} LibroGuiasConfig
|
|
31
|
+
* @property {Object} emisor - Datos del emisor
|
|
32
|
+
* @property {Object} receptor - Datos del receptor
|
|
33
|
+
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
34
|
+
* @property {Object} certificado - Instancia de Certificado
|
|
35
|
+
* @property {number} [folioNotificacion=3] - Folio de notificación SII
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
class LibroGuias {
|
|
39
|
+
/**
|
|
40
|
+
* @param {LibroGuiasConfig} config
|
|
41
|
+
*/
|
|
42
|
+
constructor(config) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.emisor = config.emisor;
|
|
45
|
+
this.receptor = config.receptor;
|
|
46
|
+
this.periodo = config.periodo;
|
|
47
|
+
this.certificado = config.certificado;
|
|
48
|
+
this.folioNotificacion = config.folioNotificacion || FOLIO_NOTIFICACION_DEFAULT;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Genera el libro de guías desde los resultados del SetGuia
|
|
53
|
+
* @param {Object} setGuiaResult - Resultado de SetGuia.ejecutar()
|
|
54
|
+
* @param {Object} [options] - Opciones adicionales
|
|
55
|
+
* @param {Object} [options.casosLibro] - Configuración especial por caso (anulado, operacion)
|
|
56
|
+
* @returns {Object} { libro, xml, detalle }
|
|
57
|
+
*/
|
|
58
|
+
generar(setGuiaResult, options = {}) {
|
|
59
|
+
const { documentos } = setGuiaResult;
|
|
60
|
+
|
|
61
|
+
if (!documentos || documentos.length === 0) {
|
|
62
|
+
throw new Error('LibroGuias: No hay documentos del SetGuia para generar libro');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const casosLibro = options.casosLibro || this._getDefaultCasosLibro(documentos.length);
|
|
66
|
+
const detalles = [];
|
|
67
|
+
|
|
68
|
+
// Procesar cada documento del set guía
|
|
69
|
+
for (let i = 0; i < documentos.length; i++) {
|
|
70
|
+
const doc = documentos[i];
|
|
71
|
+
const casoConfig = casosLibro[i] || {};
|
|
72
|
+
const detalle = this._buildDetalle(doc, casoConfig);
|
|
73
|
+
detalles.push(detalle);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Crear libro (el resumen se genera automáticamente en LibroGuia._buildResumenPeriodo)
|
|
77
|
+
const libro = new LibroGuia(this.certificado);
|
|
78
|
+
libro.setCaratula({
|
|
79
|
+
RutEmisorLibro: this.emisor.rut,
|
|
80
|
+
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
81
|
+
PeriodoTributario: this.periodo,
|
|
82
|
+
FchResol: this.emisor.fch_resol,
|
|
83
|
+
NroResol: this.emisor.nro_resol,
|
|
84
|
+
TipoLibro: 'ESPECIAL',
|
|
85
|
+
TipoEnvio: 'TOTAL',
|
|
86
|
+
FolioNotificacion: this.folioNotificacion,
|
|
87
|
+
});
|
|
88
|
+
libro.setDetalle(detalles);
|
|
89
|
+
libro.generar();
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
libro,
|
|
93
|
+
xml: libro.getXML(),
|
|
94
|
+
detalle: detalles,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Configuración por defecto para casos de certificación:
|
|
100
|
+
* - Caso 1 (índice 0): Normal
|
|
101
|
+
* - Caso 2 (índice 1): Operacion=1 (modificación)
|
|
102
|
+
* - Caso 3 (índice 2): Anulado=2 (guía anulada)
|
|
103
|
+
* @private
|
|
104
|
+
*/
|
|
105
|
+
_getDefaultCasosLibro(count) {
|
|
106
|
+
const casos = [];
|
|
107
|
+
for (let i = 0; i < count; i++) {
|
|
108
|
+
const caso = {};
|
|
109
|
+
if (i === 1) caso.operacion = 1; // Segundo caso: modificación
|
|
110
|
+
if (i === 2) caso.anulado = 2; // Tercer caso: anulado
|
|
111
|
+
casos.push(caso);
|
|
112
|
+
}
|
|
113
|
+
return casos;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Construye un registro de detalle desde un documento
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
_buildDetalle(doc, casoConfig) {
|
|
121
|
+
const totales = doc.totales || {};
|
|
122
|
+
const fechaDoc = doc.fecha || `${this.periodo}-15`;
|
|
123
|
+
|
|
124
|
+
// Determinar tipo de operación desde el documento o caso
|
|
125
|
+
// IndTraslado=1 (venta) => TpoOper=1
|
|
126
|
+
const indTraslado = doc.indTraslado || 1;
|
|
127
|
+
const tpoOper = indTraslado === 1 ? 1 : indTraslado;
|
|
128
|
+
|
|
129
|
+
const detalle = {
|
|
130
|
+
Folio: doc.folio,
|
|
131
|
+
TpoOper: tpoOper,
|
|
132
|
+
FchDoc: fechaDoc,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Anulado (1=folio anulado, 2=guía anulada)
|
|
136
|
+
if (casoConfig.anulado) {
|
|
137
|
+
detalle.Anulado = casoConfig.anulado;
|
|
138
|
+
detalle.MntTotal = 0;
|
|
139
|
+
// No incluir otros campos en guías anuladas
|
|
140
|
+
return detalle;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Operación (1=modificación de texto)
|
|
144
|
+
if (casoConfig.operacion) {
|
|
145
|
+
detalle.Operacion = casoConfig.operacion;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Para ventas (TpoOper=1), incluir datos comerciales
|
|
149
|
+
if (tpoOper === 1) {
|
|
150
|
+
if (this.receptor.rut) detalle.RUTDoc = this.receptor.rut;
|
|
151
|
+
if (this.receptor.razon_social) detalle.RznSoc = this.receptor.razon_social;
|
|
152
|
+
|
|
153
|
+
const mntNeto = Number(totales.MntNeto || 0);
|
|
154
|
+
const tasaIva = Number(totales.TasaIVA || 19);
|
|
155
|
+
const iva = Number(totales.IVA || 0);
|
|
156
|
+
|
|
157
|
+
if (mntNeto > 0) {
|
|
158
|
+
detalle.MntNeto = mntNeto;
|
|
159
|
+
detalle.TasaImp = tasaIva;
|
|
160
|
+
detalle.IVA = iva > 0 ? iva : Math.round(mntNeto * (tasaIva / 100));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Monto Total
|
|
165
|
+
detalle.MntTotal = Number(totales.MntTotal || 0);
|
|
166
|
+
|
|
167
|
+
return detalle;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
module.exports = LibroGuias;
|
package/cert/LibroVentas.js
CHANGED
|
@@ -1,153 +1,153 @@
|
|
|
1
1
|
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
2
|
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
-
/**
|
|
4
|
-
* LibroVentas.js - Generador de Libro de Ventas para Certificación SII
|
|
5
|
-
*
|
|
6
|
-
* Construye el libro de ventas a partir de los resultados del SetBasico.
|
|
7
|
-
*
|
|
8
|
-
* Documentos incluidos:
|
|
9
|
-
* - Facturas (33)
|
|
10
|
-
* - Notas de Crédito (61)
|
|
11
|
-
* - Notas de Débito (56)
|
|
12
|
-
*
|
|
13
|
-
* @module dte-sii/cert/LibroVentas
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const { LibroCompraVenta } = require('../index');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} LibroVentasConfig
|
|
20
|
-
* @property {Object} emisor - Datos del emisor
|
|
21
|
-
* @property {Object} receptor - Datos del receptor (para RUTDoc/RznSoc)
|
|
22
|
-
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
23
|
-
* @property {Object} certificado - Instancia de Certificado
|
|
24
|
-
* @property {string} [signoNC='POSITIVO'] - 'POSITIVO' o 'NEGATIVO' para NC
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
class LibroVentas {
|
|
28
|
-
/**
|
|
29
|
-
* @param {LibroVentasConfig} config
|
|
30
|
-
*/
|
|
31
|
-
constructor(config) {
|
|
32
|
-
this.config = config;
|
|
33
|
-
this.emisor = config.emisor;
|
|
34
|
-
this.receptor = config.receptor;
|
|
35
|
-
this.periodo = config.periodo;
|
|
36
|
-
this.certificado = config.certificado;
|
|
37
|
-
this.signoNC = (config.signoNC || 'POSITIVO').toUpperCase();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Genera el libro de ventas desde los resultados del SetBasico
|
|
42
|
-
* @param {Object} setBasicoResult - Resultado de SetBasico.ejecutar()
|
|
43
|
-
* @returns {Object} { libro, xml, detalle, resumen }
|
|
44
|
-
*/
|
|
45
|
-
generar(setBasicoResult) {
|
|
46
|
-
const { documentos } = setBasicoResult;
|
|
47
|
-
|
|
48
|
-
if (!documentos || documentos.length === 0) {
|
|
49
|
-
throw new Error('LibroVentas: No hay documentos del SetBasico para generar libro');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const fechaBase = `${this.periodo}-15`;
|
|
53
|
-
const detalles = [];
|
|
54
|
-
const resumenMap = new Map();
|
|
55
|
-
|
|
56
|
-
// Procesar cada documento del set básico
|
|
57
|
-
for (const doc of documentos) {
|
|
58
|
-
const tipoDte = doc.tipoDte;
|
|
59
|
-
const folio = doc.folio;
|
|
60
|
-
const totales = doc.totales || {};
|
|
61
|
-
|
|
62
|
-
// Calcular signo (NC puede ser negativa según configuración)
|
|
63
|
-
const sign = this._getSignByTipoDte(tipoDte);
|
|
64
|
-
|
|
65
|
-
// Construir detalle
|
|
66
|
-
const detalle = {
|
|
67
|
-
TpoDoc: tipoDte,
|
|
68
|
-
NroDoc: folio,
|
|
69
|
-
FchDoc: fechaBase,
|
|
70
|
-
RUTDoc: this.receptor.rut,
|
|
71
|
-
RznSoc: this.receptor.razon_social,
|
|
72
|
-
MntExe: Math.round(Number(totales.MntExe || 0) * sign),
|
|
73
|
-
MntNeto: Math.round(Number(totales.MntNeto || 0) * sign),
|
|
74
|
-
MntIVA: Math.round(Number(totales.IVA || 0) * sign),
|
|
75
|
-
MntTotal: Math.round(Number(totales.MntTotal || 0) * sign),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Agregar TasaImp si hay IVA
|
|
79
|
-
if (totales.TasaIVA !== undefined && totales.TasaIVA > 0) {
|
|
80
|
-
detalle.TasaImp = Number(totales.TasaIVA);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
detalles.push(detalle);
|
|
84
|
-
|
|
85
|
-
// Acumular resumen por tipo de documento
|
|
86
|
-
this._addToResumen(resumenMap, tipoDte, totales, sign);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Convertir resumen a array
|
|
90
|
-
const resumen = Array.from(resumenMap.values());
|
|
91
|
-
|
|
92
|
-
// Crear libro
|
|
93
|
-
const libro = new LibroCompraVenta(this.certificado);
|
|
94
|
-
libro.setCaratula({
|
|
95
|
-
RutEmisorLibro: this.emisor.rut,
|
|
96
|
-
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
97
|
-
PeriodoTributario: this.periodo,
|
|
98
|
-
FchResol: this.emisor.fch_resol,
|
|
99
|
-
NroResol: this.emisor.nro_resol,
|
|
100
|
-
TipoOperacion: 'VENTA',
|
|
101
|
-
TipoLibro: 'MENSUAL',
|
|
102
|
-
TipoEnvio: 'TOTAL',
|
|
103
|
-
});
|
|
104
|
-
libro.setResumen(resumen);
|
|
105
|
-
libro.setDetalle(detalles);
|
|
106
|
-
libro.generar();
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
libro,
|
|
110
|
-
xml: libro.getXML(),
|
|
111
|
-
detalle: detalles,
|
|
112
|
-
resumen,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Obtiene el signo para un tipo de DTE
|
|
118
|
-
* @private
|
|
119
|
-
*/
|
|
120
|
-
_getSignByTipoDte(tipoDte) {
|
|
121
|
-
// NC (61) puede tener signo negativo según configuración
|
|
122
|
-
if (tipoDte === 61 && this.signoNC === 'NEGATIVO') {
|
|
123
|
-
return -1;
|
|
124
|
-
}
|
|
125
|
-
return 1;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Agrega totales al resumen acumulado
|
|
130
|
-
* @private
|
|
131
|
-
*/
|
|
132
|
-
_addToResumen(resumenMap, tipoDte, totales, sign) {
|
|
133
|
-
if (!resumenMap.has(tipoDte)) {
|
|
134
|
-
resumenMap.set(tipoDte, {
|
|
135
|
-
TpoDoc: tipoDte,
|
|
136
|
-
TotDoc: 0,
|
|
137
|
-
TotMntExe: 0,
|
|
138
|
-
TotMntNeto: 0,
|
|
139
|
-
TotMntIVA: 0,
|
|
140
|
-
TotMntTotal: 0,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const r = resumenMap.get(tipoDte);
|
|
145
|
-
r.TotDoc += 1;
|
|
146
|
-
r.TotMntExe += Math.round(Number(totales.MntExe || 0) * sign);
|
|
147
|
-
r.TotMntNeto += Math.round(Number(totales.MntNeto || 0) * sign);
|
|
148
|
-
r.TotMntIVA += Math.round(Number(totales.IVA || 0) * sign);
|
|
149
|
-
r.TotMntTotal += Math.round(Number(totales.MntTotal || 0) * sign);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
module.exports = LibroVentas;
|
|
3
|
+
/**
|
|
4
|
+
* LibroVentas.js - Generador de Libro de Ventas para Certificación SII
|
|
5
|
+
*
|
|
6
|
+
* Construye el libro de ventas a partir de los resultados del SetBasico.
|
|
7
|
+
*
|
|
8
|
+
* Documentos incluidos:
|
|
9
|
+
* - Facturas (33)
|
|
10
|
+
* - Notas de Crédito (61)
|
|
11
|
+
* - Notas de Débito (56)
|
|
12
|
+
*
|
|
13
|
+
* @module dte-sii/cert/LibroVentas
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { LibroCompraVenta } = require('../index');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} LibroVentasConfig
|
|
20
|
+
* @property {Object} emisor - Datos del emisor
|
|
21
|
+
* @property {Object} receptor - Datos del receptor (para RUTDoc/RznSoc)
|
|
22
|
+
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
23
|
+
* @property {Object} certificado - Instancia de Certificado
|
|
24
|
+
* @property {string} [signoNC='POSITIVO'] - 'POSITIVO' o 'NEGATIVO' para NC
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
class LibroVentas {
|
|
28
|
+
/**
|
|
29
|
+
* @param {LibroVentasConfig} config
|
|
30
|
+
*/
|
|
31
|
+
constructor(config) {
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.emisor = config.emisor;
|
|
34
|
+
this.receptor = config.receptor;
|
|
35
|
+
this.periodo = config.periodo;
|
|
36
|
+
this.certificado = config.certificado;
|
|
37
|
+
this.signoNC = (config.signoNC || 'POSITIVO').toUpperCase();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Genera el libro de ventas desde los resultados del SetBasico
|
|
42
|
+
* @param {Object} setBasicoResult - Resultado de SetBasico.ejecutar()
|
|
43
|
+
* @returns {Object} { libro, xml, detalle, resumen }
|
|
44
|
+
*/
|
|
45
|
+
generar(setBasicoResult) {
|
|
46
|
+
const { documentos } = setBasicoResult;
|
|
47
|
+
|
|
48
|
+
if (!documentos || documentos.length === 0) {
|
|
49
|
+
throw new Error('LibroVentas: No hay documentos del SetBasico para generar libro');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fechaBase = `${this.periodo}-15`;
|
|
53
|
+
const detalles = [];
|
|
54
|
+
const resumenMap = new Map();
|
|
55
|
+
|
|
56
|
+
// Procesar cada documento del set básico
|
|
57
|
+
for (const doc of documentos) {
|
|
58
|
+
const tipoDte = doc.tipoDte;
|
|
59
|
+
const folio = doc.folio;
|
|
60
|
+
const totales = doc.totales || {};
|
|
61
|
+
|
|
62
|
+
// Calcular signo (NC puede ser negativa según configuración)
|
|
63
|
+
const sign = this._getSignByTipoDte(tipoDte);
|
|
64
|
+
|
|
65
|
+
// Construir detalle
|
|
66
|
+
const detalle = {
|
|
67
|
+
TpoDoc: tipoDte,
|
|
68
|
+
NroDoc: folio,
|
|
69
|
+
FchDoc: fechaBase,
|
|
70
|
+
RUTDoc: this.receptor.rut,
|
|
71
|
+
RznSoc: this.receptor.razon_social,
|
|
72
|
+
MntExe: Math.round(Number(totales.MntExe || 0) * sign),
|
|
73
|
+
MntNeto: Math.round(Number(totales.MntNeto || 0) * sign),
|
|
74
|
+
MntIVA: Math.round(Number(totales.IVA || 0) * sign),
|
|
75
|
+
MntTotal: Math.round(Number(totales.MntTotal || 0) * sign),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Agregar TasaImp si hay IVA
|
|
79
|
+
if (totales.TasaIVA !== undefined && totales.TasaIVA > 0) {
|
|
80
|
+
detalle.TasaImp = Number(totales.TasaIVA);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
detalles.push(detalle);
|
|
84
|
+
|
|
85
|
+
// Acumular resumen por tipo de documento
|
|
86
|
+
this._addToResumen(resumenMap, tipoDte, totales, sign);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Convertir resumen a array
|
|
90
|
+
const resumen = Array.from(resumenMap.values());
|
|
91
|
+
|
|
92
|
+
// Crear libro
|
|
93
|
+
const libro = new LibroCompraVenta(this.certificado);
|
|
94
|
+
libro.setCaratula({
|
|
95
|
+
RutEmisorLibro: this.emisor.rut,
|
|
96
|
+
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
97
|
+
PeriodoTributario: this.periodo,
|
|
98
|
+
FchResol: this.emisor.fch_resol,
|
|
99
|
+
NroResol: this.emisor.nro_resol,
|
|
100
|
+
TipoOperacion: 'VENTA',
|
|
101
|
+
TipoLibro: 'MENSUAL',
|
|
102
|
+
TipoEnvio: 'TOTAL',
|
|
103
|
+
});
|
|
104
|
+
libro.setResumen(resumen);
|
|
105
|
+
libro.setDetalle(detalles);
|
|
106
|
+
libro.generar();
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
libro,
|
|
110
|
+
xml: libro.getXML(),
|
|
111
|
+
detalle: detalles,
|
|
112
|
+
resumen,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Obtiene el signo para un tipo de DTE
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
_getSignByTipoDte(tipoDte) {
|
|
121
|
+
// NC (61) puede tener signo negativo según configuración
|
|
122
|
+
if (tipoDte === 61 && this.signoNC === 'NEGATIVO') {
|
|
123
|
+
return -1;
|
|
124
|
+
}
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Agrega totales al resumen acumulado
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
_addToResumen(resumenMap, tipoDte, totales, sign) {
|
|
133
|
+
if (!resumenMap.has(tipoDte)) {
|
|
134
|
+
resumenMap.set(tipoDte, {
|
|
135
|
+
TpoDoc: tipoDte,
|
|
136
|
+
TotDoc: 0,
|
|
137
|
+
TotMntExe: 0,
|
|
138
|
+
TotMntNeto: 0,
|
|
139
|
+
TotMntIVA: 0,
|
|
140
|
+
TotMntTotal: 0,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const r = resumenMap.get(tipoDte);
|
|
145
|
+
r.TotDoc += 1;
|
|
146
|
+
r.TotMntExe += Math.round(Number(totales.MntExe || 0) * sign);
|
|
147
|
+
r.TotMntNeto += Math.round(Number(totales.MntNeto || 0) * sign);
|
|
148
|
+
r.TotMntIVA += Math.round(Number(totales.IVA || 0) * sign);
|
|
149
|
+
r.TotMntTotal += Math.round(Number(totales.MntTotal || 0) * sign);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = LibroVentas;
|