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,897 @@
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.AvailabilityService = exports.START_PAGINATORS = void 0;
7
+ const dayjs_1 = __importDefault(require("dayjs"));
8
+ const lodash_1 = __importDefault(require("lodash"));
9
+ const mongoose_1 = __importDefault(require("mongoose"));
10
+ const decimal_js_1 = __importDefault(require("decimal.js"));
11
+ const mongo_helper_1 = require("../helpers/mongo.helper");
12
+ const ReceivableAvailability_model_1 = require("../models/ReceivableAvailability.model");
13
+ const numbers_helper_1 = require("../helpers/numbers.helper");
14
+ const AccountPayableItem_model_1 = require("../models/AccountPayableItem.model");
15
+ const collaterals_enum_1 = require("../enums/collaterals.enum");
16
+ const ReceivableAvailabilityItem_model_1 = require("../models/ReceivableAvailabilityItem.model");
17
+ const collaterals_db_1 = require("../db/collaterals.db");
18
+ const _models_1 = require("../models/_models");
19
+ const receivables_db_1 = require("../db/receivables.db");
20
+ const availability_db_1 = require("../db/availability.db");
21
+ const bbcSheets_db_1 = require("../db/bbcSheets.db");
22
+ const CustomerGroup_model_1 = require("../models/CustomerGroup.model");
23
+ const partially_paid_restated_1 = require("../queries/receivable/partially-paid-restated");
24
+ const MappedGroup_model_1 = __importDefault(require("../models/MappedGroup.model"));
25
+ const CustomerAPGroup_model_1 = __importDefault(require("../models/CustomerAPGroup.model"));
26
+ const reserve_db_1 = require("../db/reserve.db");
27
+ const inventory_availability_results_enum_1 = require("../enums/inventory-availability-results.enum");
28
+ exports.START_PAGINATORS = {
29
+ [inventory_availability_results_enum_1.EInventoryAvailabilityResults.UNIQ_ITEMS]: { pageSize: 20, pageIndex: 0 },
30
+ [inventory_availability_results_enum_1.EInventoryAvailabilityResults.NON_UNIQ_ITEMS]: { pageSize: 20, pageIndex: 0 },
31
+ [inventory_availability_results_enum_1.EInventoryAvailabilityResults.NOT_MATCHED_ITEMS]: { pageSize: 20, pageIndex: 0 },
32
+ };
33
+ class AvailabilityService {
34
+ collateralAdjustmentsService;
35
+ collateralsService;
36
+ equipmentService;
37
+ inventoryAvailabilityService;
38
+ loanStatementService;
39
+ loanTransactionsService;
40
+ manualEntryService;
41
+ reserveService;
42
+ signsService;
43
+ constructor(collateralAdjustmentsService, collateralsService, equipmentService, inventoryAvailabilityService, loanStatementService, loanTransactionsService, manualEntryService, reserveService, signsService) {
44
+ this.collateralAdjustmentsService = collateralAdjustmentsService;
45
+ this.collateralsService = collateralsService;
46
+ this.equipmentService = equipmentService;
47
+ this.inventoryAvailabilityService = inventoryAvailabilityService;
48
+ this.loanStatementService = loanStatementService;
49
+ this.loanTransactionsService = loanTransactionsService;
50
+ this.manualEntryService = manualEntryService;
51
+ this.reserveService = reserveService;
52
+ this.signsService = signsService;
53
+ }
54
+ async recombineGroups(calculationsSettings) {
55
+ const mappedSettings = await Promise.all(Object.values(ReceivableAvailability_model_1.ELimitSettings).map(async (settingsKey) => {
56
+ const customerGroupMapped = await Promise.all(calculationsSettings[settingsKey].map(async (customerGroup) => {
57
+ const idIsValid = (0, mongo_helper_1.isValidId)(customerGroup.customer);
58
+ if (idIsValid) {
59
+ const foundCustomerGroup = await CustomerGroup_model_1.CustomerGroupModel.findById(customerGroup.customer);
60
+ if (foundCustomerGroup) {
61
+ return foundCustomerGroup.items.reduce((innerAcc, item) => ({
62
+ ...innerAcc,
63
+ [item]: customerGroup.limit,
64
+ }), {});
65
+ }
66
+ return { [customerGroup.customer]: customerGroup.limit };
67
+ }
68
+ return null;
69
+ }));
70
+ const filtered = customerGroupMapped.filter((group) => !!group);
71
+ return filtered.reduce((lastAcc, item) => ({ ...lastAcc, ...item }), {});
72
+ }));
73
+ const groupedSettings = Object.values(ReceivableAvailability_model_1.ELimitSettings).reduce((acc, settings, index) => ({
74
+ ...acc,
75
+ [settings]: mappedSettings[index],
76
+ }), {});
77
+ Object.values(ReceivableAvailability_model_1.ELimitSettings).forEach((settingsKey) => {
78
+ calculationsSettings[settingsKey].forEach((customer) => {
79
+ const idIsValid = (0, mongo_helper_1.isValidId)(customer.customer);
80
+ if (!idIsValid) {
81
+ groupedSettings[settingsKey][customer.customer] = customer.limit;
82
+ }
83
+ });
84
+ });
85
+ return groupedSettings;
86
+ }
87
+ async calculateAvailability(bbcDateId, paginatorOptions, user) {
88
+ const calculationsSettings = await this.getCalculationSettings(bbcDateId);
89
+ const allReceivables = await (0, collaterals_db_1.getCollateralDocsByBBC)([bbcDateId], collaterals_enum_1.ECollaterals.RECEIVABLE);
90
+ const receivables = allReceivables.pop();
91
+ const calculation = {
92
+ borrowerId: null,
93
+ bbcDateId: new mongoose_1.default.Types.ObjectId(bbcDateId),
94
+ userCalculatedId: user?._id ? new mongoose_1.default.Types.ObjectId(String(user._id)) : null,
95
+ calculatedAt: new Date(),
96
+ actual: true,
97
+ settings: calculationsSettings,
98
+ items: [],
99
+ summary: null,
100
+ totalItems: 0,
101
+ useManualInputs: false,
102
+ };
103
+ if (!receivables) {
104
+ return calculation;
105
+ }
106
+ calculation.borrowerId = new mongoose_1.default.Types.ObjectId(receivables.borrowerId.toString());
107
+ const allCustomerGroups = await CustomerGroup_model_1.CustomerGroupModel.find({ borrowerId: receivables.borrowerId }).lean();
108
+ const enrichedEnrichedSettings = await this.recombineGroups(calculationsSettings);
109
+ const getBucket = (bbcDate, item, permittedDays, isExcluded = false) => {
110
+ const invoiceDate = (0, dayjs_1.default)(item.invoiceDate).startOf('day');
111
+ const actualDays = (0, dayjs_1.default)(bbcDate).startOf('day').diff(invoiceDate, 'day');
112
+ const data = {
113
+ bucketCurrent: 0,
114
+ bucket30: 0,
115
+ bucket60: 0,
116
+ permitted60: 0,
117
+ permittedOver: 0,
118
+ creditNotes: 0,
119
+ actualDays,
120
+ permittedDays,
121
+ };
122
+ if (isExcluded) {
123
+ return data;
124
+ }
125
+ if (item.invoiceAmount < 0) {
126
+ data.creditNotes = item.invoiceAmount;
127
+ return data;
128
+ }
129
+ if (actualDays > permittedDays) {
130
+ data.permittedOver = item.invoiceAmount;
131
+ return data;
132
+ }
133
+ if (actualDays > 60 && actualDays <= permittedDays) {
134
+ data.permitted60 = item.invoiceAmount;
135
+ return data;
136
+ }
137
+ if (actualDays > 30) {
138
+ data.bucket60 = item.invoiceAmount;
139
+ return data;
140
+ }
141
+ if (actualDays >= 1) {
142
+ data.bucket30 = item.invoiceAmount;
143
+ return data;
144
+ }
145
+ data.bucketCurrent = item.invoiceAmount;
146
+ return data;
147
+ };
148
+ const getExcludedCustomers = (items) => {
149
+ const uniqueCustomers = Array.from(new Set(items.map((c) => c.customerTitle)));
150
+ return uniqueCustomers.reduce((acc, customer) => {
151
+ if (Object.keys(enrichedEnrichedSettings.excludedCustomers).includes(customer)) {
152
+ return [...acc, customer];
153
+ }
154
+ return acc;
155
+ }, []);
156
+ };
157
+ const calculateCustomerConcentration = (items, calculationsSettings) => {
158
+ const uniqueCustomers = Array.from(new Set(items.map((c) => c.customerTitle)));
159
+ const uniqueCustomersWithTotal = items.reduce((acc, item) => {
160
+ const preliminaryEligibleBalance = item.preliminaryEligibleBalance;
161
+ const permittedOver = item.permittedOver;
162
+ if (acc[item.customerTitle]) {
163
+ return {
164
+ ...acc,
165
+ [item.customerTitle]: {
166
+ preliminaryEligibleBalance: new decimal_js_1.default(acc[item.customerTitle].preliminaryEligibleBalance).add(preliminaryEligibleBalance).toNumber(),
167
+ permittedOver: new decimal_js_1.default(acc[item.customerTitle].permittedOver).add(permittedOver).toNumber(),
168
+ },
169
+ };
170
+ }
171
+ return {
172
+ ...acc,
173
+ [item.customerTitle]: {
174
+ preliminaryEligibleBalance: preliminaryEligibleBalance,
175
+ permittedOver: permittedOver,
176
+ },
177
+ };
178
+ }, {});
179
+ const uniqueCustomersWithTotalGrouped = Object.entries(uniqueCustomersWithTotal).reduce((acc, [customer, totals]) => {
180
+ if (Object.keys(enrichedEnrichedSettings.excludedCustomers).includes(customer)) {
181
+ return { INCLUDED: { ...acc.INCLUDED }, EXCLUDED: { ...acc.EXCLUDED, [customer]: totals } };
182
+ }
183
+ return { EXCLUDED: { ...acc.EXCLUDED }, INCLUDED: { ...acc.INCLUDED, [customer]: totals } };
184
+ }, { EXCLUDED: {}, INCLUDED: {} });
185
+ const preliminaryEligibleIncluded = Object
186
+ .values(uniqueCustomersWithTotalGrouped.INCLUDED)
187
+ .reduce((total, c) => {
188
+ return new decimal_js_1.default(total).add(c.preliminaryEligibleBalance).toNumber();
189
+ }, 0);
190
+ const totalPermittedOver = Object
191
+ .values(uniqueCustomersWithTotalGrouped.INCLUDED)
192
+ .reduce((total, c) => {
193
+ return new decimal_js_1.default(total).add(c.permittedOver).toNumber();
194
+ }, 0);
195
+ const totalExcluded = new decimal_js_1.default(preliminaryEligibleIncluded).add(totalPermittedOver).toNumber();
196
+ const INCLUDED = {};
197
+ const updatedCustomers = items.map((c) => {
198
+ if (!excludedCustomers.includes(c.customerTitle)) {
199
+ INCLUDED[c.customerTitle] = {
200
+ concentration: new decimal_js_1.default(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).div(preliminaryEligibleIncluded).toNumber(),
201
+ total: uniqueCustomersWithTotal[c.customerTitle],
202
+ };
203
+ }
204
+ const permittedConcentrationLimit = enrichedEnrichedSettings.permittedConcentration[c.customerTitle];
205
+ const permittedCustomerConcentration = permittedConcentrationLimit ? permittedConcentrationLimit : calculationsSettings.customerConcentrationLimit;
206
+ let customerConcentration;
207
+ if (calculationsSettings.excludeFromTotal) {
208
+ customerConcentration = totalExcluded === 0
209
+ ? 0
210
+ : new decimal_js_1.default(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).add(uniqueCustomersWithTotal[c.customerTitle].permittedOver).div(totalExcluded).toNumber();
211
+ }
212
+ else {
213
+ customerConcentration = preliminaryEligibleIncluded === 0
214
+ ? 0 :
215
+ new decimal_js_1.default(uniqueCustomersWithTotal[c.customerTitle].preliminaryEligibleBalance).div(preliminaryEligibleIncluded).toNumber();
216
+ }
217
+ return { ...c, customerConcentration, permittedCustomerConcentration };
218
+ });
219
+ const uniqueCustomerConcentration = uniqueCustomers
220
+ .map((customer) => {
221
+ if (excludedCustomers.includes(customer)) {
222
+ return null;
223
+ }
224
+ const foundRow = updatedCustomers.find((c) => c.customerTitle === customer);
225
+ return {
226
+ customerTitle: foundRow.customerTitle,
227
+ customerGroup: foundRow.customerGroup,
228
+ concentration: foundRow.customerConcentration,
229
+ };
230
+ })
231
+ .filter((customer) => !!customer)
232
+ .filter((customer) => !!customer.customerGroup);
233
+ const groupsConcentration = uniqueCustomerConcentration.reduce((groupAcc, group) => {
234
+ return { ...groupAcc, [group.customerGroup]: (groupAcc[group.customerGroup] ?? 0) + group.concentration };
235
+ }, {});
236
+ updatedCustomers.forEach((customer) => {
237
+ const groupConcentration = groupsConcentration[customer.customerGroup];
238
+ if (groupConcentration) {
239
+ customer.customerConcentration = groupConcentration;
240
+ }
241
+ if (customer.excludedCustomer) {
242
+ customer.customerConcentration = 0;
243
+ }
244
+ customer.deductionPercent = customer.customerConcentration === 0
245
+ ? 0
246
+ : Math.min(0, customer.permittedCustomerConcentration - customer.customerConcentration) / customer.customerConcentration;
247
+ const deduction = (0, numbers_helper_1.roundToXDigits)(Math.abs(customer.grossAR * customer.deductionPercent));
248
+ customer.deduction = Math.min(deduction, customer.preliminaryEligibleBalance);
249
+ customer.balanceAfterDeduction = customer.preliminaryEligibleBalance - customer.deduction;
250
+ });
251
+ return { updatedCustomers, INCLUDED };
252
+ };
253
+ const groupData = (items, groupedBy = 'crossAge', byGroup = true) => {
254
+ const uniqueCustomers = Array.from(new Set(items.map((c) => byGroup ? c.customer : c.customerTitle)));
255
+ return uniqueCustomers.reduce((acc, customer) => {
256
+ const totals = items.reduce((innerAcc, c) => {
257
+ if (c[byGroup ? 'customer' : 'customerTitle'] === customer) {
258
+ const grossAR = (0, numbers_helper_1.roundToXDigits)(innerAcc.grossAR + c.grossAR);
259
+ const permittedOver = (0, numbers_helper_1.roundToXDigits)(innerAcc.permittedOver + c.permittedOver);
260
+ const crossAge = grossAR === 0 ? 0 : permittedOver / grossAR;
261
+ const balanceAfterCrossAge = (0, numbers_helper_1.roundToXDigits)(innerAcc.balanceAfterCrossAge + c.balanceAfterCrossAge);
262
+ const balanceAfterExclusion = (0, numbers_helper_1.roundToXDigits)(innerAcc.balanceAfterExclusion + c.balanceAfterExclusion);
263
+ return { grossAR, permittedOver, crossAge, balanceAfterCrossAge, balanceAfterExclusion };
264
+ }
265
+ return innerAcc;
266
+ }, { grossAR: 0, permittedOver: 0, crossAge: 0, balanceAfterCrossAge: 0, balanceAfterExclusion: 0 });
267
+ return { ...acc, [customer]: totals[groupedBy] };
268
+ }, {});
269
+ };
270
+ const startDate = (0, dayjs_1.default)(new Date(receivables.bbcDate)).subtract(calculationsSettings.paidDays, 'day').toDate();
271
+ const endDate = new Date(receivables.bbcDate);
272
+ const partiallyPaidData = await (0, partially_paid_restated_1.getPartialPaid)({
273
+ borrowerId: new mongoose_1.default.Types.ObjectId(String(receivables.borrowerId)),
274
+ startDate,
275
+ endDate,
276
+ });
277
+ const excludedCustomers = getExcludedCustomers(receivables.items);
278
+ const allInvoices = [...receivables.items];
279
+ let items = receivables.items.map((invoice) => {
280
+ const permittedDaysLimit = enrichedEnrichedSettings.permittedExtendedARDays[invoice.customerTitle];
281
+ const permittedDays = permittedDaysLimit ? permittedDaysLimit : calculationsSettings.standardARDays;
282
+ const isExcluded = excludedCustomers.includes(invoice.customerTitle);
283
+ const data = {
284
+ ...getBucket(receivables.bbcDate, invoice, permittedDays, isExcluded),
285
+ customer: invoice.customerTitle.trim(),
286
+ invoice: invoice,
287
+ customerTitle: invoice.customerTitle.trim(),
288
+ invoiceId: invoice._id.toString(),
289
+ invoiceNumber: invoice.invoiceNumber,
290
+ invoiceDate: invoice.invoiceDate,
291
+ invoiceAmount: invoice.invoiceAmount,
292
+ grossAR: 0,
293
+ preliminaryEligibleBalance: 0,
294
+ permittedCustomerConcentration: 0,
295
+ customerConcentration: 0,
296
+ deductionPercent: 0,
297
+ deduction: 0,
298
+ balanceAfterDeduction: 0,
299
+ partiallyPaidDifference: false,
300
+ partiallyPaidDeduction: 0,
301
+ balanceAfterPartially: 0,
302
+ excludedCustomer: isExcluded,
303
+ excludedCustomerDeduction: 0,
304
+ balanceAfterExclusion: 0,
305
+ customerGroupId: null,
306
+ customerGroup: null,
307
+ contra: 0,
308
+ contraDeduction: 0,
309
+ balanceAfterContra: 0,
310
+ crossAge: 0,
311
+ crossAgeDeduction: 0,
312
+ balanceAfterCrossAge: 0,
313
+ waivedDeduction: 0,
314
+ finalResult: 0,
315
+ insuredComponent: 0,
316
+ uninsuredComponent: 0,
317
+ };
318
+ const foundCustomerGroup = allCustomerGroups.find((group) => group.items.includes(data.customerTitle));
319
+ if (foundCustomerGroup) {
320
+ data.customer = foundCustomerGroup.groupName;
321
+ data.customerGroupId = foundCustomerGroup.id;
322
+ data.customerGroup = foundCustomerGroup.groupName;
323
+ }
324
+ const foundInvoice = partiallyPaidData.partiallyPaidInvoices.find((pInvoice) => {
325
+ return (pInvoice.invoiceNumber === invoice.invoiceNumber) && (pInvoice.customer === invoice.customerTitle);
326
+ });
327
+ if (foundInvoice) {
328
+ data.partiallyPaidDifference = foundInvoice.difference > 0;
329
+ data.partiallyPaidDeduction = invoice.invoiceAmount;
330
+ }
331
+ if (invoice.originalAmount !== 0 && invoice.originalAmount !== invoice.invoiceAmount) {
332
+ data.partiallyPaidDifference = true;
333
+ data.partiallyPaidDeduction = invoice.invoiceAmount;
334
+ }
335
+ const duplicateInvoices = allInvoices.filter((pInvoice) => {
336
+ return (pInvoice.invoiceNumber === invoice.invoiceNumber) && (pInvoice.customerTitle === invoice.customerTitle);
337
+ });
338
+ if (duplicateInvoices.length > 1) {
339
+ data.partiallyPaidDifference = true;
340
+ data.partiallyPaidDeduction = invoice.invoiceAmount;
341
+ }
342
+ data.grossAR = isExcluded ? invoice.invoiceAmount : (0, numbers_helper_1.roundToXDigits)(data.bucketCurrent + data.bucket30 + data.bucket60 + data.permitted60 + data.permittedOver);
343
+ data.preliminaryEligibleBalance = (0, numbers_helper_1.roundToXDigits)(data.bucketCurrent + data.bucket30 + data.bucket60 + data.permitted60);
344
+ return data;
345
+ });
346
+ const groupedGross = groupData(items, 'grossAR', false);
347
+ ({ updatedCustomers: items } = calculateCustomerConcentration(items, calculationsSettings));
348
+ const bbcSheets = await (0, bbcSheets_db_1.getBBCSheetsByType)([bbcDateId], collaterals_enum_1.ECollaterals.ACCOUNT_PAYABLE);
349
+ const apData = await AccountPayableItem_model_1.AccountPayableItemModel.aggregate([
350
+ {
351
+ $match: { bbcSheetId: { $in: bbcSheets.map((sheet) => sheet._id) } },
352
+ },
353
+ {
354
+ $group: {
355
+ '_id': '$customerName',
356
+ 'itemSum': {
357
+ $sum: '$amount',
358
+ },
359
+ },
360
+ },
361
+ ]);
362
+ const mappedGroups = await MappedGroup_model_1.default
363
+ .find({ borrowerId: receivables.borrowerId })
364
+ .lean();
365
+ const replacedMappedGroups = await Promise.all(mappedGroups.map(async (group) => {
366
+ const groupCopy = { ...group, items: [group.group1], customersAP: [group.group2] };
367
+ switch (group.groupType) {
368
+ case 'contras':
369
+ if ((0, mongo_helper_1.isValidId)(group.group1)) {
370
+ const foundGroup = await CustomerGroup_model_1.CustomerGroupModel.findById(group.group1).lean();
371
+ groupCopy.items = foundGroup ? foundGroup.items.slice() : [];
372
+ }
373
+ if ((0, mongo_helper_1.isValidId)(group.group2)) {
374
+ const foundGroup = await CustomerAPGroup_model_1.default.findById(group.group2).lean();
375
+ groupCopy.customersAP = foundGroup ? foundGroup.items.slice() : [];
376
+ }
377
+ }
378
+ return groupCopy;
379
+ }));
380
+ const groupedData = groupData(items);
381
+ items.forEach((customer) => {
382
+ /** --- Excluded customer --- */
383
+ customer.excludedCustomerDeduction = customer.excludedCustomer ? customer.invoiceAmount : 0;
384
+ customer.balanceAfterExclusion = customer.excludedCustomer ? 0 : customer.invoiceAmount;
385
+ if (customer.excludedCustomer) {
386
+ customer.grossAR = 0;
387
+ }
388
+ /** --- Partially paid --- */
389
+ if (customer.balanceAfterDeduction === 0) {
390
+ customer.partiallyPaidDeduction = 0;
391
+ }
392
+ customer.balanceAfterPartially = customer.partiallyPaidDifference ? 0 : customer.balanceAfterDeduction - customer.partiallyPaidDeduction;
393
+ });
394
+ const groupedBalanceAfterPartially = groupData(items, 'balanceAfterPartially', false);
395
+ items.forEach((customer) => {
396
+ /** --- Contra --- */
397
+ customer.balanceAfterContra = customer.balanceAfterPartially;
398
+ const mappedGroupMatches = replacedMappedGroups.filter((mappedGroup) => mappedGroup.items.includes(customer.customerTitle));
399
+ if (mappedGroupMatches && mappedGroupMatches.length) {
400
+ const contra = mappedGroupMatches.reduce((acc, mappedGroup) => {
401
+ return acc + mappedGroup.customersAP.reduce((innerAcc, customerAC) => {
402
+ const foundApData = apData.find((data) => data._id === customerAC);
403
+ if (foundApData) {
404
+ return foundApData.itemSum + innerAcc;
405
+ }
406
+ return innerAcc;
407
+ }, 0);
408
+ }, 0);
409
+ const contraClearDeduction = contra * calculationsSettings.contraProvision;
410
+ const eligibleBalance = groupedBalanceAfterPartially[customer.customerTitle] !== 0 ? customer.balanceAfterPartially / groupedBalanceAfterPartially[customer.customerTitle] : 0;
411
+ const contraDeduction = (0, numbers_helper_1.roundToXDigits)(eligibleBalance * contraClearDeduction);
412
+ customer.contra = contra;
413
+ customer.contraDeduction = contraDeduction >= customer.balanceAfterPartially ? customer.balanceAfterPartially : contraDeduction;
414
+ customer.balanceAfterContra = customer.balanceAfterPartially - customer.contraDeduction;
415
+ }
416
+ /** --- Cross age --- */
417
+ customer.crossAge = groupedData[customer.customer];
418
+ customer.crossAgeDeduction = customer.crossAge > calculationsSettings.crossAgeLimit ? customer.balanceAfterContra : 0;
419
+ customer.balanceAfterCrossAge = customer.crossAge > calculationsSettings.crossAgeLimit ? 0 : customer.balanceAfterContra;
420
+ });
421
+ /** --- Insurance --- */
422
+ const groupedBalanceAfterCrossAge = groupData(items, 'balanceAfterCrossAge', false);
423
+ items.forEach((customer) => {
424
+ const insured = enrichedEnrichedSettings.insuredCustomers[customer.customerTitle] ?? 0;
425
+ const totalGross = groupedGross[customer.customerTitle] ?? 0;
426
+ if (insured > totalGross) {
427
+ customer.waivedDeduction = calculationsSettings.waiveInsuredCustomers ? new decimal_js_1.default(customer.deduction).add(customer.crossAgeDeduction).toNumber() : 0;
428
+ }
429
+ customer.finalResult = new decimal_js_1.default((calculationsSettings.waiveInsuredCustomers
430
+ ? new decimal_js_1.default(customer.balanceAfterCrossAge).add(customer.waivedDeduction).toNumber()
431
+ : customer.balanceAfterCrossAge)).add(customer.creditNotes).toNumber();
432
+ customer.uninsuredComponent = customer.finalResult;
433
+ if (insured) {
434
+ const insuredAmount = groupedBalanceAfterCrossAge[customer.customerTitle] === 0
435
+ ? 0
436
+ : new decimal_js_1.default(insured).div(groupedBalanceAfterCrossAge[customer.customerTitle]).mul(customer.finalResult).toDecimalPlaces(2).toNumber();
437
+ const insuredAmountLimited = insuredAmount > customer.finalResult ? customer.finalResult : insuredAmount;
438
+ const uninsuredAmount = new decimal_js_1.default(customer.finalResult).sub(insuredAmountLimited).toNumber();
439
+ customer.insuredComponent = isNaN(insuredAmountLimited) ? 0 : insuredAmountLimited;
440
+ customer.uninsuredComponent = isNaN(uninsuredAmount) ? 0 : uninsuredAmount;
441
+ }
442
+ });
443
+ calculation.items = items;
444
+ calculation.summary = await this.calculateAvailabilitySummary(calculation);
445
+ calculation.totalItems = await this.collateralsService.countCollateralDocsByBBC([bbcDateId], collaterals_enum_1.ECollaterals.RECEIVABLE);
446
+ await this.saveCalculatedAvailability(calculation);
447
+ return await this.getAvailability(bbcDateId, paginatorOptions, user);
448
+ }
449
+ async getAvailability(bbcDateId, paginatorOptions, user) {
450
+ return await this.foundOrCreateAvailability(bbcDateId, paginatorOptions, user);
451
+ }
452
+ async getAvailabilityItemsWithFilter(bbcDateId, itemsFilter) {
453
+ const filtersMap = {
454
+ creditNotesNegative: 'creditNotes',
455
+ };
456
+ const filterField = filtersMap[itemsFilter] ? filtersMap[itemsFilter] : itemsFilter;
457
+ const emptyResponse = { itemsFilter: filterField, invoiceItems: [], customerItems: [] };
458
+ const allowedFilters = [
459
+ 'creditNotes',
460
+ 'excludedCustomerDeduction',
461
+ 'permittedOver',
462
+ 'deduction',
463
+ 'partiallyPaidDeduction',
464
+ 'contraDeduction',
465
+ 'crossAgeDeduction',
466
+ 'waivedDeduction',
467
+ ];
468
+ if (!allowedFilters.includes(filterField)) {
469
+ return emptyResponse;
470
+ }
471
+ const foundAvailability = await this.findCalculatedAvailability(bbcDateId);
472
+ if (!foundAvailability) {
473
+ return emptyResponse;
474
+ }
475
+ const items = await ReceivableAvailabilityItem_model_1.ReceivableAvailabilityItemModel.aggregate([
476
+ {
477
+ $match: {
478
+ 'availabilityId': foundAvailability._id,
479
+ [filterField]: { $ne: 0 },
480
+ },
481
+ }, {
482
+ $sort: {
483
+ 'order': 1,
484
+ },
485
+ }, {
486
+ $lookup: {
487
+ from: _models_1.MODEL_NAMES.receivableItems,
488
+ localField: 'invoiceId',
489
+ foreignField: '_id',
490
+ as: 'invoice',
491
+ },
492
+ }, {
493
+ $unwind: {
494
+ path: '$invoice',
495
+ },
496
+ },
497
+ ]);
498
+ const invoiceItems = items
499
+ .map((item) => {
500
+ return {
501
+ invoiceNumber: item.invoice.invoiceNumber,
502
+ invoiceDate: item.invoice.invoiceDate,
503
+ customerTitle: item.invoice.customerTitle,
504
+ invoiceAmount: item.invoice.invoiceAmount,
505
+ };
506
+ })
507
+ .sort((a, b) => new Date(b.invoiceDate).valueOf() - new Date(a.invoiceDate).valueOf());
508
+ const customerItemsRaw = items.reduce((acc, item) => {
509
+ if (!acc[item.invoice.customerTitle]) {
510
+ return {
511
+ ...acc,
512
+ [item.invoice.customerTitle]: item[filterField],
513
+ };
514
+ }
515
+ return {
516
+ ...acc,
517
+ [item.invoice.customerTitle]: new decimal_js_1.default(acc[item.invoice.customerTitle]).add(item[filterField]).toNumber(),
518
+ };
519
+ }, {});
520
+ const customerItemsTotal = Object
521
+ .values(customerItemsRaw)
522
+ .reduce((acc, itemValue) => new decimal_js_1.default(acc).add(itemValue).toNumber(), 0);
523
+ const findConcentration = (customerTitle) => {
524
+ const foundItem = items.find((item) => item.invoice.customerTitle === customerTitle);
525
+ if (!foundItem) {
526
+ return 0;
527
+ }
528
+ return new decimal_js_1.default(foundItem.customerConcentration).mul(100).toDP(2).toNumber();
529
+ };
530
+ const customerItems = Object.entries(customerItemsRaw).map(([customerTitle, customerAmount]) => {
531
+ return {
532
+ customerTitle,
533
+ customerConcentration: itemsFilter === 'deduction' ? findConcentration(customerTitle) : null,
534
+ customerAmount,
535
+ };
536
+ });
537
+ const customerItemsWithTotal = customerItems.length > 0
538
+ ? [...customerItems, { customerTitle: 'TOTAL', customerConcentration: null, customerAmount: customerItemsTotal }]
539
+ : customerItems;
540
+ return { itemsFilter: filterField, invoiceItems, customerItems: customerItemsWithTotal };
541
+ }
542
+ async foundOrCreateAvailability(bbcDateId, paginatorOptions, user) {
543
+ const foundAvailability = await this.findCalculatedAvailability(bbcDateId);
544
+ if (foundAvailability) {
545
+ foundAvailability.items = await this.getAvailabilityItems(foundAvailability._id.toString(), paginatorOptions);
546
+ const uniqueCustomers = await this.getUniqueCustomers(foundAvailability._id.toString());
547
+ const bbcDateIdID = new mongoose_1.default.Types.ObjectId(bbcDateId);
548
+ foundAvailability.uniqueCustomers = uniqueCustomers.map((c) => c.customerTitle);
549
+ foundAvailability.totalItems = await this.collateralsService.countCollateralDocsByBBC([bbcDateId], collaterals_enum_1.ECollaterals.RECEIVABLE);
550
+ if (foundAvailability.useManualInputs) {
551
+ const { summary: manualSummary } = await this.manualEntryService.getReceivableManualSummary(bbcDateId);
552
+ foundAvailability.summary = manualSummary;
553
+ }
554
+ else {
555
+ foundAvailability.summary = foundAvailability.summary ? foundAvailability.summary : {};
556
+ const reserves = await (0, reserve_db_1.getReserveDocs)([bbcDateIdID]);
557
+ const ARReserves = reserves.length ? reserves[0].summary?.RECEIVABLES?.amount : 0;
558
+ foundAvailability.summary.ARReserves = ARReserves;
559
+ foundAvailability.summary.ARAvailability = new decimal_js_1.default(foundAvailability.summary.ARAvailability || 0).sub(ARReserves).toNumber();
560
+ }
561
+ return foundAvailability;
562
+ }
563
+ return await this.calculateAvailability(bbcDateId, paginatorOptions, user);
564
+ }
565
+ async findCalculatedAvailability(bbcDateId) {
566
+ const calculatedAvailability = await ReceivableAvailability_model_1.ReceivableAvailabilityModel.aggregate([
567
+ {
568
+ $match: {
569
+ 'bbcDateId': new mongoose_1.default.Types.ObjectId(bbcDateId),
570
+ },
571
+ },
572
+ {
573
+ $lookup: {
574
+ from: _models_1.MODEL_NAMES.BBCDates,
575
+ localField: 'bbcDateId',
576
+ foreignField: '_id',
577
+ as: 'bbc',
578
+ },
579
+ },
580
+ {
581
+ $unwind: {
582
+ path: '$bbc',
583
+ },
584
+ },
585
+ {
586
+ $addFields: {
587
+ 'bbcDate': '$bbc.bbcDate',
588
+ },
589
+ },
590
+ {
591
+ $lookup: {
592
+ from: 'users',
593
+ localField: 'userCalculatedId',
594
+ foreignField: '_id',
595
+ as: 'userCalculated',
596
+ },
597
+ },
598
+ {
599
+ $unwind: {
600
+ path: '$userCalculated',
601
+ preserveNullAndEmptyArrays: true,
602
+ },
603
+ },
604
+ {
605
+ $group: {
606
+ '_id': '$_id',
607
+ 'bbcDate': { $first: '$bbcDate' },
608
+ 'bbcDateId': { $first: '$bbcDateId' },
609
+ 'userCalculated': { $first: '$userCalculated' },
610
+ 'calculatedAt': { $first: '$calculatedAt' },
611
+ 'actual': { $first: '$actual' },
612
+ 'settings': { $first: '$settings' },
613
+ 'summary': { $first: '$summary' },
614
+ 'useManualInputs': { $first: '$useManualInputs' },
615
+ },
616
+ },
617
+ ]);
618
+ if (calculatedAvailability.length) {
619
+ return (0, mongo_helper_1.normalizeObject)(calculatedAvailability[0], '_id', '_id');
620
+ }
621
+ return null;
622
+ }
623
+ async saveCalculatedAvailabilitySettings(bbcDateId, settings) {
624
+ await ReceivableAvailability_model_1.ReceivableAvailabilityModel.findOneAndUpdate({ bbcDateId: bbcDateId }, {
625
+ actual: false,
626
+ settings: settings,
627
+ }, { upsert: true });
628
+ }
629
+ async saveLimitSettings(bbcDateId, limitSettings) {
630
+ const availability = await this.findCalculatedAvailability(bbcDateId);
631
+ const updatedSettings = { ...availability.settings, ...limitSettings };
632
+ await this.saveCalculatedAvailabilitySettings(bbcDateId, updatedSettings);
633
+ }
634
+ async calculateAvailabilitySummary(newCalculation) {
635
+ const summaryFieldsOpt = {
636
+ invoiceAmount: {
637
+ fieldPath: 'invoiceAmount',
638
+ replacedHeader: 'invoiceAmount',
639
+ },
640
+ excludedCustomerDeduction: {
641
+ fieldPath: 'excludedCustomerDeduction',
642
+ reverse: true,
643
+ },
644
+ creditNotes: {
645
+ fieldPath: 'creditNotes',
646
+ reverse: true,
647
+ },
648
+ grossAR: {
649
+ fieldPath: 'grossAR',
650
+ },
651
+ permittedOver: {
652
+ fieldPath: 'permittedOver',
653
+ reverse: true,
654
+ },
655
+ preliminaryEligibleBalance: {
656
+ fieldPath: 'preliminaryEligibleBalance',
657
+ },
658
+ creditNotesNegative: {
659
+ fieldPath: 'creditNotes',
660
+ },
661
+ deduction: {
662
+ fieldPath: 'deduction',
663
+ reverse: true,
664
+ },
665
+ partiallyPaidDeduction: {
666
+ fieldPath: 'partiallyPaidDeduction',
667
+ reverse: true,
668
+ },
669
+ contraDeduction: {
670
+ fieldPath: 'contraDeduction',
671
+ reverse: true,
672
+ },
673
+ crossAgeDeduction: {
674
+ fieldPath: 'crossAgeDeduction',
675
+ reverse: true,
676
+ },
677
+ balanceAfterCrossAge: {
678
+ fieldPath: 'balanceAfterCrossAge',
679
+ },
680
+ waivedDeduction: {
681
+ fieldPath: 'waivedDeduction',
682
+ },
683
+ finalResult: {
684
+ fieldPath: 'finalResult',
685
+ },
686
+ insuredComponent: {
687
+ fieldPath: 'insuredComponent',
688
+ },
689
+ uninsuredComponent: {
690
+ fieldPath: 'uninsuredComponent',
691
+ },
692
+ insuredAvailability: {
693
+ fieldPath: 'insuredAvailability',
694
+ },
695
+ uninsuredAvailability: {
696
+ fieldPath: 'uninsuredAvailability',
697
+ },
698
+ ARAvailability: {
699
+ fieldPath: 'ARAvailability',
700
+ },
701
+ };
702
+ const summary = Object.entries(summaryFieldsOpt).reduce((acc, [field, desc]) => {
703
+ const total = newCalculation.items.reduce((fieldTotal, customer) => {
704
+ if (desc.fieldPath === 'insuredAvailability') {
705
+ return new decimal_js_1.default(customer.insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate).add(fieldTotal).toDP(2).toNumber();
706
+ }
707
+ if (desc.fieldPath === 'uninsuredAvailability') {
708
+ return new decimal_js_1.default(customer.uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate).add(fieldTotal).toDP(2).toNumber();
709
+ }
710
+ if (desc.fieldPath === 'ARAvailability') {
711
+ const insuredPart = new decimal_js_1.default(customer.insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate);
712
+ const uninsuredPart = new decimal_js_1.default(customer.uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate);
713
+ return new decimal_js_1.default(fieldTotal).add(insuredPart).add(uninsuredPart).toDP(2).toNumber();
714
+ }
715
+ if (lodash_1.default.get(customer, desc.fieldPath)) {
716
+ return new decimal_js_1.default(fieldTotal).add(lodash_1.default.get(customer, desc.fieldPath)).toDP(2).toNumber();
717
+ }
718
+ return fieldTotal;
719
+ }, 0);
720
+ return { ...acc, [desc.replacedHeader ?? field]: desc.reverse ? total * -1 : total };
721
+ }, {});
722
+ // TODO quick solution for creditNotesNegative
723
+ summary.balanceAfterCrossAge = summary.balanceAfterCrossAge + summary.creditNotesNegative;
724
+ const insuredComponent = summary.insuredComponent === 0 ? 0 : summary.insuredComponent;
725
+ const uninsuredComponent = new decimal_js_1.default(summary.finalResult).sub(insuredComponent).toNumber();
726
+ summary.insuredComponent = insuredComponent;
727
+ summary.uninsuredComponent = uninsuredComponent;
728
+ const insuredPart = new decimal_js_1.default(insuredComponent).mul(newCalculation.settings.insuredARAdvanceRate);
729
+ const uninsuredPart = new decimal_js_1.default(uninsuredComponent).mul(newCalculation.settings.uninsuredARAdvanceRate);
730
+ summary.ARAvailability = new decimal_js_1.default(insuredPart).add(uninsuredPart).toDP(2).toNumber();
731
+ return summary;
732
+ }
733
+ async saveCalculatedAvailability(newCalculation) {
734
+ const savedAvailability = await ReceivableAvailability_model_1.ReceivableAvailabilityModel.findOneAndReplace({ bbcDateId: newCalculation.bbcDateId }, newCalculation, { upsert: true, new: true });
735
+ await ReceivableAvailabilityItem_model_1.ReceivableAvailabilityItemModel.deleteMany({ availabilityId: savedAvailability._id });
736
+ const orderedItems = newCalculation.items.map((item, order) => ({ ...item, order }));
737
+ await Promise.all(orderedItems.map(async (item) => {
738
+ const newCollateralItem = new ReceivableAvailabilityItem_model_1.ReceivableAvailabilityItemModel({
739
+ availabilityId: savedAvailability._id,
740
+ order: item.order,
741
+ ...item,
742
+ });
743
+ try {
744
+ await newCollateralItem.save();
745
+ }
746
+ catch (e) {
747
+ console.error(e);
748
+ }
749
+ }));
750
+ }
751
+ async getCalculationSettings(bbcDateId) {
752
+ const availability = await ReceivableAvailability_model_1.ReceivableAvailabilityModel.findOne({ bbcDateId: bbcDateId }).lean();
753
+ if (!availability || !availability.settings || lodash_1.default.isEmpty(availability.settings)) {
754
+ return {
755
+ standardARDays: 90,
756
+ customerConcentrationLimit: 0.2,
757
+ crossAgeLimit: 0.25,
758
+ contraProvision: 1.25,
759
+ paidDays: 180,
760
+ insuredARAdvanceRate: 0,
761
+ uninsuredARAdvanceRate: 0,
762
+ excludeFromTotal: true,
763
+ waiveInsuredCustomers: true,
764
+ excludedCustomers: [],
765
+ insuredCustomers: [],
766
+ permittedConcentration: [],
767
+ permittedExtendedARDays: [],
768
+ };
769
+ }
770
+ return availability.settings;
771
+ }
772
+ async getAllSettings(bbcDateId = null) {
773
+ const selectedAvailability = await ReceivableAvailability_model_1.ReceivableAvailabilityModel.findOne({ bbcDateId: bbcDateId }).lean();
774
+ const availabilities = await ReceivableAvailability_model_1.ReceivableAvailabilityModel.aggregate([
775
+ {
776
+ '$match': {
777
+ '$and': [
778
+ { 'borrowerId': new mongoose_1.default.Types.ObjectId(selectedAvailability.borrowerId) },
779
+ { 'bbcDateId': { '$ne': new mongoose_1.default.Types.ObjectId(bbcDateId) } },
780
+ ],
781
+ },
782
+ },
783
+ ]);
784
+ return availabilities.reduce((acc, a) => {
785
+ const settings = Object.values(ReceivableAvailability_model_1.ELimitSettings).reduce((accSettings, limitSettings) => {
786
+ return { ...accSettings, [limitSettings]: a.settings[limitSettings] };
787
+ }, {});
788
+ return { ...acc, [a.bbcDateId.toString()]: settings };
789
+ }, {});
790
+ }
791
+ async getAvailabilityItems(availabilityId, paginatorOptions) {
792
+ return ReceivableAvailabilityItem_model_1.ReceivableAvailabilityItemModel.aggregate([
793
+ {
794
+ $match: {
795
+ 'availabilityId': new mongoose_1.default.Types.ObjectId(availabilityId),
796
+ },
797
+ },
798
+ {
799
+ $sort: {
800
+ 'order': 1,
801
+ },
802
+ },
803
+ ...(0, collaterals_db_1.ITEMS_PAGINATION)(paginatorOptions),
804
+ {
805
+ $lookup: {
806
+ from: _models_1.MODEL_NAMES.receivableItems,
807
+ localField: 'invoiceId',
808
+ foreignField: '_id',
809
+ as: 'invoice',
810
+ },
811
+ },
812
+ {
813
+ $unwind: {
814
+ path: '$invoice',
815
+ },
816
+ },
817
+ ]);
818
+ }
819
+ async getUniqueCustomers(availabilityId) {
820
+ return ReceivableAvailabilityItem_model_1.ReceivableAvailabilityItemModel.aggregate([
821
+ {
822
+ $match: {
823
+ 'availabilityId': new mongoose_1.default.Types.ObjectId(availabilityId),
824
+ },
825
+ },
826
+ {
827
+ $lookup: {
828
+ from: _models_1.MODEL_NAMES.receivableItems,
829
+ localField: 'invoiceId',
830
+ foreignField: '_id',
831
+ as: 'invoice',
832
+ },
833
+ },
834
+ {
835
+ $unwind: {
836
+ path: '$invoice',
837
+ },
838
+ },
839
+ {
840
+ $group: {
841
+ '_id': null,
842
+ 'customerTitle': {
843
+ $addToSet: '$invoice.customerTitle',
844
+ },
845
+ },
846
+ },
847
+ {
848
+ $project: {
849
+ '_id': 0,
850
+ 'customerTitle': 1,
851
+ },
852
+ },
853
+ {
854
+ $unwind: {
855
+ path: '$customerTitle',
856
+ },
857
+ },
858
+ ]);
859
+ }
860
+ async getAllSummaries(bbcDateId, user) {
861
+ const equipment = await this.equipmentService.getEquipmentForBBC(bbcDateId);
862
+ const reserve = await this.reserveService.getReserve(bbcDateId);
863
+ const loanBalances = await this.loanTransactionsService.getLoanBalanceForBBCDateId(bbcDateId);
864
+ const collateralAdjustmentSummary = await this.collateralAdjustmentsService.getCollateralAdjustmentSummary(bbcDateId);
865
+ const inventoryAvailability = await this.inventoryAvailabilityService.getInventoryAvailability(bbcDateId, exports.START_PAGINATORS);
866
+ const receivableAvailability = await this.getAvailability(bbcDateId, { pageIndex: null, pageSize: null }, user);
867
+ const savedData = await this.signsService.getSavedData(bbcDateId);
868
+ if (!savedData.accruedStatement) {
869
+ savedData.accruedStatement = await this.loanStatementService.calculateAccruedStatementForBBC(bbcDateId);
870
+ }
871
+ return {
872
+ reserve: reserve.summary,
873
+ equipment: equipment.summary,
874
+ inventory: inventoryAvailability.summary,
875
+ receivable: receivableAvailability.summary,
876
+ loanBalances,
877
+ collateralAdjustment: { summary: collateralAdjustmentSummary },
878
+ manualInputs: {
879
+ receivableAvailability: receivableAvailability.useManualInputs,
880
+ inventoryAvailability: inventoryAvailability.useManualInputs,
881
+ },
882
+ ...savedData,
883
+ };
884
+ }
885
+ async isBBCEditable(bbcDateId) {
886
+ const availabilitiesSigns = await (0, availability_db_1.getAvailabilitySigns)([bbcDateId]);
887
+ if (!availabilitiesSigns.length) {
888
+ return true;
889
+ }
890
+ const aSign = availabilitiesSigns[0];
891
+ return aSign.signs.filter((s) => !s.revokedAt).length < aSign.requiredSigns;
892
+ }
893
+ async toggleManualReceivableAvailability(bbcDateId, useManualInputs) {
894
+ await (0, receivables_db_1.setReceivableManualInputsToggle)(bbcDateId, useManualInputs);
895
+ }
896
+ }
897
+ exports.AvailabilityService = AvailabilityService;