diff --git a/src/features/accounting/CategorizeReview.tsx b/src/features/accounting/CategorizeReview.tsx index 80522ea..12a09d8 100644 --- a/src/features/accounting/CategorizeReview.tsx +++ b/src/features/accounting/CategorizeReview.tsx @@ -34,6 +34,11 @@ function buildQueue(txns: Txn[]): ReviewItem[] { const groups = new Map(); for (const t of txns) { if (t.category) continue; // defensive — the fetch already scopes to Uncategorized + // Only uncategorized SPENDING needs sorting. Salary (auto-detected income), + // any income, and transfers between accounts are not expenses to categorize. + if (t.salary === true) continue; + if (t.direction === "income") continue; + if (t.transfer === true) continue; const key = t.matchKey || t.title; if (!key) continue; const amount = parseFloat(t.amount) || 0; diff --git a/src/features/accounting/TransactionList.tsx b/src/features/accounting/TransactionList.tsx index 7439a46..9729d81 100644 --- a/src/features/accounting/TransactionList.tsx +++ b/src/features/accounting/TransactionList.tsx @@ -163,7 +163,15 @@ export function TransactionList() { const categoryOptions = useMemo(() => categoriesIn(all), [all]); const hasUncategorized = useMemo( - () => all.some((txn) => !txn.category && (txn.matchKey || txn.title)), + () => + all.some( + (txn) => + !txn.category && + txn.salary !== true && + txn.transfer !== true && + txn.direction !== "income" && + (txn.matchKey || txn.title), + ), [all], );