KYC management: stage-wise notices, pagination, RD base chart

This commit is contained in:
2026-10-20 17:23:42 +05:30
parent 3e5cc26e49
commit 357f666421
108 changed files with 15001 additions and 2721 deletions
+48 -5
View File
@@ -61,8 +61,19 @@ export class DashboardComponent implements OnInit {
slrRate: number | null;
crrRequired: string;
slrRequired: string;
/** Surplus, or the shortfall, per ratio. A shortfall is its own amount, not a negative surplus. */
crrGap: { text: string; short: boolean } | null;
slrGap: { text: string; short: boolean } | null;
status: 'ok' | 'short' | 'unknown';
} = { crrRate: null, slrRate: null, crrRequired: '—', slrRequired: '—', status: 'unknown' };
} = {
crrRate: null,
slrRate: null,
crrRequired: '—',
slrRequired: '—',
crrGap: null,
slrGap: null,
status: 'unknown'
};
/** Array of all user activities */
userActivity: string[];
@@ -93,8 +104,27 @@ export class DashboardComponent implements OnInit {
/** Fills the CRR/SLR tile from the return, leaving it dashed where the return cannot compute. */
private loadCrrSlr(): void {
const crore = (v: number | null | undefined): string =>
v === null || v === undefined ? '—' : `₹${Math.round(v / 1e7).toLocaleString('en-IN')} Cr`;
// Rupees, not crore. Rounding to crore on a bank this size collapsed the figures to "₹2 Cr"
// and "₹13 Cr" — two requirements a crore apart both read the same, and a tile a treasury desk
// glances at said nothing it could act on.
// Scaled to the Indian crore/lakh so the tile holds its figure however large the bank grows.
// Plain rupees ran to nineteen characters on a seventy-crore requirement and overflowed the
// card; rounding to crore outright collapsed two requirements a crore apart to the same "₹2
// Cr". This keeps four or five characters and two decimals of precision at any scale.
const money = (v: number | null | undefined): string => {
if (v === null || v === undefined) {
return '—';
}
const abs = Math.abs(v);
const sign = v < 0 ? '-' : '';
if (abs >= 1e7) {
return `${sign}₹${(abs / 1e7).toLocaleString('en-IN', { maximumFractionDigits: 2 })} Cr`;
}
if (abs >= 1e5) {
return `${sign}₹${(abs / 1e5).toLocaleString('en-IN', { maximumFractionDigits: 2 })} L`;
}
return `${sign}₹${Math.round(abs).toLocaleString('en-IN')}`;
};
// The statutory Form 1, which is the return this bank files — twice a month, at the 15th and at
// the month end. The working report's 45-day lag lands mid-fortnight, on a date the bank never
@@ -107,8 +137,8 @@ export class DashboardComponent implements OnInit {
}
this.crrSlr.crrRate = data?.crrPercentage ?? null;
this.crrSlr.slrRate = data?.slrPercentage ?? null;
this.crrSlr.crrRequired = crore(data?.partB?.required?.[latest]);
this.crrSlr.slrRequired = crore(data?.partC?.required?.[latest]);
this.crrSlr.crrRequired = money(data?.partB?.required?.[latest]);
this.crrSlr.slrRequired = money(data?.partC?.required?.[latest]);
// Compliant only where BOTH ratios are met: the tile carries one tag for the pair, and a
// bank short on SLR is not within limits because its cash reserve happens to be ample.
@@ -116,6 +146,19 @@ export class DashboardComponent implements OnInit {
const crrNeed = data?.partB?.required?.[latest];
const slrHeld = data?.partC?.maintained?.[latest];
const slrNeed = data?.partC?.required?.[latest];
// Each ratio's own standing, under its requirement: surplus where it is met, and the
// shortfall — in red, named as one — where it is not. A single "Shortfall" tag said the bank
// was short without saying which ratio or by how much, which is the first thing asked.
const gap = (held: number | null | undefined, need: number | null | undefined) => {
if (held === null || held === undefined || need === null || need === undefined) {
return null;
}
const short = held < need;
return { text: `${short ? 'Short' : 'Surplus'} ${money(Math.abs(held - need))}`, short };
};
this.crrSlr.crrGap = gap(crrHeld, crrNeed);
this.crrSlr.slrGap = gap(slrHeld, slrNeed);
if ([
crrHeld,
crrNeed,