@sachin9822/reports-lib 0.0.211 → 0.0.213

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.
Files changed (211) hide show
  1. package/karma.conf.js +44 -0
  2. package/ng-package.json +17 -0
  3. package/package.json +16 -31
  4. package/src/assets/images/BreadcrumbArrow.png +0 -0
  5. package/src/lib/assets/images/Search.svg +10 -0
  6. package/src/lib/assets/images/XMLogo.png +0 -0
  7. package/src/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.html +27 -0
  8. package/src/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.scss +46 -0
  9. package/src/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.spec.ts +23 -0
  10. package/src/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.ts +292 -0
  11. package/src/lib/components/accounting-report/accounting-report.component.html +64 -0
  12. package/src/lib/components/accounting-report/accounting-report.component.scss +24 -0
  13. package/src/lib/components/accounting-report/accounting-report.component.spec.ts +23 -0
  14. package/src/lib/components/accounting-report/accounting-report.component.ts +738 -0
  15. package/src/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.html +46 -0
  16. package/src/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.scss +2 -0
  17. package/src/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.spec.ts +23 -0
  18. package/src/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.ts +247 -0
  19. package/src/lib/components/branch-activity-send-report/branch-activity-send-report.component.html +46 -0
  20. package/src/lib/components/branch-activity-send-report/branch-activity-send-report.component.scss +3 -0
  21. package/src/lib/components/branch-activity-send-report/branch-activity-send-report.component.spec.ts +23 -0
  22. package/src/lib/components/branch-activity-send-report/branch-activity-send-report.component.ts +276 -0
  23. package/src/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.html +51 -0
  24. package/src/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.scss +7 -0
  25. package/src/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.spec.ts +23 -0
  26. package/src/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.ts +840 -0
  27. package/src/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.html +68 -0
  28. package/src/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.scss +65 -0
  29. package/src/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.spec.ts +23 -0
  30. package/src/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.ts +290 -0
  31. package/src/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.html +25 -0
  32. package/src/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.scss +1 -0
  33. package/src/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.spec.ts +23 -0
  34. package/src/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.ts +213 -0
  35. package/src/lib/components/funding-report/funding-report.component.html +49 -0
  36. package/src/lib/components/funding-report/funding-report.component.scss +1 -0
  37. package/src/lib/components/funding-report/funding-report.component.spec.ts +23 -0
  38. package/src/lib/components/funding-report/funding-report.component.ts +221 -0
  39. package/src/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.html +26 -0
  40. package/src/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.scss +1 -0
  41. package/src/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.spec.ts +23 -0
  42. package/src/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.ts +267 -0
  43. package/src/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.html +95 -0
  44. package/src/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.scss +54 -0
  45. package/src/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.spec.ts +23 -0
  46. package/src/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.ts +904 -0
  47. package/src/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.html +49 -0
  48. package/src/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.scss +2 -0
  49. package/src/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.spec.ts +32 -0
  50. package/src/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.ts +304 -0
  51. package/src/lib/components/transaction-monitoring-report/transaction-monitoring-report.component.html +46 -0
  52. package/src/lib/components/transaction-monitoring-report/transaction-monitoring-report.component.scss +7 -0
  53. package/src/lib/components/transaction-monitoring-report/transaction-monitoring-report.component.ts +343 -0
  54. package/src/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.html +25 -0
  55. package/src/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.scss +23 -0
  56. package/src/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.spec.ts +23 -0
  57. package/src/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.ts +745 -0
  58. package/src/lib/models/acc-gl-details-enquiry-report/accgl-details-enquiry-reportmodel.ts +10 -0
  59. package/src/lib/models/acc-gl-details-enquiry-report/journal-voucher-entry-model.ts +20 -0
  60. package/src/lib/models/accounting-report/accounting-report-branch-group.ts +7 -0
  61. package/src/lib/models/accounting-report/accounting-report-entry-model.ts +25 -0
  62. package/src/lib/models/accounting-report/accounting-report-model.ts +7 -0
  63. package/{lib/models/accounting-report/accounting-report-sub-group.d.ts → src/lib/models/accounting-report/accounting-report-sub-group.ts} +3 -2
  64. package/{lib/models/app-config.model.d.ts → src/lib/models/app-config.model.ts} +8 -7
  65. package/src/lib/models/branch-activity-receive-report/branch-activity-receive.model.ts +18 -0
  66. package/src/lib/models/branch-activity-report/branch-activity-send-report-request-domain-model.ts +16 -0
  67. package/src/lib/models/branch-activity-report/branch-activity-send-report.ts +26 -0
  68. package/src/lib/models/cancellation-transaction-report/cancellation-transaction.model.ts +65 -0
  69. package/src/lib/models/component-loading-states.ts +5 -0
  70. package/src/lib/models/credit-limit-status-enquiry-report/credit-limit-status-enquiry.model.ts +28 -0
  71. package/{lib/models/revenue-per-transaction-report/revenue-per-transaction.model.d.ts → src/lib/models/revenue-per-transaction-report/revenue-per-transaction.model.ts} +1 -1
  72. package/src/lib/models/statement-of-account-report/statement-of-accounts.model.ts +21 -0
  73. package/src/lib/models/transaction-enquiry-report/transaction-enquiry.model.ts +127 -0
  74. package/src/lib/models/transaction-monitoring-report/transaction-monitoring-report-request-domain-model.ts +16 -0
  75. package/src/lib/models/transaction-monitoring-report/transaction-monitoring-report.ts +29 -0
  76. package/src/lib/models/trial-balance-accounts-report/trial-balance-accounts.models.ts +46 -0
  77. package/src/lib/reports-lib-routing.module.ts +20 -0
  78. package/src/lib/reports-lib.component.spec.ts +23 -0
  79. package/src/lib/reports-lib.component.ts +20 -0
  80. package/src/lib/reports-lib.module.ts +102 -0
  81. package/src/lib/reports-lib.service.spec.ts +16 -0
  82. package/src/lib/reports-lib.service.ts +9 -0
  83. package/src/lib/services/config.service.ts +43 -0
  84. package/src/lib/services/report.service.spec.ts +16 -0
  85. package/src/lib/services/report.service.ts +463 -0
  86. package/src/lib/shared/export-generic.service.ts +557 -0
  87. package/src/lib/shared/export.service.spec.ts +16 -0
  88. package/src/lib/shared/export.service.ts +457 -0
  89. package/src/lib/shared/loader/loader.component.html +8 -0
  90. package/src/lib/shared/loader/loader.component.scss +52 -0
  91. package/src/lib/shared/loader/loader.component.ts +10 -0
  92. package/src/lib/shared/metadata/metadata.component.html +42 -0
  93. package/src/lib/shared/metadata/metadata.component.scss +95 -0
  94. package/src/lib/shared/metadata/metadata.component.spec.ts +23 -0
  95. package/src/lib/shared/metadata/metadata.component.ts +38 -0
  96. package/src/lib/shared/page-size-selector/page-size-selector.component.html +7 -0
  97. package/src/lib/shared/page-size-selector/page-size-selector.component.scss +42 -0
  98. package/src/lib/shared/page-size-selector/page-size-selector.component.spec.ts +23 -0
  99. package/src/lib/shared/page-size-selector/page-size-selector.component.ts +38 -0
  100. package/src/lib/shared/search-filter/search-filter.component.html +601 -0
  101. package/src/lib/shared/search-filter/search-filter.component.scss +203 -0
  102. package/src/lib/shared/search-filter/search-filter.component.spec.ts +23 -0
  103. package/src/lib/shared/search-filter/search-filter.component.ts +2387 -0
  104. package/src/lib/shared/shared.module.ts +13 -0
  105. package/{public-api.d.ts → src/public-api.ts} +17 -7
  106. package/src/styles/ag-grid-report-style.scss +71 -0
  107. package/src/styles/common-ag-grid-style.scss +146 -0
  108. package/src/styles/common-report-style.scss +395 -0
  109. package/src/styles/common-style.scss +615 -0
  110. package/src/test.ts +27 -0
  111. package/tsconfig.lib.json +15 -0
  112. package/tsconfig.lib.prod.json +10 -0
  113. package/tsconfig.spec.json +17 -0
  114. package/esm2020/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.mjs +0 -240
  115. package/esm2020/lib/components/accounting-report/accounting-report.component.mjs +0 -596
  116. package/esm2020/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.mjs +0 -218
  117. package/esm2020/lib/components/branch-activity-send-report/branch-activity-send-report.component.mjs +0 -243
  118. package/esm2020/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.mjs +0 -700
  119. package/esm2020/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.mjs +0 -255
  120. package/esm2020/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.mjs +0 -192
  121. package/esm2020/lib/components/funding-report/funding-report.component.mjs +0 -198
  122. package/esm2020/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.mjs +0 -244
  123. package/esm2020/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.mjs +0 -776
  124. package/esm2020/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.mjs +0 -266
  125. package/esm2020/lib/components/transaction-monitoring-report/transaction-monitoring-report.component.mjs +0 -311
  126. package/esm2020/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.mjs +0 -631
  127. package/esm2020/lib/models/acc-gl-details-enquiry-report/accgl-details-enquiry-reportmodel.mjs +0 -2
  128. package/esm2020/lib/models/acc-gl-details-enquiry-report/journal-voucher-entry-model.mjs +0 -2
  129. package/esm2020/lib/models/accounting-report/accounting-report-branch-group.mjs +0 -2
  130. package/esm2020/lib/models/accounting-report/accounting-report-entry-model.mjs +0 -2
  131. package/esm2020/lib/models/accounting-report/accounting-report-model.mjs +0 -2
  132. package/esm2020/lib/models/accounting-report/accounting-report-sub-group.mjs +0 -2
  133. package/esm2020/lib/models/app-config.model.mjs +0 -2
  134. package/esm2020/lib/models/branch-activity-receive-report/branch-activity-receive.model.mjs +0 -2
  135. package/esm2020/lib/models/branch-activity-report/branch-activity-send-report-request-domain-model.mjs +0 -6
  136. package/esm2020/lib/models/branch-activity-report/branch-activity-send-report.mjs +0 -3
  137. package/esm2020/lib/models/cancellation-transaction-report/cancellation-transaction.model.mjs +0 -10
  138. package/esm2020/lib/models/component-loading-states.mjs +0 -7
  139. package/esm2020/lib/models/credit-limit-status-enquiry-report/credit-limit-status-enquiry.model.mjs +0 -5
  140. package/esm2020/lib/models/revenue-per-transaction-report/revenue-per-transaction.model.mjs +0 -2
  141. package/esm2020/lib/models/statement-of-account-report/statement-of-accounts.model.mjs +0 -2
  142. package/esm2020/lib/models/transaction-enquiry-report/transaction-enquiry.model.mjs +0 -8
  143. package/esm2020/lib/models/transaction-monitoring-report/transaction-monitoring-report-request-domain-model.mjs +0 -6
  144. package/esm2020/lib/models/transaction-monitoring-report/transaction-monitoring-report.mjs +0 -5
  145. package/esm2020/lib/models/trial-balance-accounts-report/trial-balance-accounts.models.mjs +0 -3
  146. package/esm2020/lib/reports-lib-routing.module.mjs +0 -24
  147. package/esm2020/lib/reports-lib.component.mjs +0 -22
  148. package/esm2020/lib/reports-lib.module.mjs +0 -172
  149. package/esm2020/lib/reports-lib.service.mjs +0 -14
  150. package/esm2020/lib/services/config.service.mjs +0 -39
  151. package/esm2020/lib/services/report.service.mjs +0 -374
  152. package/esm2020/lib/shared/export-generic.service.mjs +0 -443
  153. package/esm2020/lib/shared/export.service.mjs +0 -379
  154. package/esm2020/lib/shared/loader/loader.component.mjs +0 -11
  155. package/esm2020/lib/shared/metadata/metadata.component.mjs +0 -51
  156. package/esm2020/lib/shared/page-size-selector/page-size-selector.component.mjs +0 -44
  157. package/esm2020/lib/shared/search-filter/search-filter.component.mjs +0 -2134
  158. package/esm2020/lib/shared/shared.module.mjs +0 -21
  159. package/esm2020/public-api.mjs +0 -41
  160. package/esm2020/sachin9822-reports-lib.mjs +0 -5
  161. package/fesm2015/sachin9822-reports-lib.mjs +0 -8402
  162. package/fesm2015/sachin9822-reports-lib.mjs.map +0 -1
  163. package/fesm2020/sachin9822-reports-lib.mjs +0 -8384
  164. package/fesm2020/sachin9822-reports-lib.mjs.map +0 -1
  165. package/index.d.ts +0 -5
  166. package/lib/components/acc-gl-details-enquiry-report/acc-gl-details-enquiry-report.component.d.ts +0 -70
  167. package/lib/components/accounting-report/accounting-report.component.d.ts +0 -76
  168. package/lib/components/branch-activity-receive-report/branch-activity-receive-report.component.d.ts +0 -58
  169. package/lib/components/branch-activity-send-report/branch-activity-send-report.component.d.ts +0 -69
  170. package/lib/components/branch-activity-send-summary-report/branch-activity-send-summary-report.component.d.ts +0 -78
  171. package/lib/components/cancellation-transaction-report/cancellation-transaction-report.component.d.ts +0 -95
  172. package/lib/components/credit-limit-status-enquiry-report/credit-limit-status-enquiry-report.component.d.ts +0 -55
  173. package/lib/components/funding-report/funding-report.component.d.ts +0 -55
  174. package/lib/components/revenue-per-transaction-report/revenue-per-transaction-report.component.d.ts +0 -57
  175. package/lib/components/statement-of-accounts-report/statement-of-accounts-report.component.d.ts +0 -109
  176. package/lib/components/transaction-enquiry-report/transaction-enquiry-report.component.d.ts +0 -76
  177. package/lib/components/transaction-monitoring-report/transaction-monitoring-report.component.d.ts +0 -68
  178. package/lib/components/trial-balance-accounts-report/trial-balance-accounts-report.component.d.ts +0 -71
  179. package/lib/models/acc-gl-details-enquiry-report/accgl-details-enquiry-reportmodel.d.ts +0 -8
  180. package/lib/models/acc-gl-details-enquiry-report/journal-voucher-entry-model.d.ts +0 -18
  181. package/lib/models/accounting-report/accounting-report-branch-group.d.ts +0 -6
  182. package/lib/models/accounting-report/accounting-report-entry-model.d.ts +0 -24
  183. package/lib/models/accounting-report/accounting-report-model.d.ts +0 -6
  184. package/lib/models/branch-activity-receive-report/branch-activity-receive.model.d.ts +0 -18
  185. package/lib/models/branch-activity-report/branch-activity-send-report-request-domain-model.d.ts +0 -13
  186. package/lib/models/branch-activity-report/branch-activity-send-report.d.ts +0 -24
  187. package/lib/models/cancellation-transaction-report/cancellation-transaction.model.d.ts +0 -44
  188. package/lib/models/component-loading-states.d.ts +0 -5
  189. package/lib/models/credit-limit-status-enquiry-report/credit-limit-status-enquiry.model.d.ts +0 -26
  190. package/lib/models/statement-of-account-report/statement-of-accounts.model.d.ts +0 -18
  191. package/lib/models/transaction-enquiry-report/transaction-enquiry.model.d.ts +0 -113
  192. package/lib/models/transaction-monitoring-report/transaction-monitoring-report-request-domain-model.d.ts +0 -13
  193. package/lib/models/transaction-monitoring-report/transaction-monitoring-report.d.ts +0 -23
  194. package/lib/models/trial-balance-accounts-report/trial-balance-accounts.models.d.ts +0 -38
  195. package/lib/reports-lib-routing.module.d.ts +0 -7
  196. package/lib/reports-lib.component.d.ts +0 -8
  197. package/lib/reports-lib.module.d.ts +0 -33
  198. package/lib/reports-lib.service.d.ts +0 -6
  199. package/lib/services/config.service.d.ts +0 -20
  200. package/lib/services/report.service.d.ts +0 -80
  201. package/lib/shared/export-generic.service.d.ts +0 -50
  202. package/lib/shared/export.service.d.ts +0 -32
  203. package/lib/shared/loader/loader.component.d.ts +0 -5
  204. package/lib/shared/metadata/metadata.component.d.ts +0 -24
  205. package/lib/shared/page-size-selector/page-size-selector.component.d.ts +0 -15
  206. package/lib/shared/search-filter/search-filter.component.d.ts +0 -127
  207. package/lib/shared/shared.module.d.ts +0 -11
  208. /package/{lib → src}/assets/images/Search.svg +0 -0
  209. /package/{lib/assets/images/XMLogo.png → src/assets/images/XMCoral.png} +0 -0
  210. /package/{lib → src/lib}/assets/config/app-config.json +0 -0
  211. /package/{lib → src/lib}/assets/svg-loaders/blue-tail-spin.svg +0 -0
@@ -0,0 +1,557 @@
1
+ // export-generic.service.ts
2
+ import { Injectable } from '@angular/core';
3
+ import jsPDF from 'jspdf';
4
+ import 'jspdf-autotable';
5
+ import * as ExcelJS from 'exceljs';
6
+ import * as FileSaver from 'file-saver';
7
+
8
+ export interface ExportOptions {
9
+ currencyFields?: string[];
10
+ dateFields?: string[];
11
+ dateFieldFormats?: {
12
+ [field: string]: 'dd/MM/yyyy' | 'dd/MM/yyyy hh:mm a' | 'dd/MM/yyyy HH:mm' | string;
13
+ };
14
+ excludeFields?: string[];
15
+ groupByKey?: boolean;
16
+ specialDecimalFields?: {
17
+ [field: string]: number;
18
+ };
19
+ }
20
+
21
+ @Injectable({ providedIn: 'root' })
22
+ export class GenericExportService {
23
+ private logoBase64?: string;
24
+ private readonly pdfFontSize = 10;
25
+ private readonly pdfLineHeight = 7;
26
+ private readonly pdfLeftMargin = 15;
27
+
28
+ constructor() {
29
+ this.loadImageAsBase64('assets/reports-lib/assets/images/XMLogo.png').then(base64 => {
30
+ this.logoBase64 = base64;
31
+ }).catch(() => {
32
+ console.warn('Logo image failed to load');
33
+ });
34
+ }
35
+
36
+ private loadImageAsBase64(imagePath: string): Promise<string> {
37
+ return new Promise((resolve, reject) => {
38
+ const img = new Image();
39
+ img.crossOrigin = 'anonymous';
40
+ img.src = imagePath;
41
+ img.onload = () => {
42
+ const scale = 6;
43
+ const canvas = document.createElement('canvas');
44
+ canvas.width = img.width * scale;
45
+ canvas.height = img.height * scale;
46
+ const ctx = canvas.getContext('2d');
47
+ if (ctx) {
48
+ ctx.scale(scale, scale);
49
+ ctx.drawImage(img, 0, 0);
50
+ resolve(canvas.toDataURL('image/png', 1.0));
51
+ } else {
52
+ reject('Failed to get canvas context.');
53
+ }
54
+ };
55
+ img.onerror = () => reject('Failed to load image: ' + imagePath);
56
+ });
57
+ }
58
+
59
+ // ==================== PDF EXPORT ====================
60
+ async exportToPdf({ title, metadata, searchCriteria, columns, data, options = {} }: {
61
+ title: string;
62
+ metadata: [string, string][];
63
+ searchCriteria: [string, string][];
64
+ columns: any[];
65
+ data: any[] | Record<string, any[]>;
66
+ options?: ExportOptions;
67
+ }): Promise<void> {
68
+ const doc = new jsPDF({ orientation: 'landscape', unit: 'mm', format: [400, 210] });
69
+ const pageWidth = doc.internal.pageSize.width;
70
+ const pageHeight = doc.internal.pageSize.height;
71
+ const marginLeft = 15;
72
+ const headers = ['Sr No', ...columns.map(c => c.headerName || c.field)];
73
+ let rows: any[][] = [];
74
+
75
+ if (options.groupByKey && !Array.isArray(data)) {
76
+ for (const [group, items] of Object.entries(data)) {
77
+ rows.push([{ content: group, colSpan: headers.length, styles: { halign: 'left', fontStyle: 'bold' } }]);
78
+ rows.push(...this.prepareTableRows(items, columns, options));
79
+ }
80
+ } else {
81
+ rows = this.prepareTableRows(data as any[], columns, options);
82
+ }
83
+
84
+ // Add logo at top left if available
85
+ if (this.logoBase64) {
86
+ doc.addImage(this.logoBase64, 'PNG', 15, 10, 38.4, 17.5);
87
+ }
88
+
89
+ // Title
90
+ doc.setFontSize(15);
91
+ doc.setFont('helvetica', 'bold');
92
+ doc.text(title, doc.internal.pageSize.width / 2, 18, { align: 'center' });
93
+
94
+ // Metadata
95
+ let currentY = 32;
96
+ doc.setFontSize(this.pdfFontSize);
97
+ metadata.forEach(([label, value], i) => {
98
+ const y = currentY + i * this.pdfLineHeight;
99
+ doc.setFont('helvetica', 'bold');
100
+ const labelWithColon = label + ' :';
101
+ doc.text(labelWithColon, this.pdfLeftMargin, y);
102
+ const labelWidth = doc.getTextWidth(labelWithColon + ' ');
103
+ doc.setFont('helvetica', 'normal');
104
+ doc.text(value, this.pdfLeftMargin + labelWidth, y);
105
+ });
106
+
107
+ currentY += metadata.length * this.pdfLineHeight + 5;
108
+
109
+ // --- draw search criteria heading ---
110
+ doc.setFont('helvetica', 'bold');
111
+ doc.text('Search Criteria:-', marginLeft, currentY);
112
+ currentY += 8;
113
+
114
+ const rowHeightPerLine = 6;
115
+ const minGap = 2; // gap between key and value
116
+ const betweenColumnsGap = 15; // gap between left and right columns (you can tune this)
117
+ const pageContentWidth = doc.internal.pageSize.width - 2 * marginLeft;
118
+
119
+ // --- helper function to measure total width of a key-value pair ---
120
+ const measurePairWidth = (key: string, value: string): number => {
121
+ doc.setFont('helvetica', 'bold');
122
+ const keyWidth = doc.getTextWidth(key + ' ');
123
+ doc.setFont('helvetica', 'normal');
124
+ const valueWidth = doc.getTextWidth(value);
125
+ return keyWidth + minGap + valueWidth;
126
+ };
127
+
128
+ // --- calculate max width of left column pairs dynamically ---
129
+ let maxLeftPairWidth = 0;
130
+ for (let i = 0; i < searchCriteria.length; i += 2) {
131
+ if (searchCriteria[i]) {
132
+ const [key, value] = searchCriteria[i];
133
+ const width = measurePairWidth(key, value);
134
+ if (width > maxLeftPairWidth) maxLeftPairWidth = width;
135
+ }
136
+ }
137
+
138
+ // --- compute where right column should start ---
139
+ const leftColumnStart = marginLeft;
140
+ const rightColumnStart = leftColumnStart + maxLeftPairWidth + betweenColumnsGap;
141
+
142
+ // --- print search criteria dynamically ---
143
+ for (let i = 0; i < searchCriteria.length; i += 2) {
144
+ let maxLineCount = 1;
145
+
146
+ // Left column
147
+ if (searchCriteria[i]) {
148
+ const [key, value] = searchCriteria[i];
149
+ doc.setFont('helvetica', 'bold');
150
+ doc.text(key, leftColumnStart, currentY);
151
+
152
+ doc.setFont('helvetica', 'normal');
153
+ const keyWidth = doc.getTextWidth(key + ' ');
154
+ const valueX = leftColumnStart + keyWidth + minGap;
155
+ doc.text(value, valueX, currentY);
156
+ }
157
+
158
+ // Right column
159
+ if (searchCriteria[i + 1]) {
160
+ const [key, value] = searchCriteria[i + 1];
161
+ doc.setFont('helvetica', 'bold');
162
+ doc.text(key, rightColumnStart, currentY);
163
+
164
+ doc.setFont('helvetica', 'normal');
165
+ const keyWidth = doc.getTextWidth(key + ' ');
166
+ const valueX = rightColumnStart + keyWidth + minGap;
167
+ doc.text(value, valueX, currentY);
168
+ }
169
+
170
+ currentY += rowHeightPerLine;
171
+ }
172
+
173
+ // Table styling
174
+ const columnStyles: { [key: number]: any } = {};
175
+ headers.forEach((_, i) => columnStyles[i] = { cellWidth: 'auto' });
176
+
177
+ doc.autoTable({
178
+ head: [headers],
179
+ body: rows,
180
+ startY: currentY,
181
+ theme: 'grid',
182
+ didParseCell: function (data) {
183
+
184
+ // Check if this row contains 'Total'
185
+ const rowValues = data.row.cells;
186
+
187
+ const isTotalRow = Object.values(rowValues).some(
188
+ (cell: any) => cell.raw === 'Total'
189
+ );
190
+
191
+ if (isTotalRow) {
192
+ data.cell.styles.fontStyle = 'bold';
193
+ data.cell.styles.fontSize = 10;
194
+ }
195
+ },
196
+ headStyles: {
197
+ fillColor: [220, 220, 220],
198
+ textColor: 0,
199
+ fontStyle: 'bold',
200
+ fontSize: 9,
201
+ halign: 'center'
202
+ },
203
+ bodyStyles: {
204
+ fontSize: 8,
205
+ fillColor: [255, 255, 255],
206
+ textColor: [0, 0, 0]
207
+ },
208
+ styles: {
209
+ cellPadding: 2,
210
+ overflow: 'linebreak',
211
+ minCellHeight: 6
212
+ },
213
+ columnStyles
214
+ });
215
+
216
+ const pageCount = (doc as any).internal.getNumberOfPages();
217
+ for (let i = 1; i <= pageCount; i++) {
218
+ doc.setFont("helvetica", "bold");
219
+ doc.setPage(i);
220
+ doc.setFontSize(9);
221
+ doc.setTextColor(100);
222
+
223
+ const footerY = pageHeight - 6;
224
+
225
+ doc.text(
226
+ `Page ${i} of ${pageCount}`,
227
+ pageWidth - 15,
228
+ footerY,
229
+ { align: 'right' }
230
+ );
231
+ }
232
+
233
+ doc.save(title.replace(/\s/g, '-') + '.pdf');
234
+ }
235
+
236
+ // ========== EXCEL EXPORT ==========
237
+ async exportToExcel({
238
+ title,
239
+ metadata,
240
+ searchCriteria,
241
+ columns,
242
+ data,
243
+ options = {}
244
+ }: {
245
+ title: string;
246
+ metadata: [string, string][];
247
+ searchCriteria: [string, string][];
248
+ columns: any[];
249
+ data: any[] | Record<string, any[]>;
250
+ options?: ExportOptions;
251
+ }): Promise<void> {
252
+ const workbook = new ExcelJS.Workbook();
253
+ const worksheet = workbook.addWorksheet(title);
254
+
255
+ // Pre-calc
256
+ const headerLabels = ['Sr No', ...columns.map(col => col.headerName || col.field)];
257
+ const totalCols = headerLabels.length;
258
+ const titleStartCol = 4; // Title in 4th Cell
259
+ const titleColLetter = String.fromCharCode(64 + titleStartCol);
260
+ const titleMergeEnd = String.fromCharCode(64 + totalCols);
261
+
262
+ // --- Add logo in A1:C3, and make those cells tall
263
+ if (this.logoBase64) {
264
+ const base64Data = this.logoBase64.replace(/^data:image\/\w+;base64,/, '');
265
+ const imageId = workbook.addImage({
266
+ base64: base64Data,
267
+ extension: 'png',
268
+ });
269
+ worksheet.addImage(imageId, {
270
+ tl: { col: 0, row: 0 },
271
+ ext: { width: 145, height: 66 }
272
+ });
273
+ // Optionally, make rows 1-3 taller to fully display logo
274
+ worksheet.getRow(1).height = 24;
275
+ worksheet.getRow(2).height = 22;
276
+ worksheet.getRow(3).height = 22;
277
+ }
278
+
279
+ // --- Title in D1 merged up to end
280
+ worksheet.mergeCells(`${titleColLetter}1:${titleMergeEnd}1`);
281
+ const titleCell = worksheet.getRow(1).getCell(titleStartCol);
282
+ titleCell.value = title;
283
+ titleCell.font = { bold: true, size: 16 };
284
+ titleCell.alignment = { vertical: 'middle', horizontal: 'left' };
285
+
286
+ // --- Leave A2:C3 empty (for image), place metadata starting at row 4
287
+ let metaRowStart = 4;
288
+ metadata.forEach(([label, value], idx) => {
289
+ const row = worksheet.getRow(metaRowStart + idx);
290
+ row.getCell(1).value = {
291
+ richText: [
292
+ { text: `${label} : `, font: { bold: true } },
293
+ { text: value ?? '', font: { bold: false } }
294
+ ]
295
+ };
296
+ worksheet.mergeCells(`A${row.number}:${titleMergeEnd}${row.number}`);
297
+ });
298
+
299
+ let currentRow = metaRowStart + metadata.length;
300
+
301
+ worksheet.getRow(++currentRow).height = 6; // Blank for readability
302
+ currentRow++;
303
+
304
+ worksheet.mergeCells(currentRow, 1, currentRow, 6);
305
+ worksheet.getCell(currentRow, 1).value = 'Search Criteria:-';
306
+ worksheet.getCell(currentRow, 1).font = { bold: true };
307
+ currentRow += 2;
308
+
309
+ for (let i = 0; i < searchCriteria.length; i += 2) {
310
+ const left = searchCriteria[i];
311
+ const right = searchCriteria[i + 1]; // may be undefined
312
+ const row = worksheet.getRow(currentRow++);
313
+
314
+ // Left cell
315
+ worksheet.mergeCells(row.number, 1, row.number, 3);
316
+ row.getCell(1).value = {
317
+ richText: [
318
+ { text: (left?.[0] ?? '') + ' ', font: { bold: true } },
319
+ { text: left?.[1] ?? '' }
320
+ ]
321
+ };
322
+ row.getCell(1).alignment = { wrapText: true, vertical: 'middle' };
323
+
324
+ // Right cell (only if present)
325
+ if (right) {
326
+ worksheet.mergeCells(row.number, 4, row.number, 6);
327
+ row.getCell(4).value = {
328
+ richText: [
329
+ { text: (right?.[0] ?? '') + ' ', font: { bold: true } },
330
+ { text: right?.[1] ?? '' }
331
+ ]
332
+ };
333
+ row.getCell(4).alignment = { wrapText: true, vertical: 'middle' };
334
+ }
335
+ }
336
+ currentRow++;
337
+
338
+ // Add headers and rest as before...
339
+ const headerRow = worksheet.getRow(currentRow++);
340
+ headerLabels.forEach((header, i) => {
341
+ const cell = headerRow.getCell(i + 1);
342
+ cell.value = header;
343
+ cell.font = { bold: true };
344
+ cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'D9D9D9' } };
345
+ cell.alignment = { vertical: 'middle', horizontal: 'center' };
346
+ cell.border = {
347
+ top: { style: 'thin' },
348
+ bottom: { style: 'thin' },
349
+ left: { style: 'thin' },
350
+ right: { style: 'thin' }
351
+ };
352
+ });
353
+
354
+ // Data rows (handle grouping if present)
355
+ let rowIdx = currentRow;
356
+ if (options.groupByKey && !Array.isArray(data)) {
357
+ for (const [group, items] of Object.entries(data)) {
358
+ // Group header: bold, merged row
359
+ const groupRow = worksheet.getRow(rowIdx++);
360
+ groupRow.getCell(1).value = group;
361
+ groupRow.font = { bold: true };
362
+ worksheet.mergeCells(`A${groupRow.number}:${titleMergeEnd}${groupRow.number}`);
363
+
364
+ // Data rows
365
+ this.prepareTableRows(items, columns, options).forEach(rowData => {
366
+ const dataRow = worksheet.getRow(rowIdx++);
367
+ rowData.forEach((cellVal, cIdx) => {
368
+ const cell = dataRow.getCell(cIdx + 1);
369
+ cell.value = cellVal;
370
+ cell.border = {
371
+ top: { style: 'thin' }, bottom: { style: 'thin' },
372
+ left: { style: 'thin' }, right: { style: 'thin' }
373
+ };
374
+ });
375
+ });
376
+ }
377
+ } else {
378
+ this.prepareTableRows(data as any[], columns, options).forEach(rowData => {
379
+ const dataRow = worksheet.getRow(rowIdx++);
380
+ rowData.forEach((cellVal, cIdx) => {
381
+ const cell = dataRow.getCell(cIdx + 1);
382
+ cell.value = cellVal;
383
+ cell.border = {
384
+ top: { style: 'thin' }, bottom: { style: 'thin' },
385
+ left: { style: 'thin' }, right: { style: 'thin' }
386
+ };
387
+ });
388
+ });
389
+ }
390
+
391
+ // --- Auto column widths
392
+ for (let i = 1; i <= totalCols; i++) {
393
+ let maxLength = 10; // minimum width
394
+ worksheet.eachRow({ includeEmpty: true }, (row) => {
395
+ const cell = row.getCell(i);
396
+ if (cell.isMerged) return;
397
+ let cellValue = '';
398
+ if (cell.value == null) {
399
+ cellValue = '';
400
+ } else if (typeof cell.value === 'object' && 'richText' in cell.value) {
401
+ cellValue = cell.value.richText.map(rt => rt.text).join('');
402
+ } else {
403
+ cellValue = cell.value.toString();
404
+ }
405
+ maxLength = Math.max(maxLength, cellValue.length);
406
+ });
407
+ // cap max width (so branch names don't explode sheet)
408
+ const adjustedWidth = Math.min(maxLength, 50);
409
+ worksheet.getColumn(i).width = adjustedWidth;
410
+ }
411
+
412
+ // --- Save workbook
413
+ const buffer = await workbook.xlsx.writeBuffer();
414
+ FileSaver.saveAs(
415
+ new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }),
416
+ title.replace(/\s/g, '-') + '.xlsx'
417
+ );
418
+ }
419
+
420
+
421
+ // ========== CSV EXPORT ==========
422
+ exportToCsv({
423
+ title,
424
+ metadata,
425
+ searchCriteria,
426
+ columns,
427
+ data,
428
+ options = {}
429
+ }: {
430
+ title: string;
431
+ metadata: [string, string][];
432
+ searchCriteria: [string, string][];
433
+ columns: any[];
434
+ data: any[] | Record<string, any[]>;
435
+ options?: ExportOptions;
436
+ }): void {
437
+ const headers = ['Sr No', ...columns.map(c => c.headerName || c.field)];
438
+ const metaLines = metadata.map(([lbl, val]) => `${lbl}: ${val}`);
439
+ let csv = [title, ...metaLines, ''];
440
+
441
+ //=======================================================
442
+ csv.push('Search Criteria:-');
443
+
444
+ const maxLeftLength = Math.max(...searchCriteria.map(([k, v]) => (k + ' ' + v).length));
445
+
446
+ for (let i = 0; i < searchCriteria.length; i += 2) {
447
+ const left = searchCriteria[i];
448
+ const right = searchCriteria[i + 1];
449
+
450
+ const leftText = left ? `${left[0]} ${left[1]}`.padEnd(maxLeftLength + 5, ' ') : '';
451
+ const rightText = right ? `${right[0]} ${right[1]}` : '';
452
+
453
+ csv.push(`"${leftText}${rightText}"`); // wrap in quotes for single CSV cell
454
+ }
455
+
456
+ csv.push(''); // blank line after criteria
457
+
458
+ // --- Headers ---
459
+ csv.push(headers.join(','));
460
+ //========================================================
461
+
462
+ if (options.groupByKey && !Array.isArray(data)) {
463
+ for (const [group, items] of Object.entries(data)) {
464
+ csv.push(group);
465
+ this.prepareTableRows(items, columns, options).forEach(row => {
466
+ csv.push(row.map(val => `"${val}"`).join(','));
467
+ });
468
+ }
469
+ } else {
470
+ this.prepareTableRows(data as any[], columns, options).forEach(row => {
471
+ csv.push(row.map(val => `"${val}"`).join(','));
472
+ });
473
+ }
474
+
475
+ // Download
476
+ const blob = new Blob([csv.join('\n')], { type: 'text/csv' });
477
+ FileSaver.saveAs(blob, title.replace(/\s/g, '-') + '.csv');
478
+ }
479
+
480
+ // ================= HELPER: TABLE ROW PREPARATION =================
481
+ private prepareTableRows(data: any[], columns: any[], options: ExportOptions = {}): any[][] {
482
+ const {
483
+ excludeFields = [],
484
+ currencyFields = [],
485
+ dateFields = [],
486
+ dateFieldFormats = {},
487
+ specialDecimalFields = {}
488
+ } = options;
489
+
490
+ const filtered = columns.filter(c =>
491
+ c.field && !excludeFields.some(f => c.field.toLowerCase().includes(f))
492
+ );
493
+
494
+ return data.map((row, idx) => ([
495
+ idx + 1,
496
+ ...filtered.map(col => {
497
+ let value = row[col.field];
498
+
499
+ // Format date fields with custom formats if specified
500
+ if (col.field && (dateFields.includes(col.field) || col.field in dateFieldFormats)) {
501
+ const format = dateFieldFormats[col.field] || 'dd/MM/yyyy';
502
+ value = this.formatDateByFormat(value, format);
503
+ }
504
+ // Format special decimal fields with specific decimals
505
+ else if (col.field && col.field in specialDecimalFields && value != null) {
506
+ const decimals = specialDecimalFields[col.field];
507
+ value = parseFloat(value).toFixed(decimals);
508
+ }
509
+ // Format regular currency fields with 3 decimals
510
+ else if (col.field && currencyFields.includes(col.field) && value != null) {
511
+ value = parseFloat(value).toFixed(3);
512
+ }
513
+
514
+ return value ?? '';
515
+ })
516
+ ]));
517
+ }
518
+
519
+ private formatDateByFormat(val: any, format: string): string {
520
+ // Helper to pad single digits
521
+ const pad = (n: number) => n.toString().padStart(2, '0');
522
+
523
+ // Parse date for "DD/MM/YYYY hh:mm AM/PM" format manually
524
+ if (typeof val === 'string' && /^\d{2}\/\d{2}\/\d{4} \d{2}:\d{2} [AP]M$/.test(val)) {
525
+ const parts = val.match(/^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}) ([AP]M)$/);
526
+ if (!parts) return '';
527
+ let [, dd, mm, yyyy, hh, min, ampm] = parts;
528
+ let hours = parseInt(hh, 10);
529
+ if (ampm === 'PM' && hours !== 12) hours += 12;
530
+ if (ampm === 'AM' && hours === 12) hours = 0;
531
+ val = new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10), hours, parseInt(min, 10));
532
+ } else {
533
+ val = new Date(val);
534
+ }
535
+
536
+ if (isNaN(val.getTime())) return '';
537
+
538
+ const dt = val;
539
+
540
+ switch (format) {
541
+ case 'dd/MM/yyyy hh:mm a': { // 11/07/2025 10:56 PM
542
+ let hours12 = dt.getHours() % 12 || 12;
543
+ let ampm = dt.getHours() >= 12 ? 'PM' : 'AM';
544
+ return `${pad(dt.getDate())}/${pad(dt.getMonth() + 1)}/${dt.getFullYear()} ${pad(hours12)}:${pad(dt.getMinutes())} ${ampm}`;
545
+ }
546
+ case 'dd/MM/yyyy HH:mm': { // 28/07/2025 13:08
547
+ return `${pad(dt.getDate())}/${pad(dt.getMonth() + 1)}/${dt.getFullYear()} ${pad(dt.getHours())}:${pad(dt.getMinutes())}`;
548
+ }
549
+ case 'dd/MM/yyyy': { // 28/07/2025
550
+ return `${pad(dt.getDate())}/${pad(dt.getMonth() + 1)}/${dt.getFullYear()}`;
551
+ }
552
+ default:
553
+ return dt.toLocaleString();
554
+ }
555
+ }
556
+
557
+ }
@@ -0,0 +1,16 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { ExportService } from './export.service';
4
+
5
+ describe('ExportService', () => {
6
+ let service: ExportService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(ExportService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });