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>
399 lines
12 KiB
TypeScript
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);
|
|
}
|
|
}
|