Open Finapp
Reference

Transaction Types

Transaction types, adjustment logic, statistics.

Overview

Finapp supports three transaction types and one special category:

TypeTrnTypeDescription
Expense0Money spent
Income1Money received
Transfer2Money moved between wallets
Adjustment0 or 1Balance correction (Expense or Income with categoryId: 'adjustment')

Expense

A regular expense transaction. Decreases wallet balance.

  • Requires: wallet, category, amount
  • Shown in expense statistics
  • Amount displayed with minus sign

Income

A regular income transaction. Increases wallet balance.

  • Requires: wallet, category, amount
  • Shown in income statistics
  • Amount displayed with plus sign

Transfer

Money moved between two wallets. Does not affect income/expense statistics.

  • Requires: expense wallet, income wallet, amounts for each
  • Supports different currencies (e.g., USD wallet -> EUR wallet)
  • Uses system category transfer (categoryId: 'transfer')
  • Available only when 2+ wallets exist

Adjustment

Balance correction for a wallet. Affects wallet balance but is excluded from income/expense statistics.

How it works

Adjustment is not a separate transaction type. It is a regular Expense or Income transaction with the system category adjustment (categoryId === 'adjustment').

  • Expense + adjustment - decreases wallet balance (e.g., "lost money", correction down)
  • Income + adjustment - increases wallet balance (e.g., "found money", correction up)

Detection

Adjustment is identified by categoryId, not by type:

const isAdjustment = trn.categoryId === 'adjustment'

Behavior

  • Excluded from income/expense statistics in getTotal()
  • Excluded from recent categories in recentCategoriesIds
  • Excluded from last created transaction in lastCreatedTrnId
  • Included in wallet balance calculation (as a separate adjustment field in totals)
  • Not shown in the category tree (system category)
  • Available in the category selector modal (Command Palette)

System Categories

Two system categories are always present in the store and cannot be edited or deleted:

IDNamePurpose
transferTransferUsed by Transfer transactions
adjustmentAdjustmentUsed for balance corrections

These categories are:

  • Auto-added in useCategoriesStore.setCategories()
  • Excluded from categoriesRootIds (not shown in category tree)
  • Protected from editing/deletion in saveCategory() and deleteCategory()

Type Change Behavior

When a transaction's type changes, normalization happens client-side in trnToRow (services/powersync/transforms.ts) at write time - there is no server backend doing it:

  • → Transfer: trnToRow sets categoryId to 'transfer' and writes expenseWalletId/incomeWalletId/expenseAmount/incomeAmount; non-transfer fields (walletId, amount) are written as null
  • → Expense/Income: trnToRow writes walletId/amount; transfer-specific fields (expenseWalletId, incomeWalletId, expenseAmount, incomeAmount) are written as null
  • → Adjustment: no extra clearing - adjustment uses regular Expense/Income fields with categoryId: 'adjustment'

This ensures only the fields relevant to the current type carry non-null values in the local SQLite row and on the server.

Statistics

getTotal() returns:

FieldIncludes
incomeIncome transactions (excluding transfers and adjustments)
expenseExpense transactions (excluding transfers and adjustments)
adjustmentAdjustment transactions (affects wallet balance)
incomeTransfersTransfer income side
expenseTransfersTransfer expense side
sumincome - expense
sumTransfersincomeTransfers - expenseTransfers

Next Steps