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.
- package/classes/bank-transaction-item.d.ts +17 -0
- package/classes/bank-transaction-item.js +64 -0
- package/classes/bank-transaction-item.ts +66 -0
- package/classes/bank-uploaded-transaction.d.ts +17 -0
- package/classes/bank-uploaded-transaction.js +35 -0
- package/classes/bank-uploaded-transaction.ts +35 -0
- package/classes/inventory-item.d.ts +41 -0
- package/classes/inventory-item.js +44 -0
- package/classes/inventory-item.ts +63 -0
- package/classes/payable-account-item.d.ts +22 -0
- package/classes/payable-account-item.js +27 -0
- package/classes/payable-account-item.ts +35 -0
- package/classes/quickbook-item.d.ts +37 -0
- package/classes/quickbook-item.js +51 -0
- package/classes/quickbook-item.ts +59 -0
- package/classes/receivable-item.d.ts +26 -0
- package/classes/receivable-item.js +28 -0
- package/classes/receivable-item.ts +38 -0
- package/constants/date-formats.contsants.d.ts +1 -0
- package/constants/date-formats.contsants.js +4 -0
- package/constants/date-formats.contsants.ts +1 -0
- package/db/brokers.db.d.ts +185 -0
- package/db/brokers.db.js +35 -2
- package/db/brokers.db.ts +34 -1
- package/db/collateral-adjustments.db.d.ts +34 -0
- package/db/collateral-adjustments.db.js +52 -0
- package/db/collateral-adjustments.db.ts +54 -0
- package/db/collaterals.db.d.ts +1 -1
- package/db/equipment.db.d.ts +40 -0
- package/db/equipment.db.js +55 -0
- package/db/equipment.db.ts +56 -0
- package/db/financial-spreading.db.ts +2 -1
- package/db/groups.d.ts +5 -0
- package/db/groups.js +57 -0
- package/db/groups.ts +52 -0
- package/db/inventories.d.ts +91 -0
- package/db/inventories.js +449 -0
- package/db/inventories.ts +481 -0
- package/db/inventory-availability.d.ts +3 -0
- package/db/inventory-availability.js +103 -0
- package/db/inventory-availability.ts +113 -0
- package/db/new-summary.d.ts +31 -0
- package/db/new-summary.js +1295 -0
- package/db/new-summary.ts +1509 -0
- package/db/payable-accounts.d.ts +30 -0
- package/db/payable-accounts.js +55 -0
- package/db/payable-accounts.ts +50 -0
- package/db/reserve.db.d.ts +34 -0
- package/db/reserve.db.js +52 -0
- package/db/reserve.db.ts +48 -0
- package/db/uploads.db.d.ts +2 -0
- package/db/uploads.db.js +29 -0
- package/db/uploads.db.ts +24 -0
- package/helpers/main.helper.d.ts +31 -0
- package/helpers/main.helper.js +63 -0
- package/helpers/main.helper.ts +63 -0
- package/models/AccountPayableItem.model.d.ts +6 -6
- package/models/AllocatedBankTransaction.model.d.ts +54 -0
- package/models/AllocatedBankTransaction.model.js +70 -0
- package/models/AllocatedBankTransaction.model.ts +94 -0
- package/models/AllocatedData.model.d.ts +33 -0
- package/models/AllocatedData.model.js +19 -0
- package/models/AllocatedData.model.ts +24 -0
- package/models/BBCDate.model.d.ts +3 -3
- package/models/BBCSheet.model.d.ts +3 -3
- package/models/Banks.model.d.ts +3 -3
- package/models/Borrower.model.d.ts +3 -3
- package/models/BorrowerData.model.d.ts +3 -3
- package/models/BorrowerDataInsurance.model.d.ts +3 -3
- package/models/BorrowerDataTerm.model.d.ts +3 -3
- package/models/BorrowerSummary.model.js +1 -1
- package/models/BorrowerSummary.model.ts +1 -1
- package/models/CalandarDay.model.d.ts +40 -0
- package/models/CalandarDay.model.js +47 -0
- package/models/CalandarDay.model.ts +61 -0
- package/models/CashAllocationProduct.model.d.ts +119 -0
- package/models/CashAllocationProduct.model.js +102 -0
- package/models/CashAllocationProduct.model.ts +112 -0
- package/models/CashAllocationReference.model.d.ts +37 -0
- package/models/CashAllocationReference.model.js +27 -0
- package/models/CashAllocationReference.model.ts +40 -0
- package/models/CollateralAdjustment.model.d.ts +51 -0
- package/models/CollateralAdjustment.model.js +61 -0
- package/models/CollateralAdjustment.model.ts +98 -0
- package/models/Company.model.d.ts +35 -0
- package/models/Company.model.js +18 -0
- package/models/Company.model.ts +29 -0
- package/models/CustomerAPGroup.model.d.ts +32 -0
- package/models/CustomerAPGroup.model.js +24 -0
- package/models/CustomerAPGroup.model.ts +31 -0
- package/models/Equipment.model.d.ts +53 -0
- package/models/Equipment.model.js +140 -0
- package/models/Equipment.model.ts +172 -0
- package/models/FinancialCompliance.model.d.ts +39 -0
- package/models/FinancialCompliance.model.js +64 -0
- package/models/FinancialCompliance.model.ts +78 -0
- package/models/FinancialComplianceBorrower.model.d.ts +58 -0
- package/models/FinancialComplianceBorrower.model.js +82 -0
- package/models/FinancialComplianceBorrower.model.ts +118 -0
- package/models/FinancialIndexes.model.d.ts +36 -0
- package/models/FinancialIndexes.model.js +27 -0
- package/models/FinancialIndexes.model.ts +37 -0
- package/models/Inventory.model.d.ts +18 -18
- package/models/InventoryAvailability.model.d.ts +21 -21
- package/models/InventoryAvailabilityItem.model.d.ts +6 -6
- package/models/InventoryItem.model.d.ts +24 -24
- package/models/InventoryManualEntry.model.d.ts +9 -9
- package/models/InventorySeasonalRates.model.d.ts +3 -3
- package/models/LoanBroker.model.d.ts +3 -3
- package/models/LoanCharges.model.d.ts +12 -12
- package/models/LoanProducts.model.d.ts +9 -9
- package/models/LoanStatementStatus.model.d.ts +35 -0
- package/models/LoanStatementStatus.model.js +34 -0
- package/models/LoanStatementStatus.model.ts +45 -0
- package/models/LoanStatementTransaction.model.d.ts +9 -9
- package/models/LoanTransactionFile.model.d.ts +41 -0
- package/models/LoanTransactionFile.model.js +44 -0
- package/models/LoanTransactionFile.model.ts +61 -0
- package/models/MappedGroup.model.d.ts +37 -0
- package/models/MappedGroup.model.js +33 -0
- package/models/MappedGroup.model.ts +46 -0
- package/models/MonthEndData.Model.d.ts +41 -0
- package/models/MonthEndData.Model.js +42 -0
- package/models/MonthEndData.Model.ts +53 -0
- package/models/OrganizationEmails.model.d.ts +44 -0
- package/models/OrganizationEmails.model.js +40 -0
- package/models/OrganizationEmails.model.ts +54 -0
- package/models/ProductBroker.model.d.ts +9 -9
- package/models/QuickbooksAccount.model.d.ts +39 -0
- package/models/QuickbooksAccount.model.js +43 -0
- package/models/QuickbooksAccount.model.ts +57 -0
- package/models/Receivable.model.d.ts +12 -12
- package/models/ReceivableAvailability.model.d.ts +54 -54
- package/models/ReceivableAvailabilityItem.model.d.ts +57 -57
- package/models/ReceivableItem.model.d.ts +6 -6
- package/models/Reserve.model.d.ts +51 -0
- package/models/Reserve.model.js +96 -0
- package/models/Reserve.model.ts +125 -0
- package/models/TermLoan.model.d.ts +3 -3
- package/models/TermLoanCalculated.model.d.ts +6 -6
- package/models/TransactionAttachedFile.Model.d.ts +35 -0
- package/models/TransactionAttachedFile.Model.js +37 -0
- package/models/TransactionAttachedFile.Model.ts +48 -0
- package/models/UploadedBankTransaction.model.d.ts +56 -0
- package/models/UploadedBankTransaction.model.js +78 -0
- package/models/UploadedBankTransaction.model.ts +110 -0
- package/models/UploadedData.model.d.ts +36 -0
- package/models/UploadedData.model.js +23 -0
- package/models/UploadedData.model.ts +35 -0
- package/models/UploadedFile.model.d.ts +40 -0
- package/models/UploadedFile.model.js +41 -0
- package/models/UploadedFile.model.ts +57 -0
- package/models/UploadedSheet.model.d.ts +46 -0
- package/models/UploadedSheet.model.js +27 -0
- package/models/UploadedSheet.model.ts +51 -0
- package/package.json +10 -1
- package/repositories/globals.repository.d.ts +8 -0
- package/repositories/globals.repository.js +24 -0
- package/repositories/globals.repository.ts +21 -0
- package/services/attached-files.service.d.ts +57 -0
- package/services/attached-files.service.js +103 -0
- package/services/attached-files.service.ts +123 -0
- package/services/availability.service.d.ts +77 -0
- package/services/availability.service.js +897 -0
- package/services/availability.service.ts +1034 -0
- package/services/bank-uploaded-transactions.service.d.ts +33 -0
- package/services/bank-uploaded-transactions.service.js +430 -0
- package/services/bank-uploaded-transactions.service.ts +475 -0
- package/services/banks.service.d.ts +36 -0
- package/services/banks.service.js +91 -0
- package/services/banks.service.ts +95 -0
- package/services/borrower-summary.service.d.ts +35 -0
- package/services/borrower-summary.service.js +310 -0
- package/services/borrower-summary.service.ts +334 -0
- package/services/borrowers.service.d.ts +103 -0
- package/services/borrowers.service.js +268 -0
- package/services/borrowers.service.ts +302 -0
- package/services/brokers.service.d.ts +212 -0
- package/services/brokers.service.js +160 -0
- package/services/brokers.service.ts +200 -0
- package/services/calendar.service.d.ts +53 -0
- package/services/calendar.service.js +108 -0
- package/services/calendar.service.ts +128 -0
- package/services/cash-allocation.service.d.ts +40 -0
- package/services/cash-allocation.service.js +92 -0
- package/services/cash-allocation.service.ts +105 -0
- package/services/collateral-adjustments.service.d.ts +38 -0
- package/services/collateral-adjustments.service.js +82 -0
- package/services/collateral-adjustments.service.ts +95 -0
- package/services/collaterals.service.d.ts +69 -0
- package/services/collaterals.service.js +279 -0
- package/services/collaterals.service.ts +319 -0
- package/services/companies.service.d.ts +5 -0
- package/services/companies.service.js +21 -0
- package/services/companies.service.ts +23 -0
- package/services/compliance-borrowers.service.d.ts +152 -0
- package/services/compliance-borrowers.service.js +569 -0
- package/services/compliance-borrowers.service.ts +617 -0
- package/services/equipment.service.d.ts +42 -0
- package/services/equipment.service.js +120 -0
- package/services/equipment.service.ts +149 -0
- package/services/file-manager.service.d.ts +44 -0
- package/services/file-manager.service.js +120 -0
- package/services/file-manager.service.ts +146 -0
- package/services/financial-compliance.service.d.ts +58 -0
- package/services/financial-compliance.service.js +281 -0
- package/services/financial-compliance.service.ts +309 -0
- package/services/financial-indexes.service.d.ts +20 -0
- package/services/financial-indexes.service.js +241 -0
- package/services/financial-indexes.service.ts +257 -0
- package/services/financial-spreading.service.d.ts +74 -0
- package/services/financial-spreading.service.js +450 -0
- package/services/financial-spreading.service.ts +517 -0
- package/services/globals.service.d.ts +5 -0
- package/services/globals.service.js +11 -0
- package/services/globals.service.ts +8 -0
- package/services/groups.service.d.ts +39 -0
- package/services/groups.service.js +65 -0
- package/services/groups.service.ts +64 -0
- package/services/inventory-availability.service.d.ts +13 -0
- package/services/inventory-availability.service.js +170 -0
- package/services/inventory-availability.service.ts +187 -0
- package/services/inventory.service.d.ts +118 -0
- package/services/inventory.service.js +239 -0
- package/services/inventory.service.ts +276 -0
- package/services/loan-charges.service.d.ts +83 -0
- package/services/loan-charges.service.js +343 -0
- package/services/loan-charges.service.ts +396 -0
- package/services/loan-payments.service.d.ts +94 -0
- package/services/loan-payments.service.js +485 -0
- package/services/loan-payments.service.ts +541 -0
- package/services/loan-products.service.d.ts +12 -0
- package/services/loan-products.service.js +55 -0
- package/services/loan-products.service.ts +58 -0
- package/services/loan-statement-balance.service.d.ts +16 -0
- package/services/loan-statement-balance.service.js +106 -0
- package/services/loan-statement-balance.service.ts +113 -0
- package/services/loan-statement-effects.service.d.ts +8 -0
- package/services/loan-statement-effects.service.js +42 -0
- package/services/loan-statement-effects.service.ts +41 -0
- package/services/loan-statement-status.service.d.ts +208 -0
- package/services/loan-statement-status.service.js +159 -0
- package/services/loan-statement-status.service.ts +177 -0
- package/services/loan-statement.service.d.ts +186 -0
- package/services/loan-statement.service.js +935 -0
- package/services/loan-statement.service.ts +1040 -0
- package/services/loan-transactions.service.d.ts +169 -0
- package/services/loan-transactions.service.js +941 -0
- package/services/loan-transactions.service.ts +1042 -0
- package/services/lock.service.d.ts +6 -0
- package/services/lock.service.js +45 -0
- package/services/lock.service.ts +45 -0
- package/services/manual-entry.service.d.ts +20 -0
- package/services/manual-entry.service.js +186 -0
- package/services/manual-entry.service.ts +201 -0
- package/services/month-end-data.service.d.ts +34 -0
- package/services/month-end-data.service.js +30 -0
- package/services/month-end-data.service.ts +35 -0
- package/services/nodemailer.service.d.ts +96 -0
- package/services/nodemailer.service.js +689 -0
- package/services/nodemailer.service.ts +774 -0
- package/services/organization-emails.service.d.ts +31 -0
- package/services/organization-emails.service.js +10 -0
- package/services/organization-emails.service.ts +7 -0
- package/services/organizations.service.d.ts +34 -0
- package/services/organizations.service.js +74 -0
- package/services/organizations.service.ts +84 -0
- package/services/pdf.service.d.ts +61 -0
- package/services/pdf.service.js +547 -0
- package/services/pdf.service.ts +642 -0
- package/services/quickbooks.service.d.ts +99 -0
- package/services/quickbooks.service.js +640 -0
- package/services/quickbooks.service.ts +734 -0
- package/services/reports/investor-summary.service.d.ts +28 -0
- package/services/reports/investor-summary.service.js +136 -0
- package/services/reports/investor-summary.service.ts +159 -0
- package/services/reports.service.d.ts +126 -0
- package/services/reports.service.js +584 -0
- package/services/reports.service.ts +702 -0
- package/services/reserve.service.d.ts +37 -0
- package/services/reserve.service.js +76 -0
- package/services/reserve.service.ts +79 -0
- package/services/sentry.service.d.ts +11 -0
- package/services/sentry.service.js +49 -0
- package/services/sentry.service.ts +33 -0
- package/services/signs.service.d.ts +69 -0
- package/services/signs.service.js +230 -0
- package/services/signs.service.ts +260 -0
- package/services/term-loan.service.d.ts +30 -0
- package/services/term-loan.service.js +614 -0
- package/services/term-loan.service.ts +696 -0
- package/services/uploads.service.d.ts +134 -0
- package/services/uploads.service.js +587 -0
- package/services/uploads.service.ts +643 -0
- package/services/user-logs.service.d.ts +23 -0
- package/services/user-logs.service.js +160 -0
- package/services/user-logs.service.ts +177 -0
- package/services/users.service.d.ts +4 -4
- package/services/yield.service.d.ts +46 -0
- package/services/yield.service.js +42 -12
- package/services/yield.service.ts +38 -8
- package/tsconfig.json +5 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,1295 @@
|
|
|
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.NewSummaryExcel = void 0;
|
|
7
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
8
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const common_helper_1 = require("../helpers/common.helper");
|
|
11
|
+
const loan_transactions_db_1 = require("./loan-transactions.db");
|
|
12
|
+
const Borrower_model_1 = require("../models/Borrower.model");
|
|
13
|
+
const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
14
|
+
const loan_products_db_1 = require("./loan-products.db");
|
|
15
|
+
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
|
|
16
|
+
const bbcDates_db_1 = require("./bbcDates.db");
|
|
17
|
+
const reserve_types_enum_1 = require("../enums/reserve-types.enum");
|
|
18
|
+
const ComplianceItem_model_1 = require("../models/ComplianceItem.model");
|
|
19
|
+
const LoanCharges_model_1 = require("../models/LoanCharges.model");
|
|
20
|
+
const BorrowerCompliance_model_1 = require("../models/BorrowerCompliance.model");
|
|
21
|
+
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
|
|
22
|
+
const loan_types_enum_1 = require("../enums/loan-types.enum");
|
|
23
|
+
const equipment_types_enum_1 = require("../enums/equipment-types.enum");
|
|
24
|
+
const BorrowerSummary_model_1 = require("../models/BorrowerSummary.model");
|
|
25
|
+
const FinancialSpreadingSheet_model_1 = require("../models/FinancialSpreadingSheet.model");
|
|
26
|
+
const ProspectIndustry_model_1 = require("../models/ProspectIndustry.model");
|
|
27
|
+
const financial_spreading_service_1 = require("../services/financial-spreading.service");
|
|
28
|
+
const yield_service_1 = require("../services/yield.service");
|
|
29
|
+
const MonthEndData_Model_1 = require("../models/MonthEndData.Model");
|
|
30
|
+
const styles = {
|
|
31
|
+
blackOnWhiteWithTopBorder: {
|
|
32
|
+
fill: {
|
|
33
|
+
type: 'pattern',
|
|
34
|
+
pattern: 'solid',
|
|
35
|
+
fgColor: { argb: 'FFFFFFFF' },
|
|
36
|
+
},
|
|
37
|
+
font: {
|
|
38
|
+
bold: true,
|
|
39
|
+
color: { argb: 'FF000000' },
|
|
40
|
+
},
|
|
41
|
+
border: {
|
|
42
|
+
top: { style: 'thin' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
blackOnWhiteWithBottomBorder: {
|
|
46
|
+
fill: {
|
|
47
|
+
type: 'pattern',
|
|
48
|
+
pattern: 'solid',
|
|
49
|
+
fgColor: { argb: 'FFFFFFFF' },
|
|
50
|
+
},
|
|
51
|
+
font: {
|
|
52
|
+
bold: true,
|
|
53
|
+
color: { argb: 'FF000000' },
|
|
54
|
+
},
|
|
55
|
+
border: {
|
|
56
|
+
bottom: { style: 'thin' },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
whiteOnBlack: {
|
|
60
|
+
fill: {
|
|
61
|
+
type: 'pattern',
|
|
62
|
+
pattern: 'solid',
|
|
63
|
+
fgColor: { argb: 'FF000000' },
|
|
64
|
+
},
|
|
65
|
+
font: {
|
|
66
|
+
bold: true,
|
|
67
|
+
color: { argb: 'FFFFFFFF' },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
whiteOnGray: {
|
|
71
|
+
fill: {
|
|
72
|
+
type: 'pattern',
|
|
73
|
+
pattern: 'solid',
|
|
74
|
+
fgColor: { argb: 'FF7F7F7F' },
|
|
75
|
+
},
|
|
76
|
+
font: {
|
|
77
|
+
bold: true,
|
|
78
|
+
color: { argb: 'FFFFFFFF' },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
const numberFormat = {
|
|
83
|
+
fullNumber: '#,##0.00',
|
|
84
|
+
shortNumber: '#,##0',
|
|
85
|
+
xNumber: '0.00"x"',
|
|
86
|
+
xNumber1DP: '0.0"x"',
|
|
87
|
+
thousands: '0.0,',
|
|
88
|
+
percent: '0.00%',
|
|
89
|
+
percent1DP: '0.0%',
|
|
90
|
+
};
|
|
91
|
+
const borrowerTitleLength = 12;
|
|
92
|
+
const dateFormat = 'MM-DD-YYYY';
|
|
93
|
+
const productsDataMap = new Map();
|
|
94
|
+
const borrowersMap = new Map();
|
|
95
|
+
const productsMap = new Map();
|
|
96
|
+
const chargesMap = new Map();
|
|
97
|
+
const complianceBorrowersMap = new Map();
|
|
98
|
+
const allBorrowerSummaryRowFields = {
|
|
99
|
+
commitmentAmount: {
|
|
100
|
+
t: 'n',
|
|
101
|
+
z: numberFormat.thousands,
|
|
102
|
+
},
|
|
103
|
+
endingBalance: {
|
|
104
|
+
t: 'n',
|
|
105
|
+
z: numberFormat.thousands,
|
|
106
|
+
},
|
|
107
|
+
endingParticipationBalance: {
|
|
108
|
+
t: 'n',
|
|
109
|
+
z: numberFormat.thousands,
|
|
110
|
+
},
|
|
111
|
+
netExposure: {
|
|
112
|
+
t: 'n',
|
|
113
|
+
z: numberFormat.thousands,
|
|
114
|
+
},
|
|
115
|
+
averageBalanceSinceInception: {
|
|
116
|
+
t: 'n',
|
|
117
|
+
z: numberFormat.thousands,
|
|
118
|
+
},
|
|
119
|
+
totalIncomeReceived: {
|
|
120
|
+
t: 'n',
|
|
121
|
+
z: numberFormat.thousands,
|
|
122
|
+
},
|
|
123
|
+
loanLifeIRR: {
|
|
124
|
+
t: 'n',
|
|
125
|
+
z: numberFormat.percent,
|
|
126
|
+
},
|
|
127
|
+
MOIC: {
|
|
128
|
+
t: 'n',
|
|
129
|
+
z: numberFormat.xNumber,
|
|
130
|
+
},
|
|
131
|
+
participantBalance: {
|
|
132
|
+
t: 'n',
|
|
133
|
+
z: numberFormat.thousands,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const borrowerSummaryRowFields = {
|
|
137
|
+
title: {
|
|
138
|
+
t: 's',
|
|
139
|
+
},
|
|
140
|
+
'grossValue': {
|
|
141
|
+
t: 'n',
|
|
142
|
+
z: numberFormat.thousands,
|
|
143
|
+
},
|
|
144
|
+
'ineligible': {
|
|
145
|
+
t: 'n',
|
|
146
|
+
z: numberFormat.thousands,
|
|
147
|
+
},
|
|
148
|
+
'netValue': {
|
|
149
|
+
t: 'n',
|
|
150
|
+
z: numberFormat.thousands,
|
|
151
|
+
},
|
|
152
|
+
'advanceRate': {
|
|
153
|
+
t: 'n',
|
|
154
|
+
z: numberFormat.percent1DP,
|
|
155
|
+
},
|
|
156
|
+
'availability': {
|
|
157
|
+
t: 'n',
|
|
158
|
+
z: numberFormat.thousands,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
const emptyRow = [{ v: '', t: 's' }];
|
|
162
|
+
const negativeCell = { v: 'NA', t: 's', s: { alignment: { horizontal: 'right' } } };
|
|
163
|
+
const rightAlign = {
|
|
164
|
+
alignment: { horizontal: 'right' },
|
|
165
|
+
};
|
|
166
|
+
const styleRowData = (rows, style) => {
|
|
167
|
+
return rows.map((row) => {
|
|
168
|
+
return Object.entries(row).map(([key, v]) => {
|
|
169
|
+
return {
|
|
170
|
+
v,
|
|
171
|
+
t: borrowerSummaryRowFields[key].t,
|
|
172
|
+
z: borrowerSummaryRowFields[key].z,
|
|
173
|
+
...(style ? { s: style } : {}),
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
class NewSummaryExcel {
|
|
179
|
+
availabilityService;
|
|
180
|
+
borrowerService;
|
|
181
|
+
borrowerSummaryService;
|
|
182
|
+
complianceBorrowersService;
|
|
183
|
+
financialSpreadingService;
|
|
184
|
+
loanChargesService;
|
|
185
|
+
loanTransactionsService;
|
|
186
|
+
signsService;
|
|
187
|
+
yieldService;
|
|
188
|
+
monthEndDataService;
|
|
189
|
+
constructor(availabilityService, borrowerService, borrowerSummaryService, complianceBorrowersService, financialSpreadingService, loanChargesService, loanTransactionsService, signsService, yieldService, monthEndDataService) {
|
|
190
|
+
this.availabilityService = availabilityService;
|
|
191
|
+
this.borrowerService = borrowerService;
|
|
192
|
+
this.borrowerSummaryService = borrowerSummaryService;
|
|
193
|
+
this.complianceBorrowersService = complianceBorrowersService;
|
|
194
|
+
this.financialSpreadingService = financialSpreadingService;
|
|
195
|
+
this.loanChargesService = loanChargesService;
|
|
196
|
+
this.loanTransactionsService = loanTransactionsService;
|
|
197
|
+
this.signsService = signsService;
|
|
198
|
+
this.yieldService = yieldService;
|
|
199
|
+
this.monthEndDataService = monthEndDataService;
|
|
200
|
+
}
|
|
201
|
+
async getMainData(borrowerIds, start, end) {
|
|
202
|
+
const periodDays = 360;
|
|
203
|
+
const header = {
|
|
204
|
+
borrowerName: 'Client name',
|
|
205
|
+
productName: 'Product name',
|
|
206
|
+
maturityDate: 'Maturity date',
|
|
207
|
+
payOffDate: 'PayOff Date',
|
|
208
|
+
commitmentAmount: 'Commitment amount',
|
|
209
|
+
endingBalance: 'Ending Balance',
|
|
210
|
+
endingParticipationBalance: 'Ending Participation balance',
|
|
211
|
+
netExposure: 'Net Exposure',
|
|
212
|
+
lastSignedBBCDate: 'Last Signed BBC',
|
|
213
|
+
compliance: 'Compliance',
|
|
214
|
+
averageBalanceSinceInception: 'Average Balance Since Inception',
|
|
215
|
+
totalIncomeReceived: 'Total Income Received',
|
|
216
|
+
loanLifeIRR: 'Loan Life IRR',
|
|
217
|
+
MOIC: 'MOIC',
|
|
218
|
+
participantBalance: 'Participant Balance',
|
|
219
|
+
};
|
|
220
|
+
const headerKeys = Object.keys(header);
|
|
221
|
+
const allBorrowersSummary = [];
|
|
222
|
+
for (const borrowerId of borrowerIds) {
|
|
223
|
+
let periodEnd = end;
|
|
224
|
+
const borrower = borrowersMap.get(borrowerId);
|
|
225
|
+
if (!borrower) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const products = [...productsMap.values()].filter((product) => product.borrowerId.toString() === borrowerId);
|
|
229
|
+
const complianceBorrower = [...complianceBorrowersMap.values()].find((complianceBorrower) => complianceBorrower.borrower.toString() === borrowerId);
|
|
230
|
+
for (const product of products) {
|
|
231
|
+
if (product.deactivationDate) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if ((0, dayjs_1.default)(product.payoffDate).isBefore((0, dayjs_1.default)(periodEnd))) {
|
|
235
|
+
periodEnd = product.payoffDate;
|
|
236
|
+
}
|
|
237
|
+
const daysHeld = (0, dayjs_1.default)(periodEnd).diff((0, dayjs_1.default)(product.startDate), 'day');
|
|
238
|
+
const lastTransaction = await (0, loan_transactions_db_1.getLastTransactionForDate)(product._id.toString(), periodEnd);
|
|
239
|
+
const endingBalance = lastTransaction ? lastTransaction.balance : 0;
|
|
240
|
+
const endingParticipationBalance = product.isParticipant ? endingBalance : 0;
|
|
241
|
+
const netExposure = new decimal_js_1.default(endingBalance).sub(endingParticipationBalance).toNumber();
|
|
242
|
+
const lastSignedBBC = await this.signsService.getLatestSignedBBCDate(borrowerId, periodEnd);
|
|
243
|
+
const lastSignedBBCDate = lastSignedBBC ? (0, dayjs_1.default)(lastSignedBBC.bbcDate).format(dateFormat) : '';
|
|
244
|
+
const averageBalanceSinceInception = new decimal_js_1.default(await (0, loan_products_db_1.getAverageActualBalance)(product._id.toString(), {
|
|
245
|
+
start: (0, dayjs_1.default)(product.startDate),
|
|
246
|
+
end: (0, dayjs_1.default)(periodEnd),
|
|
247
|
+
})).toDP(2).toNumber();
|
|
248
|
+
const totalIncomeReceived = await this.getPaidAmount(product._id.toString());
|
|
249
|
+
const MOIC = averageBalanceSinceInception ? new decimal_js_1.default(totalIncomeReceived).add(Math.abs(averageBalanceSinceInception)).div(Math.abs(averageBalanceSinceInception)).toDP(2).toNumber() : 0;
|
|
250
|
+
const loanLifeIRRMul = daysHeld ? new decimal_js_1.default(periodDays).div(daysHeld).toNumber() : 0;
|
|
251
|
+
const loanLifeIRR = MOIC ? new decimal_js_1.default(MOIC).pow(loanLifeIRRMul).sub(1).toDP(4).toNumber() : 0;
|
|
252
|
+
const participantBalance = await this.loanChargesService.getParticipationBalance(product.code, end);
|
|
253
|
+
const newDataRow = {
|
|
254
|
+
borrowerName: borrower.name,
|
|
255
|
+
productName: product.name,
|
|
256
|
+
maturityDate: product.maturityDate ? (0, dayjs_1.default)(product.maturityDate).format(dateFormat) : '',
|
|
257
|
+
payOffDate: product.payoffDate ? (0, dayjs_1.default)(product.payoffDate).format(dateFormat) : '',
|
|
258
|
+
commitmentAmount: product.commitment,
|
|
259
|
+
endingBalance,
|
|
260
|
+
endingParticipationBalance,
|
|
261
|
+
netExposure,
|
|
262
|
+
lastSignedBBCDate,
|
|
263
|
+
compliance: complianceBorrower ? complianceBorrower.fundingStatus : '',
|
|
264
|
+
averageBalanceSinceInception,
|
|
265
|
+
totalIncomeReceived,
|
|
266
|
+
loanLifeIRR,
|
|
267
|
+
MOIC,
|
|
268
|
+
participantBalance
|
|
269
|
+
};
|
|
270
|
+
productsDataMap.set(product._id.toString(), newDataRow);
|
|
271
|
+
allBorrowersSummary.push(newDataRow);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const sortedData = lodash_1.default.orderBy(allBorrowersSummary, ['borrowerName', 'productName'], ['asc', 'asc']);
|
|
275
|
+
const headerAsArray = (0, common_helper_1.reorderObject)(header, headerKeys);
|
|
276
|
+
const dataAsArrays = sortedData.map((row) => (0, common_helper_1.reorderObject)(row, headerKeys));
|
|
277
|
+
const headerWithStyles = Object.values(headerAsArray).reduce((acc, header) => [...acc, {
|
|
278
|
+
v: header,
|
|
279
|
+
t: 's',
|
|
280
|
+
s: styles.whiteOnGray,
|
|
281
|
+
}], []);
|
|
282
|
+
const dataWithStyles = Object.values(dataAsArrays).map((dataRow) => {
|
|
283
|
+
return Object.entries(dataRow).map(([key, v]) => ({
|
|
284
|
+
v,
|
|
285
|
+
t: allBorrowerSummaryRowFields[key] ? allBorrowerSummaryRowFields[key].t : 's',
|
|
286
|
+
z: allBorrowerSummaryRowFields[key] ? allBorrowerSummaryRowFields[key].z : {},
|
|
287
|
+
}));
|
|
288
|
+
});
|
|
289
|
+
const reportPeriod = [
|
|
290
|
+
[{ v: 'Report period', t: 's' }],
|
|
291
|
+
[{ v: 'From', t: 's' }, { v: (0, dayjs_1.default)(start).utcOffset(0).format(dateFormat), t: 's' }],
|
|
292
|
+
[{ v: 'To', t: 's' }, { v: (0, dayjs_1.default)(end).utcOffset(0).format(dateFormat), t: 's' }],
|
|
293
|
+
];
|
|
294
|
+
return { report: [...reportPeriod, emptyRow, headerWithStyles, ...dataWithStyles] };
|
|
295
|
+
}
|
|
296
|
+
;
|
|
297
|
+
async getAllBorrowersSummary(borrowerIds, start, end) {
|
|
298
|
+
const borrowerSummary = {};
|
|
299
|
+
for (const borrowerId of borrowerIds) {
|
|
300
|
+
const borrower = borrowersMap.get(borrowerId);
|
|
301
|
+
const formattedBorrowerName = borrower.name.replace(/[*?:\\/[\]]/g, '-');
|
|
302
|
+
const borrowerNameOrId = borrower ? formattedBorrowerName : borrowerId;
|
|
303
|
+
const borrowerData = await this.getBorrowerSummary(borrowerId, start, end);
|
|
304
|
+
if (borrowerData) {
|
|
305
|
+
borrowerSummary[borrowerNameOrId] = borrowerData;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return borrowerSummary;
|
|
309
|
+
}
|
|
310
|
+
;
|
|
311
|
+
async getBorrowerSummary(borrowerId, start, end) {
|
|
312
|
+
const header = {
|
|
313
|
+
title: '',
|
|
314
|
+
grossValue: 'Gross Value',
|
|
315
|
+
ineligible: 'Ineligible',
|
|
316
|
+
netValue: 'Net Value',
|
|
317
|
+
advanceRate: 'Advance Rate',
|
|
318
|
+
availability: 'Availability',
|
|
319
|
+
};
|
|
320
|
+
const borrower = borrowersMap.get(borrowerId);
|
|
321
|
+
const borrowerTitle = [
|
|
322
|
+
{ v: borrower?.name ?? borrowerId, s: styles.whiteOnBlack },
|
|
323
|
+
...Array(borrowerTitleLength).fill({ v: '', s: styles.whiteOnBlack }),
|
|
324
|
+
];
|
|
325
|
+
const getBorrowerSubTitle = async () => {
|
|
326
|
+
const borrowerOption = await this.borrowerService.getBorrowerOption(borrowerId, 'NAISC Code');
|
|
327
|
+
if (!borrowerOption) {
|
|
328
|
+
return [{ v: '' }];
|
|
329
|
+
}
|
|
330
|
+
const industry = await ProspectIndustry_model_1.CRMProspectIndustry.findOne({ code: borrowerOption.dataValue.toString() });
|
|
331
|
+
if (!industry) {
|
|
332
|
+
return [{ v: `NASIC CODE: ${borrowerOption.dataValue}` }];
|
|
333
|
+
}
|
|
334
|
+
return [{ v: `NASIC CODE: ${borrowerOption.dataValue} INDUSTRY: ${industry.name.toLocaleUpperCase()}` }];
|
|
335
|
+
};
|
|
336
|
+
const borrowerSubTitle = await getBorrowerSubTitle();
|
|
337
|
+
const borrowerSummary = await BorrowerSummary_model_1.BorrowerSummary.findOne({ borrowerId });
|
|
338
|
+
const getSegment = (title) => {
|
|
339
|
+
const segment = [
|
|
340
|
+
{ v: title, t: 's', s: styles.whiteOnGray },
|
|
341
|
+
...Array(borrowerTitleLength).fill({ v: '', t: 's', s: styles.whiteOnGray }),
|
|
342
|
+
];
|
|
343
|
+
return segment;
|
|
344
|
+
};
|
|
345
|
+
const headerKeys = Object.keys(header);
|
|
346
|
+
const headerAsArray = (0, common_helper_1.reorderObject)(header, headerKeys);
|
|
347
|
+
const headerWithStyles = Object.values(headerAsArray).reduce((acc, v) => [...acc, {
|
|
348
|
+
v,
|
|
349
|
+
t: 's',
|
|
350
|
+
s: rightAlign,
|
|
351
|
+
}], []);
|
|
352
|
+
const lastBBC = await (0, bbcDates_db_1.getLatestSignedBBCDateDoc)(borrowerId, (0, dayjs_1.default)(end).endOf('day').toDate());
|
|
353
|
+
if (!lastBBC) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
const availability = await this.availabilityService.getAllSummaries(lastBBC._id, null);
|
|
357
|
+
// INVENTORY
|
|
358
|
+
const inventoryRows = availability.inventory.reduce((acc, inventory) => {
|
|
359
|
+
if (inventory.inventoryName === 'TOTAL') {
|
|
360
|
+
return acc;
|
|
361
|
+
}
|
|
362
|
+
const newRow = {
|
|
363
|
+
title: inventory.inventoryName,
|
|
364
|
+
grossValue: inventory.totalValue,
|
|
365
|
+
ineligible: inventory.advanceRate === 0 ? inventory.totalValue : 0,
|
|
366
|
+
netValue: inventory.advanceRate === 0 ? 0 : inventory.totalValue,
|
|
367
|
+
advanceRate: inventory.advanceRate,
|
|
368
|
+
availability: inventory.availability,
|
|
369
|
+
};
|
|
370
|
+
return [...acc, newRow];
|
|
371
|
+
}, []);
|
|
372
|
+
inventoryRows.push({
|
|
373
|
+
title: 'Inventory Reserves',
|
|
374
|
+
grossValue: null,
|
|
375
|
+
ineligible: null,
|
|
376
|
+
netValue: null,
|
|
377
|
+
advanceRate: null,
|
|
378
|
+
availability: availability.reserve[reserve_types_enum_1.EReserveTypes.INVENTORY].availability ?? 0,
|
|
379
|
+
});
|
|
380
|
+
const inventorySumKeys = ['grossValue', 'ineligible', 'netValue', 'availability'];
|
|
381
|
+
const emptyTotalRow = {
|
|
382
|
+
title: 'Total Collateral',
|
|
383
|
+
grossValue: 0,
|
|
384
|
+
ineligible: 0,
|
|
385
|
+
netValue: 0,
|
|
386
|
+
advanceRate: 0,
|
|
387
|
+
availability: 0,
|
|
388
|
+
};
|
|
389
|
+
const inventoryTotalRow = inventoryRows.reduce((acc, row) => {
|
|
390
|
+
inventorySumKeys.forEach((key) => {
|
|
391
|
+
acc[key] = new decimal_js_1.default(acc[key]).add(row[key] ?? 0).toNumber();
|
|
392
|
+
});
|
|
393
|
+
acc.advanceRate = acc.netValue ? new decimal_js_1.default(acc.availability).div(acc.netValue).toDP(4).toNumber() : 0;
|
|
394
|
+
return acc;
|
|
395
|
+
}, { ...emptyTotalRow, title: 'Total Inventory Collateral' });
|
|
396
|
+
const inventoryDataAsArrays = inventoryRows.map((row) => (0, common_helper_1.reorderObject)(row, headerKeys));
|
|
397
|
+
const inventoryRowDataWithStyles = styleRowData(inventoryDataAsArrays);
|
|
398
|
+
const inventoryTotalDataWithStyles = styleRowData([inventoryTotalRow], styles.blackOnWhiteWithTopBorder);
|
|
399
|
+
const inventoryDataWithStyles = [...inventoryRowDataWithStyles, ...inventoryTotalDataWithStyles];
|
|
400
|
+
// RECEIVABLE
|
|
401
|
+
const receivableRows = [];
|
|
402
|
+
const totalComponents = new decimal_js_1.default(availability.receivable.uninsuredComponent).add(availability.receivable.insuredComponent);
|
|
403
|
+
const grossUninsured = totalComponents
|
|
404
|
+
? new decimal_js_1.default(availability.receivable.uninsuredComponent).div(totalComponents).mul(availability.receivable.invoiceAmount).toDP(4).toNumber()
|
|
405
|
+
: 0;
|
|
406
|
+
const ARUninsuredRow = {
|
|
407
|
+
title: 'AR Uninsured',
|
|
408
|
+
grossValue: grossUninsured, // B
|
|
409
|
+
ineligible: new decimal_js_1.default(availability.receivable.uninsuredComponent).sub(grossUninsured).toNumber(), // C
|
|
410
|
+
netValue: availability.receivable.uninsuredComponent, // D
|
|
411
|
+
advanceRate: availability.receivable.uninsuredAvailability ? new decimal_js_1.default(availability.receivable.uninsuredAvailability).div(availability.receivable.uninsuredComponent).toDP(4).toNumber() : 0, // E
|
|
412
|
+
availability: availability.receivable.uninsuredAvailability, // F
|
|
413
|
+
};
|
|
414
|
+
const grossInsured = totalComponents
|
|
415
|
+
? new decimal_js_1.default(availability.receivable.insuredComponent).div(totalComponents).mul(availability.receivable.invoiceAmount).toDP(4).toNumber()
|
|
416
|
+
: 0;
|
|
417
|
+
const ARInsuredRow = {
|
|
418
|
+
title: 'AR Insured',
|
|
419
|
+
grossValue: grossInsured, // B
|
|
420
|
+
ineligible: new decimal_js_1.default(availability.receivable.insuredComponent).sub(grossInsured).toNumber(), // C
|
|
421
|
+
netValue: availability.receivable.insuredComponent, // D
|
|
422
|
+
advanceRate: availability.receivable.insuredAvailability ? new decimal_js_1.default(availability.receivable.insuredAvailability).div(availability.receivable.insuredComponent).toDP(4).toNumber() : 0, // E
|
|
423
|
+
availability: availability.receivable.insuredAvailability, // F
|
|
424
|
+
};
|
|
425
|
+
const ARReservesRow = {
|
|
426
|
+
title: 'AR Reserves',
|
|
427
|
+
grossValue: null,
|
|
428
|
+
ineligible: null,
|
|
429
|
+
netValue: null,
|
|
430
|
+
advanceRate: null,
|
|
431
|
+
availability: availability.reserve[reserve_types_enum_1.EReserveTypes.RECEIVABLES].amount ?? 0,
|
|
432
|
+
};
|
|
433
|
+
receivableRows.push(ARUninsuredRow, ARInsuredRow, ARReservesRow);
|
|
434
|
+
const receivableTotalRow = { ...emptyTotalRow, title: 'Total Receivable Collateral' };
|
|
435
|
+
const fields = ['grossValue', 'ineligible', 'netValue', 'availability'];
|
|
436
|
+
fields.forEach((field) => {
|
|
437
|
+
receivableTotalRow[field] = new decimal_js_1.default(ARUninsuredRow[field]).add(ARInsuredRow[field]).sub(ARReservesRow[field] ?? 0).toNumber();
|
|
438
|
+
});
|
|
439
|
+
receivableTotalRow.advanceRate = receivableTotalRow.netValue ? new decimal_js_1.default(receivableTotalRow.availability).div(receivableTotalRow.netValue).toDP(4).toNumber() : 0;
|
|
440
|
+
const receivableDataAsArrays = receivableRows.map((row) => (0, common_helper_1.reorderObject)(row, headerKeys));
|
|
441
|
+
const receivableRowDataWithStyles = styleRowData(receivableDataAsArrays);
|
|
442
|
+
const receivableTotalDataWithStyles = styleRowData([receivableTotalRow], styles.blackOnWhiteWithTopBorder);
|
|
443
|
+
const receivableDataWithStyles = [...receivableRowDataWithStyles, ...receivableTotalDataWithStyles];
|
|
444
|
+
// RESERVES
|
|
445
|
+
const reserveRows = [];
|
|
446
|
+
const otherReservesAvailability = Object
|
|
447
|
+
.entries(availability.reserve)
|
|
448
|
+
.reduce((acc, [key, el]) => {
|
|
449
|
+
if (key === reserve_types_enum_1.EReserveTypes.EQUIPMENT || key === reserve_types_enum_1.EReserveTypes.INVENTORY || key === reserve_types_enum_1.EReserveTypes.RECEIVABLES) {
|
|
450
|
+
return acc;
|
|
451
|
+
}
|
|
452
|
+
return new decimal_js_1.default(acc).sub(el.amount).toNumber();
|
|
453
|
+
}, 0);
|
|
454
|
+
const otherReservesRow = {
|
|
455
|
+
title: 'Other non-equipment reserves',
|
|
456
|
+
grossValue: null,
|
|
457
|
+
ineligible: null,
|
|
458
|
+
netValue: null,
|
|
459
|
+
advanceRate: null,
|
|
460
|
+
availability: otherReservesAvailability,
|
|
461
|
+
};
|
|
462
|
+
const collateralAdjustmentsAvailability = Object.values(availability.collateralAdjustment.summary).reduce((acc, summaryRow) => new decimal_js_1.default(acc).add(summaryRow.amount).abs().toNumber(), 0);
|
|
463
|
+
const collateralAdjustmentsRow = {
|
|
464
|
+
title: 'Collateral Adjustments',
|
|
465
|
+
grossValue: null,
|
|
466
|
+
ineligible: null,
|
|
467
|
+
netValue: null,
|
|
468
|
+
advanceRate: null,
|
|
469
|
+
availability: collateralAdjustmentsAvailability,
|
|
470
|
+
};
|
|
471
|
+
reserveRows.push(otherReservesRow, collateralAdjustmentsRow);
|
|
472
|
+
const reserveTotalRow = reserveRows.reduce((acc, row) => {
|
|
473
|
+
inventorySumKeys.forEach((key) => {
|
|
474
|
+
acc[key] = new decimal_js_1.default(acc[key]).add(row[key] ?? 0).toNumber();
|
|
475
|
+
});
|
|
476
|
+
acc.advanceRate = row.netValue ? new decimal_js_1.default(acc.availability).div(row.netValue).toNumber() : 0;
|
|
477
|
+
return acc;
|
|
478
|
+
}, { ...emptyTotalRow, title: 'Other Reserves & Adjustments' });
|
|
479
|
+
const reserveDataAsArrays = reserveRows.map((row) => (0, common_helper_1.reorderObject)(row, headerKeys));
|
|
480
|
+
const reserveRowDataWithStyles = styleRowData(reserveDataAsArrays);
|
|
481
|
+
const reserveTotalDataWithStyles = styleRowData([reserveTotalRow], styles.blackOnWhiteWithTopBorder);
|
|
482
|
+
const reserveDataWithStyles = [...reserveRowDataWithStyles, ...reserveTotalDataWithStyles];
|
|
483
|
+
const loanBalanceTotalRows = [];
|
|
484
|
+
const totalRevolvingCollateralRow = {
|
|
485
|
+
title: 'Total Revolving Collateral',
|
|
486
|
+
grossValue: '',
|
|
487
|
+
ineligible: '',
|
|
488
|
+
netValue: '',
|
|
489
|
+
advanceRate: '',
|
|
490
|
+
availability: new decimal_js_1.default(inventoryTotalRow.availability).add(receivableTotalRow.availability).sub(reserveTotalRow.availability).toNumber(),
|
|
491
|
+
};
|
|
492
|
+
const loanBalanceRow = {
|
|
493
|
+
title: 'Loan Balance',
|
|
494
|
+
grossValue: '',
|
|
495
|
+
ineligible: '',
|
|
496
|
+
netValue: '',
|
|
497
|
+
advanceRate: '',
|
|
498
|
+
availability: availability.loanBalances.REVOLVER,
|
|
499
|
+
};
|
|
500
|
+
const netRevolverAvailabilityRow = {
|
|
501
|
+
title: 'Net Revolver Availability',
|
|
502
|
+
grossValue: '',
|
|
503
|
+
ineligible: '',
|
|
504
|
+
netValue: '',
|
|
505
|
+
advanceRate: '',
|
|
506
|
+
availability: new decimal_js_1.default(totalRevolvingCollateralRow.availability).sub(loanBalanceRow.availability).toNumber(),
|
|
507
|
+
};
|
|
508
|
+
const accruedStatementRow = {
|
|
509
|
+
title: 'Accrued Statement',
|
|
510
|
+
grossValue: '',
|
|
511
|
+
ineligible: '',
|
|
512
|
+
netValue: '',
|
|
513
|
+
advanceRate: '',
|
|
514
|
+
availability: availability.accruedStatement,
|
|
515
|
+
};
|
|
516
|
+
const availableToBorrowRow = {
|
|
517
|
+
title: 'Available To Borrow',
|
|
518
|
+
grossValue: '',
|
|
519
|
+
ineligible: '',
|
|
520
|
+
netValue: '',
|
|
521
|
+
advanceRate: '',
|
|
522
|
+
availability: new decimal_js_1.default(netRevolverAvailabilityRow.availability).sub(accruedStatementRow.availability).toNumber(),
|
|
523
|
+
};
|
|
524
|
+
loanBalanceTotalRows.push(totalRevolvingCollateralRow, loanBalanceRow, netRevolverAvailabilityRow, accruedStatementRow, availableToBorrowRow, { title: '', netValue: null, grossValue: null, ineligible: null, advanceRate: null, availability: null });
|
|
525
|
+
const borrowerProducts = [...productsMap.values()]
|
|
526
|
+
.filter((product) => product.borrowerId.toString() === borrowerId)
|
|
527
|
+
.sort((a, b) => (a.type > b.type ? 1 : -1));
|
|
528
|
+
for (const product of borrowerProducts) {
|
|
529
|
+
const month = (0, dayjs_1.default)(end).month() + 1;
|
|
530
|
+
const year = (0, dayjs_1.default)(end).year();
|
|
531
|
+
const monthEndData = await this.monthEndDataService.getMonthEndData(product._id.toString(), year, month);
|
|
532
|
+
const productType = product.type[0].toUpperCase() + product.type.slice(1).toLowerCase();
|
|
533
|
+
const dataToShow = {
|
|
534
|
+
[MonthEndData_Model_1.EMonthEndDataType.BALANCE]: `Month End ${productType} Loan Balance`,
|
|
535
|
+
[MonthEndData_Model_1.EMonthEndDataType.STATEMENT_BALANCE]: `Month End ${productType} Accrued Statement`,
|
|
536
|
+
};
|
|
537
|
+
Object.entries(dataToShow).forEach(([dataType, title]) => {
|
|
538
|
+
const monthEndDataDoc = monthEndData.find((msd) => msd.dataType === dataType);
|
|
539
|
+
if (monthEndDataDoc) {
|
|
540
|
+
const monthEndLoanBalanceRow = {
|
|
541
|
+
title,
|
|
542
|
+
grossValue: '',
|
|
543
|
+
ineligible: '',
|
|
544
|
+
netValue: '',
|
|
545
|
+
advanceRate: '',
|
|
546
|
+
availability: monthEndDataDoc.dataValue,
|
|
547
|
+
};
|
|
548
|
+
loanBalanceTotalRows.push(monthEndLoanBalanceRow);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
if (monthEndData.length) {
|
|
552
|
+
loanBalanceTotalRows.push({ ...emptyTotalRow, title: '', netValue: null, availability: null, grossValue: null, ineligible: null, advanceRate: null });
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const loanBalanceTotalDataAsArrays = loanBalanceTotalRows.map((row) => (0, common_helper_1.reorderObject)(row, headerKeys));
|
|
556
|
+
const totalRows = ['Net Revolver Availability', 'Available To Borrow'];
|
|
557
|
+
const loanBalanceTotalDataAsArraysWithStyles = Object.values([...loanBalanceTotalDataAsArrays]).map((dataRow) => {
|
|
558
|
+
return Object.values(dataRow).map((v) => ({
|
|
559
|
+
v,
|
|
560
|
+
t: typeof v === 'number' ? 'n' : 's',
|
|
561
|
+
z: numberFormat.thousands,
|
|
562
|
+
...(totalRows.includes(dataRow.title) ? { s: styles.blackOnWhiteWithTopBorder } : {}),
|
|
563
|
+
}));
|
|
564
|
+
});
|
|
565
|
+
// TERM LOAN
|
|
566
|
+
const getTermLoanSection = async () => {
|
|
567
|
+
const termProduct = [...productsMap.values()].find((product) => product.borrowerId.toString() === borrowerId && product.type === loan_types_enum_1.ELoanTypes.TERM && product.active);
|
|
568
|
+
if (!termProduct || !availability) {
|
|
569
|
+
return [emptyRow];
|
|
570
|
+
}
|
|
571
|
+
const equipmentOrder = {
|
|
572
|
+
[equipment_types_enum_1.EEquipmentTypes.EQUIPMENT]: { title: 'Equipment', s: {} },
|
|
573
|
+
[equipment_types_enum_1.EEquipmentTypes.REAL_ESTATE]: { title: 'Real estate', s: {} },
|
|
574
|
+
[equipment_types_enum_1.EEquipmentTypes.BOOT_COLLATERAL]: { title: 'Boot collateral', s: {} },
|
|
575
|
+
'grossTotal': { title: 'GROSS TOTAL', s: { font: { bold: true } } },
|
|
576
|
+
[equipment_types_enum_1.EEquipmentTypes.INTELLECTUAL_PROPERTY]: { title: 'Intellectual property', s: {} },
|
|
577
|
+
'netTotal': { title: 'NET TOTAL', s: { font: { bold: true } } },
|
|
578
|
+
};
|
|
579
|
+
const rows = [[{ v: 'Inventory type' }, { v: 'Amount' }, { v: 'Advance rate' }, { v: 'Availability' }]];
|
|
580
|
+
Object.entries(equipmentOrder).forEach(([key, desc]) => {
|
|
581
|
+
const newRow = [
|
|
582
|
+
{ v: desc.title, s: desc.s },
|
|
583
|
+
{ v: availability.equipment[key].amount ?? 0, t: 'n', z: numberFormat.thousands, s: desc.s },
|
|
584
|
+
{ v: availability.equipment[key].advanceRate ?? 0, t: 'n', z: numberFormat.percent, s: desc.s },
|
|
585
|
+
{ v: availability.equipment[key].availability ?? 0, t: 'n', z: numberFormat.thousands, s: desc.s },
|
|
586
|
+
];
|
|
587
|
+
rows.push(newRow);
|
|
588
|
+
});
|
|
589
|
+
rows.push(emptyRow);
|
|
590
|
+
rows.push([{ v: '' }, { v: 'Gross Amount' }, { v: 'Availability' }]);
|
|
591
|
+
rows.push([
|
|
592
|
+
{ v: 'Term Reserve' },
|
|
593
|
+
{ v: availability.equipment[equipment_types_enum_1.EEquipmentTypes.EQUIPMENT].amount, t: 'n', z: numberFormat.thousands },
|
|
594
|
+
{ v: availability.equipment[equipment_types_enum_1.EEquipmentTypes.EQUIPMENT].availability, t: 'n', z: numberFormat.thousands },
|
|
595
|
+
]);
|
|
596
|
+
rows.push([
|
|
597
|
+
{ v: 'Loan Balance' },
|
|
598
|
+
{ v: 0, t: 'n' },
|
|
599
|
+
{ v: availability.loanBalances.TERM, t: 'n', z: numberFormat.thousands },
|
|
600
|
+
]);
|
|
601
|
+
rows.push([
|
|
602
|
+
{ v: 'Net Availability' },
|
|
603
|
+
{ v: availability.equipment[equipment_types_enum_1.EEquipmentTypes.EQUIPMENT].amount, t: 'n', z: numberFormat.thousands },
|
|
604
|
+
{
|
|
605
|
+
v: new decimal_js_1.default(availability.equipment['netTotal'].availability).sub(availability.loanBalances.TERM).toNumber(),
|
|
606
|
+
t: 'n',
|
|
607
|
+
z: numberFormat.thousands,
|
|
608
|
+
},
|
|
609
|
+
]);
|
|
610
|
+
return [
|
|
611
|
+
getSegment('TERM LOAN DETAILS'),
|
|
612
|
+
emptyRow,
|
|
613
|
+
...rows,
|
|
614
|
+
emptyRow,
|
|
615
|
+
];
|
|
616
|
+
};
|
|
617
|
+
const termLoanData = await getTermLoanSection();
|
|
618
|
+
// LOAN ECONOMICS
|
|
619
|
+
const getLoanEconomics = async () => {
|
|
620
|
+
const loanEconomicsData = [];
|
|
621
|
+
const products = [...productsMap.values()];
|
|
622
|
+
const yieldProducts = products.filter((product) => product.borrowerId.toString() === borrowerId);
|
|
623
|
+
for (const product of yieldProducts) {
|
|
624
|
+
const chargesData = [];
|
|
625
|
+
const productData = productsDataMap.get(product._id.toString());
|
|
626
|
+
loanEconomicsData.push(emptyRow);
|
|
627
|
+
loanEconomicsData.push([
|
|
628
|
+
{ v: product.name, s: styles.blackOnWhiteWithBottomBorder },
|
|
629
|
+
...Array(6).fill({ v: '', s: styles.blackOnWhiteWithBottomBorder }),
|
|
630
|
+
]);
|
|
631
|
+
if (productData) {
|
|
632
|
+
loanEconomicsData.push([{ v: 'Average Balance Since Inception' }, {
|
|
633
|
+
v: productData.averageBalanceSinceInception,
|
|
634
|
+
t: 'n',
|
|
635
|
+
z: numberFormat.thousands,
|
|
636
|
+
}]);
|
|
637
|
+
loanEconomicsData.push([{ v: 'Total Income Received' }, {
|
|
638
|
+
v: productData.totalIncomeReceived,
|
|
639
|
+
t: 'n',
|
|
640
|
+
z: numberFormat.thousands,
|
|
641
|
+
}]);
|
|
642
|
+
loanEconomicsData.push([{ v: 'Loan Life IRR' }, {
|
|
643
|
+
v: productData.loanLifeIRR,
|
|
644
|
+
t: 'n',
|
|
645
|
+
z: numberFormat.percent,
|
|
646
|
+
}]);
|
|
647
|
+
loanEconomicsData.push([{ v: 'MOIC' }, {
|
|
648
|
+
v: productData.MOIC,
|
|
649
|
+
t: 'n',
|
|
650
|
+
z: numberFormat.xNumber,
|
|
651
|
+
}]);
|
|
652
|
+
}
|
|
653
|
+
const dataDeep = 5;
|
|
654
|
+
const periodEnd = { month: (0, dayjs_1.default)(start).month() + 1, year: (0, dayjs_1.default)(start).year() };
|
|
655
|
+
const periodStart = (0, financial_spreading_service_1.getShiftedMonth)(periodEnd, -dataDeep);
|
|
656
|
+
const monthHeader = [];
|
|
657
|
+
const months = [];
|
|
658
|
+
for (let i = 0; i <= dataDeep; i++) {
|
|
659
|
+
const currentMonth = (0, financial_spreading_service_1.getShiftedMonth)(periodEnd, -i);
|
|
660
|
+
monthHeader.push({
|
|
661
|
+
v: `${currentMonth.month}/${currentMonth.year}`,
|
|
662
|
+
t: 's',
|
|
663
|
+
s: { alignment: { horizontal: 'right' } },
|
|
664
|
+
});
|
|
665
|
+
months.push((0, dayjs_1.default)(`${currentMonth.year}-${currentMonth.month}-01`).format('YYYY-MM'));
|
|
666
|
+
}
|
|
667
|
+
loanEconomicsData.push(emptyRow);
|
|
668
|
+
loanEconomicsData.push([{ v: '' }, ...monthHeader]);
|
|
669
|
+
const transactions = await this.loanTransactionsService.getLoanTransactions({
|
|
670
|
+
borrowerId,
|
|
671
|
+
productId: product._id.toString(),
|
|
672
|
+
periodStart: (0, dayjs_1.default)(`${periodStart.year}-${periodStart.month}-01`).utcOffset(0).toDate(),
|
|
673
|
+
periodEnd: end,
|
|
674
|
+
}, null, false);
|
|
675
|
+
const grouped = lodash_1.default.groupBy(transactions, (tx) => (0, dayjs_1.default)(tx.date).format('YYYY-MM'));
|
|
676
|
+
const yieldData = await this.yieldService.getCalculatedYieldTotalsForPeriod(product._id.toString(), periodStart, periodEnd);
|
|
677
|
+
const yieldDataSorted = yieldData.slice().sort((a, b) => {
|
|
678
|
+
if (a.year !== b.year) {
|
|
679
|
+
return b.year - a.year;
|
|
680
|
+
}
|
|
681
|
+
return b.month - a.month;
|
|
682
|
+
});
|
|
683
|
+
const totalDisbursementRow = [{ v: 'Total Disbursements in Month' }];
|
|
684
|
+
const countDisbursementRow = [{ v: 'Number of Disbursements' }];
|
|
685
|
+
const loanTurnRow = [{ v: 'Loan Turn (days)' }];
|
|
686
|
+
const totalCollectionRow = [{ v: 'Total Collections in Month' }];
|
|
687
|
+
const countCollectionRow = [{ v: 'Number of Collections' }];
|
|
688
|
+
const charges = [...chargesMap.values()]
|
|
689
|
+
.filter((charge) => charge.productId.toString() === product._id.toString() && charge.includeInYield)
|
|
690
|
+
.sort((a, b) => a.order - b.order);
|
|
691
|
+
for (const month of months) {
|
|
692
|
+
const txs = grouped[month] ?? [];
|
|
693
|
+
const disbursements = txs.filter((t) => t.transactionType === LoanTransaction_model_1.ELoanTransactionTypes.DISBURSEMENT);
|
|
694
|
+
const collections = txs.filter((t) => t.transactionType === LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION);
|
|
695
|
+
const totalDisbursements = lodash_1.default.sumBy(disbursements, 'amount');
|
|
696
|
+
const totalCollections = Math.abs(lodash_1.default.sumBy(collections, 'amount'));
|
|
697
|
+
totalDisbursementRow.push({ v: totalDisbursements, t: 'n', z: numberFormat.thousands });
|
|
698
|
+
countDisbursementRow.push({ v: disbursements.length, t: 'n', z: numberFormat.shortNumber });
|
|
699
|
+
totalCollectionRow.push({ v: totalCollections, t: 'n', z: numberFormat.thousands });
|
|
700
|
+
countCollectionRow.push({ v: collections.length, t: 'n', z: numberFormat.shortNumber });
|
|
701
|
+
}
|
|
702
|
+
charges.forEach((charge) => {
|
|
703
|
+
const chargeYieldData = yieldDataSorted.filter((yieldDataDoc) => {
|
|
704
|
+
return yieldDataDoc.chargeId?.toString() === charge._id.toString();
|
|
705
|
+
});
|
|
706
|
+
const chargeData = chargeYieldData.reduce((acc, chargeYieldDataDoc) => {
|
|
707
|
+
const newRow = chargeYieldDataDoc.valuePercent < 0
|
|
708
|
+
? negativeCell
|
|
709
|
+
: { v: chargeYieldDataDoc.valuePercent, t: 'n', z: numberFormat.percent };
|
|
710
|
+
return [...acc, newRow];
|
|
711
|
+
}, [{ v: charge.name, t: 's' }]);
|
|
712
|
+
chargesData.push(chargeData);
|
|
713
|
+
});
|
|
714
|
+
chargesData.push(emptyRow);
|
|
715
|
+
Object.entries(yield_service_1.YIELD_TOTALS_MAP).forEach(([totalType, total]) => {
|
|
716
|
+
const totalYieldData = yieldDataSorted
|
|
717
|
+
.filter((yieldDataDoc) => {
|
|
718
|
+
return yieldDataDoc.totalType === totalType;
|
|
719
|
+
})
|
|
720
|
+
.sort((a, b) => {
|
|
721
|
+
if (a.year !== b.year) {
|
|
722
|
+
return b.year - a.year;
|
|
723
|
+
}
|
|
724
|
+
return b.month - a.month;
|
|
725
|
+
});
|
|
726
|
+
if (totalType === yield_service_1.ETotalType.AVERAGE_BALANCE) {
|
|
727
|
+
const loanTurn = totalYieldData.reduce((acc, yieldDataDoc) => {
|
|
728
|
+
const monthStart = (0, dayjs_1.default)(`${yieldDataDoc.year}-${yieldDataDoc.month}-01`).utcOffset(0);
|
|
729
|
+
const daysInMonth = monthStart.daysInMonth();
|
|
730
|
+
const txs = grouped[monthStart.format('YYYY-MM')] ?? [];
|
|
731
|
+
const collections = txs.filter((t) => t.transactionType === LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION);
|
|
732
|
+
const totalCollections = lodash_1.default.sumBy(collections, 'amount');
|
|
733
|
+
const newRow = {
|
|
734
|
+
v: totalCollections === 0 ? 0 : new decimal_js_1.default(yieldDataDoc.value).div(Math.abs(totalCollections)).mul(daysInMonth).round().toNumber(),
|
|
735
|
+
t: 'n',
|
|
736
|
+
z: numberFormat.thousands,
|
|
737
|
+
};
|
|
738
|
+
return [...acc, newRow];
|
|
739
|
+
}, []);
|
|
740
|
+
loanTurnRow.push(...loanTurn);
|
|
741
|
+
}
|
|
742
|
+
const replacedValues = [yield_service_1.ETotalType.APR_GROSS, yield_service_1.ETotalType.APR_LIFE_GROSS];
|
|
743
|
+
const chargeData = totalYieldData.reduce((acc, yieldDataDoc) => {
|
|
744
|
+
const isPercent = !!yieldDataDoc.valuePercent;
|
|
745
|
+
if (replacedValues.includes(yield_service_1.ETotalType[yieldDataDoc.totalType]) && yieldDataDoc.valuePercent < 0) {
|
|
746
|
+
return [...acc, negativeCell];
|
|
747
|
+
}
|
|
748
|
+
const newRow = isPercent
|
|
749
|
+
? { v: yieldDataDoc.valuePercent, t: 'n', z: numberFormat.percent }
|
|
750
|
+
: { v: yieldDataDoc.value, t: 'n', z: numberFormat.thousands };
|
|
751
|
+
return [...acc, newRow];
|
|
752
|
+
}, [{ v: total.title, t: 's' }]);
|
|
753
|
+
chargesData.push(chargeData);
|
|
754
|
+
});
|
|
755
|
+
loanEconomicsData.push(totalDisbursementRow);
|
|
756
|
+
loanEconomicsData.push(totalCollectionRow);
|
|
757
|
+
loanEconomicsData.push(loanTurnRow);
|
|
758
|
+
loanEconomicsData.push(countDisbursementRow);
|
|
759
|
+
loanEconomicsData.push(countCollectionRow);
|
|
760
|
+
loanEconomicsData.push(emptyRow);
|
|
761
|
+
loanEconomicsData.push(...chargesData);
|
|
762
|
+
}
|
|
763
|
+
return loanEconomicsData;
|
|
764
|
+
};
|
|
765
|
+
const loanEconomicsData = await getLoanEconomics();
|
|
766
|
+
// FINANCIAL
|
|
767
|
+
const getFinancialDate = async (endDate) => {
|
|
768
|
+
const month = (0, dayjs_1.default)(endDate).month();
|
|
769
|
+
const year = (0, dayjs_1.default)(endDate).year();
|
|
770
|
+
const monthDeep = 11;
|
|
771
|
+
const { data: dataPL, sheets: sheetsPL } = await this.financialSpreadingService.getFinancialSpreadingData({
|
|
772
|
+
borrowerId,
|
|
773
|
+
financialSpreadingType: FinancialSpreadingSheet_model_1.EFinancialSpreadingType.PROFIT_LOSS,
|
|
774
|
+
selectedMonth: { month, year },
|
|
775
|
+
}, monthDeep);
|
|
776
|
+
const { data: dataBS, sheets: sheetsBS } = await this.financialSpreadingService.getFinancialSpreadingData({
|
|
777
|
+
borrowerId,
|
|
778
|
+
financialSpreadingType: FinancialSpreadingSheet_model_1.EFinancialSpreadingType.BALANCE_SHEET,
|
|
779
|
+
selectedMonth: { month, year },
|
|
780
|
+
}, monthDeep);
|
|
781
|
+
const data = [...dataPL, ...dataBS];
|
|
782
|
+
const sheets = [...sheetsPL, ...sheetsBS];
|
|
783
|
+
const generateMonthCells = () => {
|
|
784
|
+
const result = [];
|
|
785
|
+
for (let i = 0; i <= monthDeep; i++) {
|
|
786
|
+
const date = (0, dayjs_1.default)(`${year}-${String(month).padStart(2, '0')}-01`).subtract(i, 'month');
|
|
787
|
+
result.push({
|
|
788
|
+
v: date.endOf('month').format(dateFormat),
|
|
789
|
+
t: 's',
|
|
790
|
+
s: rightAlign,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return [{ v: 'Last 12 months', t: 's' }, ...result];
|
|
794
|
+
};
|
|
795
|
+
const percentageIndexes = [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_MARGIN, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_MARGIN];
|
|
796
|
+
const convertAmount = (index, amount) => {
|
|
797
|
+
if (percentageIndexes.includes(index)) {
|
|
798
|
+
return new decimal_js_1.default(amount).div(100).toDP(4).toNumber();
|
|
799
|
+
}
|
|
800
|
+
return amount;
|
|
801
|
+
};
|
|
802
|
+
const createFinancialIndexes = (enumObj, keys, overrides = {}) => keys.reduce((acc, key) => {
|
|
803
|
+
const enumValue = enumObj[key];
|
|
804
|
+
acc[enumValue] = {
|
|
805
|
+
format: overrides[key]?.format ?? numberFormat.thousands,
|
|
806
|
+
title: overrides[key]?.title ?? FinancialSpreadingSheet_model_1.financialSpreadingTotalDictionary[enumValue],
|
|
807
|
+
addEmptyRow: overrides[key]?.addEmptyRow ?? false,
|
|
808
|
+
style: overrides[key]?.style ?? {},
|
|
809
|
+
};
|
|
810
|
+
return acc;
|
|
811
|
+
}, {});
|
|
812
|
+
const financialPLIndexes = createFinancialIndexes(FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal, [
|
|
813
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES,
|
|
814
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES,
|
|
815
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_PROFIT,
|
|
816
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_MARGIN,
|
|
817
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_EXPENSES,
|
|
818
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_MARGIN,
|
|
819
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS,
|
|
820
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_EXPENSES,
|
|
821
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_INCOME,
|
|
822
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS,
|
|
823
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION,
|
|
824
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.TAX,
|
|
825
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NET_INCOME,
|
|
826
|
+
], {
|
|
827
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.GROSS_MARGIN]: {
|
|
828
|
+
format: numberFormat.percent,
|
|
829
|
+
addEmptyRow: true,
|
|
830
|
+
style: { font: { italic: true, name: 'Calibri' } },
|
|
831
|
+
},
|
|
832
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.OPERATING_MARGIN]: {
|
|
833
|
+
format: numberFormat.percent,
|
|
834
|
+
addEmptyRow: true,
|
|
835
|
+
style: { font: { italic: true, name: 'Calibri' } },
|
|
836
|
+
},
|
|
837
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS]: {
|
|
838
|
+
title: 'EBITDA',
|
|
839
|
+
format: numberFormat.thousands,
|
|
840
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
841
|
+
},
|
|
842
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_EXPENSES]: {
|
|
843
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
844
|
+
},
|
|
845
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NON_OPERATING_INCOME]: {
|
|
846
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
847
|
+
},
|
|
848
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.NET_INCOME]: {
|
|
849
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
850
|
+
},
|
|
851
|
+
});
|
|
852
|
+
const financialBSIndexes = createFinancialIndexes(FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal, [
|
|
853
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.CASH_EQUIVALENTS,
|
|
854
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TRADE_RECEIVABLES,
|
|
855
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.INVENTORY,
|
|
856
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_CURRENT_ASSETS,
|
|
857
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET,
|
|
858
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_ASSETS,
|
|
859
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE,
|
|
860
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_CURRENT_LIABILITIES,
|
|
861
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES,
|
|
862
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT,
|
|
863
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_DEBT,
|
|
864
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_LONG_TERM_LIABILITIES,
|
|
865
|
+
FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_LIABILITIES,
|
|
866
|
+
], {
|
|
867
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET]: { style: { font: { bold: true, name: 'Calibri' } } },
|
|
868
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_ASSETS]: {
|
|
869
|
+
addEmptyRow: true,
|
|
870
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
871
|
+
},
|
|
872
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES]: {
|
|
873
|
+
addEmptyRow: true,
|
|
874
|
+
style: { font: { bold: true, name: 'Calibri' } },
|
|
875
|
+
},
|
|
876
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT]: { title: 'Senior Debt' },
|
|
877
|
+
[FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TOTAL_LIABILITIES]: { style: { font: { bold: true, name: 'Calibri' } } },
|
|
878
|
+
});
|
|
879
|
+
const generateFinancialRows = (financialIndexes, enumObj) => {
|
|
880
|
+
return Object.entries(financialIndexes).reduce((acc, [financialIndex, options]) => {
|
|
881
|
+
const sheet = sheets.find((s) => financialIndex === enumObj[s.rowType] && s.isTotal);
|
|
882
|
+
if (!sheet) {
|
|
883
|
+
return acc;
|
|
884
|
+
}
|
|
885
|
+
const dataEntry = data.find((d) => d.sheetId === sheet._id);
|
|
886
|
+
if (!dataEntry) {
|
|
887
|
+
return acc;
|
|
888
|
+
}
|
|
889
|
+
const baseValue = convertAmount(enumObj[financialIndex], dataEntry.amount);
|
|
890
|
+
const monthlyValues = Array.from({ length: monthDeep }, (_, i) => ({
|
|
891
|
+
v: convertAmount(enumObj[financialIndex], dataEntry[`minus_${i + 1}`]),
|
|
892
|
+
t: 'n',
|
|
893
|
+
z: options.format,
|
|
894
|
+
s: options.style,
|
|
895
|
+
}));
|
|
896
|
+
const row = [
|
|
897
|
+
{ v: options.title, t: 's', s: options.style },
|
|
898
|
+
{ v: baseValue, t: 'n', z: options.format },
|
|
899
|
+
...monthlyValues,
|
|
900
|
+
];
|
|
901
|
+
return options.addEmptyRow ? [...acc, row, emptyRow] : [...acc, row];
|
|
902
|
+
}, []);
|
|
903
|
+
};
|
|
904
|
+
const get = (base, i, key) => new decimal_js_1.default(i === 0 ? base[key]?.amount : base[key]?.[`minus_${i}`] ?? 0);
|
|
905
|
+
const financialRatioFormulas = [
|
|
906
|
+
{
|
|
907
|
+
label: 'Fixed Charge Coverage Ratio',
|
|
908
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS],
|
|
909
|
+
format: numberFormat.xNumber1DP,
|
|
910
|
+
formula: (base, i) => {
|
|
911
|
+
const EBITDA = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS);
|
|
912
|
+
const DepreciationAmortization = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION);
|
|
913
|
+
const FinancingCost = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS);
|
|
914
|
+
if (FinancingCost.lessThanOrEqualTo(0)) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
const result = EBITDA.sub(DepreciationAmortization).add(FinancingCost).div(FinancingCost).toDP(4);
|
|
918
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
return result.toNumber();
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
label: 'Interest Coverage Ratio',
|
|
926
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS],
|
|
927
|
+
format: numberFormat.xNumber1DP,
|
|
928
|
+
formula: (base, i) => {
|
|
929
|
+
const EBITDA = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS);
|
|
930
|
+
const DepreciationAmortization = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION);
|
|
931
|
+
const FinancingCost = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.FINANCING_COSTS);
|
|
932
|
+
if (FinancingCost.lessThanOrEqualTo(0)) {
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
const result = EBITDA.add(DepreciationAmortization).div(FinancingCost).toDP(4);
|
|
936
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
939
|
+
return result.toNumber();
|
|
940
|
+
},
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
label: 'Total Debt / EBITDA',
|
|
944
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_DEBT],
|
|
945
|
+
format: numberFormat.xNumber1DP,
|
|
946
|
+
formula: (base, i) => {
|
|
947
|
+
const EBITDA = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS);
|
|
948
|
+
const CompanyDebt = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT);
|
|
949
|
+
const OtherDebt = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.OTHER_DEBT);
|
|
950
|
+
if (EBITDA.lessThanOrEqualTo(0)) {
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
const result = CompanyDebt.add(OtherDebt).abs().div(EBITDA).toDP(4);
|
|
954
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
return result.toNumber();
|
|
958
|
+
},
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
label: 'Senior Debt / EBITDA',
|
|
962
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT],
|
|
963
|
+
format: numberFormat.xNumber1DP,
|
|
964
|
+
formula: (base, i) => {
|
|
965
|
+
const EBITDA = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS);
|
|
966
|
+
const CompanyDebt = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.COMPANY_DEBT);
|
|
967
|
+
if (EBITDA.lessThanOrEqualTo(0)) {
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
const result = CompanyDebt.div(EBITDA).toDP(4);
|
|
971
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
return result.toNumber();
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
label: 'AR Turnover Days',
|
|
979
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TRADE_RECEIVABLES],
|
|
980
|
+
format: numberFormat.fullNumber,
|
|
981
|
+
formula: (base, i) => {
|
|
982
|
+
const Sales = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.SALES);
|
|
983
|
+
const TradeReceivables = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.TRADE_RECEIVABLES);
|
|
984
|
+
if (Sales.lessThanOrEqualTo(0)) {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
const result = TradeReceivables.abs().div(Sales).mul(30).toDP(0);
|
|
988
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
return result.toNumber();
|
|
992
|
+
},
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
label: 'AP Turnover Days',
|
|
996
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE],
|
|
997
|
+
format: numberFormat.fullNumber,
|
|
998
|
+
formula: (base, i) => {
|
|
999
|
+
const CostOfSales = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES);
|
|
1000
|
+
const AccountsPayable = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE);
|
|
1001
|
+
if (CostOfSales.lessThanOrEqualTo(0)) {
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
const result = AccountsPayable.abs().div(CostOfSales).mul(30).toDP(0);
|
|
1005
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
return result.toNumber();
|
|
1009
|
+
},
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
label: 'Inventory Turnover Days',
|
|
1013
|
+
dependencies: [FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.INVENTORY],
|
|
1014
|
+
format: numberFormat.fullNumber,
|
|
1015
|
+
formula: (base, i) => {
|
|
1016
|
+
const CostOfSales = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal.COST_OF_SALES);
|
|
1017
|
+
const Inventory = get(base, i, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal.INVENTORY);
|
|
1018
|
+
if (CostOfSales.lessThanOrEqualTo(0)) {
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
const result = Inventory.abs().div(CostOfSales).mul(30).toDP(0);
|
|
1022
|
+
if (result.lessThanOrEqualTo(0)) {
|
|
1023
|
+
return null;
|
|
1024
|
+
}
|
|
1025
|
+
return result.toNumber();
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
];
|
|
1029
|
+
const generateFinancialRatios = () => {
|
|
1030
|
+
const ratiosData = [[{ v: 'FINANCIAL RATIOS:' }]];
|
|
1031
|
+
const financialRatiosBaseKeys = lodash_1.default.uniq(financialRatioFormulas.flatMap((f) => f.dependencies));
|
|
1032
|
+
const financialRatiosBase = financialRatiosBaseKeys.reduce((acc, key) => {
|
|
1033
|
+
const sheet = sheets.find((s) => s.rowType === key && s.isTotal);
|
|
1034
|
+
const dataEntry = data.find((d) => d.sheetId.toString() === sheet._id.toString());
|
|
1035
|
+
return { ...acc, [key]: dataEntry };
|
|
1036
|
+
}, {});
|
|
1037
|
+
const ratioRows = financialRatioFormulas.map(({ label, formula, format }) => {
|
|
1038
|
+
const row = [{ v: label, t: 's' }];
|
|
1039
|
+
for (let i = 0; i <= monthDeep; i++) {
|
|
1040
|
+
const result = formula(financialRatiosBase, i);
|
|
1041
|
+
if (result) {
|
|
1042
|
+
row.push({ v: result, t: 'n', z: format ?? numberFormat.thousands });
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
row.push(negativeCell);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return row;
|
|
1049
|
+
});
|
|
1050
|
+
return [...ratiosData, ...ratioRows];
|
|
1051
|
+
};
|
|
1052
|
+
const financialPLDataFull = generateFinancialRows(financialPLIndexes, FinancialSpreadingSheet_model_1.EFinanceSpreadingPLTotal);
|
|
1053
|
+
const financialBSDataFull = generateFinancialRows(financialBSIndexes, FinancialSpreadingSheet_model_1.EFinanceSpreadingBSTotal);
|
|
1054
|
+
const financialRatios = generateFinancialRatios();
|
|
1055
|
+
return [
|
|
1056
|
+
generateMonthCells(),
|
|
1057
|
+
emptyRow,
|
|
1058
|
+
[{ v: 'PROFIT & LOSS HIGHLIGHTS', t: 's' }],
|
|
1059
|
+
...financialPLDataFull,
|
|
1060
|
+
emptyRow,
|
|
1061
|
+
[{ v: 'BALANCE SHEET HIGHLIGHTS', t: 's' }],
|
|
1062
|
+
...financialBSDataFull,
|
|
1063
|
+
emptyRow,
|
|
1064
|
+
...financialRatios,
|
|
1065
|
+
];
|
|
1066
|
+
};
|
|
1067
|
+
const financialData = await getFinancialDate(end);
|
|
1068
|
+
// HEADROOM
|
|
1069
|
+
const getHeadRoomRows = async () => {
|
|
1070
|
+
if (!borrowerSummary) {
|
|
1071
|
+
return [];
|
|
1072
|
+
}
|
|
1073
|
+
const headSourceData = borrowerSummary.chartData.LAST_6_MONTH.data.reverse();
|
|
1074
|
+
const headRoomHeader = headSourceData.reduce((acc, row) => {
|
|
1075
|
+
return [...acc, { v: row.bbc, t: 's', s: rightAlign }];
|
|
1076
|
+
}, [{ v: 'Last 6 months' }]);
|
|
1077
|
+
const totalRevolver = headSourceData.map((dataRow) => dataRow.revolverCollateralTotal);
|
|
1078
|
+
const totalRevolverBalance = headSourceData.map((dataRow) => dataRow.revolverLoanBalance);
|
|
1079
|
+
const headRoom = headSourceData.map((dataRow) => {
|
|
1080
|
+
if (dataRow.revolverCollateralTotal === 0) {
|
|
1081
|
+
return 0;
|
|
1082
|
+
}
|
|
1083
|
+
return new decimal_js_1.default(dataRow.revolverCollateralTotal).sub(dataRow.revolverLoanBalance).div(dataRow.revolverCollateralTotal).toDP(2).toNumber();
|
|
1084
|
+
});
|
|
1085
|
+
const totalRevolverWithStyle = totalRevolver.map((v) => ({
|
|
1086
|
+
v,
|
|
1087
|
+
t: 'n',
|
|
1088
|
+
z: numberFormat.thousands,
|
|
1089
|
+
}));
|
|
1090
|
+
const totalRevolverBalanceWithStyle = totalRevolverBalance.map((v) => ({
|
|
1091
|
+
v,
|
|
1092
|
+
t: 'n',
|
|
1093
|
+
z: numberFormat.thousands,
|
|
1094
|
+
}));
|
|
1095
|
+
const headRoomWithStyle = headRoom.map((v) => ({ v, t: 'n', z: '0.00%' }));
|
|
1096
|
+
const headRoomRows = [
|
|
1097
|
+
[{ v: 'Total Revolver Availability', t: 's' }, ...totalRevolverWithStyle],
|
|
1098
|
+
[{ v: 'Revolver Loan Balance', t: 's' }, ...totalRevolverBalanceWithStyle],
|
|
1099
|
+
[{ v: 'Headroom %', t: 's' }, ...headRoomWithStyle],
|
|
1100
|
+
];
|
|
1101
|
+
return [headRoomHeader, ...headRoomRows];
|
|
1102
|
+
};
|
|
1103
|
+
const headRoomData = await getHeadRoomRows();
|
|
1104
|
+
// TOP 5
|
|
1105
|
+
const getTop5Data = async () => {
|
|
1106
|
+
const top5SKUs_new = await this.borrowerSummaryService.getTop5SKUsData(borrowerId);
|
|
1107
|
+
const top5CustomerConcentration_new = await this.borrowerSummaryService.getTop5CustomersConcentrationData(borrowerId);
|
|
1108
|
+
const gap = 1;
|
|
1109
|
+
const titleStyleLeft = {
|
|
1110
|
+
font: { bold: true, name: 'Calibri' },
|
|
1111
|
+
alignment: { horizontal: 'left' },
|
|
1112
|
+
};
|
|
1113
|
+
const titleStyleRight = {
|
|
1114
|
+
font: { bold: true, name: 'Calibri' },
|
|
1115
|
+
alignment: { horizontal: 'right' },
|
|
1116
|
+
};
|
|
1117
|
+
const getTop5SummaryTable = (title, headers, rows, startColIndex) => {
|
|
1118
|
+
const result = [];
|
|
1119
|
+
const total = rows.reduce((acc, row) => new decimal_js_1.default(acc).add(row.totalAmount).toNumber(), 0);
|
|
1120
|
+
// Title
|
|
1121
|
+
result.push(Array(startColIndex).fill({ v: '' }).concat([
|
|
1122
|
+
{ v: title, t: 's', s: titleStyleLeft },
|
|
1123
|
+
{ v: '' },
|
|
1124
|
+
{ v: '' },
|
|
1125
|
+
]));
|
|
1126
|
+
// Headers
|
|
1127
|
+
result.push(Array(startColIndex).fill({ v: '' }).concat(headers.map((header, index) => ({ v: header, t: 's', s: index > 0 ? titleStyleRight : titleStyleLeft }))));
|
|
1128
|
+
// Data rows
|
|
1129
|
+
for (const row of rows.slice(0, 5)) {
|
|
1130
|
+
result.push(Array(startColIndex).fill({ v: '' }).concat([
|
|
1131
|
+
{ v: row._id, t: 's' },
|
|
1132
|
+
{
|
|
1133
|
+
v: row.totalAmount,
|
|
1134
|
+
t: 'n',
|
|
1135
|
+
z: numberFormat.thousands,
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
v: new decimal_js_1.default(row.totalAmount).div(total).toDP(4).toNumber(),
|
|
1139
|
+
t: 'n',
|
|
1140
|
+
z: numberFormat.percent,
|
|
1141
|
+
},
|
|
1142
|
+
]));
|
|
1143
|
+
}
|
|
1144
|
+
return result;
|
|
1145
|
+
};
|
|
1146
|
+
const leftTable = getTop5SummaryTable('Top 5 SKU', ['SKU', 'Value', '%'], top5SKUs_new, 1);
|
|
1147
|
+
const rightTable = getTop5SummaryTable('Top 5 customers', ['Customer', 'Value', '%'], top5CustomerConcentration_new, gap);
|
|
1148
|
+
const maxRows = Math.max(leftTable.length, rightTable.length);
|
|
1149
|
+
const combined = [];
|
|
1150
|
+
for (let i = 0; i < maxRows; i++) {
|
|
1151
|
+
combined.push([
|
|
1152
|
+
...(leftTable[i] || []),
|
|
1153
|
+
...(rightTable[i] || []),
|
|
1154
|
+
]);
|
|
1155
|
+
}
|
|
1156
|
+
return combined;
|
|
1157
|
+
};
|
|
1158
|
+
const top5Data = await getTop5Data();
|
|
1159
|
+
// COMPLIANCE REPORT
|
|
1160
|
+
const complianceItemsHeader = {
|
|
1161
|
+
name: 'Item',
|
|
1162
|
+
dueDate: 'Due date',
|
|
1163
|
+
progress: 'Progress',
|
|
1164
|
+
submittedDate: 'Submitted date',
|
|
1165
|
+
status: 'Status',
|
|
1166
|
+
};
|
|
1167
|
+
const complianceItemsHeaderKeys = Object.keys(complianceItemsHeader);
|
|
1168
|
+
const complianceItemsHeaderAsArray = (0, common_helper_1.reorderObject)(complianceItemsHeader, complianceItemsHeaderKeys);
|
|
1169
|
+
const complianceItemsHeaderAsArrayWithStyles = Object.values(complianceItemsHeaderAsArray).reduce((acc, v) => [...acc, {
|
|
1170
|
+
v,
|
|
1171
|
+
t: 's',
|
|
1172
|
+
}], []);
|
|
1173
|
+
const complianceBorrower = complianceBorrowersMap.get(borrowerId);
|
|
1174
|
+
const fullComplianceBorrower = await this.complianceBorrowersService.getFullComplianceBorrowerById(complianceBorrower._id.toString());
|
|
1175
|
+
const dueItems = [];
|
|
1176
|
+
const acceptedItems = [];
|
|
1177
|
+
const previousMonthStart = (0, dayjs_1.default)(start).utcOffset(0).subtract(1, 'months').startOf('month');
|
|
1178
|
+
if (fullComplianceBorrower) {
|
|
1179
|
+
fullComplianceBorrower.items.forEach((item) => {
|
|
1180
|
+
item.instances.forEach((instance) => {
|
|
1181
|
+
const row = {
|
|
1182
|
+
name: `${item.item?.name} (${(0, dayjs_1.default)(instance.nextDate).format('MMM.D, YYYY')})`,
|
|
1183
|
+
dueDate: instance.nextDate ? (0, dayjs_1.default)(instance.nextDate).format('YYYY/MM/DD') : '-',
|
|
1184
|
+
progress: instance.progress?.text ?? '-',
|
|
1185
|
+
submittedDate: instance.submittedDate ? (0, dayjs_1.default)(instance.submittedDate).format('YYYY/MM/DD') : '-',
|
|
1186
|
+
status: instance.status,
|
|
1187
|
+
};
|
|
1188
|
+
if (instance.status === ComplianceItem_model_1.EComplianceItemStatus.ACCEPTED) {
|
|
1189
|
+
if (previousMonthStart.isBefore((0, dayjs_1.default)(instance.submittedDate))) {
|
|
1190
|
+
acceptedItems.push(row);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
else {
|
|
1194
|
+
dueItems.push(row);
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
const dueItemsDataAsArrays = dueItems.map((row) => (0, common_helper_1.reorderObject)(row, complianceItemsHeaderKeys));
|
|
1200
|
+
const dueItemsDataAsArraysWithStyles = Object.values([...dueItemsDataAsArrays]).map((dataRow) => {
|
|
1201
|
+
return Object.values(dataRow).map((v) => ({ v, t: 's' }));
|
|
1202
|
+
});
|
|
1203
|
+
const acceptedItemsDataAsArrays = acceptedItems.map((row) => (0, common_helper_1.reorderObject)(row, complianceItemsHeaderKeys));
|
|
1204
|
+
const acceptedItemsDataAsArraysWithStyles = Object.values([...acceptedItemsDataAsArrays]).map((dataRow) => {
|
|
1205
|
+
return Object.values(dataRow).map((v) => ({ v, t: 's' }));
|
|
1206
|
+
});
|
|
1207
|
+
return [
|
|
1208
|
+
borrowerTitle,
|
|
1209
|
+
borrowerSubTitle,
|
|
1210
|
+
[{ v: 'ALL VALUES $\'000' }],
|
|
1211
|
+
emptyRow,
|
|
1212
|
+
getSegment(`REVOLVER LOAN BALANCE & COLLATERAL - AS AT ${(0, dayjs_1.default)(lastBBC.bbcDate).utcOffset(0).format(dateFormat)} (LAST SIGNED BBC)`),
|
|
1213
|
+
headerWithStyles,
|
|
1214
|
+
...inventoryDataWithStyles,
|
|
1215
|
+
emptyRow,
|
|
1216
|
+
...receivableDataWithStyles,
|
|
1217
|
+
emptyRow,
|
|
1218
|
+
...reserveDataWithStyles,
|
|
1219
|
+
emptyRow,
|
|
1220
|
+
...loanBalanceTotalDataAsArraysWithStyles,
|
|
1221
|
+
...termLoanData,
|
|
1222
|
+
getSegment('LOAN ECONOMICS'),
|
|
1223
|
+
...loanEconomicsData,
|
|
1224
|
+
emptyRow,
|
|
1225
|
+
getSegment('FINANCIALS'),
|
|
1226
|
+
emptyRow,
|
|
1227
|
+
...financialData,
|
|
1228
|
+
emptyRow,
|
|
1229
|
+
getSegment('HEADROOM'),
|
|
1230
|
+
emptyRow,
|
|
1231
|
+
...headRoomData,
|
|
1232
|
+
emptyRow,
|
|
1233
|
+
getSegment('TOP 5'),
|
|
1234
|
+
emptyRow,
|
|
1235
|
+
...top5Data,
|
|
1236
|
+
emptyRow,
|
|
1237
|
+
getSegment('COMPLIANCE REPORT'),
|
|
1238
|
+
complianceItemsHeaderAsArrayWithStyles,
|
|
1239
|
+
emptyRow,
|
|
1240
|
+
...dueItemsDataAsArraysWithStyles,
|
|
1241
|
+
emptyRow,
|
|
1242
|
+
...acceptedItemsDataAsArraysWithStyles,
|
|
1243
|
+
];
|
|
1244
|
+
}
|
|
1245
|
+
;
|
|
1246
|
+
async getPaidAmount(productId) {
|
|
1247
|
+
const loanCharges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
1248
|
+
const yieldLoanCharges = loanCharges.filter((charge) => charge.includeInYield);
|
|
1249
|
+
const totals = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
1250
|
+
{
|
|
1251
|
+
'$match': {
|
|
1252
|
+
'chargeId': {
|
|
1253
|
+
'$in': yieldLoanCharges.map((charge) => charge._id),
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
}, {
|
|
1257
|
+
'$group': {
|
|
1258
|
+
'_id': null,
|
|
1259
|
+
'totalAmountPaid': {
|
|
1260
|
+
'$sum': '$amountPaid',
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
}, {
|
|
1264
|
+
'$project': {
|
|
1265
|
+
'_id': 0,
|
|
1266
|
+
'totalAmountPaid': {
|
|
1267
|
+
'$round': [
|
|
1268
|
+
'$totalAmountPaid', 2,
|
|
1269
|
+
],
|
|
1270
|
+
},
|
|
1271
|
+
},
|
|
1272
|
+
},
|
|
1273
|
+
]);
|
|
1274
|
+
if (totals.length > 0) {
|
|
1275
|
+
return totals[0].totalAmountPaid;
|
|
1276
|
+
}
|
|
1277
|
+
return 0;
|
|
1278
|
+
}
|
|
1279
|
+
;
|
|
1280
|
+
async getNewSummaryData(borrowerIds, start, end) {
|
|
1281
|
+
const allBorrowers = await Borrower_model_1.BorrowerModel.find().lean();
|
|
1282
|
+
allBorrowers.forEach((borrower) => borrowersMap.set(borrower._id.toString(), borrower));
|
|
1283
|
+
const allProducts = await LoanProducts_model_1.LoanProduct.find().lean();
|
|
1284
|
+
allProducts.forEach((product) => productsMap.set(product._id.toString(), product));
|
|
1285
|
+
const allCharges = await LoanCharges_model_1.LoanCharge.find().lean();
|
|
1286
|
+
allCharges.forEach((charge) => chargesMap.set(charge._id.toString(), charge));
|
|
1287
|
+
const allComplianceBorrowers = await BorrowerCompliance_model_1.BorrowerCompliance.find().lean();
|
|
1288
|
+
allComplianceBorrowers.forEach((borrower) => complianceBorrowersMap.set(borrower.borrower.toString(), borrower));
|
|
1289
|
+
const mainData = await this.getMainData(borrowerIds, start, end);
|
|
1290
|
+
const borrowersSummary = await this.getAllBorrowersSummary(borrowerIds, start, end);
|
|
1291
|
+
return { ...mainData, ...borrowersSummary };
|
|
1292
|
+
}
|
|
1293
|
+
;
|
|
1294
|
+
}
|
|
1295
|
+
exports.NewSummaryExcel = NewSummaryExcel;
|