fix(branchday): count account closures in the day-close cash reconciliation

Day Close reported SHORT by exactly the amount a closure had paid out. The
"Cash paid out (approved withdrawals)" line only summed
m_clerk_withdrawal_request, so a closure -- which pays cash over the same
counter but lives in its own register -- was missing from the total. Expected
cash stayed high by what had actually been handed over, and a closure that
balanced perfectly read as a shortage.

Verified against live data: opening 145,304 + deposits 0 - slip withdrawals 0
- closure 12,999 = 132,305, which is exactly the counted branch total. Variance
goes from -12,999 to 0.

Sums cashier_disbursed_amount rather than closing_amount: the member's ledger
gives up the paisa-exact figure, but what physically leaves the drawer is that
figure rounded to whole rupees, and this total has to agree with a physical
count. Restricted to CLOSED rows dated on disbursed_at, since a closure takes
nothing out of the drawer until the cashier hands it over.

Note: dividend cash claims (m_share_dividend_claim_request) are also absent
from this total and will misreport the same way. Left alone deliberately --
that register splits a claim across loan/savings/cash with its own cash_status
column that nothing in the Java sets, so the right predicate cannot be
established from the code as it stands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 21:58:12 +05:30
co-authored by Claude Opus 5
parent 5a9a103832
commit 942bad6c8c
@@ -853,12 +853,29 @@ public class BranchDayRegisterApiResource {
// disbursements use APPROVED; withdrawal is the one register that doesn't) -- filtering on
// APPROVED alone silently excluded every completed withdrawal from this total, overstating
// the branch's expected cash by exactly what had actually been paid out that day.
final BigDecimal withdrawals = zero(jdbcTemplate.queryForObject("""
final BigDecimal slipWithdrawals = zero(jdbcTemplate.queryForObject("""
SELECT COALESCE(SUM(amount), 0) FROM m_clerk_withdrawal_request
WHERE status = 'WITHDRAWN'
AND COALESCE(txn_date, CAST(submitted_at AS DATE)) = ?
""", BigDecimal.class, date));
// Account closures pay cash over the same counter but live in their own register, so they
// were missing from this total entirely -- the branch's expected cash stayed high by exactly
// what had been handed over, reporting a shortage for a closure that balanced perfectly.
//
// cashier_disbursed_amount, not closing_amount: the member's ledger gives up the paisa-exact
// figure, but what physically leaves the drawer is that figure rounded to whole rupees, and
// this total has to agree with a physical count. Only CLOSED rows, dated on disbursed_at,
// since a closure takes nothing out of the drawer until the cashier hands it over (a
// nil-balance closure never reaches a cashier at all, hence the closure_date fallback).
final BigDecimal closureWithdrawals = zero(jdbcTemplate.queryForObject("""
SELECT COALESCE(SUM(cashier_disbursed_amount), 0) FROM m_clerk_closure_request
WHERE status = 'CLOSED'
AND COALESCE(CAST(disbursed_at AS DATE), closure_date) = ?
""", BigDecimal.class, date));
final BigDecimal withdrawals = slipWithdrawals.add(closureWithdrawals);
// Cash drawn from the vault to a cashier — more physical cash sitting in the branch.
final BigDecimal vaultIn = zero(jdbcTemplate.queryForObject("""
SELECT COALESCE(SUM(transfer_amount), 0) FROM m_cash_transfer