fix(shared): exclude payments from balance when tag filter is active

This commit is contained in:
2026-04-13 05:41:00 +10:00
parent 1b561af9e9
commit 07b8c1ef16
2 changed files with 21 additions and 12 deletions
+3
View File
@@ -299,6 +299,9 @@ export default function SharedPage() {
{addingParticipant && <AddParticipantForm onDone={() => setAddingParticipant(false)} />}
{/* Balance cards */}
{realTagIds.length === 0 && tagIds.includes("untagged") ? null : realTagIds.length > 0 && (
<p className="text-xs text-zinc-500 mb-2">Showing split totals for selected tag payments excluded (payments settle overall debt, not per-tag)</p>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{balLoading ? (
<p className="text-zinc-500 text-sm col-span-3">Loading balances...</p>
+18 -12
View File
@@ -287,10 +287,25 @@ export async function getParticipantBalances(ownerId: number, tagIds?: number[])
tagFilter = `AND EXISTS (SELECT 1 FROM transaction_tags tt WHERE tt.transaction_id = t.id AND tt.tag_id = ANY($2::int[]))`;
}
// Payments settle the total relationship between two people, not a specific tag.
// Only subtract payments when viewing the unfiltered total; with a tag filter
// active, show the raw split amount for that tag context only.
const paymentsJoin = tagIds?.length ? "" : `
LEFT JOIN (
SELECT
CASE WHEN sp.from_participant_id != $1 THEN sp.from_participant_id ELSE sp.to_participant_id END AS pid,
SUM(CASE WHEN sp.to_participant_id = $1 THEN sp.amount ELSE -sp.amount END) AS net_paid
FROM split_payments sp
WHERE sp.from_participant_id = $1 OR sp.to_participant_id = $1
GROUP BY pid
) payments ON payments.pid = p.id`;
const paymentsSelect = tagIds?.length ? "" : "- COALESCE(payments.net_paid, 0)::numeric(12,2)";
const paymentsGroup = tagIds?.length ? "" : ", payments.net_paid";
return queryRaw<ParticipantBalance>(`
SELECT p.id, p.name,
COALESCE(SUM(splits.signed_amount), 0)::numeric(12,2)
- COALESCE(payments.net_paid, 0)::numeric(12,2) AS total_owed,
${paymentsSelect} AS total_owed,
COALESCE(SUM(splits.split_count), 0)::int AS unsettled_count
FROM participants p
@@ -317,19 +332,10 @@ export async function getParticipantBalances(ownerId: number, tagIds?: number[])
WHERE ts.participant_id = $1 AND COALESCE(t.owner_id, s.owner_id) != $1
${tagFilter}
) splits ON splits.pid = p.id
-- Net payments always unfiltered (payments are against total debt, not per-tag)
LEFT JOIN (
SELECT
CASE WHEN sp.from_participant_id != $1 THEN sp.from_participant_id ELSE sp.to_participant_id END AS pid,
SUM(CASE WHEN sp.to_participant_id = $1 THEN sp.amount ELSE -sp.amount END) AS net_paid
FROM split_payments sp
WHERE sp.from_participant_id = $1 OR sp.to_participant_id = $1
GROUP BY pid
) payments ON payments.pid = p.id
${paymentsJoin}
WHERE p.id != $1
GROUP BY p.id, p.name, payments.net_paid
GROUP BY p.id, p.name ${paymentsGroup}
ORDER BY p.name
`, params);
}