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>
This commit is contained in:
Aditya
2026-04-29 18:59:50 +05:30
co-authored by Claude Opus 4.7
parent 634003a75c
commit 12bfc81219
61 changed files with 22207 additions and 725 deletions
@@ -0,0 +1,840 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, inject } from '@angular/core';
import { CommonModule, DecimalPipe } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
export type CimsFormCode = 'OSS-2' | 'PSA' | 'RAL' | 'RCP-UCB';
export interface CimsXbrlDialogData {
initialForm?: CimsFormCode;
}
interface CimsField {
ref: string;
label: string;
section: string;
value: number | string;
unit?: 'INR-Lakh' | 'INR-Cr' | 'Pure' | 'Percent' | 'Number';
type?: 'monetary' | 'numeric' | 'string' | 'percent';
}
interface CimsForm {
code: CimsFormCode;
name: string;
fullName: string;
description: string;
cimsCode: string;
taxonomy: string;
periodicity: 'Daily' | 'Monthly' | 'Quarterly' | 'Half-Yearly' | 'Annual';
lastFiled: string;
nextDue: string;
fields: CimsField[];
}
@Component({
selector: 'mifosx-cims-xbrl-dialog',
standalone: true,
templateUrl: './cims-xbrl-dialog.component.html',
styleUrls: ['./cims-xbrl-dialog.component.scss'],
imports: [
CommonModule,
DecimalPipe,
FormsModule,
MatDialogModule,
MatButtonModule,
MatTooltipModule,
FaIconComponent
]
})
export class CimsXbrlDialogComponent {
private dialogRef = inject(MatDialogRef<CimsXbrlDialogComponent>);
activeForm: CimsFormCode;
activeTab: 'filing' | 'data' | 'preview' = 'filing';
bank = {
name: 'Bharat Urban Bank Ltd',
branch: 'Head Office',
bsrCode: 'BUBL0000123',
ifsc: 'BUBL0000123',
legalEntityId: 'BUBL-LEI-2025',
pan: 'AAACB1234L',
asOnDate: '31-03-2026',
quarter: 'Q4 FY 2025-26',
fiscalYear: '2025-26',
principalOfficer: 'Karthik Kamath',
poDesignation: 'Chief Compliance Officer',
poEmail: 'compliance@bharaturbanbank.in',
poMobile: '+91-9988002211',
cimsUserId: 'BUBL_CIMS_001',
cimsPortal: 'https://cims.rbi.org.in/'
};
forms: Record<CimsFormCode, CimsForm> = {
'OSS-2': {
code: 'OSS-2',
name: 'OSS-2',
fullName: 'Off-site Surveillance — Statement of Asset Quality &amp; Capital Adequacy',
description: 'Quarterly return on asset classification, NPA, capital adequacy and profitability indicators.',
cimsCode: 'CIMS_OSS_002',
taxonomy: 'rbi-cims-oss-2024-04-01',
periodicity: 'Quarterly',
lastFiled: '15-01-2026',
nextDue: '15-04-2026',
fields: [
{
ref: 'oss2:CapAdq.CRAR',
section: 'Capital Adequacy',
label: 'CRAR (Total)',
value: 14.82,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:CapAdq.Tier1',
section: 'Capital Adequacy',
label: 'Tier-I CRAR',
value: 12.18,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:CapAdq.Tier2',
section: 'Capital Adequacy',
label: 'Tier-II CRAR',
value: 2.64,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Asset.Total',
section: 'Asset Classification',
label: 'Total Advances (₹ Lakh)',
value: 416265,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.Standard',
section: 'Asset Classification',
label: 'Standard Assets (₹ Lakh)',
value: 401820,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.SubStd',
section: 'Asset Classification',
label: 'Sub-Standard (₹ Lakh)',
value: 6220,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.Doubtful',
section: 'Asset Classification',
label: 'Doubtful (₹ Lakh)',
value: 5840,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.Loss',
section: 'Asset Classification',
label: 'Loss (₹ Lakh)',
value: 2385,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.GNPA',
section: 'NPA Position',
label: 'Gross NPA (₹ Lakh)',
value: 14445,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.NNPA',
section: 'NPA Position',
label: 'Net NPA (₹ Lakh)',
value: 5212,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'oss2:Asset.GNPAPct',
section: 'NPA Position',
label: 'GNPA %',
value: 3.47,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Asset.NNPAPct',
section: 'NPA Position',
label: 'NNPA %',
value: 1.27,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Asset.PCR',
section: 'NPA Position',
label: 'Provision Coverage Ratio',
value: 64.13,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Profit.NIM',
section: 'Profitability',
label: 'Net Interest Margin',
value: 3.21,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Profit.ROA',
section: 'Profitability',
label: 'Return on Assets',
value: 0.92,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Profit.ROE',
section: 'Profitability',
label: 'Return on Equity',
value: 11.48,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Liq.CRR',
section: 'Liquidity',
label: 'CRR Maintained',
value: 4.52,
unit: 'Percent',
type: 'percent'
},
{
ref: 'oss2:Liq.SLR',
section: 'Liquidity',
label: 'SLR Maintained',
value: 19.24,
unit: 'Percent',
type: 'percent'
}
]
},
PSA: {
code: 'PSA',
name: 'PSA',
fullName: 'Priority Sector Advances — Annual Achievement Statement',
description: 'Annual return on priority sector lending, sub-targets and weaker section coverage.',
cimsCode: 'CIMS_PSA_001',
taxonomy: 'rbi-cims-psa-2024-04-01',
periodicity: 'Annual',
lastFiled: '30-04-2025',
nextDue: '30-04-2026',
fields: [
{
ref: 'psa:Reference.ANBC',
section: 'Reference',
label: 'Adjusted Net Bank Credit (₹ Lakh)',
value: 600000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:Reference.OBE',
section: 'Reference',
label: 'Credit Equivalent of OBE (₹ Lakh)',
value: 25000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.Total',
section: 'PSL Achievement',
label: 'Total PSL Outstanding (₹ Lakh)',
value: 241700,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.TotalPct',
section: 'PSL Achievement',
label: 'Total PSL % of ANBC',
value: 40.28,
unit: 'Percent',
type: 'percent'
},
{
ref: 'psa:PSL.Agri',
section: 'Sub-targets',
label: 'Agriculture (₹ Lakh)',
value: 124000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.AgriPct',
section: 'Sub-targets',
label: 'Agriculture % of ANBC',
value: 20.67,
unit: 'Percent',
type: 'percent'
},
{
ref: 'psa:PSL.SMF',
section: 'Sub-targets',
label: 'Small &amp; Marginal Farmers (₹ Lakh)',
value: 72000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.SMFPct',
section: 'Sub-targets',
label: 'SMF % of ANBC',
value: 12.0,
unit: 'Percent',
type: 'percent'
},
{
ref: 'psa:PSL.MicroEnt',
section: 'Sub-targets',
label: 'Micro Enterprises (₹ Lakh)',
value: 51000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.MicroPct',
section: 'Sub-targets',
label: 'Micro Enterprises % of ANBC',
value: 8.5,
unit: 'Percent',
type: 'percent'
},
{
ref: 'psa:PSL.Weaker',
section: 'Sub-targets',
label: 'Weaker Sections (₹ Lakh)',
value: 85500,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.WeakerPct',
section: 'Sub-targets',
label: 'Weaker Sections % of ANBC',
value: 14.25,
unit: 'Percent',
type: 'percent'
},
{
ref: 'psa:PSL.Housing',
section: 'Other Categories',
label: 'Housing (₹ Lakh)',
value: 9200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.Education',
section: 'Other Categories',
label: 'Education (₹ Lakh)',
value: 4200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.Export',
section: 'Other Categories',
label: 'Export Credit (₹ Lakh)',
value: 3800,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'psa:PSL.RenEnergy',
section: 'Other Categories',
label: 'Renewable Energy (₹ Lakh)',
value: 2400,
unit: 'INR-Lakh',
type: 'monetary'
}
]
},
RAL: {
code: 'RAL',
name: 'RAL',
fullName: 'Return on Assets &amp; Liabilities (Half-Yearly)',
description: 'Half-yearly statement of bank-wide assets and liabilities composition for cooperative banks.',
cimsCode: 'CIMS_RAL_004',
taxonomy: 'rbi-cims-ral-2024-04-01',
periodicity: 'Half-Yearly',
lastFiled: '30-10-2025',
nextDue: '30-04-2026',
fields: [
{
ref: 'ral:Liab.Capital',
section: 'Liabilities',
label: 'Capital (₹ Lakh)',
value: 22000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.Reserves',
section: 'Liabilities',
label: 'Reserves &amp; Surplus (₹ Lakh)',
value: 32400,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.DepDemand',
section: 'Liabilities',
label: 'Demand Deposits (₹ Lakh)',
value: 84200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.DepSavings',
section: 'Liabilities',
label: 'Savings Deposits (₹ Lakh)',
value: 218600,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.DepTerm',
section: 'Liabilities',
label: 'Term Deposits (₹ Lakh)',
value: 462800,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.Borrow',
section: 'Liabilities',
label: 'Borrowings (₹ Lakh)',
value: 24180,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Liab.Other',
section: 'Liabilities',
label: 'Other Liabilities &amp; Provisions (₹ Lakh)',
value: 18420,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Cash',
section: 'Assets',
label: 'Cash on Hand (₹ Lakh)',
value: 8420,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.RBI',
section: 'Assets',
label: 'Balances with RBI (₹ Lakh)',
value: 38400,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Banks',
section: 'Assets',
label: 'Balances with Banks (₹ Lakh)',
value: 24800,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Money',
section: 'Assets',
label: 'Money at Call &amp; Short Notice (₹ Lakh)',
value: 12400,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Invest',
section: 'Assets',
label: 'Investments (₹ Lakh)',
value: 184600,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Adv',
section: 'Assets',
label: 'Advances (₹ Lakh)',
value: 416265,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Fixed',
section: 'Assets',
label: 'Fixed Assets (₹ Lakh)',
value: 14200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:Asset.Other',
section: 'Assets',
label: 'Other Assets (₹ Lakh)',
value: 12480,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:OBE.Guar',
section: 'Off-Balance Sheet',
label: 'Bank Guarantees (₹ Lakh)',
value: 18420,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'ral:OBE.LC',
section: 'Off-Balance Sheet',
label: 'Letters of Credit (₹ Lakh)',
value: 6580,
unit: 'INR-Lakh',
type: 'monetary'
}
]
},
'RCP-UCB': {
code: 'RCP-UCB',
name: 'RCP (UCB)',
fullName: 'Return on Cash Position — Urban Cooperative Banks',
description: 'Fortnightly statement of CRR/SLR maintenance, NDTL composition and reserve position.',
cimsCode: 'CIMS_RCP_UCB_007',
taxonomy: 'rbi-cims-rcp-ucb-2024-04-01',
periodicity: 'Monthly',
lastFiled: '14-04-2026',
nextDue: '28-04-2026',
fields: [
{
ref: 'rcp:Liab.DemandDep',
section: 'NDTL Composition',
label: 'Demand Liabilities (₹ Lakh)',
value: 84200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:Liab.TimeDep',
section: 'NDTL Composition',
label: 'Time Liabilities (₹ Lakh)',
value: 681400,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:Liab.NDTL',
section: 'NDTL Composition',
label: 'Net Demand &amp; Time Liabilities (NDTL)',
value: 740000,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:CRR.Required',
section: 'CRR Maintenance',
label: 'CRR Required (4.5%)',
value: 33300,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:CRR.Maintained',
section: 'CRR Maintenance',
label: 'CRR Maintained (avg)',
value: 33448,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:CRR.MaintainedPct',
section: 'CRR Maintenance',
label: 'CRR % Maintained',
value: 4.52,
unit: 'Percent',
type: 'percent'
},
{
ref: 'rcp:CRR.Surplus',
section: 'CRR Maintenance',
label: 'CRR Surplus / Shortfall (₹ Lakh)',
value: 148,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:SLR.Required',
section: 'SLR Maintenance',
label: 'SLR Required (18%)',
value: 133200,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:SLR.Maintained',
section: 'SLR Maintenance',
label: 'SLR Maintained (avg)',
value: 142376,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:SLR.MaintainedPct',
section: 'SLR Maintenance',
label: 'SLR % Maintained',
value: 19.24,
unit: 'Percent',
type: 'percent'
},
{
ref: 'rcp:SLR.Surplus',
section: 'SLR Maintenance',
label: 'SLR Surplus / Shortfall (₹ Lakh)',
value: 9176,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:Cash.OnHand',
section: 'Cash Position',
label: 'Cash on Hand (₹ Lakh)',
value: 8420,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:Cash.AtRBI',
section: 'Cash Position',
label: 'Cash at RBI (₹ Lakh)',
value: 33448,
unit: 'INR-Lakh',
type: 'monetary'
},
{
ref: 'rcp:Cash.AtBanks',
section: 'Cash Position',
label: 'Cash at other Banks (₹ Lakh)',
value: 24800,
unit: 'INR-Lakh',
type: 'monetary'
}
]
}
};
formCodes: CimsFormCode[] = [
'OSS-2',
'PSA',
'RAL',
'RCP-UCB'
];
data = inject<CimsXbrlDialogData | null>(MAT_DIALOG_DATA, { optional: true });
constructor() {
this.activeForm = this.data?.initialForm ?? 'OSS-2';
}
get currentForm(): CimsForm {
return this.forms[this.activeForm];
}
get sections(): string[] {
const s: string[] = [];
for (const f of this.currentForm.fields) {
if (!s.includes(f.section)) s.push(f.section);
}
return s;
}
fieldsBySection(section: string): CimsField[] {
return this.currentForm.fields.filter((f) => f.section === section);
}
setForm(code: CimsFormCode): void {
this.activeForm = code;
this.activeTab = 'filing';
}
setTab(tab: 'filing' | 'data' | 'preview'): void {
this.activeTab = tab;
}
close(): void {
this.dialogRef.close();
}
/** Format a number for XBRL output (no commas, 2 dp for monetary/percent) */
private fmtXbrl(f: CimsField): string {
if (typeof f.value === 'string') return f.value;
if (f.type === 'monetary' || f.type === 'percent') return f.value.toFixed(2);
return f.value.toString();
}
/** Build XBRL XML document for a form (representative; real CIMS taxonomy differs) */
generateXbrlXml(): string {
const f = this.currentForm;
const period = `${this.bank.fiscalYear} (${this.bank.quarter})`;
const ctxId = `ctx_${f.code.replace('-', '_')}`;
const startDate = '2026-01-01';
const endDate = '2026-03-31';
const lines: string[] = [];
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
lines.push('<xbrl');
lines.push(' xmlns="http://www.xbrl.org/2003/instance"');
lines.push(' xmlns:link="http://www.xbrl.org/2003/linkbase"');
lines.push(' xmlns:xbrli="http://www.xbrl.org/2003/instance"');
lines.push(' xmlns:iso4217="http://www.xbrl.org/2003/iso4217"');
lines.push(' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"');
lines.push(` xmlns:${f.code.replace('-', '').toLowerCase()}="${f.taxonomy}">`);
lines.push(` <link:schemaRef xlink:type="simple" xlink:href="${f.taxonomy}.xsd"/>`);
lines.push(` <!-- Period: ${period} -->`);
lines.push(` <context id="${ctxId}">`);
lines.push(' <entity>');
lines.push(` <identifier scheme="http://www.rbi.org.in/cims">${this.bank.legalEntityId}</identifier>`);
lines.push(' </entity>');
lines.push(' <period>');
lines.push(` <startDate>${startDate}</startDate>`);
lines.push(` <endDate>${endDate}</endDate>`);
lines.push(' </period>');
lines.push(' </context>');
lines.push(' <unit id="INR_Lakh"><measure>iso4217:INR</measure></unit>');
lines.push(' <unit id="Pure"><measure>xbrli:pure</measure></unit>');
lines.push(' <!-- Filing -->');
lines.push(` <cims:bankName contextRef="${ctxId}">${this.bank.name}</cims:bankName>`);
lines.push(` <cims:bsrCode contextRef="${ctxId}">${this.bank.bsrCode}</cims:bsrCode>`);
lines.push(` <cims:cimsFormCode contextRef="${ctxId}">${f.cimsCode}</cims:cimsFormCode>`);
lines.push(' <!-- Facts -->');
for (const fld of f.fields) {
const unitRef = fld.type === 'monetary' ? 'INR_Lakh' : 'Pure';
const decimals = fld.type === 'monetary' || fld.type === 'percent' ? '2' : '0';
lines.push(
` <${fld.ref} contextRef="${ctxId}" unitRef="${unitRef}" decimals="${decimals}">${this.fmtXbrl(
fld
)}</${fld.ref}>`
);
}
lines.push('</xbrl>');
return lines.join('\n');
}
/** Validation summary — counts fields, flags any zero/missing */
get validation(): { total: number; populated: number; empty: number; status: 'valid' | 'warning' } {
const fields = this.currentForm.fields;
const populated = fields.filter((f) => f.value !== '' && f.value !== 0).length;
const empty = fields.length - populated;
return {
total: fields.length,
populated,
empty,
status: empty === 0 ? 'valid' : 'warning'
};
}
// -------------------- Exports --------------------
exportXbrl(): void {
const xml = this.generateXbrlXml();
this.saveBlob(xml, `${this.currentForm.cimsCode}-${this.dateTag()}.xml`, 'application/xml');
}
copyXbrl(): void {
const xml = this.generateXbrlXml();
if (navigator.clipboard) navigator.clipboard.writeText(xml);
}
exportExcel(): void {
const f = this.currentForm;
const rows: string[][] = [];
rows.push([
this.bank.name,
this.bank.branch
]);
rows.push([
f.fullName,
`As On ${this.bank.asOnDate}`
]);
rows.push([
`CIMS Code: ${f.cimsCode}`,
`Taxonomy: ${f.taxonomy}`
]);
rows.push([
`Periodicity: ${f.periodicity}`,
`Quarter: ${this.bank.quarter}`
]);
rows.push([
`Principal Officer: ${this.bank.principalOfficer}`,
`Email: ${this.bank.poEmail}`
]);
rows.push([]);
rows.push([
'Section',
'Element Ref',
'Label',
'Value',
'Unit'
]);
for (const fld of f.fields) {
rows.push([
fld.section,
fld.ref,
fld.label.replace(/&amp;/g, '&'),
this.fmtXbrl(fld),
fld.unit ?? ''
]);
}
rows.push([]);
rows.push([
'Certified by',
this.bank.principalOfficer + ' — ' + this.bank.poDesignation
]);
this.saveTsv(rows, `${f.cimsCode}-${this.dateTag()}.xls`);
}
// ---- helpers ----
private dateTag(): string {
const d = new Date();
return `${String(d.getDate()).padStart(2, '0')}${String(d.getMonth() + 1).padStart(2, '0')}${d.getFullYear()}`;
}
private saveTsv(rows: string[][], filename: string): void {
const tsv = rows.map((r) => r.map((v) => (v || '').replace(/\t/g, ' ')).join('\t')).join('\n');
this.saveBlob(tsv, filename, 'application/vnd.ms-excel');
}
private saveBlob(content: string, filename: string, mime: string): void {
const blob = new Blob([content], { type: `${mime};charset=utf-8` });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
}