@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.
@@ -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
- // Método principal unificado
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
- // Métodos de conveniencia para casos comunes
194
- generatePDF(data, forceDownload = false) {
195
- this.reportPort.generate({
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
- forceDownload,
423
+ useV1Api,
199
424
  });
200
425
  }
201
- generateExcel(data, forceDownload = false) {
202
- this.reportPort.generate({
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
- forceDownload,
455
+ useV1Api,
206
456
  });
207
457
  }
208
- generateWord(data, forceDownload = false) {
209
- this.reportPort.generate({
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
- forceDownload,
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
- * Utilidades para manejo de archivos y respuestas HTTP
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, format = 'pdf', useV1Api = false, forceDownload = false, returnBlob = false, } = options;
693
- const reportVersionPath = useV1Api ? '/v1/' : '/v2/';
694
- const fullPath = `${API_PATHS.REPORTS}${reportVersionPath}download`;
695
- const requestOptions = {
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, format),
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, format) {
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', returnBlob = false) {
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', true);
1266
- const reportResult = this.reportFacade.generate(reportOptions);
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);