gemcap-be-common 1.2.136 → 1.2.138
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/collaterals.db.d.ts +1 -1
- package/db/collaterals.db.js +2 -2
- package/db/collaterals.db.ts +2 -2
- package/db/financial-spreading.db.d.ts +34 -0
- package/db/financial-spreading.db.js +103 -0
- package/db/financial-spreading.db.ts +112 -0
- package/db/inventories.db.js +2 -2
- package/db/inventories.db.ts +2 -2
- package/db/loan-products.db.js +3 -4
- package/db/loan-products.db.ts +3 -4
- package/db/loan-transactions.db.d.ts +3 -1
- package/db/loan-transactions.db.js +62 -2
- package/db/loan-transactions.db.ts +72 -1
- package/db/receivables.db.js +1 -1
- package/db/receivables.db.ts +1 -1
- package/helpers/date.helper.d.ts +4 -0
- package/helpers/date.helper.js +6 -1
- package/helpers/date.helper.ts +5 -0
- package/models/AvilabilitySignedData.model.d.ts +33 -0
- package/models/AvilabilitySignedData.model.js +22 -0
- package/models/AvilabilitySignedData.model.ts +30 -0
- package/models/BorrowerSummary.model.d.ts +70 -0
- package/models/BorrowerSummary.model.js +37 -0
- package/models/BorrowerSummary.model.ts +72 -0
- package/models/FinancialSpreading.model.d.ts +76 -0
- package/models/FinancialSpreading.model.js +65 -0
- package/models/FinancialSpreading.model.ts +110 -0
- package/models/FinancialSpreadingSheet.model.d.ts +152 -0
- package/models/FinancialSpreadingSheet.model.js +207 -0
- package/models/FinancialSpreadingSheet.model.ts +240 -0
- package/models/LoanTransaction.model.d.ts +8 -0
- package/models/LoanTransaction.model.ts +9 -0
- package/models/PostponedTransactions.model.d.ts +3 -3
- package/models/ProspectIndustry.model.d.ts +35 -0
- package/models/ProspectIndustry.model.js +26 -0
- package/models/ProspectIndustry.model.ts +38 -0
- package/models/Yield.model.d.ts +46 -0
- package/models/Yield.model.js +20 -1
- package/models/Yield.model.ts +20 -0
- package/package.json +1 -1
- package/queries/inventory/extension.js +3 -3
- package/queries/inventory/extension.ts +3 -3
- package/queries/inventory/turn.js +3 -3
- package/queries/inventory/turn.ts +3 -3
- package/reports/new-summary.d.ts +0 -0
- package/reports/new-summary.js +1327 -0
- package/reports/new-summary.ts +1327 -0
- package/services/users.service.d.ts +3 -3
- package/services/users.service.js +3 -3
- package/services/users.service.ts +4 -4
- package/services/yield.service.d.ts +53 -0
- package/services/yield.service.js +161 -0
- package/services/yield.service.ts +198 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
/// <reference types="mongoose/types/inferschematype" />
|
|
25
25
|
import express from 'express';
|
|
26
26
|
import mongoose from 'mongoose';
|
|
27
|
+
import { IKeycloakUser } from '../interfaces/keycloak-user.interface';
|
|
27
28
|
import { IUser, IUserDocument } from '../models/User.model';
|
|
28
29
|
import { IGroupedKeycloakRoles, IKeycloakRole } from '../interfaces/keycloak-role.interface';
|
|
29
|
-
import { IKeycloakUser } from '../interfaces/keycloak-user.interface';
|
|
30
30
|
interface IKeycloakConfig {
|
|
31
31
|
keycloakHost: string;
|
|
32
32
|
realm: string;
|
|
@@ -55,12 +55,12 @@ export declare class UsersService {
|
|
|
55
55
|
hasAccess: boolean;
|
|
56
56
|
canSignDocument: boolean;
|
|
57
57
|
};
|
|
58
|
-
email: string;
|
|
59
58
|
firstName: string;
|
|
60
59
|
lastName: string;
|
|
61
60
|
roles?: IGroupedKeycloakRoles;
|
|
62
61
|
username: string;
|
|
63
62
|
enabled: boolean;
|
|
63
|
+
email: string;
|
|
64
64
|
emailVerified: boolean;
|
|
65
65
|
access: {
|
|
66
66
|
manageGroupMembership: boolean;
|
|
@@ -141,12 +141,12 @@ export declare class UsersService {
|
|
|
141
141
|
hasAccess: boolean;
|
|
142
142
|
canSignDocument: boolean;
|
|
143
143
|
};
|
|
144
|
-
email: string;
|
|
145
144
|
firstName: string;
|
|
146
145
|
lastName: string;
|
|
147
146
|
roles?: IGroupedKeycloakRoles;
|
|
148
147
|
username: string;
|
|
149
148
|
enabled: boolean;
|
|
149
|
+
email: string;
|
|
150
150
|
emailVerified: boolean;
|
|
151
151
|
access: {
|
|
152
152
|
manageGroupMembership: boolean;
|
|
@@ -10,11 +10,11 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
10
10
|
const dayjs_1 = __importDefault(require("dayjs"));
|
|
11
11
|
const mongoose_1 = __importDefault(require("mongoose"));
|
|
12
12
|
const qs_1 = __importDefault(require("qs"));
|
|
13
|
+
const user_logs_db_1 = require("../db/user-logs.db");
|
|
13
14
|
const User_model_1 = require("../models/User.model");
|
|
14
15
|
const BorrowerCompliance_model_1 = require("../models/BorrowerCompliance.model");
|
|
15
16
|
const UserMobileAccess_model_1 = require("../models/UserMobileAccess.model");
|
|
16
17
|
const UserLog_model_1 = require("../models/UserLog.model");
|
|
17
|
-
const user_logs_db_1 = require("../db/user-logs.db");
|
|
18
18
|
class UsersService {
|
|
19
19
|
config;
|
|
20
20
|
constructor(config) {
|
|
@@ -343,13 +343,13 @@ class UsersService {
|
|
|
343
343
|
return;
|
|
344
344
|
}
|
|
345
345
|
await Promise.all(Array.from(dateSet).map(async (date) => {
|
|
346
|
-
const existingLog = await this.getUserLogByDate(user._id, new Date(date));
|
|
346
|
+
const existingLog = await this.getUserLogByDate(String(user._id), new Date(date));
|
|
347
347
|
if (!existingLog) {
|
|
348
348
|
const newLog = {
|
|
349
349
|
action: UserLog_model_1.ELogActionType.CREATE,
|
|
350
350
|
timestamp: new Date(date),
|
|
351
351
|
logType: UserLog_model_1.ELogType.LOGIN,
|
|
352
|
-
userId: user._id,
|
|
352
|
+
userId: String(user._id),
|
|
353
353
|
details: {},
|
|
354
354
|
};
|
|
355
355
|
await (0, user_logs_db_1.createLog)(newLog);
|
|
@@ -6,13 +6,13 @@ import dayjs from 'dayjs';
|
|
|
6
6
|
import mongoose from 'mongoose';
|
|
7
7
|
import qs from 'qs';
|
|
8
8
|
|
|
9
|
+
import { createLog, ICreateLogParams } from '../db/user-logs.db';
|
|
10
|
+
import { IKeycloakUser } from '../interfaces/keycloak-user.interface';
|
|
9
11
|
import { IUser, IUserDocument, UserModel } from '../models/User.model';
|
|
10
12
|
import { BorrowerCompliance } from '../models/BorrowerCompliance.model';
|
|
11
13
|
import { UserMobileAccess } from '../models/UserMobileAccess.model';
|
|
12
14
|
import { ELogActionType, ELogType, UserLog } from '../models/UserLog.model';
|
|
13
15
|
import { IGroupedKeycloakRoles, IKeycloakRole } from '../interfaces/keycloak-role.interface';
|
|
14
|
-
import { IKeycloakUser } from '../interfaces/keycloak-user.interface';
|
|
15
|
-
import { createLog, ICreateLogParams } from '../db/user-logs.db';
|
|
16
16
|
|
|
17
17
|
interface IKeycloakConfig {
|
|
18
18
|
keycloakHost: string;
|
|
@@ -384,13 +384,13 @@ export class UsersService {
|
|
|
384
384
|
return;
|
|
385
385
|
}
|
|
386
386
|
await Promise.all(Array.from(dateSet).map(async (date) => {
|
|
387
|
-
const existingLog = await this.getUserLogByDate(user._id, new Date(date));
|
|
387
|
+
const existingLog = await this.getUserLogByDate(String(user._id), new Date(date));
|
|
388
388
|
if (!existingLog) {
|
|
389
389
|
const newLog: ICreateLogParams = {
|
|
390
390
|
action: ELogActionType.CREATE,
|
|
391
391
|
timestamp: new Date(date),
|
|
392
392
|
logType: ELogType.LOGIN,
|
|
393
|
-
userId: user._id,
|
|
393
|
+
userId: String(user._id),
|
|
394
394
|
details: {},
|
|
395
395
|
};
|
|
396
396
|
await createLog(newLog);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/// <reference types="mongoose/types/aggregate" />
|
|
2
|
+
/// <reference types="mongoose/types/callback" />
|
|
3
|
+
/// <reference types="mongoose/types/collection" />
|
|
4
|
+
/// <reference types="mongoose/types/connection" />
|
|
5
|
+
/// <reference types="mongoose/types/cursor" />
|
|
6
|
+
/// <reference types="mongoose/types/document" />
|
|
7
|
+
/// <reference types="mongoose/types/error" />
|
|
8
|
+
/// <reference types="mongoose/types/expressions" />
|
|
9
|
+
/// <reference types="mongoose/types/helpers" />
|
|
10
|
+
/// <reference types="mongoose/types/middlewares" />
|
|
11
|
+
/// <reference types="mongoose/types/indexes" />
|
|
12
|
+
/// <reference types="mongoose/types/models" />
|
|
13
|
+
/// <reference types="mongoose/types/mongooseoptions" />
|
|
14
|
+
/// <reference types="mongoose/types/pipelinestage" />
|
|
15
|
+
/// <reference types="mongoose/types/populate" />
|
|
16
|
+
/// <reference types="mongoose/types/query" />
|
|
17
|
+
/// <reference types="mongoose/types/schemaoptions" />
|
|
18
|
+
/// <reference types="mongoose/types/schematypes" />
|
|
19
|
+
/// <reference types="mongoose/types/session" />
|
|
20
|
+
/// <reference types="mongoose/types/types" />
|
|
21
|
+
/// <reference types="mongoose/types/utility" />
|
|
22
|
+
/// <reference types="mongoose/types/validation" />
|
|
23
|
+
/// <reference types="mongoose/types/virtuals" />
|
|
24
|
+
/// <reference types="mongoose/types/inferschematype" />
|
|
25
|
+
import mongoose from 'mongoose';
|
|
26
|
+
import { ISelectedMonth } from '../helpers/date.helper';
|
|
27
|
+
import { IYieldParams } from '../models/Yield.model';
|
|
28
|
+
import { ILoanChargeDoc } from '../models/LoanCharges.model';
|
|
29
|
+
import { ILoanProductDoc } from '../models/LoanProducts.model';
|
|
30
|
+
interface IYieldViewData {
|
|
31
|
+
order: number;
|
|
32
|
+
charge: ILoanChargeDoc;
|
|
33
|
+
product: ILoanProductDoc;
|
|
34
|
+
title: string;
|
|
35
|
+
class?: string;
|
|
36
|
+
[data: string]: number | string | ILoanChargeDoc | ILoanProductDoc;
|
|
37
|
+
}
|
|
38
|
+
export declare class YieldService {
|
|
39
|
+
private getCalculatedYieldData;
|
|
40
|
+
getCalculatedYieldTotals(productId: string, selectedMonth: ISelectedMonth): Promise<(mongoose.FlattenMaps<import("../models/Yield.model").IYieldData> & Required<{
|
|
41
|
+
_id: mongoose.Types.ObjectId;
|
|
42
|
+
}>)[]>;
|
|
43
|
+
getCalculatedYieldTotalsForPeriod(productId: string, periodStart: ISelectedMonth, periodEnd: ISelectedMonth): Promise<(mongoose.FlattenMaps<import("../models/Yield.model").IYieldData> & Required<{
|
|
44
|
+
_id: mongoose.Types.ObjectId;
|
|
45
|
+
}>)[]>;
|
|
46
|
+
getYieldData(params: IYieldParams): Promise<{
|
|
47
|
+
data: IYieldViewData[];
|
|
48
|
+
dateColumns: string[];
|
|
49
|
+
}>;
|
|
50
|
+
createYieldTask(params: IYieldParams): Promise<void>;
|
|
51
|
+
private getEmptyResult;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
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.YieldService = void 0;
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
9
|
+
const date_helper_1 = require("../helpers/date.helper");
|
|
10
|
+
const Yield_model_1 = require("../models/Yield.model");
|
|
11
|
+
const LoanCharges_model_1 = require("../models/LoanCharges.model");
|
|
12
|
+
const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
13
|
+
const microservice_tasks_db_1 = require("../db/microservice-tasks.db");
|
|
14
|
+
const microservice_task_enum_1 = require("../enums/microservice-task.enum");
|
|
15
|
+
class YieldService {
|
|
16
|
+
async getCalculatedYieldData(charge, selectedMonth) {
|
|
17
|
+
const oldData = await Yield_model_1.YieldData.findOne({
|
|
18
|
+
chargeId: charge._id,
|
|
19
|
+
productId: charge.productId,
|
|
20
|
+
year: selectedMonth.year,
|
|
21
|
+
month: selectedMonth.month,
|
|
22
|
+
}).lean();
|
|
23
|
+
if (oldData) {
|
|
24
|
+
return oldData.valuePercent;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
async getCalculatedYieldTotals(productId, selectedMonth) {
|
|
29
|
+
return Yield_model_1.YieldData.find({
|
|
30
|
+
chargeId: null,
|
|
31
|
+
productId: productId,
|
|
32
|
+
year: selectedMonth.year,
|
|
33
|
+
month: selectedMonth.month,
|
|
34
|
+
}).lean();
|
|
35
|
+
}
|
|
36
|
+
async getCalculatedYieldTotalsForPeriod(productId, periodStart, periodEnd) {
|
|
37
|
+
return Yield_model_1.YieldData.find({
|
|
38
|
+
productId: new mongoose_1.default.Types.ObjectId(productId),
|
|
39
|
+
$or: [
|
|
40
|
+
{
|
|
41
|
+
year: periodStart.year,
|
|
42
|
+
month: { $gte: periodStart.month },
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
year: periodEnd.year,
|
|
46
|
+
month: { $lte: periodEnd.month },
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
year: { $gt: periodStart.year, $lt: periodEnd.year },
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
}).sort({
|
|
53
|
+
year: 1,
|
|
54
|
+
month: 1,
|
|
55
|
+
}).lean();
|
|
56
|
+
}
|
|
57
|
+
async getYieldData(params) {
|
|
58
|
+
const validationRes = Yield_model_1.yieldParamsValidationSchema.validate(params);
|
|
59
|
+
if (validationRes.error) {
|
|
60
|
+
console.error(validationRes.error);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (params.ignoreOldData) {
|
|
64
|
+
return await this.getEmptyResult(params);
|
|
65
|
+
}
|
|
66
|
+
const startDate = (0, date_helper_1.convertSelectedMonthToDate)(params.periodStart);
|
|
67
|
+
const endDate = (0, date_helper_1.convertSelectedMonthToDate)(params.periodEnd);
|
|
68
|
+
const charges = await LoanCharges_model_1.LoanCharge.find({ includeInYield: true }).lean();
|
|
69
|
+
const productIDs = charges.reduce((acc, charge) => [...acc, charge.productId.toString()], []);
|
|
70
|
+
const products = await LoanProducts_model_1.LoanProduct
|
|
71
|
+
.find({ _id: { $in: Object.values(productIDs).map((id) => new mongoose_1.default.Types.ObjectId(id)) } })
|
|
72
|
+
.populate('borrowerId')
|
|
73
|
+
.lean();
|
|
74
|
+
const productMap = products
|
|
75
|
+
.reduce((acc, product) => {
|
|
76
|
+
return {
|
|
77
|
+
...acc,
|
|
78
|
+
[product._id.toString()]: {
|
|
79
|
+
product,
|
|
80
|
+
charges: charges.filter((charge) => charge.productId.toString() == product._id.toString()),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}, {});
|
|
84
|
+
const allDates = (0, date_helper_1.getStartOfMonthsBetweenDates)(startDate, endDate);
|
|
85
|
+
const allResults = [];
|
|
86
|
+
let hasAllData = true;
|
|
87
|
+
await Promise.all(Object.keys(productMap).map(async (productId) => {
|
|
88
|
+
await Promise.all(productMap[productId].charges.map(async (charge) => {
|
|
89
|
+
const monthlyData = {
|
|
90
|
+
charge,
|
|
91
|
+
product: productMap[charge.productId.toString()].product,
|
|
92
|
+
order: 0,
|
|
93
|
+
title: productMap[charge.productId.toString()].product.name,
|
|
94
|
+
};
|
|
95
|
+
await Promise.all(Object.entries(allDates).map(async ([dateView, date]) => {
|
|
96
|
+
if (!hasAllData) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const yieldDataValuePercent = await this.getCalculatedYieldData(charge, date);
|
|
100
|
+
if (!yieldDataValuePercent) {
|
|
101
|
+
console.error(`Yield data not found for product ${productMap[productId].product.name} for charge ${charge.name} and date ${dateView}`);
|
|
102
|
+
hasAllData = false;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
monthlyData[`valuePercent-${dateView}`] = yieldDataValuePercent;
|
|
106
|
+
}
|
|
107
|
+
}));
|
|
108
|
+
allResults.push({ ...monthlyData });
|
|
109
|
+
}));
|
|
110
|
+
}));
|
|
111
|
+
if (!hasAllData) {
|
|
112
|
+
// return await this.getEmptyResult(params); // TODO return it
|
|
113
|
+
}
|
|
114
|
+
await Promise.all(products.map(async (product) => {
|
|
115
|
+
const productTotalList = [
|
|
116
|
+
Yield_model_1.ETotalType.GROSS,
|
|
117
|
+
Yield_model_1.ETotalType.BROKER,
|
|
118
|
+
Yield_model_1.ETotalType.NET,
|
|
119
|
+
Yield_model_1.ETotalType.AVERAGE_BALANCE,
|
|
120
|
+
Yield_model_1.ETotalType.AVERAGE_LIFE_BALANCE,
|
|
121
|
+
Yield_model_1.ETotalType.APR_GROSS,
|
|
122
|
+
Yield_model_1.ETotalType.APR_LIFE_GROSS,
|
|
123
|
+
];
|
|
124
|
+
const productTotal = productTotalList.reduce((acc, p) => {
|
|
125
|
+
return {
|
|
126
|
+
...acc,
|
|
127
|
+
[p]: {
|
|
128
|
+
charge: null,
|
|
129
|
+
product,
|
|
130
|
+
order: Yield_model_1.YIELD_TOTALS_MAP[p].order,
|
|
131
|
+
title: `${product.name} (${Yield_model_1.YIELD_TOTALS_MAP[p].title})`,
|
|
132
|
+
class: Yield_model_1.YIELD_TOTALS_MAP[p].class,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}, {});
|
|
136
|
+
await Promise.all(Object.entries(allDates).map(async ([dateView, dateValue]) => {
|
|
137
|
+
const productTotals = await this.getCalculatedYieldTotals(product._id.toString(), dateValue);
|
|
138
|
+
Object.keys(productTotal).forEach((totalType) => {
|
|
139
|
+
const foundType = productTotals.find((total) => total.totalType === totalType);
|
|
140
|
+
if (foundType) {
|
|
141
|
+
productTotal[totalType][`value-${dateView}`] = foundType.value;
|
|
142
|
+
productTotal[totalType][`valuePercent-${dateView}`] = foundType.valuePercent;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}));
|
|
146
|
+
Object.values(productTotal).forEach((values) => {
|
|
147
|
+
allResults.push(values);
|
|
148
|
+
});
|
|
149
|
+
}));
|
|
150
|
+
const sortedData = lodash_1.default.orderBy(allResults, ['product.borrowerId.name', 'product.order', 'order', 'charge.name'], ['asc', 'asc', 'asc', 'asc']);
|
|
151
|
+
return { data: sortedData, dateColumns: Object.keys(allDates) };
|
|
152
|
+
}
|
|
153
|
+
async createYieldTask(params) {
|
|
154
|
+
await (0, microservice_tasks_db_1.createMicroserviceTasks)(microservice_task_enum_1.EMicroserviceTask.YIELD, params);
|
|
155
|
+
}
|
|
156
|
+
async getEmptyResult(params) {
|
|
157
|
+
await this.createYieldTask(params);
|
|
158
|
+
return { data: [], dateColumns: [] };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.YieldService = YieldService;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
|
|
4
|
+
import { convertSelectedMonthToDate, getStartOfMonthsBetweenDates, ISelectedMonth } from '../helpers/date.helper';
|
|
5
|
+
import {
|
|
6
|
+
ETotalType,
|
|
7
|
+
IYieldParams,
|
|
8
|
+
YIELD_TOTALS_MAP,
|
|
9
|
+
YieldData,
|
|
10
|
+
yieldParamsValidationSchema,
|
|
11
|
+
} from '../models/Yield.model';
|
|
12
|
+
import { ILoanChargeDoc, LoanCharge } from '../models/LoanCharges.model';
|
|
13
|
+
import { ILoanProductDoc, LoanProduct } from '../models/LoanProducts.model';
|
|
14
|
+
import { createMicroserviceTasks } from '../db/microservice-tasks.db';
|
|
15
|
+
import { EMicroserviceTask } from '../enums/microservice-task.enum';
|
|
16
|
+
|
|
17
|
+
interface IYieldViewData {
|
|
18
|
+
order: number;
|
|
19
|
+
charge: ILoanChargeDoc;
|
|
20
|
+
product: ILoanProductDoc;
|
|
21
|
+
title: string;
|
|
22
|
+
class?: string;
|
|
23
|
+
|
|
24
|
+
[data: string]: number | string | ILoanChargeDoc | ILoanProductDoc;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class YieldService {
|
|
28
|
+
|
|
29
|
+
private async getCalculatedYieldData(charge: ILoanChargeDoc, selectedMonth: ISelectedMonth) {
|
|
30
|
+
const oldData = await YieldData.findOne({
|
|
31
|
+
chargeId: charge._id,
|
|
32
|
+
productId: charge.productId,
|
|
33
|
+
year: selectedMonth.year,
|
|
34
|
+
month: selectedMonth.month,
|
|
35
|
+
}).lean();
|
|
36
|
+
if (oldData) {
|
|
37
|
+
return oldData.valuePercent;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getCalculatedYieldTotals(productId: string, selectedMonth: ISelectedMonth) {
|
|
43
|
+
return YieldData.find({
|
|
44
|
+
chargeId: null,
|
|
45
|
+
productId: productId,
|
|
46
|
+
year: selectedMonth.year,
|
|
47
|
+
month: selectedMonth.month,
|
|
48
|
+
}).lean();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getCalculatedYieldTotalsForPeriod(productId: string, periodStart: ISelectedMonth, periodEnd: ISelectedMonth) {
|
|
52
|
+
return YieldData.find({
|
|
53
|
+
productId: new mongoose.Types.ObjectId(productId),
|
|
54
|
+
$or: [
|
|
55
|
+
{
|
|
56
|
+
year: periodStart.year,
|
|
57
|
+
month: { $gte: periodStart.month },
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
year: periodEnd.year,
|
|
61
|
+
month: { $lte: periodEnd.month },
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
year: { $gt: periodStart.year, $lt: periodEnd.year },
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
}).sort({
|
|
68
|
+
year: 1,
|
|
69
|
+
month: 1,
|
|
70
|
+
}).lean();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async getYieldData(params: IYieldParams): Promise<{ data: IYieldViewData[], dateColumns: string[] }> {
|
|
74
|
+
const validationRes = yieldParamsValidationSchema.validate(params);
|
|
75
|
+
if (validationRes.error) {
|
|
76
|
+
console.error(validationRes.error);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (params.ignoreOldData) {
|
|
81
|
+
return await this.getEmptyResult(params);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const startDate = convertSelectedMonthToDate(params.periodStart);
|
|
85
|
+
const endDate = convertSelectedMonthToDate(params.periodEnd);
|
|
86
|
+
const charges = await LoanCharge.find({ includeInYield: true }).lean();
|
|
87
|
+
|
|
88
|
+
const productIDs = charges.reduce((acc, charge) => [...acc, charge.productId.toString()], <string[]>[]);
|
|
89
|
+
|
|
90
|
+
const products = await LoanProduct
|
|
91
|
+
.find({ _id: { $in: Object.values(productIDs).map((id) => new mongoose.Types.ObjectId(id)) } })
|
|
92
|
+
.populate('borrowerId')
|
|
93
|
+
.lean();
|
|
94
|
+
|
|
95
|
+
const productMap = products
|
|
96
|
+
.reduce((acc, product) => {
|
|
97
|
+
return {
|
|
98
|
+
...acc,
|
|
99
|
+
[product._id.toString()]: {
|
|
100
|
+
product,
|
|
101
|
+
charges: charges.filter((charge) => charge.productId.toString() == product._id.toString()),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}, <{ [productId: string]: { product: ILoanProductDoc, charges: ILoanChargeDoc[] } }>{});
|
|
105
|
+
|
|
106
|
+
const allDates = getStartOfMonthsBetweenDates(startDate, endDate);
|
|
107
|
+
|
|
108
|
+
const allResults: IYieldViewData[] = [];
|
|
109
|
+
|
|
110
|
+
let hasAllData = true;
|
|
111
|
+
|
|
112
|
+
await Promise.all(Object.keys(productMap).map(async (productId) => {
|
|
113
|
+
|
|
114
|
+
await Promise.all(productMap[productId].charges.map(async (charge) => {
|
|
115
|
+
|
|
116
|
+
const monthlyData = {
|
|
117
|
+
charge,
|
|
118
|
+
product: productMap[charge.productId.toString()].product,
|
|
119
|
+
order: 0,
|
|
120
|
+
title: productMap[charge.productId.toString()].product.name,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
await Promise.all(Object.entries(allDates).map(async ([dateView, date]) => {
|
|
124
|
+
if (!hasAllData) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const yieldDataValuePercent = await this.getCalculatedYieldData(charge, date);
|
|
128
|
+
if (!yieldDataValuePercent) {
|
|
129
|
+
console.error(`Yield data not found for product ${productMap[productId].product.name} for charge ${charge.name} and date ${dateView}`);
|
|
130
|
+
hasAllData = false;
|
|
131
|
+
} else {
|
|
132
|
+
monthlyData[`valuePercent-${dateView}`] = yieldDataValuePercent;
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
allResults.push({ ...monthlyData });
|
|
137
|
+
|
|
138
|
+
}));
|
|
139
|
+
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
if (!hasAllData) {
|
|
143
|
+
// return await this.getEmptyResult(params); // TODO return it
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await Promise.all(products.map(async (product) => {
|
|
147
|
+
|
|
148
|
+
const productTotalList = [
|
|
149
|
+
ETotalType.GROSS,
|
|
150
|
+
ETotalType.BROKER,
|
|
151
|
+
ETotalType.NET,
|
|
152
|
+
ETotalType.AVERAGE_BALANCE,
|
|
153
|
+
ETotalType.AVERAGE_LIFE_BALANCE,
|
|
154
|
+
ETotalType.APR_GROSS,
|
|
155
|
+
ETotalType.APR_LIFE_GROSS,
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const productTotal = productTotalList.reduce((acc, p) => {
|
|
159
|
+
return {
|
|
160
|
+
...acc,
|
|
161
|
+
[p]: {
|
|
162
|
+
charge: null,
|
|
163
|
+
product,
|
|
164
|
+
order: YIELD_TOTALS_MAP[p].order,
|
|
165
|
+
title: `${product.name} (${YIELD_TOTALS_MAP[p].title})`,
|
|
166
|
+
class: YIELD_TOTALS_MAP[p].class,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}, {});
|
|
170
|
+
|
|
171
|
+
await Promise.all(Object.entries(allDates).map(async ([dateView, dateValue]) => {
|
|
172
|
+
const productTotals = await this.getCalculatedYieldTotals(product._id.toString(), dateValue);
|
|
173
|
+
Object.keys(productTotal).forEach((totalType) => {
|
|
174
|
+
const foundType = productTotals.find((total) => total.totalType === totalType);
|
|
175
|
+
if (foundType) {
|
|
176
|
+
productTotal[totalType][`value-${dateView}`] = foundType.value;
|
|
177
|
+
productTotal[totalType][`valuePercent-${dateView}`] = foundType.valuePercent;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}));
|
|
181
|
+
Object.values(productTotal).forEach((values: IYieldViewData) => {
|
|
182
|
+
allResults.push(values);
|
|
183
|
+
});
|
|
184
|
+
}));
|
|
185
|
+
|
|
186
|
+
const sortedData = _.orderBy(allResults, ['product.borrowerId.name', 'product.order', 'order', 'charge.name'], ['asc', 'asc', 'asc', 'asc']);
|
|
187
|
+
return { data: sortedData, dateColumns: Object.keys(allDates) };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async createYieldTask(params: IYieldParams) {
|
|
191
|
+
await createMicroserviceTasks(EMicroserviceTask.YIELD, params);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private async getEmptyResult(params: IYieldParams) {
|
|
195
|
+
await this.createYieldTask(params);
|
|
196
|
+
return { data: [], dateColumns: [] };
|
|
197
|
+
}
|
|
198
|
+
}
|