Skip to content

1.10.0

Released: 2026-06-10

Highlights

  • Expense categories can now carry an icon and be grouped one level deep into subcategories.
  • Credit cards are now first-class: track expenses paid on a credit card by selecting it as the expense's payment account.

Added

  • Expense category icons & subcategoriesExpenseCategory gains a free-form icon string and a single-level parentId, plus an expenseCategoryTree query that returns top-level categories with their children. See Expense Category Icons, Subcategories & Credit Cards.
  • CREDIT_CARD account type — new AccountType.CREDIT_CARD (a liability, auto-coded in the 2400 range) that can be selected as an expense payment account.

Changed

  • validateExpenseAccounts now accepts cash, bank, or credit-card accounts for an expense's paymentAccountId. Customer payments and receipts remain cash/bank only.

Fixed

  • None.

Migrations

In down_revision order (tip was add_recurring_id_to_invoices):

  1. add_icon_to_expense_categoriesalembic/versions/20260610_add_icon_to_expense_categories.py (adds expense_categories.icon).
  2. add_parent_id_to_exp_categoriesalembic/versions/20260610_add_parent_id_to_expense_categories.py (adds expense_categories.parent_id FK + index).
  3. exp_category_no_self_parentalembic/versions/20260610_expense_category_no_self_parent.py (NULLs any existing self-parented rows and adds a CHECK constraint forbidding parent_id = id).

No migration for AccountType.CREDIT_CARD: it is persisted as an integer (IntEnum) and the member is appended, so existing rows are unaffected. No one-off scripts.

Frontend impact

  • Category icons: send icon (any string) on createExpenseCategory / updateExpenseCategory; read it back on ExpenseCategory and ExpenseCategoryLandingPage. Meaning is frontend-defined.
  • Subcategories: pass parentId to nest a category one level under another. The backend rejects nesting under a category that already has a parent, re-parenting a category that already has children, and self-parenting. Use expenseCategoryTree for grouped lists; children holds a category's subcategories.
  • Credit cards: create one via createAccount with accountType: CREDIT_CARD, then select it as an expense's paymentAccountId.

Versioning notes

  • Bumped with uv run python scripts/bump_version.py 1.10.0 --changelog-stub.
  • MINOR (additive schema only) — no deploy-order constraint beyond applying the two expense-category migrations.