feat(cashier): add end-of-day report, my work, and cashier updates

This commit is contained in:
2026-09-08 10:38:59 +05:30
parent fbce73fd17
commit 035b11fa96
20 changed files with 3146 additions and 124 deletions
@@ -9,9 +9,8 @@
<div class="pg">
<header class="pg-head">
<div>
<div class="pg-eyebrow">CASHIER · REPORT</div>
<h1 class="pg-title">Closing Cash Report</h1>
<p class="pg-sub">Denomination-wise deposit, withdrawal and net cash position for the selected day.</p>
<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">
@@ -27,79 +26,71 @@
</div>
</header>
<!-- Shown whenever the day has any cash at all, not only when a denomination breakdown exists:
a cash Demand Draft is recorded without one, and gating on rows hid its money entirely. -->
@if (!showGl && (rows.length > 0 || ddCashTotal > 0)) {
<!-- Summary totals bar -->
<div class="closing-summary">
<!-- 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">Total Deposits (IN)</span>
<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">
incl. ₹{{ ddCashTotal | number: '1.2-2' }} not itemised ({{ ddCashCount }} cash DD{{
₹{{ 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">Total Withdrawals (OUT)</span>
<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"
[class.closing-summary__item--net-pos]="netTotal >= 0"
[class.closing-summary__item--net-neg]="netTotal < 0"
>
<!-- Named for what it is: the counter's own movement. It was labelled "Net Closing Cash",
which it is not -- it carries nothing forward from yesterday and leaves out vault and
inter-cashier legs, so it can differ from the drawer by a large amount. The real
closing figure is in the balance strip below. -->
<span class="closing-summary__label">Net Counter Movement</span>
<span class="closing-summary__value">₹{{ netTotal | number: '1.2-2' }}</span>
</div>
</div>
}
<!-- Opening → closing, the figure a physical count is checked against. Shown only for a
cashier-scoped report; a branch-wide one has no single drawer to open and close. -->
@if (!showGl && scopedToCashier && closingBalance !== null) {
<div class="closing-summary closing-summary--balance">
<div class="closing-summary__item">
<span class="closing-summary__label">Opening Balance</span>
<span class="closing-summary__value">₹{{ openingBalance | number: '1.2-2' }}</span>
</div>
<div class="closing-summary__sep">+</div>
<div
class="closing-summary__item"
[class.closing-summary__item--in]="(cashMovement ?? 0) >= 0"
[class.closing-summary__item--out]="(cashMovement ?? 0) < 0"
>
<span class="closing-summary__label">Cash Movement</span>
<span class="closing-summary__value">₹{{ cashMovement | number: '1.2-2' }}</span>
<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]="(closingBalance ?? 0) >= 0"
[class.closing-summary__item--net-neg]="(closingBalance ?? 0) < 0"
[class.closing-summary__item--net-pos]="expectedInDrawer >= 0"
[class.closing-summary__item--net-neg]="expectedInDrawer < 0"
>
<span class="closing-summary__label">Closing Balance</span>
<span class="closing-summary__value">₹{{ closingBalance | number: '1.2-2' }}</span>
<span class="closing-summary__label">Cash left with you now</span>
<span class="closing-summary__value">₹{{ expectedInDrawer | number: '1.2-2' }}</span>
</div>
</div>
@if (cashMovement !== null && netTotal !== cashMovement) {
<!-- The gap between the two is not an error: it is the vault, inter-cashier and exchange
traffic that never crossed the counter. Said plainly, because two different "net"
figures on one screen otherwise read as a bug. -->
<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">
Cash movement differs from counter movement by ₹{{ cashMovement - netTotal | number: '1.2-2' }} — vault,
inter-cashier and exchange legs that did not pass over the counter.
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>
}
}
@@ -111,13 +102,16 @@
<table class="pg-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num col-in">Deposit Pcs</th>
<th class="num col-in">Deposit Amt</th>
<th class="num col-out">Withdrawal Pcs</th>
<th class="num col-out">Withdrawal Amt</th>
<th class="num col-net">Net Pcs</th>
<th class="num col-net">Net Amount</th>
<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>
@@ -126,15 +120,16 @@
<td>
<span class="pg-chip">₹{{ r.denominationValue }}</span>
</td>
<td class="num col-in">{{ r.depositCount }}</td>
<td class="num col-in">₹{{ r.depositAmount | number: '1.2-2' }}</td>
<td class="num col-out">{{ r.withdrawalCount }}</td>
<td class="num col-out">₹{{ r.withdrawalAmount | number: '1.2-2' }}</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 > 0 ? '+' : '' }}{{ r.netCount }}
{{ r.netCount }}
</td>
<td
class="num col-net"
@@ -153,15 +148,13 @@
@if (ddCashTotal > 0) {
<tr>
<td>
<span class="pg-chip">Not itemised</span>
<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-in">₹{{ ddCashTotal | number: '1.2-2' }}</td>
<td class="num col-out">—</td>
<td class="num col-out">₹0.00</td>
<td class="num col-net">—</td>
<td class="num col-net" style="color: #16a34a">+₹{{ ddCashTotal | number: '1.2-2' }}</td>
<td class="num col-net" style="color: #16a34a">₹{{ ddCashTotal | number: '1.2-2' }}</td>
</tr>
}
</tbody>
@@ -170,13 +163,11 @@
<tfoot>
<tr>
<td>Total</td>
<td class="num col-in">{{ noteDepositCount }}</td>
<td class="num col-in">₹{{ noteDepositAmount | number: '1.2-2' }}</td>
<td class="num col-out">{{ noteWithdrawalCount }}</td>
<td class="num col-out">₹{{ noteWithdrawalAmount | number: '1.2-2' }}</td>
<td class="num col-net"></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 < 0 ? -noteNetAmount : noteNetAmount) | number: '1.2-2'
}}
</td>
@@ -190,13 +181,11 @@
<table class="pg-table">
<thead>
<tr>
<th>Denomination</th>
<th class="num col-in">Deposit Pcs</th>
<th class="num col-in">Deposit Amt</th>
<th class="num col-out">Withdrawal Pcs</th>
<th class="num col-out">Withdrawal Amt</th>
<th class="num col-net">Net Pcs</th>
<th class="num col-net">Net Amount</th>
<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>
@@ -205,15 +194,16 @@
<td>
<span class="pg-chip pg-chip--coin">₹{{ r.denominationValue }}</span>
</td>
<td class="num col-in">{{ r.depositCount }}</td>
<td class="num col-in">₹{{ r.depositAmount | number: '1.2-2' }}</td>
<td class="num col-out">{{ r.withdrawalCount }}</td>
<td class="num col-out">₹{{ r.withdrawalAmount | number: '1.2-2' }}</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 > 0 ? '+' : '' }}{{ r.netCount }}
{{ r.netCount }}
</td>
<td
class="num col-net"
@@ -236,9 +226,9 @@
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 && closingBalance !== null) {
No cash crossed the counter on this date. The drawer still holds ₹{{ closingBalance | number: '1.2-2' }},
carried forward.
@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.
}