Skip to content

Honor user's manual category override (trackingCategory)#1

Open
ItzikEzra wants to merge 1 commit into
arsolutioner:mainfrom
ItzikEzra:fix/use-tracking-category
Open

Honor user's manual category override (trackingCategory)#1
ItzikEzra wants to merge 1 commit into
arsolutioner:mainfrom
ItzikEzra:fix/use-tracking-category

Conversation

@ItzikEzra
Copy link
Copy Markdown

@ItzikEzra ItzikEzra commented Apr 21, 2026

Problem

When a user manually re-classifies a transaction inside the RiseUp app (via the "Change category" action), the override is persisted by RiseUp on Transaction.trackingCategory. The auto-classification stays on Transaction.expense.

Today the CLI only reads tx.expense, so any manual override is invisible to the CLI. Users see one set of numbers in the app and a different set from:

  • riseup spending
  • riseup transactions
  • riseup trends
  • riseup income --salary-only

This makes the CLI unreliable for anyone who curates their own categorization in the app.

Example (hypothetical)

A user moves a restaurant from the default "Leisure" category to "Eating Out" inside the RiseUp app. Afterwards:

  • The app shows the correct "Eating Out" total — the override is respected.
  • The CLI still shows the old "Leisure" total — the override is ignored.

For a month with several re-classifications, the discrepancy can be hundreds of shekels.

Second wrinkle: internal tracking flags

trackingCategory is also used by RiseUp for internal flags that are not user-chosen categories. The most common one is "blacklist", which RiseUp applies to transactions it wants to exclude from the budget total (typically peer-to-peer BIT transfers matched by a matchingPredicates rule keyed on businessName).

Those aren't categories the user picked — showing "blacklist" in riseup spending output is confusing. So the helper treats that (and any future internal flag added to a small allowlist) as a signal to fall back to expense.

Fix

New shared helper src/client/categories.ts:

const INTERNAL_TRACKING_FLAGS = new Set(["blacklist"]);

export function getEffectiveCategory(tx: Transaction): string {
  const name = tx.trackingCategory?.name;
  if (name && !INTERNAL_TRACKING_FLAGS.has(name)) return name;
  return tx.expense ?? "";
}

All category-reading call sites now route through the helper:

  • src/commands/transactions.ts--category filter, JSON output, table row
  • src/commands/spending.ts--category filter and group-by-category key
  • src/commands/trends.ts — per-month category aggregation
  • src/commands/income.ts--salary-only filter and outputs

src/commands/manage.ts is intentionally left using tx.expense for the rename API call, since the backend rename endpoint expects the auto category. The unclassified command's display output does use the effective category for consistency.

Why this shape

  • Minimal surface area — one helper, used at each read site.
  • Backwards compatible — when trackingCategory is absent, behavior is identical.
  • Type-safe — trackingCategory is already modeled in client/types.ts.
  • Internal flags are explicit and extensible — just add names to the set.

When a user manually reclassifies a transaction in the RiseUp app
(e.g., moves "גן הפקאן" from "פנאי" to "אוכל בחוץ"), the override is
stored on `Transaction.trackingCategory.name`. The auto-classification
remains on `Transaction.expense`.

Before this change the CLI read only `tx.expense`, so manual overrides
were invisible. `riseup spending`, `riseup transactions`, `riseup trends`,
and `riseup income` all showed numbers that disagreed with the app.

This adds a shared `getEffectiveCategory(tx)` helper that returns
`tx.trackingCategory.name` when set, falling back to `tx.expense`.
All category-reading call sites now route through the helper.

`manage.ts rename` is left alone — it forwards the auto category to
the rename API on purpose; only the display side is updated.
@ItzikEzra ItzikEzra force-pushed the fix/use-tracking-category branch from a7755f3 to f9b5f96 Compare April 21, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant