Files
cbsnexora/src/app/gl-reports/gl-reports.component.html
T

1056 lines
40 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="glr-root">
<!-- HEADER -->
<div class="glr-head">
<div class="glr-head-left">
<fa-icon icon="book-open" class="glr-head-ic"></fa-icon>
<div>
<h2 class="glr-head-t">General Ledger — {{ reportLabel }}</h2>
<div class="glr-head-s">
CBSNexora Bank Ltd &middot; {{ branchLabel }} &middot; Period <strong>{{ rangeLabel }}</strong> &middot;
{{ ledgerPeriod.label }}
</div>
</div>
</div>
<button class="glr-btn glr-btn-xls" [disabled]="generating" (click)="exportExcel()">
<fa-icon icon="file-csv"></fa-icon>
{{ generating ? 'Exporting…' : 'Export to Excel' }}
</button>
</div>
<!-- FILTER BAR -->
<div class="glr-filters">
<div class="glr-filter">
<label>Period</label>
<div class="glr-seg">
<button [class.active]="periodMode === 'day'" (click)="periodMode = 'day'">Day-wise</button>
<button [class.active]="periodMode === 'month'" (click)="periodMode = 'month'">Month-wise</button>
<button [class.active]="periodMode === 'range'" (click)="periodMode = 'range'">Date Range</button>
</div>
</div>
@if (periodMode === 'day') {
<div class="glr-filter">
<label>Date</label>
<input
class="glr-inp"
type="date"
[(ngModel)]="filterDay"
[min]="ledgerPeriod.fyStart"
[max]="ledgerPeriod.fyEnd"
/>
</div>
}
@if (periodMode === 'month') {
<div class="glr-filter">
<label>Month</label>
<input class="glr-inp" type="month" [(ngModel)]="filterMonth" min="2026-04" max="2026-07" />
</div>
}
@if (periodMode === 'range') {
<div class="glr-filter">
<label>From</label>
<input
class="glr-inp"
type="date"
[(ngModel)]="filterFrom"
[min]="ledgerPeriod.fyStart"
[max]="ledgerPeriod.fyEnd"
/>
</div>
<div class="glr-filter">
<label>To</label>
<input
class="glr-inp"
type="date"
[(ngModel)]="filterTo"
[min]="ledgerPeriod.fyStart"
[max]="ledgerPeriod.fyEnd"
/>
</div>
}
<div class="glr-filter">
<label>Branch</label>
@if (canSelectBranch) {
<select class="glr-inp" [(ngModel)]="branchCode">
<option value="">All Branches (Consolidated)</option>
@for (b of branches; track b.code) {
<option [value]="b.code">{{ b.code }} — {{ b.name }}</option>
}
</select>
} @else {
<span class="glr-chip" title="Your login is restricted to this branch">
<fa-icon icon="university"></fa-icon> {{ scopeName }}
</span>
}
</div>
<button class="glr-btn" (click)="resetFilters()">Reset</button>
</div>
@if (scopeIsUnresolved) {
<div class="glr-balance glr-balance--bad">
<fa-icon icon="exclamation-triangle"></fa-icon>
<span>
Your office <strong>{{ scopeName }}</strong> does not match any branch in the ledger, so no figures can be
shown. Ask an administrator to align your office name with a branch.
</span>
</div>
}
<!-- REPORT SELECTOR -->
<div class="glr-tabs">
@for (r of reports; track r.code) {
<button class="glr-tab" [class.active]="activeReport === r.code" (click)="showReport(r.code)">
<fa-icon [icon]="r.icon"></fa-icon> {{ r.label }}
</button>
}
</div>
<!-- ══════════════ TRIAL BALANCE ══════════════ -->
@if (activeReport === 'trial-balance') {
<div class="glr-section">
<div class="glr-balance" [class.glr-balance--bad]="!tbBalanced">
<fa-icon [icon]="tbBalanced ? 'check-circle' : 'exclamation-triangle'"></fa-icon>
<span>
Closing Dr <strong>&#8377;{{ tbClosingDebit | number: '1.2-2' }}</strong> &middot; Closing Cr
<strong>&#8377;{{ tbClosingCredit | number: '1.2-2' }}</strong> —
{{ tbBalanced ? 'trial balance tallies' : 'OUT OF BALANCE — investigate before closing the period' }}
</span>
</div>
<div class="glr-tbl-wrap">
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Account</th>
<th>Group</th>
<th class="ta-r">Opening Dr</th>
<th class="ta-r">Opening Cr</th>
<th class="ta-r">Period Dr</th>
<th class="ta-r">Period Cr</th>
<th class="ta-r">Closing Dr</th>
<th class="ta-r">Closing Cr</th>
</tr>
</thead>
<tbody>
@for (r of data.trialBalance; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.group }}</td>
<td class="ta-r mono">{{ r.openingDebit ? (r.openingDebit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.openingCredit ? (r.openingCredit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.periodDebit ? (r.periodDebit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.periodCredit ? (r.periodCredit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.closingDebit ? (r.closingDebit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.closingCredit ? (r.closingCredit | number: '1.2-2') : '' }}</td>
</tr>
} @empty {
<tr>
<td colspan="9" class="glr-empty">No ledger activity for this period and branch.</td>
</tr>
}
<tr class="total-row">
<td colspan="3">TOTAL ({{ data.trialBalance.length }} accounts)</td>
<td class="ta-r mono">{{ tbOpeningDebit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ tbOpeningCredit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ tbPeriodDebit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ tbPeriodCredit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ tbClosingDebit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ tbClosingCredit | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ PROFIT AND LOSS ══════════════ -->
@if (activeReport === 'profit-and-loss') {
<div class="glr-section">
<div class="glr-kpis">
<div class="glr-kpi glr-kpi--teal">
<div class="glr-kpi-n">&#8377;{{ totalIncome | number: '1.2-2' }}</div>
<div class="glr-kpi-l">Total Income</div>
</div>
<div class="glr-kpi glr-kpi--amber">
<div class="glr-kpi-n">&#8377;{{ totalExpense | number: '1.2-2' }}</div>
<div class="glr-kpi-l">Total Expenditure</div>
</div>
<div class="glr-kpi" [class.glr-kpi--green]="netProfit >= 0" [class.glr-kpi--red]="netProfit < 0">
<div class="glr-kpi-n">&#8377;{{ netProfit | number: '1.2-2' }}</div>
<div class="glr-kpi-l">{{ netProfit >= 0 ? 'Net Profit' : 'Net Loss' }}</div>
<div class="glr-kpi-sub">{{ netProfitPct | number: '1.2-2' }}% of income</div>
</div>
</div>
<div class="glr-two-col">
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">Expenditure</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of pnlExpense; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>
{{ r.name }}<span class="muted"> · {{ r.group }}</span>
</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="3" class="glr-empty">No expenditure posted in this period.</td>
</tr>
}
<tr class="total-row">
<td colspan="2">Total Expenditure</td>
<td class="ta-r mono">{{ totalExpense | number: '1.2-2' }}</td>
</tr>
@if (netProfit >= 0) {
<tr class="total-row glr-profit-row">
<td colspan="2">Net Profit carried to Balance Sheet</td>
<td class="ta-r mono">{{ netProfit | number: '1.2-2' }}</td>
</tr>
}
</tbody>
</table>
</div>
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">Income</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of pnlIncome; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>
{{ r.name }}<span class="muted"> · {{ r.group }}</span>
</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="3" class="glr-empty">No income posted in this period.</td>
</tr>
}
<tr class="total-row">
<td colspan="2">Total Income</td>
<td class="ta-r mono">{{ totalIncome | number: '1.2-2' }}</td>
</tr>
@if (netProfit < 0) {
<tr class="total-row glr-loss-row">
<td colspan="2">Net Loss carried to Balance Sheet</td>
<td class="ta-r mono">{{ -netProfit | number: '1.2-2' }}</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
<!-- ══════════════ CONSOLIDATED BALANCE SHEET ══════════════ -->
@if (activeReport === 'balance-sheet') {
<div class="glr-section">
<div class="glr-balance" [class.glr-balance--bad]="!bsBalanced">
<fa-icon [icon]="bsBalanced ? 'check-circle' : 'exclamation-triangle'"></fa-icon>
<span>
Total Assets <strong>&#8377;{{ bsTotalAssets | number: '1.2-2' }}</strong> &middot; Total Liabilities
<strong>&#8377;{{ bsTotalLiabilities | number: '1.2-2' }}</strong> —
{{ bsBalanced ? 'balance sheet tallies' : 'OUT OF BALANCE' }}
</span>
</div>
<div class="glr-two-col">
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">Capital &amp; Liabilities &mdash; as at {{ range.to }}</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of bsLiabilities; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>
{{ r.name }}<span class="muted"> · {{ r.group }}</span>
</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
}
<tr>
<td class="mono">—</td>
<td>Surplus for the period<span class="muted"> · Profit &amp; Loss</span></td>
<td class="ta-r mono">{{ bsSurplus | number: '1.2-2' }}</td>
</tr>
<tr class="total-row">
<td colspan="2">Total Capital &amp; Liabilities</td>
<td class="ta-r mono">{{ bsTotalLiabilities | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">Assets &mdash; as at {{ range.to }}</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of bsAssets; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>
{{ r.name }}<span class="muted"> · {{ r.group }}</span>
</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
}
<tr class="total-row">
<td colspan="2">Total Assets</td>
<td class="ta-r mono">{{ bsTotalAssets | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
}
<!-- ══════════════ COMPARATIVE BALANCE SHEET ══════════════ -->
@if (activeReport === 'comparative-balance-sheet') {
<div class="glr-section">
<p class="glr-note">
Compares the closing position as at <strong>{{ range.to }}</strong> against the position immediately before this
period began. Change the filter above to compare a different span.
</p>
@for (
block of [
{ title: 'Capital & Liabilities', rows: compLiabilities },
{ title: 'Assets', rows: compAssets }
];
track block.title
) {
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">{{ block.title }}</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th class="ta-r">Current (&#8377;)</th>
<th class="ta-r">Previous (&#8377;)</th>
<th class="ta-r">Variance (&#8377;)</th>
<th class="ta-r">Variance %</th>
</tr>
</thead>
<tbody>
@for (r of block.rows; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>
{{ r.name }}<span class="muted"> · {{ r.group }}</span>
</td>
<td class="ta-r mono">{{ r.current | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ r.previous | number: '1.2-2' }}</td>
<td class="ta-r mono" [class.var-up]="r.variance > 0" [class.var-down]="r.variance < 0">
{{ r.variance | number: '1.2-2' }}
</td>
<td class="ta-r mono" [class.var-up]="r.variance > 0" [class.var-down]="r.variance < 0">
{{ r.variancePct === null ? '—' : (r.variancePct | number: '1.2-2') + '%' }}
</td>
</tr>
} @empty {
<tr>
<td colspan="6" class="glr-empty">Nothing to compare for this period and branch.</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
}
<!-- ══════════════ CONTINGENT LIABILITIES ══════════════ -->
@if (activeReport === 'contingent-liabilities') {
<div class="glr-section">
<p class="glr-note">
Off-balance-sheet commitments outstanding as at <strong>{{ range.to }}</strong
>. These are memoranda accounts and are deliberately excluded from the Trial Balance and Balance Sheet totals.
</p>
<div class="glr-tbl-wrap">
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Particulars</th>
<th>Group</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of data.contingent; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.group }}</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="4" class="glr-empty">No contingent exposures outstanding for this selection.</td>
</tr>
}
<tr class="total-row">
<td colspan="3">Total Contingent Liabilities</td>
<td class="ta-r mono">{{ contingentTotal | number: '1.2-2' }}</td>
</tr>
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ GL WISE DR / CR ══════════════ -->
@if (activeReport === 'gl-wise-dr-cr') {
<div class="glr-section">
<p class="glr-note">
Debit and credit turnover posted between <strong>{{ rangeLabel }}</strong> for {{ branchLabel }}. Every voucher
is double-entry, so the debit and credit totals below must match.
</p>
<div class="glr-tbl-wrap">
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Account</th>
<th>Type</th>
<th>Group</th>
<th class="ta-r">Debit (&#8377;)</th>
<th class="ta-r">Credit (&#8377;)</th>
<th class="ta-r">Net (Dr &minus; Cr)</th>
<th class="ta-c">Vouchers</th>
</tr>
</thead>
<tbody>
@for (r of data.glWise; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td>
<span class="type-pill" [class]="'type-' + r.type.toLowerCase()">{{ r.type }}</span>
</td>
<td class="muted">{{ r.group }}</td>
<td class="ta-r mono">{{ r.debit ? (r.debit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.credit ? (r.credit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono" [class.var-up]="r.net > 0" [class.var-down]="r.net < 0">
{{ r.net | number: '1.2-2' }}
</td>
<td class="ta-c mono">{{ r.vouchers }}</td>
</tr>
} @empty {
<tr>
<td colspan="8" class="glr-empty">No postings for this period and branch.</td>
</tr>
}
<tr class="total-row">
<td colspan="4">TOTAL ({{ data.glWise.length }} accounts)</td>
<td class="ta-r mono">{{ glWiseDebit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ glWiseCredit | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ glWiseDebit - glWiseCredit | number: '1.2-2' }}</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ MISC EXPENSE LEDGER ══════════════ -->
@if (activeReport === 'misc-expense-ledger') {
<div class="glr-section">
<p class="glr-note">
Every miscellaneous-expense voucher posted between <strong>{{ rangeLabel }}</strong> for
{{ branchLabel }} &mdash; {{ miscRows.length | number }} entries.
</p>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>Date</th>
<th>Voucher No</th>
<th>Branch</th>
<th>GL Code</th>
<th>Account</th>
<th>Narration</th>
<th class="ta-r">Amount (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of miscRows; track r.voucherNo + r.code) {
<tr>
<td class="mono">{{ r.date }}</td>
<td class="mono">{{ r.voucherNo }}</td>
<td>{{ branchName(r.branchCode) }}</td>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.narration }}</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="7" class="glr-empty">No miscellaneous expenses posted for this selection.</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="6">Total Miscellaneous Expenses</td>
<td class="ta-r mono">{{ miscTotal | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- ══════════════ GL AVERAGE BALANCE ══════════════ -->
@if (activeReport === 'gl-average-balance') {
<div class="glr-section">
<p class="glr-note">
Average of each account's closing balance across every day in
<strong>{{ rangeLabel }}</strong> for {{ branchLabel }}. Banks work from average balances rather than a closing
snapshot for interest, CRR/SLR and profitability, because a single day's figure can be dressed up.
</p>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Account</th>
<th>Group</th>
<th class="ta-r">Opening</th>
<th class="ta-r">Closing</th>
<th class="ta-r">Average Daily</th>
<th class="ta-r">Peak</th>
<th class="ta-r">Lowest</th>
<th class="ta-c">Active Days</th>
</tr>
</thead>
<tbody>
@for (r of averageRows; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.group }}</td>
<td class="ta-r mono">{{ r.opening | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ r.closing | number: '1.2-2' }}</td>
<td class="ta-r mono">
<strong>{{ r.average | number: '1.2-2' }}</strong>
</td>
<td class="ta-r mono">{{ r.peak | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ r.lowest | number: '1.2-2' }}</td>
<td class="ta-c mono">{{ r.activeDays }}</td>
</tr>
} @empty {
<tr>
<td colspan="9" class="glr-empty">No ledger activity for this period and branch.</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="5">TOTAL ({{ averageRows.length }} accounts)</td>
<td class="ta-r mono">{{ avgTotalAverage | number: '1.2-2' }}</td>
<td colspan="3"></td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- ══════════════ GL MAPPING FOR REPORT ══════════════ -->
@if (activeReport === 'gl-mapping') {
<div class="glr-section">
<p class="glr-note">
Which General Ledger accounts feed each RBI return line. Mappings are created on the OSS 1, OSS 2 and OSS 3
screens using <strong>Add / Remove GLs</strong>; this is the consolidated view of them, with the value each
mapping currently produces from the ledger.
</p>
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">Mapped return lines ({{ mappingRows.length }})</div>
<table class="glr-tbl">
<thead>
<tr>
<th>Return</th>
<th>Return line</th>
<th>Mapped GL accounts</th>
<th class="ta-r">Ledger value (&#8377; thousands)</th>
</tr>
</thead>
<tbody>
@for (r of mappingRows; track r.returnCode + r.lineKey) {
<tr>
<td>{{ r.returnLabel }}</td>
<td class="mono">{{ r.lineKey }}</td>
<td>
@for (c of r.codes; track c) {
<span class="glr-gl-chip mono">{{ c }}</span>
}
</td>
<td class="ta-r mono">{{ r.value | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="4" class="glr-empty">
No return lines mapped yet. Open OSS 1, 2 or 3 and use “Add / Remove GLs” to pin accounts to a line.
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<div class="glr-tbl-h">Unmapped GL accounts ({{ unmappedAccounts.length }}) — not feeding any return line</div>
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Account</th>
<th>Group</th>
</tr>
</thead>
<tbody>
@for (a of unmappedAccounts; track a.code) {
<tr>
<td class="mono">{{ a.code }}</td>
<td>{{ a.name }}</td>
<td class="muted">{{ a.group }}</td>
</tr>
} @empty {
<tr>
<td colspan="3" class="glr-empty">Every GL account feeds at least one return line.</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ ACCOUNT WISE BALANCE LIST ══════════════ -->
@if (activeReport === 'account-wise-balance') {
<div class="glr-section">
<p class="glr-note">
Closing balance of every account as at <strong>{{ range.to }}</strong
>, broken out by branch — which branch is actually carrying each balance.
</p>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>GL Code</th>
<th>Account</th>
<th>Group</th>
@for (c of branchColumns; track c) {
<th class="ta-r">{{ branchName(c) }}</th>
}
<th class="ta-r">Total</th>
</tr>
</thead>
<tbody>
@for (r of accountWiseRows; track r.code) {
<tr>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.group }}</td>
@for (c of branchColumns; track c) {
<td class="ta-r mono">{{ r.byBranch[c] ? (r.byBranch[c] | number: '1.2-2') : '' }}</td>
}
<td class="ta-r mono">
<strong>{{ r.total | number: '1.2-2' }}</strong>
</td>
</tr>
} @empty {
<tr>
<td [attr.colspan]="branchColumns.length + 4" class="glr-empty">
No balances for this period and branch.
</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="3">TOTAL ({{ accountWiseRows.length }} accounts)</td>
@for (c of branchColumns; track c) {
<td class="ta-r mono">{{ branchColumnTotal(c) | number: '1.2-2' }}</td>
}
<td class="ta-r mono">{{ accountWiseGrandTotal | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- ══════════════ DISTRIBUTION OF BALANCE ══════════════ -->
@if (activeReport === 'distribution-of-balance') {
<div class="glr-section">
<p class="glr-note">
Composition of the book as at <strong>{{ range.to }}</strong> for {{ branchLabel }} — how concentrated each side
of the balance sheet is by account group.
</p>
<div class="glr-two-col">
@for (
block of [
{ title: 'Capital & Liabilities', rows: liabilityDistribution, total: liabilityDistTotal },
{ title: 'Assets', rows: assetDistribution, total: assetDistTotal }
];
track block.title
) {
<div class="glr-tbl-wrap">
<div class="glr-tbl-h">{{ block.title }}</div>
<table class="glr-tbl">
<thead>
<tr>
<th>Group</th>
<th class="ta-c">A/cs</th>
<th class="ta-r">Amount (&#8377;)</th>
<th class="ta-r">Share</th>
<th class="glr-bar-col">Distribution</th>
</tr>
</thead>
<tbody>
@for (r of block.rows; track r.group) {
<tr>
<td>{{ r.group }}</td>
<td class="ta-c mono">{{ r.accounts }}</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
<td class="ta-r mono">
<strong>{{ r.share | number: '1.2-2' }}%</strong>
</td>
<td>
<div class="glr-bar">
<div class="glr-bar-fill" [style.width.%]="r.share"></div>
</div>
</td>
</tr>
} @empty {
<tr>
<td colspan="5" class="glr-empty">No balances on this side.</td>
</tr>
}
<tr class="total-row">
<td colspan="2">Total</td>
<td class="ta-r mono">{{ block.total | number: '1.2-2' }}</td>
<td class="ta-r mono">100.00%</td>
<td></td>
</tr>
</tbody>
</table>
</div>
}
</div>
</div>
}
<!-- ══════════════ CLIENT WISE BALANCE ══════════════ -->
@if (activeReport === 'client-wise-balance') {
<div class="glr-section">
<div class="glr-kpis">
<div class="glr-kpi glr-kpi--teal">
<div class="glr-kpi-n">&#8377;{{ clientDeposits | number: '1.2-2' }}</div>
<div class="glr-kpi-l">Customer Deposits</div>
</div>
<div class="glr-kpi glr-kpi--amber">
<div class="glr-kpi-n">&#8377;{{ clientAdvances | number: '1.2-2' }}</div>
<div class="glr-kpi-l">Customer Advances</div>
</div>
<div class="glr-kpi glr-kpi--green">
<div class="glr-kpi-n">{{ clientRows.length | number }}</div>
<div class="glr-kpi-l">Relationships</div>
<div class="glr-kpi-sub">as at {{ range.to }} · {{ branchLabel }}</div>
</div>
</div>
<p class="glr-note">
Customer balances are derived from the General Ledger — each relationship holds a share of its branch's balance
in that head — so this report always reconciles to the GL rather than drifting as a second source.
</p>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>CIF</th>
<th>Customer</th>
<th>Branch</th>
<th>Account No</th>
<th>Product</th>
<th class="ta-c">Side</th>
<th class="ta-r">Balance (&#8377;)</th>
</tr>
</thead>
<tbody>
@for (r of clientRows; track r.cif) {
<tr>
<td class="mono">{{ r.cif }}</td>
<td>{{ r.name }}</td>
<td>{{ branchName(r.branchCode) }}</td>
<td class="mono">{{ r.accountNo }}</td>
<td class="muted">{{ r.product }}</td>
<td class="ta-c">
<span
class="type-pill"
[class.type-liability]="r.side === 'DEPOSIT'"
[class.type-asset]="r.side === 'ADVANCE'"
>
{{ r.side }}
</span>
</td>
<td class="ta-r mono">{{ r.balance | number: '1.2-2' }}</td>
</tr>
} @empty {
<tr>
<td colspan="7" class="glr-empty">No customer relationships for this period and branch.</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ BANKER HEAD REPORT ══════════════ -->
@if (activeReport === 'banker-head') {
<div class="glr-section">
<p class="glr-note">
Head-wise abstract for <strong>{{ rangeLabel }}</strong> — the trial balance rolled up to principal heads, with
opening, turnover and closing for each. Debit and credit turnover must agree.
</p>
<div class="glr-tbl-wrap">
<table class="glr-tbl">
<thead>
<tr>
<th>Principal Head</th>
<th class="ta-c">Accounts</th>
<th class="ta-r">Opening</th>
<th class="ta-r">Debit</th>
<th class="ta-r">Credit</th>
<th class="ta-r">Closing</th>
</tr>
</thead>
<tbody>
@for (r of bankerHeadRows; track r.head) {
<tr>
<td>
<strong>{{ r.head }}</strong>
</td>
<td class="ta-c mono">{{ r.accounts }}</td>
<td class="ta-r mono">{{ r.opening | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ r.debit ? (r.debit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">{{ r.credit ? (r.credit | number: '1.2-2') : '' }}</td>
<td class="ta-r mono">
<strong>{{ r.closing | number: '1.2-2' }}</strong>
</td>
</tr>
} @empty {
<tr>
<td colspan="6" class="glr-empty">No activity for this period and branch.</td>
</tr>
}
<tr class="total-row">
<td colspan="2">TOTAL ({{ bankerHeadRows.length }} heads)</td>
<td></td>
<td class="ta-r mono">{{ headDebitTotal | number: '1.2-2' }}</td>
<td class="ta-r mono">{{ headCreditTotal | number: '1.2-2' }}</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
}
<!-- ══════════════ SUNDRY REGISTER ══════════════ -->
@if (activeReport === 'sundry-register') {
<div class="glr-section">
<p class="glr-note">
Outstanding items in the sundry, suspense and bills-payable heads as at <strong>{{ range.to }}</strong
>, aged from their posting date. These heads are where unreconciled entries collect, so ageing is the point of
the register.
</p>
@if (sundryBuckets.length) {
<div class="glr-kpis">
@for (b of sundryBuckets; track b.bucket) {
<div
class="glr-kpi"
[class.glr-kpi--green]="b.bucket === 'Up to 30 days'"
[class.glr-kpi--amber]="b.bucket === '31 to 90 days'"
[class.glr-kpi--red]="b.bucket === 'Over 180 days'"
>
<div class="glr-kpi-n">&#8377;{{ b.amount | number: '1.2-2' }}</div>
<div class="glr-kpi-l">{{ b.bucket }}</div>
<div class="glr-kpi-sub">{{ b.count }} item(s)</div>
</div>
}
</div>
}
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>Date</th>
<th>Voucher No</th>
<th>Branch</th>
<th>GL Code</th>
<th>Account</th>
<th>Narration</th>
<th class="ta-r">Amount (&#8377;)</th>
<th class="ta-c">Age</th>
</tr>
</thead>
<tbody>
@for (r of sundryRows; track r.voucherNo + r.code + r.date) {
<tr>
<td class="mono">{{ r.date }}</td>
<td class="mono">{{ r.voucherNo }}</td>
<td>{{ branchName(r.branchCode) }}</td>
<td class="mono">{{ r.code }}</td>
<td>{{ r.name }}</td>
<td class="muted">{{ r.narration }}</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
<td class="ta-c">
<span
class="type-pill"
[class.type-income]="r.bucket === 'Up to 30 days'"
[class.type-expense]="r.bucket === '31 to 90 days'"
[class.type-contingent]="r.bucket === 'Over 180 days'"
>
{{ r.ageDays }}d
</span>
</td>
</tr>
} @empty {
<tr>
<td colspan="8" class="glr-empty">No sundry or suspense items outstanding for this selection.</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="6">Total outstanding ({{ sundryRows.length }} items)</td>
<td class="ta-r mono">{{ sundryTotal | number: '1.2-2' }}</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- ══════════════ VOUCHER REGISTER ══════════════ -->
@if (activeReport === 'voucher-register') {
<div class="glr-section">
<div class="glr-balance" [class.glr-balance--bad]="unbalancedVouchers > 0">
<fa-icon [icon]="unbalancedVouchers ? 'exclamation-triangle' : 'check-circle'"></fa-icon>
<span>
{{ voucherRows.length | number }} voucher(s) raised in {{ rangeLabel }} for {{ branchLabel }} —
{{
unbalancedVouchers
? unbalancedVouchers + ' voucher(s) do NOT balance and must be investigated'
: 'every voucher is double-entry and balances'
}}
</span>
</div>
<div class="glr-tbl-wrap glr-tbl-wrap--tall">
<table class="glr-tbl">
<thead>
<tr>
<th>Date</th>
<th>Voucher No</th>
<th>Branch</th>
<th>Narration</th>
<th>Debit account(s)</th>
<th>Credit account(s)</th>
<th class="ta-r">Amount (&#8377;)</th>
<th class="ta-c">Legs</th>
</tr>
</thead>
<tbody>
@for (r of voucherRows; track r.voucherNo) {
<tr [class.glr-unbalanced]="!r.balanced">
<td class="mono">{{ r.date }}</td>
<td class="mono">{{ r.voucherNo }}</td>
<td>{{ branchName(r.branchCode) }}</td>
<td class="muted">{{ r.narration }}</td>
<td class="mono">{{ r.debitAccounts }}</td>
<td class="mono">{{ r.creditAccounts }}</td>
<td class="ta-r mono">{{ r.amount | number: '1.2-2' }}</td>
<td class="ta-c mono">{{ r.legs }}</td>
</tr>
} @empty {
<tr>
<td colspan="8" class="glr-empty">No vouchers raised in this period for this branch.</td>
</tr>
}
</tbody>
<tfoot>
<tr class="total-row">
<td colspan="6">Total ({{ voucherRows.length }} vouchers)</td>
<td class="ta-r mono">{{ voucherTotal | number: '1.2-2' }}</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
}
<!-- TOAST -->
<div class="glr-toast" [class.show]="toastVisible">{{ toastMsg }}</div>
</div>