Compare commits

...
7 Commits
Author SHA1 Message Date
keshav b9b37397fe Merge remote-tracking branch 'origin/demo' into demo 2026-07-31 15:10:42 +05:30
keshav f2525080c8 static update 2026-07-31 15:09:18 +05:30
nitesh 43e3c546a7 Merge remote-tracking branch 'origin/dev' into demo 2026-07-31 14:47:03 +05:30
nitesh b68b25f789 Merge remote-tracking branch 'origin/uate' into demo 2026-07-31 14:46:38 +05:30
niteshandClaude Sonnet 5 1dc378c2dd feat(cashier): add Cash/Clearing/Transfer summary report with branch-wise drill-down
Adds a dummy-data summary report (Opening/Credit/Debit/Closing) under Sub
Ledger > Cash / Clearing / Transfer, with Cash, Transfer (GL-wise), Clearing
and Digital (RTGS/NEFT/UPI/ATM/NACH) tabs plus a transaction-details drill
down. Credit/Debit/Closing are always derived from the transaction lines so
the summary and detail views can never drift apart.

Head Office logins (officeId 1 or office name containing "head office") get
a branch switcher across two demo branches plus a consolidated view with a
branch-wise break-up; branch-level logins see only their own office's data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 13:39:13 +05:30
keshav 0bf337b962 Merge branch 'uate' of https://git.lincpay.tech/CBSNexora/cbsnexora into uate 2026-07-31 11:00:14 +05:30
keshav 1daa12a117 kyc risk and AML & Compliance Checks automate on client creation 2026-07-31 10:58:58 +05:30
39 changed files with 4454 additions and 1706 deletions
+89
View File
@@ -0,0 +1,89 @@
/**
* 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 { Injectable, inject } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
/**
* AML transaction-monitoring API: live alerts raised by the rule engine, the
* two-tier AML Officer / Principal Officer review workflow, STR cases (with
* their 7-working-day FIU filing deadline), monthly CTR reports, and a
* client's risk-category change history.
*/
@Injectable({
providedIn: 'root'
})
export class AmlService {
private http = inject(HttpClient);
// ═══ Alerts (AML Officer) ═══
getAlerts(status?: string): Observable<any[]> {
const params = status ? new HttpParams().set('status', status) : undefined;
return this.http.get<any[]>('/aml/alerts', { params });
}
/** disposition: 'WHITELIST' | 'ESCALATE' */
reviewAlert(alertId: number, disposition: 'WHITELIST' | 'ESCALATE', notes: string): Observable<any> {
return this.http.post(`/aml/alerts/${alertId}/review`, { disposition, notes });
}
// ═══ STR cases (Principal Officer) ═══
getStrCases(): Observable<any[]> {
return this.http.get<any[]>('/aml/str-cases');
}
getStrCase(strCaseId: number): Observable<any> {
return this.http.get(`/aml/str-cases/${strCaseId}`);
}
getWeeklyDigest(): Observable<any[]> {
return this.http.get<any[]>('/aml/str-cases/weekly-digest');
}
assignPrincipalOfficer(strCaseId: number, principalOfficerUserId: number): Observable<any> {
return this.http.post(`/aml/str-cases/${strCaseId}/assign`, { principalOfficerUserId });
}
/** conclusion: 'NOT_SUSPICIOUS' | 'SUSPICIOUS' */
concludeStrCase(
strCaseId: number,
conclusion: 'NOT_SUSPICIOUS' | 'SUSPICIOUS',
investigationNotes: string,
sourceOfFundsNotes: string
): Observable<any> {
return this.http.post(`/aml/str-cases/${strCaseId}/conclude`, {
conclusion,
investigationNotes,
sourceOfFundsNotes
});
}
fileStr(strCaseId: number, referenceNo: string): Observable<any> {
return this.http.post(`/aml/str-cases/${strCaseId}/file`, { referenceNo });
}
// ═══ CTR reports (mechanical, threshold-based) ═══
getCtrReports(): Observable<any[]> {
return this.http.get<any[]>('/aml/ctr-reports');
}
/** month format: yyyy-MM */
generateCtrReport(month: string): Observable<any> {
return this.http.post('/aml/ctr-reports/generate', { month });
}
// ═══ Client risk-category history ═══
getRiskCategoryHistory(clientId: number): Observable<any[]> {
return this.http.get<any[]>(`/clients/${clientId}/risk-category-history`);
}
}
@@ -0,0 +1,267 @@
<!--
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="pg">
<header class="pg-head">
<div>
<div class="pg-eyebrow">SUB LEDGER · TRANSACTIONS · {{ scopeName | uppercase }}</div>
<h1 class="pg-title">Cash / Clearing / Transfer - Summary Report</h1>
<p class="pg-sub">
Opening, credit, debit and closing position across Cash, Transfer, Clearing and Digital channels as on
{{ asOnDate }}.
</p>
</div>
<div class="pg-toolbar">
@if (isHeadOffice) {
<label class="pg-field">
<span>Branch</span>
<select class="pg-select" [ngModel]="selectedBranchCode" (ngModelChange)="setBranch($event)">
<option value="ALL">All Branches (Consolidated)</option>
@for (b of branches; track b.code) {
<option [value]="b.code">{{ b.name }}</option>
}
</select>
</label>
}
<button type="button" class="pg-btn" [class.pg-btn--active]="showDetails" (click)="toggleDetails()">
{{ showDetails ? 'Summary View' : 'Transaction Details' }}
</button>
</div>
</header>
<div class="cctr-grand-summary">
<div class="cctr-grand-summary__item">
<span class="cctr-grand-summary__label">Opening</span>
<span class="cctr-grand-summary__value">₹{{ grandOpening | number: '1.2-2' }}</span>
</div>
<div class="cctr-grand-summary__item">
<span class="cctr-grand-summary__label">Credit</span>
<span class="cctr-grand-summary__value col-in">₹{{ grandCredit | number: '1.2-2' }}</span>
</div>
<div class="cctr-grand-summary__item">
<span class="cctr-grand-summary__label">Debit</span>
<span class="cctr-grand-summary__value col-out">₹{{ grandDebit | number: '1.2-2' }}</span>
</div>
<div class="cctr-grand-summary__item">
<span class="cctr-grand-summary__label">Closing</span>
<span class="cctr-grand-summary__value">₹{{ grandClosing | number: '1.2-2' }}</span>
</div>
</div>
@if (isHeadOffice && selectedBranchCode === 'ALL') {
<div class="pg-card">
<div class="pos-section-label">Branch-wise Break-up — click a branch to drill in</div>
<table class="pg-table">
<thead>
<tr>
<th>Branch</th>
<th class="num">Opening</th>
<th class="num col-in">Credit</th>
<th class="num col-out">Debit</th>
<th class="num col-net">Closing</th>
</tr>
</thead>
<tbody>
@for (b of branchBreakup; track b.code) {
<tr class="cctr-branch-row" (click)="setBranch(b.code)">
<td>{{ b.name }}</td>
<td class="num">₹{{ b.opening | number: '1.2-2' }}</td>
<td class="num col-in">₹{{ b.credit | number: '1.2-2' }}</td>
<td class="num col-out">₹{{ b.debit | number: '1.2-2' }}</td>
<td class="num col-net">₹{{ b.closing | number: '1.2-2' }}</td>
</tr>
}
</tbody>
<tfoot>
<tr class="cctr-total-row">
<td>All Branches</td>
<td class="num">₹{{ grandOpening | number: '1.2-2' }}</td>
<td class="num">₹{{ grandCredit | number: '1.2-2' }}</td>
<td class="num">₹{{ grandDebit | number: '1.2-2' }}</td>
<td class="num">₹{{ grandClosing | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
</div>
}
<div class="cctr-tabs">
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="activeTab === 'cash'"
(click)="setTab('cash')"
>
Cash
</button>
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="activeTab === 'transfer'"
(click)="setTab('transfer')"
>
Transfer
</button>
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="activeTab === 'clearing'"
(click)="setTab('clearing')"
>
Clearing
</button>
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="activeTab === 'digital'"
(click)="setTab('digital')"
>
Digital
</button>
</div>
<!-- Summary (Opening | Credit | Debit | Closing) -->
@if (!showDetails) {
<div class="pg-card">
@if (activeTab === 'digital') {
<div class="pos-section-label">RTGS · NEFT · UPI · ATM · NACH — Overall Position</div>
}
<table class="pg-table">
<thead>
<tr>
@if (activeTab !== 'digital') {
<th>GL Code</th>
}
<th>Particulars</th>
<th class="num">Opening</th>
<th class="num col-in">Credit</th>
<th class="num col-out">Debit</th>
<th class="num col-net">Closing</th>
</tr>
</thead>
<tbody>
@for (r of rowsForActiveTab; track r.code) {
<tr>
@if (activeTab !== 'digital') {
<td>
<span class="pg-chip">{{ r.code }}</span>
</td>
}
<td>{{ r.name }}</td>
<td class="num">₹{{ r.opening | number: '1.2-2' }}</td>
<td class="num col-in">₹{{ rowCredit(r, txnsForActiveTab) | number: '1.2-2' }}</td>
<td class="num col-out">₹{{ rowDebit(r, txnsForActiveTab) | number: '1.2-2' }}</td>
<td class="num col-net">₹{{ closing(r, txnsForActiveTab) | number: '1.2-2' }}</td>
</tr>
}
</tbody>
<tfoot>
<tr class="cctr-total-row">
<td [attr.colspan]="activeTab !== 'digital' ? 2 : 1">Total</td>
<td class="num">₹{{ total(rowsForActiveTab, txnsForActiveTab, 'opening') | number: '1.2-2' }}</td>
<td class="num">₹{{ total(rowsForActiveTab, txnsForActiveTab, 'credit') | number: '1.2-2' }}</td>
<td class="num">₹{{ total(rowsForActiveTab, txnsForActiveTab, 'debit') | number: '1.2-2' }}</td>
<td class="num">₹{{ totalClosing(rowsForActiveTab, txnsForActiveTab) | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
</div>
}
<!-- Transaction Details (all txns for the selected category, optionally filtered by GL/channel) -->
@if (showDetails) {
@if (rowsForActiveTab.length > 1) {
<div class="cctr-tabs">
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="glFilter === null"
(click)="setGlFilter(null)"
>
All
</button>
@for (r of rowsForActiveTab; track r.code) {
<button
type="button"
class="pg-btn pg-btn--ghost"
[class.pg-btn--active]="glFilter === r.code"
(click)="setGlFilter(r.code)"
>
{{ filterLabel(r) }}
</button>
}
</div>
}
<div class="pg-card">
<div class="pos-section-label">
{{
activeTab === 'cash'
? 'Cash'
: activeTab === 'transfer'
? 'Transfer'
: activeTab === 'clearing'
? 'Clearing'
: 'Digital'
}}
— Transaction Details
@if (glFilter !== null) {
· {{ glFilter }}
}
</div>
<table class="pg-table">
<thead>
<tr>
<th>Date</th>
<th>Voucher / Ref No</th>
<th>Account / Reference</th>
<th>Particulars</th>
<th class="num col-out">Debit</th>
<th class="num col-in">Credit</th>
</tr>
</thead>
<tbody>
@for (t of filteredTxns; track t.voucherNo) {
<tr>
<td>{{ asOnDate }}</td>
<td>
<span class="pg-chip">{{ t.voucherNo }}</span>
</td>
<td>
<div>{{ t.accountName }}</div>
<div class="cctr-subtext">{{ t.accountNo }}</div>
</td>
<td>{{ t.particulars }}</td>
<td class="num col-out">
@if (t.debit > 0) {
₹{{ t.debit | number: '1.2-2' }}
}
</td>
<td class="num col-in">
@if (t.credit > 0) {
₹{{ t.credit | number: '1.2-2' }}
}
</td>
</tr>
} @empty {
<tr>
<td colspan="6" class="pg-empty">No transactions for this filter.</td>
</tr>
}
</tbody>
<tfoot>
<tr class="cctr-total-row">
<td colspan="4">Total</td>
<td class="num">₹{{ txnTotal(filteredTxns, 'debit') | number: '1.2-2' }}</td>
<td class="num">₹{{ txnTotal(filteredTxns, 'credit') | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
</div>
}
</div>
@@ -0,0 +1,73 @@
/**
* 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/.
*/
.cctr-tabs {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.cctr-total-row {
td {
background: #f1f5f9;
font-weight: 800;
border-top: 2px solid #cbd5e1;
border-bottom: none;
color: #0f172a;
}
}
.cctr-grand-summary {
display: flex;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
&__item {
flex: 1;
padding: 14px 20px;
display: flex;
flex-direction: column;
gap: 4px;
border-right: 1px solid #e2e8f0;
&:last-child {
border-right: none;
}
}
&__label {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
color: #64748b;
}
&__value {
font-size: 20px;
font-weight: 800;
font-variant-numeric: tabular-nums;
color: #0f172a;
}
}
.cctr-subtext {
font-size: 11px;
color: #94a3b8;
margin-top: 2px;
}
.cctr-branch-row {
cursor: pointer;
&:hover td {
background: #eff6ff;
}
}
+11
View File
@@ -179,6 +179,17 @@ const routes: Routes = [
loadComponent: () => loadComponent: () =>
import('./my-deposit-requests/my-deposit-requests.component').then((m) => m.MyDepositRequestsComponent), import('./my-deposit-requests/my-deposit-requests.component').then((m) => m.MyDepositRequestsComponent),
data: { title: 'My Deposit Requests', breadcrumb: 'My Deposit Requests' } data: { title: 'My Deposit Requests', breadcrumb: 'My Deposit Requests' }
},
{
path: 'cash-clearing-transfer-summary',
loadComponent: () =>
import('./cash-clearing-transfer-summary/cash-clearing-transfer-summary.component').then(
(m) => m.CashClearingTransferSummaryComponent
),
data: {
title: 'Cash / Clearing / Transfer - Summary Report',
breadcrumb: 'Cash / Clearing / Transfer - Summary Report'
}
} }
] ]
} }
@@ -12,16 +12,16 @@
* have meaningful variety out of the box. * have meaningful variety out of the box.
*/ */
export const SAMPLE_CKYC_FILE = `10|24401|IN4711|MH|30|31-08-2020|V1.1||||| 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|||||| 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|CBSNEXORA BANK|IN4711|1|0||0|4||||||
30|1|C|AAQPS8560J||01|02||||| 30|1|C|AAQPS8560J||01|02|||||
70|1|10078_Sign.jpeg|09|01|01|||| 70|1|10078_Sign.jpeg|09|01|01||||
70|1|10078_Photo.jpeg|02|01|01|||| 70|1|10078_Photo.jpeg|02|01|01||||
70|1|10078_AP.jpg|04|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|||||| 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|CBSNEXORA BANK|IN4711|1|0||0|3||||||
30|2|E|XXXXXXXX2210||01|02||||| 30|2|E|XXXXXXXX2210||01|02|||||
70|2|10079_Sign.jpeg|09|01|01|||| 70|2|10079_Sign.jpeg|09|01|01||||
70|2|10079_Photo.jpeg|02|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|||||| 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|CBSNEXORA BANK|IN4711|1|0||0|3||||||
30|3|C|AGTPD1144K||01|02||||| 30|3|C|AGTPD1144K||01|02|||||
70|3|10080_Sign.jpeg|09|01|01|||| 70|3|10080_Sign.jpeg|09|01|01||||
70|3|10080_AP.jpg|04|01|01|||| 70|3|10080_AP.jpg|04|01|01||||
@@ -2134,7 +2134,15 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
return generalDetails; return generalDetails;
} }
get calculatedRisk(): { category: number; reason: string } { get calculatedRisk(): {
category: number;
reason: string;
amlCheckStatus?: string;
bannedEntityMatch?: string;
willfulDefaulter?: string;
ecgcSalFlag?: string;
dormantFlag?: string;
} {
const legalFormId = this.createClientForm.get('legalFormId')?.value || 1; // 1 = Person, 2 = Entity const legalFormId = this.createClientForm.get('legalFormId')?.value || 1; // 1 = Person, 2 = Entity
const occCatId = this.createClientForm.get('occupationCategoryCvId')?.value; const occCatId = this.createClientForm.get('occupationCategoryCvId')?.value;
const occId = this.createClientForm.get('occupationCvId')?.value; const occId = this.createClientForm.get('occupationCvId')?.value;
@@ -2151,6 +2159,21 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
// Evaluate rules from DB (ordered by orderPosition ASC) // Evaluate rules from DB (ordered by orderPosition ASC)
if (this.riskRules && this.riskRules.length > 0) { if (this.riskRules && this.riskRules.length > 0) {
for (const rule of this.riskRules) { for (const rule of this.riskRules) {
// Skip transaction/AML rules that have no KYC criteria
if (
rule.legalFormId == null &&
rule.occupationCategoryCvId == null &&
rule.occupationCvId == null &&
rule.minMonthlyIncome == null &&
rule.maxMonthlyIncome == null &&
rule.minAnnualIncome == null &&
rule.maxAnnualIncome == null &&
rule.minAnnualTurnover == null &&
rule.maxAnnualTurnover == null
) {
continue;
}
// Match legal form // Match legal form
if (rule.legalFormId != null && rule.legalFormId !== legalFormId) { if (rule.legalFormId != null && rule.legalFormId !== legalFormId) {
continue; continue;
@@ -2191,15 +2214,39 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
} }
// All matched! // All matched!
return { category: rule.riskCategory, reason: rule.reason }; return {
category: rule.riskCategory,
reason: rule.reason,
amlCheckStatus: rule.amlCheckStatus || (rule.riskCategory === 3 ? 'FLAGGED' : 'CLEAR'),
bannedEntityMatch: rule.bannedEntityMatch || 'NO',
willfulDefaulter: rule.willfulDefaulter || 'NO',
ecgcSalFlag: rule.ecgcSalFlag || 'NO',
dormantFlag: rule.dormantFlag || 'ACTIVE'
};
} }
} }
// Default Fallbacks // Default Fallbacks
if (legalFormId !== LegalFormId.PERSON) { if (legalFormId !== LegalFormId.PERSON) {
return { category: 2, reason: 'Non-Individual / Entity profile (Default Medium Risk)' }; return {
category: 2,
reason: 'Non-Individual / Entity profile (Default Medium Risk)',
amlCheckStatus: 'CLEAR',
bannedEntityMatch: 'NO',
willfulDefaulter: 'NO',
ecgcSalFlag: 'NO',
dormantFlag: 'ACTIVE'
};
} }
return { category: 1, reason: 'Default Low Risk profile (Salary / Service / Agriculture / Others)' }; return {
category: 1,
reason: 'Default Low Risk profile (Salary / Service / Agriculture / Others)',
amlCheckStatus: 'CLEAR',
bannedEntityMatch: 'NO',
willfulDefaulter: 'NO',
ecgcSalFlag: 'NO',
dormantFlag: 'ACTIVE'
};
} }
} }
@@ -1251,6 +1251,12 @@
</h4> </h4>
<div class="form-grid"> <div class="form-grid">
<div class="checkbox-field override-checkbox-wrapper full-width">
<mat-checkbox formControlName="allowAmlOverride" [disabled]="readOnly">
Override Auto-calculated AML &amp; Compliance Checks
</mat-checkbox>
</div>
<mat-form-field appearance="outline" class="form-field"> <mat-form-field appearance="outline" class="form-field">
<mat-label>AML Check Status</mat-label> <mat-label>AML Check Status</mat-label>
<mat-select required formControlName="amlCheckStatus"> <mat-select required formControlName="amlCheckStatus">
@@ -71,7 +71,15 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
@Input() entityName: string = ''; @Input() entityName: string = '';
/** Read-only mode — disables form editing until user clicks edit icon */ /** Read-only mode — disables form editing until user clicks edit icon */
@Input() readOnly = false; @Input() readOnly = false;
@Input() calculatedRisk: { category: number; reason: string } | null = null; @Input() calculatedRisk: {
category: number;
reason: string;
amlCheckStatus?: string;
bannedEntityMatch?: string;
willfulDefaulter?: string;
ecgcSalFlag?: string;
dormantFlag?: string;
} | null = null;
today = new Date(); today = new Date();
kycId: string = ''; kycId: string = '';
@@ -458,6 +466,7 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
kycDocExpiryDate: [''], kycDocExpiryDate: [''],
riskCategory: [1], riskCategory: [1],
allowRiskOverride: [false], allowRiskOverride: [false],
allowAmlOverride: [false],
remarks: [''], remarks: [''],
amlCheckStatus: [ amlCheckStatus: [
'CLEAR', 'CLEAR',
@@ -535,9 +544,35 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
} }
}); });
this.kycForm.get('allowAmlOverride')?.valueChanges.subscribe((allowed: boolean) => {
const amlControls = [
this.kycForm.get('amlCheckStatus'),
this.kycForm.get('bannedEntityMatch'),
this.kycForm.get('willfulDefaulter'),
this.kycForm.get('ecgcSalFlag'),
this.kycForm.get('dormantFlag')
];
if (allowed) {
if (!this.readOnly) {
amlControls.forEach((ctrl) => ctrl?.enable());
}
} else {
amlControls.forEach((ctrl) => ctrl?.disable());
this.applyAutoCalculatedAmlFlags();
}
});
if (!this.kycForm.get('allowRiskOverride')?.value) { if (!this.kycForm.get('allowRiskOverride')?.value) {
this.kycForm.get('riskCategory')?.disable(); this.kycForm.get('riskCategory')?.disable();
} }
if (!this.kycForm.get('allowAmlOverride')?.value) {
this.kycForm.get('amlCheckStatus')?.disable();
this.kycForm.get('bannedEntityMatch')?.disable();
this.kycForm.get('willfulDefaulter')?.disable();
this.kycForm.get('ecgcSalFlag')?.disable();
this.kycForm.get('dormantFlag')?.disable();
}
} }
/** Seed the document-type picker from defaults (entity RBI docs vs. individual OVDs, /** Seed the document-type picker from defaults (entity RBI docs vs. individual OVDs,
@@ -645,6 +680,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
if (!this.kycForm.get('allowRiskOverride')?.value) { if (!this.kycForm.get('allowRiskOverride')?.value) {
this.kycForm.get('riskCategory')?.disable({ emitEvent: false }); this.kycForm.get('riskCategory')?.disable({ emitEvent: false });
} }
if (!this.kycForm.get('allowAmlOverride')?.value) {
this.kycForm.get('amlCheckStatus')?.disable({ emitEvent: false });
this.kycForm.get('bannedEntityMatch')?.disable({ emitEvent: false });
this.kycForm.get('willfulDefaulter')?.disable({ emitEvent: false });
this.kycForm.get('ecgcSalFlag')?.disable({ emitEvent: false });
this.kycForm.get('dormantFlag')?.disable({ emitEvent: false });
}
} }
} }
if (changes['calculatedRisk']) { if (changes['calculatedRisk']) {
@@ -657,6 +699,11 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
riskControl.setValue(newRisk.category); riskControl.setValue(newRisk.category);
riskControl.markAsDirty(); riskControl.markAsDirty();
} }
const allowAmlOverride = this.kycForm.get('allowAmlOverride')?.value;
if (!allowAmlOverride) {
this.applyAutoCalculatedAmlFlags(newRisk);
}
} else { } else {
this.riskCalculationReason = ''; this.riskCalculationReason = '';
} }
@@ -871,6 +918,13 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
// Risk & Remarks // Risk & Remarks
riskCategory: data.riskCategory || 1, riskCategory: data.riskCategory || 1,
allowRiskOverride: this.calculatedRisk ? this.calculatedRisk.category !== (data.riskCategory || 1) : false, allowRiskOverride: this.calculatedRisk ? this.calculatedRisk.category !== (data.riskCategory || 1) : false,
allowAmlOverride: this.calculatedRisk
? this.calculatedRisk.amlCheckStatus !== (data.amlCheckStatus || 'CLEAR') ||
this.calculatedRisk.bannedEntityMatch !== (data.bannedEntityMatch || 'NO') ||
this.calculatedRisk.willfulDefaulter !== (data.willfulDefaulter || 'NO') ||
this.calculatedRisk.ecgcSalFlag !== (data.ecgcSalFlag || 'NO') ||
this.calculatedRisk.dormantFlag !== (data.dormantFlag || 'ACTIVE')
: false,
remarks: data.remarks || '', remarks: data.remarks || '',
hasPan: !!data.panNumber, hasPan: !!data.panNumber,
// AML & Compliance // AML & Compliance
@@ -1805,6 +1859,21 @@ export class ClientKycStepComponent implements OnInit, OnChanges, OnDestroy {
v.form15Applicable v.form15Applicable
); );
} }
private applyAutoCalculatedAmlFlags(risk = this.calculatedRisk): void {
if (!risk) return;
this.kycForm.get('amlCheckStatus')?.setValue(risk.amlCheckStatus || 'CLEAR');
this.kycForm.get('bannedEntityMatch')?.setValue(risk.bannedEntityMatch || 'NO');
this.kycForm.get('willfulDefaulter')?.setValue(risk.willfulDefaulter || 'NO');
this.kycForm.get('ecgcSalFlag')?.setValue(risk.ecgcSalFlag || 'NO');
this.kycForm.get('dormantFlag')?.setValue(risk.dormantFlag || 'ACTIVE');
this.kycForm.get('amlCheckStatus')?.markAsDirty();
this.kycForm.get('bannedEntityMatch')?.markAsDirty();
this.kycForm.get('willfulDefaulter')?.markAsDirty();
this.kycForm.get('ecgcSalFlag')?.markAsDirty();
this.kycForm.get('dormantFlag')?.markAsDirty();
}
} }
/** /**
@@ -392,6 +392,22 @@ export class AuthenticationService {
return roleNames.some((n) => n === 'clerk'); return roleNames.some((n) => n === 'clerk');
} }
/** Returns true when the logged-in user has the "Principal Officer" role (STR conclusion/filing authority). */
isPrincipalOfficerUser(): boolean {
const creds = this.getCredentials();
if (!creds || !creds.roles) return false;
const roleNames: string[] = (creds.roles as any[]).map((r) => (r.name || r.role || '').toLowerCase().trim());
return roleNames.some((n) => n === 'principal officer');
}
/** Returns true when the logged-in user has the "AML Officer" role (first-level alert review). */
isAmlOfficerUser(): boolean {
const creds = this.getCredentials();
if (!creds || !creds.roles) return false;
const roleNames: string[] = (creds.roles as any[]).map((r) => (r.name || r.role || '').toLowerCase().trim());
return roleNames.some((n) => n === 'aml officer');
}
/** /**
* Returns true ONLY for regular cashier role (not Head Cashier). * Returns true ONLY for regular cashier role (not Head Cashier).
* Regular cashier → full-screen transaction entry, no nav. * Regular cashier → full-screen transaction entry, no nav.
@@ -424,6 +440,18 @@ export class AuthenticationService {
return roleNames.some((n) => n === 'cashier'); return roleNames.some((n) => n === 'cashier');
} }
/**
* Returns true when the logged-in user belongs to the Head Office.
* Fineract seeds Head Office as officeId 1 with hierarchy "."; office name is
* checked too since some deployments rename/reseed the root office.
*/
isHeadOfficeUser(): boolean {
const creds = this.getCredentials();
if (!creds) return false;
if (creds.officeId === 1) return true;
return (creds.officeName || '').toLowerCase().includes('head office');
}
/** /**
* Sets the user credentials. * Sets the user credentials.
* *
+64 -21
View File
@@ -653,6 +653,7 @@
<a <a
class="gl-link" class="gl-link"
[class.hidden]="!matchesSubLedgerSearch('Cash / Clearing / Transfer')" [class.hidden]="!matchesSubLedgerSearch('Cash / Clearing / Transfer')"
routerLink="/cashier/cash-clearing-transfer-summary"
(click)="subLedgerOpen = false" (click)="subLedgerOpen = false"
><fa-icon icon="money-bill" class="gl-icon"></fa-icon> Cash / Clearing / Transfer</a ><fa-icon icon="money-bill" class="gl-icon"></fa-icon> Cash / Clearing / Transfer</a
> >
@@ -701,8 +702,11 @@
</div> </div>
<div class="gl-group"> <div class="gl-group">
<div class="gl-group-title">Deposits</div> <div class="gl-group-title">Deposits</div>
<a class="gl-link" [class.hidden]="!matchesSubLedgerSearch('FD / RD Entry')" (click)="subLedgerOpen = false" <a
><fa-icon icon="piggy-bank" class="gl-icon"></fa-icon> FD / RD Entry</a class="gl-link"
[class.hidden]="!matchesSubLedgerSearch('FD / RD Entry')"
(click)="subLedgerOpen = false; openFdRdEntryDialog()"
><fa-icon icon="piggy-bank" class="gl-icon" style="color: #0f766e"></fa-icon> FD / RD Entry</a
> >
<a class="gl-link" [class.hidden]="!matchesSubLedgerSearch('DDS Entry')" (click)="subLedgerOpen = false" <a class="gl-link" [class.hidden]="!matchesSubLedgerSearch('DDS Entry')" (click)="subLedgerOpen = false"
><fa-icon icon="coins" class="gl-icon"></fa-icon> DDS Entry</a ><fa-icon icon="coins" class="gl-icon"></fa-icon> DDS Entry</a
@@ -808,8 +812,11 @@
<div class="gl-columns"> <div class="gl-columns">
<div class="gl-group"> <div class="gl-group">
<div class="gl-group-title">Financial Statements</div> <div class="gl-group-title">Financial Statements</div>
<a class="gl-link" [class.hidden]="!matchesGlSearch('Trial Balance')" (click)="glMenuOpen = false" <a
><fa-icon icon="balance-scale" class="gl-icon"></fa-icon> Trial Balance</a class="gl-link"
[class.hidden]="!matchesGlSearch('Trial Balance')"
(click)="glMenuOpen = false; openAccountingReportsDialog('GL-DR-CR')"
><fa-icon icon="balance-scale" class="gl-icon" style="color: #b45309"></fa-icon> Trial Balance</a
> >
<a <a
class="gl-link" class="gl-link"
@@ -864,8 +871,12 @@
(click)="glMenuOpen = false" (click)="glMenuOpen = false"
><fa-icon icon="users" class="gl-icon"></fa-icon> Client wise Balance Report</a ><fa-icon icon="users" class="gl-icon"></fa-icon> Client wise Balance Report</a
> >
<a class="gl-link" [class.hidden]="!matchesGlSearch('Contingent Expense')" (click)="glMenuOpen = false" <a
><fa-icon icon="exclamation-circle" class="gl-icon"></fa-icon> Contingent Expense</a class="gl-link"
[class.hidden]="!matchesGlSearch('Contingent Expense')"
(click)="glMenuOpen = false; openAccountingReportsDialog('CONTINGENT-LIAB')"
><fa-icon icon="exclamation-circle" class="gl-icon" style="color: #b45309"></fa-icon> Contingent
Expense</a
> >
<a class="gl-link" [class.hidden]="!matchesGlSearch('Banker Head Report')" (click)="glMenuOpen = false" <a class="gl-link" [class.hidden]="!matchesGlSearch('Banker Head Report')" (click)="glMenuOpen = false"
><fa-icon icon="university" class="gl-icon"></fa-icon> Banker Head Report</a ><fa-icon icon="university" class="gl-icon"></fa-icon> Banker Head Report</a
@@ -1167,10 +1178,6 @@
<button mat-menu-item tabindex="0"> <button mat-menu-item tabindex="0">
<fa-icon icon="chart-bar" class="menu-item-icon"></fa-icon> Depreciation Register <fa-icon icon="chart-bar" class="menu-item-icon"></fa-icon> Depreciation Register
</button> </button>
<button mat-menu-item routerLink="/fiu" tabindex="0">
<fa-icon icon="shield-alt" class="menu-item-icon" style="color: #dc2626"></fa-icon> Financial Intelligence
Unit (FIU-IND)
</button>
</div> </div>
<!-- Investment --> <!-- Investment -->
<div class="deposit-menu-group"> <div class="deposit-menu-group">
@@ -1571,7 +1578,8 @@
<a <a
class="gl-link" class="gl-link"
[class.hidden]="!matchesRbiSearch('CTR / STR')" [class.hidden]="!matchesRbiSearch('CTR / STR')"
(click)="rbiMenuOpen = false; openCtrStrDialog()" routerLink="/fiu"
(click)="rbiMenuOpen = false"
> >
<fa-icon icon="calculator" class="gl-icon"></fa-icon> CTR / STR <fa-icon icon="calculator" class="gl-icon"></fa-icon> CTR / STR
</a> </a>
@@ -1668,15 +1676,24 @@
> >
<fa-icon icon="bell" class="gl-icon"></fa-icon> Daily Position Notice <fa-icon icon="bell" class="gl-icon"></fa-icon> Daily Position Notice
</a> </a>
<a class="gl-link" [class.hidden]="!matchesRbiSearch('R/SLR')" (click)="rbiMenuOpen = false"> <a
<fa-icon icon="lock" class="gl-icon"></fa-icon> R/SLR class="gl-link"
[class.hidden]="!matchesRbiSearch('AML')"
routerLink="/rbi/aml"
(click)="rbiMenuOpen = false"
>
<fa-icon icon="shield-alt" class="gl-icon"></fa-icon> AML
</a> </a>
</div> </div>
<!-- OSS Reports --> <!-- OSS Reports -->
<div class="gl-group"> <div class="gl-group">
<div class="gl-group-title">OSS Reports</div> <div class="gl-group-title">OSS Reports</div>
<a class="gl-link" [class.hidden]="!matchesRbiSearch('OSS 1 XBRL/CIMS')" (click)="rbiMenuOpen = false"> <a
<fa-icon icon="file-alt" class="gl-icon"></fa-icon> OSS 1 XBRL/CIMS class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 1 XBRL/CIMS')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('RAL')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 1 XBRL/CIMS
</a> </a>
<a <a
class="gl-link" class="gl-link"
@@ -1685,14 +1702,40 @@
> >
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 2 XBRL <fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 2 XBRL
</a> </a>
<a class="gl-link" [class.hidden]="!matchesRbiSearch('OSS 3 XBRL/CIMS')" (click)="rbiMenuOpen = false"> <a
<fa-icon icon="file-alt" class="gl-icon"></fa-icon> OSS 3 XBRL/CIMS class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 3 XBRL/CIMS')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('OSS-2')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 3 XBRL/CIMS
</a> </a>
<a class="gl-link" [class.hidden]="!matchesRbiSearch('OSS 4/OSS 5/OSS 6')" (click)="rbiMenuOpen = false"> <a
<fa-icon icon="file-alt" class="gl-icon"></fa-icon> OSS 4 / OSS 5 / OSS 6 class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 4')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('OSS-4')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 4
</a> </a>
<a class="gl-link" [class.hidden]="!matchesRbiSearch('OSS 7')" (click)="rbiMenuOpen = false"> <a
<fa-icon icon="file-alt" class="gl-icon"></fa-icon> OSS 7 class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 5')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('OSS-5')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 5
</a>
<a
class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 6')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('OSS-6')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 6
</a>
<a
class="gl-link"
[class.hidden]="!matchesRbiSearch('OSS 7')"
(click)="rbiMenuOpen = false; openCimsXbrlDialog('OSS-7')"
>
<fa-icon icon="file-alt" class="gl-icon" style="color: #0891b2"></fa-icon> OSS 7
</a> </a>
</div> </div>
<!-- CIMS / XBRL Returns --> <!-- CIMS / XBRL Returns -->
+21 -19
View File
@@ -242,7 +242,7 @@ export class ShellComponent implements OnInit, OnDestroy {
* GL Wise DR/CR / Misc Expense Ledger). Initial report selected via menu link. * GL Wise DR/CR / Misc Expense Ledger). Initial report selected via menu link.
*/ */
async openAccountingReportsDialog( async openAccountingReportsDialog(
initialReport: 'PNL' | 'BALANCE-SHEET' | 'COMPARATIVE-BS' | 'GL-DR-CR' | 'MISC-EXP' = 'PNL' initialReport: 'PNL' | 'BALANCE-SHEET' | 'COMPARATIVE-BS' | 'CONTINGENT-LIAB' | 'GL-DR-CR' | 'MISC-EXP' = 'PNL'
): Promise<void> { ): Promise<void> {
const { AccountingReportsDialogComponent } = const { AccountingReportsDialogComponent } =
await import('../../home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component'); await import('../../home/dashboard/accounting-reports-dialog/accounting-reports-dialog.component');
@@ -280,29 +280,14 @@ export class ShellComponent implements OnInit, OnDestroy {
}); });
} }
/**
* Opens the CTR & STR Compliance Center dialog.
*/
async openCtrStrDialog(): Promise<void> {
const { CtrStrDialogComponent } = await import('../../home/dashboard/ctr-str-dialog/ctr-str-dialog.component');
this.dialog.open(CtrStrDialogComponent, {
width: '95vw',
maxWidth: '1280px',
maxHeight: '92vh',
panelClass: 'crr-slr-dialog-panel',
autoFocus: false,
disableClose: false,
hasBackdrop: true,
backdropClass: 'ckyc-rpt-dlg-backdrop'
});
}
/** /**
* Opens the CIMS / XBRL Returns dialog. The initial form (OSS-2, PSA, RAL, RCP-UCB) * Opens the CIMS / XBRL Returns dialog. The initial form (OSS-2, PSA, RAL, RCP-UCB)
* is selected based on the menu link the user clicked; user can switch between * is selected based on the menu link the user clicked; user can switch between
* forms via the form-pill selector inside the dialog. * forms via the form-pill selector inside the dialog.
*/ */
async openCimsXbrlDialog(initialForm: 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB' = 'OSS-2'): Promise<void> { async openCimsXbrlDialog(
initialForm: 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB' | 'OSS-4' | 'OSS-5' | 'OSS-6' | 'OSS-7' = 'OSS-2'
): Promise<void> {
const { CimsXbrlDialogComponent } = const { CimsXbrlDialogComponent } =
await import('../../home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component'); await import('../../home/dashboard/cims-xbrl-dialog/cims-xbrl-dialog.component');
this.dialog.open(CimsXbrlDialogComponent, { this.dialog.open(CimsXbrlDialogComponent, {
@@ -318,6 +303,23 @@ export class ShellComponent implements OnInit, OnDestroy {
}); });
} }
/** Opens the FD / RD Entry dialog under Sub Ledger → Deposits. */
async openFdRdEntryDialog(): Promise<void> {
const { FdRdEntryDialogComponent } =
await import('../../home/dashboard/fd-rd-entry-dialog/fd-rd-entry-dialog.component');
this.dialog.open(FdRdEntryDialogComponent, {
width: '100vw',
height: '100vh',
maxWidth: '100vw',
maxHeight: '100vh',
panelClass: ['crr-slr-dialog-panel', 'fdrd-fullscreen-panel'],
autoFocus: false,
disableClose: false,
hasBackdrop: true,
backdropClass: 'fdrd-dlg-backdrop'
});
}
/** /**
* Opens the Asset Liability Management dialog (Form ALM-1). * Opens the Asset Liability Management dialog (Form ALM-1).
* Covers maturity ladder, monthly snapshot, and structural liquidity returns. * Covers maturity ladder, monthly snapshot, and structural liquidity returns.
@@ -266,7 +266,7 @@
<a <a
class="subledger-link" class="subledger-link"
[class.hidden]="!matchesSearch('Cash / Clearing / Transfer')" [class.hidden]="!matchesSearch('Cash / Clearing / Transfer')"
(click)="subLedgerOpen = false" (click)="navigateMenu('/cashier/cash-clearing-transfer-summary'); subLedgerOpen = false"
> >
<fa-icon icon="money-bill" class="subledger-icon"></fa-icon> Cash / Clearing / Transfer <fa-icon icon="money-bill" class="subledger-icon"></fa-icon> Cash / Clearing / Transfer
</a> </a>
+3 -3
View File
@@ -81,7 +81,7 @@ export interface DeafEligibleAccount {
/** Form I — Dec 2025 cycle */ /** Form I — Dec 2025 cycle */
export const FORM_I: DeafForm = { export const FORM_I: DeafForm = {
bankName: 'Bharat Urban Bank Ltd, Mumbai Main Branch', bankName: 'CBSNexora Bank Ltd, Mumbai Main Branch',
bankDeafCode: '1100', bankDeafCode: '1100',
remittedThroughSponsor: 'Y', remittedThroughSponsor: 'Y',
sponsorBankName: 'State Bank of India', sponsorBankName: 'State Bank of India',
@@ -170,7 +170,7 @@ export const FORM_I: DeafForm = {
/** Form II — Jan 2026 (head office consolidated) */ /** Form II — Jan 2026 (head office consolidated) */
export const FORM_II: DeafForm = { export const FORM_II: DeafForm = {
bankName: 'Bharat Urban Bank Ltd, Head Office', bankName: 'CBSNexora Bank Ltd, Head Office',
bankDeafCode: '1100', bankDeafCode: '1100',
remittedThroughSponsor: 'Y', remittedThroughSponsor: 'Y',
sponsorBankName: 'State Bank of India', sponsorBankName: 'State Bank of India',
@@ -271,7 +271,7 @@ export const REFUND_CLAIMS: {
auditor: string; auditor: string;
rows: DeafClaim[]; rows: DeafClaim[];
} = { } = {
bankName: 'Bharat Urban Bank Ltd', bankName: 'CBSNexora Bank Ltd',
bankDeafCode: '1100', bankDeafCode: '1100',
currentAcWithRbi: '03010200099001', currentAcWithRbi: '03010200099001',
sponsorBank: 'State Bank of India', sponsorBank: 'State Bank of India',
+1 -1
View File
@@ -53,7 +53,7 @@ export interface SahayDepositor {
} }
export const DICGC_BANK = { export const DICGC_BANK = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
reportAsOn: '29-01-2026', reportAsOn: '29-01-2026',
printDate: '29-01-2026 04:19 PM', printDate: '29-01-2026 04:19 PM',
+2 -2
View File
@@ -114,13 +114,13 @@ export interface KycEntity {
} }
export const FIU_BANK = { export const FIU_BANK = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
reBankingFlag: 'SCB', reBankingFlag: 'SCB',
reportingMonth: 'January 2026', reportingMonth: 'January 2026',
reBuid: 'REBANK-2031', reBuid: 'REBANK-2031',
principalOfficer: 'Karthik Kamath', principalOfficer: 'Karthik Kamath',
principalOfficerEmail: 'po.fiu@bharaturbanbank.example', principalOfficerEmail: 'po.fiu@cbsnexorabank.example',
filerUserId: 'PO-001', filerUserId: 'PO-001',
cashThreshold: 1000000, // ₹10 L per single cash txn cashThreshold: 1000000, // ₹10 L per single cash txn
monthlyAggThreshold: 1000000, // aggregate ≥ ₹10 L in a month monthlyAggThreshold: 1000000, // aggregate ≥ ₹10 L in a month
+221 -2
View File
@@ -129,9 +129,130 @@
<!-- CTR --> <!-- CTR -->
@if (activeTab === 'ctr') { @if (activeTab === 'ctr') {
<div class="fiu-section"> <div class="fiu-section">
<!-- LIVE: cash-related AML alerts awaiting AML Officer review -->
<div class="fiu-form-meta"> <div class="fiu-form-meta">
<div class="fiu-form-title"> <div class="fiu-form-title">
<h3>CTR — Cash Transaction Report</h3> <h3>Live Cash Alerts</h3>
<p>
Raised by the transaction-monitoring rule engine — review and whitelist or escalate to Principal Officer
</p>
</div>
<select class="fiu-inp" [(ngModel)]="amlAlertStatusFilter" (ngModelChange)="loadAmlAlerts()">
<option value="">All statuses</option>
<option value="RAISED">Raised</option>
<option value="UNDER_REVIEW">Under Review</option>
<option value="WHITELISTED">Whitelisted</option>
<option value="ESCALATED">Escalated</option>
</select>
</div>
<div class="fiu-tbl-wrap">
<table class="fiu-tbl">
<thead>
<tr>
<th>ID</th>
<th>Client</th>
<th>Type</th>
<th>Rule</th>
<th class="ta-r">Amount</th>
<th>Raised On</th>
<th>Status</th>
<th>Notes</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@for (alert of amlAlerts; track alert.id) {
<tr>
<td>{{ alert.id }}</td>
<td>{{ alert.clientDisplayName }}</td>
<td>{{ alert.transactionType }}</td>
<td>{{ alert.ruleCode }} &mdash; {{ alert.ruleDescription }}</td>
<td class="ta-r mono">&#8377;{{ alert.amount | number: '1.0-0' }}</td>
<td>{{ alert.raisedOn | date: 'medium' }}</td>
<td>
<span [class]="amlStatusClass(alert.status)">{{ alert.status }}</span>
</td>
<td>
@if (alert.status === 'RAISED' || alert.status === 'UNDER_REVIEW') {
<input
type="text"
class="fiu-inp"
placeholder="Review notes"
[(ngModel)]="amlReviewNotes[alert.id]"
/>
} @else {
<span class="muted">{{ alert.reviewNotes }}</span>
}
</td>
<td>
@if (alert.status === 'RAISED' || alert.status === 'UNDER_REVIEW') {
<div class="fiu-action-group" *mifosxHasPermission="'REVIEW_AML_ALERT'">
<button class="fiu-btn fiu-btn-g" (click)="whitelistAlert(alert)">Whitelist</button>
<button class="fiu-btn fiu-btn-xml" (click)="escalateAlert(alert)">Escalate</button>
</div>
}
</td>
</tr>
} @empty {
<tr>
<td colspan="9" class="fiu-empty">No live alerts.</td>
</tr>
}
</tbody>
</table>
</div>
<!-- LIVE: monthly CTR reports (mechanical, threshold-based) -->
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>Monthly CTR Reports</h3>
<p>
Threshold: <strong>&#8377;{{ bank.cashThreshold | number }}</strong> per single txn / connected aggregate
per month
</p>
</div>
<div class="fiu-action-group">
<input class="fiu-inp" type="month" [(ngModel)]="ctrGenerateMonth" />
<button class="fiu-btn fiu-btn-g" *mifosxHasPermission="'REVIEW_AML_ALERT'" (click)="generateCtrReport()">
Generate
</button>
</div>
</div>
<div class="fiu-tbl-wrap">
<table class="fiu-tbl">
<thead>
<tr>
<th>Month</th>
<th>Generated On</th>
<th class="ta-c">Records</th>
<th>Status</th>
<th>Submitted On</th>
</tr>
</thead>
<tbody>
@for (r of amlCtrReports; track r.id) {
<tr>
<td>{{ r.reportMonth }}</td>
<td>{{ r.generatedOn | date: 'medium' }}</td>
<td class="ta-c">{{ r.recordCount }}</td>
<td>
<span [class]="amlStatusClass(r.status)">{{ r.status }}</span>
</td>
<td>{{ r.submittedOn ? (r.submittedOn | date: 'medium') : '—' }}</td>
</tr>
} @empty {
<tr>
<td colspan="5" class="fiu-empty">No CTR reports generated yet.</td>
</tr>
}
</tbody>
</table>
</div>
<!-- FINnet export template (separate concern — TC1/TC2 file-format generation) -->
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>FINnet TC2 Export Template</h3>
<p> <p>
All cash transactions ≥ &#8377;{{ bank.cashThreshold | number }} (single or aggregate per month per account) All cash transactions ≥ &#8377;{{ bank.cashThreshold | number }} (single or aggregate per month per account)
</p> </p>
@@ -221,9 +342,107 @@
<!-- STR --> <!-- STR -->
@if (activeTab === 'str') { @if (activeTab === 'str') {
<div class="fiu-section"> <div class="fiu-section">
<!-- LIVE: STR cases with full Principal Officer actions -->
<div class="fiu-form-meta"> <div class="fiu-form-meta">
<div class="fiu-form-title"> <div class="fiu-form-title">
<h3>STR — Suspicious Transaction Report</h3> <h3>Live STR Cases</h3>
<p>
Escalated by an AML Officer; Principal Officer investigates, concludes and files with FIU-IND within 7
working days
</p>
</div>
</div>
<div class="fiu-tbl-wrap">
<table class="fiu-tbl">
<thead>
<tr>
<th>ID</th>
<th>Client</th>
<th class="ta-c">Cumulative Cases</th>
<th>Assigned PO</th>
<th>Conclusion</th>
<th>FIU Due Date</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@for (sc of amlStrCases; track sc.id) {
<tr [class.selected-row]="selectedStrCase?.id === sc.id">
<td>{{ sc.id }}</td>
<td>{{ sc.clientDisplayName }}</td>
<td class="ta-c" [class.escalation-warning]="sc.cumulativeStrCaseCountForClient >= 3">
{{ sc.cumulativeStrCaseCountForClient }}
</td>
<td>{{ sc.assignedPoUsername || '—' }}</td>
<td>{{ sc.conclusion || '—' }}</td>
<td>{{ sc.fiuDueDate || '—' }}</td>
<td>
<span [class]="amlStatusClass(sc.status)">{{ sc.status }}</span>
</td>
<td><button class="fiu-btn fiu-btn-g" (click)="selectStrCase(sc)">Investigate</button></td>
</tr>
} @empty {
<tr>
<td colspan="8" class="fiu-empty">No STR cases yet.</td>
</tr>
}
</tbody>
</table>
</div>
@if (selectedStrCase) {
<div class="str-case-detail">
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>STR Case #{{ selectedStrCase.id }} — {{ selectedStrCase.clientDisplayName }}</h3>
<p>
Alert #{{ selectedStrCase.alertId }} &middot;
{{ selectedStrCase.cumulativeStrCaseCountForClient }} cumulative STR case(s) for this client
@if (selectedStrCase.cumulativeStrCaseCountForClient >= 3) {
<span class="escalation-badge">Risk category auto-escalated</span>
}
</p>
</div>
<button class="fiu-btn fiu-btn-g" (click)="closeStrCaseDetail()">Close</button>
</div>
<div class="case-form" *mifosxHasPermission="'CONCLUDE_AML_STR'">
<div class="form-row">
<label for="fiuInvestigationNotes">Investigation notes (source-of-transaction history reviewed)</label>
<textarea id="fiuInvestigationNotes" rows="3" [(ngModel)]="concludeInvestigationNotes"></textarea>
</div>
<div class="form-row">
<label for="fiuSourceOfFundsNotes">Source-of-funds notes</label>
<textarea id="fiuSourceOfFundsNotes" rows="2" [(ngModel)]="concludeSourceOfFundsNotes"></textarea>
</div>
<div class="form-row conclusion-row">
<label>
<input type="radio" name="fiuConclusion" value="NOT_SUSPICIOUS" [(ngModel)]="concludeConclusion" />
Not suspicious (whitelist)
</label>
<label>
<input type="radio" name="fiuConclusion" value="SUSPICIOUS" [(ngModel)]="concludeConclusion" />
Suspicious (file STR)
</label>
</div>
<button class="fiu-btn fiu-btn-xml" (click)="concludeStrCase()">Conclude</button>
@if (selectedStrCase.status === 'CONCLUDED_SUSPICIOUS') {
<div class="file-str-row">
<label for="fiuFileReferenceNo">FIU-IND acknowledgement / reference number</label>
<input id="fiuFileReferenceNo" class="fiu-inp" type="text" [(ngModel)]="fileReferenceNo" />
<button class="fiu-btn fiu-btn-xml" (click)="fileStrCase()">Mark STR Filed</button>
</div>
}
</div>
</div>
}
<!-- FINnet export template (separate concern — STR XML file-format generation) -->
<div class="fiu-form-meta">
<div class="fiu-form-title">
<h3>FINnet STR XML Export Template</h3>
<p>Detected by AML rules engine + branch compliance officers; filed within 7 days</p> <p>Detected by AML rules engine + branch compliance officers; filed within 7 days</p>
</div> </div>
<button class="fiu-btn fiu-btn-xml" (click)="exportStrXml()"> <button class="fiu-btn fiu-btn-xml" (click)="exportStrXml()">
+117
View File
@@ -579,6 +579,41 @@
background: var(--f-red-l); background: var(--f-red-l);
color: var(--f-red); color: var(--f-red);
} }
&.status-raised {
background: var(--f-amber-l);
color: var(--f-amber);
}
&.status-under-review {
background: var(--f-blue-l);
color: var(--f-blue);
}
&.status-whitelisted,
&.status-concluded-clear,
&.status-generated,
&.status-submitted {
background: var(--f-green-l);
color: var(--f-green);
}
&.status-escalated,
&.status-open,
&.status-concluded-suspicious {
background: var(--f-red-l);
color: var(--f-red);
}
&.status-filed {
background: var(--f-purple-l);
color: var(--f-purple);
}
&.status-draft {
background: var(--f-g100);
color: var(--f-g500);
}
} }
.risk-pill { .risk-pill {
@@ -892,3 +927,85 @@
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
} }
.selected-row {
background: var(--f-blue-l);
}
.escalation-warning {
color: var(--f-red);
font-weight: 700;
}
.escalation-badge {
display: inline-block;
margin-left: 8px;
padding: 2px 8px;
border-radius: 10px;
background: var(--f-amber-l);
color: var(--f-amber);
font-size: 11px;
font-weight: 600;
}
.str-case-detail {
border: 1px solid var(--f-g200);
border-radius: 10px;
padding: 16px;
background: #fff;
margin-bottom: 16px;
}
.case-form {
display: flex;
flex-direction: column;
gap: 12px;
textarea {
width: 100%;
padding: 8px 10px;
border-radius: 6px;
border: 1px solid var(--f-g300);
font-family: inherit;
font-size: 13px;
}
}
.form-row {
display: flex;
flex-direction: column;
gap: 4px;
label {
font-size: 12px;
font-weight: 600;
color: var(--f-g500);
}
}
.conclusion-row {
flex-direction: row;
gap: 24px;
label {
display: flex;
align-items: center;
gap: 6px;
font-weight: 500;
}
}
.file-str-row {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
padding-top: 12px;
border-top: 1px dashed var(--f-g300);
label {
font-size: 12px;
font-weight: 600;
color: var(--f-g500);
}
}
+126 -2
View File
@@ -6,10 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
import { Component } from '@angular/core'; import { Component, OnInit, inject } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { AmlService } from 'app/aml/aml.service';
import { AuthenticationService } from 'app/core/authentication/authentication.service';
import { import {
CtrTransaction, CtrTransaction,
CTR_TXNS, CTR_TXNS,
@@ -33,7 +35,9 @@ import {
FormsModule FormsModule
] ]
}) })
export class FiuComponent { export class FiuComponent implements OnInit {
private amlService = inject(AmlService);
private authenticationService = inject(AuthenticationService);
/** Active tab */ /** Active tab */
activeTab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files' = 'dashboard'; activeTab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files' = 'dashboard';
@@ -57,6 +61,126 @@ export class FiuComponent {
toastVisible = false; toastVisible = false;
private toastTimer: any; private toastTimer: any;
// -------------------- Live AML monitoring (real backend, replaces mock CTR/STR tables) --------------------
amlAlerts: any[] = [];
amlAlertStatusFilter = '';
amlReviewNotes: Record<number, string> = {};
amlCtrReports: any[] = [];
ctrGenerateMonth = '';
amlStrCases: any[] = [];
selectedStrCase: any = null;
concludeConclusion: 'NOT_SUSPICIOUS' | 'SUSPICIOUS' = 'NOT_SUSPICIOUS';
concludeInvestigationNotes = '';
concludeSourceOfFundsNotes = '';
fileReferenceNo = '';
get isPrincipalOfficer(): boolean {
return this.authenticationService.isPrincipalOfficerUser();
}
get isAmlOfficer(): boolean {
return this.authenticationService.isAmlOfficerUser();
}
ngOnInit(): void {
this.loadAmlAlerts();
this.loadAmlCtrReports();
this.loadAmlStrCases();
}
loadAmlAlerts(): void {
this.amlService
.getAlerts(this.amlAlertStatusFilter || undefined)
.subscribe((data) => (this.amlAlerts = data || []));
}
loadAmlCtrReports(): void {
this.amlService.getCtrReports().subscribe((data) => (this.amlCtrReports = data || []));
}
loadAmlStrCases(): void {
this.amlService.getStrCases().subscribe((data) => (this.amlStrCases = data || []));
}
whitelistAlert(alert: any): void {
this.amlService.reviewAlert(alert.id, 'WHITELIST', this.amlReviewNotes[alert.id] || '').subscribe(() => {
this.toast(`Alert #${alert.id} whitelisted`);
this.loadAmlAlerts();
});
}
escalateAlert(alert: any): void {
this.amlService.reviewAlert(alert.id, 'ESCALATE', this.amlReviewNotes[alert.id] || '').subscribe(() => {
this.toast(`Alert #${alert.id} escalated to Principal Officer`);
this.loadAmlAlerts();
this.loadAmlStrCases();
});
}
generateCtrReport(): void {
if (!this.ctrGenerateMonth) {
return;
}
this.amlService.generateCtrReport(this.ctrGenerateMonth).subscribe({
next: () => {
this.toast(`CTR report generated for ${this.ctrGenerateMonth}`);
this.loadAmlCtrReports();
},
error: (err) => this.toast(err?.error?.defaultUserMessage || 'CTR report generation failed')
});
}
selectStrCase(strCase: any): void {
this.selectedStrCase = strCase;
this.concludeConclusion = 'NOT_SUSPICIOUS';
this.concludeInvestigationNotes = strCase.investigationNotes || '';
this.concludeSourceOfFundsNotes = strCase.sourceOfFundsNotes || '';
this.fileReferenceNo = '';
}
closeStrCaseDetail(): void {
this.selectedStrCase = null;
}
concludeStrCase(): void {
if (!this.selectedStrCase) {
return;
}
this.amlService
.concludeStrCase(
this.selectedStrCase.id,
this.concludeConclusion,
this.concludeInvestigationNotes,
this.concludeSourceOfFundsNotes
)
.subscribe((updated) => {
this.toast(
this.concludeConclusion === 'SUSPICIOUS'
? `STR case #${updated.id} concluded suspicious — FIU due date ${updated.fiuDueDate}`
: `STR case #${updated.id} concluded not suspicious`
);
this.selectedStrCase = updated;
this.loadAmlStrCases();
});
}
fileStrCase(): void {
if (!this.selectedStrCase || !this.fileReferenceNo) {
return;
}
this.amlService.fileStr(this.selectedStrCase.id, this.fileReferenceNo).subscribe((updated) => {
this.toast(`STR case #${updated.id} recorded as filed (ref ${updated.strReferenceNo})`);
this.selectedStrCase = updated;
this.loadAmlStrCases();
});
}
amlStatusClass(status: string): string {
return 'status-pill status-' + (status || '').toLowerCase().replace(/_/g, '-');
}
showTab(tab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files'): void { showTab(tab: 'dashboard' | 'ctr' | 'str' | 'customers' | 'files'): void {
this.activeTab = tab; this.activeTab = tab;
} }
@@ -397,6 +397,64 @@
</div> </div>
</ng-container> </ng-container>
<!-- ════════════ CONTINGENT LIABILITIES ════════════ -->
<ng-container *ngIf="activeReport === 'CONTINGENT-LIAB'">
<div class="acr-stats">
<div class="acr-stat-card alert">
<div class="acr-stat-l">Total Contingent Liabilities — {{ bank.fiscalYear }}</div>
<div class="acr-stat-v">&#8377; {{ contingentLiab.current | number: '1.2-2' }} Lakh</div>
<div class="acr-stat-meta">vs {{ contingentLiab.previous | number: '1.2-2' }} Lakh (PY)</div>
</div>
<div class="acr-stat-card" [ngClass]="clVariation <= 0 ? 'good' : 'alert'">
<div class="acr-stat-l">Variation over Previous Year</div>
<div class="acr-stat-v">{{ clVariation >= 0 ? '+' : '' }}{{ clVariation | number: '1.2-2' }} Lakh</div>
<div class="acr-stat-meta">{{ clVariationPct >= 0 ? '+' : '' }}{{ clVariationPct.toFixed(2) }}%</div>
</div>
<div class="acr-stat-card">
<div class="acr-stat-l">As On Date</div>
<div class="acr-stat-v" style="font-size: 16px">{{ bank.asOnDate }}</div>
<div class="acr-stat-meta">Schedule 12</div>
</div>
</div>
<div class="acr-section">
<div class="acr-sec-h">
<h3 class="acr-sec-t">Contingent Liabilities &mdash; As on {{ bank.asOnDate }} (₹ in Lakhs)</h3>
<button mat-stroked-button class="acr-btn-xls" (click)="exportContingentLiabCsv()">
<fa-icon icon="file-csv"></fa-icon>&nbsp; Export CSV
</button>
</div>
<div class="acr-tbl-wrap">
<table class="acr-tbl">
<thead>
<tr>
<th colspan="2" class="acr-sub-h">Items Covered Under This Schedule</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of contingentLiabItems">
<td class="mono">{{ item.code }}</td>
<td>{{ item.label }}</td>
</tr>
</tbody>
<tfoot>
<tr class="row-total expense">
<td colspan="2">
<strong>Total Contingent Liabilities: &#8377; {{ contingentLiab.current | number: '1.2-2' }} Lakh</strong>
</td>
</tr>
</tfoot>
</table>
</div>
<p class="acr-note">
<fa-icon icon="info-circle"></fa-icon>&nbsp; Individual item-wise figures for this schedule are not broken
out here — only the audited total is shown, to avoid presenting a sub-item split that could not be
independently verified against the source statement.
</p>
</div>
</ng-container>
<!-- ════════════ GL DR/CR ════════════ --> <!-- ════════════ GL DR/CR ════════════ -->
<ng-container *ngIf="activeReport === 'GL-DR-CR'"> <ng-container *ngIf="activeReport === 'GL-DR-CR'">
<div class="acr-stats"> <div class="acr-stats">
@@ -318,6 +318,16 @@
border-bottom: 1px dashed var(--acr-g200); border-bottom: 1px dashed var(--acr-g200);
} }
.acr-note {
margin: 12px 0 0;
font-size: 12px;
font-style: italic;
color: var(--acr-g500);
display: flex;
align-items: flex-start;
gap: 6px;
}
.acr-btn-xls { .acr-btn-xls {
font-size: 12px !important; font-size: 12px !important;
height: 34px !important; height: 34px !important;
@@ -14,7 +14,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
export type AcctReportCode = 'PNL' | 'BALANCE-SHEET' | 'COMPARATIVE-BS' | 'GL-DR-CR' | 'MISC-EXP'; export type AcctReportCode = 'PNL' | 'BALANCE-SHEET' | 'COMPARATIVE-BS' | 'CONTINGENT-LIAB' | 'GL-DR-CR' | 'MISC-EXP';
export interface AcctReportsDialogData { export interface AcctReportsDialogData {
initialReport?: AcctReportCode; initialReport?: AcctReportCode;
@@ -47,6 +47,11 @@ interface GlLine {
closing: number; closing: number;
} }
interface ContingentLiabItem {
code: string;
label: string;
}
interface MiscExpRow { interface MiscExpRow {
txnDate: string; txnDate: string;
branch: string; branch: string;
@@ -81,7 +86,7 @@ export class AccountingReportsDialogComponent {
activeReport: AcctReportCode; activeReport: AcctReportCode;
bank = { bank = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
asOnDate: '31-03-2026', asOnDate: '31-03-2026',
period: '01-04-2025 to 31-03-2026', period: '01-04-2025 to 31-03-2026',
@@ -94,7 +99,7 @@ export class AccountingReportsDialogComponent {
poDesignation: 'Chief Finance Officer', poDesignation: 'Chief Finance Officer',
auditor: 'M/s Sharma & Associates, Chartered Accountants', auditor: 'M/s Sharma & Associates, Chartered Accountants',
reportRefNo: '157/2', reportRefNo: '157/2',
userCode: 'BUBL01', userCode: 'CBSN01',
branchCount: 15 branchCount: 15
}; };
@@ -120,6 +125,13 @@ export class AccountingReportsDialogComponent {
description: 'Side-by-side comparison of current vs previous fiscal year.', description: 'Side-by-side comparison of current vs previous fiscal year.',
group: 'Financial Statement' group: 'Financial Statement'
}, },
{
code: 'CONTINGENT-LIAB' as AcctReportCode,
name: 'Contingent Liabilities',
icon: 'exclamation-triangle',
description: 'Off-balance-sheet claims, guarantees & contingent exposures (Schedule 12).',
group: 'Financial Statement'
},
{ {
code: 'GL-DR-CR' as AcctReportCode, code: 'GL-DR-CR' as AcctReportCode,
name: 'GL Wise DR/CR', name: 'GL Wise DR/CR',
@@ -136,309 +148,192 @@ export class AccountingReportsDialogComponent {
} }
]; ];
// -------------------- P&L Data (₹ in Lakhs) -------------------- // -------------------- P&L Data (₹ in Lakhs) — FY 2025-26 audited figures --------------------
pnl: PnlLine[] = [ pnl: PnlLine[] = [
{ code: 'I-01', group: 'INCOME', subgroup: 'Interest Earned', label: 'Interest on Advances', amount: 38420 }, { code: 'I-01', group: 'INCOME', subgroup: 'Interest Earned', label: 'Interest on Advances & Bills', amount: 102641.73 },
{ code: 'I-02', group: 'INCOME', subgroup: 'Interest Earned', label: 'Interest on Investments', amount: 14860 }, { code: 'I-02', group: 'INCOME', subgroup: 'Interest Earned', label: 'Income on Investments', amount: 41936.75 },
{ {
code: 'I-03', code: 'I-03',
group: 'INCOME', group: 'INCOME',
subgroup: 'Interest Earned',
label: 'Interest on Balances with RBI / Other Banks',
amount: 1240
},
{
code: 'I-04',
group: 'INCOME',
subgroup: 'Other Income', subgroup: 'Other Income',
label: 'Commission, Exchange & Brokerage', label: 'Commission, Exchange & Brokerage',
amount: 1820 amount: 144.2
},
{ code: 'I-04', group: 'INCOME', subgroup: 'Other Income', label: 'Dividend Income', amount: 4.11 },
{
code: 'I-05',
group: 'INCOME',
subgroup: 'Other Income',
label: 'Net Income on Sale of Fixed Assets',
amount: 0.3
},
{ code: 'I-06', group: 'INCOME', subgroup: 'Other Income', label: 'Other Income (ATM/POS/UPI/Rental/Misc)', amount: 280 },
{
code: 'E-01',
group: 'EXPENDITURE',
subgroup: 'Interest Expended',
label: 'Interest Paid on Deposits & Other Borrowings',
amount: 62861.8
}, },
{ 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', code: 'E-02',
group: 'EXPENDITURE', group: 'EXPENDITURE',
subgroup: 'Interest Expended', subgroup: 'Interest Expended',
label: 'Interest on RBI / Inter-Bank Borrowings', label: 'Interest Paid on NABARD Borrowings',
amount: 980 amount: 47083.71
}, },
{ {
code: 'E-03', code: 'E-03',
group: 'EXPENDITURE', group: 'EXPENDITURE',
subgroup: 'Operating Expenses', subgroup: 'Operating Expenses',
label: 'Payments to & Provisions for Employees', label: 'Operating Expenses (Staff, Admin, Establishment)',
amount: 8420 amount: 14503.79
}, },
{ {
code: 'E-04', code: 'E-04',
group: 'EXPENDITURE', group: 'EXPENDITURE',
subgroup: 'Operating Expenses', subgroup: 'Provisions',
label: 'Rent, Taxes & Lighting', label: 'Provision for Standard Assets',
amount: 1240 amount: 730
},
{
code: 'E-05',
group: 'EXPENDITURE',
subgroup: 'Provisions',
label: 'Provision for Doubtful & Bad Debts (Loans & Advances)',
amount: 2068.2
}, },
{ code: 'E-05', group: 'EXPENDITURE', subgroup: 'Operating Expenses', label: 'Printing & Stationery', amount: 184 },
{ {
code: 'E-06', code: 'E-06',
group: 'EXPENDITURE', group: 'EXPENDITURE',
subgroup: 'Operating Expenses', subgroup: 'Provisions',
label: 'Depreciation on Bank Property', label: 'Bad Debts Written Off',
amount: 642 amount: 906.44
}, },
{ {
code: 'E-07', code: 'E-07',
group: 'EXPENDITURE', 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', subgroup: 'Provisions',
label: 'Provisions for NPA / Standard Assets', label: 'Provision for Staff Training Reserve',
amount: 4280 amount: 245.06
}, },
{ code: 'E-11', group: 'EXPENDITURE', subgroup: 'Provisions', label: 'Provisions for Tax', amount: 2120 } { code: 'E-08', group: 'EXPENDITURE', subgroup: 'Provisions', label: 'Provision for Income Tax', amount: 4600 }
]; ];
// -------------------- Balance Sheet (₹ in Lakhs) -------------------- // -------------------- Balance Sheet (₹ in Lakhs) — as on 31-03-2026, audited --------------------
bs: BsLine[] = [ bs: BsLine[] = [
{ {
code: 'L-01', code: 'L-01',
side: 'CAPITAL-LIAB', side: 'CAPITAL-LIAB',
group: 'Capital & Reserves', group: 'Capital & Reserves',
label: 'Capital (Paid-up Share Capital)', label: 'Capital (Paid-up Share Capital)',
amount: 22000, amount: 92837.18,
prevAmount: 20800 prevAmount: 88842.87
}, },
{ {
code: 'L-02', code: 'L-02',
side: 'CAPITAL-LIAB', side: 'CAPITAL-LIAB',
group: 'Capital & Reserves', group: 'Capital & Reserves',
label: 'Reserves & Surplus', label: 'Reserves & Surplus',
amount: 32400, amount: 155847.97,
prevAmount: 28640 prevAmount: 145524.37
}, },
{ {
code: 'L-03', code: 'L-03',
side: 'CAPITAL-LIAB', side: 'CAPITAL-LIAB',
group: 'Deposits', group: 'Deposits',
label: 'Demand Deposits — Current Accounts', label: 'Deposits',
amount: 84200, amount: 1154054.96,
prevAmount: 78420 prevAmount: 997239.51
}, },
{ {
code: 'L-04', code: 'L-04',
side: 'CAPITAL-LIAB', side: 'CAPITAL-LIAB',
group: 'Deposits', group: 'Borrowings',
label: 'Savings Bank Deposits', label: 'Borrowings (NABARD / Other Banks)',
amount: 218600, amount: 1113954.48,
prevAmount: 198400 prevAmount: 993301.8
}, },
{ {
code: 'L-05', code: 'L-05',
side: 'CAPITAL-LIAB', 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', group: 'Other Liabilities',
label: 'Other Liabilities & Provisions', label: 'Other Liabilities & Provisions',
amount: 5940, amount: 121037.61,
prevAmount: 5420 prevAmount: 124706.3
}, },
{ {
code: 'A-01', code: 'A-01',
side: 'ASSETS', side: 'ASSETS',
group: 'Cash & Bank Balances', group: 'Cash & Bank Balances',
label: 'Cash on Hand', label: 'Cash & Balances with RBI',
amount: 8420, amount: 32627.16,
prevAmount: 7820 prevAmount: 33361.93
}, },
{ {
code: 'A-02', code: 'A-02',
side: 'ASSETS', side: 'ASSETS',
group: 'Cash & Bank Balances', group: 'Money at Call',
label: 'Balances with RBI', label: 'Balances with Other Banks & Money at Call/Short Notice',
amount: 38400, amount: 604619.45,
prevAmount: 34200 prevAmount: 549599.47
}, },
{ {
code: 'A-03', code: 'A-03',
side: 'ASSETS', side: 'ASSETS',
group: 'Cash & Bank Balances', group: 'Investments',
label: 'Balances with Other Banks — Current', label: 'Investments',
amount: 14800, amount: 316953.61,
prevAmount: 12400 prevAmount: 248274.61
}, },
{ {
code: 'A-04', code: 'A-04',
side: 'ASSETS', side: 'ASSETS',
group: 'Cash & Bank Balances', group: 'Advances',
label: 'Balances with Other Banks — Term Deposits', label: 'Loans & Advances',
amount: 10000, amount: 1627711.4,
prevAmount: 9200 prevAmount: 1466401.79
}, },
{ {
code: 'A-05', code: 'A-05',
side: 'ASSETS', side: 'ASSETS',
group: 'Money at Call', group: 'Fixed Assets',
label: 'Money at Call & Short Notice', label: 'Fixed Assets (Property, Plant & Equipment)',
amount: 12400, amount: 33791.75,
prevAmount: 10800 prevAmount: 33727.66
}, },
{ {
code: 'A-06', code: 'A-06',
side: 'ASSETS', 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', group: 'Other Assets',
label: 'Inter-Office Adjustments (Net)', label: 'Other Assets',
amount: 0, amount: 22028.83,
prevAmount: 0 prevAmount: 18249.4
},
{
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
} }
]; ];
// -------------------- Contingent Liabilities (Schedule 12, ₹ in Lakhs) --------------------
// Only the audited TOTAL is reported here — the schedule's individual line items (claims not
// acknowledged as debts, letter of guarantees, DEAF transfers, gratuity/RBI-penalty/GST disputes)
// could not be re-verified against the source figures with confidence, so they are omitted
// rather than risk showing an unverified breakdown.
contingentLiabItems: ContingentLiabItem[] = [
{ code: 'I', label: 'Claims against the bank not acknowledged as debts' },
{ code: 'II', label: 'Letter of Guarantees' },
{ code: 'III', label: 'Amounts transferred to Depositor Education and Awareness Fund (DEAF)' },
{ code: 'IV', label: 'Other items for which the bank is contingently liable (Gratuity / RBI Penalty / GST Demand)' }
];
contingentLiab = {
current: 1633.3,
previous: 1522.13
};
get clVariation(): number {
return this.variation(this.contingentLiab.current, this.contingentLiab.previous);
}
get clVariationPct(): number {
return this.variationPct(this.contingentLiab.current, this.contingentLiab.previous);
}
// -------------------- GL Wise DR/CR Data -------------------- // -------------------- GL Wise DR/CR Data --------------------
glReport: GlLine[] = [ glReport: GlLine[] = [
{ {
@@ -1008,6 +903,7 @@ export class AccountingReportsDialogComponent {
if (this.activeReport === 'PNL') return this.exportPnlCsv(); if (this.activeReport === 'PNL') return this.exportPnlCsv();
if (this.activeReport === 'BALANCE-SHEET') return this.exportBsCsv(false); if (this.activeReport === 'BALANCE-SHEET') return this.exportBsCsv(false);
if (this.activeReport === 'COMPARATIVE-BS') return this.exportBsCsv(true); if (this.activeReport === 'COMPARATIVE-BS') return this.exportBsCsv(true);
if (this.activeReport === 'CONTINGENT-LIAB') return this.exportContingentLiabCsv();
if (this.activeReport === 'GL-DR-CR') return this.exportGlCsv(); if (this.activeReport === 'GL-DR-CR') return this.exportGlCsv();
if (this.activeReport === 'MISC-EXP') return this.exportMiscCsv(); if (this.activeReport === 'MISC-EXP') return this.exportMiscCsv();
} }
@@ -1155,6 +1051,49 @@ export class AccountingReportsDialogComponent {
this.saveCsv(rows, `${comparative ? 'Comparative-' : ''}Balance-Sheet-${this.dateTag()}.csv`); this.saveCsv(rows, `${comparative ? 'Comparative-' : ''}Balance-Sheet-${this.dateTag()}.csv`);
} }
exportContingentLiabCsv(): void {
const rows: string[][] = [];
rows.push([
this.bank.name,
this.bank.branch
]);
rows.push([
'CONTINGENT LIABILITIES (SCHEDULE 12)',
`As on ${this.bank.asOnDate}`
]);
rows.push([]);
rows.push(['Items covered:']);
for (const item of this.contingentLiabItems) {
rows.push([
item.code,
item.label
]);
}
rows.push([]);
rows.push([
'',
'TOTAL CONTINGENT LIABILITIES',
`FY ${this.bank.fiscalYear} (₹ Lakh)`,
`FY ${this.bank.prevFiscalYear} (₹ Lakh)`,
'Variation',
'Variation %'
]);
rows.push([
'',
'',
this.contingentLiab.current.toFixed(2),
this.contingentLiab.previous.toFixed(2),
this.clVariation.toFixed(2),
this.clVariationPct.toFixed(2) + '%'
]);
rows.push([]);
rows.push([
'Certified by',
this.bank.principalOfficer + ' — ' + this.bank.poDesignation
]);
this.saveCsv(rows, `Contingent-Liabilities-${this.dateTag()}.csv`);
}
exportGlCsv(): void { exportGlCsv(): void {
const rows: string[][] = []; const rows: string[][] = [];
rows.push([ rows.push([
@@ -54,7 +54,7 @@ export class AlmReportDialogComponent {
activeTab: 'dashboard' | 'ladder' | 'monthly' | 'returns' = 'dashboard'; activeTab: 'dashboard' | 'ladder' | 'monthly' | 'returns' = 'dashboard';
bank = { bank = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
asOnDate: '31-03-2026', asOnDate: '31-03-2026',
quarter: 'Q4 FY 2025-26', quarter: 'Q4 FY 2025-26',
@@ -63,7 +63,7 @@ export class AlmReportDialogComponent {
principalOfficer: 'Karthik Kamath', principalOfficer: 'Karthik Kamath',
formCode: 'Form ALM-1', formCode: 'Form ALM-1',
reportRefNo: '1036', reportRefNo: '1036',
userCode: 'BUBL01', userCode: 'CBSN01',
/** Net Demand & Time Liabilities — denominator for ratios */ /** Net Demand & Time Liabilities — denominator for ratios */
ndtl: 740000, // ₹7,400 Cr in Lakhs ndtl: 740000, // ₹7,400 Cr in Lakhs
/** RBI tolerance: cumulative negative gap up to 14 days ≤ 5%, 28 days ≤ 10% */ /** RBI tolerance: cumulative negative gap up to 14 days ≤ 5%, 28 days ≤ 10% */
@@ -14,7 +14,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
export type CimsFormCode = 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB'; export type CimsFormCode = 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB' | 'OSS-4' | 'OSS-5' | 'OSS-6' | 'OSS-7';
export interface CimsXbrlDialogData { export interface CimsXbrlDialogData {
initialForm?: CimsFormCode; initialForm?: CimsFormCode;
@@ -64,20 +64,20 @@ export class CimsXbrlDialogComponent {
activeTab: 'filing' | 'data' | 'preview' = 'filing'; activeTab: 'filing' | 'data' | 'preview' = 'filing';
bank = { bank = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
bsrCode: 'BUBL0000123', bsrCode: 'CBSN0000123',
ifsc: 'BUBL0000123', ifsc: 'CBSN0000123',
legalEntityId: 'BUBL-LEI-2025', legalEntityId: 'CBSN-LEI-2025',
pan: 'AAACB1234L', pan: 'AAACB1234L',
asOnDate: '31-03-2026', asOnDate: '31-03-2026',
quarter: 'Q4 FY 2025-26', quarter: 'Q4 FY 2025-26',
fiscalYear: '2025-26', fiscalYear: '2025-26',
principalOfficer: 'Karthik Kamath', principalOfficer: 'Karthik Kamath',
poDesignation: 'Chief Compliance Officer', poDesignation: 'Chief Compliance Officer',
poEmail: 'compliance@bharaturbanbank.in', poEmail: 'compliance@cbsnexorabank.in',
poMobile: '+91-9988002211', poMobile: '+91-9988002211',
cimsUserId: 'BUBL_CIMS_001', cimsUserId: 'CBSN_CIMS_001',
cimsPortal: 'https://cims.rbi.org.in/' cimsPortal: 'https://cims.rbi.org.in/'
}; };
@@ -97,7 +97,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:CapAdq.CRAR', ref: 'oss2:CapAdq.CRAR',
section: 'Capital Adequacy', section: 'Capital Adequacy',
label: 'CRAR (Total)', label: 'CRAR (Total)',
value: 14.82, value: 21.82,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -105,7 +105,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:CapAdq.Tier1', ref: 'oss2:CapAdq.Tier1',
section: 'Capital Adequacy', section: 'Capital Adequacy',
label: 'Tier-I CRAR', label: 'Tier-I CRAR',
value: 12.18, value: 20.51,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -113,7 +113,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:CapAdq.Tier2', ref: 'oss2:CapAdq.Tier2',
section: 'Capital Adequacy', section: 'Capital Adequacy',
label: 'Tier-II CRAR', label: 'Tier-II CRAR',
value: 2.64, value: 1.31,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -121,7 +121,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.Total', ref: 'oss2:Asset.Total',
section: 'Asset Classification', section: 'Asset Classification',
label: 'Total Advances (₹ Lakh)', label: 'Total Advances (₹ Lakh)',
value: 416265, value: 4112.04,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -129,7 +129,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.Standard', ref: 'oss2:Asset.Standard',
section: 'Asset Classification', section: 'Asset Classification',
label: 'Standard Assets (₹ Lakh)', label: 'Standard Assets (₹ Lakh)',
value: 401820, value: 3845.88,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -137,7 +137,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.SubStd', ref: 'oss2:Asset.SubStd',
section: 'Asset Classification', section: 'Asset Classification',
label: 'Sub-Standard (₹ Lakh)', label: 'Sub-Standard (₹ Lakh)',
value: 6220, value: 102.41,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -145,7 +145,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.Doubtful', ref: 'oss2:Asset.Doubtful',
section: 'Asset Classification', section: 'Asset Classification',
label: 'Doubtful (₹ Lakh)', label: 'Doubtful (₹ Lakh)',
value: 5840, value: 163.75,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -153,7 +153,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.Loss', ref: 'oss2:Asset.Loss',
section: 'Asset Classification', section: 'Asset Classification',
label: 'Loss (₹ Lakh)', label: 'Loss (₹ Lakh)',
value: 2385, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -161,7 +161,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.GNPA', ref: 'oss2:Asset.GNPA',
section: 'NPA Position', section: 'NPA Position',
label: 'Gross NPA (₹ Lakh)', label: 'Gross NPA (₹ Lakh)',
value: 14445, value: 266.16,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -169,7 +169,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.NNPA', ref: 'oss2:Asset.NNPA',
section: 'NPA Position', section: 'NPA Position',
label: 'Net NPA (₹ Lakh)', label: 'Net NPA (₹ Lakh)',
value: 5212, value: -96.33,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -177,7 +177,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.GNPAPct', ref: 'oss2:Asset.GNPAPct',
section: 'NPA Position', section: 'NPA Position',
label: 'GNPA %', label: 'GNPA %',
value: 3.47, value: 6.47,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -185,7 +185,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.NNPAPct', ref: 'oss2:Asset.NNPAPct',
section: 'NPA Position', section: 'NPA Position',
label: 'NNPA %', label: 'NNPA %',
value: 1.27, value: -2.6,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -193,7 +193,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Asset.PCR', ref: 'oss2:Asset.PCR',
section: 'NPA Position', section: 'NPA Position',
label: 'Provision Coverage Ratio', label: 'Provision Coverage Ratio',
value: 64.13, value: 136.24,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -201,7 +201,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Profit.NIM', ref: 'oss2:Profit.NIM',
section: 'Profitability', section: 'Profitability',
label: 'Net Interest Margin', label: 'Net Interest Margin',
value: 3.21, value: 0.51,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -209,7 +209,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Profit.ROA', ref: 'oss2:Profit.ROA',
section: 'Profitability', section: 'Profitability',
label: 'Return on Assets', label: 'Return on Assets',
value: 0.92, value: 0.67,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -217,7 +217,7 @@ export class CimsXbrlDialogComponent {
ref: 'oss2:Profit.ROE', ref: 'oss2:Profit.ROE',
section: 'Profitability', section: 'Profitability',
label: 'Return on Equity', label: 'Return on Equity',
value: 11.48, value: 5.38,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -254,7 +254,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:Reference.ANBC', ref: 'psa:Reference.ANBC',
section: 'Reference', section: 'Reference',
label: 'Adjusted Net Bank Credit (₹ Lakh)', label: 'Adjusted Net Bank Credit (₹ Lakh)',
value: 600000, value: 3274.83,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -262,7 +262,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:Reference.OBE', ref: 'psa:Reference.OBE',
section: 'Reference', section: 'Reference',
label: 'Credit Equivalent of OBE (₹ Lakh)', label: 'Credit Equivalent of OBE (₹ Lakh)',
value: 25000, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -270,7 +270,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Total', ref: 'psa:PSL.Total',
section: 'PSL Achievement', section: 'PSL Achievement',
label: 'Total PSL Outstanding (₹ Lakh)', label: 'Total PSL Outstanding (₹ Lakh)',
value: 241700, value: 2485.67,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -278,7 +278,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.TotalPct', ref: 'psa:PSL.TotalPct',
section: 'PSL Achievement', section: 'PSL Achievement',
label: 'Total PSL % of ANBC', label: 'Total PSL % of ANBC',
value: 40.28, value: 75.9,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -286,7 +286,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Agri', ref: 'psa:PSL.Agri',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Agriculture (₹ Lakh)', label: 'Agriculture (₹ Lakh)',
value: 124000, value: 39.0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -294,7 +294,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.AgriPct', ref: 'psa:PSL.AgriPct',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Agriculture % of ANBC', label: 'Agriculture % of ANBC',
value: 20.67, value: 1.19,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -302,7 +302,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.SMF', ref: 'psa:PSL.SMF',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Small &amp; Marginal Farmers (₹ Lakh)', label: 'Small &amp; Marginal Farmers (₹ Lakh)',
value: 72000, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -310,23 +310,23 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.SMFPct', ref: 'psa:PSL.SMFPct',
section: 'Sub-targets', section: 'Sub-targets',
label: 'SMF % of ANBC', label: 'SMF % of ANBC',
value: 12.0, value: 0,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
{ {
ref: 'psa:PSL.MicroEnt', ref: 'psa:PSL.MicroEnt',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Micro Enterprises (₹ Lakh)', label: 'MSME Enterprises (₹ Lakh)',
value: 51000, value: 2313.83,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
{ {
ref: 'psa:PSL.MicroPct', ref: 'psa:PSL.MicroPct',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Micro Enterprises % of ANBC', label: 'MSME Enterprises % of ANBC',
value: 8.5, value: 70.65,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -334,7 +334,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Weaker', ref: 'psa:PSL.Weaker',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Weaker Sections (₹ Lakh)', label: 'Weaker Sections (₹ Lakh)',
value: 85500, value: 990.28,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -342,7 +342,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.WeakerPct', ref: 'psa:PSL.WeakerPct',
section: 'Sub-targets', section: 'Sub-targets',
label: 'Weaker Sections % of ANBC', label: 'Weaker Sections % of ANBC',
value: 14.25, value: 30.22,
unit: 'Percent', unit: 'Percent',
type: 'percent' type: 'percent'
}, },
@@ -350,7 +350,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Housing', ref: 'psa:PSL.Housing',
section: 'Other Categories', section: 'Other Categories',
label: 'Housing (₹ Lakh)', label: 'Housing (₹ Lakh)',
value: 9200, value: 115.12,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -358,7 +358,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Education', ref: 'psa:PSL.Education',
section: 'Other Categories', section: 'Other Categories',
label: 'Education (₹ Lakh)', label: 'Education (₹ Lakh)',
value: 4200, value: 17.32,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -366,7 +366,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.Export', ref: 'psa:PSL.Export',
section: 'Other Categories', section: 'Other Categories',
label: 'Export Credit (₹ Lakh)', label: 'Export Credit (₹ Lakh)',
value: 3800, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -374,7 +374,7 @@ export class CimsXbrlDialogComponent {
ref: 'psa:PSL.RenEnergy', ref: 'psa:PSL.RenEnergy',
section: 'Other Categories', section: 'Other Categories',
label: 'Renewable Energy (₹ Lakh)', label: 'Renewable Energy (₹ Lakh)',
value: 2400, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
} }
@@ -395,15 +395,15 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.Capital', ref: 'ral:Liab.Capital',
section: 'Liabilities', section: 'Liabilities',
label: 'Capital (₹ Lakh)', label: 'Capital (₹ Lakh)',
value: 22000, value: 272.78,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
{ {
ref: 'ral:Liab.Reserves', ref: 'ral:Liab.Reserves',
section: 'Liabilities', section: 'Liabilities',
label: 'Reserves &amp; Surplus (₹ Lakh)', label: 'Reserves, Surplus &amp; Profit (₹ Lakh)',
value: 32400, value: 712.6,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -411,7 +411,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.DepDemand', ref: 'ral:Liab.DepDemand',
section: 'Liabilities', section: 'Liabilities',
label: 'Demand Deposits (₹ Lakh)', label: 'Demand Deposits (₹ Lakh)',
value: 84200, value: 195.92,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -419,7 +419,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.DepSavings', ref: 'ral:Liab.DepSavings',
section: 'Liabilities', section: 'Liabilities',
label: 'Savings Deposits (₹ Lakh)', label: 'Savings Deposits (₹ Lakh)',
value: 218600, value: 2081.16,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -427,7 +427,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.DepTerm', ref: 'ral:Liab.DepTerm',
section: 'Liabilities', section: 'Liabilities',
label: 'Term Deposits (₹ Lakh)', label: 'Term Deposits (₹ Lakh)',
value: 462800, value: 4066.01,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -435,7 +435,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.Borrow', ref: 'ral:Liab.Borrow',
section: 'Liabilities', section: 'Liabilities',
label: 'Borrowings (₹ Lakh)', label: 'Borrowings (₹ Lakh)',
value: 24180, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -443,7 +443,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Liab.Other', ref: 'ral:Liab.Other',
section: 'Liabilities', section: 'Liabilities',
label: 'Other Liabilities &amp; Provisions (₹ Lakh)', label: 'Other Liabilities &amp; Provisions (₹ Lakh)',
value: 18420, value: 780.73,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -451,7 +451,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.Cash', ref: 'ral:Asset.Cash',
section: 'Assets', section: 'Assets',
label: 'Cash on Hand (₹ Lakh)', label: 'Cash on Hand (₹ Lakh)',
value: 8420, value: 99.96,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -459,15 +459,15 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.RBI', ref: 'ral:Asset.RBI',
section: 'Assets', section: 'Assets',
label: 'Balances with RBI (₹ Lakh)', label: 'Balances with RBI (₹ Lakh)',
value: 38400, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
{ {
ref: 'ral:Asset.Banks', ref: 'ral:Asset.Banks',
section: 'Assets', section: 'Assets',
label: 'Balances with Banks (₹ Lakh)', label: 'Balances with Banks &amp; Other Institutions (₹ Lakh)',
value: 24800, value: 1349.53,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -475,7 +475,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.Money', ref: 'ral:Asset.Money',
section: 'Assets', section: 'Assets',
label: 'Money at Call &amp; Short Notice (₹ Lakh)', label: 'Money at Call &amp; Short Notice (₹ Lakh)',
value: 12400, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -483,7 +483,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.Invest', ref: 'ral:Asset.Invest',
section: 'Assets', section: 'Assets',
label: 'Investments (₹ Lakh)', label: 'Investments (₹ Lakh)',
value: 184600, value: 2342.97,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -491,7 +491,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.Adv', ref: 'ral:Asset.Adv',
section: 'Assets', section: 'Assets',
label: 'Advances (₹ Lakh)', label: 'Advances (₹ Lakh)',
value: 416265, value: 3912.88,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -499,23 +499,23 @@ export class CimsXbrlDialogComponent {
ref: 'ral:Asset.Fixed', ref: 'ral:Asset.Fixed',
section: 'Assets', section: 'Assets',
label: 'Fixed Assets (₹ Lakh)', label: 'Fixed Assets (₹ Lakh)',
value: 14200, value: 49.48,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
{ {
ref: 'ral:Asset.Other', ref: 'ral:Asset.Other',
section: 'Assets', section: 'Assets',
label: 'Other Assets (₹ Lakh)', label: 'Other Assets &amp; Interest Receivable (₹ Lakh)',
value: 12480, value: 354.38,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
{ {
ref: 'ral:OBE.Guar', ref: 'ral:OBE.Guar',
section: 'Off-Balance Sheet', section: 'Off-Balance Sheet',
label: 'Bank Guarantees (₹ Lakh)', label: 'Contingent Liabilities (₹ Lakh)',
value: 18420, value: 26.38,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
}, },
@@ -523,7 +523,7 @@ export class CimsXbrlDialogComponent {
ref: 'ral:OBE.LC', ref: 'ral:OBE.LC',
section: 'Off-Balance Sheet', section: 'Off-Balance Sheet',
label: 'Letters of Credit (₹ Lakh)', label: 'Letters of Credit (₹ Lakh)',
value: 6580, value: 0,
unit: 'INR-Lakh', unit: 'INR-Lakh',
type: 'monetary' type: 'monetary'
} }
@@ -653,6 +653,293 @@ export class CimsXbrlDialogComponent {
type: 'monetary' type: 'monetary'
} }
] ]
},
'OSS-4': {
code: 'OSS-4',
name: 'OSS-4',
fullName: 'Connected Lending &amp; Large Exposure Statement',
description:
'Quarterly statement of loans to directors/relatives, senior officers, and exposures exceeding prudential thresholds. Illustrative figures — no source filing was provided for this return; values are modelled on this bank\'s own capital/advances scale for consistency.',
cimsCode: 'CIMS_OSS_004',
taxonomy: 'rbi-cims-oss4-2024-04-01',
periodicity: 'Quarterly',
lastFiled: '15-01-2026',
nextDue: '15-04-2026',
fields: [
{
ref: 'oss4:Connected.Directors',
section: 'Connected Lending',
label: 'Loans to Directors &amp; Relatives (₹ Lakh)',
value: 42.5,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss4:Connected.SrOfficers',
section: 'Connected Lending',
label: 'Loans to Senior Officers (₹ Lakh)',
value: 18.75,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss4:Connected.Accounts',
section: 'Connected Lending',
label: 'Number of Connected Lending Accounts',
value: 12,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss4:LargeExp.Amount',
section: 'Large Exposures',
label: 'Exposures &gt; 10% of Capital Funds (₹ Lakh)',
value: 285.0,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss4:LargeExp.Pct',
section: 'Large Exposures',
label: 'Large Exposures as % of Capital Funds',
value: 30.58,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss4:LargeExp.SingleBreach',
section: 'Large Exposures',
label: 'Single Borrower Limit Breaches',
value: 0,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss4:LargeExp.GroupBreach',
section: 'Large Exposures',
label: 'Group Borrower Limit Breaches',
value: 0,
unit: 'Number',
type: 'numeric'
}
]
},
'OSS-5': {
code: 'OSS-5',
name: 'OSS-5',
fullName: 'Institutional &amp; Membership Profile',
description:
'Annual statement of branch network, staff strength, membership and depositor/borrower base. Illustrative figures — no source filing was provided for this return; membership share capital is aligned to this bank\'s real paid-up capital figure.',
cimsCode: 'CIMS_OSS_005',
taxonomy: 'rbi-cims-oss5-2024-04-01',
periodicity: 'Annual',
lastFiled: '30-04-2025',
nextDue: '30-04-2026',
fields: [
{
ref: 'oss5:Profile.Branches',
section: 'Network',
label: 'Number of Branches',
value: 8,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss5:Profile.Employees',
section: 'Network',
label: 'Number of Employees',
value: 64,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss5:Profile.AreaOfOperation',
section: 'Network',
label: 'Area of Operation',
value: 'Single District',
type: 'string'
},
{
ref: 'oss5:Profile.Members',
section: 'Membership',
label: 'Number of Regular Members',
value: 18450,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss5:Profile.ShareCapital',
section: 'Membership',
label: 'Membership Share Capital (₹ Lakh)',
value: 272.78,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss5:Profile.Depositors',
section: 'Customer Base',
label: 'Number of Depositors',
value: 42180,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss5:Profile.Borrowers',
section: 'Customer Base',
label: 'Number of Borrowers',
value: 3860,
unit: 'Number',
type: 'numeric'
}
]
},
'OSS-6': {
code: 'OSS-6',
name: 'OSS-6',
fullName: 'Structural Liquidity &amp; Interest Rate Sensitivity Statement',
description:
'Fortnightly maturity-bucket gap analysis of rate-sensitive assets and liabilities. Illustrative figures — no source filing was provided for this return.',
cimsCode: 'CIMS_OSS_006',
taxonomy: 'rbi-cims-oss6-2024-04-01',
periodicity: 'Monthly',
lastFiled: '14-04-2026',
nextDue: '28-04-2026',
fields: [
{
ref: 'oss6:Gap.1to14Days',
section: 'Maturity Gap (₹ Lakh)',
label: '1-14 Days',
value: -45.2,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.15to28Days',
section: 'Maturity Gap (₹ Lakh)',
label: '15-28 Days',
value: -12.8,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.29DaysTo3M',
section: 'Maturity Gap (₹ Lakh)',
label: '29 Days to 3 Months',
value: 88.4,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.Over3To6M',
section: 'Maturity Gap (₹ Lakh)',
label: 'Over 3 Months to 6 Months',
value: 62.15,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.Over6MTo1Y',
section: 'Maturity Gap (₹ Lakh)',
label: 'Over 6 Months to 1 Year',
value: -28.9,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.Over1To3Y',
section: 'Maturity Gap (₹ Lakh)',
label: 'Over 1 Year to 3 Years',
value: 210.6,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.Over3To5Y',
section: 'Maturity Gap (₹ Lakh)',
label: 'Over 3 Years to 5 Years',
value: 95.3,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Gap.Over5Y',
section: 'Maturity Gap (₹ Lakh)',
label: 'Over 5 Years',
value: -18.75,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss6:Liq.CumNegGapPct',
section: 'Liquidity Ratios',
label: 'Cumulative Negative Gap (14-Day) as % of Cumulative Outflows',
value: 4.85,
unit: 'Percent',
type: 'percent'
}
]
},
'OSS-7': {
code: 'OSS-7',
name: 'OSS-7',
fullName: 'Statement of Frauds &amp; Customer Complaints',
description:
'Quarterly statement of frauds reported and customer-complaint disposal. Illustrative figures — no source filing was provided for this return.',
cimsCode: 'CIMS_OSS_007',
taxonomy: 'rbi-cims-oss7-2024-04-01',
periodicity: 'Quarterly',
lastFiled: '15-01-2026',
nextDue: '15-04-2026',
fields: [
{
ref: 'oss7:Fraud.Count',
section: 'Frauds',
label: 'Number of Frauds Reported',
value: 1,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss7:Fraud.Amount',
section: 'Frauds',
label: 'Amount Involved in Frauds (₹ Lakh)',
value: 2.4,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss7:Complaints.Received',
section: 'Customer Complaints',
label: 'Complaints Received (Quarter)',
value: 14,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss7:Complaints.Resolved',
section: 'Customer Complaints',
label: 'Complaints Resolved',
value: 12,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss7:Complaints.Pending',
section: 'Customer Complaints',
label: 'Complaints Pending',
value: 2,
unit: 'Number',
type: 'numeric'
},
{
ref: 'oss7:Complaints.PendingOver30',
section: 'Customer Complaints',
label: 'Complaints Pending &gt; 30 Days',
value: 0,
unit: 'Number',
type: 'numeric'
}
]
} }
}; };
@@ -660,7 +947,11 @@ export class CimsXbrlDialogComponent {
'OSS-2', 'OSS-2',
'PSA', 'PSA',
'RAL', 'RAL',
'RCP-UCB' 'RCP-UCB',
'OSS-4',
'OSS-5',
'OSS-6',
'OSS-7'
]; ];
data = inject<CimsXbrlDialogData | null>(MAT_DIALOG_DATA, { optional: true }); data = inject<CimsXbrlDialogData | null>(MAT_DIALOG_DATA, { optional: true });
@@ -76,7 +76,7 @@ export class CkycReportsDialogComponent {
activeReport: CkycReportCode; activeReport: CkycReportCode;
bank = { bank = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
asOnDate: '29-01-2026', asOnDate: '29-01-2026',
fiscalYear: '2025-26', fiscalYear: '2025-26',
@@ -84,7 +84,7 @@ export class CkycReportsDialogComponent {
principalOfficer: 'Karthik Kamath', principalOfficer: 'Karthik Kamath',
poDesignation: 'Principal Compliance Officer', poDesignation: 'Principal Compliance Officer',
reportRefNo: '2075/1', reportRefNo: '2075/1',
userCode: 'BUBL01', userCode: 'CBSN01',
fyStart: '01-04-2025', fyStart: '01-04-2025',
fyEnd: '31-03-2026' fyEnd: '31-03-2026'
}; };
@@ -121,7 +121,7 @@ export class CkycReportsDialogComponent {
rekyc: RekycRow[] = [ rekyc: RekycRow[] = [
{ {
ucode: 'BUBL000201', ucode: 'CBSN000201',
name: 'Ravi Kumar Sharma', name: 'Ravi Kumar Sharma',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -132,7 +132,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL000418', ucode: 'CBSN000418',
name: 'Priya Patel', name: 'Priya Patel',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -143,7 +143,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL000522', ucode: 'CBSN000522',
name: 'Arjun Deshmukh', name: 'Arjun Deshmukh',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -154,7 +154,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL000711', ucode: 'CBSN000711',
name: 'Vikram Reddy', name: 'Vikram Reddy',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'HIGH', risk: 'HIGH',
@@ -165,7 +165,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL000814', ucode: 'CBSN000814',
name: 'Lakshmi Iyer', name: 'Lakshmi Iyer',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -176,7 +176,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL001012', ucode: 'CBSN001012',
name: 'Nirav Shah', name: 'Nirav Shah',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -187,7 +187,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL001145', ucode: 'CBSN001145',
name: 'Kavita Verma', name: 'Kavita Verma',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'HIGH', risk: 'HIGH',
@@ -198,7 +198,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL001458', ucode: 'CBSN001458',
name: 'Sameer Kapoor', name: 'Sameer Kapoor',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -209,7 +209,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL001721', ucode: 'CBSN001721',
name: 'Karthik Subramanian', name: 'Karthik Subramanian',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -220,7 +220,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL002012', ucode: 'CBSN002012',
name: 'Abhishek Banerjee', name: 'Abhishek Banerjee',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -231,7 +231,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL002314', ucode: 'CBSN002314',
name: 'Gaurav Thakur', name: 'Gaurav Thakur',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -242,7 +242,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL002615', ucode: 'CBSN002615',
name: 'Manmohan Singh Gill', name: 'Manmohan Singh Gill',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'HIGH', risk: 'HIGH',
@@ -253,7 +253,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL002918', ucode: 'CBSN002918',
name: 'Sunita Devi', name: 'Sunita Devi',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'LOW', risk: 'LOW',
@@ -264,7 +264,7 @@ export class CkycReportsDialogComponent {
segment: 'BELOW-25-LACS' segment: 'BELOW-25-LACS'
}, },
{ {
ucode: 'BUBL003221', ucode: 'CBSN003221',
name: 'Deepak Tiwari', name: 'Deepak Tiwari',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -275,7 +275,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL003524', ucode: 'CBSN003524',
name: 'Harpreet Kaur', name: 'Harpreet Kaur',
customerType: 'INDIVIDUAL', customerType: 'INDIVIDUAL',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -286,7 +286,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL003812', ucode: 'CBSN003812',
name: 'Saraswati Mahila Co-op Society', name: 'Saraswati Mahila Co-op Society',
customerType: 'SOCIETY', customerType: 'SOCIETY',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -297,7 +297,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL004115', ucode: 'CBSN004115',
name: 'Aditya Enterprises', name: 'Aditya Enterprises',
customerType: 'FIRM', customerType: 'FIRM',
risk: 'HIGH', risk: 'HIGH',
@@ -308,7 +308,7 @@ export class CkycReportsDialogComponent {
segment: 'ABOVE-25-LACS' segment: 'ABOVE-25-LACS'
}, },
{ {
ucode: 'BUBL004318', ucode: 'CBSN004318',
name: 'Vasudha Trading HUF', name: 'Vasudha Trading HUF',
customerType: 'HUF', customerType: 'HUF',
risk: 'MEDIUM', risk: 'MEDIUM',
@@ -332,7 +332,7 @@ export class CkycReportsDialogComponent {
branchCode: '01', branchCode: '01',
branch: 'Mumbai Main', branch: 'Mumbai Main',
acType: 'SB', acType: 'SB',
clientId: 'BUBL000201', clientId: 'CBSN000201',
customerName: 'Ravi Kumar Sharma', customerName: 'Ravi Kumar Sharma',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'LOW', newRisk: 'LOW',
@@ -345,7 +345,7 @@ export class CkycReportsDialogComponent {
branchCode: '02', branchCode: '02',
branch: 'Pune FC Road', branch: 'Pune FC Road',
acType: 'CA', acType: 'CA',
clientId: 'BUBL000418', clientId: 'CBSN000418',
customerName: 'Priya Patel', customerName: 'Priya Patel',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'MEDIUM', newRisk: 'MEDIUM',
@@ -358,7 +358,7 @@ export class CkycReportsDialogComponent {
branchCode: '03', branchCode: '03',
branch: 'Nagpur Dharampeth', branch: 'Nagpur Dharampeth',
acType: 'TL', acType: 'TL',
clientId: 'BUBL000522', clientId: 'CBSN000522',
customerName: 'Arjun Deshmukh', customerName: 'Arjun Deshmukh',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'LOW', newRisk: 'LOW',
@@ -371,7 +371,7 @@ export class CkycReportsDialogComponent {
branchCode: '04', branchCode: '04',
branch: 'Bengaluru Jayanagar', branch: 'Bengaluru Jayanagar',
acType: 'OD', acType: 'OD',
clientId: 'BUBL000711', clientId: 'CBSN000711',
customerName: 'Vikram Reddy', customerName: 'Vikram Reddy',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'HIGH', newRisk: 'HIGH',
@@ -384,7 +384,7 @@ export class CkycReportsDialogComponent {
branchCode: '05', branchCode: '05',
branch: 'Bengaluru Indiranagar', branch: 'Bengaluru Indiranagar',
acType: 'SB', acType: 'SB',
clientId: 'BUBL000814', clientId: 'CBSN000814',
customerName: 'Lakshmi Iyer', customerName: 'Lakshmi Iyer',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'LOW', newRisk: 'LOW',
@@ -397,7 +397,7 @@ export class CkycReportsDialogComponent {
branchCode: '06', branchCode: '06',
branch: 'Ahmedabad Satellite', branch: 'Ahmedabad Satellite',
acType: 'CC', acType: 'CC',
clientId: 'BUBL001012', clientId: 'CBSN001012',
customerName: 'Nirav Shah', customerName: 'Nirav Shah',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'MEDIUM', newRisk: 'MEDIUM',
@@ -410,7 +410,7 @@ export class CkycReportsDialogComponent {
branchCode: '07', branchCode: '07',
branch: 'Lucknow Gomti Nagar', branch: 'Lucknow Gomti Nagar',
acType: 'CA', acType: 'CA',
clientId: 'BUBL001145', clientId: 'CBSN001145',
customerName: 'Kavita Verma', customerName: 'Kavita Verma',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'HIGH', newRisk: 'HIGH',
@@ -423,7 +423,7 @@ export class CkycReportsDialogComponent {
branchCode: '08', branchCode: '08',
branch: 'Delhi Greater Kailash', branch: 'Delhi Greater Kailash',
acType: 'OD', acType: 'OD',
clientId: 'BUBL001458', clientId: 'CBSN001458',
customerName: 'Sameer Kapoor', customerName: 'Sameer Kapoor',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'MEDIUM', newRisk: 'MEDIUM',
@@ -436,7 +436,7 @@ export class CkycReportsDialogComponent {
branchCode: '09', branchCode: '09',
branch: 'Chennai Valasaravakkam', branch: 'Chennai Valasaravakkam',
acType: 'SB', acType: 'SB',
clientId: 'BUBL001721', clientId: 'CBSN001721',
customerName: 'Karthik Subramanian', customerName: 'Karthik Subramanian',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'LOW', newRisk: 'LOW',
@@ -449,7 +449,7 @@ export class CkycReportsDialogComponent {
branchCode: '10', branchCode: '10',
branch: 'Kolkata Salt Lake', branch: 'Kolkata Salt Lake',
acType: 'TL', acType: 'TL',
clientId: 'BUBL002012', clientId: 'CBSN002012',
customerName: 'Abhishek Banerjee', customerName: 'Abhishek Banerjee',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'MEDIUM', newRisk: 'MEDIUM',
@@ -462,7 +462,7 @@ export class CkycReportsDialogComponent {
branchCode: '11', branchCode: '11',
branch: 'Shimla Sector 4', branch: 'Shimla Sector 4',
acType: 'SB', acType: 'SB',
clientId: 'BUBL002314', clientId: 'CBSN002314',
customerName: 'Gaurav Thakur', customerName: 'Gaurav Thakur',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'LOW', newRisk: 'LOW',
@@ -475,7 +475,7 @@ export class CkycReportsDialogComponent {
branchCode: '12', branchCode: '12',
branch: 'Amritsar Model Town', branch: 'Amritsar Model Town',
acType: 'CC', acType: 'CC',
clientId: 'BUBL002615', clientId: 'CBSN002615',
customerName: 'Manmohan Singh Gill', customerName: 'Manmohan Singh Gill',
oldRisk: 'HIGH', oldRisk: 'HIGH',
newRisk: 'HIGH', newRisk: 'HIGH',
@@ -488,7 +488,7 @@ export class CkycReportsDialogComponent {
branchCode: '13', branchCode: '13',
branch: 'Bhopal Arera Colony', branch: 'Bhopal Arera Colony',
acType: 'SB', acType: 'SB',
clientId: 'BUBL002918', clientId: 'CBSN002918',
customerName: 'Sunita Devi', customerName: 'Sunita Devi',
oldRisk: 'LOW', oldRisk: 'LOW',
newRisk: 'LOW', newRisk: 'LOW',
@@ -501,7 +501,7 @@ export class CkycReportsDialogComponent {
branchCode: '14', branchCode: '14',
branch: 'Indore Vijay Nagar', branch: 'Indore Vijay Nagar',
acType: 'OD', acType: 'OD',
clientId: 'BUBL003221', clientId: 'CBSN003221',
customerName: 'Deepak Tiwari', customerName: 'Deepak Tiwari',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'HIGH', newRisk: 'HIGH',
@@ -514,7 +514,7 @@ export class CkycReportsDialogComponent {
branchCode: '15', branchCode: '15',
branch: 'Ludhiana Sarabha Nagar', branch: 'Ludhiana Sarabha Nagar',
acType: 'TL', acType: 'TL',
clientId: 'BUBL003524', clientId: 'CBSN003524',
customerName: 'Harpreet Kaur', customerName: 'Harpreet Kaur',
oldRisk: 'MEDIUM', oldRisk: 'MEDIUM',
newRisk: 'MEDIUM', newRisk: 'MEDIUM',
@@ -1,285 +0,0 @@
<!--
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="ctr-str-dlg">
<!-- Dialog Header -->
<div class="dlg-header">
<div class="dlg-title-area">
<fa-icon icon="shield-alt" class="header-icon"></fa-icon>
<div>
<h2 class="title">CTR & STR Compliance Control Center</h2>
<p class="subtitle">Anti-Money Laundering (AML) & CFT Regulatory Reporting Terminal</p>
</div>
</div>
<button mat-icon-button [mat-dialog-close]="true" class="close-btn">
<mat-icon>close</mat-icon>
</button>
</div>
<!-- Tab Navigators -->
<div class="tab-navs">
<button class="tab-btn" [class.active]="activeTab === 'dashboard'" (click)="activeTab = 'dashboard'">
<fa-icon icon="chart-bar" class="tab-icon"></fa-icon> Compliance Dashboard
</button>
<button class="tab-btn" [class.active]="activeTab === 'guidelines'" (click)="activeTab = 'guidelines'">
<fa-icon icon="university" class="tab-icon"></fa-icon> RBI Regulatory Guidelines
</button>
</div>
<!-- Dialog Body (Tabs) -->
<div class="dlg-body">
<!-- TAB 1: COMPLIANCE DASHBOARD -->
@if (activeTab === 'dashboard') {
<div class="tab-content dashboard-tab">
<!-- SECTION 1: CTR MANAGMENT -->
<div class="dashboard-section card-glass">
<div class="section-hdr">
<div>
<h3>Cash Transaction Reports (CTR - Monthly Cash Registry)</h3>
<p class="section-sub">
Scans and compiles cash transactions exceeding INR 10 Lakhs limit (Single or monthly cumulative cash
deposits/withdrawals).
</p>
</div>
<button class="action-btn primary" [disabled]="isGeneratingCtr" (click)="generateCtrReport()">
@if (isGeneratingCtr) {
<span class="spinner"></span> Generating Report...
} @else {
<fa-icon icon="sync-alt"></fa-icon> Generate Monthly CTR
}
</button>
</div>
<div class="table-actions">
<input
type="text"
placeholder="Search CTR files by month or name..."
[(ngModel)]="ctrFilter"
class="search-input"
/>
</div>
<div class="table-container">
<table class="compliance-table">
<thead>
<tr>
<th>Report Payload Name</th>
<th>Reporting Period</th>
<th>Record Count</th>
<th>Generated By</th>
<th>Generated Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@for (report of filteredCtrReports; track report.id) {
<tr>
<td class="bold-text"><fa-icon icon="file-code" class="xml-icon"></fa-icon> {{ report.name }}</td>
<td>{{ report.month }}</td>
<td class="center-text">
<span class="cnt-badge">{{ report.recordCount }} txns</span>
</td>
<td>{{ report.generatedBy }}</td>
<td>{{ report.generatedAt | date: 'medium' }}</td>
<td><span class="status-badge ready">Ready</span></td>
<td>
<button
class="icon-action-btn"
matTooltip="Download FIU-IND XML Payload"
(click)="downloadCtrFile(report.name)"
>
<fa-icon icon="download"></fa-icon> Download XML
</button>
</td>
</tr>
} @empty {
<tr>
<td colspan="7" class="empty-state">No CTR reports found matching your search.</td>
</tr>
}
</tbody>
</table>
</div>
</div>
<!-- SECTION 2: STR ALERTS PIPELINE -->
<div class="dashboard-section card-glass mt-20">
<div class="section-hdr">
<div>
<h3>Suspicious Transaction Activity Pipeline (STR Management)</h3>
<p class="section-sub">
Behavioral & structural AML alerts generated by KYC profile rules engine. Investigator actions update
state in real-time.
</p>
</div>
<button class="action-btn secondary" (click)="downloadStrReportTemplate()">
<fa-icon icon="file-excel"></fa-icon> Export Active Alerts (CSV)
</button>
</div>
<div class="table-actions">
<input
type="text"
placeholder="Search alerts by Client, CIF, or alert type..."
[(ngModel)]="strFilter"
class="search-input"
/>
</div>
<div class="alerts-grid">
@for (alert of filteredStrAlerts; track alert.id) {
<div class="alert-card" [class]="alert.status.toLowerCase().replace(' ', '-')">
<div class="card-hdr">
<div class="client-info">
<h4>{{ alert.clientName }}</h4>
<span class="cif-number">{{ alert.clientCif }}</span>
</div>
<span class="status-badge" [class]="alert.status.toLowerCase().replace(' ', '-')">{{
alert.status
}}</span>
</div>
<div class="card-body">
<div class="amount-line">
<span class="label">Amount Highlighted:</span>
<span class="amount">₹{{ alert.amount | number: '1.2-2' }}</span>
</div>
<div class="type-line">
<span class="label">Alert Flag:</span>
<span class="type-value">{{ alert.alertType }}</span>
</div>
<p class="desc">{{ alert.description }}</p>
<span class="date-stamp"
><fa-icon icon="clock"></fa-icon> Flagged Date: {{ alert.date | date: 'mediumDate' }}</span
>
</div>
<div class="card-footer">
@if (alert.status === 'Open') {
<button class="card-btn investigate" (click)="changeAlertStatus(alert.id, 'Investigating')">
<fa-icon icon="search"></fa-icon> Investigate
</button>
}
@if (alert.status === 'Open' || alert.status === 'Investigating') {
<button class="card-btn file-str" (click)="changeAlertStatus(alert.id, 'Reported STR')">
<fa-icon icon="flag"></fa-icon> File STR
</button>
<button class="card-btn close-alert" (click)="changeAlertStatus(alert.id, 'Closed')">
<fa-icon icon="ban"></fa-icon> Dismiss
</button>
}
@if (alert.status === 'Closed' || alert.status === 'Reported STR') {
<button class="card-btn reset-alert" (click)="changeAlertStatus(alert.id, 'Open')">
<fa-icon icon="redo-alt"></fa-icon> Reopen Alert
</button>
}
</div>
</div>
} @empty {
<div class="empty-alerts">No Suspicious Transaction alerts matching your criteria.</div>
}
</div>
</div>
</div>
}
<!-- TAB 2: RBI REGULATORY GUIDELINES -->
@if (activeTab === 'guidelines') {
<div class="tab-content scroll-y">
<div class="info-layout">
<!-- CTR Rules Card -->
<div class="info-card card-glass">
<div class="card-title-badge ctr-badge-color">
<fa-icon icon="coins" class="badge-icon"></fa-icon> CTR - Cash Transaction Report
</div>
<div class="rules-body">
<p class="lead-desc">
As mandated by Section 12 of the **Prevention of Money Laundering Act (PMLA), 2002** and RBI KYC
guidelines, banks must record and report all high-value cash transactions.
</p>
<div class="rule-point">
<h5>1. Transaction Threshold</h5>
<p>
All cash transactions (either deposits or withdrawals) of **value more than INR 10 Lakhs** (or its
equivalent in foreign currency) in a single transaction.
</p>
</div>
<div class="rule-point">
<h5>2. Monthly Aggregation Rule (Structuring check)</h5>
<p>
A series of cash transactions in a calendar month that are **integrally connected** and accumulate to
a total value exceeding **INR 10 Lakhs** must be compiled and reported in the monthly CTR.
</p>
</div>
<div class="rule-point">
<h5>3. Reporting Timeline</h5>
<p>
CTRs must be generated and uploaded to the FIU-IND gateway **by the 15th day of the succeeding month**
(e.g., June transactions must be uploaded by July 15th).
</p>
</div>
<div class="rule-point text-warning-border">
<strong>Important Note:</strong> Transactions where the net cash balance does not change but high volume
is rotated (e.g., cash withdrawals immediately deposited back) must be reported under CTR as separate
debit and credit records.
</div>
</div>
</div>
<!-- STR Rules Card -->
<div class="info-card card-glass">
<div class="card-title-badge str-badge-color">
<fa-icon icon="flag" class="badge-icon"></fa-icon> STR - Suspicious Transaction Report
</div>
<div class="rules-body">
<p class="lead-desc">
Every reporting entity must maintain vigilance over account transactions. If there are reasonable
grounds to suspect transactions involve proceeds of crime, an STR must be generated.
</p>
<div class="rule-point">
<h5>1. What Constitutes Suspicion?</h5>
<p>
Transactions that are complex, show unusual patterns, mismatch the customer’s business/risk profile,
have no apparent economic rationale, or involve suspected proceeds of crime or terror funding.
</p>
</div>
<div class="rule-point">
<h5>2. No Minimum Threshold Limit</h5>
<p>
**There is no minimum monetary limit** for STR. An alert should be generated for any transaction,
regardless of size, if suspicious behavior is flagged.
</p>
</div>
<div class="rule-point">
<h5>3. Reporting Timeline</h5>
<p>
The STR must be filed to FIU-IND **within 7 days** of the Principal Compliance Officer confirming the
suspicion.
</p>
</div>
<div class="rule-point text-danger-border">
<strong>Strict Tipping-off Restriction:</strong> It is illegal to disclose or "tip-off" to the client
that their account is being monitored, investigated, or reported under an STR. Complete confidentiality
must be maintained.
</div>
</div>
</div>
</div>
</div>
}
</div>
</div>
@@ -1,750 +0,0 @@
/**
* 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/.
*/
.ctr-str-dlg {
display: flex;
flex-direction: column;
height: 90vh;
max-height: 820px;
background: #f8fafc;
color: #1e293b;
font-family: Inter, sans-serif;
overflow: hidden;
}
// Header
.dlg-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: #fff;
border-bottom: 1px solid #e2e8f0;
.dlg-title-area {
display: flex;
align-items: center;
gap: 14px;
.header-icon {
font-size: 26px;
color: #4f46e5;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 700;
color: #0f172a;
}
.subtitle {
margin: 2px 0 0;
font-size: 12px;
color: #64748b;
}
}
.close-btn {
color: #64748b;
&:hover {
color: #0f172a;
}
}
}
// Navigation Tabs
.tab-navs {
display: flex;
gap: 6px;
background: #fff;
padding: 8px 24px;
border-bottom: 1px solid #e2e8f0;
.tab-btn {
display: flex;
align-items: center;
gap: 8px;
background: transparent;
border: none;
padding: 10px 16px;
font-size: 13px;
font-weight: 600;
color: #64748b;
cursor: pointer;
border-radius: 6px;
transition: all 0.2s ease;
.tab-icon {
font-size: 14px;
}
&:hover {
background: #f1f5f9;
color: #0f172a;
}
&.active {
background: #e0e7ff;
color: #4f46e5;
}
}
}
// Scroll layout
.scroll-y {
overflow-y: auto;
flex: 1;
}
// Dialog Body
.dlg-body {
flex: 1;
overflow: hidden;
padding: 24px;
background: #f1f5f9;
display: flex;
flex-direction: column;
}
.tab-content {
height: 100%;
display: flex;
flex-direction: column;
&.scroll-y {
overflow-y: auto;
padding-right: 6px;
/* Custom scrollbar */
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
&:hover {
background: #94a3b8;
}
}
}
}
// Dashboard Tab specific layout
.dashboard-tab {
overflow-y: auto;
padding-right: 6px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 3px;
}
}
// Card Style Container
.card-glass {
background: #fff;
border-radius: 12px;
box-shadow:
0 4px 6px -1px rgb(0 0 0 / 5%),
0 2px 4px -1px rgb(0 0 0 / 2%);
border: 1px solid #e2e8f0;
padding: 20px;
}
.dashboard-section {
display: flex;
flex-direction: column;
.section-hdr {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
h3 {
margin: 0;
font-size: 15px;
font-weight: 700;
color: #0f172a;
}
.section-sub {
margin: 4px 0 0;
font-size: 12px;
color: #64748b;
}
}
}
.mt-20 {
margin-top: 20px;
}
.mt-25 {
margin-top: 25px;
}
// Search input
.table-actions {
margin-bottom: 14px;
.search-input {
width: 100%;
max-width: 380px;
padding: 8px 12px;
font-size: 13px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: #fff;
color: #1e293b;
outline: none;
&:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 2px rgb(79 70 229 / 10%);
}
}
}
// Button styles
.action-btn {
display: inline-flex;
align-items: center;
gap: 8px;
border: none;
border-radius: 6px;
padding: 10px 16px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
&.primary {
background: #4f46e5;
color: #fff;
&:hover:not([disabled]) {
background: #4338ca;
}
&:disabled {
background: #94a3b8;
cursor: not-allowed;
}
}
&.secondary {
background: #0ea5e9;
color: #fff;
&:hover {
background: #0284c7;
}
}
}
// Spinner animation
.spinner {
width: 12px;
height: 12px;
border: 2px solid rgb(255 255 255 / 30%);
border-radius: 50%;
border-top-color: #fff;
animation: spin 0.8s linear infinite;
display: inline-block;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
// Tables styling
.table-container {
overflow-x: auto;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
.compliance-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
text-align: left;
th {
background: #f8fafc;
color: #475569;
font-weight: 600;
padding: 12px 16px;
border-bottom: 1px solid #e2e8f0;
}
td {
padding: 12px 16px;
border-bottom: 1px solid #f1f5f9;
color: #334155;
&.bold-text {
font-weight: 600;
color: #0f172a;
}
&.center-text {
text-align: center;
}
}
tr:last-child td {
border-bottom: none;
}
.cnt-badge {
background: #e2e8f0;
color: #475569;
padding: 3px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: 500;
}
.xml-icon {
color: #ea580c;
margin-right: 4px;
}
.empty-state {
text-align: center;
color: #64748b;
padding: 24px;
font-style: italic;
}
}
// Icon Action Button
.icon-action-btn {
background: #f1f5f9;
border: 1px solid #cbd5e1;
color: #475569;
border-radius: 4px;
padding: 6px 12px;
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: #e2e8f0;
color: #0f172a;
}
}
// Alert Cards Grid
.alerts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
.alert-card {
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
transition: box-shadow 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 5%);
}
// Left boundary color codes based on status
&.open {
border-left: 4px solid #dc2626;
}
&.investigating {
border-left: 4px solid #ea580c;
}
&.reported-str {
border-left: 4px solid #16a34a;
}
&.closed {
border-left: 4px solid #64748b;
}
.card-hdr {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
h4 {
margin: 0;
font-size: 14px;
font-weight: 700;
color: #0f172a;
}
.cif-number {
font-size: 11px;
color: #64748b;
}
}
.card-body {
flex: 1;
font-size: 12px;
color: #475569;
.amount-line,
.type-line {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
.label {
color: #64748b;
}
.amount {
font-weight: 700;
color: #0f172a;
}
.type-value {
font-weight: 600;
color: #4f46e5;
}
}
.desc {
margin: 8px 0;
line-height: 1.4;
background: #f8fafc;
padding: 8px;
border-radius: 4px;
border: 1px dashed #e2e8f0;
font-style: italic;
}
.date-stamp {
font-size: 11px;
color: #94a3b8;
display: block;
margin-top: 8px;
}
}
.card-footer {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 14px;
border-top: 1px solid #f1f5f9;
padding-top: 12px;
.card-btn {
flex: 1;
min-width: 75px;
padding: 6px 8px;
font-size: 11px;
font-weight: 600;
border: none;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
transition: all 0.2s ease;
&.investigate {
background: #fef3c7;
color: #d97706;
&:hover {
background: #fde68a;
}
}
&.file-str {
background: #dcfce7;
color: #16a34a;
&:hover {
background: #bbf7d0;
}
}
&.close-alert {
background: #fee2e2;
color: #dc2626;
&:hover {
background: #fecaca;
}
}
&.reset-alert {
background: #f1f5f9;
color: #475569;
border: 1px solid #cbd5e1;
&:hover {
background: #e2e8f0;
}
}
}
}
}
// Badges
.status-badge {
padding: 3px 8px;
border-radius: 12px;
font-size: 10px;
font-weight: 600;
&.ready {
background: #dcfce7;
color: #16a34a;
}
&.open {
background: #fee2e2;
color: #dc2626;
}
&.investigating {
background: #fef3c7;
color: #d97706;
}
&.reported-str {
background: #dbeafe;
color: #2563eb;
}
&.closed {
background: #f1f5f9;
color: #475569;
}
}
.empty-alerts {
grid-column: 1 / -1;
text-align: center;
padding: 36px;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 8px;
color: #64748b;
font-style: italic;
font-size: 13px;
}
// Tab 2: Guidelines layout
.info-layout {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
@media (width >= 900px) {
grid-template-columns: 1fr 1fr;
}
}
.info-card {
display: flex;
flex-direction: column;
.card-title-badge {
align-self: flex-start;
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 14px;
border-radius: 20px;
font-size: 12px;
font-weight: 700;
margin-bottom: 16px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #fff;
}
.ctr-badge-color {
background: #ea580c;
}
.str-badge-color {
background: #be185d;
}
.rules-body {
font-size: 13px;
line-height: 1.5;
color: #334155;
.lead-desc {
font-size: 14px;
color: #0f172a;
margin-bottom: 20px;
}
.rule-point {
margin-bottom: 18px;
h5 {
margin: 0 0 6px;
font-size: 13px;
font-weight: 700;
color: #0f172a;
}
p {
margin: 0;
color: #475569;
}
}
.text-warning-border {
border-left: 3px solid #eab308;
background: #fef9c3;
padding: 10px 14px;
border-radius: 0 6px 6px 0;
color: #854d0e;
}
.text-danger-border {
border-left: 3px solid #ef4444;
background: #fee2e2;
padding: 10px 14px;
border-radius: 0 6px 6px 0;
color: #991b1b;
}
}
}
// Tab 3: Architecture & Schema
.arch-container {
display: flex;
flex-direction: column;
h3 {
margin: 0;
font-size: 16px;
font-weight: 700;
color: #0f172a;
}
.desc {
margin: 4px 0 20px;
font-size: 12px;
color: #64748b;
}
}
.arch-section {
h4 {
margin: 0 0 4px;
font-size: 14px;
font-weight: 700;
color: #0f172a;
}
.section-sub {
margin: 0 0 10px;
font-size: 12px;
color: #64748b;
}
}
.code-snippet {
background: #0f172a;
color: #e2e8f0;
padding: 14px;
border-radius: 8px;
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
overflow-x: auto;
line-height: 1.4;
margin: 0;
&.plain {
background: #1e293b;
color: #94a3b8;
}
}
// API Routes
.route-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 12px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
margin-bottom: 8px;
font-size: 12px;
.method {
padding: 3px 8px;
border-radius: 4px;
font-size: 10px;
font-weight: 700;
color: #fff;
min-width: 45px;
text-align: center;
&.get {
background: #16a34a;
}
&.put {
background: #ea580c;
}
&.post {
background: #2563eb;
}
}
.path {
font-family: 'Courier New', Courier, monospace;
font-weight: 700;
color: #0f172a;
}
.route-desc {
color: #475569;
flex: 1;
}
}
@@ -1,279 +0,0 @@
/**
* 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 } from '@angular/common';
import { FormsModule } from '@angular/forms';
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 { MatIconModule } from '@angular/material/icon';
import { FaIconComponent, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import {
faShieldAlt,
faChartBar,
faUniversity,
faCode,
faSyncAlt,
faFileExcel,
faFileCode,
faDownload,
faClock,
faSearch,
faFlag,
faBan,
faRedoAlt,
faCoins
} from '@fortawesome/free-solid-svg-icons';
interface CtrReport {
id: number;
name: string;
month: string;
recordCount: number;
generatedBy: string;
generatedAt: Date;
status: 'Ready' | 'Generating';
}
interface StrAlert {
id: number;
clientName: string;
clientCif: string;
alertType: string;
amount: number;
status: 'Open' | 'Investigating' | 'Reported STR' | 'Closed';
description: string;
date: Date;
}
@Component({
selector: 'mifosx-ctr-str-dialog',
standalone: true,
templateUrl: './ctr-str-dialog.component.html',
styleUrls: ['./ctr-str-dialog.component.scss'],
imports: [
CommonModule,
FormsModule,
MatDialogModule,
MatButtonModule,
MatTabsModule,
MatTooltipModule,
MatIconModule,
FaIconComponent
]
})
export class CtrStrDialogComponent {
dialogRef = inject(MatDialogRef<CtrStrDialogComponent>);
constructor() {
const library = inject(FaIconLibrary);
library.addIcons(
faShieldAlt,
faChartBar,
faUniversity,
faCode,
faSyncAlt,
faFileExcel,
faFileCode,
faDownload,
faClock,
faSearch,
faFlag,
faBan,
faRedoAlt,
faCoins
);
}
activeTab: 'dashboard' | 'guidelines' = 'dashboard';
// Mock State
isGeneratingCtr = false;
ctrFilter = '';
strFilter = '';
ctrReports: CtrReport[] = [
{
id: 1,
name: 'CTR_REPORT_JUNE_2026.xml',
month: 'June 2026',
recordCount: 14,
generatedBy: 'Compliance_Officer_1',
generatedAt: new Date(2026, 6, 5, 10, 30),
status: 'Ready'
},
{
id: 2,
name: 'CTR_REPORT_MAY_2026.xml',
month: 'May 2026',
recordCount: 22,
generatedBy: 'Compliance_Officer_1',
generatedAt: new Date(2026, 5, 4, 14, 15),
status: 'Ready'
},
{
id: 3,
name: 'CTR_REPORT_APRIL_2026.xml',
month: 'April 2026',
recordCount: 9,
generatedBy: 'Compliance_Officer_2',
generatedAt: new Date(2026, 4, 3, 11, 45),
status: 'Ready'
}
];
strAlerts: StrAlert[] = [
{
id: 101,
clientName: 'Aarav Sharma',
clientCif: 'CIF982348',
alertType: 'Profile Mismatch',
amount: 450000,
status: 'Open',
description:
'Student account received a large inward transfer (INR 4.5L) mismatching declared zero/student income category.',
date: new Date(2026, 6, 26)
},
{
id: 102,
clientName: 'Karan Malhotra',
clientCif: 'CIF229482',
alertType: 'Structuring (Smurfing)',
amount: 980000,
status: 'Investigating',
description:
'Multiple cash deposits of INR 2.45L, 2.45L, 2.45L, 2.45L within 10 days, totaling INR 9.8L cash (deliberately staying below the 10L single cash transaction CTR threshold).',
date: new Date(2026, 6, 24)
},
{
id: 103,
clientName: 'Nexora Real Estate Ltd',
clientCif: 'CIF500481',
alertType: 'Turnover Outlier',
amount: 15000000,
status: 'Open',
description:
'Entity account registered an outward wire of INR 1.5Cr when maximum monthly turnover limit is set to INR 50L.',
date: new Date(2026, 6, 20)
},
{
id: 104,
clientName: 'Priyanka Patel',
clientCif: 'CIF774920',
alertType: 'Dormant Account Reactivation',
amount: 600000,
status: 'Reported STR',
description:
'Account inactive for 18 months suddenly received a cash deposit of INR 6L, followed by immediate online transfers.',
date: new Date(2026, 6, 15)
}
];
get filteredCtrReports() {
return this.ctrReports.filter(
(r) =>
r.name.toLowerCase().includes(this.ctrFilter.toLowerCase()) ||
r.month.toLowerCase().includes(this.ctrFilter.toLowerCase())
);
}
get filteredStrAlerts() {
return this.strAlerts.filter(
(a) =>
a.clientName.toLowerCase().includes(this.strFilter.toLowerCase()) ||
a.clientCif.toLowerCase().includes(this.strFilter.toLowerCase()) ||
a.alertType.toLowerCase().includes(this.strFilter.toLowerCase())
);
}
// Actions
generateCtrReport() {
if (this.isGeneratingCtr) {
return;
}
this.isGeneratingCtr = true;
// Simulate API call to Spring Boot batch job
setTimeout(() => {
const date = new Date();
const newReport: CtrReport = {
id: this.ctrReports.length + 1,
name: `CTR_REPORT_JULY_2026_DRAFT.xml`,
month: 'July 2026 (Draft)',
recordCount: 8,
generatedBy: 'Current_Admin',
generatedAt: date,
status: 'Ready'
};
this.ctrReports.unshift(newReport);
this.isGeneratingCtr = false;
}, 2000);
}
downloadCtrFile(reportName: string) {
const dummyXml = `<?xml version="1.0" encoding="UTF-8"?>
<FIU-IND-CTR-Report xmlns="http://fiuindia.gov.in/ctr/v1">
<Header>
<ReportType>CTR</ReportType>
<ReportingEntityName>CBS Nexora Bank</ReportingEntityName>
<ReportingPeriod>2026-06</ReportingPeriod>
</Header>
<ReportData>
<Record>
<TransactionID>TXN88924824</TransactionID>
<Date>2026-06-15</Date>
<Mode>CASH</Mode>
<Amount>1250000.00</Amount>
<AccountType>SAVINGS</AccountType>
<CustomerName>Aarav Sharma</CustomerName>
<CifNumber>CIF982348</CifNumber>
</Record>
</ReportData>
</FIU-IND-CTR-Report>`;
const blob = new Blob([dummyXml], { type: 'application/xml' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = reportName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
changeAlertStatus(alertId: number, newStatus: 'Open' | 'Investigating' | 'Reported STR' | 'Closed') {
const alert = this.strAlerts.find((a) => a.id === alertId);
if (alert) {
alert.status = newStatus;
}
}
downloadStrReportTemplate() {
const dummyCsv =
`AlertID,ClientName,CIF,AlertType,Amount,Status,Description\n` +
this.strAlerts
.map(
(a) =>
`"${a.id}","${a.clientName}","${a.clientCif}","${a.alertType}","${a.amount}","${a.status}","${a.description.replace(/"/g, '""')}"`
)
.join('\n');
const blob = new Blob([dummyCsv], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `STR_AML_ALERTS_EXPORT.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
}
@@ -165,12 +165,12 @@
<div class="da-compare"> <div class="da-compare">
<div class="da-item"> <div class="da-item">
<div class="da-label">Total Deposits</div> <div class="da-label">Total Deposits</div>
<div class="da-value deposit">&#8377; 3,264.42 Cr</div> <div class="da-value deposit">&#8377; 11,540.55 Cr</div>
<div class="da-bar"><div class="da-fill deposit-fill" style="width: 78%"></div></div> <div class="da-bar"><div class="da-fill deposit-fill" style="width: 71%"></div></div>
</div> </div>
<div class="da-item"> <div class="da-item">
<div class="da-label">Total Advances</div> <div class="da-label">Total Advances</div>
<div class="da-value advance">&#8377; 4,162.65 Cr</div> <div class="da-value advance">&#8377; 16,277.11 Cr</div>
<div class="da-bar"><div class="da-fill advance-fill" style="width: 100%"></div></div> <div class="da-bar"><div class="da-fill advance-fill" style="width: 100%"></div></div>
</div> </div>
</div> </div>
@@ -0,0 +1,169 @@
<!--
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="fdrd-dlg">
<!-- HEADER -->
<div class="fdrd-head">
<div class="fdrd-head-left">
<div class="fdrd-head-ic"><fa-icon icon="piggy-bank"></fa-icon></div>
<div>
<h2 class="fdrd-title">FD / RD Entry</h2>
<p class="fdrd-sub">Open a new Fixed Deposit or Recurring Deposit account.</p>
</div>
</div>
<div class="fdrd-head-actions">
<span class="fdrd-meta"> <fa-icon icon="university"></fa-icon>&nbsp;{{ bank.name }} </span>
<span class="fdrd-meta"> <fa-icon icon="map-marker-alt"></fa-icon>&nbsp;{{ bank.branch }} </span>
<button class="fdrd-close" (click)="close()" matTooltip="Close" aria-label="Close">&#10005;</button>
</div>
</div>
<div class="fdrd-body">
<!-- Success banner -->
<div class="fdrd-success" *ngIf="saved">
<fa-icon icon="check-circle"></fa-icon>
<div>
<strong>{{ lastAccountNo }}</strong> created for <strong>{{ lastClientName }}</strong>
<div class="fdrd-success-sub">You can enter another deposit below, or close this dialog.</div>
</div>
</div>
<!-- FORM -->
<div class="fdrd-section">
<!-- Row 1: Deposit Info | Client & Amount -->
<div class="ws-group-row">
<div class="ws-group">
<span class="ws-caption">Deposit Info</span>
<div class="ws-group__fields">
<div class="ws-f" style="width: 200px">
<label class="ws-f__lbl">Deposit Type</label>
<select class="ws-f__ctl" [(ngModel)]="depositType" (ngModelChange)="onDepositTypeChange()">
<option *ngFor="let dt of depositTypes" [value]="dt.code">{{ dt.label }}</option>
</select>
</div>
<div class="ws-f" style="width: 150px">
<label class="ws-f__lbl">Pay Mode</label>
<select class="ws-f__ctl" [(ngModel)]="payMode">
<option *ngFor="let m of payModes" [value]="m">{{ m }}</option>
</select>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Client & Amount</span>
<div class="ws-group__fields">
<div class="ws-f" style="flex: 1.5; min-width: 180px">
<label class="ws-f__lbl">Client</label>
<select class="ws-f__ctl" [(ngModel)]="clientId">
<option value="" disabled>-- Select Client --</option>
<option *ngFor="let c of clients" [value]="c.id">{{ c.name }} ({{ c.id }})</option>
</select>
</div>
<div class="ws-f" style="flex: 1; min-width: 120px">
<label class="ws-f__lbl">{{ isRecurring ? 'Monthly Instalment (₹)' : 'Deposit Amount (₹)' }}</label>
<input
class="ws-f__ctl ws-f__ctl--mono"
type="number"
min="0"
[(ngModel)]="principal"
placeholder="e.g. 100000"
/>
</div>
</div>
</div>
</div>
<!-- Row 2: Term & Rate | Instructions -->
<div class="ws-group-row ws-group-row--mt">
<div class="ws-group">
<span class="ws-caption">Interest & Tenure</span>
<div class="ws-group__fields">
<div class="ws-f" style="width: 110px">
<label class="ws-f__lbl">Int. Rate (% p.a.)</label>
<input class="ws-f__ctl" type="number" min="0" step="0.05" [(ngModel)]="interestRate" />
</div>
<div class="ws-f" style="width: 240px">
<label class="ws-f__lbl">Tenure</label>
<div class="fdrd-tenure-row">
<input class="ws-f__ctl" type="number" min="0" [(ngModel)]="tenureYears" />
<span>Yrs</span>
<input class="ws-f__ctl" type="number" min="0" max="11" [(ngModel)]="tenureMonths" />
<span>Mo</span>
<input class="ws-f__ctl" type="number" min="0" max="30" [(ngModel)]="tenureDays" />
<span>Days</span>
</div>
</div>
</div>
</div>
<div class="ws-vdivider"></div>
<div class="ws-group ws-group--flex">
<span class="ws-caption">Nominee & Maturity Details</span>
<div class="ws-group__fields">
<div class="ws-f" style="flex: 1; min-width: 120px">
<label class="ws-f__lbl">Interest Payout</label>
<select class="ws-f__ctl" [(ngModel)]="interestPayout">
<option *ngFor="let o of interestPayoutOptions" [value]="o">{{ o }}</option>
</select>
</div>
<div class="ws-f" style="flex: 1; min-width: 130px">
<label class="ws-f__lbl">Nominee Name (optional)</label>
<input class="ws-f__ctl" type="text" [(ngModel)]="nomineeName" placeholder="Spouse / Child name" />
</div>
<div class="ws-f" style="flex: 1.2; min-width: 160px">
<label class="ws-f__lbl">Maturity Instruction</label>
<select class="ws-f__ctl" [(ngModel)]="maturityInstruction">
<option *ngFor="let m of maturityInstructions" [value]="m">{{ m }}</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- SUMMARY -->
<div class="fdrd-summary">
<div class="fdrd-stat">
<div class="fdrd-stat-l">Principal</div>
<div class="fdrd-stat-v">&#8377; {{ principal || 0 | number: '1.0-2' }}</div>
</div>
<div class="fdrd-stat">
<div class="fdrd-stat-l">Tenure</div>
<div class="fdrd-stat-v">{{ tenureYears }}y {{ tenureMonths }}m {{ tenureDays }}d</div>
</div>
<div class="fdrd-stat">
<div class="fdrd-stat-l">Maturity Date</div>
<div class="fdrd-stat-v">{{ maturityDate | date: 'dd-MM-yyyy' }}</div>
</div>
<div class="fdrd-stat fdrd-stat-good">
<div class="fdrd-stat-l">Maturity Amount</div>
<div class="fdrd-stat-v">&#8377; {{ maturityAmount | number: '1.2-2' }}</div>
</div>
</div>
</div>
<!-- FOOTER -->
<div class="fdrd-foot">
<div class="fdrd-foot-l">
<fa-icon icon="info-circle"></fa-icon>&nbsp; Demo entry — quarterly-compounded interest, no real account is
created.
</div>
<div class="fdrd-foot-actions">
<button mat-stroked-button class="fdrd-btn-ghost" (click)="resetForm()">
<fa-icon icon="undo"></fa-icon>&nbsp; Reset
</button>
<button mat-stroked-button class="fdrd-btn-save" [disabled]="!isValid" (click)="save()">
<fa-icon icon="save"></fa-icon>&nbsp; Save
</button>
</div>
</div>
</div>
@@ -0,0 +1,395 @@
/**
* 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 {
display: block;
font-family: 'Hanken Grotesk', Inter, sans-serif;
color: #1a2a3e;
}
.fdrd-dlg {
background: #fff;
border-radius: 0;
display: flex;
flex-direction: column;
overflow: hidden;
height: 100vh;
}
/* HEADER - Matches .ws-header in clerk dashboard */
.fdrd-head {
padding: 0 12px;
height: 32px;
background: linear-gradient(90deg, #0d1b2e 0%, #162542 100%);
border-bottom: 2px solid #e87722;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-shrink: 0;
}
.fdrd-head-left {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
}
.fdrd-head-ic {
width: 22px;
height: 22px;
border-radius: 6px;
background: #e87722;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
flex-shrink: 0;
}
.fdrd-title {
margin: 0;
font-family: Newsreader, Georgia, serif;
font-size: 14px;
font-weight: 500;
color: #e8f0fb;
letter-spacing: 0.01em;
}
.fdrd-sub {
display: none; /* Hide subtitle in header to match clean style */
}
.fdrd-head-actions {
display: flex;
align-items: center;
gap: 12px;
}
.fdrd-meta {
display: inline-flex;
align-items: center;
font-size: 11px;
color: #7a95b0;
background: transparent;
border: none;
padding: 0;
fa-icon {
color: #5a7589;
margin-right: 4px;
}
}
.fdrd-close {
background: transparent;
border: none;
color: #7a95b0;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 4px;
transition: all 0.15s;
&:hover {
background: rgb(255 255 255 / 10%);
color: #fff;
}
}
/* BODY - Styled like canvas + card grid */
.fdrd-body {
padding: 16px 20px;
overflow-y: auto;
flex: 1;
background: #f0f4f9;
}
.fdrd-success {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 16px;
border-radius: 10px;
background: #dcfce7;
color: #15803d;
margin-bottom: 14px;
font-size: 13px;
fa-icon {
font-size: 16px;
margin-top: 1px;
}
}
.fdrd-success-sub {
margin-top: 2px;
font-size: 11px;
font-weight: 400;
opacity: 0.85;
}
/* Card container for form */
.fdrd-section {
background: #fff;
border: 1px solid #dce4ef;
border-radius: 10px;
padding: 16px 20px;
margin-bottom: 16px;
}
/* Transaction Entry Style Layout mappings */
.ws-group-row {
display: flex;
align-items: stretch;
gap: 0;
&--mt {
margin-top: 12px;
}
}
.ws-group {
display: flex;
flex-direction: column;
gap: 4px;
flex-shrink: 0;
&--flex {
flex: 1;
min-width: 0;
}
&__fields {
display: flex;
align-items: flex-end;
gap: 8px;
flex-wrap: nowrap;
height: 100%;
align-content: flex-end;
}
}
.ws-caption {
font-family: Newsreader, Georgia, serif;
font-style: italic;
font-size: 11px;
color: #7a95b0;
padding-bottom: 2px;
}
.ws-vdivider {
width: 1px;
background: #dce4ef;
margin: 12px 10px 0;
flex-shrink: 0;
}
.ws-f {
display: flex;
flex-direction: column;
gap: 3px;
flex-shrink: 0;
&__lbl {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #7a95b0;
white-space: nowrap;
}
&__ctl {
height: 26px;
padding: 0 7px;
border: 1px solid #c8d8eb;
border-radius: 6px;
font-family: 'Hanken Grotesk', Inter, sans-serif;
font-size: 11.5px;
color: #1a2a3e;
background: #f4f7fc;
box-sizing: border-box;
width: 100%;
transition:
border-color 0.15s,
box-shadow 0.15s;
&:hover {
border-color: #9ab5d0;
}
&:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgb(37 99 235 / 12%);
background: #fff;
}
&--mono {
font-family: 'Roboto Mono', monospace;
font-size: 11.5px;
letter-spacing: 0.03em;
}
}
}
.fdrd-tenure-row {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
.ws-f__ctl {
width: 100%;
min-width: 0;
}
span {
font-size: 10px;
font-weight: 600;
color: #7a95b0;
text-transform: uppercase;
}
}
/* SUMMARY - Styled like info summary block */
.fdrd-summary {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
padding: 12px 16px;
background: #f4f7fc;
border: 1px solid #c8d8eb;
border-radius: 10px;
margin-top: 16px;
}
.fdrd-stat {
display: flex;
flex-direction: column;
gap: 2px;
}
.fdrd-stat-l {
font-size: 10px;
font-weight: 600;
color: #7a95b0;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.fdrd-stat-v {
font-size: 14px;
font-weight: 700;
color: #1a2a3e;
}
.fdrd-stat-good .fdrd-stat-v {
color: #1a7a4a;
}
/* FOOTER - Styled like action panel */
.fdrd-foot {
padding: 10px 16px;
border-top: 1px solid #dce4ef;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-shrink: 0;
background: #f0f4f9;
}
.fdrd-foot-l {
font-size: 11px;
color: #7a95b0;
display: flex;
align-items: center;
gap: 4px;
}
.fdrd-foot-actions {
display: flex;
gap: 8px;
}
.fdrd-btn-ghost {
display: inline-flex;
align-items: center;
gap: 4px;
height: 28px;
padding: 0 12px !important;
border-radius: 7px;
border: 1px solid #dce4ef !important;
background: #fff !important;
color: #1a2a3e !important;
font-family: 'Hanken Grotesk', sans-serif;
font-size: 11.5px !important;
font-weight: 600 !important;
cursor: pointer;
box-shadow: none !important;
&:hover {
background: #f0f4f9 !important;
border-color: #a8bdd4 !important;
}
}
.fdrd-btn-save {
display: inline-flex;
align-items: center;
gap: 4px;
height: 28px;
padding: 0 16px !important;
border-radius: 7px;
border: none !important;
background: #e87722 !important;
color: #fff !important;
font-family: 'Hanken Grotesk', sans-serif;
font-size: 11.5px !important;
font-weight: 700 !important;
cursor: pointer;
box-shadow: 0 2px 8px rgb(232 119 34 / 30%) !important;
&:hover:not(:disabled) {
filter: brightness(1.08);
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
background: #c8d8eb !important;
box-shadow: none !important;
}
}
@media (width <= 768px) {
.ws-group-row {
flex-direction: column;
gap: 12px;
}
.ws-vdivider {
display: none;
}
.ws-group__fields {
flex-wrap: wrap;
}
.fdrd-summary {
grid-template-columns: repeat(2, 1fr);
}
}
@@ -0,0 +1,146 @@
/**
* 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 { MatSnackBar } from '@angular/material/snack-bar';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
type DepositType = 'FD-SINGLE' | 'FD-JOINT' | 'RD-MONTHLY' | 'RD-QUARTERLY';
interface DemoClient {
id: string;
name: string;
}
@Component({
selector: 'mifosx-fd-rd-entry-dialog',
standalone: true,
templateUrl: './fd-rd-entry-dialog.component.html',
styleUrls: ['./fd-rd-entry-dialog.component.scss'],
imports: [CommonModule, DecimalPipe, FormsModule, MatDialogModule, MatButtonModule, MatTooltipModule, FaIconComponent]
})
export class FdRdEntryDialogComponent {
private dialogRef = inject(MatDialogRef<FdRdEntryDialogComponent>);
private snackBar = inject(MatSnackBar);
bank = {
name: 'CBSNexora Bank Ltd',
branch: 'Head Office'
};
depositTypes: { code: DepositType; label: string; defaultRate: number }[] = [
{ code: 'FD-SINGLE', label: 'Fixed Deposit — Single A/c', defaultRate: 7.25 },
{ code: 'FD-JOINT', label: 'Fixed Deposit — Joint A/c', defaultRate: 7.25 },
{ code: 'RD-MONTHLY', label: 'Recurring Deposit — Monthly', defaultRate: 7.0 },
{ code: 'RD-QUARTERLY', label: 'Recurring Deposit — Quarterly', defaultRate: 7.0 }
];
clients: DemoClient[] = [
{ id: 'C-10231', name: 'Ramesh Kumar Sharma' },
{ id: 'C-10344', name: 'Priya Enterprises' },
{ id: 'C-10412', name: 'Anjali Verma' },
{ id: 'C-10567', name: 'Suresh & Sons HUF' },
{ id: 'C-10689', name: 'Vikram Traders' }
];
payModes = ['Cash', 'Cheque', 'Transfer from Savings A/c'];
interestPayoutOptions = ['Cumulative (Paid on Maturity)', 'Monthly Payout', 'Quarterly Payout'];
maturityInstructions = [
'Renew Principal & Interest',
'Renew Principal Only, Payout Interest',
'Payout on Maturity (No Renewal)'
];
depositType: DepositType = 'FD-SINGLE';
clientId = '';
principal: number | null = null;
interestRate = 7.25;
tenureYears = 1;
tenureMonths = 0;
tenureDays = 0;
interestPayout = this.interestPayoutOptions[0];
payMode = this.payModes[0];
nomineeName = '';
maturityInstruction = this.maturityInstructions[0];
saved = false;
lastAccountNo = '';
lastClientName = '';
get selectedClient(): DemoClient | undefined {
return this.clients.find((c) => c.id === this.clientId);
}
onDepositTypeChange(): void {
const dt = this.depositTypes.find((d) => d.code === this.depositType);
if (dt) this.interestRate = dt.defaultRate;
}
get isRecurring(): boolean {
return this.depositType.startsWith('RD');
}
get tenureInYears(): number {
return this.tenureYears + this.tenureMonths / 12 + this.tenureDays / 365;
}
get maturityDate(): Date {
const d = new Date();
d.setFullYear(d.getFullYear() + this.tenureYears);
d.setMonth(d.getMonth() + this.tenureMonths);
d.setDate(d.getDate() + this.tenureDays);
return d;
}
/** Quarterly-compounded maturity value, the standard FD/RD convention */
get maturityAmount(): number {
const p = this.principal || 0;
const t = this.tenureInYears;
if (p <= 0 || t <= 0) return 0;
return p * Math.pow(1 + this.interestRate / 100 / 4, 4 * t);
}
get isValid(): boolean {
return !!this.clientId && (this.principal ?? 0) > 0 && this.tenureInYears > 0;
}
save(): void {
if (!this.isValid) return;
const prefix = this.isRecurring ? 'RD' : 'FD';
this.lastAccountNo = `${prefix}${Math.floor(1000000 + Math.random() * 8999999)}`;
this.lastClientName = this.selectedClient?.name ?? '';
this.saved = true;
this.snackBar.open(`${prefix} account ${this.lastAccountNo} created for ${this.lastClientName}`, 'Close', {
duration: 4000
});
}
resetForm(): void {
this.depositType = 'FD-SINGLE';
this.clientId = '';
this.principal = null;
this.interestRate = 7.25;
this.tenureYears = 1;
this.tenureMonths = 0;
this.tenureDays = 0;
this.nomineeName = '';
this.interestPayout = this.interestPayoutOptions[0];
this.payMode = this.payModes[0];
this.maturityInstruction = this.maturityInstructions[0];
this.saved = false;
}
close(): void {
this.dialogRef.close();
}
}
@@ -63,7 +63,7 @@ export class PslReportDialogComponent {
activeTab: 'dashboard' | 'categories' | 'register' | 'returns' = 'dashboard'; activeTab: 'dashboard' | 'categories' | 'register' | 'returns' = 'dashboard';
bank = { bank = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
quarter: 'Q3 FY 2025-26', quarter: 'Q3 FY 2025-26',
asOnDate: '29-01-2026', asOnDate: '29-01-2026',
@@ -0,0 +1,87 @@
<!--
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="container aml-rules-container">
<mat-card class="aml-card">
<mat-card-header class="aml-card-header">
<div class="aml-header-content">
<mat-icon class="header-icon">shield</mat-icon>
<div>
<mat-card-title class="header-title">AML Rules Master</mat-card-title>
<mat-card-subtitle class="header-subtitle">
Database-driven rules for client risk categorization and AML compliance checks
</mat-card-subtitle>
</div>
</div>
</mat-card-header>
<mat-card-content class="aml-card-content">
<table mat-table [dataSource]="rules" class="aml-table mat-elevation-z1">
<!-- Order Position Column -->
<ng-container matColumnDef="orderPosition">
<th mat-header-cell *matHeaderCellDef class="col-center">Rule Srno</th>
<td mat-cell *matCellDef="let element" class="col-center">{{ element.orderPosition }}</td>
</ng-container>
<!-- Rule Name Column -->
<ng-container matColumnDef="ruleName">
<th mat-header-cell *matHeaderCellDef>Rule Description</th>
<td mat-cell *matCellDef="let element" class="rule-name-cell">{{ element.ruleName }}</td>
</ng-container>
<!-- Risk Category Column -->
<ng-container matColumnDef="riskCategory">
<th mat-header-cell *matHeaderCellDef class="col-center">Risk Category</th>
<td mat-cell *matCellDef="let element" class="col-center">
<span class="risk-badge" [ngClass]="getRiskColorClass(element.riskCategory)">
<span class="dot"></span>
{{ getRiskCategoryLabel(element.riskCategory) }}
</span>
</td>
</ng-container>
<!-- Reason Column -->
<ng-container matColumnDef="reason">
<th mat-header-cell *matHeaderCellDef>Rule Reason / Criteria</th>
<td mat-cell *matCellDef="let element" class="reason-cell">{{ element.reason }}</td>
</ng-container>
<!-- AML Check Status Column -->
<ng-container matColumnDef="amlCheckStatus">
<th mat-header-cell *matHeaderCellDef class="col-center">AML Check Status</th>
<td mat-cell *matCellDef="let element" class="col-center">
<span class="compliance-badge" [ngClass]="'status-' + (element.amlCheckStatus || 'clear').toLowerCase()">
{{ element.amlCheckStatus || 'CLEAR' }}
</span>
</td>
</ng-container>
<!-- Banned Entity Match Column -->
<ng-container matColumnDef="bannedEntityMatch">
<th mat-header-cell *matHeaderCellDef class="col-center">Banned Match</th>
<td mat-cell *matCellDef="let element" class="col-center">
<span class="compliance-badge" [ngClass]="'match-' + (element.bannedEntityMatch || 'no').toLowerCase()">
{{ element.bannedEntityMatch || 'NO' }}
</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns" class="rule-row"></tr>
</table>
<!-- Mat Paginator for standard pagination -->
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons class="aml-paginator"></mat-paginator>
<div *ngIf="rules.data.length === 0" class="no-rules-message">
<mat-icon>info_outline</mat-icon>
<span>No AML rules found in the database.</span>
</div>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,228 @@
/**
* 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/.
*/
.aml-rules-container {
padding: 24px;
background-color: var(--bg-neutral-light, #f8fafc);
min-height: calc(100vh - 64px);
}
.aml-card {
border-radius: 12px;
box-shadow:
0 4px 20px -2px rgb(0 0 0 / 5%),
0 2px 8px -1px rgb(0 0 0 / 3%);
border: 1px solid rgb(226 232 240 / 80%);
background: #fff;
overflow: hidden;
}
.aml-card-header {
background: linear-gradient(135deg, #1e293b, #0f172a);
color: #fff;
padding: 24px;
}
.aml-header-content {
display: flex;
align-items: center;
gap: 16px;
.header-icon {
font-size: 32px;
width: 32px;
height: 32px;
color: #38bdf8;
}
.header-title {
font-size: 20px;
font-weight: 600;
letter-spacing: -0.02em;
color: #fff;
margin-bottom: 4px;
}
.header-subtitle {
font-size: 13px;
color: #94a3b8;
}
}
.aml-card-content {
padding: 24px;
}
.aml-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e2e8f0;
th {
background-color: #f1f5f9;
color: #475569;
font-weight: 600;
font-size: 13px;
padding: 16px;
text-transform: uppercase;
letter-spacing: 0.05em;
border-bottom: 1px solid #cbd5e1;
}
td {
padding: 16px;
font-size: 14px;
color: #334155;
border-bottom: 1px solid #e2e8f0;
}
.rule-row {
transition: background-color 0.2s ease;
&:hover {
background-color: #f8fafc;
}
&:last-child td {
border-bottom: none;
}
}
}
.col-center {
text-align: center;
justify-content: center;
}
.rule-name-cell {
font-weight: 500;
color: #1e293b;
max-width: 300px;
}
.reason-cell {
color: #64748b;
font-size: 13px;
max-width: 400px;
}
/* Badges for Risk Category */
.risk-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
&.risk-high {
background-color: #fef2f2;
color: #ef4444;
border: 1px solid #fee2e2;
.dot {
background-color: #ef4444;
}
}
&.risk-medium {
background-color: #fffbeb;
color: #d97706;
border: 1px solid #fef3c7;
.dot {
background-color: #d97706;
}
}
&.risk-low {
background-color: #f0fdf4;
color: #16a34a;
border: 1px solid #dcfce7;
.dot {
background-color: #16a34a;
}
}
}
/* Badges for Compliance status */
.compliance-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
/* AML Statuses */
&.status-clear {
background-color: #dcfce7;
color: #15803d;
}
&.status-flagged {
background-color: #fef3c7;
color: #b45309;
}
&.status-blocked {
background-color: #fee2e2;
color: #b91c1c;
}
/* Banned Matches */
&.match-no {
background-color: #f1f5f9;
color: #475569;
}
&.match-yes {
background-color: #fee2e2;
color: #b91c1c;
}
&.match-partial_match {
background-color: #ffedd5;
color: #c2410c;
}
}
.no-rules-message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 48px;
color: #64748b;
gap: 12px;
mat-icon {
font-size: 48px;
width: 48px;
height: 48px;
}
}
.aml-paginator {
background: #fff;
border-top: 1px solid #e2e8f0;
padding: 8px 0;
font-size: 13px;
color: #475569;
}
@@ -0,0 +1,87 @@
/**
* 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/.
*/
/** Angular Imports */
import { Component, OnInit, inject, ViewChild } from '@angular/core';
import { MatTableModule, MatTableDataSource } from '@angular/material/table';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
import { CommonModule } from '@angular/common';
/** Custom Services */
import { ClientsService } from '../../clients/clients.service';
@Component({
selector: 'mifosx-aml-rules',
templateUrl: './aml-rules.component.html',
styleUrls: ['./aml-rules.component.scss'],
standalone: true,
imports: [
CommonModule,
MatTableModule,
MatCardModule,
MatIconModule,
MatButtonModule,
MatCheckboxModule,
MatPaginatorModule
]
})
export class AmlRulesComponent implements OnInit {
private clientsService = inject(ClientsService);
rules = new MatTableDataSource<any>([]);
displayedColumns: string[] = [
'orderPosition',
'ruleName',
'riskCategory',
'reason',
'amlCheckStatus',
'bannedEntityMatch'
];
@ViewChild(MatPaginator) paginator!: MatPaginator;
ngOnInit(): void {
this.clientsService.getClientRiskRules().subscribe({
next: (data) => {
this.rules.data = data || [];
this.rules.paginator = this.paginator;
},
error: () => {}
});
}
getRiskCategoryLabel(category: number): string {
switch (category) {
case 1:
return 'Low Risk';
case 2:
return 'Medium Risk';
case 3:
return 'High Risk';
default:
return 'Unknown';
}
}
getRiskColorClass(category: number): string {
switch (category) {
case 1:
return 'risk-low';
case 2:
return 'risk-medium';
case 3:
return 'risk-high';
default:
return '';
}
}
}
@@ -11,6 +11,7 @@ import { Routes, RouterModule } from '@angular/router';
import { Route } from '../core/route/route.service'; import { Route } from '../core/route/route.service';
import { RbiCircularsComponent } from './rbi-circulars.component'; import { RbiCircularsComponent } from './rbi-circulars.component';
import { AmlRulesComponent } from './aml-rules/aml-rules.component';
const routes: Routes = [ const routes: Routes = [
Route.withShell([ Route.withShell([
@@ -18,6 +19,11 @@ const routes: Routes = [
path: 'rbi-circulars', path: 'rbi-circulars',
component: RbiCircularsComponent, component: RbiCircularsComponent,
data: { title: 'RBI Circulars', breadcrumb: 'RBI Circulars' } data: { title: 'RBI Circulars', breadcrumb: 'RBI Circulars' }
},
{
path: 'rbi/aml',
component: AmlRulesComponent,
data: { title: 'AML Rules Master', breadcrumb: 'AML Rules Master' }
} }
]) ])
]; ];
+11 -11
View File
@@ -141,11 +141,11 @@ export interface SftInterestDividendRecord {
} }
export const SFT_BANK = { export const SFT_BANK = {
name: 'Bharat Urban Bank Ltd', name: 'CBSNexora Bank Ltd',
branch: 'Head Office', branch: 'Head Office',
filerPan: 'AABCB1100M', filerPan: 'AABCB1100M',
filerTan: 'MUMR99001E', filerTan: 'MUMR99001E',
itdReportingPortalId: 'BHARATURBAN-285BA', itdReportingPortalId: 'CBSNEXORA-285BA',
fy: '2025-26', fy: '2025-26',
filingDueDate: '31-05-2026', filingDueDate: '31-05-2026',
principalOfficer: 'Karthik Kamath', principalOfficer: 'Karthik Kamath',
@@ -159,7 +159,7 @@ export const SFT_BANK = {
branchStdCode: '022', branchStdCode: '022',
branchPhone: '24938000', branchPhone: '24938000',
branchMobile: '9892000100', branchMobile: '9892000100',
branchEmail: 'compliance@bharaturbanbank.example' branchEmail: 'compliance@cbsnexorabank.example'
}; };
/** ── SFT-001 / 002 / 004 — Person-wise records ── */ /** ── SFT-001 / 002 / 004 — Person-wise records ── */
@@ -202,7 +202,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 1500000, cashAmountReceivedFromPerson: 1500000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001101000838', relatedAccountNumber: '110001101000838',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-001' sftCode: 'SFT-001'
}, },
@@ -243,7 +243,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 1100000, cashAmountReceivedFromPerson: 1100000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001352000357', relatedAccountNumber: '110001352000357',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-001' sftCode: 'SFT-001'
}, },
@@ -285,7 +285,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 2500000, cashAmountReceivedFromPerson: 2500000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001352000420', relatedAccountNumber: '110001352000420',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-002' sftCode: 'SFT-002'
}, },
@@ -326,7 +326,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 1800000, cashAmountReceivedFromPerson: 1800000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001352000511', relatedAccountNumber: '110001352000511',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-002' sftCode: 'SFT-002'
}, },
@@ -368,7 +368,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 1250000, cashAmountReceivedFromPerson: 1250000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001101001145', relatedAccountNumber: '110001101001145',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-004' sftCode: 'SFT-004'
}, },
@@ -409,7 +409,7 @@ export const SFT_PERSON_RECORDS: SftPersonRecord[] = [
cashAmountReceivedFromPerson: 2150000, cashAmountReceivedFromPerson: 2150000,
amountPaidToPerson: 0, amountPaidToPerson: 0,
relatedAccountNumber: '110001101001458', relatedAccountNumber: '110001101001458',
relatedInstituteName: 'BHARAT URBAN BANK', relatedInstituteName: 'CBSNEXORA BANK',
relatedInstituteRefNumber: 'YESB0BHRT', relatedInstituteRefNumber: 'YESB0BHRT',
sftCode: 'SFT-004' sftCode: 'SFT-004'
} }
@@ -436,7 +436,7 @@ export const SFT_ACCOUNT_RECORDS: SftAccountRecord[] = [
branchMobile: '9892000100', branchMobile: '9892000100',
branchFaxStd: '022', branchFaxStd: '022',
branchFaxPhone: '24938011', branchFaxPhone: '24938011',
branchEmail: 'compliance@bharaturbanbank.example', branchEmail: 'compliance@cbsnexorabank.example',
aggGrossCashCredited: 6500000, aggGrossCashCredited: 6500000,
aggGrossCashDebited: 4200000, aggGrossCashDebited: 4200000,
amountCreditedBeforeDemonetization: 0, amountCreditedBeforeDemonetization: 0,
@@ -485,7 +485,7 @@ export const SFT_ACCOUNT_RECORDS: SftAccountRecord[] = [
branchMobile: '9892000100', branchMobile: '9892000100',
branchFaxStd: '022', branchFaxStd: '022',
branchFaxPhone: '24938011', branchFaxPhone: '24938011',
branchEmail: 'compliance@bharaturbanbank.example', branchEmail: 'compliance@cbsnexorabank.example',
aggGrossCashCredited: 5800000, aggGrossCashCredited: 5800000,
aggGrossCashDebited: 3450000, aggGrossCashDebited: 3450000,
amountCreditedBeforeDemonetization: 0, amountCreditedBeforeDemonetization: 0,
+16
View File
@@ -1017,6 +1017,22 @@ mat-card {
} }
} }
/* ─── Full-Screen Dialog Panel (e.g. FD/RD Entry) — edge-to-edge, no rounded corners ─── */
.fdrd-fullscreen-panel {
max-width: 100vw !important;
width: 100vw !important;
height: 100vh !important;
max-height: 100vh !important;
.mat-mdc-dialog-container {
border-radius: 0 !important;
}
.mat-mdc-dialog-surface {
border-radius: 0 !important;
}
}
/* ─── CIC DQR Dialog Backdrop — ensure outside clicks register ─── */ /* ─── CIC DQR Dialog Backdrop — ensure outside clicks register ─── */
.cic-dlg-backdrop { .cic-dlg-backdrop {
background: rgb(15 23 42 / 45%); background: rgb(15 23 42 / 45%);