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,260 @@
<!--
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/.
-->
<div class="cims-dlg">
<!-- HEADER -->
<div class="cims-head">
<div class="cims-head-left">
<div class="cims-head-ic"><fa-icon icon="code"></fa-icon></div>
<div>
<h2 class="cims-title">CIMS / XBRL Returns &mdash; {{ currentForm.name }}</h2>
<p class="cims-sub" [innerHTML]="currentForm.fullName"></p>
</div>
</div>
<div class="cims-head-actions">
<span class="cims-status" [ngClass]="validation.status">
<fa-icon [icon]="validation.status === 'valid' ? 'check-double' : 'exclamation-triangle'"></fa-icon>
&nbsp;{{ validation.populated }}/{{ validation.total }} populated
</span>
<button class="cims-close" (click)="close()" matTooltip="Close" aria-label="Close">&#10005;</button>
</div>
</div>
<!-- FORM SELECTOR -->
<div class="cims-form-bar">
<span class="cims-form-bar-l">Active Form:</span>
<button
*ngFor="let code of formCodes"
class="cims-form-pill"
[class.active]="activeForm === code"
(click)="setForm(code)"
[matTooltip]="forms[code].fullName"
>
{{ forms[code].name }}
</button>
<span class="cims-form-meta mono"> <fa-icon icon="hashtag"></fa-icon>&nbsp;{{ currentForm.cimsCode }} </span>
<span class="cims-form-meta"> <fa-icon icon="calendar-alt"></fa-icon>&nbsp;{{ currentForm.periodicity }} </span>
<span class="cims-form-meta">
<fa-icon icon="clock"></fa-icon>&nbsp;Next due: <strong>{{ currentForm.nextDue }}</strong>
</span>
</div>
<!-- TABS -->
<div class="cims-tabs">
<button class="cims-tab" [class.active]="activeTab === 'filing'" (click)="setTab('filing')">
<fa-icon icon="address-card"></fa-icon>&nbsp; Filing Information
</button>
<button class="cims-tab" [class.active]="activeTab === 'data'" (click)="setTab('data')">
<fa-icon icon="layer-group"></fa-icon>&nbsp; Form Data &amp; Facts
</button>
<button class="cims-tab" [class.active]="activeTab === 'preview'" (click)="setTab('preview')">
<fa-icon icon="code"></fa-icon>&nbsp; XBRL XML Preview
</button>
</div>
<div class="cims-body">
<!-- ════════════ FILING INFO ════════════ -->
<ng-container *ngIf="activeTab === 'filing'">
<div class="cims-section">
<div class="cims-sec-h">
<h3 class="cims-sec-t">Reporting Entity</h3>
</div>
<div class="cims-info-grid">
<div class="cims-info">
<div class="cims-info-l">Bank Name</div>
<div class="cims-info-v">{{ bank.name }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Branch</div>
<div class="cims-info-v">{{ bank.branch }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">BSR Code</div>
<div class="cims-info-v mono">{{ bank.bsrCode }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">IFSC</div>
<div class="cims-info-v mono">{{ bank.ifsc }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Legal Entity ID</div>
<div class="cims-info-v mono">{{ bank.legalEntityId }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">PAN</div>
<div class="cims-info-v mono">{{ bank.pan }}</div>
</div>
</div>
</div>
<div class="cims-section">
<div class="cims-sec-h">
<h3 class="cims-sec-t">Return Particulars</h3>
</div>
<div class="cims-info-grid">
<div class="cims-info">
<div class="cims-info-l">CIMS Form Code</div>
<div class="cims-info-v mono">{{ currentForm.cimsCode }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Taxonomy Version</div>
<div class="cims-info-v mono">{{ currentForm.taxonomy }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Periodicity</div>
<div class="cims-info-v">{{ currentForm.periodicity }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Reporting Period</div>
<div class="cims-info-v">{{ bank.quarter }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">As On Date</div>
<div class="cims-info-v">{{ bank.asOnDate }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Last Filed</div>
<div class="cims-info-v">{{ currentForm.lastFiled }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Next Due</div>
<div class="cims-info-v">
<strong>{{ currentForm.nextDue }}</strong>
</div>
</div>
<div class="cims-info">
<div class="cims-info-l">CIMS Portal</div>
<div class="cims-info-v mono">{{ bank.cimsPortal }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">CIMS User ID</div>
<div class="cims-info-v mono">{{ bank.cimsUserId }}</div>
</div>
</div>
<div class="cims-desc" [innerHTML]="currentForm.description"></div>
</div>
<div class="cims-section">
<div class="cims-sec-h">
<h3 class="cims-sec-t">Principal Officer</h3>
</div>
<div class="cims-info-grid">
<div class="cims-info">
<div class="cims-info-l">Name</div>
<div class="cims-info-v">{{ bank.principalOfficer }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Designation</div>
<div class="cims-info-v">{{ bank.poDesignation }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Email</div>
<div class="cims-info-v mono">{{ bank.poEmail }}</div>
</div>
<div class="cims-info">
<div class="cims-info-l">Mobile</div>
<div class="cims-info-v mono">{{ bank.poMobile }}</div>
</div>
</div>
</div>
</ng-container>
<!-- ════════════ FORM DATA ════════════ -->
<ng-container *ngIf="activeTab === 'data'">
<div class="cims-section" *ngFor="let sec of sections">
<div class="cims-sec-h">
<h3 class="cims-sec-t">{{ sec }}</h3>
</div>
<div class="cims-tbl-wrap">
<table class="cims-tbl">
<thead>
<tr>
<th>XBRL Element</th>
<th>Label</th>
<th class="ta-r">Value</th>
<th class="ta-c">Unit</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let f of fieldsBySection(sec)">
<td class="mono cell-ref">{{ f.ref }}</td>
<td [innerHTML]="f.label"></td>
<td class="ta-r">
<strong>{{ f.value }}</strong>
<span *ngIf="f.type === 'percent'" class="muted">&nbsp;%</span>
</td>
<td class="ta-c">
<span class="unit-pill">{{ f.unit }}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</ng-container>
<!-- ════════════ XBRL PREVIEW ════════════ -->
<ng-container *ngIf="activeTab === 'preview'">
<div class="cims-section">
<div class="cims-sec-h">
<h3 class="cims-sec-t">XBRL Instance Document Preview</h3>
<div class="cims-sec-actions">
<button mat-stroked-button class="cims-btn-ghost" (click)="copyXbrl()" matTooltip="Copy XML to clipboard">
<fa-icon icon="copy"></fa-icon>&nbsp; Copy
</button>
<button mat-stroked-button class="cims-btn-xls" (click)="exportXbrl()">
<fa-icon icon="file-export"></fa-icon>&nbsp; Download .xml
</button>
<button mat-stroked-button class="cims-btn-xls cims-btn-excel" (click)="exportExcel()">
<fa-icon icon="file-excel"></fa-icon>&nbsp; Download Excel
</button>
</div>
</div>
<div class="cims-validation" [ngClass]="validation.status">
<fa-icon [icon]="validation.status === 'valid' ? 'check-double' : 'exclamation-triangle'"></fa-icon>
<div>
<div class="cims-val-t">
<ng-container *ngIf="validation.status === 'valid'"
>XBRL Document Valid &mdash; ready to upload</ng-container
>
<ng-container *ngIf="validation.status === 'warning'"
>{{ validation.empty }} field(s) need attention</ng-container
>
</div>
<div class="cims-val-d">
{{ validation.populated }} of {{ validation.total }} elements populated &middot; Taxonomy
<span class="mono">{{ currentForm.taxonomy }}</span>
</div>
</div>
</div>
<pre class="xbrl-preview"><code>{{ generateXbrlXml() }}</code></pre>
</div>
</ng-container>
</div>
<!-- FOOTER ACTIONS -->
<div class="cims-foot">
<div class="cims-foot-l">
<fa-icon icon="shield-alt"></fa-icon>&nbsp;
<span
>Submitted to RBI CIMS Portal under <strong>{{ currentForm.cimsCode }}</strong> by
{{ bank.principalOfficer }}</span
>
</div>
<div class="cims-foot-actions">
<button mat-stroked-button class="cims-btn-ghost" (click)="exportExcel()">
<fa-icon icon="file-excel"></fa-icon>&nbsp; Excel
</button>
<button mat-stroked-button class="cims-btn-xls" (click)="exportXbrl()">
<fa-icon icon="file-export"></fa-icon>&nbsp; Generate XBRL
</button>
</div>
</div>
</div>
@@ -0,0 +1,514 @@
/**
* 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/.
*/
:host {
--cims-good: #15803d;
--cims-good-l: #dcfce7;
--cims-mid: #d97706;
--cims-mid-l: #fef3c7;
--cims-low: #c0392b;
--cims-low-l: #fdecea;
--cims-cyan: #0891b2;
--cims-cyan-l: #cffafe;
--cims-teal: #0d9488;
--cims-teal-l: #ccfbf1;
--cims-g50: #f8fafc;
--cims-g100: #f1f5f9;
--cims-g200: #e2e8f0;
--cims-g300: #cbd5e1;
--cims-g500: #64748b;
--cims-g700: #334155;
--cims-g900: #0f172a;
display: block;
font-family:
'DM Sans',
system-ui,
-apple-system,
sans-serif;
color: var(--cims-g900);
}
.cims-dlg {
background: #fff;
border-radius: 14px;
display: flex;
flex-direction: column;
overflow: hidden;
max-height: 92vh;
}
/* HEADER */
.cims-head {
padding: 18px 22px;
border-bottom: 1px solid var(--cims-g200);
display: flex;
align-items: center;
gap: 16px;
flex-shrink: 0;
background: linear-gradient(135deg, #ecfeff 0%, #fff 100%);
}
.cims-head-left {
display: flex;
align-items: center;
gap: 14px;
flex: 1;
}
.cims-head-ic {
width: 44px;
height: 44px;
border-radius: 11px;
background: var(--cims-cyan-l);
color: var(--cims-cyan);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.cims-title {
margin: 0;
font-size: 17px;
font-weight: 700;
color: var(--cims-g900);
}
.cims-sub {
margin: 2px 0 0;
font-size: 12px;
color: var(--cims-g500);
}
.cims-head-actions {
display: flex;
align-items: center;
gap: 12px;
}
.cims-status {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
&.valid {
background: var(--cims-good-l);
color: var(--cims-good);
}
&.warning {
background: var(--cims-mid-l);
color: var(--cims-mid);
}
}
.cims-close {
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px solid var(--cims-g200);
background: #fff;
font-size: 14px;
color: var(--cims-g500);
cursor: pointer;
transition: all 0.15s;
&:hover {
background: var(--cims-g100);
color: var(--cims-g900);
}
}
/* FORM SELECTOR BAR */
.cims-form-bar {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 22px;
background: var(--cims-g50);
border-bottom: 1px solid var(--cims-g200);
flex-shrink: 0;
flex-wrap: wrap;
}
.cims-form-bar-l {
font-size: 11px;
font-weight: 700;
color: var(--cims-g500);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.cims-form-pill {
padding: 6px 14px;
border-radius: 999px;
border: 1px solid var(--cims-g200);
background: #fff;
cursor: pointer;
font-family: inherit;
font-size: 12px;
font-weight: 600;
color: var(--cims-g700);
transition: all 0.15s;
&:hover {
background: var(--cims-cyan-l);
border-color: var(--cims-cyan);
color: var(--cims-cyan);
}
&.active {
background: var(--cims-cyan);
border-color: var(--cims-cyan);
color: #fff;
box-shadow: 0 2px 6px rgb(8 145 178 / 25%);
}
}
.cims-form-meta {
display: inline-flex;
align-items: center;
font-size: 11px;
color: var(--cims-g500);
padding: 4px 10px;
background: #fff;
border: 1px solid var(--cims-g200);
border-radius: 6px;
fa-icon {
color: var(--cims-cyan);
margin-right: 2px;
}
strong {
color: var(--cims-g900);
margin-left: 3px;
}
}
/* TABS */
.cims-tabs {
display: flex;
background: #fff;
border-bottom: 1px solid var(--cims-g200);
padding: 0 22px;
overflow-x: auto;
flex-shrink: 0;
}
.cims-tab {
display: flex;
align-items: center;
padding: 12px 18px;
border: none;
background: transparent;
cursor: pointer;
font-family: inherit;
font-size: 13px;
font-weight: 600;
color: var(--cims-g500);
border-bottom: 2px solid transparent;
transition: all 0.15s;
white-space: nowrap;
&:hover {
color: var(--cims-g700);
background: var(--cims-g50);
}
&.active {
background: #fff;
color: var(--cims-cyan);
border-bottom-color: var(--cims-cyan);
}
}
/* BODY */
.cims-body {
padding: 18px 22px;
display: flex;
flex-direction: column;
gap: 16px;
overflow: auto;
flex: 1;
background: var(--cims-g50);
}
.cims-section {
background: #fff;
border: 1px solid var(--cims-g200);
border-radius: 12px;
padding: 16px 18px;
box-shadow: 0 1px 3px rgb(0 0 0 / 4%);
}
.cims-sec-h {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 14px;
flex-wrap: wrap;
}
.cims-sec-t {
margin: 0;
font-size: 14px;
font-weight: 700;
color: var(--cims-g700);
}
.cims-sec-actions {
display: flex;
gap: 8px;
}
.cims-btn-xls {
font-size: 12px !important;
height: 34px !important;
color: #fff !important;
background: var(--cims-cyan) !important;
}
.cims-btn-excel {
background: #16a34a !important;
}
.cims-btn-ghost {
font-size: 12px !important;
height: 34px !important;
color: var(--cims-g700) !important;
background: #fff !important;
border: 1px solid var(--cims-g200) !important;
}
/* INFO GRID */
.cims-info-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px 24px;
}
.cims-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.cims-info-l {
font-size: 10px;
font-weight: 600;
color: var(--cims-g500);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.cims-info-v {
font-size: 13px;
font-weight: 600;
color: var(--cims-g900);
}
.cims-desc {
margin-top: 12px;
padding: 10px 14px;
background: var(--cims-g50);
border-left: 3px solid var(--cims-cyan);
border-radius: 4px;
font-size: 12px;
color: var(--cims-g700);
line-height: 1.5;
}
/* TABLE */
.cims-tbl-wrap {
border: 1px solid var(--cims-g200);
border-radius: 8px;
overflow: auto;
}
.cims-tbl {
width: 100%;
border-collapse: collapse;
th {
text-align: left;
padding: 10px 14px;
font-size: 11px;
font-weight: 600;
color: var(--cims-g500);
text-transform: uppercase;
letter-spacing: 0.05em;
background: var(--cims-g50);
border-bottom: 1px solid var(--cims-g200);
white-space: nowrap;
}
td {
padding: 10px 14px;
border-bottom: 1px solid var(--cims-g100);
font-size: 13px;
color: var(--cims-g700);
vertical-align: middle;
}
tr:last-child td {
border-bottom: none;
}
tr:hover td {
background: var(--cims-g50);
}
}
.cell-ref {
color: var(--cims-cyan);
font-weight: 600;
}
.unit-pill {
display: inline-block;
padding: 2px 8px;
border-radius: 6px;
background: var(--cims-teal-l);
color: var(--cims-teal);
font-family: 'DM Mono', monospace;
font-size: 10px;
font-weight: 600;
}
.ta-c {
text-align: center;
}
.ta-r {
text-align: right;
}
.mono {
font-family: 'DM Mono', monospace;
font-size: 12px;
}
.muted {
color: var(--cims-g500);
font-weight: 400;
}
/* VALIDATION BANNER */
.cims-validation {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 12px;
fa-icon {
font-size: 20px;
}
&.valid {
background: var(--cims-good-l);
color: var(--cims-good);
}
&.warning {
background: var(--cims-mid-l);
color: var(--cims-mid);
}
}
.cims-val-t {
font-size: 13px;
font-weight: 700;
}
.cims-val-d {
font-size: 11px;
margin-top: 2px;
opacity: 0.85;
}
/* XBRL PREVIEW */
.xbrl-preview {
background: #0f172a;
color: #e2e8f0;
padding: 16px;
border-radius: 8px;
font-family: 'DM Mono', 'Cascadia Code', monospace;
font-size: 11.5px;
line-height: 1.6;
overflow: auto;
max-height: 480px;
margin: 0;
white-space: pre;
border: 1px solid var(--cims-g200);
code {
font-family: inherit;
background: transparent;
color: inherit;
padding: 0;
}
}
/* FOOTER */
.cims-foot {
padding: 12px 22px;
border-top: 1px solid var(--cims-g200);
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-shrink: 0;
flex-wrap: wrap;
}
.cims-foot-l {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--cims-g500);
fa-icon {
color: var(--cims-cyan);
}
}
.cims-foot-actions {
display: flex;
gap: 8px;
}
/* RESPONSIVE */
@media (width <= 1100px) {
.cims-info-grid {
grid-template-columns: 1fr 1fr;
}
}
@media (width <= 720px) {
.cims-info-grid {
grid-template-columns: 1fr;
}
.cims-head,
.cims-form-bar,
.cims-foot {
flex-wrap: wrap;
}
}
@@ -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);
}
}