Files
cbsnexora/src/app/cashier/closing-cash-report/closing-cash-report.component.html
T
niteshandClaude Sonnet 5 ab58d7f74c fix(cashier): tag GL-entry denomination to the vault, show both voucher legs
- Transaction Entry's manual GL-entry screen was recording its denomination
  breakdown against whichever GL account the teller selected, not the vault
  account whose physical notes actually moved -- resolve and use the Cash
  Vault GL's own code/name dynamically instead
- Closing Cash Report's GL Entries table showed only the one account its
  denomination row knew about; now shows both the debit and credit account
  of each voucher (backend now returns debitParty/creditParty), matching
  what My Vouchers already shows for the same entries

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 07:19:50 +05:30

489 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
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="pg">
<header class="pg-head">
<div>
<h1 class="pg-title">Closing Cash Report</h1>
<p class="pg-sub">How much cash you started with, what came in and went out, and how much you must hand back.</p>
</div>
<div class="pg-toolbar">
<label class="pg-field">
<span>Date</span>
<input type="date" [value]="date" (change)="date = $any($event.target).value" />
</label>
<button type="button" class="pg-btn pg-btn--primary" (click)="load()" [disabled]="loading">
<mat-icon>search</mat-icon> Load
</button>
<button type="button" class="pg-btn" [class.pg-btn--active]="showGl" (click)="toggleGl()">
<mat-icon>account_balance</mat-icon> GL View
</button>
</div>
</header>
<!-- The day's own cash, leg by leg, ending in what should physically be in the drawer.
A cashier carries no balance: they draw from the vault in the morning and hand back at close,
so this is the figure a physical count is checked against.
This used to sit under a second strip reading Deposits − Withdrawals = Net Counter Movement.
That strip stated the same two figures again and then a net the cashier has no use for: it
excludes the vault legs, so on a day the vault gave ₹600 and customer business cancelled out
it read ₹0 while the drawer held ₹600. Two panels, four repeated figures, and the one that
came first was the one to ignore. -->
@if (!showGl && scopedToCashier && hasTodayLegs) {
<!-- Says what the strip is before the reader meets five figures and a running sum. Somebody
opening this screen for the first time has no way to know that the last tile is the one
that matters. -->
<p class="closing-lede">Your cash for the day, step by step</p>
<div class="closing-summary closing-summary--balance">
<div class="closing-summary__item closing-summary__item--in">
<span class="closing-summary__label">Cash you got this morning</span>
<span class="closing-summary__value">₹{{ todayVaultIn | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">+</div>
<div class="closing-summary__item closing-summary__item--in">
<span class="closing-summary__label">Cash customers gave you</span>
<span class="closing-summary__value">₹{{ depositTotalWithDd | number: '1.2-2' }}</span>
@if (ddCashTotal > 0) {
<!-- Named on the tile as well as in the table: this part of the total carries no
denomination, so without saying so the figures read as failing to add up. -->
<span class="closing-summary__note">
₹{{ ddCashTotal | number: '1.2-2' }} of this was for {{ ddCashCount }} draft{{
ddCashCount === 1 ? '' : 's'
}}, notes not listed
</span>
}
</div>
<div class="closing-summary__sep">−</div>
<div class="closing-summary__item closing-summary__item--out">
<span class="closing-summary__label">Cash you gave customers</span>
<span class="closing-summary__value">₹{{ withdrawalTotal | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">−</div>
<div class="closing-summary__item closing-summary__item--out">
<span class="closing-summary__label">Cash already returned</span>
<span class="closing-summary__value">₹{{ todaySettledOut | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">=</div>
<!-- The figure the day ends on. Named for what the cashier does with it — hand this back to
the vault — rather than "closing balance", which reads as something they keep. -->
<div
class="closing-summary__item"
[class.closing-summary__item--net-pos]="expectedInDrawer >= 0"
[class.closing-summary__item--net-neg]="expectedInDrawer < 0"
>
<span class="closing-summary__label">Cash left with you now</span>
<span class="closing-summary__value">₹{{ expectedInDrawer | number: '1.2-2' }}</span>
</div>
</div>
<p class="closing-note">
Count your cash now &mdash; it should come to ₹{{ expectedInDrawer | number: '1.2-2' }}. Hand that amount back and
your day is done.
</p>
<!-- Cashier-to-cashier hand-overs are left out of the row above to keep it to the four legs a
teller sees, and stated here when they happened rather than silently folded in. -->
@if ((todayInterCashierIn ?? 0) > 0 || (todayInterCashierOut ?? 0) > 0) {
<p class="closing-note">
This already counts cash swapped with another cashier &mdash; ₹{{ todayInterCashierIn | number: '1.2-2' }} taken
from them, ₹{{ todayInterCashierOut | number: '1.2-2' }} given to them.
</p>
}
}
@if (!showGl) {
<div class="pg-card">
@if (noteRows.length > 0 || ddCashTotal > 0) {
<div class="pos-section-label">Notes</div>
<table class="pg-table">
<thead>
<tr>
<th>Note</th>
<!-- Counted in pieces, because that is what a teller does with notes. Nine columns of
pieces-and-amount for every leg was a reconciliation nobody could read at a
counter; these five are the sentence the day actually makes: this many came in
from the vault, this many over the counter, this many went out, this many are
left, and that is worth this much. -->
<th class="num col-in">Came in</th>
<th class="num col-out">Went out</th>
<th class="num col-net">Left with you</th>
<th class="num col-net">Worth</th>
</tr>
</thead>
<tbody>
@for (r of noteRows; track r.denominationId) {
<tr>
<td>
<span class="pg-chip">₹{{ r.denominationValue }}</span>
</td>
<!-- Vault and counter added together: to a teller both are notes arriving, and
splitting them made the reader do the sum. A dash rather than a zero where
nothing happened, so the eye passes over it. -->
<td class="num col-in">{{ r.vaultInCount + r.depositCount || '—' }}</td>
<td class="num col-out">{{ r.withdrawalCount + r.settledOutCount || '—' }}</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount }}
</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount >= 0 ? '' : '−' }}₹{{
(r.netAmount < 0 ? -r.netAmount : r.netAmount) | number: '1.2-2'
}}
</td>
</tr>
}
<!-- Cash taken for a Demand Draft, on the deposit side where it belongs. It has no
denomination to sit against -- the notes were never itemised -- so the piece
counts show a dash rather than a zero, which would read as "no notes". -->
@if (ddCashTotal > 0) {
<tr>
<td>
<span class="pg-chip">Notes not listed</span>
<span class="closing-row__hint"> {{ ddCashCount }} cash DD{{ ddCashCount === 1 ? '' : 's' }} </span>
</td>
<td class="num col-in">—</td>
<td class="num col-out">—</td>
<td class="num col-net">—</td>
<td class="num col-net" style="color: #16a34a">₹{{ ddCashTotal | number: '1.2-2' }}</td>
</tr>
}
</tbody>
<!-- Totals, so the table reconciles against the summary above rather than having to be
added up by hand — and so the deposit column visibly accounts for the DD row. -->
<tfoot>
<tr>
<td>Total</td>
<td class="num col-in">{{ noteVaultInCount + noteDepositCount || '—' }}</td>
<td class="num col-out">{{ noteWithdrawalCount + noteSettledOutCount || '—' }}</td>
<td class="num col-net">{{ noteNetCount }}</td>
<td class="num col-net">
{{ noteNetAmount >= 0 ? '' : '−' }}₹{{
(noteNetAmount < 0 ? -noteNetAmount : noteNetAmount) | number: '1.2-2'
}}
</td>
</tr>
</tfoot>
</table>
}
@if (coinRows.length > 0) {
<div class="pos-section-label pos-section-label--coin">Coins</div>
<table class="pg-table">
<thead>
<tr>
<th>Coin</th>
<th class="num col-in">Came in</th>
<th class="num col-out">Went out</th>
<th class="num col-net">Left with you</th>
<th class="num col-net">Worth</th>
</tr>
</thead>
<tbody>
@for (r of coinRows; track r.denominationId) {
<tr>
<td>
<span class="pg-chip pg-chip--coin">₹{{ r.denominationValue }}</span>
</td>
<!-- Vault and counter added together: to a teller both are notes arriving, and
splitting them made the reader do the sum. A dash rather than a zero where
nothing happened, so the eye passes over it. -->
<td class="num col-in">{{ r.vaultInCount + r.depositCount || '—' }}</td>
<td class="num col-out">{{ r.withdrawalCount + r.settledOutCount || '—' }}</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount }}
</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount >= 0 ? '' : '−' }}₹{{
(r.netAmount < 0 ? -r.netAmount : r.netAmount) | number: '1.2-2'
}}
</td>
</tr>
}
</tbody>
</table>
}
<!-- Not shown when a Demand Draft row is on the table: there is something to read then, and
an "empty" notice under it contradicts it. -->
@if (loaded && rows.length === 0 && exchangeRows.length === 0 && ddCashTotal === 0) {
<!-- A day with no counter work is not an empty report: the cashier is still holding
whatever they held yesterday, and that balance is shown in the strip above. Saying
only "no data" here read as though the drawer were empty. -->
<div class="pg-empty">
@if (scopedToCashier && hasTodayLegs && expectedInDrawer !== 0) {
No cash crossed the counter on this date. The drawer should hold ₹{{ expectedInDrawer | number: '1.2-2' }}
from the day's vault and settlement legs.
} @else {
No cash denomination data for this date.
}
</div>
}
</div>
}
<!-- end !showGl -->
<!-- GL View Panel -->
@if (showGl) {
<div class="gl-card">
<!-- Card Header -->
<div class="gl-card__header">
<div class="gl-card__header-left">
<div class="gl-card__icon">
<mat-icon>account_balance</mat-icon>
</div>
<div>
<div class="gl-card__title">GL Entries</div>
<div class="gl-card__sub">Cashier transaction ledger for {{ date }}</div>
</div>
</div>
<div class="gl-card__totals">
<div class="gl-card__total gl-card__total--dr">
<span class="gl-card__total-label">Total DR</span>
<span class="gl-card__total-value">₹{{ glTotalDr | number: '1.2-2' }}</span>
</div>
<div class="gl-card__total-sep">|</div>
<div class="gl-card__total gl-card__total--cr">
<span class="gl-card__total-label">Total CR</span>
<span class="gl-card__total-value">₹{{ glTotalCr | number: '1.2-2' }}</span>
</div>
</div>
</div>
<!-- Filters Bar -->
<div class="gl-card__filters">
<label class="pg-field">
<span>Teller Head</span>
<select class="pg-select" [(ngModel)]="selectedTellerId" (change)="onTellerChange()">
<option [ngValue]="null">All Tellers</option>
@for (t of tellers; track t.id) {
<option [ngValue]="t.id">{{ t.name }}</option>
}
</select>
</label>
@if (selectedTellerId) {
<label class="pg-field">
<span>Cashier</span>
<select class="pg-select" [(ngModel)]="selectedCashierId" (change)="onCashierChange()">
<option [ngValue]="null">All Cashiers</option>
@for (c of cashiers; track c.id) {
<option [ngValue]="c.id">{{ c.staffName }}</option>
}
</select>
</label>
}
<button type="button" class="pg-btn pg-btn--primary" (click)="loadGl()" [disabled]="glLoading">
<mat-icon>refresh</mat-icon> Refresh
</button>
</div>
<!-- Table -->
@if (glLoading) {
<div class="pg-empty">Loading...</div>
} @else if (glRows.length === 0 && glLoaded) {
<div class="pg-empty">No entries found for this date and filter.</div>
} @else if (glRows.length > 0) {
<table class="pg-table gl-table">
<thead>
<tr>
<th class="gl-th--voucher">Voucher No</th>
<th class="gl-th--date">Date</th>
<th>Debit</th>
<th>Credit</th>
<th class="gl-th--pt">Payment Type</th>
<th class="gl-th--teller">Teller</th>
<th class="num gl-th--dr">DR (₹)</th>
<th class="num gl-th--cr">CR (₹)</th>
</tr>
</thead>
<tbody>
@for (r of glRows; track $index) {
<tr [class.gl-row--dr]="r.dr > 0" [class.gl-row--cr]="r.cr > 0">
<td class="gl-voucher">
@if (r.txnSource === 'GL') {
<span class="gl-badge">GL</span>
}
{{ r.voucherNo }}
</td>
<td class="gl-date">
<div>{{ r.txnDate }}</div>
@if (r.txnTime) {
<div class="gl-time">{{ r.txnTime }}</div>
}
</td>
<!-- Both legs of the voucher, the same pair My Vouchers shows — not just the one
side this row's own denomination breakdown happens to know about. Falls back
to that one side (acNo/acName) when the voucher predates m_voucher_line. -->
<td class="gl-acname">
<div>{{ r.debitParty || r.acName }}</div>
@if (r.debitAccount || r.acNo) {
<div class="gl-acno">{{ r.debitAccount || r.acNo }}</div>
}
</td>
<td class="gl-acname">
@if (r.creditParty) {
<div>{{ r.creditParty }}</div>
@if (r.creditAccount) {
<div class="gl-acno">{{ r.creditAccount }}</div>
}
} @else {
<span class="gl-pt-none">—</span>
}
</td>
<td class="gl-pt">
@if (r.paymentType) {
<span class="gl-pt-badge">{{ r.paymentType }}</span>
} @else {
<span class="gl-pt-none">—</span>
}
</td>
<td class="gl-teller">{{ r.tellerName || '—' }}</td>
<td class="num gl-amt--dr">
@if (r.dr > 0) {
₹{{ r.dr | number: '1.2-2' }}
}
</td>
<td class="num gl-amt--cr">
@if (r.cr > 0) {
₹{{ r.cr | number: '1.2-2' }}
}
</td>
</tr>
}
</tbody>
<tfoot>
<tr class="gl-foot">
<td colspan="6" class="gl-foot__label">Total (All Pages)</td>
<td class="num gl-amt--dr gl-foot__val">₹{{ glTotalDr | number: '1.2-2' }}</td>
<td class="num gl-amt--cr gl-foot__val">₹{{ glTotalCr | number: '1.2-2' }}</td>
</tr>
</tfoot>
</table>
<mat-paginator
[length]="glTotal"
[pageSize]="glPageSize"
[pageSizeOptions]="glPageSizeOptions"
[pageIndex]="glOffset / glPageSize"
showFirstLastButtons
(page)="onGlPageChange($event)"
></mat-paginator>
}
</div>
}
@if (!showGl && exchangeRows.length > 0) {
<div class="pg-card">
<div class="closing-exchange-head">
<mat-icon>swap_horiz</mat-icon>
<div>
<div class="closing-exchange-title">Cash Exchanges</div>
<div class="closing-exchange-sub">Denomination swaps recorded on this date (net cash impact: ₹0.00)</div>
</div>
</div>
@if (exchangeNoteRows.length > 0) {
<div class="pos-section-label">Notes</div>
<table class="pg-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num col-in">Received Pcs</th>
<th class="num col-in">Received Amt</th>
<th class="num col-out">Given Pcs</th>
<th class="num col-out">Given Amt</th>
<th class="num col-net">Net Pcs</th>
<th class="num col-net">Net Amount</th>
</tr>
</thead>
<tbody>
@for (r of exchangeNoteRows; track r.denominationId) {
<tr>
<td>
<span class="pg-chip">₹{{ r.denominationValue }}</span>
</td>
<td class="num col-in">{{ r.receivedCount }}</td>
<td class="num col-in">₹{{ r.receivedAmount | number: '1.2-2' }}</td>
<td class="num col-out">{{ r.givenCount }}</td>
<td class="num col-out">₹{{ r.givenAmount | number: '1.2-2' }}</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount > 0 ? '+' : '' }}{{ r.netCount }}
</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount >= 0 ? '' : '−' }}₹{{
(r.netAmount < 0 ? -r.netAmount : r.netAmount) | number: '1.2-2'
}}
</td>
</tr>
}
</tbody>
</table>
}
@if (exchangeCoinRows.length > 0) {
<div class="pos-section-label pos-section-label--coin">Coins</div>
<table class="pg-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num col-in">Received Pcs</th>
<th class="num col-in">Received Amt</th>
<th class="num col-out">Given Pcs</th>
<th class="num col-out">Given Amt</th>
<th class="num col-net">Net Pcs</th>
<th class="num col-net">Net Amount</th>
</tr>
</thead>
<tbody>
@for (r of exchangeCoinRows; track r.denominationId) {
<tr>
<td>
<span class="pg-chip pg-chip--coin">₹{{ r.denominationValue }}</span>
</td>
<td class="num col-in">{{ r.receivedCount }}</td>
<td class="num col-in">₹{{ r.receivedAmount | number: '1.2-2' }}</td>
<td class="num col-out">{{ r.givenCount }}</td>
<td class="num col-out">₹{{ r.givenAmount | number: '1.2-2' }}</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount > 0 ? '+' : '' }}{{ r.netCount }}
</td>
<td
class="num col-net"
[style.color]="r.netCount > 0 ? '#16a34a' : r.netCount < 0 ? '#dc2626' : '#94a3b8'"
>
{{ r.netCount >= 0 ? '' : '−' }}₹{{
(r.netAmount < 0 ? -r.netAmount : r.netAmount) | number: '1.2-2'
}}
</td>
</tr>
}
</tbody>
</table>
}
</div>
}
</div>