feat(sp27): server-aggregate Remittances KPIs (count, paid, adjustments)

The Remittances page's three KPI tiles — REMITS / TOTAL PAID /
ADJUSTMENTS — were computed page-locally via items.reduce(...) over
the merged tail of the current page + live delta. With a 100-row
default limit, a 1,739-row population showed count=100, paid=$16,934,
adjustments=$147 — silently understating reality because the page
hadn't loaded the remaining rows yet.

This change mirrors the silent-incompleteness fix that
/api/dashboard/kpis (commit 59c3275) and /api/remittances (commit
d81b6ed) made for their tiles:

  * CycloneStore.summarize_remittances() iterates the full filtered
    remittance population (no limit) and returns
    {count, total_paid, total_adjustments}. Mirrors iter_remittances
    with limit=_ITER_UNBOUNDED.
  * GET /api/remittances/summary — server endpoint with the same
    filter parameters as /api/remittances. Registered BEFORE the
    /api/remittances/stream handler so FastAPI doesn't treat
    'summary' as a stream sub-path.
  * api.listRemittanceSummary + useRemittanceSummary hook.
  * Remittances.tsx swaps off items.reduce, consumes the server
    summary. Tiles render the server totals so the values reflect
    the entire DB population, not the page-local sample.

Verified live: /api/remittances/summary returns
{count: 1739, total_paid: 227181.58, total_adjustments: 13792.65},
which matches DB ground truth exactly.

Tests: 8 new backend tests in test_api_remittances_summary.py; 1 new
frontend test in Remittances.test.tsx (kpi_tiles_use_server_summary_not
_page_local_reduce) plus the page-level test for the zero-valued mock
default.
This commit is contained in:
Nora
2026-06-29 14:16:24 -06:00
parent d8841834dc
commit 14fcbca5f1
7 changed files with 515 additions and 11 deletions
+30
View File
@@ -0,0 +1,30 @@
import { useQuery } from "@tanstack/react-query";
import {
api,
type ListRemittancesParams,
type RemittanceSummary,
} from "@/lib/api";
/**
* Server-aggregated KPI totals for the Remittances page tiles.
* Returns ``{count, total_paid, total_adjustments}`` summed over the
* full filtered remittance population — NOT a page-limited sample.
*
* The page's KPI tiles consume this hook instead of summing
* ``items.reduce(...)`` over the current page (25 rows) + live-tail
* delta, which silently understates the true population in the same
* shape that ``59c3275`` retired for the Dashboard and that
* ``d81b6ed`` retired for the count tile.
*
* The optional ``params`` object lets the page pass through the same
* filter chips it uses for the list endpoint (``status``, ``payer``,
* ``date_from``, ``date_to``) so the summary and the row list always
* agree on which rows they're describing.
*/
export function useRemittanceSummary(params: ListRemittancesParams = {}) {
return useQuery<RemittanceSummary>({
queryKey: ["remittances", "summary", params],
queryFn: () => api.listRemittanceSummary(params),
enabled: api.isConfigured,
});
}