Release Changelog¶
Unreleased — Future changes¶
Features¶
- Year-over-Year "Until year" selector — The Year-over-Year analysis page now includes an "Until year" dropdown alongside "Years to compare". Users can select an end year (e.g. 2025) so the comparison covers the N years up to and including that year (e.g. last 3 years until 2025 = 2023, 2024, 2025). Defaults to the current year, preserving existing behaviour. The
/api/expenseand/api/incomeendpoints now accept an optionaluntil_yearquery parameter.
Documentation¶
- Reference documentation updated — All new features from this release cycle are now documented in the reference section:
- Dashboard — new page covering the recent-transactions widget, monthly cashflow widget, reconciliation widget, and account drill-down.
- Accounts — account notes, account reconciliation (badges, dashboard widget, API), and account-level transaction history.
- Analysis — Year-over-Year comparison charts (
/analysis/yoy). - Cash Flow — 30-day spending trends with sparklines and the monthly cashflow widget.
- Commodities — price export feature (Ledger / hLedger / Beancount formats, single-file and ZIP).
- Import — import preview (dry-run), import presets (save / load / delete), and built-in presets.
- Sync — async sync with job polling, sync history overlay, price & journal freshness indicators, Firefly III webhook integration, and Firefly III reconciliation (Labs).
- Configuration —
add_journal_path,enable_reconciliation,fireflyblock, andlabsblock.
Performance¶
- Materialized account balance summary table — Introduces
account_balancestable (migration v7) that stores pre-computed per-(account, commodity)balance totals updated atomically on every journal sync. account_balance.RefreshFromPostings(tx, postings)— computes sums in-memory from the already-loaded postings slice and atomically replaces the entireaccount_balancestable within the sync transaction. Forecast postings are excluded.account_balance.ByAccount(db, account)— O(1) index lookup for a single account's balance rows, replacing the previous O(N) fullpostingstable scan.account_balance.All(db)— returns all materialized balance rows ordered by account and commodity.SyncJournalnow callsRefreshFromPostingsinside the same DB transaction asposting.UpsertAll, guaranteeing the summary table is always consistent with the postings table.-
Unit tests added for
RefreshFromPostings,All, andByAccountcovering aggregation, forecast exclusion, idempotent replacement, and empty-slice clearing. -
SQL-level aggregation for balance queries — Introduces
query.GroupSum()as a reusable SQL aggregation primitive and improves theComputeBreakdownsalgorithm. query.GroupSum()— new method onQuerythat returns per(account, commodity)aggregatedSUM(amount)andSUM(quantity)rows directly from the database, as a lightweight alternative toAll()when only totals are needed.ComputeBreakdowns— internal O(A × N) loop replaced with a two-phase O(N + A × C) approach: postings are first grouped by effective account in O(N), then each breakdown group collects from the pre-built index (O(A × C) where C is the number of distinct leaf accounts).- Unit tests added for
GroupSumandComputeBreakdowns.
New features¶
- Epic 12: Account Reconciliation Status Tracker — Added account reconciliation metadata storage, reconciliation status badges, and a dashboard reconciliation summary widget.
- Subtask 12.1 (Backend – Account Reconciliation Metadata) — Added schema migration v6 with new
account_reconciliationtable (account,last_reconciled_date,frequency_days) and new APIs:GET /api/accounts/reconciliation,GET /api/accounts/:account/reconciliation, andPATCH /api/accounts/:account/reconciliation. Responses include computeddays_sinceandis_overdue. - Subtask 12.2 (Frontend – Reconciliation Status Badge) — Assets balance rows and account detail pages now show color-coded reconciliation badges ("Last reconciled: ..."). Clicking a badge opens reconciliation actions on the account detail page.
- Subtask 12.3 (Frontend – Reconciliation Dashboard Widget) — Dashboard now includes an "Account Reconciliation" widget with up-to-date/overdue counts and quick links to reconcile overdue accounts.
- Subtask 12.4 (Frontend & Backend – Optional Feature Toggle) — Reconciliation is now disabled by default and can be enabled via
enable_reconciliationin the configuration. All reconciliation-related UI components and API fetches are gated by this flag. -
Subtask 12.5 (Frontend – Navigation-free Reconciliation) — Reconciliation badges now open a global modal in-place rather than navigating away, preserving page state across Dashboard, Assets, and Account detail pages.
-
Epic: Import feature improvements (Subtask 1) — Added
POST /api/import/previewto parse CSV content in dry-run mode and return row-by-row preview data with validation status/error messages before committing anything to journal files. - Subtask 2 (Frontend preview table) — Added reusable
ImportPreviewTable.sveltewith per-row valid/invalid badges, error messages, and include/exclude checkboxes (plus select-all) for import previews. - Subtask 3 (Confirm-and-write flow) — Import page now saves only selected preview rows via
POST /api/editor/saveand shows loading state while confirming/writing. - Subtask 4 (Import preset CRUD API) — Added
GET/POST/DELETE /api/import/presetsbacked by a new SQLite model + migration for reusable import presets (name, column mappings, date format, default accounts, delimiter). - Subtask 5 (Preset selector UI) — Added
PresetSelector.svelteon the import page with preset dropdown and “Save Current” action. -
Subtask 6 (Built-in presets) — Added built-in import presets for common formats: Generic Bank CSV, Chase Credit Card CSV, SBI Account Statement CSV, and ICICI Credit Card CSV.
-
Epic 11: Year-over-Year Comparison Charts — Added backend multi-year series support and a new YoY analysis experience for comparing spending and income trends across calendar years.
- Subtask 11.1 (Backend – Multi-Year Expense/Income Data) —
GET /api/expenseandGET /api/incomenow accept an optionalyearsquery parameter (default1, max10) and returnmulti_yeardata shaped as{ "<year>": { month: { "YYYY-MM": amount }, total } }. Leap-day transactions are naturally included in February aggregates. - Subtask 11.2 (Frontend – YoY Comparison Chart) — Added reusable
YoYChart.sveltecomponent supporting line and grouped-bar modes with Jan–Dec alignment, year legends, and month-level hover tooltips across all selected years. -
Subtask 11.3 (Frontend – YoY Analysis Page) — Added
/analysis/yoypage with configurable year range (2–5 years), spending + income YoY charts, category YoY chart (line/bar toggle), computed YoY insights, and CSV export. -
Epic 4: Month-over-Month Spending Trends — Added rolling 30-day spending trend comparison to the Monthly Expenses page. Each expense category now shows its current 30-day total alongside the previous 30-day total, a variance amount, a colour-coded percentage change (↑ red / ↓ green), and a 6-month sparkline bar chart.
- Subtask 4.1 (Backend – Monthly Expense Trends) —
GetExpense()ininternal/server/expense.gonow computes and returns atrendsarray. Each entry containscategory,current_month,previous_month,variance, andvariance_pct(null when there are no previous-period expenses). Rolling windows: today−30 to today (current) and today−60 to today−30 (previous).Expenses:Taxpostings are excluded. Six unit tests added ininternal/server/expense_test.go. - Subtask 4.2 (Frontend – Trend Indicators) — New
ExpenseTrendCard.sveltecomponent renders the category name, current amount, previous amount, and a coloured arrow + percentage badge. The Monthly Expenses page now displays a "30-Day Spending Trends" grid below the calendar using this component. -
Subtask 4.3 (Frontend – Monthly Trend Sparkline) — New
SparklineChart.sveltecomponent renders a compact SVG bar chart for up to 6 months of per-category spending history. The last bar (current month) is highlighted in the category colour; a dashed red average line is overlaid. Sparklines are embedded inside eachExpenseTrendCardwhen more than one month of history is available. -
Epic 3: Simple Account Notes / Metadata — Users can now attach free-text notes to any account (e.g. "Emergency fund", "Company 401k") without editing the ledger file.
- Subtask 3.1 (Backend – DB) — New
account_notesSQLite table with a unique index onaccountname. Added as schema migration v4 viainternal/model/account_notepackage. - Subtask 3.2 (Backend – API) — Four new REST endpoints:
GET /api/account_notes(list all notes),GET /api/account_notes/:account(fetch one note),POST /api/account_notes/upsert(create/update),POST /api/account_notes/delete(remove). All write endpoints require theReadonlyMiddlewareguard. - Subtask 3.3 (Frontend – Detail Page) — New
/accounts/[name]/overview page with a textarea for composing and saving a note, plus a delete button. Notes are persisted immediately via the new API. -
Subtask 3.4 (Frontend – List Widget) — The
/accounts/[name]/transactionspage now fetches and displays an existing note inline in the page header as a highlighted tag, and includes a "Notes" button to navigate to the notes editor. -
Epic 2: Recent Transactions Widget on Dashboard — Added a feed of the 15 most recent transactions to the main dashboard for at-a-glance activity overview.
- Subtask 2.1 (Backend) —
GET /api/transactionnow accepts optionallimitandoffsetquery parameters for server-friendly pagination. Both parameters are applied at the transaction level (after grouping postings) so every returned transaction includes all of its postings. - Subtask 2.2 (Frontend) — New
RecentTransactionsWidget.sveltecomponent encapsulates the recent-transactions feed. It accepts atransactionsprop and an optionallimitprop (default 15) and renders each entry viaTransactionCard. -
Subtask 2.3 (Frontend) — Dashboard (
+page.svelte) now usesRecentTransactionsWidgetinstead of inline transaction rendering, keeping the layout clean and the widget reusable. -
Account-Level Transaction Drill-Down — Users can now click on any account balance widget on the dashboard to view a filtered transaction history for that specific account. A new route
/accounts/[name]/transactionsdisplays all transactions touching the selected account. TheGET /api/transactionendpoint now accepts an optionalaccountquery parameter to return only transactions for the given account prefix. -
Price & Journal Freshness Tracking — Added visual indicators to the navigation bar to track the freshness of your financial data. The "Update Prices" icon turns amber after 24 hours and red after 48 hours. The "Sync Journal" icon turns amber if any journal files have been modified since the last sync.
-
Firefly III Webhook Integration — Added a dedicated
/api/webhooks/fireflyendpoint that automatically parses and imports transactions from Firefly III via webhooks. Supported transactions are appended to the configuredadd_journal_pathjournal file. -
Firefly III Reconciliation (Labs) — Introduced a new balance reconciliation tool to compare Paisa (Ledger) account balances with Firefly III data. The feature is hidden by default and can be enabled under the new "Labs" configuration section. Includes support for ignoring specific accounts and case-insensitive account matching.
-
Price Export Feature — Users can now export their commodity price history in Ledger, hLedger, or Beancount formats. The export supports filtering by commodity and provides options for single-file or multi-file (ZIP) output.
-
Dynamic Multi-Currency Reporting — Improved the Income Statement and Market services to use period-end or dynamic exchange rates for foreign currency transactions, ensuring more accurate marked-to-market financial reporting across different timeframes.
-
Monthly Cashflow Widget — Added a new multi-currency monthly cashflow breakdown widget to the dashboard. The widget provides a high-level view of income vs. expenses using original ledger quantities.
-
Advanced Journal Configuration — Added
add_journal_pathto the configuration, allowing users to specify a dedicated journal file for transactions added via the API or webhooks, keeping the main journal file clean. -
Mobile & PWA Enhancements — Implemented a responsive navigation layout with a mobile-optimized side menu. Improved PWA support with better icon sizing, manifest validation, and consistent theme switching across mobile devices.
-
Assets Balance flat view + exports — Added a Flat Accounts toggle on the Assets → Balance page and support for exporting the current view to CSV or Excel. Exports now respect the selected display mode (hierarchy or flat), and
/api/assets/balanceacceptsflat=trueto return non-rollup account rows. -
Epic: Refining Paisa Configuration Interface — Redesigned the configuration screen to improve navigation and layout. Transitioned from a monolithic JSON form to a structured, sidebar-based interface with categorized sections for better discoverability and user experience.
-
Epic 2: Architectural Alignment & Cleanup — Finalized structural patterns and removed legacy Svelte 4 compatibility:
- Snippet Transition (2.1) —
Modal.sveltemigrated from named<slot>elements to typed snippet props (head,body,foot). All consumers updated to use{#snippet head(close)}...{/snippet}blocks:FileModal,PriceCodeSearchModal,DiffViewModal,SyncHistoryOverlay, and the import page inline modal. - Callback Prop Migration (2.2) —
createEventDispatcherremoved fromFileModal.svelte(replaced withonsavecallback) andPriceCodeSearchModal.svelte(replaced withonselectcallback). All fourFileModalcall-sites andJsonSchemaForm'sPriceCodeSearchModalusage updated to pass callback props directly. - Global Store Evolution (2.3) — New
src/lib/state/ui.svelte.ts(UIState) andsrc/lib/state/persisted.svelte.ts(PersistedState) class-based wrappers created usingfromStore. Components can now access stores viauiState.<prop>.currentor continue using the existing$storesyntax. - Lifecycle & Context Update (2.4) —
Navbar.svelteonMount/onDestroylifecycle hooks replaced with$effect(cleanup via returned function). Root+layout.svelteand(app)/+layout.svelteupdated from<slot />to{@render children()}with typedSnippetprop. - Final Switch-Over (2.5) —
componentApi: 4compatibility shim removed fromsvelte.config.js. All remaining Svelte 4 deprecation warnings resolved:Dropzone,Spinner,ZeroState,PostingGroupslots converted to snippet props;JsonSchemaForm<svelte:self>replaced with self-import;LastNMonthsoptionsarray made$derived;MonthPickerselectedYearinitialised from raw prop value;ThemeSwitcherinitial store call decoupled from reactive variable.isBurgerin(app)/+layout.sveltedeclared with$state(). - prettier-plugin-svelte bumped to
^3.3to support{@render ...}syntax in formatting. -
svelte-checknow reports 0 errors and 0 warnings across the entire frontend. -
Epic 1: Component Modernization (Runes & Event Syntax) — Systematically migrated all Svelte components and route pages from Svelte 4 syntax to Svelte 5 runes:
- All
export letprops converted to$props()/$bindable() - All
$:reactive statements converted to$derived()or$effect() - All mutable local state annotated with
$state() - All DOM event attributes (
on:click,on:change,on:keydown, etc.) replaced with lowercase equivalents (onclick,onchange,onkeydown, etc.) on:*|preventDefaultmodifiers inlined ase.preventDefault()callscreateEventDispatcherreplaced with callback props (ondrop,onselect,onpreview,onsave)-
All callers of modernized components updated to pass callback props instead of
on:eventlisteners -
Svelte 5 runes migration (P2.3) — Converted 15 remaining
src/lib/components/files and 5 route pages from Svelte 4 syntax to Svelte 5 runes:export let→$props()/$bindable(),$:derived expressions →$derived(),$:side-effect blocks →$effect(), and mutable local state →$state().createEventDispatcherinBulkEditForm,DiffViewModal, andFileTreereplaced with callback props (onpreview,onsave,onselect); all callers updated.<svelte:self>inFileTreereplaced with an explicit self-import. Goals pages (savings,retirement) converted allonMount-assigned variables to$state()for correct reactivity. -
Svelte 5 upgrade & UI modernization (P2.2) — Upgrades the frontend framework to Svelte 5 and begins the incremental migration to rune-based reactivity:
- Svelte 5 (
^5.0.0), svelte-check (^4.0.0), @sveltejs/vite-plugin-svelte (^4.0.0), and eslint-plugin-svelte (^3.0.0) bumped inpackage.json(#226). - Svelte 4 compatibility layer enabled in
svelte.config.jsviacompilerOptions.compatibility.componentApi: 4, allowing all existing components to keep working while new ones adopt runes (#227). - Modal.svelte migrated to Svelte 5 runes — props use
$props()/$bindable(); internal state uses$state();on:clickreplaced withonclick. Bulma structural classes (modal,modal-background,modal-card,modal-card-head/body/foot,is-active) replaced with DaisyUI equivalents (du-modal,du-modal-box,du-modal-backdrop,du-modal-open) and Tailwind flex utilities (#228 #229). - BoxedTabs.svelte migrated to Svelte 5 runes —
export letreplaced with$props()/$bindable();$:block replaced with$effect();on:clickreplaced withonclick(#229). - AccountTree.svelte — new Svelte 5 rune-based component that renders a hierarchical
AccountNode[]tree with keyboard-accessible expand/collapse and single-node selection via a$bindableselectedprop. Depth-limited auto-expansion (first two levels open by default) and focus ring via DaisyUI/Tailwind utilities (#230). -
Modal callers updated —
FileModal,DiffViewModal,PriceCodeSearchModal, andSyncHistoryOverlayupdated to use DaisyUI button classes (du-btn), Tailwind layout utilities, and the newonclickevent syntax. -
Local JSON price provider — A new built-in price provider (
local-json) allows users to maintain custom commodity prices in a plain JSON file on the local filesystem. No network access is required; the file is read on every price-update run. The code field inpaisa.yamlis the path to the JSON file (absolute, or relative to the config directory). See Commodities for the full JSON format specification and examples. - Extensible PriceProvider interface —
internal/model/price.PriceProvideris now fully documented with explicit return-value semantics for every method, making it straightforward to implement a custom provider. Compile-time interface-satisfaction checks (var _ price.PriceProvider = ...) have been added to every built-in provider package to catch drift early.
Bug fixes¶
-
Journal sync posting writes optimized —
posting.UpsertAllnow performs replacement inserts using GORMCreateInBatchesinstead of one-row-at-a-time inserts, reducing sync time on large journals while preserving atomic replace behavior. -
Error toast close button fixed — Removed an unbound inline
deletebutton from the global runtime error toast markup insrc/hooks.client.tsand now rely on Bulma Toast's built-in dismiss control (dismissible: true). The visible close X now dismisses error boxes correctly. -
Config mobile sidebar auto-close on selection — On mobile widths, choosing a section in More → Configuration now automatically collapses the sidebar overlay so the selected section content is immediately visible.
-
Regression: Configuration schema descriptions — Updated all regression test fixtures to match the latest configuration schema metadata. This ensures that intentional improvements to configuration documentation (like detailed tooltips and hover text) don't break the regression suite.
-
Income Statement start/end balance fixed —
startingBalanceandendingBalanceare now computed directly from the actual balance sheet (assets at market value + liabilities at book value), mirroring how the Networth page works. Previously they were derived from an income-flow reconstruction formula that had two bugs: (1) liabilities were negated with the wrong sign, and (2) the current fiscal year'sendingBalancewas priced at the FY end date (a future date) rather than today. The new approach guarantees that the End Balance row and the Networth page always agree for the current year, and correctly handles liability-funded transactions. -
Navbar effect recursion fixed — Reworked breadcrumb/nav selection in
src/lib/components/Navbar.svelteto resolve selection from the current path in a pure helper (src/lib/navbar_selection.ts) instead of reading and mutating the same reactive state inside one$effect. This prevents startup/runtimeeffect_update_depth_exceededcrashes on built apps. -
Embedded static asset routing fixed — Updated
internal/server/server.goto serve root-level PWA assets (/manifest.webmanifest,/sw.js, Workbox files, and/pwa-*.png) from embeddedweb/staticinstead of falling throughNoRoutetoindex.html. This resolvesManifest: Line: 1, column: 1, Syntax errorwhen using the Go-served app. -
Dashboard startup loop fixed — Updated
src/routes/(app)/+page.svelteto remove a read-after-write reactive pattern in the dashboard$effect(selectedExpenses), preventing a recursive update cycle that could raiseeffect_update_depth_exceededon load when the current month has no expense bucket. -
Manifest endpoint syntax error fixed — Added
static/manifest.webmanifestso/manifest.webmanifestserves valid JSON instead of fallback HTML, resolving browser console errors likeManifest: Line: 1, column: 1, Syntax error. -
Startup effect loop hardening — Updated
src/lib/components/Actions.svelteto stabilize theobscurestore subscription (track previous value correctly and unsubscribe on destroy), preventing repeatedrefresh()cascades that could triggereffect_update_depth_exceededon app load. -
Assets Analysis render crash fixed —
src/routes/(app)/assets/analysis/+page.sveltenow initializes the commodity color mapper with a safe fallback function and explicit callable type so the page no longer throwsTypeError: ... is not a functionduring first render while async data is still settling. -
Negative SVG width warnings fixed — Clamped stacked bar segment widths in
src/lib/gain.tsandsrc/lib/liabilities/interest.tstoMath.max(0, ...)to avoid invalid<rect width>values caused by floating-point precision drift. -
Interest chart null-safety hardening —
src/lib/liabilities/interest.tsnow guards missing DOM containers and empty timelines, uses safe fallbacks for current overview values, and aligns D3 tick formatter typing with strict TypeScript checks without changing chart behavior. -
Frontend check dependency resolution — Installed missing Node packages from
package.jsonso generated Connect/Protobuf client modules resolve correctly duringnpm run check. -
AccountTree compatibility fix —
src/lib/components/AccountTree.sveltewas migrated from rune-specific APIs to compatibleexport letprops + reactive state/effects, restoring type-safe recursive bindings and removing compile errors. -
Mobile navbar burger alignment fix — Updated
src/lib/components/Navbar.svelteto enforce left-aligned burger placement on mobile (.navbar-brand .navbar-burger.mobile-drawer-toggle) and replaced invalid self-closing non-void tags in the navbar markup to avoid ambiguous render behavior. -
UI warning cleanup (actions/sync modal) — Updated
src/lib/components/SyncHistoryOverlay.svelteandsrc/lib/components/Actions.svelteto remove ambiguous self-closing non-void icon tags, and improvedsrc/lib/components/Modal.sveltebackdrop semantics by using a real button instead of a clickable label. -
Global non-void self-closing cleanup — Applied a repo-wide Svelte markup normalization pass replacing ambiguous self-closing non-void HTML tags (for example
<i />,<div />,<span />) with explicit opening/closing tags acrosssrc/**/*.svelte. -
Closeable runtime error toasts — Updated global client error handling in
src/hooks.client.tsto show dismissible bottom-right toasts with sanitized stack/message content instead of center-screen modal-style error blocks, so users can always close or ignore transient errors. -
Remaining Svelte accessibility warnings cleared — Replaced lingering click-only anchors with semantic buttons, added missing labels to icon-only controls, fixed a remaining self-closing non-void table body, and restored
src/lib/components/BoxedTabs.svelteto compatible classic Svelte props/reactivity sosvelte-checknow reports 0 errors and 0 warnings. -
Repo line endings normalized — Added a repository-level
.gitattributespolicy to keep source files on LF across platforms (with Windows-native script exceptions), preventing recurring Windows/Linux newline churn that was causinggofmt -l .lint failures from formatting-only diffs. -
Client error toast null-safety + mobile navbar action overflow fix — Hardened
src/hooks.client.tsto safely formatnull/non-Error runtime exceptions (preventingCannot read properties of null (reading 'stack')while rendering error toasts), updatedsrc/store.tsto allow nullableaccountTfIdfinitialization, and adjusted mobile navbar action placement so top-right controls no longer get truncated on narrow screens. -
Responsive navbar action placement fix — Updated
src/lib/components/Navbar.svelteso hamburger-layout breakpoints remain consistent up to tablet width (preventing burger drift before desktop menu switch) and added top-right action icons (SyncingIndicator, theme toggle, andActions) in hamburger mode while hiding duplicate drawer-end actions. -
Docker build fix — Replaced
svelte-file-dropzone(incompatible with Svelte 5) with a local self-containedDropzone.sveltecomponent insrc/lib/components/. The local component matches the same API (multiple,accept,inputElementprops; dispatchesdropevent with{ acceptedFiles, fileRejections }). -
Dashboard crash on Svelte 5 fixed — Removed
@egjs/svelte-gridusage from dashboard and credit-card detail routes and replaced it with native CSS grid wrappers. This avoids runtime failures likeTypeError: Class constructor ... cannot be invoked without 'new'caused by legacy class-based Svelte components during route hydration. -
Frontend build emits sourcemaps — Enabled
build.sourcemapinvite.config.jsso minified hashed chunks can be mapped back to original source during debugging. -
Mobile navbar accessibility fix — Closing the burger menu now blurs any focused descendant before hiding the menu, and the closed menu is marked
inertto prevent focus from remaining inside hidden navigation content. -
PWA cache/update hardening — Switched to
registerType: "autoUpdate", enabledskipWaiting,clientsClaim, andcleanupOutdatedCaches, and disabled dev-time service worker registration to reduce stale asset/manifest mismatches during local development. -
Manifest and dashboard action robustness — Updated the app manifest link to use
%sveltekit.assets%/manifest.webmanifestand changed the dashboard "Setup Demo" trigger to a semantic<button>. -
Theme toggle duplication on desktop fixed — The navbar theme switcher was appearing twice on desktop (in both
mobile-top-actionsandmenu-actions-row) due to missing CSS specificity. Addeddisplay: none !importanttomobile-top-actionsto ensure it only shows on mobile (≤1023px breakpoint), and explicitly setdisplay: flexformenu-actions-rowto show it on desktop. -
MonthPicker non-selected month styling improved — Non-selected month buttons in the month picker dropdown now have a subtle hover background and outline, making them appear clearly interactive and clickable instead of plain text. Selected months remain highlighted in blue with bold text.
-
Local stale-chunk recovery — Added a client bootstrap safeguard that, on local/dev hosts, unregisters existing service workers, clears Cache Storage, and performs a one-time reload to avoid mixed old/new chunk runtime errors such as
TypeError: ... is not a function. -
AutoComplete no longer crashes the server — The
in-mfapi(MF API) andcom-purifiedbytes-npsproviders previously calledlog.Fatalwhen the autocomplete cache could not be populated, killing the server process. They now log the error atErrorlevel and return an empty suggestion list instead. -
Typed API via Protobuf/Connect (P3.1) — Eliminates hand-maintained TypeScript interfaces and
ajaxfetch wrappers for selected endpoints by driving the API contract from a.protoschema, giving compile-time type safety on both sides. proto/api.proto— definesTransaction,Posting,AccountBalance,AccountNode, and thePaisaServiceservice with aGetAccountTreeRPC. Package namepaisa.v1; Go packagegithub.com/ananthakumaran/paisa/internal/gen/paisa/v1;paisav1(#234).internal/gen/— committed generated Go stubs (api.pb.go,paisav1connect/api.connect.go) produced byprotoc-gen-go+protoc-gen-connect-go(#235).- Connect-Go integrated into Gin —
connectrpc.com/connectis added as a dependency;PaisaServiceendpoints are mounted at/connect/paisa.v1.PaisaService/…alongside the existing REST routes.TokenAuthMiddlewarenow protects/connect/paths with the same session-token logic as/api/paths (#235). GetAccountTreeendpoint —internal/server/connect_service.goimplementsPaisaServiceHandler.GetAccountTree, which converts the flat[]stringfromaccounting.AllAccountsinto a hierarchicalAccountNodetree sorted alphabetically at each level. Intermediate-only nodes (not direct leaf accounts) haveis_leaf: false(#236).src/lib/gen/api_pb.ts— committed generated TypeScript types (produced byprotoc-gen-es@2.x). Regenerate withnpm run generate:protoormake proto(#237).src/lib/connect_client.ts— typedPaisaServiceConnect transport client. UsescreateConnectTransportfrom@connectrpc/connect-webtargeting/connect; injects the sameX-Authsession token as the RESTajaxhelper (#237, #238).src/lib/account_tree.ts—fetchAccountTree()callspaisaClient.getAccountTree()with the sameloadingstore behaviour asajax;flattenAccountTree()converts the tree back to a flatstring[]for backward-compatible consumption (#238).-
make proto/npm run generate:proto— regeneration targets for both backend and frontend stubs (#237). -
Sync history/log overlay — A "Sync History" button (clock-rotate-left icon) is added to the header action bar. Clicking it opens a modal overlay that lists all known background jobs in reverse-chronological order, showing status badge (colour-coded: success/danger/info/warning), truncated job ID, created-at timestamp, started/finished timestamps, wall-clock duration, error message snippet (for failed jobs), and an expandable list of per-step
details. A "Clear history" button resets the in-memory jobs store. The overlay can be opened and closed at any time without interrupting an in-progress sync. A count badge on the history button shows the total number of tracked jobs (P1.3). - Header syncing indicator — A non-intrusive "Syncing…" badge with a spinning icon appears in the app header (navbar-end) while any background job is active (
isJobRunningis true) and disappears with a smooth fade once the job completes. The indicator is hidden on mobile viais-hidden-mobileto keep the layout stable, and is declared witharia-live="polite"for accessibility. No layout shift occurs because the element is not present in the DOM when idle (P1.3). - Frontend jobs store — New
src/lib/stores/jobs.tsmodule provides a global Svelte store (jobs) that tracks backgroundJobobjects keyed by their ID. Exposesupsert(insert or replace),updateById(partial merge; returnstruewhen the ID was found),reset(clear all), andsnapshot(synchronous read). Derived storesjobsList(sorted bycreated_at) andisJobRunning(true while any job is pending or running) are exported for reactive UI consumption. Thejobs,jobsList, andisJobRunningnames are re-exported fromsrc/store.tsfor consistency with other app-wide stores (P1.3). JobandJobStatustypes in utils.ts — AddedJobStatusunion type ("pending" | "running" | "completed" | "failed") andJobinterface mirroring the Goworker.Jobstruct. Date fields (created_at,started_at,finished_at) are typed asstringbecause the ajax reviver only converts keys matching/Date|date|time|now/.POST /api/syncajax overload updated — The TypeScript overload for/api/syncnow declares the return type as{ job_id: string }, matching the202 Acceptedresponse the backend already returns. A new overload forGET /api/jobs/:idreturningJobis also added.src/lib/sync.tsadapted for async API —sync()now extractsjob_idfrom the202response and immediately upserts apendingjob into the jobs store. Network-level failures (non-2xx, connection errors) are surfaced as a Bulma toast; job-level failures will be surfaced via polling (P1.3-12).startPolling— background job status polling —startPolling(jobId, onTerminal?, options?)pollsGET /api/jobs/:idin the background at 2-second intervals, updating the jobs store on each response. Polling stops automatically when the job reaches a terminal state (completed or failed) or after 150 attempts (~5 minutes). Up to 5 consecutive network errors are tolerated before polling aborts; the error counter resets on the next successful response. On failure, a Bulma toast is shown with the error message. TheonTerminalcallback is called with the final job, enabling callers (e.g.Actions.svelte) to trigger a data refresh. All timing and retry parameters are injectable for unit testing (P1.3).Actions.svelteupdated for async sync —syncWithLoadernow callsstartPollingaftersync()returns ajob_id, deferring the datarefresh()to theonTerminalcallback rather than running it immediately. This ensures the UI reflects the completed sync result rather than stale data (P1.3).createJobsStoreexported — ThecreateJobsStorefactory insrc/lib/stores/jobs.tsis now exported so tests and tooling can create isolated store instances without sharing the module-level singleton.- Asynchronous POST /api/sync —
POST /api/syncnow returns202 Acceptedimmediately with{"job_id": "<uuid>"}instead of blocking until the sync completes. The sync work is performed in the background via theworker.Registry; callers can poll the job status viaGET /api/jobs/:id(P1.2). Readonly and authentication behaviour are preserved: the endpoint is still guarded byReadonlyMiddlewareandTokenAuthMiddleware. - GET /api/jobs/:id — New read endpoint that returns the current state of a background job as a JSON object (fields:
id,status,created_at,started_at,finished_at,error,details). Returns404with the standard error envelope when the job ID is unknown. - Background worker package — New
internal/service/workerpackage provides a thread-safeRegistryfor submitting and tracking background jobs. EachJobprogresses through a defined state machine (Pending → Running → Completed | Failed) with creation, start, and finish timestamps. Callers submit afunc(context.Context) errorviaRegistry.Submitand receive a unique job ID; status can be polled withRegistry.Getor enumerated withRegistry.List. This lays the groundwork for the asynchronous sync API (P1.2). - Per-step job details —
Jobnow carries aDetails []stringfield that holds per-step diagnostic messages accumulated during job execution. For example, individual commodity fetch failures from a price-scraper job are each recorded as a separate entry rather than collapsed into a single top-level error string. Present on both successful and failed jobs.Registry.SubmitDetailedaccepts afunc(context.Context) ([]string, error)callback and stores the returned details in the job. - Price scraper failures in job details — Each commodity price fetch failure from
SyncCommodities(e.g. network errors, missing quote currency) is now recorded as a separate entry inJob.Detailsso operators can identify the exact failing commodity without inspecting server logs. - XIRR pre-computation in sync job — After a journal or price sync,
service.WarmXIRRCachepre-computes XIRR for every investment account and stores the results in the SQLite computation cache. Any accounts whose Newton-Raphson XIRR solver does not converge are recorded inJob.Detailsas warnings so operators can identify data problems. Subsequent API calls to/api/gainand/api/networthare served from cache. xirr.XIRRWithConvergence— New exported function alongside the existingXIRRthat returns(decimal.Decimal, bool), where the bool reports whether the Newton-Raphson iteration converged. Callers that need to distinguish a genuine zero XIRR from a non-convergent solver can use this variant.- Explicit Job state-machine enforcement — The
workerpackage now defines avalidTransitionsmap that enumerates every legalJobStatustransition. Atransition()helper validates each state change at runtime and panics on any illegal move (e.g. Completed → Running), making invalid transitions immediately observable.JobStatus.IsTerminal()returns true forCompletedandFailed, enabling callers to check whether a job has reached a terminal state without inspecting the status string directly. - Deterministic
Registry.Listordering —Registry.List()now returns jobs sorted byCreatedAtascending (oldest job first), replacing the previous non-deterministic map-iteration order. All concurrent-access tests forList,Get, andSubmitpass under-race. - SHA-256 file hash utility — Added
SHA256File(path string) (string, error)ininternal/utils/hash.go. Streams file content in chunks for efficient large-file handling, and returns descriptive errors on open/read failures. This utility supports upcoming incremental sync checks (P1.1). - Metadata key/value table — New
internal/model/metadatapackage adds aMetadatamodel backed by a SQLite table (metadata) with a unique index onkey. A new schema migration (v3) creates the table for both fresh installs and existing databases. - Metadata persistence API —
metadata.Get(db, key)returns the stored value orgorm.ErrRecordNotFoundfor missing keys;metadata.GetOrDefault(db, key, default)returns a caller-supplied fallback for missing keys;metadata.Set(db, key, value)performs an atomicINSERT … ON CONFLICT DO UPDATEupsert, covering both create and update paths consistently. - SyncJournal hash-skip optimisation —
SyncJournalnow computes the SHA-256 hash of the journal file before invoking any ledger CLI commands. When the hash matches the value stored in thejournal_hashmetadata key from the last successful sync, all CLI validation and parsing work is skipped and the result carriesSkipped: true. The hash is only persisted after a fully successful sync, so a partial failure never silently suppresses the next run.
0.8-beta (2026-04-26) — Multi-currency pricing rollout¶
New features¶
- Multi-currency price schema —
pricestable gainsquote_commodityandsourcecolumns; a backward-compatible migration (v1 → v2) backfillsquote_commodityfrom the ledger default currency for all existing rows. - Pair-aware rate resolver —
service.GetRateresolves exchange rates via direct pairs, inverse pairs, and one-hop cross rates through the configured default currency (e.g. INR). - Extended price API —
GET /api/priceaccepts optionalbase,quote,from,to,source, andreport_currencyquery parameters; unfiltered calls continue to return the legacy map-keyed format for backward compatibility. - Price export endpoint —
GET /api/price/exportexports the full price history as ledger, hledger, or beancount directives. - Rollback flag — set
disable_multi_currency_prices: trueinpaisa.yamlto disable cross-rate resolution andreport_currencyconversion and revert to pre-rollout behaviour without downgrading the binary. - UI Enhancements — Improved Actions and Navbar components, added portfolio sync option to dropdown actions. Increased size of navbar action icons on mobile devices for better touchability.
- Original Balances — Added currency field and original balance display to Credit Card Summary, Liability breakdown, and asset breakdown for non-equity commodities.
- New Configuration Fields — Added
currenciesfield to distinguish currencies from securities, andprovider_debug_httpto log provider HTTP requests. - FX Rates Page — Added a new page for tracking exchange rates (FX Rates) with derived rate support.
- Background Sync — Implement periodic background sync for backend to improve data freshness, with schedule option for journal and price sync.
Bug fixes¶
- Assets -> Gain page calculations — Fixed units-vs-currency mismatch in XIRR, investment, and absolute return calculations by using historical market prices when ledger falls back to units.
- Same-Day Price/Rate Race Conditions — Fixed by using EndOfDay pivot.
- Yahoo Provider Handling — Fixed handling of nil close values and empty responses.
- Currency Detection — Fixed
IsForeignCurrencydetection by normalizing commodity input. - Net Worth Timeline — Reconciled net worth discrepancies between dashboard and timeline by including today's transactions and latest prices.
- Core Stability — Fixed server crash in
GetUnitPricewhen prices are missing by replacinglog.Fatalwith a warning, and resolved USD->INR conversion for non-posting commodities and implicit journal quotes. - Missing Price & FX Reporting — Enhanced missing price warnings with dates and added summary counts for missing FX rates during cache warming.
- Mobile UI — Re-enabled zoom functionality on mobile devices.
- Dependencies — Addressed multiple npm dependency vulnerabilities (kit, axios, handlebars, lodash, pdfjs-dist, vite).
Upgrade guide¶
- Run
paisa update(or restart the server) — the database migration runs automatically on startup; no manual steps are required. - Verify prices with
GET /api/price?base=<COMMODITY>to confirmquote_commodityhas been backfilled correctly. - If unexpected valuation changes are observed, set
disable_multi_currency_prices: trueinpaisa.yamland restart as a rollback measure.
Rollback procedure¶
- Add
disable_multi_currency_prices: truetopaisa.yaml. - Restart the Paisa server — no database changes are required.
- Report the regression so it can be investigated before re-enabling.
0.7.4 (2025-02-23)¶
- Update price data domain
- Fix NixOS build
0.7.3 (2025-02-23)¶
- Fix yahoo price fetcher
- Build fixes
0.7.1 (2024-10-20)¶
- Fix remote code execution vulnerability
0.7.0 (2024-08-26)¶
- Add docker image variant for hledger and beancount
- Bug fixes
0.6.6 (2024-02-10)¶
- Improve tables (make it sortable)
- Show tabulated value on allocation page
- Show invested value on goals page
- Bug fixes
0.6.5 (2024-02-02)¶
- Add Liabilities > Credit Card page
- Support password protected XLSX file
- Allow user to configure timezone
- Bug fixes
0.6.4 (2024-01-22)¶
- Add checking accounts balance to dashboard
- Improve template management UI
- Improve spinner and page transition
- Bug fixes
0.6.3 (2024-01-13)¶
- Introduce Sheets: A notepad calculator with access to your ledger
- Remove flat option from cashflow > yearly page
- Dockerimage now installs paisa to /usr/bin
- Improve legends rendering on all pages
- Allow user to cancel pdf password prompt
- Add new warning for missing assets accounts from allocation target
- Support hledger's balance assertion
- Bug fixes
0.6.2 (2023-12-23)¶
- New logo
- Allow goals to be reordered
- Show goals on the dashboard page
- Bug fixes
0.6.1 (2023-12-16)¶
- Add new price provider: Alpha Vantage
- Make first day of the week configurable
- Support ledger strict mode
- Add user login support, go to
User Accountssection in configuration page to enable it - Show notes associated with a transaction/posting
- Bug fixes
0.6.0 (2023-12-09)¶
- Add individual account balance on goals page
- Add keyboard shortcuts to format/save file on editor page
- Add ability to search posting/transaction by note
- Add option to reverse the order of generated transactions on import page
- Add option to clear price cache
- Bug fixes
0.5.9 (2023-11-26)¶
- Improve postings page
- Add income statement page (Cash Flow > Income Statement)
- Bug fixes
0.5.8 (2023-11-18)¶
- Add ability to specify rate, target date or monthly contribution to savings goal
- Improve price page
- Bug fixes
0.5.7 (2023-11-11)¶
- Add goals
- Remove retirement page (available under goals)
- Bug fixes
Breaking Changes
¶
Retirement page has been moved under goals. If you have used retirement, you need to setup a new retirement goal
0.5.6 (2023-11-04)¶
- Add support for Income:CapitalGains
- Add option to control display precision
- Add new price provider for gold and silver (IBJA India)
- Add option to disable budget rollover
- Bug fixes
0.5.5 (2023-10-07)¶
- Support account icon customization
- Add beancount ledger client support
0.5.4 (2023-10-07)¶
- Add calendar view to recurring page
- Support recurring period configuration
- Support European number format
- Bug fixes
0.5.3 (2023-09-30)¶
- Add Docker Image
- Add Linux Application (deb package)
- Move import templates to configuration file
- Bug fixes
Breaking Changes
¶
User's custom import templates used to be stored in Database, which is a bad idea in hindsight. It's being moved to the configuration file. With this change, all the data in paisa.db would be transient and can be deleted and re created from the journal and configuration files without any data loss.
If you have custom template, take a backup before you upgrade and add
it again via new version. If you have already upgraded, you can still
get the data directly from the db file using the following query
sqlite3 paisa.db "select * from templates";
0.5.2 (2023-09-22)¶
- Add Desktop app
- Support password protected PDF on import page
- Bug fixes
Breaking Changes
¶
- The structure of price code configuration has been updated to make it easier to add more price provider in the future. In addition to the code, the provider name also has to be added. Refer the config documentation for more details
0.5.0 (2023-09-16)¶
- Add config page
- Embed ledger binary inside paisa
- Bug fixes
0.4.9 (2023-09-09)¶
- Add search query support in transaction page
- Spends at child accounts level would be included in the budget of parent account.
- Fix the windows build, which was broken by the recent changes to ledger import
- Bug fixes
0.4.8 (2023-09-01)¶
- Add budget
- Add hierarchial cash flow
- Switch from float64 to decimal
- Bug fixes
0.4.7 (2023-08-19)¶
- Add dark mode
- Add bulk transaction editor