@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/LibroCompras.js
CHANGED
|
@@ -1,359 +1,359 @@
|
|
|
1
1
|
// Copyright (c) 2026 Devlas SpA — https://devlas.cl
|
|
2
2
|
// Licencia MIT. Ver archivo LICENSE para mas detalles.
|
|
3
|
-
/**
|
|
4
|
-
* LibroCompras.js - Generador de Libro de Compras para Certificación SII
|
|
5
|
-
*
|
|
6
|
-
* Construye el libro de compras a partir de los resultados del SetCompra.
|
|
7
|
-
*
|
|
8
|
-
* Documentos incluidos:
|
|
9
|
-
* - Facturas de Compra (46) con IVA Retenido Total
|
|
10
|
-
* - Notas de Crédito (61)
|
|
11
|
-
* - Notas de Débito (56)
|
|
12
|
-
*
|
|
13
|
-
* Maneja campos especiales:
|
|
14
|
-
* - IVANoRec (IVA no recuperable)
|
|
15
|
-
* - IVAUsoComun (IVA de uso común con factor proporcionalidad)
|
|
16
|
-
* - OtrosImp (otros impuestos)
|
|
17
|
-
* - IVARetTotal (IVA retenido total)
|
|
18
|
-
*
|
|
19
|
-
* @module dte-sii/cert/LibroCompras
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
const { LibroCompraVenta } = require('../index');
|
|
23
|
-
|
|
24
|
-
// Factor de proporcionalidad para IVA de uso común (60%)
|
|
25
|
-
const FACTOR_PROPORCIONALIDAD = 0.6;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {Object} LibroComprasConfig
|
|
29
|
-
* @property {Object} emisor - Datos del emisor
|
|
30
|
-
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
31
|
-
* @property {Object} certificado - Instancia de Certificado
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
class LibroCompras {
|
|
35
|
-
/**
|
|
36
|
-
* @param {LibroComprasConfig} config
|
|
37
|
-
*/
|
|
38
|
-
constructor(config) {
|
|
39
|
-
this.config = config;
|
|
40
|
-
this.emisor = config.emisor;
|
|
41
|
-
this.periodo = config.periodo;
|
|
42
|
-
this.certificado = config.certificado;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Genera el libro de compras desde los resultados del SetCompra
|
|
47
|
-
* @param {Object} setCompraResult - Resultado de SetCompra.ejecutar()
|
|
48
|
-
* @returns {Object} { libro, xml, detalle, resumen }
|
|
49
|
-
*/
|
|
50
|
-
generar(setCompraResult) {
|
|
51
|
-
const { documentos } = setCompraResult;
|
|
52
|
-
|
|
53
|
-
if (!documentos || documentos.length === 0) {
|
|
54
|
-
throw new Error('LibroCompras: No hay documentos del SetCompra para generar libro');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const fechaBase = `${this.periodo}-15`;
|
|
58
|
-
const detalles = [];
|
|
59
|
-
|
|
60
|
-
// Procesar cada documento del set compra
|
|
61
|
-
for (const doc of documentos) {
|
|
62
|
-
const detalle = this._buildDetalle(doc, fechaBase);
|
|
63
|
-
detalles.push(detalle);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Calcular resumen automáticamente desde el detalle
|
|
67
|
-
const resumen = this._calcularResumenDesdeDetalle(detalles);
|
|
68
|
-
|
|
69
|
-
// Crear libro
|
|
70
|
-
const libro = new LibroCompraVenta(this.certificado);
|
|
71
|
-
libro.setCaratula({
|
|
72
|
-
RutEmisorLibro: this.emisor.rut,
|
|
73
|
-
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
74
|
-
PeriodoTributario: this.periodo,
|
|
75
|
-
FchResol: this.emisor.fch_resol,
|
|
76
|
-
NroResol: this.emisor.nro_resol,
|
|
77
|
-
TipoOperacion: 'COMPRA',
|
|
78
|
-
TipoLibro: 'MENSUAL',
|
|
79
|
-
TipoEnvio: 'TOTAL',
|
|
80
|
-
});
|
|
81
|
-
libro.setResumen(resumen);
|
|
82
|
-
libro.setDetalle(detalles);
|
|
83
|
-
libro.generar();
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
libro,
|
|
87
|
-
xml: libro.getXML(),
|
|
88
|
-
detalle: detalles,
|
|
89
|
-
resumen,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Genera el libro de compras desde los datos pre-procesados del SII (estructuras)
|
|
95
|
-
* @param {Object} libroComprasData - Datos del libro de compras { detalle, resumen, factorProporcionalidad }
|
|
96
|
-
* @param {string} periodo - Período tributario (YYYY-MM)
|
|
97
|
-
* @returns {Object} { libro, xml, detalle, resumen }
|
|
98
|
-
*/
|
|
99
|
-
generarDesdeEstructuras(libroComprasData, periodo) {
|
|
100
|
-
const { detalle, resumen, factorProporcionalidad } = libroComprasData;
|
|
101
|
-
|
|
102
|
-
if (!detalle || detalle.length === 0) {
|
|
103
|
-
throw new Error('LibroCompras: No hay detalle en los datos del SII');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Ajustar fechas al período correcto y aplicar reglas SII
|
|
107
|
-
const fechaBase = `${periodo}-15`;
|
|
108
|
-
const detalleAjustado = detalle.map(doc => {
|
|
109
|
-
const docAjustado = {
|
|
110
|
-
...doc,
|
|
111
|
-
FchDoc: fechaBase,
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Regla SII: Cuando hay IVAUsoComun, TasaImp y MntIVA van en 0
|
|
115
|
-
if (doc.IVAUsoComun) {
|
|
116
|
-
docAjustado.TasaImp = 0;
|
|
117
|
-
docAjustado.MntIVA = 0;
|
|
118
|
-
}
|
|
119
|
-
// Regla SII: Cuando hay IVANoRec, TasaImp y MntIVA van en 0
|
|
120
|
-
else if (doc.IVANoRec) {
|
|
121
|
-
docAjustado.TasaImp = 0;
|
|
122
|
-
docAjustado.MntIVA = 0;
|
|
123
|
-
}
|
|
124
|
-
// Caso normal: Asegurar TasaImp sea número entero (19) no decimal (0.19)
|
|
125
|
-
else if (doc.TasaImp !== undefined) {
|
|
126
|
-
docAjustado.TasaImp = doc.TasaImp < 1 ? Math.round(doc.TasaImp * 100) : doc.TasaImp;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return docAjustado;
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Recalcular resumen desde el detalle ajustado
|
|
133
|
-
const resumenRecalculado = this._calcularResumenDesdeDetalle(detalleAjustado, factorProporcionalidad);
|
|
134
|
-
|
|
135
|
-
// Crear libro
|
|
136
|
-
const libro = new LibroCompraVenta(this.certificado);
|
|
137
|
-
libro.setCaratula({
|
|
138
|
-
RutEmisorLibro: this.emisor.rut,
|
|
139
|
-
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
140
|
-
PeriodoTributario: periodo,
|
|
141
|
-
FchResol: this.emisor.fch_resol,
|
|
142
|
-
NroResol: this.emisor.nro_resol,
|
|
143
|
-
TipoOperacion: 'COMPRA',
|
|
144
|
-
TipoLibro: 'MENSUAL',
|
|
145
|
-
TipoEnvio: 'TOTAL',
|
|
146
|
-
});
|
|
147
|
-
libro.setResumen(resumenRecalculado);
|
|
148
|
-
libro.setDetalle(detalleAjustado);
|
|
149
|
-
libro.generar();
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
libro,
|
|
153
|
-
xml: libro.getXML(),
|
|
154
|
-
detalle: detalleAjustado,
|
|
155
|
-
resumen: resumenRecalculado,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Construye un registro de detalle desde un documento
|
|
161
|
-
* @private
|
|
162
|
-
*/
|
|
163
|
-
_buildDetalle(doc, fechaBase) {
|
|
164
|
-
const tipoDte = doc.tipoDte;
|
|
165
|
-
const totales = doc.totales || {};
|
|
166
|
-
|
|
167
|
-
// En Factura de Compra (46), el emisor ES el receptor del documento
|
|
168
|
-
// porque nosotros RECIBIMOS la factura de compra
|
|
169
|
-
const rutDoc = this.emisor.rut;
|
|
170
|
-
const rznSoc = this.emisor.razon_social;
|
|
171
|
-
|
|
172
|
-
const detalle = {
|
|
173
|
-
TpoDoc: tipoDte,
|
|
174
|
-
NroDoc: doc.folio,
|
|
175
|
-
FchDoc: fechaBase,
|
|
176
|
-
RUTDoc: rutDoc,
|
|
177
|
-
RznSoc: rznSoc,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// Montos básicos
|
|
181
|
-
const mntNeto = Number(totales.MntNeto || 0);
|
|
182
|
-
const mntExe = Number(totales.MntExe || 0);
|
|
183
|
-
const mntIva = Number(totales.IVA || 0);
|
|
184
|
-
const ivaRetTotal = Number(totales.IVARetTotal || 0);
|
|
185
|
-
|
|
186
|
-
if (mntExe > 0) detalle.MntExe = mntExe;
|
|
187
|
-
if (mntNeto > 0) detalle.MntNeto = mntNeto;
|
|
188
|
-
|
|
189
|
-
// Tasa de IVA - siempre informar si hay MntNeto
|
|
190
|
-
const tasaIva = Number(totales.TasaIVA || 19);
|
|
191
|
-
|
|
192
|
-
// IVA No Recuperable
|
|
193
|
-
if (totales.IVANoRec) {
|
|
194
|
-
detalle.IVANoRec = {
|
|
195
|
-
CodIVANoRec: totales.IVANoRec.CodIVANoRec || 1,
|
|
196
|
-
MntIVANoRec: Number(totales.IVANoRec.MntIVANoRec || 0),
|
|
197
|
-
};
|
|
198
|
-
// Con IVA no recuperable, TasaImp y MntIVA van en 0
|
|
199
|
-
detalle.TasaImp = 0;
|
|
200
|
-
detalle.MntIVA = 0;
|
|
201
|
-
} else if (totales.IVAUsoComun !== undefined && totales.IVAUsoComun > 0) {
|
|
202
|
-
// IVA Uso Común
|
|
203
|
-
detalle.IVAUsoComun = Number(totales.IVAUsoComun);
|
|
204
|
-
// Con IVA uso común, TasaImp y MntIVA van en 0
|
|
205
|
-
detalle.TasaImp = 0;
|
|
206
|
-
detalle.MntIVA = 0;
|
|
207
|
-
} else if (mntNeto > 0) {
|
|
208
|
-
// Caso normal: informar TasaImp y MntIVA
|
|
209
|
-
detalle.TasaImp = tasaIva;
|
|
210
|
-
detalle.MntIVA = mntIva;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// IVA Retenido Total (para Factura de Compra tipo 46 y sus NC/ND)
|
|
214
|
-
// Se informa ADEMÁS del MntIVA, no en lugar de
|
|
215
|
-
if (ivaRetTotal > 0) {
|
|
216
|
-
detalle.IVARetTotal = ivaRetTotal;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Otros Impuestos
|
|
220
|
-
const otrosImpMonto = totales.OtrosImp ? Number(totales.OtrosImp.MntImp || 0) : 0;
|
|
221
|
-
if (totales.OtrosImp) {
|
|
222
|
-
detalle.OtrosImp = {
|
|
223
|
-
CodImp: totales.OtrosImp.CodImp,
|
|
224
|
-
TasaImp: totales.OtrosImp.TasaImp || 0,
|
|
225
|
-
MntImp: otrosImpMonto,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Monto Total - RECALCULAR según fórmula del libro:
|
|
230
|
-
// MntTotal = MntNeto + MntExe + MntIVA + IVANoRec + IVAUsoComun + OtrosImp - IVARetTotal
|
|
231
|
-
const mntIvaNoRec = totales.IVANoRec ? Number(totales.IVANoRec.MntIVANoRec || 0) : 0;
|
|
232
|
-
const ivaUsoComun = Number(totales.IVAUsoComun || 0);
|
|
233
|
-
detalle.MntTotal = mntNeto + mntExe + mntIva + mntIvaNoRec + ivaUsoComun + otrosImpMonto - ivaRetTotal;
|
|
234
|
-
|
|
235
|
-
return detalle;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Calcula el RESUMEN automáticamente desde el DETALLE
|
|
240
|
-
* @private
|
|
241
|
-
* @param {Array} detalle - Array de documentos
|
|
242
|
-
* @param {number} [factorProp] - Factor de proporcionalidad (default 0.6)
|
|
243
|
-
*/
|
|
244
|
-
_calcularResumenDesdeDetalle(detalle, factorProp) {
|
|
245
|
-
const factor = factorProp || FACTOR_PROPORCIONALIDAD;
|
|
246
|
-
const resumenMap = new Map();
|
|
247
|
-
|
|
248
|
-
for (const doc of detalle) {
|
|
249
|
-
const tipo = doc.TpoDoc;
|
|
250
|
-
|
|
251
|
-
if (!resumenMap.has(tipo)) {
|
|
252
|
-
resumenMap.set(tipo, {
|
|
253
|
-
TpoDoc: tipo,
|
|
254
|
-
TotDoc: 0,
|
|
255
|
-
TotMntExe: 0,
|
|
256
|
-
TotMntNeto: 0,
|
|
257
|
-
TotMntIVA: 0,
|
|
258
|
-
TotMntTotal: 0,
|
|
259
|
-
TotOpIVAUsoComun: 0,
|
|
260
|
-
TotIVAUsoComun: 0,
|
|
261
|
-
TotCredIVAUsoComun: 0,
|
|
262
|
-
TotIVANoRec: {},
|
|
263
|
-
TotOtrosImp: {},
|
|
264
|
-
TotIVARetTotal: 0,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const r = resumenMap.get(tipo);
|
|
269
|
-
r.TotDoc += 1;
|
|
270
|
-
r.TotMntExe += Number(doc.MntExe || 0);
|
|
271
|
-
r.TotMntNeto += Number(doc.MntNeto || 0);
|
|
272
|
-
r.TotMntIVA += Number(doc.MntIVA || 0);
|
|
273
|
-
r.TotMntTotal += Number(doc.MntTotal || 0);
|
|
274
|
-
|
|
275
|
-
// IVA Uso Común
|
|
276
|
-
if (doc.IVAUsoComun) {
|
|
277
|
-
r.TotOpIVAUsoComun += 1;
|
|
278
|
-
r.TotIVAUsoComun += Number(doc.IVAUsoComun);
|
|
279
|
-
r.TotCredIVAUsoComun += Math.round(Number(doc.IVAUsoComun) * factor);
|
|
280
|
-
r.FctProp = factor;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// IVA No Recuperable
|
|
284
|
-
if (doc.IVANoRec) {
|
|
285
|
-
const codIVA = doc.IVANoRec.CodIVANoRec;
|
|
286
|
-
if (!r.TotIVANoRec[codIVA]) {
|
|
287
|
-
r.TotIVANoRec[codIVA] = {
|
|
288
|
-
CodIVANoRec: codIVA,
|
|
289
|
-
TotOpIVANoRec: 0,
|
|
290
|
-
TotMntIVANoRec: 0,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
r.TotIVANoRec[codIVA].TotOpIVANoRec += 1;
|
|
294
|
-
r.TotIVANoRec[codIVA].TotMntIVANoRec += Number(doc.IVANoRec.MntIVANoRec || 0);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Otros Impuestos
|
|
298
|
-
if (doc.OtrosImp) {
|
|
299
|
-
const codImp = doc.OtrosImp.CodImp;
|
|
300
|
-
if (!r.TotOtrosImp[codImp]) {
|
|
301
|
-
r.TotOtrosImp[codImp] = {
|
|
302
|
-
CodImp: codImp,
|
|
303
|
-
TotMntImp: 0,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
r.TotOtrosImp[codImp].TotMntImp += Number(doc.OtrosImp.MntImp || 0);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// IVA Retenido Total
|
|
310
|
-
if (doc.IVARetTotal) {
|
|
311
|
-
r.TotIVARetTotal += Number(doc.IVARetTotal);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Convertir a array limpio
|
|
316
|
-
const resumenArray = Array.from(resumenMap.values()).map((r) => {
|
|
317
|
-
const limpio = {
|
|
318
|
-
TpoDoc: r.TpoDoc,
|
|
319
|
-
TotDoc: r.TotDoc,
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
if (r.TotMntExe > 0) limpio.TotMntExe = r.TotMntExe;
|
|
323
|
-
if (r.TotMntNeto > 0) limpio.TotMntNeto = r.TotMntNeto;
|
|
324
|
-
if (r.TotMntIVA > 0) limpio.TotMntIVA = r.TotMntIVA;
|
|
325
|
-
if (r.TotMntTotal > 0) limpio.TotMntTotal = r.TotMntTotal;
|
|
326
|
-
|
|
327
|
-
// IVA Uso Común
|
|
328
|
-
if (r.TotOpIVAUsoComun > 0) {
|
|
329
|
-
limpio.TotOpIVAUsoComun = r.TotOpIVAUsoComun;
|
|
330
|
-
limpio.TotIVAUsoComun = r.TotIVAUsoComun;
|
|
331
|
-
limpio.FctProp = r.FctProp;
|
|
332
|
-
limpio.TotCredIVAUsoComun = r.TotCredIVAUsoComun;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// IVA No Recuperable
|
|
336
|
-
const ivaNoRecArray = Object.values(r.TotIVANoRec);
|
|
337
|
-
if (ivaNoRecArray.length > 0) {
|
|
338
|
-
limpio.TotIVANoRec = ivaNoRecArray;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Otros Impuestos
|
|
342
|
-
const otrosImpArray = Object.values(r.TotOtrosImp);
|
|
343
|
-
if (otrosImpArray.length > 0) {
|
|
344
|
-
limpio.TotOtrosImp = otrosImpArray;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// IVA Retenido Total
|
|
348
|
-
if (r.TotIVARetTotal > 0) {
|
|
349
|
-
limpio.TotIVARetTotal = r.TotIVARetTotal;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return limpio;
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
return resumenArray;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
module.exports = LibroCompras;
|
|
3
|
+
/**
|
|
4
|
+
* LibroCompras.js - Generador de Libro de Compras para Certificación SII
|
|
5
|
+
*
|
|
6
|
+
* Construye el libro de compras a partir de los resultados del SetCompra.
|
|
7
|
+
*
|
|
8
|
+
* Documentos incluidos:
|
|
9
|
+
* - Facturas de Compra (46) con IVA Retenido Total
|
|
10
|
+
* - Notas de Crédito (61)
|
|
11
|
+
* - Notas de Débito (56)
|
|
12
|
+
*
|
|
13
|
+
* Maneja campos especiales:
|
|
14
|
+
* - IVANoRec (IVA no recuperable)
|
|
15
|
+
* - IVAUsoComun (IVA de uso común con factor proporcionalidad)
|
|
16
|
+
* - OtrosImp (otros impuestos)
|
|
17
|
+
* - IVARetTotal (IVA retenido total)
|
|
18
|
+
*
|
|
19
|
+
* @module dte-sii/cert/LibroCompras
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const { LibroCompraVenta } = require('../index');
|
|
23
|
+
|
|
24
|
+
// Factor de proporcionalidad para IVA de uso común (60%)
|
|
25
|
+
const FACTOR_PROPORCIONALIDAD = 0.6;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} LibroComprasConfig
|
|
29
|
+
* @property {Object} emisor - Datos del emisor
|
|
30
|
+
* @property {string} periodo - Período tributario (YYYY-MM)
|
|
31
|
+
* @property {Object} certificado - Instancia de Certificado
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
class LibroCompras {
|
|
35
|
+
/**
|
|
36
|
+
* @param {LibroComprasConfig} config
|
|
37
|
+
*/
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.emisor = config.emisor;
|
|
41
|
+
this.periodo = config.periodo;
|
|
42
|
+
this.certificado = config.certificado;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Genera el libro de compras desde los resultados del SetCompra
|
|
47
|
+
* @param {Object} setCompraResult - Resultado de SetCompra.ejecutar()
|
|
48
|
+
* @returns {Object} { libro, xml, detalle, resumen }
|
|
49
|
+
*/
|
|
50
|
+
generar(setCompraResult) {
|
|
51
|
+
const { documentos } = setCompraResult;
|
|
52
|
+
|
|
53
|
+
if (!documentos || documentos.length === 0) {
|
|
54
|
+
throw new Error('LibroCompras: No hay documentos del SetCompra para generar libro');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const fechaBase = `${this.periodo}-15`;
|
|
58
|
+
const detalles = [];
|
|
59
|
+
|
|
60
|
+
// Procesar cada documento del set compra
|
|
61
|
+
for (const doc of documentos) {
|
|
62
|
+
const detalle = this._buildDetalle(doc, fechaBase);
|
|
63
|
+
detalles.push(detalle);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Calcular resumen automáticamente desde el detalle
|
|
67
|
+
const resumen = this._calcularResumenDesdeDetalle(detalles);
|
|
68
|
+
|
|
69
|
+
// Crear libro
|
|
70
|
+
const libro = new LibroCompraVenta(this.certificado);
|
|
71
|
+
libro.setCaratula({
|
|
72
|
+
RutEmisorLibro: this.emisor.rut,
|
|
73
|
+
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
74
|
+
PeriodoTributario: this.periodo,
|
|
75
|
+
FchResol: this.emisor.fch_resol,
|
|
76
|
+
NroResol: this.emisor.nro_resol,
|
|
77
|
+
TipoOperacion: 'COMPRA',
|
|
78
|
+
TipoLibro: 'MENSUAL',
|
|
79
|
+
TipoEnvio: 'TOTAL',
|
|
80
|
+
});
|
|
81
|
+
libro.setResumen(resumen);
|
|
82
|
+
libro.setDetalle(detalles);
|
|
83
|
+
libro.generar();
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
libro,
|
|
87
|
+
xml: libro.getXML(),
|
|
88
|
+
detalle: detalles,
|
|
89
|
+
resumen,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Genera el libro de compras desde los datos pre-procesados del SII (estructuras)
|
|
95
|
+
* @param {Object} libroComprasData - Datos del libro de compras { detalle, resumen, factorProporcionalidad }
|
|
96
|
+
* @param {string} periodo - Período tributario (YYYY-MM)
|
|
97
|
+
* @returns {Object} { libro, xml, detalle, resumen }
|
|
98
|
+
*/
|
|
99
|
+
generarDesdeEstructuras(libroComprasData, periodo) {
|
|
100
|
+
const { detalle, resumen, factorProporcionalidad } = libroComprasData;
|
|
101
|
+
|
|
102
|
+
if (!detalle || detalle.length === 0) {
|
|
103
|
+
throw new Error('LibroCompras: No hay detalle en los datos del SII');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Ajustar fechas al período correcto y aplicar reglas SII
|
|
107
|
+
const fechaBase = `${periodo}-15`;
|
|
108
|
+
const detalleAjustado = detalle.map(doc => {
|
|
109
|
+
const docAjustado = {
|
|
110
|
+
...doc,
|
|
111
|
+
FchDoc: fechaBase,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Regla SII: Cuando hay IVAUsoComun, TasaImp y MntIVA van en 0
|
|
115
|
+
if (doc.IVAUsoComun) {
|
|
116
|
+
docAjustado.TasaImp = 0;
|
|
117
|
+
docAjustado.MntIVA = 0;
|
|
118
|
+
}
|
|
119
|
+
// Regla SII: Cuando hay IVANoRec, TasaImp y MntIVA van en 0
|
|
120
|
+
else if (doc.IVANoRec) {
|
|
121
|
+
docAjustado.TasaImp = 0;
|
|
122
|
+
docAjustado.MntIVA = 0;
|
|
123
|
+
}
|
|
124
|
+
// Caso normal: Asegurar TasaImp sea número entero (19) no decimal (0.19)
|
|
125
|
+
else if (doc.TasaImp !== undefined) {
|
|
126
|
+
docAjustado.TasaImp = doc.TasaImp < 1 ? Math.round(doc.TasaImp * 100) : doc.TasaImp;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return docAjustado;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Recalcular resumen desde el detalle ajustado
|
|
133
|
+
const resumenRecalculado = this._calcularResumenDesdeDetalle(detalleAjustado, factorProporcionalidad);
|
|
134
|
+
|
|
135
|
+
// Crear libro
|
|
136
|
+
const libro = new LibroCompraVenta(this.certificado);
|
|
137
|
+
libro.setCaratula({
|
|
138
|
+
RutEmisorLibro: this.emisor.rut,
|
|
139
|
+
RutEnvia: this.certificado.rut || this.emisor.rut,
|
|
140
|
+
PeriodoTributario: periodo,
|
|
141
|
+
FchResol: this.emisor.fch_resol,
|
|
142
|
+
NroResol: this.emisor.nro_resol,
|
|
143
|
+
TipoOperacion: 'COMPRA',
|
|
144
|
+
TipoLibro: 'MENSUAL',
|
|
145
|
+
TipoEnvio: 'TOTAL',
|
|
146
|
+
});
|
|
147
|
+
libro.setResumen(resumenRecalculado);
|
|
148
|
+
libro.setDetalle(detalleAjustado);
|
|
149
|
+
libro.generar();
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
libro,
|
|
153
|
+
xml: libro.getXML(),
|
|
154
|
+
detalle: detalleAjustado,
|
|
155
|
+
resumen: resumenRecalculado,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Construye un registro de detalle desde un documento
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
_buildDetalle(doc, fechaBase) {
|
|
164
|
+
const tipoDte = doc.tipoDte;
|
|
165
|
+
const totales = doc.totales || {};
|
|
166
|
+
|
|
167
|
+
// En Factura de Compra (46), el emisor ES el receptor del documento
|
|
168
|
+
// porque nosotros RECIBIMOS la factura de compra
|
|
169
|
+
const rutDoc = this.emisor.rut;
|
|
170
|
+
const rznSoc = this.emisor.razon_social;
|
|
171
|
+
|
|
172
|
+
const detalle = {
|
|
173
|
+
TpoDoc: tipoDte,
|
|
174
|
+
NroDoc: doc.folio,
|
|
175
|
+
FchDoc: fechaBase,
|
|
176
|
+
RUTDoc: rutDoc,
|
|
177
|
+
RznSoc: rznSoc,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Montos básicos
|
|
181
|
+
const mntNeto = Number(totales.MntNeto || 0);
|
|
182
|
+
const mntExe = Number(totales.MntExe || 0);
|
|
183
|
+
const mntIva = Number(totales.IVA || 0);
|
|
184
|
+
const ivaRetTotal = Number(totales.IVARetTotal || 0);
|
|
185
|
+
|
|
186
|
+
if (mntExe > 0) detalle.MntExe = mntExe;
|
|
187
|
+
if (mntNeto > 0) detalle.MntNeto = mntNeto;
|
|
188
|
+
|
|
189
|
+
// Tasa de IVA - siempre informar si hay MntNeto
|
|
190
|
+
const tasaIva = Number(totales.TasaIVA || 19);
|
|
191
|
+
|
|
192
|
+
// IVA No Recuperable
|
|
193
|
+
if (totales.IVANoRec) {
|
|
194
|
+
detalle.IVANoRec = {
|
|
195
|
+
CodIVANoRec: totales.IVANoRec.CodIVANoRec || 1,
|
|
196
|
+
MntIVANoRec: Number(totales.IVANoRec.MntIVANoRec || 0),
|
|
197
|
+
};
|
|
198
|
+
// Con IVA no recuperable, TasaImp y MntIVA van en 0
|
|
199
|
+
detalle.TasaImp = 0;
|
|
200
|
+
detalle.MntIVA = 0;
|
|
201
|
+
} else if (totales.IVAUsoComun !== undefined && totales.IVAUsoComun > 0) {
|
|
202
|
+
// IVA Uso Común
|
|
203
|
+
detalle.IVAUsoComun = Number(totales.IVAUsoComun);
|
|
204
|
+
// Con IVA uso común, TasaImp y MntIVA van en 0
|
|
205
|
+
detalle.TasaImp = 0;
|
|
206
|
+
detalle.MntIVA = 0;
|
|
207
|
+
} else if (mntNeto > 0) {
|
|
208
|
+
// Caso normal: informar TasaImp y MntIVA
|
|
209
|
+
detalle.TasaImp = tasaIva;
|
|
210
|
+
detalle.MntIVA = mntIva;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// IVA Retenido Total (para Factura de Compra tipo 46 y sus NC/ND)
|
|
214
|
+
// Se informa ADEMÁS del MntIVA, no en lugar de
|
|
215
|
+
if (ivaRetTotal > 0) {
|
|
216
|
+
detalle.IVARetTotal = ivaRetTotal;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Otros Impuestos
|
|
220
|
+
const otrosImpMonto = totales.OtrosImp ? Number(totales.OtrosImp.MntImp || 0) : 0;
|
|
221
|
+
if (totales.OtrosImp) {
|
|
222
|
+
detalle.OtrosImp = {
|
|
223
|
+
CodImp: totales.OtrosImp.CodImp,
|
|
224
|
+
TasaImp: totales.OtrosImp.TasaImp || 0,
|
|
225
|
+
MntImp: otrosImpMonto,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Monto Total - RECALCULAR según fórmula del libro:
|
|
230
|
+
// MntTotal = MntNeto + MntExe + MntIVA + IVANoRec + IVAUsoComun + OtrosImp - IVARetTotal
|
|
231
|
+
const mntIvaNoRec = totales.IVANoRec ? Number(totales.IVANoRec.MntIVANoRec || 0) : 0;
|
|
232
|
+
const ivaUsoComun = Number(totales.IVAUsoComun || 0);
|
|
233
|
+
detalle.MntTotal = mntNeto + mntExe + mntIva + mntIvaNoRec + ivaUsoComun + otrosImpMonto - ivaRetTotal;
|
|
234
|
+
|
|
235
|
+
return detalle;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Calcula el RESUMEN automáticamente desde el DETALLE
|
|
240
|
+
* @private
|
|
241
|
+
* @param {Array} detalle - Array de documentos
|
|
242
|
+
* @param {number} [factorProp] - Factor de proporcionalidad (default 0.6)
|
|
243
|
+
*/
|
|
244
|
+
_calcularResumenDesdeDetalle(detalle, factorProp) {
|
|
245
|
+
const factor = factorProp || FACTOR_PROPORCIONALIDAD;
|
|
246
|
+
const resumenMap = new Map();
|
|
247
|
+
|
|
248
|
+
for (const doc of detalle) {
|
|
249
|
+
const tipo = doc.TpoDoc;
|
|
250
|
+
|
|
251
|
+
if (!resumenMap.has(tipo)) {
|
|
252
|
+
resumenMap.set(tipo, {
|
|
253
|
+
TpoDoc: tipo,
|
|
254
|
+
TotDoc: 0,
|
|
255
|
+
TotMntExe: 0,
|
|
256
|
+
TotMntNeto: 0,
|
|
257
|
+
TotMntIVA: 0,
|
|
258
|
+
TotMntTotal: 0,
|
|
259
|
+
TotOpIVAUsoComun: 0,
|
|
260
|
+
TotIVAUsoComun: 0,
|
|
261
|
+
TotCredIVAUsoComun: 0,
|
|
262
|
+
TotIVANoRec: {},
|
|
263
|
+
TotOtrosImp: {},
|
|
264
|
+
TotIVARetTotal: 0,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const r = resumenMap.get(tipo);
|
|
269
|
+
r.TotDoc += 1;
|
|
270
|
+
r.TotMntExe += Number(doc.MntExe || 0);
|
|
271
|
+
r.TotMntNeto += Number(doc.MntNeto || 0);
|
|
272
|
+
r.TotMntIVA += Number(doc.MntIVA || 0);
|
|
273
|
+
r.TotMntTotal += Number(doc.MntTotal || 0);
|
|
274
|
+
|
|
275
|
+
// IVA Uso Común
|
|
276
|
+
if (doc.IVAUsoComun) {
|
|
277
|
+
r.TotOpIVAUsoComun += 1;
|
|
278
|
+
r.TotIVAUsoComun += Number(doc.IVAUsoComun);
|
|
279
|
+
r.TotCredIVAUsoComun += Math.round(Number(doc.IVAUsoComun) * factor);
|
|
280
|
+
r.FctProp = factor;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// IVA No Recuperable
|
|
284
|
+
if (doc.IVANoRec) {
|
|
285
|
+
const codIVA = doc.IVANoRec.CodIVANoRec;
|
|
286
|
+
if (!r.TotIVANoRec[codIVA]) {
|
|
287
|
+
r.TotIVANoRec[codIVA] = {
|
|
288
|
+
CodIVANoRec: codIVA,
|
|
289
|
+
TotOpIVANoRec: 0,
|
|
290
|
+
TotMntIVANoRec: 0,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
r.TotIVANoRec[codIVA].TotOpIVANoRec += 1;
|
|
294
|
+
r.TotIVANoRec[codIVA].TotMntIVANoRec += Number(doc.IVANoRec.MntIVANoRec || 0);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Otros Impuestos
|
|
298
|
+
if (doc.OtrosImp) {
|
|
299
|
+
const codImp = doc.OtrosImp.CodImp;
|
|
300
|
+
if (!r.TotOtrosImp[codImp]) {
|
|
301
|
+
r.TotOtrosImp[codImp] = {
|
|
302
|
+
CodImp: codImp,
|
|
303
|
+
TotMntImp: 0,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
r.TotOtrosImp[codImp].TotMntImp += Number(doc.OtrosImp.MntImp || 0);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// IVA Retenido Total
|
|
310
|
+
if (doc.IVARetTotal) {
|
|
311
|
+
r.TotIVARetTotal += Number(doc.IVARetTotal);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Convertir a array limpio
|
|
316
|
+
const resumenArray = Array.from(resumenMap.values()).map((r) => {
|
|
317
|
+
const limpio = {
|
|
318
|
+
TpoDoc: r.TpoDoc,
|
|
319
|
+
TotDoc: r.TotDoc,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
if (r.TotMntExe > 0) limpio.TotMntExe = r.TotMntExe;
|
|
323
|
+
if (r.TotMntNeto > 0) limpio.TotMntNeto = r.TotMntNeto;
|
|
324
|
+
if (r.TotMntIVA > 0) limpio.TotMntIVA = r.TotMntIVA;
|
|
325
|
+
if (r.TotMntTotal > 0) limpio.TotMntTotal = r.TotMntTotal;
|
|
326
|
+
|
|
327
|
+
// IVA Uso Común
|
|
328
|
+
if (r.TotOpIVAUsoComun > 0) {
|
|
329
|
+
limpio.TotOpIVAUsoComun = r.TotOpIVAUsoComun;
|
|
330
|
+
limpio.TotIVAUsoComun = r.TotIVAUsoComun;
|
|
331
|
+
limpio.FctProp = r.FctProp;
|
|
332
|
+
limpio.TotCredIVAUsoComun = r.TotCredIVAUsoComun;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// IVA No Recuperable
|
|
336
|
+
const ivaNoRecArray = Object.values(r.TotIVANoRec);
|
|
337
|
+
if (ivaNoRecArray.length > 0) {
|
|
338
|
+
limpio.TotIVANoRec = ivaNoRecArray;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Otros Impuestos
|
|
342
|
+
const otrosImpArray = Object.values(r.TotOtrosImp);
|
|
343
|
+
if (otrosImpArray.length > 0) {
|
|
344
|
+
limpio.TotOtrosImp = otrosImpArray;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// IVA Retenido Total
|
|
348
|
+
if (r.TotIVARetTotal > 0) {
|
|
349
|
+
limpio.TotIVARetTotal = r.TotIVARetTotal;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return limpio;
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
return resumenArray;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = LibroCompras;
|