Files
cbsnexora/src/app/dicgc/dicgc.component.ts
T
AdityaandClaude Opus 4.7 12bfc81219 feat: add 11 banking compliance & accounting report modules
Adds dialog components and feature modules for RBI/banking compliance,
KYC, and accounting reports. Wires them into the shell menu (RBI,
General Ledger, CKYC Mgmt, Others, Insurance) and dashboard cards.

Dialog components (lazy-loaded MatDialog):
- PSL Returns (Form III) — dashboard card + RBI menu
- ALM Statement of Structural Liquidity (Form ALM-1) — RBI menu
- CIMS / XBRL Returns (OSS-2 / PSA / RAL / RCP-UCB) — RBI menu, shared
  dialog with form-pill switcher
- CIC DQR (CIBIL / Experian / Equifax / CRIF, 72-col TUDF) — Others menu
  + dashboard card
- CKYC Compliance Reports (Re-KYC / Risk Review / CERSAI) — CKYC Mgmt
- Accounting Reports (P&L / Balance Sheet / Comparative B/S / GL DR/CR
  / Misc Expense Ledger) — General Ledger menu

Feature modules (Route.withShell):
- DEAF (/deaf): Form I, Form II, Annex 4, accounts register
- DICGC (/dicgc): Slab Report, SAHAY Depositor List
- FIU-IND (/fiu): FINnet 2.0 Statement of Financial Transactions
- SFT (/sft): Income Tax Section 285BA SFT
- CKYC Reports (/ckyc/ckyc-reports): standalone dashboard

UI:
- Theme-colored icons on 22 wired menu links across 5 menus —
  visually distinguishes wired (colored) from placeholder (gray) links.
- Backdrop classes per dialog in main.scss.

All using dummy data (Bharat Urban Bank Ltd, Karthik Kamath as Principal
Officer, dummy customer names) — no real bank or customer references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:59:50 +05:30

399 lines
12 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 } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import {
DEPOSIT_SLABS,
DICGC_BANK,
LOAN_SLABS,
SahayDepositor,
SAHAY_DEPOSITORS,
SlabRow,
SUMMARY_D
} from './dicgc-data';
@Component({
selector: 'mifosx-dicgc',
templateUrl: './dicgc.component.html',
styleUrls: ['./dicgc.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
FormsModule
]
})
export class DicgcComponent {
/** Active tab */
activeTab: 'dashboard' | 'slab' | 'sahay' = 'dashboard';
bank = DICGC_BANK;
deposits: SlabRow[] = DEPOSIT_SLABS;
loans: SlabRow[] = LOAN_SLABS;
summary = SUMMARY_D;
depositors: SahayDepositor[] = SAHAY_DEPOSITORS;
/** SAHAY filters */
filterName = '';
filterAccountNumber = '';
filterAboveLimit = false;
/** Toast */
toastMsg = '';
toastVisible = false;
private toastTimer: any;
showTab(tab: 'dashboard' | 'slab' | 'sahay'): void {
this.activeTab = tab;
}
// -------------------- KPI helpers --------------------
private sumSlab(
rows: SlabRow[],
slab: keyof Pick<SlabRow, 'upto1Lakh' | 'oneTo2Lakh' | 'twoTo3Lakh' | 'above3Lakh' | 'total'>
): { amount: number; accounts: number } {
let amount = 0;
let accounts = 0;
for (const r of rows) {
amount += r[slab].amount;
accounts += r[slab].accounts;
}
return { amount, accounts };
}
get depositTotal(): { amount: number; accounts: number } {
return this.sumSlab(this.deposits, 'total');
}
get loanTotal(): { amount: number; accounts: number } {
return this.sumSlab(this.loans, 'total');
}
/** Insured = full amount for ≤5L, capped at 5L for >5L (sum across slabs ≤3L is fully insured here) */
get fullyInsuredAccounts(): number {
const u1 = this.sumSlab(this.deposits, 'upto1Lakh').accounts;
const u2 = this.sumSlab(this.deposits, 'oneTo2Lakh').accounts;
const u3 = this.sumSlab(this.deposits, 'twoTo3Lakh').accounts;
return u1 + u2 + u3;
}
get above3LakhAccounts(): number {
return this.sumSlab(this.deposits, 'above3Lakh').accounts;
}
get insuredAmount(): number {
return this.depositors.reduce((s, d) => s + d.insuredAmount, 0);
}
/** DICGC premium = 0.12% of total assessable deposits per half year */
get premiumDue(): number {
return this.depositTotal.amount * (this.bank.premiumRatePer100 / 100);
}
// SAHAY filtered list
get filteredDepositors(): SahayDepositor[] {
const name = this.filterName.trim().toLowerCase();
const acc = this.filterAccountNumber.trim();
return this.depositors.filter((d) => {
if (name && !`${d.nameFirst} ${d.nameSecond} ${d.nameThird}`.toLowerCase().includes(name)) return false;
if (acc && !d.accountNumber.includes(acc)) return false;
if (this.filterAboveLimit && d.amountOfDeposit <= this.bank.insuranceLimit) return false;
return true;
});
}
resetFilters(): void {
this.filterName = '';
this.filterAccountNumber = '';
this.filterAboveLimit = false;
}
/** True if depositor's amount exceeds the DICGC ₹5L cap */
isUninsured(d: SahayDepositor): boolean {
return d.amountOfDeposit > this.bank.insuranceLimit;
}
// -------------------- Excel exports --------------------
exportSlabReport(): void {
const rows: string[][] = [];
rows.push([
this.bank.name,
this.bank.branch
]);
rows.push([`A/C-Wise Deposit Insurance and Credit Guarantee Report — As On ${this.bank.reportAsOn}`]);
rows.push([]);
rows.push([
`Report Ref: ${this.bank.reportRefNo}`,
`Print Date: ${this.bank.printDate}`,
`User Code: ${this.bank.userCode}`
]);
rows.push([]);
// Header section
rows.push(['CREDIT BALANCE — DEPOSITS']);
rows.push([
'Sr.No',
'Description',
'Up to 1L Amount',
'Up to 1L #A/c',
'1L-2L Amount',
'1L-2L #A/c',
'2L-3L Amount',
'2L-3L #A/c',
'Above 3L Amount',
'Above 3L #A/c',
'Total Amount',
'Total #A/c'
]);
for (const r of this.deposits) {
rows.push(this.flattenSlabRow(r));
}
const dt = this.depositTotal;
rows.push([
'',
'TOTAL',
this.sumSlab(this.deposits, 'upto1Lakh').amount.toFixed(2),
this.sumSlab(this.deposits, 'upto1Lakh').accounts.toString(),
this.sumSlab(this.deposits, 'oneTo2Lakh').amount.toFixed(2),
this.sumSlab(this.deposits, 'oneTo2Lakh').accounts.toString(),
this.sumSlab(this.deposits, 'twoTo3Lakh').amount.toFixed(2),
this.sumSlab(this.deposits, 'twoTo3Lakh').accounts.toString(),
this.sumSlab(this.deposits, 'above3Lakh').amount.toFixed(2),
this.sumSlab(this.deposits, 'above3Lakh').accounts.toString(),
dt.amount.toFixed(2),
dt.accounts.toString()
]);
rows.push([]);
rows.push(['CREDIT BALANCE (LOAN A/C) — Section A']);
rows.push([
'Sr.No',
'Description',
'Up to 1L Amount',
'Up to 1L #A/c',
'1L-2L Amount',
'1L-2L #A/c',
'2L-3L Amount',
'2L-3L #A/c',
'Above 3L Amount',
'Above 3L #A/c',
'Total Amount',
'Total #A/c'
]);
for (const r of this.loans) {
rows.push(this.flattenSlabRow(r));
}
const lt = this.loanTotal;
rows.push([
'',
'TOTAL',
this.sumSlab(this.loans, 'upto1Lakh').amount.toFixed(2),
this.sumSlab(this.loans, 'upto1Lakh').accounts.toString(),
this.sumSlab(this.loans, 'oneTo2Lakh').amount.toFixed(2),
this.sumSlab(this.loans, 'oneTo2Lakh').accounts.toString(),
this.sumSlab(this.loans, 'twoTo3Lakh').amount.toFixed(2),
this.sumSlab(this.loans, 'twoTo3Lakh').accounts.toString(),
this.sumSlab(this.loans, 'above3Lakh').amount.toFixed(2),
this.sumSlab(this.loans, 'above3Lakh').accounts.toString(),
lt.amount.toFixed(2),
lt.accounts.toString()
]);
rows.push([]);
rows.push(['Section D — Final Summary']);
rows.push([
'Sr.No',
'Description',
'Amount',
'#A/c'
]);
for (const s of this.summary) {
rows.push([
s.sl.toString(),
s.description,
s.amount.toFixed(2),
s.accounts.toString()
]);
}
const grandAmount = this.summary.reduce((a, x) => a + x.amount, 0);
const grandAcc = this.summary.reduce((a, x) => a + x.accounts, 0);
rows.push([
'',
'TOTAL',
grandAmount.toFixed(2),
grandAcc.toString()
]);
this.saveTsv(rows, `DICGC-SlabReport-${this.fileTag()}.xls`);
this.toast('DICGC slab report downloaded');
}
exportSahay(): void {
const headers = [
'Input Serial Number',
'Customer ID (UCIC) Number',
'Account Number',
'Type of account',
'Nature of account',
'Number of Acct. Holders (in case of Joint)',
'Name Of First Customer',
'Name Of Second Customer',
'Name Of Third Customer',
'Date of Birth (Primary Holder) or Date of incorporation in case of Entity',
'Amount of Deposit',
'Insured Amount',
'Loan against deposits only (LAD / LAFD)',
'PAN Number',
'Aadhaar Number',
'Any Other OVD / KYC Document Type',
'Any Other OVD / KYC Number',
'Mobile Number',
'Type of Deposit',
'Date of Last Customer inducted transaction'
];
const rows: string[][] = [headers];
for (const d of this.filteredDepositors) {
rows.push([
d.inputSerialNumber.toString(),
d.customerIdUcic,
d.accountNumber,
d.typeOfAccount,
d.natureOfAccount,
d.numberOfHolders.toString(),
d.nameFirst,
d.nameSecond,
d.nameThird,
d.dateOfBirthOrIncorporation,
d.amountOfDeposit.toFixed(2),
d.insuredAmount.toFixed(2),
d.loanAgainstDeposit,
d.panNumber,
d.aadhaarNumber,
d.otherKycType,
d.otherKycNumber,
d.mobileNumber,
d.typeOfDeposit,
d.lastCustomerInducedTxnDate
]);
}
this.saveTsv(rows, `DICGC-SAHAY-Depositors-${this.fileTag()}.xls`);
this.toast(`${this.filteredDepositors.length} depositors exported (SAHAY format)`);
}
exportSahayCsv(): void {
// CSV mirrors the Input File Template DICGC ships
const headers = [
'Input Serial Number',
'Customer ID (UCIC) Number',
'Account Number',
'Type of account',
'Nature of account',
'Number of Acct. Holders (in case of Joint)',
'Name Of First Customer',
'Name Of Second Customer',
'Name Of Third Customer',
'Date of Birth (Primary Holder) or Date of incorporation in case of Entity',
'Amount of Deposit',
'Insured Amount',
'Loan against deposits only (LAD / LAFD)',
'PAN Number',
'Aadhaar Number',
'Any Other OVD / KYC Document Type',
'Any Other OVD / KYC Number',
'Mobile Number',
'Type of Deposit',
'Date of Last Customer inducted transaction'
];
const escape = (v: string): string => {
const s = (v || '').replace(/"/g, '""');
if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`;
return s;
};
const lines: string[] = [headers.map(escape).join(',')];
for (const d of this.filteredDepositors) {
lines.push(
[
d.inputSerialNumber.toString(),
d.customerIdUcic,
d.accountNumber,
d.typeOfAccount,
d.natureOfAccount,
d.numberOfHolders.toString(),
d.nameFirst,
d.nameSecond,
d.nameThird,
d.dateOfBirthOrIncorporation,
d.amountOfDeposit.toFixed(2),
d.insuredAmount.toFixed(2),
d.loanAgainstDeposit,
d.panNumber,
d.aadhaarNumber,
d.otherKycType,
d.otherKycNumber,
d.mobileNumber,
d.typeOfDeposit,
d.lastCustomerInducedTxnDate
]
.map(escape)
.join(',')
);
}
const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `DICGC-SAHAY-Depositors-${this.fileTag()}.csv`;
a.click();
URL.revokeObjectURL(url);
this.toast(`${this.filteredDepositors.length} depositors exported (CSV)`);
}
private flattenSlabRow(r: SlabRow): string[] {
return [
r.sl.toString(),
r.description,
r.upto1Lakh.amount.toFixed(2),
r.upto1Lakh.accounts.toString(),
r.oneTo2Lakh.amount.toFixed(2),
r.oneTo2Lakh.accounts.toString(),
r.twoTo3Lakh.amount.toFixed(2),
r.twoTo3Lakh.accounts.toString(),
r.above3Lakh.amount.toFixed(2),
r.above3Lakh.accounts.toString(),
r.total.amount.toFixed(2),
r.total.accounts.toString()
];
}
private saveTsv(rows: string[][], filename: string): void {
const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n');
const blob = new Blob([tsv], { type: 'application/vnd.ms-excel;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
private fileTag(): string {
const d = new Date();
const dd = String(d.getDate()).padStart(2, '0');
const mm = String(d.getMonth() + 1).padStart(2, '0');
return `${dd}${mm}${d.getFullYear()}`;
}
toast(msg: string): void {
this.toastMsg = msg;
this.toastVisible = true;
if (this.toastTimer) clearTimeout(this.toastTimer);
this.toastTimer = setTimeout(() => (this.toastVisible = false), 2800);
}
}