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,541 @@
|
|
|
1
|
+
import mongoose from 'mongoose';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import Joi from 'joi';
|
|
5
|
+
import { RedisClientType } from 'redis';
|
|
6
|
+
|
|
7
|
+
import { createFilteredObject } from '../helpers/common.helper';
|
|
8
|
+
import { IPaginatorOptions } from '../interfaces/collaterals.interface';
|
|
9
|
+
import { ELoanChargeType } from '../enums/loan-charge-type.enum';
|
|
10
|
+
import { ELoanTypes } from '../enums/loan-types.enum';
|
|
11
|
+
import {
|
|
12
|
+
ILoanStatementTransactionDoc,
|
|
13
|
+
ILoanStatementTransactionWithId,
|
|
14
|
+
LoanStatementTransactionModel,
|
|
15
|
+
} from '../models/LoanStatementTransaction.model';
|
|
16
|
+
import { ITEMS_PAGINATION } from '../db/collaterals.db';
|
|
17
|
+
import {
|
|
18
|
+
ELoanTransactionTypes,
|
|
19
|
+
ILoanTransaction,
|
|
20
|
+
LoanTransaction,
|
|
21
|
+
} from '../models/LoanTransaction.model';
|
|
22
|
+
import { LoanProduct } from '../models/LoanProducts.model';
|
|
23
|
+
import { BorrowerModel } from '../models/Borrower.model';
|
|
24
|
+
import { UserModel } from '../models/User.model';
|
|
25
|
+
import {
|
|
26
|
+
ICombinedPayment,
|
|
27
|
+
ILoanPaymentDoc,
|
|
28
|
+
ILoanPaymentWithId,
|
|
29
|
+
LOAN_PAYMENT_FIELDS,
|
|
30
|
+
LoanPaymentModel,
|
|
31
|
+
} from '../models/LoanPayment.model';
|
|
32
|
+
|
|
33
|
+
import { BorrowerService } from './borrowers.service';
|
|
34
|
+
import { LoanStatementBalanceService } from './loan-statement-balance.service';
|
|
35
|
+
import { LoanProductsService } from './loan-products.service';
|
|
36
|
+
import { LoanChargesService } from './loan-charges.service';
|
|
37
|
+
import { FinancialComplianceService } from './financial-compliance.service';
|
|
38
|
+
import { LoanStatementService } from './loan-statement.service';
|
|
39
|
+
import { LoanStatementStatusService } from './loan-statement-status.service';
|
|
40
|
+
import { LoanTransactionsService } from './loan-transactions.service';
|
|
41
|
+
import { TermLoanService } from './term-loan.service';
|
|
42
|
+
import { LockService } from './lock.service';
|
|
43
|
+
|
|
44
|
+
export const paymentOrder = [
|
|
45
|
+
ELoanChargeType.RECOVERABLE,
|
|
46
|
+
ELoanChargeType.INTEREST_FEE,
|
|
47
|
+
ELoanChargeType.ADMIN_FEE,
|
|
48
|
+
ELoanChargeType.UNUSED_LINE_FEE,
|
|
49
|
+
ELoanChargeType.WIRE_FEE,
|
|
50
|
+
ELoanChargeType.OTHER,
|
|
51
|
+
ELoanChargeType.ANNUAL_LINE_FEE,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const CALCULATION_VAR = 'BULK_PAYMENT';
|
|
55
|
+
|
|
56
|
+
export class LoanPaymentsService {
|
|
57
|
+
constructor(
|
|
58
|
+
private readonly redisClient: RedisClientType,
|
|
59
|
+
private readonly borrowerService: BorrowerService,
|
|
60
|
+
private readonly financialComplianceService: FinancialComplianceService,
|
|
61
|
+
private readonly loanChargesService: LoanChargesService,
|
|
62
|
+
private readonly loanProductsService: LoanProductsService,
|
|
63
|
+
private readonly loanStatementBalanceService: LoanStatementBalanceService,
|
|
64
|
+
private readonly getLoanStatementService: () => LoanStatementService,
|
|
65
|
+
private readonly loanStatementStatusService: LoanStatementStatusService,
|
|
66
|
+
private readonly loanTransactionsService: LoanTransactionsService,
|
|
67
|
+
private readonly lockService: LockService,
|
|
68
|
+
private readonly termLoanService: TermLoanService,
|
|
69
|
+
) {
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getLoanPayment(paymentId: string) {
|
|
73
|
+
return LoanPaymentModel.findById(paymentId).lean();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getLoanPayments(filter: { productIds: string[], start: Date, end: Date }, paginatorOptions: IPaginatorOptions) {
|
|
77
|
+
return LoanPaymentModel.aggregate<ILoanPaymentDoc>([
|
|
78
|
+
{
|
|
79
|
+
$match: {
|
|
80
|
+
$and: [
|
|
81
|
+
{ 'productId': { $in: filter.productIds.map((id) => new mongoose.Types.ObjectId(id)) } },
|
|
82
|
+
{ 'date': { $gte: filter.start } },
|
|
83
|
+
{ 'date': { $lte: dayjs(filter.end).utcOffset(0).endOf('day').toDate() } },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
}, {
|
|
87
|
+
$sort: {
|
|
88
|
+
'date': 1,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
...ITEMS_PAGINATION(paginatorOptions),
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getTotalLoanPaymentsForProduct(filter: { productIds: string[], start: Date, end: Date }) {
|
|
96
|
+
return LoanPaymentModel.countDocuments(
|
|
97
|
+
{
|
|
98
|
+
$and: [
|
|
99
|
+
{ 'productId': { $in: filter.productIds } },
|
|
100
|
+
{ 'date': { $gte: filter.start } },
|
|
101
|
+
{ 'date': { $lte: filter.end } },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async saveLoanPayment(payment: Partial<ILoanPaymentWithId>, userId: string) {
|
|
108
|
+
if (!payment._id) {
|
|
109
|
+
delete payment._id;
|
|
110
|
+
const newPayment = new LoanPaymentModel(payment);
|
|
111
|
+
await newPayment.save();
|
|
112
|
+
await this.calculatePaymentProportions(newPayment as Partial<ILoanPaymentWithId>, newPayment.productId.toString(), userId);
|
|
113
|
+
} else {
|
|
114
|
+
await LoanPaymentModel.findByIdAndUpdate(payment._id, payment);
|
|
115
|
+
await this.calculatePaymentProportions(payment, payment.productId.toString(), userId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async createLoanPayment(payment: Partial<ILoanPaymentWithId>, userId: string) {
|
|
120
|
+
const purePayment = createFilteredObject<ILoanPaymentWithId>(payment, LOAN_PAYMENT_FIELDS);
|
|
121
|
+
await this.saveLoanPayment(purePayment, userId);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async updateLoanPayment(payment: Partial<ILoanPaymentWithId>, userId: string) {
|
|
125
|
+
const foundPayment = await LoanPaymentModel.findById(payment._id).lean();
|
|
126
|
+
if (!foundPayment) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
await this.loanStatementBalanceService.cleanDecreasePayment(foundPayment as unknown as ILoanPaymentWithId, userId);
|
|
130
|
+
const purePayment = createFilteredObject<ILoanPaymentWithId>(payment, LOAN_PAYMENT_FIELDS);
|
|
131
|
+
await this.saveLoanPayment(purePayment, userId);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async recalculateAfterPayment(productId: string, userId: string) {
|
|
135
|
+
console.log('started recalculating product', productId);
|
|
136
|
+
await this.recalculateProduct(productId, userId);
|
|
137
|
+
await this.loanTransactionsService.recalculateProduct(productId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async deleteLoanPayment(paymentId: string, userId: string) {
|
|
141
|
+
const foundPayment = await LoanPaymentModel.findById(paymentId).lean();
|
|
142
|
+
if (!foundPayment) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
await this.loanStatementBalanceService.cleanDecreasePayment(foundPayment as unknown as ILoanPaymentWithId, userId);
|
|
146
|
+
await LoanPaymentModel.findByIdAndDelete(paymentId);
|
|
147
|
+
const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(foundPayment.date));
|
|
148
|
+
await this.loanStatementStatusService.updateStatementStatus(foundPayment.productId.toString(), statementDate, false);
|
|
149
|
+
await this.termLoanService.setTermLoanExpected(foundPayment.productId.toString());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private async calculatePaymentProportions(payment: Partial<ILoanPaymentWithId>, productId: string, userId: string) {
|
|
153
|
+
const product = await LoanProduct.findById(productId).lean();
|
|
154
|
+
const settlementCode = payment.settlementCode;
|
|
155
|
+
const borrower = await BorrowerModel.findById(product.borrowerId).lean();
|
|
156
|
+
|
|
157
|
+
const loanCharges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
158
|
+
const chargesIds = loanCharges.map((charge) => charge._id);
|
|
159
|
+
|
|
160
|
+
const productPaymentOrder = !payment.paymentOrder || payment.paymentOrder.length === 0
|
|
161
|
+
? paymentOrder
|
|
162
|
+
: payment.paymentOrder.map((p) => loanCharges.find((charge) => charge._id.toString() === p).chargeType);
|
|
163
|
+
|
|
164
|
+
const paymentOrderQuery = productPaymentOrder.map((charge, index) => {
|
|
165
|
+
return {
|
|
166
|
+
case: {
|
|
167
|
+
$eq: ['$charge.chargeType', charge],
|
|
168
|
+
},
|
|
169
|
+
then: index,
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const unpaidStatements = await LoanStatementTransactionModel.aggregate([
|
|
174
|
+
{
|
|
175
|
+
$match: {
|
|
176
|
+
'chargeId': { '$in': chargesIds },
|
|
177
|
+
'date': { $lte: dayjs(new Date(payment.date)).utcOffset(0).startOf('month').subtract(1, 'second').toDate() },
|
|
178
|
+
$expr: { $lt: ['$amountPaid', '$amount'] },
|
|
179
|
+
},
|
|
180
|
+
}, {
|
|
181
|
+
$lookup:
|
|
182
|
+
{
|
|
183
|
+
from: 'loan_charges',
|
|
184
|
+
localField: 'chargeId',
|
|
185
|
+
foreignField: '_id',
|
|
186
|
+
as: 'charge',
|
|
187
|
+
},
|
|
188
|
+
}, {
|
|
189
|
+
$unwind:
|
|
190
|
+
{
|
|
191
|
+
path: '$charge',
|
|
192
|
+
},
|
|
193
|
+
}, {
|
|
194
|
+
$addFields:
|
|
195
|
+
{
|
|
196
|
+
customSortValue: {
|
|
197
|
+
$switch: {
|
|
198
|
+
branches: paymentOrderQuery,
|
|
199
|
+
default: paymentOrder.length,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
}, {
|
|
204
|
+
$sort:
|
|
205
|
+
{
|
|
206
|
+
customSortValue: 1,
|
|
207
|
+
date: 1,
|
|
208
|
+
order: 1,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
]);
|
|
212
|
+
let availableAmount = payment.amount;
|
|
213
|
+
const paidArray = [];
|
|
214
|
+
let firstPaidStatement: any = null;
|
|
215
|
+
for (const statement of unpaidStatements) {
|
|
216
|
+
const foundStatement = await LoanStatementTransactionModel.findById(statement._id);
|
|
217
|
+
if (!foundStatement) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const prefPaidAmount = new Decimal(statement.amount).sub(foundStatement.amountPaid).toNumber();
|
|
221
|
+
const amountPaid = prefPaidAmount > availableAmount ? availableAmount : prefPaidAmount;
|
|
222
|
+
availableAmount = new Decimal(availableAmount).sub(amountPaid).toNumber();
|
|
223
|
+
paidArray.push({ statementId: statement._id, amount: amountPaid });
|
|
224
|
+
await LoanStatementTransactionModel.findByIdAndUpdate(statement._id, {
|
|
225
|
+
amountPaid: new Decimal(foundStatement.amountPaid).add(amountPaid).toNumber(),
|
|
226
|
+
settlementCode,
|
|
227
|
+
});
|
|
228
|
+
if (!firstPaidStatement ||
|
|
229
|
+
(statement.date.getTime() < firstPaidStatement.date.getTime()) ||
|
|
230
|
+
(firstPaidStatement.date.getTime() === statement.date.getTime() && firstPaidStatement.order > statement.order)
|
|
231
|
+
) {
|
|
232
|
+
firstPaidStatement = statement;
|
|
233
|
+
}
|
|
234
|
+
if (availableAmount === 0) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const loanTransactions = [];
|
|
239
|
+
|
|
240
|
+
if (availableAmount > 0) {
|
|
241
|
+
const transaction: Partial<ILoanTransaction> = {
|
|
242
|
+
amount: -availableAmount,
|
|
243
|
+
date: payment.date,
|
|
244
|
+
description: `${borrower.code} - STATEMENT PAID`,
|
|
245
|
+
effectiveDate: null,
|
|
246
|
+
loanPaymentId: payment._id,
|
|
247
|
+
order: 0,
|
|
248
|
+
productId: new mongoose.Types.ObjectId(productId),
|
|
249
|
+
reference: `${borrower.code} - STATEMENT PAID`,
|
|
250
|
+
transactionType: ELoanTransactionTypes.COLLECTION,
|
|
251
|
+
userId: new mongoose.Types.ObjectId(userId),
|
|
252
|
+
};
|
|
253
|
+
transaction.effectiveDate = await this.loanTransactionsService.calculateEffectiveDate(transaction);
|
|
254
|
+
const newTransaction = await this.loanTransactionsService.createLoanTransaction(transaction);
|
|
255
|
+
loanTransactions.push({ transactionId: newTransaction._id, amount: newTransaction.amount });
|
|
256
|
+
}
|
|
257
|
+
await LoanPaymentModel.findByIdAndUpdate(payment._id, { paid: paidArray, loanTransactions }, { new: true });
|
|
258
|
+
if (firstPaidStatement) {
|
|
259
|
+
await this.loanStatementBalanceService.calculateProductBalance(productId, firstPaidStatement.date);
|
|
260
|
+
}
|
|
261
|
+
await this.updateComplianceBorrowerAmountReceived(borrower._id.toString());
|
|
262
|
+
const statementDate = this.loanStatementStatusService.getStatementDateForDate(new Date(payment.date));
|
|
263
|
+
await this.loanStatementStatusService.updateStatementStatus(productId, statementDate, false);
|
|
264
|
+
await this.termLoanService.setTermLoanExpected(productId, true);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async updateComplianceBorrowerAmountReceived(borrowerId: string) {
|
|
268
|
+
const allProducts = await this.loanChargesService.getLoanProducts(borrowerId);
|
|
269
|
+
const productIds = allProducts.map((p) => p._id.toString());
|
|
270
|
+
const startDate = dayjs().startOf('month').toDate();
|
|
271
|
+
const endDate = dayjs().endOf('month').toDate();
|
|
272
|
+
const payments = await this.getPaymentsForPeriod(productIds, startDate, endDate);
|
|
273
|
+
const totalPayments = payments.reduce((acc, p) => new Decimal(acc).add(p.amount ?? 0).toNumber(), 0);
|
|
274
|
+
const complianceBorrower = await this.financialComplianceService.getFinancialComplianceBorrower(borrowerId);
|
|
275
|
+
if (!complianceBorrower) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
await this.financialComplianceService.updateBorrower(complianceBorrower._id.toString(), {
|
|
279
|
+
...complianceBorrower,
|
|
280
|
+
amountReceived: totalPayments,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async recalculateProduct(productId: string, userId: string) {
|
|
285
|
+
const loanCharges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
286
|
+
const chargesIds = loanCharges.map((charge) => charge._id);
|
|
287
|
+
await LoanStatementTransactionModel.updateMany({ 'chargeId': { $in: chargesIds } }, { amountPaid: 0 });
|
|
288
|
+
|
|
289
|
+
const firstStatements = await LoanStatementTransactionModel.aggregate<ILoanStatementTransactionDoc>([
|
|
290
|
+
{
|
|
291
|
+
$match: {
|
|
292
|
+
'chargeId': { $in: chargesIds },
|
|
293
|
+
},
|
|
294
|
+
}, {
|
|
295
|
+
$sort: {
|
|
296
|
+
'date': 1,
|
|
297
|
+
'order': 1,
|
|
298
|
+
},
|
|
299
|
+
}, {
|
|
300
|
+
$limit: 1,
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
if (firstStatements.length > 0) {
|
|
305
|
+
const loanStatementService = this.getLoanStatementService();
|
|
306
|
+
await loanStatementService.updateStatementTransaction(firstStatements[0] as Partial<ILoanStatementTransactionWithId>);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const payments = await LoanPaymentModel.aggregate<ILoanPaymentDoc>([
|
|
310
|
+
{
|
|
311
|
+
$match: { 'productId': new mongoose.Types.ObjectId(productId) },
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
$sort: {
|
|
315
|
+
'date': 1,
|
|
316
|
+
'createdAt': 1,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
]);
|
|
320
|
+
for (const [index, payment] of payments.entries()) {
|
|
321
|
+
try {
|
|
322
|
+
console.log(`updating payment ${index + 1}/${payments.length}`, dayjs(payment.date).format('DD-MM-YYYY'), payment.amount);
|
|
323
|
+
for (const loanTransaction of payment.loanTransactions) {
|
|
324
|
+
await LoanTransaction.findByIdAndDelete(loanTransaction.transactionId);
|
|
325
|
+
}
|
|
326
|
+
await LoanPaymentModel.findByIdAndUpdate(payment._id, { paid: [], loanTransactions: [] });
|
|
327
|
+
await this.updateLoanPayment(payment as Partial<ILoanPaymentWithId>, userId);
|
|
328
|
+
} catch (e) {
|
|
329
|
+
console.error({ productId, payments });
|
|
330
|
+
console.error(e);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async getPaymentsForPeriod(productIds: string[], startDate: Date, endDate: Date) {
|
|
336
|
+
return LoanPaymentModel.aggregate<ILoanPaymentDoc>([
|
|
337
|
+
{
|
|
338
|
+
$match: {
|
|
339
|
+
$and: [
|
|
340
|
+
{ 'productId': { $in: productIds.map((id) => new mongoose.Types.ObjectId(id)) } },
|
|
341
|
+
{ 'date': { $gte: startDate } },
|
|
342
|
+
{ 'date': { $lte: endDate } },
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
}, {
|
|
346
|
+
$sort: {
|
|
347
|
+
'date': 1,
|
|
348
|
+
'createdAt': 1,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async proceedCombinedPayment(combinedPayment: ICombinedPayment, userId: string) {
|
|
355
|
+
try {
|
|
356
|
+
if (combinedPayment.addToRevolver) {
|
|
357
|
+
const isLockedRes = await Promise.all([...combinedPayment.products, ...combinedPayment.linkedProducts].map(async (productGroup) => {
|
|
358
|
+
const product = await LoanProduct.findById(productGroup.productId).lean();
|
|
359
|
+
if (product) {
|
|
360
|
+
return await this.lockService.isDateLocked(new Date(combinedPayment.date), product.borrowerId.toString());
|
|
361
|
+
}
|
|
362
|
+
return false;
|
|
363
|
+
}));
|
|
364
|
+
if (isLockedRes.some((isLocked) => !!isLocked)) {
|
|
365
|
+
return { success: false, message: 'Period is signed and locked' };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const loanStatementService = this.getLoanStatementService();
|
|
370
|
+
const data = await loanStatementService.getBorrowerComplianceData(combinedPayment.borrowerId, new Date());
|
|
371
|
+
let totalSumToCover = combinedPayment.payment;
|
|
372
|
+
const products = await this.loanChargesService.getLoanProducts(combinedPayment.borrowerId);
|
|
373
|
+
const revolverProduct = products.find((product) => product.type === ELoanTypes.REVOLVER);
|
|
374
|
+
|
|
375
|
+
for (const p of combinedPayment.products) {
|
|
376
|
+
await this.loanProductsService.savePaymentOrder(p.productId, p.paymentOrder);
|
|
377
|
+
const desiredProductSum = data.productTotals[p.productId];
|
|
378
|
+
const product = await LoanProduct.findById(p.productId).lean();
|
|
379
|
+
const sumForProduct = desiredProductSum > totalSumToCover ? totalSumToCover : desiredProductSum;
|
|
380
|
+
if (sumForProduct > 0) {
|
|
381
|
+
const newPayment: Partial<ILoanPaymentWithId> = {
|
|
382
|
+
_id: null,
|
|
383
|
+
productId: new mongoose.Types.ObjectId(p.productId),
|
|
384
|
+
amount: sumForProduct,
|
|
385
|
+
date: new Date(combinedPayment.date),
|
|
386
|
+
settlementCode: p.settlementCode,
|
|
387
|
+
paymentOrder: p.paymentOrder,
|
|
388
|
+
};
|
|
389
|
+
await this.createLoanPayment(newPayment, userId);
|
|
390
|
+
|
|
391
|
+
if (combinedPayment.addToRevolver && revolverProduct) {
|
|
392
|
+
const newTransactions: Partial<ILoanTransaction> = {
|
|
393
|
+
amount: sumForProduct,
|
|
394
|
+
bankId: null,
|
|
395
|
+
date: combinedPayment.date,
|
|
396
|
+
description: `${product.type} STATEMENT ADDED TO LOAN`,
|
|
397
|
+
effectiveDate: null,
|
|
398
|
+
productId: revolverProduct._id,
|
|
399
|
+
reference: `${product.type} STATEMENT ADDED TO LOAN`,
|
|
400
|
+
transactionType: ELoanTransactionTypes.DISBURSEMENT,
|
|
401
|
+
userId: new mongoose.Types.ObjectId(userId),
|
|
402
|
+
};
|
|
403
|
+
newTransactions.effectiveDate = await this.loanTransactionsService.calculateEffectiveDate(newTransactions);
|
|
404
|
+
await this.loanTransactionsService.createLoanTransaction(newTransactions);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
totalSumToCover = new Decimal(totalSumToCover).sub(sumForProduct).toNumber();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (totalSumToCover > 0 && revolverProduct && !combinedPayment.addToRevolver) {
|
|
411
|
+
const newTransactions: Partial<ILoanTransaction> = {
|
|
412
|
+
amount: -totalSumToCover,
|
|
413
|
+
bankId: null,
|
|
414
|
+
date: combinedPayment.date,
|
|
415
|
+
description: `OVERPAYMENT OF STATEMENT`,
|
|
416
|
+
effectiveDate: null,
|
|
417
|
+
productId: revolverProduct._id,
|
|
418
|
+
reference: `OVERPAYMENT OF STATEMENT`,
|
|
419
|
+
transactionType: ELoanTransactionTypes.COLLECTION,
|
|
420
|
+
userId: new mongoose.Types.ObjectId(userId),
|
|
421
|
+
};
|
|
422
|
+
newTransactions.effectiveDate = await this.loanTransactionsService.calculateEffectiveDate(newTransactions);
|
|
423
|
+
await this.loanTransactionsService.createLoanTransaction(newTransactions);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
await Promise.all(combinedPayment.linkedProducts.map(async (p) => {
|
|
427
|
+
const newTransactions: Partial<ILoanTransaction> = {
|
|
428
|
+
amount: -p.payableSum,
|
|
429
|
+
bankId: null,
|
|
430
|
+
date: combinedPayment.date,
|
|
431
|
+
description: `EXTRA PAYMENT`,
|
|
432
|
+
effectiveDate: null,
|
|
433
|
+
productId: new mongoose.Types.ObjectId(p.productId),
|
|
434
|
+
reference: `EXTRA PAYMENT REF`,
|
|
435
|
+
transactionType: ELoanTransactionTypes.COLLECTION,
|
|
436
|
+
userId: new mongoose.Types.ObjectId(userId),
|
|
437
|
+
};
|
|
438
|
+
newTransactions.effectiveDate = await this.loanTransactionsService.calculateEffectiveDate(newTransactions);
|
|
439
|
+
await this.loanTransactionsService.createLoanTransaction(newTransactions);
|
|
440
|
+
}));
|
|
441
|
+
|
|
442
|
+
return { success: true, message: null };
|
|
443
|
+
} catch (e) {
|
|
444
|
+
console.error(e);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async getExpectedPayments(userId: string) {
|
|
449
|
+
const user = await UserModel.findById(userId).lean();
|
|
450
|
+
if (!user) {
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
const borrowers = await this.borrowerService.getAllowedBorrowers(user);
|
|
454
|
+
const isCalculated = await this.redisClient.get(CALCULATION_VAR);
|
|
455
|
+
const borrowersData = await Promise.all(borrowers
|
|
456
|
+
.filter((borrower) => borrower.active)
|
|
457
|
+
.map(async (borrower) => {
|
|
458
|
+
const loanStatementService = this.getLoanStatementService();
|
|
459
|
+
const dueAmounts = await loanStatementService.getBorrowerProductTotals(borrower._id.toString());
|
|
460
|
+
return {
|
|
461
|
+
borrowerId: borrower.id,
|
|
462
|
+
borrowerTitle: borrower.name,
|
|
463
|
+
totalDue: dueAmounts.total,
|
|
464
|
+
productTotals: dueAmounts.productTotals,
|
|
465
|
+
};
|
|
466
|
+
}),
|
|
467
|
+
);
|
|
468
|
+
return { borrowers: borrowersData, isCalculated: !!isCalculated };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async payExpectedPayments(userId: string, borrowerIds: string[]) {
|
|
472
|
+
|
|
473
|
+
const arrayOfStringsSchema = Joi.array().items(Joi.string()).required();
|
|
474
|
+
const { error } = arrayOfStringsSchema.validate(borrowerIds, { abortEarly: false });
|
|
475
|
+
if (error) {
|
|
476
|
+
return { message: 'Incorrect list of borrowers IDs' };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const addToRevolver = true;
|
|
480
|
+
|
|
481
|
+
await this.redisClient.set(CALCULATION_VAR, 1);
|
|
482
|
+
const user = await UserModel.findById(userId).lean();
|
|
483
|
+
if (!user) {
|
|
484
|
+
return { message: 'Cannot find user with this id' };
|
|
485
|
+
}
|
|
486
|
+
const borrowers = await this.borrowerService.getAllowedBorrowers(user);
|
|
487
|
+
const paymentPromises = borrowers.map(async (borrower) => {
|
|
488
|
+
if (borrowerIds.includes(borrower._id.toString())) {
|
|
489
|
+
try {
|
|
490
|
+
const loanStatementService = this.getLoanStatementService();
|
|
491
|
+
const dueAmounts = await loanStatementService.getBorrowerProductTotals(borrower._id.toString());
|
|
492
|
+
if (dueAmounts.total === 0) {
|
|
493
|
+
console.log('no payment for this borrower');
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const products = await Promise.all(
|
|
497
|
+
Object.entries(dueAmounts.productTotals).map(async ([productId, amount]) => {
|
|
498
|
+
const product = await LoanProduct.findById(productId);
|
|
499
|
+
if (product) {
|
|
500
|
+
return {
|
|
501
|
+
productId: productId,
|
|
502
|
+
payableSum: amount,
|
|
503
|
+
settlementCode: addToRevolver ? product.code : product.settlementCode,
|
|
504
|
+
paymentOrder: null,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
const combinedPayment: ICombinedPayment = {
|
|
511
|
+
addToRevolver,
|
|
512
|
+
borrowerId: borrower._id.toString(),
|
|
513
|
+
date: new Date(),
|
|
514
|
+
linkedProducts: [],
|
|
515
|
+
payment: dueAmounts.total,
|
|
516
|
+
products: products.filter((product) => !!product),
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
await this.proceedCombinedPayment(combinedPayment, userId);
|
|
520
|
+
console.log(`Success on payment ${borrower.title}`);
|
|
521
|
+
} catch (err) {
|
|
522
|
+
console.error(`Error on payment ${borrower.title}:`, err);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const processPayments = async (paymentPromises: Promise<void>[]) => {
|
|
528
|
+
try {
|
|
529
|
+
await Promise.allSettled(paymentPromises);
|
|
530
|
+
console.log('All payments processed.');
|
|
531
|
+
await this.redisClient.del(CALCULATION_VAR);
|
|
532
|
+
} catch (err) {
|
|
533
|
+
console.error('Error during payment processing:', err);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
processPayments(paymentPromises).catch(console.error);
|
|
538
|
+
|
|
539
|
+
return { message: 'Payments are being processed in the background.' };
|
|
540
|
+
}
|
|
541
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ILoanProductView } from '../models/LoanProducts.model';
|
|
2
|
+
import { LoanChargesService } from './loan-charges.service';
|
|
3
|
+
export declare class LoanProductsService {
|
|
4
|
+
private readonly loanChargesService;
|
|
5
|
+
constructor(loanChargesService: LoanChargesService);
|
|
6
|
+
getLinkedLoanProducts(codes: string[]): Promise<{
|
|
7
|
+
[code: string]: string;
|
|
8
|
+
}>;
|
|
9
|
+
getProductsWithBalances(products: ILoanProductView[], showFloatedBalance: boolean): Promise<ILoanProductView[]>;
|
|
10
|
+
getProductPaymentOrder(productId: string): Promise<any[]>;
|
|
11
|
+
savePaymentOrder(productId: string, paymentOrder: string[]): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoanProductsService = void 0;
|
|
4
|
+
const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
5
|
+
const LoanCharges_model_1 = require("../models/LoanCharges.model");
|
|
6
|
+
const loan_payments_service_1 = require("./loan-payments.service");
|
|
7
|
+
class LoanProductsService {
|
|
8
|
+
loanChargesService;
|
|
9
|
+
constructor(loanChargesService) {
|
|
10
|
+
this.loanChargesService = loanChargesService;
|
|
11
|
+
}
|
|
12
|
+
async getLinkedLoanProducts(codes) {
|
|
13
|
+
const loanProducts = await LoanProducts_model_1.LoanProduct
|
|
14
|
+
.find({ isParticipant: true, masterCode: { $in: codes } })
|
|
15
|
+
.sort({ code: 1 })
|
|
16
|
+
.lean();
|
|
17
|
+
return loanProducts.reduce((acc, loanProduct) => ({ ...acc, [loanProduct.code]: loanProduct._id.toString() }), {});
|
|
18
|
+
}
|
|
19
|
+
async getProductsWithBalances(products, showFloatedBalance) {
|
|
20
|
+
await Promise.all(products.map(async (product, index) => {
|
|
21
|
+
const balances = await this.loanChargesService.getLoanProductBalance(product._id.toString());
|
|
22
|
+
products[index].balance = balances.balance;
|
|
23
|
+
products[index].floatedBalance = showFloatedBalance ? balances.floatedBalance : 0;
|
|
24
|
+
}));
|
|
25
|
+
return products;
|
|
26
|
+
}
|
|
27
|
+
async getProductPaymentOrder(productId) {
|
|
28
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
|
|
29
|
+
const charges = await LoanCharges_model_1.LoanCharge.find({ productId }).lean();
|
|
30
|
+
if (product.isDefaultPaymentOrder) {
|
|
31
|
+
const chargeOrders = charges.map((charge) => {
|
|
32
|
+
const foundIndex = loan_payments_service_1.paymentOrder.findIndex((order) => order === charge.chargeType);
|
|
33
|
+
return { chargeId: charge._id.toString(), order: foundIndex };
|
|
34
|
+
});
|
|
35
|
+
const sortedChargeOrders = chargeOrders.sort((a, b) => a.order - b.order);
|
|
36
|
+
return sortedChargeOrders.map((item) => item.chargeId);
|
|
37
|
+
}
|
|
38
|
+
const sortedCharges = charges.sort((a, b) => {
|
|
39
|
+
return (a.paymentOrder ?? 0) - (b.paymentOrder ?? 0);
|
|
40
|
+
});
|
|
41
|
+
return sortedCharges.map((charge) => charge._id.toString());
|
|
42
|
+
}
|
|
43
|
+
async savePaymentOrder(productId, paymentOrder) {
|
|
44
|
+
if (paymentOrder === null || paymentOrder.length === 0) {
|
|
45
|
+
await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: true });
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: false });
|
|
49
|
+
await Promise.all(paymentOrder.map(async (chargeId, index) => {
|
|
50
|
+
await LoanCharges_model_1.LoanCharge.findByIdAndUpdate(chargeId, { paymentOrder: index });
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.LoanProductsService = LoanProductsService;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ILoanProductView, ILoanProductViewWithBalances, LoanProduct } from '../models/LoanProducts.model';
|
|
2
|
+
import { LoanCharge } from '../models/LoanCharges.model';
|
|
3
|
+
|
|
4
|
+
import { paymentOrder } from './loan-payments.service';
|
|
5
|
+
import { LoanChargesService } from './loan-charges.service';
|
|
6
|
+
|
|
7
|
+
export class LoanProductsService {
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
private readonly loanChargesService: LoanChargesService,
|
|
11
|
+
) {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getLinkedLoanProducts(codes: string[]): Promise<{ [code: string]: string }> {
|
|
15
|
+
const loanProducts = await LoanProduct
|
|
16
|
+
.find({ isParticipant: true, masterCode: { $in: codes } })
|
|
17
|
+
.sort({ code: 1 })
|
|
18
|
+
.lean();
|
|
19
|
+
return loanProducts.reduce((acc, loanProduct) => ({ ...acc, [loanProduct.code]: loanProduct._id.toString() }), {});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getProductsWithBalances(products: ILoanProductView[], showFloatedBalance: boolean) {
|
|
23
|
+
await Promise.all(products.map(async (product, index) => {
|
|
24
|
+
const balances = await this.loanChargesService.getLoanProductBalance(product._id.toString());
|
|
25
|
+
(<ILoanProductViewWithBalances>products[index]).balance = balances.balance;
|
|
26
|
+
(<ILoanProductViewWithBalances>products[index]).floatedBalance = showFloatedBalance ? balances.floatedBalance : 0;
|
|
27
|
+
}));
|
|
28
|
+
return products;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getProductPaymentOrder(productId: string) {
|
|
32
|
+
const product = await LoanProduct.findById(productId).lean();
|
|
33
|
+
const charges = await LoanCharge.find({ productId }).lean();
|
|
34
|
+
if (product.isDefaultPaymentOrder) {
|
|
35
|
+
const chargeOrders = charges.map((charge) => {
|
|
36
|
+
const foundIndex = paymentOrder.findIndex((order) => order === charge.chargeType);
|
|
37
|
+
return { chargeId: charge._id.toString(), order: foundIndex };
|
|
38
|
+
});
|
|
39
|
+
const sortedChargeOrders = chargeOrders.sort((a, b) => a.order - b.order);
|
|
40
|
+
return sortedChargeOrders.map((item) => item.chargeId);
|
|
41
|
+
}
|
|
42
|
+
const sortedCharges = charges.sort((a, b) => {
|
|
43
|
+
return (a.paymentOrder ?? 0) - (b.paymentOrder ?? 0);
|
|
44
|
+
});
|
|
45
|
+
return sortedCharges.map((charge) => charge._id.toString());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async savePaymentOrder(productId: string, paymentOrder: string[]) {
|
|
49
|
+
if (paymentOrder === null || paymentOrder.length === 0) {
|
|
50
|
+
await LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: true });
|
|
51
|
+
} else {
|
|
52
|
+
await LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: false });
|
|
53
|
+
await Promise.all(paymentOrder.map(async (chargeId, index) => {
|
|
54
|
+
await LoanCharge.findByIdAndUpdate(chargeId, { paymentOrder: index });
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ILoanPaymentWithId } from '../models/LoanPayment.model';
|
|
2
|
+
import { ILoanStatementTransactionDoc } from '../models/LoanStatementTransaction.model';
|
|
3
|
+
import { LoanChargesService } from './loan-charges.service';
|
|
4
|
+
import { LoanStatementService } from './loan-statement.service';
|
|
5
|
+
import { LoanTransactionsService } from './loan-transactions.service';
|
|
6
|
+
export declare class LoanStatementBalanceService {
|
|
7
|
+
private readonly loanChargesService;
|
|
8
|
+
private readonly getLoanStatementService;
|
|
9
|
+
private readonly loanTransactionsService;
|
|
10
|
+
constructor(loanChargesService: LoanChargesService, getLoanStatementService: () => LoanStatementService, loanTransactionsService: LoanTransactionsService);
|
|
11
|
+
cleanDecreasePayment(payment: ILoanPaymentWithId, userId: string): Promise<void>;
|
|
12
|
+
calculateProductBalance(productId: string, startDate: Date): Promise<void>;
|
|
13
|
+
updateBalance(loanStatementTransaction: ILoanStatementTransactionDoc): Promise<void>;
|
|
14
|
+
getLastStatementTransactionForDate(productId: string, startDate: Date): Promise<ILoanStatementTransactionDoc>;
|
|
15
|
+
getNextAdminTransactionForDate(productId: string, date: Date): Promise<number>;
|
|
16
|
+
}
|