feat: add 11 banking compliance & accounting report modules

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) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-29 18:59:50 +05:30
co-authored by Claude Opus 4.7
parent 634003a75c
commit 12bfc81219
61 changed files with 22207 additions and 725 deletions
+480
View File
@@ -0,0 +1,480 @@
<!--
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/.
-->
<div class="fiu-root">
<!-- HEADER -->
<div class="fiu-head">
<div class="fiu-head-left">
<fa-icon icon="shield-alt" class="fiu-head-ic"></fa-icon>
<div>
<h2 class="fiu-head-t">Financial Intelligence Unit – India (FIU-IND)</h2>
<div class="fiu-head-s">
{{ bank.name }} &middot; {{ bank.branch }} &middot; Reporting Month <strong>{{ bank.reportingMonth }}</strong>
&middot; PMLA 2002 / FINnet 2.0
</div>
</div>
</div>
<div class="fiu-head-meta">
<div>
<span class="fiu-meta-l">Principal Officer</span><span class="fiu-meta-v">{{ bank.principalOfficer }}</span>
</div>
<div>
<span class="fiu-meta-l">REBANK BUID</span><span class="fiu-meta-v mono">{{ bank.reBuid }}</span>
</div>
</div>
</div>
<!-- TABS -->
<div class="fiu-tabs">
<button class="fiu-tab" [class.active]="activeTab === 'dashboard'" (click)="showTab('dashboard')">
<fa-icon icon="chart-pie"></fa-icon> Dashboard
</button>
<button class="fiu-tab" [class.active]="activeTab === 'ctr'" (click)="showTab('ctr')">
<fa-icon icon="coins"></fa-icon> CTR — Cash Transactions
</button>
<button class="fiu-tab" [class.active]="activeTab === 'str'" (click)="showTab('str')">
<fa-icon icon="exclamation-triangle"></fa-icon> STR — Suspicious
</button>
<button class="fiu-tab" [class.active]="activeTab === 'customers'" (click)="showTab('customers')">
<fa-icon icon="users"></fa-icon> Customer Cards
</button>
<button class="fiu-tab" [class.active]="activeTab === 'files'" (click)="showTab('files')">
<fa-icon icon="file-csv"></fa-icon> FINnet Files
</button>
</div>
<!-- DASHBOARD -->
@if (activeTab === 'dashboard') {
<div class="fiu-section">
<div class="fiu-kpis">
<div class="fiu-kpi fiu-kpi--blue">
<div class="fiu-kpi-ic"><fa-icon icon="file-alt"></fa-icon></div>
<div>
<div class="fiu-kpi-n">{{ totalReports | number }}</div>
<div class="fiu-kpi-l">CTR Reports</div>
<div class="fiu-kpi-sub">{{ totalCtrTxns | number }} txns this cycle</div>
</div>
</div>
<div class="fiu-kpi fiu-kpi--teal">
<div class="fiu-kpi-ic"><fa-icon icon="coins"></fa-icon></div>
<div>
<div class="fiu-kpi-n">&#8377;{{ totalCtrAmount | number: '1.0-0' }}</div>
<div class="fiu-kpi-l">Total CTR Amount</div>
<div class="fiu-kpi-sub">aggregate cash movement</div>
</div>
</div>
<div class="fiu-kpi fiu-kpi--amber">
<div class="fiu-kpi-ic"><fa-icon icon="exclamation-triangle"></fa-icon></div>
<div>
<div class="fiu-kpi-n">{{ pendingStr | number }}</div>
<div class="fiu-kpi-l">STR Pending</div>
<div class="fiu-kpi-sub">draft / PO review</div>
</div>
</div>
<div class="fiu-kpi fiu-kpi--green">
<div class="fiu-kpi-ic"><fa-icon icon="check-circle"></fa-icon></div>
<div>
<div class="fiu-kpi-n">{{ filedStr | number }}</div>
<div class="fiu-kpi-l">STR Filed</div>
<div class="fiu-kpi-sub">acknowledged by FIU</div>
</div>
</div>
<div class="fiu-kpi fiu-kpi--purple">
<div class="fiu-kpi-ic"><fa-icon icon="users"></fa-icon></div>
<div>
<div class="fiu-kpi-n">{{ totalCustomers | number }}</div>
<div class="fiu-kpi-l">Customers in Cycle</div>
<div class="fiu-kpi-sub">{{ kc1.length }} ind &middot; {{ kc2.length }} entity</div>
</div>
</div>
</div>
<!-- Compliance cards -->
<div class="fiu-compliance">
<div class="dc-card">
<div class="dc-card-h">
<fa-icon icon="check-circle" class="dc-ok"></fa-icon><span>CTR — Cash Transaction Report</span>
</div>
<div class="dc-meta">
Threshold: <strong>&#8377;{{ bank.cashThreshold | number }}</strong> per single txn / aggregate per month
</div>
<div class="dc-meta">Due: <strong>15 Feb 2026</strong></div>
<button class="dc-btn" (click)="showTab('ctr')"><fa-icon icon="external-link-alt"></fa-icon> Open</button>
</div>
<div class="dc-card">
<div class="dc-card-h">
<fa-icon icon="exclamation-triangle" class="dc-warn"></fa-icon><span>STR — Suspicious Transaction</span>
</div>
<div class="dc-meta">Filing window: <strong>within 7 days of detection</strong></div>
<div class="dc-meta">
Pending: <strong>{{ pendingStr }}</strong> &middot; Filed: <strong>{{ filedStr }}</strong>
</div>
<button class="dc-btn" (click)="showTab('str')"><fa-icon icon="external-link-alt"></fa-icon> Open</button>
</div>
<div class="dc-card">
<div class="dc-card-h"><fa-icon icon="file-csv" class="dc-info"></fa-icon><span>FINnet 2.0 Files</span></div>
<div class="dc-meta">6 CSVs: TC1 / TC2 / KC1 / KC2 / Account_Detail / Account_Person_Relation</div>
<div class="dc-meta">+ STR XML for suspicious filings</div>
<button class="dc-btn" (click)="showTab('files')"><fa-icon icon="external-link-alt"></fa-icon> Open</button>
</div>
</div>
</div>
}
<!-- CTR -->
@if (activeTab === 'ctr') {
<div class="fiu-section">
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>CTR — Cash Transaction Report</h3>
<p>
All cash transactions ≥ &#8377;{{ bank.cashThreshold | number }} (single or aggregate per month per account)
</p>
</div>
<button class="fiu-btn fiu-btn-csv" (click)="exportTc2()">
<fa-icon icon="file-csv"></fa-icon> Download TC2.csv
</button>
</div>
<div class="fiu-filters">
<div class="fiu-filter">
<label>Type</label>
<select class="fiu-inp" [(ngModel)]="ctrFilterMode">
<option value="">All</option>
<option value="Deposit">Deposit</option>
<option value="Withdrawal">Withdrawal</option>
</select>
</div>
<div class="fiu-filter">
<label>Account contains</label>
<input class="fiu-inp" type="text" [(ngModel)]="ctrFilterAccount" placeholder="A/c partial" />
</div>
<div class="fiu-filter">
<label>Min Amount (&#8377;)</label>
<input class="fiu-inp" type="number" [(ngModel)]="ctrFilterMinAmount" placeholder="0" />
</div>
<button class="fiu-btn fiu-btn-g" (click)="resetCtrFilters()">Reset</button>
</div>
<div class="fiu-tbl-wrap">
<table class="fiu-tbl">
<thead>
<tr>
<th class="ta-c">Ref</th>
<th>Account</th>
<th>Customer</th>
<th class="ta-c">Type</th>
<th>Date / Time</th>
<th class="ta-r">Amount</th>
<th>Instrument</th>
<th>Branch</th>
<th class="ta-c">Relationship</th>
</tr>
</thead>
<tbody>
@for (t of filteredCtr; track t.txnId) {
<tr>
<td class="ta-c">{{ t.reportRefNo }}</td>
<td class="mono">{{ t.accountNumber }}</td>
<td>{{ t.customerName }}</td>
<td class="ta-c">
<span
class="dir-pill"
[class.deposit]="t.depositOrWithdrawal === 'Deposit'"
[class.withdrawal]="t.depositOrWithdrawal === 'Withdrawal'"
>
{{ t.depositOrWithdrawal }}
</span>
</td>
<td>
{{ t.txnDate }} <span class="muted">{{ t.txnTime }}</span>
</td>
<td class="ta-r mono">
<strong>&#8377;{{ t.txnAmount | number: '1.0-0' }}</strong>
</td>
<td>
{{ t.instrumentType }} <span class="muted">{{ t.instrumentId }}</span>
</td>
<td class="mono">{{ t.branchCode }}</td>
<td class="ta-c">
<span class="rel-pill" [class.non-cust]="t.relationshipFlag === 'Non-Customer'">{{
t.relationshipFlag
}}</span>
</td>
</tr>
} @empty {
<tr>
<td colspan="9" class="fiu-empty">No transactions match the filters.</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
<!-- STR -->
@if (activeTab === 'str') {
<div class="fiu-section">
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>STR — Suspicious Transaction Report</h3>
<p>Detected by AML rules engine + branch compliance officers; filed within 7 days</p>
</div>
<button class="fiu-btn fiu-btn-xml" (click)="exportStrXml()">
<fa-icon icon="file-alt"></fa-icon> Download STR XML
</button>
</div>
<div class="fiu-filters">
<div class="fiu-filter">
<label>Status</label>
<select class="fiu-inp" [(ngModel)]="strFilterStatus">
<option value="">All</option>
<option value="draft">Draft</option>
<option value="po-review">PO Review</option>
<option value="filed">Filed</option>
<option value="rejected">Rejected</option>
</select>
</div>
<div class="fiu-filter">
<label>Risk Category</label>
<select class="fiu-inp" [(ngModel)]="strFilterRisk">
<option value="">All</option>
<option value="structuring">Structuring</option>
<option value="dormant-revival">Dormant Revival</option>
<option value="high-risk-country">High-Risk Country</option>
<option value="unusual-pattern">Unusual Pattern</option>
<option value="pep-related">PEP Related</option>
<option value="sanctions-hit">Sanctions Hit</option>
</select>
</div>
<button class="fiu-btn fiu-btn-g" (click)="resetStrFilters()">Reset</button>
</div>
<div class="fiu-str-list">
@for (s of filteredStr; track s.reportRefNo) {
<div class="str-card" [class.str-card--filed]="s.status === 'filed'">
<div class="str-card-h">
<div class="str-card-id">
<span class="str-ref">STR-{{ s.reportRefNo }}</span>
<span class="status-pill" [class]="'st-' + s.status">{{ statusLabel(s.status) }}</span>
<span class="risk-pill" [class]="'rk-' + s.riskCategory">{{ riskLabel(s.riskCategory) }}</span>
</div>
<div class="str-card-amt">
<span class="muted">{{ s.depositOrWithdrawal }}</span>
<strong>&#8377;{{ s.txnAmount | number: '1.0-0' }}</strong>
</div>
</div>
<div class="str-card-body">
<div class="str-meta">
<div>
<label>Customer</label><span>{{ s.customerName }}</span>
</div>
<div>
<label>Account</label><span class="mono">{{ s.accountNumber }}</span>
</div>
<div>
<label>Txn Date</label><span>{{ s.txnDate }} {{ s.txnTime }}</span>
</div>
<div>
<label>Detected</label><span>{{ s.detectedOn }}</span>
</div>
<div>
<label>Flagged By</label><span>{{ s.flaggedBy }}</span>
</div>
<div>
<label>Instrument</label><span>{{ s.instrumentType }} {{ s.instrumentId }}</span>
</div>
</div>
<div class="str-narrative">
<label>Narrative</label>
<p>{{ s.narrative }}</p>
</div>
@if (s.poRemarks) {
<div class="str-po-remarks">
<label><fa-icon icon="check-circle"></fa-icon> PO Remarks</label>
<p>{{ s.poRemarks }}</p>
</div>
}
</div>
</div>
} @empty {
<div class="fiu-empty-card">No STR records match the filters.</div>
}
</div>
</div>
}
<!-- CUSTOMER CARDS -->
@if (activeTab === 'customers') {
<div class="fiu-section">
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>Customer Cards — KC1 (Individuals) &amp; KC2 (Entities)</h3>
<p>Customers attached to reports filed in this cycle</p>
</div>
<div class="fiu-action-group">
<button class="fiu-btn fiu-btn-csv" (click)="exportKc1()"><fa-icon icon="file-csv"></fa-icon> KC1.csv</button>
<button class="fiu-btn fiu-btn-csv" (click)="exportKc2()"><fa-icon icon="file-csv"></fa-icon> KC2.csv</button>
</div>
</div>
<div class="fiu-tbl-wrap">
<div class="fiu-tbl-h">KC1 — Individuals</div>
<table class="fiu-tbl">
<thead>
<tr>
<th class="ta-c">Ref</th>
<th>UCIC</th>
<th>Name</th>
<th class="ta-c">Gender</th>
<th>DOB</th>
<th>PAN</th>
<th>Mobile</th>
<th>City</th>
<th class="ta-r">Annual Income</th>
<th class="ta-c">Risk</th>
</tr>
</thead>
<tbody>
@for (k of kc1; track k.ucic) {
<tr>
<td class="ta-c">{{ k.reportRefNo }}</td>
<td class="mono">{{ k.ucic }}</td>
<td>{{ k.firstName }} {{ k.middleName }} {{ k.lastName }}</td>
<td class="ta-c">{{ k.gender }}</td>
<td>{{ k.dob }}</td>
<td class="mono">{{ k.pan }}</td>
<td class="mono">{{ k.mobileNumber }}</td>
<td>{{ k.city }}, {{ k.state }}</td>
<td class="ta-r mono">&#8377;{{ k.annualIncome | number }}</td>
<td class="ta-c">
<span class="risk-tag" [class]="'rk-tag-' + k.riskLevel.toLowerCase()">{{ k.riskLevel }}</span>
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="fiu-tbl-wrap">
<div class="fiu-tbl-h">KC2 — Entities</div>
<table class="fiu-tbl">
<thead>
<tr>
<th class="ta-c">Ref</th>
<th>UCIC</th>
<th>Entity Name</th>
<th>PAN</th>
<th>GSTIN</th>
<th>City</th>
<th>Type</th>
<th>Owner (if Sole Prop)</th>
<th class="ta-c">Risk</th>
</tr>
</thead>
<tbody>
@for (k of kc2; track k.ucic) {
<tr>
<td class="ta-c">{{ k.reportRefNo }}</td>
<td class="mono">{{ k.ucic }}</td>
<td>{{ k.entityName }}</td>
<td class="mono">{{ k.panNumber }}</td>
<td class="mono">{{ k.gstin }}</td>
<td>{{ k.city }}, {{ k.state }}</td>
<td>{{ k.customerType }}</td>
<td>{{ k.isSoleProprietor ? k.ownerName : '—' }}</td>
<td class="ta-c">
<span class="risk-tag" [class]="'rk-tag-' + k.riskLevel.toLowerCase()">{{ k.riskLevel }}</span>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
<!-- FINNET FILES -->
@if (activeTab === 'files') {
<div class="fiu-section">
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>FINnet 2.0 Submission Files</h3>
<p>6 CSV files in FIU-IND prescribed schema, plus STR XML</p>
</div>
<button class="fiu-btn fiu-btn-xls" (click)="exportAll()">
<fa-icon icon="file-csv"></fa-icon> Download All
</button>
</div>
<div class="files-grid">
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>TC1.csv</div>
<p>ATM / card transaction template (header only — no card txns this cycle)</p>
<button class="fiu-btn" (click)="exportTc1()"><fa-icon icon="file-csv"></fa-icon> Download</button>
</div>
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>TC2.csv</div>
<p>Branch / cash transactions ({{ ctrTxns.length }} rows)</p>
<button class="fiu-btn fiu-btn-csv" (click)="exportTc2()">
<fa-icon icon="file-csv"></fa-icon> Download
</button>
</div>
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>KC1.csv</div>
<p>Individual customer cards ({{ kc1.length }} rows)</p>
<button class="fiu-btn fiu-btn-csv" (click)="exportKc1()">
<fa-icon icon="file-csv"></fa-icon> Download
</button>
</div>
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>KC2.csv</div>
<p>Entity customer cards ({{ kc2.length }} rows)</p>
<button class="fiu-btn fiu-btn-csv" (click)="exportKc2()">
<fa-icon icon="file-csv"></fa-icon> Download
</button>
</div>
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>Account_Detail.csv</div>
<p>Per-account 12-month aggregates derived from CTR list</p>
<button class="fiu-btn fiu-btn-csv" (click)="exportAccountDetail()">
<fa-icon icon="file-csv"></fa-icon> Download
</button>
</div>
<div class="file-card">
<div class="file-card-h"><fa-icon icon="file-csv"></fa-icon>Account_Person_Relation.csv</div>
<p>Account ↔ customer relationship mapping</p>
<button class="fiu-btn fiu-btn-csv" (click)="exportAccountPersonRelation()">
<fa-icon icon="file-csv"></fa-icon> Download
</button>
</div>
<div class="file-card file-card--xml">
<div class="file-card-h"><fa-icon icon="file-alt"></fa-icon>STR.xml</div>
<p>Suspicious Transaction Report ({{ filedStr + pendingStr }} suspects)</p>
<button class="fiu-btn fiu-btn-xml" (click)="exportStrXml()">
<fa-icon icon="file-alt"></fa-icon> Download XML
</button>
</div>
</div>
<div class="fiu-note">
<fa-icon icon="info-circle"></fa-icon>
<span
><strong>Note:</strong> 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.</span
>
</div>
</div>
}
<!-- TOAST -->
<div class="fiu-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
</div>