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,614 @@
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.TermLoanService = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const dayjs_1 = __importDefault(require("dayjs"));
9
+ const decimal_js_1 = __importDefault(require("decimal.js"));
10
+ const mongoose_1 = __importDefault(require("mongoose"));
11
+ const loan_charge_type_enum_1 = require("../enums/loan-charge-type.enum");
12
+ const loan_types_enum_1 = require("../enums/loan-types.enum");
13
+ const numbers_helper_1 = require("../helpers/numbers.helper");
14
+ const TermLoanSettings_model_1 = require("../models/TermLoanSettings.model");
15
+ const TermLoan_model_1 = require("../models/TermLoan.model");
16
+ const TermLoanCalculated_model_1 = require("../models/TermLoanCalculated.model");
17
+ const LoanProducts_model_1 = require("../models/LoanProducts.model");
18
+ const financial_indexes_service_1 = require("./financial-indexes.service");
19
+ const newEmptySettings = {
20
+ termMonths: 12,
21
+ amortizationProfileMonths: 12,
22
+ gracePeriodMonths: 1,
23
+ principalDueDay: 7,
24
+ };
25
+ class TermLoanService {
26
+ financialIndexesService;
27
+ loanChargesService;
28
+ getLoanStatementStatusService;
29
+ getLoanTransactionsService;
30
+ getUploadsService;
31
+ constructor(financialIndexesService, loanChargesService, getLoanStatementStatusService, getLoanTransactionsService, getUploadsService) {
32
+ this.financialIndexesService = financialIndexesService;
33
+ this.loanChargesService = loanChargesService;
34
+ this.getLoanStatementStatusService = getLoanStatementStatusService;
35
+ this.getLoanTransactionsService = getLoanTransactionsService;
36
+ this.getUploadsService = getUploadsService;
37
+ }
38
+ async createTermLoan(productId, actual) {
39
+ const product = await this.loanChargesService.getLoanProductById(productId);
40
+ const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
41
+ if (charges.length === 0) {
42
+ return null;
43
+ }
44
+ const adminCharge = charges.find((charge) => charge.chargeType === loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE);
45
+ const interestCharge = charges.find((charge) => charge.chargeType === loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE);
46
+ if (!adminCharge || !interestCharge) {
47
+ return null;
48
+ }
49
+ const primeRate = await this.financialIndexesService.getFinancialIndexValue(financial_indexes_service_1.EFinancialIndex.PRIME_RATE);
50
+ const newEmptyTerm = {
51
+ productId: new mongoose_1.default.Types.ObjectId(String(product._id)),
52
+ initialBalance: product.commitment,
53
+ adminRate: adminCharge.percent,
54
+ interestRate: interestCharge.percent,
55
+ loanStartDate: product.startDate,
56
+ primeRate: primeRate,
57
+ actual,
58
+ isMismatched: false,
59
+ calculationStatus: TermLoan_model_1.ETermLoanStatus.NEW,
60
+ lastCalculated: null,
61
+ };
62
+ return TermLoan_model_1.TermLoanModel.findOneAndUpdate({ productId, actual }, newEmptyTerm, { upsert: true, new: true });
63
+ }
64
+ async createTermLoanSettings(termLoanId) {
65
+ return TermLoanSettings_model_1.TermLoanSettingsModel.findOneAndUpdate({ termLoanId }, { termLoanId, ...newEmptySettings }, {
66
+ upsert: true,
67
+ new: true,
68
+ });
69
+ }
70
+ async getCreateTermLoan(productId, actual) {
71
+ if (!productId) {
72
+ return null;
73
+ }
74
+ const foundTermLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId, actual }).lean();
75
+ const termLoan = foundTermLoan ? foundTermLoan : await this.createTermLoan(productId, actual);
76
+ if (!termLoan) {
77
+ return null;
78
+ }
79
+ const foundSettings = termLoan ? await TermLoanSettings_model_1.TermLoanSettingsModel.findOne({ termLoanId: termLoan._id }).lean() : null;
80
+ const settings = foundSettings ? foundSettings : await this.createTermLoanSettings(String(termLoan._id));
81
+ const pureTermLoan = lodash_1.default.pick(termLoan, TermLoan_model_1.TERM_LOAN_FIELDS);
82
+ const termLoanFull = {
83
+ ...pureTermLoan,
84
+ settings: lodash_1.default.pick(settings, TermLoanSettings_model_1.TERM_LOAN_SETTINGS_FIELDS),
85
+ };
86
+ return termLoanFull;
87
+ }
88
+ async getTermLoan(productId, actual) {
89
+ const termLoan = await this.getCreateTermLoan(productId, actual);
90
+ if (!termLoan) {
91
+ return null;
92
+ }
93
+ const calculatedTermLoan = await TermLoanCalculated_model_1.TermLoanCalculatedModel
94
+ .find({ termLoanId: termLoan._id })
95
+ .sort({ relevantStatement: 1, openingBalance: -1, monthlyPrincipal: -1 })
96
+ .lean();
97
+ const mappedCalculatedTermLoan = calculatedTermLoan.map((c) => {
98
+ const startDate = termLoan.loanStartDate;
99
+ const minStart = dayjs_1.default.max((0, dayjs_1.default)(startDate), (0, dayjs_1.default)(c.relevantStatement).startOf('month'));
100
+ const period = `${minStart.format('MM/DD/YYYY')} - ${(0, dayjs_1.default)(c.relevantStatement).format('MM/DD/YYYY')}`;
101
+ return {
102
+ ...c,
103
+ period,
104
+ };
105
+ });
106
+ return { ...termLoan, calculated: mappedCalculatedTermLoan };
107
+ }
108
+ async saveTermLoanSettings(productId, actual, settings) {
109
+ let termLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId, actual }).lean();
110
+ if (!termLoan || termLoan.calculationStatus === TermLoan_model_1.ETermLoanStatus.NEW) {
111
+ termLoan = await this.createTermLoan(productId, actual);
112
+ }
113
+ const updatedSettings = await TermLoanSettings_model_1.TermLoanSettingsModel.findOneAndUpdate({ termLoanId: termLoan._id }, settings, { new: true });
114
+ return { productId, settings: lodash_1.default.pick(updatedSettings, TermLoanSettings_model_1.TERM_LOAN_SETTINGS_FIELDS) };
115
+ }
116
+ async calculateAllTermLoans() {
117
+ const products = await LoanProducts_model_1.LoanProduct.find({ type: loan_types_enum_1.ELoanTypes.TERM }).lean();
118
+ for (const product of products) {
119
+ await this.calculateTermLoan(product._id.toString(), true);
120
+ }
121
+ }
122
+ async setTermLoanExpected(productId, recalculate = false) {
123
+ const termLoan = await this.getCreateTermLoan(productId, true);
124
+ if (termLoan) {
125
+ await this.updateTermLoanStatus(termLoan._id.toString(), TermLoan_model_1.ETermLoanStatus.EXPECTED);
126
+ if (recalculate) {
127
+ await this.calculateTermLoan(productId, true);
128
+ }
129
+ }
130
+ }
131
+ async updateTermLoanStatus(termLoanId, calculationStatus, lastCalculated = null) {
132
+ try {
133
+ await TermLoan_model_1.TermLoanModel.findByIdAndUpdate(termLoanId, { calculationStatus });
134
+ if (lastCalculated) {
135
+ await TermLoan_model_1.TermLoanModel.findByIdAndUpdate(termLoanId, { lastCalculated });
136
+ }
137
+ }
138
+ catch (e) {
139
+ console.error(e);
140
+ }
141
+ }
142
+ async calculateTermLoan(productId, actual) {
143
+ const loanTransactionsService = this.getLoanTransactionsService();
144
+ let termLoan = await this.getCreateTermLoan(productId, actual);
145
+ if (!termLoan || termLoan.calculationStatus === TermLoan_model_1.ETermLoanStatus.NEW) {
146
+ await this.createTermLoan(productId, actual);
147
+ termLoan = await this.getCreateTermLoan(productId, actual);
148
+ }
149
+ if (!termLoan) {
150
+ return;
151
+ }
152
+ await this.updateTermLoanStatus(termLoan._id.toString(), TermLoan_model_1.ETermLoanStatus.CALCULATING);
153
+ const daysInYear = 360;
154
+ const initialTransaction = await loanTransactionsService.getLastTransactionForDate(productId, termLoan.loanStartDate);
155
+ const initialData = actual
156
+ ? { date: termLoan.loanStartDate, amount: initialTransaction ? initialTransaction.balance : 0 }
157
+ : { date: termLoan.loanStartDate, amount: termLoan.initialBalance };
158
+ const initialBalance = actual ? initialData.amount : termLoan.initialBalance;
159
+ const startDate = (0, dayjs_1.default)(initialData.date);
160
+ await TermLoan_model_1.TermLoanModel.findByIdAndUpdate(termLoan._id, { loanStartDate: startDate, initialBalance: initialBalance });
161
+ const endGraceDate = (0, dayjs_1.default)(startDate).add(termLoan.settings.gracePeriodMonths, 'month');
162
+ const endTermMonths = (0, dayjs_1.default)(startDate).add(termLoan.settings.termMonths, 'month');
163
+ const endAmortizationDate = (0, dayjs_1.default)(startDate).add(termLoan.settings.amortizationProfileMonths, 'month');
164
+ const product = await this.loanChargesService.getLoanProductById(productId);
165
+ const minPercent = product.minPercent ?? 0;
166
+ const maxPercent = (product.maxPercent ?? 0) === 0 ? 1 : product.maxPercent;
167
+ const getDynamicInterestRate = async (date) => {
168
+ const primeRate = await this.financialIndexesService.getFinancialIndexValue(financial_indexes_service_1.EFinancialIndex.PRIME_RATE, date);
169
+ const interestRate = new decimal_js_1.default(primeRate).add(termLoan.interestRate).toDP(4).toNumber();
170
+ return Math.min(Math.max(interestRate, minPercent), maxPercent);
171
+ };
172
+ let currentDate = startDate;
173
+ const getMonthPayments = async (date) => {
174
+ const transactions = await loanTransactionsService.getPaymentsForPeriod(productId, date.toDate(), date.endOf('month').toDate());
175
+ if (!initialTransaction) {
176
+ return transactions;
177
+ }
178
+ return transactions.filter((transaction) => {
179
+ return ((0, dayjs_1.default)(transaction.date).format('YYYY-MM-DD') !== (0, dayjs_1.default)(initialTransaction.date).format('YYYY-MM-DD'));
180
+ });
181
+ };
182
+ const getMonthDisbursements = async (date) => {
183
+ const transactions = await loanTransactionsService.getLastDisbursementTransactionForDate(productId, date.toDate(), date.endOf('month').toDate());
184
+ if (!initialTransaction) {
185
+ return transactions;
186
+ }
187
+ return transactions.filter((transaction) => {
188
+ return ((0, dayjs_1.default)(transaction.date).format('YYYY-MM-DD') !== (0, dayjs_1.default)(initialTransaction.date).format('YYYY-MM-DD'));
189
+ });
190
+ };
191
+ const checkGracePeriodDate = (checkDate) => {
192
+ return checkDate < endGraceDate.endOf('month');
193
+ };
194
+ const interestRates = await this.financialIndexesService.getFinancialAllIndexes(financial_indexes_service_1.EFinancialIndex.PRIME_RATE);
195
+ const transactions = await loanTransactionsService.getLoanTransactionsForProduct(productId);
196
+ const findLastIndexValue = (date) => {
197
+ const found = interestRates.find((i) => i.date.getTime() <= date.getTime());
198
+ const primeRate = found ? found.value : 0;
199
+ const interestRate = new decimal_js_1.default(primeRate).add(termLoan.interestRate).toDP(4).toNumber();
200
+ return Math.min(Math.max(interestRate, minPercent), maxPercent);
201
+ };
202
+ const findLastTransaction = (date) => {
203
+ return transactions.find((i) => i.date.getTime() <= date.getTime());
204
+ };
205
+ const getComplexEMI = async (termLoan) => {
206
+ const calculateBalance = async (termLoan, EMI) => {
207
+ console.log({ EMI });
208
+ let balance = initialData.amount;
209
+ let accumulatedMonthlyInterest = 0;
210
+ let lastMonthInterest = 0;
211
+ let EMICurrentDate = currentDate;
212
+ let firstPeriod = true;
213
+ const endAmortization = endAmortizationDate;
214
+ let monthPayments = [];
215
+ while (EMICurrentDate.startOf('day').isBefore(endAmortization.startOf('day')) || EMICurrentDate.startOf('day').isSame(endAmortization.startOf('day'))) {
216
+ const lastTransaction = findLastTransaction(EMICurrentDate.subtract(1, 'day').toDate());
217
+ if (actual && lastTransaction && (0, dayjs_1.default)(lastTransaction.date).format('YYYY-MM-DD') === EMICurrentDate.subtract(1, 'day').format('YYYY-MM-DD')) {
218
+ balance = lastTransaction.balance;
219
+ }
220
+ const interestRate = findLastIndexValue(EMICurrentDate.toDate());
221
+ const interestRateDaily = interestRate / daysInYear;
222
+ const dailyInterest = EMICurrentDate.startOf('day').isAfter(endAmortizationDate.startOf('day')) ? 0 : new decimal_js_1.default(balance).mul(interestRateDaily).toDP(2).toNumber();
223
+ accumulatedMonthlyInterest = new decimal_js_1.default(accumulatedMonthlyInterest).add(dailyInterest).toDP(2).toNumber();
224
+ if (termLoan.actual) {
225
+ if ((EMICurrentDate.format('YYYY-MM-DD') === EMICurrentDate.startOf('month').format('YYYY-MM-DD')) ||
226
+ (EMICurrentDate.format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD'))) {
227
+ monthPayments = await getMonthPayments(EMICurrentDate);
228
+ }
229
+ }
230
+ if ((EMICurrentDate.format('YYYY-MM-DD') === EMICurrentDate.endOf('month').format('YYYY-MM-DD')) ||
231
+ (EMICurrentDate.format('YYYY-MM-DD') === endAmortizationDate.format('YYYY-MM-DD'))) {
232
+ if (EMICurrentDate.format('YYYY-MM-DD') === endAmortizationDate.format('YYYY-MM-DD')) {
233
+ lastMonthInterest = lastMonthInterest + accumulatedMonthlyInterest;
234
+ }
235
+ else {
236
+ lastMonthInterest = accumulatedMonthlyInterest;
237
+ }
238
+ accumulatedMonthlyInterest = 0;
239
+ firstPeriod = false;
240
+ }
241
+ if (monthPayments.length > 0) {
242
+ const principal = monthPayments
243
+ .filter((p) => (0, dayjs_1.default)(p.date).format('YYYY-MM-DD') === EMICurrentDate.format('YYYY-MM-DD'))
244
+ .reduce((acc, p) => acc + Math.abs(p.amount), 0);
245
+ if (principal > 0) {
246
+ balance = new decimal_js_1.default(balance).sub(principal).toDP(2).toNumber();
247
+ lastMonthInterest = 0;
248
+ }
249
+ }
250
+ else {
251
+ if ((EMICurrentDate.format('YYYY-MM-DD') === EMICurrentDate.date(termLoan.settings.principalDueDay).format('YYYY-MM-DD'))) {
252
+ const isGracePeriod = checkGracePeriodDate(EMICurrentDate);
253
+ const principal = (isGracePeriod || firstPeriod) ? 0 : new decimal_js_1.default(EMI).sub(lastMonthInterest).toDP(2).toNumber();
254
+ balance = new decimal_js_1.default(balance).sub(principal).toDP(2).toNumber();
255
+ lastMonthInterest = 0;
256
+ }
257
+ }
258
+ EMICurrentDate = EMICurrentDate.add(1, 'day');
259
+ }
260
+ const principal = new decimal_js_1.default(EMI).sub(lastMonthInterest).toDP(2).toNumber();
261
+ balance = new decimal_js_1.default(balance).sub(principal).toDP(2).toNumber();
262
+ return balance;
263
+ };
264
+ let lowerBound = 0;
265
+ let upperBound = initialBalance;
266
+ let optimalEMI = 0;
267
+ const tolerance = 0.01;
268
+ while (new decimal_js_1.default(upperBound).sub(lowerBound).toDP(2).toNumber() > tolerance) {
269
+ const mid = new decimal_js_1.default(lowerBound).add(upperBound).div(2).toDP(2).toNumber();
270
+ const currentBalance = await calculateBalance(termLoan, mid);
271
+ if (currentBalance < 0) {
272
+ upperBound = mid;
273
+ }
274
+ else {
275
+ lowerBound = mid;
276
+ optimalEMI = mid;
277
+ }
278
+ }
279
+ return new decimal_js_1.default(optimalEMI).add(tolerance).toDP(2).toNumber();
280
+ };
281
+ const getNextPaymentDate = (date) => {
282
+ let paymentDate = date.date(termLoan.settings.principalDueDay);
283
+ if (paymentDate < date) {
284
+ paymentDate = paymentDate.add(1, 'month').date(termLoan.settings.principalDueDay);
285
+ }
286
+ return paymentDate;
287
+ };
288
+ const complexEMI = await getComplexEMI(termLoan);
289
+ console.log({ complexEMI });
290
+ const getDaysInMonth = (date) => {
291
+ const maxStartDate = dayjs_1.default.max(startDate, date.startOf('month')).startOf('day');
292
+ const maxEndDate = dayjs_1.default.min(endAmortizationDate, date.endOf('month')).startOf('day');
293
+ return maxEndDate.diff(maxStartDate, 'day') + 1;
294
+ };
295
+ let calculatedDailyResults = [];
296
+ const calculatedMonthlyResults = [];
297
+ let monthOpeningBalance = initialBalance;
298
+ let openingBalance = initialBalance;
299
+ let closingBalance = openingBalance;
300
+ let monthInterest = 0;
301
+ let lastMonthInterest = 0;
302
+ let lastMonthPrincipal = 0;
303
+ let dayAdminFee = 0;
304
+ let monthAdminFee = 0;
305
+ const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
306
+ const adminCharge = charges.find((charge) => charge.chargeType === loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE);
307
+ const minAdminFee = adminCharge ? adminCharge.minimumAmount : 0;
308
+ let monthPayments = [];
309
+ let monthDisbursements = [];
310
+ let isLastPeriod = false;
311
+ while ((currentDate.startOf('day').isBefore(endAmortizationDate.startOf('day')) || currentDate.startOf('day').isSame(endAmortizationDate.startOf('day'))) &&
312
+ !isLastPeriod) {
313
+ if ((currentDate.format('YYYY-MM-DD') === currentDate.startOf('month').format('YYYY-MM-DD')) ||
314
+ (currentDate.format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD'))) {
315
+ if (actual) {
316
+ monthPayments = await getMonthPayments(currentDate.startOf('day'));
317
+ monthDisbursements = await getMonthDisbursements(currentDate.startOf('day'));
318
+ }
319
+ monthOpeningBalance = openingBalance;
320
+ }
321
+ const lastTransaction = await loanTransactionsService.getLastTransactionForDate(productId, currentDate.subtract(1, 'day').toDate());
322
+ if (actual && lastTransaction && (0, dayjs_1.default)(lastTransaction.date).format('YYYY-MM-DD') === currentDate.subtract(1, 'day').format('YYYY-MM-DD')) {
323
+ openingBalance = lastTransaction.balance;
324
+ }
325
+ if (currentDate.format('YYYY-MM-DD') === currentDate.date(termLoan.settings.principalDueDay).add(1, 'day').format('YYYY-MM-DD')) {
326
+ openingBalance = closingBalance;
327
+ }
328
+ dayAdminFee = openingBalance * termLoan.adminRate / getDaysInMonth(currentDate);
329
+ monthAdminFee = monthAdminFee + dayAdminFee;
330
+ const interestRate = await getDynamicInterestRate(currentDate.toDate());
331
+ const interestRateDaily = interestRate / daysInYear;
332
+ const dailyInterest = new decimal_js_1.default(openingBalance).mul(interestRateDaily).toDP(2).toNumber();
333
+ const principalDueDay = getNextPaymentDate(currentDate);
334
+ if (monthPayments.length > 0) {
335
+ const foundPayment = monthPayments.find((payment) => (0, dayjs_1.default)(payment.date).format('YYYY-MM-DD') === currentDate.format('YYYY-MM-DD'));
336
+ if (foundPayment) {
337
+ closingBalance = foundPayment.balance;
338
+ }
339
+ const lastTransaction = await loanTransactionsService.getLastTransactionForDate(productId, currentDate.toDate());
340
+ if (actual && lastTransaction && (0, dayjs_1.default)(lastTransaction.date).format('YYYY-MM-DD') === currentDate.format('YYYY-MM-DD')) {
341
+ closingBalance = lastTransaction.balance;
342
+ }
343
+ }
344
+ else {
345
+ if (currentDate.format('YYYY-MM-DD') === principalDueDay.format('YYYY-MM-DD')) {
346
+ const monthlyPayment = new decimal_js_1.default(complexEMI).sub(lastMonthInterest).toDP(2).toNumber();
347
+ const isLastPeriod = monthlyPayment > openingBalance;
348
+ const isGracePeriod = checkGracePeriodDate(principalDueDay);
349
+ const monthlyPrincipal = (isGracePeriod) ? 0 : (isLastPeriod ? openingBalance : monthlyPayment);
350
+ closingBalance = (0, numbers_helper_1.roundToXDigits)(openingBalance - monthlyPrincipal);
351
+ }
352
+ }
353
+ monthInterest = (0, numbers_helper_1.roundToXDigits)(monthInterest + dailyInterest);
354
+ const dailyResult = {
355
+ day: currentDate.format('YYYY-MM-DD'),
356
+ interestRate: interestRate,
357
+ interest: dailyInterest,
358
+ paymentDate: currentDate.date() === termLoan.settings.principalDueDay,
359
+ };
360
+ calculatedDailyResults.push(dailyResult);
361
+ if ((currentDate.format('YYYY-MM-DD') === currentDate.endOf('month').format('YYYY-MM-DD')) ||
362
+ (currentDate.format('YYYY-MM-DD') === endAmortizationDate.format('YYYY-MM-DD')) ||
363
+ (currentDate.format('YYYY-MM-DD') === endTermMonths.format('YYYY-MM-DD'))) {
364
+ if (monthPayments.length > 0 && calculatedMonthlyResults.length > 0) {
365
+ calculatedMonthlyResults[calculatedMonthlyResults.length - 1].payments = monthPayments.map((p) => ({
366
+ paymentId: new mongoose_1.default.Types.ObjectId(String(p._id)),
367
+ amount: Math.abs(p.amount),
368
+ }));
369
+ calculatedMonthlyResults[calculatedMonthlyResults.length - 1].monthlyPrincipal = monthPayments.reduce((acc, p) => acc + Math.abs(p.amount), 0);
370
+ }
371
+ const actualMonthlyPayment = new decimal_js_1.default(complexEMI).sub(monthInterest).toDP(2).toNumber();
372
+ isLastPeriod = actualMonthlyPayment > monthOpeningBalance || (currentDate.format('YYYY-MM-DD') === endTermMonths.format('YYYY-MM-DD'));
373
+ const isGracePeriod = checkGracePeriodDate(principalDueDay);
374
+ const monthlyPrincipal = (isGracePeriod) ? 0 : (isLastPeriod ? monthOpeningBalance : actualMonthlyPayment);
375
+ const totalPayment = monthPayments.length > 0 ? monthPayments.reduce((acc, p) => acc + Math.abs(p.amount), 0) : lastMonthPrincipal;
376
+ const totalDisbursements = monthDisbursements.length > 0 ? monthDisbursements.reduce((acc, p) => acc + Math.abs(p.amount), 0) : 0;
377
+ const closingBalance = new decimal_js_1.default(monthOpeningBalance).sub(totalPayment).add(totalDisbursements).toDP(2).toNumber();
378
+ const monthlyResult = {
379
+ termLoanId: termLoan._id,
380
+ openingBalance: monthOpeningBalance,
381
+ relevantStatement: currentDate.endOf('month').format('YYYY-MM-DD'),
382
+ monthlyInterest: monthInterest,
383
+ monthlyPrincipal: monthlyPrincipal,
384
+ closingBalance: closingBalance,
385
+ adminFee: Math.max((0, numbers_helper_1.roundToXDigits)(monthAdminFee), minAdminFee),
386
+ interestRate: interestRate,
387
+ interestDaily: monthOpeningBalance * interestRate / daysInYear,
388
+ gracePeriod: isGracePeriod,
389
+ amortization: true, // TODO calculate
390
+ paymentDueDate: principalDueDay.date(termLoan.settings.principalDueDay).toDate(),
391
+ payments: null,
392
+ disbursements: totalDisbursements,
393
+ dailyResults: calculatedDailyResults,
394
+ };
395
+ calculatedMonthlyResults.push(monthlyResult);
396
+ lastMonthInterest = monthInterest;
397
+ lastMonthPrincipal = monthlyPrincipal;
398
+ monthInterest = 0;
399
+ monthAdminFee = 0;
400
+ calculatedDailyResults = [];
401
+ }
402
+ currentDate = currentDate.add(1, 'day');
403
+ }
404
+ const paymentDates = [...new Set(calculatedMonthlyResults.map((res) => (0, dayjs_1.default)(res.paymentDueDate).format('YYYY-MM-DD')))];
405
+ const mergedResults = paymentDates.reduce((acc, paymentDate) => {
406
+ console.log(paymentDate, 'processing results');
407
+ const filteredDateResults = calculatedMonthlyResults.filter((res) => (0, dayjs_1.default)(res.paymentDueDate).format('YYYY-MM-DD') === paymentDate);
408
+ if (filteredDateResults.length === 1) {
409
+ return [...acc, ...filteredDateResults];
410
+ }
411
+ const totalMonthlyInterest = filteredDateResults.reduce((acc, res) => {
412
+ return (0, numbers_helper_1.roundToXDigits)(acc + res.monthlyInterest);
413
+ }, 0);
414
+ const mergedOpeningBalance = filteredDateResults[0].openingBalance;
415
+ const totalMonthlyPrincipal = Math.min(filteredDateResults[0].closingBalance, (0, numbers_helper_1.roundToXDigits)(complexEMI - totalMonthlyInterest));
416
+ const mergedClosingBalance = filteredDateResults[0].closingBalance;
417
+ const totalAdminFee = filteredDateResults.reduce((acc, res) => {
418
+ return (0, numbers_helper_1.roundToXDigits)(acc + res.adminFee);
419
+ }, 0);
420
+ const lastStatement = {
421
+ termLoanId: filteredDateResults[0].termLoanId,
422
+ openingBalance: mergedOpeningBalance,
423
+ relevantStatement: filteredDateResults[0].relevantStatement,
424
+ monthlyInterest: totalMonthlyInterest,
425
+ monthlyPrincipal: totalMonthlyPrincipal,
426
+ closingBalance: mergedClosingBalance,
427
+ adminFee: totalAdminFee,
428
+ interestRate: filteredDateResults[0].interestRate,
429
+ interestDaily: filteredDateResults[0].openingBalance * filteredDateResults[0].interestRate / daysInYear,
430
+ gracePeriod: filteredDateResults[0].gracePeriod,
431
+ amortization: true, // TODO calculate
432
+ paymentDueDate: filteredDateResults[0].paymentDueDate,
433
+ payments: null,
434
+ disbursements: filteredDateResults[0].disbursements,
435
+ dailyResults: filteredDateResults[0].dailyResults, // totalCalculatedDailyResults
436
+ };
437
+ const lastPaymentDate = (0, dayjs_1.default)(filteredDateResults[1].paymentDueDate).isBefore((0, dayjs_1.default)(filteredDateResults[1].relevantStatement))
438
+ ? getNextPaymentDate((0, dayjs_1.default)(filteredDateResults[1].relevantStatement)).toDate()
439
+ : filteredDateResults[1].paymentDueDate;
440
+ const emptyStatement = {
441
+ termLoanId: filteredDateResults[1].termLoanId,
442
+ openingBalance: mergedClosingBalance,
443
+ relevantStatement: filteredDateResults[1].relevantStatement,
444
+ monthlyInterest: 0,
445
+ monthlyPrincipal: endTermMonths.format() !== endAmortizationDate.format() ? mergedClosingBalance : 0,
446
+ closingBalance: 0,
447
+ adminFee: 0,
448
+ interestRate: 0,
449
+ interestDaily: 0,
450
+ gracePeriod: filteredDateResults[1].gracePeriod,
451
+ amortization: true, // TODO calculate
452
+ paymentDueDate: lastPaymentDate,
453
+ payments: null,
454
+ disbursements: filteredDateResults[1].disbursements,
455
+ dailyResults: filteredDateResults[1].dailyResults,
456
+ };
457
+ return [...acc, lastStatement, emptyStatement];
458
+ }, []);
459
+ if (mergedResults[mergedResults.length - 1].closingBalance !== 0) {
460
+ // mergedResults[mergedResults.length - 1].monthlyPrincipal = mergedResults[mergedResults.length - 1].closingBalance;
461
+ const lastUnfinishedResult = mergedResults[mergedResults.length - 1];
462
+ const finishingResult = JSON.parse(JSON.stringify(lastUnfinishedResult));
463
+ finishingResult.openingBalance = lastUnfinishedResult.closingBalance;
464
+ finishingResult.monthlyInterest = 0;
465
+ finishingResult.monthlyPrincipal = 0;
466
+ finishingResult.adminFee = 0;
467
+ finishingResult.interestRate = 0;
468
+ finishingResult.interestDaily = 0;
469
+ finishingResult.closingBalance = 0;
470
+ finishingResult.dailyResults = [];
471
+ mergedResults.push(finishingResult);
472
+ }
473
+ console.log('saving...');
474
+ const filteredResults = mergedResults
475
+ .map((res) => ({ ...res, monthlyPrincipal: res.monthlyPrincipal > 0 ? res.monthlyPrincipal : 0 }));
476
+ const filteredRes = (await Promise.all(filteredResults.map(async (res) => {
477
+ if (!actual) {
478
+ return res;
479
+ }
480
+ const trDate = typeof res.paymentDueDate === 'string' ? new Date(res.paymentDueDate) : res.paymentDueDate;
481
+ const lastTransaction = await loanTransactionsService.getLastTransactionForDate(product._id.toString(), trDate);
482
+ if (lastTransaction && lastTransaction.balance > 0) {
483
+ return res;
484
+ }
485
+ }))).filter((res) => !!res);
486
+ await this.saveCalculatedTermLoan(filteredRes, productId);
487
+ await this.updateTermLoanStatus(termLoan._id.toString(), TermLoan_model_1.ETermLoanStatus.CALCULATED, new Date());
488
+ return await this.getTermLoan(productId, actual);
489
+ }
490
+ async saveCalculatedTermLoan(calculatedTermLoans, productId) {
491
+ if (calculatedTermLoans.length === 0) {
492
+ return;
493
+ }
494
+ const termLoanId = calculatedTermLoans[0].termLoanId;
495
+ await TermLoanCalculated_model_1.TermLoanCalculatedModel.deleteMany({ termLoanId, payments: null });
496
+ await Promise.all(calculatedTermLoans.map(async (calculatedTermLoan) => {
497
+ if (calculatedTermLoan.payments === null) {
498
+ await TermLoanCalculated_model_1.TermLoanCalculatedModel.findOneAndDelete({
499
+ termLoanId: calculatedTermLoan.termLoanId,
500
+ relevantStatement: calculatedTermLoan.relevantStatement,
501
+ });
502
+ const newCalculatedTermLoan = new TermLoanCalculated_model_1.TermLoanCalculatedModel(calculatedTermLoan);
503
+ await newCalculatedTermLoan.save();
504
+ }
505
+ else {
506
+ const foundTermLoanCalculated = await TermLoanCalculated_model_1.TermLoanCalculatedModel.findOne({
507
+ termLoanId: calculatedTermLoan.termLoanId,
508
+ relevantStatement: calculatedTermLoan.relevantStatement,
509
+ }).lean();
510
+ if (!foundTermLoanCalculated) {
511
+ const newCalculatedTermLoan = new TermLoanCalculated_model_1.TermLoanCalculatedModel(calculatedTermLoan);
512
+ await newCalculatedTermLoan.save();
513
+ }
514
+ else {
515
+ const loanStatementStatusService = this.getLoanStatementStatusService();
516
+ const statementStatus = await loanStatementStatusService.getStatementStatus(productId, new Date(calculatedTermLoan.relevantStatement));
517
+ if (!statementStatus.locked) {
518
+ await TermLoanCalculated_model_1.TermLoanCalculatedModel.findByIdAndUpdate(foundTermLoanCalculated._id, calculatedTermLoan);
519
+ }
520
+ }
521
+ }
522
+ }));
523
+ }
524
+ async getTermLoanExcel(productId, actual, short) {
525
+ const termLoan = await this.getTermLoan(productId, actual);
526
+ const header = short
527
+ ? ['PERIOD', 'OPENING BALANCE', 'PRINCIPAL', 'INTEREST', 'CLOSING BALANCE', 'ADMIN FEE']
528
+ : ['DATE', 'OPENING BALANCE', 'INTEREST', 'DAILY INTEREST ACCRUED', 'PRINCIPAL', 'CLOSING BALANCE', 'STATEMENT DATE'];
529
+ const getMonthlyData = (data) => {
530
+ return data.calculated.map((row) => {
531
+ return [
532
+ new Date(row.relevantStatement),
533
+ row.openingBalance,
534
+ row.monthlyPrincipal,
535
+ row.monthlyInterest,
536
+ row.closingBalance,
537
+ row.adminFee,
538
+ ];
539
+ });
540
+ };
541
+ const getDailyData = (data) => {
542
+ let lastMonthClosingBalance = null;
543
+ let lastPaymentClosingBalance = null;
544
+ let lastPaymentMonthlyPrincipal = null;
545
+ let lastPaymentDate = null;
546
+ const calculatedResults = data.calculated.reduce((acc, row) => {
547
+ let isPaymentDay = false;
548
+ let openingBalance = lastMonthClosingBalance ?? row.openingBalance;
549
+ let closingBalance = openingBalance;
550
+ return [
551
+ ...acc,
552
+ ...row.dailyResults.map((daily, index) => {
553
+ isPaymentDay = isPaymentDay || daily.paymentDate;
554
+ openingBalance = closingBalance;
555
+ if ((0, dayjs_1.default)(daily.day).format('YYYY-MM-DD') === (0, dayjs_1.default)(lastPaymentDate).format('YYYY-MM-DD')) {
556
+ closingBalance = lastPaymentClosingBalance - lastPaymentMonthlyPrincipal;
557
+ }
558
+ const newRow = [
559
+ new Date(daily.day),
560
+ openingBalance,
561
+ daily.interestRate,
562
+ daily.interest,
563
+ daily.paymentDate ? lastPaymentMonthlyPrincipal : null,
564
+ closingBalance,
565
+ row.relevantStatement,
566
+ ];
567
+ if ((0, dayjs_1.default)(daily.day).format('YYYY-MM-DD') === (0, dayjs_1.default)(lastPaymentDate).format('YYYY-MM-DD')) {
568
+ openingBalance = closingBalance;
569
+ }
570
+ isPaymentDay = isPaymentDay || daily.paymentDate;
571
+ if (daily.paymentDate) {
572
+ lastPaymentClosingBalance = row.closingBalance;
573
+ lastPaymentMonthlyPrincipal = row.monthlyPrincipal;
574
+ lastPaymentDate = row.paymentDueDate;
575
+ }
576
+ if (index + 1 === row.dailyResults.length) {
577
+ lastMonthClosingBalance = row.closingBalance;
578
+ }
579
+ return newRow;
580
+ })
581
+ ];
582
+ }, []);
583
+ const lastRow = data.calculated[data.calculated.length - 1];
584
+ const lastPayment = [
585
+ new Date(lastRow.paymentDueDate),
586
+ lastRow.openingBalance,
587
+ null,
588
+ null,
589
+ lastRow.monthlyPrincipal,
590
+ lastRow.closingBalance,
591
+ lastRow.relevantStatement,
592
+ ];
593
+ return [...calculatedResults, lastPayment];
594
+ };
595
+ const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
596
+ const preparedData = short ? getMonthlyData(termLoan) : getDailyData(termLoan);
597
+ const dataToConvert = [{ [product.name]: [header, ...preparedData] }];
598
+ const uploadsService = this.getUploadsService();
599
+ return await uploadsService.convertDataToFile(dataToConvert);
600
+ }
601
+ async isBorrowerTermLoanActual(borrowerId) {
602
+ const products = await this.loanChargesService.getLoanProducts(borrowerId);
603
+ const termLoanProduct = products.find((product) => product.type === loan_types_enum_1.ELoanTypes.TERM);
604
+ if (!termLoanProduct) {
605
+ return true;
606
+ }
607
+ const termLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId: termLoanProduct._id, actual: true });
608
+ if (!termLoan) {
609
+ return true;
610
+ }
611
+ return termLoan.calculationStatus === TermLoan_model_1.ETermLoanStatus.CALCULATED;
612
+ }
613
+ }
614
+ exports.TermLoanService = TermLoanService;