gemcap-be-common 1.2.140 → 1.3.0

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 (303) hide show
  1. package/classes/bank-transaction-item.d.ts +17 -0
  2. package/classes/bank-transaction-item.js +64 -0
  3. package/classes/bank-transaction-item.ts +66 -0
  4. package/classes/bank-uploaded-transaction.d.ts +17 -0
  5. package/classes/bank-uploaded-transaction.js +35 -0
  6. package/classes/bank-uploaded-transaction.ts +35 -0
  7. package/classes/inventory-item.d.ts +41 -0
  8. package/classes/inventory-item.js +44 -0
  9. package/classes/inventory-item.ts +63 -0
  10. package/classes/payable-account-item.d.ts +22 -0
  11. package/classes/payable-account-item.js +27 -0
  12. package/classes/payable-account-item.ts +35 -0
  13. package/classes/quickbook-item.d.ts +37 -0
  14. package/classes/quickbook-item.js +51 -0
  15. package/classes/quickbook-item.ts +59 -0
  16. package/classes/receivable-item.d.ts +26 -0
  17. package/classes/receivable-item.js +28 -0
  18. package/classes/receivable-item.ts +38 -0
  19. package/constants/date-formats.contsants.d.ts +1 -0
  20. package/constants/date-formats.contsants.js +4 -0
  21. package/constants/date-formats.contsants.ts +1 -0
  22. package/db/brokers.db.d.ts +185 -0
  23. package/db/brokers.db.js +35 -2
  24. package/db/brokers.db.ts +34 -1
  25. package/db/collateral-adjustments.db.d.ts +34 -0
  26. package/db/collateral-adjustments.db.js +52 -0
  27. package/db/collateral-adjustments.db.ts +54 -0
  28. package/db/collaterals.db.d.ts +1 -1
  29. package/db/equipment.db.d.ts +40 -0
  30. package/db/equipment.db.js +55 -0
  31. package/db/equipment.db.ts +56 -0
  32. package/db/financial-spreading.db.ts +2 -1
  33. package/db/groups.d.ts +5 -0
  34. package/db/groups.js +57 -0
  35. package/db/groups.ts +52 -0
  36. package/db/inventories.d.ts +91 -0
  37. package/db/inventories.js +449 -0
  38. package/db/inventories.ts +481 -0
  39. package/db/inventory-availability.d.ts +3 -0
  40. package/db/inventory-availability.js +103 -0
  41. package/db/inventory-availability.ts +113 -0
  42. package/db/new-summary.d.ts +31 -0
  43. package/db/new-summary.js +1295 -0
  44. package/db/new-summary.ts +1509 -0
  45. package/db/payable-accounts.d.ts +30 -0
  46. package/db/payable-accounts.js +55 -0
  47. package/db/payable-accounts.ts +50 -0
  48. package/db/reserve.db.d.ts +34 -0
  49. package/db/reserve.db.js +52 -0
  50. package/db/reserve.db.ts +48 -0
  51. package/db/uploads.db.d.ts +2 -0
  52. package/db/uploads.db.js +29 -0
  53. package/db/uploads.db.ts +24 -0
  54. package/helpers/main.helper.d.ts +31 -0
  55. package/helpers/main.helper.js +63 -0
  56. package/helpers/main.helper.ts +63 -0
  57. package/models/AccountPayableItem.model.d.ts +6 -6
  58. package/models/AllocatedBankTransaction.model.d.ts +54 -0
  59. package/models/AllocatedBankTransaction.model.js +70 -0
  60. package/models/AllocatedBankTransaction.model.ts +94 -0
  61. package/models/AllocatedData.model.d.ts +33 -0
  62. package/models/AllocatedData.model.js +19 -0
  63. package/models/AllocatedData.model.ts +24 -0
  64. package/models/BBCDate.model.d.ts +3 -3
  65. package/models/BBCSheet.model.d.ts +3 -3
  66. package/models/Banks.model.d.ts +3 -3
  67. package/models/Borrower.model.d.ts +3 -3
  68. package/models/BorrowerData.model.d.ts +3 -3
  69. package/models/BorrowerDataInsurance.model.d.ts +3 -3
  70. package/models/BorrowerDataTerm.model.d.ts +3 -3
  71. package/models/BorrowerSummary.model.js +1 -1
  72. package/models/BorrowerSummary.model.ts +1 -1
  73. package/models/CalandarDay.model.d.ts +40 -0
  74. package/models/CalandarDay.model.js +47 -0
  75. package/models/CalandarDay.model.ts +61 -0
  76. package/models/CashAllocationProduct.model.d.ts +119 -0
  77. package/models/CashAllocationProduct.model.js +102 -0
  78. package/models/CashAllocationProduct.model.ts +112 -0
  79. package/models/CashAllocationReference.model.d.ts +37 -0
  80. package/models/CashAllocationReference.model.js +27 -0
  81. package/models/CashAllocationReference.model.ts +40 -0
  82. package/models/CollateralAdjustment.model.d.ts +51 -0
  83. package/models/CollateralAdjustment.model.js +61 -0
  84. package/models/CollateralAdjustment.model.ts +98 -0
  85. package/models/Company.model.d.ts +35 -0
  86. package/models/Company.model.js +18 -0
  87. package/models/Company.model.ts +29 -0
  88. package/models/CustomerAPGroup.model.d.ts +32 -0
  89. package/models/CustomerAPGroup.model.js +24 -0
  90. package/models/CustomerAPGroup.model.ts +31 -0
  91. package/models/Equipment.model.d.ts +53 -0
  92. package/models/Equipment.model.js +140 -0
  93. package/models/Equipment.model.ts +172 -0
  94. package/models/FinancialCompliance.model.d.ts +39 -0
  95. package/models/FinancialCompliance.model.js +64 -0
  96. package/models/FinancialCompliance.model.ts +78 -0
  97. package/models/FinancialComplianceBorrower.model.d.ts +58 -0
  98. package/models/FinancialComplianceBorrower.model.js +82 -0
  99. package/models/FinancialComplianceBorrower.model.ts +118 -0
  100. package/models/FinancialIndexes.model.d.ts +36 -0
  101. package/models/FinancialIndexes.model.js +27 -0
  102. package/models/FinancialIndexes.model.ts +37 -0
  103. package/models/Inventory.model.d.ts +18 -18
  104. package/models/InventoryAvailability.model.d.ts +21 -21
  105. package/models/InventoryAvailabilityItem.model.d.ts +6 -6
  106. package/models/InventoryItem.model.d.ts +24 -24
  107. package/models/InventoryManualEntry.model.d.ts +9 -9
  108. package/models/InventorySeasonalRates.model.d.ts +3 -3
  109. package/models/LoanBroker.model.d.ts +3 -3
  110. package/models/LoanCharges.model.d.ts +12 -12
  111. package/models/LoanProducts.model.d.ts +9 -9
  112. package/models/LoanStatementStatus.model.d.ts +35 -0
  113. package/models/LoanStatementStatus.model.js +34 -0
  114. package/models/LoanStatementStatus.model.ts +45 -0
  115. package/models/LoanStatementTransaction.model.d.ts +9 -9
  116. package/models/LoanTransactionFile.model.d.ts +41 -0
  117. package/models/LoanTransactionFile.model.js +44 -0
  118. package/models/LoanTransactionFile.model.ts +61 -0
  119. package/models/MappedGroup.model.d.ts +37 -0
  120. package/models/MappedGroup.model.js +33 -0
  121. package/models/MappedGroup.model.ts +46 -0
  122. package/models/MonthEndData.Model.d.ts +41 -0
  123. package/models/MonthEndData.Model.js +42 -0
  124. package/models/MonthEndData.Model.ts +53 -0
  125. package/models/OrganizationEmails.model.d.ts +44 -0
  126. package/models/OrganizationEmails.model.js +40 -0
  127. package/models/OrganizationEmails.model.ts +54 -0
  128. package/models/ProductBroker.model.d.ts +9 -9
  129. package/models/QuickbooksAccount.model.d.ts +39 -0
  130. package/models/QuickbooksAccount.model.js +43 -0
  131. package/models/QuickbooksAccount.model.ts +57 -0
  132. package/models/Receivable.model.d.ts +12 -12
  133. package/models/ReceivableAvailability.model.d.ts +54 -54
  134. package/models/ReceivableAvailabilityItem.model.d.ts +57 -57
  135. package/models/ReceivableItem.model.d.ts +6 -6
  136. package/models/Reserve.model.d.ts +51 -0
  137. package/models/Reserve.model.js +96 -0
  138. package/models/Reserve.model.ts +125 -0
  139. package/models/TermLoan.model.d.ts +3 -3
  140. package/models/TermLoanCalculated.model.d.ts +6 -6
  141. package/models/TransactionAttachedFile.Model.d.ts +35 -0
  142. package/models/TransactionAttachedFile.Model.js +37 -0
  143. package/models/TransactionAttachedFile.Model.ts +48 -0
  144. package/models/UploadedBankTransaction.model.d.ts +56 -0
  145. package/models/UploadedBankTransaction.model.js +78 -0
  146. package/models/UploadedBankTransaction.model.ts +110 -0
  147. package/models/UploadedData.model.d.ts +36 -0
  148. package/models/UploadedData.model.js +23 -0
  149. package/models/UploadedData.model.ts +35 -0
  150. package/models/UploadedFile.model.d.ts +40 -0
  151. package/models/UploadedFile.model.js +41 -0
  152. package/models/UploadedFile.model.ts +57 -0
  153. package/models/UploadedSheet.model.d.ts +46 -0
  154. package/models/UploadedSheet.model.js +27 -0
  155. package/models/UploadedSheet.model.ts +51 -0
  156. package/package.json +10 -1
  157. package/repositories/globals.repository.d.ts +8 -0
  158. package/repositories/globals.repository.js +24 -0
  159. package/repositories/globals.repository.ts +21 -0
  160. package/services/attached-files.service.d.ts +57 -0
  161. package/services/attached-files.service.js +103 -0
  162. package/services/attached-files.service.ts +123 -0
  163. package/services/availability.service.d.ts +77 -0
  164. package/services/availability.service.js +897 -0
  165. package/services/availability.service.ts +1034 -0
  166. package/services/bank-uploaded-transactions.service.d.ts +33 -0
  167. package/services/bank-uploaded-transactions.service.js +430 -0
  168. package/services/bank-uploaded-transactions.service.ts +475 -0
  169. package/services/banks.service.d.ts +36 -0
  170. package/services/banks.service.js +91 -0
  171. package/services/banks.service.ts +95 -0
  172. package/services/borrower-summary.service.d.ts +35 -0
  173. package/services/borrower-summary.service.js +310 -0
  174. package/services/borrower-summary.service.ts +334 -0
  175. package/services/borrowers.service.d.ts +103 -0
  176. package/services/borrowers.service.js +268 -0
  177. package/services/borrowers.service.ts +302 -0
  178. package/services/brokers.service.d.ts +212 -0
  179. package/services/brokers.service.js +160 -0
  180. package/services/brokers.service.ts +200 -0
  181. package/services/calendar.service.d.ts +53 -0
  182. package/services/calendar.service.js +108 -0
  183. package/services/calendar.service.ts +128 -0
  184. package/services/cash-allocation.service.d.ts +40 -0
  185. package/services/cash-allocation.service.js +92 -0
  186. package/services/cash-allocation.service.ts +105 -0
  187. package/services/collateral-adjustments.service.d.ts +38 -0
  188. package/services/collateral-adjustments.service.js +82 -0
  189. package/services/collateral-adjustments.service.ts +95 -0
  190. package/services/collaterals.service.d.ts +69 -0
  191. package/services/collaterals.service.js +279 -0
  192. package/services/collaterals.service.ts +319 -0
  193. package/services/companies.service.d.ts +5 -0
  194. package/services/companies.service.js +21 -0
  195. package/services/companies.service.ts +23 -0
  196. package/services/compliance-borrowers.service.d.ts +152 -0
  197. package/services/compliance-borrowers.service.js +569 -0
  198. package/services/compliance-borrowers.service.ts +617 -0
  199. package/services/equipment.service.d.ts +42 -0
  200. package/services/equipment.service.js +120 -0
  201. package/services/equipment.service.ts +149 -0
  202. package/services/file-manager.service.d.ts +44 -0
  203. package/services/file-manager.service.js +120 -0
  204. package/services/file-manager.service.ts +146 -0
  205. package/services/financial-compliance.service.d.ts +58 -0
  206. package/services/financial-compliance.service.js +281 -0
  207. package/services/financial-compliance.service.ts +309 -0
  208. package/services/financial-indexes.service.d.ts +20 -0
  209. package/services/financial-indexes.service.js +241 -0
  210. package/services/financial-indexes.service.ts +257 -0
  211. package/services/financial-spreading.service.d.ts +74 -0
  212. package/services/financial-spreading.service.js +450 -0
  213. package/services/financial-spreading.service.ts +517 -0
  214. package/services/globals.service.d.ts +5 -0
  215. package/services/globals.service.js +11 -0
  216. package/services/globals.service.ts +8 -0
  217. package/services/groups.service.d.ts +39 -0
  218. package/services/groups.service.js +65 -0
  219. package/services/groups.service.ts +64 -0
  220. package/services/inventory-availability.service.d.ts +13 -0
  221. package/services/inventory-availability.service.js +170 -0
  222. package/services/inventory-availability.service.ts +187 -0
  223. package/services/inventory.service.d.ts +118 -0
  224. package/services/inventory.service.js +239 -0
  225. package/services/inventory.service.ts +276 -0
  226. package/services/loan-charges.service.d.ts +83 -0
  227. package/services/loan-charges.service.js +343 -0
  228. package/services/loan-charges.service.ts +396 -0
  229. package/services/loan-payments.service.d.ts +94 -0
  230. package/services/loan-payments.service.js +485 -0
  231. package/services/loan-payments.service.ts +541 -0
  232. package/services/loan-products.service.d.ts +12 -0
  233. package/services/loan-products.service.js +55 -0
  234. package/services/loan-products.service.ts +58 -0
  235. package/services/loan-statement-balance.service.d.ts +16 -0
  236. package/services/loan-statement-balance.service.js +106 -0
  237. package/services/loan-statement-balance.service.ts +113 -0
  238. package/services/loan-statement-effects.service.d.ts +8 -0
  239. package/services/loan-statement-effects.service.js +42 -0
  240. package/services/loan-statement-effects.service.ts +41 -0
  241. package/services/loan-statement-status.service.d.ts +208 -0
  242. package/services/loan-statement-status.service.js +159 -0
  243. package/services/loan-statement-status.service.ts +177 -0
  244. package/services/loan-statement.service.d.ts +186 -0
  245. package/services/loan-statement.service.js +935 -0
  246. package/services/loan-statement.service.ts +1040 -0
  247. package/services/loan-transactions.service.d.ts +169 -0
  248. package/services/loan-transactions.service.js +941 -0
  249. package/services/loan-transactions.service.ts +1042 -0
  250. package/services/lock.service.d.ts +6 -0
  251. package/services/lock.service.js +45 -0
  252. package/services/lock.service.ts +45 -0
  253. package/services/manual-entry.service.d.ts +20 -0
  254. package/services/manual-entry.service.js +186 -0
  255. package/services/manual-entry.service.ts +201 -0
  256. package/services/month-end-data.service.d.ts +34 -0
  257. package/services/month-end-data.service.js +30 -0
  258. package/services/month-end-data.service.ts +35 -0
  259. package/services/nodemailer.service.d.ts +96 -0
  260. package/services/nodemailer.service.js +689 -0
  261. package/services/nodemailer.service.ts +774 -0
  262. package/services/organization-emails.service.d.ts +31 -0
  263. package/services/organization-emails.service.js +10 -0
  264. package/services/organization-emails.service.ts +7 -0
  265. package/services/organizations.service.d.ts +34 -0
  266. package/services/organizations.service.js +74 -0
  267. package/services/organizations.service.ts +84 -0
  268. package/services/pdf.service.d.ts +61 -0
  269. package/services/pdf.service.js +547 -0
  270. package/services/pdf.service.ts +642 -0
  271. package/services/quickbooks.service.d.ts +99 -0
  272. package/services/quickbooks.service.js +640 -0
  273. package/services/quickbooks.service.ts +734 -0
  274. package/services/reports/investor-summary.service.d.ts +28 -0
  275. package/services/reports/investor-summary.service.js +136 -0
  276. package/services/reports/investor-summary.service.ts +159 -0
  277. package/services/reports.service.d.ts +126 -0
  278. package/services/reports.service.js +584 -0
  279. package/services/reports.service.ts +702 -0
  280. package/services/reserve.service.d.ts +37 -0
  281. package/services/reserve.service.js +76 -0
  282. package/services/reserve.service.ts +79 -0
  283. package/services/sentry.service.d.ts +11 -0
  284. package/services/sentry.service.js +49 -0
  285. package/services/sentry.service.ts +33 -0
  286. package/services/signs.service.d.ts +69 -0
  287. package/services/signs.service.js +230 -0
  288. package/services/signs.service.ts +260 -0
  289. package/services/term-loan.service.d.ts +30 -0
  290. package/services/term-loan.service.js +614 -0
  291. package/services/term-loan.service.ts +696 -0
  292. package/services/uploads.service.d.ts +134 -0
  293. package/services/uploads.service.js +587 -0
  294. package/services/uploads.service.ts +643 -0
  295. package/services/user-logs.service.d.ts +23 -0
  296. package/services/user-logs.service.js +160 -0
  297. package/services/user-logs.service.ts +177 -0
  298. package/services/users.service.d.ts +4 -4
  299. package/services/yield.service.d.ts +46 -0
  300. package/services/yield.service.js +42 -12
  301. package/services/yield.service.ts +38 -8
  302. package/tsconfig.json +5 -5
  303. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,941 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LoanTransactionsService = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const mongoose_1 = __importDefault(require("mongoose"));
9
+ const dayjs_1 = __importDefault(require("dayjs"));
10
+ const decimal_js_1 = __importDefault(require("decimal.js"));
11
+ const date_helper_1 = require("../helpers/date.helper");
12
+ const numbers_helper_1 = require("../helpers/numbers.helper");
13
+ const common_helper_1 = require("../helpers/common.helper");
14
+ const BBCDate_model_1 = require("../models/BBCDate.model");
15
+ const loan_types_enum_1 = require("../enums/loan-types.enum");
16
+ const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
17
+ const collaterals_db_1 = require("../db/collaterals.db");
18
+ const PostponedTransactions_model_1 = require("../models/PostponedTransactions.model");
19
+ const LoanProducts_model_1 = require("../models/LoanProducts.model");
20
+ const Borrower_model_1 = require("../models/Borrower.model");
21
+ const BorrowerCompliance_model_1 = require("../models/BorrowerCompliance.model");
22
+ const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
23
+ const LoanPayment_model_1 = require("../models/LoanPayment.model");
24
+ const loan_products_db_1 = require("../db/loan-products.db");
25
+ const loan_transactions_db_1 = require("../db/loan-transactions.db");
26
+ const LoanTransactionFile_model_1 = require("../models/LoanTransactionFile.model");
27
+ class LoanTransactionsService {
28
+ attachedFilesService;
29
+ banksService;
30
+ borrowerService;
31
+ calendarService;
32
+ loanChargesService;
33
+ getLoanPaymentsService;
34
+ getLoanStatementService;
35
+ loanStatementStatusService;
36
+ getNodemailerService;
37
+ lockService;
38
+ termLoanService;
39
+ uploadsService;
40
+ config;
41
+ constructor(config, attachedFilesService, banksService, borrowerService, calendarService, loanChargesService, getLoanPaymentsService, getLoanStatementService, loanStatementStatusService, getNodemailerService, lockService, termLoanService, uploadsService) {
42
+ this.attachedFilesService = attachedFilesService;
43
+ this.banksService = banksService;
44
+ this.borrowerService = borrowerService;
45
+ this.calendarService = calendarService;
46
+ this.loanChargesService = loanChargesService;
47
+ this.getLoanPaymentsService = getLoanPaymentsService;
48
+ this.getLoanStatementService = getLoanStatementService;
49
+ this.loanStatementStatusService = loanStatementStatusService;
50
+ this.getNodemailerService = getNodemailerService;
51
+ this.lockService = lockService;
52
+ this.termLoanService = termLoanService;
53
+ this.uploadsService = uploadsService;
54
+ }
55
+ async getTransactionsById(transactionId) {
56
+ return LoanTransaction_model_1.LoanTransaction.findById(transactionId).lean();
57
+ }
58
+ async getLoanTransactions(transactionsFilter, paginatorOptions, withBanks, isReverse = false, isMinified = false) {
59
+ const borrowerProducts = await this.loanChargesService.getLoanProducts(transactionsFilter.borrowerId);
60
+ const productIds = borrowerProducts
61
+ .filter((product) => transactionsFilter.productId ? product._id.toString() === transactionsFilter.productId : true)
62
+ .map((product) => product._id);
63
+ const optionalFilters = [];
64
+ if (transactionsFilter.amount) {
65
+ optionalFilters.push({ 'amount': transactionsFilter.amount });
66
+ }
67
+ if (transactionsFilter.reference) {
68
+ optionalFilters.push({ 'reference': { $regex: transactionsFilter.reference, $options: 'i' } });
69
+ }
70
+ const bankLookup = !withBanks
71
+ ? []
72
+ : [
73
+ {
74
+ $lookup: {
75
+ from: 'banks',
76
+ localField: 'bankId',
77
+ foreignField: '_id',
78
+ as: 'bank',
79
+ },
80
+ }, {
81
+ $unwind: {
82
+ path: '$bank',
83
+ preserveNullAndEmptyArrays: true,
84
+ },
85
+ },
86
+ ];
87
+ const projectedFields = isMinified
88
+ ? [{ $project: { _id: 1, date: 1, amount: 1, reference: 1, balance: 1, transactionType: 1 } }]
89
+ : [];
90
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
91
+ {
92
+ $match: {
93
+ $and: [
94
+ { 'date': { $gte: transactionsFilter.periodStart } },
95
+ { 'date': { $lte: transactionsFilter.periodEnd } },
96
+ { 'productId': { $in: productIds } },
97
+ ...optionalFilters,
98
+ ],
99
+ },
100
+ }, {
101
+ $sort: {
102
+ 'date': isReverse ? -1 : 1,
103
+ 'order': isReverse ? -1 : 1,
104
+ 'createdAt': isReverse ? -1 : 1,
105
+ },
106
+ },
107
+ ...(0, collaterals_db_1.ITEMS_PAGINATION)(paginatorOptions),
108
+ ...bankLookup,
109
+ {
110
+ $unset: loan_products_db_1.fieldsToUnset,
111
+ },
112
+ ...projectedFields,
113
+ ]);
114
+ }
115
+ async getLoanTransactionsForFilter(periodStart, periodEnd, productId) {
116
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
117
+ {
118
+ $match: {
119
+ $and: [
120
+ { 'date': { $gte: periodStart } },
121
+ { 'date': { $lte: periodEnd } },
122
+ { 'productId': new mongoose_1.default.Types.ObjectId(productId) },
123
+ ],
124
+ },
125
+ }, {
126
+ $sort: {
127
+ 'date': -1,
128
+ 'order': -1,
129
+ 'createdAt': -1,
130
+ },
131
+ }, {
132
+ $unset: loan_products_db_1.fieldsToUnset,
133
+ },
134
+ ]);
135
+ }
136
+ async getLoanTransactionsForProduct(productId) {
137
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
138
+ {
139
+ $match: {
140
+ $and: [
141
+ { 'productId': new mongoose_1.default.Types.ObjectId(productId) },
142
+ ],
143
+ },
144
+ }, {
145
+ $sort: {
146
+ 'date': -1,
147
+ 'order': -1,
148
+ 'createdAt': -1,
149
+ },
150
+ }, {
151
+ $unset: loan_products_db_1.fieldsToUnset,
152
+ },
153
+ ]);
154
+ }
155
+ async getLoanTransactionFiles() {
156
+ const transactionFiles = await LoanTransactionFile_model_1.LoanTransactionFileModel.aggregate([
157
+ {
158
+ $sort: {
159
+ 'effectiveDate': -1,
160
+ 'createdAt': 1,
161
+ },
162
+ }, {
163
+ $unset: [...loan_products_db_1.fieldsToUnset.filter((field) => field !== 'createdAt')],
164
+ },
165
+ ]);
166
+ return await Promise.all(transactionFiles.map(async (file) => {
167
+ const effectiveDate = (0, dayjs_1.default)(file.effectiveDate).format('YYYY-MM-DD');
168
+ const attachedFiles = await this.attachedFilesService.getAttachedFilesByEffectiveDate(effectiveDate);
169
+ return { ...file, attachedFileIds: attachedFiles.map((attachedFile) => attachedFile._id.toString()) };
170
+ }));
171
+ }
172
+ async getDisbursementTransactions(productId, date) {
173
+ return LoanTransaction_model_1.LoanTransaction.countDocuments({
174
+ date: date,
175
+ productId: new mongoose_1.default.Types.ObjectId(productId),
176
+ transactionType: LoanTransaction_model_1.ELoanTransactionTypes.DISBURSEMENT,
177
+ bankId: { $ne: null },
178
+ });
179
+ }
180
+ async getTotalLoanTransactions(transactionsFilter) {
181
+ const borrowerProducts = await this.loanChargesService.getLoanProducts(transactionsFilter.borrowerId);
182
+ const productIds = borrowerProducts
183
+ .filter((product) => transactionsFilter.productId ? product._id.toString() === transactionsFilter.productId : true)
184
+ .map((product) => product._id);
185
+ return LoanTransaction_model_1.LoanTransaction.countDocuments({
186
+ $and: [
187
+ { 'date': { $gte: transactionsFilter.periodStart } },
188
+ { 'date': { $lte: transactionsFilter.periodEnd } },
189
+ { 'productId': { $in: productIds } },
190
+ ],
191
+ });
192
+ }
193
+ async uploadLoanTransactions(uploadedItems, uploadOption, userId, shift = 0) {
194
+ const orderedItems = uploadedItems.map((item, index) => ({
195
+ ...item,
196
+ order: index + shift,
197
+ }));
198
+ const transactionsToRecalculate = [];
199
+ const statementToSave = [];
200
+ await Promise.all(orderedItems.map(async (item) => {
201
+ const foundBank = await this.banksService.findBankByNumber(item.bankAccountNumber);
202
+ const productId = await this.loanChargesService.getLoanProductIdByCode(item.ledgerAccountCode);
203
+ if (productId) {
204
+ const itemToSave = {
205
+ ...(lodash_1.default.pick(item, LoanTransaction_model_1.LOAN_TRANSACTION_FIELDS)),
206
+ date: (0, date_helper_1.calculateTimeZone)(item.date),
207
+ bankId: new mongoose_1.default.Types.ObjectId(String(foundBank._id)),
208
+ productId: new mongoose_1.default.Types.ObjectId(productId),
209
+ balance: 0,
210
+ floatedBalance: 0,
211
+ effectiveDate: await this.calculateEffectiveDate(item),
212
+ userId: new mongoose_1.default.Types.ObjectId(userId),
213
+ };
214
+ const newLoanTransaction = new LoanTransaction_model_1.LoanTransaction({
215
+ order: item.order,
216
+ ...itemToSave,
217
+ });
218
+ await newLoanTransaction.save();
219
+ transactionsToRecalculate.push({
220
+ productId: newLoanTransaction.productId.toString(),
221
+ date: newLoanTransaction.date,
222
+ transactionId: newLoanTransaction._id.toString(),
223
+ order: newLoanTransaction.order,
224
+ });
225
+ }
226
+ const chargeId = await this.loanChargesService.getLoanChargeIdByCode(item.ledgerAccountCode);
227
+ if (chargeId) {
228
+ const itemToSave = {
229
+ ...(lodash_1.default.pick(item, LoanStatementTransaction_model_1.LOAN_STATEMENT_TRANSACTION_FIELDS)),
230
+ date: (0, date_helper_1.calculateTimeZone)(item.date),
231
+ chargeId: new mongoose_1.default.Types.ObjectId(chargeId),
232
+ memo: item.reference,
233
+ isSystem: false,
234
+ amountPaid: 0,
235
+ };
236
+ const loanStatementService = this.getLoanStatementService();
237
+ const lastOrder = await loanStatementService.getLastTransactionOrder(itemToSave);
238
+ itemToSave.order = lastOrder + 1;
239
+ const newLoanStatement = new LoanStatementTransaction_model_1.LoanStatementTransactionModel(itemToSave);
240
+ await newLoanStatement.save();
241
+ statementToSave.push(newLoanStatement._id.toString());
242
+ }
243
+ }));
244
+ const newLoanTransactionFile = new LoanTransactionFile_model_1.LoanTransactionFileModel({
245
+ userUploaded: '',
246
+ transactionsId: transactionsToRecalculate.map((transaction) => new mongoose_1.default.Types.ObjectId(transaction.transactionId)),
247
+ statementIds: statementToSave.map((statement) => new mongoose_1.default.Types.ObjectId(statement)),
248
+ fileName: uploadOption.fileName,
249
+ sheetName: uploadOption.sheetName,
250
+ effectiveDate: uploadOption.effectiveDate,
251
+ });
252
+ await newLoanTransactionFile.save();
253
+ const earliestTransactionIds = new Map();
254
+ transactionsToRecalculate.forEach((transaction) => {
255
+ const { productId, transactionId, date } = transaction;
256
+ if (earliestTransactionIds.has(productId)) {
257
+ if (date < earliestTransactionIds.get(productId).date) {
258
+ earliestTransactionIds.set(productId, { transactionId, date });
259
+ }
260
+ }
261
+ else {
262
+ earliestTransactionIds.set(productId, { transactionId, date });
263
+ }
264
+ });
265
+ const earliestTransactionIdsList = Array
266
+ .from(earliestTransactionIds.values())
267
+ .map((entry) => entry.transactionId);
268
+ await Promise.all(earliestTransactionIdsList.map(async (transactionId) => {
269
+ setTimeout(async () => await this.recalculateBalance(transactionId));
270
+ }));
271
+ }
272
+ async normalizeLoanTransaction(transaction, userId) {
273
+ transaction.effectiveDate = await this.calculateEffectiveDate(transaction);
274
+ transaction.date = new Date(transaction.date);
275
+ switch (LoanTransaction_model_1.ELoanTransactionTypes[transaction.transactionType]) {
276
+ case LoanTransaction_model_1.ELoanTransactionTypes.DISBURSEMENT:
277
+ transaction.amount = new decimal_js_1.default(transaction.amount).abs().toNumber();
278
+ break;
279
+ case LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION:
280
+ transaction.amount = new decimal_js_1.default(transaction.amount).abs().neg().toNumber();
281
+ break;
282
+ }
283
+ transaction.userId = new mongoose_1.default.Types.ObjectId(userId);
284
+ return transaction;
285
+ }
286
+ async createLoanTransaction(transaction) {
287
+ const pureTransactions = (0, common_helper_1.createFilteredObject)(transaction, LoanTransaction_model_1.LOAN_TRANSACTION_FIELDS);
288
+ const product = await this.loanChargesService.getLoanProductById(pureTransactions.productId.toString());
289
+ if (product) {
290
+ const bank = await this.banksService.findBankByAccountCode(product.code);
291
+ if (bank) {
292
+ pureTransactions.bankId = bank._id;
293
+ }
294
+ }
295
+ pureTransactions.customerId = '';
296
+ return await this.saveLoanTransaction(pureTransactions);
297
+ }
298
+ async updateLoanTransaction(transaction) {
299
+ const pureTransactions = (0, common_helper_1.createFilteredObject)(transaction, LoanTransaction_model_1.LOAN_TRANSACTION_FIELDS);
300
+ await this.saveLoanTransaction(pureTransactions);
301
+ }
302
+ async saveLoanTransaction(transaction) {
303
+ let startId;
304
+ let updatedTransaction;
305
+ if (!transaction._id) {
306
+ delete transaction._id;
307
+ const newTransaction = new LoanTransaction_model_1.LoanTransaction(transaction);
308
+ startId = newTransaction._id.toString();
309
+ updatedTransaction = await newTransaction.save();
310
+ }
311
+ else {
312
+ startId = transaction._id.toString();
313
+ updatedTransaction = await LoanTransaction_model_1.LoanTransaction.findByIdAndUpdate(transaction._id, transaction, { new: true });
314
+ }
315
+ if (startId) {
316
+ await this.recalculateBalance(startId);
317
+ }
318
+ const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(transaction.date));
319
+ await this.loanStatementStatusService.updateStatementStatus(transaction.productId.toString(), statementDate, false);
320
+ await this.termLoanService.setTermLoanExpected(transaction.productId.toString());
321
+ return updatedTransaction;
322
+ }
323
+ async recalculateProduct(productId) {
324
+ const firstTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
325
+ {
326
+ '$match': {
327
+ 'productId': new mongoose_1.default.Types.ObjectId(productId),
328
+ },
329
+ }, {
330
+ '$sort': {
331
+ 'date': 1,
332
+ },
333
+ }, {
334
+ '$limit': 1,
335
+ },
336
+ ]);
337
+ if (firstTransactions.length > 0) {
338
+ await this.recalculateBalance(firstTransactions[0]._id.toString());
339
+ }
340
+ }
341
+ async recalculateBalance(transactionId) {
342
+ const changedTransaction = await LoanTransaction_model_1.LoanTransaction.findById(transactionId);
343
+ if (!changedTransaction) {
344
+ console.error(`no transactions with id ${transactionId}`);
345
+ return;
346
+ }
347
+ setTimeout(async () => {
348
+ await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: false });
349
+ const previousTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
350
+ {
351
+ $match: {
352
+ 'productId': new mongoose_1.default.Types.ObjectId(changedTransaction.productId.toString()),
353
+ 'date': { $lt: new Date(changedTransaction.date) },
354
+ },
355
+ }, {
356
+ $sort: {
357
+ 'date': -1,
358
+ 'order': -1,
359
+ 'createdAt': -1,
360
+ },
361
+ }, {
362
+ $limit: 1,
363
+ },
364
+ ]);
365
+ let currentBalance = previousTransaction.length
366
+ ? previousTransaction[0].balance ?? 0
367
+ : 0;
368
+ const recalculatedTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
369
+ {
370
+ $match: {
371
+ 'productId': new mongoose_1.default.Types.ObjectId(changedTransaction.productId.toString()),
372
+ 'date': { $gte: new Date(changedTransaction.date) },
373
+ },
374
+ }, {
375
+ $sort: {
376
+ 'date': 1,
377
+ 'order': 1,
378
+ 'createdAt': 1,
379
+ },
380
+ },
381
+ ]);
382
+ for (const transaction of recalculatedTransactions) {
383
+ currentBalance = new decimal_js_1.default(currentBalance).add(transaction.amount).toNumber();
384
+ await LoanTransaction_model_1.LoanTransaction.findByIdAndUpdate(transaction._id, { balance: currentBalance });
385
+ }
386
+ await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: true });
387
+ });
388
+ setTimeout(async () => {
389
+ await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: false });
390
+ const previousEqualBalancesTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
391
+ {
392
+ $match: {
393
+ 'productId': new mongoose_1.default.Types.ObjectId(changedTransaction.productId.toString()),
394
+ 'date': { $lt: new Date(changedTransaction.date) },
395
+ $expr: { $eq: ['$balance', '$floatedBalance'] },
396
+ },
397
+ }, {
398
+ $sort: {
399
+ 'date': 1,
400
+ 'order': 1,
401
+ 'createdAt': 1,
402
+ },
403
+ }, {
404
+ $limit: 1,
405
+ },
406
+ ]);
407
+ let currentFloatedBalance = 0;
408
+ if (previousEqualBalancesTransaction.length) {
409
+ const previousTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
410
+ {
411
+ $match: {
412
+ 'productId': new mongoose_1.default.Types.ObjectId(previousEqualBalancesTransaction[0].productId.toString()),
413
+ 'date': { $lt: new Date(previousEqualBalancesTransaction[0].date) },
414
+ },
415
+ }, {
416
+ $sort: {
417
+ 'date': -1,
418
+ 'order': -1,
419
+ 'createdAt': -1,
420
+ },
421
+ }, {
422
+ $limit: 1,
423
+ },
424
+ ]);
425
+ if (previousTransaction.length) {
426
+ currentFloatedBalance = previousTransaction[0].floatedBalance;
427
+ }
428
+ }
429
+ const initTransaction = previousEqualBalancesTransaction.length ? previousEqualBalancesTransaction[0] : changedTransaction;
430
+ const recalculatedTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
431
+ {
432
+ $match: {
433
+ 'productId': new mongoose_1.default.Types.ObjectId(initTransaction.productId.toString()),
434
+ 'date': { $gte: new Date(initTransaction.date) },
435
+ },
436
+ }, {
437
+ $sort: {
438
+ 'date': 1,
439
+ 'order': 1,
440
+ 'createdAt': 1,
441
+ },
442
+ },
443
+ ]);
444
+ for (const transaction of recalculatedTransactions) {
445
+ const totalPostponed = await (0, loan_products_db_1.getPostponedTransactions)(transaction.date, transaction.productId.toString(), true);
446
+ switch (transaction.transactionType) {
447
+ case LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION:
448
+ await this.cleanRecalculated(transaction._id.toString());
449
+ await this.createPostponedTransaction(transaction);
450
+ currentFloatedBalance = (0, numbers_helper_1.roundToXDigits)(currentFloatedBalance + totalPostponed);
451
+ break;
452
+ case LoanTransaction_model_1.ELoanTransactionTypes.DISBURSEMENT:
453
+ currentFloatedBalance = (0, numbers_helper_1.roundToXDigits)(currentFloatedBalance + transaction.amount + totalPostponed);
454
+ break;
455
+ case LoanTransaction_model_1.ELoanTransactionTypes.ADJUSTMENT:
456
+ currentFloatedBalance = (0, numbers_helper_1.roundToXDigits)(currentFloatedBalance + transaction.amount + totalPostponed);
457
+ break;
458
+ }
459
+ await LoanTransaction_model_1.LoanTransaction.findByIdAndUpdate(transaction._id, { floatedBalance: currentFloatedBalance });
460
+ }
461
+ await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: true });
462
+ }, 1000);
463
+ }
464
+ async deleteLoanTransaction(transactionId, updateLoanPayment, userId = null) {
465
+ const currentTransaction = await LoanTransaction_model_1.LoanTransaction.findById(transactionId).lean();
466
+ if (!currentTransaction) {
467
+ return;
468
+ }
469
+ const nextTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
470
+ {
471
+ $match: {
472
+ 'productId': new mongoose_1.default.Types.ObjectId(currentTransaction.productId.toString()),
473
+ 'date': { $gte: new Date(currentTransaction.date) },
474
+ '_id': { $ne: new mongoose_1.default.Types.ObjectId(transactionId) },
475
+ },
476
+ }, {
477
+ $sort: {
478
+ 'date': 1,
479
+ 'order': 1,
480
+ 'createdAt': 1,
481
+ },
482
+ }, {
483
+ $limit: 1,
484
+ },
485
+ ]);
486
+ await LoanTransaction_model_1.LoanTransaction.findByIdAndDeleteWithUserId(transactionId, userId);
487
+ await PostponedTransactions_model_1.PostponedTransaction.findOneAndDelete({ transactionId });
488
+ if (currentTransaction.loanPaymentId && updateLoanPayment) {
489
+ const payment = await LoanPayment_model_1.LoanPaymentModel.findById(currentTransaction.loanPaymentId).lean();
490
+ if (payment) {
491
+ const loanPaymentsService = this.getLoanPaymentsService();
492
+ await loanPaymentsService.updateLoanPayment(payment, userId);
493
+ }
494
+ }
495
+ if (nextTransaction.length) {
496
+ await this.recalculateBalance(nextTransaction[0]._id.toString());
497
+ }
498
+ const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(currentTransaction.date));
499
+ await this.loanStatementStatusService.updateStatementStatus(currentTransaction.productId.toString(), statementDate, false);
500
+ await this.termLoanService.setTermLoanExpected(currentTransaction.productId.toString());
501
+ }
502
+ async createPostponedTransaction(transaction) {
503
+ const currentDay = (0, dayjs_1.default)(new Date(transaction.date)).format('YYYY-MM-DD');
504
+ const workingDate = await this.calendarService.getNextWorkingDay(currentDay);
505
+ const postponedTransaction = {
506
+ postponedToDate: workingDate,
507
+ transactionId: new mongoose_1.default.Types.ObjectId(String(transaction._id)),
508
+ productId: new mongoose_1.default.Types.ObjectId(String(transaction.productId)),
509
+ };
510
+ await PostponedTransactions_model_1.PostponedTransaction.create(postponedTransaction);
511
+ }
512
+ async cleanRecalculated(transactionId) {
513
+ await PostponedTransactions_model_1.PostponedTransaction.deleteMany({ transactionId });
514
+ }
515
+ async deleteLoanTransactionFile(transactionFileId, userId) {
516
+ const foundFile = await LoanTransactionFile_model_1.LoanTransactionFileModel.findById(transactionFileId).lean();
517
+ if (!foundFile) {
518
+ return null;
519
+ }
520
+ await Promise.all(foundFile.transactionsId.map(async (transactionId) => {
521
+ await this.deleteLoanTransaction(transactionId.toString(), true, userId);
522
+ }));
523
+ await Promise.all(foundFile.statementIds.map(async (statementId) => {
524
+ await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndDelete(statementId);
525
+ }));
526
+ await LoanTransactionFile_model_1.LoanTransactionFileModel.findByIdAndDelete(transactionFileId);
527
+ }
528
+ async getFirstTransaction(productId, excludeAdjustments = false) {
529
+ const excludeFilter = excludeAdjustments
530
+ ? {
531
+ 'transactionType': {
532
+ $nin: ['ADJUSTMENT'],
533
+ },
534
+ }
535
+ : {};
536
+ const lastTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
537
+ {
538
+ $match: {
539
+ 'productId': new mongoose_1.default.Types.ObjectId(productId),
540
+ ...excludeFilter,
541
+ },
542
+ }, {
543
+ $sort: {
544
+ 'date': 1,
545
+ 'order': 1,
546
+ 'createdAt': 1,
547
+ },
548
+ }, {
549
+ $limit: 1,
550
+ },
551
+ ]);
552
+ if (!lastTransaction.length) {
553
+ return null;
554
+ }
555
+ return lastTransaction[0];
556
+ }
557
+ async getLastTransactionForDate(productId, date = new Date(), excludeAdjustments = false) {
558
+ return (0, loan_transactions_db_1.getLastTransactionForDate)(productId, date, excludeAdjustments);
559
+ }
560
+ async getLastDisbursementTransactionForDate(productId, startDate, endDate) {
561
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
562
+ {
563
+ $match: {
564
+ $and: [
565
+ { 'date': { $gte: startDate } },
566
+ { 'date': { $lte: endDate } },
567
+ { 'productId': new mongoose_1.default.Types.ObjectId(productId) },
568
+ { 'transactionType': { $ne: LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION } },
569
+ ],
570
+ },
571
+ }, {
572
+ $sort: {
573
+ 'date': -1,
574
+ 'order': -1,
575
+ 'createdAt': -1,
576
+ },
577
+ },
578
+ ]);
579
+ }
580
+ async getPaymentsForPeriod(productId, startDate, endDate) {
581
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
582
+ {
583
+ $match: {
584
+ $and: [
585
+ { 'date': { $gte: startDate } },
586
+ { 'date': { $lte: endDate } },
587
+ { 'productId': new mongoose_1.default.Types.ObjectId(productId) },
588
+ { 'transactionType': LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION },
589
+ ],
590
+ },
591
+ }, {
592
+ $sort: {
593
+ 'date': -1,
594
+ 'order': -1,
595
+ 'createdAt': -1,
596
+ },
597
+ },
598
+ ]);
599
+ }
600
+ async getLoanBalanceForBBCDateId(BBCDateId) {
601
+ const bbcDate = await BBCDate_model_1.BBCDateModel.findById(BBCDateId);
602
+ const loanProducts = await LoanProducts_model_1.LoanProduct.aggregate([
603
+ {
604
+ '$match': {
605
+ 'borrowerId': new mongoose_1.default.Types.ObjectId(bbcDate.borrowerId),
606
+ },
607
+ },
608
+ ]);
609
+ const balances = {
610
+ [loan_types_enum_1.ELoanTypes.TERM]: 0,
611
+ [loan_types_enum_1.ELoanTypes.REVOLVER]: 0,
612
+ };
613
+ await Promise.all(loanProducts.map(async (product) => {
614
+ const lastTransaction = await this.getLastTransactionForDate(product._id.toString(), (0, dayjs_1.default)(bbcDate.bbcDate).utcOffset(0).endOf('day').toDate());
615
+ if (lastTransaction) {
616
+ balances[loan_types_enum_1.ELoanTypes[product.type]] = new decimal_js_1.default(balances[loan_types_enum_1.ELoanTypes[product.type]]).add(lastTransaction.balance).toNumber();
617
+ }
618
+ }));
619
+ return balances;
620
+ }
621
+ async getLoanBalanceForBBCDateIdPerProduct(BBCDateId, productType) {
622
+ const bbcDate = await BBCDate_model_1.BBCDateModel.findById(BBCDateId);
623
+ const productFilter = productType ? { 'type': productType } : {};
624
+ const loanProducts = await LoanProducts_model_1.LoanProduct.aggregate([
625
+ {
626
+ '$match': {
627
+ 'borrowerId': new mongoose_1.default.Types.ObjectId(bbcDate.borrowerId),
628
+ ...productFilter,
629
+ },
630
+ },
631
+ ]);
632
+ const balances = {};
633
+ await Promise.all(loanProducts.map(async (product) => {
634
+ const lastTransaction = await this.getLastTransactionForDate(product._id.toString(), (0, dayjs_1.default)(bbcDate.bbcDate).utcOffset(0).endOf('day').toDate());
635
+ balances[product._id.toString()] = { balance: lastTransaction.balance, product };
636
+ }, {}));
637
+ return balances;
638
+ }
639
+ async getLoanBalanceForBorrowerId(borrowerId) {
640
+ const products = await this.loanChargesService.getLoanProducts(borrowerId);
641
+ const extraInfo = {
642
+ [loan_types_enum_1.ELoanTypes.REVOLVER]: {
643
+ balance: 0,
644
+ lastActivityDate: null,
645
+ },
646
+ [loan_types_enum_1.ELoanTypes.TERM]: {
647
+ balance: 0,
648
+ lastActivityDate: null,
649
+ },
650
+ accruedStatement: {
651
+ balance: 0,
652
+ lastActivityDate: null,
653
+ },
654
+ effectiveLoanBalance: {
655
+ balance: 0,
656
+ lastActivityDate: null,
657
+ },
658
+ };
659
+ if (products.length === 0) {
660
+ return extraInfo;
661
+ }
662
+ for (const product of products) {
663
+ try {
664
+ const lastTransaction = await this.getLastTransactionForDate(product._id.toString());
665
+ if (lastTransaction) {
666
+ extraInfo[product.type].balance = new decimal_js_1.default(extraInfo[product.type].balance).add(lastTransaction.balance).toNumber();
667
+ }
668
+ const latRegularTransaction = await this.getLastTransactionForDate(product._id.toString(), new Date(), true);
669
+ if (latRegularTransaction) {
670
+ if (!extraInfo[product.type].lastActivityDate || latRegularTransaction.date > extraInfo[product.type].lastActivityDate) {
671
+ extraInfo[product.type].lastActivityDate = latRegularTransaction.date;
672
+ }
673
+ }
674
+ }
675
+ catch (error) {
676
+ console.error('Error:', error);
677
+ }
678
+ }
679
+ const loanStatementService = this.getLoanStatementService();
680
+ extraInfo.accruedStatement.balance = await loanStatementService.calculateAccruedStatementForBorrower(borrowerId);
681
+ extraInfo.effectiveLoanBalance.balance = new decimal_js_1.default(extraInfo[loan_types_enum_1.ELoanTypes.REVOLVER].balance).add(extraInfo.accruedStatement.balance).toNumber();
682
+ return extraInfo;
683
+ }
684
+ async getUnsentTransactionForDate(borrowerId, date) {
685
+ const products = await this.loanChargesService.getBorrowersLoanProducts(borrowerId);
686
+ return LoanTransaction_model_1.LoanTransaction.aggregate([
687
+ {
688
+ $match: {
689
+ 'productId': { $in: products.map((product) => product._id) },
690
+ 'isSent': { $in: [false, null] },
691
+ 'date': { $gte: date },
692
+ },
693
+ }, {
694
+ $lookup: {
695
+ from: 'loan_products',
696
+ localField: 'productId',
697
+ foreignField: '_id',
698
+ as: 'product',
699
+ },
700
+ }, {
701
+ $unwind: {
702
+ path: '$product',
703
+ },
704
+ },
705
+ ]);
706
+ }
707
+ async getTransactionReport(transactionIds, borrowerId, effectiveDate) {
708
+ const transactionIdsObjects = transactionIds.map((t) => new mongoose_1.default.Types.ObjectId(t));
709
+ const uploadedTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
710
+ {
711
+ $match: {
712
+ '_id': {
713
+ $in: transactionIdsObjects,
714
+ },
715
+ },
716
+ }, {
717
+ $lookup: {
718
+ from: 'loan_products',
719
+ localField: 'productId',
720
+ foreignField: '_id',
721
+ as: 'product',
722
+ },
723
+ }, {
724
+ $unwind: {
725
+ path: '$product',
726
+ },
727
+ }, {
728
+ $match: {
729
+ 'product.borrowerId': new mongoose_1.default.Types.ObjectId(borrowerId),
730
+ },
731
+ },
732
+ ]);
733
+ const products = await this.loanChargesService.getBorrowersLoanProducts(borrowerId);
734
+ const backDatedTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
735
+ {
736
+ $match: {
737
+ '_id': { $nin: transactionIdsObjects },
738
+ 'productId': { $in: products.map((product) => product._id) },
739
+ 'isSent': { $in: [false, null] },
740
+ 'date': { $lte: effectiveDate },
741
+ },
742
+ }, {
743
+ $lookup: {
744
+ from: 'loan_products',
745
+ localField: 'productId',
746
+ foreignField: '_id',
747
+ as: 'product',
748
+ },
749
+ }, {
750
+ $unwind: {
751
+ path: '$product',
752
+ },
753
+ },
754
+ ]);
755
+ const productGroups = products
756
+ .reduce((acc, product) => {
757
+ acc[product._id.toString()] = [];
758
+ return acc;
759
+ }, {});
760
+ const headerRow = [
761
+ 'date',
762
+ 'amount',
763
+ 'reference',
764
+ 'transaction type',
765
+ ];
766
+ const backDatedTransactionsTitle = [[''], ['Backdated transactions']];
767
+ const transactionIdsToMark = [...uploadedTransactions, ...backDatedTransactions].map((tr) => tr._id.toString());
768
+ const mappedTransactions = uploadedTransactions
769
+ .filter((t) => !!t)
770
+ .reduce((acc, t) => {
771
+ const reportRow = [
772
+ t.date,
773
+ t.amount,
774
+ t.reference,
775
+ t.transactionType,
776
+ ];
777
+ acc[t.productId.toString()] = [...acc[t.productId.toString()], reportRow];
778
+ return acc;
779
+ }, lodash_1.default.cloneDeep(productGroups));
780
+ const mappedBackDatedTransactions = backDatedTransactions
781
+ .filter((t) => !!t)
782
+ .reduce((acc, t) => {
783
+ const reportRow = [
784
+ t.date,
785
+ t.amount,
786
+ t.reference,
787
+ t.transactionType,
788
+ ];
789
+ acc[t.productId.toString()] = [...acc[t.productId.toString()], reportRow];
790
+ return acc;
791
+ }, lodash_1.default.cloneDeep(productGroups));
792
+ const transactions = products.map((product) => {
793
+ const uploaded = mappedTransactions[product._id.toString()];
794
+ const backdated = mappedBackDatedTransactions[product._id.toString()];
795
+ return {
796
+ [product.name]: [
797
+ headerRow,
798
+ ...uploaded,
799
+ ...backDatedTransactionsTitle,
800
+ headerRow,
801
+ ...backdated,
802
+ ],
803
+ };
804
+ });
805
+ return { transactionIdsToMark, transactions };
806
+ }
807
+ async getBorrowerIdsForFile(transactionFileId) {
808
+ const transactionFile = await LoanTransactionFile_model_1.LoanTransactionFileModel.findById(transactionFileId).lean();
809
+ if (!transactionFile) {
810
+ console.log('unknown file id', transactionFileId);
811
+ return;
812
+ }
813
+ const productIdsSet = new Set();
814
+ await Promise.all(transactionFile.transactionsId.map(async (transactionId) => {
815
+ const transaction = await LoanTransaction_model_1.LoanTransaction.findById(transactionId);
816
+ if (transaction) {
817
+ productIdsSet.add(transaction.productId.toString());
818
+ }
819
+ }));
820
+ const productIds = Array.from(productIdsSet.values());
821
+ const borrowerIdsSet = new Map();
822
+ await Promise.all(productIds.map(async (productId) => {
823
+ const product = await LoanProducts_model_1.LoanProduct.findById(productId);
824
+ if (product) {
825
+ const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: product.borrowerId.toString() });
826
+ const isActive = foundBorrower.isEmailingActive && foundBorrower.isDailyTransactionsEmailingActive;
827
+ borrowerIdsSet.set(product.borrowerId.toString(), isActive);
828
+ }
829
+ }));
830
+ return Object.fromEntries(borrowerIdsSet);
831
+ }
832
+ async getEmailBorrowers(transactionFileId) {
833
+ return await this.getBorrowerIdsForFile(transactionFileId);
834
+ }
835
+ async sendReport(transactionFileId, selectedBorrowerIds) {
836
+ const transactionFile = await LoanTransactionFile_model_1.LoanTransactionFileModel.findById(transactionFileId).lean();
837
+ if (!transactionFile) {
838
+ console.log('unknown file id', transactionFileId);
839
+ return;
840
+ }
841
+ const borrowerIds = await this.getBorrowerIdsForFile(transactionFileId);
842
+ const transactionIds = transactionFile.transactionsId.map((id) => id.toString());
843
+ await Promise.all(Object.entries(borrowerIds).map(async ([borrowerId, active]) => {
844
+ if (!selectedBorrowerIds.includes(borrowerId)) {
845
+ return;
846
+ }
847
+ if (!active) {
848
+ return;
849
+ }
850
+ const borrower = await Borrower_model_1.BorrowerModel.findById(borrowerId).lean();
851
+ if (!borrower) {
852
+ console.log('unknown borrower', borrowerId);
853
+ return;
854
+ }
855
+ const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: borrowerId });
856
+ if (!foundBorrower) {
857
+ return;
858
+ }
859
+ const addresses = [...foundBorrower.mainEmails, ...foundBorrower.financialEmails].reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
860
+ const title = `${borrower.code} ACTIVITY REPORT: ${(0, dayjs_1.default)(transactionFile.createdAt).format('MM-DD-YYYY')}`;
861
+ const fileName = title;
862
+ const subject = title;
863
+ let text = 'Attached is your daily activity report. Please log into the portal to view your account balance.';
864
+ const { transactions, transactionIdsToMark, } = await this.getTransactionReport(transactionIds, borrowerId, new Date(transactionFile.effectiveDate));
865
+ const attachedFile = await this.uploadsService.convertDataToFile(transactions);
866
+ const attachment = { filename: `${fileName}.xlsx`, content: attachedFile };
867
+ const attachedPDFs = await this.attachedFilesService.getAttachedRealFilesByTransactionFileId(borrowerId, transactionFile.effectiveDate);
868
+ if (attachedPDFs.length > 0) {
869
+ text = text + ' Uploaded PDF files are available for downloading.';
870
+ }
871
+ const email = { addresses: [...addresses, this.config.portfolioEmail], subject, text };
872
+ const nodemailerService = this.getNodemailerService();
873
+ await nodemailerService.sendLoanUploadReport(email, [attachment], borrowerId);
874
+ await Promise.all(transactionIdsToMark.map(async (id) => {
875
+ await this.switchTransactionSentStatus(id, true);
876
+ }));
877
+ }));
878
+ }
879
+ async sendAllReport(effectiveDate) {
880
+ const borrowers = await this.borrowerService.getActiveBorrowers();
881
+ await Promise.all(borrowers.map(async (borrower) => {
882
+ if (!borrower.active) {
883
+ return;
884
+ }
885
+ const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: borrower._id.toString() });
886
+ if (!foundBorrower) {
887
+ return;
888
+ }
889
+ const addresses = [...foundBorrower.mainEmails, ...foundBorrower.financialEmails].reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
890
+ const title = `${borrower.code} ACTIVITY REPORT: ${(0, dayjs_1.default)(effectiveDate).format('MM-DD-YYYY')}`;
891
+ const fileName = title;
892
+ const subject = title;
893
+ const dailyTransactions = await this.getUnsentTransactionForDate(borrower._id.toString(), effectiveDate);
894
+ const transactionIds = dailyTransactions.map((tr) => tr._id.toString());
895
+ const { transactions, transactionIdsToMark, } = await this.getTransactionReport(transactionIds, borrower._id.toString(), effectiveDate);
896
+ if (transactions.length === 0 || transactionIdsToMark.length === 0) {
897
+ return;
898
+ }
899
+ const attachedFile = await this.uploadsService.convertDataToFile(transactions);
900
+ const attachment = { filename: `${fileName}.xlsx`, content: attachedFile };
901
+ const attachedPDFs = await this.attachedFilesService.getAttachedRealFilesByTransactionFileId(borrower._id.toString(), (0, dayjs_1.default)(effectiveDate).format('YYYY-MM-DD'));
902
+ let text = 'Attached is your daily activity report. Please log into the portal to view your account balance.';
903
+ if (attachedPDFs.length > 0) {
904
+ text = text + ' Uploaded PDF files are available for downloading.';
905
+ }
906
+ const email = { addresses: [...addresses, this.config.portfolioEmail], subject, text };
907
+ const nodemailerService = this.getNodemailerService();
908
+ await nodemailerService.sendLoanUploadReport(email, [attachment], borrower._id.toString());
909
+ await Promise.all(transactionIdsToMark.map(async (id) => {
910
+ await this.switchTransactionSentStatus(id, true);
911
+ }));
912
+ }));
913
+ }
914
+ async calculateEffectiveDate(transaction) {
915
+ if (transaction.transactionType !== LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION) {
916
+ return new Date(transaction.date);
917
+ }
918
+ const nextDate = await this.calendarService.getNextWorkingDay((0, dayjs_1.default)(transaction.date).format('YYYY-MM-DD'));
919
+ return new Date(nextDate);
920
+ }
921
+ async switchTransactionSentStatus(transactionId, isSent) {
922
+ if (isSent) {
923
+ await LoanTransaction_model_1.LoanTransaction.findByIdAndUpdate(transactionId, { isSent });
924
+ }
925
+ else {
926
+ const transaction = await LoanTransaction_model_1.LoanTransaction.findById(transactionId).lean();
927
+ if (transaction) {
928
+ await LoanTransaction_model_1.LoanTransaction.findByIdAndUpdate(transactionId, { isSent: !transaction.isSent ?? false });
929
+ }
930
+ }
931
+ }
932
+ async checkLockStatus(transaction) {
933
+ const product = await this.loanChargesService.getLoanProductById(transaction.productId.toString());
934
+ if (product) {
935
+ return await this.lockService.isDateLocked(new Date(transaction.date), product.borrowerId.toString());
936
+ }
937
+ return false;
938
+ }
939
+ ;
940
+ }
941
+ exports.LoanTransactionsService = LoanTransactionsService;