kyc risk and AML & Compliance Checks automate on client creation

This commit is contained in:
2026-07-31 10:58:58 +05:30
parent 29f1cdc646
commit 1daa12a117
17 changed files with 1113 additions and 1347 deletions
@@ -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);
}
}