gemcap-be-common 1.2.59 → 1.2.60
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/db/brokers.db.d.ts +5 -0
- package/db/brokers.db.js +62 -0
- package/db/brokers.db.ts +60 -0
- package/db/loan-charges.db.d.ts +2 -0
- package/db/loan-charges.db.js +26 -0
- package/db/loan-charges.db.ts +21 -0
- package/db/loan-payments.db.d.ts +2 -0
- package/db/loan-payments.db.js +23 -0
- package/db/loan-payments.db.ts +18 -0
- package/db/loan-products.db.d.ts +3 -0
- package/db/loan-products.db.js +22 -1
- package/db/loan-products.db.ts +23 -1
- package/db/loan-statement.db.d.ts +44 -0
- package/db/loan-statement.db.js +186 -1
- package/db/loan-statement.db.ts +203 -5
- package/db/loan-transactions.db.d.ts +2 -0
- package/db/loan-transactions.db.js +39 -0
- package/db/loan-transactions.db.ts +33 -0
- package/db/reports.db.d.ts +56 -0
- package/db/reports.db.js +295 -0
- package/db/reports.db.ts +354 -0
- package/models/Borrower.model.d.ts +46 -0
- package/models/Borrower.model.js +75 -0
- package/models/Borrower.model.ts +103 -0
- package/models/LoanPayment.model.d.ts +66 -0
- package/models/LoanPayment.model.js +62 -0
- package/models/LoanPayment.model.ts +97 -0
- package/models/TermLoan.model.d.ts +53 -0
- package/models/TermLoan.model.js +74 -0
- package/models/TermLoan.model.ts +97 -0
- package/models/TermLoanCalculated.model.d.ts +54 -0
- package/models/TermLoanCalculated.model.js +98 -0
- package/models/TermLoanCalculated.model.ts +128 -0
- package/models/TermLoanSettings.model.d.ts +38 -0
- package/models/TermLoanSettings.model.js +39 -0
- package/models/TermLoanSettings.model.ts +53 -0
- package/models/_models.d.ts +3 -0
- package/models/_models.js +3 -0
- package/models/_models.ts +3 -0
- package/package.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/db/loan-statement.db.ts
CHANGED
|
@@ -1,7 +1,50 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
1
2
|
import mongoose from 'mongoose';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
2
4
|
|
|
3
|
-
import { LoanStatementTransactionModel } from '../models/LoanStatementTransaction.model';
|
|
5
|
+
import { IStatementPeriod, LoanStatementTransactionModel } from '../models/LoanStatementTransaction.model';
|
|
4
6
|
import { LoanTransaction } from '../models/LoanTransaction.model';
|
|
7
|
+
import { getLoanProductById, getLoanProducts } from './loan-products.db';
|
|
8
|
+
import { LoanProduct } from '../models/LoanProducts.model';
|
|
9
|
+
import { BorrowerModel } from '../models/Borrower.model';
|
|
10
|
+
import { getLastTransactionForDate } from './loan-transactions.db';
|
|
11
|
+
import { getLedger } from './reports.db';
|
|
12
|
+
|
|
13
|
+
export enum ELedgerReportType {
|
|
14
|
+
SHORT,
|
|
15
|
+
DETAILED,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ledgerHeadersMap = {
|
|
19
|
+
[ELedgerReportType.SHORT]: {
|
|
20
|
+
borrowerCode: 'borrower code',
|
|
21
|
+
type: 'type',
|
|
22
|
+
chargeCode: 'BS code',
|
|
23
|
+
PLCode: 'P&L code',
|
|
24
|
+
productCode: 'product code',
|
|
25
|
+
productName: 'product name',
|
|
26
|
+
info: 'memo/reference',
|
|
27
|
+
title: 'title',
|
|
28
|
+
amount: 'amount',
|
|
29
|
+
statementAmount: 'Statement amount',
|
|
30
|
+
},
|
|
31
|
+
[ELedgerReportType.DETAILED]: {
|
|
32
|
+
borrowerCode: 'borrower code',
|
|
33
|
+
date: 'date',
|
|
34
|
+
type: 'type',
|
|
35
|
+
chargeCode: 'BS code',
|
|
36
|
+
PLCode: 'P&L code',
|
|
37
|
+
productCode: 'product code',
|
|
38
|
+
productName: 'product name',
|
|
39
|
+
info: 'memo/reference',
|
|
40
|
+
amount: 'amount',
|
|
41
|
+
statementAmount: 'Statement amount',
|
|
42
|
+
title: 'title',
|
|
43
|
+
balance: 'balance',
|
|
44
|
+
floatedBalance: 'floated balance',
|
|
45
|
+
paymentDate: 'payment date',
|
|
46
|
+
},
|
|
47
|
+
};
|
|
5
48
|
|
|
6
49
|
export const getTotalTransactionAmountForCharge = async (chargeId: string, start: Date, end: Date) => {
|
|
7
50
|
const results = await LoanStatementTransactionModel.aggregate([
|
|
@@ -48,7 +91,162 @@ export const getTotalForDate = async (productId: string, date: Date): Promise<nu
|
|
|
48
91
|
},
|
|
49
92
|
]);
|
|
50
93
|
if (totalAmount.length === 0) {
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return totalAmount[0].totalAmount;
|
|
54
|
-
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
return totalAmount[0].totalAmount;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const getStatementBorrowersTransactions = async (start: string, end: string, selectedPeriod: string, selectedBorrowerIds: string[], ledgerType: ELedgerReportType) => {
|
|
100
|
+
|
|
101
|
+
const header = ledgerHeadersMap[ledgerType];
|
|
102
|
+
|
|
103
|
+
if (!selectedBorrowerIds) {
|
|
104
|
+
return [{ transactions: [header] }];
|
|
105
|
+
}
|
|
106
|
+
const allTransactions: { [borrowerId: string]: unknown[] } = {};
|
|
107
|
+
await Promise.all(selectedBorrowerIds.map(async (borrowerId) => {
|
|
108
|
+
const products = await getLoanProducts(borrowerId);
|
|
109
|
+
const periodFull = await getBorrowerWithPeriods(borrowerId, {
|
|
110
|
+
selectedStart: new Date(start),
|
|
111
|
+
selectedEnd: new Date(end),
|
|
112
|
+
selectedPeriod,
|
|
113
|
+
});
|
|
114
|
+
const someMappedResults = products.reduce((acc, product) => {
|
|
115
|
+
return [
|
|
116
|
+
...acc,
|
|
117
|
+
...periodFull.map((periodDesc) => ({
|
|
118
|
+
productStart: periodDesc.period.start,
|
|
119
|
+
productEnd: periodDesc.period.end,
|
|
120
|
+
productId: product._id.toString(),
|
|
121
|
+
})),
|
|
122
|
+
];
|
|
123
|
+
}, []);
|
|
124
|
+
await Promise.all(someMappedResults.map(async (productDesc) => {
|
|
125
|
+
const borrowerTransactions = await getLedger(
|
|
126
|
+
productDesc.productId,
|
|
127
|
+
new Date(productDesc.productStart),
|
|
128
|
+
new Date(productDesc.productEnd),
|
|
129
|
+
ledgerType,
|
|
130
|
+
true,
|
|
131
|
+
);
|
|
132
|
+
Object.values(borrowerTransactions).forEach((transactions) => {
|
|
133
|
+
const [_header, ...onlyData] = transactions;
|
|
134
|
+
const mappedData = _.map(onlyData, (obj) => _.omit(obj, ['balance', 'floatedBalance']));
|
|
135
|
+
allTransactions[borrowerId] = mappedData;
|
|
136
|
+
});
|
|
137
|
+
}));
|
|
138
|
+
}));
|
|
139
|
+
const sortedTransactions = [];
|
|
140
|
+
for (const borrowerId of selectedBorrowerIds) {
|
|
141
|
+
console.log(borrowerId);
|
|
142
|
+
sortedTransactions.push(...allTransactions[borrowerId]);
|
|
143
|
+
}
|
|
144
|
+
return [{ transactions: [header, ...sortedTransactions] }];
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const getBorrowerWithPeriods = async (borrowerId: string, period: {
|
|
148
|
+
selectedPeriod: string,
|
|
149
|
+
selectedStart: Date,
|
|
150
|
+
selectedEnd: Date
|
|
151
|
+
}, splitPeriods = false) => {
|
|
152
|
+
const borrower = await BorrowerModel.findById(borrowerId).lean();
|
|
153
|
+
let start: dayjs.Dayjs;
|
|
154
|
+
let end: dayjs.Dayjs;
|
|
155
|
+
switch (period.selectedPeriod) {
|
|
156
|
+
case 'SELECTED_PERIOD': {
|
|
157
|
+
start = dayjs(new Date(period.selectedStart)).startOf('day');
|
|
158
|
+
end = dayjs(new Date(period.selectedEnd)).endOf('day');
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case 'ENTIRE_LOAN': {
|
|
162
|
+
const products = await LoanProduct.find({ borrowerId: borrower._id.toString() }).lean();
|
|
163
|
+
const productPeriods = await Promise.all(products.map(async (product) => {
|
|
164
|
+
const periods = await getPeriods(period.selectedPeriod, product._id.toString());
|
|
165
|
+
const periodStart = periods[0].start.startOf('day');
|
|
166
|
+
const periodEnd = periods[periods.length - 1].end.endOf('day');
|
|
167
|
+
return { periodStart, periodEnd };
|
|
168
|
+
}));
|
|
169
|
+
const starts = productPeriods.map((p) => p.periodStart);
|
|
170
|
+
const ends = productPeriods.map((p) => p.periodEnd);
|
|
171
|
+
start = dayjs.min(starts);
|
|
172
|
+
end = dayjs.max(ends);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
default: {
|
|
176
|
+
const periods = await getPeriods(period.selectedPeriod, null);
|
|
177
|
+
start = periods[0].start.startOf('day');
|
|
178
|
+
end = periods[periods.length - 1].end.endOf('day');
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (splitPeriods) {
|
|
184
|
+
const result = [];
|
|
185
|
+
let currentMonth = start.startOf('month');
|
|
186
|
+
while (currentMonth.isBefore(end) || currentMonth.isSame(end, 'month')) {
|
|
187
|
+
const startOfMonth = currentMonth.startOf('month');
|
|
188
|
+
const endOfMonth = currentMonth.endOf('month');
|
|
189
|
+
result.push({
|
|
190
|
+
_id: borrower._id.toString(),
|
|
191
|
+
code: borrower.code,
|
|
192
|
+
name: borrower.name,
|
|
193
|
+
period: {
|
|
194
|
+
selectedPeriod: period.selectedPeriod,
|
|
195
|
+
start: startOfMonth.utc().toDate().toISOString(),
|
|
196
|
+
end: endOfMonth.utc().toDate().toISOString(),
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
currentMonth = currentMonth.add(1, 'month');
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
} else {
|
|
203
|
+
return [{
|
|
204
|
+
_id: borrower._id.toString(),
|
|
205
|
+
code: borrower.code,
|
|
206
|
+
name: borrower.name,
|
|
207
|
+
period: {
|
|
208
|
+
selectedPeriod: period.selectedPeriod,
|
|
209
|
+
start: start.utc().toDate().toISOString(),
|
|
210
|
+
end: end.utc().toDate().toISOString(),
|
|
211
|
+
},
|
|
212
|
+
}];
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export const getPeriods = async (selectedPeriod: string, productId: string): Promise<IStatementPeriod[]> => {
|
|
217
|
+
let start: dayjs.Dayjs;
|
|
218
|
+
let end: dayjs.Dayjs;
|
|
219
|
+
switch (selectedPeriod) {
|
|
220
|
+
case 'CURRENT_MONTH': {
|
|
221
|
+
start = dayjs().utcOffset(0).startOf('month');
|
|
222
|
+
end = dayjs().utcOffset(0).endOf('month');
|
|
223
|
+
return [{ start, end }];
|
|
224
|
+
}
|
|
225
|
+
case 'LAST_MONTH': {
|
|
226
|
+
start = dayjs().subtract(1, 'month').utcOffset(0).startOf('month');
|
|
227
|
+
end = dayjs().subtract(1, 'month').utcOffset(0).endOf('month');
|
|
228
|
+
return [{ start, end }];
|
|
229
|
+
}
|
|
230
|
+
case 'ENTIRE_LOAN': {
|
|
231
|
+
const product = await getLoanProductById(productId);
|
|
232
|
+
const lastTransaction = await getLastTransactionForDate(productId);
|
|
233
|
+
start = dayjs(product.startDate).tz('UTC');
|
|
234
|
+
let lastDate = dayjs(new Date()).endOf('month').toDate();
|
|
235
|
+
if (lastTransaction) {
|
|
236
|
+
if (dayjs(lastTransaction.date).tz('UTC').toDate() > dayjs(new Date()).endOf('month').toDate()) {
|
|
237
|
+
lastDate = lastTransaction.date;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
end = dayjs(lastDate);
|
|
241
|
+
const finish = dayjs(lastDate);
|
|
242
|
+
const periods = [];
|
|
243
|
+
do {
|
|
244
|
+
end = start.endOf('month');
|
|
245
|
+
periods.push({ start, end });
|
|
246
|
+
start = end.add(1, 'day').startOf('month').utc();
|
|
247
|
+
} while (finish.toDate() > end.toDate());
|
|
248
|
+
return periods;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return [];
|
|
252
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
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.getLastTransactionForDate = void 0;
|
|
7
|
+
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
|
|
8
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
9
|
+
const getLastTransactionForDate = async (productId, date = new Date(), excludeAdjustments = false) => {
|
|
10
|
+
const excludeFilter = excludeAdjustments
|
|
11
|
+
? {
|
|
12
|
+
'transactionType': {
|
|
13
|
+
$nin: ['ADJUSTMENT'],
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
: {};
|
|
17
|
+
const lastTransaction = await LoanTransaction_model_1.LoanTransaction.aggregate([
|
|
18
|
+
{
|
|
19
|
+
$match: {
|
|
20
|
+
'productId': new mongoose_1.default.Types.ObjectId(productId),
|
|
21
|
+
'date': { $lte: date },
|
|
22
|
+
...excludeFilter,
|
|
23
|
+
},
|
|
24
|
+
}, {
|
|
25
|
+
$sort: {
|
|
26
|
+
'date': -1,
|
|
27
|
+
'order': -1,
|
|
28
|
+
'createdAt': -1,
|
|
29
|
+
},
|
|
30
|
+
}, {
|
|
31
|
+
$limit: 1,
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
if (!lastTransaction.length) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return lastTransaction[0];
|
|
38
|
+
};
|
|
39
|
+
exports.getLastTransactionForDate = getLastTransactionForDate;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ILoanTransactionDoc, LoanTransaction } from '../models/LoanTransaction.model';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
|
|
4
|
+
export const getLastTransactionForDate = async (productId: string, date: Date = new Date(), excludeAdjustments = false) => {
|
|
5
|
+
const excludeFilter = excludeAdjustments
|
|
6
|
+
? {
|
|
7
|
+
'transactionType': {
|
|
8
|
+
$nin: ['ADJUSTMENT'],
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
: {};
|
|
12
|
+
const lastTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
|
|
13
|
+
{
|
|
14
|
+
$match: {
|
|
15
|
+
'productId': new mongoose.Types.ObjectId(productId),
|
|
16
|
+
'date': { $lte: date },
|
|
17
|
+
...excludeFilter,
|
|
18
|
+
},
|
|
19
|
+
}, {
|
|
20
|
+
$sort: {
|
|
21
|
+
'date': -1,
|
|
22
|
+
'order': -1,
|
|
23
|
+
'createdAt': -1,
|
|
24
|
+
},
|
|
25
|
+
}, {
|
|
26
|
+
$limit: 1,
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
if (!lastTransaction.length) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return lastTransaction[0];
|
|
33
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ELedgerReportType } from './loan-statement.db';
|
|
2
|
+
export declare enum EChargeType {
|
|
3
|
+
INTEREST_FEE = "INTEREST",
|
|
4
|
+
ADMIN_FEE = "ADMIN FEE",
|
|
5
|
+
UNUSED_LINE_FEE = "UNUSED LINE FEE",
|
|
6
|
+
ANNUAL_LINE_FEE = "ANNUAL LINE FEE",
|
|
7
|
+
WIRE_FEE = "WIRE FEE",
|
|
8
|
+
RECOVERABLE = "RECOVERABLE",
|
|
9
|
+
OTHER = "OTHER",
|
|
10
|
+
DISBURSEMENT = "DISBURSEMENT",
|
|
11
|
+
PRINCIPAL = "PRINCIPAL",
|
|
12
|
+
COLLECTION = "COLLECTION",
|
|
13
|
+
ADJUSTMENT = "ADJUSTMENT",
|
|
14
|
+
EXPECTED_PRINCIPAL = "EXPECTED PRINCIPAL ACCRUED",
|
|
15
|
+
EXPECTED_PRINCIPAL_PAID = "EXPECTED PRINCIPAL PAID"
|
|
16
|
+
}
|
|
17
|
+
export interface ILedgerReportRow {
|
|
18
|
+
borrowerCode: string;
|
|
19
|
+
date: string;
|
|
20
|
+
type: string;
|
|
21
|
+
chargeCode: string;
|
|
22
|
+
PLCode: string;
|
|
23
|
+
productCode: string;
|
|
24
|
+
productName: string;
|
|
25
|
+
amount: number;
|
|
26
|
+
statementAmount: number;
|
|
27
|
+
title: string;
|
|
28
|
+
balance: number;
|
|
29
|
+
floatedBalance: number;
|
|
30
|
+
info: string;
|
|
31
|
+
paymentDate: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ILedgerDataRow {
|
|
34
|
+
typeOrder: number;
|
|
35
|
+
order: number;
|
|
36
|
+
date: Date;
|
|
37
|
+
type: EChargeType;
|
|
38
|
+
amount: number;
|
|
39
|
+
statementAmount: number;
|
|
40
|
+
title: string;
|
|
41
|
+
chargeCode: string;
|
|
42
|
+
PLCode: string;
|
|
43
|
+
productCode: string;
|
|
44
|
+
productName: string;
|
|
45
|
+
balance: number;
|
|
46
|
+
floatedBalance: number;
|
|
47
|
+
createdAt: Date;
|
|
48
|
+
info: string;
|
|
49
|
+
paymentDate: Date;
|
|
50
|
+
isBrokerFee?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export declare const getLedger: (productId: string, startDate: Date, endDate: Date, reportType: ELedgerReportType, showFloatedBalance?: boolean) => Promise<{
|
|
53
|
+
[x: string]: string[][];
|
|
54
|
+
} | {
|
|
55
|
+
[x: string]: Pick<any, string>[];
|
|
56
|
+
}>;
|
package/db/reports.db.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
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.getLedger = exports.EChargeType = void 0;
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
9
|
+
const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
10
|
+
const loan_statement_db_1 = require("./loan-statement.db");
|
|
11
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
12
|
+
const Borrower_model_1 = require("../models/Borrower.model");
|
|
13
|
+
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
|
|
14
|
+
const loan_types_enum_1 = require("../enums/loan-types.enum");
|
|
15
|
+
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
|
|
16
|
+
const date_helper_1 = require("../helpers/date.helper");
|
|
17
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
18
|
+
const loan_charges_db_1 = require("./loan-charges.db");
|
|
19
|
+
const loan_products_db_1 = require("./loan-products.db");
|
|
20
|
+
const loan_payments_db_1 = require("./loan-payments.db");
|
|
21
|
+
const brokers_db_1 = require("./brokers.db");
|
|
22
|
+
const TermLoan_model_1 = require("../models/TermLoan.model");
|
|
23
|
+
const TermLoanCalculated_model_1 = require("../models/TermLoanCalculated.model");
|
|
24
|
+
var EChargeType;
|
|
25
|
+
(function (EChargeType) {
|
|
26
|
+
EChargeType["INTEREST_FEE"] = "INTEREST";
|
|
27
|
+
EChargeType["ADMIN_FEE"] = "ADMIN FEE";
|
|
28
|
+
EChargeType["UNUSED_LINE_FEE"] = "UNUSED LINE FEE";
|
|
29
|
+
EChargeType["ANNUAL_LINE_FEE"] = "ANNUAL LINE FEE";
|
|
30
|
+
EChargeType["WIRE_FEE"] = "WIRE FEE";
|
|
31
|
+
EChargeType["RECOVERABLE"] = "RECOVERABLE";
|
|
32
|
+
EChargeType["OTHER"] = "OTHER";
|
|
33
|
+
EChargeType["DISBURSEMENT"] = "DISBURSEMENT";
|
|
34
|
+
EChargeType["PRINCIPAL"] = "PRINCIPAL";
|
|
35
|
+
EChargeType["COLLECTION"] = "COLLECTION";
|
|
36
|
+
EChargeType["ADJUSTMENT"] = "ADJUSTMENT";
|
|
37
|
+
EChargeType["EXPECTED_PRINCIPAL"] = "EXPECTED PRINCIPAL ACCRUED";
|
|
38
|
+
EChargeType["EXPECTED_PRINCIPAL_PAID"] = "EXPECTED PRINCIPAL PAID";
|
|
39
|
+
})(EChargeType || (exports.EChargeType = EChargeType = {}));
|
|
40
|
+
const getLedger = async (productId, startDate, endDate, reportType, showFloatedBalance = true) => {
|
|
41
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
|
|
42
|
+
const emptyWorkBook = { [`${product.code} - ${product.name}`]: [['']] };
|
|
43
|
+
if (startDate === null || endDate === null) {
|
|
44
|
+
return emptyWorkBook;
|
|
45
|
+
}
|
|
46
|
+
const fullTransactions = await getLedgerData(productId, startDate, endDate);
|
|
47
|
+
const header = loan_statement_db_1.ledgerHeadersMap[reportType];
|
|
48
|
+
const headerKeys = showFloatedBalance
|
|
49
|
+
? Object.keys(header).filter((key) => key !== 'floatedBalance')
|
|
50
|
+
: Object.keys(header);
|
|
51
|
+
const sumKeys = ['amount', 'statementAmount'];
|
|
52
|
+
const groupKeys = headerKeys.filter((key) => !sumKeys.includes(key));
|
|
53
|
+
const finalTransactions = reportType === loan_statement_db_1.ELedgerReportType.SHORT
|
|
54
|
+
? mergeLedgerData(fullTransactions, groupKeys, sumKeys)
|
|
55
|
+
: fullTransactions;
|
|
56
|
+
const mappedArray = lodash_1.default.map([header, ...finalTransactions], (obj) => lodash_1.default.pick(obj, headerKeys));
|
|
57
|
+
const sheetName = `${product.code} - ${product.name}`;
|
|
58
|
+
return { [sheetName]: mappedArray };
|
|
59
|
+
};
|
|
60
|
+
exports.getLedger = getLedger;
|
|
61
|
+
const getLedgerData = async (productId, startDate, endDate) => {
|
|
62
|
+
const addBrokers = true;
|
|
63
|
+
const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
|
|
64
|
+
const borrower = await Borrower_model_1.BorrowerModel.findById(product.borrowerId).lean();
|
|
65
|
+
const transactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
|
|
66
|
+
{
|
|
67
|
+
$match: {
|
|
68
|
+
$and: [
|
|
69
|
+
{ 'date': { $gte: startDate } },
|
|
70
|
+
{ 'date': { $lte: (0, dayjs_1.default)(endDate).endOf('day').toDate() } },
|
|
71
|
+
{ 'productId': new mongoose_1.default.Types.ObjectId(productId) },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
}, {
|
|
75
|
+
$lookup: {
|
|
76
|
+
from: 'loan_products',
|
|
77
|
+
localField: 'productId',
|
|
78
|
+
foreignField: '_id',
|
|
79
|
+
as: 'product',
|
|
80
|
+
},
|
|
81
|
+
}, {
|
|
82
|
+
$unwind: {
|
|
83
|
+
path: '$product',
|
|
84
|
+
},
|
|
85
|
+
}, {
|
|
86
|
+
$sort: {
|
|
87
|
+
'date': 1,
|
|
88
|
+
'order': 1,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
const convertTypes = (transactionType) => {
|
|
93
|
+
if (product.type === loan_types_enum_1.ELoanTypes.TERM && transactionType === LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION) {
|
|
94
|
+
return 'PRINCIPAL';
|
|
95
|
+
}
|
|
96
|
+
return transactionType;
|
|
97
|
+
};
|
|
98
|
+
const mappedTransactions = transactions.map((transaction) => {
|
|
99
|
+
return {
|
|
100
|
+
typeOrder: 0,
|
|
101
|
+
order: transaction.order,
|
|
102
|
+
date: transaction.date,
|
|
103
|
+
type: convertTypes(transaction.transactionType),
|
|
104
|
+
amount: transaction.amount,
|
|
105
|
+
statementAmount: null,
|
|
106
|
+
title: convertTypes(transaction.transactionType),
|
|
107
|
+
balance: transaction.balance,
|
|
108
|
+
floatedBalance: transaction.floatedBalance,
|
|
109
|
+
chargeCode: transaction.product.code,
|
|
110
|
+
PLCode: null,
|
|
111
|
+
productCode: transaction.product.code,
|
|
112
|
+
productName: transaction.product.name,
|
|
113
|
+
createdAt: transaction['createdAt'],
|
|
114
|
+
info: transaction.reference,
|
|
115
|
+
paymentDate: null,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
const loanCharges = await (0, loan_charges_db_1.getLoanChargeForProduct)(productId);
|
|
119
|
+
const chargesIds = loanCharges.map((charge) => charge._id);
|
|
120
|
+
const statementTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
121
|
+
{
|
|
122
|
+
$match: {
|
|
123
|
+
$and: [
|
|
124
|
+
{ 'date': { $gte: startDate } },
|
|
125
|
+
{ 'date': { $lte: (0, dayjs_1.default)(endDate).endOf('day').toDate() } },
|
|
126
|
+
{ 'chargeId': { '$in': chargesIds } },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
}, {
|
|
130
|
+
$lookup: {
|
|
131
|
+
from: 'loan_charges',
|
|
132
|
+
localField: 'chargeId',
|
|
133
|
+
foreignField: '_id',
|
|
134
|
+
as: 'charge',
|
|
135
|
+
},
|
|
136
|
+
}, {
|
|
137
|
+
$unwind: {
|
|
138
|
+
path: '$charge',
|
|
139
|
+
},
|
|
140
|
+
}, {
|
|
141
|
+
$sort: {
|
|
142
|
+
'date': 1,
|
|
143
|
+
'createdAt': 1,
|
|
144
|
+
'order': 1,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
const statementTransactionsWithBalance = await Promise.all(statementTransactions.map(async (statement) => {
|
|
149
|
+
const balances = await (0, loan_products_db_1.getLoanProductBalance)(productId, statement.date);
|
|
150
|
+
return {
|
|
151
|
+
...statement,
|
|
152
|
+
balance: balances.balance,
|
|
153
|
+
floatedBalance: balances.floatedBalance,
|
|
154
|
+
};
|
|
155
|
+
}));
|
|
156
|
+
const isParticipant = product.isParticipant;
|
|
157
|
+
const groupedMappedStatementTransactions = await Promise.all(statementTransactionsWithBalance.map(async (statement) => {
|
|
158
|
+
const transactions = [];
|
|
159
|
+
if (statement.amountPaid > 0) {
|
|
160
|
+
const payments = await (0, loan_payments_db_1.getPaymentForTransaction)(productId, statement._id.toString());
|
|
161
|
+
for (const payment of payments) {
|
|
162
|
+
transactions.push({
|
|
163
|
+
typeOrder: 1,
|
|
164
|
+
order: statement.order,
|
|
165
|
+
date: statement.date,
|
|
166
|
+
type: statement.charge.chargeType,
|
|
167
|
+
amount: null,
|
|
168
|
+
statementAmount: isParticipant ? statement.amountPaid : -statement.amountPaid,
|
|
169
|
+
title: `${statement.charge.name} PAID`,
|
|
170
|
+
chargeCode: statement.charge.code,
|
|
171
|
+
PLCode: statement.charge.PLCode,
|
|
172
|
+
productCode: product.code,
|
|
173
|
+
productName: product.name,
|
|
174
|
+
balance: statement.balance,
|
|
175
|
+
floatedBalance: statement.floatedBalance,
|
|
176
|
+
createdAt: statement['createdAt'],
|
|
177
|
+
info: statement.memo,
|
|
178
|
+
paymentDate: (0, dayjs_1.default)(payment.date).format(date_helper_1.defaultDateFormat),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return [...transactions, {
|
|
183
|
+
typeOrder: 1,
|
|
184
|
+
order: statement.order,
|
|
185
|
+
date: statement.date,
|
|
186
|
+
type: statement.charge.chargeType,
|
|
187
|
+
amount: null,
|
|
188
|
+
statementAmount: isParticipant ? -statement.amount : statement.amount,
|
|
189
|
+
title: `${statement.charge.name} ACCRUED`,
|
|
190
|
+
chargeCode: statement.charge.code,
|
|
191
|
+
PLCode: statement.charge.PLCode,
|
|
192
|
+
productCode: product.code,
|
|
193
|
+
productName: product.name,
|
|
194
|
+
balance: statement.balance,
|
|
195
|
+
floatedBalance: statement.floatedBalance,
|
|
196
|
+
createdAt: statement['createdAt'],
|
|
197
|
+
info: statement.memo,
|
|
198
|
+
paymentDate: null,
|
|
199
|
+
}];
|
|
200
|
+
}));
|
|
201
|
+
const mappedStatementTransactions = groupedMappedStatementTransactions
|
|
202
|
+
.reduce((acc, group) => [...acc, ...group], []);
|
|
203
|
+
const statementTransactionsWithBrokers = addBrokers
|
|
204
|
+
? await (0, brokers_db_1.enrichWithBrokers)(mappedStatementTransactions, product)
|
|
205
|
+
: mappedStatementTransactions;
|
|
206
|
+
const sortedTransactions = [...statementTransactionsWithBrokers, ...mappedTransactions]
|
|
207
|
+
.sort((a, b) => {
|
|
208
|
+
if (a.date.valueOf() !== b.date.valueOf()) {
|
|
209
|
+
return b.date.valueOf() - a.date.valueOf();
|
|
210
|
+
}
|
|
211
|
+
if (a.typeOrder !== b.typeOrder) {
|
|
212
|
+
return b.typeOrder - a.typeOrder;
|
|
213
|
+
}
|
|
214
|
+
return b.createdAt.valueOf() - a.createdAt.valueOf();
|
|
215
|
+
});
|
|
216
|
+
const fullTransactions = sortedTransactions
|
|
217
|
+
.map((row) => ({
|
|
218
|
+
borrowerCode: borrower.code,
|
|
219
|
+
date: (0, date_helper_1.formatDate)(row.date),
|
|
220
|
+
type: !!row.isBrokerFee ? `${EChargeType[row.type]} BROKER` : EChargeType[row.type],
|
|
221
|
+
chargeCode: row.chargeCode,
|
|
222
|
+
PLCode: row.PLCode,
|
|
223
|
+
productCode: row.productCode,
|
|
224
|
+
productName: row.productName,
|
|
225
|
+
amount: row.amount,
|
|
226
|
+
statementAmount: row.statementAmount,
|
|
227
|
+
title: row.title,
|
|
228
|
+
balance: row.balance,
|
|
229
|
+
floatedBalance: row.floatedBalance,
|
|
230
|
+
info: row.info,
|
|
231
|
+
paymentDate: row.paymentDate ? (0, date_helper_1.formatDate)(row.paymentDate) : '',
|
|
232
|
+
}));
|
|
233
|
+
const termLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId, actual: true });
|
|
234
|
+
if (termLoan) {
|
|
235
|
+
const calculatedTermLoan = await TermLoanCalculated_model_1.TermLoanCalculatedModel.findOne({
|
|
236
|
+
termLoanId: termLoan._id.toString(),
|
|
237
|
+
relevantStatement: (0, dayjs_1.default)(endDate).format('YYYY-MM-DD'),
|
|
238
|
+
});
|
|
239
|
+
if (calculatedTermLoan) {
|
|
240
|
+
const expectedPayment = {
|
|
241
|
+
borrowerCode: borrower.code,
|
|
242
|
+
date: (0, date_helper_1.formatDate)(endDate),
|
|
243
|
+
type: EChargeType.EXPECTED_PRINCIPAL,
|
|
244
|
+
chargeCode: null,
|
|
245
|
+
PLCode: null,
|
|
246
|
+
productCode: product.code,
|
|
247
|
+
amount: isParticipant ? -calculatedTermLoan.monthlyPrincipal : calculatedTermLoan.monthlyPrincipal,
|
|
248
|
+
statementAmount: null,
|
|
249
|
+
title: EChargeType.EXPECTED_PRINCIPAL,
|
|
250
|
+
balance: null,
|
|
251
|
+
floatedBalance: null,
|
|
252
|
+
info: EChargeType.EXPECTED_PRINCIPAL,
|
|
253
|
+
paymentDate: null,
|
|
254
|
+
};
|
|
255
|
+
fullTransactions.unshift(expectedPayment);
|
|
256
|
+
if (calculatedTermLoan.payments && calculatedTermLoan.payments.length > 0) {
|
|
257
|
+
for (const payment of calculatedTermLoan.payments) {
|
|
258
|
+
const paymentDoc = await LoanTransaction_model_1.LoanTransaction.findById(payment.paymentId).lean();
|
|
259
|
+
if (paymentDoc) {
|
|
260
|
+
const expectedPayment = {
|
|
261
|
+
borrowerCode: borrower.code,
|
|
262
|
+
date: (0, date_helper_1.formatDate)(endDate),
|
|
263
|
+
type: EChargeType.EXPECTED_PRINCIPAL_PAID,
|
|
264
|
+
chargeCode: null,
|
|
265
|
+
PLCode: null,
|
|
266
|
+
productCode: product.code,
|
|
267
|
+
amount: isParticipant ? -payment.amount : payment.amount,
|
|
268
|
+
statementAmount: null,
|
|
269
|
+
title: EChargeType.EXPECTED_PRINCIPAL_PAID,
|
|
270
|
+
balance: null,
|
|
271
|
+
floatedBalance: null,
|
|
272
|
+
info: EChargeType.EXPECTED_PRINCIPAL_PAID,
|
|
273
|
+
paymentDate: (0, date_helper_1.formatDate)(paymentDoc.date),
|
|
274
|
+
};
|
|
275
|
+
fullTransactions.unshift(expectedPayment);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return fullTransactions;
|
|
282
|
+
};
|
|
283
|
+
const mergeLedgerData = (transactions, groupKeys, sumKeys) => {
|
|
284
|
+
const groupedObjects = lodash_1.default.groupBy(transactions, (obj) => groupKeys.reduce((acc, key) => `${acc}${obj[key]}`, ''));
|
|
285
|
+
const mergedObjects = lodash_1.default.mapValues(groupedObjects, group => lodash_1.default.reduce(group, (result, obj) => {
|
|
286
|
+
sumKeys.forEach((key) => {
|
|
287
|
+
result[key] = new decimal_js_1.default(result[key] || 0).plus(obj[key] || 0).toNumber();
|
|
288
|
+
});
|
|
289
|
+
groupKeys.forEach((key) => {
|
|
290
|
+
result[key] = obj[key];
|
|
291
|
+
});
|
|
292
|
+
return result;
|
|
293
|
+
}, {}));
|
|
294
|
+
return lodash_1.default.values(mergedObjects);
|
|
295
|
+
};
|