The EXPENSE CATEGORIES table on the Income vs Expense tab has three defects that compound. As rendered today (range Jun 2 – Sep 2):
Rent/Mortgage $2,600.00 -38%
Home Insurance -$5.06 0%
Parking -$21.00 0%
...
Restaurants -$2,028.89 29%
Education -$3,500.00 51%
1. Sorted backwards
return result.sort((a, b) => b.total - a.total); // src/queries/reports.ts:333
Expense totals are negative, so descending signed order is ascending magnitude. The largest expense (Education, $3,500) lands at the bottom and the smallest (Home Insurance, $5.06) at the top. Correct for the income list, inverted for expenses. The Spending tab sorts the same data descending, so the two tabs disagree on ordering as well as value.
2. Percentages divide by a signed net that is never shown
const denominator = rowIsIncome ? totalIncome : totalExpenses;
percentOfTotal: denominator !== 0 ? (row.total / denominator) * 100 : 0; // :319-329
totalExpenses here is the signed sum of category totals = -$6,906.78. Check: Education -3500 / -6906.78 = 51%; Rent 2600 / -6906.78 = -38%. So the visible shares are fractions of a number that appears nowhere on screen and bears no relation to the $28,494.34 tile directly above. Individually they exceed 100% in aggregate (51 + 29 + 27 = 107) and one is negative.
The code documents an assumption the data violates:
// A category total and its pool total share the same sign, so the ratio is a positive share.
3. Uncategorized is dropped from the breakdown
isNotNull(transactions.categoryId) (:308) excludes null-category rows from the grouping, while the tile above includes them. 61% of spending ($15,589.86) is missing from the table meant to explain the total. The same applies to the income side: the three listed sources total $15,325.02 against a $33,185.24 tile.
Also
Rent/Mortgage renders positive among negatives because non-income categories sum the raw signed amount (:304), so a category that nets positive flips. Nothing on screen indicates this is anomalous.
Proposal
- Sort by
Math.abs(total) descending, or normalize expense totals to positive magnitudes for display and sort those.
- Compute
percentOfTotal against the same figure shown in the Total Expenses tile.
- Include an "Uncategorized" row rather than filtering it out — it is the largest single line.
- Flag sign-flipped categories rather than rendering them silently among their opposites.
The EXPENSE CATEGORIES table on the Income vs Expense tab has three defects that compound. As rendered today (range Jun 2 – Sep 2):
1. Sorted backwards
Expense totals are negative, so descending signed order is ascending magnitude. The largest expense (Education, $3,500) lands at the bottom and the smallest (Home Insurance, $5.06) at the top. Correct for the income list, inverted for expenses. The Spending tab sorts the same data descending, so the two tabs disagree on ordering as well as value.
2. Percentages divide by a signed net that is never shown
totalExpenseshere is the signed sum of category totals = -$6,906.78. Check: Education-3500 / -6906.78 = 51%; Rent2600 / -6906.78 = -38%. So the visible shares are fractions of a number that appears nowhere on screen and bears no relation to the $28,494.34 tile directly above. Individually they exceed 100% in aggregate (51 + 29 + 27 = 107) and one is negative.The code documents an assumption the data violates:
3. Uncategorized is dropped from the breakdown
isNotNull(transactions.categoryId)(:308) excludes null-category rows from the grouping, while the tile above includes them. 61% of spending ($15,589.86) is missing from the table meant to explain the total. The same applies to the income side: the three listed sources total $15,325.02 against a $33,185.24 tile.Also
Rent/Mortgagerenders positive among negatives because non-income categories sum the raw signed amount (:304), so a category that nets positive flips. Nothing on screen indicates this is anomalous.Proposal
Math.abs(total)descending, or normalize expense totals to positive magnitudes for display and sort those.percentOfTotalagainst the same figure shown in the Total Expenses tile.