feat(investment): interest in transaction log, accrued interest since last coupon, investment-only GL list
This commit is contained in:
@@ -853,7 +853,7 @@
|
||||
<tr [style.opacity]="r.due ? 1 : 0.6">
|
||||
<td>
|
||||
<span class="pg-chip">{{ r.type === 'COUPON' ? 'Coupon' : 'Provision' }}</span>
|
||||
@if (r.due) {
|
||||
@if (r.due && !r.posted) {
|
||||
<span class="pg-chip">Due</span>
|
||||
}
|
||||
</td>
|
||||
|
||||
@@ -58,6 +58,7 @@ import { exportWorkbook } from '../investment-export';
|
||||
import {
|
||||
InvestmentDailyRateDto,
|
||||
InvestmentInterestDueDto,
|
||||
InvestmentInterestPostingDto,
|
||||
InvestmentInterestRowDto,
|
||||
InvestmentInterestScheduleDto,
|
||||
InvestmentPurchaseDto,
|
||||
@@ -243,11 +244,42 @@ export class BondInvestmentComponent implements OnInit {
|
||||
this.holdings = purchases.map(toHolding);
|
||||
this.txns = purchases.map((d) => this.purchaseTxn(d));
|
||||
this.purchaseForm = this.blankPurchaseForm();
|
||||
this.loadInterestLog();
|
||||
},
|
||||
error: (err) => this.toast(`Could not load purchases: ${apiErrorMessage(err)}`)
|
||||
});
|
||||
}
|
||||
|
||||
/** Posted coupons / provisions join the purchases in the transaction log, latest date first. */
|
||||
private loadInterestLog(): void {
|
||||
this.investmentService.getInterestPostings().subscribe({
|
||||
next: (postings) => {
|
||||
const others = this.txns.filter((t) => !(t.txnType === 'INTEREST' && t.voucherNo.startsWith('JV/')));
|
||||
this.txns = [
|
||||
...others,
|
||||
...postings.map((x) => this.interestTxn(x))
|
||||
].sort((a, b) => parseDDMMYYYY(b.txnDate).getTime() - parseDDMMYYYY(a.txnDate).getTime());
|
||||
},
|
||||
error: () => undefined
|
||||
});
|
||||
}
|
||||
|
||||
/** 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;
|
||||
@@ -1128,6 +1160,7 @@ export class BondInvestmentComponent implements OnInit {
|
||||
);
|
||||
this.retrieveInterestSchedule();
|
||||
this.loadDueInterest();
|
||||
this.loadInterestLog();
|
||||
},
|
||||
error: (err) => {
|
||||
this.intPosting = false;
|
||||
@@ -1531,8 +1564,20 @@ export class BondInvestmentComponent implements OnInit {
|
||||
}
|
||||
|
||||
// -------------------- Helpers --------------------
|
||||
/**
|
||||
* Interest earned since the last coupon up to the as-on date (30/360), as the backend schedule counts it.
|
||||
* Coupons fall every six months from the purchase's last interest date; a coupon received resets it to zero.
|
||||
*/
|
||||
accrued(h: InvestmentHolding): number {
|
||||
return accruedInterest(h, this.asOnDate);
|
||||
if (!h.lastIntDate || h.status !== 'ACTIVE') {
|
||||
return h.lastIntDate ? 0 : accruedInterest(h, this.asOnDate);
|
||||
}
|
||||
const asOn = parseDDMMYYYY(this.asOnDate) > parseDDMMYYYY(h.maturityDate) ? h.maturityDate : this.asOnDate;
|
||||
let lastCoupon = h.lastIntDate;
|
||||
for (let k = 1; parseDDMMYYYY(addPeriod(h.lastIntDate, 0, 6 * k)) <= parseDDMMYYYY(asOn); k++) {
|
||||
lastCoupon = addPeriod(h.lastIntDate, 0, 6 * k);
|
||||
}
|
||||
return Math.round((h.faceValue * (h.interestRate / 100) * days360(lastCoupon, asOn)) / 360);
|
||||
}
|
||||
|
||||
maturity(h: InvestmentHolding): number {
|
||||
|
||||
Reference in New Issue
Block a user