Recording Transactions
A transaction moves amounts between accounts: your salary arrives, you pay for lunch, you settle a credit card bill. You can write transactions in your ledger files with any text editor, or enter them in the web UI. Either way they end up as plain text in your files.
This guide uses a small household ledger. The full syntax is in Transaction.
Write a transaction
Section titled “Write a transaction”2024-03-01 * "ACME Corp" "March salary" Assets:Bank:Checking 12,000.00 CNY Income:Salary- The header line has the date, a flag, the payee and the narration.
*marks a completed transaction and!one you still want to check. - Each indented line below it is a posting: an account and an amount.
- The postings of a transaction must add up to zero in each commodity. Here
Income:Salaryhas no amount, so Zhang gives it whatever balances the transaction:-12,000.00 CNY. - Only one posting of a transaction can leave out its amount, and the other postings must then add up in a single commodity. Otherwise Zhang cannot tell what to fill in: it reports an error and leaves the transaction out of the ledger.
- Amounts may use
,or_to group digits, and simple arithmetic:-(2 * 420) CNYis-840 CNY.
With a single string after the flag, the string is the narration:
2024-03-10 ! "Taxi" Assets:Cash -35 CNY Expenses:Travel 35 CNYEvery account a transaction uses must be opened first with open. Every commodity must be declared with commodity, unless it is the operating currency.
Tags, links and metadata
Section titled “Tags, links and metadata”2024-03-09 * "Hotel" "Two nights in Hangzhou" #trip-hangzhou ^booking-8812 invoice: "INV-2024-0309" Liabilities:CreditCard -840 CNY Expenses:Travel receipt: "hotel.pdf"- A tag (
#trip-hangzhou) groups transactions by theme, such as a trip or a project. A link (^booking-8812) ties together the transactions of one event, such as a booking and its refund. Tags and links come after the narration, in any order. - Metadata lines are
key: valuepairs. A line right under the header belongs to the transaction (invoice). A line indented deeper than a posting belongs to that posting (receipt). - On the Journals page, selecting a tag or a link filters the journal by it.
- In queries,
'trip-hangzhou' IN tagsselects the tagged postings,entry_meta('invoice')reads the transaction’s metadata andmeta('receipt')the posting’s. - A
documentmetadata entry attaches a file to the transaction. See Documents.
Date and time
Section titled “Date and time”A date in a Zhang ledger can carry a time of day, as HH:MM or HH:MM:SS:
2024-03-02 12:30 * "Noodle House" "Lunch" #work Liabilities:CreditCard -48 CNY Expenses:Food 48 CNY- Times are in the ledger’s timezone, the
timezoneoption. It defaults to the timezone of the machine Zhang runs on. - Entries are ordered by date and time. An entry without a time counts as the start of its day (
00:00:00). Entries with the same date and time keep the order of the files. - The time matters most for balance assertions, which check the balance at the moment they are dated.
- Beancount dates have no time of day. In a beancount ledger, Zhang reads a
time: "12:30:00"metadata entry as the time, and writes the time that way too.
Split the ledger into files
Section titled “Split the ledger into files”As a ledger grows, keep the accounts and each period’s transactions in files of their own, and include them from the main file:
option "title" "Household"option "operating_currency" "CNY"
include "accounts.zhang"include "data/*.zhang"- A relative path is relative to the file that includes it.
*matches within one directory:data/*.zhangreads every.zhangfile directly indata/, but not the files indata/2024/. Include has the limits of patterns.- The extension of the main file decides the syntax of every file in the ledger: Zhang syntax for
main.zhang, beancount syntax formain.bean.
Record a transaction in the web UI
Section titled “Record a transaction in the web UI”Select New transaction at the top of the sidebar, and fill in:
- Date: pick the day. A new transaction gets the current time of day. Dates and times are the ledger’s, in its
timezone, whatever the timezone of your browser: editing a transaction without touching its date keeps its time. - Payee and Narration.
- Postings: an account and an amount written as
amount commodity, such as-28 CNY, read as in a ledger file:1,000 CNYand(10 + 2) / 4 USDwork too, and a number alone is in the operating currency. Leave one amount empty and Zhang fills it in. - Posting details, behind the details button of a posting: its cost and price, written as in a ledger file (
{150 USD},{{1500 USD}}or{}for the cost,@ 6 USDor@@ 60 USDfor the price; see Lots and cost basis), a comment for the end of its line, and its metadata. - Metadata: key and value pairs for the transaction.
While you type, Zhang checks the transaction as the ledger will once it is saved, and the form shows the result:
- Preview shows the text that will be written to the file.
- A field Zhang cannot read, such as a cost without its braces, is marked with the reason, and the transaction cannot be saved until it is fixed.
- Under the postings, the form shows what the postings are off by, if they do not balance. Each posting is weighed as the ledger weighs it, by its cost or price, and rounded at each commodity’s precision. So
10 AAPL {150 USD}against-1500 USDbalances. - It also lists any other error the ledger would report, such as an account that is closed at the transaction’s date or a sale with no lot to sell. You can still save such a transaction, as you can write it in a file.
The form has no fields for tags or links. For those, write the transaction in a file, for example on the Raw Editing page, which edits the ledger files in the browser.
To change a transaction, open the menu of its row on the Journals page and choose Edit. Zhang writes the edited transaction back in place, in the file it came from. It rewrites the whole transaction from the form: each posting keeps its cost, price, comment, metadata and flag, and a posting written without an amount stays that way. Only the layout of the text and comment lines between the postings (or at the end of the first line) are not kept; before an edit that would drop such lines, Zhang asks you to confirm. A transaction a plugin generated cannot be edited: it is in no file of the ledger, so the edit is refused and nothing is written.
The same holds for the API: PUT /api/transactions/{id} takes a cost, a price and a comment per posting in the same forms, keeps those of a posting when the fields are left out, and removes one sent as null. The datetime of a request is the ledger’s wall-clock time in its timezone, written without an offset ("2024-01-02T07:00:00"), as every response gives it; an instant with an offset or Z is still read, as the wall-clock time it is in the ledger’s timezone. The unit of a posting is either an object {"number": "-28", "commodity": "CNY"} or the text of the amount, such as "-28 CNY", which is read as the form reads it. POST /api/transactions/preview and POST /api/transactions/{id}/preview take the same body as the create and the update, and answer with the form’s check without writing anything:
text: what would be written.field_errors: each field the create or update would refuse: the posting and the field, akindsuch asinvalid_amountorbeancount_commoditywith thevalueit is about, and the message of the 400.unbalanced: what the transaction is unbalanced by.errors: the errors the ledger would report.
Where new entries are written
Section titled “Where new entries are written”Zhang appends what you record in the web UI to a file chosen by the entry’s date and the directive_output_path option. The default is data/{{year}}/{{month_str}}.{{ext}}, where ext is the extension of the main file. A coffee recorded for 3 April 2024 goes to data/2024/04.zhang:
2024-04-03 13:15:00 * "Coffee Lab" "Flat white" Assets:Cash -28 CNY Expenses:Food- If the ledger does not read that file yet, Zhang also adds
include "data/2024/04.zhang"at the end of the main file. - Everything else the web UI adds as a new entry goes to the same place: balance assertions written on an account page or with the Batch balance tool, and documents uploaded to an account.
- A different template changes the layout.
option "directive_output_path" "data/{{year}}.{{ext}}"keeps one file per year. The template can useyear,month,month_str,day,day_str,type(the kind of entry) andext. - In a beancount ledger the file has the main file’s extension, such as
.bean, and is written in beancount syntax, with the time of day in atimemetadata entry.
When something is wrong
Section titled “When something is wrong”Zhang checks the ledger every time it loads it.
- Problems in an entry go to the ledger’s error list. The Overview item in the sidebar shows how many there are, and the Overview page lists them: select one to see the entry it is about and to open its file in Raw Editing at that line. Typical problems are a transaction that does not balance, an account that is not open and a commodity that is not declared. Error Codes explains each one.
- A transaction that does not balance stays in the ledger, marked Unbalanced in the journal.
- A transaction whose missing amount cannot be filled in, for example one with two postings without an amount, is left out of the ledger until you fix it.
- A syntax error stops the whole ledger from loading. When Zhang starts, it exits with the error and the file, line and column where it found it. When the error appears while Zhang is running, Zhang keeps serving the last version that loaded and only logs the error: the web UI shows no error. If your changes do not show up, look at the terminal, or at
docker logs.
Reloading
Section titled “Reloading”For a ledger on the local disk, zhang serve watches the ledger directory and reloads when one of the ledger’s files changes, so an edit in your text editor shows up within a second or two: see Local File System for what triggers a reload. Recording something in the web UI, or saving a file on the Raw Editing page, reloads the ledger too.
The Reload ledger button, the circular arrow next to the ledger’s title in the sidebar, reloads it on demand. Use it for a ledger on S3, WebDAV or GitHub: there Zhang does not notice changes made by other programs.
