gemcap-be-common 1.2.63 → 1.2.64

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/reports.db.js CHANGED
@@ -37,38 +37,57 @@ var EChargeType;
37
37
  EChargeType["EXPECTED_PRINCIPAL"] = "EXPECTED PRINCIPAL ACCRUED";
38
38
  EChargeType["EXPECTED_PRINCIPAL_PAID"] = "EXPECTED PRINCIPAL PAID";
39
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);
40
+ const defaultReportLedgerOptions = {
41
+ showBalances: true,
42
+ showFloatedBalance: true,
43
+ };
44
+ const getLedger = async (params, options = defaultReportLedgerOptions) => {
45
+ const { productIds, reportType } = params;
46
+ const fullTransactions = await getLedgerData(params, options);
47
+ const products = await LoanProducts_model_1.LoanProduct
48
+ .find({ _id: { $in: productIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } })
49
+ .sort({ code: 1 })
50
+ .lean();
47
51
  const header = loan_statement_db_1.ledgerHeadersMap[reportType];
48
- const headerKeys = showFloatedBalance
52
+ const headerKeys = options.showFloatedBalance
49
53
  ? Object.keys(header).filter((key) => key !== 'floatedBalance')
50
54
  : Object.keys(header);
51
55
  const sumKeys = ['amount', 'statementAmount'];
52
56
  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 };
57
+ return products.reduce((acc, product) => {
58
+ const transactions = fullTransactions[product._id.toString()];
59
+ const finalTransactions = reportType === loan_statement_db_1.ELedgerReportType.SHORT
60
+ ? mergeLedgerData(transactions, groupKeys, sumKeys)
61
+ : transactions;
62
+ const sheetName = `${product.code} - ${product.name}`;
63
+ return {
64
+ ...acc,
65
+ [sheetName]: lodash_1.default.map(finalTransactions, (obj) => lodash_1.default.pick(obj, headerKeys)),
66
+ };
67
+ }, {});
59
68
  };
60
69
  exports.getLedger = getLedger;
61
- const getLedgerData = async (productId, startDate, endDate) => {
70
+ const getLedgerData = async (params, options = defaultReportLedgerOptions) => {
71
+ const { productIds, startDate, endDate } = params;
62
72
  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([
73
+ const products = await LoanProducts_model_1.LoanProduct.find({ _id: { $in: productIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } }).lean();
74
+ const borrowers = await Borrower_model_1.BorrowerModel.find({ _id: { $in: products.map((product) => product.borrowerId) } }).lean();
75
+ const borrowerMap = borrowers.reduce((acc, borrower) => ({ ...acc, [borrower._id.toString()]: borrower.code }), {});
76
+ const borrowerCodesMap = products.reduce((acc, product) => ({
77
+ ...acc,
78
+ [product._id.toString()]: borrowerMap[product.borrowerId.toString()],
79
+ }), {});
80
+ const groupedTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
66
81
  {
67
82
  $match: {
68
83
  $and: [
69
84
  { 'date': { $gte: startDate } },
70
85
  { 'date': { $lte: (0, dayjs_1.default)(endDate).endOf('day').toDate() } },
71
- { 'productId': new mongoose_1.default.Types.ObjectId(productId) },
86
+ {
87
+ productId: {
88
+ $in: products.map((product) => product._id),
89
+ },
90
+ },
72
91
  ],
73
92
  },
74
93
  }, {
@@ -88,197 +107,226 @@ const getLedgerData = async (productId, startDate, endDate) => {
88
107
  'order': 1,
89
108
  },
90
109
  },
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
110
  {
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',
111
+ $group: {
112
+ _id: '$productId',
113
+ transactions: { $push: '$$ROOT' },
114
+ product: { $first: '$product' },
139
115
  },
140
- }, {
141
- $sort: {
142
- 'date': 1,
143
- 'createdAt': 1,
144
- 'order': 1,
116
+ },
117
+ {
118
+ $project: {
119
+ _id: 0,
120
+ productId: '$_id',
121
+ product: 1,
122
+ transactions: 1,
145
123
  },
146
124
  },
147
125
  ]);
148
- const statementTransactionsWithBalance = await Promise.all(statementTransactions.map(async (statement) => {
149
- const balances = await (0, loan_products_db_1.getLoanProductBalance)(productId, statement.date);
126
+ const convertTypes = (transactionType, product) => {
127
+ if (product.type === loan_types_enum_1.ELoanTypes.TERM && transactionType === LoanTransaction_model_1.ELoanTransactionTypes.COLLECTION) {
128
+ return EChargeType.PRINCIPAL;
129
+ }
130
+ return EChargeType[transactionType];
131
+ };
132
+ const mappedGroups = groupedTransactions.reduce((acc, group) => {
150
133
  return {
151
- ...statement,
152
- balance: balances.balance,
153
- floatedBalance: balances.floatedBalance,
134
+ ...acc,
135
+ [group.productId.toString()]: {
136
+ product: group.product,
137
+ transactions: group.transactions.map((transaction) => {
138
+ return {
139
+ typeOrder: 0,
140
+ order: transaction.order,
141
+ date: transaction.date,
142
+ type: convertTypes(transaction.transactionType, group.product),
143
+ amount: transaction.amount,
144
+ statementAmount: null,
145
+ title: convertTypes(transaction.transactionType, group.product),
146
+ balance: transaction.balance,
147
+ floatedBalance: transaction.floatedBalance,
148
+ chargeCode: group.product.code,
149
+ PLCode: null,
150
+ productCode: group.product.code,
151
+ productName: group.product.name,
152
+ createdAt: transaction['createdAt'],
153
+ info: transaction.reference,
154
+ paymentDate: null,
155
+ };
156
+ }),
157
+ },
154
158
  };
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({
159
+ }, {});
160
+ const allData = {};
161
+ await Promise.all(Object.entries(mappedGroups).map(async ([productId, group]) => {
162
+ const loanCharges = await (0, loan_charges_db_1.getLoanChargeForProduct)(productId);
163
+ const chargesIds = loanCharges.map((charge) => charge._id);
164
+ const statementTransactions = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
165
+ {
166
+ $match: {
167
+ $and: [
168
+ { 'date': { $gte: startDate } },
169
+ { 'date': { $lte: (0, dayjs_1.default)(endDate).endOf('day').toDate() } },
170
+ { 'chargeId': { '$in': chargesIds } },
171
+ ],
172
+ },
173
+ }, {
174
+ $lookup: {
175
+ from: 'loan_charges',
176
+ localField: 'chargeId',
177
+ foreignField: '_id',
178
+ as: 'charge',
179
+ },
180
+ }, {
181
+ $unwind: {
182
+ path: '$charge',
183
+ },
184
+ }, {
185
+ $sort: {
186
+ 'date': 1,
187
+ 'createdAt': 1,
188
+ 'order': 1,
189
+ },
190
+ },
191
+ ]);
192
+ const statementTransactionsWithBalance = options.showBalances
193
+ ? await Promise.all(statementTransactions.map(async (statement) => {
194
+ const balances = await (0, loan_products_db_1.getLoanProductBalance)(productId, statement.date);
195
+ return {
196
+ ...statement,
197
+ balance: balances.balance,
198
+ floatedBalance: balances.floatedBalance,
199
+ };
200
+ }))
201
+ : statementTransactions.slice();
202
+ const isParticipant = group.product.isParticipant;
203
+ const groupedMappedStatementTransactions = await Promise.all(statementTransactionsWithBalance.map(async (statement) => {
204
+ const transactions = [];
205
+ if (statement.amountPaid > 0) {
206
+ const payments = await (0, loan_payments_db_1.getPaymentForTransaction)(productId, statement._id.toString());
207
+ for (const payment of payments) {
208
+ transactions.push({
209
+ typeOrder: 1,
210
+ order: statement.order,
211
+ date: statement.date,
212
+ type: statement.charge.chargeType,
213
+ amount: null,
214
+ statementAmount: isParticipant ? statement.amountPaid : -statement.amountPaid,
215
+ title: `${statement.charge.name} PAID`,
216
+ chargeCode: statement.charge.code,
217
+ PLCode: statement.charge.PLCode,
218
+ productCode: group.product.code,
219
+ productName: group.product.name,
220
+ balance: statement.balance,
221
+ floatedBalance: statement.floatedBalance,
222
+ createdAt: statement['createdAt'],
223
+ info: statement.memo,
224
+ paymentDate: (0, dayjs_1.default)(payment.date).format(date_helper_1.defaultDateFormat),
225
+ });
226
+ }
227
+ }
228
+ return [...transactions, {
163
229
  typeOrder: 1,
164
230
  order: statement.order,
165
231
  date: statement.date,
166
232
  type: statement.charge.chargeType,
167
233
  amount: null,
168
- statementAmount: isParticipant ? statement.amountPaid : -statement.amountPaid,
169
- title: `${statement.charge.name} PAID`,
234
+ statementAmount: isParticipant ? -statement.amount : statement.amount,
235
+ title: `${statement.charge.name} ACCRUED`,
170
236
  chargeCode: statement.charge.code,
171
237
  PLCode: statement.charge.PLCode,
172
- productCode: product.code,
173
- productName: product.name,
238
+ productCode: group.product.code,
239
+ productName: group.product.name,
174
240
  balance: statement.balance,
175
241
  floatedBalance: statement.floatedBalance,
176
242
  createdAt: statement['createdAt'],
177
243
  info: statement.memo,
178
- paymentDate: (0, dayjs_1.default)(payment.date).format(date_helper_1.defaultDateFormat),
179
- });
244
+ paymentDate: null,
245
+ }];
246
+ }));
247
+ const mappedStatementTransactions = groupedMappedStatementTransactions
248
+ .reduce((acc, group) => [...acc, ...group], []);
249
+ const statementTransactionsWithBrokers = addBrokers
250
+ ? await (0, brokers_db_1.enrichWithBrokers)(mappedStatementTransactions, group.product)
251
+ : mappedStatementTransactions;
252
+ const sortedTransactions = [...statementTransactionsWithBrokers, ...group.transactions]
253
+ .sort((a, b) => {
254
+ if (a.date.valueOf() !== b.date.valueOf()) {
255
+ return b.date.valueOf() - a.date.valueOf();
180
256
  }
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'),
257
+ if (a.typeOrder !== b.typeOrder) {
258
+ return b.typeOrder - a.typeOrder;
259
+ }
260
+ return b.createdAt.valueOf() - a.createdAt.valueOf();
238
261
  });
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);
262
+ const fullTransactions = sortedTransactions
263
+ .map((row) => ({
264
+ borrowerCode: borrowerCodesMap[productId],
265
+ date: (0, date_helper_1.formatDate)(row.date),
266
+ type: row.isBrokerFee ? `${EChargeType[row.type]} BROKER` : EChargeType[row.type],
267
+ chargeCode: row.chargeCode,
268
+ PLCode: row.PLCode,
269
+ productCode: row.productCode,
270
+ productName: row.productName,
271
+ amount: row.amount,
272
+ statementAmount: row.statementAmount,
273
+ title: row.title,
274
+ balance: row.balance,
275
+ floatedBalance: row.floatedBalance,
276
+ info: row.info,
277
+ paymentDate: row.paymentDate ? (0, date_helper_1.formatDate)(row.paymentDate) : '',
278
+ }));
279
+ const termLoan = await TermLoan_model_1.TermLoanModel.findOne({ productId, actual: true });
280
+ if (termLoan) {
281
+ const calculatedTermLoan = await TermLoanCalculated_model_1.TermLoanCalculatedModel.findOne({
282
+ termLoanId: termLoan._id.toString(),
283
+ relevantStatement: (0, dayjs_1.default)(endDate).format('YYYY-MM-DD'),
284
+ });
285
+ if (calculatedTermLoan) {
286
+ const expectedPayment = {
287
+ borrowerCode: borrowerCodesMap[productId],
288
+ date: (0, date_helper_1.formatDate)(endDate),
289
+ type: EChargeType.EXPECTED_PRINCIPAL,
290
+ chargeCode: null,
291
+ PLCode: null,
292
+ productCode: group.product.code,
293
+ amount: isParticipant ? -calculatedTermLoan.monthlyPrincipal : calculatedTermLoan.monthlyPrincipal,
294
+ statementAmount: null,
295
+ title: EChargeType.EXPECTED_PRINCIPAL,
296
+ balance: null,
297
+ floatedBalance: null,
298
+ info: EChargeType.EXPECTED_PRINCIPAL,
299
+ paymentDate: null,
300
+ };
301
+ fullTransactions.unshift(expectedPayment);
302
+ if (calculatedTermLoan.payments && calculatedTermLoan.payments.length > 0) {
303
+ for (const payment of calculatedTermLoan.payments) {
304
+ const paymentDoc = await LoanTransaction_model_1.LoanTransaction.findById(payment.paymentId).lean();
305
+ if (paymentDoc) {
306
+ const expectedPayment = {
307
+ borrowerCode: borrowerCodesMap[productId],
308
+ date: (0, date_helper_1.formatDate)(endDate),
309
+ type: EChargeType.EXPECTED_PRINCIPAL_PAID,
310
+ chargeCode: null,
311
+ PLCode: null,
312
+ productCode: group.product.code,
313
+ amount: isParticipant ? -payment.amount : payment.amount,
314
+ statementAmount: null,
315
+ title: EChargeType.EXPECTED_PRINCIPAL_PAID,
316
+ balance: null,
317
+ floatedBalance: null,
318
+ info: EChargeType.EXPECTED_PRINCIPAL_PAID,
319
+ paymentDate: (0, date_helper_1.formatDate)(paymentDoc.date),
320
+ };
321
+ fullTransactions.unshift(expectedPayment);
322
+ }
276
323
  }
277
324
  }
278
325
  }
279
326
  }
280
- }
281
- return fullTransactions;
327
+ allData[productId] = fullTransactions;
328
+ }));
329
+ return allData;
282
330
  };
283
331
  const mergeLedgerData = (transactions, groupKeys, sumKeys) => {
284
332
  const groupedObjects = lodash_1.default.groupBy(transactions, (obj) => groupKeys.reduce((acc, key) => `${acc}${obj[key]}`, ''));