From 12bfc8121959d4b508bf1108f875a403498c7ee5 Mon Sep 17 00:00:00 2001 From: Aditya Date: Wed, 29 Apr 2026 18:59:50 +0530 Subject: [PATCH] feat: add 11 banking compliance & accounting report modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds dialog components and feature modules for RBI/banking compliance, KYC, and accounting reports. Wires them into the shell menu (RBI, General Ledger, CKYC Mgmt, Others, Insurance) and dashboard cards. Dialog components (lazy-loaded MatDialog): - PSL Returns (Form III) — dashboard card + RBI menu - ALM Statement of Structural Liquidity (Form ALM-1) — RBI menu - CIMS / XBRL Returns (OSS-2 / PSA / RAL / RCP-UCB) — RBI menu, shared dialog with form-pill switcher - CIC DQR (CIBIL / Experian / Equifax / CRIF, 72-col TUDF) — Others menu + dashboard card - CKYC Compliance Reports (Re-KYC / Risk Review / CERSAI) — CKYC Mgmt - Accounting Reports (P&L / Balance Sheet / Comparative B/S / GL DR/CR / Misc Expense Ledger) — General Ledger menu Feature modules (Route.withShell): - DEAF (/deaf): Form I, Form II, Annex 4, accounts register - DICGC (/dicgc): Slab Report, SAHAY Depositor List - FIU-IND (/fiu): FINnet 2.0 Statement of Financial Transactions - SFT (/sft): Income Tax Section 285BA SFT - CKYC Reports (/ckyc/ckyc-reports): standalone dashboard UI: - Theme-colored icons on 22 wired menu links across 5 menus — visually distinguishes wired (colored) from placeholder (gray) links. - Backdrop classes per dialog in main.scss. All using dummy data (Bharat Urban Bank Ltd, Karthik Kamath as Principal Officer, dummy customer names) — no real bank or customer references. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/app.module.ts | 8 + .../ckyc-reports/ckyc-reports.component.html | 108 +- .../ckyc-reports/ckyc-reports.component.scss | 9 + .../ckyc-reports/ckyc-reports.component.ts | 246 ++-- src/app/ckyc/ckyc-reports/ckyc-sample-data.ts | 209 ++- src/app/core/shell/shell.component.html | 195 ++- src/app/core/shell/shell.component.ts | 122 ++ .../core/shell/sidenav/sidenav.component.html | 260 ++-- .../core/shell/sidenav/sidenav.component.scss | 19 +- src/app/deaf/deaf-data.ts | 475 ++++++ src/app/deaf/deaf-routing.module.ts | 29 + src/app/deaf/deaf.component.html | 416 ++++++ src/app/deaf/deaf.component.scss | 672 +++++++++ src/app/deaf/deaf.component.ts | 358 +++++ src/app/deaf/deaf.module.ts | 22 + src/app/dicgc/dicgc-data.ts | 485 +++++++ src/app/dicgc/dicgc-routing.module.ts | 29 + src/app/dicgc/dicgc.component.html | 425 ++++++ src/app/dicgc/dicgc.component.scss | 602 ++++++++ src/app/dicgc/dicgc.component.ts | 398 +++++ src/app/dicgc/dicgc.module.ts | 22 + src/app/fiu/fiu-data.ts | 560 +++++++ src/app/fiu/fiu-routing.module.ts | 29 + src/app/fiu/fiu.component.html | 480 ++++++ src/app/fiu/fiu.component.scss | 894 ++++++++++++ src/app/fiu/fiu.component.ts | 547 +++++++ src/app/fiu/fiu.module.ts | 22 + .../accounting-reports-dialog.component.html | 646 +++++++++ .../accounting-reports-dialog.component.scss | 706 +++++++++ .../accounting-reports-dialog.component.ts | 1292 +++++++++++++++++ .../alm-report-dialog.component.html | 478 ++++++ .../alm-report-dialog.component.scss | 772 ++++++++++ .../alm-report-dialog.component.ts | 468 ++++++ .../cic-dqr-dialog.component.html | 135 ++ .../cic-dqr-dialog.component.scss | 528 +++++++ .../cic-dqr-dialog.component.ts | 1032 +++++++++++++ .../cims-xbrl-dialog.component.html | 260 ++++ .../cims-xbrl-dialog.component.scss | 514 +++++++ .../cims-xbrl-dialog.component.ts | 840 +++++++++++ .../ckyc-reports-dialog.component.html | 419 ++++++ .../ckyc-reports-dialog.component.scss | 610 ++++++++ .../ckyc-reports-dialog.component.ts | 849 +++++++++++ .../crar-dialog/crar-dialog.component.html | 53 +- .../crar-dialog/crar-dialog.component.scss | 284 +++- .../crar-dialog/crar-dialog.component.ts | 261 +++- .../crr-slr-dialog.component.html | 101 +- .../crr-slr-dialog.component.scss | 247 +++- .../crr-slr-dialog.component.ts | 486 ++++++- .../home/dashboard/dashboard.component.html | 19 +- .../home/dashboard/dashboard.component.scss | 55 +- src/app/home/dashboard/dashboard.component.ts | 28 + .../psl-report-dialog.component.html | 501 +++++++ .../psl-report-dialog.component.scss | 753 ++++++++++ .../psl-report-dialog.component.ts | 661 +++++++++ src/app/sft/sft-data.ts | 654 +++++++++ src/app/sft/sft-routing.module.ts | 29 + src/app/sft/sft.component.html | 397 +++++ src/app/sft/sft.component.scss | 689 +++++++++ src/app/sft/sft.component.ts | 466 ++++++ src/app/sft/sft.module.ts | 22 + src/main.scss | 36 + 61 files changed, 22207 insertions(+), 725 deletions(-) create mode 100644 src/app/deaf/deaf-data.ts create mode 100644 src/app/deaf/deaf-routing.module.ts create mode 100644 src/app/deaf/deaf.component.html create mode 100644 src/app/deaf/deaf.component.scss create mode 100644 src/app/deaf/deaf.component.ts create mode 100644 src/app/deaf/deaf.module.ts create mode 100644 src/app/dicgc/dicgc-data.ts create mode 100644 src/app/dicgc/dicgc-routing.module.ts create mode 100644 src/app/dicgc/dicgc.component.html create mode 100644 src/app/dicgc/dicgc.component.scss create mode 100644 src/app/dicgc/dicgc.component.ts create mode 100644 src/app/dicgc/dicgc.module.ts create mode 100644 src/app/fiu/fiu-data.ts create mode 100644 src/app/fiu/fiu-routing.module.ts create mode 100644 src/app/fiu/fiu.component.html create mode 100644 src/app/fiu/fiu.component.scss create mode 100644 src/app/fiu/fiu.component.ts create mode 100644 src/app/fiu/fiu.module.ts create mode 100644 src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.html create mode 100644 src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.scss create mode 100644 src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.ts create mode 100644 src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.html create mode 100644 src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.scss create mode 100644 src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.ts create mode 100644 src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.html create mode 100644 src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.scss create mode 100644 src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.ts create mode 100644 src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.html create mode 100644 src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.scss create mode 100644 src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.ts create mode 100644 src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.html create mode 100644 src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.scss create mode 100644 src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.ts create mode 100644 src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.html create mode 100644 src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.scss create mode 100644 src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.ts create mode 100644 src/app/sft/sft-data.ts create mode 100644 src/app/sft/sft-routing.module.ts create mode 100644 src/app/sft/sft.component.html create mode 100644 src/app/sft/sft.component.scss create mode 100644 src/app/sft/sft.component.ts create mode 100644 src/app/sft/sft.module.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 368a2b07e..1a72a4c8f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -55,6 +55,10 @@ import { KycManagementModule } from './kyc-management/kyc-management.module'; import { RbiCircularsModule } from './rbi-circulars/rbi-circulars.module'; import { CfcfrmsModule } from './cfcfrms/cfcfrms.module'; import { BankCalendarModule } from './bank-calendar/bank-calendar.module'; +import { DeafModule } from './deaf/deaf.module'; +import { DicgcModule } from './dicgc/dicgc.module'; +import { FiuModule } from './fiu/fiu.module'; +import { SftModule } from './sft/sft.module'; import { PortalModule } from '@angular/cdk/portal'; /** Main Routing Module */ @@ -146,6 +150,10 @@ export function HttpLoaderFactory(http: HttpClient) { RbiCircularsModule, CfcfrmsModule, BankCalendarModule, + DeafModule, + DicgcModule, + FiuModule, + SftModule, AppRoutingModule, NotFoundComponent, CallbackComponent, diff --git a/src/app/ckyc/ckyc-reports/ckyc-reports.component.html b/src/app/ckyc/ckyc-reports/ckyc-reports.component.html index a28980fd5..5cfa86983 100644 --- a/src/app/ckyc/ckyc-reports/ckyc-reports.component.html +++ b/src/app/ckyc/ckyc-reports/ckyc-reports.component.html @@ -13,16 +13,9 @@

CKYC Reports

-
Customer KYC analytics, file ingestion & search
+
Customer KYC analytics & search
- @if (upload.fileName) { -
- - {{ upload.fileName }} - · {{ upload.customers }} customers · {{ upload.totalRecords }} records -
- } @@ -30,9 +23,6 @@ - @@ -117,96 +107,6 @@ } - - @if (activeTab === 'upload') { -
-
-
- -

Drag & drop your CKYC file here

-

or click to browse — pipe-delimited (.txt) format

- -
- - @if (upload.status !== 'idle') { -
-
- {{ upload.fileName }} - {{ uploadProgress | number: '1.0-0' }}% -
-
-
-
-
- } - - @if (upload.status === 'success') { -
-
- - Parsed successfully -
-
-
-
{{ upload.totalRecords }}
-
Total Records
-
-
-
{{ upload.customers }}
-
Customers (20)
-
-
-
{{ upload.ids }}
-
KYC IDs (30)
-
-
-
{{ upload.documents }}
-
Documents (70)
-
-
-
- } - - @if (upload.status === 'error') { -
-
- - Upload failed -
- @for (err of upload.errors; track err) { -
{{ err }}
- } -
- } -
- - -
-

CKYC File Schema

-
-
10|...File header (1 line)
-
20|seq|...Customer demographic record
-
- 30|seq|type|valueKYC ID — C=PAN, E=Aadhaar, B=Passport, V=Voter, D=DL -
-
70|seq|file|...Document attachment
-
-
-
- } - @if (activeTab === 'customers') {
@@ -247,6 +147,9 @@
{{ filteredCustomers.length }} matching records
+ @@ -331,6 +234,9 @@ > @if (searchResults.length > 0) {
+ diff --git a/src/app/ckyc/ckyc-reports/ckyc-reports.component.scss b/src/app/ckyc/ckyc-reports/ckyc-reports.component.scss index fca649f7e..2ff691d3d 100644 --- a/src/app/ckyc/ckyc-reports/ckyc-reports.component.scss +++ b/src/app/ckyc/ckyc-reports/ckyc-reports.component.scss @@ -326,6 +326,7 @@ /* default style — already grey */ } +.ck-btn-txt, .ck-btn-csv, .ck-btn-xls, .ck-btn-pdf { @@ -333,6 +334,14 @@ border: none; } +.ck-btn-txt { + background: var(--ck-teal); + + &:hover:not(:disabled) { + background: #0a6d5c; + } +} + .ck-btn-csv { background: #16a34a; diff --git a/src/app/ckyc/ckyc-reports/ckyc-reports.component.ts b/src/app/ckyc/ckyc-reports/ckyc-reports.component.ts index 4ea2b917f..a4f4f68df 100644 --- a/src/app/ckyc/ckyc-reports/ckyc-reports.component.ts +++ b/src/app/ckyc/ckyc-reports/ckyc-reports.component.ts @@ -46,18 +46,12 @@ interface CkycCustomer { voterId: string; drivingLicense: string; documents: string[]; -} - -interface UploadResult { - status: 'idle' | 'parsing' | 'success' | 'error'; - fileName: string; - totalRecords: number; - customers: number; - ids: number; - documents: number; - errors: string[]; - startedAt?: number; - finishedAt?: number; + /** Original `20|...` line from the source file — used for verbatim TXT export */ + rawLine: string; + /** Original `30|...` lines linked to this customer */ + rawIdLines: string[]; + /** Original `70|...` lines linked to this customer */ + rawDocLines: string[]; } @Component({ @@ -76,23 +70,13 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { @ViewChild('trendChartCanvas') trendChartCanvas!: ElementRef; /** Active tab */ - activeTab: 'dashboard' | 'upload' | 'customers' | 'search' = 'dashboard'; + activeTab: 'dashboard' | 'customers' | 'search' = 'dashboard'; - /** All customers (parsed + seed) */ + /** All customers (seeded from sample data) */ customers: CkycCustomer[] = []; - /** Upload state */ - upload: UploadResult = { - status: 'idle', - fileName: '', - totalRecords: 0, - customers: 0, - ids: 0, - documents: 0, - errors: [] - }; - uploadProgress = 0; - isDragging = false; + /** Original `10|...` header line from the source file */ + headerLine = ''; /** Customer list filters */ filterName = ''; @@ -113,9 +97,9 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { pageIndex = 0; /** Charts */ - private stateChart: Chart | null = null; - private branchChart: Chart | null = null; - private trendChart: Chart | null = null; + private stateChart: any = null; + private branchChart: any = null; + private trendChart: any = null; /** Toast */ toastMsg = ''; @@ -123,8 +107,17 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { private toastTimer: any; ngOnInit(): void { - // Seed data so the dashboard isn't empty on first load - this.parseAndStore(SAMPLE_CKYC_FILE, 'ckyc-sample-IN4711.txt'); + this.parseAndStore(SAMPLE_CKYC_FILE); + // eslint-disable-next-line no-console + console.log( + '[CKYC] Sample loaded —', + this.customers.length, + 'customers,', + new Set(this.customers.map((c) => c.branch)).size, + 'unique branches,', + new Set(this.customers.map((c) => c.state)).size, + 'unique states' + ); } ngAfterViewInit(): void { @@ -134,7 +127,7 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { // -------------------- Tabs -------------------- - showTab(tab: 'dashboard' | 'upload' | 'customers' | 'search'): void { + showTab(tab: 'dashboard' | 'customers' | 'search'): void { this.activeTab = tab; if (tab === 'dashboard') { setTimeout(() => this.renderCharts(), 0); @@ -241,76 +234,6 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { }); } - // -------------------- Upload -------------------- - - onDragOver(e: DragEvent): void { - e.preventDefault(); - e.stopPropagation(); - this.isDragging = true; - } - onDragLeave(e: DragEvent): void { - e.preventDefault(); - e.stopPropagation(); - this.isDragging = false; - } - onDrop(e: DragEvent): void { - e.preventDefault(); - e.stopPropagation(); - this.isDragging = false; - const files = e.dataTransfer?.files; - if (files && files.length) this.handleFile(files[0]); - } - onFilePicked(e: Event): void { - const input = e.target as HTMLInputElement; - if (input.files && input.files.length) this.handleFile(input.files[0]); - input.value = ''; - } - - private handleFile(file: File): void { - this.upload = { - status: 'parsing', - fileName: file.name, - totalRecords: 0, - customers: 0, - ids: 0, - documents: 0, - errors: [], - startedAt: Date.now() - }; - this.uploadProgress = 0; - - // Animate progress while reading - const progressTimer = setInterval(() => { - if (this.uploadProgress < 90) this.uploadProgress += Math.random() * 12; - if (this.uploadProgress > 90) this.uploadProgress = 90; - }, 120); - - const reader = new FileReader(); - reader.onload = () => { - clearInterval(progressTimer); - try { - this.parseAndStore(reader.result as string, file.name); - this.uploadProgress = 100; - this.upload.status = 'success'; - this.upload.finishedAt = Date.now(); - this.toast(`Parsed ${this.upload.customers} customers from ${file.name}`); - // Re-render dashboard charts with new data - if (this.activeTab === 'dashboard') setTimeout(() => this.renderCharts(), 0); - } catch (err: any) { - this.upload.status = 'error'; - this.upload.errors.push(err?.message || 'Failed to parse file'); - this.uploadProgress = 0; - } - }; - reader.onerror = () => { - clearInterval(progressTimer); - this.upload.status = 'error'; - this.upload.errors.push('Failed to read file'); - this.uploadProgress = 0; - }; - reader.readAsText(file); - } - // -------------------- CKYC Parser -------------------- /** @@ -320,36 +243,27 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { * 30 = KYC ID record (linked by sequence) * 70 = document attachment (linked by sequence) */ - private parseAndStore(text: string, fileName: string): void { + private parseAndStore(text: string): void { const lines = text.split(/\r?\n/).filter((l) => l.trim().length > 0); const newCustomers = new Map(); - let recordCount = 0; - let idCount = 0; - let docCount = 0; + let header = ''; for (const line of lines) { const f = line.split('|'); const type = f[0]; - recordCount++; - if (type === '20') { + if (type === '10') { + header = line; + } else if (type === '20') { const seq = parseInt(f[1], 10); const cust: CkycCustomer = { - id: 'CKYC-' + (f[15] || seq.toString()).padStart(8, '0'), + id: 'CKYC-' + (f[18] || seq.toString()).padStart(8, '0'), seq, - title: (f[16] || '').trim(), - firstName: (f[17] || '').trim(), - middleName: (f[18] || '').trim(), - lastName: (f[19] || '').trim(), + title: (f[19] || '').trim(), + firstName: (f[20] || '').trim(), + middleName: (f[21] || '').trim(), + lastName: (f[22] || '').trim(), fullName: '', fatherName: [ - f[26], - f[27], - f[28] - ] - .map((s) => (s || '').trim()) - .filter(Boolean) - .join(' '), - motherName: [ f[31], f[32], f[33] @@ -357,31 +271,42 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { .map((s) => (s || '').trim()) .filter(Boolean) .join(' '), - gender: ((f[35] || '').trim() as 'M' | 'F') || '', - dob: (f[39] || '').trim(), - occupationCode: (f[38] || '').trim(), - address1: (f[51] || '').trim(), - address2: (f[52] || '').trim(), - address3: (f[53] || '').trim(), - city: (f[54] || '').trim(), - district: (f[55] || '').trim(), - state: (f[56] || '').trim(), - country: (f[57] || '').trim(), - pin: (f[58] || '').trim(), + motherName: [ + f[36], + f[37], + f[38] + ] + .map((s) => (s || '').trim()) + .filter(Boolean) + .join(' '), + gender: ((f[40] || '').trim() as 'M' | 'F') || '', + dob: (f[44] || '').trim(), + occupationCode: (f[43] || '').trim(), + address1: (f[60] || '').trim(), + address2: (f[61] || '').trim(), + address3: (f[62] || '').trim(), + city: (f[63] || '').trim(), + district: (f[64] || '').trim(), + state: (f[65] || '').trim(), + country: (f[66] || '').trim(), + pin: (f[67] || '').trim(), mobile: this.firstNonEmpty([ - f[80], - f[78], - f[83] + f[96], + f[94], + f[99] ]), - uploadDate: (f[88] || '').trim(), - branch: (f[89] || '').trim(), - officer: (f[92] || '').trim(), + uploadDate: (f[101] || '').trim(), + branch: (f[102] || '').trim(), + officer: (f[105] || '').trim(), pan: '', aadhaarMasked: '', passport: '', voterId: '', drivingLicense: '', - documents: [] + documents: [], + rawLine: line, + rawIdLines: [], + rawDocLines: [] }; cust.fullName = [ cust.title, @@ -404,25 +329,21 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { else if (idType === 'B') cust.passport = idValue; else if (idType === 'V') cust.voterId = idValue; else if (idType === 'D') cust.drivingLicense = idValue; + cust.rawIdLines.push(line); } - idCount++; } else if (type === '70') { const seq = parseInt(f[1], 10); const file = (f[2] || '').trim(); const cust = newCustomers.get(seq); - if (cust && file) cust.documents.push(file); - docCount++; + if (cust) { + if (file) cust.documents.push(file); + cust.rawDocLines.push(line); + } } } - const list = Array.from(newCustomers.values()); - // Replace store on each parse so KPIs reflect the latest file - this.customers = list; - this.upload.fileName = fileName; - this.upload.totalRecords = recordCount; - this.upload.customers = list.length; - this.upload.ids = idCount; - this.upload.documents = docCount; + this.customers = Array.from(newCustomers.values()); + this.headerLine = header; this.pageIndex = 0; } @@ -550,6 +471,31 @@ export class CkycReportsComponent implements OnInit, AfterViewInit { // -------------------- Exports -------------------- + /** + * Exports a CKYC `.txt` file in the original pipe-delimited record format + * (10 header + 20/30/70 records per customer). Lines are emitted verbatim + * from the source file, so output is byte-identical to the upload format. + */ + exportTxt(rows?: CkycCustomer[]): void { + const list = rows || this.filteredCustomers; + const out: string[] = []; + if (this.headerLine) out.push(this.headerLine); + for (const c of list) { + out.push(c.rawLine); + for (const id of c.rawIdLines) out.push(id); + for (const doc of c.rawDocLines) out.push(doc); + } + // Trailing newline matches the source file convention + const blob = new Blob([out.join('\n') + '\n'], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `ckyc-report-${new Date().toISOString().slice(0, 10)}.txt`; + a.click(); + URL.revokeObjectURL(url); + this.toast(`Exported ${list.length} customers as TXT`); + } + exportCsv(rows?: CkycCustomer[]): void { this.downloadDelimited(rows || this.filteredCustomers, ',', 'csv'); } diff --git a/src/app/ckyc/ckyc-reports/ckyc-sample-data.ts b/src/app/ckyc/ckyc-reports/ckyc-sample-data.ts index 81a53860d..57cc82bfa 100644 --- a/src/app/ckyc/ckyc-reports/ckyc-sample-data.ts +++ b/src/app/ckyc/ckyc-reports/ckyc-sample-data.ts @@ -7,80 +7,139 @@ */ /** - * Sample CKYC pipe-delimited file (NAG BANK / GANJ BASODA branch). - * Used to seed the reports dashboard so the page is meaningful before any upload. + * Sample CKYC pipe-delimited file used to seed the reports dashboard. + * Spread across multiple banks, branches and states so charts and filters + * have meaningful variety out of the box. */ -export const SAMPLE_CKYC_FILE = `10|24401|IN4711|MP|70|31-08-2020|V1.1||||| -20|1|01|01|||||||||||01|||01|78|MR|BRAHAM |PRAKASH |JAIN |||||||01|MR|MISHRI|LAL|JAIN||MRS|A|B|C||M|02|IN|B-01|10-08-1968|||||||||01|02|||||02|WARD N.11|BRAHAM KIRANA STORE|PATHASHALA ROAD BASODA|BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD N.11|BRAHAM KIRANA STORE|PATHASHALA ROAD BASODA|BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9827612655|||||31-08-2020|GANJ BASODA|31-08-2020|01|GYANENDRA SINGH GAUTAM|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|4|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|1|C|AAQPJ8560J||01|02||||| -70|1|78_Sign.jpeg|09|01|01|||| -70|1|78_Photo.jpeg|02|01|01|||| -20|2|01|01|||||||||||01|||01|2181|MR|DEVI |SHING |KUSHWA H|||||||01|MR|CHATAR SINGH||||MRS|BUDA|BAI|||M|01|IN|B-01|01-01-1973|||||||||01|02|||||02|WARD NO 13|JIWAJIPUR|BASODA|BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 13|JIWAJIPUR|BASODA|BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||8602462283|91|8602462283|||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|2|E|XXXXXXXX6509||01|02||||| -20|3|01|01|||||||||||01|||01|6576|MR|RAM |MOHAN |SINGH |||||||01|MR|MEHTAB||DANGI||MRS|A|B|C||M|01|IN|B-01|01-01-1963|||||||||01|02|||||02|WARD NO 18|PACHMA ROAD|BASODA|BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 18|PACHMA ROAD|BASODA|BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9826628312|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|3|C|AQPPR9351Q||01|02||||| -20|4|01|01|||||||||||01|||01|33502|MRS|RUKSHAR |BEE||||||||01|MR|HASIN|KHAN|||MRS|A|B|C||F|01|IN|O-04|01-01-1991|||||||||01|02|||||02|PACHAMA|GAMAKHAR VIDISHA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|PACHAMA|GAMAKHAR VIDISHA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9009372818|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|4|E|XXXXXXXX3695||01|02||||| -20|5|01|01|||||||||||01|||01|33720|MR|ANKUR||PINGLE|||||||01|MR|SANTOSH KUMAR||||MRS|A|B|C||M|02|IN|B-01|18-06-1982|||||||||01|02|||||02|WARD NO 16|PACHMA ROAD|HOUSE NO 7|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 16|PACHMA ROAD|HOUSE NO 7|GANJ BASODA|VIDISHA|MP|IN|464221|01|||||||||||91|9993262850||||9993262850|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|5|C|BDYPP8413Q||01|02||||| -20|6|01|01|||||||||||01|||01|33847|MR|RAGHUVEER||VISHWAKARMA|||||||01|MR|SHAYAMLAL ||||MRS|A|B|C||M|01|IN|X-01|01-01-1965|||||||||01|02|||||02|WARD NO 6|LADDHA AGENCY VALI GALI BARETH ROAD|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 6|LADDHA AGENCY VALI GALI BARETH ROAD|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||7869252915|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|6|E|XXXXXXXX2861||01|02||||| -20|7|01|01|||||||||||01|||01|34081|MR|ROHIT ||JAIN|||||||01|MR|MAHESH JAIN||||MRS|A|B|C||M|02|IN|O-05|01-01-1988|||||||||01|02|||||02|HOUSE NO 411 BAHADURPUR|DIST ASHOKNAGAR GUNA||BAHADURPUR|ASHOK NAGAR|MP|IN|473443|01||Y|02|HOUSE NO 411 BAHADURPUR|DIST ASHOKNAGAR GUNA||BAHADURPUR|ASHOK NAGAR|MP|IN|473443|01||||||||||||||||9713583272|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|7|E|XXXXXXXX6260||01|02||||| -20|8|01|01|||||||||||01|||01|34177|MR|MAHENDRA||SINGH|||||||01|MR|MOHAN |SINGH|||MRS|A|B|C||M|02|IN|B-01|12-08-1998|||||||||01|02|||||02|WARD 21|SARDA NAGAR RAJODA ROAD|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD 21|SARDA NAGAR RAJODA ROAD|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01|||||||||||||||||||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|8|E|XXXXXXXX3543||01|02||||| -20|9|01|01|||||||||||01|||01|34178|MRS|SHREYA||SHARMA|||||||01|MR|PRAVEEN SHARMA||||MRS|A|B|C||F|02|IN|O-05|27-09-1997|||||||||01|02|||||02|WARD NO 24 HOUSE 5 KRISHNAPURA GALI|HANUMAN CHOWK|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 24 HOUSE 5 KRISHNAPURA GALI|HANUMAN CHOWK|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||8962639307|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|9|C|GZCPS9037N||01|02||||| -20|10|01|01|||||||||||01|||01|34181|MR|SORABH||JAIN|||||||01|MR|SURESH JAIN||||MRS|A|B|C||M|02|IN|B-01|24-11-1986|||||||||01|02|||||02|WARD NO 8 PARASARI NADI KE PUL KE |PASS GANJ BASODA|VIDISHA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 8 PARASARI NADI KE PUL KE |PASS GANJ BASODA|VIDISHA|GANJ BASODA|VIDISHA|MP|IN|464221|01|||||||||||91|9131156740||||9131156740|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|10|C|APUPJ8338R||01|02||||| -20|11|01|01|||||||||||01|||01|34185|MR|DARMENDRA||KUSHWAH|||||||01|MR|GULAB||||MRS|A|B|C||M|02|IN|B-01|01-01-1991|||||||||01|02|||||02|WARD NO 13 CHAK SWROOP NAGAR|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 13 CHAK SWROOP NAGAR|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9907260317|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|11|E|XXXXXXXX7722||01|02||||| -20|12|01|01|||||||||||01|||01|34186|MRS|MANESHA||RAJPUT|||||||01|MR|VEER SINGH||||MRS|A|B|C||F|02|IN|O-04|01-01-1979|||||||||01|02|||||02|WARD NO 21|LOHA MEEL ROAD BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 21|LOHA MEEL ROAD BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||7987077923|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|12|E|XXXXXXXX5640||01|02||||| -20|13|01|01|||||||||||01|||01|34188|MR|YOGESH||DANGI|||||||01|MR|BIHARI SINGH||||MRS|A|B|C||M|02|IN|B-01|01-01-1994|||||||||01|02|||||02|SEMRA POST PABAI|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|SEMRA POST PABAI|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9009571392|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|13|E|XXXXXXXX8016||01|02||||| -20|14|01|01|||||||||||01|||01|34190|MRS|RAJNI||SEN|||||||01|MR|SACHIN SEN||||MRS|A|B|C||F|01|IN|O-04|01-01-1990|||||||||01|02|||||02|WARD NO 16|PANJABI DHARAM SHALA GALI|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 16|PANJABI DHARAM SHALA GALI|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9584363875|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|14|B|WHU2321339||01|02||||| -20|15|01|01|||||||||||01|||01|34191|MR|SACHIN||SEN|||||||01|MR|GANESH RAM||||MRS|A|B|C||M|01|IN|B-01|13-08-1984|||||||||01|02|||||02|WARD NO 16 PANJABI DHARMSHALA |GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 16 PANJABI DHARMSHALA |GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9584363875|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|15|C|HONPS2337E||01|02||||| -20|16|01|01|||||||||||01|||01|34194|MRS|JAMNA||MALVIYA|||||||01|MR|BABLU||MALVIYA||MRS|A|B|C||F|01|IN|O-04|01-01-1990|||||||||01|02|||||02|MIRJAPUR|WARD NO 15|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|MIRJAPUR|WARD NO 15|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9977516599|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|16|E|XXXXXXXX6332||01|02||||| -20|17|01|01|||||||||||01|||01|34195|MR|LAKY||AHIRWAR|||||||01|MR|MOHAN LAL||||MRS|A|B|C||M|02|IN|O-05|01-01-1996|||||||||01|02|||||02|WARD NO 10|GOUSHALA|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 10|GOUSHALA|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9131915301|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|17|E|XXXXXXXX2402||01|02||||| -20|18|01|01|||||||||||01|||01|34197|MRS|PRAVESHIKA ||JAIN|||||||01|MR|RISHI JAIN||||MRS|MAMTA||||F|01|IN|O-04|07-08-1989|||||||||01|02|||||02|WARD NO 21 GANDHI MARG SIRONJ CHORA|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 21 GANDHI MARG SIRONJ CHORA|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9893971160|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|18|E|XXXXXXXX0211||01|02||||| -20|19|01|01|||||||||||01|||01|34201|MR|HEERA LAL||JAIN HUF|||||||01|MR|PURAN CHAND||||MRS|A|B|C||M|01|IN|B-01|01-01-1939|||||||||01|02|||||02|WARD NO 12|JAIN MANDIR GALI NEHRU CHOWK|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 12|JAIN MANDIR GALI NEHRU CHOWK|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||8962445182|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|19|E|XXXXXXXX1723||01|02||||| -20|20|01|01|||||||||||01|||01|34202|MR|RAJESH|KUMAR|JAIN HUF|||||||01|MR|HEERA LAL||||MRS|A|B|C||M|01|IN|B-01|12-05-1962|||||||||01|02|||||02|WARD NO 12 JAIN MANDIR GALI|NEHRU CHOWK BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 12 JAIN MANDIR GALI|NEHRU CHOWK BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9408510555|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|20|E|XXXXXXXX9147||01|02||||| -20|21|01|01|||||||||||01|||01|34203|MR|SHIV|KUMAR|JAIN HUF|||||||01|MR|HEERA LAL||||MRS|A|B|C||M|01|IN|B-01|10-05-1976|||||||||01|02|||||02|WARD NO 12|JAIN MANDIR GALI NEHRU CHOWK|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 12|JAIN MANDIR GALI NEHRU CHOWK|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9425432076|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|21|E|XXXXXXXX9363||01|02||||| -20|22|01|01|||||||||||01|||01|34205|MRS|KIRTI||BAIRAGIE|||||||01|MR|MUKESH||||MRS|A|B|C||F|02|IN|O-05|01-01-2000|||||||||01|02|||||02|WARD NO 7 LODHI COLONY|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 7 LODHI COLONY|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9644750547|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|22|E|XXXXXXXX6035||01|02||||| -20|23|01|01|||||||||||01|||01|34208|MR|SANJIV| |RAIKWAR|||||||01|MR|SUNDAR|LAL|RAIKWAR||MRS|A|B|C||M|01|IN|B-01|03-05-1986|||||||||01|02|||||02|WARD NO. 1|FREE GANJ MACHCHLI MARKET|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO. 1|FREE GANJ MACHCHLI MARKET|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||6260405821|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|23|C|DMJPR6517R||01|02||||| -20|24|01|01|||||||||||01|||01|34209|MRS|NEETU| |RAIKWAR|||||||01|MR|SANJIV| |RAIKWAR||MRS|A|B|C||F|01|IN|O-04|06-10-1990|||||||||01|02|||||02|FREE GANJ|WARD NO.1 MACHLI MARKET|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|FREE GANJ|WARD NO.1 MACHLI MARKET|GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||6260405821|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|24|C|DMHPR0471Q||01|02||||| -20|25|01|01|||||||||||01|||01|34210|MRS|LATA| |BANGAD|||||||01|MR|SATYA|NARAYAN|BANGAD||MRS|A|B|C||F|01|IN|O-04|25-02-1975|||||||||01|02|||||02|WARD NO.12 |JAIN MANDIR GALI|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO.12 |JAIN MANDIR GALI|BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01|||||||||||91|7987163771||||7987163771|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|25|C|AJVPB1142J||01|02||||| -20|26|01|01|||||||||||01|||01|34227|MR|SEETA RAM|SINGH||||||||01|MR|UMRAV SINGH||||MRS|A|B|C||M|01|IN|S-01|01-01-1982|||||||||01|02|||||02|BALRAMPUR|PANCHAYAT SAKRAI POST FUFER NATERAN|VIDISHA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|BALRAMPUR|PANCHAYAT SAKRAI POST FUFER NATERAN|VIDISHA|GANJ BASODA|VIDISHA|MP|IN|464221|01|||||||||||||||||||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|26|B|BBK1122340||01|02||||| -20|27|01|01|||||||||||01|||01|34232|MRS|JANKE|DANGI||||||||01|MR|MEHTAB SINGH||||MRS|A|B|C||F|01|IN|O-04|01-01-1936|||||||||01|02|||||02|MAHAGAUR|VIDISHA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|MAHAGAUR|VIDISHA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9406522221|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|27|E|XXXXXXXX5383||01|02||||| -20|28|01|01|||||||||||01|||01|34245|MR|KESHAV|PRAJAPATI||||||||01|MR|KARAN|SINGH|||MRS|A|B|C||M|01|IN|B-01|01-01-1991|||||||||01|02|||||02|SARASWATI NAGAR SWROOP NAGAR|GIRAJ DHAM MANDIR|LAPATHER BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|SARASWATI NAGAR SWROOP NAGAR|GIRAJ DHAM MANDIR|LAPATHER BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||7440844734|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|28|E|XXXXXXXX0585||01|02||||| -20|29|01|01|||||||||||01|||01|34246|MRS|VANDANA|JAIN||||||||01|MR|NEELESH|JAIN|||MRS|A|B|C||F|01|IN|O-04|10-07-1979|||||||||01|02|||||02|ONLINE CYBER CAFE NAYA BAZAR CITY|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|ONLINE CYBER CAFE NAYA BAZAR CITY|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9406522223|||||31-08-2020|GANJ BASODA|31-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|29|E|XXXXXXXX5997||01|02||||| -20|30|01|01|||||||||||01|||01|34255|MRS|NENASHI|NAMDEV||||||||01|MR|RAM SEVAK|NAMDEV|||MRS|A|B|C||F|02|IN|S-01|27-12-1997|||||||||01|02|||||02|WARD NO 24 KRISHNA PURA|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 24 KRISHNA PURA|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9685768714|||||30-08-2020|GANJ BASODA|30-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|30|E|XXXXXXXX6697||01|02||||| -20|31|01|01|||||||||||01|||01|34258|MRS|RANI|AGRAWAL||||||||01|MR|MILIND|AGRAWAL|||MRS|A|B|C||F|01|IN|O-04|22-12-1996|||||||||01|02|||||02|WARD NO 12 MILL ROAD|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 12 MILL ROAD|BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9752979179|||||30-08-2020|GANJ BASODA|30-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|31|C|CVUPA9262K||01|02||||| -20|32|01|01|||||||||||01|||01|34260|MR|DEVI |PRASHAD||||||||01|MR|PRABHU |LAL|||MRS|A|B|C||M|01|IN|S-01|10-06-1965|||||||||01|02|||||02|WARD NO 7|KAMDHENU COLONY BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 7|KAMDHENU COLONY BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||8889089308|||||29-08-2020|GANJ BASODA|29-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|32|B|BBK0131060||01|02||||| -20|33|01|01|||||||||||01|||01|34275|MR|ANKIT|PINGLE||||||||01|MR|SHIRISH|PINGLE|||MRS|A|B|C||M|01|IN|B-01|28-05-1988|||||||||01|02|||||02|WARD NO 16 PACHMA ROAD|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|WARD NO 16 PACHMA ROAD|GANJ BASODA||GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||7723075050|||||29-08-2020|GANJ BASODA|29-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|33|C|BRAPP2466F||01|02||||| -20|34|01|01|||||||||||01|||01|34277|MR|SANJAY|PINGLE||||||||01|MR|NARAYAN|RAO|||MRS|A|B|C||M|01|IN|B-01|05-09-1957|||||||||01|02|||||02|M N 7|PACHAMA BALI GALI|WARD NO 16 GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||Y|02|M N 7|PACHAMA BALI GALI|WARD NO 16 GANJ BASODA|GANJ BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||9893485050|||||29-08-2020|GANJ BASODA|29-08-2020|01|PRADEEP AHIRWAR|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|12|BASODA NAG BANK|IN4711|1|0||0|4|||||| -30|34|B|MP13247207128||01|02||||| -20|35|01|01|||||||||||01|||01|34885|MR|SHALENDRA|RAGHUWANSHI||||||||01|MR|MALAM|SINGH|||MRS|PUSPA||||M|01|IN|X-01|03-01-1983|||||||||01|02|||||02|GRAM MEVLI PANCHAYAT|MUDRA POSTKAKRAWDA|BASODA|BASODA|VIDISHA|MP|IN|464221|01||Y|02|GRAM MEVLI PANCHAYAT|MUDRA POSTKAKRAWDA|BASODA|BASODA|VIDISHA|MP|IN|464221|01||||||||||||||||6266886495|||||28-08-2020|GANJ BASODA|28-08-2020|01|BRIJESHKUMAR SHRIVASTVA|ASSTT ACCOUCHIEF CASHIERSCO |HEAD OFFICE|3|BASODA NAG BANK|IN4711|1|0||0|3|||||| -30|35|E|XXXXXXXX7677||01|02||||| +export const SAMPLE_CKYC_FILE = `10|24401|IN4711|MH|30|31-08-2020|V1.1||||| +20|1|01|01|||||||||||01|||01|10078|MR|RAVI|KUMAR|SHARMA|||||||01|MR|RAJESH|KUMAR|SHARMA||MRS|SUNITA|DEVI|||M|02|IN|B-01|10-08-1985|||||||||01|02|||||02|FLAT 402, SUNRISE TOWERS|MAROL ANDHERI EAST|MUMBAI|MUMBAI|MUMBAI|MH|IN|400069|01||Y|02|FLAT 402, SUNRISE TOWERS|MAROL ANDHERI EAST|MUMBAI|MUMBAI|MUMBAI|MH|IN|400069|01||||||||||||||||9892011001|||||31-08-2020|MUMBAI MAIN|31-08-2020|01|ANJALI MEHTA|ASSISTANT MANAGER|HEAD OFFICE|4|BHARAT URBAN BANK|IN4711|1|0||0|4|||||| +30|1|C|AAQPS8560J||01|02||||| +70|1|10078_Sign.jpeg|09|01|01|||| +70|1|10078_Photo.jpeg|02|01|01|||| +70|1|10078_AP.jpg|04|01|01|||| +20|2|01|01|||||||||||01|||01|10079|MRS|PRIYA||PATEL|||||||01|MR|HARISH|PATEL|||MRS|MEENA|PATEL|||F|01|IN|O-04|22-03-1990|||||||||01|02|||||02|B-12, SHIVAJI NAGAR|FC ROAD|PUNE|PUNE|PUNE|MH|IN|411005|01||Y|02|B-12, SHIVAJI NAGAR|FC ROAD|PUNE|PUNE|PUNE|MH|IN|411005|01||||||||||||||||9822055002|||||31-08-2020|PUNE FC ROAD|31-08-2020|01|VIKRAM SINGH|BRANCH MANAGER|HEAD OFFICE|4|BHARAT URBAN BANK|IN4711|1|0||0|3|||||| +30|2|E|XXXXXXXX2210||01|02||||| +70|2|10079_Sign.jpeg|09|01|01|||| +70|2|10079_Photo.jpeg|02|01|01|||| +20|3|01|01|||||||||||01|||01|10080|MR|ARJUN||DESHMUKH|||||||01|MR|VIJAY|DESHMUKH|||MRS|REKHA|DESHMUKH|||M|01|IN|B-01|15-01-1978|||||||||01|02|||||02|HOUSE NO 7, DHARAMPETH|RAMDASPETH|NAGPUR|NAGPUR|NAGPUR|MH|IN|440010|01||Y|02|HOUSE NO 7, DHARAMPETH|RAMDASPETH|NAGPUR|NAGPUR|NAGPUR|MH|IN|440010|01||||||||||||||||9823033003|||||30-08-2020|NAGPUR DHARAMPETH|30-08-2020|01|RAHUL JOSHI|ASSISTANT MANAGER|HEAD OFFICE|4|BHARAT URBAN BANK|IN4711|1|0||0|3|||||| +30|3|C|AGTPD1144K||01|02||||| +70|3|10080_Sign.jpeg|09|01|01|||| +70|3|10080_AP.jpg|04|01|01|||| +20|4|01|01|||||||||||01|||01|20101|MR|VIKRAM||REDDY|||||||01|MR|SURESH|REDDY|||MRS|LAKSHMI|REDDY|||M|02|IN|B-01|07-07-1982|||||||||01|02|||||02|NO 23, 4TH CROSS|JAYANAGAR 5TH BLOCK|BENGALURU|BENGALURU|BENGALURU|KA|IN|560041|01||Y|02|NO 23, 4TH CROSS|JAYANAGAR 5TH BLOCK|BENGALURU|BENGALURU|BENGALURU|KA|IN|560041|01||||||||||||||||9845022004|||||31-08-2020|BENGALURU JAYANAGAR|31-08-2020|01|KARTHIK KAMATH|RELATIONSHIP OFFICER|HEAD OFFICE|7|KOTAK COOPERATIVE BANK|IN5712|1|0||0|4|||||| +30|4|C|AAEPR9352L||01|02||||| +70|4|20101_Sign.jpeg|09|01|01|||| +70|4|20101_Photo.jpeg|02|01|01|||| +70|4|20101_AP.jpg|04|01|01|||| +20|5|01|01|||||||||||01|||01|20102|MRS|LAKSHMI||IYER|||||||01|MR|RAMESH|IYER|||MRS|KAMALA|IYER|||F|01|IN|O-04|14-09-1986|||||||||01|02|||||02|NO 88, INDIRA NAGAR|2ND STAGE|BENGALURU|BENGALURU|BENGALURU|KA|IN|560038|01||Y|02|NO 88, INDIRA NAGAR|2ND STAGE|BENGALURU|BENGALURU|BENGALURU|KA|IN|560038|01||||||||||||||||9845066005|||||31-08-2020|BENGALURU INDIRANAGAR|31-08-2020|01|MEERA HEGDE|TELLER|HEAD OFFICE|7|KOTAK COOPERATIVE BANK|IN5712|1|0||0|3|||||| +30|5|E|XXXXXXXX5511||01|02||||| +70|5|20102_Sign.jpeg|09|01|01|||| +70|5|20102_Photo.jpeg|02|01|01|||| +20|6|01|01|||||||||||01|||01|20103|MR|SHIVA||GOWDA|||||||01|MR|MAHESH|GOWDA|||MRS|GAYATRI|GOWDA|||M|01|IN|S-01|19-04-1972|||||||||01|02|||||02|HOUSE 14, KUVEMPU NAGAR|MYSORE PALACE ROAD|MYSURU|MYSURU|MYSURU|KA|IN|570023|01||Y|02|HOUSE 14, KUVEMPU NAGAR|MYSORE PALACE ROAD|MYSURU|MYSURU|MYSURU|KA|IN|570023|01||||||||||||||||9844077006|||||30-08-2020|MYSURU PALACE ROAD|30-08-2020|01|RAHUL SHETTY|FIELD OFFICER|HEAD OFFICE|7|KOTAK COOPERATIVE BANK|IN5712|1|0||0|3|||||| +30|6|B|KA09KL5544||01|02||||| +70|6|20103_Sign.jpeg|09|01|01|||| +70|6|20103_AP.jpg|04|01|01|||| +20|7|01|01|||||||||||01|||01|30201|MR|MOHIT||AGARWAL|||||||01|MR|RAJU|AGARWAL|||MRS|SARITA|AGARWAL|||M|02|IN|B-01|25-12-1989|||||||||01|02|||||02|45 SUBHASH MARG|JOHARI BAZAR|JAIPUR|JAIPUR|JAIPUR|RJ|IN|302002|01||Y|02|45 SUBHASH MARG|JOHARI BAZAR|JAIPUR|JAIPUR|JAIPUR|RJ|IN|302002|01||||||||||||||||9829088007|||||31-08-2020|JAIPUR JOHARI BAZAR|31-08-2020|01|POOJA SHARMA|ASSISTANT MANAGER|HEAD OFFICE|9|MARWAR RURAL BANK|IN6713|1|0||0|4|||||| +30|7|C|ACTPA8821M||01|02||||| +70|7|30201_Sign.jpeg|09|01|01|||| +70|7|30201_Photo.jpeg|02|01|01|||| +70|7|30201_AP.jpg|04|01|01|||| +20|8|01|01|||||||||||01|||01|30202|MRS|SARITA||CHOUDHARY|||||||01|MR|MAHENDRA|CHOUDHARY|||MRS|ANITA|CHOUDHARY|||F|01|IN|O-04|03-06-1981|||||||||01|02|||||02|HOUSE 12, RATANADA|AIRPORT ROAD|JODHPUR|JODHPUR|JODHPUR|RJ|IN|342011|01||Y|02|HOUSE 12, RATANADA|AIRPORT ROAD|JODHPUR|JODHPUR|JODHPUR|RJ|IN|342011|01||||||||||||||||9829099008|||||31-08-2020|JODHPUR RATANADA|31-08-2020|01|VINOD MALI|TELLER|HEAD OFFICE|9|MARWAR RURAL BANK|IN6713|1|0||0|3|||||| +30|8|E|XXXXXXXX6622||01|02||||| +70|8|30202_Sign.jpeg|09|01|01|||| +70|8|30202_AP.jpg|04|01|01|||| +20|9|01|01|||||||||||01|||01|30203|MR|RAVINDRA||SINGH|||||||01|MR|JAGDISH|SINGH|||MRS|KAMLA|SINGH|||M|01|IN|S-01|11-11-1965|||||||||01|02|||||02|GRAM POST DEORI|TEHSIL TONK|TONK|TONK|TONK|RJ|IN|304001|01||Y|02|GRAM POST DEORI|TEHSIL TONK|TONK|TONK|TONK|RJ|IN|304001|01||||||||||||||||9928110009|||||30-08-2020|TONK MAIN|30-08-2020|01|DINESH JAT|FIELD OFFICER|HEAD OFFICE|9|MARWAR RURAL BANK|IN6713|1|0||0|4|||||| +30|9|C|BHJPS3322N||01|02||||| +70|9|30203_Sign.jpeg|09|01|01|||| +70|9|30203_Photo.jpeg|02|01|01|||| +20|10|01|01|||||||||||01|||01|40301|MR|NIRAV||SHAH|||||||01|MR|JAYESH|SHAH|||MRS|RITA|SHAH|||M|02|IN|B-01|18-05-1992|||||||||01|02|||||02|A-401, ATLANTIS APARTMENTS|SATELLITE ROAD|AHMEDABAD|AHMEDABAD|AHMEDABAD|GJ|IN|380015|01||Y|02|A-401, ATLANTIS APARTMENTS|SATELLITE ROAD|AHMEDABAD|AHMEDABAD|AHMEDABAD|GJ|IN|380015|01||||||||||||||||9824012010|||||31-08-2020|AHMEDABAD SATELLITE|31-08-2020|01|HARSH PATEL|RELATIONSHIP OFFICER|HEAD OFFICE|6|GUJARAT NAGRIK BANK|IN7714|1|0||0|4|||||| +30|10|C|AKLPS9933P||01|02||||| +70|10|40301_Sign.jpeg|09|01|01|||| +70|10|40301_Photo.jpeg|02|01|01|||| +70|10|40301_AP.jpg|04|01|01|||| +20|11|01|01|||||||||||01|||01|40302|MRS|HEENA||PATEL|||||||01|MR|KANTI|PATEL|||MRS|JYOTI|PATEL|||F|02|IN|O-05|07-08-1995|||||||||01|02|||||02|34 RING ROAD|UDHNA|SURAT|SURAT|SURAT|GJ|IN|394210|01||Y|02|34 RING ROAD|UDHNA|SURAT|SURAT|SURAT|GJ|IN|394210|01||||||||||||||||9824055011|||||31-08-2020|SURAT UDHNA|31-08-2020|01|TEJAS DESAI|ASSISTANT MANAGER|HEAD OFFICE|6|GUJARAT NAGRIK BANK|IN7714|1|0||0|3|||||| +30|11|E|XXXXXXXX7733||01|02||||| +70|11|40302_Sign.jpeg|09|01|01|||| +70|11|40302_AP.jpg|04|01|01|||| +20|12|01|01|||||||||||01|||01|40303|MR|JAYESH||TRIVEDI|||||||01|MR|BHARAT|TRIVEDI|||MRS|JASHODA|TRIVEDI|||M|01|IN|B-01|29-02-1976|||||||||01|02|||||02|HOUSE 9, AKOTA STADIUM|RACECOURSE ROAD|VADODARA|VADODARA|VADODARA|GJ|IN|390020|01||Y|02|HOUSE 9, AKOTA STADIUM|RACECOURSE ROAD|VADODARA|VADODARA|VADODARA|GJ|IN|390020|01||||||||||||||||9824077012|||||30-08-2020|VADODARA AKOTA|30-08-2020|01|NEHA RAVAL|TELLER|HEAD OFFICE|6|GUJARAT NAGRIK BANK|IN7714|1|0||0|3|||||| +30|12|B|GJ06BB7711||01|02||||| +70|12|40303_Sign.jpeg|09|01|01|||| +70|12|40303_Photo.jpeg|02|01|01|||| +20|13|01|01|||||||||||01|||01|50401|MR|ANIL||MISHRA|||||||01|MR|RAMESH|MISHRA|||MRS|VIDYA|MISHRA|||M|01|IN|S-01|14-10-1970|||||||||01|02|||||02|H-22 GOMTI NAGAR|VIPUL KHAND|LUCKNOW|LUCKNOW|LUCKNOW|UP|IN|226010|01||Y|02|H-22 GOMTI NAGAR|VIPUL KHAND|LUCKNOW|LUCKNOW|LUCKNOW|UP|IN|226010|01||||||||||||||||9415022013|||||31-08-2020|LUCKNOW GOMTI NAGAR|31-08-2020|01|SUMIT TIWARI|BRANCH MANAGER|HEAD OFFICE|11|AVADH NAGRIK BANK|IN8715|1|0||0|3|||||| +30|13|E|XXXXXXXX1144||01|02||||| +70|13|50401_Sign.jpeg|09|01|01|||| +70|13|50401_AP.jpg|04|01|01|||| +20|14|01|01|||||||||||01|||01|50402|MRS|KAVITA||VERMA|||||||01|MR|DEEPAK|VERMA|||MRS|USHA|VERMA|||F|02|IN|O-04|01-04-1988|||||||||01|02|||||02|45 CIVIL LINES|MALL ROAD|KANPUR|KANPUR|KANPUR|UP|IN|208001|01||Y|02|45 CIVIL LINES|MALL ROAD|KANPUR|KANPUR|KANPUR|UP|IN|208001|01||||||||||||||||9415055014|||||31-08-2020|KANPUR CIVIL LINES|31-08-2020|01|RUCHI AGRAWAL|ASSISTANT MANAGER|HEAD OFFICE|11|AVADH NAGRIK BANK|IN8715|1|0||0|4|||||| +30|14|C|AKHPV2244R||01|02||||| +70|14|50402_Sign.jpeg|09|01|01|||| +70|14|50402_Photo.jpeg|02|01|01|||| +70|14|50402_AP.jpg|04|01|01|||| +20|15|01|01|||||||||||01|||01|50403|MR|SUDHIR||PANDEY|||||||01|MR|MUKESH|PANDEY|||MRS|SHANTI|PANDEY|||M|01|IN|B-01|20-07-1983|||||||||01|02|||||02|74 ASSI GHAT ROAD|LANKA|VARANASI|VARANASI|VARANASI|UP|IN|221005|01||Y|02|74 ASSI GHAT ROAD|LANKA|VARANASI|VARANASI|VARANASI|UP|IN|221005|01||||||||||||||||9415077015|||||30-08-2020|VARANASI ASSI GHAT|30-08-2020|01|ASHISH MISHRA|TELLER|HEAD OFFICE|11|AVADH NAGRIK BANK|IN8715|1|0||0|3|||||| +30|15|E|XXXXXXXX0099||01|02||||| +70|15|50403_Sign.jpeg|09|01|01|||| +70|15|50403_AP.jpg|04|01|01|||| +20|16|01|01|||||||||||01|||01|60501|MR|KARTHIK||SUBRAMANIAN|||||||01|MR|RAMASWAMY|SUBRAMANIAN|||MRS|GEETHA|SUBRAMANIAN|||M|02|IN|B-01|05-05-1980|||||||||01|02|||||02|14 KAMAKOTI NAGAR|VALASARAVAKKAM|CHENNAI|CHENNAI|CHENNAI|TN|IN|600087|01||Y|02|14 KAMAKOTI NAGAR|VALASARAVAKKAM|CHENNAI|CHENNAI|CHENNAI|TN|IN|600087|01||||||||||||||||9444022016|||||31-08-2020|CHENNAI VALASARAVAKKAM|31-08-2020|01|VIDHYA RAJAN|RELATIONSHIP OFFICER|HEAD OFFICE|5|TAMIL COMMUNITY BANK|IN9716|1|0||0|4|||||| +30|16|C|AGRPS5566T||01|02||||| +70|16|60501_Sign.jpeg|09|01|01|||| +70|16|60501_Photo.jpeg|02|01|01|||| +70|16|60501_AP.jpg|04|01|01|||| +20|17|01|01|||||||||||01|||01|60502|MRS|SARANYA||KUMAR|||||||01|MR|VENKAT|KUMAR|||MRS|MEENAKSHI|KUMAR|||F|01|IN|O-04|17-12-1991|||||||||01|02|||||02|24 GANDHIPURAM|RS PURAM|COIMBATORE|COIMBATORE|COIMBATORE|TN|IN|641002|01||Y|02|24 GANDHIPURAM|RS PURAM|COIMBATORE|COIMBATORE|COIMBATORE|TN|IN|641002|01||||||||||||||||9444055017|||||31-08-2020|COIMBATORE RS PURAM|31-08-2020|01|MURUGAN VEL|ASSISTANT MANAGER|HEAD OFFICE|5|TAMIL COMMUNITY BANK|IN9716|1|0||0|3|||||| +30|17|E|XXXXXXXX2255||01|02||||| +70|17|60502_Sign.jpeg|09|01|01|||| +70|17|60502_AP.jpg|04|01|01|||| +20|18|01|01|||||||||||01|||01|60503|MR|SELVAM||PILLAI|||||||01|MR|RAMAN|PILLAI|||MRS|SHANTHI|PILLAI|||M|01|IN|S-01|22-08-1969|||||||||01|02|||||02|HOUSE 56, EAST GATE|MEENAKSHI TEMPLE ROAD|MADURAI|MADURAI|MADURAI|TN|IN|625001|01||Y|02|HOUSE 56, EAST GATE|MEENAKSHI TEMPLE ROAD|MADURAI|MADURAI|MADURAI|TN|IN|625001|01||||||||||||||||9444077018|||||30-08-2020|MADURAI EAST|30-08-2020|01|RAMYA KRISHNAN|TELLER|HEAD OFFICE|5|TAMIL COMMUNITY BANK|IN9716|1|0||0|3|||||| +30|18|B|TN14CH4422||01|02||||| +70|18|60503_Sign.jpeg|09|01|01|||| +70|18|60503_Photo.jpeg|02|01|01|||| +20|19|01|01|||||||||||01|||01|70601|MR|SAMEER||KAPOOR|||||||01|MR|RAJINDER|KAPOOR|||MRS|NEELAM|KAPOOR|||M|02|IN|B-01|03-02-1987|||||||||01|02|||||02|B-148 GREATER KAILASH|GK PART 1|NEW DELHI|NEW DELHI|SOUTH DELHI|DL|IN|110048|01||Y|02|B-148 GREATER KAILASH|GK PART 1|NEW DELHI|NEW DELHI|SOUTH DELHI|DL|IN|110048|01||||||||||||||||9811022019|||||31-08-2020|DELHI GREATER KAILASH|31-08-2020|01|ROHAN MALHOTRA|RELATIONSHIP OFFICER|HEAD OFFICE|2|RAJDHANI URBAN BANK|IN1717|1|0||0|4|||||| +30|19|C|AHKPK7788Q||01|02||||| +70|19|70601_Sign.jpeg|09|01|01|||| +70|19|70601_Photo.jpeg|02|01|01|||| +70|19|70601_AP.jpg|04|01|01|||| +20|20|01|01|||||||||||01|||01|70602|MRS|RITU||ARORA|||||||01|MR|VINOD|ARORA|||MRS|SUSHMA|ARORA|||F|01|IN|O-04|26-06-1984|||||||||01|02|||||02|FLAT 92 ROHINI SECTOR 9|MAIN ROAD|NEW DELHI|NEW DELHI|NORTH WEST DELHI|DL|IN|110085|01||Y|02|FLAT 92 ROHINI SECTOR 9|MAIN ROAD|NEW DELHI|NEW DELHI|NORTH WEST DELHI|DL|IN|110085|01||||||||||||||||9811055020|||||31-08-2020|DELHI ROHINI|31-08-2020|01|SAURABH GUPTA|ASSISTANT MANAGER|HEAD OFFICE|2|RAJDHANI URBAN BANK|IN1717|1|0||0|3|||||| +30|20|E|XXXXXXXX8877||01|02||||| +70|20|70602_Sign.jpeg|09|01|01|||| +70|20|70602_AP.jpg|04|01|01|||| +20|21|01|01|||||||||||01|||01|80701|MR|ABHISHEK||BANERJEE|||||||01|MR|SANJOY|BANERJEE|||MRS|MONISHA|BANERJEE|||M|01|IN|B-01|09-09-1979|||||||||01|02|||||02|14/B SALT LAKE SECTOR V|BIDHANNAGAR|KOLKATA|KOLKATA|KOLKATA|WB|IN|700091|01||Y|02|14/B SALT LAKE SECTOR V|BIDHANNAGAR|KOLKATA|KOLKATA|KOLKATA|WB|IN|700091|01||||||||||||||||9831022021|||||31-08-2020|KOLKATA SALT LAKE|31-08-2020|01|DEBANGSHU ROY|BRANCH MANAGER|HEAD OFFICE|10|BENGAL HERITAGE BANK|IN2718|1|0||0|3|||||| +30|21|E|XXXXXXXX3344||01|02||||| +70|21|80701_Sign.jpeg|09|01|01|||| +70|21|80701_AP.jpg|04|01|01|||| +20|22|01|01|||||||||||01|||01|80702|MRS|MAITRYEE||DAS|||||||01|MR|SUBHASH|DAS|||MRS|JAYA|DAS|||F|01|IN|O-04|11-03-1993|||||||||01|02|||||02|82 RABINDRA SARANI|HOWRAH STATION ROAD|HOWRAH|HOWRAH|HOWRAH|WB|IN|711101|01||Y|02|82 RABINDRA SARANI|HOWRAH STATION ROAD|HOWRAH|HOWRAH|HOWRAH|WB|IN|711101|01||||||||||||||||9831055022|||||30-08-2020|HOWRAH STATION|30-08-2020|01|RUPA SEN|TELLER|HEAD OFFICE|10|BENGAL HERITAGE BANK|IN2718|1|0||0|3|||||| +30|22|E|XXXXXXXX9911||01|02||||| +70|22|80702_Sign.jpeg|09|01|01|||| +70|22|80702_AP.jpg|04|01|01|||| +20|23|01|01|||||||||||01|||01|90801|MR|GAURAV||THAKUR|||||||01|MR|VIKRAM|THAKUR|||MRS|SUNITA|THAKUR|||M|02|IN|B-01|13-06-1986|||||||||01|02|||||02|HOUSE 7, NEW SHIMLA SECTOR 4|MIST CHAMBER ROAD|SHIMLA|SHIMLA|SHIMLA|HP|IN|171002|01||Y|02|HOUSE 7, NEW SHIMLA SECTOR 4|MIST CHAMBER ROAD|SHIMLA|SHIMLA|SHIMLA|HP|IN|171002|01||||||||||||||||9418022023|||||31-08-2020|SHIMLA NEW SECTOR 4|31-08-2020|01|ANIL CHAUHAN|ASSISTANT MANAGER|HEAD OFFICE|3|HIMACHAL PARVAT BANK|IN3719|1|0||0|4|||||| +30|23|C|AILPT4455G||01|02||||| +70|23|90801_Sign.jpeg|09|01|01|||| +70|23|90801_Photo.jpeg|02|01|01|||| +70|23|90801_AP.jpg|04|01|01|||| +20|24|01|01|||||||||||01|||01|90802|MRS|AANCHAL||SHARMA|||||||01|MR|RAJESH|SHARMA|||MRS|REKHA|SHARMA|||F|01|IN|O-05|28-11-1996|||||||||01|02|||||02|14 LOWER BAZAR|BUS STAND ROAD|MANDI|MANDI|MANDI|HP|IN|175001|01||Y|02|14 LOWER BAZAR|BUS STAND ROAD|MANDI|MANDI|MANDI|HP|IN|175001|01||||||||||||||||9418055024|||||31-08-2020|MANDI BUS STAND|31-08-2020|01|POOJA NEGI|TELLER|HEAD OFFICE|3|HIMACHAL PARVAT BANK|IN3719|1|0||0|3|||||| +30|24|E|XXXXXXXX5566||01|02||||| +70|24|90802_Sign.jpeg|09|01|01|||| +70|24|90802_AP.jpg|04|01|01|||| +20|25|01|01|||||||||||01|||01|10500|MR|FAIZAN||KHAN|||||||01|MR|MAQBOOL|KHAN|||MRS|RUKHSANA|KHAN|||M|01|IN|S-01|16-04-1990|||||||||01|02|||||02|22 LAL DARWAZA|HAJI ALI ROAD|HYDERABAD|HYDERABAD|HYDERABAD|TS|IN|500002|01||Y|02|22 LAL DARWAZA|HAJI ALI ROAD|HYDERABAD|HYDERABAD|HYDERABAD|TS|IN|500002|01||||||||||||||||9849022025|||||30-08-2020|HYDERABAD CHARMINAR|30-08-2020|01|ZAINAB ALI|RELATIONSHIP OFFICER|HEAD OFFICE|8|DECCAN URBAN BANK|IN4720|1|0||0|3|||||| +30|25|E|XXXXXXXX7788||01|02||||| +70|25|10500_Sign.jpeg|09|01|01|||| +70|25|10500_AP.jpg|04|01|01|||| +20|26|01|01|||||||||||01|||01|10501|MRS|SWATI||RAO|||||||01|MR|MURALI|RAO|||MRS|VANI|RAO|||F|02|IN|S-01|02-10-1988|||||||||01|02|||||02|FLAT 12B SAINIKPURI|ECIL CROSS ROAD|HYDERABAD|HYDERABAD|HYDERABAD|TS|IN|500094|01||Y|02|FLAT 12B SAINIKPURI|ECIL CROSS ROAD|HYDERABAD|HYDERABAD|HYDERABAD|TS|IN|500094|01||||||||||||||||9849055026|||||31-08-2020|HYDERABAD SAINIKPURI|31-08-2020|01|RAVI TEJA|ASSISTANT MANAGER|HEAD OFFICE|8|DECCAN URBAN BANK|IN4720|1|0||0|3|||||| +30|26|E|XXXXXXXX1199||01|02||||| +70|26|10501_Sign.jpeg|09|01|01|||| +70|26|10501_AP.jpg|04|01|01|||| +20|27|01|01|||||||||||01|||01|11500|MR|MANMOHAN|SINGH|GILL|||||||01|MR|HARDEEP|SINGH|GILL||MRS|SIMRAN|KAUR|||M|01|IN|B-01|12-05-1974|||||||||01|02|||||02|H 32 MODEL TOWN|MAJITHA ROAD|AMRITSAR|AMRITSAR|AMRITSAR|PB|IN|143001|01||Y|02|H 32 MODEL TOWN|MAJITHA ROAD|AMRITSAR|AMRITSAR|AMRITSAR|PB|IN|143001|01||||||||||||||||9876022027|||||31-08-2020|AMRITSAR MODEL TOWN|31-08-2020|01|JASPREET KAUR|TELLER|HEAD OFFICE|13|PUNJAB SAHAKARI BANK|IN5721|1|0||0|4|||||| +30|27|C|ANBPG2233J||01|02||||| +70|27|11500_Sign.jpeg|09|01|01|||| +70|27|11500_Photo.jpeg|02|01|01|||| +70|27|11500_AP.jpg|04|01|01|||| +20|28|01|01|||||||||||01|||01|11501|MRS|HARPREET||KAUR|||||||01|MR|MANJIT|SINGH|||MRS|GURMEET|KAUR|||F|01|IN|O-04|24-07-1985|||||||||01|02|||||02|HOUSE 18 SARABHA NAGAR|FEROZEPUR ROAD|LUDHIANA|LUDHIANA|LUDHIANA|PB|IN|141001|01||Y|02|HOUSE 18 SARABHA NAGAR|FEROZEPUR ROAD|LUDHIANA|LUDHIANA|LUDHIANA|PB|IN|141001|01||||||||||||||||9876055028|||||31-08-2020|LUDHIANA SARABHA NAGAR|31-08-2020|01|RANBIR SANDHU|RELATIONSHIP OFFICER|HEAD OFFICE|13|PUNJAB SAHAKARI BANK|IN5721|1|0||0|3|||||| +30|28|E|XXXXXXXX0044||01|02||||| +70|28|11501_Sign.jpeg|09|01|01|||| +70|28|11501_AP.jpg|04|01|01|||| +20|29|01|01|||||||||||01|||01|12500|MR|DEEPAK||TIWARI|||||||01|MR|RAKESH|TIWARI|||MRS|KAMINI|TIWARI|||M|01|IN|B-01|31-01-1981|||||||||01|02|||||02|MIG 102 ARERA COLONY|E-7 EXTENSION|BHOPAL|BHOPAL|BHOPAL|MP|IN|462016|01||Y|02|MIG 102 ARERA COLONY|E-7 EXTENSION|BHOPAL|BHOPAL|BHOPAL|MP|IN|462016|01||||||||||||||||9425022029|||||30-08-2020|BHOPAL ARERA COLONY|30-08-2020|01|RAJESH MALVIYA|ASSISTANT MANAGER|HEAD OFFICE|4|MADHYA PRADESH NAGRIK BANK|IN6722|1|0||0|3|||||| +30|29|E|XXXXXXXX2233||01|02||||| +70|29|12500_Sign.jpeg|09|01|01|||| +70|29|12500_AP.jpg|04|01|01|||| +20|30|01|01|||||||||||01|||01|12501|MRS|SHALINI||JAIN|||||||01|MR|ARVIND|JAIN|||MRS|MEENAKSHI|JAIN|||F|02|IN|O-04|18-09-1991|||||||||01|02|||||02|FLAT 7 VIJAY NAGAR|RING ROAD|INDORE|INDORE|INDORE|MP|IN|452010|01||Y|02|FLAT 7 VIJAY NAGAR|RING ROAD|INDORE|INDORE|INDORE|MP|IN|452010|01||||||||||||||||9425055030|||||31-08-2020|INDORE VIJAY NAGAR|31-08-2020|01|NIDHI AGRAWAL|TELLER|HEAD OFFICE|4|MADHYA PRADESH NAGRIK BANK|IN6722|1|0||0|3|||||| +30|30|C|AKSPJ6677Y||01|02||||| +70|30|12501_Sign.jpeg|09|01|01|||| +70|30|12501_Photo.jpeg|02|01|01|||| +70|30|12501_AP.jpg|04|01|01|||| `; diff --git a/src/app/core/shell/shell.component.html b/src/app/core/shell/shell.component.html index 503471f30..72c6c548f 100644 --- a/src/app/core/shell/shell.component.html +++ b/src/app/core/shell/shell.component.html @@ -27,22 +27,29 @@ @if ((isHandset$ | async) === false) { - + } @@ -56,7 +63,12 @@ Institution - + Tasks @@ -70,11 +82,21 @@ Admin - + Sub Ledger - + General Ledger @@ -121,7 +143,12 @@ Insurance - + RBI @@ -373,14 +400,24 @@ Trial Balance - Profit and Loss Profit and Loss Consolidate Balance Sheet Consolidate Balance Sheet + Comparative Balance + Sheet
Account Reports
@@ -424,6 +468,12 @@ Voucher Register + Misc Expense Ledger
@@ -649,8 +699,8 @@ - +
@@ -671,8 +725,8 @@ -
@@ -745,12 +799,21 @@ - + + + @@ -858,7 +921,9 @@ - + + + + + + + + + @if (activeTab === 'dashboard') { +
+
+
+
+
+
₹{{ totalWithFund | number: '1.2-2' }}
+
Total with Fund
+
{{ totalAccountsWithFund | number }} accounts
+
+
+
+
+
+
₹{{ transferredThisMonth | number: '1.2-2' }}
+
Transferred this Month
+
{{ accountsTransferredThisMonth | number }} accounts
+
+
+
+
+
+
₹{{ refundedThisCycle | number: '1.2-2' }}
+
Claims Paid (refunded)
+
{{ refundAccountsThisCycle | number }} accounts
+
+
+
+
+
+
{{ accountsCrossing10Y | number }}
+
Crossing 10y this Cycle
+
eligible to transfer
+
+
+
+
+
+
{{ pendingClaims | number }}
+
Refund Claims Pending
+
awaiting RBI settlement
+
+
+
+ + +
+
+
+ Form I — Online Submission +
+
Last filed: 15 Jan 2026 · Cycle: {{ formI.monthYear }}
+
Next due: 15 Feb 2026
+ +
+
+
+ Form II — Physical Hardcopy +
+
Last filed: 15 Jan 2026 · Cycle: {{ formII.monthYear }}
+
Next due: 15 Feb 2026
+ +
+
+
+ Annex 4 — Refund Claim +
+
+ Cycle: {{ refund.month }} {{ refund.year }} +
+
+ Pending claims: {{ pendingClaims }} +
+ +
+
+
+ } + + + @if (activeTab === 'form1' || activeTab === 'form2') { + @let _form = activeTab === 'form1' ? formI : formII; +
+
+
+ @if (activeTab === 'form1') { +

Form I — Monthly Return of Unclaimed Deposits Transferred to DEAF

+

Filed online with RBI by 15th of the succeeding month

+ } @else { +

Form II — Position with the Fund (snapshot)

+

Original hardcopy submitted to RBI by 15th of the succeeding month

+ } +
+ +
+ +
+
+ {{ _form.bankName }} +
+
+ {{ _form.bankDeafCode }} +
+
+ {{ _form.remittedThroughSponsor === 'Y' ? 'Yes' : 'No' }} +
+
+ {{ _form.sponsorBankName }} +
+
+ {{ _form.monthYear }} +
+
+ {{ _form.dateOfTransfer }} +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + @for (r of _form.rows; track r.sl) { + + + + + + + + + + + + + } + +
Sr.NoParticularsInterest Bearing Deposits (a)Non-Interest Bearing Deposits (b)Other Credits (c)Total (d) = (a)+(b)+(c)
No. of A/cAmountNo. of A/cAmountNo. of A/cAmountNo. of A/cAmount
{{ r.sl }}{{ r.particulars }}{{ r.ibdAccounts | number }}{{ r.ibdAmount | number: '1.2-2' }}{{ r.nibdAccounts | number }}{{ r.nibdAmount | number: '1.2-2' }}{{ r.ocAccounts | number }}{{ r.ocAmount | number: '1.2-2' }}{{ r.totalAccounts | number }}{{ r.totalAmount | number: '1.2-2' }}
+
+ +
+
+ +
{{ _form.officer }}
+
{{ _form.officerDesignation }}
+
+
+ +
{{ _form.auditor }}
+
{{ _form.auditorAddress }}
+
+
+
+ } + + + @if (activeTab === 'refund') { +
+
+
+

Annex 4 — Monthly Return claiming refund from DEA Fund

+

Submitted by 15th of succeeding month for the cycle to which the claim pertains

+
+ +
+ +
+
+ {{ refund.bankName }} +
+
+ {{ refund.bankDeafCode }} +
+
+ {{ refund.currentAcWithRbi }} +
+
+ {{ refund.sponsorBank }} +
+
+ {{ refund.month }} {{ refund.year }} +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + @for (r of refund.rows; track r.slNo) { + + + + + + + + + + + + + } + +
Sr.NoInterest Bearing DepositsNon-Interest Bearing DepositsOther CreditsGrand Total
PrincipalInterestTotalNo. of A/cAmountNo. of A/cAmountNo. of A/cAmount
{{ r.slNo }}{{ r.ibdPrincipal | number: '1.2-2' }}{{ r.ibdInterest | number: '1.2-2' }}{{ r.ibdTotal | number: '1.2-2' }}{{ r.nibdAccounts | number }}{{ r.nibdAmount | number: '1.2-2' }}{{ r.ocAccounts | number }}{{ r.ocAmount | number: '1.2-2' }}{{ r.grandTotalAccounts | number }}{{ r.grandTotalAmount | number: '1.2-2' }}
+
+ +
+ + Note: No individual customer/depositor wise details should be furnished. In case of refund + of part amount by the depositor whose unclaimed amount/inoperative deposit had been transferred to the Fund, + the bank shall claim the entire amount transferred to the Fund in respect of such depositor, along with + interest payable, if any. +
+ +
+
+ +
{{ refund.firstSignatory }}
+
{{ refund.firstDesignation }}
+
+
+ +
{{ refund.secondSignatory }}
+
{{ refund.secondDesignation }}
+
+
+ +
{{ refund.auditor }}
+
+
+
+ } + + + @if (activeTab === 'eligible') { +
+
+
+

Accounts Register

+

Inoperative / unclaimed accounts crossing 10y, transferred, claimed and refunded

+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + @for (a of filteredAccounts; track a.acNo) { + + + + + + + + + + + + + + + } @empty { + + + + } + +
A/c NoCustomerTypeBranchOpenedLast TxnInactiveBucketPrincipalInterestTotalStatus
{{ a.acNo }}{{ a.customerName }}{{ a.acType }}{{ a.branch }}{{ a.openedDate }}{{ a.lastTxnDate }}{{ a.inactiveYears }}y + {{ a.bucket }} + {{ a.principal | number: '1.2-2' }}{{ a.interest | number: '1.2-2' }}{{ a.total | number: '1.2-2' }} + {{ statusLabel(a.status) }} +
No accounts match the filters.
+
+
+ } + + +
{{ toastMsg }}
+ diff --git a/src/app/deaf/deaf.component.scss b/src/app/deaf/deaf.component.scss new file mode 100644 index 000000000..a4009d3f8 --- /dev/null +++ b/src/app/deaf/deaf.component.scss @@ -0,0 +1,672 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --d-navy: #0b1e3d; + --d-blue: #1d4ed8; + --d-blue-l: #eff6ff; + --d-teal: #0e7e6b; + --d-teal-l: #e0f4f0; + --d-green: #15803d; + --d-green-l: #dcfce7; + --d-amber: #d97706; + --d-amber-l: #fef3c7; + --d-red: #c0392b; + --d-red-l: #fdecea; + --d-purple: #6d28d9; + --d-purple-l: #ede9fe; + --d-g50: #f8fafc; + --d-g100: #f1f5f9; + --d-g200: #e2e8f0; + --d-g300: #cbd5e1; + --d-g500: #64748b; + --d-g700: #334155; + --d-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + background: var(--d-g50); + color: var(--d-g900); + font-size: 14px; + line-height: 1.5; +} + +.deaf-root { + padding: 18px 22px; + max-width: 1400px; + margin: 0 auto; +} + +/* HEADER */ +.deaf-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 16px; +} + +.deaf-head-left { + display: flex; + align-items: center; + gap: 14px; +} + +.deaf-head-ic { + font-size: 28px; + color: var(--d-purple); +} + +.deaf-head-t { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--d-g900); +} + +.deaf-head-s { + font-size: 12px; + color: var(--d-g500); +} + +.deaf-head-meta { + display: flex; + gap: 22px; + + > div { + display: flex; + flex-direction: column; + + .deaf-meta-l { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--d-g500); + } + + .deaf-meta-v { + font-size: 14px; + font-weight: 600; + color: var(--d-g900); + } + } +} + +/* TABS */ +.deaf-tabs { + display: flex; + gap: 4px; + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 10px; + padding: 4px; + margin-bottom: 16px; + overflow-x: auto; +} + +.deaf-tab { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 14px; + border: none; + background: transparent; + font-size: 13px; + font-weight: 500; + color: var(--d-g500); + cursor: pointer; + border-radius: 7px; + white-space: nowrap; + transition: all 0.15s; + font-family: inherit; + + &:hover { + color: var(--d-g900); + } + + &.active { + background: var(--d-purple); + color: #fff; + box-shadow: 0 2px 6px rgb(109 40 217 / 25%); + } +} + +.deaf-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* KPIs */ +.deaf-kpis { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.deaf-kpi { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + align-items: center; + gap: 12px; + box-shadow: 0 1px 3px rgb(0 0 0 / 5%); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 14px rgb(0 0 0 / 10%); + } +} + +.deaf-kpi-ic { + width: 42px; + height: 42px; + border-radius: 11px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.deaf-kpi--blue .deaf-kpi-ic { + background: var(--d-blue-l); + color: var(--d-blue); +} + +.deaf-kpi--teal .deaf-kpi-ic { + background: var(--d-teal-l); + color: var(--d-teal); +} + +.deaf-kpi--green .deaf-kpi-ic { + background: var(--d-green-l); + color: var(--d-green); +} + +.deaf-kpi--amber .deaf-kpi-ic { + background: var(--d-amber-l); + color: var(--d-amber); +} + +.deaf-kpi--red .deaf-kpi-ic { + background: var(--d-red-l); + color: var(--d-red); +} + +.deaf-kpi-n { + font-size: 18px; + font-weight: 700; + color: var(--d-g900); + line-height: 1.1; +} + +.deaf-kpi-l { + font-size: 12px; + color: var(--d-g700); + margin-top: 2px; + font-weight: 500; +} + +.deaf-kpi-sub { + font-size: 11px; + color: var(--d-g500); + margin-top: 1px; +} + +/* COMPLIANCE CARDS */ +.deaf-compliance { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 12px; +} + +.dc-card { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + padding: 14px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.dc-card-h { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--d-g900); + margin-bottom: 8px; +} + +.dc-ok { + color: var(--d-green); +} + +.dc-info { + color: var(--d-blue); +} + +.dc-meta { + font-size: 12px; + color: var(--d-g500); + margin: 2px 0; + + strong { + color: var(--d-g900); + font-weight: 600; + } +} + +.dc-btn { + margin-top: 10px; + padding: 6px 12px; + border: 1px solid var(--d-purple); + background: var(--d-purple-l); + color: var(--d-purple); + border-radius: 8px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + font-family: inherit; + display: inline-flex; + align-items: center; + gap: 6px; + + &:hover { + background: var(--d-purple); + color: #fff; + } +} + +/* FORM HEADER ROW */ +.deaf-form-meta { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 14px; +} + +.deaf-form-title { + h3 { + margin: 0; + font-size: 16px; + font-weight: 700; + color: var(--d-g900); + } + + p { + margin: 2px 0 0; + font-size: 12px; + color: var(--d-g500); + } +} + +.deaf-meta-grid { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + padding: 14px 18px; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px 24px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + > div { + display: flex; + flex-direction: column; + + label { + font-size: 10px; + font-weight: 600; + color: var(--d-g500); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 2px; + } + + span { + font-size: 13px; + color: var(--d-g900); + font-weight: 500; + } + } +} + +/* BUTTONS */ +.deaf-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--d-g300); + background: #fff; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + color: var(--d-g700); + cursor: pointer; + font-family: inherit; + transition: all 0.15s; + + &:hover { + background: var(--d-g50); + border-color: var(--d-g500); + } +} + +.deaf-btn-xls { + background: #16a34a; + border-color: #16a34a; + color: #fff; + + &:hover { + background: #15803d; + border-color: #15803d; + color: #fff; + } +} + +.deaf-btn-g { + align-self: end; +} + +/* INPUTS */ +.deaf-inp { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--d-g200); + border-radius: 8px; + font-size: 13px; + background: #fff; + font-family: inherit; + + &:focus { + outline: none; + border-color: var(--d-purple); + box-shadow: 0 0 0 3px rgb(109 40 217 / 15%); + } +} + +/* FILTERS */ +.deaf-filters { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + padding: 14px; + display: grid; + grid-template-columns: repeat(3, 1fr) auto; + gap: 12px; + align-items: end; +} + +.deaf-filter { + display: flex; + flex-direction: column; + gap: 4px; + + label { + font-size: 11px; + font-weight: 600; + color: var(--d-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } +} + +/* TABLE */ +.deaf-tbl-wrap { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + overflow: auto; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.deaf-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 12px; + font-size: 11px; + font-weight: 600; + color: var(--d-g500); + text-transform: uppercase; + letter-spacing: 0.04em; + background: var(--d-g50); + border-bottom: 1px solid var(--d-g200); + } + + th.hd-1, + tr.hd-1 th { + background: #f0f4ff; + color: var(--d-blue); + } + + td { + padding: 10px 12px; + border-bottom: 1px solid var(--d-g100); + font-size: 13px; + color: var(--d-g700); + vertical-align: top; + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--d-g50); + } + + tr.amt-bold td { + background: #f0fdf4; + font-weight: 600; + color: var(--d-g900); + } + + tr.amt-neg td { + color: var(--d-red); + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; + letter-spacing: 0.02em; +} + +.deaf-empty { + text-align: center; + padding: 30px; + color: var(--d-g500); + font-style: italic; +} + +/* SIGNATURE BLOCKS */ +.deaf-sign { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 14px; +} + +.deaf-sign-block { + background: #fff; + border: 1px solid var(--d-g200); + border-radius: 12px; + padding: 14px 16px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + label { + font-size: 10px; + font-weight: 600; + color: var(--d-g500); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 6px; + display: block; + } + + .sign-name { + font-size: 14px; + font-weight: 600; + color: var(--d-g900); + } + + .sign-desg { + font-size: 12px; + color: var(--d-g500); + margin-top: 2px; + } +} + +/* NOTE / INFO */ +.deaf-note { + background: var(--d-blue-l); + border: 1px solid #bfdbfe; + border-radius: 10px; + padding: 12px 16px; + display: flex; + gap: 10px; + font-size: 12px; + color: #1e3a8a; + line-height: 1.5; + + fa-icon { + font-size: 14px; + flex-shrink: 0; + margin-top: 2px; + } +} + +/* PILLS */ +.bucket-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + + &.bk-ibd { + background: var(--d-blue-l); + color: var(--d-blue); + } + + &.bk-nibd { + background: var(--d-teal-l); + color: var(--d-teal); + } + + &.bk-oc { + background: var(--d-purple-l); + color: var(--d-purple); + } +} + +.status-pill { + display: inline-block; + padding: 2px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + + &.st-eligible-this-month { + background: var(--d-amber-l); + color: var(--d-amber); + } + + &.st-transferred { + background: var(--d-green-l); + color: var(--d-green); + } + + &.st-claim-pending { + background: var(--d-red-l); + color: var(--d-red); + } + + &.st-refunded { + background: var(--d-blue-l); + color: var(--d-blue); + } +} + +/* TOAST */ +.deaf-toast { + position: fixed; + bottom: 32px; + left: 50%; + transform: translateX(-50%) translateY(20px); + background: var(--d-navy); + color: #fff; + padding: 12px 22px; + border-radius: 10px; + font-size: 13px; + font-weight: 500; + box-shadow: 0 6px 20px rgb(0 0 0 / 25%); + opacity: 0; + pointer-events: none; + transition: all 0.25s; + z-index: 1500; + + &.show { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .deaf-kpis { + grid-template-columns: repeat(3, 1fr); + } + + .deaf-compliance { + grid-template-columns: 1fr; + } + + .deaf-meta-grid { + grid-template-columns: 1fr 1fr; + } + + .deaf-sign { + grid-template-columns: 1fr; + } + + .deaf-filters { + grid-template-columns: 1fr 1fr; + } +} + +@media (width <= 720px) { + .deaf-kpis { + grid-template-columns: 1fr 1fr; + } + + .deaf-meta-grid { + grid-template-columns: 1fr; + } +} diff --git a/src/app/deaf/deaf.component.ts b/src/app/deaf/deaf.component.ts new file mode 100644 index 000000000..5c647cf76 --- /dev/null +++ b/src/app/deaf/deaf.component.ts @@ -0,0 +1,358 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { + DeafEligibleAccount, + DeafForm, + DeafFormRow, + ELIGIBLE_ACCOUNTS, + FORM_I, + FORM_II, + REFUND_CLAIMS +} from './deaf-data'; + +@Component({ + selector: 'mifosx-deaf', + templateUrl: './deaf.component.html', + styleUrls: ['./deaf.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FaIconComponent, + FormsModule + ] +}) +export class DeafComponent { + /** Active tab */ + activeTab: 'dashboard' | 'form1' | 'form2' | 'refund' | 'eligible' = 'dashboard'; + + formI: DeafForm = FORM_I; + formII: DeafForm = FORM_II; + refund = REFUND_CLAIMS; + accounts: DeafEligibleAccount[] = ELIGIBLE_ACCOUNTS; + + /** Eligible-list filters */ + filterStatus: '' | DeafEligibleAccount['status'] = ''; + filterBranch = ''; + filterBucket: '' | DeafEligibleAccount['bucket'] = ''; + + /** Toast */ + toastMsg = ''; + toastVisible = false; + private toastTimer: any; + + // -------------------- Tabs -------------------- + showTab(tab: 'dashboard' | 'form1' | 'form2' | 'refund' | 'eligible'): void { + this.activeTab = tab; + } + + // -------------------- KPIs -------------------- + /** Total amount currently with the DEAF Fund (closing balance from Form II row 6) */ + get totalWithFund(): number { + return this.formII.rows[5]?.totalAmount || 0; + } + get totalAccountsWithFund(): number { + return this.formII.rows[5]?.totalAccounts || 0; + } + get transferredThisMonth(): number { + return this.formII.rows[2]?.totalAmount || 0; + } + get accountsTransferredThisMonth(): number { + return this.formII.rows[2]?.totalAccounts || 0; + } + get refundedThisCycle(): number { + return this.refund.rows.reduce((s, r) => s + r.grandTotalAmount, 0); + } + get refundAccountsThisCycle(): number { + return this.refund.rows.reduce((s, r) => s + r.grandTotalAccounts, 0); + } + get accountsCrossing10Y(): number { + return this.accounts.filter((a) => a.status === 'eligible-this-month').length; + } + get pendingClaims(): number { + return this.accounts.filter((a) => a.status === 'claim-pending').length; + } + + // -------------------- Eligible filter -------------------- + get filteredAccounts(): DeafEligibleAccount[] { + const branch = this.filterBranch.trim().toLowerCase(); + return this.accounts.filter((a) => { + if (this.filterStatus && a.status !== this.filterStatus) return false; + if (this.filterBucket && a.bucket !== this.filterBucket) return false; + if (branch && !a.branch.toLowerCase().includes(branch)) return false; + return true; + }); + } + + resetFilters(): void { + this.filterStatus = ''; + this.filterBranch = ''; + this.filterBucket = ''; + } + + statusLabel(s: DeafEligibleAccount['status']): string { + switch (s) { + case 'eligible-this-month': + return 'Eligible'; + case 'transferred': + return 'Transferred'; + case 'claim-pending': + return 'Claim Pending'; + case 'refunded': + return 'Refunded'; + } + } + + // -------------------- Excel export -------------------- + /** Builds Form I / Form II rows as a 2-D array — same layout as the RBI return */ + private buildFormRows(form: DeafForm, label: 'Form I' | 'Form II'): string[][] { + const rows: string[][] = []; + rows.push([ + label, + 'Monthly Return of Unclaimed Deposits Transferred to DEAF Fund' + ]); + rows.push([]); + rows.push([ + 'Name of the Bank', + form.bankName + ]); + rows.push([ + 'Bank DEAF Code allotted by RBI', + form.bankDeafCode + ]); + rows.push([ + 'If remitted through Sponsor Bank', + form.remittedThroughSponsor + ]); + rows.push([ + 'Name of the Sponsor Bank', + form.sponsorBankName + ]); + rows.push([ + 'Month / Year', + form.monthYear + ]); + rows.push([ + 'Date of Transfer to the Fund', + form.dateOfTransfer + ]); + rows.push([]); + rows.push(['Particulars (Amount in Rupees)']); + rows.push([ + 'Sr.No', + 'Particulars', + 'IBD No. of A/c', + 'IBD Amount', + 'NIBD No. of A/c', + 'NIBD Amount', + 'OC No. of A/c', + 'OC Amount', + 'Total No. of A/c', + 'Total Amount' + ]); + for (const r of form.rows) { + rows.push([ + r.sl.toString(), + r.particulars, + r.ibdAccounts.toString(), + r.ibdAmount.toFixed(2), + r.nibdAccounts.toString(), + r.nibdAmount.toFixed(2), + r.ocAccounts.toString(), + r.ocAmount.toFixed(2), + r.totalAccounts.toString(), + r.totalAmount.toFixed(2) + ]); + } + rows.push([]); + rows.push([ + 'Authorised Officer', + form.officer + ]); + rows.push([ + 'Designation', + form.officerDesignation + ]); + rows.push([ + 'Concurrent Auditor', + form.auditor + ]); + rows.push([ + 'Auditor Address', + form.auditorAddress + ]); + return rows; + } + + private buildRefundRows(): string[][] { + const c = this.refund; + const rows: string[][] = []; + rows.push([ + 'Annex 4', + 'Monthly Return claiming refund from DEA Fund' + ]); + rows.push([]); + rows.push([ + 'Name of the Bank', + c.bankName + ]); + rows.push([ + 'Bank DEAF Code allotted by RBI', + c.bankDeafCode + ]); + rows.push([ + 'Current A/c with RBI', + `${c.currentAcWithRbi} via ${c.sponsorBank}` + ]); + rows.push([ + 'Month / Year', + `${c.month} ${c.year}` + ]); + rows.push([]); + rows.push(['Details of claims made during the month (Amount in Rupees)']); + rows.push([ + 'Sr.No', + 'IBD Principal', + 'IBD Interest', + 'IBD Total', + 'NIBD No. of A/c', + 'NIBD Amount', + 'OC No. of A/c', + 'OC Amount', + 'Grand Total No. of A/c', + 'Grand Total Amount' + ]); + for (const r of c.rows) { + rows.push([ + r.slNo.toString(), + r.ibdPrincipal.toFixed(2), + r.ibdInterest.toFixed(2), + r.ibdTotal.toFixed(2), + r.nibdAccounts.toString(), + r.nibdAmount.toFixed(2), + r.ocAccounts.toString(), + r.ocAmount.toFixed(2), + r.grandTotalAccounts.toString(), + r.grandTotalAmount.toFixed(2) + ]); + } + rows.push([]); + rows.push([ + 'Note', + 'No individual customer/depositor wise details should be furnished.' + ]); + rows.push([ + 'First Authorised Signatory', + `${c.firstSignatory} (${c.firstDesignation})` + ]); + rows.push([ + 'Second Authorised Signatory', + `${c.secondSignatory} (${c.secondDesignation})` + ]); + rows.push([ + 'Concurrent Auditor', + c.auditor + ]); + return rows; + } + + private buildEligibleRows(): string[][] { + const rows: string[][] = []; + rows.push(['DEAF — Accounts Register']); + rows.push([]); + rows.push([ + 'A/c No', + 'Customer Name', + 'A/c Type', + 'Branch', + 'Opened', + 'Last Txn', + 'Inactive (yrs)', + 'Bucket', + 'Principal', + 'Interest', + 'Total', + 'Status', + 'Transferred On' + ]); + for (const a of this.filteredAccounts) { + rows.push([ + a.acNo, + a.customerName, + a.acType, + a.branch, + a.openedDate, + a.lastTxnDate, + a.inactiveYears.toString(), + a.bucket, + a.principal.toFixed(2), + a.interest.toFixed(2), + a.total.toFixed(2), + this.statusLabel(a.status), + a.transferredOn || '' + ]); + } + return rows; + } + + exportFormI(): void { + this.saveTsv(this.buildFormRows(this.formI, 'Form I'), `DEAF-FormI-${this.fileTag(this.formI.monthYear)}.xls`); + this.toast('Form I downloaded'); + } + exportFormII(): void { + this.saveTsv(this.buildFormRows(this.formII, 'Form II'), `DEAF-FormII-${this.fileTag(this.formII.monthYear)}.xls`); + this.toast('Form II downloaded'); + } + exportRefund(): void { + this.saveTsv(this.buildRefundRows(), `DEAF-Annex4-${this.refund.month}-${this.refund.year}.xls`); + this.toast('Annex 4 (Refund Claim) downloaded'); + } + exportEligible(): void { + this.saveTsv(this.buildEligibleRows(), `DEAF-Accounts-${this.dateStamp()}.xls`); + this.toast(`${this.filteredAccounts.length} accounts exported`); + } + + private saveTsv(rows: string[][], filename: string): void { + const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + const blob = new Blob([tsv], { type: 'application/vnd.ms-excel;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + private fileTag(monthYear: string): string { + return monthYear.replace('/', '-'); + } + + private dateStamp(): string { + const d = new Date(); + const dd = String(d.getDate()).padStart(2, '0'); + const mm = String(d.getMonth() + 1).padStart(2, '0'); + return `${dd}${mm}${d.getFullYear()}`; + } + + /** Coerce a row's raw amounts for display — handles negatives in Form I row 5 */ + amountClass(r: DeafFormRow): string { + if (r.sl === 5 && r.totalAmount < 0) return 'amt-neg'; + if (r.sl === 6) return 'amt-bold'; + return ''; + } + + toast(msg: string): void { + this.toastMsg = msg; + this.toastVisible = true; + if (this.toastTimer) clearTimeout(this.toastTimer); + this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); + } +} diff --git a/src/app/deaf/deaf.module.ts b/src/app/deaf/deaf.module.ts new file mode 100644 index 000000000..258f7f0e6 --- /dev/null +++ b/src/app/deaf/deaf.module.ts @@ -0,0 +1,22 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { DeafRoutingModule } from './deaf-routing.module'; +import { DeafComponent } from './deaf.component'; + +@NgModule({ + imports: [ + SharedModule, + DeafRoutingModule, + DeafComponent + ] +}) +export class DeafModule {} diff --git a/src/app/dicgc/dicgc-data.ts b/src/app/dicgc/dicgc-data.ts new file mode 100644 index 000000000..78566e6a9 --- /dev/null +++ b/src/app/dicgc/dicgc-data.ts @@ -0,0 +1,485 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/** + * DICGC (Deposit Insurance and Credit Guarantee Corporation) sample data. + * Schema reproduces the actual report formats but bank/customer names are dummy. + * + * Two reports: + * Slab-wise A/C Report — accounts grouped by deposit-amount slabs + * (≤1L, 1L-2L, 2L-3L, >3L) for both deposits and loans + * SAHAY Depositor List — customer/account-wise CSV input template DICGC consumes + * when paying out claims after a bank failure + */ + +/** Single product row in the slab-wise table */ +export interface SlabRow { + sl: number; + description: string; + upto1Lakh: { amount: number; accounts: number }; + oneTo2Lakh: { amount: number; accounts: number }; + twoTo3Lakh: { amount: number; accounts: number }; + above3Lakh: { amount: number; accounts: number }; + total: { amount: number; accounts: number }; +} + +/** SAHAY depositor input record (19 columns) */ +export interface SahayDepositor { + inputSerialNumber: number; + customerIdUcic: string; + accountNumber: string; + typeOfAccount: string; + natureOfAccount: string; + numberOfHolders: number; + nameFirst: string; + nameSecond: string; + nameThird: string; + dateOfBirthOrIncorporation: string; + amountOfDeposit: number; + insuredAmount: number; + loanAgainstDeposit: 'LAD' | 'LAFD' | ''; + panNumber: string; + aadhaarNumber: string; + otherKycType: string; + otherKycNumber: string; + mobileNumber: string; + typeOfDeposit: string; + lastCustomerInducedTxnDate: string; +} + +export const DICGC_BANK = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + reportAsOn: '29-01-2026', + printDate: '29-01-2026 04:19 PM', + reportRefNo: '1290/2', + userCode: 'JTU', + insuranceLimit: 500000, // ₹5 lakh per depositor + premiumRatePer100: 0.12, // 12 paise per ₹100 of deposits + registrationNo: 'REG-CO-1290' +}; + +/** ── DEPOSITS — Section 1 (Credit Balance) ── */ +export const DEPOSIT_SLABS: SlabRow[] = [ + { + sl: 1, + description: 'CURRENT DEPOSIT INDIVIDUAL A/C', + upto1Lakh: { amount: 4161663.56, accounts: 231 }, + oneTo2Lakh: { amount: 1104137.57, accounts: 7 }, + twoTo3Lakh: { amount: 953186.92, accounts: 4 }, + above3Lakh: { amount: 8451113.76, accounts: 12 }, + total: { amount: 14670101.81, accounts: 254 } + }, + { + sl: 2, + description: 'FIX DEPOSIT DOUBLE A/C', + upto1Lakh: { amount: 38612382.0, accounts: 824 }, + oneTo2Lakh: { amount: 50661835.0, accounts: 371 }, + twoTo3Lakh: { amount: 41999156.0, accounts: 175 }, + above3Lakh: { amount: 204955057.0, accounts: 324 }, + total: { amount: 336228430.0, accounts: 1694 } + }, + { + sl: 3, + description: 'FIXED DEPOSIT SINGLE A/C', + upto1Lakh: { amount: 5488157.0, accounts: 694 }, + oneTo2Lakh: { amount: 689874.0, accounts: 6 }, + twoTo3Lakh: { amount: 250000.0, accounts: 1 }, + above3Lakh: { amount: 1018485.0, accounts: 2 }, + total: { amount: 7446516.0, accounts: 703 } + }, + { + sl: 4, + description: 'FIXED DEPOSIT MONTHLY', + upto1Lakh: { amount: 445000.0, accounts: 6 }, + oneTo2Lakh: { amount: 400000.0, accounts: 2 }, + twoTo3Lakh: { amount: 1400000.0, accounts: 5 }, + above3Lakh: { amount: 14500000.0, accounts: 14 }, + total: { amount: 16745000.0, accounts: 27 } + }, + { + sl: 5, + description: 'FIXED DEPOSIT QUARTERLY', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 3250000.0, accounts: 3 }, + total: { amount: 3250000.0, accounts: 3 } + }, + { + sl: 6, + description: 'GENERAL LEDGER', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 7, + description: 'LINKING SHARE', + upto1Lakh: { amount: 11600908.0, accounts: 937 }, + oneTo2Lakh: { amount: 8580300.0, accounts: 61 }, + twoTo3Lakh: { amount: 3290000.0, accounts: 13 }, + above3Lakh: { amount: 2876800.0, accounts: 7 }, + total: { amount: 26348008.0, accounts: 1018 } + }, + { + sl: 8, + description: 'MANGAL PARINAY A/C', + upto1Lakh: { amount: 197496.0, accounts: 2 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 197496.0, accounts: 2 } + }, + { + sl: 9, + description: 'RECURRING DEPOSIT', + upto1Lakh: { amount: 558352.0, accounts: 22 }, + oneTo2Lakh: { amount: 206756.0, accounts: 2 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 765108.0, accounts: 24 } + }, + { + sl: 10, + description: 'SUNDRY CREDITORS A/C', + upto1Lakh: { amount: 46000.0, accounts: 2 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 607913.3, accounts: 1 }, + total: { amount: 653913.3, accounts: 3 } + }, + { + sl: 11, + description: 'SAVINGS DEPOSIT INDIVIDUAL A/C', + upto1Lakh: { amount: 37172591.62, accounts: 3954 }, + oneTo2Lakh: { amount: 28097261.58, accounts: 199 }, + twoTo3Lakh: { amount: 19583645.69, accounts: 84 }, + above3Lakh: { amount: 108860739.06, accounts: 191 }, + total: { amount: 193714237.95, accounts: 4428 } + } +]; + +/** ── LOANS — Section A (Credit Balance Loan A/c) ── */ +export const LOAN_SLABS: SlabRow[] = [ + { + sl: 1, + description: 'CC AGAINST DEPOSIT', + upto1Lakh: { amount: 10214.45, accounts: 4 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 10214.45, accounts: 4 } + }, + { + sl: 2, + description: 'CASH CREDIT LOAN A/C', + upto1Lakh: { amount: 37223.99, accounts: 5 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 257039.67, accounts: 1 }, + above3Lakh: { amount: 3869523.24, accounts: 3 }, + total: { amount: 4163786.9, accounts: 9 } + }, + { + sl: 3, + description: 'OVER DRAFT BMC', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 4, + description: 'LOAN AGAINST GODOWN PLEDGE', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 5, + description: 'KISHAN CREDIT CARD', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 6, + description: 'LOAN AGAINST SECURITIES', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 7, + description: 'LOAN AGAINST DEPOSIT', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 8, + description: 'LOAN AGAINST GOLD', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 9, + description: 'LOAN AGAINST WAREHOUSE', + upto1Lakh: { amount: 13265.0, accounts: 1 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 13265.0, accounts: 1 } + }, + { + sl: 10, + description: 'SUNDRY DEBTORS', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 11, + description: 'ADVANCE TO EMPLOYEES', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 12, + description: 'LONG TERM LOAN A/C', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + }, + { + sl: 13, + description: 'PROPERTY MORTGAGE LIMIT', + upto1Lakh: { amount: 37564.07, accounts: 2 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 37564.07, accounts: 2 } + }, + { + sl: 14, + description: 'MEDIUM TERM LOAN', + upto1Lakh: { amount: 0, accounts: 0 }, + oneTo2Lakh: { amount: 0, accounts: 0 }, + twoTo3Lakh: { amount: 0, accounts: 0 }, + above3Lakh: { amount: 0, accounts: 0 }, + total: { amount: 0, accounts: 0 } + } +]; + +/** Section D: Final summary */ +export const SUMMARY_D = [ + { sl: 1, description: 'CREDIT BALANCE (1 to 16)', amount: 600018811.06, accounts: 8156 }, + { sl: 2, description: 'CREDIT BALANCE (LOAN A/C)', amount: 4224830.42, accounts: 16 }, + { sl: 3, description: 'OTHER LIABILITY', amount: 3436255.94, accounts: 18 } +]; + +/** SAHAY depositor list — sample customers (same dummy set used in CKYC + DEAF) */ +export const SAHAY_DEPOSITORS: SahayDepositor[] = [ + { + inputSerialNumber: 1, + customerIdUcic: 'UCIC0010078', + accountNumber: '110001302000312', + typeOfAccount: 'SAVINGS', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'RAVI KUMAR SHARMA', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '10-08-1985', + amountOfDeposit: 425000, + insuredAmount: 425000, + loanAgainstDeposit: '', + panNumber: 'AAQPS8560J', + aadhaarNumber: 'XXXXXXXX1234', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9892011001', + typeOfDeposit: 'SB', + lastCustomerInducedTxnDate: '05-04-2026' + }, + { + inputSerialNumber: 2, + customerIdUcic: 'UCIC0010079', + accountNumber: '110001302000418', + typeOfAccount: 'FIXED', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'PRIYA PATEL', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '22-03-1990', + amountOfDeposit: 320000, + insuredAmount: 320000, + loanAgainstDeposit: '', + panNumber: 'AKLPS9933P', + aadhaarNumber: 'XXXXXXXX2210', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9822055002', + typeOfDeposit: 'FD', + lastCustomerInducedTxnDate: '15-03-2026' + }, + { + inputSerialNumber: 3, + customerIdUcic: 'UCIC0010080', + accountNumber: '110001303000522', + typeOfAccount: 'RECURRING', + natureOfAccount: 'JOINT', + numberOfHolders: 2, + nameFirst: 'ARJUN DESHMUKH', + nameSecond: 'REKHA DESHMUKH', + nameThird: '', + dateOfBirthOrIncorporation: '15-01-1978', + amountOfDeposit: 148500, + insuredAmount: 148500, + loanAgainstDeposit: '', + panNumber: 'AGTPD1144K', + aadhaarNumber: 'XXXXXXXX5544', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9823033003', + typeOfDeposit: 'RD', + lastCustomerInducedTxnDate: '10-04-2026' + }, + { + inputSerialNumber: 4, + customerIdUcic: 'UCIC0020101', + accountNumber: '110001302000711', + typeOfAccount: 'FIXED', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'VIKRAM REDDY', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '07-07-1982', + amountOfDeposit: 600000, + insuredAmount: 500000, + loanAgainstDeposit: 'LAFD', + panNumber: 'AAEPR9352L', + aadhaarNumber: 'XXXXXXXX6622', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9845022004', + typeOfDeposit: 'FD', + lastCustomerInducedTxnDate: '20-02-2026' + }, + { + inputSerialNumber: 5, + customerIdUcic: 'UCIC0020102', + accountNumber: '110001301000814', + typeOfAccount: 'SAVINGS', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'LAKSHMI IYER', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '14-09-1986', + amountOfDeposit: 280000, + insuredAmount: 280000, + loanAgainstDeposit: '', + panNumber: '', + aadhaarNumber: 'XXXXXXXX5511', + otherKycType: 'VOTER_ID', + otherKycNumber: 'KA09KL5544', + mobileNumber: '9845066005', + typeOfDeposit: 'SB', + lastCustomerInducedTxnDate: '01-04-2026' + }, + { + inputSerialNumber: 6, + customerIdUcic: 'UCIC0040301', + accountNumber: '110001302001012', + typeOfAccount: 'FIXED', + natureOfAccount: 'JOINT', + numberOfHolders: 2, + nameFirst: 'NIRAV SHAH', + nameSecond: 'RITA SHAH', + nameThird: '', + dateOfBirthOrIncorporation: '18-05-1992', + amountOfDeposit: 750000, + insuredAmount: 500000, + loanAgainstDeposit: '', + panNumber: 'AHKPS5544Z', + aadhaarNumber: 'XXXXXXXX9933', + otherKycType: 'PASSPORT', + otherKycNumber: 'M5544321', + mobileNumber: '9824012010', + typeOfDeposit: 'FD', + lastCustomerInducedTxnDate: '20-12-2025' + }, + { + inputSerialNumber: 7, + customerIdUcic: 'UCIC0050401', + accountNumber: '110001301001145', + typeOfAccount: 'SAVINGS', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'KAVITA VERMA', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '01-04-1988', + amountOfDeposit: 95000, + insuredAmount: 95000, + loanAgainstDeposit: '', + panNumber: 'AKHPV2244R', + aadhaarNumber: 'XXXXXXXX1144', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9415055014', + typeOfDeposit: 'SB', + lastCustomerInducedTxnDate: '15-03-2026' + }, + { + inputSerialNumber: 8, + customerIdUcic: 'UCIC0070601', + accountNumber: '110001304001458', + typeOfAccount: 'TERM_LOAN', + natureOfAccount: 'INDIVIDUAL', + numberOfHolders: 1, + nameFirst: 'SAMEER KAPOOR', + nameSecond: '', + nameThird: '', + dateOfBirthOrIncorporation: '03-02-1987', + amountOfDeposit: 1200000, + insuredAmount: 500000, + loanAgainstDeposit: 'LAD', + panNumber: 'AHKPK7788Q', + aadhaarNumber: 'XXXXXXXX8877', + otherKycType: '', + otherKycNumber: '', + mobileNumber: '9811022019', + typeOfDeposit: 'TL', + lastCustomerInducedTxnDate: '12-04-2026' + } +]; diff --git a/src/app/dicgc/dicgc-routing.module.ts b/src/app/dicgc/dicgc-routing.module.ts new file mode 100644 index 000000000..3f07fa540 --- /dev/null +++ b/src/app/dicgc/dicgc-routing.module.ts @@ -0,0 +1,29 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { Route } from '../core/route/route.service'; +import { DicgcComponent } from './dicgc.component'; + +const routes: Routes = [ + Route.withShell([ + { + path: 'dicgc', + component: DicgcComponent, + data: { title: 'DICGC', breadcrumb: 'DICGC' } + } + ]) +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DicgcRoutingModule {} diff --git a/src/app/dicgc/dicgc.component.html b/src/app/dicgc/dicgc.component.html new file mode 100644 index 000000000..9b716e928 --- /dev/null +++ b/src/app/dicgc/dicgc.component.html @@ -0,0 +1,425 @@ + + +
+ +
+
+ +
+

Deposit Insurance and Credit Guarantee (DICGC)

+
+ {{ bank.name }} · {{ bank.branch }} · As on {{ bank.reportAsOn }} +
+
+
+
+
+ Insurance Limit₹{{ bank.insuranceLimit | number }} +
+
+ Premium Rate{{ bank.premiumRatePer100 }}% p.a. +
+
+ Reg. No.{{ bank.registrationNo }} +
+
+
+ + +
+ + + +
+ + + @if (activeTab === 'dashboard') { +
+
+
+
+
+
₹{{ depositTotal.amount | number: '1.0-0' }}
+
Total Deposits
+
{{ depositTotal.accounts | number }} accounts
+
+
+
+
+
+
{{ fullyInsuredAccounts | number }}
+
Fully Insured A/cs
+
balance ≤ ₹{{ bank.insuranceLimit | number }}
+
+
+
+
+
+
{{ above3LakhAccounts | number }}
+
Above ₹3L A/cs
+
partial / above-cap exposure
+
+
+
+
+
+
₹{{ loanTotal.amount | number: '1.0-0' }}
+
Loans Against Deposits
+
{{ loanTotal.accounts | number }} accounts
+
+
+
+
+
+
₹{{ premiumDue | number: '1.0-0' }}
+
DICGC Premium (Half-Yearly)
+
{{ bank.premiumRatePer100 }}% of deposits
+
+
+
+ + +
+
+
+ A/C-Wise Slab Report +
+
+ As on: {{ bank.reportAsOn }} +
+
+ Total deposits: ₹{{ depositTotal.amount | number: '1.0-0' }} across + {{ depositTotal.accounts | number }} A/cs +
+ +
+
+
SAHAY Depositor List
+
Format: DICGC SAHAY 2.0
+
+ Total entries: {{ depositors.length }} +
+ +
+
+
+ Premium Schedule +
+
+ Half-yearly assessment as of {{ bank.reportAsOn }} +
+
Next due: 31 May 2026
+
+
+
+ } + + + @if (activeTab === 'slab') { +
+
+
+

A/C-Wise Deposit Insurance & Credit Guarantee Report

+

+ {{ bank.name }} · {{ bank.branch }} · As On {{ bank.reportAsOn }} · Ref {{ bank.reportRefNo }} +

+
+ +
+ + +
+
CREDIT BALANCE — DEPOSITS
+ + + + + + + + + + + + + + + + + + + + + + + + + + @for (r of deposits; track r.sl) { + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + +
Sr.NoDescriptionUp to ₹1,00,000₹1,00,001 – 2,00,000₹2,00,001 – 3,00,000Above ₹3,00,000Total
Amount#A/cAmount#A/cAmount#A/cAmount#A/cAmount#A/c
{{ r.sl }}{{ r.description }}{{ r.upto1Lakh.amount | number: '1.2-2' }}{{ r.upto1Lakh.accounts | number }}{{ r.oneTo2Lakh.amount | number: '1.2-2' }}{{ r.oneTo2Lakh.accounts | number }}{{ r.twoTo3Lakh.amount | number: '1.2-2' }}{{ r.twoTo3Lakh.accounts | number }}{{ r.above3Lakh.amount | number: '1.2-2' }}{{ r.above3Lakh.accounts | number }} + {{ r.total.amount | number: '1.2-2' }} + + {{ r.total.accounts | number }} +
TOTAL600018811.06{{ depositTotal.accounts | number }}—————— + {{ depositTotal.amount | number: '1.2-2' }} + + {{ depositTotal.accounts | number }} +
+
+ + +
+
CREDIT BALANCE (LOAN A/C) — Section A
+ + + + + + + + + + + + + + + + + + + + + + + + + + @for (r of loans; track r.sl) { + + + + + + + + + + + + + + + } + + + + + + + + + + + + + + + +
Sr.NoDescriptionUp to ₹1,00,000₹1,00,001 – 2,00,000₹2,00,001 – 3,00,000Above ₹3,00,000Total
Amount#A/cAmount#A/cAmount#A/cAmount#A/cAmount#A/c
{{ r.sl }}{{ r.description }}{{ r.upto1Lakh.amount | number: '1.2-2' }}{{ r.upto1Lakh.accounts | number }}{{ r.oneTo2Lakh.amount | number: '1.2-2' }}{{ r.oneTo2Lakh.accounts | number }}{{ r.twoTo3Lakh.amount | number: '1.2-2' }}{{ r.twoTo3Lakh.accounts | number }}{{ r.above3Lakh.amount | number: '1.2-2' }}{{ r.above3Lakh.accounts | number }} + {{ r.total.amount | number: '1.2-2' }} + + {{ r.total.accounts | number }} +
TOTAL———————— + {{ loanTotal.amount | number: '1.2-2' }} + + {{ loanTotal.accounts | number }} +
+
+ + +
+
Section D — Final Summary
+ + + + + + + + + + + @for (s of summary; track s.sl) { + + + + + + + } + + + + + + + +
Sr.NoDescriptionAmount#A/c
{{ s.sl }}{{ s.description }}{{ s.amount | number: '1.2-2' }}{{ s.accounts | number }}
TOTAL607,679,897.428,190
+
+
+ } + + + @if (activeTab === 'sahay') { +
+
+
+

SAHAY — Depositor-wise Input File

+

DICGC claim payout format (CSV/Excel) submitted on bank failure under Section 18A

+
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + @for (d of filteredDepositors; track d.inputSerialNumber) { + + + + + + + + + + + + + + + + + } @empty { + + + + } + +
SRUCICA/c NoTypeHoldersCustomer Name(s)DOBDepositInsuredLAD/LAFDPANAadhaarMobileLast Txn
{{ d.inputSerialNumber }}{{ d.customerIdUcic }}{{ d.accountNumber }}{{ d.typeOfDeposit }}{{ d.numberOfHolders }} + {{ d.nameFirst }} + @if (d.nameSecond) { + & {{ d.nameSecond }} + } + @if (d.nameThird) { + & {{ d.nameThird }} + } + {{ d.dateOfBirthOrIncorporation }}{{ d.amountOfDeposit | number: '1.0-0' }}{{ d.insuredAmount | number: '1.0-0' }}{{ d.loanAgainstDeposit || '—' }}{{ d.panNumber || '—' }}{{ d.aadhaarNumber || '—' }}{{ d.mobileNumber }}{{ d.lastCustomerInducedTxnDate }}
No depositors match the filters.
+
+ +
+ + SAHAY Note: Insured Amount is capped at ₹{{ bank.insuranceLimit | number }} per + depositor per bank. Rows highlighted are above the cap — DICGC pays the cap, residual is unsecured. +
+
+ } + + +
{{ toastMsg }}
+
diff --git a/src/app/dicgc/dicgc.component.scss b/src/app/dicgc/dicgc.component.scss new file mode 100644 index 000000000..55c5e688e --- /dev/null +++ b/src/app/dicgc/dicgc.component.scss @@ -0,0 +1,602 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --dc-navy: #0b1e3d; + --dc-blue: #1d4ed8; + --dc-blue-l: #eff6ff; + --dc-teal: #0e7e6b; + --dc-teal-l: #e0f4f0; + --dc-green: #15803d; + --dc-green-l: #dcfce7; + --dc-amber: #d97706; + --dc-amber-l: #fef3c7; + --dc-red: #c0392b; + --dc-red-l: #fdecea; + --dc-purple: #6d28d9; + --dc-purple-l: #ede9fe; + --dc-g50: #f8fafc; + --dc-g100: #f1f5f9; + --dc-g200: #e2e8f0; + --dc-g300: #cbd5e1; + --dc-g500: #64748b; + --dc-g700: #334155; + --dc-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + background: var(--dc-g50); + color: var(--dc-g900); + font-size: 14px; + line-height: 1.5; +} + +.dic-root { + padding: 18px 22px; + max-width: 1500px; + margin: 0 auto; +} + +/* HEADER */ +.dic-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 16px; +} + +.dic-head-left { + display: flex; + align-items: center; + gap: 14px; +} + +.dic-head-ic { + font-size: 28px; + color: var(--dc-blue); +} + +.dic-head-t { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--dc-g900); +} + +.dic-head-s { + font-size: 12px; + color: var(--dc-g500); +} + +.dic-head-meta { + display: flex; + gap: 22px; + + > div { + display: flex; + flex-direction: column; + + .dic-meta-l { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--dc-g500); + } + + .dic-meta-v { + font-size: 14px; + font-weight: 600; + color: var(--dc-g900); + } + } +} + +/* TABS */ +.dic-tabs { + display: flex; + gap: 4px; + background: #fff; + border: 1px solid var(--dc-g200); + border-radius: 10px; + padding: 4px; + margin-bottom: 16px; + overflow-x: auto; +} + +.dic-tab { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 14px; + border: none; + background: transparent; + font-size: 13px; + font-weight: 500; + color: var(--dc-g500); + cursor: pointer; + border-radius: 7px; + white-space: nowrap; + transition: all 0.15s; + font-family: inherit; + + &:hover { + color: var(--dc-g900); + } + + &.active { + background: var(--dc-blue); + color: #fff; + box-shadow: 0 2px 6px rgb(29 78 216 / 25%); + } +} + +.dic-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* KPI CARDS */ +.dic-kpis { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.dic-kpi { + background: #fff; + border: 1px solid var(--dc-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + align-items: center; + gap: 12px; + box-shadow: 0 1px 3px rgb(0 0 0 / 5%); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 14px rgb(0 0 0 / 10%); + } +} + +.dic-kpi-ic { + width: 42px; + height: 42px; + border-radius: 11px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.dic-kpi--blue .dic-kpi-ic { + background: var(--dc-blue-l); + color: var(--dc-blue); +} + +.dic-kpi--green .dic-kpi-ic { + background: var(--dc-green-l); + color: var(--dc-green); +} + +.dic-kpi--amber .dic-kpi-ic { + background: var(--dc-amber-l); + color: var(--dc-amber); +} + +.dic-kpi--teal .dic-kpi-ic { + background: var(--dc-teal-l); + color: var(--dc-teal); +} + +.dic-kpi--red .dic-kpi-ic { + background: var(--dc-red-l); + color: var(--dc-red); +} + +.dic-kpi-n { + font-size: 18px; + font-weight: 700; + color: var(--dc-g900); + line-height: 1.1; +} + +.dic-kpi-l { + font-size: 12px; + color: var(--dc-g700); + font-weight: 500; + margin-top: 2px; +} + +.dic-kpi-sub { + font-size: 11px; + color: var(--dc-g500); + margin-top: 1px; +} + +/* COMPLIANCE CARDS */ +.dic-compliance { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 12px; +} + +.dc-card { + background: #fff; + border: 1px solid var(--dc-g200); + border-radius: 12px; + padding: 14px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.dc-card-h { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--dc-g900); + margin-bottom: 8px; +} + +.dc-ok { + color: var(--dc-green); +} + +.dc-info { + color: var(--dc-blue); +} + +.dc-meta { + font-size: 12px; + color: var(--dc-g500); + margin: 2px 0; + + strong { + color: var(--dc-g900); + font-weight: 600; + } +} + +.dc-btn { + margin-top: 10px; + padding: 6px 12px; + border: 1px solid var(--dc-blue); + background: var(--dc-blue-l); + color: var(--dc-blue); + border-radius: 8px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + font-family: inherit; + display: inline-flex; + align-items: center; + gap: 6px; + + &:hover { + background: var(--dc-blue); + color: #fff; + } +} + +/* FORM HEADER */ +.dic-form-meta { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 14px; + flex-wrap: wrap; +} + +.dic-form-title { + h3 { + margin: 0; + font-size: 16px; + font-weight: 700; + color: var(--dc-g900); + } + + p { + margin: 2px 0 0; + font-size: 12px; + color: var(--dc-g500); + } +} + +.dic-action-group { + display: flex; + gap: 8px; +} + +/* BUTTONS */ +.dic-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--dc-g300); + background: #fff; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + color: var(--dc-g700); + cursor: pointer; + font-family: inherit; + transition: all 0.15s; + + &:hover { + background: var(--dc-g50); + border-color: var(--dc-g500); + } +} + +.dic-btn-xls { + background: #16a34a; + border-color: #16a34a; + color: #fff; + + &:hover { + background: #15803d; + border-color: #15803d; + color: #fff; + } +} + +.dic-btn-csv { + background: #1d4ed8; + border-color: #1d4ed8; + color: #fff; + + &:hover { + background: #1e40af; + border-color: #1e40af; + color: #fff; + } +} + +.dic-btn-g { + align-self: end; +} + +/* INPUTS */ +.dic-inp { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--dc-g200); + border-radius: 8px; + font-size: 13px; + background: #fff; + font-family: inherit; + + &:focus { + outline: none; + border-color: var(--dc-blue); + box-shadow: 0 0 0 3px rgb(29 78 216 / 15%); + } +} + +/* FILTERS */ +.dic-filters { + background: #fff; + border: 1px solid var(--dc-g200); + border-radius: 12px; + padding: 14px; + display: grid; + grid-template-columns: 1fr 1fr 1.4fr auto; + gap: 12px; + align-items: end; +} + +.dic-filter { + display: flex; + flex-direction: column; + gap: 4px; + + label { + font-size: 11px; + font-weight: 600; + color: var(--dc-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } +} + +.dic-filter-check .check-row { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: var(--dc-g700); + cursor: pointer; + user-select: none; + margin-top: 14px; + + input { + width: 16px; + height: 16px; + accent-color: var(--dc-blue); + } +} + +/* TABLE */ +.dic-tbl-wrap { + background: #fff; + border: 1px solid var(--dc-g200); + border-radius: 12px; + overflow: auto; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.dic-tbl-h { + background: var(--dc-blue-l); + color: var(--dc-blue); + padding: 10px 16px; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid var(--dc-g200); +} + +.dic-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 9px 10px; + font-size: 11px; + font-weight: 600; + color: var(--dc-g500); + text-transform: uppercase; + letter-spacing: 0.04em; + background: var(--dc-g50); + border-bottom: 1px solid var(--dc-g200); + } + + th.hd-1, + tr.hd-1 th { + background: #f0f4ff; + color: var(--dc-blue); + } + + td { + padding: 9px 10px; + border-bottom: 1px solid var(--dc-g100); + font-size: 12px; + color: var(--dc-g700); + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--dc-g50); + } + + tr.row-total td { + background: #f0fdf4; + font-weight: 700; + color: var(--dc-g900); + border-top: 2px solid var(--dc-green); + } + + tr.uninsured td { + background: #fff7ed; + } + + tr.uninsured:hover td { + background: #ffedd5; + } +} + +.dic-summary-tbl { + td, + th { + padding: 11px 14px; + font-size: 13px; + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 11px; + letter-spacing: 0.02em; +} + +.cust-2 { + color: var(--dc-g500); + font-size: 11px; +} + +.dic-empty { + text-align: center; + padding: 30px; + color: var(--dc-g500); + font-style: italic; +} + +/* NOTE */ +.dic-note { + background: var(--dc-amber-l); + border: 1px solid #fed7aa; + border-radius: 10px; + padding: 12px 16px; + display: flex; + gap: 10px; + font-size: 12px; + color: #92400e; + line-height: 1.5; + + fa-icon { + font-size: 14px; + flex-shrink: 0; + margin-top: 2px; + } +} + +/* TOAST */ +.dic-toast { + position: fixed; + bottom: 32px; + left: 50%; + transform: translateX(-50%) translateY(20px); + background: var(--dc-navy); + color: #fff; + padding: 12px 22px; + border-radius: 10px; + font-size: 13px; + font-weight: 500; + box-shadow: 0 6px 20px rgb(0 0 0 / 25%); + opacity: 0; + pointer-events: none; + transition: all 0.25s; + z-index: 1500; + + &.show { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + +@media (width <= 1100px) { + .dic-kpis { + grid-template-columns: repeat(3, 1fr); + } + + .dic-compliance { + grid-template-columns: 1fr; + } + + .dic-filters { + grid-template-columns: 1fr 1fr; + } +} + +@media (width <= 720px) { + .dic-kpis { + grid-template-columns: 1fr 1fr; + } +} diff --git a/src/app/dicgc/dicgc.component.ts b/src/app/dicgc/dicgc.component.ts new file mode 100644 index 000000000..608080a6a --- /dev/null +++ b/src/app/dicgc/dicgc.component.ts @@ -0,0 +1,398 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { + DEPOSIT_SLABS, + DICGC_BANK, + LOAN_SLABS, + SahayDepositor, + SAHAY_DEPOSITORS, + SlabRow, + SUMMARY_D +} from './dicgc-data'; + +@Component({ + selector: 'mifosx-dicgc', + templateUrl: './dicgc.component.html', + styleUrls: ['./dicgc.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FaIconComponent, + FormsModule + ] +}) +export class DicgcComponent { + /** Active tab */ + activeTab: 'dashboard' | 'slab' | 'sahay' = 'dashboard'; + + bank = DICGC_BANK; + deposits: SlabRow[] = DEPOSIT_SLABS; + loans: SlabRow[] = LOAN_SLABS; + summary = SUMMARY_D; + depositors: SahayDepositor[] = SAHAY_DEPOSITORS; + + /** SAHAY filters */ + filterName = ''; + filterAccountNumber = ''; + filterAboveLimit = false; + + /** Toast */ + toastMsg = ''; + toastVisible = false; + private toastTimer: any; + + showTab(tab: 'dashboard' | 'slab' | 'sahay'): void { + this.activeTab = tab; + } + + // -------------------- KPI helpers -------------------- + private sumSlab( + rows: SlabRow[], + slab: keyof Pick + ): { amount: number; accounts: number } { + let amount = 0; + let accounts = 0; + for (const r of rows) { + amount += r[slab].amount; + accounts += r[slab].accounts; + } + return { amount, accounts }; + } + + get depositTotal(): { amount: number; accounts: number } { + return this.sumSlab(this.deposits, 'total'); + } + get loanTotal(): { amount: number; accounts: number } { + return this.sumSlab(this.loans, 'total'); + } + + /** Insured = full amount for ≤5L, capped at 5L for >5L (sum across slabs ≤3L is fully insured here) */ + get fullyInsuredAccounts(): number { + const u1 = this.sumSlab(this.deposits, 'upto1Lakh').accounts; + const u2 = this.sumSlab(this.deposits, 'oneTo2Lakh').accounts; + const u3 = this.sumSlab(this.deposits, 'twoTo3Lakh').accounts; + return u1 + u2 + u3; + } + + get above3LakhAccounts(): number { + return this.sumSlab(this.deposits, 'above3Lakh').accounts; + } + + get insuredAmount(): number { + return this.depositors.reduce((s, d) => s + d.insuredAmount, 0); + } + + /** DICGC premium = 0.12% of total assessable deposits per half year */ + get premiumDue(): number { + return this.depositTotal.amount * (this.bank.premiumRatePer100 / 100); + } + + // SAHAY filtered list + get filteredDepositors(): SahayDepositor[] { + const name = this.filterName.trim().toLowerCase(); + const acc = this.filterAccountNumber.trim(); + return this.depositors.filter((d) => { + if (name && !`${d.nameFirst} ${d.nameSecond} ${d.nameThird}`.toLowerCase().includes(name)) return false; + if (acc && !d.accountNumber.includes(acc)) return false; + if (this.filterAboveLimit && d.amountOfDeposit <= this.bank.insuranceLimit) return false; + return true; + }); + } + + resetFilters(): void { + this.filterName = ''; + this.filterAccountNumber = ''; + this.filterAboveLimit = false; + } + + /** True if depositor's amount exceeds the DICGC ₹5L cap */ + isUninsured(d: SahayDepositor): boolean { + return d.amountOfDeposit > this.bank.insuranceLimit; + } + + // -------------------- Excel exports -------------------- + exportSlabReport(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([`A/C-Wise Deposit Insurance and Credit Guarantee Report — As On ${this.bank.reportAsOn}`]); + rows.push([]); + rows.push([ + `Report Ref: ${this.bank.reportRefNo}`, + `Print Date: ${this.bank.printDate}`, + `User Code: ${this.bank.userCode}` + ]); + rows.push([]); + + // Header section + rows.push(['CREDIT BALANCE — DEPOSITS']); + rows.push([ + 'Sr.No', + 'Description', + 'Up to 1L Amount', + 'Up to 1L #A/c', + '1L-2L Amount', + '1L-2L #A/c', + '2L-3L Amount', + '2L-3L #A/c', + 'Above 3L Amount', + 'Above 3L #A/c', + 'Total Amount', + 'Total #A/c' + ]); + for (const r of this.deposits) { + rows.push(this.flattenSlabRow(r)); + } + const dt = this.depositTotal; + rows.push([ + '', + 'TOTAL', + this.sumSlab(this.deposits, 'upto1Lakh').amount.toFixed(2), + this.sumSlab(this.deposits, 'upto1Lakh').accounts.toString(), + this.sumSlab(this.deposits, 'oneTo2Lakh').amount.toFixed(2), + this.sumSlab(this.deposits, 'oneTo2Lakh').accounts.toString(), + this.sumSlab(this.deposits, 'twoTo3Lakh').amount.toFixed(2), + this.sumSlab(this.deposits, 'twoTo3Lakh').accounts.toString(), + this.sumSlab(this.deposits, 'above3Lakh').amount.toFixed(2), + this.sumSlab(this.deposits, 'above3Lakh').accounts.toString(), + dt.amount.toFixed(2), + dt.accounts.toString() + ]); + + rows.push([]); + rows.push(['CREDIT BALANCE (LOAN A/C) — Section A']); + rows.push([ + 'Sr.No', + 'Description', + 'Up to 1L Amount', + 'Up to 1L #A/c', + '1L-2L Amount', + '1L-2L #A/c', + '2L-3L Amount', + '2L-3L #A/c', + 'Above 3L Amount', + 'Above 3L #A/c', + 'Total Amount', + 'Total #A/c' + ]); + for (const r of this.loans) { + rows.push(this.flattenSlabRow(r)); + } + const lt = this.loanTotal; + rows.push([ + '', + 'TOTAL', + this.sumSlab(this.loans, 'upto1Lakh').amount.toFixed(2), + this.sumSlab(this.loans, 'upto1Lakh').accounts.toString(), + this.sumSlab(this.loans, 'oneTo2Lakh').amount.toFixed(2), + this.sumSlab(this.loans, 'oneTo2Lakh').accounts.toString(), + this.sumSlab(this.loans, 'twoTo3Lakh').amount.toFixed(2), + this.sumSlab(this.loans, 'twoTo3Lakh').accounts.toString(), + this.sumSlab(this.loans, 'above3Lakh').amount.toFixed(2), + this.sumSlab(this.loans, 'above3Lakh').accounts.toString(), + lt.amount.toFixed(2), + lt.accounts.toString() + ]); + + rows.push([]); + rows.push(['Section D — Final Summary']); + rows.push([ + 'Sr.No', + 'Description', + 'Amount', + '#A/c' + ]); + for (const s of this.summary) { + rows.push([ + s.sl.toString(), + s.description, + s.amount.toFixed(2), + s.accounts.toString() + ]); + } + const grandAmount = this.summary.reduce((a, x) => a + x.amount, 0); + const grandAcc = this.summary.reduce((a, x) => a + x.accounts, 0); + rows.push([ + '', + 'TOTAL', + grandAmount.toFixed(2), + grandAcc.toString() + ]); + + this.saveTsv(rows, `DICGC-SlabReport-${this.fileTag()}.xls`); + this.toast('DICGC slab report downloaded'); + } + + exportSahay(): void { + const headers = [ + 'Input Serial Number', + 'Customer ID (UCIC) Number', + 'Account Number', + 'Type of account', + 'Nature of account', + 'Number of Acct. Holders (in case of Joint)', + 'Name Of First Customer', + 'Name Of Second Customer', + 'Name Of Third Customer', + 'Date of Birth (Primary Holder) or Date of incorporation in case of Entity', + 'Amount of Deposit', + 'Insured Amount', + 'Loan against deposits only (LAD / LAFD)', + 'PAN Number', + 'Aadhaar Number', + 'Any Other OVD / KYC Document Type', + 'Any Other OVD / KYC Number', + 'Mobile Number', + 'Type of Deposit', + 'Date of Last Customer inducted transaction' + ]; + const rows: string[][] = [headers]; + for (const d of this.filteredDepositors) { + rows.push([ + d.inputSerialNumber.toString(), + d.customerIdUcic, + d.accountNumber, + d.typeOfAccount, + d.natureOfAccount, + d.numberOfHolders.toString(), + d.nameFirst, + d.nameSecond, + d.nameThird, + d.dateOfBirthOrIncorporation, + d.amountOfDeposit.toFixed(2), + d.insuredAmount.toFixed(2), + d.loanAgainstDeposit, + d.panNumber, + d.aadhaarNumber, + d.otherKycType, + d.otherKycNumber, + d.mobileNumber, + d.typeOfDeposit, + d.lastCustomerInducedTxnDate + ]); + } + this.saveTsv(rows, `DICGC-SAHAY-Depositors-${this.fileTag()}.xls`); + this.toast(`${this.filteredDepositors.length} depositors exported (SAHAY format)`); + } + + exportSahayCsv(): void { + // CSV mirrors the Input File Template DICGC ships + const headers = [ + 'Input Serial Number', + 'Customer ID (UCIC) Number', + 'Account Number', + 'Type of account', + 'Nature of account', + 'Number of Acct. Holders (in case of Joint)', + 'Name Of First Customer', + 'Name Of Second Customer', + 'Name Of Third Customer', + 'Date of Birth (Primary Holder) or Date of incorporation in case of Entity', + 'Amount of Deposit', + 'Insured Amount', + 'Loan against deposits only (LAD / LAFD)', + 'PAN Number', + 'Aadhaar Number', + 'Any Other OVD / KYC Document Type', + 'Any Other OVD / KYC Number', + 'Mobile Number', + 'Type of Deposit', + 'Date of Last Customer inducted transaction' + ]; + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const lines: string[] = [headers.map(escape).join(',')]; + for (const d of this.filteredDepositors) { + lines.push( + [ + d.inputSerialNumber.toString(), + d.customerIdUcic, + d.accountNumber, + d.typeOfAccount, + d.natureOfAccount, + d.numberOfHolders.toString(), + d.nameFirst, + d.nameSecond, + d.nameThird, + d.dateOfBirthOrIncorporation, + d.amountOfDeposit.toFixed(2), + d.insuredAmount.toFixed(2), + d.loanAgainstDeposit, + d.panNumber, + d.aadhaarNumber, + d.otherKycType, + d.otherKycNumber, + d.mobileNumber, + d.typeOfDeposit, + d.lastCustomerInducedTxnDate + ] + .map(escape) + .join(',') + ); + } + const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `DICGC-SAHAY-Depositors-${this.fileTag()}.csv`; + a.click(); + URL.revokeObjectURL(url); + this.toast(`${this.filteredDepositors.length} depositors exported (CSV)`); + } + + private flattenSlabRow(r: SlabRow): string[] { + return [ + r.sl.toString(), + r.description, + r.upto1Lakh.amount.toFixed(2), + r.upto1Lakh.accounts.toString(), + r.oneTo2Lakh.amount.toFixed(2), + r.oneTo2Lakh.accounts.toString(), + r.twoTo3Lakh.amount.toFixed(2), + r.twoTo3Lakh.accounts.toString(), + r.above3Lakh.amount.toFixed(2), + r.above3Lakh.accounts.toString(), + r.total.amount.toFixed(2), + r.total.accounts.toString() + ]; + } + + private saveTsv(rows: string[][], filename: string): void { + const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + const blob = new Blob([tsv], { type: 'application/vnd.ms-excel;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + private fileTag(): string { + const d = new Date(); + const dd = String(d.getDate()).padStart(2, '0'); + const mm = String(d.getMonth() + 1).padStart(2, '0'); + return `${dd}${mm}${d.getFullYear()}`; + } + + toast(msg: string): void { + this.toastMsg = msg; + this.toastVisible = true; + if (this.toastTimer) clearTimeout(this.toastTimer); + this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); + } +} diff --git a/src/app/dicgc/dicgc.module.ts b/src/app/dicgc/dicgc.module.ts new file mode 100644 index 000000000..1dd709476 --- /dev/null +++ b/src/app/dicgc/dicgc.module.ts @@ -0,0 +1,22 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { DicgcRoutingModule } from './dicgc-routing.module'; +import { DicgcComponent } from './dicgc.component'; + +@NgModule({ + imports: [ + SharedModule, + DicgcRoutingModule, + DicgcComponent + ] +}) +export class DicgcModule {} diff --git a/src/app/fiu/fiu-data.ts b/src/app/fiu/fiu-data.ts new file mode 100644 index 000000000..2f6f8dcc4 --- /dev/null +++ b/src/app/fiu/fiu-data.ts @@ -0,0 +1,560 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/** + * Sample FIU-IND data — schema mirrors the FINnet 2.0 CSV submission templates + * (TC1, TC2, KC1, KC2, Account_Detail, Account_Person_Relation). + * Bank/customer names are dummy placeholders. + * + * Reports filed with FIU-IND under PMLA 2002: + * CTR — Cash Transaction Report (≥ ₹10 L cash, monthly by 15th) + * STR — Suspicious Transaction Report (within 7 days of detection) + * CCR — Counterfeit Currency Report + * NTR — NPO Transaction Report (≥ ₹10 L, NGO/NPO clients) + * CBWTR — Cross-Border Wire Transfer Report (≥ ₹5 L) + */ + +export type ReportType = 'CTR' | 'STR' | 'CCR' | 'NTR' | 'CBWTR'; +export type StrStatus = 'draft' | 'po-review' | 'filed' | 'rejected'; + +/** TC2 — Branch / cash transaction record */ +export interface CtrTransaction { + reportType: ReportType; + reportRefNo: number; + branchCode: string; + accountNumber: string; + txnBranchCode: string; + depositOrWithdrawal: 'Deposit' | 'Withdrawal'; + txnId: number; + txnDate: string; + txnTime: string; + txnAmount: number; + instrumentType: 'Cash' | 'Cheque' | 'DD' | 'NEFT' | 'RTGS' | 'Others'; + instrumentId: string; + nonCustomerRef: string; + relationshipFlag: 'Customer' | 'Non-Customer'; + thirdPartyPan: string; + declaration: string; + /** Customer name resolved from KC1 (UI convenience, not in actual TC2 schema) */ + customerName?: string; +} + +/** STR record — workflow extension on top of TC2 */ +export interface StrTransaction extends CtrTransaction { + detectedOn: string; + flaggedBy: string; + riskCategory: + | 'structuring' + | 'dormant-revival' + | 'high-risk-country' + | 'unusual-pattern' + | 'pep-related' + | 'sanctions-hit'; + narrative: string; + status: StrStatus; + poRemarks?: string; +} + +/** KC1 — Individual customer card (subset of the 70+ fields, only what UI shows) */ +export interface KycIndividual { + reportRefNo: number; + ucic: string; + pan: string; + ckycNumber: string; + firstName: string; + middleName: string; + lastName: string; + fatherName: string; + motherName: string; + gender: 'Male' | 'Female' | 'Other'; + dob: string; + mobileNumber: string; + email: string; + primaryAddress: string; + state: string; + city: string; + pincode: string; + customerType: string; + annualIncome: number; + occupation: string; + onboardingDate: string; + lastKycDate: string; + riskLevel: 'Low' | 'Medium' | 'High'; + reportType: ReportType; + otherIdentifierName?: string; + otherIdentifierNumber?: string; +} + +/** KC2 — Entity / non-individual customer card */ +export interface KycEntity { + reportRefNo: number; + ucic: string; + entityName: string; + panNumber: string; + gstin: string; + mobileNumber: string; + address: string; + state: string; + city: string; + pincode: string; + dateOfIncorporation: string; + customerType: string; + riskLevel: 'Low' | 'Medium' | 'High'; + onboardingDate: string; + lastKycDate: string; + isSoleProprietor: boolean; + ownerUcic: string; + ownerName: string; + reportType: ReportType; +} + +export const FIU_BANK = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + reBankingFlag: 'SCB', + reportingMonth: 'January 2026', + reBuid: 'REBANK-2031', + principalOfficer: 'Karthik Kamath', + principalOfficerEmail: 'po.fiu@bharaturbanbank.example', + filerUserId: 'PO-001', + cashThreshold: 1000000, // ₹10 L per single cash txn + monthlyAggThreshold: 1000000, // aggregate ≥ ₹10 L in a month + cbwtrThreshold: 500000 // CBWTR threshold ₹5 L +}; + +/** ── Sample CTR transactions (FINnet TC2-style) ── */ +export const CTR_TXNS: CtrTransaction[] = [ + { + reportType: 'CTR', + reportRefNo: 1, + branchCode: '1', + accountNumber: '110001101000838', + txnBranchCode: '1', + depositOrWithdrawal: 'Withdrawal', + txnId: 1, + txnDate: '20-01-2026', + txnTime: '16:51:28', + txnAmount: 1500000, + instrumentType: 'Cheque', + instrumentId: '24304', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'RAVI KUMAR SHARMA' + }, + { + reportType: 'CTR', + reportRefNo: 1, + branchCode: '1', + accountNumber: '110001101000838', + txnBranchCode: '1', + depositOrWithdrawal: 'Withdrawal', + txnId: 2, + txnDate: '27-01-2026', + txnTime: '16:14:22', + txnAmount: 932000, + instrumentType: 'Cheque', + instrumentId: '24305', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'RAVI KUMAR SHARMA' + }, + { + reportType: 'CTR', + reportRefNo: 2, + branchCode: '1', + accountNumber: '110001352000357', + txnBranchCode: '1', + depositOrWithdrawal: 'Withdrawal', + txnId: 3, + txnDate: '02-01-2026', + txnTime: '15:05:36', + txnAmount: 600000, + instrumentType: 'Cheque', + instrumentId: '100039', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'PRIYA PATEL' + }, + { + reportType: 'CTR', + reportRefNo: 2, + branchCode: '1', + accountNumber: '110001352000357', + txnBranchCode: '1', + depositOrWithdrawal: 'Withdrawal', + txnId: 4, + txnDate: '07-01-2026', + txnTime: '14:14:17', + txnAmount: 700000, + instrumentType: 'Cheque', + instrumentId: '100040', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'PRIYA PATEL' + }, + { + reportType: 'CTR', + reportRefNo: 3, + branchCode: '7', + accountNumber: '110001352000420', + txnBranchCode: '7', + depositOrWithdrawal: 'Deposit', + txnId: 5, + txnDate: '12-01-2026', + txnTime: '11:28:50', + txnAmount: 1200000, + instrumentType: 'Cash', + instrumentId: 'CASH', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'VIKRAM REDDY' + }, + { + reportType: 'CTR', + reportRefNo: 4, + branchCode: '4', + accountNumber: '110001352000511', + txnBranchCode: '4', + depositOrWithdrawal: 'Deposit', + txnId: 6, + txnDate: '18-01-2026', + txnTime: '10:14:05', + txnAmount: 2500000, + instrumentType: 'Cash', + instrumentId: 'CASH', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'NIRAV SHAH' + }, + { + reportType: 'CTR', + reportRefNo: 5, + branchCode: '11', + accountNumber: '110001352000604', + txnBranchCode: '11', + depositOrWithdrawal: 'Withdrawal', + txnId: 7, + txnDate: '24-01-2026', + txnTime: '13:42:11', + txnAmount: 1100000, + instrumentType: 'Cheque', + instrumentId: '88123', + nonCustomerRef: 'NCR-001', + relationshipFlag: 'Non-Customer', + thirdPartyPan: 'AKHPV2244R', + declaration: 'NA', + customerName: 'KAVITA VERMA' + } +]; + +/** ── Sample STR transactions ── */ +export const STR_TXNS: StrTransaction[] = [ + { + reportType: 'STR', + reportRefNo: 101, + branchCode: '1', + accountNumber: '110001101000841', + txnBranchCode: '1', + depositOrWithdrawal: 'Deposit', + txnId: 8, + txnDate: '15-01-2026', + txnTime: '11:32:00', + txnAmount: 950000, + instrumentType: 'Cash', + instrumentId: 'CASH', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'ARJUN DESHMUKH', + detectedOn: '16-01-2026', + flaggedBy: 'AML Engine — Rule R-007', + riskCategory: 'structuring', + narrative: + 'Three sub-₹10L cash deposits within 5 days totalling ₹28.4L; pattern indicative of structuring to avoid CTR threshold.', + status: 'po-review' + }, + { + reportType: 'STR', + reportRefNo: 102, + branchCode: '7', + accountNumber: '110001101000922', + txnBranchCode: '7', + depositOrWithdrawal: 'Deposit', + txnId: 9, + txnDate: '21-01-2026', + txnTime: '14:18:30', + txnAmount: 4800000, + instrumentType: 'NEFT', + instrumentId: 'N1264821', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'LAKSHMI IYER', + detectedOn: '22-01-2026', + flaggedBy: 'Branch Compliance Officer', + riskCategory: 'dormant-revival', + narrative: + 'Account dormant for 18 months suddenly received ₹48L NEFT followed by 4 immediate cash withdrawals of ₹2L each.', + status: 'filed', + poRemarks: 'STR filed with FIU-IND. Acknowledgement no: STR-2026-9821.' + }, + { + reportType: 'STR', + reportRefNo: 103, + branchCode: '2', + accountNumber: '110001302001012', + txnBranchCode: '2', + depositOrWithdrawal: 'Withdrawal', + txnId: 10, + txnDate: '23-01-2026', + txnTime: '15:42:18', + txnAmount: 1800000, + instrumentType: 'RTGS', + instrumentId: 'R889013', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'SAMEER KAPOOR', + detectedOn: '24-01-2026', + flaggedBy: 'AML Engine — Rule R-014', + riskCategory: 'high-risk-country', + narrative: + 'RTGS transfer to beneficiary in jurisdiction listed under FATF grey list (Cayman Islands intermediary). KYC documentation incomplete.', + status: 'draft' + }, + { + reportType: 'STR', + reportRefNo: 104, + branchCode: '13', + accountNumber: '110001352001145', + txnBranchCode: '13', + depositOrWithdrawal: 'Deposit', + txnId: 11, + txnDate: '08-01-2026', + txnTime: '12:55:42', + txnAmount: 6500000, + instrumentType: 'Cash', + instrumentId: 'CASH', + nonCustomerRef: '', + relationshipFlag: 'Customer', + thirdPartyPan: '', + declaration: 'NA', + customerName: 'MANMOHAN SINGH GILL', + detectedOn: '09-01-2026', + flaggedBy: 'AML Engine — Rule R-022', + riskCategory: 'unusual-pattern', + narrative: + 'Account avg monthly turnover ₹1.5L; received ₹65L cash in single deposit, source-of-funds documentation pending.', + status: 'po-review' + } +]; + +/** ── KC1 sample individual customers ── */ +export const KC1_INDIVIDUALS: KycIndividual[] = [ + { + reportRefNo: 1, + ucic: '10078', + pan: 'AAQPS8560J', + ckycNumber: '60011225033445', + firstName: 'RAVI', + middleName: 'KUMAR', + lastName: 'SHARMA', + fatherName: 'RAJESH KUMAR SHARMA', + motherName: 'SUNITA DEVI', + gender: 'Male', + dob: '10-08-1985', + mobileNumber: '91-9892011001', + email: 'ravi.sharma@example.com', + primaryAddress: 'FLAT 402 SUNRISE TOWERS MAROL ANDHERI EAST', + state: 'MH', + city: 'MUMBAI', + pincode: '400069', + customerType: 'Individual', + annualIncome: 750000, + occupation: 'Service', + onboardingDate: '15-03-2020', + lastKycDate: '12-04-2025', + riskLevel: 'Medium', + reportType: 'CTR', + otherIdentifierName: 'AADHAAR', + otherIdentifierNumber: 'XXXXXXXX1234' + }, + { + reportRefNo: 2, + ucic: '10079', + pan: 'AKLPS9933P', + ckycNumber: '60022144558899', + firstName: 'PRIYA', + middleName: '', + lastName: 'PATEL', + fatherName: 'HARISH PATEL', + motherName: 'MEENA PATEL', + gender: 'Female', + dob: '22-03-1990', + mobileNumber: '91-9822055002', + email: 'priya.patel@example.com', + primaryAddress: 'B-12 SHIVAJI NAGAR FC ROAD', + state: 'MH', + city: 'PUNE', + pincode: '411005', + customerType: 'Individual', + annualIncome: 480000, + occupation: 'Self-Employed', + onboardingDate: '10-09-2023', + lastKycDate: '15-03-2025', + riskLevel: 'Low', + reportType: 'CTR', + otherIdentifierName: 'AADHAAR', + otherIdentifierNumber: 'XXXXXXXX2210' + }, + { + reportRefNo: 3, + ucic: '20101', + pan: 'AAEPR9352L', + ckycNumber: '', + firstName: 'VIKRAM', + middleName: '', + lastName: 'REDDY', + fatherName: 'SURESH REDDY', + motherName: 'LAKSHMI REDDY', + gender: 'Male', + dob: '07-07-1982', + mobileNumber: '91-9845022004', + email: '', + primaryAddress: 'NO 23 4TH CROSS JAYANAGAR 5TH BLOCK', + state: 'KA', + city: 'BENGALURU', + pincode: '560041', + customerType: 'Individual', + annualIncome: 950000, + occupation: 'Profession', + onboardingDate: '12-11-2021', + lastKycDate: '20-02-2025', + riskLevel: 'Medium', + reportType: 'CTR', + otherIdentifierName: 'AADHAAR', + otherIdentifierNumber: 'XXXXXXXX6622' + }, + { + reportRefNo: 4, + ucic: '40301', + pan: 'AHKPS5544Z', + ckycNumber: '60040301229988', + firstName: 'NIRAV', + middleName: '', + lastName: 'SHAH', + fatherName: 'JAYESH SHAH', + motherName: 'RITA SHAH', + gender: 'Male', + dob: '18-05-1992', + mobileNumber: '91-9824012010', + email: 'nirav.shah@example.com', + primaryAddress: 'A-401 ATLANTIS APARTMENTS SATELLITE ROAD', + state: 'GJ', + city: 'AHMEDABAD', + pincode: '380015', + customerType: 'Individual', + annualIncome: 1800000, + occupation: 'Service', + onboardingDate: '14-02-2018', + lastKycDate: '20-12-2024', + riskLevel: 'High', + reportType: 'CTR', + otherIdentifierName: 'PASSPORT', + otherIdentifierNumber: 'M5544321' + }, + { + reportRefNo: 5, + ucic: '50402', + pan: 'AKHPV2244R', + ckycNumber: '', + firstName: 'KAVITA', + middleName: '', + lastName: 'VERMA', + fatherName: 'DEEPAK VERMA', + motherName: 'USHA VERMA', + gender: 'Female', + dob: '01-04-1988', + mobileNumber: '91-9415055014', + email: '', + primaryAddress: '45 CIVIL LINES MALL ROAD', + state: 'UP', + city: 'KANPUR', + pincode: '208001', + customerType: 'Individual', + annualIncome: 540000, + occupation: 'Service', + onboardingDate: '08-09-2022', + lastKycDate: '15-09-2024', + riskLevel: 'Low', + reportType: 'CTR', + otherIdentifierName: 'AADHAAR', + otherIdentifierNumber: 'XXXXXXXX2244' + } +]; + +/** ── KC2 sample entity customers ── */ +export const KC2_ENTITIES: KycEntity[] = [ + { + reportRefNo: 11, + ucic: 'E10101', + entityName: 'BHARAT TRADING COMPANY', + panNumber: 'ABCFB1234N', + gstin: '27ABCFB1234N1ZT', + mobileNumber: '91-9892011010', + address: 'GALA 22 LOWER PAREL', + state: 'MH', + city: 'MUMBAI', + pincode: '400013', + dateOfIncorporation: '24-03-2008', + customerType: 'Sole Proprietor', + riskLevel: 'High', + onboardingDate: '24-03-2008', + lastKycDate: '03-09-2025', + isSoleProprietor: true, + ownerUcic: '10078', + ownerName: 'RAVI KUMAR SHARMA', + reportType: 'CTR' + }, + { + reportRefNo: 12, + ucic: 'E10102', + entityName: 'NEXTGEN TECH PVT LTD', + panNumber: 'AABCN5566K', + gstin: '27AABCN5566K1Z2', + mobileNumber: '91-9824012011', + address: 'TOWER B SUNRISE BUSINESS PARK ANDHERI EAST', + state: 'MH', + city: 'MUMBAI', + pincode: '400093', + dateOfIncorporation: '18-09-2015', + customerType: 'Private Limited', + riskLevel: 'Medium', + onboardingDate: '18-09-2015', + lastKycDate: '12-08-2025', + isSoleProprietor: false, + ownerUcic: '', + ownerName: '', + reportType: 'CTR' + } +]; diff --git a/src/app/fiu/fiu-routing.module.ts b/src/app/fiu/fiu-routing.module.ts new file mode 100644 index 000000000..273536e54 --- /dev/null +++ b/src/app/fiu/fiu-routing.module.ts @@ -0,0 +1,29 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { Route } from '../core/route/route.service'; +import { FiuComponent } from './fiu.component'; + +const routes: Routes = [ + Route.withShell([ + { + path: 'fiu', + component: FiuComponent, + data: { title: 'FIU-IND', breadcrumb: 'FIU-IND' } + } + ]) +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class FiuRoutingModule {} diff --git a/src/app/fiu/fiu.component.html b/src/app/fiu/fiu.component.html new file mode 100644 index 000000000..58ac6a00f --- /dev/null +++ b/src/app/fiu/fiu.component.html @@ -0,0 +1,480 @@ + + +
+ +
+
+ +
+

Financial Intelligence Unit – India (FIU-IND)

+
+ {{ bank.name }} · {{ bank.branch }} · Reporting Month {{ bank.reportingMonth }} + · PMLA 2002 / FINnet 2.0 +
+
+
+
+
+ Principal Officer{{ bank.principalOfficer }} +
+
+ REBANK BUID{{ bank.reBuid }} +
+
+
+ + +
+ + + + + +
+ + + @if (activeTab === 'dashboard') { +
+
+
+
+
+
{{ totalReports | number }}
+
CTR Reports
+
{{ totalCtrTxns | number }} txns this cycle
+
+
+
+
+
+
₹{{ totalCtrAmount | number: '1.0-0' }}
+
Total CTR Amount
+
aggregate cash movement
+
+
+
+
+
+
{{ pendingStr | number }}
+
STR Pending
+
draft / PO review
+
+
+
+
+
+
{{ filedStr | number }}
+
STR Filed
+
acknowledged by FIU
+
+
+
+
+
+
{{ totalCustomers | number }}
+
Customers in Cycle
+
{{ kc1.length }} ind · {{ kc2.length }} entity
+
+
+
+ + +
+
+
+ CTR — Cash Transaction Report +
+
+ Threshold: ₹{{ bank.cashThreshold | number }} per single txn / aggregate per month +
+
Due: 15 Feb 2026
+ +
+
+
+ STR — Suspicious Transaction +
+
Filing window: within 7 days of detection
+
+ Pending: {{ pendingStr }} · Filed: {{ filedStr }} +
+ +
+
+
FINnet 2.0 Files
+
6 CSVs: TC1 / TC2 / KC1 / KC2 / Account_Detail / Account_Person_Relation
+
+ STR XML for suspicious filings
+ +
+
+
+ } + + + @if (activeTab === 'ctr') { +
+
+
+

CTR — Cash Transaction Report

+

+ All cash transactions ≥ ₹{{ bank.cashThreshold | number }} (single or aggregate per month per account) +

+
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + @for (t of filteredCtr; track t.txnId) { + + + + + + + + + + + + } @empty { + + + + } + +
RefAccountCustomerTypeDate / TimeAmountInstrumentBranchRelationship
{{ t.reportRefNo }}{{ t.accountNumber }}{{ t.customerName }} + + {{ t.depositOrWithdrawal }} + + + {{ t.txnDate }} {{ t.txnTime }} + + ₹{{ t.txnAmount | number: '1.0-0' }} + + {{ t.instrumentType }} {{ t.instrumentId }} + {{ t.branchCode }} + {{ + t.relationshipFlag + }} +
No transactions match the filters.
+
+
+ } + + + @if (activeTab === 'str') { +
+
+
+

STR — Suspicious Transaction Report

+

Detected by AML rules engine + branch compliance officers; filed within 7 days

+
+ +
+ +
+
+ + +
+
+ + +
+ +
+ +
+ @for (s of filteredStr; track s.reportRefNo) { +
+
+
+ STR-{{ s.reportRefNo }} + {{ statusLabel(s.status) }} + {{ riskLabel(s.riskCategory) }} +
+
+ {{ s.depositOrWithdrawal }} + ₹{{ s.txnAmount | number: '1.0-0' }} +
+
+
+
+
+ {{ s.customerName }} +
+
+ {{ s.accountNumber }} +
+
+ {{ s.txnDate }} {{ s.txnTime }} +
+
+ {{ s.detectedOn }} +
+
+ {{ s.flaggedBy }} +
+
+ {{ s.instrumentType }} {{ s.instrumentId }} +
+
+
+ +

{{ s.narrative }}

+
+ @if (s.poRemarks) { +
+ +

{{ s.poRemarks }}

+
+ } +
+
+ } @empty { +
No STR records match the filters.
+ } +
+
+ } + + + @if (activeTab === 'customers') { +
+
+
+

Customer Cards — KC1 (Individuals) & KC2 (Entities)

+

Customers attached to reports filed in this cycle

+
+
+ + +
+
+ +
+
KC1 — Individuals
+ + + + + + + + + + + + + + + + + @for (k of kc1; track k.ucic) { + + + + + + + + + + + + + } + +
RefUCICNameGenderDOBPANMobileCityAnnual IncomeRisk
{{ k.reportRefNo }}{{ k.ucic }}{{ k.firstName }} {{ k.middleName }} {{ k.lastName }}{{ k.gender }}{{ k.dob }}{{ k.pan }}{{ k.mobileNumber }}{{ k.city }}, {{ k.state }}₹{{ k.annualIncome | number }} + {{ k.riskLevel }} +
+
+ +
+
KC2 — Entities
+ + + + + + + + + + + + + + + + @for (k of kc2; track k.ucic) { + + + + + + + + + + + + } + +
RefUCICEntity NamePANGSTINCityTypeOwner (if Sole Prop)Risk
{{ k.reportRefNo }}{{ k.ucic }}{{ k.entityName }}{{ k.panNumber }}{{ k.gstin }}{{ k.city }}, {{ k.state }}{{ k.customerType }}{{ k.isSoleProprietor ? k.ownerName : '—' }} + {{ k.riskLevel }} +
+
+
+ } + + + @if (activeTab === 'files') { +
+
+
+

FINnet 2.0 Submission Files

+

6 CSV files in FIU-IND prescribed schema, plus STR XML

+
+ +
+ +
+
+
TC1.csv
+

ATM / card transaction template (header only — no card txns this cycle)

+ +
+
+
TC2.csv
+

Branch / cash transactions ({{ ctrTxns.length }} rows)

+ +
+
+
KC1.csv
+

Individual customer cards ({{ kc1.length }} rows)

+ +
+
+
KC2.csv
+

Entity customer cards ({{ kc2.length }} rows)

+ +
+
+
Account_Detail.csv
+

Per-account 12-month aggregates derived from CTR list

+ +
+
+
Account_Person_Relation.csv
+

Account ↔ customer relationship mapping

+ +
+
+
STR.xml
+

Suspicious Transaction Report ({{ filedStr + pendingStr }} suspects)

+ +
+
+ +
+ + Note: Files are emitted in FIU-IND FINnet 2.0 CSV schema. Submit via the FINnet portal under + {{ bank.principalOfficer }}'s digital signature. CTR is due by the 15th of the following month; STR within 7 + days of detection. +
+
+ } + + +
{{ toastMsg }}
+
diff --git a/src/app/fiu/fiu.component.scss b/src/app/fiu/fiu.component.scss new file mode 100644 index 000000000..4770586b9 --- /dev/null +++ b/src/app/fiu/fiu.component.scss @@ -0,0 +1,894 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --f-navy: #0b1e3d; + --f-blue: #1d4ed8; + --f-blue-l: #eff6ff; + --f-teal: #0e7e6b; + --f-teal-l: #e0f4f0; + --f-green: #15803d; + --f-green-l: #dcfce7; + --f-amber: #d97706; + --f-amber-l: #fef3c7; + --f-red: #c0392b; + --f-red-l: #fdecea; + --f-purple: #6d28d9; + --f-purple-l: #ede9fe; + --f-g50: #f8fafc; + --f-g100: #f1f5f9; + --f-g200: #e2e8f0; + --f-g300: #cbd5e1; + --f-g500: #64748b; + --f-g700: #334155; + --f-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + background: var(--f-g50); + color: var(--f-g900); + font-size: 14px; + line-height: 1.5; +} + +.fiu-root { + padding: 18px 22px; + max-width: 1500px; + margin: 0 auto; +} + +.fiu-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 16px; +} + +.fiu-head-left { + display: flex; + align-items: center; + gap: 14px; +} + +.fiu-head-ic { + font-size: 28px; + color: var(--f-red); +} + +.fiu-head-t { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--f-g900); +} + +.fiu-head-s { + font-size: 12px; + color: var(--f-g500); +} + +.fiu-head-meta { + display: flex; + gap: 22px; + + > div { + display: flex; + flex-direction: column; + + .fiu-meta-l { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--f-g500); + } + + .fiu-meta-v { + font-size: 14px; + font-weight: 600; + color: var(--f-g900); + } + } +} + +/* TABS */ +.fiu-tabs { + display: flex; + gap: 4px; + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 10px; + padding: 4px; + margin-bottom: 16px; + overflow-x: auto; +} + +.fiu-tab { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 14px; + border: none; + background: transparent; + font-size: 13px; + font-weight: 500; + color: var(--f-g500); + cursor: pointer; + border-radius: 7px; + white-space: nowrap; + transition: all 0.15s; + font-family: inherit; + + &:hover { + color: var(--f-g900); + } + + &.active { + background: var(--f-red); + color: #fff; + box-shadow: 0 2px 6px rgb(192 57 43 / 25%); + } +} + +.fiu-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* KPIs */ +.fiu-kpis { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.fiu-kpi { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + align-items: center; + gap: 12px; + box-shadow: 0 1px 3px rgb(0 0 0 / 5%); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 14px rgb(0 0 0 / 10%); + } +} + +.fiu-kpi-ic { + width: 42px; + height: 42px; + border-radius: 11px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.fiu-kpi--blue .fiu-kpi-ic { + background: var(--f-blue-l); + color: var(--f-blue); +} + +.fiu-kpi--teal .fiu-kpi-ic { + background: var(--f-teal-l); + color: var(--f-teal); +} + +.fiu-kpi--amber .fiu-kpi-ic { + background: var(--f-amber-l); + color: var(--f-amber); +} + +.fiu-kpi--green .fiu-kpi-ic { + background: var(--f-green-l); + color: var(--f-green); +} + +.fiu-kpi--purple .fiu-kpi-ic { + background: var(--f-purple-l); + color: var(--f-purple); +} + +.fiu-kpi-n { + font-size: 18px; + font-weight: 700; + color: var(--f-g900); + line-height: 1.1; +} + +.fiu-kpi-l { + font-size: 12px; + color: var(--f-g700); + font-weight: 500; + margin-top: 2px; +} + +.fiu-kpi-sub { + font-size: 11px; + color: var(--f-g500); + margin-top: 1px; +} + +/* COMPLIANCE CARDS */ +.fiu-compliance { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 12px; +} + +.dc-card { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + padding: 14px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.dc-card-h { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 600; + color: var(--f-g900); + margin-bottom: 8px; +} + +.dc-ok { + color: var(--f-green); +} + +.dc-warn { + color: var(--f-amber); +} + +.dc-info { + color: var(--f-blue); +} + +.dc-meta { + font-size: 12px; + color: var(--f-g500); + margin: 2px 0; + + strong { + color: var(--f-g900); + font-weight: 600; + } +} + +.dc-btn { + margin-top: 10px; + padding: 6px 12px; + border: 1px solid var(--f-red); + background: var(--f-red-l); + color: var(--f-red); + border-radius: 8px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + font-family: inherit; + display: inline-flex; + align-items: center; + gap: 6px; + + &:hover { + background: var(--f-red); + color: #fff; + } +} + +/* FORM HEADER */ +.fiu-form-meta { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 14px; + flex-wrap: wrap; +} + +.fiu-form-title { + h3 { + margin: 0; + font-size: 16px; + font-weight: 700; + color: var(--f-g900); + } + + p { + margin: 2px 0 0; + font-size: 12px; + color: var(--f-g500); + } +} + +.fiu-action-group { + display: flex; + gap: 8px; +} + +/* BUTTONS */ +.fiu-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--f-g300); + background: #fff; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + color: var(--f-g700); + cursor: pointer; + font-family: inherit; + transition: all 0.15s; + + &:hover { + background: var(--f-g50); + border-color: var(--f-g500); + } +} + +.fiu-btn-csv { + background: #1d4ed8; + border-color: #1d4ed8; + color: #fff; + + &:hover { + background: #1e40af; + border-color: #1e40af; + color: #fff; + } +} + +.fiu-btn-xls { + background: #16a34a; + border-color: #16a34a; + color: #fff; + + &:hover { + background: #15803d; + border-color: #15803d; + color: #fff; + } +} + +.fiu-btn-xml { + background: var(--f-purple); + border-color: var(--f-purple); + color: #fff; + + &:hover { + background: #5b21b6; + border-color: #5b21b6; + color: #fff; + } +} + +.fiu-btn-g { + align-self: end; +} + +/* INPUTS */ +.fiu-inp { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--f-g200); + border-radius: 8px; + font-size: 13px; + background: #fff; + font-family: inherit; + + &:focus { + outline: none; + border-color: var(--f-red); + box-shadow: 0 0 0 3px rgb(192 57 43 / 15%); + } +} + +/* FILTERS */ +.fiu-filters { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + padding: 14px; + display: grid; + grid-template-columns: 1fr 1fr 1fr auto; + gap: 12px; + align-items: end; +} + +.fiu-filter { + display: flex; + flex-direction: column; + gap: 4px; + + label { + font-size: 11px; + font-weight: 600; + color: var(--f-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } +} + +/* TABLE */ +.fiu-tbl-wrap { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + overflow: auto; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.fiu-tbl-h { + background: var(--f-blue-l); + color: var(--f-blue); + padding: 10px 16px; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid var(--f-g200); +} + +.fiu-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 12px; + font-size: 11px; + font-weight: 600; + color: var(--f-g500); + text-transform: uppercase; + letter-spacing: 0.04em; + background: var(--f-g50); + border-bottom: 1px solid var(--f-g200); + } + + td { + padding: 10px 12px; + border-bottom: 1px solid var(--f-g100); + font-size: 13px; + color: var(--f-g700); + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--f-g50); + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; + letter-spacing: 0.02em; +} + +.muted { + color: var(--f-g500); + font-size: 11px; +} + +.fiu-empty { + text-align: center; + padding: 30px; + color: var(--f-g500); + font-style: italic; +} + +.fiu-empty-card { + background: #fff; + border: 1px dashed var(--f-g300); + border-radius: 12px; + padding: 50px; + text-align: center; + color: var(--f-g500); + font-size: 13px; +} + +/* PILLS */ +.dir-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + background: var(--f-g100); + color: var(--f-g700); + + &.deposit { + background: var(--f-green-l); + color: var(--f-green); + } + + &.withdrawal { + background: var(--f-red-l); + color: var(--f-red); + } +} + +.rel-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + background: var(--f-blue-l); + color: var(--f-blue); + + &.non-cust { + background: var(--f-amber-l); + color: var(--f-amber); + } +} + +.status-pill { + display: inline-block; + padding: 2px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + + &.st-draft { + background: var(--f-g100); + color: var(--f-g700); + } + + &.st-po-review { + background: var(--f-amber-l); + color: var(--f-amber); + } + + &.st-filed { + background: var(--f-green-l); + color: var(--f-green); + } + + &.st-rejected { + background: var(--f-red-l); + color: var(--f-red); + } +} + +.risk-pill { + display: inline-block; + padding: 2px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + background: var(--f-purple-l); + color: var(--f-purple); + + &.rk-structuring { + background: #fff7ed; + color: #9a3412; + } + + &.rk-dormant-revival { + background: #ecfeff; + color: #155e75; + } + + &.rk-high-risk-country { + background: var(--f-red-l); + color: var(--f-red); + } + + &.rk-unusual-pattern { + background: var(--f-amber-l); + color: var(--f-amber); + } + + &.rk-pep-related { + background: var(--f-purple-l); + color: var(--f-purple); + } + + &.rk-sanctions-hit { + background: #fee2e2; + color: #991b1b; + } +} + +.risk-tag { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + + &.rk-tag-low { + background: var(--f-green-l); + color: var(--f-green); + } + + &.rk-tag-medium { + background: var(--f-amber-l); + color: var(--f-amber); + } + + &.rk-tag-high { + background: var(--f-red-l); + color: var(--f-red); + } +} + +/* STR cards */ +.fiu-str-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.str-card { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + &--filed { + border-color: #86efac; + } +} + +.str-card-h { + padding: 12px 16px; + background: var(--f-g50); + border-bottom: 1px solid var(--f-g200); + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.str-card-id { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.str-ref { + font-family: 'DM Mono', monospace; + font-size: 13px; + font-weight: 700; + color: var(--f-g900); +} + +.str-card-amt { + display: flex; + align-items: baseline; + gap: 6px; + + strong { + font-size: 16px; + color: var(--f-g900); + font-family: 'DM Mono', monospace; + } +} + +.str-card-body { + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.str-meta { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px 18px; + + > div { + display: flex; + flex-direction: column; + + label { + font-size: 10px; + font-weight: 600; + color: var(--f-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1px; + } + + span { + font-size: 13px; + color: var(--f-g900); + } + } +} + +.str-narrative, +.str-po-remarks { + background: var(--f-g50); + border-radius: 8px; + padding: 10px 14px; + + label { + font-size: 10px; + font-weight: 700; + color: var(--f-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + display: flex; + align-items: center; + gap: 5px; + margin-bottom: 4px; + } + + p { + margin: 0; + font-size: 13px; + color: var(--f-g700); + line-height: 1.5; + } +} + +.str-po-remarks { + background: var(--f-green-l); + + label { + color: var(--f-green); + } + + p { + color: #14532d; + } +} + +/* FILES TAB */ +.files-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; +} + +.file-card { + background: #fff; + border: 1px solid var(--f-g200); + border-radius: 12px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 8px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + &--xml { + border-color: var(--f-purple); + background: linear-gradient(135deg, #fff 0%, #faf5ff 100%); + } +} + +.file-card-h { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 700; + color: var(--f-g900); + font-family: 'DM Mono', monospace; +} + +.file-card p { + margin: 0; + font-size: 12px; + color: var(--f-g500); + flex: 1; +} + +.file-card .fiu-btn { + align-self: flex-start; +} + +/* NOTE */ +.fiu-note { + background: var(--f-blue-l); + border: 1px solid #bfdbfe; + border-radius: 10px; + padding: 12px 16px; + display: flex; + gap: 10px; + font-size: 12px; + color: #1e3a8a; + line-height: 1.5; + + fa-icon { + font-size: 14px; + flex-shrink: 0; + margin-top: 2px; + } +} + +/* TOAST */ +.fiu-toast { + position: fixed; + bottom: 32px; + left: 50%; + transform: translateX(-50%) translateY(20px); + background: var(--f-navy); + color: #fff; + padding: 12px 22px; + border-radius: 10px; + font-size: 13px; + font-weight: 500; + box-shadow: 0 6px 20px rgb(0 0 0 / 25%); + opacity: 0; + pointer-events: none; + transition: all 0.25s; + z-index: 1500; + + &.show { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + +@media (width <= 1100px) { + .fiu-kpis { + grid-template-columns: repeat(3, 1fr); + } + + .fiu-compliance { + grid-template-columns: 1fr; + } + + .fiu-filters { + grid-template-columns: 1fr 1fr; + } + + .files-grid { + grid-template-columns: 1fr 1fr; + } + + .str-meta { + grid-template-columns: 1fr 1fr; + } +} + +@media (width <= 720px) { + .fiu-kpis { + grid-template-columns: 1fr 1fr; + } + + .files-grid { + grid-template-columns: 1fr; + } + + .str-meta { + grid-template-columns: 1fr; + } +} diff --git a/src/app/fiu/fiu.component.ts b/src/app/fiu/fiu.component.ts new file mode 100644 index 000000000..e0d404ee3 --- /dev/null +++ b/src/app/fiu/fiu.component.ts @@ -0,0 +1,547 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { + CtrTransaction, + CTR_TXNS, + FIU_BANK, + KC1_INDIVIDUALS, + KC2_ENTITIES, + KycEntity, + KycIndividual, + StrStatus, + StrTransaction, + STR_TXNS +} from './fiu-data'; + +@Component({ + selector: 'mifosx-fiu', + templateUrl: './fiu.component.html', + styleUrls: ['./fiu.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FaIconComponent, + FormsModule + ] +}) +export class FiuComponent { + /** Active tab */ + activeTab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files' = 'dashboard'; + + bank = FIU_BANK; + ctrTxns: CtrTransaction[] = CTR_TXNS; + strTxns: StrTransaction[] = STR_TXNS; + kc1: KycIndividual[] = KC1_INDIVIDUALS; + kc2: KycEntity[] = KC2_ENTITIES; + + /** CTR filters */ + ctrFilterMode: '' | 'Deposit' | 'Withdrawal' = ''; + ctrFilterAccount = ''; + ctrFilterMinAmount = 0; + + /** STR filters */ + strFilterStatus: '' | StrStatus = ''; + strFilterRisk: string = ''; + + /** Toast */ + toastMsg = ''; + toastVisible = false; + private toastTimer: any; + + showTab(tab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files'): void { + this.activeTab = tab; + } + + // -------------------- KPIs -------------------- + get totalReports(): number { + return new Set(this.ctrTxns.map((t) => t.reportRefNo)).size; + } + get totalCtrTxns(): number { + return this.ctrTxns.length; + } + get totalCtrAmount(): number { + return this.ctrTxns.reduce((s, t) => s + t.txnAmount, 0); + } + get pendingStr(): number { + return this.strTxns.filter((s) => s.status === 'draft' || s.status === 'po-review').length; + } + get filedStr(): number { + return this.strTxns.filter((s) => s.status === 'filed').length; + } + get totalCustomers(): number { + return this.kc1.length + this.kc2.length; + } + + // -------------------- Filtered lists -------------------- + get filteredCtr(): CtrTransaction[] { + const acc = this.ctrFilterAccount.trim(); + return this.ctrTxns.filter((t) => { + if (this.ctrFilterMode && t.depositOrWithdrawal !== this.ctrFilterMode) return false; + if (acc && !t.accountNumber.includes(acc)) return false; + if (this.ctrFilterMinAmount > 0 && t.txnAmount < this.ctrFilterMinAmount) return false; + return true; + }); + } + + get filteredStr(): StrTransaction[] { + return this.strTxns.filter((s) => { + if (this.strFilterStatus && s.status !== this.strFilterStatus) return false; + if (this.strFilterRisk && s.riskCategory !== this.strFilterRisk) return false; + return true; + }); + } + + resetCtrFilters(): void { + this.ctrFilterMode = ''; + this.ctrFilterAccount = ''; + this.ctrFilterMinAmount = 0; + } + resetStrFilters(): void { + this.strFilterStatus = ''; + this.strFilterRisk = ''; + } + + // -------------------- Helpers -------------------- + statusLabel(s: StrStatus): string { + return s === 'draft' ? 'Draft' : s === 'po-review' ? 'PO Review' : s === 'filed' ? 'Filed' : 'Rejected'; + } + riskLabel(c: StrTransaction['riskCategory']): string { + const map: Record = { + structuring: 'Structuring', + 'dormant-revival': 'Dormant Revival', + 'high-risk-country': 'High-Risk Country', + 'unusual-pattern': 'Unusual Pattern', + 'pep-related': 'PEP Related', + 'sanctions-hit': 'Sanctions Hit' + }; + return map[c]; + } + + // -------------------- Exports -------------------- + /** TC2 — Branch / cash transaction CSV */ + exportTc2(): void { + const headers = [ + 'Report Type', + 'Report Reference Number', + 'Branch Code of Account', + 'Account Number', + 'Transaction Branch Code', + 'Deposit/Withdrawal', + 'Transaction ID', + 'Transaction Date', + 'Transaction Time', + 'Transaction Amount', + 'Instrument Type', + 'Instrument ID (Card Number/ Cash/ Cheque/ DD/ Others)', + 'Non-Customer Reference Number', + 'Relationship Flag', + 'Third Party PAN (Non-Customer)', + 'Declaration (If Third Party PAN (Non-Customer) is not available)' + ]; + const rows = [headers]; + for (const t of this.filteredCtr) { + rows.push([ + t.reportType, + t.reportRefNo.toString(), + t.branchCode, + t.accountNumber, + t.txnBranchCode, + t.depositOrWithdrawal, + t.txnId.toString(), + t.txnDate.replace(/-/g, '/'), + t.txnTime, + t.txnAmount.toString(), + t.instrumentType, + t.instrumentId, + t.nonCustomerRef, + t.relationshipFlag, + t.thirdPartyPan, + t.declaration + ]); + } + this.saveCsv(rows, 'TC2.csv'); + this.toast(`TC2 exported (${this.filteredCtr.length} rows)`); + } + + /** TC1 header-only template (UI doesn't track ATM/card txns) */ + exportTc1(): void { + const headers = [ + 'Relationship Flag', + 'Transaction Status', + 'Transaction ID', + 'Transaction Date', + 'Transaction Time', + 'Deposit/Withdrawal', + 'Transaction Amount', + 'Account Number', + 'Account Type', + 'Card Type', + 'Card Number', + 'Card Issuing Country', + 'Card Issuing Bank', + 'Report Reference Number', + 'Switch Provider Transaction ID', + 'ATM ID/ CAM ID', + 'Branch Code of Account', + 'Report Type' + ]; + this.saveCsv([headers], 'TC1.csv'); + this.toast('TC1 template exported (header only)'); + } + + /** KC1 — Individual customers */ + exportKc1(): void { + const headers = [ + 'Report Reference Number', + 'UCIC', + 'Customer ID', + 'PAN', + 'Declaration (If PAN is not available)', + 'CKYC Number', + 'Declaration (If CKYC is not available)', + 'Passport Number', + 'Voter ID', + 'Other Occupation', + 'Drivers License Number', + 'NREGA Card', + 'DIN / DPIN', + 'First Name', + 'Middle Name', + 'Last Name', + 'Declaration (If Last name is not available)', + 'Name of Father', + 'Name of Mother', + 'Spouse/Partner Name', + 'Gender', + 'Date of Birth', + 'Nationality', + 'Mobile Number', + 'Alternate Mobile Number', + 'Telephone Number', + 'Email ID', + 'Primary Address 1', + 'Primary Address Country', + 'Primary Address Pin Code', + 'Primary Address Locality', + 'Primary Address State', + 'Primary Address District', + 'Primary Address City', + 'Customer Type', + 'Annual Income (INR)', + 'Occupation', + 'Date of Customer On-boarding', + 'Date of Last KYC / re-KYC', + 'Customer Risk Level', + 'Report Type', + 'Other Identifier Name', + 'Other Identifier Number', + 'Primary Entity' + ]; + const rows = [headers]; + for (const k of this.kc1) { + rows.push([ + k.reportRefNo.toString(), + k.ucic, + k.ucic, + k.pan, + k.pan ? '' : 'NA', + k.ckycNumber, + k.ckycNumber ? '' : 'NA', + '', + '', + '', + '', + '', + '', + k.firstName, + k.middleName, + k.lastName, + '', + k.fatherName, + k.motherName, + '', + k.gender, + k.dob.replace(/-/g, '/'), + '97', + k.mobileNumber, + '', + '', + k.email, + k.primaryAddress, + '95', + k.pincode, + k.primaryAddress.split(' ').slice(0, 4).join(' '), + k.state, + '501', + k.city, + k.customerType, + k.annualIncome.toString(), + k.occupation, + k.onboardingDate.replace(/-/g, '/'), + k.lastKycDate.replace(/-/g, '/'), + k.riskLevel, + k.reportType, + k.otherIdentifierName || '', + k.otherIdentifierNumber || '', + 'Yes' + ]); + } + this.saveCsv(rows, 'KC1.csv'); + this.toast(`KC1 exported (${this.kc1.length} individuals)`); + } + + /** KC2 — Entity customers */ + exportKc2(): void { + const headers = [ + 'Report Reference Number', + 'UCIC', + 'Entity Name', + 'Company ID Number', + 'Company ID Type', + 'PAN Number', + 'Declaration (If PAN is not available)', + 'TAN', + 'GSTIN', + 'IEC', + 'Declaration (If IEC is not available)', + 'No identifier available', + 'Telephone Number', + 'Mobile Number', + 'Email ID', + 'Company Website', + 'Country', + 'Address Line 1', + 'PIN Code', + 'Locality', + 'State', + 'District', + 'City / Village / Town', + 'Date Of Incorporation', + 'Customer Type', + 'Last KYC Date', + 'Onboarding Date', + 'Customer Risk Level', + 'Report Type', + 'Sole Proprietor Firm', + 'UCIC of Owner of Sole Proprietor Firm', + 'Name of Owner of Sole Proprietor Firm' + ]; + const rows = [headers]; + for (const k of this.kc2) { + rows.push([ + k.reportRefNo.toString(), + k.ucic, + k.entityName, + '', + '', + k.panNumber, + '', + '', + k.gstin, + '', + 'NA', + '', + '', + k.mobileNumber, + '', + '', + '95', + k.address, + k.pincode, + k.address.split(' ').slice(0, 4).join(' '), + k.state, + '501', + k.city, + k.dateOfIncorporation.replace(/-/g, '/'), + k.customerType, + k.lastKycDate.replace(/-/g, '/'), + k.onboardingDate.replace(/-/g, '/'), + k.riskLevel, + k.reportType, + k.isSoleProprietor ? 'Yes' : 'No', + k.ownerUcic, + k.ownerName + ]); + } + this.saveCsv(rows, 'KC2.csv'); + this.toast(`KC2 exported (${this.kc2.length} entities)`); + } + + /** Account_Detail.csv — per-report account aggregates (12-month) */ + exportAccountDetail(): void { + const headers = [ + 'Report Type', + 'Report Reference Number', + 'Account Type', + 'Account Number', + 'Branch Code of Account', + 'Date of Account Opening', + 'Date of Account Closing', + 'Account Status', + 'Reason for Account Freeze', + 'No Of Debits (In last 12 months)', + 'Total Debit Amount (In last 12 months)', + 'No Of Credits (In last 12 months)', + 'Total Credit Amount (In last 12 months)', + 'No Of Cash Transaction (In last 12 months)', + 'Total Cash Deposit (In last 12 months)', + 'Total Cash Withdrawal (In last 12 months) Amount' + ]; + const rows = [headers]; + // Build one aggregate row per (reportRefNo, accountNumber) pair + const seen = new Set(); + for (const t of this.ctrTxns) { + const key = `${t.reportRefNo}|${t.accountNumber}`; + if (seen.has(key)) continue; + seen.add(key); + const sameKey = this.ctrTxns.filter( + (x) => x.reportRefNo === t.reportRefNo && x.accountNumber === t.accountNumber + ); + const debits = sameKey.filter((x) => x.depositOrWithdrawal === 'Withdrawal'); + const credits = sameKey.filter((x) => x.depositOrWithdrawal === 'Deposit'); + const cashTxns = sameKey.filter((x) => x.instrumentType === 'Cash'); + rows.push([ + t.reportType, + t.reportRefNo.toString(), + '212', + t.accountNumber, + t.branchCode, + '20/05/2018', + '', + 'ACTIVE', + '', + debits.length.toString(), + debits.reduce((s, x) => s + x.txnAmount, 0).toString(), + credits.length.toString(), + credits.reduce((s, x) => s + x.txnAmount, 0).toString(), + cashTxns.length.toString(), + cashTxns + .filter((x) => x.depositOrWithdrawal === 'Deposit') + .reduce((s, x) => s + x.txnAmount, 0) + .toString(), + cashTxns + .filter((x) => x.depositOrWithdrawal === 'Withdrawal') + .reduce((s, x) => s + x.txnAmount, 0) + .toString() + ]); + } + this.saveCsv(rows, 'Account_Detail.csv'); + this.toast(`Account_Detail exported (${rows.length - 1} accounts)`); + } + + /** Account_Person_Relation.csv — account ↔ customer mapping */ + exportAccountPersonRelation(): void { + const headers = [ + 'Report Type', + 'Report Reference Number', + 'Branch Code of Account', + 'Account Number', + 'Relationship Type', + 'Unique Reference Number', + 'Individual / Non-individual', + 'Name of Non-customer' + ]; + const rows = [headers]; + const seen = new Set(); + for (const t of this.ctrTxns) { + const key = `${t.reportRefNo}|${t.accountNumber}`; + if (seen.has(key)) continue; + seen.add(key); + // Find matching KC1 by reportRefNo (best-effort dummy mapping) + const k = this.kc1.find((x) => x.reportRefNo === t.reportRefNo); + rows.push([ + t.reportType, + t.reportRefNo.toString(), + t.branchCode, + t.accountNumber, + '1', + k ? k.ucic : '0', + 'Individual', + t.relationshipFlag === 'Non-Customer' ? t.customerName || '' : '' + ]); + } + this.saveCsv(rows, 'Account_Person_Relation.csv'); + this.toast(`Account_Person_Relation exported (${rows.length - 1} rows)`); + } + + /** STR XML stub (FINnet 2.0 STR XML) */ + exportStrXml(): void { + const xml: string[] = []; + xml.push(''); + xml.push(``); + xml.push(` `); + xml.push(` `); + for (const s of this.filteredStr) { + xml.push(` `); + xml.push(` ${s.accountNumber}`); + xml.push( + ` ` + ); + xml.push(` ${s.riskCategory}`); + xml.push(` `); + xml.push(` ${this.escapeXml(s.narrative)}`); + if (s.poRemarks) xml.push(` ${this.escapeXml(s.poRemarks)}`); + xml.push(' '); + } + xml.push(''); + const blob = new Blob([xml.join('\n')], { type: 'application/xml;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `STR-${this.bank.reportingMonth.replace(/\s+/g, '-')}.xml`; + a.click(); + URL.revokeObjectURL(url); + this.toast(`STR XML exported (${this.filteredStr.length} suspects)`); + } + + /** Bundle: all 6 CSVs zipped — emitted as separate downloads sequentially */ + exportAll(): void { + setTimeout(() => this.exportTc1(), 0); + setTimeout(() => this.exportTc2(), 200); + setTimeout(() => this.exportKc1(), 400); + setTimeout(() => this.exportKc2(), 600); + setTimeout(() => this.exportAccountDetail(), 800); + setTimeout(() => this.exportAccountPersonRelation(), 1000); + } + + private escapeXml(s: string): string { + return (s || '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n'); + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + toast(msg: string): void { + this.toastMsg = msg; + this.toastVisible = true; + if (this.toastTimer) clearTimeout(this.toastTimer); + this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); + } +} diff --git a/src/app/fiu/fiu.module.ts b/src/app/fiu/fiu.module.ts new file mode 100644 index 000000000..6dd2f5776 --- /dev/null +++ b/src/app/fiu/fiu.module.ts @@ -0,0 +1,22 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { FiuRoutingModule } from './fiu-routing.module'; +import { FiuComponent } from './fiu.component'; + +@NgModule({ + imports: [ + SharedModule, + FiuRoutingModule, + FiuComponent + ] +}) +export class FiuModule {} diff --git a/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.html b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.html new file mode 100644 index 000000000..6d76cb648 --- /dev/null +++ b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.html @@ -0,0 +1,646 @@ + + +
+ +
+
+
+
+

Accounting Reports — {{ currentReport.name }}

+

{{ currentReport.description }}

+
+
+
+  {{ bank.name }} + +  FY {{ bank.fiscalYear }} + + +
+
+ + +
+ Report: + + {{ currentReport.group }} +
+ +
+ + +
+
+
Total Income
+
₹ {{ totalIncome / 100 | number: '1.2-2' }} Cr
+
{{ totalIncome | number: '1.2-2' }} Lakh
+
+
+
Total Expenditure
+
₹ {{ totalExpenditure / 100 | number: '1.2-2' }} Cr
+
{{ totalExpenditure | number: '1.2-2' }} Lakh
+
+
+
Net Profit / (Loss)
+
+ {{ netProfit >= 0 ? '+' : '' }}₹ {{ netProfit / 100 | number: '1.2-2' }} Cr +
+
Margin: {{ netProfitPct.toFixed(2) }}%
+
+
+
Reporting Period
+
{{ bank.period }}
+
{{ bank.branchCount }} branches consolidated
+
+
+ +
+
+

Profit & Loss A/c — FY {{ bank.fiscalYear }} (₹ in Lakhs)

+ +
+ +
+ +
+

Income

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ sub }}₹ Lakh
{{ l.code }}{{ l.label }}{{ l.amount | number: '1.2-2' }}
+ Sub-total — {{ sub }} + + {{ subTotal('INCOME', sub) | number: '1.2-2' }} +
Total Income + {{ totalIncome | number: '1.2-2' }} +
+
+
+ + +
+

Expenditure

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ sub }}₹ Lakh
{{ l.code }}{{ l.label }}{{ l.amount | number: '1.2-2' }}
+ Sub-total — {{ sub }} + + {{ subTotal('EXPENDITURE', sub) | number: '1.2-2' }} +
Total Expenditure + {{ totalExpenditure | number: '1.2-2' }} +
+
+
+
+ + +
+ +   + Net {{ netProfit >= 0 ? 'Profit' : 'Loss' }} for the Year + + + ₹ {{ netProfit | number: '1.2-2' }} Lakh + ({{ netProfitPct.toFixed(2) }}% of Income) + +
+
+
+ + + +
+
+
Total Capital & Liabilities
+
₹ {{ totalLiabilities / 100 | number: '1.2-2' }} Cr
+
{{ totalLiabilities | number: '1.2-2' }} Lakh
+
+
+
Total Assets
+
₹ {{ totalAssets / 100 | number: '1.2-2' }} Cr
+
{{ totalAssets | number: '1.2-2' }} Lakh
+
+
+
B/S Tally
+
+ {{ totalLiabilities === totalAssets ? 'BALANCED' : 'MISMATCH' }} +
+
Diff: {{ totalAssets - totalLiabilities | number: '1.2-2' }}
+
+
+
As On Date
+
{{ bank.asOnDate }}
+
{{ bank.quarter }}
+
+
+ +
+
+

Balance Sheet — As on {{ bank.asOnDate }} (₹ in Lakhs)

+ +
+ +
+ +
+

Capital & Liabilities

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ g }}₹ Lakh
{{ l.code }}{{ l.label }}{{ l.amount | number: '1.2-2' }}
Sub-total + {{ bsSubTotal('CAPITAL-LIAB', g) | number: '1.2-2' }} +
Total Capital & Liabilities + {{ totalLiabilities | number: '1.2-2' }} +
+
+
+ + +
+

Assets

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ g }}₹ Lakh
{{ l.code }}{{ l.label }}{{ l.amount | number: '1.2-2' }}
Sub-total + {{ bsSubTotal('ASSETS', g) | number: '1.2-2' }} +
Total Assets + {{ totalAssets | number: '1.2-2' }} +
+
+
+
+
+
+ + + +
+
+
Total Liab — {{ bank.fiscalYear }}
+
₹ {{ totalLiabilities / 100 | number: '1.2-2' }} Cr
+
vs {{ totalLiabilitiesPrev / 100 | number: '1.2-2' }} Cr (PY)
+
+
+
Total Assets — {{ bank.fiscalYear }}
+
₹ {{ totalAssets / 100 | number: '1.2-2' }} Cr
+
vs {{ totalAssetsPrev / 100 | number: '1.2-2' }} Cr (PY)
+
+
+
Asset Growth %
+
+{{ variationPct(totalAssets, totalAssetsPrev).toFixed(2) }}%
+
{{ variation(totalAssets, totalAssetsPrev) | number: '1.2-2' }} Lakh
+
+
+
Deposit Growth %
+
+ +{{ + variationPct( + bsSubTotal('CAPITAL-LIAB', 'Deposits'), + bsSubTotal('CAPITAL-LIAB', 'Deposits', true) + ).toFixed(2) + }}% +
+
All deposit categories
+
+
+ +
+
+

+ Comparative Balance Sheet — FY {{ bank.fiscalYear }} vs FY {{ bank.prevFiscalYear }} +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeParticularsSide / GroupFY {{ bank.fiscalYear }}FY {{ bank.prevFiscalYear }}VariationVariation %
{{ l.code }}{{ l.label }} + {{ + l.group + }} + + {{ l.amount | number: '1.2-2' }} + {{ l.prevAmount | number: '1.2-2' }} + {{ variation(l.amount, l.prevAmount) >= 0 ? '+' : '' + }}{{ variation(l.amount, l.prevAmount) | number: '1.2-2' }} + + + {{ variationPct(l.amount, l.prevAmount) >= 0 ? '+' : '' + }}{{ variationPct(l.amount, l.prevAmount).toFixed(2) }}% + +
TOTAL CAPITAL & LIABILITIES + {{ totalLiabilities | number: '1.2-2' }} + {{ totalLiabilitiesPrev | number: '1.2-2' }} + {{ variation(totalLiabilities, totalLiabilitiesPrev) | number: '1.2-2' }} + + +{{ variationPct(totalLiabilities, totalLiabilitiesPrev).toFixed(2) }}% +
TOTAL ASSETS + {{ totalAssets | number: '1.2-2' }} + {{ totalAssetsPrev | number: '1.2-2' }}{{ variation(totalAssets, totalAssetsPrev) | number: '1.2-2' }} + +{{ variationPct(totalAssets, totalAssetsPrev).toFixed(2) }}% +
+
+
+
+ + + +
+
+
GL Accounts
+
{{ glReport.length }}
+
Filtered: {{ filteredGl.length }}
+
+
+
Total Credits
+
₹ {{ glTotalCredit / 100 | number: '1.2-2' }} Cr
+
{{ glTotalCredit | number: '1.2-2' }} Lakh
+
+
+
Total Debits
+
₹ {{ glTotalDebit / 100 | number: '1.2-2' }} Cr
+
{{ glTotalDebit | number: '1.2-2' }} Lakh
+
+
+
Period
+
+ {{ bank.quarterFrom }} + + {{ bank.quarterTo }} +
+
Consolidated across {{ bank.branchCount }} branches
+
+
+ +
+
+

GL Wise Debit / Credit Report

+ +
+ +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
A/C CodeDescriptionGroupOpeningDebitCreditClosing
{{ g.acCode }}{{ g.description }} + {{ g.group }} + {{ g.opening | number: '1.2-2' }}{{ g.debit | number: '1.2-2' }}{{ g.credit | number: '1.2-2' }} + {{ g.closing | number: '1.2-2' }} +
TOTAL— + {{ glTotalDebit | number: '1.2-2' }} + + {{ glTotalCredit | number: '1.2-2' }} + —
No GL accounts match the current filters.
+
+
+
+ + + +
+
+
Total Vouchers
+
{{ miscExp.length }}
+
Filtered: {{ filteredMisc.length }}
+
+
+
Total Withdrawal
+
₹ {{ miscTotalWithdrawal | number }}
+
Filtered subset
+
+
+
Total Deposit (Reversals)
+
₹ {{ miscTotalDeposit | number }}
+
Cancelled / refunded
+
+
+
Period
+
+ {{ bank.quarterFrom }} + + {{ bank.quarterTo }} +
+
Report Ref {{ bank.reportRefNo }}
+
+
+ +
+
+

Misc Expense Ledger — {{ bank.quarterFrom }} TO {{ bank.quarterTo }}

+ +
+ +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateBranchVoucherCHQ_NODescriptionModeWithdrawalDepositBalance
{{ r.txnDate }} + {{ r.branch }} ({{ r.branchCode }}) + {{ r.voucherNo }}{{ r.chequeNo }}{{ r.description }} + {{ r.mode }} + + {{ r.withdrawal > 0 ? (r.withdrawal | number) : '—' }} + + {{ r.deposit > 0 ? (r.deposit | number) : '—' }} + + {{ r.balance | number }} +
GRAND TOTAL + {{ miscTotalWithdrawal | number }} + + {{ miscTotalDeposit | number }} +
No vouchers match the current filters.
+
+ +

Branch-wise Net Total

+
+ + + + + + + + + + + + + +
BranchNet Withdrawal (₹)
{{ bt.branch }}{{ bt.total | number }}
+
+
+
+
+ + + +
diff --git a/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.scss b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.scss new file mode 100644 index 000000000..3e56295d9 --- /dev/null +++ b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.scss @@ -0,0 +1,706 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --acr-good: #15803d; + --acr-good-l: #dcfce7; + --acr-mid: #d97706; + --acr-mid-l: #fef3c7; + --acr-low: #c0392b; + --acr-low-l: #fdecea; + --acr-amber: #b45309; + --acr-amber-l: #fef3c7; + --acr-emerald: #059669; + --acr-emerald-l: #d1fae5; + --acr-blue: #2563eb; + --acr-blue-l: #dbeafe; + --acr-purple: #6d28d9; + --acr-purple-l: #ede9fe; + --acr-pink: #be185d; + --acr-pink-l: #fce7f3; + --acr-g50: #f8fafc; + --acr-g100: #f1f5f9; + --acr-g200: #e2e8f0; + --acr-g300: #cbd5e1; + --acr-g500: #64748b; + --acr-g700: #334155; + --acr-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--acr-g900); +} + +.acr-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 92vh; +} + +/* HEADER */ +.acr-head { + padding: 18px 22px; + border-bottom: 1px solid var(--acr-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #fffbeb 0%, #fff 100%); +} + +.acr-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.acr-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--acr-amber-l); + color: var(--acr-amber); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.acr-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--acr-g900); +} + +.acr-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--acr-g500); +} + +.acr-head-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.acr-meta { + display: inline-flex; + align-items: center; + font-size: 11px; + color: var(--acr-g500); + padding: 5px 10px; + background: #fff; + border: 1px solid var(--acr-g200); + border-radius: 6px; + + fa-icon { + color: var(--acr-amber); + margin-right: 4px; + } +} + +.acr-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--acr-g200); + background: #fff; + font-size: 14px; + color: var(--acr-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--acr-g100); + color: var(--acr-g900); + } +} + +/* SELECTOR */ +.acr-sel-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 22px; + background: var(--acr-g50); + border-bottom: 1px solid var(--acr-g200); + flex-shrink: 0; + flex-wrap: wrap; +} + +.acr-sel-l { + font-size: 11px; + font-weight: 700; + color: var(--acr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-right: 4px; +} + +.acr-sel-pill { + padding: 7px 16px; + border-radius: 999px; + border: 1px solid var(--acr-g200); + background: #fff; + cursor: pointer; + font-family: inherit; + font-size: 12px; + font-weight: 600; + color: var(--acr-g700); + transition: all 0.15s; + display: inline-flex; + align-items: center; + + fa-icon { + color: var(--acr-g500); + } + + &:hover { + background: var(--acr-amber-l); + border-color: var(--acr-amber); + color: var(--acr-amber); + + fa-icon { + color: var(--acr-amber); + } + } + + &.active { + background: var(--acr-amber); + border-color: var(--acr-amber); + color: #fff; + box-shadow: 0 2px 6px rgb(180 83 9 / 25%); + + fa-icon { + color: #fff; + } + } +} + +.acr-sel-group-tag { + margin-left: auto; + font-size: 10px; + font-weight: 700; + color: var(--acr-g500); + padding: 4px 10px; + background: #fff; + border: 1px solid var(--acr-g200); + border-radius: 6px; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* BODY */ +.acr-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + flex: 1; + background: var(--acr-g50); +} + +/* STATS */ +.acr-stats { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 12px; +} + +.acr-stat-card { + background: #fff; + border: 1px solid var(--acr-g200); + border-radius: 12px; + padding: 14px 16px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + &.alert { + background: var(--acr-low-l); + border-color: var(--acr-low); + + .acr-stat-v { + color: var(--acr-low); + } + } + + &.warn { + background: var(--acr-mid-l); + border-color: var(--acr-mid); + + .acr-stat-v { + color: var(--acr-mid); + } + } + + &.good { + background: var(--acr-good-l); + border-color: var(--acr-good); + + .acr-stat-v { + color: var(--acr-good); + } + } +} + +.acr-stat-l { + font-size: 11px; + font-weight: 600; + color: var(--acr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.acr-stat-v { + font-size: 22px; + font-weight: 700; + color: var(--acr-g900); + line-height: 1.2; + margin-top: 2px; +} + +.acr-stat-meta { + font-size: 11px; + color: var(--acr-g500); + margin-top: 4px; +} + +/* SECTION */ +.acr-section { + background: #fff; + border: 1px solid var(--acr-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.acr-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.acr-sec-t { + margin: 0; + font-size: 14px; + font-weight: 700; + color: var(--acr-g700); +} + +.acr-sub-tt { + margin: 18px 0 8px; + font-size: 12px; + font-weight: 700; + color: var(--acr-g700); + text-transform: uppercase; + letter-spacing: 0.05em; + padding-bottom: 4px; + border-bottom: 1px dashed var(--acr-g200); +} + +.acr-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: var(--acr-amber) !important; +} + +/* P&L / B/S TWO-COLUMN GRID */ +.acr-pnl-grid, +.acr-bs-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; +} + +.acr-pnl-side { + display: flex; + flex-direction: column; + gap: 8px; +} + +.acr-pnl-side-t { + margin: 0; + padding: 8px 12px; + font-size: 13px; + font-weight: 700; + border-radius: 6px; + + &.income { + background: var(--acr-good-l); + color: var(--acr-good); + } + + &.expense { + background: var(--acr-low-l); + color: var(--acr-low); + } +} + +.acr-pnl-net { + margin-top: 14px; + padding: 14px 18px; + border-radius: 10px; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + + &.good { + background: var(--acr-good-l); + color: var(--acr-good); + } + + &.alert { + background: var(--acr-low-l); + color: var(--acr-low); + } +} + +.acr-pnl-net-l { + display: flex; + align-items: center; + font-weight: 700; +} + +.acr-pnl-net-v { + font-weight: 700; + font-size: 16px; + + .acr-pnl-net-pct { + margin-left: 6px; + font-size: 12px; + font-weight: 600; + opacity: 0.8; + } +} + +/* FILTERS */ +.acr-filters { + display: flex; + gap: 12px; + padding: 12px 14px; + background: var(--acr-g50); + border: 1px solid var(--acr-g200); + border-radius: 8px; + margin-bottom: 12px; + align-items: flex-end; + flex-wrap: wrap; +} + +.acr-f { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 160px; + + label { + font-size: 10px; + font-weight: 600; + color: var(--acr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + select, + input { + height: 32px; + padding: 0 10px; + border: 1px solid var(--acr-g200); + border-radius: 6px; + background: #fff; + font-family: inherit; + font-size: 12px; + color: var(--acr-g900); + + &:focus { + outline: none; + border-color: var(--acr-amber); + box-shadow: 0 0 0 3px rgb(180 83 9 / 12%); + } + } +} + +.acr-f-reset { + height: 32px; + width: 32px; + border-radius: 6px; + border: 1px solid var(--acr-g200); + background: #fff; + color: var(--acr-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--acr-low-l); + color: var(--acr-low); + border-color: var(--acr-low); + } +} + +/* TABLE */ +.acr-tbl-wrap { + border: 1px solid var(--acr-g200); + border-radius: 8px; + overflow: auto; +} + +.acr-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--acr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--acr-g50); + border-bottom: 1px solid var(--acr-g200); + white-space: nowrap; + } + + td { + padding: 8px 14px; + border-bottom: 1px solid var(--acr-g100); + font-size: 13px; + color: var(--acr-g700); + vertical-align: middle; + + &.pos { + color: var(--acr-good); + font-weight: 600; + } + + &.neg { + color: var(--acr-low); + font-weight: 600; + } + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--acr-g50); + } + + .row-sub-total td { + background: var(--acr-g100); + color: var(--acr-g900); + } + + .row-total td { + background: var(--acr-amber-l); + color: var(--acr-g900); + font-weight: 700; + border-top: 2px solid var(--acr-amber); + } + + .row-total.income td { + background: var(--acr-good-l); + border-top-color: var(--acr-good); + color: var(--acr-good); + } + + .row-total.expense td { + background: var(--acr-low-l); + border-top-color: var(--acr-low); + color: var(--acr-low); + } + + .acr-sub-h { + font-size: 11px; + color: var(--acr-amber); + font-weight: 700; + background: #fff; + border-bottom: 1px solid var(--acr-g200); + } +} + +.cell-acc { + color: var(--acr-amber); + font-weight: 700; +} + +/* GROUP PILLS */ +.grp-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 6px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.04em; + + &.gl-group-asset { + background: var(--acr-blue-l); + color: var(--acr-blue); + } + + &.gl-group-liability { + background: var(--acr-purple-l); + color: var(--acr-purple); + } + + &.gl-group-income { + background: var(--acr-good-l); + color: var(--acr-good); + } + + &.gl-group-expense { + background: var(--acr-low-l); + color: var(--acr-low); + } +} + +.side-pill { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + font-size: 10px; + font-weight: 600; + + &.side-liab { + background: var(--acr-low-l); + color: var(--acr-low); + } + + &.side-asset { + background: var(--acr-good-l); + color: var(--acr-good); + } +} + +.mode-pill { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + font-family: 'DM Mono', monospace; + font-size: 10px; + font-weight: 700; + + &.mode-cash { + background: var(--acr-mid-l); + color: var(--acr-mid); + } + + &.mode-cheque { + background: var(--acr-blue-l); + color: var(--acr-blue); + } + + &.mode-trf { + background: var(--acr-purple-l); + color: var(--acr-purple); + } + + &.mode-neft { + background: var(--acr-emerald-l); + color: var(--acr-emerald); + } +} + +.muted { + color: var(--acr-g300); + font-weight: 400; +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.empty { + text-align: center; + color: var(--acr-g500); + padding: 24px !important; + font-style: italic; +} + +.pos { + color: var(--acr-good); +} + +.neg { + color: var(--acr-low); +} + +/* DIALOG FOOTER */ +.acr-footer { + padding: 12px 22px; + border-top: 1px solid var(--acr-g200); + background: #fff; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-shrink: 0; + flex-wrap: wrap; +} + +.acr-footer-l { + font-size: 12px; + color: var(--acr-g500); + display: flex; + align-items: center; + + fa-icon { + color: var(--acr-amber); + margin-right: 4px; + } +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .acr-stats { + grid-template-columns: repeat(2, 1fr); + } + + .acr-pnl-grid, + .acr-bs-grid { + grid-template-columns: 1fr; + } +} + +@media (width <= 720px) { + .acr-stats { + grid-template-columns: 1fr; + } + + .acr-head, + .acr-sel-bar, + .acr-footer { + flex-wrap: wrap; + } +} diff --git a/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.ts b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.ts new file mode 100644 index 000000000..817c5ab5b --- /dev/null +++ b/src/app/home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component.ts @@ -0,0 +1,1292 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +export type AcctReportCode = 'PNL' | 'BALANCE-SHEET' | 'COMPARATIVE-BS' | 'GL-DR-CR' | 'MISC-EXP'; + +export interface AcctReportsDialogData { + initialReport?: AcctReportCode; +} + +interface PnlLine { + code: string; + label: string; + amount: number; + group: 'INCOME' | 'EXPENDITURE'; + subgroup: string; +} + +interface BsLine { + code: string; + label: string; + amount: number; + prevAmount: number; + side: 'CAPITAL-LIAB' | 'ASSETS'; + group: string; +} + +interface GlLine { + acCode: string; + description: string; + group: 'ASSET' | 'LIABILITY' | 'INCOME' | 'EXPENSE'; + opening: number; + debit: number; + credit: number; + closing: number; +} + +interface MiscExpRow { + txnDate: string; + branch: string; + branchCode: string; + voucherNo: string; + chequeNo: string; + description: string; + mode: 'CASH' | 'CHEQUE' | 'TRF' | 'NEFT'; + withdrawal: number; + deposit: number; + balance: number; +} + +@Component({ + selector: 'mifosx-accounting-reports-dialog', + standalone: true, + templateUrl: './accounting-reports-dialog.component.html', + styleUrls: ['./accounting-reports-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + FormsModule, + MatDialogModule, + MatButtonModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class AccountingReportsDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeReport: AcctReportCode; + + bank = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + asOnDate: '31-03-2026', + period: '01-04-2025 to 31-03-2026', + fiscalYear: '2025-26', + prevFiscalYear: '2024-25', + quarter: 'Q4 FY 2025-26', + quarterFrom: '01-10-2025', + quarterTo: '31-12-2025', + principalOfficer: 'Karthik Kamath', + poDesignation: 'Chief Finance Officer', + auditor: 'M/s Sharma & Associates, Chartered Accountants', + reportRefNo: '157/2', + userCode: 'BUBL01', + branchCount: 15 + }; + + reports = [ + { + code: 'PNL' as AcctReportCode, + name: 'Profit & Loss', + icon: 'chart-line', + description: 'Income, expenditure & net profit for the financial year.', + group: 'Financial Statement' + }, + { + code: 'BALANCE-SHEET' as AcctReportCode, + name: 'Balance Sheet', + icon: 'file-alt', + description: 'Capital, liabilities & assets as on reporting date.', + group: 'Financial Statement' + }, + { + code: 'COMPARATIVE-BS' as AcctReportCode, + name: 'Comparative B/S', + icon: 'balance-scale', + description: 'Side-by-side comparison of current vs previous fiscal year.', + group: 'Financial Statement' + }, + { + code: 'GL-DR-CR' as AcctReportCode, + name: 'GL Wise DR/CR', + icon: 'project-diagram', + description: 'GL-account-wise debit/credit consolidated movement.', + group: 'GL Report' + }, + { + code: 'MISC-EXP' as AcctReportCode, + name: 'Misc Expense Ledger', + icon: 'receipt', + description: 'Voucher-level register of miscellaneous expenses.', + group: 'Register' + } + ]; + + // -------------------- P&L Data (₹ in Lakhs) -------------------- + pnl: PnlLine[] = [ + { code: 'I-01', group: 'INCOME', subgroup: 'Interest Earned', label: 'Interest on Advances', amount: 38420 }, + { code: 'I-02', group: 'INCOME', subgroup: 'Interest Earned', label: 'Interest on Investments', amount: 14860 }, + { + code: 'I-03', + group: 'INCOME', + subgroup: 'Interest Earned', + label: 'Interest on Balances with RBI / Other Banks', + amount: 1240 + }, + { + code: 'I-04', + group: 'INCOME', + subgroup: 'Other Income', + label: 'Commission, Exchange & Brokerage', + amount: 1820 + }, + { code: 'I-05', group: 'INCOME', subgroup: 'Other Income', label: 'Profit on Sale of Investments', amount: 480 }, + { code: 'I-06', group: 'INCOME', subgroup: 'Other Income', label: 'Dividend Income', amount: 142 }, + { code: 'I-07', group: 'INCOME', subgroup: 'Other Income', label: 'Miscellaneous Income', amount: 318 }, + { code: 'E-01', group: 'EXPENDITURE', subgroup: 'Interest Expended', label: 'Interest on Deposits', amount: 28640 }, + { + code: 'E-02', + group: 'EXPENDITURE', + subgroup: 'Interest Expended', + label: 'Interest on RBI / Inter-Bank Borrowings', + amount: 980 + }, + { + code: 'E-03', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Payments to & Provisions for Employees', + amount: 8420 + }, + { + code: 'E-04', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Rent, Taxes & Lighting', + amount: 1240 + }, + { code: 'E-05', group: 'EXPENDITURE', subgroup: 'Operating Expenses', label: 'Printing & Stationery', amount: 184 }, + { + code: 'E-06', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Depreciation on Bank Property', + amount: 642 + }, + { + code: 'E-07', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Auditors Fees & Expenses', + amount: 124 + }, + { + code: 'E-08', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Repairs, Maintenance & Insurance', + amount: 386 + }, + { + code: 'E-09', + group: 'EXPENDITURE', + subgroup: 'Operating Expenses', + label: 'Other Operating Expenses', + amount: 824 + }, + { + code: 'E-10', + group: 'EXPENDITURE', + subgroup: 'Provisions', + label: 'Provisions for NPA / Standard Assets', + amount: 4280 + }, + { code: 'E-11', group: 'EXPENDITURE', subgroup: 'Provisions', label: 'Provisions for Tax', amount: 2120 } + ]; + + // -------------------- Balance Sheet (₹ in Lakhs) -------------------- + bs: BsLine[] = [ + { + code: 'L-01', + side: 'CAPITAL-LIAB', + group: 'Capital & Reserves', + label: 'Capital (Paid-up Share Capital)', + amount: 22000, + prevAmount: 20800 + }, + { + code: 'L-02', + side: 'CAPITAL-LIAB', + group: 'Capital & Reserves', + label: 'Reserves & Surplus', + amount: 32400, + prevAmount: 28640 + }, + { + code: 'L-03', + side: 'CAPITAL-LIAB', + group: 'Deposits', + label: 'Demand Deposits — Current Accounts', + amount: 84200, + prevAmount: 78420 + }, + { + code: 'L-04', + side: 'CAPITAL-LIAB', + group: 'Deposits', + label: 'Savings Bank Deposits', + amount: 218600, + prevAmount: 198400 + }, + { + code: 'L-05', + side: 'CAPITAL-LIAB', + group: 'Deposits', + label: 'Term Deposits', + amount: 462800, + prevAmount: 412400 + }, + { + code: 'L-06', + side: 'CAPITAL-LIAB', + group: 'Borrowings', + label: 'Borrowings from RBI / NABARD', + amount: 14180, + prevAmount: 16240 + }, + { + code: 'L-07', + side: 'CAPITAL-LIAB', + group: 'Borrowings', + label: 'Other Borrowings', + amount: 10000, + prevAmount: 9420 + }, + { + code: 'L-08', + side: 'CAPITAL-LIAB', + group: 'Other Liabilities', + label: 'Bills Payable', + amount: 4420, + prevAmount: 3980 + }, + { + code: 'L-09', + side: 'CAPITAL-LIAB', + group: 'Other Liabilities', + label: 'Inter-Office Adjustments (Net)', + amount: 1820, + prevAmount: 1640 + }, + { + code: 'L-10', + side: 'CAPITAL-LIAB', + group: 'Other Liabilities', + label: 'Interest Accrued', + amount: 6240, + prevAmount: 5840 + }, + { + code: 'L-11', + side: 'CAPITAL-LIAB', + group: 'Other Liabilities', + label: 'Other Liabilities & Provisions', + amount: 5940, + prevAmount: 5420 + }, + { + code: 'A-01', + side: 'ASSETS', + group: 'Cash & Bank Balances', + label: 'Cash on Hand', + amount: 8420, + prevAmount: 7820 + }, + { + code: 'A-02', + side: 'ASSETS', + group: 'Cash & Bank Balances', + label: 'Balances with RBI', + amount: 38400, + prevAmount: 34200 + }, + { + code: 'A-03', + side: 'ASSETS', + group: 'Cash & Bank Balances', + label: 'Balances with Other Banks — Current', + amount: 14800, + prevAmount: 12400 + }, + { + code: 'A-04', + side: 'ASSETS', + group: 'Cash & Bank Balances', + label: 'Balances with Other Banks — Term Deposits', + amount: 10000, + prevAmount: 9200 + }, + { + code: 'A-05', + side: 'ASSETS', + group: 'Money at Call', + label: 'Money at Call & Short Notice', + amount: 12400, + prevAmount: 10800 + }, + { + code: 'A-06', + side: 'ASSETS', + group: 'Investments', + label: 'Government Securities (SLR)', + amount: 142400, + prevAmount: 128600 + }, + { + code: 'A-07', + side: 'ASSETS', + group: 'Investments', + label: 'Other Approved Securities', + amount: 28200, + prevAmount: 24800 + }, + { + code: 'A-08', + side: 'ASSETS', + group: 'Investments', + label: 'Shares / Debentures / Bonds', + amount: 14000, + prevAmount: 12200 + }, + { + code: 'A-09', + side: 'ASSETS', + group: 'Advances', + label: 'Cash Credit, Overdraft & Demand Loans', + amount: 168400, + prevAmount: 152800 + }, + { + code: 'A-10', + side: 'ASSETS', + group: 'Advances', + label: 'Term Loans (Secured)', + amount: 217865, + prevAmount: 198400 + }, + { + code: 'A-11', + side: 'ASSETS', + group: 'Advances', + label: 'Term Loans (Unsecured)', + amount: 30000, + prevAmount: 27200 + }, + { + code: 'A-12', + side: 'ASSETS', + group: 'Fixed Assets', + label: 'Premises (Net of Depreciation)', + amount: 9420, + prevAmount: 8820 + }, + { + code: 'A-13', + side: 'ASSETS', + group: 'Fixed Assets', + label: 'Other Fixed Assets', + amount: 4780, + prevAmount: 4620 + }, + { + code: 'A-14', + side: 'ASSETS', + group: 'Other Assets', + label: 'Inter-Office Adjustments (Net)', + amount: 0, + prevAmount: 0 + }, + { + code: 'A-15', + side: 'ASSETS', + group: 'Other Assets', + label: 'Tax Paid in Advance & TDS', + amount: 5420, + prevAmount: 4820 + }, + { code: 'A-16', side: 'ASSETS', group: 'Other Assets', label: 'Stationery & Stamps', amount: 184, prevAmount: 142 }, + { + code: 'A-17', + side: 'ASSETS', + group: 'Other Assets', + label: 'Sundry Debtors & Other Assets', + amount: 2876, + prevAmount: 2540 + } + ]; + + // -------------------- GL Wise DR/CR Data -------------------- + glReport: GlLine[] = [ + { + acCode: '110001', + description: 'Capital A/c — Equity Share', + group: 'LIABILITY', + opening: 20800, + debit: 0, + credit: 1200, + closing: 22000 + }, + { + acCode: '120020', + description: 'Reserves & Surplus A/c', + group: 'LIABILITY', + opening: 28640, + debit: 0, + credit: 3760, + closing: 32400 + }, + { + acCode: '130001', + description: 'Demand Deposits — Current A/c', + group: 'LIABILITY', + opening: 78420, + debit: 124800, + credit: 130580, + closing: 84200 + }, + { + acCode: '130020', + description: 'Savings Bank Deposits A/c', + group: 'LIABILITY', + opening: 198400, + debit: 84200, + credit: 104400, + closing: 218600 + }, + { + acCode: '130040', + description: 'Term Deposits A/c', + group: 'LIABILITY', + opening: 412400, + debit: 142800, + credit: 193200, + closing: 462800 + }, + { + acCode: '140020', + description: 'Borrowings — RBI / NABARD A/c', + group: 'LIABILITY', + opening: 16240, + debit: 4060, + credit: 2000, + closing: 14180 + }, + { + acCode: '210010', + description: 'Cash on Hand A/c', + group: 'ASSET', + opening: 7820, + debit: 248400, + credit: 247800, + closing: 8420 + }, + { + acCode: '210020', + description: 'Balance with RBI A/c', + group: 'ASSET', + opening: 34200, + debit: 84200, + credit: 80000, + closing: 38400 + }, + { + acCode: '210030', + description: 'Balance with Other Banks — Current A/c', + group: 'ASSET', + opening: 12400, + debit: 22400, + credit: 20000, + closing: 14800 + }, + { + acCode: '220010', + description: 'Government Securities — SLR A/c', + group: 'ASSET', + opening: 128600, + debit: 28000, + credit: 14200, + closing: 142400 + }, + { + acCode: '230010', + description: 'Cash Credit / Overdraft A/c', + group: 'ASSET', + opening: 152800, + debit: 84200, + credit: 68600, + closing: 168400 + }, + { + acCode: '230020', + description: 'Term Loans — Secured A/c', + group: 'ASSET', + opening: 198400, + debit: 38420, + credit: 18955, + closing: 217865 + }, + { + acCode: '240010', + description: 'Premises (Net) A/c', + group: 'ASSET', + opening: 8820, + debit: 824, + credit: 224, + closing: 9420 + }, + { + acCode: '310010', + description: 'Interest on Advances A/c', + group: 'INCOME', + opening: 0, + debit: 0, + credit: 38420, + closing: 38420 + }, + { + acCode: '310020', + description: 'Interest on Investments A/c', + group: 'INCOME', + opening: 0, + debit: 0, + credit: 14860, + closing: 14860 + }, + { + acCode: '320010', + description: 'Commission & Brokerage A/c', + group: 'INCOME', + opening: 0, + debit: 0, + credit: 1820, + closing: 1820 + }, + { + acCode: '410010', + description: 'Interest on Deposits A/c', + group: 'EXPENSE', + opening: 0, + debit: 28640, + credit: 0, + closing: 28640 + }, + { + acCode: '420010', + description: 'Salaries & Allowances A/c', + group: 'EXPENSE', + opening: 0, + debit: 8420, + credit: 0, + closing: 8420 + }, + { + acCode: '420020', + description: 'Rent, Taxes & Lighting A/c', + group: 'EXPENSE', + opening: 0, + debit: 1240, + credit: 0, + closing: 1240 + }, + { + acCode: '420030', + description: 'Depreciation A/c', + group: 'EXPENSE', + opening: 0, + debit: 642, + credit: 0, + closing: 642 + }, + { + acCode: '420090', + description: 'Miscellaneous Expenses A/c', + group: 'EXPENSE', + opening: 0, + debit: 824, + credit: 0, + closing: 824 + } + ]; + + // -------------------- Misc Expense Ledger -------------------- + miscExp: MiscExpRow[] = [ + { + txnDate: '02-10-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12001', + chequeNo: '142008', + description: 'Petrol Bill — Branch Vehicle', + mode: 'CHEQUE', + withdrawal: 4280, + deposit: 0, + balance: 4280 + }, + { + txnDate: '04-10-2025', + branch: 'Pune FC Road', + branchCode: '02', + voucherNo: 'VR-12012', + chequeNo: '—', + description: 'Diesel Bill — Generator', + mode: 'CASH', + withdrawal: 2840, + deposit: 0, + balance: 7120 + }, + { + txnDate: '08-10-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12025', + chequeNo: '142014', + description: 'Office Stationery & Printing', + mode: 'CHEQUE', + withdrawal: 8420, + deposit: 0, + balance: 15540 + }, + { + txnDate: '12-10-2025', + branch: 'Bengaluru Jayanagar', + branchCode: '04', + voucherNo: 'VR-12041', + chequeNo: '—', + description: 'Tea / Refreshments — Customer Visit', + mode: 'CASH', + withdrawal: 1240, + deposit: 0, + balance: 16780 + }, + { + txnDate: '15-10-2025', + branch: 'Delhi Greater Kailash', + branchCode: '08', + voucherNo: 'VR-12058', + chequeNo: '184201', + description: 'Newspaper & Periodical Subscriptions', + mode: 'CHEQUE', + withdrawal: 3680, + deposit: 0, + balance: 20460 + }, + { + txnDate: '18-10-2025', + branch: 'Pune FC Road', + branchCode: '02', + voucherNo: 'VR-12064', + chequeNo: '—', + description: 'Petrol Bill — Officer Visit', + mode: 'CASH', + withdrawal: 1820, + deposit: 0, + balance: 22280 + }, + { + txnDate: '22-10-2025', + branch: 'Chennai Valasaravakkam', + branchCode: '09', + voucherNo: 'VR-12082', + chequeNo: 'NEFT-2210', + description: 'AMC — Office Equipment', + mode: 'NEFT', + withdrawal: 24800, + deposit: 0, + balance: 47080 + }, + { + txnDate: '28-10-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12101', + chequeNo: '142098', + description: 'Audit Stationery & Bind Charges', + mode: 'CHEQUE', + withdrawal: 6420, + deposit: 0, + balance: 53500 + }, + { + txnDate: '02-11-2025', + branch: 'Ahmedabad Satellite', + branchCode: '06', + voucherNo: 'VR-12118', + chequeNo: '—', + description: 'Postage & Telegram', + mode: 'CASH', + withdrawal: 982, + deposit: 0, + balance: 54482 + }, + { + txnDate: '08-11-2025', + branch: 'Lucknow Gomti Nagar', + branchCode: '07', + voucherNo: 'VR-12134', + chequeNo: '184244', + description: 'Diesel Bill — Branch Generator', + mode: 'CHEQUE', + withdrawal: 3420, + deposit: 0, + balance: 57902 + }, + { + txnDate: '14-11-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12152', + chequeNo: '142112', + description: 'Reverse — Cancelled Voucher', + mode: 'CHEQUE', + withdrawal: 0, + deposit: 1240, + balance: 56662 + }, + { + txnDate: '20-11-2025', + branch: 'Kolkata Salt Lake', + branchCode: '10', + voucherNo: 'VR-12168', + chequeNo: '—', + description: 'Petrol Bill — Field Officer', + mode: 'CASH', + withdrawal: 1640, + deposit: 0, + balance: 58302 + }, + { + txnDate: '28-11-2025', + branch: 'Bengaluru Jayanagar', + branchCode: '04', + voucherNo: 'VR-12184', + chequeNo: 'TRF-2811', + description: 'Internet & Telephone Charges', + mode: 'TRF', + withdrawal: 18420, + deposit: 0, + balance: 76722 + }, + { + txnDate: '06-12-2025', + branch: 'Delhi Greater Kailash', + branchCode: '08', + voucherNo: 'VR-12211', + chequeNo: '184272', + description: 'Office Cleaning & Housekeeping', + mode: 'CHEQUE', + withdrawal: 12400, + deposit: 0, + balance: 89122 + }, + { + txnDate: '15-12-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12238', + chequeNo: '—', + description: 'Local Conveyance — Officers', + mode: 'CASH', + withdrawal: 2480, + deposit: 0, + balance: 91602 + }, + { + txnDate: '22-12-2025', + branch: 'Indore Vijay Nagar', + branchCode: '14', + voucherNo: 'VR-12262', + chequeNo: 'NEFT-2212', + description: 'Pest Control — Annual Contract', + mode: 'NEFT', + withdrawal: 8420, + deposit: 0, + balance: 100022 + }, + { + txnDate: '28-12-2025', + branch: 'Pune FC Road', + branchCode: '02', + voucherNo: 'VR-12281', + chequeNo: '142158', + description: 'Misc Office Supplies', + mode: 'CHEQUE', + withdrawal: 4620, + deposit: 0, + balance: 104642 + }, + { + txnDate: '31-12-2025', + branch: 'Mumbai Main', + branchCode: '01', + voucherNo: 'VR-12298', + chequeNo: 'TRF-3112', + description: 'Newspaper Bill (Q3)', + mode: 'TRF', + withdrawal: 1820, + deposit: 0, + balance: 106462 + } + ]; + + // -------------------- Filters -------------------- + miscFilterBranch = ''; + miscFilterMode: '' | 'CASH' | 'CHEQUE' | 'TRF' | 'NEFT' = ''; + glFilterGroup: '' | 'ASSET' | 'LIABILITY' | 'INCOME' | 'EXPENSE' = ''; + glFilterSearch = ''; + + data = inject(MAT_DIALOG_DATA, { optional: true }); + + constructor() { + this.activeReport = this.data?.initialReport ?? 'PNL'; + } + + setReport(code: AcctReportCode): void { + this.activeReport = code; + } + close(): void { + this.dialogRef.close(); + } + + get currentReport() { + return this.reports.find((r) => r.code === this.activeReport)!; + } + + // -------------------- P&L computed -------------------- + get pnlIncome(): PnlLine[] { + return this.pnl.filter((l) => l.group === 'INCOME'); + } + get pnlExpenditure(): PnlLine[] { + return this.pnl.filter((l) => l.group === 'EXPENDITURE'); + } + get pnlIncomeSubgroups(): string[] { + const s: string[] = []; + for (const l of this.pnlIncome) if (!s.includes(l.subgroup)) s.push(l.subgroup); + return s; + } + get pnlExpSubgroups(): string[] { + const s: string[] = []; + for (const l of this.pnlExpenditure) if (!s.includes(l.subgroup)) s.push(l.subgroup); + return s; + } + pnlLinesIn(group: 'INCOME' | 'EXPENDITURE', sub: string): PnlLine[] { + return this.pnl.filter((l) => l.group === group && l.subgroup === sub); + } + subTotal(group: 'INCOME' | 'EXPENDITURE', sub: string): number { + return this.pnlLinesIn(group, sub).reduce((s, l) => s + l.amount, 0); + } + get totalIncome(): number { + return this.pnlIncome.reduce((s, l) => s + l.amount, 0); + } + get totalExpenditure(): number { + return this.pnlExpenditure.reduce((s, l) => s + l.amount, 0); + } + get netProfit(): number { + return this.totalIncome - this.totalExpenditure; + } + get netProfitPct(): number { + return this.totalIncome === 0 ? 0 : +((this.netProfit / this.totalIncome) * 100).toFixed(2); + } + + // -------------------- Balance Sheet computed -------------------- + get bsLiabilities(): BsLine[] { + return this.bs.filter((l) => l.side === 'CAPITAL-LIAB'); + } + get bsAssets(): BsLine[] { + return this.bs.filter((l) => l.side === 'ASSETS'); + } + bsGroupsBySide(side: 'CAPITAL-LIAB' | 'ASSETS'): string[] { + const s: string[] = []; + for (const l of this.bs) if (l.side === side && !s.includes(l.group)) s.push(l.group); + return s; + } + bsLinesInGroup(side: 'CAPITAL-LIAB' | 'ASSETS', group: string): BsLine[] { + return this.bs.filter((l) => l.side === side && l.group === group); + } + bsSubTotal(side: 'CAPITAL-LIAB' | 'ASSETS', group: string, prev = false): number { + return this.bsLinesInGroup(side, group).reduce((s, l) => s + (prev ? l.prevAmount : l.amount), 0); + } + get totalLiabilities(): number { + return this.bsLiabilities.reduce((s, l) => s + l.amount, 0); + } + get totalLiabilitiesPrev(): number { + return this.bsLiabilities.reduce((s, l) => s + l.prevAmount, 0); + } + get totalAssets(): number { + return this.bsAssets.reduce((s, l) => s + l.amount, 0); + } + get totalAssetsPrev(): number { + return this.bsAssets.reduce((s, l) => s + l.prevAmount, 0); + } + variation(curr: number, prev: number): number { + return curr - prev; + } + variationPct(curr: number, prev: number): number { + return prev === 0 ? 0 : +(((curr - prev) / prev) * 100).toFixed(2); + } + + // -------------------- GL DR/CR computed -------------------- + get filteredGl(): GlLine[] { + const search = this.glFilterSearch.trim().toLowerCase(); + return this.glReport.filter((g) => { + if (this.glFilterGroup && g.group !== this.glFilterGroup) return false; + if (search && !g.description.toLowerCase().includes(search) && !g.acCode.includes(search)) return false; + return true; + }); + } + resetGlFilters(): void { + this.glFilterGroup = ''; + this.glFilterSearch = ''; + } + get glTotalDebit(): number { + return this.filteredGl.reduce((s, g) => s + g.debit, 0); + } + get glTotalCredit(): number { + return this.filteredGl.reduce((s, g) => s + g.credit, 0); + } + glGroupClass(group: 'ASSET' | 'LIABILITY' | 'INCOME' | 'EXPENSE'): string { + return 'gl-group-' + group.toLowerCase(); + } + + // -------------------- Misc Exp computed -------------------- + get filteredMisc(): MiscExpRow[] { + const branch = this.miscFilterBranch.trim().toLowerCase(); + return this.miscExp.filter((r) => { + if (branch && !r.branch.toLowerCase().includes(branch)) return false; + if (this.miscFilterMode && r.mode !== this.miscFilterMode) return false; + return true; + }); + } + resetMiscFilters(): void { + this.miscFilterBranch = ''; + this.miscFilterMode = ''; + } + get miscTotalWithdrawal(): number { + return this.filteredMisc.reduce((s, r) => s + r.withdrawal, 0); + } + get miscTotalDeposit(): number { + return this.filteredMisc.reduce((s, r) => s + r.deposit, 0); + } + get miscBranchTotals(): { branch: string; total: number }[] { + const map: Record = {}; + for (const r of this.filteredMisc) { + map[r.branch] = (map[r.branch] || 0) + r.withdrawal - r.deposit; + } + return Object.entries(map).map( + ([ + branch, + total + ]) => ({ branch, total }) + ); + } + + // -------------------- Exports -------------------- + exportCurrentCsv(): void { + if (this.activeReport === 'PNL') return this.exportPnlCsv(); + if (this.activeReport === 'BALANCE-SHEET') return this.exportBsCsv(false); + if (this.activeReport === 'COMPARATIVE-BS') return this.exportBsCsv(true); + if (this.activeReport === 'GL-DR-CR') return this.exportGlCsv(); + if (this.activeReport === 'MISC-EXP') return this.exportMiscCsv(); + } + + exportPnlCsv(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + 'PROFIT & LOSS A/C', + `For the year ended ${this.bank.asOnDate}` + ]); + rows.push([]); + rows.push([ + 'Code', + 'Group', + 'Sub-Group', + 'Particulars', + 'Amount (₹ Lakh)' + ]); + for (const l of this.pnl) { + rows.push([ + l.code, + l.group, + l.subgroup, + l.label, + l.amount.toFixed(2) + ]); + } + rows.push([]); + rows.push([ + '', + '', + '', + 'Total Income', + this.totalIncome.toFixed(2) + ]); + rows.push([ + '', + '', + '', + 'Total Expenditure', + this.totalExpenditure.toFixed(2) + ]); + rows.push([ + '', + '', + '', + 'Net Profit / (Loss)', + this.netProfit.toFixed(2) + ]); + rows.push([ + '', + '', + '', + 'Net Profit Margin %', + this.netProfitPct.toFixed(2) + '%' + ]); + rows.push([]); + rows.push([ + 'Certified by', + this.bank.principalOfficer + ' — ' + this.bank.poDesignation + ]); + rows.push([ + 'Auditor', + this.bank.auditor + ]); + this.saveCsv(rows, `Profit-Loss-${this.dateTag()}.csv`); + } + + exportBsCsv(comparative: boolean): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + comparative ? 'COMPARATIVE BALANCE SHEET' : 'BALANCE SHEET', + `As on ${this.bank.asOnDate}` + ]); + rows.push([]); + if (comparative) { + rows.push([ + 'Code', + 'Side', + 'Group', + 'Particulars', + `${this.bank.fiscalYear} (₹ Lakh)`, + `${this.bank.prevFiscalYear} (₹ Lakh)`, + 'Variation', + 'Variation %' + ]); + for (const l of this.bs) { + rows.push([ + l.code, + l.side, + l.group, + l.label, + l.amount.toFixed(2), + l.prevAmount.toFixed(2), + this.variation(l.amount, l.prevAmount).toFixed(2), + this.variationPct(l.amount, l.prevAmount).toFixed(2) + '%' + ]); + } + } else { + rows.push([ + 'Code', + 'Side', + 'Group', + 'Particulars', + 'Amount (₹ Lakh)' + ]); + for (const l of this.bs) { + rows.push([ + l.code, + l.side, + l.group, + l.label, + l.amount.toFixed(2) + ]); + } + } + rows.push([]); + rows.push([ + '', + '', + '', + 'Total Capital & Liabilities', + this.totalLiabilities.toFixed(2) + ]); + rows.push([ + '', + '', + '', + 'Total Assets', + this.totalAssets.toFixed(2) + ]); + rows.push([]); + rows.push([ + 'Certified by', + this.bank.principalOfficer + ' — ' + this.bank.poDesignation + ]); + this.saveCsv(rows, `${comparative ? 'Comparative-' : ''}Balance-Sheet-${this.dateTag()}.csv`); + } + + exportGlCsv(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + 'GL WISE DEBIT / CREDIT REPORT', + `Period ${this.bank.quarterFrom} TO ${this.bank.quarterTo}` + ]); + rows.push([]); + rows.push([ + 'A/C Code', + 'Description', + 'Group', + 'Opening (₹ Lakh)', + 'Debit', + 'Credit', + 'Closing (₹ Lakh)' + ]); + for (const g of this.filteredGl) { + rows.push([ + g.acCode, + g.description, + g.group, + g.opening.toFixed(2), + g.debit.toFixed(2), + g.credit.toFixed(2), + g.closing.toFixed(2) + ]); + } + rows.push([]); + rows.push([ + '', + 'TOTAL', + '', + '', + this.glTotalDebit.toFixed(2), + this.glTotalCredit.toFixed(2), + '' + ]); + this.saveCsv(rows, `GL-DrCr-${this.dateTag()}.csv`); + } + + exportMiscCsv(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + 'MISCELLANEOUS EXPENSE LEDGER', + `Period ${this.bank.quarterFrom} TO ${this.bank.quarterTo}` + ]); + rows.push([ + `Report Ref: ${this.bank.reportRefNo}`, + `User Code: ${this.bank.userCode}` + ]); + rows.push([]); + rows.push([ + 'Date', + 'Branch', + 'Branch Code', + 'Voucher No', + 'Cheque No', + 'Description', + 'Mode', + 'Withdrawal', + 'Deposit', + 'Balance' + ]); + for (const r of this.filteredMisc) { + rows.push([ + r.txnDate, + r.branch, + r.branchCode, + r.voucherNo, + r.chequeNo, + r.description, + r.mode, + r.withdrawal.toString(), + r.deposit.toString(), + r.balance.toString() + ]); + } + rows.push([]); + rows.push([ + '', + '', + '', + '', + '', + '', + 'TOTAL', + this.miscTotalWithdrawal.toString(), + this.miscTotalDeposit.toString(), + '' + ]); + rows.push([]); + rows.push(['Branch Wise Total']); + for (const bt of this.miscBranchTotals) { + rows.push([ + bt.branch, + bt.total.toString() + ]); + } + rows.push([]); + rows.push([ + 'Print Date', + new Date().toLocaleString('en-IN') + ]); + this.saveCsv(rows, `Misc-Exp-Ledger-${this.dateTag()}.csv`); + } + + // ---- helpers ---- + private dateTag(): string { + const d = new Date(); + return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`; + } + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n'; + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } +} diff --git a/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.html b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.html new file mode 100644 index 000000000..4528ff587 --- /dev/null +++ b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.html @@ -0,0 +1,478 @@ + + +
+ +
+
+
+
+

Asset Liability Management — Structural Liquidity

+

+ {{ bank.name }} · {{ bank.formCode }} · {{ bank.quarter }} · As on + {{ bank.asOnDate }} +

+
+
+
+ + 14d Tolerance + {{ gap14dPct.toFixed(2) }}% + / -{{ bank.tolerance14d }}% + + +
+
+ + +
+ + + + +
+ +
+ + +
+
+
+
+
{{ totalGapPct.toFixed(1) }}%
+
Net Gap / Outflow
+
+
+
+
+
NDTL
+
₹ {{ bank.ndtl / 100 | number: '1.2-2' }} Cr
+
Net Demand & Time Liabilities
+
+
+
Total Outflows
+
₹ {{ totalOutflow / 100 | number: '1.2-2' }} Cr
+
All maturity buckets
+
+
+
Total Inflows
+
₹ {{ totalInflow / 100 | number: '1.2-2' }} Cr
+
+ Net: {{ totalGap >= 0 ? '+' : '' }}{{ totalGap / 100 | number: '1.2-2' }} Cr +
+
+
+ + +
+
+

RBI Tolerance Limits — Cumulative Gap

+ RBI MD: {{ bank.rbiCircular }} +
+
+
+
+   Cumulative Gap up to 14 days + + {{ gap14dPct.toFixed(2) }}% + limit -{{ bank.tolerance14d }}% + {{ gap14dWithinTolerance ? 'WITHIN' : 'BREACH' }} + +
+
+
+
+
+
+
+
+   Cumulative Gap up to 30 days + + {{ gap28dPct.toFixed(2) }}% + limit -{{ bank.tolerance28d }}% + {{ gap28dWithinTolerance ? 'WITHIN' : 'BREACH' }} + +
+
+
+
+
+
+
+
+ + +
+
+

Bucket-wise Mismatch Heatmap

+
+
+
+
{{ b.code }}
+
{{ bucketGapPct(b).toFixed(1) }}%
+
{{ b.label }}
+
+
+
+ +
+
+

Reporting Snapshot

+
+
+
+
Reporting Bank
+
{{ bank.name }}
+
+
+
Branch
+
{{ bank.branch }}
+
+
+
Principal Officer
+
{{ bank.principalOfficer }}
+
+
+
Form Code
+
{{ bank.formCode }}
+
+
+
Report Ref No
+
{{ bank.reportRefNo }}
+
+
+
User Code
+
{{ bank.userCode }}
+
+
+
+
+ + + +
+
+

Statement of Structural Liquidity — Maturity Ladder

+
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BucketTime PeriodOutflow (Liab)Inflow (Assets)GapGap %Status
{{ b.code }}{{ b.label }}{{ b.outflow | number: '1.2-2' }}{{ b.inflow | number: '1.2-2' }} + {{ bucketGap(b) >= 0 ? '+' : '' }}{{ bucketGap(b) | number: '1.2-2' }} + + {{ bucketGapPct(b) >= 0 ? '+' : '' }}{{ bucketGapPct(b).toFixed(2) }}% + + + SURPLUS + DEFICIT + BALANCED + +
TOTAL + {{ totalOutflow | number: '1.2-2' }} + + {{ totalInflow | number: '1.2-2' }} + + {{ totalGap >= 0 ? '+' : '' }}{{ totalGap | number: '1.2-2' }} + + {{ totalGapPct >= 0 ? '+' : '' }}{{ totalGapPct.toFixed(2) }}% +
+
+ + +

Cumulative Position

+
+ + + + + + + + + + + + + + + + + + + +
Through BucketCum. OutflowCum. InflowCum. GapCum. Gap %
{{ b.code }} — {{ b.label }}{{ cumulativeOutflow(i) | number: '1.2-2' }}{{ cumulativeInflow(i) | number: '1.2-2' }} + {{ cumulativeGap(i) >= 0 ? '+' : '' }}{{ cumulativeGap(i) | number: '1.2-2' }} + + {{ cumulativeGapPct(i) >= 0 ? '+' : '' }}{{ cumulativeGapPct(i).toFixed(2) }}% +
+
+
+
+ + + +
+
+

Monthly Asset / Liability Snapshot — FY {{ bank.fiscalYear }}

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MonthLiabilities (₹ Lakh)Assets (₹ Lakh)
Reserve/ProvBorrowingsFDsTotal LiabCashInvestmentsOther AssetsTotal Assets
{{ m.month }}{{ m.reserveProv | number: '1.2-2' }}{{ m.borrowings | number: '1.2-2' }}{{ m.fds | number: '1.2-2' }} + {{ monthTotalLiab(m) | number: '1.2-2' }} + {{ m.cashOnHand | number: '1.2-2' }}{{ m.investments | number: '1.2-2' }}{{ m.otherAssets | number: '1.2-2' }} + {{ monthTotalAssets(m) | number: '1.2-2' }} +
AVERAGE{{ avg('reserveProv') | number: '1.2-2' }}{{ avg('borrowings') | number: '1.2-2' }}{{ avg('fds') | number: '1.2-2' }} + {{ avg('reserveProv') + avg('borrowings') + avg('fds') | number: '1.2-2' }} + {{ avg('cashOnHand') | number: '1.2-2' }}{{ avg('investments') | number: '1.2-2' }}{{ avg('otherAssets') | number: '1.2-2' }} + {{ avg('cashOnHand') + avg('investments') + avg('otherAssets') | number: '1.2-2' }} +
+
+
+
+ + + +
+
+

{{ bank.formCode }} — Statement of Structural Liquidity

+ +
+ +
+
+
+ {{ bank.name }} — {{ bank.branch }} +
+
+   As On {{ bank.asOnDate }} +   {{ bank.quarter }} +   Ref: {{ bank.reportRefNo }} +
+
+ +

A. NDTL & Reference

+ + + + + + + + + + + +
Net Demand & Time Liabilities (NDTL) (₹ Lakhs) + {{ bank.ndtl | number: '1.2-2' }} +
RBI Master Direction{{ bank.rbiCircular }}
+ +

B. Maturity Ladder (₹ in Lakhs)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BucketTime PeriodOutflowsInflowsMismatchCum. MismatchCum. % of Outflow
{{ b.code }}{{ b.label }}{{ b.outflow | number: '1.2-2' }}{{ b.inflow | number: '1.2-2' }} + {{ bucketGap(b) >= 0 ? '+' : '' }}{{ bucketGap(b) | number: '1.2-2' }} + + {{ cumulativeGap(i) >= 0 ? '+' : '' }}{{ cumulativeGap(i) | number: '1.2-2' }} + + {{ cumulativeGapPct(i) >= 0 ? '+' : '' }}{{ cumulativeGapPct(i).toFixed(2) }}% +
TOTAL + {{ totalOutflow | number: '1.2-2' }} + + {{ totalInflow | number: '1.2-2' }} + + {{ totalGap >= 0 ? '+' : '' }}{{ totalGap | number: '1.2-2' }} +
+ +

C. RBI Tolerance Compliance

+ + + + + + + + + + + + + + + +
Cumulative Negative Gap up to 14 days + {{ gap14dPct.toFixed(2) }}% + Limit: -{{ bank.tolerance14d }}% + + {{ gap14dWithinTolerance ? 'WITHIN' : 'BREACH' }} + +
Cumulative Negative Gap up to 30 days + {{ gap28dPct.toFixed(2) }}% + Limit: -{{ bank.tolerance28d }}% + + {{ gap28dWithinTolerance ? 'WITHIN' : 'BREACH' }} + +
+ +
+
+
Certified by
+
{{ bank.principalOfficer }}
+
Principal Officer — {{ bank.name }}
+
+
+
Submitted under
+
{{ bank.rbiCircular }}
+
RBI Master Direction on Asset Liability Management
+
+
+
+
+
+
+
diff --git a/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.scss b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.scss new file mode 100644 index 000000000..c27fdcf76 --- /dev/null +++ b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.scss @@ -0,0 +1,772 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --alm-good: #15803d; + --alm-good-l: #dcfce7; + --alm-mid: #d97706; + --alm-mid-l: #fef3c7; + --alm-low: #c0392b; + --alm-low-l: #fdecea; + --alm-blue: #2563eb; + --alm-blue-l: #dbeafe; + --alm-indigo: #4f46e5; + --alm-indigo-l: #e0e7ff; + --alm-g50: #f8fafc; + --alm-g100: #f1f5f9; + --alm-g200: #e2e8f0; + --alm-g300: #cbd5e1; + --alm-g500: #64748b; + --alm-g700: #334155; + --alm-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--alm-g900); +} + +.alm-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 90vh; +} + +/* HEADER */ +.alm-head { + padding: 18px 22px; + border-bottom: 1px solid var(--alm-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #eef2ff 0%, #fff 100%); +} + +.alm-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.alm-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--alm-indigo-l); + color: var(--alm-indigo); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.alm-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--alm-g900); +} + +.alm-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--alm-g500); +} + +.alm-head-actions { + display: flex; + align-items: center; + gap: 12px; +} + +.alm-overall { + display: flex; + align-items: baseline; + gap: 6px; + padding: 6px 14px; + border-radius: 999px; + font-weight: 700; + + &.on-target { + background: var(--alm-good-l); + color: var(--alm-good); + } + + &.below { + background: var(--alm-low-l); + color: var(--alm-low); + } + + .alm-overall-l { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 600; + } + + .alm-overall-v { + font-size: 16px; + } + + .alm-overall-t { + font-size: 11px; + font-weight: 600; + opacity: 0.8; + } +} + +.alm-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--alm-g200); + background: #fff; + font-size: 14px; + color: var(--alm-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--alm-g100); + color: var(--alm-g900); + } +} + +/* TABS */ +.alm-tabs { + display: flex; + background: var(--alm-g50); + border-bottom: 1px solid var(--alm-g200); + padding: 0 22px; + overflow-x: auto; + flex-shrink: 0; +} + +.alm-tab { + display: flex; + align-items: center; + padding: 12px 18px; + border: none; + background: transparent; + cursor: pointer; + font-family: inherit; + font-size: 13px; + font-weight: 600; + color: var(--alm-g500); + border-bottom: 2px solid transparent; + transition: all 0.15s; + white-space: nowrap; + + &:hover { + color: var(--alm-g700); + background: rgb(255 255 255 / 50%); + } + + &.active { + background: #fff; + color: var(--alm-indigo); + border-bottom-color: var(--alm-indigo); + } +} + +/* BODY */ +.alm-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 18px; + overflow: auto; + flex: 1; +} + +/* KPI STATS */ +.alm-stats { + display: grid; + grid-template-columns: 220px 1fr 1fr 1fr; + gap: 14px; +} + +.alm-stat-card { + background: #fff; + border: 1px solid var(--alm-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 4px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.alm-stat-l { + font-size: 11px; + font-weight: 600; + color: var(--alm-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.alm-stat-v { + font-size: 20px; + font-weight: 700; + color: var(--alm-g900); + line-height: 1.2; +} + +.alm-stat-meta { + font-size: 11px; + color: var(--alm-g500); + margin-top: 4px; + + &.pos { + color: var(--alm-good); + font-weight: 600; + } + + &.neg { + color: var(--alm-low); + font-weight: 600; + } +} + +.alm-stat-gauge { + align-items: center; + justify-content: center; +} + +/* GAUGE */ +.gauge { + width: 140px; + height: 140px; + border-radius: 50%; + background: conic-gradient(currentcolor calc(var(--pct) * 1%), var(--alm-g100) 0); + display: flex; + align-items: center; + justify-content: center; + + &.on-target { + color: var(--alm-good); + } + + &.below { + color: var(--alm-low); + } +} + +.gauge-inner { + width: 108px; + height: 108px; + border-radius: 50%; + background: #fff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: inset 0 0 0 1px var(--alm-g200); +} + +.gauge-pct { + font-size: 20px; + font-weight: 700; + color: var(--alm-g900); + line-height: 1; +} + +.gauge-label { + font-size: 10px; + color: var(--alm-g500); + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 600; +} + +/* SECTIONS */ +.alm-section { + background: #fff; + border: 1px solid var(--alm-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.alm-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 14px; + flex-wrap: wrap; +} + +.alm-sec-t { + margin: 0; + font-size: 14px; + font-weight: 700; + color: var(--alm-g700); +} + +.alm-sec-actions { + display: flex; + gap: 8px; + align-items: center; +} + +.alm-sel { + height: 34px; + padding: 0 10px; + border: 1px solid var(--alm-g200); + border-radius: 6px; + background: #fff; + font-family: inherit; + font-size: 12px; + color: var(--alm-g900); + + &:focus { + outline: none; + border-color: var(--alm-indigo); + box-shadow: 0 0 0 3px rgb(79 70 229 / 12%); + } +} + +.alm-circular { + font-family: 'DM Mono', monospace; + font-size: 11px; + color: var(--alm-g500); + padding: 4px 10px; + background: var(--alm-g50); + border-radius: 6px; + border: 1px solid var(--alm-g200); +} + +.alm-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: var(--alm-indigo) !important; +} + +/* TOLERANCE BARS */ +.alm-tolerance { + display: flex; + flex-direction: column; + gap: 16px; +} + +.alm-tol-row { + display: flex; + flex-direction: column; + gap: 6px; + + &.good .alm-tol-fill { + background: var(--alm-good); + } + + &.bad .alm-tol-fill { + background: var(--alm-low); + } + + &.good .alm-tol-st { + background: var(--alm-good-l); + color: var(--alm-good); + } + + &.bad .alm-tol-st { + background: var(--alm-low-l); + color: var(--alm-low); + } +} + +.alm-tol-h { + display: flex; + justify-content: space-between; + align-items: center; +} + +.alm-tol-l { + font-size: 12px; + font-weight: 600; + color: var(--alm-g700); + + fa-icon { + color: var(--alm-indigo); + } +} + +.alm-tol-p { + display: flex; + align-items: center; + gap: 10px; + font-size: 13px; + + strong { + font-size: 14px; + color: var(--alm-g900); + } + + .alm-tol-lim { + font-size: 11px; + color: var(--alm-g500); + } + + .alm-tol-st { + font-size: 10px; + font-weight: 700; + padding: 2px 8px; + border-radius: 999px; + } +} + +.alm-tol-track { + position: relative; + height: 8px; + background: var(--alm-g100); + border-radius: 4px; + overflow: hidden; +} + +.alm-tol-fill { + height: 100%; + border-radius: 4px; + transition: width 0.4s ease-out; +} + +.alm-tol-marker { + position: absolute; + top: -2px; + bottom: -2px; + left: 50%; + width: 2px; + background: var(--alm-g500); +} + +/* HEATMAP */ +.alm-heatmap { + display: grid; + grid-template-columns: repeat(11, 1fr); + gap: 6px; +} + +.heat-cell { + display: flex; + flex-direction: column; + align-items: center; + padding: 10px 6px; + border-radius: 8px; + text-align: center; + cursor: default; + transition: transform 0.15s; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgb(0 0 0 / 8%); + } + + &.surplus { + background: var(--alm-good-l); + color: var(--alm-good); + } + + &.deficit { + background: var(--alm-low-l); + color: var(--alm-low); + } + + &.balanced { + background: var(--alm-g100); + color: var(--alm-g500); + } +} + +.heat-code { + font-family: 'DM Mono', monospace; + font-size: 10px; + font-weight: 700; + opacity: 0.7; +} + +.heat-pct { + font-size: 14px; + font-weight: 800; + margin-top: 2px; +} + +.heat-lbl { + font-size: 9px; + margin-top: 4px; + opacity: 0.85; + line-height: 1.1; +} + +/* INFO GRID */ +.alm-info-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px 24px; +} + +.alm-info { + display: flex; + flex-direction: column; + gap: 2px; +} + +.alm-info-l { + font-size: 10px; + font-weight: 600; + color: var(--alm-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.alm-info-v { + font-size: 13px; + font-weight: 600; + color: var(--alm-g900); +} + +/* TABLE */ +.alm-tbl-wrap { + border: 1px solid var(--alm-g200); + border-radius: 8px; + overflow: auto; + margin-bottom: 12px; +} + +.alm-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--alm-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--alm-g50); + border-bottom: 1px solid var(--alm-g200); + white-space: nowrap; + } + + td { + padding: 10px 14px; + border-bottom: 1px solid var(--alm-g100); + font-size: 13px; + color: var(--alm-g700); + vertical-align: middle; + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--alm-g50); + } + + .row-total td { + background: var(--alm-indigo-l); + color: var(--alm-g900); + font-weight: 700; + border-top: 2px solid var(--alm-indigo); + } + + .grp-liab { + background: var(--alm-low-l) !important; + color: var(--alm-low) !important; + } + + .grp-asset { + background: var(--alm-good-l) !important; + color: var(--alm-good) !important; + } +} + +.status-pill { + display: inline-block; + padding: 3px 10px; + font-size: 11px; + font-weight: 600; + border-radius: 999px; + + &.on-target, + &.surplus { + background: var(--alm-good-l); + color: var(--alm-good); + } + + &.below, + &.deficit { + background: var(--alm-low-l); + color: var(--alm-low); + } + + &.balanced { + background: var(--alm-g100); + color: var(--alm-g500); + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.pos { + color: var(--alm-good); +} + +.neg { + color: var(--alm-low); +} + +/* FORM ALM-1 */ +.form-iii { + background: var(--alm-g50); + border: 1px solid var(--alm-g200); + border-radius: 10px; + padding: 18px; +} + +.form-iii-h { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 12px; + margin-bottom: 14px; + border-bottom: 2px solid var(--alm-indigo); + flex-wrap: wrap; + gap: 10px; +} + +.form-iii-bank { + font-size: 14px; + color: var(--alm-g900); +} + +.form-iii-meta { + display: flex; + gap: 18px; + font-size: 12px; + color: var(--alm-g500); + flex-wrap: wrap; + + fa-icon { + color: var(--alm-indigo); + } +} + +.form-iii-t { + font-size: 13px; + font-weight: 700; + color: var(--alm-g700); + margin: 16px 0 8px; + padding-bottom: 4px; + border-bottom: 1px dashed var(--alm-g200); +} + +.form-tbl { + background: #fff; + border: 1px solid var(--alm-g200); + border-radius: 6px; + overflow: hidden; +} + +.form-total td { + background: var(--alm-indigo-l) !important; + font-weight: 700; + color: var(--alm-g900); +} + +.form-iii-foot { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 18px; + margin-top: 18px; + padding-top: 16px; + border-top: 1px dashed var(--alm-g300); +} + +.cert { + background: #fff; + border: 1px solid var(--alm-g200); + border-radius: 8px; + padding: 12px 14px; +} + +.cert-l { + font-size: 10px; + font-weight: 600; + color: var(--alm-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cert-v { + font-size: 14px; + font-weight: 700; + color: var(--alm-g900); + margin: 4px 0; +} + +.cert-r { + font-size: 11px; + color: var(--alm-g500); +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .alm-stats { + grid-template-columns: 1fr 1fr; + } + + .alm-info-grid { + grid-template-columns: 1fr 1fr; + } + + .form-iii-foot { + grid-template-columns: 1fr; + } + + .alm-heatmap { + grid-template-columns: repeat(6, 1fr); + } +} + +@media (width <= 720px) { + .alm-stats, + .alm-info-grid { + grid-template-columns: 1fr; + } + + .alm-head { + flex-wrap: wrap; + } + + .alm-heatmap { + grid-template-columns: repeat(3, 1fr); + } +} diff --git a/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.ts b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.ts new file mode 100644 index 000000000..c15e6b559 --- /dev/null +++ b/src/app/home/dashboard/alm-report-dialog/alm-report-dialog.component.ts @@ -0,0 +1,468 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +interface MaturityBucket { + code: string; + label: string; + /** Outflows / Liabilities (₹ Lakhs) */ + outflow: number; + /** Inflows / Assets (₹ Lakhs) */ + inflow: number; +} + +interface MonthSnapshot { + month: string; + reserveProv: number; + borrowings: number; + fds: number; + cashOnHand: number; + investments: number; + otherAssets: number; +} + +@Component({ + selector: 'mifosx-alm-report-dialog', + standalone: true, + templateUrl: './alm-report-dialog.component.html', + styleUrls: ['./alm-report-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + FormsModule, + MatDialogModule, + MatButtonModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class AlmReportDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeTab: 'dashboard' | 'ladder' | 'monthly' | 'returns' = 'dashboard'; + + bank = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + asOnDate: '31-03-2026', + quarter: 'Q4 FY 2025-26', + fiscalYear: '2025-26', + rbiCircular: 'DCBR.BPD.(PCB).Cir.No.4/12.05.001/2018-19', + principalOfficer: 'Karthik Kamath', + formCode: 'Form ALM-1', + reportRefNo: '1036', + userCode: 'BUBL01', + /** Net Demand & Time Liabilities — denominator for ratios */ + ndtl: 740000, // ₹7,400 Cr in Lakhs + /** RBI tolerance: cumulative negative gap up to 14 days ≤ 5%, 28 days ≤ 10% */ + tolerance14d: 5, + tolerance28d: 10 + }; + + /** Standard RBI ALM time buckets per Master Direction */ + buckets: MaturityBucket[] = [ + { code: 'B01', label: 'Day 1', outflow: 1840, inflow: 2120 }, + { code: 'B02', label: '2 to 7 days', outflow: 6420, inflow: 5880 }, + { code: 'B03', label: '8 to 14 days', outflow: 4180, inflow: 4360 }, + { code: 'B04', label: '15 to 30 days', outflow: 8240, inflow: 7920 }, + { code: 'B05', label: '31 days to 2 months', outflow: 12480, inflow: 13160 }, + { code: 'B06', label: '> 2 months to 3 months', outflow: 9620, inflow: 10240 }, + { code: 'B07', label: '> 3 months to 6 months', outflow: 28640, inflow: 32180 }, + { code: 'B08', label: '> 6 months to 1 year', outflow: 64280, inflow: 68420 }, + { code: 'B09', label: '> 1 year to 3 years', outflow: 142800, inflow: 156400 }, + { code: 'B10', label: '> 3 years to 5 years', outflow: 96400, inflow: 108200 }, + { code: 'B11', label: 'Over 5 years', outflow: 124800, inflow: 138400 } + ]; + + /** Monthly snapshot — mirrors the format of the legacy ALM Excel */ + monthly: MonthSnapshot[] = [ + { + month: 'APR-2025', + reserveProv: 18420, + borrowings: 24180, + fds: 462800, + cashOnHand: 8420, + investments: 184600, + otherAssets: 12480 + }, + { + month: 'MAY-2025', + reserveProv: 18640, + borrowings: 23980, + fds: 468200, + cashOnHand: 8680, + investments: 186400, + otherAssets: 12620 + }, + { + month: 'JUN-2025', + reserveProv: 18820, + borrowings: 23640, + fds: 472400, + cashOnHand: 9120, + investments: 188200, + otherAssets: 12840 + }, + { + month: 'JUL-2025', + reserveProv: 19080, + borrowings: 23420, + fds: 478600, + cashOnHand: 9280, + investments: 190400, + otherAssets: 13020 + }, + { + month: 'AUG-2025', + reserveProv: 19280, + borrowings: 23180, + fds: 482400, + cashOnHand: 9420, + investments: 192800, + otherAssets: 13280 + }, + { + month: 'SEP-2025', + reserveProv: 19460, + borrowings: 22940, + fds: 488200, + cashOnHand: 9640, + investments: 195200, + otherAssets: 13420 + }, + { + month: 'OCT-2025', + reserveProv: 19680, + borrowings: 22680, + fds: 492600, + cashOnHand: 9820, + investments: 197600, + otherAssets: 13620 + }, + { + month: 'NOV-2025', + reserveProv: 19880, + borrowings: 22420, + fds: 498400, + cashOnHand: 9980, + investments: 200200, + otherAssets: 13820 + }, + { + month: 'DEC-2025', + reserveProv: 20120, + borrowings: 22180, + fds: 504200, + cashOnHand: 10240, + investments: 202800, + otherAssets: 14040 + }, + { + month: 'JAN-2026', + reserveProv: 20340, + borrowings: 21940, + fds: 508800, + cashOnHand: 10420, + investments: 205200, + otherAssets: 14240 + }, + { + month: 'FEB-2026', + reserveProv: 20580, + borrowings: 21680, + fds: 514600, + cashOnHand: 10620, + investments: 207800, + otherAssets: 14460 + }, + { + month: 'MAR-2026', + reserveProv: 20840, + borrowings: 21420, + fds: 520400, + cashOnHand: 10820, + investments: 210400, + otherAssets: 14680 + } + ]; + + filterBucket = ''; + + // -------------------- Computed -------------------- + bucketGap(b: MaturityBucket): number { + return b.inflow - b.outflow; + } + bucketGapPct(b: MaturityBucket): number { + return b.outflow === 0 ? 0 : +(((b.inflow - b.outflow) / b.outflow) * 100).toFixed(2); + } + cumulativeOutflow(idx: number): number { + return this.buckets.slice(0, idx + 1).reduce((s, b) => s + b.outflow, 0); + } + cumulativeInflow(idx: number): number { + return this.buckets.slice(0, idx + 1).reduce((s, b) => s + b.inflow, 0); + } + cumulativeGap(idx: number): number { + return this.cumulativeInflow(idx) - this.cumulativeOutflow(idx); + } + cumulativeGapPct(idx: number): number { + const out = this.cumulativeOutflow(idx); + return out === 0 ? 0 : +((this.cumulativeGap(idx) / out) * 100).toFixed(2); + } + + get totalOutflow(): number { + return this.buckets.reduce((s, b) => s + b.outflow, 0); + } + get totalInflow(): number { + return this.buckets.reduce((s, b) => s + b.inflow, 0); + } + get totalGap(): number { + return this.totalInflow - this.totalOutflow; + } + get totalGapPct(): number { + return this.totalOutflow === 0 ? 0 : +((this.totalGap / this.totalOutflow) * 100).toFixed(2); + } + + /** RBI tolerance test: cumulative negative gap up to bucket 3 (14 days) ≤ 5% of cumulative outflow */ + get gap14dPct(): number { + return this.cumulativeGapPct(2); + } + /** Up to bucket 4 (30 days) ≤ 10% */ + get gap28dPct(): number { + return this.cumulativeGapPct(3); + } + get gap14dWithinTolerance(): boolean { + return this.gap14dPct >= -this.bank.tolerance14d; + } + get gap28dWithinTolerance(): boolean { + return this.gap28dPct >= -this.bank.tolerance28d; + } + + bucketStatus(b: MaturityBucket): 'surplus' | 'deficit' | 'balanced' { + const gap = this.bucketGap(b); + if (gap > 0) return 'surplus'; + if (gap < 0) return 'deficit'; + return 'balanced'; + } + + /** Monthly totals helpers */ + monthTotalLiab(m: MonthSnapshot): number { + return m.reserveProv + m.borrowings + m.fds; + } + monthTotalAssets(m: MonthSnapshot): number { + return m.cashOnHand + m.investments + m.otherAssets; + } + /** Average across months */ + avg(key: K): number { + if (typeof this.monthly[0][key] !== 'number') return 0; + const total = this.monthly.reduce((s, m) => s + (m[key] as number), 0); + return +(total / this.monthly.length).toFixed(2); + } + + // -------------------- Filter -------------------- + get filteredBuckets(): MaturityBucket[] { + if (!this.filterBucket) return this.buckets; + if (this.filterBucket === 'short') return this.buckets.slice(0, 4); + if (this.filterBucket === 'medium') return this.buckets.slice(4, 8); + if (this.filterBucket === 'long') return this.buckets.slice(8); + return this.buckets; + } + + // -------------------- Excel Exports -------------------- + + /** Form ALM-1 — Statement of Structural Liquidity */ + exportFormAlm(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + `STATEMENT OF STRUCTURAL LIQUIDITY (${this.bank.formCode})`, + `As On ${this.bank.asOnDate}` + ]); + rows.push([ + `RBI Circular: ${this.bank.rbiCircular}`, + `Quarter: ${this.bank.quarter}` + ]); + rows.push([ + `Report Ref: ${this.bank.reportRefNo}`, + `User Code: ${this.bank.userCode}` + ]); + rows.push([]); + rows.push([ + 'NDTL (₹ Lakhs)', + this.bank.ndtl.toString() + ]); + rows.push([]); + rows.push(['Maturity Ladder (₹ in Lakhs)']); + rows.push([ + 'Bucket', + 'Time Period', + 'Outflows (Liabilities)', + 'Inflows (Assets)', + 'Mismatch (Gap)', + 'Cum. Outflows', + 'Cum. Inflows', + 'Cum. Gap', + 'Cum. Gap % of Outflow' + ]); + this.buckets.forEach((b, i) => { + rows.push([ + b.code, + b.label, + b.outflow.toFixed(2), + b.inflow.toFixed(2), + this.bucketGap(b).toFixed(2), + this.cumulativeOutflow(i).toFixed(2), + this.cumulativeInflow(i).toFixed(2), + this.cumulativeGap(i).toFixed(2), + this.cumulativeGapPct(i).toFixed(2) + '%' + ]); + }); + rows.push([]); + rows.push([ + 'TOTAL', + '', + this.totalOutflow.toFixed(2), + this.totalInflow.toFixed(2), + this.totalGap.toFixed(2) + ]); + rows.push([]); + rows.push(['RBI Tolerance Compliance']); + rows.push([ + 'Cumulative Gap up to 14 days', + this.gap14dPct.toFixed(2) + '%', + `Limit: -${this.bank.tolerance14d}%`, + this.gap14dWithinTolerance ? 'WITHIN' : 'BREACH' + ]); + rows.push([ + 'Cumulative Gap up to 30 days', + this.gap28dPct.toFixed(2) + '%', + `Limit: -${this.bank.tolerance28d}%`, + this.gap28dWithinTolerance ? 'WITHIN' : 'BREACH' + ]); + rows.push([]); + rows.push([ + 'Certified by', + this.bank.principalOfficer + ' — Principal Officer' + ]); + this.saveTsv(rows, `ALM-StructuralLiquidity-${this.dateTag()}.xls`); + } + + /** Monthly snapshot export */ + exportMonthlyXls(): void { + const rows: string[][] = []; + rows.push([ + `${this.bank.name} — ${this.bank.branch}`, + `Monthly ALM Snapshot — FY ${this.bank.fiscalYear}` + ]); + rows.push([]); + rows.push([ + 'Month', + 'Reserve/Prov (Excl. OIR)', + 'Borrowings', + 'FDs', + 'Total Liabilities', + 'Cash on Hand', + 'Investments incl. Bal/Repo', + 'Other Assets (Excl. IRA)', + 'Total Assets' + ]); + for (const m of this.monthly) { + rows.push([ + m.month, + m.reserveProv.toFixed(2), + m.borrowings.toFixed(2), + m.fds.toFixed(2), + this.monthTotalLiab(m).toFixed(2), + m.cashOnHand.toFixed(2), + m.investments.toFixed(2), + m.otherAssets.toFixed(2), + this.monthTotalAssets(m).toFixed(2) + ]); + } + rows.push([ + 'AVERAGE', + this.avg('reserveProv').toFixed(2), + this.avg('borrowings').toFixed(2), + this.avg('fds').toFixed(2), + (this.avg('reserveProv') + this.avg('borrowings') + this.avg('fds')).toFixed(2), + this.avg('cashOnHand').toFixed(2), + this.avg('investments').toFixed(2), + this.avg('otherAssets').toFixed(2), + (this.avg('cashOnHand') + this.avg('investments') + this.avg('otherAssets')).toFixed(2) + ]); + this.saveTsv(rows, `ALM-Monthly-${this.dateTag()}.xls`); + } + + /** Maturity ladder CSV */ + exportLadderCsv(): void { + const headers = [ + 'Bucket', + 'Time Period', + 'Outflow (₹ Lakh)', + 'Inflow (₹ Lakh)', + 'Gap', + 'Gap %' + ]; + const rows = [headers]; + for (const b of this.buckets) { + rows.push([ + b.code, + b.label, + b.outflow.toString(), + b.inflow.toString(), + this.bucketGap(b).toString(), + this.bucketGapPct(b).toFixed(2) + '%' + ]); + } + this.saveCsv(rows, `ALM-Ladder-${this.dateTag()}.csv`); + } + + setTab(tab: 'dashboard' | 'ladder' | 'monthly' | 'returns'): void { + this.activeTab = tab; + } + close(): void { + this.dialogRef.close(); + } + + // ---- helpers ---- + private dateTag(): string { + const d = new Date(); + return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`; + } + private saveTsv(rows: string[][], filename: string): void { + const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + this.saveBlob(tsv, filename, 'application/vnd.ms-excel'); + } + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n'; + this.saveBlob(csv, filename, 'text/csv'); + } + private saveBlob(content: string, filename: string, mime: string): void { + const blob = new Blob([content], { type: `${mime};charset=utf-8` }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } +} diff --git a/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.html b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.html new file mode 100644 index 000000000..1c797103e --- /dev/null +++ b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.html @@ -0,0 +1,135 @@ + + +
+
+
+
+
+

Credit Information Companies — Data Quality Report

+

+ Reporting period: {{ reportingPeriod }} · as per RBI Master Direction on Credit + Information Reporting +

+
+
+
+ + +
+
+ + +
+ +
+ +
+ +
+
+
+
+
{{ current.dqrPct.toFixed(1) }}%
+
DQR Score
+
+
+
+
+
Status
+
+ {{ current.status }} +
+
+ File: {{ current.fileRef }} +
+
+
+
Last Submitted
+
{{ current.lastSubmitted }}
+
Next due: {{ current.nextDue }}
+
+
+
Records This Cycle
+
{{ current.totalRecords | number }}
+
+ {{ current.accepted | number }} accepted + {{ current.warnings | number }} warnings + {{ current.rejected | number }} rejected +
+
+
+ + +
+

Sub-DQR Breakdown

+
+
+
+ {{ m.label }} + {{ m.pct.toFixed(1) }}% +
+
+
+
+
+
+
+ + +
+
+

Error & Warning Details — Last Submission

+ +
+
+ + + + + + + + + + + + + + + + + + + +
CodeSegmentDescriptionSeverityRecords
{{ e.code }}{{ e.segment }}{{ e.description }} + {{ e.severity }} + {{ e.count | number }}
+
+
+
+
diff --git a/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.scss b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.scss new file mode 100644 index 000000000..b0c648294 --- /dev/null +++ b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.scss @@ -0,0 +1,528 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --cic-good: #15803d; + --cic-good-l: #dcfce7; + --cic-mid: #d97706; + --cic-mid-l: #fef3c7; + --cic-low: #c0392b; + --cic-low-l: #fdecea; + --cic-purple: #6d28d9; + --cic-purple-l: #ede9fe; + --cic-g50: #f8fafc; + --cic-g100: #f1f5f9; + --cic-g200: #e2e8f0; + --cic-g300: #cbd5e1; + --cic-g500: #64748b; + --cic-g700: #334155; + --cic-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--cic-g900); +} + +.cic-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 90vh; +} + +/* HEADER */ +.cic-head { + padding: 18px 22px; + border-bottom: 1px solid var(--cic-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #f9fafb 0%, #fff 100%); +} + +.cic-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.cic-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--cic-purple-l); + color: var(--cic-purple); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.cic-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--cic-g900); +} + +.cic-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--cic-g500); +} + +.cic-head-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.cic-btn-ghost { + font-size: 12px !important; + height: 36px !important; +} + +.cic-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--cic-g200); + background: #fff; + font-size: 14px; + color: var(--cic-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--cic-g100); + color: var(--cic-g900); + } +} + +/* TABS */ +.cic-tabs { + display: flex; + gap: 0; + background: var(--cic-g50); + border-bottom: 1px solid var(--cic-g200); + padding: 0 22px; + overflow-x: auto; + flex-shrink: 0; +} + +.cic-tab { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + border: none; + background: transparent; + cursor: pointer; + font-family: inherit; + border-bottom: 2px solid transparent; + transition: all 0.15s; + white-space: nowrap; + + &:hover { + background: rgb(255 255 255 / 50%); + } + + &.active { + background: #fff; + border-bottom-color: var(--cic-purple); + } +} + +.cic-tab-logo { + width: 28px; + height: 28px; + border-radius: 6px; + flex-shrink: 0; +} + +.cic-tab-name { + font-size: 13px; + font-weight: 600; + color: var(--cic-g700); +} + +.cic-tab-pct { + font-size: 11px; + font-weight: 700; + padding: 2px 8px; + border-radius: 999px; + + &.good { + background: var(--cic-good-l); + color: var(--cic-good); + } + + &.moderate { + background: var(--cic-mid-l); + color: var(--cic-mid); + } + + &.low { + background: var(--cic-low-l); + color: var(--cic-low); + } +} + +/* BODY */ +.cic-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 18px; + overflow: auto; + flex: 1; +} + +/* TOP STAT CARDS */ +.cic-stats { + display: grid; + grid-template-columns: 220px 1fr 1fr 1.4fr; + gap: 14px; +} + +.cic-stat-card { + background: #fff; + border: 1px solid var(--cic-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 4px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.cic-stat-l { + font-size: 11px; + font-weight: 600; + color: var(--cic-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cic-stat-v { + font-size: 18px; + font-weight: 700; + color: var(--cic-g900); + line-height: 1.2; +} + +.cic-stat-meta { + font-size: 11px; + color: var(--cic-g500); + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 4px; +} + +.cic-stat-gauge { + align-items: center; + justify-content: center; +} + +/* GAUGE */ +.gauge { + width: 140px; + height: 140px; + border-radius: 50%; + background: conic-gradient(currentcolor calc(var(--pct) * 1%), var(--cic-g100) 0); + display: flex; + align-items: center; + justify-content: center; + + &.good { + color: var(--cic-good); + } + + &.moderate { + color: var(--cic-mid); + } + + &.low { + color: var(--cic-low); + } +} + +.gauge-inner { + width: 108px; + height: 108px; + border-radius: 50%; + background: #fff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: inset 0 0 0 1px var(--cic-g200); +} + +.gauge-pct { + font-size: 22px; + font-weight: 700; + color: var(--cic-g900); + line-height: 1; +} + +.gauge-label { + font-size: 10px; + color: var(--cic-g500); + margin-top: 2px; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 600; +} + +/* STATUS PILL */ +.status-pill { + display: inline-block; + padding: 3px 10px; + font-size: 11px; + font-weight: 600; + border-radius: 999px; + + &.st-good { + background: var(--cic-good-l); + color: var(--cic-good); + } + + &.st-mid { + background: var(--cic-mid-l); + color: var(--cic-mid); + } + + &.st-low { + background: var(--cic-low-l); + color: var(--cic-low); + } +} + +/* MINI BADGES IN STAT META */ +.badge { + display: inline-block; + padding: 2px 8px; + border-radius: 999px; + font-size: 10px; + font-weight: 600; + + &.ok { + background: var(--cic-good-l); + color: var(--cic-good); + } + + &.warn { + background: var(--cic-mid-l); + color: var(--cic-mid); + } + + &.err { + background: var(--cic-low-l); + color: var(--cic-low); + } +} + +/* SECTIONS */ +.cic-section { + background: #fff; + border: 1px solid var(--cic-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.cic-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 12px; +} + +.cic-sec-t { + margin: 0 0 12px; + font-size: 14px; + font-weight: 700; + color: var(--cic-g700); +} + +.cic-sec-h .cic-sec-t { + margin-bottom: 0; +} + +.cic-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: #16a34a !important; +} + +/* SUB-DQR BARS */ +.cic-bars { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px 24px; +} + +.cic-bar-h { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 5px; +} + +.cic-bar-l { + font-size: 12px; + color: var(--cic-g700); + font-weight: 500; +} + +.cic-bar-p { + font-size: 12px; + font-weight: 700; + + &.good { + color: var(--cic-good); + } + + &.moderate { + color: var(--cic-mid); + } + + &.low { + color: var(--cic-low); + } +} + +.cic-bar-track { + height: 7px; + background: var(--cic-g100); + border-radius: 4px; + overflow: hidden; +} + +.cic-bar-fill { + height: 100%; + border-radius: 4px; + transition: width 0.3s ease-out; + + &.good { + background: var(--cic-good); + } + + &.moderate { + background: var(--cic-mid); + } + + &.low { + background: var(--cic-low); + } +} + +/* ERRORS TABLE */ +.cic-tbl-wrap { + border: 1px solid var(--cic-g200); + border-radius: 8px; + overflow: hidden; +} + +.cic-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--cic-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--cic-g50); + border-bottom: 1px solid var(--cic-g200); + } + + td { + padding: 10px 14px; + border-bottom: 1px solid var(--cic-g100); + font-size: 13px; + color: var(--cic-g700); + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--cic-g50); + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.sev { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + + &.sev-critical { + background: var(--cic-low-l); + color: var(--cic-low); + } + + &.sev-warning { + background: var(--cic-mid-l); + color: var(--cic-mid); + } + + &.sev-info { + background: #e0e7ff; + color: #4338ca; + } +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .cic-stats { + grid-template-columns: 1fr 1fr; + } + + .cic-bars { + grid-template-columns: 1fr; + } +} + +@media (width <= 700px) { + .cic-stats { + grid-template-columns: 1fr; + } +} diff --git a/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.ts b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.ts new file mode 100644 index 000000000..2ba64760e --- /dev/null +++ b/src/app/home/dashboard/cic-dqr-dialog/cic-dqr-dialog.component.ts @@ -0,0 +1,1032 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +interface DqrErrorRow { + code: string; + segment: string; + description: string; + severity: 'Critical' | 'Warning' | 'Info'; + count: number; +} + +interface DqrSubMetric { + label: string; + pct: number; +} + +interface CicReport { + key: string; + name: string; + full: string; + logo: string; + dqrPct: number; + status: 'Submitted' | 'In Progress' | 'Pending'; + lastSubmitted: string; + nextDue: string; + totalRecords: number; + accepted: number; + rejected: number; + warnings: number; + fileRef: string; + subMetrics: DqrSubMetric[]; + errors: DqrErrorRow[]; +} + +/** + * CIBIL TUDF consumer + tradeline record (one row per loan account submitted to a CIC). + * Schema mirrors the real 72-column CIBIL Consumer file (e.g. COBBNS1059-Consumer-DDMMYYYY-HHMMSS.xls). + * NOTE: ASSEST_CLASSIFICATION typo is intentional — preserved as-is from the published CIBIL spec. + */ +interface ConsumerRecord { + CONSUMER_NAME: string; + DATE_OF_BIRTH: string; + GENDER: string; + INCOME_TAX_ID: string; + PASSPORT: string; + PASSPORT_ISSUE_DATE: string; + PASSPORT_EXPIRY_DATE: string; + VOTER_ID: string; + DRIVING_LICENSE_NO: string; + DRIVING_LICENSE_ISSUE_DATE: string; + DRIVING_LICENSE_EXPIRY_DATE: string; + RATION_CARD_NO: string; + UNIVERSAL_ID: string; + ADDITIONAL_ID_1: string; + ADDITIONAL_ID_2: string; + TELEPHONE_NO_MOBILE: string; + TELEPHONE_NO_RESIDENCE: string; + TELEPHONE_NO_OFFICE: string; + EXTENSION_OFFICE: string; + TELEPHONE_NO_OTHER: string; + EXTENSION_OTHER: string; + EMAIL_ID_1: string; + EMAIL_ID_2: string; + ADDRESS_1: string; + STATE_CODE_1: string; + PINCODE_1: string; + ADDRESS_CATEGORY_1: string; + RESIDENCE_CODE_1: string; + ADDRESS_2: string; + STATE_CODE_2: string; + PINCODE_2: string; + ADDRESS_CATEGORY_2: string; + RESIDENCE_CODE_2: string; + MEMBER_CODE: string; + MEMBER_SHORT_NAME: string; + AC_NUMBER: string; + AC_TYPE: string; + OWNERSHIP_IND: string; + OPENED_DATE: string; + LASTPAYMENTDATE: string; + DATE_CLOSED: string; + DATE_REPORTED: string; + SANCTIONED_AMT: string; + CURRENT_BALANCE: string; + AMOUNT_OVERDUE: string; + NO_OF_DAYS_PAST_DUE: string; + OLD_MEMBER_CODE: string; + OLD_MEMBER_NAME: string; + OLD_ACC_NO: string; + OLD_ACC_TYPE: string; + OLD_OWNERSHIP_INDICATOR: string; + SUIT_FILED: string; + CREDIT_FACILITY_STATUS: string; + ASSEST_CLASSIFICATION: string; + VALUE_OF_COLLATERAL: string; + TYPE_OF_COLLATERAL: string; + CREDIT_LIMIT: string; + CASH_LIMIT: string; + RATE_OF_INTEREST: string; + REPAYMENT_TENURE: string; + EMI_AMOUNT: string; + WRITTEN_OFF_AMOUNT_TOTAL: string; + WRITTEN_OFF_PRINCIPAL_AMOUNT: string; + SETTLEMENT_AMT: string; + PAYMENT_FREQUENCY: string; + ACTUAL_PAY_AMT: string; + OCCU_CODE: string; + INCOME: string; + NET_GROSS_INCOME_ID: string; + MONTHLY_ANNUAL_INCOME_ID: string; + CKYC_ID: string; + NREGA_CARD: string; +} + +@Component({ + selector: 'mifosx-cic-dqr-dialog', + standalone: true, + templateUrl: './cic-dqr-dialog.component.html', + styleUrls: ['./cic-dqr-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + MatDialogModule, + MatButtonModule, + MatTabsModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class CicDqrDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeTab: 'cibil' | 'experian' | 'equifax' | 'crif' = 'cibil'; + + /** Reporting period — current month */ + today = new Date(); + reportingPeriod: string; + + reports: CicReport[] = [ + { + key: 'cibil', + name: 'TransUnion CIBIL', + full: 'Credit Information Bureau India Limited', + logo: 'assets/images/cic-cibil.svg', + dqrPct: 98.2, + status: 'Submitted', + lastSubmitted: '07 Apr 2026', + nextDue: '07 May 2026', + totalRecords: 12420, + accepted: 12197, + rejected: 47, + warnings: 176, + fileRef: 'CIBIL-CIR-202604-INC4711.txt', + subMetrics: [ + { label: 'Personal Data', pct: 99.4 }, + { label: 'Identifier (PAN/UID)', pct: 98.8 }, + { label: 'Account Data', pct: 98.1 }, + { label: 'Address', pct: 96.7 }, + { label: 'Phone / Email', pct: 97.9 } + ], + errors: [ + { + code: 'CR-001', + segment: 'Personal', + description: 'Date of birth missing or invalid format', + severity: 'Critical', + count: 12 + }, + { + code: 'CR-014', + segment: 'Identifier', + description: 'PAN format mismatch (10-char alphanumeric)', + severity: 'Critical', + count: 6 + }, + { + code: 'CR-027', + segment: 'Account', + description: 'Reported amount > sanctioned amount', + severity: 'Critical', + count: 9 + }, + { + code: 'WN-103', + segment: 'Address', + description: 'PIN code not in 6-digit numeric format', + severity: 'Warning', + count: 64 + }, + { + code: 'WN-118', + segment: 'Phone', + description: 'Mobile starts with 0 (should be 6-9)', + severity: 'Warning', + count: 38 + }, + { + code: 'WN-141', + segment: 'Account', + description: 'Closed-date earlier than open-date', + severity: 'Warning', + count: 11 + }, + { + code: 'IN-204', + segment: 'Personal', + description: 'Father name contains numeric characters', + severity: 'Info', + count: 22 + }, + { + code: 'IN-211', + segment: 'Address', + description: 'Address line 1 exceeds 40 chars (truncated)', + severity: 'Info', + count: 41 + } + ] + }, + { + key: 'experian', + name: 'Experian', + full: 'Experian Credit Information Company of India Pvt. Ltd.', + logo: 'assets/images/cic-experian.svg', + dqrPct: 97.5, + status: 'Submitted', + lastSubmitted: '05 Apr 2026', + nextDue: '05 May 2026', + totalRecords: 12420, + accepted: 12108, + rejected: 78, + warnings: 234, + fileRef: 'EXP-CIR-202604-INC4711.xml', + subMetrics: [ + { label: 'Personal Data', pct: 98.7 }, + { label: 'Identifier (PAN/UID)', pct: 98.2 }, + { label: 'Account Data', pct: 97.4 }, + { label: 'Address', pct: 95.9 }, + { label: 'Phone / Email', pct: 97.1 } + ], + errors: [ + { + code: 'EX-CR-002', + segment: 'Personal', + description: 'Gender code not in [M/F/O]', + severity: 'Critical', + count: 18 + }, + { + code: 'EX-CR-019', + segment: 'Identifier', + description: 'Aadhaar (UID) checksum failed', + severity: 'Critical', + count: 8 + }, + { + code: 'EX-CR-031', + segment: 'Account', + description: 'EMI amount missing for term loan', + severity: 'Critical', + count: 14 + }, + { + code: 'EX-WN-105', + segment: 'Address', + description: 'State code outside valid ISO 3166-2', + severity: 'Warning', + count: 89 + }, + { + code: 'EX-WN-122', + segment: 'Phone', + description: 'Phone country code mismatch', + severity: 'Warning', + count: 27 + }, + { + code: 'EX-WN-148', + segment: 'Account', + description: 'Days-Past-Due reported negative', + severity: 'Warning', + count: 18 + }, + { code: 'EX-IN-202', segment: 'Personal', description: 'Marital status missing', severity: 'Info', count: 56 }, + { + code: 'EX-IN-219', + segment: 'Address', + description: 'Permanent and current address identical', + severity: 'Info', + count: 44 + } + ] + }, + { + key: 'equifax', + name: 'Equifax', + full: 'Equifax Credit Information Services Pvt. Ltd.', + logo: 'assets/images/cic-equifax.svg', + dqrPct: 94.8, + status: 'In Progress', + lastSubmitted: '06 Apr 2026', + nextDue: '06 May 2026', + totalRecords: 12420, + accepted: 11774, + rejected: 142, + warnings: 504, + fileRef: 'EFX-CIR-202604-INC4711.txt', + subMetrics: [ + { label: 'Personal Data', pct: 96.8 }, + { label: 'Identifier (PAN/UID)', pct: 95.4 }, + { label: 'Account Data', pct: 94.2 }, + { label: 'Address', pct: 91.8 }, + { label: 'Phone / Email', pct: 95.1 } + ], + errors: [ + { + code: 'EQ-CR-008', + segment: 'Identifier', + description: 'PAN starts with non-letter', + severity: 'Critical', + count: 24 + }, + { + code: 'EQ-CR-022', + segment: 'Account', + description: 'Loan tenure < 1 month', + severity: 'Critical', + count: 19 + }, + { code: 'EQ-CR-035', segment: 'Personal', description: 'DOB > today', severity: 'Critical', count: 7 }, + { + code: 'EQ-CR-048', + segment: 'Account', + description: 'Account holder type code unknown', + severity: 'Critical', + count: 12 + }, + { + code: 'EQ-WN-110', + segment: 'Address', + description: 'City not matched against pincode master', + severity: 'Warning', + count: 142 + }, + { + code: 'EQ-WN-127', + segment: 'Phone', + description: 'Landline missing area code', + severity: 'Warning', + count: 88 + }, + { + code: 'EQ-WN-156', + segment: 'Personal', + description: 'Father name same as customer name', + severity: 'Warning', + count: 31 + }, + { + code: 'EQ-IN-208', + segment: 'Address', + description: 'Country code defaulted to IN', + severity: 'Info', + count: 178 + } + ] + }, + { + key: 'crif', + name: 'CRIF High Mark', + full: 'CRIF High Mark Credit Information Services Pvt. Ltd.', + logo: 'assets/images/cic-crif.svg', + dqrPct: 96.1, + status: 'Submitted', + lastSubmitted: '04 Apr 2026', + nextDue: '04 May 2026', + totalRecords: 12420, + accepted: 11942, + rejected: 96, + warnings: 312, + fileRef: 'CRIF-CIR-202604-INC4711.txt', + subMetrics: [ + { label: 'Personal Data', pct: 97.9 }, + { label: 'Identifier (PAN/UID)', pct: 97.1 }, + { label: 'Account Data', pct: 95.8 }, + { label: 'Address', pct: 94.2 }, + { label: 'Phone / Email', pct: 96.4 } + ], + errors: [ + { + code: 'CF-CR-005', + segment: 'Personal', + description: 'Name contains special characters', + severity: 'Critical', + count: 11 + }, + { + code: 'CF-CR-018', + segment: 'Identifier', + description: 'Voter ID format invalid', + severity: 'Critical', + count: 9 + }, + { + code: 'CF-CR-029', + segment: 'Account', + description: 'Closure reason missing for closed account', + severity: 'Critical', + count: 16 + }, + { + code: 'CF-WN-107', + segment: 'Address', + description: 'Address change not flagged', + severity: 'Warning', + count: 78 + }, + { + code: 'CF-WN-124', + segment: 'Phone', + description: 'Mobile flagged DND but reported', + severity: 'Warning', + count: 42 + }, + { + code: 'CF-WN-152', + segment: 'Account', + description: 'Overdue amount but DPD = 0', + severity: 'Warning', + count: 19 + }, + { + code: 'CF-IN-205', + segment: 'Personal', + description: 'Email domain unverifiable', + severity: 'Info', + count: 88 + }, + { + code: 'CF-IN-217', + segment: 'Address', + description: 'Address quality score below threshold', + severity: 'Info', + count: 64 + } + ] + } + ]; + + /** + * Sample consumer/tradeline rows mirroring the real CIBIL TUDF Consumer file + * (e.g. COBBNS1059-Consumer-DDMMYYYY.xls). Date format = DDMMYYYY (no separator); + * GENDER = '1' (Female) | '2' (Male); STATE_CODE = numeric (23=MP, 27=MH, 29=KA, 24=GJ, 09=UP, 33=TN, 07=DL). + * Codes used: AC_TYPE 02=HL, 03=AL, 04=TL, 05=PL, 07=GL, 10=CC, 15=Agri, 50=Property; OWNERSHIP_IND 1=Single,3=Guarantor,4=Joint; + * SUIT_FILED 00=No; CREDIT_FACILITY_STATUS 00=NoSuit/Active, 13=Closed; ASSEST_CLASSIFICATION 01=Std, 02=SMA-1, 03=SMA-2, 04=SS; + * PAYMENT_FREQUENCY 09=Monthly; OCCU_CODE 01=Salaried,02=Self-Emp,03=Profession,04=Retired; NET_GROSS_INCOME_ID 02=Gross. + */ + consumers: ConsumerRecord[] = [ + this.makeConsumer({ + CONSUMER_NAME: 'PREM NARAYAN LODHI', + DATE_OF_BIRTH: '24121964', + GENDER: '2', + INCOME_TAX_ID: 'AOOPN5028H', + UNIVERSAL_ID: '981481904746', + TELEPHONE_NO_MOBILE: '9522384824', + ADDRESS_1: 'LAL PHATHAR BASODA BASODA BASODA', + STATE_CODE_1: '23', + PINCODE_1: '464221', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001131301000090', + AC_TYPE: '15', + OWNERSHIP_IND: '1', + OPENED_DATE: '16061995', + LASTPAYMENTDATE: '18112025', + SANCTIONED_AMT: '224000', + CURRENT_BALANCE: '751', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + TYPE_OF_COLLATERAL: '00', + RATE_OF_INTEREST: '9', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '220000', + OCCU_CODE: '02', + CKYC_ID: 'NULL' + }), + this.makeConsumer({ + CONSUMER_NAME: 'GHANSHYAM YADAV', + DATE_OF_BIRTH: '11081960', + GENDER: '2', + INCOME_TAX_ID: 'ACDPY7665C', + UNIVERSAL_ID: '237742690370', + TELEPHONE_NO_MOBILE: '9826822168', + ADDRESS_1: 'WARD NO 7 LAXMI NARAYAN MANDIR K EY PASS BASODA BASODA', + STATE_CODE_1: '23', + PINCODE_1: '464221', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001131301000140', + AC_TYPE: '15', + OWNERSHIP_IND: '1', + OPENED_DATE: '05021999', + LASTPAYMENTDATE: '15012026', + SANCTIONED_AMT: '795000', + CURRENT_BALANCE: '525798', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + TYPE_OF_COLLATERAL: '00', + RATE_OF_INTEREST: '9.50', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '60000', + OCCU_CODE: '02', + CKYC_ID: '60022671103351' + }), + this.makeConsumer({ + CONSUMER_NAME: 'RAVINDRA BHAVSAR', + DATE_OF_BIRTH: '03011960', + GENDER: '2', + INCOME_TAX_ID: 'AEVPB6489H', + UNIVERSAL_ID: '592561453214', + TELEPHONE_NO_MOBILE: '9826810033', + ADDRESS_1: 'WARD NO 24 HANUMAN CHOWK BASODA BASODA', + STATE_CODE_1: '23', + PINCODE_1: '464221', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001131301000155', + AC_TYPE: '15', + OWNERSHIP_IND: '1', + OPENED_DATE: '06112000', + LASTPAYMENTDATE: '17052025', + SANCTIONED_AMT: '265000', + CURRENT_BALANCE: '777', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + TYPE_OF_COLLATERAL: '00', + RATE_OF_INTEREST: '9.50', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '3500', + OCCU_CODE: '02', + CKYC_ID: '60040198535752' + }), + this.makeConsumer({ + CONSUMER_NAME: 'SMT VIJAYA DEVI', + ADDRESS_1: 'WO SHRI NIRMAL KUMAR JAIN GANDHI CHOAK GANJ BASODA', + STATE_CODE_1: '23', + PINCODE_1: '464221', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001135201000244', + AC_TYPE: '50', + OWNERSHIP_IND: '3', + OPENED_DATE: '06022003', + LASTPAYMENTDATE: '14012026', + SANCTIONED_AMT: '2850000', + CURRENT_BALANCE: '1533970', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '3', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + VALUE_OF_COLLATERAL: '1850000', + TYPE_OF_COLLATERAL: '01', + RATE_OF_INTEREST: '11.50', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '1000000', + OCCU_CODE: '04', + CKYC_ID: 'NULL' + }), + this.makeConsumer({ + CONSUMER_NAME: 'MUNNI BAI GUPTA', + DATE_OF_BIRTH: '01011979', + GENDER: '1', + INCOME_TAX_ID: 'ABDPG5525A', + UNIVERSAL_ID: '520037914822', + TELEPHONE_NO_MOBILE: '9425148784', + ADDRESS_1: 'WARD N.03 NEHRU CHOWK BASODA GANJ BASODA', + STATE_CODE_1: '23', + PINCODE_1: '464221', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001135801202520', + AC_TYPE: '04', + OWNERSHIP_IND: '4', + OPENED_DATE: '02042025', + LASTPAYMENTDATE: '02042025', + SANCTIONED_AMT: '1600000', + CURRENT_BALANCE: '1743547', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '4', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + TYPE_OF_COLLATERAL: '00', + RATE_OF_INTEREST: '11.50', + PAYMENT_FREQUENCY: '09', + OCCU_CODE: '02', + CKYC_ID: 'NULL' + }), + this.makeConsumer({ + CONSUMER_NAME: 'RAJESH KUMAR SHARMA', + DATE_OF_BIRTH: '10081985', + GENDER: '2', + INCOME_TAX_ID: 'AAQPS8560J', + UNIVERSAL_ID: '981400112233', + TELEPHONE_NO_MOBILE: '9892011001', + EMAIL_ID_1: 'rajesh@example.com', + ADDRESS_1: 'FLAT 402 SUNRISE TOWERS MAROL ANDHERI EAST', + STATE_CODE_1: '27', + PINCODE_1: '400069', + ADDRESS_CATEGORY_1: '01', + RESIDENCE_CODE_1: '01', + AC_NUMBER: '001131302000312', + AC_TYPE: '02', + OWNERSHIP_IND: '1', + OPENED_DATE: '15032020', + LASTPAYMENTDATE: '05042026', + SANCTIONED_AMT: '5000000', + CURRENT_BALANCE: '4200000', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '01', + VALUE_OF_COLLATERAL: '7000000', + TYPE_OF_COLLATERAL: '01', + RATE_OF_INTEREST: '8.50', + REPAYMENT_TENURE: '240', + EMI_AMOUNT: '45000', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '45000', + OCCU_CODE: '01', + INCOME: '750000', + NET_GROSS_INCOME_ID: '02', + MONTHLY_ANNUAL_INCOME_ID: '02', + CKYC_ID: '60011225033445' + }), + this.makeConsumer({ + CONSUMER_NAME: 'PRIYA PATEL', + DATE_OF_BIRTH: '22031990', + GENDER: '1', + INCOME_TAX_ID: 'AKLPS9933P', + UNIVERSAL_ID: '982200221221', + TELEPHONE_NO_MOBILE: '9822055002', + EMAIL_ID_1: 'priya@example.com', + ADDRESS_1: 'B-12 SHIVAJI NAGAR FC ROAD', + STATE_CODE_1: '27', + PINCODE_1: '411005', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001131302000418', + AC_TYPE: '05', + OWNERSHIP_IND: '1', + OPENED_DATE: '10092023', + LASTPAYMENTDATE: '15032026', + SANCTIONED_AMT: '500000', + CURRENT_BALANCE: '320000', + AMOUNT_OVERDUE: '12500', + NO_OF_DAYS_PAST_DUE: '30', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + ASSEST_CLASSIFICATION: '02', + TYPE_OF_COLLATERAL: '00', + RATE_OF_INTEREST: '12.75', + REPAYMENT_TENURE: '36', + EMI_AMOUNT: '16700', + PAYMENT_FREQUENCY: '09', + ACTUAL_PAY_AMT: '16700', + OCCU_CODE: '02', + INCOME: '480000', + NET_GROSS_INCOME_ID: '02', + MONTHLY_ANNUAL_INCOME_ID: '02', + CKYC_ID: '60022144558899' + }), + this.makeConsumer({ + CONSUMER_NAME: 'NIRAV SHAH', + DATE_OF_BIRTH: '18051992', + GENDER: '2', + INCOME_TAX_ID: 'AHKPS5544Z', + PASSPORT: 'M5544321', + PASSPORT_ISSUE_DATE: '12082020', + PASSPORT_EXPIRY_DATE: '11082030', + UNIVERSAL_ID: '982400993311', + TELEPHONE_NO_MOBILE: '9824012010', + EMAIL_ID_1: 'nirav@example.com', + ADDRESS_1: 'A-401 ATLANTIS APARTMENTS SATELLITE ROAD', + STATE_CODE_1: '24', + PINCODE_1: '380015', + ADDRESS_CATEGORY_1: '01', + AC_NUMBER: '001131302000511', + AC_TYPE: '02', + OWNERSHIP_IND: '1', + OPENED_DATE: '14022018', + LASTPAYMENTDATE: '20122025', + DATE_CLOSED: '20122025', + SANCTIONED_AMT: '7500000', + CURRENT_BALANCE: '0', + AMOUNT_OVERDUE: '0', + NO_OF_DAYS_PAST_DUE: '0', + OLD_OWNERSHIP_INDICATOR: '1', + SUIT_FILED: '00', + CREDIT_FACILITY_STATUS: '13', + ASSEST_CLASSIFICATION: '01', + VALUE_OF_COLLATERAL: '11000000', + TYPE_OF_COLLATERAL: '01', + RATE_OF_INTEREST: '8.10', + REPAYMENT_TENURE: '180', + EMI_AMOUNT: '0', + PAYMENT_FREQUENCY: '09', + OCCU_CODE: '01', + INCOME: '1800000', + NET_GROSS_INCOME_ID: '02', + MONTHLY_ANNUAL_INCOME_ID: '02', + CKYC_ID: '60040301229988' + }) + ]; + + /** + * Builds a fully-populated ConsumerRecord by spreading the supplied partial fields + * onto an empty 72-column template. Fields not provided default to '' (empty string in the file). + */ + private makeConsumer(partial: Partial): ConsumerRecord { + const empty: ConsumerRecord = { + CONSUMER_NAME: '', + DATE_OF_BIRTH: '', + GENDER: '', + INCOME_TAX_ID: '', + PASSPORT: '', + PASSPORT_ISSUE_DATE: '', + PASSPORT_EXPIRY_DATE: '', + VOTER_ID: '', + DRIVING_LICENSE_NO: '', + DRIVING_LICENSE_ISSUE_DATE: '', + DRIVING_LICENSE_EXPIRY_DATE: '', + RATION_CARD_NO: '', + UNIVERSAL_ID: '', + ADDITIONAL_ID_1: '', + ADDITIONAL_ID_2: '', + TELEPHONE_NO_MOBILE: '', + TELEPHONE_NO_RESIDENCE: '', + TELEPHONE_NO_OFFICE: '', + EXTENSION_OFFICE: '', + TELEPHONE_NO_OTHER: '', + EXTENSION_OTHER: '', + EMAIL_ID_1: '', + EMAIL_ID_2: '', + ADDRESS_1: '', + STATE_CODE_1: '', + PINCODE_1: '', + ADDRESS_CATEGORY_1: '', + RESIDENCE_CODE_1: '', + ADDRESS_2: '', + STATE_CODE_2: '', + PINCODE_2: '', + ADDRESS_CATEGORY_2: '', + RESIDENCE_CODE_2: '', + MEMBER_CODE: 'COBBNS1059', + MEMBER_SHORT_NAME: 'BNSBM', + AC_NUMBER: '', + AC_TYPE: '', + OWNERSHIP_IND: '', + OPENED_DATE: '', + LASTPAYMENTDATE: '', + DATE_CLOSED: '', + DATE_REPORTED: '15012026', + SANCTIONED_AMT: '', + CURRENT_BALANCE: '', + AMOUNT_OVERDUE: '', + NO_OF_DAYS_PAST_DUE: '', + OLD_MEMBER_CODE: '', + OLD_MEMBER_NAME: '', + OLD_ACC_NO: '', + OLD_ACC_TYPE: '', + OLD_OWNERSHIP_INDICATOR: '', + SUIT_FILED: '', + CREDIT_FACILITY_STATUS: '', + ASSEST_CLASSIFICATION: '', + VALUE_OF_COLLATERAL: '', + TYPE_OF_COLLATERAL: '', + CREDIT_LIMIT: '', + CASH_LIMIT: '', + RATE_OF_INTEREST: '', + REPAYMENT_TENURE: '', + EMI_AMOUNT: '', + WRITTEN_OFF_AMOUNT_TOTAL: '', + WRITTEN_OFF_PRINCIPAL_AMOUNT: '', + SETTLEMENT_AMT: '', + PAYMENT_FREQUENCY: '', + ACTUAL_PAY_AMT: '', + OCCU_CODE: '', + INCOME: '', + NET_GROSS_INCOME_ID: '', + MONTHLY_ANNUAL_INCOME_ID: '', + CKYC_ID: '', + NREGA_CARD: '' + }; + return { ...empty, ...partial }; + } + + /** Tail of header record — DT1..DT64 member-data fields (most empty in real submissions) */ + private dtFields(): string[] { + const dt = new Array(64).fill(''); + dt[0] = 'BRANCH-HO'; // DT1 — branch code + dt[1] = 'CYC-' + this.today.toISOString().slice(0, 7).replace('-', ''); // DT2 — internal cycle id + dt[2] = 'BATCH-001'; // DT3 + return dt; + } + + constructor() { + const month = this.today.toLocaleString('en-IN', { month: 'long', year: 'numeric' }); + this.reportingPeriod = month; + } + + get current(): CicReport { + return this.reports.find((r) => r.key === this.activeTab) || this.reports[0]; + } + + dqrBadge(pct: number): 'good' | 'moderate' | 'low' { + if (pct >= 97) return 'good'; + if (pct >= 95) return 'moderate'; + return 'low'; + } + + statusClass(status: string): string { + if (status === 'Submitted') return 'st-good'; + if (status === 'In Progress') return 'st-mid'; + return 'st-low'; + } + + /** Header column names for the HD line — matches real CIBIL Consumer file (8 fixed + DT1..DT64 = 72) */ + private readonly HD_COLS = [ + 'REPORTING_MEMBER_ID', + 'SHORT_NAME', + 'CYCLE_IDENTIFICATION', + 'DATE_REPORTED', + 'REPORTING_PASSWORD', + 'AUTHENTICATION_METHOD', + 'FUTURE_USE', + 'MEMBER_DATA', + ...Array.from({ length: 64 }, (_, i) => `DT${i + 1}`) + ]; + + /** + * Consumer + tradeline column names — 72-column CIBIL TUDF Consumer schema. + * Order is positional and must not change. Note: ASSEST_CLASSIFICATION typo is from the spec, not a bug. + */ + private readonly CONSUMER_COLS: (keyof ConsumerRecord)[] = [ + 'CONSUMER_NAME', + 'DATE_OF_BIRTH', + 'GENDER', + 'INCOME_TAX_ID', + 'PASSPORT', + 'PASSPORT_ISSUE_DATE', + 'PASSPORT_EXPIRY_DATE', + 'VOTER_ID', + 'DRIVING_LICENSE_NO', + 'DRIVING_LICENSE_ISSUE_DATE', + 'DRIVING_LICENSE_EXPIRY_DATE', + 'RATION_CARD_NO', + 'UNIVERSAL_ID', + 'ADDITIONAL_ID_1', + 'ADDITIONAL_ID_2', + 'TELEPHONE_NO_MOBILE', + 'TELEPHONE_NO_RESIDENCE', + 'TELEPHONE_NO_OFFICE', + 'EXTENSION_OFFICE', + 'TELEPHONE_NO_OTHER', + 'EXTENSION_OTHER', + 'EMAIL_ID_1', + 'EMAIL_ID_2', + 'ADDRESS_1', + 'STATE_CODE_1', + 'PINCODE_1', + 'ADDRESS_CATEGORY_1', + 'RESIDENCE_CODE_1', + 'ADDRESS_2', + 'STATE_CODE_2', + 'PINCODE_2', + 'ADDRESS_CATEGORY_2', + 'RESIDENCE_CODE_2', + 'MEMBER_CODE', + 'MEMBER_SHORT_NAME', + 'AC_NUMBER', + 'AC_TYPE', + 'OWNERSHIP_IND', + 'OPENED_DATE', + 'LASTPAYMENTDATE', + 'DATE_CLOSED', + 'DATE_REPORTED', + 'SANCTIONED_AMT', + 'CURRENT_BALANCE', + 'AMOUNT_OVERDUE', + 'NO_OF_DAYS_PAST_DUE', + 'OLD_MEMBER_CODE', + 'OLD_MEMBER_NAME', + 'OLD_ACC_NO', + 'OLD_ACC_TYPE', + 'OLD_OWNERSHIP_INDICATOR', + 'SUIT_FILED', + 'CREDIT_FACILITY_STATUS', + 'ASSEST_CLASSIFICATION', + 'VALUE_OF_COLLATERAL', + 'TYPE_OF_COLLATERAL', + 'CREDIT_LIMIT', + 'CASH_LIMIT', + 'RATE_OF_INTEREST', + 'REPAYMENT_TENURE', + 'EMI_AMOUNT', + 'WRITTEN_OFF_AMOUNT_TOTAL', + 'WRITTEN_OFF_PRINCIPAL_AMOUNT', + 'SETTLEMENT_AMT', + 'PAYMENT_FREQUENCY', + 'ACTUAL_PAY_AMT', + 'OCCU_CODE', + 'INCOME', + 'NET_GROSS_INCOME_ID', + 'MONTHLY_ANNUAL_INCOME_ID', + 'CKYC_ID', + 'NREGA_CARD' + ]; + + /** + * Builds the 72-column CIBIL TUDF Consumer file as a 2-D row array. + * Layout matches the real .xls (e.g. COBBNS1059-Consumer-DDMMYYYY-HHMMSS.xls): + * Rows 0-3: blank padding + * Row 4: HD column-name header (72 cols) + * Row 5: HD values + * Rows 6-8: blank + * Row 9: Consumer column-name header (72 cols) + * Rows 10+: Consumer/tradeline data (one row per loan account) + */ + private buildTudfRows(report: CicReport): string[][] { + const dd = String(this.today.getDate()).padStart(2, '0'); + const mm = String(this.today.getMonth() + 1).padStart(2, '0'); + const yyyy = String(this.today.getFullYear()); + const dateReported = `${dd}${mm}${yyyy}`; // DDMMYYYY (CIBIL format) + + const rows: string[][] = []; + // 4 blank padding rows + rows.push([], [], [], []); + // HD column header + rows.push([...this.HD_COLS]); + // HD values + rows.push([ + 'COBBNS1059', // REPORTING_MEMBER_ID + 'BNSBM', // SHORT_NAME + 'CO', // CYCLE_IDENTIFICATION (CO = Consumer) + dateReported, // DATE_REPORTED (DDMMYYYY) + 'N', // REPORTING_PASSWORD (N = none) + 'A', // AUTHENTICATION_METHOD (A = standard) + '', // FUTURE_USE + this.consumers.length.toString(), // MEMBER_DATA (record count) + ...this.dtFields() // DT1 - DT64 + ]); + // 3 blank separator rows + rows.push([], [], []); + // Consumer column header + rows.push([...this.CONSUMER_COLS]); + // Consumer data rows + for (const c of this.consumers) { + rows.push(this.CONSUMER_COLS.map((k) => (c[k] || '').replace(/\t/g, ' '))); + } + return rows; + } + + /** + * Tab-delimited .xls of the active bureau, in the real CIBIL Consumer schema + * (matches COBBNS1059-Consumer-DDMMYYYY.xls layout: 4 blank rows, HD header at row 5, + * HD values at row 6, 3 blanks, consumer header at row 10, then data). + */ + downloadExcel(): void { + const r = this.current; + const rows = this.buildTudfRows(r); + const tsv = rows.map((row) => row.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + this.saveBlob(tsv, `${r.key.toUpperCase()}-Consumer-${this.dateStamp()}.xls`, 'application/vnd.ms-excel'); + } + + /** Concatenated bureau exports — one section per bureau, separated by a blank row. */ + downloadAllExcel(): void { + const sections = this.reports.map((r) => + this.buildTudfRows(r) + .map((row) => row.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')) + .join('\n') + ); + const tsv = sections.join('\n\n'); + this.saveBlob(tsv, `All-Bureaus-Consumer-${this.dateStamp()}.xls`, 'application/vnd.ms-excel'); + } + + private dateStamp(): string { + const dd = String(this.today.getDate()).padStart(2, '0'); + const mm = String(this.today.getMonth() + 1).padStart(2, '0'); + const yyyy = String(this.today.getFullYear()); + const hh = String(this.today.getHours()).padStart(2, '0'); + const mi = String(this.today.getMinutes()).padStart(2, '0'); + const ss = String(this.today.getSeconds()).padStart(2, '0'); + return `${dd}${mm}${yyyy}-${hh}${mi}${ss}`; + } + + private saveBlob(content: string, filename: string, mime: string): void { + const blob = new Blob([content], { type: `${mime};charset=utf-8` }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + setTab(tab: 'cibil' | 'experian' | 'equifax' | 'crif'): void { + this.activeTab = tab; + } + + close(): void { + this.dialogRef.close(); + } +} diff --git a/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.html b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.html new file mode 100644 index 000000000..43842a57c --- /dev/null +++ b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.html @@ -0,0 +1,260 @@ + + +
+ +
+
+
+
+

CIMS / XBRL Returns — {{ currentForm.name }}

+

+
+
+
+ + +  {{ validation.populated }}/{{ validation.total }} populated + + +
+
+ + +
+ Active Form: + +  {{ currentForm.cimsCode }} +  {{ currentForm.periodicity }} + +  Next due: {{ currentForm.nextDue }} + +
+ + +
+ + + +
+ +
+ + +
+
+

Reporting Entity

+
+
+
+
Bank Name
+
{{ bank.name }}
+
+
+
Branch
+
{{ bank.branch }}
+
+
+
BSR Code
+
{{ bank.bsrCode }}
+
+
+
IFSC
+
{{ bank.ifsc }}
+
+
+
Legal Entity ID
+
{{ bank.legalEntityId }}
+
+
+
PAN
+
{{ bank.pan }}
+
+
+
+ +
+
+

Return Particulars

+
+
+
+
CIMS Form Code
+
{{ currentForm.cimsCode }}
+
+
+
Taxonomy Version
+
{{ currentForm.taxonomy }}
+
+
+
Periodicity
+
{{ currentForm.periodicity }}
+
+
+
Reporting Period
+
{{ bank.quarter }}
+
+
+
As On Date
+
{{ bank.asOnDate }}
+
+
+
Last Filed
+
{{ currentForm.lastFiled }}
+
+
+
Next Due
+
+ {{ currentForm.nextDue }} +
+
+
+
CIMS Portal
+
{{ bank.cimsPortal }}
+
+
+
CIMS User ID
+
{{ bank.cimsUserId }}
+
+
+
+
+ +
+
+

Principal Officer

+
+
+
+
Name
+
{{ bank.principalOfficer }}
+
+
+
Designation
+
{{ bank.poDesignation }}
+
+
+
Email
+
{{ bank.poEmail }}
+
+
+
Mobile
+
{{ bank.poMobile }}
+
+
+
+
+ + + +
+
+

{{ sec }}

+
+
+ + + + + + + + + + + + + + + + + +
XBRL ElementLabelValueUnit
{{ f.ref }} + {{ f.value }} +  % + + {{ f.unit }} +
+
+
+
+ + + +
+
+

XBRL Instance Document Preview

+
+ + + +
+
+ +
+ +
+
+ XBRL Document Valid — ready to upload + {{ validation.empty }} field(s) need attention +
+
+ {{ validation.populated }} of {{ validation.total }} elements populated · Taxonomy + {{ currentForm.taxonomy }} +
+
+
+ +
{{ generateXbrlXml() }}
+
+
+
+ + +
+
+   + Submitted to RBI CIMS Portal under {{ currentForm.cimsCode }} by + {{ bank.principalOfficer }} +
+
+ + +
+
+
diff --git a/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.scss b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.scss new file mode 100644 index 000000000..d6d834e6c --- /dev/null +++ b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.scss @@ -0,0 +1,514 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --cims-good: #15803d; + --cims-good-l: #dcfce7; + --cims-mid: #d97706; + --cims-mid-l: #fef3c7; + --cims-low: #c0392b; + --cims-low-l: #fdecea; + --cims-cyan: #0891b2; + --cims-cyan-l: #cffafe; + --cims-teal: #0d9488; + --cims-teal-l: #ccfbf1; + --cims-g50: #f8fafc; + --cims-g100: #f1f5f9; + --cims-g200: #e2e8f0; + --cims-g300: #cbd5e1; + --cims-g500: #64748b; + --cims-g700: #334155; + --cims-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--cims-g900); +} + +.cims-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 92vh; +} + +/* HEADER */ +.cims-head { + padding: 18px 22px; + border-bottom: 1px solid var(--cims-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #ecfeff 0%, #fff 100%); +} + +.cims-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.cims-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--cims-cyan-l); + color: var(--cims-cyan); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.cims-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--cims-g900); +} + +.cims-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--cims-g500); +} + +.cims-head-actions { + display: flex; + align-items: center; + gap: 12px; +} + +.cims-status { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 6px 12px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + + &.valid { + background: var(--cims-good-l); + color: var(--cims-good); + } + + &.warning { + background: var(--cims-mid-l); + color: var(--cims-mid); + } +} + +.cims-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--cims-g200); + background: #fff; + font-size: 14px; + color: var(--cims-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--cims-g100); + color: var(--cims-g900); + } +} + +/* FORM SELECTOR BAR */ +.cims-form-bar { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 22px; + background: var(--cims-g50); + border-bottom: 1px solid var(--cims-g200); + flex-shrink: 0; + flex-wrap: wrap; +} + +.cims-form-bar-l { + font-size: 11px; + font-weight: 700; + color: var(--cims-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cims-form-pill { + padding: 6px 14px; + border-radius: 999px; + border: 1px solid var(--cims-g200); + background: #fff; + cursor: pointer; + font-family: inherit; + font-size: 12px; + font-weight: 600; + color: var(--cims-g700); + transition: all 0.15s; + + &:hover { + background: var(--cims-cyan-l); + border-color: var(--cims-cyan); + color: var(--cims-cyan); + } + + &.active { + background: var(--cims-cyan); + border-color: var(--cims-cyan); + color: #fff; + box-shadow: 0 2px 6px rgb(8 145 178 / 25%); + } +} + +.cims-form-meta { + display: inline-flex; + align-items: center; + font-size: 11px; + color: var(--cims-g500); + padding: 4px 10px; + background: #fff; + border: 1px solid var(--cims-g200); + border-radius: 6px; + + fa-icon { + color: var(--cims-cyan); + margin-right: 2px; + } + + strong { + color: var(--cims-g900); + margin-left: 3px; + } +} + +/* TABS */ +.cims-tabs { + display: flex; + background: #fff; + border-bottom: 1px solid var(--cims-g200); + padding: 0 22px; + overflow-x: auto; + flex-shrink: 0; +} + +.cims-tab { + display: flex; + align-items: center; + padding: 12px 18px; + border: none; + background: transparent; + cursor: pointer; + font-family: inherit; + font-size: 13px; + font-weight: 600; + color: var(--cims-g500); + border-bottom: 2px solid transparent; + transition: all 0.15s; + white-space: nowrap; + + &:hover { + color: var(--cims-g700); + background: var(--cims-g50); + } + + &.active { + background: #fff; + color: var(--cims-cyan); + border-bottom-color: var(--cims-cyan); + } +} + +/* BODY */ +.cims-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + flex: 1; + background: var(--cims-g50); +} + +.cims-section { + background: #fff; + border: 1px solid var(--cims-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.cims-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 14px; + flex-wrap: wrap; +} + +.cims-sec-t { + margin: 0; + font-size: 14px; + font-weight: 700; + color: var(--cims-g700); +} + +.cims-sec-actions { + display: flex; + gap: 8px; +} + +.cims-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: var(--cims-cyan) !important; +} + +.cims-btn-excel { + background: #16a34a !important; +} + +.cims-btn-ghost { + font-size: 12px !important; + height: 34px !important; + color: var(--cims-g700) !important; + background: #fff !important; + border: 1px solid var(--cims-g200) !important; +} + +/* INFO GRID */ +.cims-info-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px 24px; +} + +.cims-info { + display: flex; + flex-direction: column; + gap: 2px; +} + +.cims-info-l { + font-size: 10px; + font-weight: 600; + color: var(--cims-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cims-info-v { + font-size: 13px; + font-weight: 600; + color: var(--cims-g900); +} + +.cims-desc { + margin-top: 12px; + padding: 10px 14px; + background: var(--cims-g50); + border-left: 3px solid var(--cims-cyan); + border-radius: 4px; + font-size: 12px; + color: var(--cims-g700); + line-height: 1.5; +} + +/* TABLE */ +.cims-tbl-wrap { + border: 1px solid var(--cims-g200); + border-radius: 8px; + overflow: auto; +} + +.cims-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--cims-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--cims-g50); + border-bottom: 1px solid var(--cims-g200); + white-space: nowrap; + } + + td { + padding: 10px 14px; + border-bottom: 1px solid var(--cims-g100); + font-size: 13px; + color: var(--cims-g700); + vertical-align: middle; + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--cims-g50); + } +} + +.cell-ref { + color: var(--cims-cyan); + font-weight: 600; +} + +.unit-pill { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + background: var(--cims-teal-l); + color: var(--cims-teal); + font-family: 'DM Mono', monospace; + font-size: 10px; + font-weight: 600; +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.muted { + color: var(--cims-g500); + font-weight: 400; +} + +/* VALIDATION BANNER */ +.cims-validation { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + border-radius: 8px; + margin-bottom: 12px; + + fa-icon { + font-size: 20px; + } + + &.valid { + background: var(--cims-good-l); + color: var(--cims-good); + } + + &.warning { + background: var(--cims-mid-l); + color: var(--cims-mid); + } +} + +.cims-val-t { + font-size: 13px; + font-weight: 700; +} + +.cims-val-d { + font-size: 11px; + margin-top: 2px; + opacity: 0.85; +} + +/* XBRL PREVIEW */ +.xbrl-preview { + background: #0f172a; + color: #e2e8f0; + padding: 16px; + border-radius: 8px; + font-family: 'DM Mono', 'Cascadia Code', monospace; + font-size: 11.5px; + line-height: 1.6; + overflow: auto; + max-height: 480px; + margin: 0; + white-space: pre; + border: 1px solid var(--cims-g200); + + code { + font-family: inherit; + background: transparent; + color: inherit; + padding: 0; + } +} + +/* FOOTER */ +.cims-foot { + padding: 12px 22px; + border-top: 1px solid var(--cims-g200); + background: #fff; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-shrink: 0; + flex-wrap: wrap; +} + +.cims-foot-l { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: var(--cims-g500); + + fa-icon { + color: var(--cims-cyan); + } +} + +.cims-foot-actions { + display: flex; + gap: 8px; +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .cims-info-grid { + grid-template-columns: 1fr 1fr; + } +} + +@media (width <= 720px) { + .cims-info-grid { + grid-template-columns: 1fr; + } + + .cims-head, + .cims-form-bar, + .cims-foot { + flex-wrap: wrap; + } +} diff --git a/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.ts b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.ts new file mode 100644 index 000000000..fbf07020c --- /dev/null +++ b/src/app/home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component.ts @@ -0,0 +1,840 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +export type CimsFormCode = 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB'; + +export interface CimsXbrlDialogData { + initialForm?: CimsFormCode; +} + +interface CimsField { + ref: string; + label: string; + section: string; + value: number | string; + unit?: 'INR-Lakh' | 'INR-Cr' | 'Pure' | 'Percent' | 'Number'; + type?: 'monetary' | 'numeric' | 'string' | 'percent'; +} + +interface CimsForm { + code: CimsFormCode; + name: string; + fullName: string; + description: string; + cimsCode: string; + taxonomy: string; + periodicity: 'Daily' | 'Monthly' | 'Quarterly' | 'Half-Yearly' | 'Annual'; + lastFiled: string; + nextDue: string; + fields: CimsField[]; +} + +@Component({ + selector: 'mifosx-cims-xbrl-dialog', + standalone: true, + templateUrl: './cims-xbrl-dialog.component.html', + styleUrls: ['./cims-xbrl-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + FormsModule, + MatDialogModule, + MatButtonModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class CimsXbrlDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeForm: CimsFormCode; + activeTab: 'filing' | 'data' | 'preview' = 'filing'; + + bank = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + bsrCode: 'BUBL0000123', + ifsc: 'BUBL0000123', + legalEntityId: 'BUBL-LEI-2025', + pan: 'AAACB1234L', + asOnDate: '31-03-2026', + quarter: 'Q4 FY 2025-26', + fiscalYear: '2025-26', + principalOfficer: 'Karthik Kamath', + poDesignation: 'Chief Compliance Officer', + poEmail: 'compliance@bharaturbanbank.in', + poMobile: '+91-9988002211', + cimsUserId: 'BUBL_CIMS_001', + cimsPortal: 'https://cims.rbi.org.in/' + }; + + forms: Record = { + 'OSS-2': { + code: 'OSS-2', + name: 'OSS-2', + fullName: 'Off-site Surveillance — Statement of Asset Quality & Capital Adequacy', + description: 'Quarterly return on asset classification, NPA, capital adequacy and profitability indicators.', + cimsCode: 'CIMS_OSS_002', + taxonomy: 'rbi-cims-oss-2024-04-01', + periodicity: 'Quarterly', + lastFiled: '15-01-2026', + nextDue: '15-04-2026', + fields: [ + { + ref: 'oss2:CapAdq.CRAR', + section: 'Capital Adequacy', + label: 'CRAR (Total)', + value: 14.82, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:CapAdq.Tier1', + section: 'Capital Adequacy', + label: 'Tier-I CRAR', + value: 12.18, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:CapAdq.Tier2', + section: 'Capital Adequacy', + label: 'Tier-II CRAR', + value: 2.64, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Asset.Total', + section: 'Asset Classification', + label: 'Total Advances (₹ Lakh)', + value: 416265, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.Standard', + section: 'Asset Classification', + label: 'Standard Assets (₹ Lakh)', + value: 401820, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.SubStd', + section: 'Asset Classification', + label: 'Sub-Standard (₹ Lakh)', + value: 6220, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.Doubtful', + section: 'Asset Classification', + label: 'Doubtful (₹ Lakh)', + value: 5840, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.Loss', + section: 'Asset Classification', + label: 'Loss (₹ Lakh)', + value: 2385, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.GNPA', + section: 'NPA Position', + label: 'Gross NPA (₹ Lakh)', + value: 14445, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.NNPA', + section: 'NPA Position', + label: 'Net NPA (₹ Lakh)', + value: 5212, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'oss2:Asset.GNPAPct', + section: 'NPA Position', + label: 'GNPA %', + value: 3.47, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Asset.NNPAPct', + section: 'NPA Position', + label: 'NNPA %', + value: 1.27, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Asset.PCR', + section: 'NPA Position', + label: 'Provision Coverage Ratio', + value: 64.13, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Profit.NIM', + section: 'Profitability', + label: 'Net Interest Margin', + value: 3.21, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Profit.ROA', + section: 'Profitability', + label: 'Return on Assets', + value: 0.92, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Profit.ROE', + section: 'Profitability', + label: 'Return on Equity', + value: 11.48, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Liq.CRR', + section: 'Liquidity', + label: 'CRR Maintained', + value: 4.52, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'oss2:Liq.SLR', + section: 'Liquidity', + label: 'SLR Maintained', + value: 19.24, + unit: 'Percent', + type: 'percent' + } + ] + }, + PSA: { + code: 'PSA', + name: 'PSA', + fullName: 'Priority Sector Advances — Annual Achievement Statement', + description: 'Annual return on priority sector lending, sub-targets and weaker section coverage.', + cimsCode: 'CIMS_PSA_001', + taxonomy: 'rbi-cims-psa-2024-04-01', + periodicity: 'Annual', + lastFiled: '30-04-2025', + nextDue: '30-04-2026', + fields: [ + { + ref: 'psa:Reference.ANBC', + section: 'Reference', + label: 'Adjusted Net Bank Credit (₹ Lakh)', + value: 600000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:Reference.OBE', + section: 'Reference', + label: 'Credit Equivalent of OBE (₹ Lakh)', + value: 25000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.Total', + section: 'PSL Achievement', + label: 'Total PSL Outstanding (₹ Lakh)', + value: 241700, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.TotalPct', + section: 'PSL Achievement', + label: 'Total PSL % of ANBC', + value: 40.28, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'psa:PSL.Agri', + section: 'Sub-targets', + label: 'Agriculture (₹ Lakh)', + value: 124000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.AgriPct', + section: 'Sub-targets', + label: 'Agriculture % of ANBC', + value: 20.67, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'psa:PSL.SMF', + section: 'Sub-targets', + label: 'Small & Marginal Farmers (₹ Lakh)', + value: 72000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.SMFPct', + section: 'Sub-targets', + label: 'SMF % of ANBC', + value: 12.0, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'psa:PSL.MicroEnt', + section: 'Sub-targets', + label: 'Micro Enterprises (₹ Lakh)', + value: 51000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.MicroPct', + section: 'Sub-targets', + label: 'Micro Enterprises % of ANBC', + value: 8.5, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'psa:PSL.Weaker', + section: 'Sub-targets', + label: 'Weaker Sections (₹ Lakh)', + value: 85500, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.WeakerPct', + section: 'Sub-targets', + label: 'Weaker Sections % of ANBC', + value: 14.25, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'psa:PSL.Housing', + section: 'Other Categories', + label: 'Housing (₹ Lakh)', + value: 9200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.Education', + section: 'Other Categories', + label: 'Education (₹ Lakh)', + value: 4200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.Export', + section: 'Other Categories', + label: 'Export Credit (₹ Lakh)', + value: 3800, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'psa:PSL.RenEnergy', + section: 'Other Categories', + label: 'Renewable Energy (₹ Lakh)', + value: 2400, + unit: 'INR-Lakh', + type: 'monetary' + } + ] + }, + RAL: { + code: 'RAL', + name: 'RAL', + fullName: 'Return on Assets & Liabilities (Half-Yearly)', + description: 'Half-yearly statement of bank-wide assets and liabilities composition for cooperative banks.', + cimsCode: 'CIMS_RAL_004', + taxonomy: 'rbi-cims-ral-2024-04-01', + periodicity: 'Half-Yearly', + lastFiled: '30-10-2025', + nextDue: '30-04-2026', + fields: [ + { + ref: 'ral:Liab.Capital', + section: 'Liabilities', + label: 'Capital (₹ Lakh)', + value: 22000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.Reserves', + section: 'Liabilities', + label: 'Reserves & Surplus (₹ Lakh)', + value: 32400, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.DepDemand', + section: 'Liabilities', + label: 'Demand Deposits (₹ Lakh)', + value: 84200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.DepSavings', + section: 'Liabilities', + label: 'Savings Deposits (₹ Lakh)', + value: 218600, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.DepTerm', + section: 'Liabilities', + label: 'Term Deposits (₹ Lakh)', + value: 462800, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.Borrow', + section: 'Liabilities', + label: 'Borrowings (₹ Lakh)', + value: 24180, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Liab.Other', + section: 'Liabilities', + label: 'Other Liabilities & Provisions (₹ Lakh)', + value: 18420, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Cash', + section: 'Assets', + label: 'Cash on Hand (₹ Lakh)', + value: 8420, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.RBI', + section: 'Assets', + label: 'Balances with RBI (₹ Lakh)', + value: 38400, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Banks', + section: 'Assets', + label: 'Balances with Banks (₹ Lakh)', + value: 24800, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Money', + section: 'Assets', + label: 'Money at Call & Short Notice (₹ Lakh)', + value: 12400, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Invest', + section: 'Assets', + label: 'Investments (₹ Lakh)', + value: 184600, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Adv', + section: 'Assets', + label: 'Advances (₹ Lakh)', + value: 416265, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Fixed', + section: 'Assets', + label: 'Fixed Assets (₹ Lakh)', + value: 14200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:Asset.Other', + section: 'Assets', + label: 'Other Assets (₹ Lakh)', + value: 12480, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:OBE.Guar', + section: 'Off-Balance Sheet', + label: 'Bank Guarantees (₹ Lakh)', + value: 18420, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'ral:OBE.LC', + section: 'Off-Balance Sheet', + label: 'Letters of Credit (₹ Lakh)', + value: 6580, + unit: 'INR-Lakh', + type: 'monetary' + } + ] + }, + 'RCP-UCB': { + code: 'RCP-UCB', + name: 'RCP (UCB)', + fullName: 'Return on Cash Position — Urban Cooperative Banks', + description: 'Fortnightly statement of CRR/SLR maintenance, NDTL composition and reserve position.', + cimsCode: 'CIMS_RCP_UCB_007', + taxonomy: 'rbi-cims-rcp-ucb-2024-04-01', + periodicity: 'Monthly', + lastFiled: '14-04-2026', + nextDue: '28-04-2026', + fields: [ + { + ref: 'rcp:Liab.DemandDep', + section: 'NDTL Composition', + label: 'Demand Liabilities (₹ Lakh)', + value: 84200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:Liab.TimeDep', + section: 'NDTL Composition', + label: 'Time Liabilities (₹ Lakh)', + value: 681400, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:Liab.NDTL', + section: 'NDTL Composition', + label: 'Net Demand & Time Liabilities (NDTL)', + value: 740000, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:CRR.Required', + section: 'CRR Maintenance', + label: 'CRR Required (4.5%)', + value: 33300, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:CRR.Maintained', + section: 'CRR Maintenance', + label: 'CRR Maintained (avg)', + value: 33448, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:CRR.MaintainedPct', + section: 'CRR Maintenance', + label: 'CRR % Maintained', + value: 4.52, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'rcp:CRR.Surplus', + section: 'CRR Maintenance', + label: 'CRR Surplus / Shortfall (₹ Lakh)', + value: 148, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:SLR.Required', + section: 'SLR Maintenance', + label: 'SLR Required (18%)', + value: 133200, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:SLR.Maintained', + section: 'SLR Maintenance', + label: 'SLR Maintained (avg)', + value: 142376, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:SLR.MaintainedPct', + section: 'SLR Maintenance', + label: 'SLR % Maintained', + value: 19.24, + unit: 'Percent', + type: 'percent' + }, + { + ref: 'rcp:SLR.Surplus', + section: 'SLR Maintenance', + label: 'SLR Surplus / Shortfall (₹ Lakh)', + value: 9176, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:Cash.OnHand', + section: 'Cash Position', + label: 'Cash on Hand (₹ Lakh)', + value: 8420, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:Cash.AtRBI', + section: 'Cash Position', + label: 'Cash at RBI (₹ Lakh)', + value: 33448, + unit: 'INR-Lakh', + type: 'monetary' + }, + { + ref: 'rcp:Cash.AtBanks', + section: 'Cash Position', + label: 'Cash at other Banks (₹ Lakh)', + value: 24800, + unit: 'INR-Lakh', + type: 'monetary' + } + ] + } + }; + + formCodes: CimsFormCode[] = [ + 'OSS-2', + 'PSA', + 'RAL', + 'RCP-UCB' + ]; + + data = inject(MAT_DIALOG_DATA, { optional: true }); + + constructor() { + this.activeForm = this.data?.initialForm ?? 'OSS-2'; + } + + get currentForm(): CimsForm { + return this.forms[this.activeForm]; + } + + get sections(): string[] { + const s: string[] = []; + for (const f of this.currentForm.fields) { + if (!s.includes(f.section)) s.push(f.section); + } + return s; + } + fieldsBySection(section: string): CimsField[] { + return this.currentForm.fields.filter((f) => f.section === section); + } + + setForm(code: CimsFormCode): void { + this.activeForm = code; + this.activeTab = 'filing'; + } + setTab(tab: 'filing' | 'data' | 'preview'): void { + this.activeTab = tab; + } + close(): void { + this.dialogRef.close(); + } + + /** Format a number for XBRL output (no commas, 2 dp for monetary/percent) */ + private fmtXbrl(f: CimsField): string { + if (typeof f.value === 'string') return f.value; + if (f.type === 'monetary' || f.type === 'percent') return f.value.toFixed(2); + return f.value.toString(); + } + + /** Build XBRL XML document for a form (representative; real CIMS taxonomy differs) */ + generateXbrlXml(): string { + const f = this.currentForm; + const period = `${this.bank.fiscalYear} (${this.bank.quarter})`; + const ctxId = `ctx_${f.code.replace('-', '_')}`; + const startDate = '2026-01-01'; + const endDate = '2026-03-31'; + const lines: string[] = []; + lines.push(''); + lines.push('`); + lines.push(` `); + lines.push(` `); + lines.push(` `); + lines.push(' '); + lines.push(` ${this.bank.legalEntityId}`); + lines.push(' '); + lines.push(' '); + lines.push(` ${startDate}`); + lines.push(` ${endDate}`); + lines.push(' '); + lines.push(' '); + lines.push(' iso4217:INR'); + lines.push(' xbrli:pure'); + lines.push(' '); + lines.push(` ${this.bank.name}`); + lines.push(` ${this.bank.bsrCode}`); + lines.push(` ${f.cimsCode}`); + lines.push(' '); + for (const fld of f.fields) { + const unitRef = fld.type === 'monetary' ? 'INR_Lakh' : 'Pure'; + const decimals = fld.type === 'monetary' || fld.type === 'percent' ? '2' : '0'; + lines.push( + ` <${fld.ref} contextRef="${ctxId}" unitRef="${unitRef}" decimals="${decimals}">${this.fmtXbrl( + fld + )}` + ); + } + lines.push(''); + return lines.join('\n'); + } + + /** Validation summary — counts fields, flags any zero/missing */ + get validation(): { total: number; populated: number; empty: number; status: 'valid' | 'warning' } { + const fields = this.currentForm.fields; + const populated = fields.filter((f) => f.value !== '' && f.value !== 0).length; + const empty = fields.length - populated; + return { + total: fields.length, + populated, + empty, + status: empty === 0 ? 'valid' : 'warning' + }; + } + + // -------------------- Exports -------------------- + exportXbrl(): void { + const xml = this.generateXbrlXml(); + this.saveBlob(xml, `${this.currentForm.cimsCode}-${this.dateTag()}.xml`, 'application/xml'); + } + copyXbrl(): void { + const xml = this.generateXbrlXml(); + if (navigator.clipboard) navigator.clipboard.writeText(xml); + } + exportExcel(): void { + const f = this.currentForm; + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + f.fullName, + `As On ${this.bank.asOnDate}` + ]); + rows.push([ + `CIMS Code: ${f.cimsCode}`, + `Taxonomy: ${f.taxonomy}` + ]); + rows.push([ + `Periodicity: ${f.periodicity}`, + `Quarter: ${this.bank.quarter}` + ]); + rows.push([ + `Principal Officer: ${this.bank.principalOfficer}`, + `Email: ${this.bank.poEmail}` + ]); + rows.push([]); + rows.push([ + 'Section', + 'Element Ref', + 'Label', + 'Value', + 'Unit' + ]); + for (const fld of f.fields) { + rows.push([ + fld.section, + fld.ref, + fld.label.replace(/&/g, '&'), + this.fmtXbrl(fld), + fld.unit ?? '' + ]); + } + rows.push([]); + rows.push([ + 'Certified by', + this.bank.principalOfficer + ' — ' + this.bank.poDesignation + ]); + this.saveTsv(rows, `${f.cimsCode}-${this.dateTag()}.xls`); + } + + // ---- helpers ---- + private dateTag(): string { + const d = new Date(); + return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`; + } + private saveTsv(rows: string[][], filename: string): void { + const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + this.saveBlob(tsv, filename, 'application/vnd.ms-excel'); + } + private saveBlob(content: string, filename: string, mime: string): void { + const blob = new Blob([content], { type: `${mime};charset=utf-8` }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } +} diff --git a/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.html b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.html new file mode 100644 index 000000000..355e27c99 --- /dev/null +++ b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.html @@ -0,0 +1,419 @@ + + +
+ +
+
+
+
+

CKYC Compliance Reports — {{ currentReport.name }}

+

{{ currentReport.description }}

+
+
+
+  Ref {{ currentReport.sourceRef }} + +  As on {{ bank.asOnDate }} + + +
+
+ + +
+ Report: + +
+ +
+ + + +
+
+
Total Customers
+
{{ rekyc.length | number }}
+
Filtered: {{ filteredRekyc.length | number }}
+
+
+
KYC Overdue
+
{{ rekycOverdue }}
+
Past expiry date
+
+
+
Due in 90 days
+
{{ rekycDueSoon }}
+
Refresh window opening
+
+
+
High Risk
+
{{ rekycHighRisk }}
+
Yearly KYC required
+
+
+
≥ ₹25 Lacs Segment
+
{{ rekycAbove25L }}
+
Enhanced due diligence
+
+
+ +
+
+

Re-KYC Pending Register — {{ filteredRekyc.length }} of {{ rekyc.length }}

+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UCodeCustomer NameTypeRiskBranchKYC ExpiryStatusSegment
{{ r.ucode }}{{ r.name }}{{ r.customerType }} + {{ r.risk }} + + {{ r.branch }} ({{ r.branchCode }}) + {{ r.kycExpiry }} + + OVERDUE {{ r.daysOverdue }}d + DUE IN {{ -r.daysOverdue }}d + WITHIN + + + ≥ 25 L + < 25 L +
No customers match the current filters.
+
+ +
+ TOTAL COUNT: {{ filteredRekyc.length }}  ·  Report Ref: + {{ bank.reportRefNo }}  ·  User Code: + {{ bank.userCode }} +
+
+
+ + + +
+
+
Total Reviews
+
{{ riskReview.length | number }}
+
{{ riskReviewPeriod.from }} to {{ riskReviewPeriod.to }}
+
+
+
Risk Inserted
+
{{ riskInsertedCount }}
+
Occupation / Pancard
+
+
+
Risk Upgraded
+
{{ riskUpgradedCount }}
+
LOW→MED, MED→HIGH
+
+
+
Accounts Frozen
+
{{ riskFrozenCount }}
+
Suspicious / High-Risk
+
+
+ +
+
+

+ Risk Updating History — {{ riskReviewPeriod.from }} TO {{ riskReviewPeriod.to }} +

+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BranchA/c TypeClient IDCustomerOld → New RiskMobileDescriptionFrozenUpdated On
+ {{ r.branch }} ({{ r.branchCode }}) + + {{ r.acType }} + {{ r.clientId }}{{ r.customerName }} + {{ r.oldRisk }} + + {{ r.newRisk }} + {{ r.mobile }}{{ r.description }} + FROZEN + — + {{ r.updatedOn }}
No risk-update entries match the filters.
+
+ +
+ Risk Category Updated Summary — Total Clients: + {{ filteredRiskReview.length }}  ·  Print Date: + {{ bank.asOnDate }} +
+
+
+ + + +
+
+
Total Secured A/c
+
{{ cersaiTotalSecured | number }}
+
Across all branches
+
+
+
CERSAI Found
+
{{ cersaiTotalFound | number }}
+
Registered with CERSAI
+
+
+
CERSAI Not Found
+
{{ cersaiTotalNotFound | number }}
+
Pending registration
+
+
+
Compliance %
+
+ {{ cersaiCompliancePct.toFixed(2) }}% +
+
Target: ≥ 95%
+
+
+ +
+
+

CERSAI Registration Status — Branch & Account-Type Wise

+ +
+ +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Branch CodeBranchA/c TypeCERSAI FoundCERSAI Not FoundTotal SecuredCompliance %
{{ r.branchCode }}{{ r.branch }} + {{ r.acType }} + {{ r.found | number }}{{ r.notFound | number }} + {{ r.totalSecured | number }} + + {{ cersaiPct(r).toFixed(1) }}% +
BANK TOTAL + {{ cersaiTotalFound | number }} + + {{ cersaiTotalNotFound | number }} + + {{ cersaiTotalSecured | number }} + + {{ cersaiCompliancePct.toFixed(2) }}% +
No CERSAI rows match the filters.
+
+ +
+ {{ bank.name }} — {{ bank.branch }} · Certified by + {{ bank.principalOfficer }} · As on + {{ bank.asOnDate }} +
+
+
+
+ + + +
diff --git a/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.scss b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.scss new file mode 100644 index 000000000..3445a6c15 --- /dev/null +++ b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.scss @@ -0,0 +1,610 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --ckr-good: #15803d; + --ckr-good-l: #dcfce7; + --ckr-mid: #d97706; + --ckr-mid-l: #fef3c7; + --ckr-low: #c0392b; + --ckr-low-l: #fdecea; + --ckr-pink: #be185d; + --ckr-pink-l: #fce7f3; + --ckr-purple: #6d28d9; + --ckr-purple-l: #ede9fe; + --ckr-blue: #2563eb; + --ckr-blue-l: #dbeafe; + --ckr-g50: #f8fafc; + --ckr-g100: #f1f5f9; + --ckr-g200: #e2e8f0; + --ckr-g300: #cbd5e1; + --ckr-g500: #64748b; + --ckr-g700: #334155; + --ckr-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--ckr-g900); +} + +.ckr-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 92vh; +} + +/* HEADER */ +.ckr-head { + padding: 18px 22px; + border-bottom: 1px solid var(--ckr-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #fdf2f8 0%, #fff 100%); +} + +.ckr-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.ckr-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--ckr-pink-l); + color: var(--ckr-pink); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.ckr-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--ckr-g900); +} + +.ckr-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--ckr-g500); +} + +.ckr-head-actions { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.ckr-meta { + display: inline-flex; + align-items: center; + font-size: 11px; + color: var(--ckr-g500); + padding: 5px 10px; + background: #fff; + border: 1px solid var(--ckr-g200); + border-radius: 6px; + + fa-icon { + color: var(--ckr-pink); + margin-right: 4px; + } +} + +.ckr-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--ckr-g200); + background: #fff; + font-size: 14px; + color: var(--ckr-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--ckr-g100); + color: var(--ckr-g900); + } +} + +/* REPORT SELECTOR BAR */ +.ckr-sel-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 22px; + background: var(--ckr-g50); + border-bottom: 1px solid var(--ckr-g200); + flex-shrink: 0; + flex-wrap: wrap; +} + +.ckr-sel-l { + font-size: 11px; + font-weight: 700; + color: var(--ckr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-right: 4px; +} + +.ckr-sel-pill { + padding: 7px 16px; + border-radius: 999px; + border: 1px solid var(--ckr-g200); + background: #fff; + cursor: pointer; + font-family: inherit; + font-size: 12px; + font-weight: 600; + color: var(--ckr-g700); + transition: all 0.15s; + display: inline-flex; + align-items: center; + + fa-icon { + color: var(--ckr-g500); + } + + &:hover { + background: var(--ckr-pink-l); + border-color: var(--ckr-pink); + color: var(--ckr-pink); + + fa-icon { + color: var(--ckr-pink); + } + } + + &.active { + background: var(--ckr-pink); + border-color: var(--ckr-pink); + color: #fff; + box-shadow: 0 2px 6px rgb(190 24 93 / 25%); + + fa-icon { + color: #fff; + } + } +} + +/* BODY */ +.ckr-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + flex: 1; + background: var(--ckr-g50); +} + +/* KPI STATS */ +.ckr-stats { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.ckr-stat-card { + background: #fff; + border: 1px solid var(--ckr-g200); + border-radius: 12px; + padding: 14px 16px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); + + &.alert { + background: var(--ckr-low-l); + border-color: var(--ckr-low); + + .ckr-stat-v { + color: var(--ckr-low); + } + } + + &.warn { + background: var(--ckr-mid-l); + border-color: var(--ckr-mid); + + .ckr-stat-v { + color: var(--ckr-mid); + } + } + + &.good { + background: var(--ckr-good-l); + border-color: var(--ckr-good); + + .ckr-stat-v { + color: var(--ckr-good); + } + } +} + +.ckr-stat-l { + font-size: 11px; + font-weight: 600; + color: var(--ckr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.ckr-stat-v { + font-size: 22px; + font-weight: 700; + color: var(--ckr-g900); + line-height: 1.2; + margin-top: 2px; + + &.good { + color: var(--ckr-good); + } + + &.mid { + color: var(--ckr-mid); + } + + &.low { + color: var(--ckr-low); + } +} + +.ckr-stat-meta { + font-size: 11px; + color: var(--ckr-g500); + margin-top: 4px; +} + +/* SECTION */ +.ckr-section { + background: #fff; + border: 1px solid var(--ckr-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.ckr-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.ckr-sec-t { + margin: 0; + font-size: 14px; + font-weight: 700; + color: var(--ckr-g700); +} + +.ckr-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: var(--ckr-pink) !important; +} + +/* FILTERS */ +.ckr-filters { + display: flex; + gap: 12px; + padding: 12px 14px; + background: var(--ckr-g50); + border: 1px solid var(--ckr-g200); + border-radius: 8px; + margin-bottom: 12px; + align-items: flex-end; + flex-wrap: wrap; +} + +.ckr-f { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 140px; + + label { + font-size: 10px; + font-weight: 600; + color: var(--ckr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + select, + input { + height: 32px; + padding: 0 10px; + border: 1px solid var(--ckr-g200); + border-radius: 6px; + background: #fff; + font-family: inherit; + font-size: 12px; + color: var(--ckr-g900); + + &:focus { + outline: none; + border-color: var(--ckr-pink); + box-shadow: 0 0 0 3px rgb(190 24 93 / 12%); + } + } +} + +.ckr-f-reset { + height: 32px; + width: 32px; + border-radius: 6px; + border: 1px solid var(--ckr-g200); + background: #fff; + color: var(--ckr-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--ckr-low-l); + color: var(--ckr-low); + border-color: var(--ckr-low); + } +} + +/* TABLE */ +.ckr-tbl-wrap { + border: 1px solid var(--ckr-g200); + border-radius: 8px; + overflow: auto; +} + +.ckr-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--ckr-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--ckr-g50); + border-bottom: 1px solid var(--ckr-g200); + white-space: nowrap; + } + + td { + padding: 10px 14px; + border-bottom: 1px solid var(--ckr-g100); + font-size: 13px; + color: var(--ckr-g700); + vertical-align: middle; + + &.bad { + color: var(--ckr-low); + font-weight: 600; + } + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--ckr-g50); + } + + .row-total td { + background: var(--ckr-pink-l); + color: var(--ckr-g900); + font-weight: 700; + border-top: 2px solid var(--ckr-pink); + } +} + +/* PILLS */ +.risk-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + + &.risk-low { + background: var(--ckr-good-l); + color: var(--ckr-good); + } + + &.risk-mid { + background: var(--ckr-mid-l); + color: var(--ckr-mid); + } + + &.risk-high { + background: var(--ckr-low-l); + color: var(--ckr-low); + } +} + +.risk-arrow { + margin: 0 6px; + color: var(--ckr-g500); +} + +.actyp-pill { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + background: var(--ckr-blue-l); + color: var(--ckr-blue); + font-family: 'DM Mono', monospace; + font-size: 11px; + font-weight: 600; +} + +.status-pill { + display: inline-block; + padding: 3px 10px; + font-size: 10px; + font-weight: 700; + border-radius: 999px; + letter-spacing: 0.04em; + + &.bad { + background: var(--ckr-low-l); + color: var(--ckr-low); + } + + &.warn { + background: var(--ckr-mid-l); + color: var(--ckr-mid); + } + + &.good { + background: var(--ckr-good-l); + color: var(--ckr-good); + } +} + +.seg-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 6px; + font-size: 11px; + font-weight: 700; + + &.seg-high { + background: var(--ckr-purple-l); + color: var(--ckr-purple); + } + + &.seg-low { + background: var(--ckr-g100); + color: var(--ckr-g500); + } +} + +.pct-pill { + display: inline-block; + padding: 3px 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + + &.good { + background: var(--ckr-good-l); + color: var(--ckr-good); + } + + &.mid { + background: var(--ckr-mid-l); + color: var(--ckr-mid); + } + + &.low { + background: var(--ckr-low-l); + color: var(--ckr-low); + } +} + +.muted { + color: var(--ckr-g300); + font-weight: 400; +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.empty { + text-align: center; + color: var(--ckr-g500); + padding: 24px !important; + font-style: italic; +} + +.ckr-foot { + margin-top: 12px; + padding: 10px 14px; + font-size: 12px; + color: var(--ckr-g500); + background: var(--ckr-g50); + border-radius: 6px; + text-align: right; +} + +/* DIALOG FOOTER */ +.ckr-footer { + padding: 12px 22px; + border-top: 1px solid var(--ckr-g200); + background: #fff; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-shrink: 0; + flex-wrap: wrap; +} + +.ckr-footer-l { + font-size: 12px; + color: var(--ckr-g500); + display: flex; + align-items: center; + + fa-icon { + color: var(--ckr-pink); + margin-right: 4px; + } +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .ckr-stats { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (width <= 720px) { + .ckr-stats { + grid-template-columns: 1fr 1fr; + } + + .ckr-head, + .ckr-sel-bar, + .ckr-footer { + flex-wrap: wrap; + } +} diff --git a/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.ts b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.ts new file mode 100644 index 000000000..9f4270bfe --- /dev/null +++ b/src/app/home/dashboard/ckyc-reports-dialog/ckyc-reports-dialog.component.ts @@ -0,0 +1,849 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +export type CkycReportCode = 'REKYC' | 'RISK-REVIEW' | 'CERSAI'; + +export interface CkycReportsDialogData { + initialReport?: CkycReportCode; +} + +interface RekycRow { + ucode: string; + name: string; + customerType: 'INDIVIDUAL' | 'FIRM' | 'SOCIETY' | 'HUF'; + risk: 'LOW' | 'MEDIUM' | 'HIGH'; + branch: string; + branchCode: string; + kycExpiry: string; + daysOverdue: number; + segment: 'ABOVE-25-LACS' | 'BELOW-25-LACS'; +} + +interface RiskReviewRow { + branchCode: string; + branch: string; + acType: 'SB' | 'CA' | 'OD' | 'TL' | 'CC' | 'LT'; + clientId: string; + customerName: string; + oldRisk: 'LOW' | 'MEDIUM' | 'HIGH'; + newRisk: 'LOW' | 'MEDIUM' | 'HIGH'; + mobile: string; + description: string; + frozen: boolean; + updatedOn: string; +} + +interface CersaiRow { + branchCode: string; + branch: string; + acType: 'TL' | 'CC' | 'OD' | 'HL'; + found: number; + notFound: number; + totalSecured: number; +} + +@Component({ + selector: 'mifosx-ckyc-reports-dialog', + standalone: true, + templateUrl: './ckyc-reports-dialog.component.html', + styleUrls: ['./ckyc-reports-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + FormsModule, + MatDialogModule, + MatButtonModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class CkycReportsDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeReport: CkycReportCode; + + bank = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + asOnDate: '29-01-2026', + fiscalYear: '2025-26', + quarter: 'Q3 FY 2025-26', + principalOfficer: 'Karthik Kamath', + poDesignation: 'Principal Compliance Officer', + reportRefNo: '2075/1', + userCode: 'BUBL01', + fyStart: '01-04-2025', + fyEnd: '31-03-2026' + }; + + reports = [ + { + code: 'REKYC' as CkycReportCode, + name: 'Re-KYC Report', + icon: 'sync-alt', + description: 'Customers due / overdue for periodic KYC refresh based on risk category.', + sourceRef: '2075/1' + }, + { + code: 'RISK-REVIEW' as CkycReportCode, + name: 'Risk Review', + icon: 'exclamation-triangle', + description: 'Audit trail of risk-category changes per RBI Master Direction on KYC.', + sourceRef: '2075/2' + }, + { + code: 'CERSAI' as CkycReportCode, + name: 'CERSAI Report', + icon: 'shield-alt', + description: 'Branch-wise CERSAI registration status for secured advances under SARFAESI.', + sourceRef: '2075/3' + } + ]; + + // -------------------- Re-KYC Data -------------------- + rekycFilterRisk: '' | 'LOW' | 'MEDIUM' | 'HIGH' = ''; + rekycFilterBranch = ''; + rekycFilterSegment: '' | 'ABOVE-25-LACS' | 'BELOW-25-LACS' = ''; + rekycFilterStatus: '' | 'OVERDUE' | 'DUE-SOON' = ''; + + rekyc: RekycRow[] = [ + { + ucode: 'BUBL000201', + name: 'Ravi Kumar Sharma', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Mumbai Main', + branchCode: '01', + kycExpiry: '12-04-2026', + daysOverdue: -73, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL000418', + name: 'Priya Patel', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Pune FC Road', + branchCode: '02', + kycExpiry: '08-12-2025', + daysOverdue: 52, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL000522', + name: 'Arjun Deshmukh', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Nagpur Dharampeth', + branchCode: '03', + kycExpiry: '01-08-2026', + daysOverdue: -184, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL000711', + name: 'Vikram Reddy', + customerType: 'INDIVIDUAL', + risk: 'HIGH', + branch: 'Bengaluru Jayanagar', + branchCode: '04', + kycExpiry: '15-11-2025', + daysOverdue: 75, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL000814', + name: 'Lakshmi Iyer', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Bengaluru Indiranagar', + branchCode: '05', + kycExpiry: '08-10-2026', + daysOverdue: -252, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL001012', + name: 'Nirav Shah', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Ahmedabad Satellite', + branchCode: '06', + kycExpiry: '14-02-2026', + daysOverdue: -16, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL001145', + name: 'Kavita Verma', + customerType: 'INDIVIDUAL', + risk: 'HIGH', + branch: 'Lucknow Gomti Nagar', + branchCode: '07', + kycExpiry: '11-11-2025', + daysOverdue: 79, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL001458', + name: 'Sameer Kapoor', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Delhi Greater Kailash', + branchCode: '08', + kycExpiry: '25-08-2026', + daysOverdue: -208, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL001721', + name: 'Karthik Subramanian', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Chennai Valasaravakkam', + branchCode: '09', + kycExpiry: '03-05-2027', + daysOverdue: -460, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL002012', + name: 'Abhishek Banerjee', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Kolkata Salt Lake', + branchCode: '10', + kycExpiry: '17-12-2025', + daysOverdue: 43, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL002314', + name: 'Gaurav Thakur', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Shimla Sector 4', + branchCode: '11', + kycExpiry: '22-04-2027', + daysOverdue: -448, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL002615', + name: 'Manmohan Singh Gill', + customerType: 'INDIVIDUAL', + risk: 'HIGH', + branch: 'Amritsar Model Town', + branchCode: '12', + kycExpiry: '05-01-2026', + daysOverdue: 24, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL002918', + name: 'Sunita Devi', + customerType: 'INDIVIDUAL', + risk: 'LOW', + branch: 'Bhopal Arera Colony', + branchCode: '13', + kycExpiry: '18-06-2027', + daysOverdue: -505, + segment: 'BELOW-25-LACS' + }, + { + ucode: 'BUBL003221', + name: 'Deepak Tiwari', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Indore Vijay Nagar', + branchCode: '14', + kycExpiry: '12-03-2026', + daysOverdue: -42, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL003524', + name: 'Harpreet Kaur', + customerType: 'INDIVIDUAL', + risk: 'MEDIUM', + branch: 'Ludhiana Sarabha Nagar', + branchCode: '15', + kycExpiry: '08-09-2025', + daysOverdue: 143, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL003812', + name: 'Saraswati Mahila Co-op Society', + customerType: 'SOCIETY', + risk: 'MEDIUM', + branch: 'Mumbai Main', + branchCode: '01', + kycExpiry: '14-12-2025', + daysOverdue: 46, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL004115', + name: 'Aditya Enterprises', + customerType: 'FIRM', + risk: 'HIGH', + branch: 'Pune FC Road', + branchCode: '02', + kycExpiry: '02-10-2025', + daysOverdue: 119, + segment: 'ABOVE-25-LACS' + }, + { + ucode: 'BUBL004318', + name: 'Vasudha Trading HUF', + customerType: 'HUF', + risk: 'MEDIUM', + branch: 'Delhi Greater Kailash', + branchCode: '08', + kycExpiry: '28-11-2025', + daysOverdue: 62, + segment: 'ABOVE-25-LACS' + } + ]; + + // -------------------- Risk Review Data -------------------- + riskFilterBranch = ''; + riskFilterNew: '' | 'LOW' | 'MEDIUM' | 'HIGH' = ''; + riskFilterFrozen: '' | 'Y' | 'N' = ''; + + riskReviewPeriod = { from: '01-10-2025', to: '31-12-2025' }; + + riskReview: RiskReviewRow[] = [ + { + branchCode: '01', + branch: 'Mumbai Main', + acType: 'SB', + clientId: 'BUBL000201', + customerName: 'Ravi Kumar Sharma', + oldRisk: 'LOW', + newRisk: 'LOW', + mobile: '+91-9988002211', + description: 'Periodic Risk Review — Individual', + frozen: false, + updatedOn: '12-10-2025' + }, + { + branchCode: '02', + branch: 'Pune FC Road', + acType: 'CA', + clientId: 'BUBL000418', + customerName: 'Priya Patel', + oldRisk: 'LOW', + newRisk: 'MEDIUM', + mobile: '+91-9617086784', + description: 'Risk Updated — Account Turn Over Medium', + frozen: false, + updatedOn: '18-10-2025' + }, + { + branchCode: '03', + branch: 'Nagpur Dharampeth', + acType: 'TL', + clientId: 'BUBL000522', + customerName: 'Arjun Deshmukh', + oldRisk: 'MEDIUM', + newRisk: 'LOW', + mobile: '+91-9755179366', + description: 'Risk Insert With Pancard verification', + frozen: false, + updatedOn: '02-11-2025' + }, + { + branchCode: '04', + branch: 'Bengaluru Jayanagar', + acType: 'OD', + clientId: 'BUBL000711', + customerName: 'Vikram Reddy', + oldRisk: 'MEDIUM', + newRisk: 'HIGH', + mobile: '+91-9826544604', + description: 'Risk Insert With Occupation — Politically Exposed', + frozen: true, + updatedOn: '08-11-2025' + }, + { + branchCode: '05', + branch: 'Bengaluru Indiranagar', + acType: 'SB', + clientId: 'BUBL000814', + customerName: 'Lakshmi Iyer', + oldRisk: 'LOW', + newRisk: 'LOW', + mobile: '+91-9893677629', + description: 'Identification — Individual Confirmed', + frozen: false, + updatedOn: '15-11-2025' + }, + { + branchCode: '06', + branch: 'Ahmedabad Satellite', + acType: 'CC', + clientId: 'BUBL001012', + customerName: 'Nirav Shah', + oldRisk: 'LOW', + newRisk: 'MEDIUM', + mobile: '+91-9926362230', + description: 'Risk Updated — Account Turn Over Medium', + frozen: false, + updatedOn: '22-11-2025' + }, + { + branchCode: '07', + branch: 'Lucknow Gomti Nagar', + acType: 'CA', + clientId: 'BUBL001145', + customerName: 'Kavita Verma', + oldRisk: 'MEDIUM', + newRisk: 'HIGH', + mobile: '+91-9926322804', + description: 'Suspicious Transaction Trigger — STR raised', + frozen: true, + updatedOn: '28-11-2025' + }, + { + branchCode: '08', + branch: 'Delhi Greater Kailash', + acType: 'OD', + clientId: 'BUBL001458', + customerName: 'Sameer Kapoor', + oldRisk: 'MEDIUM', + newRisk: 'MEDIUM', + mobile: '+91-9827444636', + description: 'Periodic Risk Review — Individual', + frozen: false, + updatedOn: '04-12-2025' + }, + { + branchCode: '09', + branch: 'Chennai Valasaravakkam', + acType: 'SB', + clientId: 'BUBL001721', + customerName: 'Karthik Subramanian', + oldRisk: 'LOW', + newRisk: 'LOW', + mobile: '+91-9827565456', + description: 'Identification — Individual Confirmed', + frozen: false, + updatedOn: '11-12-2025' + }, + { + branchCode: '10', + branch: 'Kolkata Salt Lake', + acType: 'TL', + clientId: 'BUBL002012', + customerName: 'Abhishek Banerjee', + oldRisk: 'LOW', + newRisk: 'MEDIUM', + mobile: '+91-9174151366', + description: 'Risk Insert With Occupation', + frozen: false, + updatedOn: '15-12-2025' + }, + { + branchCode: '11', + branch: 'Shimla Sector 4', + acType: 'SB', + clientId: 'BUBL002314', + customerName: 'Gaurav Thakur', + oldRisk: 'LOW', + newRisk: 'LOW', + mobile: '+91-9111026263', + description: 'Periodic Risk Review — Individual', + frozen: false, + updatedOn: '18-12-2025' + }, + { + branchCode: '12', + branch: 'Amritsar Model Town', + acType: 'CC', + clientId: 'BUBL002615', + customerName: 'Manmohan Singh Gill', + oldRisk: 'HIGH', + newRisk: 'HIGH', + mobile: '+91-9407272122', + description: 'High-Risk Re-confirmation — PEP review', + frozen: true, + updatedOn: '22-12-2025' + }, + { + branchCode: '13', + branch: 'Bhopal Arera Colony', + acType: 'SB', + clientId: 'BUBL002918', + customerName: 'Sunita Devi', + oldRisk: 'LOW', + newRisk: 'LOW', + mobile: '+91-9691683108', + description: 'Identification — Individual Confirmed', + frozen: false, + updatedOn: '24-12-2025' + }, + { + branchCode: '14', + branch: 'Indore Vijay Nagar', + acType: 'OD', + clientId: 'BUBL003221', + customerName: 'Deepak Tiwari', + oldRisk: 'MEDIUM', + newRisk: 'HIGH', + mobile: '+91-9826875772', + description: 'Risk Updated — Account Turn Over High', + frozen: true, + updatedOn: '28-12-2025' + }, + { + branchCode: '15', + branch: 'Ludhiana Sarabha Nagar', + acType: 'TL', + clientId: 'BUBL003524', + customerName: 'Harpreet Kaur', + oldRisk: 'MEDIUM', + newRisk: 'MEDIUM', + mobile: '+91-9407256202', + description: 'Risk Insert With Pancard', + frozen: false, + updatedOn: '30-12-2025' + } + ]; + + // -------------------- CERSAI Data -------------------- + cersai: CersaiRow[] = [ + { branchCode: '01', branch: 'Mumbai Main', acType: 'TL', found: 142, notFound: 8, totalSecured: 150 }, + { branchCode: '02', branch: 'Pune FC Road', acType: 'TL', found: 98, notFound: 4, totalSecured: 102 }, + { branchCode: '02', branch: 'Pune FC Road', acType: 'CC', found: 38, notFound: 12, totalSecured: 50 }, + { branchCode: '03', branch: 'Nagpur Dharampeth', acType: 'HL', found: 64, notFound: 6, totalSecured: 70 }, + { branchCode: '04', branch: 'Bengaluru Jayanagar', acType: 'TL', found: 124, notFound: 0, totalSecured: 124 }, + { branchCode: '04', branch: 'Bengaluru Jayanagar', acType: 'OD', found: 42, notFound: 8, totalSecured: 50 }, + { branchCode: '05', branch: 'Bengaluru Indiranagar', acType: 'HL', found: 86, notFound: 4, totalSecured: 90 }, + { branchCode: '06', branch: 'Ahmedabad Satellite', acType: 'TL', found: 110, notFound: 14, totalSecured: 124 }, + { branchCode: '07', branch: 'Lucknow Gomti Nagar', acType: 'CC', found: 28, notFound: 12, totalSecured: 40 }, + { branchCode: '08', branch: 'Delhi Greater Kailash', acType: 'TL', found: 156, notFound: 4, totalSecured: 160 }, + { branchCode: '08', branch: 'Delhi Greater Kailash', acType: 'OD', found: 64, notFound: 16, totalSecured: 80 }, + { branchCode: '09', branch: 'Chennai Valasaravakkam', acType: 'HL', found: 72, notFound: 8, totalSecured: 80 }, + { branchCode: '10', branch: 'Kolkata Salt Lake', acType: 'TL', found: 88, notFound: 12, totalSecured: 100 }, + { branchCode: '11', branch: 'Shimla Sector 4', acType: 'TL', found: 32, notFound: 8, totalSecured: 40 }, + { branchCode: '12', branch: 'Amritsar Model Town', acType: 'CC', found: 18, notFound: 22, totalSecured: 40 }, + { branchCode: '13', branch: 'Bhopal Arera Colony', acType: 'HL', found: 56, notFound: 4, totalSecured: 60 }, + { branchCode: '14', branch: 'Indore Vijay Nagar', acType: 'TL', found: 92, notFound: 18, totalSecured: 110 }, + { branchCode: '15', branch: 'Ludhiana Sarabha Nagar', acType: 'OD', found: 36, notFound: 14, totalSecured: 50 } + ]; + + cersaiFilterBranch = ''; + cersaiFilterAcType: '' | 'TL' | 'CC' | 'OD' | 'HL' = ''; + + data = inject(MAT_DIALOG_DATA, { optional: true }); + + constructor() { + this.activeReport = this.data?.initialReport ?? 'REKYC'; + } + + // -------------------- Re-KYC computed -------------------- + get filteredRekyc(): RekycRow[] { + const branch = this.rekycFilterBranch.trim().toLowerCase(); + return this.rekyc.filter((r) => { + if (this.rekycFilterRisk && r.risk !== this.rekycFilterRisk) return false; + if (branch && !r.branch.toLowerCase().includes(branch)) return false; + if (this.rekycFilterSegment && r.segment !== this.rekycFilterSegment) return false; + if (this.rekycFilterStatus === 'OVERDUE' && r.daysOverdue <= 0) return false; + if (this.rekycFilterStatus === 'DUE-SOON' && (r.daysOverdue > 0 || r.daysOverdue < -90)) return false; + return true; + }); + } + resetRekycFilters(): void { + this.rekycFilterRisk = ''; + this.rekycFilterBranch = ''; + this.rekycFilterSegment = ''; + this.rekycFilterStatus = ''; + } + get rekycOverdue(): number { + return this.rekyc.filter((r) => r.daysOverdue > 0).length; + } + get rekycDueSoon(): number { + return this.rekyc.filter((r) => r.daysOverdue <= 0 && r.daysOverdue >= -90).length; + } + get rekycHighRisk(): number { + return this.rekyc.filter((r) => r.risk === 'HIGH').length; + } + get rekycAbove25L(): number { + return this.rekyc.filter((r) => r.segment === 'ABOVE-25-LACS').length; + } + + // -------------------- Risk Review computed -------------------- + get filteredRiskReview(): RiskReviewRow[] { + const branch = this.riskFilterBranch.trim().toLowerCase(); + return this.riskReview.filter((r) => { + if (branch && !r.branch.toLowerCase().includes(branch)) return false; + if (this.riskFilterNew && r.newRisk !== this.riskFilterNew) return false; + if (this.riskFilterFrozen === 'Y' && !r.frozen) return false; + if (this.riskFilterFrozen === 'N' && r.frozen) return false; + return true; + }); + } + resetRiskFilters(): void { + this.riskFilterBranch = ''; + this.riskFilterNew = ''; + this.riskFilterFrozen = ''; + } + get riskInsertedCount(): number { + return this.riskReview.filter((r) => r.description.toLowerCase().includes('insert')).length; + } + get riskUpgradedCount(): number { + return this.riskReview.filter( + (r) => (r.oldRisk === 'LOW' && r.newRisk !== 'LOW') || (r.oldRisk === 'MEDIUM' && r.newRisk === 'HIGH') + ).length; + } + get riskFrozenCount(): number { + return this.riskReview.filter((r) => r.frozen).length; + } + riskBadgeClass(risk: 'LOW' | 'MEDIUM' | 'HIGH'): string { + return risk === 'HIGH' ? 'risk-high' : risk === 'MEDIUM' ? 'risk-mid' : 'risk-low'; + } + + // -------------------- CERSAI computed -------------------- + get filteredCersai(): CersaiRow[] { + const branch = this.cersaiFilterBranch.trim().toLowerCase(); + return this.cersai.filter((r) => { + if (branch && !r.branch.toLowerCase().includes(branch)) return false; + if (this.cersaiFilterAcType && r.acType !== this.cersaiFilterAcType) return false; + return true; + }); + } + resetCersaiFilters(): void { + this.cersaiFilterBranch = ''; + this.cersaiFilterAcType = ''; + } + get cersaiTotalFound(): number { + return this.cersai.reduce((s, r) => s + r.found, 0); + } + get cersaiTotalNotFound(): number { + return this.cersai.reduce((s, r) => s + r.notFound, 0); + } + get cersaiTotalSecured(): number { + return this.cersai.reduce((s, r) => s + r.totalSecured, 0); + } + get cersaiCompliancePct(): number { + return this.cersaiTotalSecured === 0 ? 0 : +((this.cersaiTotalFound / this.cersaiTotalSecured) * 100).toFixed(2); + } + cersaiPct(r: CersaiRow): number { + return r.totalSecured === 0 ? 0 : +((r.found / r.totalSecured) * 100).toFixed(1); + } + cersaiPctClass(pct: number): string { + if (pct >= 95) return 'good'; + if (pct >= 80) return 'mid'; + return 'low'; + } + + setReport(code: CkycReportCode): void { + this.activeReport = code; + } + close(): void { + this.dialogRef.close(); + } + + get currentReport() { + return this.reports.find((r) => r.code === this.activeReport)!; + } + + // -------------------- Exports -------------------- + + exportRekycCsv(): void { + const headers = [ + 'UCode', + 'Name', + 'Customer Type', + 'Risk', + 'Branch', + 'Branch Code', + 'KYC Expiry', + 'Days Overdue', + 'Status', + 'Segment' + ]; + const rows = [headers]; + for (const r of this.filteredRekyc) { + const status = r.daysOverdue > 0 ? 'OVERDUE' : r.daysOverdue >= -90 ? 'DUE SOON' : 'WITHIN'; + rows.push([ + r.ucode, + r.name, + r.customerType, + r.risk, + r.branch, + r.branchCode, + r.kycExpiry, + r.daysOverdue.toString(), + status, + r.segment + ]); + } + rows.push([]); + rows.push([ + 'TOTAL COUNT', + this.filteredRekyc.length.toString() + ]); + rows.push([ + 'Report Ref No', + this.bank.reportRefNo, + 'User Code', + this.bank.userCode + ]); + this.saveCsv(rows, `Rekyc-Report-${this.dateTag()}.csv`); + } + + exportRiskReviewCsv(): void { + const headers = [ + 'Branch Code', + 'Branch', + 'A/c Type', + 'Client ID', + 'Customer Name', + 'Old Risk', + 'New Risk', + 'Mobile', + 'Description', + 'Account Frozen', + 'Updated On' + ]; + const rows = [headers]; + for (const r of this.filteredRiskReview) { + rows.push([ + r.branchCode, + r.branch, + r.acType, + r.clientId, + r.customerName, + r.oldRisk, + r.newRisk, + r.mobile, + r.description, + r.frozen ? 'Y' : 'N', + r.updatedOn + ]); + } + rows.push([]); + rows.push(['Risk Category Updated Summary']); + rows.push([ + 'Total Clients', + this.filteredRiskReview.length.toString() + ]); + rows.push([ + 'Risk Inserted', + this.riskInsertedCount.toString() + ]); + rows.push([ + 'Risk Upgraded', + this.riskUpgradedCount.toString() + ]); + rows.push([ + 'Account Frozen', + this.riskFrozenCount.toString() + ]); + rows.push([]); + rows.push([ + 'Period', + `${this.riskReviewPeriod.from} TO ${this.riskReviewPeriod.to}` + ]); + rows.push([ + 'Print Date', + new Date().toLocaleString('en-IN') + ]); + this.saveCsv(rows, `Risk-Review-${this.dateTag()}.csv`); + } + + exportCersaiCsv(): void { + const headers = [ + 'Branch Code', + 'Branch', + 'A/c Type', + 'CERSAI Found', + 'CERSAI Not Found', + 'Total Secured', + 'Compliance %' + ]; + const rows = [headers]; + for (const r of this.filteredCersai) { + rows.push([ + r.branchCode, + r.branch, + r.acType, + r.found.toString(), + r.notFound.toString(), + r.totalSecured.toString(), + this.cersaiPct(r).toFixed(2) + '%' + ]); + } + rows.push([]); + rows.push([ + 'BANK TOTAL', + '', + '', + this.cersaiTotalFound.toString(), + this.cersaiTotalNotFound.toString(), + this.cersaiTotalSecured.toString(), + this.cersaiCompliancePct.toFixed(2) + '%' + ]); + rows.push([]); + rows.push([ + 'Bank', + this.bank.name + ]); + rows.push([ + 'Branch', + this.bank.branch + ]); + rows.push([ + 'As On', + this.bank.asOnDate + ]); + rows.push([ + 'Principal Officer', + this.bank.principalOfficer + ]); + this.saveCsv(rows, `CERSAI-Report-${this.dateTag()}.csv`); + } + + exportCurrentExcel(): void { + if (this.activeReport === 'REKYC') return this.exportRekycCsv(); + if (this.activeReport === 'RISK-REVIEW') return this.exportRiskReviewCsv(); + if (this.activeReport === 'CERSAI') return this.exportCersaiCsv(); + } + + // ---- helpers ---- + private dateTag(): string { + const d = new Date(); + return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`; + } + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n'; + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } +} diff --git a/src/app/home/dashboard/crar-dialog/crar-dialog.component.html b/src/app/home/dashboard/crar-dialog/crar-dialog.component.html index ed3276aa1..d17105661 100644 --- a/src/app/home/dashboard/crar-dialog/crar-dialog.component.html +++ b/src/app/home/dashboard/crar-dialog/crar-dialog.component.html @@ -22,7 +22,13 @@ Q3 FY 2025-26 Reporting Quarter - + RBI Circulars @@ -111,13 +117,19 @@ {{ item.code }} - + {{ item.category }} {{ item.prev }} {{ item.curr }} - {{ item.change }} + + {{ item.change }} + } @@ -159,7 +171,9 @@ {{ item.code }} {{ item.prev }} {{ item.curr }} - {{ item.change }} + + {{ item.change }} + } @@ -199,7 +213,9 @@
{{ item.sub }}
{{ item.code }} - {{ item.rw }} + + {{ item.rw }} + {{ item.gross }} {{ item.rwa }} @@ -240,7 +256,9 @@
{{ item.sub }}
{{ item.code }} - {{ item.approach }} + + {{ item.approach }} + {{ item.cc }} {{ item.rwa }} @@ -276,7 +294,7 @@
Gross Income Average (3-year)
Average of last 3 years positive gross income
- ₹{{ opRiskGrossIncome | number:'1.2-2' }} Cr + ₹{{ opRiskGrossIncome | number: '1.2-2' }} Cr 02 @@ -292,7 +310,7 @@
Capital Charge
Gross Income × Alpha
- ₹{{ opRiskCapitalCharge | number:'1.2-2' }} Cr + ₹{{ opRiskCapitalCharge | number: '1.2-2' }} Cr 04 @@ -300,7 +318,7 @@
RWA (Capital Charge × 12.5)
{{ opRiskCapitalCharge }} × 12.5 = {{ opRiskRWA }}
- ₹{{ opRiskRWA | number:'1.2-2' }} Cr + ₹{{ opRiskRWA | number: '1.2-2' }} Cr @@ -319,25 +337,25 @@
Tier 1 Capital
-
₹{{ totalTier1 | number:'1.2-2' }}
+
₹{{ totalTier1 | number: '1.2-2' }}
CET1 + AT1
+
Tier 2 Capital
-
₹{{ totalTier2 | number:'1.2-2' }}
+
₹{{ totalTier2 | number: '1.2-2' }}
Supplementary
÷
Total RWA
-
₹{{ totalRWA | number:'1.0-0' }}
+
₹{{ totalRWA | number: '1.0-0' }}
Credit + Market + Op
×
-
-
100
-
100
+
+
100
+
100
 
=
@@ -353,7 +371,10 @@
Basel III Compliance Status
-
All thresholds met · CRAR: {{ crar }}% · CET1: {{ cet1Ratio }}% · Tier 1: {{ tier1Ratio }}% · Tier 2: {{ tier2Ratio }}%
+
+ All thresholds met · CRAR: {{ crar }}% · CET1: {{ cet1Ratio }}% · Tier 1: {{ tier1Ratio }}% + · Tier 2: {{ tier2Ratio }}% +
diff --git a/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss b/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss index 17b254499..a80b2411c 100644 --- a/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss +++ b/src/app/home/dashboard/crar-dialog/crar-dialog.component.scss @@ -198,12 +198,29 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; } } -.sc-t1c::before { background: $t1c; } -.sc-t2c::before { background: $t2c; } -.sc-total-cap::before { background: linear-gradient(90deg, $t1c, $t2c); } -.sc-rwa::before { background: $rwa; } -.sc-crar::before { background: $crar; } -.sc-buffer::before { background: $green; } +.sc-t1c::before { + background: $t1c; +} + +.sc-t2c::before { + background: $t2c; +} + +.sc-total-cap::before { + background: linear-gradient(90deg, $t1c, $t2c); +} + +.sc-rwa::before { + background: $rwa; +} + +.sc-crar::before { + background: $crar; +} + +.sc-buffer::before { + background: $green; +} .sc-label { font-size: 10px; @@ -239,8 +256,13 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; margin-top: 4px; } -.up { color: $green; } -.dn { color: $red; } +.up { + color: $green; +} + +.dn { + color: $red; +} // ===== Tab Navigation ===== .tab-nav { @@ -291,11 +313,35 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; letter-spacing: 0.5px; } -.pill-t1c { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.pill-t2c { background: $t2c-bg; color: $t2c; border: 1px solid $t2c-b; } -.pill-cr { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.pill-mr { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.pill-or { background: $or-bg; color: $or; border: 1px solid $or-b; } +.pill-t1c { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.pill-t2c { + background: $t2c-bg; + color: $t2c; + border: 1px solid $t2c-b; +} + +.pill-cr { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.pill-mr { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.pill-or { + background: $or-bg; + color: $or; + border: 1px solid $or-b; +} .section-desc { font-size: 12px; @@ -335,15 +381,22 @@ thead th { text-transform: uppercase; white-space: nowrap; - &.right { text-align: right; } + &.right { + text-align: right; + } } tbody tr { border-bottom: 1px solid $border; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } td { @@ -351,7 +404,10 @@ td { color: $ink2; vertical-align: middle; - &.right { text-align: right; font-family: $mono; } + &.right { + text-align: right; + font-family: $mono; + } } .sl { @@ -361,7 +417,10 @@ td { width: 36px; } -.mono { font-family: $mono; font-size: 12px; } +.mono { + font-family: $mono; + font-size: 12px; +} .item-name { font-weight: 500; @@ -379,12 +438,29 @@ td { font-weight: 500; } -.amt-t1c { color: $t1c; } -.amt-t2c { color: $t2c; } -.amt-cr { color: $cr; } -.amt-mr { color: $mr; } -.amt-or { color: $or; } -.amt-crar { color: $crar; } +.amt-t1c { + color: $t1c; +} + +.amt-t2c { + color: $t2c; +} + +.amt-cr { + color: $cr; +} + +.amt-mr { + color: $mr; +} + +.amt-or { + color: $or; +} + +.amt-crar { + color: $crar; +} .status-chip { display: inline-flex; @@ -397,17 +473,70 @@ td { border-radius: 4px; } -.chip-cet1 { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.chip-at1 { background: $rwa-bg; color: $rwa; border: 1px solid $rwa-b; } -.chip-rw { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.chip-approach { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.chip-active { background: #dcfce7; color: #15803d; } -.chip-t1c { background: $t1c-bg; color: $t1c; border: 1px solid $t1c-b; } -.chip-t2c { background: $t2c-bg; color: $t2c; border: 1px solid $t2c-b; } -.chip-cr { background: $cr-bg; color: $cr; border: 1px solid $cr-b; } -.chip-mr { background: $mr-bg; color: $mr; border: 1px solid $mr-b; } -.chip-or { background: $or-bg; color: $or; border: 1px solid $or-b; } -.chip-crar { background: $crar-bg; color: $crar; border: 1px solid $crar-b; } +.chip-cet1 { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.chip-at1 { + background: $rwa-bg; + color: $rwa; + border: 1px solid $rwa-b; +} + +.chip-rw { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.chip-approach { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.chip-active { + background: #dcfce7; + color: #15803d; +} + +.chip-t1c { + background: $t1c-bg; + color: $t1c; + border: 1px solid $t1c-b; +} + +.chip-t2c { + background: $t2c-bg; + color: $t2c; + border: 1px solid $t2c-b; +} + +.chip-cr { + background: $cr-bg; + color: $cr; + border: 1px solid $cr-b; +} + +.chip-mr { + background: $mr-bg; + color: $mr; + border: 1px solid $mr-b; +} + +.chip-or { + background: $or-bg; + color: $or; + border: 1px solid $or-b; +} + +.chip-crar { + background: $crar-bg; + color: $crar; + border: 1px solid $crar-b; +} tfoot tr { background: $surface2; @@ -420,15 +549,35 @@ tfoot td { font-family: $mono; padding: 12px 14px; - &.right { text-align: right; font-size: 14px; } + &.right { + text-align: right; + font-size: 14px; + } } -.foot-t1c { color: $t1c; } -.foot-t2c { color: $t2c; } -.foot-cr { color: $cr; } -.foot-mr { color: $mr; } -.foot-or { color: $or; } -.foot-crar { color: $crar; } +.foot-t1c { + color: $t1c; +} + +.foot-t2c { + color: $t2c; +} + +.foot-cr { + color: $cr; +} + +.foot-mr { + color: $mr; +} + +.foot-or { + color: $or; +} + +.foot-crar { + color: $crar; +} // ===== Formula Block ===== .formula-block { @@ -511,33 +660,58 @@ tfoot td { padding: 0 8px; flex-shrink: 0; - &.minus { color: $red; } - &.equals { color: $ink2; font-weight: 500; } + &.minus { + color: $red; + } + + &.equals { + color: $ink2; + font-weight: 500; + } } .ft-t1c { border-color: $t1c-b; background: $t1c-bg; - .formula-term-label, .formula-term-val { color: $t1c; } + + .formula-term-label, + .formula-term-val { + color: $t1c; + } } .ft-t2c { border-color: $t2c-b; background: $t2c-bg; - .formula-term-label, .formula-term-val { color: $t2c; } + + .formula-term-label, + .formula-term-val { + color: $t2c; + } } .ft-rwa { border-color: $rwa-b; background: $rwa-bg; - .formula-term-label, .formula-term-val { color: $rwa; } + + .formula-term-label, + .formula-term-val { + color: $rwa; + } } .ft-crar-result { border-color: $crar-b; background: linear-gradient(135deg, #f0fdf4, #ecfdf5); - .formula-term-label { color: #15803d; } - .formula-term-val { color: $crar; font-size: 20px; } + + .formula-term-label { + color: #15803d; + } + + .formula-term-val { + color: $crar; + font-size: 20px; + } } // ===== Compliance Bar ===== @@ -553,7 +727,9 @@ tfoot td { flex-wrap: wrap; } -.compliance-left { flex: 1; } +.compliance-left { + flex: 1; +} .compliance-title { font-size: 13px; @@ -572,7 +748,9 @@ tfoot td { gap: 24px; } -.compliance-calc { text-align: right; } +.compliance-calc { + text-align: right; +} .compliance-calc-label { font-size: 10px; @@ -587,7 +765,9 @@ tfoot td { color: #14532d; font-family: $mono; - &.surplus { color: $crar; } + &.surplus { + color: $crar; + } } // ===== Footer ===== diff --git a/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts b/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts index 675f13b83..a1e937346 100644 --- a/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts +++ b/src/app/home/dashboard/crar-dialog/crar-dialog.component.ts @@ -35,9 +35,9 @@ export class CrarDialogComponent { activeTab: 'tier1' | 'tier2' | 'creditRisk' | 'marketRisk' | 'opRisk' = 'tier1'; /** Capital Adequacy computations */ - readonly totalTier1 = 8284.80; - readonly totalTier2 = 1556.00; - readonly totalCapital = 9840.80; + readonly totalTier1 = 8284.8; + readonly totalTier2 = 1556.0; + readonly totalCapital = 9840.8; readonly totalRWA = 58420; readonly crar = 16.84; readonly cet1Ratio = 12.42; @@ -46,52 +46,251 @@ export class CrarDialogComponent { readonly capitalBuffer = 6.34; /** Credit Risk RWA */ - readonly totalCreditRWA = 47820.40; + readonly totalCreditRWA = 47820.4; /** Market Risk RWA */ - readonly totalMarketRWA = 7005.00; + readonly totalMarketRWA = 7005.0; /** Operational Risk RWA */ - readonly totalOpRWA = 3595.60; + readonly totalOpRWA = 3595.6; tier1Capital = [ - { sl: '01', name: 'Paid-up Equity Share Capital', sub: 'Ordinary equity shares fully paid', code: '5001-ESC', category: 'CET1', prev: '1,200.00', curr: '1,200.00', change: '\u2014' }, - { sl: '02', name: 'Statutory Reserves (Sec 17)', sub: 'Reserves under Sec 17 of BR Act', code: '5002-SR', category: 'CET1', prev: '2,840.60', curr: '3,012.40', change: '+171.80', changeDir: 'up' }, - { sl: '03', name: 'Capital Reserve', sub: 'Non-distributable capital reserves', code: '5003-CR', category: 'CET1', prev: '680.20', curr: '720.80', change: '+40.60', changeDir: 'up' }, - { sl: '04', name: 'Share Premium Account', sub: 'Premium collected on equity issuance', code: '5004-SP', category: 'CET1', prev: '1,480.00', curr: '1,480.00', change: '\u2014' }, - { sl: '05', name: 'Retained Earnings (Current Year)', sub: 'Profit retained after dividend payout', code: '5005-RE', category: 'CET1', prev: '1,060.40', curr: '1,284.60', change: '+224.20', changeDir: 'up' }, - { sl: '06', name: 'PNCPS', sub: 'Perpetual Non-Cumulative Preference Shares', code: '5101-PNCPS', category: 'AT1', prev: '500.00', curr: '500.00', change: '\u2014' }, - { sl: '07', name: 'Perpetual Debt Instruments', sub: 'Basel III compliant perpetual bonds', code: '5102-PDI', category: 'AT1', prev: '800.00', curr: '800.00', change: '\u2014' }, - { sl: '08', name: 'Innovative PDI', sub: 'Innovative perpetual debt instruments', code: '5103-IPDI', category: 'AT1', prev: '200.00', curr: '200.00', change: '\u2014' } + { + sl: '01', + name: 'Paid-up Equity Share Capital', + sub: 'Ordinary equity shares fully paid', + code: '5001-ESC', + category: 'CET1', + prev: '1,200.00', + curr: '1,200.00', + change: '\u2014' + }, + { + sl: '02', + name: 'Statutory Reserves (Sec 17)', + sub: 'Reserves under Sec 17 of BR Act', + code: '5002-SR', + category: 'CET1', + prev: '2,840.60', + curr: '3,012.40', + change: '+171.80', + changeDir: 'up' + }, + { + sl: '03', + name: 'Capital Reserve', + sub: 'Non-distributable capital reserves', + code: '5003-CR', + category: 'CET1', + prev: '680.20', + curr: '720.80', + change: '+40.60', + changeDir: 'up' + }, + { + sl: '04', + name: 'Share Premium Account', + sub: 'Premium collected on equity issuance', + code: '5004-SP', + category: 'CET1', + prev: '1,480.00', + curr: '1,480.00', + change: '\u2014' + }, + { + sl: '05', + name: 'Retained Earnings (Current Year)', + sub: 'Profit retained after dividend payout', + code: '5005-RE', + category: 'CET1', + prev: '1,060.40', + curr: '1,284.60', + change: '+224.20', + changeDir: 'up' + }, + { + sl: '06', + name: 'PNCPS', + sub: 'Perpetual Non-Cumulative Preference Shares', + code: '5101-PNCPS', + category: 'AT1', + prev: '500.00', + curr: '500.00', + change: '\u2014' + }, + { + sl: '07', + name: 'Perpetual Debt Instruments', + sub: 'Basel III compliant perpetual bonds', + code: '5102-PDI', + category: 'AT1', + prev: '800.00', + curr: '800.00', + change: '\u2014' + }, + { + sl: '08', + name: 'Innovative PDI', + sub: 'Innovative perpetual debt instruments', + code: '5103-IPDI', + category: 'AT1', + prev: '200.00', + curr: '200.00', + change: '\u2014' + } ]; tier2Capital = [ - { sl: '01', name: 'Subordinated Debt', sub: 'Long-term subordinated bonds', code: '6001-SD', prev: '620.00', curr: '620.00', change: '\u2014' }, - { sl: '02', name: 'General Provisions & Loan Loss', sub: 'General provisions for standard assets', code: '6002-GP', prev: '480.40', curr: '520.60', change: '+40.20', changeDir: 'up' }, - { sl: '03', name: 'Revaluation Reserves', sub: 'Reserves from asset revaluation (55%)', code: '6003-RR', prev: '184.20', curr: '196.80', change: '+12.60', changeDir: 'up' }, - { sl: '04', name: 'Investment Fluctuation Reserve', sub: 'IFR as per RBI guidelines', code: '6004-IFR', prev: '120.00', curr: '142.60', change: '+22.60', changeDir: 'up' }, - { sl: '05', name: 'Upper Tier 2 Bonds', sub: 'Upper Tier 2 capital instruments', code: '6005-UTB', prev: '80.00', curr: '76.00', change: '-4.00', changeDir: 'dn' } + { + sl: '01', + name: 'Subordinated Debt', + sub: 'Long-term subordinated bonds', + code: '6001-SD', + prev: '620.00', + curr: '620.00', + change: '\u2014' + }, + { + sl: '02', + name: 'General Provisions & Loan Loss', + sub: 'General provisions for standard assets', + code: '6002-GP', + prev: '480.40', + curr: '520.60', + change: '+40.20', + changeDir: 'up' + }, + { + sl: '03', + name: 'Revaluation Reserves', + sub: 'Reserves from asset revaluation (55%)', + code: '6003-RR', + prev: '184.20', + curr: '196.80', + change: '+12.60', + changeDir: 'up' + }, + { + sl: '04', + name: 'Investment Fluctuation Reserve', + sub: 'IFR as per RBI guidelines', + code: '6004-IFR', + prev: '120.00', + curr: '142.60', + change: '+22.60', + changeDir: 'up' + }, + { + sl: '05', + name: 'Upper Tier 2 Bonds', + sub: 'Upper Tier 2 capital instruments', + code: '6005-UTB', + prev: '80.00', + curr: '76.00', + change: '-4.00', + changeDir: 'dn' + } ]; creditRiskRWA = [ - { sl: '01', name: 'Claims on Sovereign', sub: 'Central & State Government exposures', code: '7001-SOV', rw: '0%', gross: '18,420.60', rwa: '0.00' }, - { sl: '02', name: 'Claims on Banks', sub: 'Inter-bank exposures — scheduled banks', code: '7002-BNK', rw: '20%', gross: '4,210.40', rwa: '842.08' }, - { sl: '03', name: 'Corporate Loans Rated', sub: 'Rated corporate term loans', code: '7003-CORP', rw: '100%', gross: '12,840.20', rwa: '12,840.20' }, - { sl: '04', name: 'Retail Portfolio', sub: 'Retail loans — personal, auto, education', code: '7004-RTL', rw: '75%', gross: '8,640.80', rwa: '6,480.60' }, - { sl: '05', name: 'Housing Loans', sub: 'Home loans — LTV based risk weight', code: '7005-HLE', rw: '35%', gross: '6,820.40', rwa: '2,387.14' } + { + sl: '01', + name: 'Claims on Sovereign', + sub: 'Central & State Government exposures', + code: '7001-SOV', + rw: '0%', + gross: '18,420.60', + rwa: '0.00' + }, + { + sl: '02', + name: 'Claims on Banks', + sub: 'Inter-bank exposures — scheduled banks', + code: '7002-BNK', + rw: '20%', + gross: '4,210.40', + rwa: '842.08' + }, + { + sl: '03', + name: 'Corporate Loans Rated', + sub: 'Rated corporate term loans', + code: '7003-CORP', + rw: '100%', + gross: '12,840.20', + rwa: '12,840.20' + }, + { + sl: '04', + name: 'Retail Portfolio', + sub: 'Retail loans — personal, auto, education', + code: '7004-RTL', + rw: '75%', + gross: '8,640.80', + rwa: '6,480.60' + }, + { + sl: '05', + name: 'Housing Loans', + sub: 'Home loans — LTV based risk weight', + code: '7005-HLE', + rw: '35%', + gross: '6,820.40', + rwa: '2,387.14' + } ]; marketRiskRWA = [ - { sl: '01', name: 'Interest Rate Risk', sub: 'Duration & modified duration approach', code: '8001-IRR', approach: 'SDA', cc: '284.60', rwa: '3,557.50' }, - { sl: '02', name: 'Forex Risk', sub: 'Net open position in foreign currencies', code: '8002-FX', approach: 'Std', cc: '148.40', rwa: '1,855.00' }, - { sl: '03', name: 'Equity Risk', sub: 'Listed & unlisted equity exposures', code: '8003-EQR', approach: 'Std', cc: '84.20', rwa: '1,052.50' }, - { sl: '04', name: 'Commodity Price Risk', sub: 'Commodity position risk charges', code: '8004-CMR', approach: 'Simplified', cc: '24.80', rwa: '310.00' }, - { sl: '05', name: 'Options Risk', sub: 'Options portfolio — scenario analysis', code: '8005-OPR', approach: 'Scenario', cc: '18.40', rwa: '230.00' } + { + sl: '01', + name: 'Interest Rate Risk', + sub: 'Duration & modified duration approach', + code: '8001-IRR', + approach: 'SDA', + cc: '284.60', + rwa: '3,557.50' + }, + { + sl: '02', + name: 'Forex Risk', + sub: 'Net open position in foreign currencies', + code: '8002-FX', + approach: 'Std', + cc: '148.40', + rwa: '1,855.00' + }, + { + sl: '03', + name: 'Equity Risk', + sub: 'Listed & unlisted equity exposures', + code: '8003-EQR', + approach: 'Std', + cc: '84.20', + rwa: '1,052.50' + }, + { + sl: '04', + name: 'Commodity Price Risk', + sub: 'Commodity position risk charges', + code: '8004-CMR', + approach: 'Simplified', + cc: '24.80', + rwa: '310.00' + }, + { + sl: '05', + name: 'Options Risk', + sub: 'Options portfolio — scenario analysis', + code: '8005-OPR', + approach: 'Scenario', + cc: '18.40', + rwa: '230.00' + } ]; /** Operational Risk — Basic Indicator Approach */ - readonly opRiskGrossIncome = 2984.40; + readonly opRiskGrossIncome = 2984.4; readonly opRiskAlpha = 15; readonly opRiskCapitalCharge = 447.66; - readonly opRiskRWA = 3595.60; // 447.66 × 12.5 = 5595.75 — using provided value + readonly opRiskRWA = 3595.6; // 447.66 × 12.5 = 5595.75 — using provided value close(): void { this.dialogRef.close(); diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html index 6dc0b90ca..cf231ab89 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.html @@ -22,7 +22,13 @@ {{ reportingDateStr }} Reporting Fortnight (45-day lag)
- + RBI Circulars @@ -56,8 +62,8 @@
CRR Required ({{ crrRate }}%)
-
₹{{ crrRequired | number:'1.0-0' }}Cr
-
SLR Req: ₹{{ slrRequired | number:'1.0-0' }} Cr ({{ slrRate }}%)
+
₹{{ crrRequired | number: '1.0-0' }}Cr
+
SLR Req: ₹{{ slrRequired | number: '1.0-0' }} Cr ({{ slrRate }}%)
@@ -104,11 +110,15 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} + + {{ item.change }} + - + [class.chip-overdue]="item.status === 'Overdue' || item.status === 'Unclaimed'" + > {{ item.status }} @@ -155,11 +165,15 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} + + {{ item.change }} + - + [class.chip-locked]="item.status === 'Locked'" + > {{ item.status }} @@ -206,14 +220,20 @@ {{ item.code }} {{ item.opening }} {{ item.current }} - {{ item.change }} - {{ item.status }} + + {{ item.change }} + + + {{ item.status }} + } - Total Assets with Banking System ( Deducted from NDTL ) + + Total Assets with Banking System ( Deducted from NDTL ) + 7,600.70 8,240.20 +639.50 @@ -226,7 +246,9 @@ @if (activeTab === 'slr') { -
SLR = (Liquid Assets / NDTL) × 100 — RBI Sec 24(2A) of Banking Regulation Act, 1949
+
+ SLR = (Liquid Assets / NDTL) × 100 — RBI Sec 24(2A) of Banking Regulation Act, 1949 +
@@ -250,11 +272,17 @@
{{ item.sub }}
- + - - + + } @@ -286,9 +314,9 @@
Net Demand & Time
×
-
-
100
-
100
+
+
100
+
100
 
=
@@ -304,20 +332,25 @@
SLR Compliance Status
-
Actual: {{ slrActualPercent }}% · Required: {{ slrRate }}.00% · NDTL: ₹{{ ndtl | number:'1.2-2' }} Cr
+
+ Actual: {{ slrActualPercent }}% · Required: {{ slrRate }}.00% · NDTL: ₹{{ + ndtl | number: '1.2-2' + }} + Cr +
SLR Required ({{ slrRate }}%)
-
₹{{ slrRequired | number:'1.2-2' }} Cr
+
₹{{ slrRequired | number: '1.2-2' }} Cr
Liquid Assets Held
-
₹{{ totalLiquidAssets | number:'1.2-2' }} Cr
+
₹{{ totalLiquidAssets | number: '1.2-2' }} Cr
Surplus
-
₹{{ slrSurplus | number:'1.2-2' }} Cr
+
₹{{ slrSurplus | number: '1.2-2' }} Cr
@@ -325,29 +358,31 @@
-
NDTL Computation — Formula Breakdown · Data as of {{ reportingDateStr }}
+
+ NDTL Computation — Formula Breakdown · Data as of {{ reportingDateStr }} +
Demand Liabilities
-
₹{{ totalDemand | number:'1.2-2' }}
+
₹{{ totalDemand | number: '1.2-2' }}
16 line items
+
Time Liabilities
-
₹{{ totalTime | number:'1.2-2' }}
+
₹{{ totalTime | number: '1.2-2' }}
13 line items
−
Assets with Banking
-
₹{{ totalBankingAssets | number:'1.2-2' }}
+
₹{{ totalBankingAssets | number: '1.2-2' }}
3 line items
=
NDTL
-
₹{{ ndtl | number:'1.2-2' }}
+
₹{{ ndtl | number: '1.2-2' }}
CRR & SLR base
@@ -357,20 +392,22 @@
CRR & SLR Reserve Requirements — RBI Computation on NDTL
-
Based on NDTL of ₹{{ ndtl | number:'1.2-2' }} Cr · Data as of {{ reportingDateStr }} (45-day lag)
+
+ Based on NDTL of ₹{{ ndtl | number: '1.2-2' }} Cr · Data as of {{ reportingDateStr }} (45-day lag) +
CRR @ {{ crrRate }}.00%
-
₹{{ crrRequired | number:'1.2-2' }} Cr
+
₹{{ crrRequired | number: '1.2-2' }} Cr
SLR @ {{ slrRate }}.00%
-
₹{{ slrRequired | number:'1.2-2' }} Cr
+
₹{{ slrRequired | number: '1.2-2' }} Cr
Total Reserve
-
₹{{ totalReserve | number:'1.2-2' }} Cr
+
₹{{ totalReserve | number: '1.2-2' }} Cr
diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss index 7a18f668f..4db85bd22 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.scss @@ -186,11 +186,25 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; } } -.sc-demand::before { background: $demand; } -.sc-time::before { background: $time; } -.sc-asset::before { background: $asset; } -.sc-ndtl::before { background: linear-gradient(90deg, $asset, $time, $demand); } -.sc-crr::before { background: $amber; } +.sc-demand::before { + background: $demand; +} + +.sc-time::before { + background: $time; +} + +.sc-asset::before { + background: $asset; +} + +.sc-ndtl::before { + background: linear-gradient(90deg, $asset, $time, $demand); +} + +.sc-crr::before { + background: $amber; +} .sc-label { font-size: 10px; @@ -226,8 +240,13 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; margin-top: 4px; } -.up { color: $green; } -.dn { color: $red; } +.up { + color: $green; +} + +.dn { + color: $red; +} // ===== Tab Navigation ===== .tab-nav { @@ -278,9 +297,23 @@ $mono: 'IBM Plex Mono', 'Roboto Mono', monospace; letter-spacing: 0.5px; } -.pill-demand { background: $demand-bg; color: $demand; border: 1px solid $demand-b; } -.pill-time { background: $time-bg; color: $time; border: 1px solid $time-b; } -.pill-asset { background: $asset-bg; color: $asset; border: 1px solid $asset-b; } +.pill-demand { + background: $demand-bg; + color: $demand; + border: 1px solid $demand-b; +} + +.pill-time { + background: $time-bg; + color: $time; + border: 1px solid $time-b; +} + +.pill-asset { + background: $asset-bg; + color: $asset; + border: 1px solid $asset-b; +} .section-desc { font-size: 12px; @@ -320,15 +353,22 @@ thead th { text-transform: uppercase; white-space: nowrap; - &.right { text-align: right; } + &.right { + text-align: right; + } } tbody tr { border-bottom: 1px solid $border; transition: background 0.15s; - &:last-child { border-bottom: none; } - &:hover { background: #f8fafc; } + &:last-child { + border-bottom: none; + } + + &:hover { + background: #f8fafc; + } } td { @@ -336,7 +376,10 @@ td { color: $ink2; vertical-align: middle; - &.right { text-align: right; font-family: $mono; } + &.right { + text-align: right; + font-family: $mono; + } } .sl { @@ -346,7 +389,10 @@ td { width: 36px; } -.mono { font-family: $mono; font-size: 12px; } +.mono { + font-family: $mono; + font-size: 12px; +} .item-name { font-weight: 500; @@ -364,9 +410,17 @@ td { font-weight: 500; } -.amt-demand { color: $demand; } -.amt-time { color: $time; } -.amt-asset { color: $asset; } +.amt-demand { + color: $demand; +} + +.amt-time { + color: $time; +} + +.amt-asset { + color: $asset; +} .status-chip { display: inline-flex; @@ -379,9 +433,20 @@ td { border-radius: 4px; } -.chip-active { background: #dcfce7; color: #15803d; } -.chip-overdue { background: #fef2f2; color: $red; } -.chip-locked { background: #fefce8; color: #92400e; } +.chip-active { + background: #dcfce7; + color: #15803d; +} + +.chip-overdue { + background: #fef2f2; + color: $red; +} + +.chip-locked { + background: #fefce8; + color: #92400e; +} tfoot tr { background: $surface2; @@ -394,34 +459,77 @@ tfoot td { font-family: $mono; padding: 12px 14px; - &.right { text-align: right; font-size: 14px; } + &.right { + text-align: right; + font-size: 14px; + } } $slr: #0369a1; $slr-bg: #f0f9ff; $slr-b: #bae6fd; -.foot-demand { color: $demand; } -.foot-time { color: $time; } -.foot-asset { color: $asset; } -.foot-slr { color: $slr; } -.deduct-tag { font-size: 10px; color: $asset; font-family: $mono; margin-left: 8px; } +.foot-demand { + color: $demand; +} -.amt-slr { color: $slr; } -.chip-slr { background: $slr-bg; color: $slr; border: 1px solid $slr-b; } -.pill-slr { background: $slr-bg; color: $slr; border: 1px solid $slr-b; } +.foot-time { + color: $time; +} + +.foot-asset { + color: $asset; +} + +.foot-slr { + color: $slr; +} + +.deduct-tag { + font-size: 10px; + color: $asset; + font-family: $mono; + margin-left: 8px; +} + +.amt-slr { + color: $slr; +} + +.chip-slr { + background: $slr-bg; + color: $slr; + border: 1px solid $slr-b; +} + +.pill-slr { + background: $slr-bg; + color: $slr; + border: 1px solid $slr-b; +} .ft-slr { border-color: $slr-b; background: $slr-bg; - .formula-term-label, .formula-term-val { color: $slr; } + + .formula-term-label, + .formula-term-val { + color: $slr; + } } .ft-slr-result { border-color: #bbf7d0; background: linear-gradient(135deg, #f0fdf4, #ecfdf5); - .formula-term-label { color: #15803d; } - .formula-term-val { color: $green; font-size: 20px; } + + .formula-term-label { + color: #15803d; + } + + .formula-term-val { + color: $green; + font-size: 20px; + } } .slr-formula::before { @@ -441,7 +549,9 @@ $slr-b: #bae6fd; flex-wrap: wrap; } -.slr-status-left { flex: 1; } +.slr-status-left { + flex: 1; +} .slr-status-title { font-size: 13px; @@ -460,7 +570,9 @@ $slr-b: #bae6fd; gap: 24px; } -.slr-calc { text-align: right; } +.slr-calc { + text-align: right; +} .slr-calc-label { font-size: 10px; @@ -475,7 +587,9 @@ $slr-b: #bae6fd; color: #14532d; font-family: $mono; - &.surplus { color: $green; } + &.surplus { + color: $green; + } } // ===== Formula Block ===== @@ -555,19 +669,58 @@ $slr-b: #bae6fd; padding: 0 8px; flex-shrink: 0; - &.minus { color: $red; } - &.equals { color: $ink2; font-weight: 500; } + &.minus { + color: $red; + } + + &.equals { + color: $ink2; + font-weight: 500; + } } -.ft-demand { border-color: $demand-b; background: $demand-bg; .formula-term-label, .formula-term-val { color: $demand; } } -.ft-time { border-color: $time-b; background: $time-bg; .formula-term-label, .formula-term-val { color: $time; } } -.ft-asset { border-color: $asset-b; background: $asset-bg; .formula-term-label, .formula-term-val { color: $asset; } } +.ft-demand { + border-color: $demand-b; + background: $demand-bg; + + .formula-term-label, + .formula-term-val { + color: $demand; + } +} + +.ft-time { + border-color: $time-b; + background: $time-bg; + + .formula-term-label, + .formula-term-val { + color: $time; + } +} + +.ft-asset { + border-color: $asset-b; + background: $asset-bg; + + .formula-term-label, + .formula-term-val { + color: $asset; + } +} .ft-ndtl { border-color: #a78bfa; background: linear-gradient(135deg, #f5f3ff, #f0fdfa); - .formula-term-label { color: #6d28d9; } - .formula-term-val { color: $ink; font-size: 18px; } + + .formula-term-label { + color: #6d28d9; + } + + .formula-term-val { + color: $ink; + font-size: 18px; + } } // ===== CRR Bar ===== @@ -595,14 +748,18 @@ $slr-b: #bae6fd; color: #a16207; } -.crr-text { flex: 1; } +.crr-text { + flex: 1; +} .crr-calcs { display: flex; gap: 24px; } -.crr-calc { text-align: right; } +.crr-calc { + text-align: right; +} .crr-calc-label { font-size: 10px; diff --git a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts index ef230c679..7f7746251 100644 --- a/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts +++ b/src/app/home/dashboard/crr-slr-dialog/crr-slr-dialog.component.ts @@ -40,18 +40,18 @@ export class CrrSlrDialogComponent { reportingDateStr: string; /** NDTL & Reserve computations */ - readonly totalDemand = 12843.50; - readonly totalTime = 64618.00; - readonly totalBankingAssets = 8240.20; - readonly ndtl = 69221.30; // Demand + Time - Banking Assets + readonly totalDemand = 12843.5; + readonly totalTime = 64618.0; + readonly totalBankingAssets = 8240.2; + readonly ndtl = 69221.3; // Demand + Time - Banking Assets readonly crrRate = 3; // Current RBI CRR rate (%) readonly slrRate = 18; // Current RBI SLR rate (%) - readonly crrRequired = 69221.30 * 0.03; // ₹2,076.64 Cr - readonly slrRequired = 69221.30 * 0.18; // ₹12,459.83 Cr - readonly totalLiquidAssets = 13238.80; - readonly slrActualPercent = +(13238.80 / 69221.30 * 100).toFixed(2); // 19.12% - readonly slrSurplus = +(13238.80 - 69221.30 * 0.18).toFixed(2); // 778.97 - readonly totalReserve = +(69221.30 * 0.03 + 69221.30 * 0.18).toFixed(2); // 14,536.47 + readonly crrRequired = 69221.3 * 0.03; // ₹2,076.64 Cr + readonly slrRequired = 69221.3 * 0.18; // ₹12,459.83 Cr + readonly totalLiquidAssets = 13238.8; + readonly slrActualPercent = +((13238.8 / 69221.3) * 100).toFixed(2); // 19.12% + readonly slrSurplus = +(13238.8 - 69221.3 * 0.18).toFixed(2); // 778.97 + readonly totalReserve = +(69221.3 * 0.03 + 69221.3 * 0.18).toFixed(2); // 14,536.47 constructor() { // CRR/SLR uses data from 45 days ago @@ -59,58 +59,446 @@ export class CrrSlrDialogComponent { this.reportingDate = new Date(this.today); this.reportingDate.setDate(this.reportingDate.getDate() - lagDays); this.reportingDateStr = this.reportingDate.toLocaleDateString('en-IN', { - day: '2-digit', month: 'short', year: 'numeric' + day: '2-digit', + month: 'short', + year: 'numeric' }); } demandLiabilities = [ - { sl: '01', name: 'Current Account Deposits', sub: 'Corporate & individual CA balances', code: '2001-CA', opening: '3,842.50', current: '4,120.75', change: '+278.25', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Savings Bank Deposits (Withdrawable)', sub: 'Demand portion — 10% of SB balance', code: '2002-SB', opening: '1,240.00', current: '1,318.40', change: '+78.40', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Demand Drafts Payable', sub: 'Issued DDs outstanding for payment', code: '2003-DD', opening: '284.20', current: '311.60', change: '+27.40', changeDir: 'up', status: 'Active' }, - { sl: '04', name: "Banker's Cheques / Pay Orders", sub: 'Issued pay orders pending encashment', code: '2004-PO', opening: '198.75', current: '176.30', change: '-22.45', changeDir: 'dn', status: 'Active' }, - { sl: '05', name: 'Credit Balance in Cash Credit Accounts', sub: 'CC accounts in credit position', code: '2005-CC', opening: '542.10', current: '618.90', change: '+76.80', changeDir: 'up', status: 'Active' }, - { sl: '06', name: 'Overdue Fixed Deposits', sub: 'FDs past maturity — treated as demand', code: '2006-OFD', opening: '89.40', current: '112.85', change: '+23.45', changeDir: 'dn', status: 'Overdue' }, - { sl: '07', name: 'Interest Payable on Demand Deposits', sub: 'Accrued interest on CA/SB', code: '2007-INT', opening: '34.20', current: '38.55', change: '+4.35', changeDir: 'up', status: 'Active' }, - { sl: '08', name: 'Margins held against Letters of Credit', sub: 'Withdrawable LC margin deposits', code: '2008-LCM', opening: '412.60', current: '388.20', change: '-24.40', changeDir: 'dn', status: 'Active' }, - { sl: '09', name: 'Margins held against Bank Guarantees', sub: 'Withdrawable BG margin deposits', code: '2009-BGM', opening: '318.40', current: '342.10', change: '+23.70', changeDir: 'up', status: 'Active' }, - { sl: '10', name: 'Security Deposits (Withdrawable)', sub: 'EMD / SD refundable on demand', code: '2010-SD', opening: '124.80', current: '118.60', change: '-6.20', changeDir: 'dn', status: 'Active' }, - { sl: '11', name: 'Unclaimed Deposits (Demand Portion)', sub: 'Balances unclaimed >10 years', code: '2011-UCD', opening: '56.30', current: '59.40', change: '+3.10', changeDir: 'dn', status: 'Unclaimed' }, - { sl: '12', name: 'Inter-Bank Demand Deposits', sub: 'Demand deposits from other banks', code: '2012-IBD', opening: '2,840.00', current: '3,120.50', change: '+280.50', changeDir: 'up', status: 'Active' }, - { sl: '13', name: 'Clearing Adjustments / Balances', sub: 'Net clearing settlement balances', code: '2013-CLR', opening: '186.40', current: '210.80', change: '+24.40', changeDir: 'up', status: 'Active' }, - { sl: '14', name: 'Outstanding Payment Instruments', sub: 'Cheques issued but not yet presented', code: '2014-OPI', opening: '142.60', current: '158.90', change: '+16.30', changeDir: 'up', status: 'Active' }, - { sl: '15', name: 'Demand Liabilities towards Other Banks', sub: 'Nostro / Vostro demand obligations', code: '2015-DLB', opening: '984.20', current: '1,048.60', change: '+64.40', changeDir: 'up', status: 'Active' }, - { sl: '16', name: 'Deposits Repayable on Demand', sub: 'Miscellaneous on-demand deposits', code: '2016-DRD', opening: '618.30', current: '698.05', change: '+79.75', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Current Account Deposits', + sub: 'Corporate & individual CA balances', + code: '2001-CA', + opening: '3,842.50', + current: '4,120.75', + change: '+278.25', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Savings Bank Deposits (Withdrawable)', + sub: 'Demand portion — 10% of SB balance', + code: '2002-SB', + opening: '1,240.00', + current: '1,318.40', + change: '+78.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Demand Drafts Payable', + sub: 'Issued DDs outstanding for payment', + code: '2003-DD', + opening: '284.20', + current: '311.60', + change: '+27.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '04', + name: "Banker's Cheques / Pay Orders", + sub: 'Issued pay orders pending encashment', + code: '2004-PO', + opening: '198.75', + current: '176.30', + change: '-22.45', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '05', + name: 'Credit Balance in Cash Credit Accounts', + sub: 'CC accounts in credit position', + code: '2005-CC', + opening: '542.10', + current: '618.90', + change: '+76.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '06', + name: 'Overdue Fixed Deposits', + sub: 'FDs past maturity — treated as demand', + code: '2006-OFD', + opening: '89.40', + current: '112.85', + change: '+23.45', + changeDir: 'dn', + status: 'Overdue' + }, + { + sl: '07', + name: 'Interest Payable on Demand Deposits', + sub: 'Accrued interest on CA/SB', + code: '2007-INT', + opening: '34.20', + current: '38.55', + change: '+4.35', + changeDir: 'up', + status: 'Active' + }, + { + sl: '08', + name: 'Margins held against Letters of Credit', + sub: 'Withdrawable LC margin deposits', + code: '2008-LCM', + opening: '412.60', + current: '388.20', + change: '-24.40', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '09', + name: 'Margins held against Bank Guarantees', + sub: 'Withdrawable BG margin deposits', + code: '2009-BGM', + opening: '318.40', + current: '342.10', + change: '+23.70', + changeDir: 'up', + status: 'Active' + }, + { + sl: '10', + name: 'Security Deposits (Withdrawable)', + sub: 'EMD / SD refundable on demand', + code: '2010-SD', + opening: '124.80', + current: '118.60', + change: '-6.20', + changeDir: 'dn', + status: 'Active' + }, + { + sl: '11', + name: 'Unclaimed Deposits (Demand Portion)', + sub: 'Balances unclaimed >10 years', + code: '2011-UCD', + opening: '56.30', + current: '59.40', + change: '+3.10', + changeDir: 'dn', + status: 'Unclaimed' + }, + { + sl: '12', + name: 'Inter-Bank Demand Deposits', + sub: 'Demand deposits from other banks', + code: '2012-IBD', + opening: '2,840.00', + current: '3,120.50', + change: '+280.50', + changeDir: 'up', + status: 'Active' + }, + { + sl: '13', + name: 'Clearing Adjustments / Balances', + sub: 'Net clearing settlement balances', + code: '2013-CLR', + opening: '186.40', + current: '210.80', + change: '+24.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '14', + name: 'Outstanding Payment Instruments', + sub: 'Cheques issued but not yet presented', + code: '2014-OPI', + opening: '142.60', + current: '158.90', + change: '+16.30', + changeDir: 'up', + status: 'Active' + }, + { + sl: '15', + name: 'Demand Liabilities towards Other Banks', + sub: 'Nostro / Vostro demand obligations', + code: '2015-DLB', + opening: '984.20', + current: '1,048.60', + change: '+64.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '16', + name: 'Deposits Repayable on Demand', + sub: 'Miscellaneous on-demand deposits', + code: '2016-DRD', + opening: '618.30', + current: '698.05', + change: '+79.75', + changeDir: 'up', + status: 'Active' + } ]; timeLiabilities = [ - { sl: '01', name: 'Fixed Deposits (FD)', sub: '7 days to 10 years · 6.5–7.2%', code: '3001-FD', opening: '28,420.50', current: '29,184.30', change: '+763.80', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Recurring Deposits (RD)', sub: 'Monthly instalment · 6.0–6.8%', code: '3002-RD', opening: '4,218.60', current: '4,412.80', change: '+194.20', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Time Liability Portion of Savings', sub: '90% of SB balance treated as time', code: '3003-SBT', opening: '10,840.20', current: '11,204.60', change: '+364.40', changeDir: 'up', status: 'Active' }, - { sl: '04', name: 'Reinvestment Deposits', sub: 'Cumulative interest plans · 7.0%', code: '3004-RID', opening: '1,842.40', current: '1,918.70', change: '+76.30', changeDir: 'up', status: 'Active' }, - { sl: '05', name: 'Staff Security Deposits (Fixed Period)', sub: 'Employee security deposits — locked', code: '3005-SSD', opening: '84.60', current: '86.40', change: '+1.80', changeDir: 'up', status: 'Locked' }, - { sl: '06', name: 'Margin Money for Guarantees / LC', sub: 'LC/BG margins — fixed lock-in period', code: '3006-MGM', opening: '620.80', current: '644.20', change: '+23.40', changeDir: 'up', status: 'Locked' }, - { sl: '07', name: 'Certificates of Deposit (CD)', sub: 'Issued to corporates / FIs · 7.35%', code: '3007-CD', opening: '3,840.00', current: '3,920.50', change: '+80.50', changeDir: 'up', status: 'Active' }, - { sl: '08', name: 'Interest Accrued on Time Deposits', sub: 'Accrued but unpaid interest on FD/RD', code: '3008-IAT', opening: '842.60', current: '918.40', change: '+75.80', changeDir: 'up', status: 'Active' }, - { sl: '09', name: 'Long-term Deposits', sub: 'Tenor >5 years · 7.5%', code: '3009-LTD', opening: '5,620.40', current: '5,818.60', change: '+198.20', changeDir: 'up', status: 'Active' }, - { sl: '10', name: 'Tax Saving Deposits', sub: 'Sec 80C FDs — 5-year lock-in · 6.8%', code: '3010-TSD', opening: '1,240.80', current: '1,318.40', change: '+77.60', changeDir: 'up', status: 'Locked' }, - { sl: '11', name: 'Foreign Currency Deposits', sub: 'FCNR(B), RFC — USD/EUR/GBP', code: '3011-FCD', opening: '2,184.60', current: '2,348.20', change: '+163.60', changeDir: 'up', status: 'Active' }, - { sl: '12', name: 'Time Liabilities to Other Banks', sub: 'Inter-bank term deposits — call money', code: '3012-TLB', opening: '1,840.20', current: '1,924.40', change: '+84.20', changeDir: 'up', status: 'Active' }, - { sl: '13', name: 'Term Borrowings', sub: 'Bonds, debentures, refinance · 8.1%', code: '3013-TBR', opening: '1,018.40', current: '1,118.50', change: '+100.10', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Fixed Deposits (FD)', + sub: '7 days to 10 years · 6.5–7.2%', + code: '3001-FD', + opening: '28,420.50', + current: '29,184.30', + change: '+763.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Recurring Deposits (RD)', + sub: 'Monthly instalment · 6.0–6.8%', + code: '3002-RD', + opening: '4,218.60', + current: '4,412.80', + change: '+194.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Time Liability Portion of Savings', + sub: '90% of SB balance treated as time', + code: '3003-SBT', + opening: '10,840.20', + current: '11,204.60', + change: '+364.40', + changeDir: 'up', + status: 'Active' + }, + { + sl: '04', + name: 'Reinvestment Deposits', + sub: 'Cumulative interest plans · 7.0%', + code: '3004-RID', + opening: '1,842.40', + current: '1,918.70', + change: '+76.30', + changeDir: 'up', + status: 'Active' + }, + { + sl: '05', + name: 'Staff Security Deposits (Fixed Period)', + sub: 'Employee security deposits — locked', + code: '3005-SSD', + opening: '84.60', + current: '86.40', + change: '+1.80', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '06', + name: 'Margin Money for Guarantees / LC', + sub: 'LC/BG margins — fixed lock-in period', + code: '3006-MGM', + opening: '620.80', + current: '644.20', + change: '+23.40', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '07', + name: 'Certificates of Deposit (CD)', + sub: 'Issued to corporates / FIs · 7.35%', + code: '3007-CD', + opening: '3,840.00', + current: '3,920.50', + change: '+80.50', + changeDir: 'up', + status: 'Active' + }, + { + sl: '08', + name: 'Interest Accrued on Time Deposits', + sub: 'Accrued but unpaid interest on FD/RD', + code: '3008-IAT', + opening: '842.60', + current: '918.40', + change: '+75.80', + changeDir: 'up', + status: 'Active' + }, + { + sl: '09', + name: 'Long-term Deposits', + sub: 'Tenor >5 years · 7.5%', + code: '3009-LTD', + opening: '5,620.40', + current: '5,818.60', + change: '+198.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '10', + name: 'Tax Saving Deposits', + sub: 'Sec 80C FDs — 5-year lock-in · 6.8%', + code: '3010-TSD', + opening: '1,240.80', + current: '1,318.40', + change: '+77.60', + changeDir: 'up', + status: 'Locked' + }, + { + sl: '11', + name: 'Foreign Currency Deposits', + sub: 'FCNR(B), RFC — USD/EUR/GBP', + code: '3011-FCD', + opening: '2,184.60', + current: '2,348.20', + change: '+163.60', + changeDir: 'up', + status: 'Active' + }, + { + sl: '12', + name: 'Time Liabilities to Other Banks', + sub: 'Inter-bank term deposits — call money', + code: '3012-TLB', + opening: '1,840.20', + current: '1,924.40', + change: '+84.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '13', + name: 'Term Borrowings', + sub: 'Bonds, debentures, refinance · 8.1%', + code: '3013-TBR', + opening: '1,018.40', + current: '1,118.50', + change: '+100.10', + changeDir: 'up', + status: 'Active' + } ]; bankingAssets = [ - { sl: '01', name: 'Balances with Other Banks', sub: 'Nostro / Vostro balances — SBI, PNB, BOB', code: '4001-BWB', opening: '3,840.20', current: '4,112.40', change: '+272.20', changeDir: 'up', status: 'Active' }, - { sl: '02', name: 'Money at Call with Banks', sub: 'Overnight & short-notice call money', code: '4002-MCB', opening: '2,180.50', current: '2,418.60', change: '+238.10', changeDir: 'up', status: 'Active' }, - { sl: '03', name: 'Loans to Banks (Inter-Bank Lending)', sub: 'Term loans & credit lines to SCBs', code: '4003-LTB', opening: '1,580.00', current: '1,709.20', change: '+129.20', changeDir: 'up', status: 'Active' } + { + sl: '01', + name: 'Balances with Other Banks', + sub: 'Nostro / Vostro balances — SBI, PNB, BOB', + code: '4001-BWB', + opening: '3,840.20', + current: '4,112.40', + change: '+272.20', + changeDir: 'up', + status: 'Active' + }, + { + sl: '02', + name: 'Money at Call with Banks', + sub: 'Overnight & short-notice call money', + code: '4002-MCB', + opening: '2,180.50', + current: '2,418.60', + change: '+238.10', + changeDir: 'up', + status: 'Active' + }, + { + sl: '03', + name: 'Loans to Banks (Inter-Bank Lending)', + sub: 'Term loans & credit lines to SCBs', + code: '4003-LTB', + opening: '1,580.00', + current: '1,709.20', + change: '+129.20', + changeDir: 'up', + status: 'Active' + } ]; liquidAssets = [ - { sl: '01', name: 'Central Government Securities', sub: 'G-Secs held in HTM, AFS & HFT portfolios', code: '5001-CGS', opening: '6,840.50', current: '7,218.40', change: '+377.90', changeDir: 'up', status: 'Active', category: 'G-Sec' }, - { sl: '02', name: 'State Development Loans (SDLs)', sub: 'State govt bonds — various maturities', code: '5002-SDL', opening: '2,420.30', current: '2,584.60', change: '+164.30', changeDir: 'up', status: 'Active', category: 'SDL' }, - { sl: '03', name: 'Treasury Bills (91/182/364 day)', sub: 'Short-term govt securities — discounted', code: '5003-TB', opening: '1,840.20', current: '1,962.80', change: '+122.60', changeDir: 'up', status: 'Active', category: 'T-Bill' }, - { sl: '04', name: 'Gold (valued at current market)', sub: 'Gold reserves held per RBI valuation norms', code: '5004-GLD', opening: '680.40', current: '724.60', change: '+44.20', changeDir: 'up', status: 'Active', category: 'Gold' }, - { sl: '05', name: 'Special Deposit Scheme with RBI', sub: 'Deposits placed under SDS — SLR eligible', code: '5005-SDS', opening: '420.00', current: '420.00', change: '0.00', changeDir: 'up', status: 'Active', category: 'RBI Dep' }, - { sl: '06', name: 'NABARD / SIDBI / NHB Bonds', sub: 'PSE bonds approved as SLR-eligible', code: '5006-PSE', opening: '310.80', current: '328.40', change: '+17.60', changeDir: 'up', status: 'Active', category: 'PSE Bond' } + { + sl: '01', + name: 'Central Government Securities', + sub: 'G-Secs held in HTM, AFS & HFT portfolios', + code: '5001-CGS', + opening: '6,840.50', + current: '7,218.40', + change: '+377.90', + changeDir: 'up', + status: 'Active', + category: 'G-Sec' + }, + { + sl: '02', + name: 'State Development Loans (SDLs)', + sub: 'State govt bonds — various maturities', + code: '5002-SDL', + opening: '2,420.30', + current: '2,584.60', + change: '+164.30', + changeDir: 'up', + status: 'Active', + category: 'SDL' + }, + { + sl: '03', + name: 'Treasury Bills (91/182/364 day)', + sub: 'Short-term govt securities — discounted', + code: '5003-TB', + opening: '1,840.20', + current: '1,962.80', + change: '+122.60', + changeDir: 'up', + status: 'Active', + category: 'T-Bill' + }, + { + sl: '04', + name: 'Gold (valued at current market)', + sub: 'Gold reserves held per RBI valuation norms', + code: '5004-GLD', + opening: '680.40', + current: '724.60', + change: '+44.20', + changeDir: 'up', + status: 'Active', + category: 'Gold' + }, + { + sl: '05', + name: 'Special Deposit Scheme with RBI', + sub: 'Deposits placed under SDS — SLR eligible', + code: '5005-SDS', + opening: '420.00', + current: '420.00', + change: '0.00', + changeDir: 'up', + status: 'Active', + category: 'RBI Dep' + }, + { + sl: '06', + name: 'NABARD / SIDBI / NHB Bonds', + sub: 'PSE bonds approved as SLR-eligible', + code: '5006-PSE', + opening: '310.80', + current: '328.40', + change: '+17.60', + changeDir: 'up', + status: 'Active', + category: 'PSE Bond' + } ]; close(): void { diff --git a/src/app/home/dashboard/dashboard.component.html b/src/app/home/dashboard/dashboard.component.html index 8f531977a..dd51410fe 100644 --- a/src/app/home/dashboard/dashboard.component.html +++ b/src/app/home/dashboard/dashboard.component.html @@ -177,10 +177,18 @@
-
+
Priority Sector Lending (PSL)
+
65%
@@ -206,6 +214,14 @@
Credit Information Companies
+
Data Quality Report (DQR) — as per RBI Guidelines
@@ -330,6 +346,5 @@ RBI circular pending review — compliance action due by month end.
-
diff --git a/src/app/home/dashboard/dashboard.component.scss b/src/app/home/dashboard/dashboard.component.scss index ca3372b38..c0d81a80e 100644 --- a/src/app/home/dashboard/dashboard.component.scss +++ b/src/app/home/dashboard/dashboard.component.scss @@ -576,10 +576,64 @@ $purple: #7c3aed; text-align: right; } +/* ─── Priority Sector Lending — Open button ─── */ +.psl-card { + .card-top { + align-items: center; + } +} + +.psl-open-btn { + margin-left: auto; + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid #e2e8f0; + background: #fff; + color: #16a34a; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 13px; + transition: all 0.15s; + + &:hover { + background: #dcfce7; + border-color: #16a34a; + transform: translateY(-1px); + box-shadow: 0 2px 6px rgb(22 163 74 / 20%); + } +} + /* ─── Credit Information Companies — DQR ─── */ .cic-card { .card-top { margin-bottom: 0; + align-items: center; + } +} + +.cic-open-btn { + margin-left: auto; + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid #e2e8f0; + background: #fff; + color: #6d28d9; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 13px; + transition: all 0.15s; + + &:hover { + background: #ede9fe; + border-color: #6d28d9; + transform: translateY(-1px); + box-shadow: 0 2px 6px rgb(109 40 217 / 20%); } } @@ -833,5 +887,4 @@ $purple: #7c3aed; gap: 4px; text-align: center; } - } diff --git a/src/app/home/dashboard/dashboard.component.ts b/src/app/home/dashboard/dashboard.component.ts index e7ff1dbc1..877f5b3f7 100644 --- a/src/app/home/dashboard/dashboard.component.ts +++ b/src/app/home/dashboard/dashboard.component.ts @@ -170,4 +170,32 @@ export class DashboardComponent implements OnInit { autoFocus: false }); } + + async openCicDqrDialog(): Promise { + const { CicDqrDialogComponent } = await import('./cic-dqr-dialog/cic-dqr-dialog.component'); + this.dialog.open(CicDqrDialogComponent, { + width: '88vw', + maxWidth: '1200px', + maxHeight: '92vh', + panelClass: 'crr-slr-dialog-panel', + autoFocus: false, + disableClose: false, + hasBackdrop: true, + backdropClass: 'cic-dlg-backdrop' + }); + } + + async openPslReportDialog(): Promise { + const { PslReportDialogComponent } = await import('./psl-report-dialog/psl-report-dialog.component'); + this.dialog.open(PslReportDialogComponent, { + width: '92vw', + maxWidth: '1280px', + maxHeight: '92vh', + panelClass: 'crr-slr-dialog-panel', + autoFocus: false, + disableClose: false, + hasBackdrop: true, + backdropClass: 'psl-dlg-backdrop' + }); + } } diff --git a/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.html b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.html new file mode 100644 index 000000000..b6d4d1082 --- /dev/null +++ b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.html @@ -0,0 +1,501 @@ + + +
+ +
+
+
+
+

Priority Sector Lending — Returns & Register

+

+ {{ bank.name }} · {{ bank.formCode }} · Quarter + {{ bank.quarter }} · As on {{ bank.asOnDate }} +

+
+
+
+ + Overall PSL + {{ totalPslAchievedPct.toFixed(2) }}% + / {{ TOTAL_PSL_TARGET_PCT }}% + + +
+
+ + +
+ + + + +
+ +
+ + + +
+
+
+
+
{{ totalPslAchievedPct.toFixed(2) }}%
+
PSL of ANBC
+
+
+
+
+
ANBC
+
₹ {{ bank.anbc / 10000000 | number: '1.2-2' }} Cr
+
Adjusted Net Bank Credit
+
+
+
PSL Outstanding
+
₹ {{ totalPslOutstanding / 10000000 | number: '1.2-2' }} Cr
+
{{ totalPslAccounts | number }} accounts
+
+
+
Gap / Surplus
+
+ {{ totalPslGapPct >= 0 ? '+' : '' }}{{ totalPslGapPct.toFixed(2) }} pp +
+
Vs. {{ TOTAL_PSL_TARGET_PCT }}% target
+
+
+ + +
+
+

RBI Sub-Target Compliance

+ RBI MD: {{ bank.rbiCircular }} +
+
+
+
+   Agriculture (18% of ANBC) + {{ agriAchievedPct.toFixed(2) }}% +
+
+
+
+
+
+
+   Small & Marginal Farmers (10%) + {{ smfAchievedPct.toFixed(2) }}% +
+
+
+
+
+
+
+   Micro Enterprises (7.5%) + {{ microAchievedPct.toFixed(2) }}% +
+
+
+
+
+
+
+   Weaker Sections (12%) + {{ weakerAchievedPct.toFixed(2) }}% +
+
+
+
+
+
+
+ + +
+
+

Bank & Reporting Snapshot

+
+
+
+
Reporting Bank
+
{{ bank.name }}
+
+
+
Branch
+
{{ bank.branch }}
+
+
+
Principal Officer
+
{{ bank.principalOfficer }}
+
+
+
Form Code
+
{{ bank.formCode }}
+
+
+
Credit Equivalent (OBE)
+
₹ {{ bank.creditEquivalentOBE / 10000000 | number: '1.2-2' }} Cr
+
+
+
RBI Master Direction
+
{{ bank.rbiCircular }}
+
+
+
+
+ + + +
+
+

Category-wise Outstanding & Achievement

+ +
+
+
{{ item.code }}{{ item.category }} + {{ item.category }} + {{ item.opening }} {{ item.current }}{{ item.change }}{{ item.status }} + {{ item.change }} + + {{ item.status }} +
+ + + + + + + + + + + + + + + + + + + + + + +
CodeCategoryOutstanding#A/cAchieved %Sub-Target %Status
{{ c.code }} +
{{ c.name }}
+
{{ c.description }}
+
₹ {{ c.outstanding / 10000000 | number: '1.2-2' }} Cr{{ c.accounts | number }} + {{ c.achievedPct.toFixed(2) }}% + + {{ c.targetPct }}% + — + + + ACHIEVED + BELOW + NA + +
+
+
+ + + + +
+
+

PSL Account Register — {{ filteredAccounts.length }} of {{ accounts.length }}

+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
A/c NoCustomerCategorySub-CategoryBranchSanctionedOutstandingAsset ClassWeakerSMFSanctioned
{{ a.acNo }}{{ a.customerName }} + {{ a.category }} + {{ a.subCategory }}{{ a.branch }}₹ {{ a.sanctioned | number }}₹ {{ a.outstanding | number }} + {{ a.npaFlag }} + + + — + + + — + {{ a.sanctionedDate }}
No accounts match the current filters.
+
+
+
+ + + +
+
+

{{ bank.formCode }} — Priority Sector Achievement Return

+ +
+ +
+
+
+ {{ bank.name }} — {{ bank.branch }} +
+
+   As On {{ bank.asOnDate }} +   {{ bank.quarter }} +
+
+ +

A. ANBC Computation (₹ in Lakhs)

+ + + + + + + + + + + + + + + +
Adjusted Net Bank Credit (ANBC) + {{ bank.anbc / 100000 | number: '1.2-2' }} +
Credit Equivalent of Off-Balance Sheet Exposures + {{ bank.creditEquivalentOBE / 100000 | number: '1.2-2' }} +
Total Reference for PSL % + {{ (bank.anbc + bank.creditEquivalentOBE) / 100000 | number: '1.2-2' }} +
+ +

B. Category-wise Achievement

+ + + + + + + + + + + + + + + + + + + + + + + +
#CategoryOutstanding (₹ Lakh)#A/cAchieved %Sub-Target %Status
{{ i + 1 }}{{ c.code }} — {{ c.name }}{{ c.outstanding / 100000 | number: '1.2-2' }}{{ c.accounts | number }} + {{ c.achievedPct.toFixed(2) }}% + + {{ c.targetPct }}% + — + + + ACHIEVED + BELOW + NA + +
+ +

C. Overall PSL Position

+ + + + + + + + + + + + + + + + + + + + + + + +
Total PSL Outstanding (₹ Lakh) + {{ totalPslOutstanding / 100000 | number: '1.2-2' }} +
Achieved % of ANBC + {{ totalPslAchievedPct.toFixed(2) }}% +
RBI Target{{ TOTAL_PSL_TARGET_PCT }}%
Gap / Surplus (Percentage Points) + {{ totalPslGapPct >= 0 ? '+' : '' }}{{ totalPslGapPct.toFixed(2) }} +
Compliance Status + {{ totalPslAchievedPct >= TOTAL_PSL_TARGET_PCT ? 'ACHIEVED' : 'BELOW TARGET' }} +
+ +
+
+
Certified by
+
{{ bank.principalOfficer }}
+
Principal Officer — {{ bank.name }}
+
+
+
Submitted under
+
{{ bank.rbiCircular }}
+
RBI Master Direction on Priority Sector Lending, 2020
+
+
+
+
+
+
+ diff --git a/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.scss b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.scss new file mode 100644 index 000000000..c440bc441 --- /dev/null +++ b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.scss @@ -0,0 +1,753 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --psl-good: #15803d; + --psl-good-l: #dcfce7; + --psl-mid: #d97706; + --psl-mid-l: #fef3c7; + --psl-low: #c0392b; + --psl-low-l: #fdecea; + --psl-green: #16a34a; + --psl-green-l: #dcfce7; + --psl-blue: #2563eb; + --psl-blue-l: #dbeafe; + --psl-g50: #f8fafc; + --psl-g100: #f1f5f9; + --psl-g200: #e2e8f0; + --psl-g300: #cbd5e1; + --psl-g500: #64748b; + --psl-g700: #334155; + --psl-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + color: var(--psl-g900); +} + +.psl-dlg { + background: #fff; + border-radius: 14px; + display: flex; + flex-direction: column; + overflow: hidden; + max-height: 90vh; +} + +/* HEADER */ +.psl-head { + padding: 18px 22px; + border-bottom: 1px solid var(--psl-g200); + display: flex; + align-items: center; + gap: 16px; + flex-shrink: 0; + background: linear-gradient(135deg, #f0fdf4 0%, #fff 100%); +} + +.psl-head-left { + display: flex; + align-items: center; + gap: 14px; + flex: 1; +} + +.psl-head-ic { + width: 44px; + height: 44px; + border-radius: 11px; + background: var(--psl-green-l); + color: var(--psl-green); + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.psl-title { + margin: 0; + font-size: 17px; + font-weight: 700; + color: var(--psl-g900); +} + +.psl-sub { + margin: 2px 0 0; + font-size: 12px; + color: var(--psl-g500); +} + +.psl-head-actions { + display: flex; + align-items: center; + gap: 12px; +} + +.psl-overall { + display: flex; + align-items: baseline; + gap: 6px; + padding: 6px 14px; + border-radius: 999px; + font-weight: 700; + + &.on-target { + background: var(--psl-good-l); + color: var(--psl-good); + } + + &.below { + background: var(--psl-low-l); + color: var(--psl-low); + } + + .psl-overall-l { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 600; + } + + .psl-overall-v { + font-size: 16px; + } + + .psl-overall-t { + font-size: 11px; + font-weight: 600; + opacity: 0.8; + } +} + +.psl-close { + width: 32px; + height: 32px; + border-radius: 8px; + border: 1px solid var(--psl-g200); + background: #fff; + font-size: 14px; + color: var(--psl-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--psl-g100); + color: var(--psl-g900); + } +} + +/* TABS */ +.psl-tabs { + display: flex; + background: var(--psl-g50); + border-bottom: 1px solid var(--psl-g200); + padding: 0 22px; + overflow-x: auto; + flex-shrink: 0; +} + +.psl-tab { + display: flex; + align-items: center; + padding: 12px 18px; + border: none; + background: transparent; + cursor: pointer; + font-family: inherit; + font-size: 13px; + font-weight: 600; + color: var(--psl-g500); + border-bottom: 2px solid transparent; + transition: all 0.15s; + white-space: nowrap; + + &:hover { + color: var(--psl-g700); + background: rgb(255 255 255 / 50%); + } + + &.active { + background: #fff; + color: var(--psl-green); + border-bottom-color: var(--psl-green); + } +} + +/* BODY */ +.psl-body { + padding: 18px 22px; + display: flex; + flex-direction: column; + gap: 18px; + overflow: auto; + flex: 1; +} + +/* KPI STATS */ +.psl-stats { + display: grid; + grid-template-columns: 220px 1fr 1fr 1fr; + gap: 14px; +} + +.psl-stat-card { + background: #fff; + border: 1px solid var(--psl-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 4px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.psl-stat-l { + font-size: 11px; + font-weight: 600; + color: var(--psl-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.psl-stat-v { + font-size: 20px; + font-weight: 700; + color: var(--psl-g900); + line-height: 1.2; + + &.pos { + color: var(--psl-good); + } + + &.neg { + color: var(--psl-low); + } +} + +.psl-stat-meta { + font-size: 11px; + color: var(--psl-g500); + margin-top: 4px; +} + +.psl-stat-gauge { + align-items: center; + justify-content: center; +} + +/* GAUGE */ +.gauge { + width: 140px; + height: 140px; + border-radius: 50%; + background: conic-gradient(currentcolor calc(var(--pct) * 1%), var(--psl-g100) 0); + display: flex; + align-items: center; + justify-content: center; + + &.on-target { + color: var(--psl-good); + } + + &.below { + color: var(--psl-low); + } +} + +.gauge-inner { + width: 108px; + height: 108px; + border-radius: 50%; + background: #fff; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: inset 0 0 0 1px var(--psl-g200); +} + +.gauge-pct { + font-size: 20px; + font-weight: 700; + color: var(--psl-g900); + line-height: 1; +} + +.gauge-label { + font-size: 10px; + color: var(--psl-g500); + margin-top: 4px; + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 600; +} + +/* SECTIONS */ +.psl-section { + background: #fff; + border: 1px solid var(--psl-g200); + border-radius: 12px; + padding: 16px 18px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.psl-sec-h { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + margin-bottom: 14px; +} + +.psl-sec-t { + margin: 0; + font-size: 14px; + font-weight: 700; + color: var(--psl-g700); +} + +.psl-circular { + font-family: 'DM Mono', monospace; + font-size: 11px; + color: var(--psl-g500); + padding: 4px 10px; + background: var(--psl-g50); + border-radius: 6px; + border: 1px solid var(--psl-g200); +} + +.psl-btn-xls { + font-size: 12px !important; + height: 34px !important; + color: #fff !important; + background: var(--psl-green) !important; +} + +/* SUB-TARGET BARS */ +.psl-bars { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px 28px; +} + +.psl-bar-row { + display: flex; + flex-direction: column; + gap: 6px; +} + +.psl-bar-h { + display: flex; + justify-content: space-between; + align-items: center; +} + +.psl-bar-l { + font-size: 12px; + font-weight: 600; + color: var(--psl-g700); + + fa-icon { + color: var(--psl-green); + } +} + +.psl-bar-p { + font-size: 13px; + font-weight: 700; + + &.good { + color: var(--psl-good); + } + + &.low { + color: var(--psl-low); + } +} + +.psl-bar-track { + height: 8px; + background: var(--psl-g100); + border-radius: 4px; + overflow: hidden; +} + +.psl-bar-fill { + height: 100%; + border-radius: 4px; + transition: width 0.4s ease-out; + + &.good { + background: var(--psl-good); + } + + &.low { + background: var(--psl-low); + } +} + +/* INFO GRID */ +.psl-info-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px 24px; +} + +.psl-info { + display: flex; + flex-direction: column; + gap: 2px; +} + +.psl-info-l { + font-size: 10px; + font-weight: 600; + color: var(--psl-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.psl-info-v { + font-size: 13px; + font-weight: 600; + color: var(--psl-g900); +} + +/* TABLE */ +.psl-tbl-wrap { + border: 1px solid var(--psl-g200); + border-radius: 8px; + overflow: auto; +} + +.psl-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 14px; + font-size: 11px; + font-weight: 600; + color: var(--psl-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--psl-g50); + border-bottom: 1px solid var(--psl-g200); + white-space: nowrap; + } + + td { + padding: 10px 14px; + border-bottom: 1px solid var(--psl-g100); + font-size: 13px; + color: var(--psl-g700); + vertical-align: middle; + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--psl-g50); + } +} + +.cat-name { + font-weight: 600; + color: var(--psl-g900); +} + +.cat-desc { + font-size: 11px; + color: var(--psl-g500); + margin-top: 2px; +} + +.cat-pill { + display: inline-block; + padding: 2px 8px; + border-radius: 6px; + background: var(--psl-blue-l); + color: var(--psl-blue); + font-family: 'DM Mono', monospace; + font-size: 11px; + font-weight: 600; +} + +.npa-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + + &.ok { + background: var(--psl-good-l); + color: var(--psl-good); + } + + &.warn { + background: var(--psl-mid-l); + color: var(--psl-mid); + } + + &.err { + background: var(--psl-low-l); + color: var(--psl-low); + } +} + +.status-pill { + display: inline-block; + padding: 3px 10px; + font-size: 11px; + font-weight: 600; + border-radius: 999px; + + &.on-target { + background: var(--psl-good-l); + color: var(--psl-good); + } + + &.below { + background: var(--psl-low-l); + color: var(--psl-low); + } + + &.na { + background: var(--psl-g100); + color: var(--psl-g500); + } +} + +.ico-yes { + color: var(--psl-good); +} + +.muted { + color: var(--psl-g300); +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 12px; +} + +.empty { + text-align: center; + color: var(--psl-g500); + padding: 24px !important; + font-style: italic; +} + +/* FILTERS */ +.psl-filters { + display: flex; + gap: 12px; + padding: 12px 14px; + background: var(--psl-g50); + border: 1px solid var(--psl-g200); + border-radius: 8px; + margin-bottom: 12px; + align-items: flex-end; + flex-wrap: wrap; +} + +.psl-f { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 140px; + + label { + font-size: 10px; + font-weight: 600; + color: var(--psl-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + select, + input { + height: 32px; + padding: 0 10px; + border: 1px solid var(--psl-g200); + border-radius: 6px; + background: #fff; + font-family: inherit; + font-size: 12px; + color: var(--psl-g900); + + &:focus { + outline: none; + border-color: var(--psl-green); + box-shadow: 0 0 0 3px rgb(22 163 74 / 12%); + } + } +} + +.psl-f-reset { + height: 32px; + width: 32px; + border-radius: 6px; + border: 1px solid var(--psl-g200); + background: #fff; + color: var(--psl-g500); + cursor: pointer; + transition: all 0.15s; + + &:hover { + background: var(--psl-low-l); + color: var(--psl-low); + border-color: var(--psl-low); + } +} + +/* FORM III */ +.form-iii { + background: var(--psl-g50); + border: 1px solid var(--psl-g200); + border-radius: 10px; + padding: 18px; +} + +.form-iii-h { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 12px; + margin-bottom: 14px; + border-bottom: 2px solid var(--psl-green); +} + +.form-iii-bank { + font-size: 14px; + color: var(--psl-g900); +} + +.form-iii-meta { + display: flex; + gap: 18px; + font-size: 12px; + color: var(--psl-g500); + + fa-icon { + color: var(--psl-green); + } +} + +.form-iii-t { + font-size: 13px; + font-weight: 700; + color: var(--psl-g700); + margin: 16px 0 8px; + padding-bottom: 4px; + border-bottom: 1px dashed var(--psl-g200); +} + +.form-tbl { + background: #fff; + border: 1px solid var(--psl-g200); + border-radius: 6px; + overflow: hidden; +} + +.form-total td { + background: var(--psl-green-l) !important; + font-weight: 700; + color: var(--psl-g900); +} + +.pos { + color: var(--psl-good); +} + +.neg { + color: var(--psl-low); +} + +.form-iii-foot { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 18px; + margin-top: 18px; + padding-top: 16px; + border-top: 1px dashed var(--psl-g300); +} + +.cert { + background: #fff; + border: 1px solid var(--psl-g200); + border-radius: 8px; + padding: 12px 14px; +} + +.cert-l { + font-size: 10px; + font-weight: 600; + color: var(--psl-g500); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cert-v { + font-size: 14px; + font-weight: 700; + color: var(--psl-g900); + margin: 4px 0; +} + +.cert-r { + font-size: 11px; + color: var(--psl-g500); +} + +/* RESPONSIVE */ +@media (width <= 1100px) { + .psl-stats { + grid-template-columns: 1fr 1fr; + } + + .psl-bars { + grid-template-columns: 1fr; + } + + .psl-info-grid { + grid-template-columns: 1fr 1fr; + } + + .form-iii-foot { + grid-template-columns: 1fr; + } +} + +@media (width <= 720px) { + .psl-stats, + .psl-info-grid { + grid-template-columns: 1fr; + } + + .psl-head { + flex-wrap: wrap; + } +} diff --git a/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.ts b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.ts new file mode 100644 index 000000000..1bf9ff773 --- /dev/null +++ b/src/app/home/dashboard/psl-report-dialog/psl-report-dialog.component.ts @@ -0,0 +1,661 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component, inject } from '@angular/core'; +import { CommonModule, DecimalPipe } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; + +interface PslCategory { + code: string; + name: string; + description: string; + /** RBI sub-target as % of ANBC (null = no specific sub-target, only counts toward 40% overall) */ + targetPct: number | null; + /** Outstanding amount in ₹ */ + outstanding: number; + /** Number of accounts */ + accounts: number; + /** Achievement % of ANBC */ + achievedPct: number; +} + +interface PslAccount { + acNo: string; + customerName: string; + category: string; + subCategory: string; + branch: string; + sanctioned: number; + outstanding: number; + npaFlag: 'STANDARD' | 'SMA-1' | 'SMA-2' | 'SUB-STD' | 'DOUBTFUL' | 'LOSS'; + weakerSection: boolean; + smallMarginalFarmer: boolean; + sanctionedDate: string; +} + +@Component({ + selector: 'mifosx-psl-report-dialog', + standalone: true, + templateUrl: './psl-report-dialog.component.html', + styleUrls: ['./psl-report-dialog.component.scss'], + imports: [ + CommonModule, + DecimalPipe, + FormsModule, + MatDialogModule, + MatButtonModule, + MatTooltipModule, + FaIconComponent + ] +}) +export class PslReportDialogComponent { + private dialogRef = inject(MatDialogRef); + + activeTab: 'dashboard' | 'categories' | 'register' | 'returns' = 'dashboard'; + + bank = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + quarter: 'Q3 FY 2025-26', + asOnDate: '29-01-2026', + rbiCircular: 'FIDD.CO.PSD.BC.16/04.09.001/2020-21', + principalOfficer: 'Karthik Kamath', + formCode: 'Form III', + /** Adjusted Net Bank Credit — basis for all PSL % calculations */ + anbc: 6000000000, // ₹600 Cr + psBalanceCash: 0, + creditEquivalentOBE: 250000000 // ₹25 Cr + }; + + /** PSL targets per RBI Master Direction (2020) */ + readonly TOTAL_PSL_TARGET_PCT = 40; + readonly AGRI_TARGET_PCT = 18; + readonly SMALL_MARGINAL_FARMER_TARGET_PCT = 10; + readonly MICRO_ENTERPRISES_TARGET_PCT = 7.5; + readonly WEAKER_SECTIONS_TARGET_PCT = 12; + + categories: PslCategory[] = [ + { + code: 'AGRI', + name: 'Agriculture', + description: 'Farm credit, agri-infrastructure, ancillary activities', + targetPct: 18, + outstanding: 1240000000, // ₹124 Cr + accounts: 4824, + achievedPct: 20.67 + }, + { + code: 'AGRI-SMF', + name: '— Small & Marginal Farmers', + description: 'Sub-target: 10% of ANBC for SMF (within Agri)', + targetPct: 10, + outstanding: 720000000, // ₹72 Cr + accounts: 3110, + achievedPct: 12.0 + }, + { + code: 'MSME-MICRO', + name: 'Micro Enterprises', + description: 'Investment ≤₹1 Cr, turnover ≤₹5 Cr', + targetPct: 7.5, + outstanding: 510000000, // ₹51 Cr + accounts: 1842, + achievedPct: 8.5 + }, + { + code: 'MSME-SMALL', + name: 'Small Enterprises', + description: 'Investment ≤₹10 Cr, turnover ≤₹50 Cr', + targetPct: null, + outstanding: 285000000, // ₹28.5 Cr + accounts: 412, + achievedPct: 4.75 + }, + { + code: 'MSME-MED', + name: 'Medium Enterprises', + description: 'Investment ≤₹50 Cr, turnover ≤₹250 Cr', + targetPct: null, + outstanding: 168000000, // ₹16.8 Cr + accounts: 87, + achievedPct: 2.8 + }, + { + code: 'EDU', + name: 'Education', + description: 'Loans ≤ ₹20 L (India) / ₹50 L (foreign)', + targetPct: null, + outstanding: 42000000, // ₹4.2 Cr + accounts: 224, + achievedPct: 0.7 + }, + { + code: 'HOUSE', + name: 'Housing', + description: 'Up to ₹35 L (metro) / ₹25 L (non-metro)', + targetPct: null, + outstanding: 92000000, // ₹9.2 Cr + accounts: 318, + achievedPct: 1.53 + }, + { + code: 'SOC-INFRA', + name: 'Social Infrastructure', + description: 'Schools, hospitals, drinking water in Tier-II–VI centres', + targetPct: null, + outstanding: 18000000, // ₹1.8 Cr + accounts: 38, + achievedPct: 0.3 + }, + { + code: 'REN-ENERGY', + name: 'Renewable Energy', + description: 'Up to ₹30 Cr per borrower (solar, wind, biomass)', + targetPct: null, + outstanding: 24000000, // ₹2.4 Cr + accounts: 12, + achievedPct: 0.4 + }, + { + code: 'EXPORT', + name: 'Export Credit', + description: 'Pre/post-shipment to MSME exporters', + targetPct: null, + outstanding: 38000000, // ₹3.8 Cr + accounts: 19, + achievedPct: 0.63 + }, + { + code: 'WEAKER', + name: 'Weaker Sections', + description: 'SC/ST, women, minorities, persons with disabilities', + targetPct: 12, + outstanding: 855000000, // ₹85.5 Cr (overlaps with other categories) + accounts: 4218, + achievedPct: 14.25 + } + ]; + + /** Sample dummy PSL accounts */ + accounts: PslAccount[] = [ + { + acNo: '110002101000211', + customerName: 'RAVI KUMAR SHARMA', + category: 'AGRI', + subCategory: 'Crop Loan', + branch: 'Mumbai Main', + sanctioned: 500000, + outstanding: 425000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: true, + sanctionedDate: '12-04-2024' + }, + { + acNo: '110002102000418', + customerName: 'PRIYA PATEL', + category: 'MSME-MICRO', + subCategory: 'Micro Manufacturing', + branch: 'Pune FC Road', + sanctioned: 800000, + outstanding: 654000, + npaFlag: 'STANDARD', + weakerSection: true, + smallMarginalFarmer: false, + sanctionedDate: '20-09-2024' + }, + { + acNo: '110002103000522', + customerName: 'ARJUN DESHMUKH', + category: 'AGRI', + subCategory: 'Allied — Dairy', + branch: 'Nagpur Dharampeth', + sanctioned: 1500000, + outstanding: 1148000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '01-06-2024' + }, + { + acNo: '110002104000711', + customerName: 'VIKRAM REDDY', + category: 'EDU', + subCategory: 'Higher Education', + branch: 'Bengaluru Jayanagar', + sanctioned: 1500000, + outstanding: 1280000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '15-07-2023' + }, + { + acNo: '110002105000814', + customerName: 'LAKSHMI IYER', + category: 'AGRI-SMF', + subCategory: 'Marginal Farmer', + branch: 'Bengaluru Indiranagar', + sanctioned: 250000, + outstanding: 210000, + npaFlag: 'STANDARD', + weakerSection: true, + smallMarginalFarmer: true, + sanctionedDate: '08-10-2024' + }, + { + acNo: '110002106001012', + customerName: 'NIRAV SHAH', + category: 'HOUSE', + subCategory: 'Housing — Non-Metro', + branch: 'Ahmedabad Satellite', + sanctioned: 2200000, + outstanding: 1840000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '14-02-2023' + }, + { + acNo: '110002107001145', + customerName: 'KAVITA VERMA', + category: 'MSME-MICRO', + subCategory: 'Service Sector', + branch: 'Lucknow Gomti Nagar', + sanctioned: 600000, + outstanding: 482000, + npaFlag: 'SMA-1', + weakerSection: true, + smallMarginalFarmer: false, + sanctionedDate: '11-11-2024' + }, + { + acNo: '110002108001458', + customerName: 'SAMEER KAPOOR', + category: 'MSME-SMALL', + subCategory: 'Small Manufacturing', + branch: 'Delhi Greater Kailash', + sanctioned: 4500000, + outstanding: 3980000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '25-08-2024' + }, + { + acNo: '110002109001721', + customerName: 'KARTHIK SUBRAMANIAN', + category: 'REN-ENERGY', + subCategory: 'Solar Rooftop', + branch: 'Chennai Valasaravakkam', + sanctioned: 1800000, + outstanding: 1620000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '03-05-2025' + }, + { + acNo: '110002110002012', + customerName: 'ABHISHEK BANERJEE', + category: 'EXPORT', + subCategory: 'Pre-shipment', + branch: 'Kolkata Salt Lake', + sanctioned: 3500000, + outstanding: 2950000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '17-12-2024' + }, + { + acNo: '110002111002314', + customerName: 'GAURAV THAKUR', + category: 'AGRI', + subCategory: 'Kisan Credit Card', + branch: 'Shimla Sector 4', + sanctioned: 350000, + outstanding: 295000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: true, + sanctionedDate: '22-04-2024' + }, + { + acNo: '110002112002615', + customerName: 'MANMOHAN SINGH GILL', + category: 'WEAKER', + subCategory: 'SC/ST — Mudra', + branch: 'Amritsar Model Town', + sanctioned: 200000, + outstanding: 165000, + npaFlag: 'STANDARD', + weakerSection: true, + smallMarginalFarmer: false, + sanctionedDate: '05-01-2025' + }, + { + acNo: '110002113002918', + customerName: 'SUNITA DEVI', + category: 'WEAKER', + subCategory: 'Women Self-Help Group', + branch: 'Bhopal Arera Colony', + sanctioned: 150000, + outstanding: 125000, + npaFlag: 'STANDARD', + weakerSection: true, + smallMarginalFarmer: false, + sanctionedDate: '18-06-2024' + }, + { + acNo: '110002114003221', + customerName: 'DEEPAK TIWARI', + category: 'SOC-INFRA', + subCategory: 'Drinking Water Project', + branch: 'Indore Vijay Nagar', + sanctioned: 5000000, + outstanding: 4280000, + npaFlag: 'STANDARD', + weakerSection: false, + smallMarginalFarmer: false, + sanctionedDate: '12-03-2025' + }, + { + acNo: '110002115003524', + customerName: 'HARPREET KAUR', + category: 'MSME-MICRO', + subCategory: 'Women — Self Employed', + branch: 'Ludhiana Sarabha Nagar', + sanctioned: 750000, + outstanding: 632000, + npaFlag: 'SMA-2', + weakerSection: true, + smallMarginalFarmer: false, + sanctionedDate: '08-09-2024' + } + ]; + + /** Filters for register tab */ + filterCategory = ''; + filterBranch = ''; + filterWeaker: '' | 'Y' | 'N' = ''; + filterNpa: '' | 'STANDARD' | 'SMA' | 'NPA' = ''; + + // -------------------- Computed -------------------- + get totalPslOutstanding(): number { + // Sum excluding sub-categories that double-count (AGRI-SMF, WEAKER overlap) + const main = [ + 'AGRI', + 'MSME-MICRO', + 'MSME-SMALL', + 'MSME-MED', + 'EDU', + 'HOUSE', + 'SOC-INFRA', + 'REN-ENERGY', + 'EXPORT' + ]; + return this.categories.filter((c) => main.includes(c.code)).reduce((s, c) => s + c.outstanding, 0); + } + get totalPslAccounts(): number { + const main = [ + 'AGRI', + 'MSME-MICRO', + 'MSME-SMALL', + 'MSME-MED', + 'EDU', + 'HOUSE', + 'SOC-INFRA', + 'REN-ENERGY', + 'EXPORT' + ]; + return this.categories.filter((c) => main.includes(c.code)).reduce((s, c) => s + c.accounts, 0); + } + get totalPslAchievedPct(): number { + return +((this.totalPslOutstanding / this.bank.anbc) * 100).toFixed(2); + } + get totalPslGapPct(): number { + return +(this.totalPslAchievedPct - this.TOTAL_PSL_TARGET_PCT).toFixed(2); + } + cat(code: string): PslCategory | undefined { + return this.categories.find((c) => c.code === code); + } + get agriAchievedPct(): number { + return this.cat('AGRI')?.achievedPct ?? 0; + } + get smfAchievedPct(): number { + return this.cat('AGRI-SMF')?.achievedPct ?? 0; + } + get microAchievedPct(): number { + return this.cat('MSME-MICRO')?.achievedPct ?? 0; + } + get weakerAchievedPct(): number { + return this.cat('WEAKER')?.achievedPct ?? 0; + } + achieveStatus(achieved: number, target: number | null): 'on-target' | 'below' | 'na' { + if (target == null) return 'na'; + return achieved >= target ? 'on-target' : 'below'; + } + + // -------------------- Filtered Register -------------------- + get filteredAccounts(): PslAccount[] { + const branch = this.filterBranch.trim().toLowerCase(); + return this.accounts.filter((a) => { + if (this.filterCategory && a.category !== this.filterCategory) return false; + if (branch && !a.branch.toLowerCase().includes(branch)) return false; + if (this.filterWeaker === 'Y' && !a.weakerSection) return false; + if (this.filterWeaker === 'N' && a.weakerSection) return false; + if (this.filterNpa === 'STANDARD' && a.npaFlag !== 'STANDARD') return false; + if (this.filterNpa === 'SMA' && !a.npaFlag.startsWith('SMA')) return false; + if (this.filterNpa === 'NPA' && (a.npaFlag === 'STANDARD' || a.npaFlag.startsWith('SMA'))) return false; + return true; + }); + } + resetFilters(): void { + this.filterCategory = ''; + this.filterBranch = ''; + this.filterWeaker = ''; + this.filterNpa = ''; + } + + // -------------------- Excel Exports -------------------- + + /** Form III — Quarterly PSL Achievement Statement */ + exportFormIII(): void { + const rows: string[][] = []; + rows.push([ + this.bank.name, + this.bank.branch + ]); + rows.push([ + `PRIORITY REGISTER STATEMENT (${this.bank.formCode})`, + `As On ${this.bank.asOnDate}` + ]); + rows.push([ + `RBI Circular: ${this.bank.rbiCircular}`, + `Quarter: ${this.bank.quarter}` + ]); + rows.push([]); + rows.push(['ANBC Computation (₹ in Lakhs)']); + rows.push([ + 'Adjusted Net Bank Credit (ANBC)', + (this.bank.anbc / 100000).toFixed(2) + ]); + rows.push([ + 'Credit Equivalent of OBE', + (this.bank.creditEquivalentOBE / 100000).toFixed(2) + ]); + rows.push([ + 'Total Reference for PSL %', + ((this.bank.anbc + this.bank.creditEquivalentOBE) / 100000).toFixed(2) + ]); + rows.push([]); + rows.push(['Category-wise Achievement (₹ in Lakhs)']); + rows.push([ + 'Sr.No', + 'Category Code', + 'Category Description', + 'Outstanding (₹ Lakh)', + '#A/c', + 'Achieved % of ANBC', + 'Sub-Target %', + 'Status', + 'Gap (PP)' + ]); + let sl = 1; + for (const c of this.categories) { + const status = this.achieveStatus(c.achievedPct, c.targetPct); + const gap = c.targetPct == null ? '' : (c.achievedPct - c.targetPct).toFixed(2); + rows.push([ + (sl++).toString(), + c.code, + c.name + ' — ' + c.description, + (c.outstanding / 100000).toFixed(2), + c.accounts.toString(), + c.achievedPct.toFixed(2), + c.targetPct == null ? '—' : c.targetPct.toString(), + status === 'on-target' ? 'ACHIEVED' : status === 'below' ? 'BELOW TARGET' : 'NA', + gap + ]); + } + rows.push([]); + rows.push(['Overall PSL Achievement']); + rows.push([ + 'Total PSL Outstanding (₹ Lakh)', + (this.totalPslOutstanding / 100000).toFixed(2) + ]); + rows.push([ + 'Achieved % of ANBC', + this.totalPslAchievedPct.toFixed(2) + ]); + rows.push([ + 'Target', + this.TOTAL_PSL_TARGET_PCT.toString() + '%' + ]); + rows.push([ + 'Gap / Surplus (PP)', + this.totalPslGapPct.toFixed(2) + ]); + rows.push([ + 'Status', + this.totalPslAchievedPct >= this.TOTAL_PSL_TARGET_PCT ? 'ACHIEVED' : 'BELOW TARGET' + ]); + rows.push([]); + rows.push([ + 'Certified by', + this.bank.principalOfficer + ' — Principal Officer' + ]); + this.saveTsv(rows, `PSL-FormIII-${this.dateTag()}.xls`); + } + + /** Account Register CSV — every PSL-tagged loan in the filtered set */ + exportRegisterCsv(): void { + const headers = [ + 'A/c No', + 'Customer Name', + 'Category', + 'Sub-Category', + 'Branch', + 'Sanctioned (₹)', + 'Outstanding (₹)', + 'NPA Flag', + 'Weaker Section', + 'Small/Marginal Farmer', + 'Sanctioned Date' + ]; + const rows = [headers]; + for (const a of this.filteredAccounts) { + rows.push([ + a.acNo, + a.customerName, + a.category, + a.subCategory, + a.branch, + a.sanctioned.toString(), + a.outstanding.toString(), + a.npaFlag, + a.weakerSection ? 'Y' : 'N', + a.smallMarginalFarmer ? 'Y' : 'N', + a.sanctionedDate + ]); + } + this.saveCsv(rows, `PSL-Register-${this.dateTag()}.csv`); + } + + /** Category Outstanding TSV */ + exportCategoriesXls(): void { + const rows: string[][] = []; + rows.push([ + `PSL Category-wise Outstanding — ${this.bank.name}`, + this.bank.asOnDate + ]); + rows.push([]); + rows.push([ + 'Code', + 'Category', + 'Description', + 'Outstanding (₹)', + '#A/c', + 'Achieved %', + 'Target %', + 'Status' + ]); + for (const c of this.categories) { + rows.push([ + c.code, + c.name, + c.description, + c.outstanding.toString(), + c.accounts.toString(), + c.achievedPct.toFixed(2), + c.targetPct == null ? '' : c.targetPct.toString(), + this.achieveStatus(c.achievedPct, c.targetPct) + ]); + } + this.saveTsv(rows, `PSL-Categories-${this.dateTag()}.xls`); + } + + setTab(tab: 'dashboard' | 'categories' | 'register' | 'returns'): void { + this.activeTab = tab; + } + close(): void { + this.dialogRef.close(); + } + + // ---- helpers ---- + private dateTag(): string { + const d = new Date(); + return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`; + } + private saveTsv(rows: string[][], filename: string): void { + const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n'); + this.saveBlob(tsv, filename, 'application/vnd.ms-excel'); + } + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n'; + this.saveBlob(csv, filename, 'text/csv'); + } + private saveBlob(content: string, filename: string, mime: string): void { + const blob = new Blob([content], { type: `${mime};charset=utf-8` }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } +} diff --git a/src/app/sft/sft-data.ts b/src/app/sft/sft-data.ts new file mode 100644 index 000000000..6deda4868 --- /dev/null +++ b/src/app/sft/sft-data.ts @@ -0,0 +1,654 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/** + * SFT — Statement of Financial Transactions, filed under + * Section 285BA of the Income Tax Act 1961 + Rule 114E, + * with the Income Tax Reporting Portal (report.insight.gov.in) + * by 31 May for the previous FY. + * + * Schema reproduces the actual SFT CSV templates; bank/customer names are dummy. + * + * SFT-001 Cash purchase of bank drafts / pay orders / banker's cheques (≥ ₹10 L cash) + * SFT-002 Time-deposit (FD/RD) opening (≥ ₹10 L cash) + * SFT-003 Cash deposits / withdrawals — current account (≥ ₹50 L per FY) + * SFT-004 Cash deposits — savings account (≥ ₹10 L per FY) + * SFT-015 Dividend payments + * SFT-016 Interest paid on deposits + */ + +export type SftCode = 'SFT-001' | 'SFT-002' | 'SFT-003' | 'SFT-004' | 'SFT-015' | 'SFT-016'; + +/** Person-wise schema (SFT-001 / 002 / 004) */ +export interface SftPersonRecord { + reportSerialNumber: number; + originalReportSerialNumber: number; + personName: string; + personType: string; // I=Individual, F=Firm, P=Partnership, etc. + customerIdentity: string; + gender: 'M' | 'F' | 'O' | ''; + fatherName: string; + pan: string; + aadhaar: string; + form60Ack: string; + identificationType: string; + identificationNumber: string; + dobOrIncorporation: string; + nationality: string; + addressType: string; + fixAddress: string; + city: string; + postCode: string; + state: string; + countryCode: string; + businessOrOccupation: string; + primaryStdCode: string; + primaryPhoneNo: string; + primaryMobileNo: string; + email: string; + remarks: string; + productType: string; // SFT-001/002/004 — DD / Pay Order / Time Deposit / SB + aggGrossReceived: number; + aggGrossReceivedCash: number; + aggGrossPaid: number; + productIdentifier: string; + lastDateOfTransaction: string; + amountReceivedFromPerson: number; + cashAmountReceivedFromPerson: number; + amountPaidToPerson: number; + relatedAccountNumber: string; + relatedInstituteName: string; + relatedInstituteRefNumber: string; + /** Which SFT code this row belongs to */ + sftCode: SftCode; +} + +/** Account-wise schema with embedded Person Card (SFT-003) */ +export interface SftAccountRecord { + reportSerialNumber: number; + originalReportSerialNumber: number; + accountType: string; + accountNumber: string; + accountHolderName: string; + accountStatus: 'A' | 'C' | 'D'; + branchReferenceNumber: string; + branchName: string; + branchAddress: string; + branchCity: string; + branchPostalCode: string; + branchState: string; + branchCountry: string; + branchStdCode: string; + branchPhone: string; + branchMobile: string; + branchFaxStd: string; + branchFaxPhone: string; + branchEmail: string; + aggGrossCashCredited: number; + aggGrossCashDebited: number; + amountCreditedBeforeDemonetization: number; + amountCreditedAfterDemonetization: number; + // Person Card (PC) + pcAccountRelationship: string; + pcPersonName: string; + pcPersonType: string; + pcCustomerIdentity: string; + pcGender: 'M' | 'F' | 'O' | ''; + pcFatherName: string; + pcPan: string; + pcAadhaar: string; + pcForm60Ack: string; + pcIdentificationType: string; + pcIdentificationNumber: string; + pcDob: string; + pcNationality: string; + pcBusiness: string; + pcAddressType: string; + pcAddress: string; + pcCity: string; + pcPin: string; + pcState: string; + pcCountry: string; + pcPrimaryStd: string; + pcPrimaryPhone: string; + pcPrimaryMobile: string; + pcEmail: string; +} + +/** Compact pipe-delimited schema (SFT-015 dividend / SFT-016 interest) */ +export interface SftInterestDividendRecord { + rsn: number; + fy: string; + name: string; + pan: string; + aadhaar: string; + mobile: string; + email: string; + /** Address only present in SFT-015 */ + address?: string; + pincode?: string; + /** Account fields only present in SFT-016 */ + accountNumber?: string; + typeOfAccount?: string; + /** SFT-015 = dividend amount; SFT-016 = interest paid */ + amount: number; + sftCode: 'SFT-015' | 'SFT-016'; +} + +export const SFT_BANK = { + name: 'Bharat Urban Bank Ltd', + branch: 'Head Office', + filerPan: 'AABCB1100M', + filerTan: 'MUMR99001E', + itdReportingPortalId: 'BHARATURBAN-285BA', + fy: '2025-26', + filingDueDate: '31-05-2026', + principalOfficer: 'Karthik Kamath', + poDesignation: 'Chief Compliance Officer', + branchRef: 'YESB0BHRT', + branchAddress: '12 MG Road, Worli', + branchCity: 'MUMBAI', + branchState: 'MH', + branchPostalCode: '400018', + branchCountry: 'IN', + branchStdCode: '022', + branchPhone: '24938000', + branchMobile: '9892000100', + branchEmail: 'compliance@bharaturbanbank.example' +}; + +/** ── SFT-001 / 002 / 004 — Person-wise records ── */ +export const SFT_PERSON_RECORDS: SftPersonRecord[] = [ + // SFT-001 — Cash purchase of DD ≥ ₹10 L + { + reportSerialNumber: 1, + originalReportSerialNumber: 0, + personName: 'RAVI KUMAR SHARMA', + personType: 'I', + customerIdentity: '10078', + gender: 'M', + fatherName: 'RAJESH KUMAR SHARMA', + pan: 'AAQPS8560J', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AAQPS8560J', + dobOrIncorporation: '10-08-1985', + nationality: 'IN', + addressType: '2', + fixAddress: 'FLAT 402 SUNRISE TOWERS MAROL ANDHERI EAST', + city: 'MUMBAI', + postCode: '400069', + state: 'MH', + countryCode: 'IN', + businessOrOccupation: 'SERVICE', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9892011001', + email: 'ravi.sharma@example.com', + remarks: '', + productType: 'BANK_DRAFT', + aggGrossReceived: 1500000, + aggGrossReceivedCash: 1500000, + aggGrossPaid: 0, + productIdentifier: 'DD24304', + lastDateOfTransaction: '20-01-2026', + amountReceivedFromPerson: 1500000, + cashAmountReceivedFromPerson: 1500000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001101000838', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-001' + }, + { + reportSerialNumber: 2, + originalReportSerialNumber: 0, + personName: 'PRIYA PATEL', + personType: 'I', + customerIdentity: '10079', + gender: 'F', + fatherName: 'HARISH PATEL', + pan: 'AKLPS9933P', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AKLPS9933P', + dobOrIncorporation: '22-03-1990', + nationality: 'IN', + addressType: '2', + fixAddress: 'B-12 SHIVAJI NAGAR FC ROAD', + city: 'PUNE', + postCode: '411005', + state: 'MH', + countryCode: 'IN', + businessOrOccupation: 'BUSINESS', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9822055002', + email: 'priya.patel@example.com', + remarks: '', + productType: 'BANKERS_CHEQUE', + aggGrossReceived: 1100000, + aggGrossReceivedCash: 1100000, + aggGrossPaid: 0, + productIdentifier: 'BC100039', + lastDateOfTransaction: '02-01-2026', + amountReceivedFromPerson: 1100000, + cashAmountReceivedFromPerson: 1100000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001352000357', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-001' + }, + // SFT-002 — Time deposit ≥ ₹10 L cash + { + reportSerialNumber: 3, + originalReportSerialNumber: 0, + personName: 'VIKRAM REDDY', + personType: 'I', + customerIdentity: '20101', + gender: 'M', + fatherName: 'SURESH REDDY', + pan: 'AAEPR9352L', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AAEPR9352L', + dobOrIncorporation: '07-07-1982', + nationality: 'IN', + addressType: '2', + fixAddress: 'NO 23 4TH CROSS JAYANAGAR 5TH BLOCK', + city: 'BENGALURU', + postCode: '560041', + state: 'KA', + countryCode: 'IN', + businessOrOccupation: 'PROFESSION', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9845022004', + email: '', + remarks: '', + productType: 'TIME_DEPOSIT', + aggGrossReceived: 2500000, + aggGrossReceivedCash: 2500000, + aggGrossPaid: 0, + productIdentifier: 'FDR-2026-001', + lastDateOfTransaction: '15-09-2025', + amountReceivedFromPerson: 2500000, + cashAmountReceivedFromPerson: 2500000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001352000420', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-002' + }, + { + reportSerialNumber: 4, + originalReportSerialNumber: 0, + personName: 'NIRAV SHAH', + personType: 'I', + customerIdentity: '40301', + gender: 'M', + fatherName: 'JAYESH SHAH', + pan: 'AHKPS5544Z', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AHKPS5544Z', + dobOrIncorporation: '18-05-1992', + nationality: 'IN', + addressType: '2', + fixAddress: 'A-401 ATLANTIS APARTMENTS SATELLITE ROAD', + city: 'AHMEDABAD', + postCode: '380015', + state: 'GJ', + countryCode: 'IN', + businessOrOccupation: 'SERVICE', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9824012010', + email: 'nirav.shah@example.com', + remarks: '', + productType: 'TIME_DEPOSIT', + aggGrossReceived: 1800000, + aggGrossReceivedCash: 1800000, + aggGrossPaid: 0, + productIdentifier: 'FDR-2026-002', + lastDateOfTransaction: '12-12-2025', + amountReceivedFromPerson: 1800000, + cashAmountReceivedFromPerson: 1800000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001352000511', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-002' + }, + // SFT-004 — Cash deposits in SB ≥ ₹10 L per FY + { + reportSerialNumber: 5, + originalReportSerialNumber: 0, + personName: 'KAVITA VERMA', + personType: 'I', + customerIdentity: '50402', + gender: 'F', + fatherName: 'DEEPAK VERMA', + pan: 'AKHPV2244R', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AKHPV2244R', + dobOrIncorporation: '01-04-1988', + nationality: 'IN', + addressType: '2', + fixAddress: '45 CIVIL LINES MALL ROAD', + city: 'KANPUR', + postCode: '208001', + state: 'UP', + countryCode: 'IN', + businessOrOccupation: 'SERVICE', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9415055014', + email: '', + remarks: '', + productType: 'SAVINGS_DEPOSIT', + aggGrossReceived: 1250000, + aggGrossReceivedCash: 1250000, + aggGrossPaid: 0, + productIdentifier: 'SB-50402', + lastDateOfTransaction: '28-01-2026', + amountReceivedFromPerson: 1250000, + cashAmountReceivedFromPerson: 1250000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001101001145', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-004' + }, + { + reportSerialNumber: 6, + originalReportSerialNumber: 0, + personName: 'SAMEER KAPOOR', + personType: 'I', + customerIdentity: '70601', + gender: 'M', + fatherName: 'RAJINDER KAPOOR', + pan: 'AHKPK7788Q', + aadhaar: '', + form60Ack: '', + identificationType: 'C', + identificationNumber: 'AHKPK7788Q', + dobOrIncorporation: '03-02-1987', + nationality: 'IN', + addressType: '2', + fixAddress: 'B-148 GREATER KAILASH GK PART 1', + city: 'NEW DELHI', + postCode: '110048', + state: 'DL', + countryCode: 'IN', + businessOrOccupation: 'SERVICE', + primaryStdCode: '', + primaryPhoneNo: '', + primaryMobileNo: '9811022019', + email: 'sameer.kapoor@example.com', + remarks: '', + productType: 'SAVINGS_DEPOSIT', + aggGrossReceived: 2150000, + aggGrossReceivedCash: 2150000, + aggGrossPaid: 0, + productIdentifier: 'SB-70601', + lastDateOfTransaction: '15-01-2026', + amountReceivedFromPerson: 2150000, + cashAmountReceivedFromPerson: 2150000, + amountPaidToPerson: 0, + relatedAccountNumber: '110001101001458', + relatedInstituteName: 'BHARAT URBAN BANK', + relatedInstituteRefNumber: 'YESB0BHRT', + sftCode: 'SFT-004' + } +]; + +/** ── SFT-003 — Account-wise (current account) cash deposits ≥ ₹50 L per FY ── */ +export const SFT_ACCOUNT_RECORDS: SftAccountRecord[] = [ + { + reportSerialNumber: 1, + originalReportSerialNumber: 0, + accountType: 'BC', + accountNumber: '110001101000685', + accountHolderName: 'NEXTGEN TECH PVT LTD', + accountStatus: 'A', + branchReferenceNumber: 'YESB0BHRT', + branchName: 'HEAD OFFICE', + branchAddress: '12 MG ROAD WORLI MUMBAI 400018', + branchCity: 'MUMBAI', + branchPostalCode: '400018', + branchState: 'MH', + branchCountry: 'IN', + branchStdCode: '022', + branchPhone: '24938000', + branchMobile: '9892000100', + branchFaxStd: '022', + branchFaxPhone: '24938011', + branchEmail: 'compliance@bharaturbanbank.example', + aggGrossCashCredited: 6500000, + aggGrossCashDebited: 4200000, + amountCreditedBeforeDemonetization: 0, + amountCreditedAfterDemonetization: 0, + pcAccountRelationship: 'F', + pcPersonName: 'NEXTGEN TECH PVT LTD', + pcPersonType: 'PF', + pcCustomerIdentity: 'E10102', + pcGender: '', + pcFatherName: '', + pcPan: 'AABCN5566K', + pcAadhaar: '', + pcForm60Ack: '', + pcIdentificationType: 'C', + pcIdentificationNumber: 'AABCN5566K', + pcDob: '18-09-2015', + pcNationality: 'IN', + pcBusiness: 'IT_SERVICES', + pcAddressType: '2', + pcAddress: 'TOWER B SUNRISE BUSINESS PARK ANDHERI EAST', + pcCity: 'MUMBAI', + pcPin: '400093', + pcState: 'MH', + pcCountry: 'IN', + pcPrimaryStd: '', + pcPrimaryPhone: '', + pcPrimaryMobile: '9824012011', + pcEmail: '' + }, + { + reportSerialNumber: 2, + originalReportSerialNumber: 0, + accountType: 'BC', + accountNumber: '110001101000922', + accountHolderName: 'BHARAT TRADING COMPANY (PROP. RAVI KUMAR SHARMA)', + accountStatus: 'A', + branchReferenceNumber: 'YESB0BHRT', + branchName: 'HEAD OFFICE', + branchAddress: '12 MG ROAD WORLI MUMBAI 400018', + branchCity: 'MUMBAI', + branchPostalCode: '400018', + branchState: 'MH', + branchCountry: 'IN', + branchStdCode: '022', + branchPhone: '24938000', + branchMobile: '9892000100', + branchFaxStd: '022', + branchFaxPhone: '24938011', + branchEmail: 'compliance@bharaturbanbank.example', + aggGrossCashCredited: 5800000, + aggGrossCashDebited: 3450000, + amountCreditedBeforeDemonetization: 0, + amountCreditedAfterDemonetization: 0, + pcAccountRelationship: 'F', + pcPersonName: 'BHARAT TRADING COMPANY', + pcPersonType: 'PF', + pcCustomerIdentity: 'E10101', + pcGender: '', + pcFatherName: '', + pcPan: 'ABCFB1234N', + pcAadhaar: '', + pcForm60Ack: '', + pcIdentificationType: 'C', + pcIdentificationNumber: 'ABCFB1234N', + pcDob: '24-03-2008', + pcNationality: 'IN', + pcBusiness: 'TRADING', + pcAddressType: '2', + pcAddress: 'GALA 22 LOWER PAREL', + pcCity: 'MUMBAI', + pcPin: '400013', + pcState: 'MH', + pcCountry: 'IN', + pcPrimaryStd: '', + pcPrimaryPhone: '', + pcPrimaryMobile: '9892011010', + pcEmail: '' + } +]; + +/** ── SFT-015 / SFT-016 — Pipe-delimited interest & dividend ── */ +export const SFT_INT_DIV_RECORDS: SftInterestDividendRecord[] = [ + // SFT-016 — Interest paid to depositors + { + rsn: 1, + fy: '2025-26', + name: 'RAVI KUMAR SHARMA', + pan: 'AAQPS8560J', + aadhaar: 'XXXXXXXX1234', + mobile: '9892011001', + email: 'ravi.sharma@example.com', + accountNumber: '110001302000312', + typeOfAccount: 'FD', + amount: 56250, + sftCode: 'SFT-016' + }, + { + rsn: 2, + fy: '2025-26', + name: 'PRIYA PATEL', + pan: 'AKLPS9933P', + aadhaar: 'XXXXXXXX2210', + mobile: '9822055002', + email: 'priya.patel@example.com', + accountNumber: '110001302000418', + typeOfAccount: 'FD', + amount: 41280, + sftCode: 'SFT-016' + }, + { + rsn: 3, + fy: '2025-26', + name: 'VIKRAM REDDY', + pan: 'AAEPR9352L', + aadhaar: 'XXXXXXXX6622', + mobile: '9845022004', + email: '', + accountNumber: '110001302000711', + typeOfAccount: 'FD', + amount: 73500, + sftCode: 'SFT-016' + }, + { + rsn: 4, + fy: '2025-26', + name: 'LAKSHMI IYER', + pan: '', + aadhaar: 'XXXXXXXX5511', + mobile: '9845066005', + email: '', + accountNumber: '110001301000814', + typeOfAccount: 'SB', + amount: 18420, + sftCode: 'SFT-016' + }, + { + rsn: 5, + fy: '2025-26', + name: 'NIRAV SHAH', + pan: 'AHKPS5544Z', + aadhaar: 'XXXXXXXX9933', + mobile: '9824012010', + email: 'nirav.shah@example.com', + accountNumber: '110001352000511', + typeOfAccount: 'FD', + amount: 124200, + sftCode: 'SFT-016' + }, + { + rsn: 6, + fy: '2025-26', + name: 'KAVITA VERMA', + pan: 'AKHPV2244R', + aadhaar: 'XXXXXXXX1144', + mobile: '9415055014', + email: '', + accountNumber: '110001101001145', + typeOfAccount: 'SB', + amount: 9850, + sftCode: 'SFT-016' + }, + { + rsn: 7, + fy: '2025-26', + name: 'SAMEER KAPOOR', + pan: 'AHKPK7788Q', + aadhaar: 'XXXXXXXX8877', + mobile: '9811022019', + email: 'sameer.kapoor@example.com', + accountNumber: '110001304001458', + typeOfAccount: 'TL', + amount: 285600, + sftCode: 'SFT-016' + }, + // SFT-015 — Dividend payments (bank as paying entity, e.g. on shares held) + { + rsn: 8, + fy: '2025-26', + name: 'ABHISHEK BANERJEE', + pan: 'AHKPK7788Q', + aadhaar: 'XXXXXXXX3344', + mobile: '9831022021', + email: 'abhishek.b@example.com', + address: '14/B SALT LAKE SECTOR V BIDHANNAGAR', + pincode: '700091', + amount: 24500, + sftCode: 'SFT-015' + }, + { + rsn: 9, + fy: '2025-26', + name: 'GAURAV THAKUR', + pan: 'AILPT4455G', + aadhaar: 'XXXXXXXX5566', + mobile: '9418022023', + email: '', + address: 'HOUSE 7 NEW SHIMLA SECTOR 4', + pincode: '171002', + amount: 18750, + sftCode: 'SFT-015' + }, + { + rsn: 10, + fy: '2025-26', + name: 'MANMOHAN SINGH GILL', + pan: 'ANBPG2233J', + aadhaar: '', + mobile: '9876022027', + email: 'manmohan.gill@example.com', + address: 'H 32 MODEL TOWN MAJITHA ROAD AMRITSAR', + pincode: '143001', + amount: 35200, + sftCode: 'SFT-015' + } +]; diff --git a/src/app/sft/sft-routing.module.ts b/src/app/sft/sft-routing.module.ts new file mode 100644 index 000000000..694c887ae --- /dev/null +++ b/src/app/sft/sft-routing.module.ts @@ -0,0 +1,29 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { Route } from '../core/route/route.service'; +import { SftComponent } from './sft.component'; + +const routes: Routes = [ + Route.withShell([ + { + path: 'sft', + component: SftComponent, + data: { title: 'Income Tax — SFT', breadcrumb: 'SFT Reports' } + } + ]) +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class SftRoutingModule {} diff --git a/src/app/sft/sft.component.html b/src/app/sft/sft.component.html new file mode 100644 index 000000000..5b60017e3 --- /dev/null +++ b/src/app/sft/sft.component.html @@ -0,0 +1,397 @@ + + +
+ +
+
+ +
+

Income Tax — Statement of Financial Transactions (SFT)

+
+ {{ bank.name }} · {{ bank.branch }} · FY {{ bank.fy }} · IT Act §285BA / + Rule 114E · Due {{ bank.filingDueDate }} +
+
+
+
+
+ Filer PAN{{ bank.filerPan }} +
+
+ TAN{{ bank.filerTan }} +
+
+ Principal Officer{{ bank.principalOfficer }} +
+
+
+ + +
+ + + + +
+ + + @if (activeTab === 'dashboard') { +
+
+
+
+
+
{{ totalRecords | number }}
+
Total Records
+
all SFT codes combined
+
+
+
+
+
+
₹{{ totalReportableAmount | number: '1.0-0' }}
+
Reportable Amount
+
aggregate per FY {{ bank.fy }}
+
+
+
+
+
+
{{ sft001Count + sft002Count + sft004Count | number }}
+
Person-wise Records
+
+ {{ sft001Count }} ·001 · {{ sft002Count }} ·002 · {{ sft004Count }} ·004 +
+
+
+
+
+
+
{{ sft003Count | number }}
+
SFT-003 Accounts
+
cash CA ≥ ₹50 L per FY
+
+
+
+
+
+
{{ sft015Count + sft016Count | number }}
+
Int / Dividend
+
{{ sft015Count }} ·015 · {{ sft016Count }} ·016
+
+
+
+ + +
+
+
+ SFT-001Cash Drafts / Pay Orders +
+

+ Cash purchase of bank drafts, pay orders or banker's cheques aggregating ≥ ₹10 L per + person/FY +

+
{{ sft001Count }} record{{ sft001Count === 1 ? '' : 's' }}
+
+
+
+ SFT-002Time Deposits (FD/RD) +
+

Time-deposit opening with cash ≥ ₹10 L per person/FY (excluding renewals)

+
{{ sft002Count }} record{{ sft002Count === 1 ? '' : 's' }}
+
+
+
SFT-003Current Account Cash
+

+ Cash deposits / withdrawals — current account aggregating ≥ + ₹50 L per FY +

+
{{ sft003Count }} account{{ sft003Count === 1 ? '' : 's' }}
+
+
+
SFT-004Savings Bank Cash
+

Cash deposits — savings account aggregating ≥ ₹10 L per FY

+
{{ sft004Count }} record{{ sft004Count === 1 ? '' : 's' }}
+
+
+
SFT-015Dividend Payments
+

Dividend payments to shareholders — any amount, aggregated per PAN/FY

+
{{ sft015Count }} record{{ sft015Count === 1 ? '' : 's' }}
+
+
+
+ SFT-016Interest Paid on Deposits +
+

Interest paid on deposits — any amount, aggregated per PAN/FY

+
{{ sft016Count }} record{{ sft016Count === 1 ? '' : 's' }}
+
+
+ +
+ +
+
+ } + + + @if (activeTab === 'person') { +
+
+
+

Person-wise Records — SFT-001 / 002 / 004

+

One record per person per SFT code per FY (aggregate)

+
+
+ + + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + @for (r of filteredPersons; track r.reportSerialNumber + '-' + r.sftCode) { + + + + + + + + + + + + + + + + } @empty { + + + + } + +
RSNCodePerson NameTypePANDOBMobileCityProductAggregateCashLast TxnAccount
{{ r.reportSerialNumber }} + {{ r.sftCode }} + {{ r.personName }}{{ r.personType }}{{ r.pan }}{{ r.dobOrIncorporation }}{{ r.primaryMobileNo }}{{ r.city }}, {{ r.state }} + {{ productLabel(r.productType) }} + ₹{{ r.aggGrossReceived | number: '1.0-0' }}₹{{ r.aggGrossReceivedCash | number: '1.0-0' }}{{ r.lastDateOfTransaction }}{{ r.relatedAccountNumber }}
No records for the selected SFT code.
+
+
+ } + + + @if (activeTab === 'account') { +
+
+
+

SFT-003 — Cash Deposits/Withdrawals in Current Accounts

+

Account-wise schema with embedded Person Card; aggregate ≥ ₹50 L cash per FY

+
+ +
+ +
+ @for (r of accountRecords; track r.reportSerialNumber) { +
+
+
+ RSN-{{ r.reportSerialNumber }} + {{ r.accountType }} + {{ + r.accountStatus === 'A' ? 'Active' : r.accountStatus === 'C' ? 'Closed' : 'Dormant' + }} +
+
+ Cash credited + ₹{{ r.aggGrossCashCredited | number: '1.0-0' }} + / debited + ₹{{ r.aggGrossCashDebited | number: '1.0-0' }} +
+
+
+
+
+ {{ r.accountNumber }} +
+
+ {{ r.accountHolderName }} +
+
+ {{ r.branchName }} ({{ r.branchReferenceNumber }}) +
+
+ {{ r.branchAddress }} +
+
+ {{ r.branchStdCode }}-{{ r.branchPhone }} +
+
+ {{ r.branchEmail }} +
+
+
+
Person Card
+
+
+ {{ r.pcPersonName }} +
+
+ {{ r.pcPersonType }} +
+
+ {{ r.pcPan }} +
+
+ {{ r.pcDob }} +
+
+ {{ productLabel(r.pcBusiness) }} +
+
+ {{ r.pcPrimaryMobile }} +
+
+ {{ r.pcAddress }}, {{ r.pcCity }}, {{ r.pcState }} {{ r.pcPin }} +
+
+
+
+
+ } @empty { +
No SFT-003 records this FY.
+ } +
+
+ } + + + @if (activeTab === 'intdiv') { +
+
+
+

SFT-015 (Dividend) & SFT-016 (Interest Paid)

+

Pipe-delimited compact format submitted to Income Tax Reporting Portal

+
+
+ + + +
+
+ +
+ + + + + + + + + + + + + + + + + @for (r of filteredIntDiv; track r.rsn + '-' + r.sftCode) { + + + + + + + + + + + + + } @empty { + + + + } + +
RSNCodeFYNamePANAadhaar (mask)MobileAccountTypeAmount
{{ r.rsn }} + {{ r.sftCode }} + {{ r.fy }}{{ r.name }}{{ r.pan || '—' }}{{ r.aadhaar || '—' }}{{ r.mobile }}{{ r.accountNumber || '—' }}{{ r.typeOfAccount || (r.sftCode === 'SFT-015' ? 'DIV' : '—') }} + ₹{{ r.amount | number: '1.0-0' }} +
No records for the selected SFT code.
+
+ +
+ + Note: SFT-015 (Dividend) and SFT-016 (Interest) are pipe-delimited (`|`) files, one row per + PAN per FY with aggregated amounts. Submit via the IT Reporting Portal under {{ bank.principalOfficer }}'s + digital signature. +
+
+ } + + +
{{ toastMsg }}
+
diff --git a/src/app/sft/sft.component.scss b/src/app/sft/sft.component.scss new file mode 100644 index 000000000..573c87d67 --- /dev/null +++ b/src/app/sft/sft.component.scss @@ -0,0 +1,689 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +:host { + --s-navy: #0b1e3d; + --s-blue: #1d4ed8; + --s-blue-l: #eff6ff; + --s-teal: #0e7e6b; + --s-teal-l: #e0f4f0; + --s-green: #15803d; + --s-green-l: #dcfce7; + --s-amber: #d97706; + --s-amber-l: #fef3c7; + --s-red: #c0392b; + --s-red-l: #fdecea; + --s-purple: #6d28d9; + --s-purple-l: #ede9fe; + --s-pink: #db2777; + --s-pink-l: #fce7f3; + --s-g50: #f8fafc; + --s-g100: #f1f5f9; + --s-g200: #e2e8f0; + --s-g300: #cbd5e1; + --s-g500: #64748b; + --s-g700: #334155; + --s-g900: #0f172a; + + display: block; + font-family: + 'DM Sans', + system-ui, + -apple-system, + sans-serif; + background: var(--s-g50); + color: var(--s-g900); + font-size: 14px; + line-height: 1.5; +} + +.sft-root { + padding: 18px 22px; + max-width: 1500px; + margin: 0 auto; +} + +.sft-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + flex-wrap: wrap; + margin-bottom: 16px; +} + +.sft-head-left { + display: flex; + align-items: center; + gap: 14px; +} + +.sft-head-ic { + font-size: 28px; + color: var(--s-blue); +} + +.sft-head-t { + margin: 0; + font-size: 18px; + font-weight: 700; + color: var(--s-g900); +} + +.sft-head-s { + font-size: 12px; + color: var(--s-g500); +} + +.sft-head-meta { + display: flex; + gap: 22px; + + > div { + display: flex; + flex-direction: column; + + .sft-meta-l { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--s-g500); + } + + .sft-meta-v { + font-size: 14px; + font-weight: 600; + color: var(--s-g900); + } + } +} + +.sft-tabs { + display: flex; + gap: 4px; + background: #fff; + border: 1px solid var(--s-g200); + border-radius: 10px; + padding: 4px; + margin-bottom: 16px; + overflow-x: auto; +} + +.sft-tab { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 9px 14px; + border: none; + background: transparent; + font-size: 13px; + font-weight: 500; + color: var(--s-g500); + cursor: pointer; + border-radius: 7px; + white-space: nowrap; + transition: all 0.15s; + font-family: inherit; + + &:hover { + color: var(--s-g900); + } + + &.active { + background: var(--s-blue); + color: #fff; + box-shadow: 0 2px 6px rgb(29 78 216 / 25%); + } +} + +.sft-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +/* KPIs */ +.sft-kpis { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.sft-kpi { + background: #fff; + border: 1px solid var(--s-g200); + border-radius: 12px; + padding: 14px 16px; + display: flex; + align-items: center; + gap: 12px; + box-shadow: 0 1px 3px rgb(0 0 0 / 5%); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 14px rgb(0 0 0 / 10%); + } +} + +.sft-kpi-ic { + width: 42px; + height: 42px; + border-radius: 11px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.sft-kpi--blue .sft-kpi-ic { + background: var(--s-blue-l); + color: var(--s-blue); +} + +.sft-kpi--teal .sft-kpi-ic { + background: var(--s-teal-l); + color: var(--s-teal); +} + +.sft-kpi--amber .sft-kpi-ic { + background: var(--s-amber-l); + color: var(--s-amber); +} + +.sft-kpi--purple .sft-kpi-ic { + background: var(--s-purple-l); + color: var(--s-purple); +} + +.sft-kpi--green .sft-kpi-ic { + background: var(--s-green-l); + color: var(--s-green); +} + +.sft-kpi-n { + font-size: 18px; + font-weight: 700; + color: var(--s-g900); + line-height: 1.1; +} + +.sft-kpi-l { + font-size: 12px; + color: var(--s-g700); + font-weight: 500; + margin-top: 2px; +} + +.sft-kpi-sub { + font-size: 11px; + color: var(--s-g500); + margin-top: 1px; +} + +/* SFT code summary cards */ +.sft-codes { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; +} + +.sft-code-card { + background: #fff; + border: 1px solid var(--s-g200); + border-radius: 12px; + padding: 14px 16px; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.sft-code-h { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; + font-size: 14px; + font-weight: 600; + color: var(--s-g900); +} + +.sft-code-tag { + display: inline-block; + padding: 3px 10px; + border-radius: 6px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + font-family: 'DM Mono', monospace; + background: var(--s-g100); + color: var(--s-g700); + + &.t-001 { + background: var(--s-blue-l); + color: var(--s-blue); + } + + &.t-002 { + background: var(--s-teal-l); + color: var(--s-teal); + } + + &.t-003 { + background: var(--s-purple-l); + color: var(--s-purple); + } + + &.t-004 { + background: var(--s-amber-l); + color: var(--s-amber); + } + + &.t-015 { + background: var(--s-pink-l); + color: var(--s-pink); + } + + &.t-016 { + background: var(--s-green-l); + color: var(--s-green); + } +} + +.sft-code-card p { + margin: 0 0 10px; + font-size: 12px; + color: var(--s-g500); + line-height: 1.5; +} + +.sft-code-meta { + font-size: 12px; + color: var(--s-g700); + font-weight: 600; +} + +/* Toolbar */ +.sft-toolbar { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +/* Form header */ +.sft-form-meta { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 14px; + flex-wrap: wrap; +} + +.sft-form-title { + h3 { + margin: 0; + font-size: 16px; + font-weight: 700; + color: var(--s-g900); + } + + p { + margin: 2px 0 0; + font-size: 12px; + color: var(--s-g500); + } +} + +.sft-action-group { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +/* Buttons */ +.sft-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 14px; + border: 1px solid var(--s-g300); + background: #fff; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + color: var(--s-g700); + cursor: pointer; + font-family: inherit; + transition: all 0.15s; + + &:hover { + background: var(--s-g50); + border-color: var(--s-g500); + } +} + +.sft-btn-csv { + background: #1d4ed8; + border-color: #1d4ed8; + color: #fff; + + &:hover { + background: #1e40af; + border-color: #1e40af; + color: #fff; + } +} + +.sft-btn-xls { + background: #16a34a; + border-color: #16a34a; + color: #fff; + + &:hover { + background: #15803d; + border-color: #15803d; + color: #fff; + } +} + +/* Inputs */ +.sft-inp { + padding: 8px 12px; + border: 1px solid var(--s-g200); + border-radius: 8px; + font-size: 13px; + background: #fff; + font-family: inherit; + + &:focus { + outline: none; + border-color: var(--s-blue); + box-shadow: 0 0 0 3px rgb(29 78 216 / 15%); + } +} + +.sft-inp-narrow { + width: 200px; +} + +/* Table */ +.sft-tbl-wrap { + background: #fff; + border: 1px solid var(--s-g200); + border-radius: 12px; + overflow: auto; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.sft-tbl { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: 10px 12px; + font-size: 11px; + font-weight: 600; + color: var(--s-g500); + text-transform: uppercase; + letter-spacing: 0.04em; + background: var(--s-g50); + border-bottom: 1px solid var(--s-g200); + } + + td { + padding: 10px 12px; + border-bottom: 1px solid var(--s-g100); + font-size: 12px; + color: var(--s-g700); + } + + tr:last-child td { + border-bottom: none; + } + + tr:hover td { + background: var(--s-g50); + } +} + +.ta-c { + text-align: center; +} + +.ta-r { + text-align: right; +} + +.mono { + font-family: 'DM Mono', monospace; + font-size: 11px; + letter-spacing: 0.02em; +} + +.muted { + color: var(--s-g500); + font-size: 11px; +} + +.sft-empty { + text-align: center; + padding: 30px; + color: var(--s-g500); + font-style: italic; +} + +.sft-empty-card { + background: #fff; + border: 1px dashed var(--s-g300); + border-radius: 12px; + padding: 50px; + text-align: center; + color: var(--s-g500); + font-size: 13px; +} + +/* Account-wise SFT-003 cards */ +.sft-acc-list { + display: flex; + flex-direction: column; + gap: 12px; +} + +.acc-card { + background: #fff; + border: 1px solid var(--s-g200); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 1px 3px rgb(0 0 0 / 4%); +} + +.acc-card-h { + padding: 12px 16px; + background: var(--s-g50); + border-bottom: 1px solid var(--s-g200); + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + flex-wrap: wrap; +} + +.acc-card-id { + display: flex; + align-items: center; + gap: 10px; +} + +.acc-rsn { + font-family: 'DM Mono', monospace; + font-size: 13px; + font-weight: 700; + color: var(--s-g900); +} + +.acc-type-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + background: var(--s-purple-l); + color: var(--s-purple); +} + +.acc-status-pill { + display: inline-block; + padding: 2px 9px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + background: var(--s-g100); + color: var(--s-g500); + + &.act { + background: var(--s-green-l); + color: var(--s-green); + } +} + +.acc-card-amt { + display: flex; + align-items: baseline; + gap: 6px; + + strong { + font-size: 14px; + color: var(--s-g900); + font-family: 'DM Mono', monospace; + } +} + +.acc-card-body { + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.acc-meta { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px 18px; + + > div { + display: flex; + flex-direction: column; + + label { + font-size: 10px; + font-weight: 600; + color: var(--s-g500); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1px; + } + + span { + font-size: 12px; + color: var(--s-g900); + } + } +} + +.acc-pc-block { + background: var(--s-blue-l); + border-radius: 8px; + padding: 12px 14px; +} + +.acc-pc-h { + display: flex; + align-items: center; + gap: 8px; + font-size: 11px; + font-weight: 700; + color: var(--s-blue); + text-transform: uppercase; + letter-spacing: 0.06em; + margin-bottom: 8px; +} + +.acc-pc-addr { + grid-column: 1 / -1; +} + +/* Note */ +.sft-note { + background: var(--s-amber-l); + border: 1px solid #fed7aa; + border-radius: 10px; + padding: 12px 16px; + display: flex; + gap: 10px; + font-size: 12px; + color: #92400e; + line-height: 1.5; + + fa-icon { + font-size: 14px; + flex-shrink: 0; + margin-top: 2px; + } +} + +/* Toast */ +.sft-toast { + position: fixed; + bottom: 32px; + left: 50%; + transform: translateX(-50%) translateY(20px); + background: var(--s-navy); + color: #fff; + padding: 12px 22px; + border-radius: 10px; + font-size: 13px; + font-weight: 500; + box-shadow: 0 6px 20px rgb(0 0 0 / 25%); + opacity: 0; + pointer-events: none; + transition: all 0.25s; + z-index: 1500; + + &.show { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + +@media (width <= 1100px) { + .sft-kpis { + grid-template-columns: repeat(3, 1fr); + } + + .sft-codes { + grid-template-columns: 1fr 1fr; + } + + .acc-meta { + grid-template-columns: 1fr 1fr; + } +} + +@media (width <= 720px) { + .sft-kpis { + grid-template-columns: 1fr 1fr; + } + + .sft-codes { + grid-template-columns: 1fr; + } + + .acc-meta { + grid-template-columns: 1fr; + } +} diff --git a/src/app/sft/sft.component.ts b/src/app/sft/sft.component.ts new file mode 100644 index 000000000..693d078ea --- /dev/null +++ b/src/app/sft/sft.component.ts @@ -0,0 +1,466 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { + SFT_ACCOUNT_RECORDS, + SFT_BANK, + SFT_INT_DIV_RECORDS, + SFT_PERSON_RECORDS, + SftAccountRecord, + SftCode, + SftInterestDividendRecord, + SftPersonRecord +} from './sft-data'; + +@Component({ + selector: 'mifosx-sft', + templateUrl: './sft.component.html', + styleUrls: ['./sft.component.scss'], + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FaIconComponent, + FormsModule + ] +}) +export class SftComponent { + /** Active tab */ + activeTab: 'dashboard' | 'person' | 'account' | 'intdiv' = 'dashboard'; + + bank = SFT_BANK; + personRecords: SftPersonRecord[] = SFT_PERSON_RECORDS; + accountRecords: SftAccountRecord[] = SFT_ACCOUNT_RECORDS; + intDivRecords: SftInterestDividendRecord[] = SFT_INT_DIV_RECORDS; + + /** Person tab — switch which SFT code is being shown */ + personSftFilter: 'ALL' | 'SFT-001' | 'SFT-002' | 'SFT-004' = 'ALL'; + + /** Int/Div tab — switch interest vs dividend */ + intDivFilter: 'ALL' | 'SFT-015' | 'SFT-016' = 'ALL'; + + toastMsg = ''; + toastVisible = false; + private toastTimer: any; + + showTab(tab: 'dashboard' | 'person' | 'account' | 'intdiv'): void { + this.activeTab = tab; + } + + // -------------------- KPIs -------------------- + get totalRecords(): number { + return this.personRecords.length + this.accountRecords.length + this.intDivRecords.length; + } + get sft001Count(): number { + return this.personRecords.filter((r) => r.sftCode === 'SFT-001').length; + } + get sft002Count(): number { + return this.personRecords.filter((r) => r.sftCode === 'SFT-002').length; + } + get sft003Count(): number { + return this.accountRecords.length; + } + get sft004Count(): number { + return this.personRecords.filter((r) => r.sftCode === 'SFT-004').length; + } + get sft015Count(): number { + return this.intDivRecords.filter((r) => r.sftCode === 'SFT-015').length; + } + get sft016Count(): number { + return this.intDivRecords.filter((r) => r.sftCode === 'SFT-016').length; + } + get totalReportableAmount(): number { + return ( + this.personRecords.reduce((s, r) => s + r.aggGrossReceived, 0) + + this.accountRecords.reduce((s, r) => s + r.aggGrossCashCredited, 0) + + this.intDivRecords.reduce((s, r) => s + r.amount, 0) + ); + } + + // -------------------- Filters -------------------- + get filteredPersons(): SftPersonRecord[] { + if (this.personSftFilter === 'ALL') return this.personRecords; + return this.personRecords.filter((r) => r.sftCode === this.personSftFilter); + } + + get filteredIntDiv(): SftInterestDividendRecord[] { + if (this.intDivFilter === 'ALL') return this.intDivRecords; + return this.intDivRecords.filter((r) => r.sftCode === this.intDivFilter); + } + + // -------------------- Helpers -------------------- + productLabel(t: string): string { + return t.replace(/_/g, ' '); + } + sftLabel(c: SftCode): string { + const map: Record = { + 'SFT-001': 'Cash Drafts/PO', + 'SFT-002': 'Time Deposits', + 'SFT-003': 'Cash CA', + 'SFT-004': 'Cash SB', + 'SFT-015': 'Dividend', + 'SFT-016': 'Interest Paid' + }; + return map[c]; + } + + // -------------------- CSV exports -------------------- + + /** SFT-001/002/004 — Person-wise CSV (comma-separated, all 43 columns) */ + exportPersonCsv(filter?: SftCode): void { + const list = filter ? this.personRecords.filter((r) => r.sftCode === filter) : this.filteredPersons; + const headers = [ + 'Report Serial Number', + 'Original Report Serial Number', + 'Person Name', + 'Person Type', + 'Customer Identity', + 'Gender', + 'Father\u2019s Name', + 'PAN', + 'Aadhaar Number', + 'Form 60 Acknowledgment', + 'Identification Type', + 'Identification Number', + 'Date of birth/Incorporation', + 'Nationality/Country of Incorporation', + 'AddressType', + 'Fix Address', + 'City/Town', + 'PostCode', + 'State', + 'CountryCode', + 'Business or occupation', + 'Primary STD Code', + 'Primary Phone No', + 'Primary Mobile No', + 'Secondary STD Code', + 'Secondary Phone No', + 'Secondary Mobile No', + 'Email', + 'Remarks', + 'Product Type', + 'Aggregate gross amount received from the person', + 'Aggregate gross amount received from the person in cash', + 'Aggregate gross amount paid to the person', + 'TransactionSummry Remarks', + 'Product Identifier', + 'Last Date of Transaction', + 'Amount Received From Person', + 'Cash Amount Received From Person', + 'Amount Paid To Person', + 'Related Account Number', + 'Related Institute Name', + 'Related Institute Ref Number', + 'Financial TransactionDtls Remarks' + ]; + const rows: string[][] = [headers]; + if (list.length === 0) { + rows.push(['NIL Report']); + } else { + for (const r of list) { + rows.push([ + r.reportSerialNumber.toString(), + r.originalReportSerialNumber.toString(), + r.personName, + r.personType, + r.customerIdentity, + r.gender, + r.fatherName, + r.pan, + r.aadhaar, + r.form60Ack, + r.identificationType, + r.identificationNumber, + r.dobOrIncorporation.replace(/-/g, '-'), + r.nationality, + r.addressType, + r.fixAddress, + r.city, + r.postCode, + r.state, + r.countryCode, + r.businessOrOccupation, + r.primaryStdCode, + r.primaryPhoneNo, + r.primaryMobileNo, + '', + '', + '', + r.email, + r.remarks, + r.productType, + r.aggGrossReceived.toString(), + r.aggGrossReceivedCash.toString(), + r.aggGrossPaid.toString(), + '', + r.productIdentifier, + r.lastDateOfTransaction, + r.amountReceivedFromPerson.toString(), + r.cashAmountReceivedFromPerson.toString(), + r.amountPaidToPerson.toString(), + r.relatedAccountNumber, + r.relatedInstituteName, + r.relatedInstituteRefNumber, + '' + ]); + } + } + const fileTag = filter || 'ALL'; + this.saveCsv(rows, `${fileTag.replace('-', '_')}.csv`); + this.toast(`${fileTag} exported (${list.length} rows)`); + } + + /** SFT-003 — Account-wise CSV with embedded Person Card */ + exportAccountCsv(): void { + const headers = [ + 'Report Serial Number', + 'Original Report Serial Number', + 'Account Type', + 'Account Number', + 'Account Holder Name', + 'Account Status', + 'Branch Reference Number', + 'Branch Name', + 'Branch Address', + 'Branch City/Town', + 'Branch Postal Code', + 'Branch State', + 'Branch Country', + 'Branch STD Code', + 'Branch Phone Number', + 'Branch Mobile Number', + 'Branch Fax STD Code', + 'Branch Fax Phone No', + 'Branch Email', + 'Branch Remarks', + 'Aggregate gross amount credited to the account in cash', + 'Aggregate gross amount debited to the account in cash', + 'Amount credited to the account before demonetization', + 'Amount credited to the account after demonetization', + 'Account Remarks', + 'Account Relationship (PC)', + 'Person Name (PC)', + 'Person Type (PC)', + 'Customer Identity (PC)', + 'Gender (PC)', + 'Father\u2019s Name (PC)', + 'PAN (PC)', + 'Aadhaar Number (PC)', + 'Form 60 Acknowledgement (PC)', + 'Identification Type (PC)', + 'Identification Number (PC)', + 'Date of Birth/ Incorporation (PC)', + 'Nationality/Country of Incorporation (PC)', + 'Business or Occupation (PC)', + 'Address Type (PC-L)', + 'Address (PC-L)', + 'City/Town (PC-L)', + 'Pin Code (PC-L)', + 'State (PC-L)', + 'Country (PC-L)', + 'Primary STD Code (PC)', + 'Primary Telephone Number (PC)', + 'Primary Mobile Number (PC)', + 'Secondary STD Code (PC)', + 'Secondary Telephone Number (PC)', + 'Secondary Mobile Number (PC)', + 'Email (PC)', + 'Remarks (PC)' + ]; + const rows: string[][] = [headers]; + if (this.accountRecords.length === 0) { + rows.push(['NIL Report']); + } else { + for (const r of this.accountRecords) { + rows.push([ + r.reportSerialNumber.toString(), + r.originalReportSerialNumber.toString(), + r.accountType, + r.accountNumber, + r.accountHolderName, + r.accountStatus, + r.branchReferenceNumber, + r.branchName, + r.branchAddress, + r.branchCity, + r.branchPostalCode, + r.branchState, + r.branchCountry, + r.branchStdCode, + r.branchPhone, + r.branchMobile, + r.branchFaxStd, + r.branchFaxPhone, + r.branchEmail, + '', + r.aggGrossCashCredited.toString(), + r.aggGrossCashDebited.toString(), + r.amountCreditedBeforeDemonetization.toString(), + r.amountCreditedAfterDemonetization.toString(), + '', + r.pcAccountRelationship, + r.pcPersonName, + r.pcPersonType, + r.pcCustomerIdentity, + r.pcGender, + r.pcFatherName, + r.pcPan, + r.pcAadhaar, + r.pcForm60Ack, + r.pcIdentificationType, + r.pcIdentificationNumber, + r.pcDob, + r.pcNationality, + r.pcBusiness, + r.pcAddressType, + r.pcAddress, + r.pcCity, + r.pcPin, + r.pcState, + r.pcCountry, + r.pcPrimaryStd, + r.pcPrimaryPhone, + r.pcPrimaryMobile, + '', + '', + '', + r.pcEmail, + '' + ]); + } + } + this.saveCsv(rows, 'SFT_003.csv'); + this.toast(`SFT-003 exported (${this.accountRecords.length} accounts)`); + } + + /** SFT-015 / SFT-016 — Pipe-delimited */ + exportIntDivCsv(filter?: 'SFT-015' | 'SFT-016'): void { + const list = filter ? this.intDivRecords.filter((r) => r.sftCode === filter) : this.filteredIntDiv; + const fileTag = filter || 'BOTH'; + const lines: string[] = []; + if (filter === 'SFT-015' || (!filter && list.some((r) => r.sftCode === 'SFT-015'))) { + // Dividend file header + const headers15 = [ + 'RSN', + 'FY', + 'Name', + 'PAN', + 'Aadhaar', + 'Address', + 'Pincode', + 'Mobile', + 'Email', + 'Dividend', + '' + ]; + const div15 = list.filter((r) => r.sftCode === 'SFT-015'); + if (filter === 'SFT-015' || div15.length > 0) { + if (filter !== 'SFT-016') { + lines.push(headers15.join('|')); + for (const r of div15) { + lines.push( + [ + r.rsn.toString(), + r.fy, + r.name, + r.pan, + r.aadhaar, + r.address || '', + r.pincode || '', + r.mobile, + r.email, + r.amount.toString(), + '' + ].join('|') + ); + } + } + } + } + if (filter === 'SFT-016' || (!filter && list.some((r) => r.sftCode === 'SFT-016'))) { + const headers16 = [ + 'RSN', + 'FY', + 'Name', + 'PAN', + 'Aadhaar', + 'Mobile', + 'Email', + 'Account Number', + 'Type of Account', + 'Interest', + '' + ]; + const int16 = list.filter((r) => r.sftCode === 'SFT-016'); + if (filter !== 'SFT-015' && int16.length > 0) { + if (lines.length > 0) lines.push(''); + lines.push(headers16.join('|')); + for (const r of int16) { + lines.push( + [ + r.rsn.toString(), + r.fy, + r.name, + r.pan, + r.aadhaar, + r.mobile, + r.email, + r.accountNumber || '', + r.typeOfAccount || '', + r.amount.toString(), + '' + ].join('|') + ); + } + } + } + const filename = filter === 'SFT-015' ? 'SFT15.csv' : filter === 'SFT-016' ? 'SFT16.csv' : 'SFT15_SFT16.csv'; + this.saveText(lines.join('\n') + '\n', filename, 'text/csv'); + this.toast(`${fileTag} exported (${list.length} rows)`); + } + + /** Bundle: all SFT files (one per SFT code) */ + exportAll(): void { + setTimeout(() => this.exportPersonCsv('SFT-001'), 0); + setTimeout(() => this.exportPersonCsv('SFT-002'), 200); + setTimeout(() => this.exportAccountCsv(), 400); + setTimeout(() => this.exportPersonCsv('SFT-004'), 600); + setTimeout(() => this.exportIntDivCsv('SFT-015'), 800); + setTimeout(() => this.exportIntDivCsv('SFT-016'), 1000); + } + + private saveCsv(rows: string[][], filename: string): void { + const escape = (v: string): string => { + const s = (v || '').replace(/"/g, '""'); + if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`; + return s; + }; + const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n'; + this.saveText(csv, filename, 'text/csv'); + } + + private saveText(content: string, filename: string, mime: string): void { + const blob = new Blob([content], { type: `${mime};charset=utf-8` }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + } + + toast(msg: string): void { + this.toastMsg = msg; + this.toastVisible = true; + if (this.toastTimer) clearTimeout(this.toastTimer); + this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800); + } +} diff --git a/src/app/sft/sft.module.ts b/src/app/sft/sft.module.ts new file mode 100644 index 000000000..0e7e55415 --- /dev/null +++ b/src/app/sft/sft.module.ts @@ -0,0 +1,22 @@ +/** + * Copyright since 2025 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { NgModule } from '@angular/core'; + +import { SharedModule } from '../shared/shared.module'; +import { SftRoutingModule } from './sft-routing.module'; +import { SftComponent } from './sft.component'; + +@NgModule({ + imports: [ + SharedModule, + SftRoutingModule, + SftComponent + ] +}) +export class SftModule {} diff --git a/src/main.scss b/src/main.scss index 9fda56e50..97c842e06 100644 --- a/src/main.scss +++ b/src/main.scss @@ -957,3 +957,39 @@ mat-card { border-radius: 16px !important; } } + +/* ─── CIC DQR Dialog Backdrop — ensure outside clicks register ─── */ +.cic-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +} + +/* ─── PSL Report Dialog Backdrop ─── */ +.psl-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +} + +/* ─── ALM Report Dialog Backdrop ─── */ +.alm-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +} + +/* ─── CIMS / XBRL Returns Dialog Backdrop ─── */ +.cims-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +} + +/* ─── CKYC Compliance Reports Dialog Backdrop ─── */ +.ckyc-rpt-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +} + +/* ─── Accounting Reports Dialog Backdrop ─── */ +.acct-rpt-dlg-backdrop { + background: rgb(15 23 42 / 45%); + pointer-events: auto; +}