feat(tasks): show payout amount on the Closure Review popup while PENDING
build-run / Run Lint, Build and Deploy (push) Canceled after 0s
Publish Image in Docker Hub / build (push) Canceled after 0s
Playwright E2E Tests / Run Playwright Tests (push) Canceled after 0s
Tests / testRigor (push) Canceled after 0s

The manager's Closure Review dialog only showed the amount once the request
reached APPROVED or later -- closing_amount/rounding_amount/cashier_disbursed_amount
were null on a PENDING record, since the backend only filled them in at
approve() time. A manager reviewing a still-pending closure saw client name,
payout mode and token, but no figure to actually approve.

The backend now populates those columns at submission (interest is posted
for real at that point, so the figure is already known -- see the matching
backend commit), so this just stops gating the amount card on
closing_amount != null and moves it to the top of the dialog as "Payout
Amount", shown for every status.

Also added: Closure Date ("Interest Posted As On"), Transaction Id once
close() has run, and a "Handed Over By Cashier" section once disbursed --
so the full lifecycle of a closure is visible from one dialog instead of
needing to cross-reference the account's own transaction history.
This commit is contained in:
2026-09-03 10:47:07 +05:30
parent 17942509de
commit 6442562df0
2 changed files with 64 additions and 23 deletions
@@ -58,6 +58,41 @@
</div>
</div>
<!-- Payout amount -- the interest is posted onto the account the moment the clerk raises the
request (see clerk-closure-request.service#postClosureInterest), so this is the account's
real, final balance, not an estimate. Approval re-confirms the identical figure; it does
not recompute a different one. Shown first because it is what a manager reviews for. -->
<div class="crv-card crv-card--approved">
<div class="crv-section-title">
<mat-icon>payments</mat-icon>
Payout Amount
</div>
<div class="crv-detail-grid">
<div class="crv-detail-item">
<span class="crv-detail-label">Exact Closing Amount</span>
<span class="crv-detail-value crv-amount">₹{{ request.closing_amount | number: '1.2-2' }}</span>
</div>
@if (request.rounding_amount && request.rounding_amount !== 0) {
<div class="crv-detail-item">
<span class="crv-detail-label">Rounding ({{ request.rounding_amount > 0 ? 'up' : 'down' }})</span>
<span class="crv-detail-value"
>{{ request.rounding_amount > 0 ? '+' : '' }}₹{{ request.rounding_amount | number: '1.2-2' }}</span
>
</div>
}
<div class="crv-detail-item">
<span class="crv-detail-label">Cash to Hand Over</span>
<span class="crv-detail-value crv-amount">₹{{ request.cashier_disbursed_amount | number: '1.2-2' }}</span>
</div>
@if (request.voucher_no) {
<div class="crv-detail-item">
<span class="crv-detail-label">Voucher No</span>
<span class="crv-detail-value cell-mono">{{ request.voucher_no }}</span>
</div>
}
</div>
</div>
<!-- Closure details -->
<div class="crv-card">
<div class="crv-section-title">
@@ -77,6 +112,16 @@
<span class="crv-detail-label">Token No</span>
<span class="crv-detail-value cell-mono">{{ request.tok_no || '—' }}</span>
</div>
<div class="crv-detail-item">
<span class="crv-detail-label">Interest Posted As On</span>
<span class="crv-detail-value">{{ formatDate(request.closure_date) }}</span>
</div>
@if (request.txn_id) {
<div class="crv-detail-item">
<span class="crv-detail-label">Transaction Id</span>
<span class="crv-detail-value cell-mono">{{ request.txn_id }}</span>
</div>
}
@if (request.description) {
<div class="crv-detail-item crv-detail-item--full">
<span class="crv-detail-label">Reason / Narration</span>
@@ -86,36 +131,22 @@
</div>
</div>
<!-- Computed amounts (once approved) -->
@if (request.closing_amount != null) {
<div class="crv-card crv-card--approved">
<!-- Cashier hand-over (once disbursed) -->
@if (request.status === 'CLOSED' && request.disbursed_at) {
<div class="crv-card">
<div class="crv-section-title">
<mat-icon>calculate</mat-icon>
Computed at Approval
<mat-icon>storefront</mat-icon>
Handed Over By Cashier
</div>
<div class="crv-detail-grid">
<div class="crv-detail-item">
<span class="crv-detail-label">Exact Closing Amount</span>
<span class="crv-detail-value crv-amount">₹{{ request.closing_amount | number: '1.2-2' }}</span>
<span class="crv-detail-label">Cashier</span>
<span class="crv-detail-value">{{ request.disbursed_by || '—' }}</span>
</div>
@if (request.rounding_amount && request.rounding_amount !== 0) {
<div class="crv-detail-item">
<span class="crv-detail-label">Rounding ({{ request.rounding_amount > 0 ? 'up' : 'down' }})</span>
<span class="crv-detail-value"
>{{ request.rounding_amount > 0 ? '+' : '' }}₹{{ request.rounding_amount | number: '1.2-2' }}</span
>
</div>
}
<div class="crv-detail-item">
<span class="crv-detail-label">Cash to Hand Over</span>
<span class="crv-detail-value crv-amount">₹{{ request.cashier_disbursed_amount | number: '1.2-2' }}</span>
<span class="crv-detail-label">Disbursed At</span>
<span class="crv-detail-value">{{ formatDateTime(request.disbursed_at) }}</span>
</div>
@if (request.voucher_no) {
<div class="crv-detail-item">
<span class="crv-detail-label">Voucher No</span>
<span class="crv-detail-value cell-mono">{{ request.voucher_no }}</span>
</div>
}
</div>
</div>
}
@@ -128,6 +128,16 @@ export class ClosureReviewDialogComponent implements OnInit {
return n.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
formatDate(val: any): string {
if (!val) return '—';
try {
const d = new Date(val);
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
} catch {
return String(val);
}
}
formatDateTime(val: any): string {
if (!val) return '—';
try {