feat: add CFCFRMS module, bank menus, mock backend, and dashboard redesign
- CFCFRMS cyber fraud complaint management with dashboard, complaints, escalation, report suspect/mule/fraud txn, FMR submission, CFR lookup - Mock HTTP interceptor with 20 dummy complaints and all endpoints - Simulator service for I4C/MHA integration testing - 3-row menu bar with 22 items (7+8+8 layout) - Mega menus for General Ledger, Sub Ledger, Deposits, Share, Others, HRM, RBI with search and grouped columns - Dropdowns for Cashier, Clearing, NPA, CKYC, Advances, Letter Mgmt, Insurance, Digital - Dashboard redesign with regulatory, financial health, deposits, risk/NPA sections using static banking data - All translations added to en-US.json - FontAwesome icons registered for all new menu items Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
0a32d7135e
commit
b3a4cce0cf
@@ -0,0 +1,849 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Complaint, ComplaintStatus, DashboardStats, TimelineEvent } from '../models/cfcfrms.models';
|
||||
|
||||
// ─── Helper: generate dates relative to now ───
|
||||
function daysAgo(n: number): string {
|
||||
const d = new Date();
|
||||
d.setDate(d.getDate() - n);
|
||||
return d.toISOString();
|
||||
}
|
||||
|
||||
function hoursAgo(n: number): string {
|
||||
const d = new Date();
|
||||
d.setHours(d.getHours() - n);
|
||||
return d.toISOString();
|
||||
}
|
||||
|
||||
// ─── 20 Realistic Dummy Complaints ───
|
||||
export const MOCK_COMPLAINTS: Complaint[] = [
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00001',
|
||||
complainantName: 'Rajesh Kumar Sharma',
|
||||
complainantMobile: '9876543210',
|
||||
complainantEmail: 'rajesh.sharma@gmail.com',
|
||||
complainantAddress: '42, MG Road, Sector 18, Noida, UP 201301',
|
||||
fraudAmount: 250000,
|
||||
fraudType: 'UPI_FRAUD',
|
||||
fraudDescription:
|
||||
'Received fake KYC update link via SMS. After clicking, ₹2.5L was debited via multiple UPI transactions within 10 minutes.',
|
||||
suspectAccountNumber: '50100268347291',
|
||||
suspectAccountName: 'Vikram Enterprises',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240115-00981',
|
||||
status: 'RECEIVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: '',
|
||||
createdAt: daysAgo(0),
|
||||
updatedAt: daysAgo(0)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00002',
|
||||
complainantName: 'Priya Nair',
|
||||
complainantMobile: '8765432109',
|
||||
complainantEmail: 'priya.nair@yahoo.com',
|
||||
complainantAddress: '15, Anna Salai, T. Nagar, Chennai, TN 600017',
|
||||
fraudAmount: 487500,
|
||||
fraudType: 'OLX_FRAUD',
|
||||
fraudDescription:
|
||||
'Victim was selling a sofa on OLX. Scammer sent a QR code claiming it was for payment. Scanning the QR debited ₹4,87,500.',
|
||||
suspectAccountNumber: '50100399281746',
|
||||
suspectAccountName: 'Deepak Trading Co',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240115-01102',
|
||||
status: 'UNDER_REVIEW',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 312000,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: 'Officer Meena K.',
|
||||
createdAt: daysAgo(1),
|
||||
updatedAt: hoursAgo(6)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00003',
|
||||
complainantName: 'Amit Patel',
|
||||
complainantMobile: '7654321098',
|
||||
complainantEmail: 'amit.patel@outlook.com',
|
||||
complainantAddress: '78, SG Highway, Bodakdev, Ahmedabad, GJ 380054',
|
||||
fraudAmount: 150000,
|
||||
fraudType: 'LOAN_APP_FRAUD',
|
||||
fraudDescription:
|
||||
'Downloaded fake loan app from link shared on WhatsApp. App collected bank details and debited ₹1.5L in two transactions.',
|
||||
suspectAccountNumber: '50100412738956',
|
||||
suspectAccountName: 'QuickCash Solutions',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'MHA_PORTAL',
|
||||
acknowledgementNumber: 'ACK-MHA-20240114-00437',
|
||||
status: 'LIEN_MARKED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 89000,
|
||||
fundsHeld: 150000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Lien placed on suspect account. Balance sufficient to cover fraud amount.',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(3),
|
||||
updatedAt: daysAgo(1)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00004',
|
||||
complainantName: 'Sunita Devi',
|
||||
complainantMobile: '6543210987',
|
||||
complainantEmail: '',
|
||||
complainantAddress: 'Village Rampur, Block Sadar, Varanasi, UP 221002',
|
||||
fraudAmount: 75000,
|
||||
fraudType: 'CUSTOMER_CARE_FRAUD',
|
||||
fraudDescription:
|
||||
'Called fake customer care number from Google. Scammer installed AnyDesk on victim phone and transferred ₹75,000.',
|
||||
suspectAccountNumber: '50100528463190',
|
||||
suspectAccountName: 'Rahul Verma',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240113-00215',
|
||||
status: 'DEBIT_FROZEN',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 42000,
|
||||
fundsHeld: 75000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Debit freeze applied. Full fraud amount held. Account holder notified via registered mobile.',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(5),
|
||||
updatedAt: daysAgo(2)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
complainantName: 'Mohammed Irfan',
|
||||
complainantMobile: '9988776655',
|
||||
complainantEmail: 'irfan.m@gmail.com',
|
||||
complainantAddress: '23, Charminar Road, Hyderabad, TS 500002',
|
||||
fraudAmount: 890000,
|
||||
fraudType: 'INVESTMENT_FRAUD',
|
||||
fraudDescription:
|
||||
'Lured into fake stock trading app via Telegram group. Invested ₹8.9L over two weeks. Unable to withdraw funds.',
|
||||
suspectAccountNumber: '50100637291845',
|
||||
suspectAccountName: 'Global Trade Hub',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240112-00876',
|
||||
status: 'FUNDS_MOVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 12500,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '91820034567812',
|
||||
nextHopBankIfsc: 'SBIN0001234',
|
||||
actionNote: 'Only ₹12,500 available. Majority funds moved to SBI account. Escalating to SBI.',
|
||||
assignedOfficer: 'Officer Meena K.',
|
||||
createdAt: daysAgo(8),
|
||||
updatedAt: daysAgo(4)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
complainantName: 'Kavitha Reddy',
|
||||
complainantMobile: '8877665544',
|
||||
complainantEmail: 'kavitha.r@hotmail.com',
|
||||
complainantAddress: '56, Indiranagar, Bangalore, KA 560038',
|
||||
fraudAmount: 320000,
|
||||
fraudType: 'PART_TIME_JOB_FRAUD',
|
||||
fraudDescription:
|
||||
'Scammed via Telegram part-time job offer. Made initial ₹5,000 "investment" then lured into paying ₹3.2L as "tax" and "processing fee".',
|
||||
suspectAccountNumber: '50100745829361',
|
||||
suspectAccountName: 'EarnMore Digital Services',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240110-00543',
|
||||
status: 'RESOLVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Full amount recovered and refunded to complainant via NEFT. Case closed.',
|
||||
assignedOfficer: 'Officer Anita S.',
|
||||
createdAt: daysAgo(15),
|
||||
updatedAt: daysAgo(5)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00007',
|
||||
complainantName: 'Arvind Singh',
|
||||
complainantMobile: '7766554433',
|
||||
complainantEmail: 'arvind.singh@gmail.com',
|
||||
complainantAddress: '12, Civil Lines, Jaipur, RJ 302006',
|
||||
fraudAmount: 45000,
|
||||
fraudType: 'UPI_FRAUD',
|
||||
fraudDescription:
|
||||
'Received call from person posing as bank official. Tricked into sharing OTP. ₹45,000 debited via UPI.',
|
||||
suspectAccountNumber: '50100854917263',
|
||||
suspectAccountName: 'Sanjay Meena',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'MHA_PORTAL',
|
||||
acknowledgementNumber: 'ACK-MHA-20240109-00129',
|
||||
status: 'REJECTED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Suspect account does not belong to LincPay. IFSC mismatch — ticket redirected to correct bank (PNB).',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(18),
|
||||
updatedAt: daysAgo(16)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00008',
|
||||
complainantName: 'Lakshmi Iyer',
|
||||
complainantMobile: '9654321076',
|
||||
complainantEmail: 'lakshmi.iyer@gmail.com',
|
||||
complainantAddress: '34, Marine Drive, Kochi, KL 682031',
|
||||
fraudAmount: 560000,
|
||||
fraudType: 'MATRIMONY_FRAUD',
|
||||
fraudDescription:
|
||||
'Met scammer on matrimony site. Paid ₹5.6L over 2 months for "visa processing" and "medical emergency".',
|
||||
suspectAccountNumber: '50100963845712',
|
||||
suspectAccountName: 'Premium Match Services',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240108-00321',
|
||||
status: 'LIEN_MARKED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 234000,
|
||||
fundsHeld: 560000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Lien marked. Sufficient balance to cover full fraud amount.',
|
||||
assignedOfficer: 'Officer Anita S.',
|
||||
createdAt: daysAgo(10),
|
||||
updatedAt: daysAgo(7)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00009',
|
||||
complainantName: 'Rajan Tiwari',
|
||||
complainantMobile: '8543219876',
|
||||
complainantEmail: 'rajan.t@yahoo.com',
|
||||
complainantAddress: '67, Boring Road, Patna, BR 800001',
|
||||
fraudAmount: 198000,
|
||||
fraudType: 'ELECTRICITY_BILL_FRAUD',
|
||||
fraudDescription:
|
||||
'Received SMS about pending electricity bill with threat of disconnection. Clicked link and ₹1.98L was stolen via screen-sharing app.',
|
||||
suspectAccountNumber: '50100172839465',
|
||||
suspectAccountName: 'Power Solutions India',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240107-00654',
|
||||
status: 'UNDER_REVIEW',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 56000,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: 'Officer Meena K.',
|
||||
createdAt: daysAgo(2),
|
||||
updatedAt: daysAgo(1)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00010',
|
||||
complainantName: 'Deepa Nayak',
|
||||
complainantMobile: '7432108765',
|
||||
complainantEmail: 'deepa.n@gmail.com',
|
||||
complainantAddress: '89, Panjim, Goa 403001',
|
||||
fraudAmount: 125000,
|
||||
fraudType: 'COURIER_FRAUD',
|
||||
fraudDescription:
|
||||
'Fake FedEx call claiming parcel with contraband. Transferred to fake "cyber cell officer" who demanded ₹1.25L as "security deposit".',
|
||||
suspectAccountNumber: '50100281934567',
|
||||
suspectAccountName: 'SecurePay Hub',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'MHA_PORTAL',
|
||||
acknowledgementNumber: 'ACK-MHA-20240106-00087',
|
||||
status: 'RECEIVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: '',
|
||||
createdAt: daysAgo(1),
|
||||
updatedAt: daysAgo(1)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00011',
|
||||
complainantName: 'Suresh Babu',
|
||||
complainantMobile: '9321654870',
|
||||
complainantEmail: 'suresh.b@gmail.com',
|
||||
complainantAddress: '22, Gandhi Nagar, Vijayawada, AP 520003',
|
||||
fraudAmount: 1500000,
|
||||
fraudType: 'INVESTMENT_FRAUD',
|
||||
fraudDescription:
|
||||
'Joined fake crypto trading group on Telegram. Deposited ₹15L over 3 months. Website is now down, funds lost.',
|
||||
suspectAccountNumber: '50100391726845',
|
||||
suspectAccountName: 'CryptoVault Exchange',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240105-01234',
|
||||
status: 'DEBIT_FROZEN',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 340000,
|
||||
fundsHeld: 340000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Only ₹3.4L available in account. Rest already moved. Debit frozen on available balance.',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(6),
|
||||
updatedAt: daysAgo(3)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00012',
|
||||
complainantName: 'Ananya Gupta',
|
||||
complainantMobile: '8210543976',
|
||||
complainantEmail: 'ananya.g@outlook.com',
|
||||
complainantAddress: '45, Connaught Place, New Delhi 110001',
|
||||
fraudAmount: 95000,
|
||||
fraudType: 'OLX_FRAUD',
|
||||
fraudDescription:
|
||||
'Tried to buy an iPhone on OLX. Seller sent fake payment link. ₹95,000 debited upon entering card details.',
|
||||
suspectAccountNumber: '50100402819375',
|
||||
suspectAccountName: 'FastDeal Online',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240104-00789',
|
||||
status: 'RESOLVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Funds recovered and refunded to complainant.',
|
||||
assignedOfficer: 'Officer Anita S.',
|
||||
createdAt: daysAgo(20),
|
||||
updatedAt: daysAgo(10)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00013',
|
||||
complainantName: 'Vikas Joshi',
|
||||
complainantMobile: '9109876543',
|
||||
complainantEmail: 'vikas.j@gmail.com',
|
||||
complainantAddress: '78, FC Road, Pune, MH 411004',
|
||||
fraudAmount: 67000,
|
||||
fraudType: 'UPI_FRAUD',
|
||||
fraudDescription:
|
||||
'Received collect request on PhonePe with message "Refund of ₹67,000". Approved the collect request and lost funds.',
|
||||
suspectAccountNumber: '50100513928476',
|
||||
suspectAccountName: 'Mohan Kumar',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240103-00456',
|
||||
status: 'RECEIVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: '',
|
||||
createdAt: hoursAgo(4),
|
||||
updatedAt: hoursAgo(4)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00014',
|
||||
complainantName: 'Geeta Menon',
|
||||
complainantMobile: '8098765432',
|
||||
complainantEmail: 'geeta.menon@gmail.com',
|
||||
complainantAddress: '90, MG Road, Thrissur, KL 680001',
|
||||
fraudAmount: 430000,
|
||||
fraudType: 'LOAN_APP_FRAUD',
|
||||
fraudDescription:
|
||||
'Downloaded "InstaCash" loan app. After providing bank details, ₹4.3L was transferred out in 5 transactions within 30 minutes.',
|
||||
suspectAccountNumber: '50100624738291',
|
||||
suspectAccountName: 'InstaCash Finance',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240102-00567',
|
||||
status: 'LIEN_MARKED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 178000,
|
||||
fundsHeld: 430000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Full fraud amount covered by lien. Investigation ongoing.',
|
||||
assignedOfficer: 'Officer Meena K.',
|
||||
createdAt: daysAgo(7),
|
||||
updatedAt: daysAgo(4)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00015',
|
||||
complainantName: 'Pankaj Mishra',
|
||||
complainantMobile: '7987654321',
|
||||
complainantEmail: 'pankaj.m@gmail.com',
|
||||
complainantAddress: '11, Hazratganj, Lucknow, UP 226001',
|
||||
fraudAmount: 210000,
|
||||
fraudType: 'SEXTORTION',
|
||||
fraudDescription:
|
||||
'Victim blackmailed after video call on dating app. Paid ₹2.1L across 4 transactions to prevent video leak.',
|
||||
suspectAccountNumber: '50100735829461',
|
||||
suspectAccountName: 'Nikhil Yadav',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20240101-00890',
|
||||
status: 'FUNDS_MOVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 5000,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '40120098765432',
|
||||
nextHopBankIfsc: 'HDFC0001234',
|
||||
actionNote: 'Funds already moved to HDFC account. Escalated.',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(12),
|
||||
updatedAt: daysAgo(8)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00016',
|
||||
complainantName: 'Rekha Pandey',
|
||||
complainantMobile: '6876543210',
|
||||
complainantEmail: 'rekha.p@yahoo.com',
|
||||
complainantAddress: '33, Lalbagh Road, Mangalore, KA 575001',
|
||||
fraudAmount: 38000,
|
||||
fraudType: 'CUSTOMER_CARE_FRAUD',
|
||||
fraudDescription:
|
||||
'Googled customer care for Paytm. Called fake number. Agent installed TeamViewer and debited ₹38,000.',
|
||||
suspectAccountNumber: '50100846729153',
|
||||
suspectAccountName: 'Digital Pay Services',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'MHA_PORTAL',
|
||||
acknowledgementNumber: 'ACK-MHA-20240101-00044',
|
||||
status: 'UNDER_REVIEW',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 18500,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: 'Officer Anita S.',
|
||||
createdAt: daysAgo(2),
|
||||
updatedAt: hoursAgo(12)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00017',
|
||||
complainantName: 'Dinesh Choudhary',
|
||||
complainantMobile: '9765432180',
|
||||
complainantEmail: 'dinesh.c@gmail.com',
|
||||
complainantAddress: '55, Ashram Road, Ahmedabad, GJ 380009',
|
||||
fraudAmount: 780000,
|
||||
fraudType: 'INVESTMENT_FRAUD',
|
||||
fraudDescription:
|
||||
'Invested in fake forex trading platform "ForexPro". Platform blocked withdrawal after total deposit of ₹7.8L.',
|
||||
suspectAccountNumber: '50100957382614',
|
||||
suspectAccountName: 'ForexPro Trading LLP',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20241230-01567',
|
||||
status: 'DEBIT_FROZEN',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 520000,
|
||||
fundsHeld: 520000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Partial amount frozen. ₹2.6L already transferred out.',
|
||||
assignedOfficer: 'Officer Meena K.',
|
||||
createdAt: daysAgo(9),
|
||||
updatedAt: daysAgo(5)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00018',
|
||||
complainantName: 'Fatima Begum',
|
||||
complainantMobile: '8654321097',
|
||||
complainantEmail: '',
|
||||
complainantAddress: '12, Chowk Bazaar, Bhopal, MP 462001',
|
||||
fraudAmount: 55000,
|
||||
fraudType: 'ELECTRICITY_BILL_FRAUD',
|
||||
fraudDescription:
|
||||
'Received WhatsApp message about electricity bill overdue. Called the number provided. Agent made victim install AnyDesk. ₹55,000 debited.',
|
||||
suspectAccountNumber: '50100168293475',
|
||||
suspectAccountName: 'Ravi Shankar',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20241229-00234',
|
||||
status: 'RESOLVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Funds recovered from suspect account and refunded.',
|
||||
assignedOfficer: 'Officer Suresh R.',
|
||||
createdAt: daysAgo(25),
|
||||
updatedAt: daysAgo(15)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00019',
|
||||
complainantName: 'Manoj Kumar',
|
||||
complainantMobile: '9543210876',
|
||||
complainantEmail: 'manoj.k@gmail.com',
|
||||
complainantAddress: '67, Park Street, Kolkata, WB 700016',
|
||||
fraudAmount: 175000,
|
||||
fraudType: 'PART_TIME_JOB_FRAUD',
|
||||
fraudDescription:
|
||||
'Joined "Amazon Review" job scam on Telegram. Initially earned small amounts, then lost ₹1.75L as "upgrade fees".',
|
||||
suspectAccountNumber: '50100279384651',
|
||||
suspectAccountName: 'SmartWork Hub',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20241228-00901',
|
||||
status: 'LIEN_MARKED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 98000,
|
||||
fundsHeld: 175000,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: 'Lien marked successfully. Full amount available.',
|
||||
assignedOfficer: 'Officer Anita S.',
|
||||
createdAt: daysAgo(4),
|
||||
updatedAt: daysAgo(2)
|
||||
},
|
||||
{
|
||||
ticketId: 'CFCFRMS-2024-00020',
|
||||
complainantName: 'Shreya Das',
|
||||
complainantMobile: '8432109765',
|
||||
complainantEmail: 'shreya.d@gmail.com',
|
||||
complainantAddress: '101, Bandra West, Mumbai, MH 400050',
|
||||
fraudAmount: 2100000,
|
||||
fraudType: 'INVESTMENT_FRAUD',
|
||||
fraudDescription:
|
||||
'Invested ₹21L in fake IPO allotment scheme promoted via Instagram ads. Payment split across 3 accounts.',
|
||||
suspectAccountNumber: '50100388274916',
|
||||
suspectAccountName: 'IPO Goldmine Pvt Ltd',
|
||||
suspectBankIfsc: 'LINC0000001',
|
||||
suspectBankName: 'LincPay Digital Bank',
|
||||
sourcePortal: 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: 'ACK-I4C-20241227-01789',
|
||||
status: 'RECEIVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: '',
|
||||
createdAt: hoursAgo(2),
|
||||
updatedAt: hoursAgo(2)
|
||||
}
|
||||
];
|
||||
|
||||
// ─── Timeline Events keyed by ticketId ───
|
||||
export const MOCK_TIMELINES: { [ticketId: string]: TimelineEvent[] } = {
|
||||
'CFCFRMS-2024-00001': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00001',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from I4C CFCFRMS portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(0),
|
||||
metadata: {}
|
||||
}
|
||||
],
|
||||
'CFCFRMS-2024-00002': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00002',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from I4C CFCFRMS portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(1),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
ticketId: 'CFCFRMS-2024-00002',
|
||||
eventType: 'ASSIGNED',
|
||||
eventDescription: 'Assigned to Officer Meena K. for review.',
|
||||
actor: 'Admin',
|
||||
actorRole: 'ADMIN',
|
||||
timestamp: hoursAgo(18),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
ticketId: 'CFCFRMS-2024-00002',
|
||||
eventType: 'UNDER_REVIEW',
|
||||
eventDescription: 'Investigation initiated. Checking suspect account transaction history.',
|
||||
actor: 'Officer Meena K.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: hoursAgo(6),
|
||||
metadata: {}
|
||||
}
|
||||
],
|
||||
'CFCFRMS-2024-00003': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00003',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from MHA Portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(3),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
ticketId: 'CFCFRMS-2024-00003',
|
||||
eventType: 'ASSIGNED',
|
||||
eventDescription: 'Assigned to Officer Suresh R.',
|
||||
actor: 'Admin',
|
||||
actorRole: 'ADMIN',
|
||||
timestamp: daysAgo(3),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
ticketId: 'CFCFRMS-2024-00003',
|
||||
eventType: 'UNDER_REVIEW',
|
||||
eventDescription: 'Account details verified. ₹1.5L fraud amount confirmed.',
|
||||
actor: 'Officer Suresh R.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(2),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
ticketId: 'CFCFRMS-2024-00003',
|
||||
eventType: 'LIEN_MARKED',
|
||||
eventDescription: 'Lien placed on suspect account. Balance ₹2,39,000. Full fraud amount secured.',
|
||||
actor: 'Officer Suresh R.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(1),
|
||||
metadata: { availableBalance: 89000, fundsHeld: 150000 }
|
||||
}
|
||||
],
|
||||
'CFCFRMS-2024-00004': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00004',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from I4C CFCFRMS portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(5),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
ticketId: 'CFCFRMS-2024-00004',
|
||||
eventType: 'ASSIGNED',
|
||||
eventDescription: 'Assigned to Officer Suresh R.',
|
||||
actor: 'Admin',
|
||||
actorRole: 'ADMIN',
|
||||
timestamp: daysAgo(5),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
ticketId: 'CFCFRMS-2024-00004',
|
||||
eventType: 'LIEN_MARKED',
|
||||
eventDescription: 'Lien placed on suspect account.',
|
||||
actor: 'Officer Suresh R.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(3),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
ticketId: 'CFCFRMS-2024-00004',
|
||||
eventType: 'DEBIT_FROZEN',
|
||||
eventDescription: 'Debit freeze applied. Full ₹75,000 held. Account holder notified via registered mobile.',
|
||||
actor: 'Officer Suresh R.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(2),
|
||||
metadata: { availableBalance: 42000, fundsHeld: 75000 }
|
||||
}
|
||||
],
|
||||
'CFCFRMS-2024-00005': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from I4C CFCFRMS portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(8),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
eventType: 'ASSIGNED',
|
||||
eventDescription: 'Assigned to Officer Meena K.',
|
||||
actor: 'Admin',
|
||||
actorRole: 'ADMIN',
|
||||
timestamp: daysAgo(8),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
eventType: 'LIEN_MARKED',
|
||||
eventDescription: 'Only ₹12,500 available in account.',
|
||||
actor: 'Officer Meena K.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(6),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
eventType: 'DEBIT_FROZEN',
|
||||
eventDescription: 'Account debit frozen.',
|
||||
actor: 'Officer Meena K.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(5),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
ticketId: 'CFCFRMS-2024-00005',
|
||||
eventType: 'FUNDS_MOVED',
|
||||
eventDescription: 'Majority of funds already moved to SBI account 91820034567812. Escalation initiated.',
|
||||
actor: 'Officer Meena K.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(4),
|
||||
metadata: { nextHopAccountNumber: '91820034567812', nextHopBankIfsc: 'SBIN0001234' }
|
||||
}
|
||||
],
|
||||
'CFCFRMS-2024-00006': [
|
||||
{
|
||||
id: 1,
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
eventType: 'COMPLAINT_RECEIVED',
|
||||
eventDescription: 'Complaint received from I4C CFCFRMS portal.',
|
||||
actor: 'System',
|
||||
actorRole: 'SYSTEM',
|
||||
timestamp: daysAgo(15),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
eventType: 'ASSIGNED',
|
||||
eventDescription: 'Assigned to Officer Anita S.',
|
||||
actor: 'Admin',
|
||||
actorRole: 'ADMIN',
|
||||
timestamp: daysAgo(15),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
eventType: 'LIEN_MARKED',
|
||||
eventDescription: 'Full amount liened.',
|
||||
actor: 'Officer Anita S.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(13),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
eventType: 'DEBIT_FROZEN',
|
||||
eventDescription: 'Debit frozen on suspect account.',
|
||||
actor: 'Officer Anita S.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(10),
|
||||
metadata: {}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
ticketId: 'CFCFRMS-2024-00006',
|
||||
eventType: 'RESOLVED',
|
||||
eventDescription: 'Full amount ₹3,20,000 recovered and refunded to complainant via NEFT. Case closed.',
|
||||
actor: 'Officer Anita S.',
|
||||
actorRole: 'COMPLIANCE_OFFICER',
|
||||
timestamp: daysAgo(5),
|
||||
metadata: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// ─── Dashboard Stats (computed from mock data) ───
|
||||
export function computeMockStats(): DashboardStats {
|
||||
const statusDist: { [key: string]: number } = {};
|
||||
const amountDist: { [key: string]: number } = {};
|
||||
let pendingReview = 0;
|
||||
let lienMarked = 0;
|
||||
let fundsRecovered = 0;
|
||||
|
||||
MOCK_COMPLAINTS.forEach((c) => {
|
||||
statusDist[c.status] = (statusDist[c.status] || 0) + 1;
|
||||
amountDist[c.status] = (amountDist[c.status] || 0) + c.fraudAmount;
|
||||
|
||||
if (c.status === 'RECEIVED' || c.status === 'UNDER_REVIEW') pendingReview++;
|
||||
if (c.status === 'LIEN_MARKED') lienMarked++;
|
||||
if (c.status === 'RESOLVED') fundsRecovered += c.fraudAmount;
|
||||
});
|
||||
|
||||
return {
|
||||
totalComplaints: MOCK_COMPLAINTS.length,
|
||||
pendingReview,
|
||||
lienMarked,
|
||||
fundsRecovered,
|
||||
statusDistribution: statusDist as any,
|
||||
amountByStatus: amountDist as any
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,618 @@
|
||||
/**
|
||||
* 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 } from '@angular/core';
|
||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { MOCK_COMPLAINTS, MOCK_TIMELINES, computeMockStats } from './cfcfrms-mock-data';
|
||||
import { Complaint, ComplaintStatus, TimelineEvent, EscalationResponse, PagedResponse } from '../models/cfcfrms.models';
|
||||
|
||||
// ─── Mutable in-memory state (survives during session) ───
|
||||
const complaints = [...MOCK_COMPLAINTS];
|
||||
const timelines: { [key: string]: TimelineEvent[] } = JSON.parse(JSON.stringify(MOCK_TIMELINES));
|
||||
let nextEventId = 1000;
|
||||
let nextTicketSeq = 21;
|
||||
|
||||
function now(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function genTicketId(): string {
|
||||
const seq = String(nextTicketSeq++).padStart(5, '0');
|
||||
return `CFCFRMS-2024-${seq}`;
|
||||
}
|
||||
|
||||
function genRefNumber(): string {
|
||||
return `ESC-${Date.now()}-${Math.floor(Math.random() * 9000 + 1000)}`;
|
||||
}
|
||||
|
||||
function addTimeline(
|
||||
ticketId: string,
|
||||
eventType: string,
|
||||
description: string,
|
||||
actor: string,
|
||||
actorRole: string,
|
||||
meta: any = {}
|
||||
): void {
|
||||
if (!timelines[ticketId]) timelines[ticketId] = [];
|
||||
timelines[ticketId].push({
|
||||
id: ++nextEventId,
|
||||
ticketId,
|
||||
eventType,
|
||||
eventDescription: description,
|
||||
actor,
|
||||
actorRole,
|
||||
timestamp: now(),
|
||||
metadata: meta
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Bank-side handlers ───
|
||||
|
||||
function handleGetComplaints(url: URL): PagedResponse<Complaint> {
|
||||
const page = parseInt(url.searchParams.get('page') || '0', 10);
|
||||
const size = parseInt(url.searchParams.get('size') || '10', 10);
|
||||
const status = url.searchParams.get('status') || '';
|
||||
const search = (url.searchParams.get('search') || '').toLowerCase();
|
||||
const dateFrom = url.searchParams.get('dateFrom') || '';
|
||||
const dateTo = url.searchParams.get('dateTo') || '';
|
||||
|
||||
let filtered = [...complaints];
|
||||
|
||||
if (status) {
|
||||
filtered = filtered.filter((c) => c.status === status);
|
||||
}
|
||||
if (search) {
|
||||
filtered = filtered.filter(
|
||||
(c) =>
|
||||
c.ticketId.toLowerCase().includes(search) ||
|
||||
c.complainantMobile.includes(search) ||
|
||||
c.complainantName.toLowerCase().includes(search)
|
||||
);
|
||||
}
|
||||
if (dateFrom) {
|
||||
const from = new Date(dateFrom);
|
||||
filtered = filtered.filter((c) => new Date(c.createdAt) >= from);
|
||||
}
|
||||
if (dateTo) {
|
||||
const to = new Date(dateTo);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
filtered = filtered.filter((c) => new Date(c.createdAt) <= to);
|
||||
}
|
||||
|
||||
filtered.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
|
||||
const totalElements = filtered.length;
|
||||
const totalPages = Math.ceil(totalElements / size);
|
||||
const start = page * size;
|
||||
const content = filtered.slice(start, start + size);
|
||||
|
||||
return { content, totalElements, totalPages, size, number: page, first: page === 0, last: page >= totalPages - 1 };
|
||||
}
|
||||
|
||||
function handleGetComplaintById(ticketId: string): Complaint | null {
|
||||
return complaints.find((c) => c.ticketId === ticketId) || null;
|
||||
}
|
||||
|
||||
function handleSubmitAction(ticketId: string, body: any): Complaint | null {
|
||||
const idx = complaints.findIndex((c) => c.ticketId === ticketId);
|
||||
if (idx === -1) return null;
|
||||
|
||||
const complaint = complaints[idx];
|
||||
complaint.status = body.action as ComplaintStatus;
|
||||
complaint.actionNote = body.actionNote || complaint.actionNote;
|
||||
complaint.updatedAt = now();
|
||||
|
||||
if (body.availableBalance != null) complaint.availableBalance = body.availableBalance;
|
||||
if (body.fundsHeld != null) complaint.fundsHeld = body.fundsHeld;
|
||||
if (body.nextHopAccountNumber) complaint.nextHopAccountNumber = body.nextHopAccountNumber;
|
||||
if (body.nextHopBankIfsc) complaint.nextHopBankIfsc = body.nextHopBankIfsc;
|
||||
|
||||
addTimeline(
|
||||
ticketId,
|
||||
body.action,
|
||||
body.actionNote || `Status changed to ${body.action}`,
|
||||
'Current User',
|
||||
'COMPLIANCE_OFFICER',
|
||||
{
|
||||
availableBalance: body.availableBalance,
|
||||
fundsHeld: body.fundsHeld,
|
||||
nextHopAccountNumber: body.nextHopAccountNumber,
|
||||
nextHopBankIfsc: body.nextHopBankIfsc
|
||||
}
|
||||
);
|
||||
|
||||
return complaint;
|
||||
}
|
||||
|
||||
function handleGetTimeline(ticketId: string): TimelineEvent[] {
|
||||
return (timelines[ticketId] || []).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
||||
}
|
||||
|
||||
function handleEscalate(ticketId: string, body: any): EscalationResponse | null {
|
||||
const complaint = complaints.find((c) => c.ticketId === ticketId);
|
||||
if (!complaint) return null;
|
||||
|
||||
complaint.nextHopAccountNumber = body.nextHopAccountNumber;
|
||||
complaint.nextHopBankIfsc = body.nextHopBankIfsc;
|
||||
complaint.status = 'FUNDS_MOVED';
|
||||
complaint.updatedAt = now();
|
||||
|
||||
addTimeline(
|
||||
ticketId,
|
||||
'ESCALATED',
|
||||
`Escalated to bank ${body.nextHopBankIfsc}. Remarks: ${body.remarks}`,
|
||||
'Current User',
|
||||
'COMPLIANCE_OFFICER',
|
||||
body
|
||||
);
|
||||
|
||||
return {
|
||||
ticketId,
|
||||
referenceNumber: genRefNumber(),
|
||||
escalatedTo: body.nextHopBankIfsc,
|
||||
escalatedBankIfsc: body.nextHopBankIfsc,
|
||||
status: 'ESCALATED',
|
||||
timestamp: now()
|
||||
};
|
||||
}
|
||||
|
||||
function handleCreateComplaint(body: any): Complaint {
|
||||
const ticket: Complaint = {
|
||||
ticketId: genTicketId(),
|
||||
complainantName: body.complainantName || 'Unknown',
|
||||
complainantMobile: body.complainantMobile || '',
|
||||
complainantEmail: body.complainantEmail || '',
|
||||
complainantAddress: body.complainantAddress || '',
|
||||
fraudAmount: body.fraudAmount || 0,
|
||||
fraudType: body.fraudType || 'UNKNOWN',
|
||||
fraudDescription: body.fraudDescription || '',
|
||||
suspectAccountNumber: body.suspectAccountNumber || '',
|
||||
suspectAccountName: body.suspectAccountName || '',
|
||||
suspectBankIfsc: body.suspectBankIfsc || 'LINC0000001',
|
||||
suspectBankName: body.suspectBankName || 'LincPay Digital Bank',
|
||||
sourcePortal: body.sourcePortal || 'I4C_CFCFRMS',
|
||||
acknowledgementNumber: body.acknowledgementNumber || `ACK-${Date.now()}`,
|
||||
status: 'RECEIVED',
|
||||
currentBankIfsc: 'LINC0000001',
|
||||
currentBankName: 'LincPay Digital Bank',
|
||||
availableBalance: 0,
|
||||
fundsHeld: 0,
|
||||
nextHopAccountNumber: '',
|
||||
nextHopBankIfsc: '',
|
||||
actionNote: '',
|
||||
assignedOfficer: '',
|
||||
createdAt: now(),
|
||||
updatedAt: now()
|
||||
};
|
||||
complaints.unshift(ticket);
|
||||
addTimeline(ticket.ticketId, 'COMPLAINT_RECEIVED', 'Complaint registered.', 'System', 'SYSTEM');
|
||||
return ticket;
|
||||
}
|
||||
|
||||
// ─── Simulator handlers ───
|
||||
|
||||
function handleSimPushComplaint(body: any): any {
|
||||
const ticket = handleCreateComplaint(body);
|
||||
return { success: true, message: 'Complaint pushed to bank', ticketId: ticket.ticketId, status: ticket.status };
|
||||
}
|
||||
|
||||
function handleSimPushHoldDirective(body: any): any {
|
||||
const ticketId = body.ticketId || body.referenceNumber;
|
||||
const complaint = complaints.find((c) => c.ticketId === ticketId);
|
||||
if (!complaint) return { success: false, message: 'Ticket not found' };
|
||||
|
||||
complaint.status = 'DEBIT_FROZEN';
|
||||
complaint.fundsHeld = body.holdAmount || complaint.fraudAmount;
|
||||
complaint.updatedAt = now();
|
||||
addTimeline(
|
||||
ticketId,
|
||||
'DEBIT_FROZEN',
|
||||
`Urgent hold directive from I4C. Amount: ${complaint.fundsHeld}`,
|
||||
'I4C System',
|
||||
'REGULATOR'
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Hold directive applied',
|
||||
ticketId,
|
||||
status: 'DEBIT_FROZEN',
|
||||
fundsHeld: complaint.fundsHeld
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimPushBulk(body: any): any {
|
||||
const items = body.complaints || [];
|
||||
const results = items.slice(0, 10).map((c: any) => handleSimPushComplaint(c));
|
||||
return { success: true, totalPushed: results.length, results };
|
||||
}
|
||||
|
||||
function handleSimReceiveBankUpdate(body: any): any {
|
||||
const complaint = complaints.find((c) => c.ticketId === body.ticketId);
|
||||
if (!complaint) return { success: false, message: 'Ticket not found' };
|
||||
return {
|
||||
success: true,
|
||||
ticketId: body.ticketId,
|
||||
currentStatus: complaint.status,
|
||||
lastUpdated: complaint.updatedAt,
|
||||
actionNote: complaint.actionNote
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimReceiveEscalation(body: any): any {
|
||||
const complaint = complaints.find((c) => c.ticketId === body.ticketId);
|
||||
if (!complaint) return { success: false, message: 'Ticket not found' };
|
||||
return {
|
||||
success: true,
|
||||
ticketId: body.ticketId,
|
||||
escalatedTo: complaint.nextHopBankIfsc || 'N/A',
|
||||
fundsHeld: complaint.fundsHeld,
|
||||
currentStatus: complaint.status
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimVerifyAccount(body: any): any {
|
||||
const acctNo = body.accountNumber || '';
|
||||
const match = complaints.find((c) => c.suspectAccountNumber === acctNo);
|
||||
return {
|
||||
accountNumber: acctNo,
|
||||
exists: !!match,
|
||||
accountName: match?.suspectAccountName || null,
|
||||
bankIfsc: match?.suspectBankIfsc || null,
|
||||
bankName: match?.suspectBankName || null,
|
||||
isFlagged: !!match,
|
||||
flagReason: match ? `Linked to CFCFRMS ticket ${match.ticketId}` : null
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimVerifyTransaction(body: any): any {
|
||||
return {
|
||||
transactionId: body.transactionId || `TXN-${Date.now()}`,
|
||||
accountNumber: body.accountNumber || '',
|
||||
amount: body.amount || 0,
|
||||
status: 'VERIFIED',
|
||||
timestamp: now(),
|
||||
riskScore: Math.floor(Math.random() * 40 + 60),
|
||||
flagged: true,
|
||||
remarks: 'Transaction linked to suspected fraud account.'
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimComplaintStatus(ref: string): any {
|
||||
const complaint = complaints.find((c) => c.ticketId === ref || c.acknowledgementNumber === ref);
|
||||
if (!complaint) return { found: false, message: 'Complaint not found' };
|
||||
return {
|
||||
found: true,
|
||||
ticketId: complaint.ticketId,
|
||||
acknowledgementNumber: complaint.acknowledgementNumber,
|
||||
status: complaint.status,
|
||||
bankAction: complaint.actionNote || 'Pending',
|
||||
lastUpdated: complaint.updatedAt,
|
||||
assignedOfficer: complaint.assignedOfficer || 'Unassigned'
|
||||
};
|
||||
}
|
||||
|
||||
function handleSimBankPerformance(): any {
|
||||
const total = complaints.length;
|
||||
const resolved = complaints.filter((c) => c.status === 'RESOLVED').length;
|
||||
const rejected = complaints.filter((c) => c.status === 'REJECTED').length;
|
||||
const frozen = complaints.filter((c) => c.status === 'DEBIT_FROZEN' || c.status === 'LIEN_MARKED').length;
|
||||
const pending = complaints.filter((c) => c.status === 'RECEIVED' || c.status === 'UNDER_REVIEW').length;
|
||||
|
||||
return {
|
||||
bankIfsc: 'LINC0000001',
|
||||
bankName: 'LincPay Digital Bank',
|
||||
totalTickets: total,
|
||||
resolvedCount: resolved,
|
||||
rejectedCount: rejected,
|
||||
frozenCount: frozen,
|
||||
pendingCount: pending,
|
||||
averageTATHours: 18.5,
|
||||
complianceScore: 92.4,
|
||||
responseRate: total > 0 ? Math.round(((total - pending) / total) * 100 * 10) / 10 : 0,
|
||||
periodFrom: new Date(Date.now() - 30 * 86400000).toISOString().split('T')[0],
|
||||
periodTo: new Date().toISOString().split('T')[0]
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Bank Proactive Reporting handlers ───
|
||||
|
||||
// In-memory stores for reported items
|
||||
const reportedSuspects: any[] = [];
|
||||
const reportedMules: any[] = [];
|
||||
const reportedFraudTxns: any[] = [];
|
||||
const fmrSubmissions: any[] = [];
|
||||
|
||||
// Dummy CFR registry data
|
||||
const cfrRegistry: { [acctNo: string]: any[] } = {
|
||||
'50100268347291': [
|
||||
{
|
||||
fraudReferenceNumber: 'CFR-2024-00891',
|
||||
fraudCategory: 'UPI_FRAUD',
|
||||
fraudAmount: 250000,
|
||||
reportingBank: 'LincPay Digital Bank',
|
||||
reportingBankIfsc: 'LINC0000001',
|
||||
dateReported: '2024-01-10',
|
||||
status: 'CONFIRMED',
|
||||
remarks: 'Account involved in multiple UPI fraud complaints.'
|
||||
}
|
||||
],
|
||||
'50100637291845': [
|
||||
{
|
||||
fraudReferenceNumber: 'CFR-2024-01234',
|
||||
fraudCategory: 'INVESTMENT_FRAUD',
|
||||
fraudAmount: 890000,
|
||||
reportingBank: 'LincPay Digital Bank',
|
||||
reportingBankIfsc: 'LINC0000001',
|
||||
dateReported: '2024-01-08',
|
||||
status: 'CONFIRMED',
|
||||
remarks: 'Mule account used for crypto investment scam.'
|
||||
},
|
||||
{
|
||||
fraudReferenceNumber: 'CFR-2024-01235',
|
||||
fraudCategory: 'INVESTMENT_FRAUD',
|
||||
fraudAmount: 450000,
|
||||
reportingBank: 'State Bank of India',
|
||||
reportingBankIfsc: 'SBIN0001234',
|
||||
dateReported: '2023-12-20',
|
||||
status: 'CONFIRMED',
|
||||
remarks: 'Cross-bank fraud trail from SBI complaint.'
|
||||
}
|
||||
],
|
||||
'50100745829361': [
|
||||
{
|
||||
fraudReferenceNumber: 'CFR-2024-00567',
|
||||
fraudCategory: 'PART_TIME_JOB_FRAUD',
|
||||
fraudAmount: 320000,
|
||||
reportingBank: 'LincPay Digital Bank',
|
||||
reportingBankIfsc: 'LINC0000001',
|
||||
dateReported: '2024-01-05',
|
||||
status: 'RESOLVED',
|
||||
remarks: 'Funds recovered. Account closed.'
|
||||
}
|
||||
],
|
||||
'40120098765432': [
|
||||
{
|
||||
fraudReferenceNumber: 'CFR-2024-00999',
|
||||
fraudCategory: 'SEXTORTION',
|
||||
fraudAmount: 210000,
|
||||
reportingBank: 'HDFC Bank',
|
||||
reportingBankIfsc: 'HDFC0001234',
|
||||
dateReported: '2024-01-02',
|
||||
status: 'UNDER_INVESTIGATION',
|
||||
remarks: 'Next-hop account in sextortion chain.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function handleReportSuspect(body: any): any {
|
||||
const ref = `SUS-${Date.now()}-${Math.floor(Math.random() * 9000 + 1000)}`;
|
||||
const record = { ...body, referenceNumber: ref, status: 'REPORTED', reportedAt: now() };
|
||||
reportedSuspects.push(record);
|
||||
return {
|
||||
success: true,
|
||||
referenceNumber: ref,
|
||||
accountNumber: body.accountNumber,
|
||||
status: 'REPORTED',
|
||||
reportedAt: record.reportedAt,
|
||||
message: 'Suspect account reported to I4C successfully.'
|
||||
};
|
||||
}
|
||||
|
||||
function handleReportMuleAccount(body: any): any {
|
||||
const ref = `MULE-${Date.now()}-${Math.floor(Math.random() * 9000 + 1000)}`;
|
||||
const record = { ...body, referenceNumber: ref, status: 'FLAGGED', reportedAt: now() };
|
||||
reportedMules.push(record);
|
||||
return {
|
||||
success: true,
|
||||
referenceNumber: ref,
|
||||
accountNumber: body.accountNumber,
|
||||
muleType: body.muleType,
|
||||
status: 'FLAGGED',
|
||||
reportedAt: record.reportedAt,
|
||||
message: 'Mule account flagged and reported to I4C/CFCFRMS.'
|
||||
};
|
||||
}
|
||||
|
||||
function handleReportFraudTransaction(body: any): any {
|
||||
const ref = `FTX-${Date.now()}-${Math.floor(Math.random() * 9000 + 1000)}`;
|
||||
const record = { ...body, referenceNumber: ref, status: 'REPORTED', reportedAt: now() };
|
||||
reportedFraudTxns.push(record);
|
||||
return {
|
||||
success: true,
|
||||
referenceNumber: ref,
|
||||
transactionId: body.transactionId,
|
||||
status: 'REPORTED',
|
||||
reportedAt: record.reportedAt,
|
||||
message: 'Fraud transaction reported to I4C successfully.'
|
||||
};
|
||||
}
|
||||
|
||||
function handleFmrSubmission(body: any): any {
|
||||
const ref = `FMR-${new Date().getFullYear()}-${String(fmrSubmissions.length + 1).padStart(6, '0')}`;
|
||||
const nextDue = new Date();
|
||||
nextDue.setDate(nextDue.getDate() + 90);
|
||||
const record = {
|
||||
...body,
|
||||
fmrReferenceNumber: ref,
|
||||
status: 'SUBMITTED',
|
||||
submittedAt: now(),
|
||||
nextDueDate: nextDue.toISOString().split('T')[0]
|
||||
};
|
||||
fmrSubmissions.push(record);
|
||||
return {
|
||||
success: true,
|
||||
fmrReferenceNumber: ref,
|
||||
reportingBankIfsc: body.reportingBankIfsc,
|
||||
fraudCategory: body.fraudCategory,
|
||||
fraudAmount: body.fraudAmount,
|
||||
status: 'SUBMITTED',
|
||||
submittedAt: record.submittedAt,
|
||||
nextDueDate: record.nextDueDate,
|
||||
message: 'FMR submitted to RBI FUA system successfully.'
|
||||
};
|
||||
}
|
||||
|
||||
function handleCfrLookup(url: URL): any {
|
||||
const accountNumber = url.searchParams.get('accountNumber') || '';
|
||||
const records = cfrRegistry[accountNumber] || [];
|
||||
return { accountNumber, found: records.length > 0, records, queryTimestamp: now() };
|
||||
}
|
||||
|
||||
// ─── Route matcher utility ───
|
||||
|
||||
function matchPath(pathname: string, pattern: string): { match: boolean; params: { [k: string]: string } } {
|
||||
const patParts = pattern.split('/').filter(Boolean);
|
||||
const urlParts = pathname.split('/').filter(Boolean);
|
||||
if (patParts.length !== urlParts.length) return { match: false, params: {} };
|
||||
|
||||
const params: { [k: string]: string } = {};
|
||||
for (let i = 0; i < patParts.length; i++) {
|
||||
if (patParts[i].startsWith(':')) {
|
||||
params[patParts[i].slice(1)] = urlParts[i];
|
||||
} else if (patParts[i] !== urlParts[i]) {
|
||||
return { match: false, params: {} };
|
||||
}
|
||||
}
|
||||
return { match: true, params };
|
||||
}
|
||||
|
||||
function parseUrl(raw: string): URL {
|
||||
try {
|
||||
return new URL(raw);
|
||||
} catch {
|
||||
return new URL(raw, 'http://localhost');
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Class-based Interceptor ───
|
||||
|
||||
@Injectable()
|
||||
export class CfcfrmsMockInterceptor implements HttpInterceptor {
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const url = parseUrl(req.url);
|
||||
const path = url.pathname;
|
||||
const method = req.method;
|
||||
|
||||
const isCfcfrms =
|
||||
path.includes('/cfcfrms/complaints') || path.includes('/cfcfrms/complaint') || path.includes('/cfcfrms/report-');
|
||||
const isSimulator = path.includes('/simulator/cfcfrms');
|
||||
const isRbi = path.includes('/rbi/fmr') || path.includes('/rbi/cfr');
|
||||
|
||||
if (!isCfcfrms && !isSimulator && !isRbi) {
|
||||
return next.handle(req);
|
||||
}
|
||||
|
||||
// Extract relative path
|
||||
let relativePath = '';
|
||||
if (isRbi) {
|
||||
const idx = path.indexOf('/rbi');
|
||||
relativePath = path.substring(idx);
|
||||
} else if (isSimulator) {
|
||||
const idx = path.indexOf('/simulator/cfcfrms');
|
||||
relativePath = path.substring(idx + '/simulator/cfcfrms'.length);
|
||||
} else {
|
||||
const idx = path.indexOf('/cfcfrms');
|
||||
relativePath = path.substring(idx + '/cfcfrms'.length);
|
||||
}
|
||||
|
||||
let responseBody: any = { error: 'Not Found' };
|
||||
let status = 200;
|
||||
const mockDelay = Math.floor(Math.random() * 300 + 150);
|
||||
|
||||
// ──── Bank-side routes ────
|
||||
if (!isSimulator) {
|
||||
if (method === 'POST' && relativePath === '/complaints') {
|
||||
responseBody = handleCreateComplaint(req.body);
|
||||
} else if (method === 'GET' && relativePath === '/complaints/stats') {
|
||||
responseBody = computeMockStats();
|
||||
} else {
|
||||
const timelineMatch = matchPath(relativePath, '/complaints/:ticketId/timeline');
|
||||
if (method === 'GET' && timelineMatch.match) {
|
||||
responseBody = handleGetTimeline(timelineMatch.params['ticketId']);
|
||||
} else {
|
||||
const actionMatch = matchPath(relativePath, '/complaints/:ticketId/action');
|
||||
if (method === 'PUT' && actionMatch.match) {
|
||||
const result = handleSubmitAction(actionMatch.params['ticketId'], req.body);
|
||||
if (!result) {
|
||||
responseBody = { error: 'Ticket not found' };
|
||||
status = 404;
|
||||
} else responseBody = result;
|
||||
} else {
|
||||
const escMatch = matchPath(relativePath, '/complaints/:ticketId/escalate');
|
||||
if (method === 'POST' && escMatch.match) {
|
||||
const result = handleEscalate(escMatch.params['ticketId'], req.body);
|
||||
if (!result) {
|
||||
responseBody = { error: 'Ticket not found' };
|
||||
status = 404;
|
||||
} else responseBody = result;
|
||||
} else {
|
||||
const detailMatch = matchPath(relativePath, '/complaints/:ticketId');
|
||||
if (method === 'GET' && detailMatch.match) {
|
||||
const result = handleGetComplaintById(detailMatch.params['ticketId']);
|
||||
if (!result) {
|
||||
responseBody = { error: 'Ticket not found' };
|
||||
status = 404;
|
||||
} else responseBody = result;
|
||||
} else if (method === 'GET' && (relativePath === '/complaints' || relativePath === '/complaints/')) {
|
||||
responseBody = handleGetComplaints(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Bank proactive reporting routes
|
||||
if (method === 'POST' && relativePath === '/report-suspect') {
|
||||
responseBody = handleReportSuspect(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/report-mule-account') {
|
||||
responseBody = handleReportMuleAccount(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/report-fraud-transaction') {
|
||||
responseBody = handleReportFraudTransaction(req.body);
|
||||
}
|
||||
}
|
||||
|
||||
// ──── RBI routes ────
|
||||
if (isRbi) {
|
||||
if (method === 'POST' && relativePath === '/rbi/fmr') {
|
||||
responseBody = handleFmrSubmission(req.body);
|
||||
} else if (method === 'GET' && relativePath.startsWith('/rbi/cfr/lookup')) {
|
||||
responseBody = handleCfrLookup(url);
|
||||
}
|
||||
}
|
||||
|
||||
// ──── Simulator routes ────
|
||||
if (isSimulator) {
|
||||
if (method === 'POST' && relativePath === '/push-complaint') {
|
||||
responseBody = handleSimPushComplaint(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/push-hold-directive') {
|
||||
responseBody = handleSimPushHoldDirective(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/push-bulk-complaints') {
|
||||
responseBody = handleSimPushBulk(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/receive-bank-update') {
|
||||
responseBody = handleSimReceiveBankUpdate(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/receive-escalation') {
|
||||
responseBody = handleSimReceiveEscalation(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/verify-account') {
|
||||
responseBody = handleSimVerifyAccount(req.body);
|
||||
} else if (method === 'POST' && relativePath === '/verify-transaction') {
|
||||
responseBody = handleSimVerifyTransaction(req.body);
|
||||
} else {
|
||||
const statusMatch = matchPath(relativePath, '/complaint-status/:ref');
|
||||
if (method === 'GET' && statusMatch.match) {
|
||||
responseBody = handleSimComplaintStatus(statusMatch.params['ref']);
|
||||
} else if (method === 'GET' && relativePath === '/bank-performance') {
|
||||
responseBody = handleSimBankPerformance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return of(new HttpResponse({ status, body: responseBody })).pipe(delay(mockDelay));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user