Skip to main content

Custom Links and Queries

Learn how to enhance your Beancount experience by implementing custom sidebar links and SQL queries to streamline your financial tracking and reporting.

Add dated sidebar-link directives to stock Fava, then save a query that finds negative ending balances after summing all postings. The examples were executed with Beancount 3.2.3, beanquery 0.2.0 and Fava 1.30.16. Use the pinned local setup.

These URLs target a local stock Fava server. Beancount.io's hosted dashboard has different report paths and is scoped separately below.

Why Customize Fava?

Sidebar shortcuts preserve a useful filtered view. A saved query can then answer a specific accounting question without repeatedly entering BQL.

Pain points this solves:

  • Repeatedly selecting the current or previous month.
  • Reopening a saved report.
  • Distinguishing an outflow from an account that actually ends below zero.

Add these directives to the complete sidebar-demo.beancount ledger in the next section. Start it with fava sidebar-demo.beancount and open its Journal report before clicking a shortcut.

2021-01-01 custom "fava-sidebar-link" "Current Month" "/jump?time=month"
2021-01-01 custom "fava-sidebar-link" "Last Month" "/jump?time=month-1"
2021-01-01 custom "fava-sidebar-link" "Clear All" "/jump?account=&time=&filter="

What They Do:

/jump returns to the page in the browser's Referer header and replaces the supplied query parameters. It does not always open the Journal report. On the balance sheet, it stays on the balance sheet. This behavior is implemented by the stock Fava redirect handler.

  • Current Month: Sets time=month on the current report.
  • Last Month: Sets time=month-1 on the current report.
  • Clear All: Removes account, time and filter. Other parameters, such as conversion and interval, remain.

The 2021 fixture will have no transactions in today's month; use Clear All before reproducing its query results. A /jump URL requires a referrer. For a bookmark opened directly, copy a complete working report URL instead.

At the host root, /jump?time=month was tested from /sidebar-demo/journal/?time=2021&account=Assets: it returned HTTP 302 to the same journal with account=Assets&time=month. The destination report returned 200.

If an administrator mounts the whole Fava application at /books, every root-relative shortcut must include that prefix. This is an alternative configuration, not another directive to add alongside Current Month above:

2021-01-01 custom "fava-sidebar-link" "Current Month" "/books/jump?time=month"

A local WSGI mount test verified /books/jump redirects back to /books/sidebar-demo/journal/ with the new filter. Plain /jump is outside that mount and returned 404. Fava's custom links preserve the supplied URL; a leading / means the host root, not the ledger root.

Hosted scope, checked 2026-09-07: the Beancount.io dashboard source uses paths such as /ledger/OWNER/LEDGER/income-statement and /ledger/OWNER/LEDGER/query. Its sidebar builds its own report menu. The inspected source has no /jump route or fava-sidebar-link consumer. This stock recipe is therefore not established for that dashboard. On the hosted product, open the desired report and bookmark its working address. The source snapshot does not prove which revision a live deployment runs.

🔍 Custom SQL Queries

Save this complete fixture as sidebar-demo.beancount. It deliberately includes both positive and negative postings in each asset account:

option "title" "Sidebar Demo"
option "operating_currency" "USD"
2021-01-01 open Assets:BCM:Positive USD
2021-01-01 open Assets:BCM:Negative USD
2021-01-01 open Equity:Opening-Balances USD
 
2021-12-01 * "Opening balances"
  Assets:BCM:Positive        100.00 USD
  Assets:BCM:Negative         20.00 USD
  Equity:Opening-Balances   -120.00 USD
 
2021-12-10 * "Outflows"
  Assets:BCM:Positive        -30.00 USD
  Assets:BCM:Negative        -50.00 USD
  Equity:Opening-Balances     80.00 USD
 
2022-01-05 * "Refund"
  Assets:BCM:Negative         10.00 USD
  Equity:Opening-Balances    -10.00 USD
 
2022-01-09 balance Assets:BCM:Positive 70.00 USD
2022-01-09 balance Assets:BCM:Negative -20.00 USD

bean-check sidebar-demo.beancount passes. At the start of January 9, Positive holds 100 - 30 = 70 USD; Negative holds 20 - 50 + 10 = -20 USD.

Run this query in stock Fava's Query page with global filters cleared:

SELECT account, currency, SUM(number) AS ending_balance
FROM postings
WHERE account ~ ':BCM:'
  AND date < 2022-01-09
GROUP BY account, currency
HAVING SUM(number) < 0
ORDER BY account, currency;
AccountCurrencyEnding balance
Assets:BCM:NegativeUSD-20.00

Breakdown:

WHERE selects the postings to aggregate. There is no lower date bound: an ending balance needs all earlier history, including the December 1 opening balances. The exclusive upper bound includes January 8 and excludes January 9 transactions.

GROUP BY account, currency keeps distinct commodities separate. HAVING SUM(number) < 0 filters after summing each group's positive and negative quantities. This is an ending units balance per currency, not market value converted into one currency. It reports each exact account, not rolled-up parent accounts.

For comparison, this executable query answers a different question: how much was posted negatively during the review window?

SELECT account, currency, SUM(number) AS negative_postings
FROM postings
WHERE account ~ ':BCM:'
  AND number < 0
  AND date >= 2021-12-09 AND date < 2022-01-09
GROUP BY account, currency
ORDER BY account, currency;
AccountCurrencyNegative postings
Assets:BCM:NegativeUSD-50.00
Assets:BCM:PositiveUSD-30.00

The second query omits the refund and opening balances. Its -30.00 USD row does not mean Positive is overdrawn. Adding a lower date bound to the first query would instead produce net movement during a period, not an ending balance. See the BQL reference for further query syntax.

Use cases:

  • Check asset accounts for unexpected negative quantities.
  • Review negative postings separately when investigating outflows or reversals.
  • Reconcile the reported balance to opening history and subsequent activity before treating it as an anomaly. A negative liability or income balance can be normal.

Fava does support links to queries. Add this saved query and shortcut to sidebar-demo.beancount:

2021-01-01 query "negative-balances" "SELECT account, currency, SUM(number) AS ending_balance FROM postings WHERE account ~ ':BCM:' AND date < 2022-01-09 GROUP BY account, currency HAVING SUM(number) < 0 ORDER BY account, currency"
2021-01-01 custom "fava-sidebar-link" "Negative Balances" "/sidebar-demo/query/?query_string=.run+%22negative-balances%22"

The query directive also appears in Fava's saved-query sidebar, subject to sidebar-show-queries (default 5). The explicit shortcut targets the fixture's sidebar-demo slug and runs the named query. The stock query page reads query_string from the URL; its query API returned the same single -20.00 USD row in the local test. See the versioned Query component.

Use your ledger's actual slug in a different book. Under the /books mount, the shortcut becomes /books/sidebar-demo/query/?query_string=.run+%22negative-balances%22. Keep global filters clear for a full-history ending balance; a date filter can remove the opening history before the saved query runs.

Final Thoughts

Use the month shortcuts to navigate reports, and use the saved query to check a reproducible ending balance. When reviewing your budget, distinguish outflows, period movement and closing balances before interpreting a negative number. For additional reports, see Fava's extension documentation and the UI guide.

Source: https://beancount.io/docs/Tips/side-bar-link