feat(clients, alm): add CIN/MCA verification & authorized signatories tab; refactor ALM report print and export
This commit is contained in:
@@ -7,556 +7,121 @@
|
||||
-->
|
||||
|
||||
<div class="alm-dlg">
|
||||
<!-- HEADER -->
|
||||
<div class="alm-head">
|
||||
<!-- Window chrome. Screen only — the print takes the statement's own heading, not this bar. -->
|
||||
<div class="alm-head no-print">
|
||||
<div class="alm-head-left">
|
||||
<div class="alm-head-ic"><fa-icon icon="balance-scale"></fa-icon></div>
|
||||
<div>
|
||||
<h2 class="alm-title">Asset Liability Management — Structural Liquidity</h2>
|
||||
<h2 class="alm-title">Average Liabilities and Assets</h2>
|
||||
<p class="alm-sub">
|
||||
{{ bank.name }} · <strong>{{ bank.formCode }}</strong> · {{ bank.quarter }} · As on
|
||||
<strong>{{ bank.asOnDate }}</strong>
|
||||
{{ bank.name }} · {{ branchName }} · FY {{ bank.financialYear }} · Month-wise during the
|
||||
year
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alm-head-actions">
|
||||
<span class="alm-overall" [ngClass]="gap14dWithinTolerance && gap28dWithinTolerance ? 'on-target' : 'below'">
|
||||
<span class="alm-overall-l">14d Tolerance</span>
|
||||
<span class="alm-overall-v">{{ gap14dPct.toFixed(2) }}%</span>
|
||||
<span class="alm-overall-t">/ -{{ bank.tolerance14d }}%</span>
|
||||
</span>
|
||||
<button class="alm-close" (click)="close()" matTooltip="Close" aria-label="Close">✕</button>
|
||||
<button class="alm-close" (click)="close()" matTooltip="Close" aria-label="Close">✕</button>
|
||||
</div>
|
||||
|
||||
<!-- Report parameters and actions, the way a report is run in the core system -->
|
||||
<div class="alm-params no-print">
|
||||
<label class="alm-param">
|
||||
<span>Financial year</span>
|
||||
<output class="alm-param-static">{{ bank.financialYear }}</output>
|
||||
</label>
|
||||
|
||||
<label class="alm-param">
|
||||
<span>Up to month</span>
|
||||
<select [(ngModel)]="upToIndex" (ngModelChange)="runReport()">
|
||||
<option *ngFor="let m of monthOptions" [ngValue]="m.index">{{ m.label }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="alm-param">
|
||||
<span>Months included</span>
|
||||
<output class="alm-param-static">{{ statementRows.length }}</output>
|
||||
</label>
|
||||
|
||||
<span class="alm-params-sp"></span>
|
||||
|
||||
<button type="button" class="alm-btn" (click)="print()"><fa-icon icon="print"></fa-icon> Print</button>
|
||||
<button type="button" class="alm-btn" (click)="exportXls()">
|
||||
<fa-icon icon="file-excel"></fa-icon> Export
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="alm-note no-print">
|
||||
<fa-icon icon="info-circle"></fa-icon> <strong>{{ unmappedColumns }}</strong> are not mapped in the
|
||||
core-system extract and print as 0.00. Total and Average cover the {{ statementRows.length }} month(s) shown.
|
||||
</p>
|
||||
|
||||
<!-- ═══ THE STATEMENT — what goes on paper ═══ -->
|
||||
<div class="alm-body">
|
||||
<div class="stmt" #statement>
|
||||
<div class="stmt-bank">{{ statementHeading }}</div>
|
||||
<div class="stmt-title">Average Liabilities and Assets Month-wise during the year</div>
|
||||
|
||||
<div class="stmt-tbl-wrap">
|
||||
<table class="stmt-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" class="col-month">Month</th>
|
||||
<th rowspan="2">Capital</th>
|
||||
<th rowspan="2">Reserve/Prov<br />Exclude OIR</th>
|
||||
<th rowspan="2">Deposits</th>
|
||||
<th rowspan="2">Borrowings</th>
|
||||
<th rowspan="2">Other Liabilities</th>
|
||||
<th rowspan="2">Cash on Hand</th>
|
||||
<th colspan="2" class="col-grp">Bank Balances</th>
|
||||
<th rowspan="2">Investments<br />including</th>
|
||||
<th rowspan="2">Advances</th>
|
||||
<th rowspan="2">Other Assets<br />Exclude IRA</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Current</th>
|
||||
<th>FDs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let r of statementRows">
|
||||
<td class="col-month">
|
||||
<strong>{{ r.label }}</strong
|
||||
><br /><span class="stmt-yr">{{ r.sub }}</span>
|
||||
</td>
|
||||
<td>{{ r.capital | number: '1.2-2' }}</td>
|
||||
<td>{{ r.reserveProv | number: '1.2-2' }}</td>
|
||||
<td>{{ r.deposits | number: '1.2-2' }}</td>
|
||||
<td>{{ r.borrowings | number: '1.2-2' }}</td>
|
||||
<td>{{ r.otherLiabilities | number: '1.2-2' }}</td>
|
||||
<td>{{ r.cashOnHand | number: '1.2-2' }}</td>
|
||||
<td>{{ r.bankCurrent | number: '1.2-2' }}</td>
|
||||
<td>{{ r.bankFds | number: '1.2-2' }}</td>
|
||||
<td>{{ r.investments | number: '1.2-2' }}</td>
|
||||
<td>{{ r.advances | number: '1.2-2' }}</td>
|
||||
<td>{{ r.otherAssets | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="stmt-sum" *ngFor="let r of [totalRow, averageRow]">
|
||||
<td class="col-month">{{ r.label }}</td>
|
||||
<td>{{ r.capital | number: '1.2-2' }}</td>
|
||||
<td>{{ r.reserveProv | number: '1.2-2' }}</td>
|
||||
<td>{{ r.deposits | number: '1.2-2' }}</td>
|
||||
<td>{{ r.borrowings | number: '1.2-2' }}</td>
|
||||
<td>{{ r.otherLiabilities | number: '1.2-2' }}</td>
|
||||
<td>{{ r.cashOnHand | number: '1.2-2' }}</td>
|
||||
<td>{{ r.bankCurrent | number: '1.2-2' }}</td>
|
||||
<td>{{ r.bankFds | number: '1.2-2' }}</td>
|
||||
<td>{{ r.investments | number: '1.2-2' }}</td>
|
||||
<td>{{ r.advances | number: '1.2-2' }}</td>
|
||||
<td>{{ r.otherAssets | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="stmt-foot">
|
||||
<div>Print Date : {{ generatedAt }}</div>
|
||||
<div>Report Ref No : {{ bank.reportRefNo }} User Code:{{ statementUserCode }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TABS -->
|
||||
<div class="alm-tabs">
|
||||
<button class="alm-tab" [class.active]="activeTab === 'dashboard'" (click)="setTab('dashboard')">
|
||||
<fa-icon icon="chart-pie"></fa-icon> Dashboard
|
||||
</button>
|
||||
<button class="alm-tab" [class.active]="activeTab === 'ladder'" (click)="setTab('ladder')">
|
||||
<fa-icon icon="layer-group"></fa-icon> Maturity Ladder
|
||||
</button>
|
||||
<button class="alm-tab" [class.active]="activeTab === 'monthly'" (click)="setTab('monthly')">
|
||||
<fa-icon icon="calendar-alt"></fa-icon> Monthly Snapshot
|
||||
</button>
|
||||
<button class="alm-tab" [class.active]="activeTab === 'avgbal'" (click)="setTab('avgbal')">
|
||||
<fa-icon icon="table"></fa-icon> Avg. Liabilities & Assets
|
||||
</button>
|
||||
<button class="alm-tab" [class.active]="activeTab === 'returns'" (click)="setTab('returns')">
|
||||
<fa-icon icon="file-invoice"></fa-icon> Form ALM-1
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="alm-body">
|
||||
<!-- ════════════ DASHBOARD ════════════ -->
|
||||
<ng-container *ngIf="activeTab === 'dashboard'">
|
||||
<div class="alm-stats">
|
||||
<div class="alm-stat-card alm-stat-gauge">
|
||||
<div
|
||||
class="gauge"
|
||||
[style.--pct.%]="totalGapPct > 0 ? (totalGapPct > 100 ? 100 : totalGapPct) : 0"
|
||||
[ngClass]="totalGap >= 0 ? 'on-target' : 'below'"
|
||||
>
|
||||
<div class="gauge-inner">
|
||||
<div class="gauge-pct">{{ totalGapPct.toFixed(1) }}%</div>
|
||||
<div class="gauge-label">Net Gap / Outflow</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alm-stat-card">
|
||||
<div class="alm-stat-l">NDTL</div>
|
||||
<div class="alm-stat-v">₹ {{ bank.ndtl / 100 | number: '1.2-2' }} Cr</div>
|
||||
<div class="alm-stat-meta">Net Demand & Time Liabilities</div>
|
||||
</div>
|
||||
<div class="alm-stat-card">
|
||||
<div class="alm-stat-l">Total Outflows</div>
|
||||
<div class="alm-stat-v">₹ {{ totalOutflow / 100 | number: '1.2-2' }} Cr</div>
|
||||
<div class="alm-stat-meta">All maturity buckets</div>
|
||||
</div>
|
||||
<div class="alm-stat-card">
|
||||
<div class="alm-stat-l">Total Inflows</div>
|
||||
<div class="alm-stat-v">₹ {{ totalInflow / 100 | number: '1.2-2' }} Cr</div>
|
||||
<div class="alm-stat-meta" [ngClass]="totalGap >= 0 ? 'pos' : 'neg'">
|
||||
Net: {{ totalGap >= 0 ? '+' : '' }}{{ totalGap / 100 | number: '1.2-2' }} Cr
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TOLERANCE BARS -->
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">RBI Tolerance Limits — Cumulative Gap</h3>
|
||||
<span class="alm-circular">RBI MD: {{ bank.rbiCircular }}</span>
|
||||
</div>
|
||||
<div class="alm-tolerance">
|
||||
<div class="alm-tol-row" [ngClass]="gap14dWithinTolerance ? 'good' : 'bad'">
|
||||
<div class="alm-tol-h">
|
||||
<span class="alm-tol-l"
|
||||
><fa-icon icon="hourglass-half"></fa-icon> Cumulative Gap up to 14 days</span
|
||||
>
|
||||
<span class="alm-tol-p">
|
||||
<strong>{{ gap14dPct.toFixed(2) }}%</strong>
|
||||
<span class="alm-tol-lim">limit -{{ bank.tolerance14d }}%</span>
|
||||
<span class="alm-tol-st">{{ gap14dWithinTolerance ? 'WITHIN' : 'BREACH' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="alm-tol-track">
|
||||
<div
|
||||
class="alm-tol-fill"
|
||||
[style.width.%]="gap14dPct >= 0 ? 100 : 100 + (gap14dPct / bank.tolerance14d) * 100"
|
||||
></div>
|
||||
<div class="alm-tol-marker"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alm-tol-row" [ngClass]="gap28dWithinTolerance ? 'good' : 'bad'">
|
||||
<div class="alm-tol-h">
|
||||
<span class="alm-tol-l"><fa-icon icon="clock"></fa-icon> Cumulative Gap up to 30 days</span>
|
||||
<span class="alm-tol-p">
|
||||
<strong>{{ gap28dPct.toFixed(2) }}%</strong>
|
||||
<span class="alm-tol-lim">limit -{{ bank.tolerance28d }}%</span>
|
||||
<span class="alm-tol-st">{{ gap28dWithinTolerance ? 'WITHIN' : 'BREACH' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="alm-tol-track">
|
||||
<div
|
||||
class="alm-tol-fill"
|
||||
[style.width.%]="gap28dPct >= 0 ? 100 : 100 + (gap28dPct / bank.tolerance28d) * 100"
|
||||
></div>
|
||||
<div class="alm-tol-marker"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- BUCKET MINI HEATMAP -->
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">Bucket-wise Mismatch Heatmap</h3>
|
||||
</div>
|
||||
<div class="alm-heatmap">
|
||||
<div
|
||||
*ngFor="let b of buckets"
|
||||
class="heat-cell"
|
||||
[ngClass]="bucketStatus(b)"
|
||||
[matTooltip]="b.label + ' — Gap: ' + bucketGapPct(b).toFixed(2) + '%'"
|
||||
>
|
||||
<div class="heat-code">{{ b.code }}</div>
|
||||
<div class="heat-pct">{{ bucketGapPct(b).toFixed(1) }}%</div>
|
||||
<div class="heat-lbl">{{ b.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">Reporting Snapshot</h3>
|
||||
</div>
|
||||
<div class="alm-info-grid">
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">Reporting Bank</div>
|
||||
<div class="alm-info-v">{{ bank.name }}</div>
|
||||
</div>
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">Branch</div>
|
||||
<div class="alm-info-v">{{ bank.branch }}</div>
|
||||
</div>
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">Principal Officer</div>
|
||||
<div class="alm-info-v">{{ bank.principalOfficer }}</div>
|
||||
</div>
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">Form Code</div>
|
||||
<div class="alm-info-v">{{ bank.formCode }}</div>
|
||||
</div>
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">Report Ref No</div>
|
||||
<div class="alm-info-v mono">{{ bank.reportRefNo }}</div>
|
||||
</div>
|
||||
<div class="alm-info">
|
||||
<div class="alm-info-l">User Code</div>
|
||||
<div class="alm-info-v mono">{{ bank.userCode }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- ════════════ MATURITY LADDER ════════════ -->
|
||||
<ng-container *ngIf="activeTab === 'ladder'">
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">Statement of Structural Liquidity — Maturity Ladder</h3>
|
||||
<div class="alm-sec-actions">
|
||||
<select class="alm-sel" [(ngModel)]="filterBucket">
|
||||
<option value="">All Buckets</option>
|
||||
<option value="short">Short Term (≤ 30 days)</option>
|
||||
<option value="medium">Medium Term (1–12 months)</option>
|
||||
<option value="long">Long Term (> 1 year)</option>
|
||||
</select>
|
||||
<button mat-stroked-button class="alm-btn-xls" (click)="exportLadderCsv()">
|
||||
<fa-icon icon="file-csv"></fa-icon> Export CSV
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alm-tbl-wrap">
|
||||
<table class="alm-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bucket</th>
|
||||
<th>Time Period</th>
|
||||
<th class="ta-r">Outflow (Liab)</th>
|
||||
<th class="ta-r">Inflow (Assets)</th>
|
||||
<th class="ta-r">Gap</th>
|
||||
<th class="ta-r">Gap %</th>
|
||||
<th class="ta-c">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let b of filteredBuckets">
|
||||
<td class="mono">{{ b.code }}</td>
|
||||
<td>{{ b.label }}</td>
|
||||
<td class="ta-r">{{ b.outflow | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ b.inflow | number: '1.2-2' }}</td>
|
||||
<td class="ta-r" [ngClass]="bucketGap(b) >= 0 ? 'pos' : 'neg'">
|
||||
<strong>{{ bucketGap(b) >= 0 ? '+' : '' }}{{ bucketGap(b) | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="bucketGap(b) >= 0 ? 'pos' : 'neg'">
|
||||
{{ bucketGapPct(b) >= 0 ? '+' : '' }}{{ bucketGapPct(b).toFixed(2) }}%
|
||||
</td>
|
||||
<td class="ta-c">
|
||||
<span class="status-pill" [ngClass]="bucketStatus(b)">
|
||||
<ng-container *ngIf="bucketStatus(b) === 'surplus'">SURPLUS</ng-container>
|
||||
<ng-container *ngIf="bucketStatus(b) === 'deficit'">DEFICIT</ng-container>
|
||||
<ng-container *ngIf="bucketStatus(b) === 'balanced'">BALANCED</ng-container>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="row-total">
|
||||
<td colspan="2"><strong>TOTAL</strong></td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ totalOutflow | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ totalInflow | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="totalGap >= 0 ? 'pos' : 'neg'">
|
||||
<strong>{{ totalGap >= 0 ? '+' : '' }}{{ totalGap | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="totalGap >= 0 ? 'pos' : 'neg'">
|
||||
<strong>{{ totalGapPct >= 0 ? '+' : '' }}{{ totalGapPct.toFixed(2) }}%</strong>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Cumulative table -->
|
||||
<h4 class="form-iii-t">Cumulative Position</h4>
|
||||
<div class="alm-tbl-wrap">
|
||||
<table class="alm-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Through Bucket</th>
|
||||
<th class="ta-r">Cum. Outflow</th>
|
||||
<th class="ta-r">Cum. Inflow</th>
|
||||
<th class="ta-r">Cum. Gap</th>
|
||||
<th class="ta-r">Cum. Gap %</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let b of buckets; let i = index">
|
||||
<td>{{ b.code }} — {{ b.label }}</td>
|
||||
<td class="ta-r">{{ cumulativeOutflow(i) | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ cumulativeInflow(i) | number: '1.2-2' }}</td>
|
||||
<td class="ta-r" [ngClass]="cumulativeGap(i) >= 0 ? 'pos' : 'neg'">
|
||||
{{ cumulativeGap(i) >= 0 ? '+' : '' }}{{ cumulativeGap(i) | number: '1.2-2' }}
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="cumulativeGap(i) >= 0 ? 'pos' : 'neg'">
|
||||
<strong>{{ cumulativeGapPct(i) >= 0 ? '+' : '' }}{{ cumulativeGapPct(i).toFixed(2) }}%</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- ════════════ MONTHLY ════════════ -->
|
||||
<ng-container *ngIf="activeTab === 'monthly'">
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">Monthly Asset / Liability Snapshot — FY {{ bank.fiscalYear }}</h3>
|
||||
<button mat-stroked-button class="alm-btn-xls" (click)="exportMonthlyXls()">
|
||||
<fa-icon icon="file-excel"></fa-icon> Export Excel
|
||||
</button>
|
||||
</div>
|
||||
<div class="alm-tbl-wrap">
|
||||
<table class="alm-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">Month</th>
|
||||
<th colspan="4" class="ta-c grp-liab">Liabilities (₹ Lakh)</th>
|
||||
<th colspan="4" class="ta-c grp-asset">Assets (₹ Lakh)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="ta-r">Reserve/Prov</th>
|
||||
<th class="ta-r">Borrowings</th>
|
||||
<th class="ta-r">FDs</th>
|
||||
<th class="ta-r">Total Liab</th>
|
||||
<th class="ta-r">Cash</th>
|
||||
<th class="ta-r">Investments</th>
|
||||
<th class="ta-r">Other Assets</th>
|
||||
<th class="ta-r">Total Assets</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let m of monthly">
|
||||
<td class="mono">{{ m.month }}</td>
|
||||
<td class="ta-r">{{ m.reserveProv | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ m.borrowings | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ m.fds | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ monthTotalLiab(m) | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r">{{ m.cashOnHand | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ m.investments | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ m.otherAssets | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ monthTotalAssets(m) | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="row-total">
|
||||
<td><strong>AVERAGE</strong></td>
|
||||
<td class="ta-r">{{ avg('reserveProv') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ avg('borrowings') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ avg('fds') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ avg('reserveProv') + avg('borrowings') + avg('fds') | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r">{{ avg('cashOnHand') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ avg('investments') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ avg('otherAssets') | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ avg('cashOnHand') + avg('investments') + avg('otherAssets') | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- ════════════ AVERAGE LIABILITIES AND ASSETS ════════════ -->
|
||||
<ng-container *ngIf="activeTab === 'avgbal'">
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">Average Liabilities and Assets Month-wise during the year</h3>
|
||||
<button mat-stroked-button class="alm-btn-xls" (click)="exportAvgBalancesXls()">
|
||||
<fa-icon icon="file-excel"></fa-icon> Export Excel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Printed-statement banner: institution and office come off the session, not the report. -->
|
||||
<div class="alm-stmt">
|
||||
<div class="alm-stmt-bank">{{ statementHeading }}</div>
|
||||
<div class="alm-stmt-title">Average Liabilities and Assets Month-wise during the year</div>
|
||||
|
||||
<div class="alm-tbl-wrap">
|
||||
<table class="alm-tbl alm-stmt-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">Month</th>
|
||||
<th rowspan="2" class="ta-r">Capital</th>
|
||||
<th rowspan="2" class="ta-r">Reserve/Prov<br />Exclude OIR</th>
|
||||
<th rowspan="2" class="ta-r">Deposits</th>
|
||||
<th rowspan="2" class="ta-r">Borrowings</th>
|
||||
<th rowspan="2" class="ta-r">Other Liabilities</th>
|
||||
<th rowspan="2" class="ta-r">Cash on Hand</th>
|
||||
<th colspan="2" class="ta-c">Bank Balances</th>
|
||||
<th rowspan="2" class="ta-r">Investments<br />including</th>
|
||||
<th rowspan="2" class="ta-r">Advances</th>
|
||||
<th rowspan="2" class="ta-r">Other Assets<br />Exclude IRA</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="ta-r">Current</th>
|
||||
<th class="ta-r">FDs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let r of avgBalanceRows">
|
||||
<td class="mono">
|
||||
<strong>{{ r.label }}</strong
|
||||
><br /><span class="alm-stmt-sub">{{ r.sub }}</span>
|
||||
</td>
|
||||
<td class="ta-r">{{ r.capital | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.reserveProv | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.deposits | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.borrowings | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.otherLiabilities | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.cashOnHand | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.bankCurrent | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.bankFds | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.investments | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.advances | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.otherAssets | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="row-total" *ngFor="let r of [avgBalanceTotal, avgBalanceAverage]">
|
||||
<td>{{ r.label }}</td>
|
||||
<td class="ta-r">{{ r.capital | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.reserveProv | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.deposits | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.borrowings | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.otherLiabilities | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.cashOnHand | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.bankCurrent | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.bankFds | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.investments | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.advances | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ r.otherAssets | number: '1.2-2' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="alm-stmt-foot">
|
||||
<div>Print Date : {{ statementPrintedAt }}</div>
|
||||
<div>Report Ref No : {{ bank.reportRefNo }} User Code:{{ statementUserCode }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- ════════════ RETURNS / FORM ALM-1 ════════════ -->
|
||||
<ng-container *ngIf="activeTab === 'returns'">
|
||||
<div class="alm-section">
|
||||
<div class="alm-sec-h">
|
||||
<h3 class="alm-sec-t">{{ bank.formCode }} — Statement of Structural Liquidity</h3>
|
||||
<button mat-stroked-button class="alm-btn-xls" (click)="exportFormAlm()">
|
||||
<fa-icon icon="file-excel"></fa-icon> Generate {{ bank.formCode }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-iii">
|
||||
<div class="form-iii-h">
|
||||
<div class="form-iii-bank">
|
||||
<strong>{{ bank.name }}</strong> — {{ bank.branch }}
|
||||
</div>
|
||||
<div class="form-iii-meta">
|
||||
<span><fa-icon icon="calendar"></fa-icon> As On {{ bank.asOnDate }}</span>
|
||||
<span><fa-icon icon="university"></fa-icon> {{ bank.quarter }}</span>
|
||||
<span class="mono"><fa-icon icon="hashtag"></fa-icon> Ref: {{ bank.reportRefNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="form-iii-t">A. NDTL & Reference</h4>
|
||||
<table class="alm-tbl form-tbl">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Net Demand & Time Liabilities (NDTL) (₹ Lakhs)</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ bank.ndtl | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>RBI Master Direction</td>
|
||||
<td class="ta-r mono">{{ bank.rbiCircular }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="form-iii-t">B. Maturity Ladder (₹ in Lakhs)</h4>
|
||||
<table class="alm-tbl form-tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bucket</th>
|
||||
<th>Time Period</th>
|
||||
<th class="ta-r">Outflows</th>
|
||||
<th class="ta-r">Inflows</th>
|
||||
<th class="ta-r">Mismatch</th>
|
||||
<th class="ta-r">Cum. Mismatch</th>
|
||||
<th class="ta-r">Cum. % of Outflow</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let b of buckets; let i = index">
|
||||
<td class="mono">{{ b.code }}</td>
|
||||
<td>{{ b.label }}</td>
|
||||
<td class="ta-r">{{ b.outflow | number: '1.2-2' }}</td>
|
||||
<td class="ta-r">{{ b.inflow | number: '1.2-2' }}</td>
|
||||
<td class="ta-r" [ngClass]="bucketGap(b) >= 0 ? 'pos' : 'neg'">
|
||||
{{ bucketGap(b) >= 0 ? '+' : '' }}{{ bucketGap(b) | number: '1.2-2' }}
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="cumulativeGap(i) >= 0 ? 'pos' : 'neg'">
|
||||
{{ cumulativeGap(i) >= 0 ? '+' : '' }}{{ cumulativeGap(i) | number: '1.2-2' }}
|
||||
</td>
|
||||
<td class="ta-r">
|
||||
<strong [ngClass]="cumulativeGap(i) >= 0 ? 'pos' : 'neg'"
|
||||
>{{ cumulativeGapPct(i) >= 0 ? '+' : '' }}{{ cumulativeGapPct(i).toFixed(2) }}%</strong
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="form-total">
|
||||
<td colspan="2"><strong>TOTAL</strong></td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ totalOutflow | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ totalInflow | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td class="ta-r" [ngClass]="totalGap >= 0 ? 'pos' : 'neg'">
|
||||
<strong>{{ totalGap >= 0 ? '+' : '' }}{{ totalGap | number: '1.2-2' }}</strong>
|
||||
</td>
|
||||
<td colspan="2"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="form-iii-t">C. RBI Tolerance Compliance</h4>
|
||||
<table class="alm-tbl form-tbl">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Cumulative Negative Gap up to 14 days</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ gap14dPct.toFixed(2) }}%</strong>
|
||||
</td>
|
||||
<td class="ta-r">Limit: -{{ bank.tolerance14d }}%</td>
|
||||
<td class="ta-c">
|
||||
<span class="status-pill" [ngClass]="gap14dWithinTolerance ? 'on-target' : 'below'">
|
||||
{{ gap14dWithinTolerance ? 'WITHIN' : 'BREACH' }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cumulative Negative Gap up to 30 days</td>
|
||||
<td class="ta-r">
|
||||
<strong>{{ gap28dPct.toFixed(2) }}%</strong>
|
||||
</td>
|
||||
<td class="ta-r">Limit: -{{ bank.tolerance28d }}%</td>
|
||||
<td class="ta-c">
|
||||
<span class="status-pill" [ngClass]="gap28dWithinTolerance ? 'on-target' : 'below'">
|
||||
{{ gap28dWithinTolerance ? 'WITHIN' : 'BREACH' }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="form-iii-foot">
|
||||
<div class="cert">
|
||||
<div class="cert-l">Certified by</div>
|
||||
<div class="cert-v">{{ bank.principalOfficer }}</div>
|
||||
<div class="cert-r">Principal Officer — {{ bank.name }}</div>
|
||||
</div>
|
||||
<div class="cert">
|
||||
<div class="cert-l">Submitted under</div>
|
||||
<div class="cert-v mono">{{ bank.rbiCircular }}</div>
|
||||
<div class="cert-r">RBI Master Direction on Asset Liability Management</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, ElementRef, ViewChild, inject } from '@angular/core';
|
||||
import { CommonModule, DecimalPipe } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
||||
@@ -16,42 +16,34 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { BranchScopeService } from 'app/core/branch-scope.service';
|
||||
import { AuthenticationService } from 'app/core/authentication/authentication.service';
|
||||
|
||||
interface MaturityBucket {
|
||||
code: string;
|
||||
label: string;
|
||||
/** Outflows / Liabilities (₹ Lakhs) */
|
||||
outflow: number;
|
||||
/** Inflows / Assets (₹ Lakhs) */
|
||||
inflow: number;
|
||||
}
|
||||
|
||||
interface MonthSnapshot {
|
||||
/**
|
||||
* One month of the average liabilities and assets extract, column for column as the core system
|
||||
* prints it.
|
||||
*/
|
||||
interface MonthlyAlmRow {
|
||||
/** e.g. 'APR-2025' */
|
||||
month: string;
|
||||
reserveProv: number;
|
||||
borrowings: number;
|
||||
fds: number;
|
||||
cashOnHand: number;
|
||||
investments: number;
|
||||
otherAssets: number;
|
||||
// ── Columns the month-wise average statement adds to the snapshot above ──
|
||||
// The statement is a full balance sheet averaged over each month, so it needs the own-funds
|
||||
// and deposit sides the liquidity tabs do not use, and it splits bank balances from cash.
|
||||
/** Paid-up share capital. */
|
||||
capital: number;
|
||||
/** Total deposits — the FD component above is part of this figure. */
|
||||
reserveProv: number;
|
||||
/** Total deposits — demand and time. */
|
||||
deposits: number;
|
||||
borrowings: number;
|
||||
otherLiabilities: number;
|
||||
cashOnHand: number;
|
||||
/** Balances with other banks, current accounts. */
|
||||
bankCurrent: number;
|
||||
/** Balances with other banks, held as fixed deposits. */
|
||||
bankFds: number;
|
||||
investments: number;
|
||||
advances: number;
|
||||
otherAssets: number;
|
||||
}
|
||||
|
||||
/** One row of the month-wise average statement — a month, or the Total / Average line. */
|
||||
interface AvgBalanceRow {
|
||||
/** One printed line — a month, or the Total / Average line the statement closes with. */
|
||||
interface StatementRow {
|
||||
label: string;
|
||||
/** Sub-label under the month, e.g. the year, as the printed statement prints it. */
|
||||
/** Sub-label under the month, the year, as the printed statement sets it. */
|
||||
sub: string;
|
||||
capital: number;
|
||||
reserveProv: number;
|
||||
@@ -66,6 +58,18 @@ interface AvgBalanceRow {
|
||||
otherAssets: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Average Liabilities and Assets Month-wise during the year.
|
||||
*
|
||||
* One report, one statement, the way the core system prints it — a parameter line, the statement,
|
||||
* and the print footer. It is deliberately not a dashboard: an ALM screen in a bank is a report
|
||||
* you run and print, not a set of tabs, and figures that go out over the bank's name have to look
|
||||
* the same on screen as they do on paper.
|
||||
*
|
||||
* Month rows are the figures as extracted. Investments, Advances, Bank-FD balances and Other
|
||||
* Assets are not mapped in the extract and print as 0.00 — stated on screen rather than filled in,
|
||||
* because a nil that is really "not captured" must not read as a real balance.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-alm-report-dialog',
|
||||
standalone: true,
|
||||
@@ -86,310 +90,226 @@ export class AlmReportDialogComponent {
|
||||
private branchScope = inject(BranchScopeService);
|
||||
private auth = inject(AuthenticationService);
|
||||
|
||||
activeTab: 'dashboard' | 'ladder' | 'monthly' | 'avgbal' | 'returns' = 'dashboard';
|
||||
/** The rendered statement, handed to the print window as-is. */
|
||||
@ViewChild('statement') private statementRef?: ElementRef<HTMLElement>;
|
||||
|
||||
bank = {
|
||||
readonly bank = {
|
||||
name: 'CBSNexora Bank Ltd',
|
||||
branch: 'Head Office',
|
||||
asOnDate: '31-03-2026',
|
||||
quarter: 'Q4 FY 2025-26',
|
||||
fiscalYear: '2025-26',
|
||||
rbiCircular: 'DCBR.BPD.(PCB).Cir.No.4/12.05.001/2018-19',
|
||||
principalOfficer: 'Karthik Kamath',
|
||||
formCode: 'Form ALM-1',
|
||||
financialYear: '2025-26',
|
||||
reportRefNo: '1036',
|
||||
userCode: 'CBSN01',
|
||||
/** Net Demand & Time Liabilities — denominator for ratios */
|
||||
ndtl: 740000, // ₹7,400 Cr in Lakhs
|
||||
/** RBI tolerance: cumulative negative gap up to 14 days ≤ 5%, 28 days ≤ 10% */
|
||||
tolerance14d: 5,
|
||||
tolerance28d: 10
|
||||
userCode: 'CBSN01'
|
||||
};
|
||||
|
||||
/** Standard RBI ALM time buckets per Master Direction */
|
||||
buckets: MaturityBucket[] = [
|
||||
{ code: 'B01', label: 'Day 1', outflow: 1840, inflow: 2120 },
|
||||
{ code: 'B02', label: '2 to 7 days', outflow: 6420, inflow: 5880 },
|
||||
{ code: 'B03', label: '8 to 14 days', outflow: 4180, inflow: 4360 },
|
||||
{ code: 'B04', label: '15 to 30 days', outflow: 8240, inflow: 7920 },
|
||||
{ code: 'B05', label: '31 days to 2 months', outflow: 12480, inflow: 13160 },
|
||||
{ code: 'B06', label: '> 2 months to 3 months', outflow: 9620, inflow: 10240 },
|
||||
{ code: 'B07', label: '> 3 months to 6 months', outflow: 28640, inflow: 32180 },
|
||||
{ code: 'B08', label: '> 6 months to 1 year', outflow: 64280, inflow: 68420 },
|
||||
{ code: 'B09', label: '> 1 year to 3 years', outflow: 142800, inflow: 156400 },
|
||||
{ code: 'B10', label: '> 3 years to 5 years', outflow: 96400, inflow: 108200 },
|
||||
{ code: 'B11', label: 'Over 5 years', outflow: 124800, inflow: 138400 }
|
||||
];
|
||||
|
||||
/** Monthly snapshot — mirrors the format of the legacy ALM Excel */
|
||||
monthly: MonthSnapshot[] = [
|
||||
/**
|
||||
* The financial year, April to March. A part-year print simply stops at the month asked for, which
|
||||
* is why `upToIndex` exists rather than the row list being cut to size here.
|
||||
*/
|
||||
monthly: MonthlyAlmRow[] = [
|
||||
{
|
||||
month: 'APR-2025',
|
||||
reserveProv: 18420,
|
||||
borrowings: 24180,
|
||||
fds: 462800,
|
||||
cashOnHand: 8420,
|
||||
investments: 184600,
|
||||
otherAssets: 12480,
|
||||
capital: 24200,
|
||||
deposits: 602800,
|
||||
otherLiabilities: 28400,
|
||||
bankCurrent: 18900,
|
||||
bankFds: 46200,
|
||||
advances: 372400
|
||||
capital: 264.66,
|
||||
reserveProv: 720.83,
|
||||
deposits: 6108.63,
|
||||
borrowings: 24.47,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 172.44,
|
||||
bankCurrent: 190.09,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'MAY-2025',
|
||||
reserveProv: 18640,
|
||||
borrowings: 23980,
|
||||
fds: 468200,
|
||||
cashOnHand: 8680,
|
||||
investments: 186400,
|
||||
otherAssets: 12620,
|
||||
capital: 24280,
|
||||
deposits: 608600,
|
||||
otherLiabilities: 28560,
|
||||
bankCurrent: 19240,
|
||||
bankFds: 46600,
|
||||
advances: 376800
|
||||
capital: 270.73,
|
||||
reserveProv: 722.11,
|
||||
deposits: 6181.15,
|
||||
borrowings: 26.36,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 129.0,
|
||||
bankCurrent: 218.27,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'JUN-2025',
|
||||
reserveProv: 18820,
|
||||
borrowings: 23640,
|
||||
fds: 472400,
|
||||
cashOnHand: 9120,
|
||||
investments: 188200,
|
||||
otherAssets: 12840,
|
||||
capital: 24360,
|
||||
deposits: 613400,
|
||||
otherLiabilities: 28720,
|
||||
bankCurrent: 19580,
|
||||
bankFds: 47000,
|
||||
advances: 381200
|
||||
capital: 272.61,
|
||||
reserveProv: 722.11,
|
||||
deposits: 6291.01,
|
||||
borrowings: 33.0,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 115.01,
|
||||
bankCurrent: 322.77,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'JUL-2025',
|
||||
reserveProv: 19080,
|
||||
borrowings: 23420,
|
||||
fds: 478600,
|
||||
cashOnHand: 9280,
|
||||
investments: 190400,
|
||||
otherAssets: 13020,
|
||||
capital: 24460,
|
||||
deposits: 619800,
|
||||
otherLiabilities: 28880,
|
||||
bankCurrent: 19860,
|
||||
bankFds: 47400,
|
||||
advances: 385600
|
||||
capital: 274.06,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6299.45,
|
||||
borrowings: 50.07,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 84.82,
|
||||
bankCurrent: 267.13,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'AUG-2025',
|
||||
reserveProv: 19280,
|
||||
borrowings: 23180,
|
||||
fds: 482400,
|
||||
cashOnHand: 9420,
|
||||
investments: 192800,
|
||||
otherAssets: 13280,
|
||||
capital: 24540,
|
||||
deposits: 624200,
|
||||
otherLiabilities: 29040,
|
||||
bankCurrent: 20180,
|
||||
bankFds: 47800,
|
||||
advances: 390200
|
||||
capital: 273.22,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6404.97,
|
||||
borrowings: 26.53,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 111.77,
|
||||
bankCurrent: 253.24,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'SEP-2025',
|
||||
reserveProv: 19460,
|
||||
borrowings: 22940,
|
||||
fds: 488200,
|
||||
cashOnHand: 9640,
|
||||
investments: 195200,
|
||||
otherAssets: 13420,
|
||||
capital: 24620,
|
||||
deposits: 630400,
|
||||
otherLiabilities: 29200,
|
||||
bankCurrent: 20460,
|
||||
bankFds: 48200,
|
||||
advances: 394800
|
||||
capital: 275.34,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6361.83,
|
||||
borrowings: 30.3,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 136.45,
|
||||
bankCurrent: 190.83,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'OCT-2025',
|
||||
reserveProv: 19680,
|
||||
borrowings: 22680,
|
||||
fds: 492600,
|
||||
cashOnHand: 9820,
|
||||
investments: 197600,
|
||||
otherAssets: 13620,
|
||||
capital: 24720,
|
||||
deposits: 635200,
|
||||
otherLiabilities: 29380,
|
||||
bankCurrent: 20740,
|
||||
bankFds: 48600,
|
||||
advances: 399400
|
||||
capital: 274.82,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6304.25,
|
||||
borrowings: 38.0,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 93.77,
|
||||
bankCurrent: 239.01,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'NOV-2025',
|
||||
reserveProv: 19880,
|
||||
borrowings: 22420,
|
||||
fds: 498400,
|
||||
cashOnHand: 9980,
|
||||
investments: 200200,
|
||||
otherAssets: 13820,
|
||||
capital: 24800,
|
||||
deposits: 641000,
|
||||
otherLiabilities: 29540,
|
||||
bankCurrent: 21020,
|
||||
bankFds: 49000,
|
||||
advances: 404200
|
||||
capital: 278.29,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6269.1,
|
||||
borrowings: 52.64,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 92.03,
|
||||
bankCurrent: 215.31,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'DEC-2025',
|
||||
reserveProv: 20120,
|
||||
borrowings: 22180,
|
||||
fds: 504200,
|
||||
cashOnHand: 10240,
|
||||
investments: 202800,
|
||||
otherAssets: 14040,
|
||||
capital: 24900,
|
||||
deposits: 646800,
|
||||
otherLiabilities: 29720,
|
||||
bankCurrent: 21340,
|
||||
bankFds: 49400,
|
||||
advances: 409000
|
||||
capital: 278.97,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6310.57,
|
||||
borrowings: 50.01,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 98.58,
|
||||
bankCurrent: 221.93,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'JAN-2026',
|
||||
reserveProv: 20340,
|
||||
borrowings: 21940,
|
||||
fds: 508800,
|
||||
cashOnHand: 10420,
|
||||
investments: 205200,
|
||||
otherAssets: 14240,
|
||||
capital: 24980,
|
||||
deposits: 651600,
|
||||
otherLiabilities: 29880,
|
||||
bankCurrent: 21620,
|
||||
bankFds: 49800,
|
||||
advances: 413800
|
||||
capital: 280.14,
|
||||
reserveProv: 755.72,
|
||||
deposits: 6358.2,
|
||||
borrowings: 47.85,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 101.36,
|
||||
bankCurrent: 228.47,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'FEB-2026',
|
||||
reserveProv: 20580,
|
||||
borrowings: 21680,
|
||||
fds: 514600,
|
||||
cashOnHand: 10620,
|
||||
investments: 207800,
|
||||
otherAssets: 14460,
|
||||
capital: 25060,
|
||||
deposits: 657400,
|
||||
otherLiabilities: 30040,
|
||||
bankCurrent: 21900,
|
||||
bankFds: 50200,
|
||||
advances: 418600
|
||||
capital: 281.42,
|
||||
reserveProv: 768.4,
|
||||
deposits: 6402.75,
|
||||
borrowings: 44.62,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 104.19,
|
||||
bankCurrent: 233.08,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
},
|
||||
{
|
||||
month: 'MAR-2026',
|
||||
reserveProv: 20840,
|
||||
borrowings: 21420,
|
||||
fds: 520400,
|
||||
cashOnHand: 10820,
|
||||
investments: 210400,
|
||||
otherAssets: 14680,
|
||||
capital: 25160,
|
||||
deposits: 663200,
|
||||
otherLiabilities: 30220,
|
||||
bankCurrent: 22240,
|
||||
bankFds: 50600,
|
||||
advances: 423600
|
||||
capital: 283.05,
|
||||
reserveProv: 768.4,
|
||||
deposits: 6471.38,
|
||||
borrowings: 41.3,
|
||||
otherLiabilities: 280.91,
|
||||
cashOnHand: 110.62,
|
||||
bankCurrent: 241.66,
|
||||
bankFds: 0,
|
||||
investments: 0,
|
||||
advances: 0,
|
||||
otherAssets: 0
|
||||
}
|
||||
];
|
||||
|
||||
filterBucket = '';
|
||||
/**
|
||||
* Last month included in the print. Defaults to the ninth month, DEC-2025, because that is where a
|
||||
* statement taken part-way through the year stops — the months after it have not closed.
|
||||
*/
|
||||
upToIndex = 8;
|
||||
|
||||
// -------------------- Computed --------------------
|
||||
bucketGap(b: MaturityBucket): number {
|
||||
return b.inflow - b.outflow;
|
||||
}
|
||||
bucketGapPct(b: MaturityBucket): number {
|
||||
return b.outflow === 0 ? 0 : +(((b.inflow - b.outflow) / b.outflow) * 100).toFixed(2);
|
||||
}
|
||||
cumulativeOutflow(idx: number): number {
|
||||
return this.buckets.slice(0, idx + 1).reduce((s, b) => s + b.outflow, 0);
|
||||
}
|
||||
cumulativeInflow(idx: number): number {
|
||||
return this.buckets.slice(0, idx + 1).reduce((s, b) => s + b.inflow, 0);
|
||||
}
|
||||
cumulativeGap(idx: number): number {
|
||||
return this.cumulativeInflow(idx) - this.cumulativeOutflow(idx);
|
||||
}
|
||||
cumulativeGapPct(idx: number): number {
|
||||
const out = this.cumulativeOutflow(idx);
|
||||
return out === 0 ? 0 : +((this.cumulativeGap(idx) / out) * 100).toFixed(2);
|
||||
}
|
||||
/** Columns the core-system extract does not map. Named on screen, not quietly filled in. */
|
||||
readonly unmappedColumns = 'Investments, Advances, Bank-FD balances and Other Assets';
|
||||
|
||||
get totalOutflow(): number {
|
||||
return this.buckets.reduce((s, b) => s + b.outflow, 0);
|
||||
}
|
||||
get totalInflow(): number {
|
||||
return this.buckets.reduce((s, b) => s + b.inflow, 0);
|
||||
}
|
||||
get totalGap(): number {
|
||||
return this.totalInflow - this.totalOutflow;
|
||||
}
|
||||
get totalGapPct(): number {
|
||||
return this.totalOutflow === 0 ? 0 : +((this.totalGap / this.totalOutflow) * 100).toFixed(2);
|
||||
}
|
||||
|
||||
/** RBI tolerance test: cumulative negative gap up to bucket 3 (14 days) ≤ 5% of cumulative outflow */
|
||||
get gap14dPct(): number {
|
||||
return this.cumulativeGapPct(2);
|
||||
}
|
||||
/** Up to bucket 4 (30 days) ≤ 10% */
|
||||
get gap28dPct(): number {
|
||||
return this.cumulativeGapPct(3);
|
||||
}
|
||||
get gap14dWithinTolerance(): boolean {
|
||||
return this.gap14dPct >= -this.bank.tolerance14d;
|
||||
}
|
||||
get gap28dWithinTolerance(): boolean {
|
||||
return this.gap28dPct >= -this.bank.tolerance28d;
|
||||
}
|
||||
|
||||
bucketStatus(b: MaturityBucket): 'surplus' | 'deficit' | 'balanced' {
|
||||
const gap = this.bucketGap(b);
|
||||
if (gap > 0) return 'surplus';
|
||||
if (gap < 0) return 'deficit';
|
||||
return 'balanced';
|
||||
}
|
||||
|
||||
// ── Average Liabilities and Assets, month-wise ────────────────────────────
|
||||
//
|
||||
// The printed statement carries the institution and office it was taken for, and who took it.
|
||||
// Those come off the session rather than being typed into the report, so a branch login prints
|
||||
// its own office and its own user code instead of whatever the last person hard-coded.
|
||||
/** Stamped when the statement is run, and again whenever the parameters change it. */
|
||||
generatedAt = this.formatPrintDate(new Date());
|
||||
|
||||
/** "<BANK> , <OFFICE>" — the banner line the printed statement opens with. */
|
||||
get statementHeading(): string {
|
||||
return `${this.bank.name.toUpperCase()} , ${this.branchScope.officeName.toUpperCase()}`;
|
||||
}
|
||||
|
||||
/** Office the statement was taken for, off the session rather than typed into the report. */
|
||||
get branchName(): string {
|
||||
return this.branchScope.officeName;
|
||||
}
|
||||
|
||||
/** Who ran the report — the login's username, which is what the printed footer calls a code. */
|
||||
get statementUserCode(): string {
|
||||
return (this.auth.getCredentials()?.username || this.bank.userCode).toUpperCase();
|
||||
}
|
||||
|
||||
/** Stamped when the tab is opened, so the footer records when the figures were taken off. */
|
||||
readonly statementPrintedAt = this.formatPrintDate(new Date());
|
||||
/** Month choices for the "up to" parameter, e.g. 'DEC-2025'. */
|
||||
get monthOptions(): { index: number; label: string }[] {
|
||||
return this.monthly.map((m, index) => ({ index, label: m.month }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Month rows, then the Total and Average lines the statement closes with.
|
||||
*
|
||||
* Average is the mean of the months shown, not Total ÷ 12 — a statement printed part-way
|
||||
* through the year would otherwise divide by months that have not happened yet.
|
||||
*/
|
||||
get avgBalanceRows(): AvgBalanceRow[] {
|
||||
return this.monthly.map((m) => {
|
||||
/** The months the statement prints. */
|
||||
get statementRows(): StatementRow[] {
|
||||
return this.monthly.slice(0, this.upToIndex + 1).map((m) => {
|
||||
const [
|
||||
name,
|
||||
year
|
||||
@@ -412,7 +332,7 @@ export class AlmReportDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private readonly avgBalanceColumns: (keyof AvgBalanceRow)[] = [
|
||||
private readonly amountColumns: (keyof StatementRow)[] = [
|
||||
'capital',
|
||||
'reserveProv',
|
||||
'deposits',
|
||||
@@ -426,190 +346,92 @@ export class AlmReportDialogComponent {
|
||||
'otherAssets'
|
||||
];
|
||||
|
||||
get avgBalanceTotal(): AvgBalanceRow {
|
||||
const rows = this.avgBalanceRows;
|
||||
const out = { label: 'Total', sub: '' } as AvgBalanceRow;
|
||||
for (const key of this.avgBalanceColumns) {
|
||||
out[key] = rows.reduce((s, r) => s + (r[key] as number), 0) as never;
|
||||
get totalRow(): StatementRow {
|
||||
const rows = this.statementRows;
|
||||
const out = { label: 'Total', sub: '' } as StatementRow;
|
||||
for (const key of this.amountColumns) {
|
||||
out[key] = +rows.reduce((s, r) => s + (r[key] as number), 0).toFixed(2) as never;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
get avgBalanceAverage(): AvgBalanceRow {
|
||||
const rows = this.avgBalanceRows;
|
||||
const total = this.avgBalanceTotal;
|
||||
const out = { label: 'Average', sub: '' } as AvgBalanceRow;
|
||||
for (const key of this.avgBalanceColumns) {
|
||||
/**
|
||||
* Mean of the months printed, not Total ÷ 12 — a statement that stops at December must divide by
|
||||
* nine, or every average on it is understated.
|
||||
*/
|
||||
get averageRow(): StatementRow {
|
||||
const rows = this.statementRows;
|
||||
const total = this.totalRow;
|
||||
const out = { label: 'Average', sub: '' } as StatementRow;
|
||||
for (const key of this.amountColumns) {
|
||||
out[key] = (rows.length ? +((total[key] as number) / rows.length).toFixed(2) : 0) as never;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** dd/MM/yyyy hh:mm:ssAM — the format the printed statement stamps its footer with. */
|
||||
private formatPrintDate(d: Date): string {
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
const hours24 = d.getHours();
|
||||
const meridiem = hours24 < 12 ? 'AM' : 'PM';
|
||||
const hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
return (
|
||||
`${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ` +
|
||||
`${pad(hours12)}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${meridiem}`
|
||||
);
|
||||
/** Re-running the statement re-stamps the footer, since the figures on it have changed. */
|
||||
runReport(): void {
|
||||
this.generatedAt = this.formatPrintDate(new Date());
|
||||
}
|
||||
|
||||
/** Monthly totals helpers */
|
||||
monthTotalLiab(m: MonthSnapshot): number {
|
||||
return m.reserveProv + m.borrowings + m.fds;
|
||||
}
|
||||
monthTotalAssets(m: MonthSnapshot): number {
|
||||
return m.cashOnHand + m.investments + m.otherAssets;
|
||||
}
|
||||
/** Average across months */
|
||||
avg<K extends keyof MonthSnapshot>(key: K): number {
|
||||
if (typeof this.monthly[0][key] !== 'number') return 0;
|
||||
const total = this.monthly.reduce((s, m) => s + (m[key] as number), 0);
|
||||
return +(total / this.monthly.length).toFixed(2);
|
||||
}
|
||||
|
||||
// -------------------- Filter --------------------
|
||||
get filteredBuckets(): MaturityBucket[] {
|
||||
if (!this.filterBucket) return this.buckets;
|
||||
if (this.filterBucket === 'short') return this.buckets.slice(0, 4);
|
||||
if (this.filterBucket === 'medium') return this.buckets.slice(4, 8);
|
||||
if (this.filterBucket === 'long') return this.buckets.slice(8);
|
||||
return this.buckets;
|
||||
}
|
||||
|
||||
// -------------------- Excel Exports --------------------
|
||||
|
||||
/** Form ALM-1 — Statement of Structural Liquidity */
|
||||
exportFormAlm(): void {
|
||||
const rows: string[][] = [];
|
||||
rows.push([
|
||||
this.bank.name,
|
||||
this.bank.branch
|
||||
]);
|
||||
rows.push([
|
||||
`STATEMENT OF STRUCTURAL LIQUIDITY (${this.bank.formCode})`,
|
||||
`As On ${this.bank.asOnDate}`
|
||||
]);
|
||||
rows.push([
|
||||
`RBI Circular: ${this.bank.rbiCircular}`,
|
||||
`Quarter: ${this.bank.quarter}`
|
||||
]);
|
||||
rows.push([
|
||||
`Report Ref: ${this.bank.reportRefNo}`,
|
||||
`User Code: ${this.bank.userCode}`
|
||||
]);
|
||||
rows.push([]);
|
||||
rows.push([
|
||||
'NDTL (₹ Lakhs)',
|
||||
this.bank.ndtl.toString()
|
||||
]);
|
||||
rows.push([]);
|
||||
rows.push(['Maturity Ladder (₹ in Lakhs)']);
|
||||
rows.push([
|
||||
'Bucket',
|
||||
'Time Period',
|
||||
'Outflows (Liabilities)',
|
||||
'Inflows (Assets)',
|
||||
'Mismatch (Gap)',
|
||||
'Cum. Outflows',
|
||||
'Cum. Inflows',
|
||||
'Cum. Gap',
|
||||
'Cum. Gap % of Outflow'
|
||||
]);
|
||||
this.buckets.forEach((b, i) => {
|
||||
rows.push([
|
||||
b.code,
|
||||
b.label,
|
||||
b.outflow.toFixed(2),
|
||||
b.inflow.toFixed(2),
|
||||
this.bucketGap(b).toFixed(2),
|
||||
this.cumulativeOutflow(i).toFixed(2),
|
||||
this.cumulativeInflow(i).toFixed(2),
|
||||
this.cumulativeGap(i).toFixed(2),
|
||||
this.cumulativeGapPct(i).toFixed(2) + '%'
|
||||
]);
|
||||
});
|
||||
rows.push([]);
|
||||
rows.push([
|
||||
'TOTAL',
|
||||
'',
|
||||
this.totalOutflow.toFixed(2),
|
||||
this.totalInflow.toFixed(2),
|
||||
this.totalGap.toFixed(2)
|
||||
]);
|
||||
rows.push([]);
|
||||
rows.push(['RBI Tolerance Compliance']);
|
||||
rows.push([
|
||||
'Cumulative Gap up to 14 days',
|
||||
this.gap14dPct.toFixed(2) + '%',
|
||||
`Limit: -${this.bank.tolerance14d}%`,
|
||||
this.gap14dWithinTolerance ? 'WITHIN' : 'BREACH'
|
||||
]);
|
||||
rows.push([
|
||||
'Cumulative Gap up to 30 days',
|
||||
this.gap28dPct.toFixed(2) + '%',
|
||||
`Limit: -${this.bank.tolerance28d}%`,
|
||||
this.gap28dWithinTolerance ? 'WITHIN' : 'BREACH'
|
||||
]);
|
||||
rows.push([]);
|
||||
rows.push([
|
||||
'Certified by',
|
||||
this.bank.principalOfficer + ' — Principal Officer'
|
||||
]);
|
||||
this.saveTsv(rows, `ALM-StructuralLiquidity-${this.dateTag()}.xls`);
|
||||
}
|
||||
|
||||
/** Monthly snapshot export */
|
||||
exportMonthlyXls(): void {
|
||||
const rows: string[][] = [];
|
||||
rows.push([
|
||||
`${this.bank.name} — ${this.bank.branch}`,
|
||||
`Monthly ALM Snapshot — FY ${this.bank.fiscalYear}`
|
||||
]);
|
||||
rows.push([]);
|
||||
rows.push([
|
||||
'Month',
|
||||
'Reserve/Prov (Excl. OIR)',
|
||||
'Borrowings',
|
||||
'FDs',
|
||||
'Total Liabilities',
|
||||
'Cash on Hand',
|
||||
'Investments incl. Bal/Repo',
|
||||
'Other Assets (Excl. IRA)',
|
||||
'Total Assets'
|
||||
]);
|
||||
for (const m of this.monthly) {
|
||||
rows.push([
|
||||
m.month,
|
||||
m.reserveProv.toFixed(2),
|
||||
m.borrowings.toFixed(2),
|
||||
m.fds.toFixed(2),
|
||||
this.monthTotalLiab(m).toFixed(2),
|
||||
m.cashOnHand.toFixed(2),
|
||||
m.investments.toFixed(2),
|
||||
m.otherAssets.toFixed(2),
|
||||
this.monthTotalAssets(m).toFixed(2)
|
||||
]);
|
||||
/**
|
||||
* Prints the statement on its own.
|
||||
*
|
||||
* The dialog sits in an overlay on top of the application, so `window.print()` would put the whole
|
||||
* screen behind it on the paper. The rendered statement is handed to a window of its own instead,
|
||||
* which needs no application-wide print stylesheet and cannot pick up anything else on the page.
|
||||
*/
|
||||
print(): void {
|
||||
const node = this.statementRef?.nativeElement;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
rows.push([
|
||||
'AVERAGE',
|
||||
this.avg('reserveProv').toFixed(2),
|
||||
this.avg('borrowings').toFixed(2),
|
||||
this.avg('fds').toFixed(2),
|
||||
(this.avg('reserveProv') + this.avg('borrowings') + this.avg('fds')).toFixed(2),
|
||||
this.avg('cashOnHand').toFixed(2),
|
||||
this.avg('investments').toFixed(2),
|
||||
this.avg('otherAssets').toFixed(2),
|
||||
(this.avg('cashOnHand') + this.avg('investments') + this.avg('otherAssets')).toFixed(2)
|
||||
]);
|
||||
this.saveTsv(rows, `ALM-Monthly-${this.dateTag()}.xls`);
|
||||
const win = window.open('', '_blank', 'width=1100,height=760');
|
||||
if (!win) {
|
||||
// Blocked by the browser — fall back to the page print, which the print stylesheet handles.
|
||||
window.print();
|
||||
return;
|
||||
}
|
||||
win.document.write(
|
||||
`<!doctype html><html><head><title>Average Liabilities and Assets — ${this.bank.financialYear}</title>` +
|
||||
`<style>${AlmReportDialogComponent.PRINT_CSS}</style></head><body>${node.outerHTML}</body></html>`
|
||||
);
|
||||
win.document.close();
|
||||
win.focus();
|
||||
// Let the document lay out before the print dialog measures it.
|
||||
win.setTimeout(() => {
|
||||
win.print();
|
||||
win.close();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
/** Average Liabilities and Assets — the printed statement, column for column. */
|
||||
exportAvgBalancesXls(): void {
|
||||
/** Stylesheet for the print window — the statement as the core system lays it out on paper. */
|
||||
private static readonly PRINT_CSS = `
|
||||
@page { size: A4 landscape; margin: 12mm; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; font-family: Arial, Helvetica, sans-serif; color: #000; }
|
||||
.stmt { padding: 0; }
|
||||
.stmt-bank { font-size: 8.5pt; font-weight: 700; }
|
||||
.stmt-title { margin: 2pt 0 14pt; font-size: 7.5pt; }
|
||||
.stmt-tbl { width: 100%; border-collapse: collapse; font-size: 8pt; }
|
||||
.stmt-tbl th { padding: 0 6pt 7pt; text-align: right; vertical-align: bottom;
|
||||
font-weight: 400; font-size: 7pt; line-height: 1.3; white-space: nowrap; }
|
||||
.stmt-tbl td { padding: 5pt 6pt; text-align: right; font-weight: 700; white-space: nowrap; }
|
||||
.stmt-tbl .col-month { text-align: left; }
|
||||
.stmt-tbl .col-grp { text-align: center; padding-bottom: 2pt; }
|
||||
.stmt-tbl tr { page-break-inside: avoid; }
|
||||
.stmt-sum td { padding-top: 10pt; }
|
||||
.stmt-sum:first-of-type td { border-top: 0.5pt solid #000; }
|
||||
.stmt-sum .col-month { font-weight: 400; }
|
||||
.stmt-foot { margin-top: 24pt; font-size: 7pt; line-height: 1.8; }
|
||||
`;
|
||||
|
||||
close(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
/** Excel copy of the statement, column for column and in the same order. */
|
||||
exportXls(): void {
|
||||
const rows: string[][] = [];
|
||||
rows.push([this.statementHeading]);
|
||||
rows.push(['Average Liabilities and Assets Month-wise during the year']);
|
||||
@@ -628,7 +450,7 @@ export class AlmReportDialogComponent {
|
||||
'Advances',
|
||||
'Other Assets Exclude IRA'
|
||||
]);
|
||||
const line = (r: AvgBalanceRow): string[] => [
|
||||
const line = (r: StatementRow): string[] => [
|
||||
`${r.label} ${r.sub}`.trim(),
|
||||
r.capital.toFixed(2),
|
||||
r.reserveProv.toFixed(2),
|
||||
@@ -642,71 +464,38 @@ export class AlmReportDialogComponent {
|
||||
r.advances.toFixed(2),
|
||||
r.otherAssets.toFixed(2)
|
||||
];
|
||||
this.avgBalanceRows.forEach((r) => rows.push(line(r)));
|
||||
rows.push(line(this.avgBalanceTotal));
|
||||
rows.push(line(this.avgBalanceAverage));
|
||||
this.statementRows.forEach((r) => rows.push(line(r)));
|
||||
rows.push(line(this.totalRow));
|
||||
rows.push(line(this.averageRow));
|
||||
rows.push([]);
|
||||
rows.push([`Print Date : ${this.statementPrintedAt}`]);
|
||||
rows.push([`Not mapped in this extract: ${this.unmappedColumns}`]);
|
||||
rows.push([`Print Date : ${this.generatedAt}`]);
|
||||
rows.push([`Report Ref No : ${this.bank.reportRefNo} User Code:${this.statementUserCode}`]);
|
||||
this.saveTsv(rows, `ALM-AverageLiabilitiesAssets-${this.dateTag()}.xls`);
|
||||
|
||||
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 = `ALM-AverageLiabilitiesAssets-${this.dateTag()}.xls`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/** Maturity ladder CSV */
|
||||
exportLadderCsv(): void {
|
||||
const headers = [
|
||||
'Bucket',
|
||||
'Time Period',
|
||||
'Outflow (₹ Lakh)',
|
||||
'Inflow (₹ Lakh)',
|
||||
'Gap',
|
||||
'Gap %'
|
||||
];
|
||||
const rows = [headers];
|
||||
for (const b of this.buckets) {
|
||||
rows.push([
|
||||
b.code,
|
||||
b.label,
|
||||
b.outflow.toString(),
|
||||
b.inflow.toString(),
|
||||
this.bucketGap(b).toString(),
|
||||
this.bucketGapPct(b).toFixed(2) + '%'
|
||||
]);
|
||||
}
|
||||
this.saveCsv(rows, `ALM-Ladder-${this.dateTag()}.csv`);
|
||||
}
|
||||
|
||||
setTab(tab: 'dashboard' | 'ladder' | 'monthly' | 'avgbal' | 'returns'): void {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
close(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
// ---- 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 saveCsv(rows: string[][], filename: string): void {
|
||||
const escape = (v: string): string => {
|
||||
const s = (v || '').replace(/"/g, '""');
|
||||
if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s}"`;
|
||||
return s;
|
||||
};
|
||||
const csv = rows.map((r) => r.map(escape).join(',')).join('\n') + '\n';
|
||||
this.saveBlob(csv, filename, 'text/csv');
|
||||
}
|
||||
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);
|
||||
|
||||
/** dd/MM/yyyy hh:mm:ssAM — the format the printed statement stamps its footer with. */
|
||||
private formatPrintDate(d: Date): string {
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
const hours24 = d.getHours();
|
||||
const meridiem = hours24 < 12 ? 'AM' : 'PM';
|
||||
const hours12 = hours24 % 12 === 0 ? 12 : hours24 % 12;
|
||||
return (
|
||||
`${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ` +
|
||||
`${pad(hours12)}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${meridiem}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user