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:
@@ -53,16 +53,45 @@ export const PROVISION_STATUS_LABELS: Record<InvestmentProvisionStatus, string>
|
||||
LOSS: 'Loss (NPI)'
|
||||
};
|
||||
|
||||
/** Board-approved portfolio-policy target — % of the total investment corpus each instrument
|
||||
* type should hold. Compared against actual holdings on the Dashboard so a treasury desk can
|
||||
* see at a glance where the book has drifted from policy. */
|
||||
/**
|
||||
* Portfolio-policy target — % of the total investment corpus each instrument type should hold.
|
||||
* Compared against actual holdings on the Dashboard so a treasury desk can see at a glance where
|
||||
* the book has drifted from policy. Four of these six trace back to real RBI rules for Urban
|
||||
* Co-operative Banks (RBI Master Direction/Circular — Investment Portfolio of Primary (Urban)
|
||||
* Co-operative Banks):
|
||||
* • SLR: UCBs must hold 18% of NDTL in approved securities — predominantly Central/State Govt
|
||||
* securities (and SLR-eligible State Development Loan bonds). A well-run treasury holds a
|
||||
* safety buffer above the bare floor, so Govt Security dominates the book far beyond the 18%
|
||||
* minimum — it isn't itself an upper target, just the reason this bucket is the largest.
|
||||
* • Non-SLR ceiling: a UCB's Non-SLR investments must not exceed 10% of its total deposits as
|
||||
* on 31 March of the previous year — a hard regulatory cap, not a board choice.
|
||||
* • Mutual Funds: RBI permits UCBs to invest only in debt-oriented schemes of SEBI-registered
|
||||
* mutual funds, in strictly limited amounts — equity/balanced schemes are not permitted.
|
||||
* • Shares: RBI does not permit UCBs to invest in equity shares of companies as a general
|
||||
* investment class; any "Share" holding is essentially the (small, largely static) shares a
|
||||
* UCB holds in affiliated co-operative institutions, not a discretionary trading book.
|
||||
* Fixed Deposit (treasury placements with other banks) and Bond (a mix of SLR-eligible SDL and
|
||||
* Non-SLR PSU/FI bonds) have no RBI-mandated percentage of their own — their split here is a
|
||||
* board-approved policy choice within the room RBI's SLR/Non-SLR framework leaves.
|
||||
*/
|
||||
export const TARGET_ALLOCATION_PCT: Record<InstrumentType, number> = {
|
||||
GOVT_SECURITY: 55,
|
||||
BOND: 18,
|
||||
FD: 15,
|
||||
GOVT_SECURITY: 35,
|
||||
NON_SLR: 10,
|
||||
BOND: 20,
|
||||
MUTUAL_FUND: 12,
|
||||
SHARE: 8
|
||||
NON_SLR: 7,
|
||||
MUTUAL_FUND: 3,
|
||||
SHARE: 2
|
||||
};
|
||||
|
||||
/** True for the instrument types whose target traces to an actual RBI rule (SLR floor / Non-SLR
|
||||
* ceiling) rather than a board-only policy choice — see TARGET_ALLOCATION_PCT's doc comment. */
|
||||
export const ALLOCATION_TARGET_IS_RBI_LINKED: Record<InstrumentType, boolean> = {
|
||||
GOVT_SECURITY: true,
|
||||
BOND: false,
|
||||
FD: false,
|
||||
NON_SLR: true,
|
||||
MUTUAL_FUND: true,
|
||||
SHARE: true
|
||||
};
|
||||
|
||||
export interface InvestmentHolding {
|
||||
@@ -90,6 +119,10 @@ export interface InvestmentHolding {
|
||||
interestPosted?: number;
|
||||
/** Cumulative interest actually received in cash to date. */
|
||||
interestReceived?: number;
|
||||
/** DD-MM-YYYY through which interest has already been calculated & posted. accruedInterestBetween()
|
||||
* never re-counts days up to this date, so re-running a Calculation & Post for an overlapping
|
||||
* or repeated period always yields 0 instead of double-posting the same interest. */
|
||||
interestPostedTillDate?: string;
|
||||
}
|
||||
|
||||
export interface InvestmentTxn {
|
||||
@@ -100,6 +133,10 @@ export interface InvestmentTxn {
|
||||
txnDate: string;
|
||||
amount: number;
|
||||
particulars: string;
|
||||
/** GL account this entry was posted/received against — the Interest Income GL for a Post,
|
||||
* the Cash/Bank GL for a Receive. Absent on older/seeded entries that predate GL selection. */
|
||||
glCode?: string;
|
||||
glName?: string;
|
||||
}
|
||||
|
||||
// -------------------- Date / interest helpers --------------------
|
||||
@@ -175,15 +212,6 @@ export function canAccrueInterest(h: InvestmentHolding): boolean {
|
||||
return !isNpi(h);
|
||||
}
|
||||
|
||||
/** Interest still to be calculated & posted this cycle. Always 0 for an NPI holding — posting
|
||||
* an accrual on a non-performing investment would overstate income. */
|
||||
export function interestToPost(h: InvestmentHolding, asOnStr: string): number {
|
||||
if (!canAccrueInterest(h)) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, accruedInterest(h, asOnStr) - (h.interestPosted ?? 0));
|
||||
}
|
||||
|
||||
/** Outstanding "Interest Receivable" GL balance — interest already posted (booked as income)
|
||||
* but not yet actually received in cash. Always 0 for an NPI holding, since nothing is ever
|
||||
* posted as a receivable on one. */
|
||||
@@ -203,6 +231,23 @@ export function suspenseInterest(h: InvestmentHolding, asOnStr: string): number
|
||||
return Math.max(0, accruedInterest(h, asOnStr) - (h.interestReceived ?? 0));
|
||||
}
|
||||
|
||||
/** Interest accrued strictly within [fromStr, toStr], clipped to the holding's own
|
||||
* purchase→maturity window — the basis for a period-based Interest Calculation & Post run
|
||||
* (as opposed to accruedInterest(), which is always measured from purchase date). Returns 0
|
||||
* for a Non-Performing holding, same accrual-basis rule as accruedInterest(). */
|
||||
export function accruedInterestBetween(h: InvestmentHolding, fromStr: string, toStr: string): number {
|
||||
if (!canAccrueInterest(h)) {
|
||||
return 0;
|
||||
}
|
||||
const purchase = parseDDMMYYYY(h.purchaseDate).getTime();
|
||||
const maturity = parseDDMMYYYY(h.maturityDate).getTime();
|
||||
const postedTill = h.interestPostedTillDate ? parseDDMMYYYY(h.interestPostedTillDate).getTime() : purchase;
|
||||
const from = Math.max(parseDDMMYYYY(fromStr).getTime(), purchase, postedTill);
|
||||
const to = Math.min(parseDDMMYYYY(toStr).getTime(), maturity);
|
||||
const days = Math.max(0, (to - from) / (24 * 3600 * 1000));
|
||||
return Math.round(h.faceValue * (h.interestRate / 100) * (days / 365.25));
|
||||
}
|
||||
|
||||
export function formatDDMMYYYY(d: Date): string {
|
||||
return `${String(d.getDate()).padStart(2, '0')}-${String(d.getMonth() + 1).padStart(2, '0')}-${d.getFullYear()}`;
|
||||
}
|
||||
@@ -388,6 +433,68 @@ export const FD_HOLDINGS: InvestmentHolding[] = [
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: '15628',
|
||||
instrumentType: 'FD',
|
||||
scriptCode: 'FD-CUM',
|
||||
scriptName: 'Cumulative Fixed Deposit',
|
||||
party: 'Zilla Sahakari Bank Ltd',
|
||||
subType: 'NON SLR',
|
||||
faceValue: 1800000,
|
||||
purchaseRate: 100,
|
||||
units: 1,
|
||||
purchaseDate: '20-08-2025',
|
||||
maturityDate: '20-08-2026',
|
||||
interestRate: 6.8,
|
||||
interestFreq: 'CUMULATIVE',
|
||||
glCode: '320020',
|
||||
glName: 'FIXED DEPO. CO-OPER. BANK MAIN',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: '15629',
|
||||
instrumentType: 'FD',
|
||||
scriptCode: 'FD-MIP',
|
||||
scriptName: 'Monthly Interest Payout FD',
|
||||
party: 'Vidarbha Cooperative Bank',
|
||||
subType: 'NON SLR',
|
||||
faceValue: 2200000,
|
||||
purchaseRate: 100,
|
||||
units: 1,
|
||||
purchaseDate: '25-09-2025',
|
||||
maturityDate: '25-09-2026',
|
||||
interestRate: 7.15,
|
||||
interestFreq: 'MONTHLY',
|
||||
glCode: '320020',
|
||||
glName: 'FIXED DEPO. CO-OPER. BANK MAIN',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE',
|
||||
interestPosted: 143220,
|
||||
interestReceived: 130800
|
||||
},
|
||||
{
|
||||
certNo: '15630',
|
||||
instrumentType: 'FD',
|
||||
scriptCode: 'FD-CUM',
|
||||
scriptName: 'Cumulative Fixed Deposit',
|
||||
party: 'Konkan Cooperative Bank Ltd',
|
||||
subType: 'NON SLR',
|
||||
faceValue: 900000,
|
||||
purchaseRate: 100,
|
||||
units: 1,
|
||||
purchaseDate: '28-10-2024',
|
||||
maturityDate: '28-10-2026',
|
||||
interestRate: 6.75,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '320020',
|
||||
glName: 'FIXED DEPO. CO-OPER. BANK MAIN',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -515,6 +622,46 @@ export const GOVT_SECURITY_HOLDINGS: InvestmentHolding[] = [
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-002',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'GSEC-2007',
|
||||
instrumentType: 'GOVT_SECURITY',
|
||||
scriptCode: 'TBILL-91D',
|
||||
scriptName: '91 Days Treasury Bill',
|
||||
party: 'RBI - SGL',
|
||||
subType: 'SLR',
|
||||
faceValue: 2000000,
|
||||
purchaseRate: 98.2,
|
||||
units: 20000,
|
||||
purchaseDate: '15-05-2026',
|
||||
maturityDate: '13-08-2026',
|
||||
interestRate: 6.2,
|
||||
interestFreq: 'CUMULATIVE',
|
||||
glCode: '310020',
|
||||
glName: 'INVESTMENT IN GOVT. SECURITIES',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-003',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'GSEC-2008',
|
||||
instrumentType: 'GOVT_SECURITY',
|
||||
scriptCode: 'GJ-SDL-2026',
|
||||
scriptName: '7.85% Gujarat SDL 2026',
|
||||
party: 'RBI - SGL',
|
||||
subType: 'SLR',
|
||||
faceValue: 1800000,
|
||||
purchaseRate: 100.2,
|
||||
units: 18000,
|
||||
purchaseDate: '18-09-2025',
|
||||
maturityDate: '18-09-2026',
|
||||
interestRate: 7.85,
|
||||
interestFreq: 'HALF_YEARLY',
|
||||
glCode: '310020',
|
||||
glName: 'INVESTMENT IN GOVT. SECURITIES',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-001',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -620,6 +767,46 @@ export const NON_SLR_HOLDINGS: InvestmentHolding[] = [
|
||||
status: 'ACTIVE',
|
||||
interestPosted: 89400,
|
||||
interestReceived: 60000
|
||||
},
|
||||
{
|
||||
certNo: 'NSLR-3006',
|
||||
instrumentType: 'NON_SLR',
|
||||
scriptCode: 'IRFC-7.55-2026',
|
||||
scriptName: 'IRFC Bond 7.55% 2026',
|
||||
party: 'Indian Railway Finance Corp',
|
||||
subType: 'NON SLR',
|
||||
faceValue: 1600000,
|
||||
purchaseRate: 100,
|
||||
units: 16000,
|
||||
purchaseDate: '25-08-2025',
|
||||
maturityDate: '25-08-2026',
|
||||
interestRate: 7.55,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310030',
|
||||
glName: 'INVESTMENT IN NON SLR SECURITIES',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-004',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'NSLR-3007',
|
||||
instrumentType: 'NON_SLR',
|
||||
scriptCode: 'NHB-7.3-2026',
|
||||
scriptName: 'NHB Bond 7.30% 2026',
|
||||
party: 'National Housing Bank',
|
||||
subType: 'NON SLR',
|
||||
faceValue: 1000000,
|
||||
purchaseRate: 100,
|
||||
units: 10000,
|
||||
purchaseDate: '10-10-2025',
|
||||
maturityDate: '10-10-2026',
|
||||
interestRate: 7.3,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310030',
|
||||
glName: 'INVESTMENT IN NON SLR SECURITIES',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-003',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -726,6 +913,46 @@ export const BOND_HOLDINGS: InvestmentHolding[] = [
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-005',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'BOND-4006',
|
||||
instrumentType: 'BOND',
|
||||
scriptCode: 'CHOLA-8.35-2026',
|
||||
scriptName: 'Cholamandalam Finance NCD 8.35% 2026',
|
||||
party: 'Cholamandalam Investment & Finance',
|
||||
subType: 'CORPORATE BOND',
|
||||
faceValue: 1400000,
|
||||
purchaseRate: 100,
|
||||
units: 14000,
|
||||
purchaseDate: '15-09-2024',
|
||||
maturityDate: '15-09-2026',
|
||||
interestRate: 8.35,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310040',
|
||||
glName: 'INVESTMENT IN CORPORATE BONDS',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-006',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'BOND-4007',
|
||||
instrumentType: 'BOND',
|
||||
scriptCode: 'PEL-8.4-2026',
|
||||
scriptName: 'Piramal Enterprises NCD 8.40% 2026',
|
||||
party: 'Piramal Enterprises Ltd',
|
||||
subType: 'CORPORATE BOND',
|
||||
faceValue: 900000,
|
||||
purchaseRate: 100,
|
||||
units: 9000,
|
||||
purchaseDate: '28-10-2025',
|
||||
maturityDate: '28-10-2026',
|
||||
interestRate: 8.4,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310040',
|
||||
glName: 'INVESTMENT IN CORPORATE BONDS',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-005',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -829,6 +1056,46 @@ export const MUTUAL_FUND_HOLDINGS: InvestmentHolding[] = [
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'MF-5006',
|
||||
instrumentType: 'MUTUAL_FUND',
|
||||
scriptCode: 'SBI-BPSU',
|
||||
scriptName: 'SBI Banking & PSU Fund',
|
||||
party: 'SBI Mutual Fund',
|
||||
subType: 'DEBT - BANKING & PSU',
|
||||
faceValue: 700000,
|
||||
purchaseRate: 18.6,
|
||||
units: 37634.4,
|
||||
purchaseDate: '10-09-2025',
|
||||
maturityDate: '10-09-2026',
|
||||
interestRate: 7.1,
|
||||
interestFreq: 'CUMULATIVE',
|
||||
glCode: '310050',
|
||||
glName: 'INVESTMENT IN MUTUAL FUNDS',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'MF-5007',
|
||||
instrumentType: 'MUTUAL_FUND',
|
||||
scriptCode: 'ICICI-CB',
|
||||
scriptName: 'ICICI Pru Corporate Bond Fund',
|
||||
party: 'ICICI Prudential Mutual Fund',
|
||||
subType: 'DEBT - CORPORATE BOND',
|
||||
faceValue: 1100000,
|
||||
purchaseRate: 24.8,
|
||||
units: 44354.84,
|
||||
purchaseDate: '14-07-2026',
|
||||
maturityDate: '14-07-2027',
|
||||
interestRate: 7.05,
|
||||
interestFreq: 'CUMULATIVE',
|
||||
glCode: '310050',
|
||||
glName: 'INVESTMENT IN MUTUAL FUNDS',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -932,6 +1199,46 @@ export const SHARE_HOLDINGS: InvestmentHolding[] = [
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'SH-6006',
|
||||
instrumentType: 'SHARE',
|
||||
scriptCode: 'ICICIBANK',
|
||||
scriptName: 'ICICI Bank Ltd',
|
||||
party: 'NSE/BSE',
|
||||
subType: 'EQUITY',
|
||||
faceValue: 420000,
|
||||
purchaseRate: 1050,
|
||||
units: 400,
|
||||
purchaseDate: '05-03-2026',
|
||||
maturityDate: '05-03-2036',
|
||||
interestRate: 2.0,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310060',
|
||||
glName: 'INVESTMENT IN SHARES',
|
||||
crToAc: '310070',
|
||||
brokerCode: 'BRK-007',
|
||||
status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
certNo: 'SH-6007',
|
||||
instrumentType: 'SHARE',
|
||||
scriptCode: 'PDCC',
|
||||
scriptName: 'Pune District Central Coop Bank Shares',
|
||||
party: 'Pune District Central Cooperative Bank',
|
||||
subType: 'CO-OP SHARE',
|
||||
faceValue: 60000,
|
||||
purchaseRate: 50,
|
||||
units: 1200,
|
||||
purchaseDate: '18-07-2025',
|
||||
maturityDate: '18-07-2035',
|
||||
interestRate: 5.0,
|
||||
interestFreq: 'YEARLY',
|
||||
glCode: '310060',
|
||||
glName: 'INVESTMENT IN SHARES',
|
||||
crToAc: '310070',
|
||||
brokerCode: '',
|
||||
status: 'ACTIVE'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1060,6 +1367,43 @@ export const ALL_TXNS: InvestmentTxn[] = [
|
||||
|
||||
// -------------------- Masters --------------------
|
||||
|
||||
export interface InvestmentGlOption {
|
||||
glCode: string;
|
||||
glName: string;
|
||||
}
|
||||
|
||||
/** Interest Income (Cr) GL per instrument type — the GL an Interest Calculation & Post run
|
||||
* credits. Selectable on the FD/Govt./Bond Interest Calculation screens instead of being
|
||||
* hard-wired, so a period's posting can be routed to a different income head when needed. */
|
||||
export const INTEREST_INCOME_GL_MASTER: InvestmentGlOption[] = [
|
||||
{ glCode: '410010', glName: 'INTEREST ON GOVT. SECURITIES' },
|
||||
{ glCode: '410020', glName: 'INTEREST ON NON SLR SECURITIES' },
|
||||
{ glCode: '410030', glName: 'INTEREST ON CORPORATE BONDS' },
|
||||
{ glCode: '410040', glName: 'INTEREST ON FIXED DEPOSITS' },
|
||||
{ glCode: '410050', glName: 'INCOME FROM MUTUAL FUNDS' },
|
||||
{ glCode: '410060', glName: 'DIVIDEND ON SHARES' }
|
||||
];
|
||||
|
||||
/** Default Interest Income GL suggested per instrument type — pre-fills the selector above,
|
||||
* which the user can still override before posting. */
|
||||
export const INTEREST_INCOME_GL_BY_TYPE: Record<InstrumentType, string> = {
|
||||
GOVT_SECURITY: '410010',
|
||||
NON_SLR: '410020',
|
||||
BOND: '410030',
|
||||
FD: '410040',
|
||||
MUTUAL_FUND: '410050',
|
||||
SHARE: '410060'
|
||||
};
|
||||
|
||||
/** Cash/Bank (Dr) GL credited when an Interest Receive entry is booked — the account the cash
|
||||
* actually lands in. Selectable on the Interest Receive screen. */
|
||||
export const CASH_BANK_GL_MASTER: InvestmentGlOption[] = [
|
||||
{ glCode: '100010', glName: 'CASH IN HAND' },
|
||||
{ glCode: '110010', glName: 'RBI CURRENT ACCOUNT' },
|
||||
{ glCode: '110020', glName: 'SBI CURRENT ACCOUNT' },
|
||||
{ glCode: '110030', glName: 'HDFC BANK CURRENT ACCOUNT' }
|
||||
];
|
||||
|
||||
export interface ScriptMaster {
|
||||
scriptCode: string;
|
||||
scriptName: string;
|
||||
|
||||
Reference in New Issue
Block a user