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,281 @@
|
|
|
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.FinancialComplianceService = void 0;
|
|
7
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
8
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
9
|
+
const Borrower_model_1 = require("../models/Borrower.model");
|
|
10
|
+
const microservice_tasks_db_1 = require("../db/microservice-tasks.db");
|
|
11
|
+
const microservice_task_enum_1 = require("../enums/microservice-task.enum");
|
|
12
|
+
const BorrowerCompliance_model_1 = require("../models/BorrowerCompliance.model");
|
|
13
|
+
const FinancialComplianceBorrower_model_1 = __importDefault(require("../models/FinancialComplianceBorrower.model"));
|
|
14
|
+
const FinancialCompliance_model_1 = __importDefault(require("../models/FinancialCompliance.model"));
|
|
15
|
+
class FinancialComplianceService {
|
|
16
|
+
globalsRepository;
|
|
17
|
+
globalsService;
|
|
18
|
+
getLoanStatementService;
|
|
19
|
+
getNodemailerService;
|
|
20
|
+
organizationsService;
|
|
21
|
+
constructor(globalsRepository, globalsService, getLoanStatementService, getNodemailerService, organizationsService) {
|
|
22
|
+
this.globalsRepository = globalsRepository;
|
|
23
|
+
this.globalsService = globalsService;
|
|
24
|
+
this.getLoanStatementService = getLoanStatementService;
|
|
25
|
+
this.getNodemailerService = getNodemailerService;
|
|
26
|
+
this.organizationsService = organizationsService;
|
|
27
|
+
}
|
|
28
|
+
async getFinancialComplianceSettings() {
|
|
29
|
+
return await FinancialCompliance_model_1.default.findOne({}) ?? await new FinancialCompliance_model_1.default({}).save();
|
|
30
|
+
}
|
|
31
|
+
async updateSettings(newSettings) {
|
|
32
|
+
try {
|
|
33
|
+
return await FinancialCompliance_model_1.default.findOneAndUpdate({}, { ...newSettings }, { new: true });
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
console.error({ e });
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async refreshFinancial() {
|
|
41
|
+
try {
|
|
42
|
+
const newSettings = {
|
|
43
|
+
invoiceSentDate: null,
|
|
44
|
+
dueDate: (0, dayjs_1.default)().date(7).startOf('day').toDate(),
|
|
45
|
+
stopFundingDate: (0, dayjs_1.default)().date(10).startOf('day').toDate(),
|
|
46
|
+
};
|
|
47
|
+
await this.updateSettings(newSettings);
|
|
48
|
+
const newBorrowerSettings = {
|
|
49
|
+
invoiceSentDate: null,
|
|
50
|
+
dueDate: null,
|
|
51
|
+
stopFundingDate: null,
|
|
52
|
+
lastReminderSentAt: null,
|
|
53
|
+
accountClearedDate: null,
|
|
54
|
+
amountOwed: 0,
|
|
55
|
+
addToLoan: null,
|
|
56
|
+
willPayIn: null,
|
|
57
|
+
transferDone: null,
|
|
58
|
+
amountReceived: 0,
|
|
59
|
+
balanceOutstanding: 0,
|
|
60
|
+
reminderCounter: 0,
|
|
61
|
+
};
|
|
62
|
+
const borrowers = await this.getAllFinancialComplianceBorrowers();
|
|
63
|
+
await Promise.all(borrowers.map(async (borrower) => {
|
|
64
|
+
await this.updateBorrower(String(borrower._id), newBorrowerSettings);
|
|
65
|
+
}));
|
|
66
|
+
return { message: 'refreshed' };
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
console.error(e);
|
|
70
|
+
return { message: 'error', e };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async refillFinancial() {
|
|
74
|
+
const borrowers = await FinancialComplianceBorrower_model_1.default.find().lean();
|
|
75
|
+
for (const borrower of borrowers) {
|
|
76
|
+
const loanStatementService = this.getLoanStatementService();
|
|
77
|
+
const complianceData = await loanStatementService.getBorrowerComplianceData(borrower.borrower.toString(), new Date());
|
|
78
|
+
if (complianceData.total > 0) {
|
|
79
|
+
const foundBorrower = await FinancialComplianceBorrower_model_1.default.findById(borrower._id).lean();
|
|
80
|
+
if (foundBorrower) {
|
|
81
|
+
await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(borrower._id, {
|
|
82
|
+
amountOwed: complianceData.total,
|
|
83
|
+
dueDate: complianceData.dueDate,
|
|
84
|
+
amountReceived: complianceData.totalPaid,
|
|
85
|
+
balanceOutstanding: new decimal_js_1.default(complianceData.total).sub(complianceData.totalPaid).toNumber(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
async sendFinancialReminders(ids = null) {
|
|
93
|
+
const reminderBorrowers = await this.sendExternalReminder(ids, true);
|
|
94
|
+
setTimeout(async () => {
|
|
95
|
+
await this.sendInternalReminders(reminderBorrowers.remindedBorrowers, reminderBorrowers.stopFundingBorrowers);
|
|
96
|
+
}, 60000);
|
|
97
|
+
}
|
|
98
|
+
async sendExternalReminder(ids = null, ignoreRules) {
|
|
99
|
+
const financialSettings = await FinancialCompliance_model_1.default.findOne({});
|
|
100
|
+
const financeBorrowers = ids
|
|
101
|
+
? await FinancialComplianceBorrower_model_1.default.find({ _id: { $in: ids } }).populate('borrower')
|
|
102
|
+
: await FinancialComplianceBorrower_model_1.default.find({}).populate('borrower');
|
|
103
|
+
const stopFundingBorrowers = [];
|
|
104
|
+
const remindedBorrowers = [];
|
|
105
|
+
await Promise.all(financeBorrowers.map(async (b) => {
|
|
106
|
+
const nodemailerService = this.getNodemailerService();
|
|
107
|
+
const fullBorrower = b.toObject();
|
|
108
|
+
const organization = await this.organizationsService.getOrganizationForBorrower(fullBorrower.borrower._id.toString());
|
|
109
|
+
fullBorrower.invoiceSentDate = fullBorrower.invoiceSentDate ?? financialSettings.invoiceSentDate;
|
|
110
|
+
fullBorrower.stopFundingDate = fullBorrower.stopFundingDate ?? financialSettings.stopFundingDate;
|
|
111
|
+
fullBorrower.dueDate = fullBorrower.dueDate ?? financialSettings.dueDate;
|
|
112
|
+
const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: fullBorrower.borrower._id });
|
|
113
|
+
if (!foundBorrower.isEmailingActive) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
fullBorrower.mainEmails = foundBorrower.mainEmails ?? [];
|
|
117
|
+
fullBorrower.financialEmails = foundBorrower.financialEmails ?? [];
|
|
118
|
+
const currentDate = (0, dayjs_1.default)();
|
|
119
|
+
const stopFundingDate = (0, dayjs_1.default)(fullBorrower.stopFundingDate);
|
|
120
|
+
const invoiceSentDate = (0, dayjs_1.default)(fullBorrower.invoiceSentDate);
|
|
121
|
+
const isTodayStopFundingDate = currentDate.isSame(stopFundingDate, 'day');
|
|
122
|
+
if (fullBorrower.balanceOutstanding > 0 && isTodayStopFundingDate && !b.transferDone) {
|
|
123
|
+
const email = {
|
|
124
|
+
text: null,
|
|
125
|
+
subject: `${organization.name.toUpperCase()}: ${b.borrower.code} UNPAID Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${(0, dayjs_1.default)().subtract(1, 'month').format('MMMM')}`,
|
|
126
|
+
addresses: fullBorrower.mainEmails.map((email) => email.email),
|
|
127
|
+
customData: fullBorrower,
|
|
128
|
+
};
|
|
129
|
+
await nodemailerService.sendFinancialExternal(email, organization._id.toString(), true);
|
|
130
|
+
stopFundingBorrowers.push(fullBorrower);
|
|
131
|
+
}
|
|
132
|
+
if (!isTodayStopFundingDate && (ignoreRules || (currentDate.diff(invoiceSentDate, 'day') >= 3 && currentDate.diff(stopFundingDate, 'day') < 0))) {
|
|
133
|
+
const email = {
|
|
134
|
+
text: null,
|
|
135
|
+
subject: `${organization.name.toUpperCase()}: ${b.borrower.code} Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${(0, dayjs_1.default)().subtract(1, 'month').format('MMMM')}`,
|
|
136
|
+
addresses: [
|
|
137
|
+
...fullBorrower.financialEmails.map((email) => email.email),
|
|
138
|
+
...fullBorrower.mainEmails.map((email) => email.email),
|
|
139
|
+
],
|
|
140
|
+
customData: fullBorrower,
|
|
141
|
+
};
|
|
142
|
+
await nodemailerService.sendFinancialExternal(email, organization._id.toString());
|
|
143
|
+
}
|
|
144
|
+
if ((currentDate.diff(invoiceSentDate, 'day') > 0 && currentDate.diff(stopFundingDate, 'day') < 0)
|
|
145
|
+
|| fullBorrower.addToLoan) {
|
|
146
|
+
remindedBorrowers.push(fullBorrower);
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
return { remindedBorrowers, stopFundingBorrowers };
|
|
150
|
+
}
|
|
151
|
+
async sendInternalReminders(remindedBorrowers, stopFundingBorrowers) {
|
|
152
|
+
try {
|
|
153
|
+
const financialSettings = await FinancialCompliance_model_1.default.findOne({});
|
|
154
|
+
const remindedBorrowersUpdated = await Promise.all(remindedBorrowers.map(async (b) => {
|
|
155
|
+
const updatedBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ _id: b._id });
|
|
156
|
+
b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
|
|
157
|
+
b.reminderCounter = updatedBorrower.reminderCounter;
|
|
158
|
+
return b;
|
|
159
|
+
}));
|
|
160
|
+
const stopFundingBorrowersUpdated = await Promise.all(stopFundingBorrowers.map(async (b) => {
|
|
161
|
+
const updatedBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ _id: b._id });
|
|
162
|
+
b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
|
|
163
|
+
b.reminderCounter = updatedBorrower.reminderCounter;
|
|
164
|
+
return b;
|
|
165
|
+
}));
|
|
166
|
+
const nodemailerService = this.getNodemailerService();
|
|
167
|
+
if (remindedBorrowersUpdated.length) {
|
|
168
|
+
const financialEmails = financialSettings.financialEmails
|
|
169
|
+
.reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
|
|
170
|
+
const email = {
|
|
171
|
+
text: null,
|
|
172
|
+
subject: 'UNPAID INTEREST STATEMENTS',
|
|
173
|
+
addresses: financialEmails,
|
|
174
|
+
borrowers: remindedBorrowersUpdated,
|
|
175
|
+
};
|
|
176
|
+
await nodemailerService.sendFinancialInternal(email);
|
|
177
|
+
}
|
|
178
|
+
if (stopFundingBorrowersUpdated.length) {
|
|
179
|
+
const managementAddresses = financialSettings.managementEmails
|
|
180
|
+
.reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
|
|
181
|
+
const email = {
|
|
182
|
+
text: null,
|
|
183
|
+
subject: 'UNPAID INTEREST STATEMENTS – POTENTIAL STOP FUNDING',
|
|
184
|
+
addresses: managementAddresses,
|
|
185
|
+
borrowers: stopFundingBorrowersUpdated,
|
|
186
|
+
};
|
|
187
|
+
await nodemailerService.sendFinancialInternal(email, true);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
console.error({ e });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async createEmptyBorrower(borrowerId) {
|
|
195
|
+
const foundBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ borrower: borrowerId });
|
|
196
|
+
if (!foundBorrower) {
|
|
197
|
+
await new FinancialComplianceBorrower_model_1.default({ borrower: borrowerId }).save();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async removeBorrower(borrowerId) {
|
|
201
|
+
await FinancialComplianceBorrower_model_1.default.findByIdAndDelete(borrowerId);
|
|
202
|
+
}
|
|
203
|
+
async getAllFinancialComplianceBorrowers() {
|
|
204
|
+
const borrowers = await FinancialComplianceBorrower_model_1.default
|
|
205
|
+
.find({})
|
|
206
|
+
.populate('borrower')
|
|
207
|
+
.lean();
|
|
208
|
+
const borrowersWithEmailingStatus = await Promise.all(borrowers.map(async (borrower) => {
|
|
209
|
+
if (!borrower.borrower.active) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: borrower.borrower._id });
|
|
213
|
+
return {
|
|
214
|
+
...borrower,
|
|
215
|
+
isEmailingActive: foundBorrower?.isEmailingActive ?? true,
|
|
216
|
+
isDailyTransactionsEmailingActive: foundBorrower?.isDailyTransactionsEmailingActive ?? true,
|
|
217
|
+
};
|
|
218
|
+
}));
|
|
219
|
+
return borrowersWithEmailingStatus
|
|
220
|
+
.filter((borrower) => !!borrower)
|
|
221
|
+
.sort((a, b) => a.borrower.code > b.borrower.code ? 1 : -1);
|
|
222
|
+
}
|
|
223
|
+
async updateBorrower(id, updatedBorrower) {
|
|
224
|
+
try {
|
|
225
|
+
updatedBorrower.amountOwed = new decimal_js_1.default(updatedBorrower.amountOwed).toDP().toNumber();
|
|
226
|
+
updatedBorrower.amountReceived = new decimal_js_1.default(updatedBorrower.amountReceived).toDP().toNumber();
|
|
227
|
+
updatedBorrower.balanceOutstanding = Math.max(0, new decimal_js_1.default(updatedBorrower.amountOwed).sub(updatedBorrower.amountReceived).toNumber());
|
|
228
|
+
if ((updatedBorrower.amountOwed === updatedBorrower.amountReceived) && (updatedBorrower.amountOwed > 0) && !updatedBorrower.accountClearedDate) {
|
|
229
|
+
updatedBorrower.accountClearedDate = new Date();
|
|
230
|
+
}
|
|
231
|
+
return await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(id, { ...updatedBorrower }, { new: true });
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
console.error({ e });
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async updateLastReminder(financialBorrowerId) {
|
|
239
|
+
try {
|
|
240
|
+
const borrower = await FinancialComplianceBorrower_model_1.default.findById(financialBorrowerId);
|
|
241
|
+
await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(financialBorrowerId, {
|
|
242
|
+
lastReminderSentAt: new Date(),
|
|
243
|
+
reminderCounter: borrower.reminderCounter + 1,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
console.error({ e });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async sendPlaidReminders() {
|
|
251
|
+
const settings = await this.globalsService.getGlobalSetting('plaidReminded');
|
|
252
|
+
if (settings && settings.plaidReminded) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const complianceBorrowers = await BorrowerCompliance_model_1.BorrowerCompliance.find().select('+plaidAccessToken').lean();
|
|
256
|
+
const borrowersWithoutTokes = complianceBorrowers.filter((c) => !c.plaidAccessToken);
|
|
257
|
+
const notifiedBorrowers = [];
|
|
258
|
+
await Promise.all(borrowersWithoutTokes.map(async (complianceBorrower) => {
|
|
259
|
+
const borrower = await Borrower_model_1.BorrowerModel.findById(complianceBorrower.borrower);
|
|
260
|
+
if (complianceBorrower.isEmailingActive && borrower.active) {
|
|
261
|
+
const mainEmails = complianceBorrower.mainEmails.map((mainEmail) => mainEmail.email.trim());
|
|
262
|
+
const financialEmails = complianceBorrower.financialEmails.map((financialEmail) => financialEmail.email.trim());
|
|
263
|
+
const allEmails = Array.from(new Set([...mainEmails, ...financialEmails]));
|
|
264
|
+
const nodemailerService = this.getNodemailerService();
|
|
265
|
+
await nodemailerService.sendPlaidReminder({
|
|
266
|
+
addresses: allEmails,
|
|
267
|
+
subject: 'Loan Requirement: online read-only bank access',
|
|
268
|
+
text: '',
|
|
269
|
+
});
|
|
270
|
+
notifiedBorrowers.push(borrower.name);
|
|
271
|
+
}
|
|
272
|
+
}));
|
|
273
|
+
await this.globalsRepository.update({ plaidReminded: true });
|
|
274
|
+
await (0, microservice_tasks_db_1.createMicroserviceTasks)(microservice_task_enum_1.EMicroserviceTask.ENABLE_PLAID_NOTIFICATION, {}, (0, dayjs_1.default)(new Date()).add(1, 'day').toDate());
|
|
275
|
+
return notifiedBorrowers.sort();
|
|
276
|
+
}
|
|
277
|
+
async getFinancialComplianceBorrower(borrowerId) {
|
|
278
|
+
return FinancialComplianceBorrower_model_1.default.findOne({ borrower: borrowerId }).lean();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.FinancialComplianceService = FinancialComplianceService;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import Decimal from 'decimal.js';
|
|
3
|
+
|
|
4
|
+
import { IEmail } from '../interfaces/email.interface';
|
|
5
|
+
import { BorrowerModel } from '../models/Borrower.model';
|
|
6
|
+
import { createMicroserviceTasks } from '../db/microservice-tasks.db';
|
|
7
|
+
import { EMicroserviceTask } from '../enums/microservice-task.enum';
|
|
8
|
+
import { BorrowerCompliance, IComplianceBorrowerDocument } from '../models/BorrowerCompliance.model';
|
|
9
|
+
|
|
10
|
+
import FinancialComplianceBorrower, {
|
|
11
|
+
IFinancialComplianceBorrowerDocument,
|
|
12
|
+
IFinancialComplianceBorrowerDocumentWithEmailingStatus,
|
|
13
|
+
IFinancialComplianceBorrowerDocumentWithMails,
|
|
14
|
+
IFinancialComplianceBorrowerSettings,
|
|
15
|
+
} from '../models/FinancialComplianceBorrower.model';
|
|
16
|
+
import FinancialCompliance, {
|
|
17
|
+
IFinancialComplianceSettings,
|
|
18
|
+
IFinancialComplianceSettingsDocument,
|
|
19
|
+
} from '../models/FinancialCompliance.model';
|
|
20
|
+
import { EmailWithFinancial, NodemailerService } from './nodemailer.service';
|
|
21
|
+
import { GlobalsRepository } from '../repositories/globals.repository';
|
|
22
|
+
import { GlobalsService } from './globals.service';
|
|
23
|
+
import { LoanStatementService } from './loan-statement.service';
|
|
24
|
+
import { OrganizationsService } from './organizations.service';
|
|
25
|
+
|
|
26
|
+
export class FinancialComplianceService {
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
private readonly globalsRepository: GlobalsRepository,
|
|
30
|
+
private readonly globalsService: GlobalsService,
|
|
31
|
+
private readonly getLoanStatementService: () => LoanStatementService,
|
|
32
|
+
private readonly getNodemailerService: () => NodemailerService,
|
|
33
|
+
private readonly organizationsService: OrganizationsService,
|
|
34
|
+
) {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async getFinancialComplianceSettings(): Promise<IFinancialComplianceSettingsDocument> {
|
|
38
|
+
return await FinancialCompliance.findOne({}) ?? await new FinancialCompliance({}).save();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async updateSettings(newSettings: IFinancialComplianceSettings) {
|
|
42
|
+
try {
|
|
43
|
+
return await FinancialCompliance.findOneAndUpdate({}, { ...newSettings }, { new: true });
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error({ e });
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async refreshFinancial(): Promise<unknown> {
|
|
51
|
+
try {
|
|
52
|
+
const newSettings = {
|
|
53
|
+
invoiceSentDate: null,
|
|
54
|
+
dueDate: dayjs().date(7).startOf('day').toDate(),
|
|
55
|
+
stopFundingDate: dayjs().date(10).startOf('day').toDate(),
|
|
56
|
+
};
|
|
57
|
+
await this.updateSettings(newSettings);
|
|
58
|
+
|
|
59
|
+
const newBorrowerSettings: IFinancialComplianceBorrowerSettings = {
|
|
60
|
+
invoiceSentDate: null,
|
|
61
|
+
dueDate: null,
|
|
62
|
+
stopFundingDate: null,
|
|
63
|
+
lastReminderSentAt: null,
|
|
64
|
+
accountClearedDate: null,
|
|
65
|
+
amountOwed: 0,
|
|
66
|
+
addToLoan: null,
|
|
67
|
+
willPayIn: null,
|
|
68
|
+
transferDone: null,
|
|
69
|
+
amountReceived: 0,
|
|
70
|
+
balanceOutstanding: 0,
|
|
71
|
+
reminderCounter: 0,
|
|
72
|
+
};
|
|
73
|
+
const borrowers = await this.getAllFinancialComplianceBorrowers();
|
|
74
|
+
await Promise.all(borrowers.map(async (borrower) => {
|
|
75
|
+
await this.updateBorrower(String(borrower._id), newBorrowerSettings);
|
|
76
|
+
}));
|
|
77
|
+
return { message: 'refreshed' };
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error(e);
|
|
80
|
+
return { message: 'error', e };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async refillFinancial(): Promise<null> {
|
|
85
|
+
const borrowers = await FinancialComplianceBorrower.find().lean();
|
|
86
|
+
for (const borrower of borrowers) {
|
|
87
|
+
const loanStatementService = this.getLoanStatementService();
|
|
88
|
+
const complianceData = await loanStatementService.getBorrowerComplianceData(borrower.borrower.toString(), new Date());
|
|
89
|
+
if (complianceData.total > 0) {
|
|
90
|
+
const foundBorrower = await FinancialComplianceBorrower.findById(borrower._id).lean();
|
|
91
|
+
if (foundBorrower) {
|
|
92
|
+
await FinancialComplianceBorrower.findByIdAndUpdate(borrower._id, {
|
|
93
|
+
amountOwed: complianceData.total,
|
|
94
|
+
dueDate: complianceData.dueDate,
|
|
95
|
+
amountReceived: complianceData.totalPaid,
|
|
96
|
+
balanceOutstanding: new Decimal(complianceData.total).sub(complianceData.totalPaid).toNumber(),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async sendFinancialReminders(ids: string = null) {
|
|
105
|
+
const reminderBorrowers = await this.sendExternalReminder(ids, true);
|
|
106
|
+
setTimeout(async () => {
|
|
107
|
+
await this.sendInternalReminders(reminderBorrowers.remindedBorrowers, reminderBorrowers.stopFundingBorrowers);
|
|
108
|
+
}, 60_000);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async sendExternalReminder(ids: string = null, ignoreRules: boolean): Promise<any> {
|
|
112
|
+
const financialSettings: IFinancialComplianceSettingsDocument = await FinancialCompliance.findOne({});
|
|
113
|
+
const financeBorrowers = ids
|
|
114
|
+
? await FinancialComplianceBorrower.find({ _id: { $in: ids } }).populate('borrower') as unknown as IFinancialComplianceBorrowerDocumentWithMails[]
|
|
115
|
+
: await FinancialComplianceBorrower.find({}).populate('borrower') as unknown as IFinancialComplianceBorrowerDocumentWithMails[];
|
|
116
|
+
const stopFundingBorrowers = [];
|
|
117
|
+
const remindedBorrowers = [];
|
|
118
|
+
await Promise.all(financeBorrowers.map(async (b) => {
|
|
119
|
+
const nodemailerService = this.getNodemailerService();
|
|
120
|
+
const fullBorrower = b.toObject();
|
|
121
|
+
const organization = await this.organizationsService.getOrganizationForBorrower(fullBorrower.borrower._id.toString());
|
|
122
|
+
fullBorrower.invoiceSentDate = fullBorrower.invoiceSentDate ?? financialSettings.invoiceSentDate;
|
|
123
|
+
fullBorrower.stopFundingDate = fullBorrower.stopFundingDate ?? financialSettings.stopFundingDate;
|
|
124
|
+
fullBorrower.dueDate = fullBorrower.dueDate ?? financialSettings.dueDate;
|
|
125
|
+
const foundBorrower: IComplianceBorrowerDocument = await BorrowerCompliance.findOne({ borrower: fullBorrower.borrower._id });
|
|
126
|
+
if (!foundBorrower.isEmailingActive) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
fullBorrower.mainEmails = foundBorrower.mainEmails ?? [];
|
|
130
|
+
fullBorrower.financialEmails = foundBorrower.financialEmails ?? [];
|
|
131
|
+
const currentDate = dayjs();
|
|
132
|
+
const stopFundingDate = dayjs(fullBorrower.stopFundingDate);
|
|
133
|
+
const invoiceSentDate = dayjs(fullBorrower.invoiceSentDate);
|
|
134
|
+
const isTodayStopFundingDate = currentDate.isSame(stopFundingDate, 'day');
|
|
135
|
+
|
|
136
|
+
if (fullBorrower.balanceOutstanding > 0 && isTodayStopFundingDate && !b.transferDone) {
|
|
137
|
+
const email: IEmail = {
|
|
138
|
+
text: null,
|
|
139
|
+
subject: `${organization.name.toUpperCase()}: ${b.borrower.code} UNPAID Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${dayjs().subtract(1, 'month').format('MMMM')}`,
|
|
140
|
+
addresses: fullBorrower.mainEmails.map((email) => email.email),
|
|
141
|
+
customData: fullBorrower,
|
|
142
|
+
};
|
|
143
|
+
await nodemailerService.sendFinancialExternal(email, organization._id.toString(), true);
|
|
144
|
+
stopFundingBorrowers.push(fullBorrower);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!isTodayStopFundingDate && (ignoreRules || (currentDate.diff(invoiceSentDate, 'day') >= 3 && currentDate.diff(stopFundingDate, 'day') < 0))) {
|
|
148
|
+
const email: IEmail = {
|
|
149
|
+
text: null,
|
|
150
|
+
subject: `${organization.name.toUpperCase()}: ${b.borrower.code} Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${dayjs().subtract(1, 'month').format('MMMM')}`,
|
|
151
|
+
addresses: [
|
|
152
|
+
...fullBorrower.financialEmails.map((email) => email.email),
|
|
153
|
+
...fullBorrower.mainEmails.map((email) => email.email),
|
|
154
|
+
],
|
|
155
|
+
customData: fullBorrower,
|
|
156
|
+
};
|
|
157
|
+
await nodemailerService.sendFinancialExternal(email, organization._id.toString());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
(currentDate.diff(invoiceSentDate, 'day') > 0 && currentDate.diff(stopFundingDate, 'day') < 0)
|
|
162
|
+
|| fullBorrower.addToLoan
|
|
163
|
+
) {
|
|
164
|
+
remindedBorrowers.push(fullBorrower);
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
return { remindedBorrowers, stopFundingBorrowers };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async sendInternalReminders(remindedBorrowers, stopFundingBorrowers) {
|
|
172
|
+
try {
|
|
173
|
+
const financialSettings: IFinancialComplianceSettingsDocument = await FinancialCompliance.findOne({});
|
|
174
|
+
|
|
175
|
+
const remindedBorrowersUpdated = await Promise.all(remindedBorrowers.map(async (b) => {
|
|
176
|
+
const updatedBorrower = await FinancialComplianceBorrower.findOne({ _id: b._id });
|
|
177
|
+
b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
|
|
178
|
+
b.reminderCounter = updatedBorrower.reminderCounter;
|
|
179
|
+
return b;
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
const stopFundingBorrowersUpdated = await Promise.all(stopFundingBorrowers.map(async (b) => {
|
|
183
|
+
const updatedBorrower = await FinancialComplianceBorrower.findOne({ _id: b._id });
|
|
184
|
+
b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
|
|
185
|
+
b.reminderCounter = updatedBorrower.reminderCounter;
|
|
186
|
+
return b;
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
const nodemailerService = this.getNodemailerService();
|
|
190
|
+
|
|
191
|
+
if (remindedBorrowersUpdated.length) {
|
|
192
|
+
const financialEmails = financialSettings.financialEmails
|
|
193
|
+
.reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
|
|
194
|
+
const email: EmailWithFinancial = {
|
|
195
|
+
text: null,
|
|
196
|
+
subject: 'UNPAID INTEREST STATEMENTS',
|
|
197
|
+
addresses: financialEmails,
|
|
198
|
+
borrowers: remindedBorrowersUpdated,
|
|
199
|
+
};
|
|
200
|
+
await nodemailerService.sendFinancialInternal(email);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (stopFundingBorrowersUpdated.length) {
|
|
204
|
+
const managementAddresses = financialSettings.managementEmails
|
|
205
|
+
.reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
|
|
206
|
+
const email: EmailWithFinancial = {
|
|
207
|
+
text: null,
|
|
208
|
+
subject: 'UNPAID INTEREST STATEMENTS – POTENTIAL STOP FUNDING',
|
|
209
|
+
addresses: managementAddresses,
|
|
210
|
+
borrowers: stopFundingBorrowersUpdated,
|
|
211
|
+
};
|
|
212
|
+
await nodemailerService.sendFinancialInternal(email, true);
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
console.error({ e });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async createEmptyBorrower(borrowerId: string): Promise<void> {
|
|
220
|
+
const foundBorrower = await FinancialComplianceBorrower.findOne({ borrower: borrowerId });
|
|
221
|
+
if (!foundBorrower) {
|
|
222
|
+
await new FinancialComplianceBorrower({ borrower: borrowerId }).save();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async removeBorrower(borrowerId: string): Promise<void> {
|
|
227
|
+
await FinancialComplianceBorrower.findByIdAndDelete(borrowerId);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async getAllFinancialComplianceBorrowers(): Promise<IFinancialComplianceBorrowerDocumentWithEmailingStatus[]> {
|
|
231
|
+
const borrowers: IFinancialComplianceBorrowerDocument[] = await FinancialComplianceBorrower
|
|
232
|
+
.find({})
|
|
233
|
+
.populate('borrower')
|
|
234
|
+
.lean();
|
|
235
|
+
const borrowersWithEmailingStatus: IFinancialComplianceBorrowerDocumentWithEmailingStatus[] = await Promise.all(borrowers.map(async (borrower) => {
|
|
236
|
+
if (!borrower.borrower.active) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const foundBorrower: IComplianceBorrowerDocument = await BorrowerCompliance.findOne({ borrower: borrower.borrower._id });
|
|
240
|
+
return {
|
|
241
|
+
...borrower,
|
|
242
|
+
isEmailingActive: foundBorrower?.isEmailingActive ?? true,
|
|
243
|
+
isDailyTransactionsEmailingActive: foundBorrower?.isDailyTransactionsEmailingActive ?? true,
|
|
244
|
+
} as IFinancialComplianceBorrowerDocumentWithEmailingStatus;
|
|
245
|
+
}));
|
|
246
|
+
return borrowersWithEmailingStatus
|
|
247
|
+
.filter((borrower) => !!borrower)
|
|
248
|
+
.sort((a, b) => a.borrower.code > b.borrower.code ? 1 : -1);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async updateBorrower(id: string, updatedBorrower: IFinancialComplianceBorrowerSettings): Promise<IFinancialComplianceBorrowerDocument> {
|
|
252
|
+
try {
|
|
253
|
+
updatedBorrower.amountOwed = new Decimal(updatedBorrower.amountOwed).toDP().toNumber();
|
|
254
|
+
updatedBorrower.amountReceived = new Decimal(updatedBorrower.amountReceived).toDP().toNumber();
|
|
255
|
+
updatedBorrower.balanceOutstanding = Math.max(0, new Decimal(updatedBorrower.amountOwed).sub(updatedBorrower.amountReceived).toNumber());
|
|
256
|
+
if ((updatedBorrower.amountOwed === updatedBorrower.amountReceived) && (updatedBorrower.amountOwed > 0) && !updatedBorrower.accountClearedDate) {
|
|
257
|
+
updatedBorrower.accountClearedDate = new Date();
|
|
258
|
+
}
|
|
259
|
+
return await FinancialComplianceBorrower.findByIdAndUpdate(id, { ...updatedBorrower }, { new: true });
|
|
260
|
+
} catch (e) {
|
|
261
|
+
console.error({ e });
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async updateLastReminder(financialBorrowerId: string) {
|
|
267
|
+
try {
|
|
268
|
+
const borrower = await FinancialComplianceBorrower.findById(financialBorrowerId);
|
|
269
|
+
await FinancialComplianceBorrower.findByIdAndUpdate(financialBorrowerId, {
|
|
270
|
+
lastReminderSentAt: new Date(),
|
|
271
|
+
reminderCounter: borrower.reminderCounter + 1,
|
|
272
|
+
});
|
|
273
|
+
} catch (e) {
|
|
274
|
+
console.error({ e });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async sendPlaidReminders() {
|
|
279
|
+
const settings = await this.globalsService.getGlobalSetting('plaidReminded');
|
|
280
|
+
if (settings && settings.plaidReminded) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const complianceBorrowers = await BorrowerCompliance.find().select('+plaidAccessToken').lean();
|
|
284
|
+
const borrowersWithoutTokes = complianceBorrowers.filter((c) => !c.plaidAccessToken);
|
|
285
|
+
const notifiedBorrowers: string[] = [];
|
|
286
|
+
await Promise.all(borrowersWithoutTokes.map(async (complianceBorrower) => {
|
|
287
|
+
const borrower = await BorrowerModel.findById(complianceBorrower.borrower);
|
|
288
|
+
if (complianceBorrower.isEmailingActive && borrower.active) {
|
|
289
|
+
const mainEmails = complianceBorrower.mainEmails.map((mainEmail) => mainEmail.email.trim());
|
|
290
|
+
const financialEmails = complianceBorrower.financialEmails.map((financialEmail) => financialEmail.email.trim());
|
|
291
|
+
const allEmails = Array.from(new Set([...mainEmails, ...financialEmails]));
|
|
292
|
+
const nodemailerService = this.getNodemailerService();
|
|
293
|
+
await nodemailerService.sendPlaidReminder({
|
|
294
|
+
addresses: allEmails,
|
|
295
|
+
subject: 'Loan Requirement: online read-only bank access',
|
|
296
|
+
text: '',
|
|
297
|
+
});
|
|
298
|
+
notifiedBorrowers.push(borrower.name);
|
|
299
|
+
}
|
|
300
|
+
}));
|
|
301
|
+
await this.globalsRepository.update({ plaidReminded: true });
|
|
302
|
+
await createMicroserviceTasks(EMicroserviceTask.ENABLE_PLAID_NOTIFICATION, {}, dayjs(new Date()).add(1, 'day').toDate());
|
|
303
|
+
return notifiedBorrowers.sort();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async getFinancialComplianceBorrower(borrowerId: string) {
|
|
307
|
+
return FinancialComplianceBorrower.findOne({ borrower: borrowerId }).lean();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IFinancialIndex, IFinancialIndexDoc } from '../models/FinancialIndexes.model';
|
|
2
|
+
export declare enum EFinancialIndex {
|
|
3
|
+
PRIME_RATE = "PRIME_RATE"
|
|
4
|
+
}
|
|
5
|
+
export interface IFinancialIndexServiceConfig {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class FinancialIndexesService {
|
|
9
|
+
private readonly config;
|
|
10
|
+
constructor(config: IFinancialIndexServiceConfig);
|
|
11
|
+
getFinancialAllIndexes(indexName: EFinancialIndex): Promise<IFinancialIndexDoc[]>;
|
|
12
|
+
getFinancialIndexValue(indexName: EFinancialIndex, date?: Date): Promise<number>;
|
|
13
|
+
getAllFinancialIndexes(date?: Date): Promise<IFinancialIndexDoc[]>;
|
|
14
|
+
getAllFinancialIndexesWithHistory(date?: Date): Promise<IFinancialIndexDoc[]>;
|
|
15
|
+
createIndex(index: IFinancialIndex): Promise<void>;
|
|
16
|
+
deleteIndex(indexId: string): Promise<void>;
|
|
17
|
+
cleanIndexes(index: EFinancialIndex): Promise<void>;
|
|
18
|
+
updatePrimeRate(): Promise<void>;
|
|
19
|
+
getPrimeRate(): Promise<number>;
|
|
20
|
+
}
|