README.org

September 8, 2025 ยท View on GitHub

#+STARTUP: showall

  • hledger-flow :PROPERTIES: :CUSTOM_ID: hledger-flow :END:

** Project Status

I haven't been able to spend much time on this project in the last few years, and looking at my current responsibilities this is likely to remain the case.

I do still try to respond to bug reports as they happen.

** What is it? :PROPERTIES: :CUSTOM_ID: what-is-it :END:

=hledger-flow= is a command-line program that gives you a guided [[https://hledger.org/][Hledger]] workflow. It is important to note that most of the heavy lifting is done by the upstream =hledger= project. For example, =hledger-flow= cares about where you put your files for long-term maintainability, but the actual conversion to classified accounting journals is done by =hledger=.

=hledger-flow= focuses on automated processing of electronic statements as much as possible, as opposed to manually adding your own hledger journal entries. Manual entries are still possible, it just saves time in the long run to automatically process a statement whenever one is available.

Within =hledger-flow= you will keep your original bank statements around permanently as input, and generate (h)ledger journals each time you run the program. The classification is done with [[https://hledger.org/csv.html][hledger's rules files]], and/or your own script hooks.

Keeping the original statements means that you never have to worry too much about "am I doing this accounting thing right?" or "what happens if I make a mistake?". If you want to change your mind about some classification, or if you made a mistake, you just change your classification rules, and run the program again.

It started when I realized that the scripts I wrote while playing around with [[https://github.com/adept/full-fledged-hledger/wiki][adept's Full-fledged Hledger]] aren't really specific to my own finances, and can be shared.

** Overview of the Basic Workflow :PROPERTIES: :CUSTOM_ID: overview-of-the-basic-workflow :END:

  1. Save an input transaction file (typically CSV) to a [[#input-files][specific directory]].
  2. Add an hledger [[#rules-files][rules file]]. Include some classification rules if you want.
  3. Run =hledger-flow import=

Add all your files to your favourite version control system.

The generated journal that you most likely want to use as your =LEDGER_FILE= is called =all-years.journal=. This has include directives to all the automatically imported journals, as well as includes for your own manually managed journal entries.

In a typical software project we don't add generated files to version control, but in this case I think it is a good idea to add all the generated files to version control as well - when you inevitably change something, e.g. how you classify transactions in your rules file, then you can easily see if your change had the desired effect by looking at a diff.

** Who should use this? :PROPERTIES: :CUSTOM_ID: who-should-use-this :END:

=hledger-flow= is intended for you if:

  • You want a way to organise your finances into a structure that will be maintainable over the long term.
  • You like the idea of treating your source transactions (typically CSV files) as input, and having your hledger journals (mostly) being generated as output.
  • You want to automate as much as possible when dealing with your financial life.
  • You don't mind writing some scripts when needed, as long as it saves you time over the long term.

** How do I install it? :PROPERTIES: :CUSTOM_ID: how-do-i-install-it :END:

If you can compile it yourself, please do so by following the [[https://github.com/apauley/hledger-flow/blob/master/CONTRIBUTING.org#build-the-project][build instructions]].

Otherwise download [[https://github.com/apauley/hledger-flow/releases][the latest release]] for your OS (Linux or Mac OS X), and copy the =hledger-flow= executable to a directory in your PATH.

On Linux you should just be able to run the executable. On Mac you may see a warning.

** Windows Support

Currently =hledger-flow= does not work on Windows.

This [[https://github.com/apauley/hledger-flow/issues?q=is%3Aissue+is%3Aopen+label%3Awindows][list of issues]] describes some of the details of what doesn't work.

I believe it wouldn't take too much effort to fix those issues, but I'm going to leave Windows support for other contributors.

Please send me some pull requests if you would like =hledger-flow= to work on Windows.

** Getting Started :PROPERTIES: :CUSTOM_ID: getting-started :END:

Have a look at the [[file:step-by-step/README.org][detailed step-by-step instructions]] and the [[file:docs/README.org][feature reference]].

You can see the example imported financial transactions as it was generated by the step-by-step instructions here:

[[https://github.com/apauley/hledger-flow-example][https://github.com/apauley/hledger-flow-example]]

** Compatibility with hledger

=hledger-flow= should work with any recent version of =hledger=.

The most recent version that it was tested with is [[https://hledger.org/relnotes.html#2025-09-03-hledger-150][hledger-1.50]] (September 2025).

Note to future readers: if you are using =hledger-flow= with a more recent version of =hledger= than mentioned above, please submit a small pull request with the updated version of =hledger=.

** Compatibility with ledger-cli :PROPERTIES: :CUSTOM_ID: compatibility-with-ledger :END:

=hledger-flow= uses =hledger= to produce journal's, so this page about [[https://hledger.org/ledger.html][hledger and Ledger]] should be relevant for all =hledger-flow= users.

That said, here are some observations that are specifically relevant to =hledger-flow=:

When writing out the journal include files, =hledger-flow= sorts the include statements by filename.

[[https://www.ledger-cli.org/][Ledger]] fails any balance assertions when the transactions aren't included in chronological order.

An easy way around this is to name your input files so that March's statement is listed before December's statement.

Another option is to add =--permissive= to any [[https://www.ledger-cli.org/][ledger]] command.

So you should easily be able to use both =ledger= and =hledger= on these journals if you take care to [[https://hledger.org/faq.html#how-is-hledger-different-from-ledger-][avoid the few incompatibilities]] which exists (eg in your rules files or manual journals).

** Project Goals :PROPERTIES: :CUSTOM_ID: project-goals :END:

My =hledger= files started to collect a bunch of supporting code that weren't really specific to my financial situation.

I want to extract and share as much as possible of that supporting code.

[[https://github.com/adept/full-fledged-hledger/wiki][Adept's]] goals also resonated with me:

  • Tracking expenses should take as little time, effort and manual work as possible
  • Eventual consistency should be achievable: even if I can't record something precisely right now, maybe I would be able to do it later, so I should be able to leave things half-done and pick them up later
  • Ability to refactor is a must. I want to be able to go back and change the way I am doing things, with as little effort as possible and without fear of irrevocably breaking things.
  • Contributing to hledger-flow

Have a look at the [[file:CONTRIBUTING.org][contribution guidelines]].

  • FAQ :PROPERTIES: :CUSTOM_ID: faq :END:

** How do you balance transfers between 2 accounts when you have statements for both accounts? :PROPERTIES: :CUSTOM_ID: transfer-2-accounts :END:

*** The Problem

In your primary bank account you've happily been classifying transfers to a secondary account as just =Expenses:OtherAccount=.

But you've recently started processing the statements from the second account as well so that you can classify those expenses more accurately.

And now the balances of these two accounts are all wrong when the statements of each account deals with money transferred between these two accounts.

In =bank1.journal=, imported from =bank1.csv=: #+BEGIN_EXAMPLE 2018/11/09 Transfer from primary account to secondary account Assets:Bank1:Primary $-200 Assets:Bank2:Secondary #+END_EXAMPLE

In =bank2.journal=, imported from =bank2.csv=: #+BEGIN_EXAMPLE 2018/11/09 Transfer from primary account to secondary account Assets:Bank2:Secondary $200 Assets:Bank1:Primary #+END_EXAMPLE

*** The Solution

As soon as you start importing statements for both accounts you will have to introduce an intermediate account for classification between these two accounts.

I use =Assets:Transfers:*=.

And we may have reports looking at these transfers accounts at some point, you should consider using the same names.

The above example then becomes as follows.

In =bank1.journal=, imported from =bank1.csv=: #+BEGIN_EXAMPLE 2019-05-18 Transfer from primary account to secondary account Assets:Bank1:Primary $-200 Assets:Transfers:Bank1Bank2 #+END_EXAMPLE

In =bank2.journal=, imported from =bank2.csv=: #+BEGIN_EXAMPLE 2019-05-18 Transfer from primary account to secondary account Assets:Bank2:Secondary $200 Assets:Transfers:Bank1Bank2 #+END_EXAMPLE

Any posting to =Assets:Transfers:*= indicates an in "in-flight" amount. You would expect the balance of =Assets:Transfers= to be zero most of the time. Whenever it isn't zero it means that you either don't yet have the other side of the transfer, or that something is wrong in your rules.

You could theoretically just use =Assets:Transfers= without any subaccounts, but I found it useful to use subaccounts. Because then the subaccounts can show me where I should look for any missing transfer transaction.

I typically use sorted names as the subaccount (Python code sample):

#+BEGIN_SRC python "Assets:Transfers:" + "".join(sorted(["Bank2", "Bank1"])) #+END_SRC

*** External references

This approach is based on what is described in Full-fledged hledger:
[[https://github.com/adept/full-fledged-hledger/wiki/Adding-more-accounts#lets-make-sure-that-transfers-are-not-double-counted]]

The question was first asked in [[https://github.com/apauley/hledger-flow/issues/51][issue #51]].