feat(investment): settle purchases from Holdings; broken-period interest to settlement date
- Purchases are saved as Pending Settlement. Holdings show the status and a Settle button (from the settlement date), and the GL transaction id once settled. - The transaction log shows "Not posted (pending settlement)" until a deal is settled, and dates the purchase on its settlement date. - Broken-period interest is counted up to the settlement date. - Accounting Preview title and the Holdings Settlement Date column reflect settlement-date posting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -239,17 +239,19 @@ export class BondInvestmentComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
/** 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.dealDate,
|
||||
txnDate: d.settlementDate,
|
||||
amount: d.settlementAmount,
|
||||
particulars: `Purchase Deal ${d.dealNo} - ${d.scriptDesc} - ${d.partyName} [${d.category}]`,
|
||||
glCode: d.investmentGlCode,
|
||||
glName: d.investmentGlName
|
||||
glCode: posted ? d.investmentGlCode : '',
|
||||
glName: posted ? `${d.investmentGlName} (txn ${d.glTransactionId})` : 'Not posted (pending settlement)'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -573,20 +575,19 @@ export class BondInvestmentComponent implements OnInit {
|
||||
return approxYtm(f.intRate, f.purchaseRate || 100, yearsBetween(f.issueDate, this.purchaseMaturityDate));
|
||||
}
|
||||
|
||||
/** Accrued Interest at purchase — interest earned by the seller since the last coupon date. */
|
||||
/** Interest owed to the seller from the last coupon date up to the deal date, on a 30/360 basis —
|
||||
* interest is counted to the event date, not the date the money moves (Jitendra Sir). */
|
||||
/** Broken-period interest owed to the seller from the last coupon date up to the settlement date, 30/360. */
|
||||
get purchaseAccruedInterest(): number {
|
||||
const f = this.purchaseForm;
|
||||
if (!f.intRate || !f.lastIntDt || !f.dealDate) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round((this.purchaseBaseAmount * (f.intRate / 100) * days360(f.lastIntDt, f.dealDate)) / 360);
|
||||
return Math.round((this.purchaseBaseAmount * (f.intRate / 100) * this.purchaseAccruedDays) / 360);
|
||||
}
|
||||
|
||||
get purchaseAccruedDays(): number {
|
||||
const f = this.purchaseForm;
|
||||
return f.lastIntDt && f.dealDate ? days360(f.lastIntDt, f.dealDate) : 0;
|
||||
// Up to the settlement date, when the bond leaves the seller (RBI para 83; Jitendra Sir).
|
||||
return f.lastIntDt && this.purchaseSettlementDate ? days360(f.lastIntDt, this.purchaseSettlementDate) : 0;
|
||||
}
|
||||
|
||||
/** Premium (+) or discount (−) paid over face value. */
|
||||
@@ -842,7 +843,9 @@ export class BondInvestmentComponent implements OnInit {
|
||||
};
|
||||
this.investmentService.createPurchase(body).subscribe({
|
||||
next: (res) => {
|
||||
this.toast(`Deal ${res.resourceIdentifier} saved. GL posting will follow once accounting is approved.`);
|
||||
this.toast(
|
||||
`Deal ${res.resourceIdentifier} saved — pending settlement. Settle it from Holdings on or after the settlement date.`
|
||||
);
|
||||
this.activeTab = 'holdings';
|
||||
this.loadPurchases();
|
||||
},
|
||||
@@ -941,6 +944,24 @@ export class BondInvestmentComponent implements OnInit {
|
||||
this.toast(`Deal for Cert No ${h.certNo} retrieved.`);
|
||||
}
|
||||
|
||||
/** Posts a pending deal to GL dated its settlement date; under maker-checker it waits in the Checker Inbox. */
|
||||
settleDeal(h: InvestmentHolding): void {
|
||||
if (!h.id) {
|
||||
return;
|
||||
}
|
||||
this.investmentService.settlePurchase(h.id).subscribe({
|
||||
next: (res) => {
|
||||
this.toast(
|
||||
res.rollbackTransaction
|
||||
? `Settlement of deal ${h.dealNo} sent for approval (Checker Inbox).`
|
||||
: `Deal ${h.dealNo} settled and posted to GL (transaction ${res.transactionId}).`
|
||||
);
|
||||
this.loadPurchases();
|
||||
},
|
||||
error: (err) => this.toast(apiErrorMessage(err))
|
||||
});
|
||||
}
|
||||
|
||||
dropPurchase(): void {
|
||||
const h = this.holdings.find((x) => x.certNo === this.purchaseForm.certNo);
|
||||
if (!h?.id) {
|
||||
|
||||
Reference in New Issue
Block a user