@acontplus/ng-common 1.0.4 → 1.0.6
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/README.md +40 -36
- package/fesm2022/acontplus-ng-common.mjs +439 -136
- package/fesm2022/acontplus-ng-common.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acontplus-ng-common.d.ts +343 -18
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, Injectable, signal, Input, Component } from '@angular/core';
|
|
3
|
+
import { tap, Observable } from 'rxjs';
|
|
3
4
|
import { HttpClient } from '@angular/common/http';
|
|
4
|
-
import { Observable, throwError } from 'rxjs';
|
|
5
5
|
import * as i1 from '@angular/forms';
|
|
6
6
|
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
7
7
|
import { CommonModule } from '@angular/common';
|
|
@@ -184,40 +184,307 @@ const WHATSAPP_MESSAGING_PORT = new InjectionToken('WHATSAPP_MESSAGING_PORT');
|
|
|
184
184
|
|
|
185
185
|
const PRINTER_PORT = new InjectionToken('PRINTER_PORT');
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Utilidades para manejo de archivos y respuestas HTTP
|
|
189
|
+
*/
|
|
190
|
+
class FileMapperUtil {
|
|
191
|
+
/**
|
|
192
|
+
* Extrae el nombre de archivo desde el header Content-Disposition
|
|
193
|
+
*/
|
|
194
|
+
static extractFileName(response) {
|
|
195
|
+
const contentDisposition = response.headers?.get('content-disposition');
|
|
196
|
+
if (!contentDisposition) {
|
|
197
|
+
return this.generateDefaultFileName();
|
|
198
|
+
}
|
|
199
|
+
// Método más robusto para extraer filename
|
|
200
|
+
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
|
|
201
|
+
if (matches && matches[1]) {
|
|
202
|
+
let fileName = matches[1].replace(/['"]/g, '');
|
|
203
|
+
// Limpiar caracteres especiales
|
|
204
|
+
fileName = fileName.replaceAll('+', ' ').trim();
|
|
205
|
+
return fileName || this.generateDefaultFileName();
|
|
206
|
+
}
|
|
207
|
+
// Fallback: método simple
|
|
208
|
+
const simpleParse = contentDisposition.split('filename=')[1];
|
|
209
|
+
if (simpleParse) {
|
|
210
|
+
return simpleParse.replace(/['"]/g, '').trim() || this.generateDefaultFileName();
|
|
211
|
+
}
|
|
212
|
+
return this.generateDefaultFileName();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Crea un objeto File desde una respuesta HTTP
|
|
216
|
+
*/
|
|
217
|
+
static fromResponse(response) {
|
|
218
|
+
const fileName = this.extractFileName(response);
|
|
219
|
+
const contentType = response.headers?.get('content-type') || 'application/octet-stream';
|
|
220
|
+
return new File([response.body], fileName, { type: contentType });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Descarga un archivo usando la API nativa del navegador
|
|
224
|
+
*/
|
|
225
|
+
static downloadFile(blob, fileName) {
|
|
226
|
+
const url = URL.createObjectURL(blob);
|
|
227
|
+
const link = document.createElement('a');
|
|
228
|
+
link.href = url;
|
|
229
|
+
link.download = fileName;
|
|
230
|
+
link.style.display = 'none';
|
|
231
|
+
document.body.appendChild(link);
|
|
232
|
+
link.click();
|
|
233
|
+
document.body.removeChild(link);
|
|
234
|
+
// Limpiar el objeto URL
|
|
235
|
+
URL.revokeObjectURL(url);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Abre un archivo en una nueva ventana (para PDFs)
|
|
239
|
+
*/
|
|
240
|
+
static openFile(blob, fileName) {
|
|
241
|
+
const fileURL = URL.createObjectURL(blob);
|
|
242
|
+
window.open(fileURL, fileName);
|
|
243
|
+
// Limpiar URL después de un tiempo
|
|
244
|
+
setTimeout(() => URL.revokeObjectURL(fileURL), 1000);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Detecta si es un navegador legacy que requiere descarga forzada
|
|
248
|
+
*/
|
|
249
|
+
static isLegacyBrowser() {
|
|
250
|
+
const userAgent = navigator.userAgent;
|
|
251
|
+
return !!(userAgent.match(/Edge/g) || userAgent.match(/.NET/g) || userAgent.match(/MSIE/g));
|
|
252
|
+
}
|
|
253
|
+
static generateDefaultFileName() {
|
|
254
|
+
const timestamp = Date.now();
|
|
255
|
+
return `document_${timestamp}.pdf`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Facade para generación de reportes
|
|
261
|
+
*
|
|
262
|
+
* Proporciona una API de alto nivel para generar reportes con manejo automático de archivos.
|
|
263
|
+
* Todos los métodos devuelven Observables - el consumidor debe hacer subscribe().
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* // Control total del blob
|
|
268
|
+
* this.reportFacade.generate(options).subscribe(response => {
|
|
269
|
+
* const blob = response.body;
|
|
270
|
+
* // Procesar blob manualmente
|
|
271
|
+
* });
|
|
272
|
+
*
|
|
273
|
+
* // Descarga automática
|
|
274
|
+
* this.reportFacade.download(options).subscribe();
|
|
275
|
+
*
|
|
276
|
+
* // Abrir PDF automáticamente
|
|
277
|
+
* this.reportFacade.open(options).subscribe();
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
187
280
|
class ReportFacade {
|
|
188
281
|
reportPort = inject(REPORT_PORT);
|
|
189
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Genera un reporte y devuelve el Observable con el blob
|
|
284
|
+
*
|
|
285
|
+
* Este método proporciona control total sobre el blob generado.
|
|
286
|
+
* No realiza ninguna acción automática (descarga/apertura).
|
|
287
|
+
* Útil cuando necesitas procesar el blob manualmente (enviar por WhatsApp, guardar en servidor, etc.)
|
|
288
|
+
*
|
|
289
|
+
* @template T - Tipo de datos del reporte
|
|
290
|
+
* @param options - Opciones de generación del reporte
|
|
291
|
+
* @param options.data - Datos del reporte (usar ReportParamsBuilder.build())
|
|
292
|
+
* @param options.format - Formato del reporte: 'pdf' | 'excel' | 'word'
|
|
293
|
+
* @param options.useV1Api - Si debe usar la API v1 (por defecto false)
|
|
294
|
+
* @returns Observable con la respuesta HTTP que contiene el blob
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* const options = ReportParamsBuilder.build(
|
|
299
|
+
* { codDoc: ELECTRONIC_DOCUMENT_CODE.FV, id: 123 },
|
|
300
|
+
* 'pdf'
|
|
301
|
+
* );
|
|
302
|
+
*
|
|
303
|
+
* this.reportFacade.generate(options).subscribe({
|
|
304
|
+
* next: (response) => {
|
|
305
|
+
* const blob = response.body;
|
|
306
|
+
* const fileName = FileMapperUtil.extractFileName(response);
|
|
307
|
+
* // Procesar blob según necesidad
|
|
308
|
+
* },
|
|
309
|
+
* error: (err) => console.error('Error:', err)
|
|
310
|
+
* });
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
190
313
|
generate(options) {
|
|
191
314
|
return this.reportPort.generate(options);
|
|
192
315
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
316
|
+
/**
|
|
317
|
+
* Genera un reporte y lo descarga automáticamente
|
|
318
|
+
*
|
|
319
|
+
* Extrae el nombre del archivo desde los headers HTTP y descarga el archivo automáticamente.
|
|
320
|
+
* Funciona para cualquier formato (PDF, Excel, Word).
|
|
321
|
+
* El Observable se completa después de iniciar la descarga.
|
|
322
|
+
*
|
|
323
|
+
* @template T - Tipo de datos del reporte
|
|
324
|
+
* @param options - Opciones de generación del reporte
|
|
325
|
+
* @returns Observable que se completa después de iniciar la descarga
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* // Con formato dinámico
|
|
330
|
+
* const options = ReportParamsBuilder.build(
|
|
331
|
+
* { codDoc: SALE_CODE_REPORT.FG },
|
|
332
|
+
* this.selectedFormat // 'pdf' | 'excel' | 'word'
|
|
333
|
+
* );
|
|
334
|
+
* this.reportFacade.download(options).subscribe();
|
|
335
|
+
*
|
|
336
|
+
* // Con formato fijo
|
|
337
|
+
* const options = ReportParamsBuilder.build(
|
|
338
|
+
* { codDoc: INVENTORY_CODE_REPORT.RCD },
|
|
339
|
+
* 'excel'
|
|
340
|
+
* );
|
|
341
|
+
* this.reportFacade.download(options).subscribe();
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
download(options) {
|
|
345
|
+
return this.reportPort.generate(options).pipe(tap((response) => {
|
|
346
|
+
const fileName = FileMapperUtil.extractFileName(response);
|
|
347
|
+
if (response.body) {
|
|
348
|
+
FileMapperUtil.downloadFile(response.body, fileName);
|
|
349
|
+
}
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Genera un reporte y lo abre en nueva ventana (o descarga en navegadores legacy)
|
|
354
|
+
*
|
|
355
|
+
* Comportamiento inteligente según el navegador:
|
|
356
|
+
* - Navegadores modernos: Abre el archivo en nueva ventana/pestaña
|
|
357
|
+
* - Navegadores legacy (IE, Edge antiguo): Descarga el archivo
|
|
358
|
+
*
|
|
359
|
+
* Ideal para PDFs que el usuario quiere visualizar inmediatamente.
|
|
360
|
+
* También funciona con Excel/Word pero la experiencia puede variar según el navegador.
|
|
361
|
+
*
|
|
362
|
+
* @template T - Tipo de datos del reporte
|
|
363
|
+
* @param options - Opciones de generación del reporte
|
|
364
|
+
* @returns Observable que se completa después de abrir/descargar el archivo
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```typescript
|
|
368
|
+
* // Abrir PDF
|
|
369
|
+
* const options = ReportParamsBuilder.build(
|
|
370
|
+
* { codDoc: ELECTRONIC_DOCUMENT_CODE.FV, id: 123 },
|
|
371
|
+
* 'pdf'
|
|
372
|
+
* );
|
|
373
|
+
* this.reportFacade.open(options).subscribe();
|
|
374
|
+
*
|
|
375
|
+
* // Con formato dinámico
|
|
376
|
+
* const format = this.isPDF ? 'pdf' : 'excel';
|
|
377
|
+
* const options = ReportParamsBuilder.build({ codDoc: code }, format);
|
|
378
|
+
* this.reportFacade.open(options).subscribe();
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
open(options) {
|
|
382
|
+
return this.reportPort.generate(options).pipe(tap((response) => {
|
|
383
|
+
const fileName = FileMapperUtil.extractFileName(response);
|
|
384
|
+
if (response.body) {
|
|
385
|
+
if (FileMapperUtil.isLegacyBrowser()) {
|
|
386
|
+
FileMapperUtil.downloadFile(response.body, fileName);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
FileMapperUtil.openFile(response.body, fileName);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Genera un PDF y lo abre automáticamente (shortcut)
|
|
396
|
+
*
|
|
397
|
+
* Método de conveniencia para el caso común de generar y abrir PDFs.
|
|
398
|
+
* Equivalente a llamar open() con format: 'pdf'.
|
|
399
|
+
*
|
|
400
|
+
* @template T - Tipo de datos del reporte
|
|
401
|
+
* @param data - Datos del reporte (resultado de ReportParamsBuilder.build().data)
|
|
402
|
+
* @param useV1Api - Si debe usar la API v1 (por defecto false)
|
|
403
|
+
* @returns Observable que se completa después de abrir el PDF
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* const reportOptions = ReportParamsBuilder.build(
|
|
408
|
+
* { codDoc: ELECTRONIC_DOCUMENT_CODE.FV, id: 123 },
|
|
409
|
+
* 'pdf'
|
|
410
|
+
* );
|
|
411
|
+
*
|
|
412
|
+
* // Forma corta
|
|
413
|
+
* this.reportFacade.openPDF(
|
|
414
|
+
* reportOptions.data,
|
|
415
|
+
* reportOptions.useV1Api
|
|
416
|
+
* ).subscribe();
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
openPDF(data, useV1Api = false) {
|
|
420
|
+
return this.open({
|
|
196
421
|
data,
|
|
197
422
|
format: 'pdf',
|
|
198
|
-
|
|
423
|
+
useV1Api,
|
|
199
424
|
});
|
|
200
425
|
}
|
|
201
|
-
|
|
202
|
-
|
|
426
|
+
/**
|
|
427
|
+
* Genera un Excel y lo descarga automáticamente (shortcut)
|
|
428
|
+
*
|
|
429
|
+
* Método de conveniencia para el caso común de generar y descargar archivos Excel.
|
|
430
|
+
* Equivalente a llamar download() con format: 'excel'.
|
|
431
|
+
*
|
|
432
|
+
* @template T - Tipo de datos del reporte
|
|
433
|
+
* @param data - Datos del reporte (resultado de ReportParamsBuilder.build().data)
|
|
434
|
+
* @param useV1Api - Si debe usar la API v1 (por defecto false)
|
|
435
|
+
* @returns Observable que se completa después de iniciar la descarga
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```typescript
|
|
439
|
+
* const reportOptions = ReportParamsBuilder.build(
|
|
440
|
+
* { codDoc: SALE_CODE_REPORT.FG, fechaInicio: '2024-01-01' },
|
|
441
|
+
* 'excel'
|
|
442
|
+
* );
|
|
443
|
+
*
|
|
444
|
+
* // Forma corta
|
|
445
|
+
* this.reportFacade.downloadExcel(
|
|
446
|
+
* reportOptions.data,
|
|
447
|
+
* reportOptions.useV1Api
|
|
448
|
+
* ).subscribe();
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
downloadExcel(data, useV1Api = false) {
|
|
452
|
+
return this.download({
|
|
203
453
|
data,
|
|
204
454
|
format: 'excel',
|
|
205
|
-
|
|
455
|
+
useV1Api,
|
|
206
456
|
});
|
|
207
457
|
}
|
|
208
|
-
|
|
209
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Genera un Word y lo descarga automáticamente (shortcut)
|
|
460
|
+
*
|
|
461
|
+
* Método de conveniencia para el caso común de generar y descargar archivos Word.
|
|
462
|
+
* Equivalente a llamar download() con format: 'word'.
|
|
463
|
+
*
|
|
464
|
+
* @template T - Tipo de datos del reporte
|
|
465
|
+
* @param data - Datos del reporte (resultado de ReportParamsBuilder.build().data)
|
|
466
|
+
* @param useV1Api - Si debe usar la API v1 (por defecto false)
|
|
467
|
+
* @returns Observable que se completa después de iniciar la descarga
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* ```typescript
|
|
471
|
+
* const reportOptions = ReportParamsBuilder.build(
|
|
472
|
+
* { codDoc: CUSTOMER_CODE_REPORT.RCL },
|
|
473
|
+
* 'word'
|
|
474
|
+
* );
|
|
475
|
+
*
|
|
476
|
+
* // Forma corta
|
|
477
|
+
* this.reportFacade.downloadWord(
|
|
478
|
+
* reportOptions.data,
|
|
479
|
+
* reportOptions.useV1Api
|
|
480
|
+
* ).subscribe();
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
downloadWord(data, useV1Api = false) {
|
|
484
|
+
return this.download({
|
|
210
485
|
data,
|
|
211
486
|
format: 'word',
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
// Método para generar y retornar blob
|
|
216
|
-
generateBlob(data, format = 'pdf') {
|
|
217
|
-
return this.reportPort.generate({
|
|
218
|
-
data,
|
|
219
|
-
format,
|
|
220
|
-
returnBlob: true,
|
|
487
|
+
useV1Api,
|
|
221
488
|
});
|
|
222
489
|
}
|
|
223
490
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -615,113 +882,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
615
882
|
}] });
|
|
616
883
|
|
|
617
884
|
/**
|
|
618
|
-
*
|
|
885
|
+
* Adapter para generación de reportes
|
|
886
|
+
* Siempre devuelve Observable - el consumidor decide qué hacer con el blob
|
|
619
887
|
*/
|
|
620
|
-
class FileMapperUtil {
|
|
621
|
-
/**
|
|
622
|
-
* Extrae el nombre de archivo desde el header Content-Disposition
|
|
623
|
-
*/
|
|
624
|
-
static extractFileName(response) {
|
|
625
|
-
const contentDisposition = response.headers?.get('content-disposition');
|
|
626
|
-
if (!contentDisposition) {
|
|
627
|
-
return this.generateDefaultFileName();
|
|
628
|
-
}
|
|
629
|
-
// Método más robusto para extraer filename
|
|
630
|
-
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
|
|
631
|
-
if (matches && matches[1]) {
|
|
632
|
-
let fileName = matches[1].replace(/['"]/g, '');
|
|
633
|
-
// Limpiar caracteres especiales
|
|
634
|
-
fileName = fileName.replaceAll('+', ' ').trim();
|
|
635
|
-
return fileName || this.generateDefaultFileName();
|
|
636
|
-
}
|
|
637
|
-
// Fallback: método simple
|
|
638
|
-
const simpleParse = contentDisposition.split('filename=')[1];
|
|
639
|
-
if (simpleParse) {
|
|
640
|
-
return simpleParse.replace(/['"]/g, '').trim() || this.generateDefaultFileName();
|
|
641
|
-
}
|
|
642
|
-
return this.generateDefaultFileName();
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Crea un objeto File desde una respuesta HTTP
|
|
646
|
-
*/
|
|
647
|
-
static fromResponse(response) {
|
|
648
|
-
const fileName = this.extractFileName(response);
|
|
649
|
-
const contentType = response.headers?.get('content-type') || 'application/octet-stream';
|
|
650
|
-
return new File([response.body], fileName, { type: contentType });
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Descarga un archivo usando la API nativa del navegador
|
|
654
|
-
*/
|
|
655
|
-
static downloadFile(blob, fileName) {
|
|
656
|
-
const url = URL.createObjectURL(blob);
|
|
657
|
-
const link = document.createElement('a');
|
|
658
|
-
link.href = url;
|
|
659
|
-
link.download = fileName;
|
|
660
|
-
link.style.display = 'none';
|
|
661
|
-
document.body.appendChild(link);
|
|
662
|
-
link.click();
|
|
663
|
-
document.body.removeChild(link);
|
|
664
|
-
// Limpiar el objeto URL
|
|
665
|
-
URL.revokeObjectURL(url);
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Abre un archivo en una nueva ventana (para PDFs)
|
|
669
|
-
*/
|
|
670
|
-
static openFile(blob, fileName) {
|
|
671
|
-
const fileURL = URL.createObjectURL(blob);
|
|
672
|
-
window.open(fileURL, fileName);
|
|
673
|
-
// Limpiar URL después de un tiempo
|
|
674
|
-
setTimeout(() => URL.revokeObjectURL(fileURL), 1000);
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Detecta si es un navegador legacy que requiere descarga forzada
|
|
678
|
-
*/
|
|
679
|
-
static isLegacyBrowser() {
|
|
680
|
-
const userAgent = navigator.userAgent;
|
|
681
|
-
return !!(userAgent.match(/Edge/g) || userAgent.match(/.NET/g) || userAgent.match(/MSIE/g));
|
|
682
|
-
}
|
|
683
|
-
static generateDefaultFileName() {
|
|
684
|
-
const timestamp = Date.now();
|
|
685
|
-
return `document_${timestamp}.pdf`;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
888
|
class ReportAdapter {
|
|
690
889
|
http = inject(HttpClient);
|
|
691
890
|
generate(options) {
|
|
692
|
-
const { data,
|
|
693
|
-
const reportVersionPath = useV1Api ? '/
|
|
694
|
-
const fullPath = `${API_PATHS.REPORTS}${reportVersionPath}
|
|
695
|
-
|
|
891
|
+
const { data, useV1Api = false } = options;
|
|
892
|
+
const reportVersionPath = useV1Api ? '/reporte/download' : '/v2/reporte/download';
|
|
893
|
+
const fullPath = `${API_PATHS.REPORTS}${reportVersionPath}`;
|
|
894
|
+
return this.http.post(fullPath, data, {
|
|
696
895
|
observe: 'response',
|
|
697
896
|
responseType: 'blob',
|
|
698
|
-
};
|
|
699
|
-
if (returnBlob) {
|
|
700
|
-
return this.http.post(fullPath, data, requestOptions);
|
|
701
|
-
}
|
|
702
|
-
this.http.post(fullPath, data, requestOptions).subscribe({
|
|
703
|
-
next: (response) => this.saveFile(response, format, forceDownload),
|
|
704
|
-
error: (err) => {
|
|
705
|
-
console.error('Error al descargar el reporte:', err);
|
|
706
|
-
},
|
|
707
897
|
});
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
getFileName(response) {
|
|
711
|
-
return FileMapperUtil.extractFileName(response);
|
|
712
|
-
}
|
|
713
|
-
saveFile(response, format, forceDownload = false) {
|
|
714
|
-
const fileName = this.getFileName(response);
|
|
715
|
-
if (!response.body)
|
|
716
|
-
return;
|
|
717
|
-
if (format === 'pdf' && !forceDownload && !FileMapperUtil.isLegacyBrowser()) {
|
|
718
|
-
// Abrir PDF en nueva ventana para navegadores modernos
|
|
719
|
-
FileMapperUtil.openFile(response.body, fileName);
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
// Descargar archivo (Excel, Word, o PDF forzado)
|
|
723
|
-
FileMapperUtil.downloadFile(response.body, fileName);
|
|
724
|
-
}
|
|
725
898
|
}
|
|
726
899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
727
900
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ReportAdapter });
|
|
@@ -1027,17 +1200,47 @@ class ReportParamsBuilder {
|
|
|
1027
1200
|
};
|
|
1028
1201
|
/**
|
|
1029
1202
|
* Construye los parámetros de reporte según el tipo de documento
|
|
1203
|
+
*
|
|
1204
|
+
* @param docData - Datos del documento con codDoc y parámetros adicionales
|
|
1205
|
+
* @param format - Formato del reporte: 'pdf' | 'excel' | 'word'
|
|
1206
|
+
* @returns Objeto con hasService, reportParams (string JSON) y dataParams (string JSON)
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* ```typescript
|
|
1210
|
+
* const params = ReportParamsBuilder.buildParams(
|
|
1211
|
+
* { codDoc: ELECTRONIC_DOCUMENT_CODE.FV, id: 123, codEstab: '001' },
|
|
1212
|
+
* 'pdf'
|
|
1213
|
+
* );
|
|
1214
|
+
* // Resultado:
|
|
1215
|
+
* // {
|
|
1216
|
+
* // hasService: true,
|
|
1217
|
+
* // reportParams: "{\"codigo\":\"FV\",\"format\":\"pdf\",\"hasParams\":true,...}",
|
|
1218
|
+
* // dataParams: "{\"id\":123,\"codEstab\":\"001\",\"codigo\":\"FV\"}"
|
|
1219
|
+
* // }
|
|
1220
|
+
* ```
|
|
1030
1221
|
*/
|
|
1031
1222
|
static buildParams(docData, format = 'pdf') {
|
|
1032
1223
|
const config = this.getReportConfig(docData.codDoc);
|
|
1033
1224
|
return {
|
|
1034
1225
|
hasService: config.hasService,
|
|
1035
1226
|
reportParams: this.buildReportParams(docData, config, format),
|
|
1036
|
-
dataParams: this.buildDataParams(docData, config
|
|
1227
|
+
dataParams: this.buildDataParams(docData, config),
|
|
1037
1228
|
};
|
|
1038
1229
|
}
|
|
1039
1230
|
/**
|
|
1040
1231
|
* Determina si debe usar la API v1 según el tipo de documento
|
|
1232
|
+
*
|
|
1233
|
+
* @param codDoc - Código del documento (FV, NE, NC, etc.)
|
|
1234
|
+
* @returns true si debe usar API v1, false para API v2
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* const useV1 = ReportParamsBuilder.shouldUseV1Api(ELECTRONIC_DOCUMENT_CODE.FV);
|
|
1239
|
+
* // useV1 = true (documentos electrónicos usan v1)
|
|
1240
|
+
*
|
|
1241
|
+
* const useV1Sales = ReportParamsBuilder.shouldUseV1Api(SALE_CODE_REPORT.FG);
|
|
1242
|
+
* // useV1Sales = false (reportes de ventas usan v2)
|
|
1243
|
+
* ```
|
|
1041
1244
|
*/
|
|
1042
1245
|
static shouldUseV1Api(codDoc) {
|
|
1043
1246
|
const config = this.getReportConfig(codDoc);
|
|
@@ -1045,6 +1248,11 @@ class ReportParamsBuilder {
|
|
|
1045
1248
|
}
|
|
1046
1249
|
/**
|
|
1047
1250
|
* Obtiene la configuración para un tipo de documento
|
|
1251
|
+
*
|
|
1252
|
+
* @param codDoc - Código del documento
|
|
1253
|
+
* @returns Configuración del reporte
|
|
1254
|
+
* @throws Error si el tipo de documento no está soportado
|
|
1255
|
+
* @private
|
|
1048
1256
|
*/
|
|
1049
1257
|
static getReportConfig(codDoc) {
|
|
1050
1258
|
const config = this.reportConfigs[codDoc];
|
|
@@ -1054,7 +1262,20 @@ class ReportParamsBuilder {
|
|
|
1054
1262
|
return config;
|
|
1055
1263
|
}
|
|
1056
1264
|
/**
|
|
1057
|
-
* Construye los parámetros del reporte
|
|
1265
|
+
* Construye los parámetros del reporte (reportParams)
|
|
1266
|
+
*
|
|
1267
|
+
* Genera un string JSON con la configuración del reporte:
|
|
1268
|
+
* - codigo: Código del reporte
|
|
1269
|
+
* - format: Formato de salida (pdf, excel, word)
|
|
1270
|
+
* - hasParams: Si el reporte acepta parámetros adicionales
|
|
1271
|
+
* - codEstab: Código de establecimiento (si aplica)
|
|
1272
|
+
* - aditionalParams: Array vacío para parámetros adicionales (si aplica)
|
|
1273
|
+
*
|
|
1274
|
+
* @param docData - Datos del documento
|
|
1275
|
+
* @param config - Configuración del tipo de reporte
|
|
1276
|
+
* @param format - Formato de salida
|
|
1277
|
+
* @returns String JSON con los parámetros del reporte
|
|
1278
|
+
* @private
|
|
1058
1279
|
*/
|
|
1059
1280
|
static buildReportParams(docData, config, format) {
|
|
1060
1281
|
const params = {
|
|
@@ -1076,9 +1297,34 @@ class ReportParamsBuilder {
|
|
|
1076
1297
|
return JSON.stringify(params);
|
|
1077
1298
|
}
|
|
1078
1299
|
/**
|
|
1079
|
-
* Construye los parámetros de datos
|
|
1300
|
+
* Construye los parámetros de datos (dataParams)
|
|
1301
|
+
*
|
|
1302
|
+
* Genera un string JSON con los datos específicos del reporte.
|
|
1303
|
+
* NO incluye hasParams ni configuración, solo datos puros.
|
|
1304
|
+
*
|
|
1305
|
+
* Incluye automáticamente:
|
|
1306
|
+
* - id: ID del documento (si existe)
|
|
1307
|
+
* - codEstab: Código de establecimiento (según configuración)
|
|
1308
|
+
* - codigo: Código del reporte (según configuración)
|
|
1309
|
+
* - Parámetros extra específicos del tipo (extraDataParams)
|
|
1310
|
+
* - Todos los parámetros adicionales del docData (excepto codDoc, codEstab, id, serie)
|
|
1311
|
+
*
|
|
1312
|
+
* @param docData - Datos del documento con parámetros adicionales
|
|
1313
|
+
* @param config - Configuración del tipo de reporte
|
|
1314
|
+
* @returns String JSON con los datos del reporte (sin hasParams anidado)
|
|
1315
|
+
* @private
|
|
1316
|
+
*
|
|
1317
|
+
* @example
|
|
1318
|
+
* ```typescript
|
|
1319
|
+
* // Input:
|
|
1320
|
+
* docData = { codDoc: 'FG', tipo: 2, codEstab: '001', fechaInicio: '2024-01-01' }
|
|
1321
|
+
* config = { idField: 'id', includeEstabInData: false, ... }
|
|
1322
|
+
*
|
|
1323
|
+
* // Output:
|
|
1324
|
+
* "{\"tipo\":2,\"fechaInicio\":\"2024-01-01\"}"
|
|
1325
|
+
* ```
|
|
1080
1326
|
*/
|
|
1081
|
-
static buildDataParams(docData, config
|
|
1327
|
+
static buildDataParams(docData, config) {
|
|
1082
1328
|
const params = {};
|
|
1083
1329
|
// Agregar ID si existe el campo configurado
|
|
1084
1330
|
if (config.idField && docData[config.idField]) {
|
|
@@ -1091,12 +1337,6 @@ class ReportParamsBuilder {
|
|
|
1091
1337
|
if (config.includeCodigoInData) {
|
|
1092
1338
|
params.codigo = config.codigo;
|
|
1093
1339
|
}
|
|
1094
|
-
if (config.hasService && config.hasParams) {
|
|
1095
|
-
params.hasParams = true;
|
|
1096
|
-
if (config.includeCodigoInData) {
|
|
1097
|
-
params.format = format;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
1340
|
// Agregar parámetros extra específicos del tipo
|
|
1101
1341
|
if (config.extraDataParams) {
|
|
1102
1342
|
Object.assign(params, config.extraDataParams);
|
|
@@ -1111,29 +1351,94 @@ class ReportParamsBuilder {
|
|
|
1111
1351
|
}
|
|
1112
1352
|
/**
|
|
1113
1353
|
* Construye las opciones completas para generar un reporte
|
|
1354
|
+
*
|
|
1355
|
+
* @param docData - Datos del documento con codDoc y parámetros adicionales
|
|
1356
|
+
* @param format - Formato del reporte: 'pdf' | 'excel' | 'word'
|
|
1357
|
+
* @returns Objeto con data (para enviar al backend), format y useV1Api
|
|
1358
|
+
*
|
|
1359
|
+
* @example
|
|
1360
|
+
* ```typescript
|
|
1361
|
+
* const options = ReportParamsBuilder.build(
|
|
1362
|
+
* { codDoc: ELECTRONIC_DOCUMENT_CODE.FV, id: 123, codEstab: '001' },
|
|
1363
|
+
* 'pdf'
|
|
1364
|
+
* );
|
|
1365
|
+
* // Resultado:
|
|
1366
|
+
* // {
|
|
1367
|
+
* // data: { hasService: true, reportParams: "{...}", dataParams: "{...}" },
|
|
1368
|
+
* // format: 'pdf',
|
|
1369
|
+
* // useV1Api: true
|
|
1370
|
+
* // }
|
|
1371
|
+
*
|
|
1372
|
+
* // Usar con facade
|
|
1373
|
+
* this.reportFacade.generate(options).subscribe();
|
|
1374
|
+
* ```
|
|
1114
1375
|
*/
|
|
1115
|
-
static build(docData, format = 'pdf'
|
|
1376
|
+
static build(docData, format = 'pdf') {
|
|
1116
1377
|
return {
|
|
1117
1378
|
data: this.buildParams(docData, format),
|
|
1118
1379
|
format,
|
|
1119
1380
|
useV1Api: this.shouldUseV1Api(docData.codDoc),
|
|
1120
|
-
returnBlob,
|
|
1121
1381
|
};
|
|
1122
1382
|
}
|
|
1123
1383
|
/**
|
|
1124
1384
|
* Obtiene la lista de tipos de documento soportados
|
|
1385
|
+
*
|
|
1386
|
+
* @returns Array con todos los códigos de documento soportados
|
|
1387
|
+
*
|
|
1388
|
+
* @example
|
|
1389
|
+
* ```typescript
|
|
1390
|
+
* const types = ReportParamsBuilder.getSupportedDocumentTypes();
|
|
1391
|
+
* // ['FV', 'NE', 'NC', 'ND', 'GR', 'SRR', 'SRRC', 'FG', ...]
|
|
1392
|
+
* ```
|
|
1125
1393
|
*/
|
|
1126
1394
|
static getSupportedDocumentTypes() {
|
|
1127
1395
|
return Object.keys(this.reportConfigs);
|
|
1128
1396
|
}
|
|
1129
1397
|
/**
|
|
1130
1398
|
* Verifica si un tipo de documento está soportado
|
|
1399
|
+
*
|
|
1400
|
+
* @param codDoc - Código del documento a verificar
|
|
1401
|
+
* @returns true si el documento está soportado, false en caso contrario
|
|
1402
|
+
*
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```typescript
|
|
1405
|
+
* if (ReportParamsBuilder.isDocumentTypeSupported('FV')) {
|
|
1406
|
+
* // Generar reporte
|
|
1407
|
+
* } else {
|
|
1408
|
+
* console.error('Tipo de documento no soportado');
|
|
1409
|
+
* }
|
|
1410
|
+
* ```
|
|
1131
1411
|
*/
|
|
1132
1412
|
static isDocumentTypeSupported(codDoc) {
|
|
1133
1413
|
return codDoc in this.reportConfigs;
|
|
1134
1414
|
}
|
|
1135
1415
|
/**
|
|
1136
1416
|
* Registra un nuevo tipo de reporte dinámicamente
|
|
1417
|
+
*
|
|
1418
|
+
* Permite agregar soporte para nuevos tipos de reportes en runtime
|
|
1419
|
+
* sin modificar el código fuente de la librería.
|
|
1420
|
+
*
|
|
1421
|
+
* @param codDoc - Código único del documento
|
|
1422
|
+
* @param config - Configuración completa del reporte
|
|
1423
|
+
*
|
|
1424
|
+
* @example
|
|
1425
|
+
* ```typescript
|
|
1426
|
+
* // Agregar un nuevo tipo de reporte personalizado
|
|
1427
|
+
* ReportParamsBuilder.registerReportType('CUSTOM_REPORT', {
|
|
1428
|
+
* codigo: 'CUSTOM_REPORT',
|
|
1429
|
+
* hasService: true,
|
|
1430
|
+
* useV1Api: false,
|
|
1431
|
+
* idField: 'id',
|
|
1432
|
+
* hasParams: true,
|
|
1433
|
+
* extraDataParams: { customField: 'value' }
|
|
1434
|
+
* });
|
|
1435
|
+
*
|
|
1436
|
+
* // Ahora se puede usar
|
|
1437
|
+
* const options = ReportParamsBuilder.build(
|
|
1438
|
+
* { codDoc: 'CUSTOM_REPORT', id: 123 },
|
|
1439
|
+
* 'pdf'
|
|
1440
|
+
* );
|
|
1441
|
+
* ```
|
|
1137
1442
|
*/
|
|
1138
1443
|
static registerReportType(codDoc, config) {
|
|
1139
1444
|
this.reportConfigs[codDoc] = config;
|
|
@@ -1262,10 +1567,8 @@ class WhatsAppSender {
|
|
|
1262
1567
|
generateReport() {
|
|
1263
1568
|
const docData = this.config.documentData;
|
|
1264
1569
|
// Construir opciones de reporte
|
|
1265
|
-
const reportOptions = ReportParamsBuilder.build(docData, 'pdf'
|
|
1266
|
-
|
|
1267
|
-
// Manejar el caso donde generate puede retornar void
|
|
1268
|
-
return reportResult || throwError(() => new Error('No se pudo generar el reporte'));
|
|
1570
|
+
const reportOptions = ReportParamsBuilder.build(docData, 'pdf');
|
|
1571
|
+
return this.reportFacade.generate(reportOptions);
|
|
1269
1572
|
}
|
|
1270
1573
|
processReportFile(response) {
|
|
1271
1574
|
const file = FileMapperUtil.fromResponse(response);
|