kyc risk and AML & Compliance Checks automate on client creation
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user