feat(investment,npa): RBI-grounded targets, GL-tracked interest posting, date/search pickers

- Correct Investment portfolio allocation and Loan Origination product-mix targets against
  RBI's UCB SLR/Non-SLR framework and PSL circulars, with a Target Basis column distinguishing
  RBI-mandated vs board-policy rows (Excel export included).
- Expand Investment holdings dataset with more near-term maturities so the Due-Date Diary
  reflects a realistic spread across FD/Govt Security/Non-SLR/Bond.
- Add From/To period-based Interest Calculation & Post (with GL selection for both posting and
  receiving) across the Investment Interest module, FD Entry and Bond Entry screens; fix a bug
  where a posted period kept re-appearing as due since accrual wasn't tracked per-holding.
- Add a reusable DD-MM-YYYY date-picker component and roll it out across all Investment date
  fields.
- NPA Summary Report: drop SMA-0/1/2 from the stage-wise table (kept in Day-wise Overdue only).
- NPA Recovery Report: add Account Type / NPA Category / Target-vs-Actual columns, a shared
  Account Type filter driving both the account picker and the register, and a searchable
  (fixed-position, not clipped by card overflow) account select for long lists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 19:26:19 +05:30
co-authored by Claude Sonnet 5
parent d7f5bfe5f5
commit ebe67cc410
20 changed files with 1401 additions and 226 deletions
@@ -483,10 +483,17 @@
<div class="pg-card">
<div class="pos-section-label">Product Mix — Target % vs Actual % (of Disbursed Amount)</div>
<p class="pg-sub npa-report-note">
KCC and Cash Credit (MSME) targets are RBI's actual Priority Sector Lending sub-targets — Agriculture 18% and
Micro Enterprises 7.5% of Adjusted Net Bank Credit (shown here against this module's own mix, since RBI sets
them against the bank's whole loan book, not one product line). The rest are internal business-plan targets, not
RBI-mandated.
</p>
<table class="pg-table">
<thead>
<tr>
<th>Product</th>
<th>Target Basis</th>
<th class="num">Target %</th>
<th class="num col-in">Actual %</th>
<th class="num">Variance</th>
@@ -497,7 +504,12 @@
@for (row of productTargetRows; track row.code) {
<tr>
<td>{{ row.label }}</td>
<td class="num">{{ row.targetPct | number: '1.0-0' }}%</td>
<td>
<span class="loan-badge" [class.loan-badge--progress]="row.isRbiMandated">
{{ row.isRbiMandated ? 'RBI PSL Sub-Target' : 'Internal Target' }}
</span>
</td>
<td class="num">{{ row.targetPct | number: '1.0-1' }}%</td>
<td class="num col-in">{{ row.actualPct | number: '1.1-1' }}%</td>
<td class="num" [class.col-out]="row.variancePct < 0" [class.col-in]="row.variancePct > 0">
{{ row.variancePct > 0 ? '+' : '' }}{{ row.variancePct | number: '1.1-1' }} pp
@@ -533,7 +545,7 @@
@for (row of channelTargetRows; track row.code) {
<tr>
<td>{{ row.label }}</td>
<td class="num">{{ row.targetPct | number: '1.0-0' }}%</td>
<td class="num">{{ row.targetPct | number: '1.0-1' }}%</td>
<td class="num col-in">{{ row.actualPct | number: '1.1-1' }}%</td>
<td class="num" [class.col-out]="row.variancePct < 0" [class.col-in]="row.variancePct > 0">
{{ row.variancePct > 0 ? '+' : '' }}{{ row.variancePct | number: '1.1-1' }} pp
@@ -71,6 +71,9 @@ interface MixTargetRow {
actualPct: number;
variancePct: number;
status: TargetStatus;
/** True only for the two rows whose target is an actual RBI PSL sub-target (Agriculture 18% /
* Micro Enterprises 7.5% of ANBC) rather than an internal business-plan assumption. */
isRbiMandated: boolean;
}
type ViewId = 'pipeline' | 'new' | 'channel' | 'bc' | 'share' | 'target';
@@ -389,7 +392,8 @@ export class LoanPipelineComponent implements OnInit {
targetPct,
actualPct,
variancePct: actualPct - targetPct,
status: targetStatus(actualPct, targetPct, 3)
status: targetStatus(actualPct, targetPct, 3),
isRbiMandated: p.code === 'KCC' || p.code === 'CC'
};
});
}
@@ -413,7 +417,8 @@ export class LoanPipelineComponent implements OnInit {
targetPct,
actualPct,
variancePct: actualPct - targetPct,
status: targetStatus(actualPct, targetPct, 3)
status: targetStatus(actualPct, targetPct, 3),
isRbiMandated: false
};
});
}
@@ -626,6 +631,7 @@ export class LoanPipelineComponent implements OnInit {
subtitle,
headers: [
'Mix',
'Target Basis',
'Target %',
'Actual %',
'Variance (pp)',
@@ -633,6 +639,7 @@ export class LoanPipelineComponent implements OnInit {
],
widths: [
26,
18,
12,
12,
14,
@@ -641,6 +648,7 @@ export class LoanPipelineComponent implements OnInit {
rows: [
...this.productTargetRows.map((r) => [
`Product — ${r.label}`,
r.isRbiMandated ? 'RBI PSL Sub-Target' : 'Internal Target',
r.targetPct,
Number(r.actualPct.toFixed(1)),
Number(r.variancePct.toFixed(1)),
@@ -648,6 +656,7 @@ export class LoanPipelineComponent implements OnInit {
]),
...this.channelTargetRows.map((r) => [
`Channel — ${r.label}`,
'Internal Target',
r.targetPct,
Number(r.actualPct.toFixed(1)),
Number(r.variancePct.toFixed(1)),