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,935 @@
|
|
|
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.LoanStatementService = void 0;
|
|
7
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
8
|
+
const utc_1 = __importDefault(require("dayjs/plugin/utc"));
|
|
9
|
+
const timezone_1 = __importDefault(require("dayjs/plugin/timezone"));
|
|
10
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
11
|
+
const loan_types_enum_1 = require("../enums/loan-types.enum");
|
|
12
|
+
const common_helper_1 = require("../helpers/common.helper");
|
|
13
|
+
const loan_charge_type_enum_1 = require("../enums/loan-charge-type.enum");
|
|
14
|
+
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
|
|
15
|
+
const LoanCharges_model_1 = require("../models/LoanCharges.model");
|
|
16
|
+
const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
17
|
+
const Borrower_model_1 = require("../models/Borrower.model");
|
|
18
|
+
const LoanPayment_model_1 = require("../models/LoanPayment.model");
|
|
19
|
+
const date_helper_1 = require("../helpers/date.helper");
|
|
20
|
+
const TermLoanCalculated_model_1 = require("../models/TermLoanCalculated.model");
|
|
21
|
+
const TermLoan_model_1 = require("../models/TermLoan.model");
|
|
22
|
+
const loan_statement_db_1 = require("../db/loan-statement.db");
|
|
23
|
+
const BBCDate_model_1 = require("../models/BBCDate.model");
|
|
24
|
+
const financial_indexes_service_1 = require("./financial-indexes.service");
|
|
25
|
+
dayjs_1.default.extend(utc_1.default);
|
|
26
|
+
dayjs_1.default.extend(timezone_1.default);
|
|
27
|
+
class LoanStatementService {
|
|
28
|
+
financialIndexesService;
|
|
29
|
+
loanChargesService;
|
|
30
|
+
loanPaymentsService;
|
|
31
|
+
loanStatementBalanceService;
|
|
32
|
+
loanStatementStatusService;
|
|
33
|
+
loanTransactionsService;
|
|
34
|
+
termLoanService;
|
|
35
|
+
uploadsService;
|
|
36
|
+
getLoanStatementEffectsService;
|
|
37
|
+
constructor(financialIndexesService, loanChargesService, loanPaymentsService, loanStatementBalanceService, loanStatementStatusService, loanTransactionsService, termLoanService, uploadsService, getLoanStatementEffectsService) {
|
|
38
|
+
this.financialIndexesService = financialIndexesService;
|
|
39
|
+
this.loanChargesService = loanChargesService;
|
|
40
|
+
this.loanPaymentsService = loanPaymentsService;
|
|
41
|
+
this.loanStatementBalanceService = loanStatementBalanceService;
|
|
42
|
+
this.loanStatementStatusService = loanStatementStatusService;
|
|
43
|
+
this.loanTransactionsService = loanTransactionsService;
|
|
44
|
+
this.termLoanService = termLoanService;
|
|
45
|
+
this.uploadsService = uploadsService;
|
|
46
|
+
this.getLoanStatementEffectsService = getLoanStatementEffectsService;
|
|
47
|
+
}
|
|
48
|
+
getGroupedData(statements) {
|
|
49
|
+
const groups = {
|
|
50
|
+
[loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE]: 0,
|
|
51
|
+
[loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE]: 0,
|
|
52
|
+
[loan_charge_type_enum_1.ELoanChargeType.UNUSED_LINE_FEE]: 0,
|
|
53
|
+
[loan_charge_type_enum_1.ELoanChargeType.WIRE_FEE]: 0,
|
|
54
|
+
[loan_charge_type_enum_1.ELoanChargeType.ANNUAL_LINE_FEE]: 0,
|
|
55
|
+
};
|
|
56
|
+
const otherStatements = [];
|
|
57
|
+
let totalAmount = 0;
|
|
58
|
+
const groupedData = statements.reduce((acc, record) => {
|
|
59
|
+
totalAmount = new decimal_js_1.default(totalAmount).add(record.amount).toNumber();
|
|
60
|
+
if (record.chargeType === loan_charge_type_enum_1.ELoanChargeType.OTHER || record.chargeType === loan_charge_type_enum_1.ELoanChargeType.RECOVERABLE) {
|
|
61
|
+
otherStatements.push(record);
|
|
62
|
+
return acc;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
...acc,
|
|
66
|
+
[record.chargeType]: new decimal_js_1.default(acc[record.chargeType]).add(record.amount).toNumber(),
|
|
67
|
+
};
|
|
68
|
+
}, groups);
|
|
69
|
+
return { groupedData, otherStatements };
|
|
70
|
+
}
|
|
71
|
+
async getTransactionById(transactionId) {
|
|
72
|
+
return LoanStatementTransaction_model_1.LoanStatementTransactionModel.findById(transactionId).lean();
|
|
73
|
+
}
|
|
74
|
+
async getFirstStatementTransaction(productId) {
|
|
75
|
+
const loanCharges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
76
|
+
const chargesIds = loanCharges.map((charge) => charge._id);
|
|
77
|
+
const firstStatementTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
78
|
+
{
|
|
79
|
+
$match: {
|
|
80
|
+
'chargeId': { '$in': chargesIds },
|
|
81
|
+
},
|
|
82
|
+
}, {
|
|
83
|
+
$sort: {
|
|
84
|
+
'date': 1,
|
|
85
|
+
'createdAt': 1,
|
|
86
|
+
'order': 1,
|
|
87
|
+
},
|
|
88
|
+
}, {
|
|
89
|
+
$limit: 1,
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
if (!firstStatementTransactions.length) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
return firstStatementTransactions[0];
|
|
96
|
+
}
|
|
97
|
+
async getBorrowerWithPeriods(borrowerId, period, splitPeriods = false) {
|
|
98
|
+
return (0, loan_statement_db_1.getBorrowerWithPeriods)(borrowerId, period, splitPeriods);
|
|
99
|
+
}
|
|
100
|
+
async getPeriods(selectedPeriod, productId) {
|
|
101
|
+
return (0, loan_statement_db_1.getPeriods)(selectedPeriod, productId);
|
|
102
|
+
}
|
|
103
|
+
getDays(period) {
|
|
104
|
+
const days = [];
|
|
105
|
+
for (let day = (0, dayjs_1.default)(period.start); day.isBefore(period.end); day = day.add(1, 'day')) {
|
|
106
|
+
days.push(day.format('YYYY-MM-DD'));
|
|
107
|
+
}
|
|
108
|
+
return days;
|
|
109
|
+
}
|
|
110
|
+
async calculateBorrowerStatement(borrowerId, selectedPeriod) {
|
|
111
|
+
const products = await this.loanChargesService.getLoanProducts(borrowerId);
|
|
112
|
+
for (const product of products) {
|
|
113
|
+
const periods = await this.getPeriods(selectedPeriod, product._id.toString());
|
|
114
|
+
if (selectedPeriod === 'ENTIRE_LOAN') {
|
|
115
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(product._id.toString());
|
|
116
|
+
await this.cleanProductAllStatements(charges.map((charge) => charge._id.toString()));
|
|
117
|
+
}
|
|
118
|
+
await this.calculateProductStatement(product._id.toString(), periods);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async recalculateAllProducts() {
|
|
122
|
+
const borrowers = await Borrower_model_1.BorrowerModel.find({ active: true });
|
|
123
|
+
for (const borrower of borrowers) {
|
|
124
|
+
const products = await this.loanChargesService.getLoanProducts(borrower._id.toString());
|
|
125
|
+
await Promise.all(products.map(async (product) => {
|
|
126
|
+
await this.loanTransactionsService.recalculateProduct(product._id.toString());
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async calculateAllBorrowers() {
|
|
131
|
+
const statementDate = this.loanStatementStatusService.getStatementDateFromStringPeriod('CURRENT_MONTH');
|
|
132
|
+
const date = this.loanStatementStatusService.getDateFromStatementDate(statementDate);
|
|
133
|
+
const periods = this.loanStatementStatusService.getPeriodsFromStatementDate(statementDate);
|
|
134
|
+
const borrowers = await Borrower_model_1.BorrowerModel.find({ active: true });
|
|
135
|
+
for (const borrower of borrowers) {
|
|
136
|
+
const products = await this.loanChargesService.getLoanProducts(borrower._id.toString());
|
|
137
|
+
await Promise.all(products.map(async (product) => {
|
|
138
|
+
const validationError = await this.loanStatementStatusService.validateDate(product._id.toString(), date);
|
|
139
|
+
if (!validationError) {
|
|
140
|
+
try {
|
|
141
|
+
console.log('calculating borrower/product', borrower.name, product.code);
|
|
142
|
+
await this.calculateProductStatement(product._id.toString(), periods);
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
console.error(e);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async calculateProductStatement(productId, periods) {
|
|
152
|
+
await Promise.all(periods.map(async (period) => {
|
|
153
|
+
const days = this.getDays(period);
|
|
154
|
+
for (const day of days) {
|
|
155
|
+
await this.calculateStatement(productId, period, new Date(day));
|
|
156
|
+
}
|
|
157
|
+
const statementDate = this.loanStatementStatusService.getStatementDateFromPeriod(period);
|
|
158
|
+
await this.loanStatementStatusService.updateStatementStatus(productId, statementDate, true);
|
|
159
|
+
}));
|
|
160
|
+
const startDate = periods[0].start.startOf('day').toDate();
|
|
161
|
+
await this.loanStatementBalanceService.calculateProductBalance(productId, startDate);
|
|
162
|
+
}
|
|
163
|
+
async useFloatingBalance(productId) {
|
|
164
|
+
const product = await this.loanChargesService.getLoanProductById(productId);
|
|
165
|
+
return product.type === loan_types_enum_1.ELoanTypes.REVOLVER;
|
|
166
|
+
}
|
|
167
|
+
async calculateStatement(productId, period, statementDate) {
|
|
168
|
+
if (!statementDate) {
|
|
169
|
+
statementDate = (0, dayjs_1.default)(new Date()).startOf('day').toDate();
|
|
170
|
+
}
|
|
171
|
+
const primeRate = await this.financialIndexesService.getFinancialIndexValue(financial_indexes_service_1.EFinancialIndex.PRIME_RATE, statementDate);
|
|
172
|
+
if (!primeRate) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
await this.cleanProductStatement(productId, statementDate);
|
|
176
|
+
const balance = await this.loanChargesService.getLoanProductBalance(productId, statementDate);
|
|
177
|
+
const loanStatementEffectsService = this.getLoanStatementEffectsService();
|
|
178
|
+
await loanStatementEffectsService.saveMonthData(productId, statementDate);
|
|
179
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
180
|
+
const product = await this.loanChargesService.getLoanProductById(productId);
|
|
181
|
+
const daysPerYear = 360;
|
|
182
|
+
const dayInMonth = (0, dayjs_1.default)(period.end).diff(period.start, 'days') + 1;
|
|
183
|
+
let orderIndex = 0;
|
|
184
|
+
for (const charge of charges) {
|
|
185
|
+
if (charge.applyFrom.getTime() > statementDate.getTime()) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const statementDateFormat = (0, dayjs_1.default)(statementDate).format('YYYY-MM-DD');
|
|
189
|
+
let calculateFee = false;
|
|
190
|
+
switch (charge.frequency) {
|
|
191
|
+
case loan_types_enum_1.EChargeFrequencies.DISBURSEMENT: {
|
|
192
|
+
calculateFee = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case loan_types_enum_1.EChargeFrequencies.DAILY: {
|
|
196
|
+
calculateFee = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case loan_types_enum_1.EChargeFrequencies.WEEKLY: {
|
|
200
|
+
if ((0, dayjs_1.default)(charge.applyFrom).day() === (0, dayjs_1.default)(statementDate).day()) {
|
|
201
|
+
calculateFee = true;
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case loan_types_enum_1.EChargeFrequencies.MONTHLY: {
|
|
206
|
+
const lastDayOfMonthFormat = (0, dayjs_1.default)(statementDate).endOf('month').format('YYYY-MM-DD');
|
|
207
|
+
if (lastDayOfMonthFormat === statementDateFormat) {
|
|
208
|
+
calculateFee = true;
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case loan_types_enum_1.EChargeFrequencies.ANNUAL: {
|
|
213
|
+
const chargeDate = (0, dayjs_1.default)(charge.applyFrom).year(statementDate.getFullYear()).format('YYYY-MM-DD');
|
|
214
|
+
if (chargeDate === statementDateFormat) {
|
|
215
|
+
calculateFee = true;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case loan_types_enum_1.EChargeFrequencies.ONE_TIME: {
|
|
220
|
+
const chargeDate = (0, dayjs_1.default)(charge.applyFrom).format('YYYY-MM-DD');
|
|
221
|
+
if (chargeDate === statementDateFormat) {
|
|
222
|
+
calculateFee = true;
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const useFloating = await this.useFloatingBalance(productId);
|
|
228
|
+
const getBalance = () => {
|
|
229
|
+
return Math.max(0, useFloating ? balance.floatedBalance : balance.balance);
|
|
230
|
+
};
|
|
231
|
+
if (calculateFee) {
|
|
232
|
+
let fee = 0;
|
|
233
|
+
let calculatedFee = 0;
|
|
234
|
+
let percentFee = charge.percent;
|
|
235
|
+
if (charge.chargeType === loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE) {
|
|
236
|
+
percentFee = new decimal_js_1.default(primeRate).add(percentFee).toDP(4).toNumber();
|
|
237
|
+
const minPercent = product.minPercent ?? 0;
|
|
238
|
+
const maxPercent = (product.maxPercent ?? 0) === 0 ? 1 : product.maxPercent;
|
|
239
|
+
percentFee = Math.min(Math.max(percentFee, minPercent), maxPercent);
|
|
240
|
+
}
|
|
241
|
+
const dailyPercent = new decimal_js_1.default(percentFee).div(daysPerYear).toNumber();
|
|
242
|
+
switch (charge.calculationBasis) {
|
|
243
|
+
case loan_types_enum_1.EChargeCalculationBasis.DAILY_BALANCE:
|
|
244
|
+
if ((0, dayjs_1.default)(statementDate).isAfter((0, dayjs_1.default)(charge.applyFrom))) {
|
|
245
|
+
fee = new decimal_js_1.default(getBalance()).mul(dailyPercent).toDP(2).toNumber();
|
|
246
|
+
}
|
|
247
|
+
break;
|
|
248
|
+
case loan_types_enum_1.EChargeCalculationBasis.AVERAGE_MONTHLY_BALANCE:
|
|
249
|
+
if ((0, dayjs_1.default)(statementDate).isAfter((0, dayjs_1.default)(charge.applyFrom))) {
|
|
250
|
+
fee = new decimal_js_1.default(getBalance()).mul(percentFee).div(dayInMonth).toDP(2).toNumber();
|
|
251
|
+
}
|
|
252
|
+
if (!percentFee && charge.minimumAmount) {
|
|
253
|
+
fee = new decimal_js_1.default(charge.minimumAmount).div(dayInMonth).toDP(2).toNumber();
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
case loan_types_enum_1.EChargeCalculationBasis.UNUSED_AMOUNT:
|
|
257
|
+
if ((0, dayjs_1.default)(statementDate).isAfter((0, dayjs_1.default)(charge.applyFrom))) {
|
|
258
|
+
calculatedFee = new decimal_js_1.default(product.commitment).sub(getBalance()).mul(charge.percent).div(daysPerYear).toDP(2).toNumber();
|
|
259
|
+
fee = Math.max(calculatedFee, 0);
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
case loan_types_enum_1.EChargeCalculationBasis.COMMITMENT:
|
|
263
|
+
fee = new decimal_js_1.default(product.commitment).mul(charge.percent).toDP(2).toNumber();
|
|
264
|
+
break;
|
|
265
|
+
case loan_types_enum_1.EChargeCalculationBasis.DISBURSEMENT_AMOUNT:
|
|
266
|
+
if ((0, dayjs_1.default)(statementDate).isAfter((0, dayjs_1.default)(charge.applyFrom))) {
|
|
267
|
+
fee = await this.calculateDisbursementFee(product._id.toString(), charge, statementDate);
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case loan_types_enum_1.EChargeCalculationBasis.FIXED:
|
|
271
|
+
if ((0, dayjs_1.default)(statementDate).isAfter((0, dayjs_1.default)(charge.applyFrom))) {
|
|
272
|
+
calculatedFee = new decimal_js_1.default(getBalance()).mul(dailyPercent).toDP(2).toNumber();
|
|
273
|
+
fee = Math.max(calculatedFee, charge.minimumAmount);
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
const checkMinAmount = (fee) => {
|
|
278
|
+
if (charge.minimumAmount > 0) {
|
|
279
|
+
if (charge.chargeType === loan_charge_type_enum_1.ELoanChargeType.WIRE_FEE) {
|
|
280
|
+
return fee ? Math.max(charge.minimumAmount, fee) : 0;
|
|
281
|
+
}
|
|
282
|
+
if (charge.frequency === loan_types_enum_1.EChargeFrequencies.DAILY && charge.calculationBasis === loan_types_enum_1.EChargeCalculationBasis.AVERAGE_MONTHLY_BALANCE) {
|
|
283
|
+
return fee;
|
|
284
|
+
}
|
|
285
|
+
return fee ? Math.max(charge.minimumAmount, fee) : charge.minimumAmount;
|
|
286
|
+
}
|
|
287
|
+
return fee;
|
|
288
|
+
};
|
|
289
|
+
fee = checkMinAmount(fee);
|
|
290
|
+
if (fee !== null && fee > 0) {
|
|
291
|
+
const newStatement = {
|
|
292
|
+
order: orderIndex,
|
|
293
|
+
date: statementDate,
|
|
294
|
+
chargeId: charge._id,
|
|
295
|
+
amount: fee,
|
|
296
|
+
amountPaid: 0,
|
|
297
|
+
isSystem: true,
|
|
298
|
+
memo: 'System Generated',
|
|
299
|
+
balance: 0,
|
|
300
|
+
};
|
|
301
|
+
const newStatementDoc = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.create(newStatement);
|
|
302
|
+
await newStatementDoc.save();
|
|
303
|
+
orderIndex = orderIndex + 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
await this.reorderStatementForDate(productId, statementDate);
|
|
308
|
+
}
|
|
309
|
+
async reorderStatementForDate(productId, date) {
|
|
310
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
311
|
+
const lastTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
312
|
+
{
|
|
313
|
+
$match: {
|
|
314
|
+
'chargeId': { $in: charges.map((charge) => charge._id) },
|
|
315
|
+
'date': { $eq: date },
|
|
316
|
+
'isSystem': false,
|
|
317
|
+
},
|
|
318
|
+
}, {
|
|
319
|
+
$sort: {
|
|
320
|
+
'order': 1,
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
]);
|
|
324
|
+
if (lastTransactions.length > 0) {
|
|
325
|
+
let lastOrder = await this.getLastTransactionOrder(lastTransactions[0]);
|
|
326
|
+
for (const transaction of lastTransactions) {
|
|
327
|
+
lastOrder = lastOrder + 1;
|
|
328
|
+
await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndUpdate(transaction._id, { order: lastOrder });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async getBalancesDaily(productId, period) {
|
|
333
|
+
const days = this.getDays({ start: (0, dayjs_1.default)(period.start).utcOffset(0), end: (0, dayjs_1.default)(period.end).utcOffset(0) });
|
|
334
|
+
return Promise.all(days.map(async (day) => {
|
|
335
|
+
const balance = await this.loanChargesService.getLoanProductBalance(productId, new Date(day));
|
|
336
|
+
return { day, ...balance };
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
async calculateDisbursementFee(productId, charge, day) {
|
|
340
|
+
const totalDoc = await this.loanTransactionsService.getDisbursementTransactions(productId, day);
|
|
341
|
+
if (totalDoc === 0) {
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
return new decimal_js_1.default(totalDoc).mul(charge.minimumAmount).toDP(2).toNumber();
|
|
345
|
+
}
|
|
346
|
+
async cleanProductStatement(productId, statementDate) {
|
|
347
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
348
|
+
await LoanStatementTransaction_model_1.LoanStatementTransactionModel.deleteMany({
|
|
349
|
+
chargeId: { $in: charges.map((charge) => charge._id) },
|
|
350
|
+
date: statementDate,
|
|
351
|
+
isSystem: true,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
async cleanProductAllStatements(chargesId) {
|
|
355
|
+
await LoanStatementTransaction_model_1.LoanStatementTransactionModel.deleteMany({ chargeId: { $in: chargesId }, isSystem: true });
|
|
356
|
+
}
|
|
357
|
+
async getProductStatementPdfData(productId, start, end) {
|
|
358
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
|
|
359
|
+
const borrower = await Borrower_model_1.BorrowerModel.findById(product.borrowerId).lean();
|
|
360
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
361
|
+
const periodStart = (0, dayjs_1.default)(start);
|
|
362
|
+
const periodEnd = (0, dayjs_1.default)(end);
|
|
363
|
+
const statements = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
364
|
+
{
|
|
365
|
+
$match: {
|
|
366
|
+
$and: [
|
|
367
|
+
{ 'chargeId': { $in: charges.map((charge) => charge._id) } },
|
|
368
|
+
{ 'date': { $gte: periodStart.toDate() } },
|
|
369
|
+
{ 'date': { $lte: periodEnd.toDate() } },
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
}, {
|
|
373
|
+
$sort: {
|
|
374
|
+
'date': 1,
|
|
375
|
+
'order': 1,
|
|
376
|
+
},
|
|
377
|
+
}, {
|
|
378
|
+
$lookup: {
|
|
379
|
+
from: 'loan_charges',
|
|
380
|
+
localField: 'chargeId',
|
|
381
|
+
foreignField: '_id',
|
|
382
|
+
as: 'charge',
|
|
383
|
+
},
|
|
384
|
+
}, {
|
|
385
|
+
$unwind: {
|
|
386
|
+
'path': '$charge',
|
|
387
|
+
},
|
|
388
|
+
}, {
|
|
389
|
+
$addFields: {
|
|
390
|
+
'chargeType': '$charge.chargeType',
|
|
391
|
+
'chargeName': '$charge.name',
|
|
392
|
+
},
|
|
393
|
+
}, {
|
|
394
|
+
$unset: ['_id', 'productId', 'chargeId', 'charge', 'createdAt', 'updatedAt', '__v'],
|
|
395
|
+
},
|
|
396
|
+
]);
|
|
397
|
+
const totalAmount = 0;
|
|
398
|
+
const { groupedData, otherStatements } = this.getGroupedData(statements);
|
|
399
|
+
const filter = {
|
|
400
|
+
borrowerId: borrower._id.toString(),
|
|
401
|
+
productId: product._id.toString(),
|
|
402
|
+
periodEnd: periodEnd.toDate(),
|
|
403
|
+
periodStart: periodStart.toDate(),
|
|
404
|
+
};
|
|
405
|
+
const transactions = await this.loanTransactionsService.getLoanTransactions(filter, null, true);
|
|
406
|
+
return {
|
|
407
|
+
groupedData,
|
|
408
|
+
otherStatements,
|
|
409
|
+
totalAmount,
|
|
410
|
+
transactions,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
async getProductShortStatementRepresentation(productId, start, end) {
|
|
414
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
|
|
415
|
+
const borrower = await Borrower_model_1.BorrowerModel.findById(product.borrowerId).lean();
|
|
416
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
417
|
+
const periodStart = (0, dayjs_1.default)(start);
|
|
418
|
+
const periodEnd = (0, dayjs_1.default)(end);
|
|
419
|
+
const statements = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
420
|
+
{
|
|
421
|
+
$match: {
|
|
422
|
+
$and: [
|
|
423
|
+
{ 'chargeId': { $in: charges.map((charge) => charge._id) } },
|
|
424
|
+
{ 'date': { $gte: periodStart.toDate() } },
|
|
425
|
+
{ 'date': { $lte: periodEnd.toDate() } },
|
|
426
|
+
],
|
|
427
|
+
},
|
|
428
|
+
}, {
|
|
429
|
+
$sort: {
|
|
430
|
+
'date': 1,
|
|
431
|
+
'order': 1,
|
|
432
|
+
},
|
|
433
|
+
}, {
|
|
434
|
+
$lookup: {
|
|
435
|
+
from: 'loan_charges',
|
|
436
|
+
localField: 'chargeId',
|
|
437
|
+
foreignField: '_id',
|
|
438
|
+
as: 'charge',
|
|
439
|
+
},
|
|
440
|
+
}, {
|
|
441
|
+
$unwind: {
|
|
442
|
+
'path': '$charge',
|
|
443
|
+
},
|
|
444
|
+
}, {
|
|
445
|
+
$addFields: {
|
|
446
|
+
'chargeType': '$charge.chargeType',
|
|
447
|
+
},
|
|
448
|
+
}, {
|
|
449
|
+
$unset: ['_id', 'productId', 'chargeId', 'charge', 'createdAt', 'updatedAt', '__v'],
|
|
450
|
+
},
|
|
451
|
+
]);
|
|
452
|
+
const totalAmount = 0;
|
|
453
|
+
const { groupedData, otherStatements } = this.getGroupedData(statements);
|
|
454
|
+
const chargeMap = {
|
|
455
|
+
[loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE]: 'INTEREST',
|
|
456
|
+
[loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE]: 'ADMIN FEE',
|
|
457
|
+
[loan_charge_type_enum_1.ELoanChargeType.UNUSED_LINE_FEE]: 'UNUSED',
|
|
458
|
+
[loan_charge_type_enum_1.ELoanChargeType.WIRE_FEE]: 'WIRE FEES',
|
|
459
|
+
[loan_charge_type_enum_1.ELoanChargeType.ANNUAL_LINE_FEE]: 'ANNUAL LINE',
|
|
460
|
+
[loan_charge_type_enum_1.ELoanChargeType.OTHER]: 'OTHER',
|
|
461
|
+
[loan_charge_type_enum_1.ELoanChargeType.RECOVERABLE]: 'RECOVERABLE',
|
|
462
|
+
};
|
|
463
|
+
const preparedStatement = Object.entries(groupedData).map(([chargeType, amount]) => {
|
|
464
|
+
return [chargeMap[chargeType], null, amount];
|
|
465
|
+
});
|
|
466
|
+
otherStatements.forEach((record) => {
|
|
467
|
+
preparedStatement.push([chargeMap[record.chargeType], record.memo, record.amount]);
|
|
468
|
+
});
|
|
469
|
+
const preparedStatementHeader = [
|
|
470
|
+
[`STATEMENT: ${borrower.name}`],
|
|
471
|
+
['PERIOD FROM', periodStart.format('MM/DD/YYYY'), 'PERIOD TO', (0, dayjs_1.default)(end.slice(0, 10)).format('MM/DD/YYYY')],
|
|
472
|
+
[null],
|
|
473
|
+
['THIS PERIOD STATEMENT', null, 'AMOUNT'],
|
|
474
|
+
];
|
|
475
|
+
const preparedStatementTotal = [
|
|
476
|
+
[null],
|
|
477
|
+
[null, 'TOTAL', totalAmount],
|
|
478
|
+
[null],
|
|
479
|
+
[null],
|
|
480
|
+
];
|
|
481
|
+
const preparedTransactionsHeader = [
|
|
482
|
+
[`LOAN TRANSACTIONS: ${product.name}`],
|
|
483
|
+
['Date', 'Type', 'Amount', 'Reference', 'Bank', 'Balance'],
|
|
484
|
+
];
|
|
485
|
+
const filter = {
|
|
486
|
+
borrowerId: borrower._id.toString(),
|
|
487
|
+
productId: product._id.toString(),
|
|
488
|
+
periodEnd: periodEnd.toDate(),
|
|
489
|
+
periodStart: periodStart.toDate(),
|
|
490
|
+
};
|
|
491
|
+
const transactions = await this.loanTransactionsService.getLoanTransactions(filter, null, true);
|
|
492
|
+
const preparedTransactions = transactions.map((transaction) => {
|
|
493
|
+
return [
|
|
494
|
+
(0, dayjs_1.default)(transaction.date).format('MM/DD/YYYY'),
|
|
495
|
+
transaction.transactionType,
|
|
496
|
+
transaction.amount,
|
|
497
|
+
transaction.reference,
|
|
498
|
+
transaction.bank?.name ?? '',
|
|
499
|
+
transaction.balance,
|
|
500
|
+
];
|
|
501
|
+
});
|
|
502
|
+
return [
|
|
503
|
+
...preparedStatementHeader,
|
|
504
|
+
...preparedStatement,
|
|
505
|
+
...preparedStatementTotal,
|
|
506
|
+
...preparedTransactionsHeader,
|
|
507
|
+
...preparedTransactions,
|
|
508
|
+
];
|
|
509
|
+
}
|
|
510
|
+
async createStatementTransaction(transaction) {
|
|
511
|
+
const pureTransactions = (0, common_helper_1.createFilteredObject)(transaction, LoanStatementTransaction_model_1.LOAN_STATEMENT_TRANSACTION_FIELDS);
|
|
512
|
+
const lastOrder = await this.getLastTransactionOrder(transaction);
|
|
513
|
+
await this.saveLoanTransaction({ ...pureTransactions, isSystem: false, amountPaid: 0, order: lastOrder + 1 });
|
|
514
|
+
}
|
|
515
|
+
async updateStatementTransaction(transaction) {
|
|
516
|
+
const foundTransaction = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findById(transaction._id).lean();
|
|
517
|
+
if (!foundTransaction) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const pureTransactions = foundTransaction.isSystem
|
|
521
|
+
? (0, common_helper_1.createFilteredObject)({
|
|
522
|
+
...foundTransaction,
|
|
523
|
+
memo: transaction.memo,
|
|
524
|
+
}, LoanStatementTransaction_model_1.LOAN_STATEMENT_TRANSACTION_FIELDS)
|
|
525
|
+
: (0, common_helper_1.createFilteredObject)(transaction, LoanStatementTransaction_model_1.LOAN_STATEMENT_TRANSACTION_FIELDS);
|
|
526
|
+
await this.saveLoanTransaction(pureTransactions);
|
|
527
|
+
}
|
|
528
|
+
async saveLoanTransaction(transaction) {
|
|
529
|
+
if (!transaction._id) {
|
|
530
|
+
delete transaction._id;
|
|
531
|
+
const updatedStatementTransaction = new LoanStatementTransaction_model_1.LoanStatementTransactionModel(transaction);
|
|
532
|
+
await updatedStatementTransaction.save();
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndUpdate(transaction._id, transaction, { new: true });
|
|
536
|
+
}
|
|
537
|
+
const charge = await LoanCharges_model_1.LoanCharge.findById(transaction.chargeId);
|
|
538
|
+
await this.loanStatementBalanceService.calculateProductBalance(charge.productId.toString(), new Date(transaction.date));
|
|
539
|
+
const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(transaction.date));
|
|
540
|
+
await this.loanStatementStatusService.updateStatementStatus(charge.productId.toString(), statementDate, false);
|
|
541
|
+
}
|
|
542
|
+
async deleteStatementTransaction(transactionId) {
|
|
543
|
+
const foundTransaction = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findById(transactionId).lean();
|
|
544
|
+
if (!foundTransaction) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const charge = await LoanCharges_model_1.LoanCharge.findById(foundTransaction.chargeId);
|
|
548
|
+
await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndDelete(transactionId);
|
|
549
|
+
await this.loanStatementBalanceService.calculateProductBalance(charge.productId.toString(), new Date(foundTransaction.date));
|
|
550
|
+
const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(foundTransaction.date));
|
|
551
|
+
await this.loanStatementStatusService.updateStatementStatus(charge.productId.toString(), statementDate, false);
|
|
552
|
+
}
|
|
553
|
+
async addBalancesToStatements(transactionDocs) {
|
|
554
|
+
return await Promise.all(transactionDocs.map(async (item) => {
|
|
555
|
+
const charge = await LoanCharges_model_1.LoanCharge.findById(item.chargeId).lean();
|
|
556
|
+
const balances = await this.loanChargesService.getLoanProductBalance(charge.productId.toString(), item.date);
|
|
557
|
+
return { ...item, floatedBalance: balances.floatedBalance };
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
async getLastTransactionOrder(transaction, onlySystem = false) {
|
|
561
|
+
const charge = await LoanCharges_model_1.LoanCharge.findById(transaction.chargeId);
|
|
562
|
+
if (!charge) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(charge.productId.toString());
|
|
566
|
+
const getOnlySystem = () => onlySystem ? { isSystem: true } : {};
|
|
567
|
+
const lastTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
568
|
+
{
|
|
569
|
+
$match: {
|
|
570
|
+
'chargeId': { $in: charges.map((charge) => charge._id) },
|
|
571
|
+
'_id': { $ne: transaction._id },
|
|
572
|
+
'date': { $eq: new Date(transaction.date) },
|
|
573
|
+
...getOnlySystem(),
|
|
574
|
+
},
|
|
575
|
+
}, {
|
|
576
|
+
$sort: {
|
|
577
|
+
'date': -1,
|
|
578
|
+
'order': -1,
|
|
579
|
+
},
|
|
580
|
+
}, {
|
|
581
|
+
$limit: 1,
|
|
582
|
+
},
|
|
583
|
+
]);
|
|
584
|
+
if (!lastTransactions || lastTransactions.length === 0) {
|
|
585
|
+
return 0;
|
|
586
|
+
}
|
|
587
|
+
return lastTransactions[0].order;
|
|
588
|
+
}
|
|
589
|
+
async getPreviousTransaction(transaction) {
|
|
590
|
+
const charge = await LoanCharges_model_1.LoanCharge.findById(transaction.chargeId);
|
|
591
|
+
if (!charge) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(charge.productId.toString());
|
|
595
|
+
const lastTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
596
|
+
{
|
|
597
|
+
$match: {
|
|
598
|
+
'chargeId': { $in: charges.map((charge) => charge._id) },
|
|
599
|
+
'_id': { $ne: transaction._id },
|
|
600
|
+
$or: [
|
|
601
|
+
{
|
|
602
|
+
$and: [
|
|
603
|
+
{ 'date': { $eq: transaction.date } },
|
|
604
|
+
{ 'order': { $lt: transaction.order } },
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
'date': { $lt: transaction.date },
|
|
609
|
+
},
|
|
610
|
+
],
|
|
611
|
+
},
|
|
612
|
+
}, {
|
|
613
|
+
$sort: {
|
|
614
|
+
'date': -1,
|
|
615
|
+
'order': -1,
|
|
616
|
+
},
|
|
617
|
+
}, {
|
|
618
|
+
$limit: 1,
|
|
619
|
+
},
|
|
620
|
+
]);
|
|
621
|
+
if (!lastTransactions || lastTransactions.length === 0) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
return lastTransactions[0];
|
|
625
|
+
}
|
|
626
|
+
async getTransactionsFromDate(productId, startDate) {
|
|
627
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
628
|
+
return LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
629
|
+
{
|
|
630
|
+
$match: {
|
|
631
|
+
'chargeId': { $in: charges.map((charge) => charge._id) },
|
|
632
|
+
'date': { $gte: startDate },
|
|
633
|
+
},
|
|
634
|
+
}, {
|
|
635
|
+
$sort: {
|
|
636
|
+
'date': 1,
|
|
637
|
+
'order': 1,
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
]);
|
|
641
|
+
}
|
|
642
|
+
async getTransactionsForPeriod(productId, period) {
|
|
643
|
+
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
644
|
+
return LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
645
|
+
{
|
|
646
|
+
$match: {
|
|
647
|
+
$and: [
|
|
648
|
+
{ 'chargeId': { $in: charges.map((charge) => charge._id) } },
|
|
649
|
+
{ 'date': { $gte: period.start } },
|
|
650
|
+
{ 'date': { $lt: period.end } },
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
}, {
|
|
654
|
+
$sort: {
|
|
655
|
+
'date': 1,
|
|
656
|
+
'order': 1,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
]);
|
|
660
|
+
}
|
|
661
|
+
async recalculateAllBalances(borrowerIds, userId) {
|
|
662
|
+
for (const borrowerId of borrowerIds) {
|
|
663
|
+
const allProducts = await LoanProducts_model_1.LoanProduct.find({ borrowerId }).lean();
|
|
664
|
+
for (const product of allProducts) {
|
|
665
|
+
const firstTransaction = await this.loanTransactionsService.getFirstTransaction(product._id.toString());
|
|
666
|
+
if (firstTransaction) {
|
|
667
|
+
try {
|
|
668
|
+
await this.loanTransactionsService.updateLoanTransaction(firstTransaction);
|
|
669
|
+
}
|
|
670
|
+
catch (e) {
|
|
671
|
+
console.error(e);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const firstStatementTransaction = await this.getFirstStatementTransaction(product._id.toString());
|
|
675
|
+
if (firstStatementTransaction) {
|
|
676
|
+
try {
|
|
677
|
+
await this.updateStatementTransaction(firstStatementTransaction);
|
|
678
|
+
}
|
|
679
|
+
catch (e) {
|
|
680
|
+
console.error(e);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const allPayments = await LoanPayment_model_1.LoanPaymentModel.find({ productId: product._id.toString() }).lean();
|
|
684
|
+
for (const payment of allPayments) {
|
|
685
|
+
await this.loanPaymentsService.updateLoanPayment(payment, userId);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
async recalculateAllStatements(selectedPeriod, borrowerIds) {
|
|
691
|
+
for (const borrowerId of borrowerIds) {
|
|
692
|
+
try {
|
|
693
|
+
await this.calculateBorrowerStatement(borrowerId, selectedPeriod);
|
|
694
|
+
}
|
|
695
|
+
catch (e) {
|
|
696
|
+
console.error('recalculateAllStatements - ERROR, borrowerId', borrowerId, e);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async recalculateAllTermLoans(borrowerIds) {
|
|
701
|
+
for (const borrowerId of borrowerIds) {
|
|
702
|
+
try {
|
|
703
|
+
const products = await this.loanChargesService.getLoanProducts(borrowerId);
|
|
704
|
+
const termProducts = products.filter((product) => product.type === loan_types_enum_1.ELoanTypes.TERM);
|
|
705
|
+
await Promise.all(termProducts.map(async (termProduct) => {
|
|
706
|
+
await this.termLoanService.calculateTermLoan(termProduct._id.toString(), true);
|
|
707
|
+
}));
|
|
708
|
+
}
|
|
709
|
+
catch (e) {
|
|
710
|
+
console.error('recalculateAllTermLoans - ERROR, borrowerId', borrowerId, e);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async getStatementBorrowersTransactions(start, end, selectedPeriod, selectedBorrowerIds, ledgerType) {
|
|
715
|
+
return (0, loan_statement_db_1.getStatementBorrowersTransactions)(start, end, selectedPeriod, selectedBorrowerIds, ledgerType);
|
|
716
|
+
}
|
|
717
|
+
async getBorrowerComplianceData(borrowerId, date) {
|
|
718
|
+
const end = (0, dayjs_1.default)(date).utcOffset(0).subtract(1, 'month').endOf('month');
|
|
719
|
+
const start = end.startOf('month');
|
|
720
|
+
const products = await this.loanChargesService.getLoanProducts(borrowerId);
|
|
721
|
+
const data = {
|
|
722
|
+
productTotals: products.reduce((acc, product) => ({ ...acc, [product._id.toString()]: 0 }), {}),
|
|
723
|
+
total: 0,
|
|
724
|
+
totalPaid: 0,
|
|
725
|
+
dueDate: null,
|
|
726
|
+
};
|
|
727
|
+
await Promise.all(products.map(async (product) => {
|
|
728
|
+
const lastTransaction = await this.loanStatementBalanceService.getLastStatementTransactionForDate(product._id.toString(), end.toDate());
|
|
729
|
+
const transactions = await this.getTransactionsForPeriod(product._id.toString(), {
|
|
730
|
+
start: start.toDate(),
|
|
731
|
+
end: end.toDate(),
|
|
732
|
+
});
|
|
733
|
+
data.totalPaid = transactions.reduce((acc, t) => new decimal_js_1.default(acc).add(t.amountPaid).toNumber(), 0);
|
|
734
|
+
if (lastTransaction) {
|
|
735
|
+
data.productTotals[product._id.toString()] = lastTransaction.balance ?? 0;
|
|
736
|
+
}
|
|
737
|
+
if (product.type === loan_types_enum_1.ELoanTypes.TERM) {
|
|
738
|
+
const termLoan = await this.termLoanService.getTermLoan(product._id.toString(), true);
|
|
739
|
+
if (termLoan) {
|
|
740
|
+
const lastDate = termLoan.calculated.slice().reverse().find((c) => (0, dayjs_1.default)(c.relevantStatement).isBefore(date));
|
|
741
|
+
if (lastDate) {
|
|
742
|
+
const calculatedResultsForDate = termLoan.calculated.filter((c) => c.relevantStatement === lastDate.relevantStatement);
|
|
743
|
+
const totalPrincipal = calculatedResultsForDate.reduce((acc, c) => new decimal_js_1.default(acc).add(c.monthlyPrincipal).toNumber(), 0);
|
|
744
|
+
const totalPrincipalPaid = calculatedResultsForDate.reduce((totalAcc, c) => {
|
|
745
|
+
const totalMonthPaid = c.payments
|
|
746
|
+
? c.payments.reduce((acc, p) => new decimal_js_1.default(acc).add(p.amount).toNumber(), 0)
|
|
747
|
+
: 0;
|
|
748
|
+
return new decimal_js_1.default(totalAcc).add(totalMonthPaid).toNumber();
|
|
749
|
+
}, 0);
|
|
750
|
+
data.productTotals[product._id.toString()] = new decimal_js_1.default(data.productTotals[product._id.toString()]).add(totalPrincipal).sub(totalPrincipalPaid).toNumber();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}));
|
|
755
|
+
data.total = Object.values(data.productTotals).reduce((acc, productDue) => new decimal_js_1.default(acc).add(productDue).toNumber(), 0);
|
|
756
|
+
return data;
|
|
757
|
+
}
|
|
758
|
+
async getOutstandingStatements(borrowerIds, selectedDate, fullMonth) {
|
|
759
|
+
const results = await Promise.all(borrowerIds.map(async (borrowerId) => {
|
|
760
|
+
const borrower = await Borrower_model_1.BorrowerModel.findById(borrowerId).lean();
|
|
761
|
+
const products = await this.loanChargesService.getLoanProducts(borrowerId);
|
|
762
|
+
return await Promise.all(products.map(async (product) => {
|
|
763
|
+
const relevantStatementEndMonth = fullMonth
|
|
764
|
+
? (0, dayjs_1.default)(selectedDate).utcOffset(0).startOf('month').subtract(1, 'seconds')
|
|
765
|
+
: (0, dayjs_1.default)(selectedDate).utcOffset(0).endOf('month');
|
|
766
|
+
const relevantStatementEnd = fullMonth
|
|
767
|
+
? (0, dayjs_1.default)(selectedDate).utcOffset(0).startOf('month').subtract(1, 'seconds')
|
|
768
|
+
: (0, dayjs_1.default)(selectedDate).utcOffset(0).endOf('day');
|
|
769
|
+
const relevantStatementStart = (0, dayjs_1.default)(selectedDate).utcOffset(0).startOf('month').subtract(1, 'seconds').startOf('month');
|
|
770
|
+
const paymentFilterEnd = (0, dayjs_1.default)(selectedDate).utcOffset(0).endOf('day');
|
|
771
|
+
const paymentFilterStart = relevantStatementStart;
|
|
772
|
+
const lastTransaction = await this.loanTransactionsService.getLastTransactionForDate(product._id.toString(), (0, dayjs_1.default)(selectedDate).endOf('day').toDate());
|
|
773
|
+
let actualBalance = 0;
|
|
774
|
+
if (lastTransaction) {
|
|
775
|
+
actualBalance = new decimal_js_1.default(lastTransaction.balance).toDP(2).toNumber();
|
|
776
|
+
}
|
|
777
|
+
const statementFilter = {
|
|
778
|
+
productIds: [product._id.toString()],
|
|
779
|
+
start: relevantStatementStart.toDate(),
|
|
780
|
+
end: relevantStatementEnd.toDate(),
|
|
781
|
+
};
|
|
782
|
+
const paymentFilter = {
|
|
783
|
+
productIds: [product._id.toString()],
|
|
784
|
+
start: paymentFilterStart.toDate(),
|
|
785
|
+
end: paymentFilterEnd.toDate(),
|
|
786
|
+
};
|
|
787
|
+
const statementTransactions = await this.loanChargesService.getLoanStatementsForForProductWithCharges(statementFilter);
|
|
788
|
+
const statementPayments = await this.loanPaymentsService.getLoanPayments(paymentFilter, null);
|
|
789
|
+
const paidStatementIds = statementPayments.reduce((acc, s) => [...acc, ...s.paid.map((p) => p.statementId.toString())], []);
|
|
790
|
+
const fees = {
|
|
791
|
+
[loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE]: 0,
|
|
792
|
+
[loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE]: 0,
|
|
793
|
+
[loan_charge_type_enum_1.ELoanChargeType.UNUSED_LINE_FEE]: 0,
|
|
794
|
+
[loan_charge_type_enum_1.ELoanChargeType.WIRE_FEE]: 0,
|
|
795
|
+
[loan_charge_type_enum_1.ELoanChargeType.OTHER]: 0,
|
|
796
|
+
[loan_charge_type_enum_1.ELoanChargeType.RECOVERABLE]: 0,
|
|
797
|
+
[loan_charge_type_enum_1.ELoanChargeType.ANNUAL_LINE_FEE]: 0,
|
|
798
|
+
};
|
|
799
|
+
const sumResults = statementTransactions.reduce((acc, t) => {
|
|
800
|
+
const amountPaid = paidStatementIds.includes(t._id.toString()) ? t.amountPaid : 0;
|
|
801
|
+
return {
|
|
802
|
+
...acc,
|
|
803
|
+
[t.charge.chargeType]: new decimal_js_1.default(acc[t.charge.chargeType] ?? 0).add(t.amount ?? 0).sub(amountPaid ?? 0).toNumber(),
|
|
804
|
+
};
|
|
805
|
+
}, fees);
|
|
806
|
+
let principal = 0;
|
|
807
|
+
let principalExpectedDate = null;
|
|
808
|
+
const termLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId: product._id.toString(), actual: true });
|
|
809
|
+
if (termLoan) {
|
|
810
|
+
const calculatedTermLoan = await TermLoanCalculated_model_1.TermLoanCalculatedModel.findOne({
|
|
811
|
+
termLoanId: termLoan._id.toString(),
|
|
812
|
+
relevantStatement: relevantStatementEndMonth.format('YYYY-MM-DD'),
|
|
813
|
+
});
|
|
814
|
+
if (calculatedTermLoan) {
|
|
815
|
+
if (calculatedTermLoan.payments && calculatedTermLoan.payments.length > 0) {
|
|
816
|
+
const totalPayment = calculatedTermLoan.payments.reduce((acc, p) => new decimal_js_1.default(acc ?? 0).add(p.amount ?? 0).toNumber(), 0);
|
|
817
|
+
principal = new decimal_js_1.default(calculatedTermLoan.monthlyPrincipal).sub(totalPayment).toDP(2).toNumber();
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
principal = calculatedTermLoan.monthlyPrincipal;
|
|
821
|
+
principalExpectedDate = calculatedTermLoan.paymentDueDate;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
borrowerCode: borrower.code,
|
|
827
|
+
...product,
|
|
828
|
+
actualBalance,
|
|
829
|
+
...sumResults,
|
|
830
|
+
principal,
|
|
831
|
+
principalExpectedDate,
|
|
832
|
+
};
|
|
833
|
+
}));
|
|
834
|
+
}));
|
|
835
|
+
return results
|
|
836
|
+
.filter((res) => !!res)
|
|
837
|
+
.reduce((acc, res) => ([...acc, ...res]), [])
|
|
838
|
+
.sort((a, b) => (b.code.toUpperCase() < a.code.toUpperCase() ? 1 : -1));
|
|
839
|
+
}
|
|
840
|
+
async getOutstandingStatementsExcel(borrowerIds, selectedDate, fullMonth) {
|
|
841
|
+
const results = await this.getOutstandingStatements(borrowerIds, selectedDate, fullMonth);
|
|
842
|
+
const header = [
|
|
843
|
+
['Date', (0, dayjs_1.default)(selectedDate).format(date_helper_1.defaultDateFormat)],
|
|
844
|
+
['', '', 'Outstanding statement'],
|
|
845
|
+
['client ID', 'product ID', 'product name', 'balance', 'interest', 'admin', 'unused', 'wire & other', 'recoverable expenses', 'principal'],
|
|
846
|
+
];
|
|
847
|
+
const onlyData = results.map((res) => {
|
|
848
|
+
return [
|
|
849
|
+
res.borrowerCode,
|
|
850
|
+
res.code,
|
|
851
|
+
res.name,
|
|
852
|
+
res.actualBalance,
|
|
853
|
+
res[loan_charge_type_enum_1.ELoanChargeType.INTEREST_FEE],
|
|
854
|
+
res[loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE],
|
|
855
|
+
res[loan_charge_type_enum_1.ELoanChargeType.UNUSED_LINE_FEE],
|
|
856
|
+
new decimal_js_1.default(res[loan_charge_type_enum_1.ELoanChargeType.WIRE_FEE]).add(res[loan_charge_type_enum_1.ELoanChargeType.OTHER]).add(res[loan_charge_type_enum_1.ELoanChargeType.ANNUAL_LINE_FEE]).toNumber(),
|
|
857
|
+
res[loan_charge_type_enum_1.ELoanChargeType.RECOVERABLE],
|
|
858
|
+
res.principal,
|
|
859
|
+
];
|
|
860
|
+
});
|
|
861
|
+
return await this.uploadsService.convertDataToFile([{ data: [...header, ...onlyData] }]);
|
|
862
|
+
}
|
|
863
|
+
async getBorrowerProductTotals(borrowerId) {
|
|
864
|
+
return await this.getBorrowerComplianceData(borrowerId, new Date());
|
|
865
|
+
}
|
|
866
|
+
async getProductsTotals(productIds) {
|
|
867
|
+
const totals = {};
|
|
868
|
+
await Promise.all(productIds.map(async (productId) => {
|
|
869
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId);
|
|
870
|
+
if (product) {
|
|
871
|
+
const borrowerData = await this.getBorrowerComplianceData(product.borrowerId.toString(), new Date());
|
|
872
|
+
Object.entries(borrowerData.productTotals).forEach(([id, value]) => {
|
|
873
|
+
if (productIds.includes(id)) {
|
|
874
|
+
totals[id] = value;
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}));
|
|
879
|
+
return totals;
|
|
880
|
+
}
|
|
881
|
+
getAccruedStatement(lastStatements) {
|
|
882
|
+
const totals = lastStatements.reduce((acc, s) => {
|
|
883
|
+
return {
|
|
884
|
+
amountPaid: new decimal_js_1.default(acc.amountPaid).add(s.amountPaid).toNumber(),
|
|
885
|
+
amount: new decimal_js_1.default(acc.amount).add(s.amount).toNumber(),
|
|
886
|
+
};
|
|
887
|
+
}, { amountPaid: 0, amount: 0 });
|
|
888
|
+
return new decimal_js_1.default(totals.amount).sub(totals.amountPaid).toNumber();
|
|
889
|
+
}
|
|
890
|
+
async calculateAccruedStatementForBBC(bbcDateId) {
|
|
891
|
+
const bbcDate = await BBCDate_model_1.BBCDateModel.findById(bbcDateId);
|
|
892
|
+
if (!bbcDate) {
|
|
893
|
+
return 0;
|
|
894
|
+
}
|
|
895
|
+
const charges = await this.loanChargesService.getLoanCharges(bbcDate.borrowerId.toString());
|
|
896
|
+
const chargesId = charges.map((c) => c._id);
|
|
897
|
+
const lastStatements = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
898
|
+
{
|
|
899
|
+
$match: {
|
|
900
|
+
'date': { $lt: bbcDate.bbcDate },
|
|
901
|
+
'chargeId': { $in: chargesId },
|
|
902
|
+
$expr: { $gt: ['$amount', '$amountPaid'] },
|
|
903
|
+
},
|
|
904
|
+
}, {
|
|
905
|
+
$project: {
|
|
906
|
+
_id: 0,
|
|
907
|
+
amount: 1,
|
|
908
|
+
amountPaid: 1,
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
]);
|
|
912
|
+
return this.getAccruedStatement(lastStatements);
|
|
913
|
+
}
|
|
914
|
+
async calculateAccruedStatementForBorrower(borrowerId) {
|
|
915
|
+
const charges = await this.loanChargesService.getLoanCharges(borrowerId);
|
|
916
|
+
const chargesId = charges.map((c) => c._id);
|
|
917
|
+
const lastStatements = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
918
|
+
{
|
|
919
|
+
$match: {
|
|
920
|
+
'date': { $lt: new Date() },
|
|
921
|
+
'chargeId': { $in: chargesId },
|
|
922
|
+
$expr: { $gt: ['$amount', '$amountPaid'] },
|
|
923
|
+
},
|
|
924
|
+
}, {
|
|
925
|
+
$project: {
|
|
926
|
+
_id: 0,
|
|
927
|
+
amount: 1,
|
|
928
|
+
amountPaid: 1,
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
]);
|
|
932
|
+
return this.getAccruedStatement(lastStatements);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
exports.LoanStatementService = LoanStatementService;
|