feat: add investment reports, bond components, dashboard, and service implementation
This commit is contained in:
@@ -74,7 +74,11 @@ import {
|
||||
InvestmentNonSlrLimitDto,
|
||||
InvestmentService,
|
||||
apiErrorMessage,
|
||||
toHolding
|
||||
toHolding,
|
||||
toInterestTxn,
|
||||
toPurchaseTxn,
|
||||
toSaleTxn,
|
||||
toTransferTxn
|
||||
} from '../investment.service';
|
||||
|
||||
type BondType = 'GOVT_SECURITY' | 'NON_SLR' | 'BOND' | 'MUTUAL_FUND';
|
||||
@@ -222,7 +226,7 @@ export class BondInvestmentComponent implements OnInit {
|
||||
this.investmentService.getPurchases().subscribe({
|
||||
next: (purchases) => {
|
||||
this.holdings = purchases.map(toHolding);
|
||||
this.txns = purchases.map((d) => this.purchaseTxn(d));
|
||||
this.txns = purchases.map(toPurchaseTxn);
|
||||
this.purchaseForm = this.blankPurchaseForm();
|
||||
this.loadNonSlrLimit();
|
||||
this.loadInterestLog();
|
||||
@@ -253,115 +257,13 @@ export class BondInvestmentComponent implements OnInit {
|
||||
);
|
||||
this.txns = [
|
||||
...others,
|
||||
...postings.map((x) => this.interestTxn(x)),
|
||||
...sales.map((x) => this.saleTxn(x)),
|
||||
...transfers.map((x) => this.transferTxn(x))
|
||||
...postings.map(toInterestTxn),
|
||||
...sales.map(toSaleTxn),
|
||||
...transfers.map(toTransferTxn)
|
||||
].sort((a, b) => parseDDMMYYYY(b.txnDate).getTime() - parseDDMMYYYY(a.txnDate).getTime());
|
||||
});
|
||||
}
|
||||
|
||||
/** A security transfer shows the book value carried to the new security. */
|
||||
private transferTxn(x: InvestmentSecurityTransferDto): InvestmentTxn {
|
||||
return {
|
||||
certNo: x.certNo,
|
||||
instrumentType: x.bondType as InstrumentType,
|
||||
txnType: 'TRANSFER',
|
||||
voucherNo: x.voucherNo ?? `TRF/${x.dealNo}/${x.id}`,
|
||||
txnDate: x.transferDate,
|
||||
amount: x.bookValue,
|
||||
particulars: `GOI category transfer - Deal ${x.dealNo}: ${x.fromScriptCode} ${x.fromScriptDesc} → ${x.toScriptCode} ${x.toScriptDesc}`,
|
||||
glCode: x.toGlCode,
|
||||
glName: x.glTransactionId
|
||||
? `${x.toGlName} / Cr ${x.fromGlCode} (txn ${x.glTransactionId})`
|
||||
: `${x.toGlName} (same GL, no entry)`
|
||||
};
|
||||
}
|
||||
|
||||
/** A sale or redemption shows the money into the bank; a pending sale is not in the GL yet. */
|
||||
private saleTxn(x: InvestmentSaleDto): InvestmentTxn {
|
||||
if (x.saleType === 'AMORTISE') {
|
||||
return {
|
||||
certNo: x.certNo,
|
||||
instrumentType: x.bondType as InstrumentType,
|
||||
txnType: 'AMORTIZE',
|
||||
voucherNo: x.voucherNo ?? `AMZ/${x.dealNo}/${x.id}`,
|
||||
txnDate: x.settlementDate,
|
||||
amount: x.bookValueSold,
|
||||
particulars: `${x.remarks ?? 'Premium amortisation'} - Deal ${x.dealNo} - ${x.scriptDesc} (${x.accruedDays} days)`,
|
||||
glCode: '500604',
|
||||
glName: `Amortisation of Premium / Cr Premium on Investments (txn ${x.glTransactionId})`
|
||||
};
|
||||
}
|
||||
if (x.saleType === 'WRITEOFF') {
|
||||
return {
|
||||
certNo: x.certNo,
|
||||
instrumentType: x.bondType as InstrumentType,
|
||||
txnType: 'WRITE_OFF',
|
||||
voucherNo: x.voucherNo ?? `WOF/${x.dealNo}/${x.id}`,
|
||||
txnDate: x.settlementDate,
|
||||
amount: x.bookValueSold,
|
||||
particulars: `Written off - Deal ${x.dealNo} - ${x.scriptDesc}${x.remarks ? ' - ' + x.remarks : ''}`,
|
||||
glCode: '500605',
|
||||
glName: `Loss on Investments / Cr ${x.investmentGlCode} (txn ${x.glTransactionId})`
|
||||
};
|
||||
}
|
||||
const redeem = x.saleType === 'REDEEM';
|
||||
const pl =
|
||||
x.profitLoss >= 0
|
||||
? `profit ₹${x.profitLoss}`
|
||||
: redeem
|
||||
? `premium written off ₹${-x.profitLoss}`
|
||||
: `loss ₹${-x.profitLoss}`;
|
||||
return {
|
||||
certNo: x.certNo,
|
||||
instrumentType: x.bondType as InstrumentType,
|
||||
txnType: x.saleType,
|
||||
voucherNo: x.voucherNo ?? `SALE/${x.dealNo}/${x.id}`,
|
||||
txnDate: x.settlementDate,
|
||||
amount: x.netAmount,
|
||||
particulars: redeem
|
||||
? `Redeemed at maturity - Deal ${x.dealNo} - ${x.scriptDesc} (${pl})`
|
||||
: `Sold ${x.units} units @ ${x.saleRate} to ${x.partyName ?? ''} - Deal ${x.dealNo} - ${x.scriptDesc} (${pl})`,
|
||||
glCode: x.status === 'SETTLED' ? x.bankGlCode : '',
|
||||
glName:
|
||||
x.status === 'SETTLED'
|
||||
? `${x.bankGlName} / Cr ${x.investmentGlCode} (txn ${x.glTransactionId})`
|
||||
: 'Not posted (pending settlement)'
|
||||
};
|
||||
}
|
||||
|
||||
/** A coupon shows the cash received into the bank; a provision the interest booked as receivable. */
|
||||
private interestTxn(x: InvestmentInterestPostingDto): InvestmentTxn {
|
||||
const coupon = x.lineType === 'COUPON';
|
||||
return {
|
||||
certNo: x.certNo,
|
||||
instrumentType: x.bondType as InstrumentType,
|
||||
txnType: 'INTEREST',
|
||||
voucherNo: x.voucherNo,
|
||||
txnDate: x.intDate,
|
||||
amount: coupon ? x.grossInterest : x.amount,
|
||||
particulars: `${coupon ? 'Coupon' : 'Provision'} ${x.fromDate} to ${x.toDate} - Deal ${x.dealNo} - ${x.scriptDesc}`,
|
||||
glCode: x.drGlCode,
|
||||
glName: `${x.drGlName} / Cr ${x.incomeGlCode} (txn ${x.glTransactionId})`
|
||||
};
|
||||
}
|
||||
|
||||
/** A purchase reaches the GL only when it is settled, dated the settlement date. */
|
||||
private purchaseTxn(d: InvestmentPurchaseDto): InvestmentTxn {
|
||||
const posted = !!d.glTransactionId;
|
||||
return {
|
||||
certNo: d.certNo,
|
||||
instrumentType: d.bondType,
|
||||
txnType: 'PURCHASE',
|
||||
voucherNo: `INV/PUR/${d.dealNo}`,
|
||||
txnDate: d.settlementDate,
|
||||
amount: d.settlementAmount,
|
||||
particulars: `Purchase Deal ${d.dealNo} - ${d.scriptDesc} - ${d.partyName} [${d.category}]`,
|
||||
glCode: posted ? d.investmentGlCode : '',
|
||||
glName: posted ? `${d.investmentGlName} (txn ${d.glTransactionId})` : 'Not posted (pending settlement)'
|
||||
};
|
||||
}
|
||||
|
||||
activeTab: BondTab = 'holdings';
|
||||
/** Purchase Entry shows only the mandatory fields; the rest sit under "More Details". */
|
||||
showMoreDetails = false;
|
||||
|
||||
Reference in New Issue
Block a user