gemcap-be-common 1.5.133 → 1.5.135
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 +3 -1
- package/db/brokers.db.js +19 -3
- package/db/brokers.db.ts +20 -2
- package/db/loan-statement.db.js +9 -2
- package/db/loan-statement.db.ts +15 -4
- package/db/reports.db.d.ts +6 -1
- package/db/reports.db.js +12 -5
- package/db/reports.db.ts +21 -6
- package/models/ProductBroker.model.d.ts +4 -0
- package/models/ProductBroker.model.js +5 -0
- package/models/ProductBroker.model.ts +6 -0
- package/package.json +1 -1
- package/services/brokers.service.js +3 -1
- package/services/brokers.service.ts +3 -1
- package/services/quickbooks.service.d.ts +3 -1
- package/services/quickbooks.service.js +14 -2
- package/services/quickbooks.service.ts +13 -1
- package/services/reports.service.d.ts +3 -1
- package/services/reports.service.js +4 -2
- package/services/reports.service.ts +3 -2
- package/tsconfig.tsbuildinfo +1 -1
package/db/brokers.db.d.ts
CHANGED
|
@@ -27,8 +27,10 @@ import mongoose from 'mongoose';
|
|
|
27
27
|
import { ILedgerDataRow } from './reports.db';
|
|
28
28
|
import { ILoanProductDoc } from '../models/LoanProducts.model';
|
|
29
29
|
import { IProductBrokerDocWithLoanBroker, IProductBrokerLean } from '../models/ProductBroker.model';
|
|
30
|
+
import { ILoanChargeDoc, ILoanChargeView } from '../models/LoanCharges.model';
|
|
31
|
+
export declare const handleBrokerInterestFee: (statementAmount: number, charges: ILoanChargeView[], interestShare: number, baseInterestTotal: number, primeRate: number) => number;
|
|
30
32
|
export declare const getProductBrokers: (productId: string) => Promise<IProductBrokerDocWithLoanBroker[]>;
|
|
31
|
-
export declare const enrichWithBrokers: (transactions: ILedgerDataRow[], product: ILoanProductDoc) => Promise<ILedgerDataRow[]>;
|
|
33
|
+
export declare const enrichWithBrokers: (transactions: ILedgerDataRow[], product: ILoanProductDoc, charges: ILoanChargeDoc[], primeRate: number) => Promise<ILedgerDataRow[]>;
|
|
32
34
|
export declare const getBorrowerBrokers: (borrowerId: string) => Promise<IProductBrokerLean[]>;
|
|
33
35
|
export declare const getAllProductBrokers: () => Promise<IProductBrokerLean[]>;
|
|
34
36
|
export declare const getAllLoanBrokers: () => Promise<{
|
package/db/brokers.db.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getBrokerProducts = exports.getAllLoanBrokers = exports.getAllProductBrokers = exports.getBorrowerBrokers = exports.enrichWithBrokers = exports.getProductBrokers = void 0;
|
|
6
|
+
exports.getBrokerProducts = exports.getAllLoanBrokers = exports.getAllProductBrokers = exports.getBorrowerBrokers = exports.enrichWithBrokers = exports.getProductBrokers = exports.handleBrokerInterestFee = void 0;
|
|
7
7
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
8
8
|
const mongoose_1 = __importDefault(require("mongoose"));
|
|
9
9
|
const _models_1 = require("../models/_models");
|
|
@@ -11,6 +11,15 @@ const reports_db_1 = require("./reports.db");
|
|
|
11
11
|
const ProductBroker_model_1 = require("../models/ProductBroker.model");
|
|
12
12
|
const LoanBroker_model_1 = require("../models/LoanBroker.model");
|
|
13
13
|
const main_helper_1 = require("../helpers/main.helper");
|
|
14
|
+
const handleBrokerInterestFee = (statementAmount, charges, interestShare, baseInterestTotal, primeRate) => {
|
|
15
|
+
const interestCharge = charges.find((charge) => reports_db_1.EChargeType[charge.chargeType] === reports_db_1.EChargeType.INTEREST_FEE && charge.active && !charge.deletedAt);
|
|
16
|
+
const interestBase = baseInterestTotal
|
|
17
|
+
? new decimal_js_1.default(primeRate).add(interestCharge?.percent || 0).div(baseInterestTotal).toDP(2).toNumber()
|
|
18
|
+
: 0;
|
|
19
|
+
const minBase = Math.min(1, interestBase);
|
|
20
|
+
return new decimal_js_1.default(statementAmount).mul(interestShare).mul(minBase).toDP(2).toNumber();
|
|
21
|
+
};
|
|
22
|
+
exports.handleBrokerInterestFee = handleBrokerInterestFee;
|
|
14
23
|
const getProductBrokers = async (productId) => {
|
|
15
24
|
return ProductBroker_model_1.ProductBroker.aggregate([
|
|
16
25
|
{
|
|
@@ -37,15 +46,22 @@ const getProductBrokers = async (productId) => {
|
|
|
37
46
|
]);
|
|
38
47
|
};
|
|
39
48
|
exports.getProductBrokers = getProductBrokers;
|
|
40
|
-
const enrichWithBrokers = async (transactions, product) => {
|
|
49
|
+
const enrichWithBrokers = async (transactions, product, charges, primeRate) => {
|
|
41
50
|
const productBrokers = await (0, exports.getProductBrokers)(product._id.toString());
|
|
42
51
|
if (productBrokers.length === 0) {
|
|
43
52
|
return transactions;
|
|
44
53
|
}
|
|
45
54
|
const handleFee = (t, broker, shareName) => {
|
|
55
|
+
let statementAmount;
|
|
56
|
+
if (shareName === 'interestShare') {
|
|
57
|
+
statementAmount = (0, exports.handleBrokerInterestFee)(-t.statementAmount, charges, broker.interestShare, broker.baseInterestTotal, primeRate);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
statementAmount = new decimal_js_1.default(-t.statementAmount).mul(broker[shareName]).toDP(2).toNumber();
|
|
61
|
+
}
|
|
46
62
|
return {
|
|
47
63
|
...t,
|
|
48
|
-
statementAmount
|
|
64
|
+
statementAmount,
|
|
49
65
|
title: `${t.title.replace('ACCRUED', '').trim()}: ${broker.loanBroker.name}`,
|
|
50
66
|
chargeCode: broker.BSCode,
|
|
51
67
|
PLCode: broker.PLCode,
|
package/db/brokers.db.ts
CHANGED
|
@@ -7,6 +7,16 @@ import { ILoanProductDoc } from '../models/LoanProducts.model';
|
|
|
7
7
|
import { IProductBrokerDocWithLoanBroker, IProductBrokerLean, ProductBroker } from '../models/ProductBroker.model';
|
|
8
8
|
import { ILoanBrokerLean, LoanBroker } from '../models/LoanBroker.model';
|
|
9
9
|
import { fieldsToUnset, sanitizePlainObject } from '../helpers/main.helper';
|
|
10
|
+
import { ILoanChargeDoc, ILoanChargeView } from '../models/LoanCharges.model';
|
|
11
|
+
|
|
12
|
+
export const handleBrokerInterestFee = (statementAmount: number, charges: ILoanChargeView[], interestShare: number, baseInterestTotal: number, primeRate: number) => {
|
|
13
|
+
const interestCharge = charges.find((charge) => EChargeType[charge.chargeType] === EChargeType.INTEREST_FEE && charge.active && !charge.deletedAt);
|
|
14
|
+
const interestBase = baseInterestTotal
|
|
15
|
+
? new Decimal(primeRate).add(interestCharge?.percent || 0).div(baseInterestTotal).toDP(2).toNumber()
|
|
16
|
+
: 0;
|
|
17
|
+
const minBase = Math.min(1, interestBase);
|
|
18
|
+
return new Decimal(statementAmount).mul(interestShare).mul(minBase).toDP(2).toNumber();
|
|
19
|
+
};
|
|
10
20
|
|
|
11
21
|
export const getProductBrokers = async (productId: string) => {
|
|
12
22
|
return ProductBroker.aggregate<IProductBrokerDocWithLoanBroker>([
|
|
@@ -34,15 +44,23 @@ export const getProductBrokers = async (productId: string) => {
|
|
|
34
44
|
]);
|
|
35
45
|
};
|
|
36
46
|
|
|
37
|
-
export const enrichWithBrokers = async (transactions: ILedgerDataRow[], product: ILoanProductDoc): Promise<ILedgerDataRow[]> => {
|
|
47
|
+
export const enrichWithBrokers = async (transactions: ILedgerDataRow[], product: ILoanProductDoc, charges: ILoanChargeDoc[], primeRate: number): Promise<ILedgerDataRow[]> => {
|
|
38
48
|
const productBrokers = await getProductBrokers(product._id.toString());
|
|
39
49
|
if (productBrokers.length === 0) {
|
|
40
50
|
return transactions;
|
|
41
51
|
}
|
|
42
52
|
const handleFee = (t: ILedgerDataRow, broker: IProductBrokerDocWithLoanBroker, shareName: 'adminShare' | 'interestShare' | 'otherShare'): ILedgerDataRow => {
|
|
53
|
+
let statementAmount: number;
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if (shareName === 'interestShare') {
|
|
57
|
+
statementAmount = handleBrokerInterestFee(-t.statementAmount, charges, broker.interestShare, broker.baseInterestTotal, primeRate);
|
|
58
|
+
} else {
|
|
59
|
+
statementAmount = new Decimal(-t.statementAmount).mul(broker[shareName]).toDP(2).toNumber();
|
|
60
|
+
}
|
|
43
61
|
return {
|
|
44
62
|
...t,
|
|
45
|
-
statementAmount
|
|
63
|
+
statementAmount,
|
|
46
64
|
title: `${t.title.replace('ACCRUED', '').trim()}: ${broker.loanBroker.name}`,
|
|
47
65
|
chargeCode: broker.BSCode,
|
|
48
66
|
PLCode: broker.PLCode,
|
package/db/loan-statement.db.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.getPeriods = exports.getBorrowerWithPeriods = exports.getStatementBorrow
|
|
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
|
8
8
|
const mongoose_1 = __importDefault(require("mongoose"));
|
|
9
9
|
const dayjs_1 = __importDefault(require("dayjs"));
|
|
10
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
10
11
|
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
|
|
11
12
|
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
|
|
12
13
|
const loan_products_db_1 = require("./loan-products.db");
|
|
@@ -14,7 +15,7 @@ const LoanProducts_model_1 = require("../models/LoanProducts.model");
|
|
|
14
15
|
const Borrower_model_1 = require("../models/Borrower.model");
|
|
15
16
|
const loan_transactions_db_1 = require("./loan-transactions.db");
|
|
16
17
|
const reports_db_1 = require("./reports.db");
|
|
17
|
-
const
|
|
18
|
+
const financial_indexes_service_1 = require("../services/financial-indexes.service");
|
|
18
19
|
var ELedgerReportType;
|
|
19
20
|
(function (ELedgerReportType) {
|
|
20
21
|
ELedgerReportType[ELedgerReportType["SHORT"] = 0] = "SHORT";
|
|
@@ -50,6 +51,7 @@ exports.ledgerHeadersMap = {
|
|
|
50
51
|
paymentDate: 'payment date',
|
|
51
52
|
},
|
|
52
53
|
};
|
|
54
|
+
const financialIndexesService = new financial_indexes_service_1.FinancialIndexesService({ apiKey: '' });
|
|
53
55
|
const getTotalTransactionAmountForCharge = async (chargeId, start, end) => {
|
|
54
56
|
const results = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
|
|
55
57
|
{
|
|
@@ -132,7 +134,12 @@ const getStatementBorrowersTransactions = async (start, end, selectedPeriod, sel
|
|
|
132
134
|
startDate: new Date((0, dayjs_1.default)(periodFull[0].period.start).utc().toDate().toISOString()),
|
|
133
135
|
endDate: new Date((0, dayjs_1.default)(periodFull[0].period.end).utc().toDate().toISOString()),
|
|
134
136
|
reportType: ledgerType,
|
|
135
|
-
}, {
|
|
137
|
+
}, {
|
|
138
|
+
showBalances: showBalances,
|
|
139
|
+
showFloatedBalance: showBalances,
|
|
140
|
+
}, {
|
|
141
|
+
getFinancialIndexValue: (index, date) => financialIndexesService.getFinancialIndexValue(index, date),
|
|
142
|
+
});
|
|
136
143
|
const transactions = Object.values(ledgerData).reduce((acc, ledgerTransaction) => [...acc, ...ledgerTransaction], []);
|
|
137
144
|
const transactionsWithoutBalance = showBalances
|
|
138
145
|
? transactions.map((tr) => {
|
package/db/loan-statement.db.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import mongoose from 'mongoose';
|
|
3
3
|
import dayjs from 'dayjs';
|
|
4
|
+
import Decimal from 'decimal.js';
|
|
4
5
|
|
|
5
6
|
import { IStatementPeriod, LoanStatementTransactionModel } from '../models/LoanStatementTransaction.model';
|
|
6
7
|
import { LoanTransaction } from '../models/LoanTransaction.model';
|
|
@@ -9,7 +10,7 @@ import { LoanProduct } from '../models/LoanProducts.model';
|
|
|
9
10
|
import { BorrowerModel } from '../models/Borrower.model';
|
|
10
11
|
import { getLastTransactionForDate } from './loan-transactions.db';
|
|
11
12
|
import { getLedger } from './reports.db';
|
|
12
|
-
import
|
|
13
|
+
import { FinancialIndexesService } from '../services/financial-indexes.service';
|
|
13
14
|
|
|
14
15
|
export enum ELedgerReportType {
|
|
15
16
|
SHORT,
|
|
@@ -47,6 +48,8 @@ export const ledgerHeadersMap = {
|
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
50
|
|
|
51
|
+
const financialIndexesService = new FinancialIndexesService({ apiKey: '' });
|
|
52
|
+
|
|
50
53
|
export const getTotalTransactionAmountForCharge = async (chargeId: string, start: Date, end: Date) => {
|
|
51
54
|
const results = await LoanStatementTransactionModel.aggregate([
|
|
52
55
|
{
|
|
@@ -129,12 +132,20 @@ export const getStatementBorrowersTransactions = async (start: string, end: stri
|
|
|
129
132
|
|
|
130
133
|
const products = await LoanProduct.find({ borrowerId: { $in: selectedBorrowerIds.map((id) => new mongoose.Types.ObjectId(id)) } }).lean();
|
|
131
134
|
const productIds = products.map((product) => product._id.toString());
|
|
132
|
-
const ledgerData = await getLedger(
|
|
135
|
+
const ledgerData = await getLedger(
|
|
136
|
+
{
|
|
133
137
|
productIds,
|
|
134
138
|
startDate: new Date(dayjs(periodFull[0].period.start).utc().toDate().toISOString()),
|
|
135
139
|
endDate: new Date(dayjs(periodFull[0].period.end).utc().toDate().toISOString()),
|
|
136
140
|
reportType: ledgerType,
|
|
137
|
-
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
showBalances: showBalances,
|
|
144
|
+
showFloatedBalance: showBalances,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
getFinancialIndexValue: (index, date) => financialIndexesService.getFinancialIndexValue(index, date),
|
|
148
|
+
},
|
|
138
149
|
);
|
|
139
150
|
const transactions = Object.values(ledgerData).reduce((acc, ledgerTransaction) => [...acc, ...ledgerTransaction], []);
|
|
140
151
|
const transactionsWithoutBalance = showBalances
|
|
@@ -143,7 +154,7 @@ export const getStatementBorrowersTransactions = async (start: string, end: stri
|
|
|
143
154
|
...tr,
|
|
144
155
|
balance: tr.balance ? new Decimal(tr.balance).toDP(2).toNumber() : tr.balance,
|
|
145
156
|
floatedBalance: tr.floatedBalance ? new Decimal(tr.floatedBalance).toDP(2).toNumber() : tr.floatedBalance,
|
|
146
|
-
}
|
|
157
|
+
};
|
|
147
158
|
})
|
|
148
159
|
: _.map(transactions, (obj) => _.omit(obj, ['balance', 'floatedBalance']));
|
|
149
160
|
return [{ transactions: [header, ...transactionsWithoutBalance] }];
|
package/db/reports.db.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { ILoanProductDoc } from '../models/LoanProducts.model';
|
|
|
27
27
|
import { ELedgerReportType } from './loan-statement.db';
|
|
28
28
|
import mongoose from 'mongoose';
|
|
29
29
|
import { ILoanTransactionDoc } from '../models/LoanTransaction.model';
|
|
30
|
+
import { EFinancialIndex } from '../services/financial-indexes.service';
|
|
30
31
|
export declare enum EChargeType {
|
|
31
32
|
INTEREST_FEE = "INTEREST",
|
|
32
33
|
ADMIN_FEE = "ADMIN FEE",
|
|
@@ -92,6 +93,10 @@ export type ReportLedgerOptions = {
|
|
|
92
93
|
showFloatedBalance: boolean;
|
|
93
94
|
showBalances: boolean;
|
|
94
95
|
};
|
|
95
|
-
|
|
96
|
+
type GetFinancialIndexValue = (index: EFinancialIndex, date?: Date) => Promise<number>;
|
|
97
|
+
export declare const getLedger: (params: ReportLedgerParams, options: ReportLedgerOptions, deps: {
|
|
98
|
+
getFinancialIndexValue: GetFinancialIndexValue;
|
|
99
|
+
}) => Promise<{
|
|
96
100
|
[productId: string]: ILedgerReportRow[];
|
|
97
101
|
}>;
|
|
102
|
+
export {};
|
package/db/reports.db.js
CHANGED
|
@@ -22,6 +22,8 @@ const brokers_db_1 = require("./brokers.db");
|
|
|
22
22
|
const TermLoan_model_1 = require("../models/TermLoan.model");
|
|
23
23
|
const TermLoanCalculated_model_1 = require("../models/TermLoanCalculated.model");
|
|
24
24
|
const _models_1 = require("../models/_models");
|
|
25
|
+
const LoanCharges_model_1 = require("../models/LoanCharges.model");
|
|
26
|
+
const financial_indexes_service_1 = require("../services/financial-indexes.service");
|
|
25
27
|
var EChargeType;
|
|
26
28
|
(function (EChargeType) {
|
|
27
29
|
EChargeType["INTEREST_FEE"] = "INTEREST";
|
|
@@ -42,9 +44,9 @@ const defaultReportLedgerOptions = {
|
|
|
42
44
|
showBalances: true,
|
|
43
45
|
showFloatedBalance: true,
|
|
44
46
|
};
|
|
45
|
-
const getLedger = async (params, options = defaultReportLedgerOptions) => {
|
|
47
|
+
const getLedger = async (params, options = defaultReportLedgerOptions, deps) => {
|
|
46
48
|
const { productIds, reportType } = params;
|
|
47
|
-
const fullTransactions = await getLedgerData(params, options);
|
|
49
|
+
const fullTransactions = await getLedgerData(params, options, deps.getFinancialIndexValue);
|
|
48
50
|
const products = await LoanProducts_model_1.LoanProduct
|
|
49
51
|
.find({ _id: { $in: productIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } })
|
|
50
52
|
.sort({ code: 1 })
|
|
@@ -66,10 +68,13 @@ const getLedger = async (params, options = defaultReportLedgerOptions) => {
|
|
|
66
68
|
}, {});
|
|
67
69
|
};
|
|
68
70
|
exports.getLedger = getLedger;
|
|
69
|
-
const getLedgerData = async (params, options = defaultReportLedgerOptions) => {
|
|
71
|
+
const getLedgerData = async (params, options = defaultReportLedgerOptions, getFinancialIndexValue) => {
|
|
70
72
|
const { productIds, startDate, endDate } = params;
|
|
71
73
|
const addBrokers = true;
|
|
72
|
-
const
|
|
74
|
+
const productMongoIds = productIds.map((id) => new mongoose_1.default.Types.ObjectId(id));
|
|
75
|
+
const products = await LoanProducts_model_1.LoanProduct.find({ _id: { $in: productMongoIds } }).lean();
|
|
76
|
+
const charges = await LoanCharges_model_1.LoanCharge.find({ productId: { $in: productMongoIds } }).lean();
|
|
77
|
+
const primeRate = await getFinancialIndexValue(financial_indexes_service_1.EFinancialIndex.PRIME_RATE, params.startDate);
|
|
73
78
|
const borrowers = await Borrower_model_1.BorrowerModel.find({ _id: { $in: products.map((product) => product.borrowerId) } }).lean();
|
|
74
79
|
const borrowerMap = borrowers.reduce((acc, borrower) => ({ ...acc, [borrower._id.toString()]: borrower.code }), {});
|
|
75
80
|
const borrowerCodesMap = products.reduce((acc, product) => ({
|
|
@@ -135,6 +140,7 @@ const getLedgerData = async (params, options = defaultReportLedgerOptions) => {
|
|
|
135
140
|
product: p,
|
|
136
141
|
productId: p._id,
|
|
137
142
|
transactions: productTransactions ? productTransactions.transactions : [],
|
|
143
|
+
charges: charges.filter((charge) => charge.productId.toString() === p._id.toString()),
|
|
138
144
|
};
|
|
139
145
|
});
|
|
140
146
|
const mappedGroups = groupedProductsWithTransactions.reduce((acc, group) => {
|
|
@@ -162,6 +168,7 @@ const getLedgerData = async (params, options = defaultReportLedgerOptions) => {
|
|
|
162
168
|
paymentDate: null,
|
|
163
169
|
};
|
|
164
170
|
}),
|
|
171
|
+
charges: group.charges,
|
|
165
172
|
},
|
|
166
173
|
};
|
|
167
174
|
}, {});
|
|
@@ -255,7 +262,7 @@ const getLedgerData = async (params, options = defaultReportLedgerOptions) => {
|
|
|
255
262
|
const mappedStatementTransactions = groupedMappedStatementTransactions
|
|
256
263
|
.reduce((acc, group) => [...acc, ...group], []);
|
|
257
264
|
const statementTransactionsWithBrokers = addBrokers
|
|
258
|
-
? await (0, brokers_db_1.enrichWithBrokers)(mappedStatementTransactions, group.product)
|
|
265
|
+
? await (0, brokers_db_1.enrichWithBrokers)(mappedStatementTransactions, group.product, group.charges, primeRate)
|
|
259
266
|
: mappedStatementTransactions;
|
|
260
267
|
const sortedTransactions = [...statementTransactionsWithBrokers, ...group.transactions]
|
|
261
268
|
.sort((a, b) => {
|
package/db/reports.db.ts
CHANGED
|
@@ -24,6 +24,8 @@ import { enrichWithBrokers } from './brokers.db';
|
|
|
24
24
|
import { TermLoanModel } from '../models/TermLoan.model';
|
|
25
25
|
import { TermLoanCalculatedModel } from '../models/TermLoanCalculated.model';
|
|
26
26
|
import { MODEL_NAMES } from '../models/_models';
|
|
27
|
+
import { ILoanChargeDoc, LoanCharge } from '../models/LoanCharges.model';
|
|
28
|
+
import { EFinancialIndex } from '../services/financial-indexes.service';
|
|
27
29
|
|
|
28
30
|
export enum EChargeType {
|
|
29
31
|
INTEREST_FEE = 'INTEREST',
|
|
@@ -101,11 +103,19 @@ const defaultReportLedgerOptions: ReportLedgerOptions = {
|
|
|
101
103
|
showFloatedBalance: true,
|
|
102
104
|
};
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
type GetFinancialIndexValue = (index: EFinancialIndex, date?: Date) => Promise<number>;
|
|
107
|
+
|
|
108
|
+
export const getLedger = async (
|
|
109
|
+
params: ReportLedgerParams,
|
|
110
|
+
options = defaultReportLedgerOptions,
|
|
111
|
+
deps: {
|
|
112
|
+
getFinancialIndexValue: GetFinancialIndexValue;
|
|
113
|
+
},
|
|
114
|
+
): Promise<{
|
|
105
115
|
[productId: string]: ILedgerReportRow []
|
|
106
116
|
}> => {
|
|
107
117
|
const { productIds, reportType } = params;
|
|
108
|
-
const fullTransactions = await getLedgerData(params, options);
|
|
118
|
+
const fullTransactions = await getLedgerData(params, options, deps.getFinancialIndexValue);
|
|
109
119
|
const products = await LoanProduct
|
|
110
120
|
.find({ _id: { $in: productIds.map((id) => new mongoose.Types.ObjectId(id)) } })
|
|
111
121
|
.sort({ code: 1 })
|
|
@@ -130,13 +140,16 @@ export const getLedger = async (params: ReportLedgerParams, options = defaultRep
|
|
|
130
140
|
}, {});
|
|
131
141
|
};
|
|
132
142
|
|
|
133
|
-
const getLedgerData = async (params: ReportLedgerParams, options = defaultReportLedgerOptions): Promise<{
|
|
143
|
+
const getLedgerData = async (params: ReportLedgerParams, options = defaultReportLedgerOptions, getFinancialIndexValue: GetFinancialIndexValue,): Promise<{
|
|
134
144
|
[productId: string]: ILedgerReportRow[]
|
|
135
145
|
}> => {
|
|
136
146
|
const { productIds, startDate, endDate } = params;
|
|
137
147
|
|
|
138
148
|
const addBrokers = true;
|
|
139
|
-
const
|
|
149
|
+
const productMongoIds = productIds.map((id) => new mongoose.Types.ObjectId(id));
|
|
150
|
+
const products = await LoanProduct.find({ _id: { $in: productMongoIds } }).lean();
|
|
151
|
+
const charges = await LoanCharge.find({ productId: { $in: productMongoIds } }).lean();
|
|
152
|
+
const primeRate = await getFinancialIndexValue(EFinancialIndex.PRIME_RATE, params.startDate);
|
|
140
153
|
const borrowers = await BorrowerModel.find({ _id: { $in: products.map((product) => product.borrowerId) } }).lean();
|
|
141
154
|
const borrowerMap = borrowers.reduce((acc, borrower) => (
|
|
142
155
|
{ ...acc, [borrower._id.toString()]: borrower.code }
|
|
@@ -207,11 +220,12 @@ const getLedgerData = async (params: ReportLedgerParams, options = defaultReport
|
|
|
207
220
|
product: p,
|
|
208
221
|
productId: p._id,
|
|
209
222
|
transactions: productTransactions ? productTransactions.transactions : [],
|
|
223
|
+
charges: charges.filter((charge) => charge.productId.toString() === p._id.toString()),
|
|
210
224
|
};
|
|
211
225
|
});
|
|
212
226
|
|
|
213
227
|
const mappedGroups: {
|
|
214
|
-
[productId: string]: { transactions: ILedgerDataRow[], product: ILoanProductDoc }
|
|
228
|
+
[productId: string]: { transactions: ILedgerDataRow[], product: ILoanProductDoc, charges: ILoanChargeDoc[] }
|
|
215
229
|
} = groupedProductsWithTransactions.reduce((acc, group) => {
|
|
216
230
|
return {
|
|
217
231
|
...acc,
|
|
@@ -237,6 +251,7 @@ const getLedgerData = async (params: ReportLedgerParams, options = defaultReport
|
|
|
237
251
|
paymentDate: null,
|
|
238
252
|
};
|
|
239
253
|
}),
|
|
254
|
+
charges: group.charges,
|
|
240
255
|
},
|
|
241
256
|
};
|
|
242
257
|
}, {});
|
|
@@ -335,7 +350,7 @@ const getLedgerData = async (params: ReportLedgerParams, options = defaultReport
|
|
|
335
350
|
const mappedStatementTransactions: ILedgerDataRow[] = groupedMappedStatementTransactions
|
|
336
351
|
.reduce((acc, group) => [...acc, ...group], []);
|
|
337
352
|
const statementTransactionsWithBrokers = addBrokers
|
|
338
|
-
? await enrichWithBrokers(mappedStatementTransactions, group.product)
|
|
353
|
+
? await enrichWithBrokers(mappedStatementTransactions, group.product, group.charges, primeRate)
|
|
339
354
|
: mappedStatementTransactions;
|
|
340
355
|
const sortedTransactions = [...statementTransactionsWithBrokers, ...group.transactions]
|
|
341
356
|
.sort((a, b) => {
|
|
@@ -39,6 +39,7 @@ export interface IProductBroker {
|
|
|
39
39
|
BSCode: string;
|
|
40
40
|
PLCode: string;
|
|
41
41
|
productId: mongoose.Types.ObjectId;
|
|
42
|
+
baseInterestTotal: number;
|
|
42
43
|
interestShare: number;
|
|
43
44
|
adminShare: number;
|
|
44
45
|
otherShare: number;
|
|
@@ -74,6 +75,7 @@ export declare const ProductBrokerSchema: mongoose.Schema<any, mongoose.Model<an
|
|
|
74
75
|
BSCode: string;
|
|
75
76
|
PLCode: string;
|
|
76
77
|
productId: mongoose.Types.ObjectId;
|
|
78
|
+
baseInterestTotal: number;
|
|
77
79
|
interestShare: number;
|
|
78
80
|
adminShare: number;
|
|
79
81
|
otherShare: number;
|
|
@@ -90,6 +92,7 @@ export declare const ProductBrokerSchema: mongoose.Schema<any, mongoose.Model<an
|
|
|
90
92
|
BSCode: string;
|
|
91
93
|
PLCode: string;
|
|
92
94
|
productId: mongoose.Types.ObjectId;
|
|
95
|
+
baseInterestTotal: number;
|
|
93
96
|
interestShare: number;
|
|
94
97
|
adminShare: number;
|
|
95
98
|
otherShare: number;
|
|
@@ -106,6 +109,7 @@ export declare const ProductBrokerSchema: mongoose.Schema<any, mongoose.Model<an
|
|
|
106
109
|
BSCode: string;
|
|
107
110
|
PLCode: string;
|
|
108
111
|
productId: mongoose.Types.ObjectId;
|
|
112
|
+
baseInterestTotal: number;
|
|
109
113
|
interestShare: number;
|
|
110
114
|
adminShare: number;
|
|
111
115
|
otherShare: number;
|
|
@@ -17,6 +17,7 @@ exports.ProductBrokerValidationSchema = joi_1.default.object({
|
|
|
17
17
|
BSCode: joi_1.default.string().required(),
|
|
18
18
|
PLCode: joi_1.default.string().required(),
|
|
19
19
|
productId: joi_1.default.string().required(),
|
|
20
|
+
baseInterestTotal: joi_1.default.number().required(),
|
|
20
21
|
interestShare: joi_1.default.number().required(),
|
|
21
22
|
adminShare: joi_1.default.number().required(),
|
|
22
23
|
otherShare: joi_1.default.number().required(),
|
|
@@ -60,6 +61,10 @@ exports.ProductBrokerSchema = new mongoose_1.default.Schema({
|
|
|
60
61
|
ref: _models_1.MODEL_NAMES.loanProducts,
|
|
61
62
|
required: true,
|
|
62
63
|
},
|
|
64
|
+
baseInterestTotal: {
|
|
65
|
+
type: Number,
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
63
68
|
interestShare: {
|
|
64
69
|
type: Number,
|
|
65
70
|
required: true,
|
|
@@ -14,6 +14,7 @@ export const ProductBrokerValidationSchema = Joi.object({
|
|
|
14
14
|
BSCode: Joi.string().required(),
|
|
15
15
|
PLCode: Joi.string().required(),
|
|
16
16
|
productId: Joi.string().required(),
|
|
17
|
+
baseInterestTotal: Joi.number().required(),
|
|
17
18
|
interestShare: Joi.number().required(),
|
|
18
19
|
adminShare: Joi.number().required(),
|
|
19
20
|
otherShare: Joi.number().required(),
|
|
@@ -28,6 +29,7 @@ export interface IProductBroker {
|
|
|
28
29
|
BSCode: string;
|
|
29
30
|
PLCode: string;
|
|
30
31
|
productId: mongoose.Types.ObjectId;
|
|
32
|
+
baseInterestTotal: number;
|
|
31
33
|
interestShare: number;
|
|
32
34
|
adminShare: number;
|
|
33
35
|
otherShare: number;
|
|
@@ -96,6 +98,10 @@ export const ProductBrokerSchema = new mongoose.Schema(
|
|
|
96
98
|
ref: MODEL_NAMES.loanProducts,
|
|
97
99
|
required: true,
|
|
98
100
|
},
|
|
101
|
+
baseInterestTotal: {
|
|
102
|
+
type: Number,
|
|
103
|
+
required: true,
|
|
104
|
+
},
|
|
99
105
|
interestShare: {
|
|
100
106
|
type: Number,
|
|
101
107
|
required: true,
|
package/package.json
CHANGED
|
@@ -18,6 +18,7 @@ const defaultBrokerList = [
|
|
|
18
18
|
PLCode: '',
|
|
19
19
|
productId: null,
|
|
20
20
|
loanBrokerId: null,
|
|
21
|
+
baseInterestTotal: 0,
|
|
21
22
|
adminShare: 0,
|
|
22
23
|
interestShare: 0,
|
|
23
24
|
otherShare: 0,
|
|
@@ -84,6 +85,7 @@ class BrokersService {
|
|
|
84
85
|
BSCode: null,
|
|
85
86
|
PLCode: null,
|
|
86
87
|
productId: product,
|
|
88
|
+
baseInterestTotal: null,
|
|
87
89
|
adminShare: shares.adminShare,
|
|
88
90
|
interestShare: shares.interestShare,
|
|
89
91
|
otherShare: shares.otherShare,
|
|
@@ -92,7 +94,7 @@ class BrokersService {
|
|
|
92
94
|
}
|
|
93
95
|
async checkTotalShares(borrowerId, brokers) {
|
|
94
96
|
const productShares = await this.getTotalShares(borrowerId, brokers);
|
|
95
|
-
return !Object.values(productShares).some(values => values.adminShare > 1 || values.interestShare > 1 || values.otherShare > 1);
|
|
97
|
+
return !Object.values(productShares).some((values) => values.adminShare > 1 || values.interestShare > 1 || values.otherShare > 1);
|
|
96
98
|
}
|
|
97
99
|
async saveProductBrokers(borrowerId, brokers) {
|
|
98
100
|
const existingBrokers = await (0, brokers_db_2.getBorrowerBrokers)(borrowerId);
|
|
@@ -26,6 +26,7 @@ const defaultBrokerList: BrokerView[] = [
|
|
|
26
26
|
PLCode: '',
|
|
27
27
|
productId: null,
|
|
28
28
|
loanBrokerId: null,
|
|
29
|
+
baseInterestTotal: 0,
|
|
29
30
|
adminShare: 0,
|
|
30
31
|
interestShare: 0,
|
|
31
32
|
otherShare: 0,
|
|
@@ -111,6 +112,7 @@ export class BrokersService {
|
|
|
111
112
|
BSCode: null,
|
|
112
113
|
PLCode: null,
|
|
113
114
|
productId: product,
|
|
115
|
+
baseInterestTotal: null,
|
|
114
116
|
adminShare: shares.adminShare,
|
|
115
117
|
interestShare: shares.interestShare,
|
|
116
118
|
otherShare: shares.otherShare,
|
|
@@ -120,7 +122,7 @@ export class BrokersService {
|
|
|
120
122
|
|
|
121
123
|
async checkTotalShares(borrowerId: string, brokers: IProductBrokerView[]) {
|
|
122
124
|
const productShares = await this.getTotalShares(borrowerId, brokers);
|
|
123
|
-
return !Object.values(productShares).some(values => values.adminShare > 1 || values.interestShare > 1 || values.otherShare > 1);
|
|
125
|
+
return !Object.values(productShares).some((values) => values.adminShare > 1 || values.interestShare > 1 || values.otherShare > 1);
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
async saveProductBrokers(borrowerId: string, brokers: IProductBrokerView[]) {
|
|
@@ -35,6 +35,7 @@ import { CashAllocationService } from './cash-allocation.service';
|
|
|
35
35
|
import { CompaniesService } from './companies.service';
|
|
36
36
|
import { LoanChargesService } from './loan-charges.service';
|
|
37
37
|
import { LoanPaymentsService } from './loan-payments.service';
|
|
38
|
+
import { FinancialIndexesService } from './financial-indexes.service';
|
|
38
39
|
export type QuickBookReportType = 'accrual' | 'payment' | 'cash';
|
|
39
40
|
interface IQBReportTransaction {
|
|
40
41
|
service: string;
|
|
@@ -58,7 +59,8 @@ export declare class QuickbooksService {
|
|
|
58
59
|
private readonly companiesService;
|
|
59
60
|
private readonly loanChargesService;
|
|
60
61
|
private readonly loanPaymentsService;
|
|
61
|
-
|
|
62
|
+
private readonly financialIndexesService;
|
|
63
|
+
constructor(banksService: BanksService, bankUploadedTransactionsService: BankUploadedTransactionsService, borrowersDB: BorrowersDB, brokersService: BrokersService, cashAllocationService: CashAllocationService, companiesService: CompaniesService, loanChargesService: LoanChargesService, loanPaymentsService: LoanPaymentsService, financialIndexesService: FinancialIndexesService);
|
|
62
64
|
uploadAccounts(accounts: IQuickbooksUploadItem[], deleteOld: boolean): Promise<void>;
|
|
63
65
|
getAllAccounts(): Promise<(mongoose.FlattenMaps<IQuickbooksAccount> & {
|
|
64
66
|
_id: mongoose.Types.ObjectId;
|
|
@@ -16,6 +16,8 @@ const TermLoan_model_1 = require("../models/TermLoan.model");
|
|
|
16
16
|
const reports_db_1 = require("../db/reports.db");
|
|
17
17
|
const QuickbooksAccount_model_1 = require("../models/QuickbooksAccount.model");
|
|
18
18
|
const date_helper_1 = require("../helpers/date.helper");
|
|
19
|
+
const financial_indexes_service_1 = require("./financial-indexes.service");
|
|
20
|
+
const brokers_db_1 = require("../db/brokers.db");
|
|
19
21
|
const headersIIF = [
|
|
20
22
|
{
|
|
21
23
|
service: '!TRNS',
|
|
@@ -88,7 +90,8 @@ class QuickbooksService {
|
|
|
88
90
|
companiesService;
|
|
89
91
|
loanChargesService;
|
|
90
92
|
loanPaymentsService;
|
|
91
|
-
|
|
93
|
+
financialIndexesService;
|
|
94
|
+
constructor(banksService, bankUploadedTransactionsService, borrowersDB, brokersService, cashAllocationService, companiesService, loanChargesService, loanPaymentsService, financialIndexesService) {
|
|
92
95
|
this.banksService = banksService;
|
|
93
96
|
this.bankUploadedTransactionsService = bankUploadedTransactionsService;
|
|
94
97
|
this.borrowersDB = borrowersDB;
|
|
@@ -97,6 +100,7 @@ class QuickbooksService {
|
|
|
97
100
|
this.companiesService = companiesService;
|
|
98
101
|
this.loanChargesService = loanChargesService;
|
|
99
102
|
this.loanPaymentsService = loanPaymentsService;
|
|
103
|
+
this.financialIndexesService = financialIndexesService;
|
|
100
104
|
}
|
|
101
105
|
async uploadAccounts(accounts, deleteOld) {
|
|
102
106
|
const accountsWithId = accounts.map((account) => ({ _id: 'new_', ...account }));
|
|
@@ -325,6 +329,7 @@ class QuickbooksService {
|
|
|
325
329
|
document: '',
|
|
326
330
|
memo: '',
|
|
327
331
|
};
|
|
332
|
+
const primeRate = await this.financialIndexesService.getFinancialIndexValue(financial_indexes_service_1.EFinancialIndex.PRIME_RATE, reportDate);
|
|
328
333
|
await Promise.all(productIds.map(async (productId) => {
|
|
329
334
|
const product = await this.loanChargesService.getLoanProductById(productId);
|
|
330
335
|
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
@@ -395,11 +400,18 @@ class QuickbooksService {
|
|
|
395
400
|
}
|
|
396
401
|
return;
|
|
397
402
|
}
|
|
403
|
+
let brokerAmount;
|
|
404
|
+
if (shareName === 'interestShare') {
|
|
405
|
+
brokerAmount = (0, brokers_db_1.handleBrokerInterestFee)(-totalAmount, charges, broker.interestShare, broker.baseInterestTotal, primeRate);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
brokerAmount = new decimal_js_1.default(-totalAmount).mul(broker[shareName]).toDP(2).toNumber();
|
|
409
|
+
}
|
|
398
410
|
const tr = {
|
|
399
411
|
...trTemplate,
|
|
400
412
|
productId,
|
|
401
413
|
class: borrower.code,
|
|
402
|
-
amount:
|
|
414
|
+
amount: brokerAmount,
|
|
403
415
|
memo: `${borrower.code} ${reports_db_1.EChargeType[charge.chargeType].toUpperCase()} BROKER ${broker.order + 1}`,
|
|
404
416
|
};
|
|
405
417
|
tr.account = quickbooksAccountBrokerBS.fullName;
|
|
@@ -23,6 +23,8 @@ import { CompaniesService } from './companies.service';
|
|
|
23
23
|
import { LoanChargesService } from './loan-charges.service';
|
|
24
24
|
import { LoanPaymentsService } from './loan-payments.service';
|
|
25
25
|
import { isProductActive } from '../helpers/date.helper';
|
|
26
|
+
import { EFinancialIndex, FinancialIndexesService } from './financial-indexes.service';
|
|
27
|
+
import { handleBrokerInterestFee } from '../db/brokers.db';
|
|
26
28
|
|
|
27
29
|
export type QuickBookReportType = 'accrual' | 'payment' | 'cash';
|
|
28
30
|
|
|
@@ -130,6 +132,7 @@ export class QuickbooksService {
|
|
|
130
132
|
private readonly companiesService: CompaniesService,
|
|
131
133
|
private readonly loanChargesService: LoanChargesService,
|
|
132
134
|
private readonly loanPaymentsService: LoanPaymentsService,
|
|
135
|
+
private readonly financialIndexesService: FinancialIndexesService,
|
|
133
136
|
) {
|
|
134
137
|
}
|
|
135
138
|
|
|
@@ -380,6 +383,8 @@ export class QuickbooksService {
|
|
|
380
383
|
memo: '',
|
|
381
384
|
};
|
|
382
385
|
|
|
386
|
+
const primeRate = await this.financialIndexesService.getFinancialIndexValue(EFinancialIndex.PRIME_RATE, reportDate)
|
|
387
|
+
|
|
383
388
|
await Promise.all(productIds.map(async (productId) => {
|
|
384
389
|
const product = await this.loanChargesService.getLoanProductById(productId);
|
|
385
390
|
const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
|
|
@@ -460,11 +465,18 @@ export class QuickbooksService {
|
|
|
460
465
|
return;
|
|
461
466
|
}
|
|
462
467
|
|
|
468
|
+
let brokerAmount: number;
|
|
469
|
+
if (shareName === 'interestShare') {
|
|
470
|
+
brokerAmount = handleBrokerInterestFee(-totalAmount, charges, broker.interestShare, broker.baseInterestTotal, primeRate);
|
|
471
|
+
} else {
|
|
472
|
+
brokerAmount = new Decimal(-totalAmount).mul(broker[shareName]).toDP(2).toNumber();
|
|
473
|
+
}
|
|
474
|
+
|
|
463
475
|
const tr: IQBReportTransaction = {
|
|
464
476
|
...trTemplate,
|
|
465
477
|
productId,
|
|
466
478
|
class: borrower.code,
|
|
467
|
-
amount:
|
|
479
|
+
amount: brokerAmount,
|
|
468
480
|
memo: `${borrower.code} ${EChargeType[charge.chargeType].toUpperCase()} BROKER ${broker.order + 1}`,
|
|
469
481
|
};
|
|
470
482
|
|
|
@@ -36,6 +36,7 @@ import { LoanChargesService } from './loan-charges.service';
|
|
|
36
36
|
import { LoanTransactionsService } from './loan-transactions.service';
|
|
37
37
|
import { SignsService } from './signs.service';
|
|
38
38
|
import { UploadsService } from './uploads.service';
|
|
39
|
+
import { FinancialIndexesService } from './financial-indexes.service';
|
|
39
40
|
export interface IBankReportData {
|
|
40
41
|
borrowerId?: string;
|
|
41
42
|
borrowerName: string;
|
|
@@ -97,8 +98,9 @@ export declare class ReportsService {
|
|
|
97
98
|
private readonly signsService;
|
|
98
99
|
private readonly uploadsService;
|
|
99
100
|
private readonly investorSummaryService;
|
|
101
|
+
private readonly financialIndexesService;
|
|
100
102
|
margin: number;
|
|
101
|
-
constructor(availabilityService: AvailabilityService, borrowerService: BorrowerService, collateralAdjustmentsService: CollateralAdjustmentsService, collateralsService: CollateralsService, equipmentService: EquipmentService, loanChargesService: LoanChargesService, loanTransactionsService: LoanTransactionsService, signsService: SignsService, uploadsService: UploadsService, investorSummaryService: InvestorSummaryService);
|
|
103
|
+
constructor(availabilityService: AvailabilityService, borrowerService: BorrowerService, collateralAdjustmentsService: CollateralAdjustmentsService, collateralsService: CollateralsService, equipmentService: EquipmentService, loanChargesService: LoanChargesService, loanTransactionsService: LoanTransactionsService, signsService: SignsService, uploadsService: UploadsService, investorSummaryService: InvestorSummaryService, financialIndexesService: FinancialIndexesService);
|
|
102
104
|
getCollateralAdjustmentsForLastSignedBBC(borrowerId: string, date: Date): Promise<number>;
|
|
103
105
|
private getProductBalances;
|
|
104
106
|
getBorrowerAndProducts(borrowerId: string, date: Date, useSignedBBC?: boolean, requireBBC?: boolean): Promise<{
|