460 lines
18 KiB
HTML
460 lines
18 KiB
HTML
<!--
|
|
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="oss-root">
|
|
<!-- HEADER -->
|
|
<div class="oss-head">
|
|
<div class="oss-head-left">
|
|
<fa-icon icon="university" class="oss-head-ic"></fa-icon>
|
|
<div>
|
|
<h2 class="oss-head-t">OSS 3 — {{ returnName }}</h2>
|
|
<div class="oss-head-s">
|
|
{{ bankName }} · Bank Code <strong>{{ bankCode }}</strong> · Return Code
|
|
<strong>{{ returnCode }}</strong> · Quarter ending <strong>{{ reportingEndDate }}</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="oss-head-right">
|
|
<span class="oss-audit-pill">{{ reportStatus }}</span>
|
|
<button class="oss-btn oss-btn-xls" [disabled]="generating" (click)="generateExcel()">
|
|
<fa-icon icon="file-csv"></fa-icon>
|
|
{{ generating ? 'Generating…' : 'Generate Excel' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- HEADLINE ASSET QUALITY -->
|
|
<div class="oss-balance" [class.oss-balance--bad]="!totalsAgree">
|
|
<fa-icon [icon]="totalsAgree ? 'check-circle' : 'exclamation-triangle'"></fa-icon>
|
|
<span>
|
|
Gross NPA <strong>₹{{ grossNpa | number: '1.2-2' }}</strong> ({{ grossNpaRatio | number: '1.2-2' }}%)
|
|
· Net NPA <strong>₹{{ netNpa | number: '1.2-2' }}</strong> ({{ netNpaRatio | number: '1.2-2' }}%)
|
|
·
|
|
{{
|
|
totalsAgree
|
|
? 'Portfolio Analysis and Classification totals agree'
|
|
: 'MISMATCH between Portfolio Analysis and Classification totals — resolve before filing'
|
|
}}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- TABS -->
|
|
<div class="oss-tabs">
|
|
@for (t of tabs; track t.id) {
|
|
<button class="oss-tab" [class.active]="activeTab === t.id" (click)="showTab(t.id)">
|
|
<fa-icon [icon]="t.icon"></fa-icon> {{ t.label }}
|
|
</button>
|
|
}
|
|
</div>
|
|
|
|
<!-- GL mapping toolbar (applies to the first figure column of each statement) -->
|
|
@if (activeTab !== 'filing' && activeTab !== 'signatory') {
|
|
<div class="oss-gl-bar" [class.on]="glMapMode">
|
|
<div class="oss-gl-bar-l">
|
|
<fa-icon icon="project-diagram"></fa-icon>
|
|
<span>
|
|
<strong>General Ledger mapping</strong>
|
|
@if (glMappedCount) {
|
|
<span class="oss-gl-count">{{ glMappedCount }} line(s) mapped</span>
|
|
} @else {
|
|
<span class="muted"> — pin GL accounts to a line and its first figure comes from the ledger</span>
|
|
}
|
|
</span>
|
|
</div>
|
|
<div class="oss-gl-bar-r">
|
|
@if (glMapMode) {
|
|
@if (canSelectGlBranch) {
|
|
<select class="oss-gl-inp" [(ngModel)]="glBranchCode">
|
|
<option value="">All branches</option>
|
|
@for (b of glBranches; track b.code) {
|
|
<option [value]="b.code">{{ b.name }}</option>
|
|
}
|
|
</select>
|
|
} @else {
|
|
<span class="oss-gl-scope" title="Your login is restricted to this branch">
|
|
<fa-icon icon="university"></fa-icon> {{ glScopeName }}
|
|
</span>
|
|
}
|
|
@if (glMappedCount) {
|
|
<button class="oss-btn oss-btn-clear" (click)="clearAllGlMappings()">Clear all</button>
|
|
}
|
|
}
|
|
<button class="oss-btn" [class.oss-btn-xls]="!glMapMode" (click)="toggleGlMapMode()">
|
|
<fa-icon [icon]="glMapMode ? 'times' : 'plus'"></fa-icon>
|
|
{{ glMapMode ? 'Done' : 'Add / Remove GLs' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- FILING INFORMATION -->
|
|
@if (activeTab === 'filing') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">Filing Information</div>
|
|
<div class="oss-tbl-wrap">
|
|
<table class="oss-tbl oss-tbl--kv">
|
|
<tbody>
|
|
@for (item of filingInfo; track item.label) {
|
|
<tr>
|
|
<th>{{ item.label }}</th>
|
|
<td [class.filled]="item.value" [class.audit]="item.label === 'Report status'">{{ item.value }}</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- PORTFOLIO ANALYSIS -->
|
|
@if (activeTab === 'portfolio') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">
|
|
PortfolioAnalysis(loans_advncs) — Portfolio Analysis (By delinquency in Interest Payment)
|
|
</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: portfolioAnalysis }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: portfolioMemo }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- CLASSIFICATION OF RISK ASSETS -->
|
|
@if (activeTab === 'classification') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">Classifcation_Loans_Adv — Classification of Risk Assets</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: classificationPartA }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: nettingItems1 }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: nettingItems2 }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: classificationMemo }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- MOVEMENT OF LOANS -->
|
|
@if (activeTab === 'movement') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">Movement_of_loans — Change in Asset Quality Profile and Recoveries of NPAs</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: movementPart1 }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: movementPart2 }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: movementPart3 }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- OTHER RISK ASSETS -->
|
|
@if (activeTab === 'other-risk') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">StatementOtherRiskAssets — Other Risk Assets and Exposures</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: otherRiskOnBs }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: otherRiskOffBs }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- TOP IMPAIRED CREDITS -->
|
|
@if (activeTab === 'impaired') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">TopImpairedCredits — Top Impaired Credits</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: capitalMemo }"></ng-container>
|
|
|
|
<div class="oss-tbl-wrap">
|
|
<div class="oss-tbl-h">
|
|
Table 5: Top Fifty Impaired Credit or more than Rs.100 lakhs — Rs in Thousands
|
|
</div>
|
|
<table class="oss-tbl">
|
|
<thead>
|
|
<tr>
|
|
<th>PAN of Borrower</th>
|
|
<th>Name of Borrower</th>
|
|
<th class="ta-r">Loans & Advances</th>
|
|
<th class="ta-r">Funded Exposure</th>
|
|
<th class="ta-r">Interest in arrear</th>
|
|
<th class="ta-r">% to Total Advances</th>
|
|
<th class="ta-r">% to Capital Funds</th>
|
|
<th>Asset Classification</th>
|
|
<th class="ta-r">Loss Provision held</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (r of impairedCredits; track $index) {
|
|
<tr>
|
|
<td class="mono">{{ r.pan }}</td>
|
|
<td>{{ r.name }}</td>
|
|
<td class="ta-r mono">{{ r.loansAdvances | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono">{{ r.funded | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono">{{ r.interestInArrear | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono">{{ r.pctTotalAdvances | number: '1.2-2' }}%</td>
|
|
<td class="ta-r mono">{{ r.pctCapitalFunds | number: '1.2-2' }}%</td>
|
|
<td>
|
|
<span class="report-pill" [class.report-bad]="r.assetClassification !== 'Standard'">
|
|
{{ r.assetClassification }}
|
|
</span>
|
|
</td>
|
|
<td class="ta-r mono">{{ r.lossProvisionHeld | number: '1.2-2' }}</td>
|
|
</tr>
|
|
}
|
|
<tr class="total-row">
|
|
<td colspan="3">Total ({{ impairedCredits.length }} borrowers)</td>
|
|
<td class="ta-r mono derived">{{ impairedFundedTotal | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono derived">{{ impairedInterestTotal | number: '1.2-2' }}</td>
|
|
<td colspan="3"></td>
|
|
<td class="ta-r mono derived">{{ impairedProvisionTotal | number: '1.2-2' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
@for (t of recoveryTables; track t.title) {
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: t }"></ng-container>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<!-- EXPOSURE LIMITS -->
|
|
@if (activeTab === 'exposure') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">ExposureLimits — Single and Group Borrower Exposure Limit</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: capitalPreviousMarch }"></ng-container>
|
|
|
|
<div class="oss-tbl-wrap">
|
|
<div class="oss-tbl-h">
|
|
Table 2: Statement on Large Exposure — Single Borrower (in excess of 15% of Tier 1 capital as on
|
|
previous March, or a minimum of 20 large credits)
|
|
</div>
|
|
<table class="oss-tbl">
|
|
<thead>
|
|
<tr>
|
|
<th>PAN of Borrower</th>
|
|
<th>Name of Borrower</th>
|
|
<th class="ta-r">Limit sanctioned</th>
|
|
<th class="ta-r">% to Total Advances</th>
|
|
<th class="ta-r">Outstanding Balance</th>
|
|
<th class="ta-r">% to Tier 1 capital</th>
|
|
<th>Asset Classification</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (r of largeExposures; track $index) {
|
|
<tr>
|
|
<td class="mono">{{ r.pan }}</td>
|
|
<td>{{ r.name }}</td>
|
|
<td class="ta-r mono">{{ r.limitSanctioned | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono">{{ r.pctTotalAdvances | number: '1.2-2' }}%</td>
|
|
<td class="ta-r mono">{{ r.outstandingBalance | number: '1.2-2' }}</td>
|
|
<td class="ta-r mono">{{ r.pctTier1Capital | number: '1.2-2' }}%</td>
|
|
<td>
|
|
<span class="report-pill report-ok">{{ r.assetClassification }}</span>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="oss-notes">
|
|
@for (note of exposureNotes; track note) {
|
|
<div class="oss-note-line">{{ note }}</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- SECURITY PORTFOLIO -->
|
|
@if (activeTab === 'securities') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">QualitySecurityPortoflio — Quality of Security Portfolio</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: investmentQuality }"></ng-container>
|
|
<div class="oss-sheet-banner">OtherNonSLRSecurity — Other Details of Non-SLR Securities</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: nonSlrSecurities }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- SECTORAL CREDIT -->
|
|
@if (activeTab === 'sectoral') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">SectoralCredit — Sectoral Credit</div>
|
|
|
|
<div class="oss-balance" [class.oss-balance--bad]="!sectoralAgreesWithClosing">
|
|
<fa-icon [icon]="sectoralAgreesWithClosing ? 'check-circle' : 'exclamation-triangle'"></fa-icon>
|
|
<span>
|
|
@if (sectoralAgreesWithClosing) {
|
|
Sectoral totals agree with the Classification statement.
|
|
} @else {
|
|
Sectoral gross advances <strong>₹{{ sectoralGross | number: '1.2-2' }}</strong> and NPAs
|
|
<strong>₹{{ sectoralNpa | number: '1.2-2' }}</strong> do not match the closing position reported by
|
|
Classification (<strong>₹{{ classificationTotal | number: '1.2-2' }}</strong> /
|
|
<strong>₹{{ grossNpa | number: '1.2-2' }}</strong
|
|
>).
|
|
@if (sectoralMatchesOpening) {
|
|
They match the <em>opening</em> balances in Movement Parts 1 and 2 instead — this statement appears to
|
|
have been prepared on the previous quarter's book.
|
|
}
|
|
}
|
|
</span>
|
|
</div>
|
|
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: sectoralCredit }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: sectoralMemo }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: segmentAnalysis }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- INDUSTRY BREAKUP -->
|
|
@if (activeTab === 'industry') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">IndustryBreakup — Industry Breakup</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: industryBreakup }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- SENSITIVE SECTOR -->
|
|
@if (activeTab === 'sensitive') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">SensitiveSector — Sensitive Sector</div>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: capitalMarket }"></ng-container>
|
|
<ng-container [ngTemplateOutlet]="matrix" [ngTemplateOutletContext]="{ t: realEstate }"></ng-container>
|
|
</div>
|
|
}
|
|
|
|
<!-- SIGNATORY -->
|
|
@if (activeTab === 'signatory') {
|
|
<div class="oss-section">
|
|
<div class="oss-sheet-banner">Signatory — Authorised Signatory</div>
|
|
<div class="oss-sign-grid">
|
|
@for (p of signatories; track p.heading) {
|
|
<div class="oss-tbl-wrap">
|
|
<div class="oss-tbl-h">{{ p.heading }}</div>
|
|
<table class="oss-tbl oss-tbl--kv">
|
|
<tbody>
|
|
<tr>
|
|
<td>{{ p.signature }}</td>
|
|
<th>Signature</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ p.name }}</td>
|
|
<th>Name</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ p.designation }}</td>
|
|
<th>Designation</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ p.email }}</td>
|
|
<th>e-mail ID</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="mono">{{ p.telephoneOffice }}</td>
|
|
<th>Tele. No. (O)</th>
|
|
</tr>
|
|
<tr>
|
|
<td class="mono">{{ p.telephoneResidence }}</td>
|
|
<th>Tele. No. (R)</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ p.place }}</td>
|
|
<th>Place</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{{ p.date }}</td>
|
|
<th>Date</th>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- Shared renderer for every multi-column statement in this return. -->
|
|
<ng-template #matrix let-t="t">
|
|
<div class="oss-tbl-wrap">
|
|
<div class="oss-tbl-h">
|
|
{{ t.title }}
|
|
@if (t.unit) {
|
|
<span class="oss-tbl-unit">{{ t.unit }}</span>
|
|
}
|
|
</div>
|
|
@if (t.subtitle) {
|
|
<div class="oss-tbl-sub">{{ t.subtitle }}</div>
|
|
}
|
|
<table class="oss-tbl">
|
|
<thead>
|
|
<tr>
|
|
<th class="oss-col-label">Particulars</th>
|
|
@if (glMapMode) {
|
|
<th class="oss-gl-col">Mapped GL accounts</th>
|
|
}
|
|
@for (c of t.columns; track $index) {
|
|
<th class="ta-r">{{ c }}</th>
|
|
}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (row of t.rows; track $index) {
|
|
<tr [class.total-row]="row.total" [class.section-row]="row.header">
|
|
<td [class]="'lvl-' + row.level">{{ row.label }}</td>
|
|
@if (glMapMode) {
|
|
<td>
|
|
@if (!row.header) {
|
|
<div class="oss-gl-cell">
|
|
@for (code of glCodesFor(t.title + ' / ' + row.label); track code) {
|
|
<span class="oss-gl-chip">
|
|
<span class="mono">{{ code }}</span>
|
|
<button
|
|
class="oss-gl-chip-x"
|
|
(click)="removeGlFromLine(t.title + ' / ' + row.label, code)"
|
|
[attr.aria-label]="'Remove ' + code"
|
|
>
|
|
<fa-icon icon="times"></fa-icon>
|
|
</button>
|
|
</span>
|
|
}
|
|
<button
|
|
class="oss-gl-add"
|
|
(click)="openGlPicker(t.title + ' / ' + row.label, row.values[0] ?? null)"
|
|
>
|
|
<fa-icon icon="plus"></fa-icon>
|
|
{{ glCodesFor(t.title + ' / ' + row.label).length ? 'Edit' : 'Add GL' }}
|
|
</button>
|
|
</div>
|
|
}
|
|
</td>
|
|
}
|
|
@if (row.header) {
|
|
<td [attr.colspan]="t.columns.length"></td>
|
|
} @else {
|
|
@for (v of row.values; track $index) {
|
|
<td
|
|
class="ta-r mono"
|
|
[class.derived]="row.total"
|
|
[class.gl-sourced]="$index === 0 && isGlMapped(t.title + ' / ' + row.label)"
|
|
[class.gl-diff]="$index === 0 && glDiffers(t.title + ' / ' + row.label, v)"
|
|
>
|
|
@if ($index === 0 && isGlMapped(t.title + ' / ' + row.label)) {
|
|
{{ effectiveValue(t.title + ' / ' + row.label, v) | number: '1.2-2' }}{{ row.percent ? '%' : '' }}
|
|
} @else {
|
|
{{ v === null ? '' : (v | number: '1.2-2') }}{{ v !== null && row.percent ? '%' : '' }}
|
|
}
|
|
</td>
|
|
}
|
|
}
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@if (t.notes?.length) {
|
|
<div class="oss-notes">
|
|
@for (note of t.notes; track note) {
|
|
<div class="oss-note-line">{{ note }}</div>
|
|
}
|
|
</div>
|
|
}
|
|
</ng-template>
|
|
|
|
<!-- TOAST -->
|
|
<div class="oss-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
|
|
</div>
|