Files
cbsnexora/src/app/digital/neft-rtgs-report/neft-rtgs-report.component.ts
T
AdityaandClaude Opus 4.7 595054b0b8 feat(digital+toolbar): NEFT/RTGS module, AI menu, polished header icons
- Add /digital/neft-rtgs (entry) and /digital/neft-rtgs-report (analytics):
  KPIs, tabbed views (Trends, Bank-wise, Date-wise, Pending/Pass/Return,
  Branch/Mobile, User-wise), filterable transaction table with CSV export,
  and a non-dismissible Initiate dialog launched from a floating action.
- Make BANL verification mandatory before NEFT/RTGS submit; auto-fill and
  lock beneficiary name from the lookup response, invalidate match on
  A/C or IFSC edit, demo fallback for unknown accounts.
- Add Digital menu links (NEFT/RTGS + Report) in shell top-nav.
- Replace toolbar theme toggle with AI / Intelligence Modules mega menu:
  4 colour-coded columns covering Lending & Risk, Investment & Profit,
  Compliance & Advisory, Governance (9 modules total). Wire RBI
  Notifications Monitoring to /rbi-circulars.
- Unify notifications, settings, AI, and profile toolbar buttons into a
  26px ring style; colourful gradient bell SVG for notifications,
  custom gear+petals AI mark, online dot on the avatar.
- Trim ISO 27001 chip in the top bar.
- Add MPL header to generated .env.ts and its template in version.js.
- Bundles other pending working-tree changes (clients family/nominee
  tab tweaks, auth/2FA fixes, translations) into the same push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 11:50:26 +05:30

748 lines
23 KiB
TypeScript

/**
* 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, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MatDialog } from '@angular/material/dialog';
import { MatTooltip } from '@angular/material/tooltip';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { NeftRtgsEntryComponent } from '../neft-rtgs-entry/neft-rtgs-entry.component';
export type PaymentType = 'NEFT' | 'RTGS';
export type ProcessingState = 'UPLOADED' | 'VALIDATED' | 'SENT' | 'SETTLED' | 'PENDING' | 'RETURNED';
export interface NeftRtgsTxn {
batchId: string;
instructionId: string;
utrNumber: string;
remitterAcc: string;
remitterBranch: string;
remitterUser: string;
remitterChannel: 'BRANCH' | 'MOBILE';
beneAcc: string;
beneIfsc: string;
beneName: string;
beneBank: string;
paymentType: PaymentType;
amount: number;
valueDate: string; // YYYY-MM-DD
purposeCode: string;
leiCode?: string;
processingState: ProcessingState;
returnCode?: string;
returnReason?: string;
sentTimestamp: string; // YYYY-MM-DD HH:mm:ss
}
type ReportTab = 'trends' | 'bank' | 'date' | 'pending' | 'channel' | 'user';
interface BankRow {
bank: string;
count: number;
amount: number;
settled: number;
pending: number;
returned: number;
}
interface DateRow {
date: string;
count: number;
amount: number;
neft: number;
rtgs: number;
}
interface UserRow {
user: string;
count: number;
amount: number;
branch: string;
}
interface ChannelRow {
channel: 'BRANCH' | 'MOBILE';
count: number;
amount: number;
share: number;
}
@Component({
selector: 'mifosx-neft-rtgs-report',
templateUrl: './neft-rtgs-report.component.html',
styleUrls: ['./neft-rtgs-report.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FormsModule,
FaIconComponent,
MatTooltip
]
})
export class NeftRtgsReportComponent implements OnInit {
private dialog = inject(MatDialog);
/** Active tab */
activeTab: ReportTab = 'trends';
/** Search / filter */
search = '';
typeFilter: '' | PaymentType = '';
stateFilter: '' | ProcessingState = '';
/** All transactions (mock data — replace with API). */
txns: NeftRtgsTxn[] = [];
/** Tab labels */
readonly tabs: { id: ReportTab; label: string; icon: string }[] = [
{ id: 'trends', label: 'Trends', icon: 'chart-line' },
{ id: 'bank', label: 'Bank-wise', icon: 'university' },
{ id: 'date', label: 'Date-wise', icon: 'calendar-alt' },
{ id: 'pending', label: 'Pending / Pass / Return', icon: 'tasks' },
{ id: 'channel', label: 'Branch / Mobile', icon: 'mobile-alt' },
{ id: 'user', label: 'User-wise', icon: 'user-tie' }
];
ngOnInit(): void {
this.txns = this.seedTransactions();
}
// ───────────────────── KPIs ─────────────────────
get totalCount(): number {
return this.txns.length;
}
get totalAmount(): number {
return this.txns.reduce((s, t) => s + t.amount, 0);
}
get settledCount(): number {
return this.txns.filter((t) => t.processingState === 'SETTLED').length;
}
get pendingCount(): number {
return this.txns.filter((t) => [
'UPLOADED',
'VALIDATED',
'SENT',
'PENDING'
].includes(t.processingState)).length;
}
get returnedCount(): number {
return this.txns.filter((t) => t.processingState === 'RETURNED').length;
}
get neftCount(): number {
return this.txns.filter((t) => t.paymentType === 'NEFT').length;
}
get rtgsCount(): number {
return this.txns.filter((t) => t.paymentType === 'RTGS').length;
}
get neftAmount(): number {
return this.txns.filter((t) => t.paymentType === 'NEFT').reduce((s, t) => s + t.amount, 0);
}
get rtgsAmount(): number {
return this.txns.filter((t) => t.paymentType === 'RTGS').reduce((s, t) => s + t.amount, 0);
}
get successRate(): number {
if (this.totalCount === 0) return 0;
return (this.settledCount / this.totalCount) * 100;
}
get avgTicket(): number {
if (this.totalCount === 0) return 0;
return this.totalAmount / this.totalCount;
}
// ───────────────────── Filtered list ─────────────────────
get filteredTxns(): NeftRtgsTxn[] {
const q = this.search.trim().toLowerCase();
return this.txns.filter((t) => {
if (this.typeFilter && t.paymentType !== this.typeFilter) return false;
if (this.stateFilter && t.processingState !== this.stateFilter) return false;
if (!q) return true;
return (
t.utrNumber.toLowerCase().includes(q) ||
t.batchId.toLowerCase().includes(q) ||
t.instructionId.toLowerCase().includes(q) ||
t.beneAcc.toLowerCase().includes(q) ||
t.beneIfsc.toLowerCase().includes(q) ||
t.beneName.toLowerCase().includes(q) ||
t.remitterAcc.toLowerCase().includes(q)
);
});
}
// ───────────────────── Tab-specific aggregations ─────────────────────
get bankRows(): BankRow[] {
const map = new Map<string, BankRow>();
for (const t of this.txns) {
const key = t.beneBank;
if (!map.has(key)) {
map.set(key, { bank: key, count: 0, amount: 0, settled: 0, pending: 0, returned: 0 });
}
const r = map.get(key)!;
r.count++;
r.amount += t.amount;
if (t.processingState === 'SETTLED') r.settled++;
else if (t.processingState === 'RETURNED') r.returned++;
else r.pending++;
}
return Array.from(map.values()).sort((a, b) => b.amount - a.amount);
}
get dateRows(): DateRow[] {
const map = new Map<string, DateRow>();
for (const t of this.txns) {
if (!map.has(t.valueDate)) {
map.set(t.valueDate, { date: t.valueDate, count: 0, amount: 0, neft: 0, rtgs: 0 });
}
const r = map.get(t.valueDate)!;
r.count++;
r.amount += t.amount;
if (t.paymentType === 'NEFT') r.neft++;
else r.rtgs++;
}
return Array.from(map.values()).sort((a, b) => (a.date < b.date ? 1 : -1));
}
get userRows(): UserRow[] {
const map = new Map<string, UserRow>();
for (const t of this.txns) {
const key = `${t.remitterUser}|${t.remitterBranch}`;
if (!map.has(key)) {
map.set(key, { user: t.remitterUser, branch: t.remitterBranch, count: 0, amount: 0 });
}
const r = map.get(key)!;
r.count++;
r.amount += t.amount;
}
return Array.from(map.values()).sort((a, b) => b.amount - a.amount);
}
get channelRows(): ChannelRow[] {
const total = this.totalAmount || 1;
const map = new Map<'BRANCH' | 'MOBILE', ChannelRow>();
for (const t of this.txns) {
if (!map.has(t.remitterChannel)) {
map.set(t.remitterChannel, {
channel: t.remitterChannel,
count: 0,
amount: 0,
share: 0
});
}
const r = map.get(t.remitterChannel)!;
r.count++;
r.amount += t.amount;
}
map.forEach((r) => (r.share = (r.amount / total) * 100));
return Array.from(map.values());
}
/** Trend buckets — last 7 value-dates */
get trendBuckets(): DateRow[] {
return this.dateRows.slice(0, 7).reverse();
}
/** Max amount across trend buckets (for bar chart scaling). */
get trendMaxAmount(): number {
const buckets = this.trendBuckets;
if (!buckets.length) return 1;
return Math.max(...buckets.map((b) => b.amount));
}
/** Return-code breakdown for Pending/Pass/Return tab */
get returnCodeRows(): { code: string; reason: string; count: number; amount: number }[] {
const map = new Map<string, { code: string; reason: string; count: number; amount: number }>();
for (const t of this.txns) {
if (t.processingState !== 'RETURNED' || !t.returnCode) continue;
if (!map.has(t.returnCode)) {
map.set(t.returnCode, {
code: t.returnCode,
reason: t.returnReason || '',
count: 0,
amount: 0
});
}
const r = map.get(t.returnCode)!;
r.count++;
r.amount += t.amount;
}
return Array.from(map.values()).sort((a, b) => b.count - a.count);
}
// ───────────────────── Actions ─────────────────────
showTab(tab: ReportTab): void {
this.activeTab = tab;
}
/** Floating action — open NEFT/RTGS entry in a non-dismissible dialog. */
openInitiateDialog(): void {
const ref = this.dialog.open(NeftRtgsEntryComponent, {
width: '880px',
maxWidth: '94vw',
maxHeight: '88vh',
disableClose: true,
autoFocus: false,
restoreFocus: true,
panelClass: 'nr-entry-dialog',
backdropClass: 'nr-entry-backdrop'
});
ref.afterClosed().subscribe((result) => {
if (result && result.submitted && result.payload) {
this.txns = [
this.toTxn(result.payload),
...this.txns
];
}
});
}
exportCsv(): void {
const headers = [
'Batch ID',
'Instruction ID',
'UTR Number',
'Remitter Acc',
'Bene Acc No',
'Bene IFSC',
'Bene Name',
'Payment Type',
'Amount',
'Value Date',
'Purpose Code',
'LEI Code',
'Processing State',
'Return Code',
'Sent Timestamp'
];
const rows: string[][] = [headers];
for (const t of this.filteredTxns) {
rows.push([
t.batchId,
t.instructionId,
t.utrNumber,
t.remitterAcc,
t.beneAcc,
t.beneIfsc,
t.beneName,
t.paymentType,
t.amount.toFixed(2),
t.valueDate,
t.purposeCode,
t.leiCode || '',
t.processingState,
t.returnCode || '',
t.sentTimestamp
]);
}
const escape = (v: string): string => {
const s = (v || '').replace(/"/g, '""');
return /[,"\n]/.test(s) ? `"${s}"` : s;
};
const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n';
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `NEFT_RTGS_Report_${new Date().toISOString().slice(0, 10)}.csv`;
a.click();
URL.revokeObjectURL(url);
}
clearFilters(): void {
this.search = '';
this.typeFilter = '';
this.stateFilter = '';
}
// ───────────────────── Helpers ─────────────────────
stateColor(state: ProcessingState): string {
switch (state) {
case 'SETTLED':
return 'green';
case 'RETURNED':
return 'red';
case 'SENT':
return 'blue';
case 'VALIDATED':
return 'teal';
case 'UPLOADED':
return 'amber';
case 'PENDING':
return 'amber';
}
}
trendBarHeight(amount: number): number {
return Math.max(6, Math.round((amount / this.trendMaxAmount) * 100));
}
/** Convert a submitted entry-form payload into a synthetic transaction row. */
private toTxn(p: any): NeftRtgsTxn {
const valueDate = p.valueDate
? new Date(p.valueDate).toISOString().slice(0, 10)
: new Date().toISOString().slice(0, 10);
const ts = new Date();
const pad = (n: number) => String(n).padStart(2, '0');
return {
batchId: `H2H-${valueDate.replace(/-/g, '')}-${pad(Math.floor(Math.random() * 99) + 1)}`,
instructionId: 'ERP' + Date.now().toString().slice(-8),
utrNumber: 'KKBK' + Date.now().toString().slice(-11),
remitterAcc: p.senderAccount || '',
remitterBranch: 'Main Branch',
remitterUser: 'Current User',
remitterChannel: 'BRANCH',
beneAcc: p.beneficiaryAccount || '',
beneIfsc: (p.ifscCode || '').toUpperCase(),
beneName: p.beneficiaryName || '',
beneBank: this.bankFromIfsc(p.ifscCode || ''),
paymentType: p.transactionType,
amount: Number(p.amount) || 0,
valueDate,
purposeCode: this.purposeCodeFor(p.purposeOfRemittance),
leiCode: Number(p.amount) >= 500000000 ? '549300ABCDEF1234' : undefined,
processingState: 'UPLOADED',
sentTimestamp:
ts.getFullYear() +
'-' +
pad(ts.getMonth() + 1) +
'-' +
pad(ts.getDate()) +
' ' +
pad(ts.getHours()) +
':' +
pad(ts.getMinutes()) +
':' +
pad(ts.getSeconds())
};
}
private bankFromIfsc(ifsc: string): string {
const code = ifsc.slice(0, 4).toUpperCase();
const map: Record<string, string> = {
HDFC: 'HDFC Bank',
ICIC: 'ICICI Bank',
SBIN: 'State Bank of India',
KKBK: 'Kotak Mahindra Bank',
AXIS: 'Axis Bank',
UTIB: 'Axis Bank',
PUNB: 'Punjab National Bank',
BARB: 'Bank of Baroda',
CNRB: 'Canara Bank',
YESB: 'YES Bank',
IDFB: 'IDFC FIRST Bank'
};
return map[code] || code + ' Bank';
}
private purposeCodeFor(p: string): string {
const map: Record<string, string> = {
CAPITAL_TRANSFER: 'P0102',
FAMILY_MAINTENANCE: 'P1301',
TRADE_SETTLEMENT: 'P0801',
SALARY_PAYMENT: 'P1006',
LOAN_REPAYMENT: 'P0103',
TAX_PAYMENT: 'P1099',
VENDOR_PAYMENT: 'P0802',
INVESTMENT: 'P0202',
GIFT: 'P1302',
OTHER: 'P0099'
};
return map[p] || 'P0099';
}
// ───────────────────── Mock seed data ─────────────────────
private seedTransactions(): NeftRtgsTxn[] {
const today = new Date();
const dt = (offset: number, h = 10, m = 0): string => {
const d = new Date(today);
d.setDate(d.getDate() - offset);
const pad = (n: number) => String(n).padStart(2, '0');
return (
d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(h) + ':' + pad(m) + ':00'
);
};
const vd = (offset: number): string => dt(offset).slice(0, 10);
return [
{
batchId: 'H2H-20260326-01',
instructionId: 'ERP77889922',
utrNumber: 'KKBKR5202608511',
remitterAcc: '50200012345678',
remitterBranch: 'Mumbai — BKC',
remitterUser: 'Anita Desai',
remitterChannel: 'BRANCH',
beneAcc: '91000222333444',
beneIfsc: 'HDFC0001234',
beneName: 'Global Logistics Ltd',
beneBank: 'HDFC Bank',
paymentType: 'RTGS',
amount: 550000,
valueDate: vd(0),
purposeCode: 'P0102',
leiCode: '549300ABCDEF1234',
processingState: 'SETTLED',
sentTimestamp: dt(0, 14, 5)
},
{
batchId: 'H2H-20260326-01',
instructionId: 'ERP77889923',
utrNumber: 'HDFCR5202608712',
remitterAcc: '50200012345678',
remitterBranch: 'Mumbai — BKC',
remitterUser: 'Anita Desai',
remitterChannel: 'BRANCH',
beneAcc: '345678901234',
beneIfsc: 'ICIC0000567',
beneName: 'Bluestone Trading Co.',
beneBank: 'ICICI Bank',
paymentType: 'NEFT',
amount: 78500,
valueDate: vd(0),
purposeCode: 'P0802',
processingState: 'SENT',
sentTimestamp: dt(0, 14, 6)
},
{
batchId: 'H2H-20260325-04',
instructionId: 'ERP77889811',
utrNumber: 'SBINR5202608902',
remitterAcc: '50200099887766',
remitterBranch: 'Bengaluru — MG Road',
remitterUser: 'Karthik Rao',
remitterChannel: 'MOBILE',
beneAcc: '912345678901',
beneIfsc: 'SBIN0011223',
beneName: 'Anita Verma',
beneBank: 'State Bank of India',
paymentType: 'NEFT',
amount: 15000,
valueDate: vd(1),
purposeCode: 'P1301',
processingState: 'SETTLED',
sentTimestamp: dt(1, 11, 22)
},
{
batchId: 'H2H-20260325-04',
instructionId: 'ERP77889812',
utrNumber: 'AXISR5202608811',
remitterAcc: '50200099887766',
remitterBranch: 'Bengaluru — MG Road',
remitterUser: 'Karthik Rao',
remitterChannel: 'MOBILE',
beneAcc: '6789012345',
beneIfsc: 'UTIB0000456',
beneName: 'Rakesh Industries',
beneBank: 'Axis Bank',
paymentType: 'RTGS',
amount: 1250000,
valueDate: vd(1),
purposeCode: 'P0801',
leiCode: '549300XYZAB7890',
processingState: 'RETURNED',
returnCode: 'R01',
returnReason: 'Insufficient Funds',
sentTimestamp: dt(1, 11, 25)
},
{
batchId: 'H2H-20260324-02',
instructionId: 'ERP77889701',
utrNumber: 'PUNBR5202608619',
remitterAcc: '50100221122334',
remitterBranch: 'Delhi — Connaught',
remitterUser: 'Sushma Iyer',
remitterChannel: 'BRANCH',
beneAcc: '50100998877665',
beneIfsc: 'PUNB0123400',
beneName: 'Madhavi Singh',
beneBank: 'Punjab National Bank',
paymentType: 'NEFT',
amount: 42000,
valueDate: vd(2),
purposeCode: 'P1006',
processingState: 'SETTLED',
sentTimestamp: dt(2, 9, 35)
},
{
batchId: 'H2H-20260324-02',
instructionId: 'ERP77889702',
utrNumber: 'YESBR5202608520',
remitterAcc: '50100221122334',
remitterBranch: 'Delhi — Connaught',
remitterUser: 'Sushma Iyer',
remitterChannel: 'BRANCH',
beneAcc: '11223344556677',
beneIfsc: 'YESB0000123',
beneName: 'Sigma Constructions',
beneBank: 'YES Bank',
paymentType: 'RTGS',
amount: 825000,
valueDate: vd(2),
purposeCode: 'P0102',
processingState: 'PENDING',
sentTimestamp: dt(2, 9, 38)
},
{
batchId: 'H2H-20260323-07',
instructionId: 'ERP77889601',
utrNumber: 'CNRBR5202608417',
remitterAcc: '50100887766554',
remitterBranch: 'Chennai — T. Nagar',
remitterUser: 'Ramesh Krishnan',
remitterChannel: 'MOBILE',
beneAcc: '99887766554433',
beneIfsc: 'CNRB0001890',
beneName: 'Padma Rao',
beneBank: 'Canara Bank',
paymentType: 'NEFT',
amount: 9800,
valueDate: vd(3),
purposeCode: 'P1302',
processingState: 'SETTLED',
sentTimestamp: dt(3, 16, 15)
},
{
batchId: 'H2H-20260323-07',
instructionId: 'ERP77889602',
utrNumber: 'BARBR5202608318',
remitterAcc: '50100887766554',
remitterBranch: 'Chennai — T. Nagar',
remitterUser: 'Ramesh Krishnan',
remitterChannel: 'BRANCH',
beneAcc: '50100123456987',
beneIfsc: 'BARB0VJVATA',
beneName: 'Vijaya Traders',
beneBank: 'Bank of Baroda',
paymentType: 'NEFT',
amount: 125000,
valueDate: vd(3),
purposeCode: 'P0802',
processingState: 'VALIDATED',
sentTimestamp: dt(3, 16, 20)
},
{
batchId: 'H2H-20260322-03',
instructionId: 'ERP77889501',
utrNumber: 'IDFBR5202608215',
remitterAcc: '50200445566778',
remitterBranch: 'Pune — Koregaon Park',
remitterUser: 'Vikram Joshi',
remitterChannel: 'BRANCH',
beneAcc: '70010023456789',
beneIfsc: 'IDFB0040101',
beneName: 'Pinnacle Realty Pvt Ltd',
beneBank: 'IDFC FIRST Bank',
paymentType: 'RTGS',
amount: 3450000,
valueDate: vd(4),
purposeCode: 'P0202',
leiCode: '549300QRSTUV5678',
processingState: 'SETTLED',
sentTimestamp: dt(4, 12, 45)
},
{
batchId: 'H2H-20260322-03',
instructionId: 'ERP77889502',
utrNumber: 'HDFCR5202608116',
remitterAcc: '50200445566778',
remitterBranch: 'Pune — Koregaon Park',
remitterUser: 'Vikram Joshi',
remitterChannel: 'MOBILE',
beneAcc: '50100654321789',
beneIfsc: 'HDFC0009876',
beneName: 'Neha Kapoor',
beneBank: 'HDFC Bank',
paymentType: 'NEFT',
amount: 25000,
valueDate: vd(4),
purposeCode: 'P1301',
processingState: 'SETTLED',
sentTimestamp: dt(4, 12, 50)
},
{
batchId: 'H2H-20260321-05',
instructionId: 'ERP77889411',
utrNumber: 'KKBKR5202608013',
remitterAcc: '50200778899001',
remitterBranch: 'Hyderabad — Banjara',
remitterUser: 'Lakshmi Naidu',
remitterChannel: 'BRANCH',
beneAcc: '12345678900011',
beneIfsc: 'KKBK0001899',
beneName: 'Aarav Enterprises',
beneBank: 'Kotak Mahindra Bank',
paymentType: 'NEFT',
amount: 67000,
valueDate: vd(5),
purposeCode: 'P0802',
processingState: 'RETURNED',
returnCode: 'R03',
returnReason: 'Account Closed',
sentTimestamp: dt(5, 10, 5)
},
{
batchId: 'H2H-20260321-05',
instructionId: 'ERP77889412',
utrNumber: 'SBINR5202607914',
remitterAcc: '50200778899001',
remitterBranch: 'Hyderabad — Banjara',
remitterUser: 'Lakshmi Naidu',
remitterChannel: 'BRANCH',
beneAcc: '50100445566778',
beneIfsc: 'SBIN0008765',
beneName: 'Spectra Components',
beneBank: 'State Bank of India',
paymentType: 'RTGS',
amount: 980000,
valueDate: vd(5),
purposeCode: 'P0801',
leiCode: '549300LMNOPQ2468',
processingState: 'SETTLED',
sentTimestamp: dt(5, 10, 10)
},
{
batchId: 'H2H-20260320-06',
instructionId: 'ERP77889301',
utrNumber: 'ICICR5202607812',
remitterAcc: '50100334455667',
remitterBranch: 'Kolkata — Park Street',
remitterUser: 'Arjun Banerjee',
remitterChannel: 'MOBILE',
beneAcc: '88990011223344',
beneIfsc: 'ICIC0001234',
beneName: 'Eastern Logistics',
beneBank: 'ICICI Bank',
paymentType: 'NEFT',
amount: 188000,
valueDate: vd(6),
purposeCode: 'P0802',
processingState: 'SETTLED',
sentTimestamp: dt(6, 14, 30)
},
{
batchId: 'H2H-20260320-06',
instructionId: 'ERP77889302',
utrNumber: 'AXISR5202607711',
remitterAcc: '50100334455667',
remitterBranch: 'Kolkata — Park Street',
remitterUser: 'Arjun Banerjee',
remitterChannel: 'BRANCH',
beneAcc: '50200998877665',
beneIfsc: 'UTIB0001234',
beneName: 'Indrani Sen',
beneBank: 'Axis Bank',
paymentType: 'NEFT',
amount: 5500,
valueDate: vd(6),
purposeCode: 'P1302',
processingState: 'SETTLED',
sentTimestamp: dt(6, 14, 32)
}
];
}
}