Show net total of listed transactions above the table

Compras minus devoluciones, one figure per currency, excluding Tasa
Cero anchors (their cuotas are what count). Tooltip explains the math.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-07-03 20:17:55 -06:00
parent f2bcce702a
commit 5296230416

View File

@@ -194,6 +194,22 @@ export default function TransactionList({
: transactions;
const empty = visibleTransactions.length === 0 && !loading;
// Net spend of the listed transactions per currency: COMPRA DEVOLUCION,
// excluding Tasa Cero anchors (their cuotas are what count in the budget).
const totalsByCurrency = useMemo(() => {
const totals = new Map<string, number>();
for (const tx of visibleTransactions) {
if (tx.is_installment_anchor) continue;
if (tx.transaction_type !== 'COMPRA' && tx.transaction_type !== 'DEVOLUCION') continue;
const sign = tx.transaction_type === 'DEVOLUCION' ? -1 : 1;
totals.set(tx.currency, (totals.get(tx.currency) ?? 0) + sign * tx.amount);
}
// CRC first, then the rest alphabetically
return [...totals.entries()].sort(([a], [b]) =>
a === 'CRC' ? -1 : b === 'CRC' ? 1 : a.localeCompare(b),
);
}, [visibleTransactions]);
return (
<>
{/* Search + Add */}
@@ -213,6 +229,25 @@ export default function TransactionList({
</Button>
</div>
{/* Total of the listed transactions */}
{totalsByCurrency.length > 0 && (
<div
className="flex items-center justify-end gap-1.5 text-sm text-muted-foreground"
title="Compras menos devoluciones de las transacciones listadas. Excluye compras convertidas a Tasa Cero (cuentan sus cuotas)."
>
<span>
{visibleTransactions.length} transaccion{visibleTransactions.length === 1 ? '' : 'es'} · Total:
</span>
{totalsByCurrency.map(([currency, net], i) => (
<span key={currency} data-sensitive className="font-mono font-semibold text-foreground">
{i > 0 && <span className="text-muted-foreground font-normal mr-1.5">+</span>}
{net < 0 ? '+' : ''}
{formatAmount(net, currency)}
</span>
))}
</div>
)}
{/* Bulk action bar */}
{selected.size > 0 && (
<div className="hidden md:flex items-center gap-2 rounded-lg border bg-muted/40 px-3 py-2">