Automating Beancount data input with custom forms makes your life 10 times easier!
We are excited to announce a fantastic new feature for BeanHub, the custom forms. Like many of you, we use BeanHub to help ease the burden of maintaining our books daily. This feature will make your life 10 times easier when adding new entries.
Generic entry forms
In the past, we realized that adding a new transaction and other entries is the most common task we need to perform on a daily basis. With that in mind, we built and released the Add Transaction, Open Account, Close Account, and Add Commodity forms with autocomplete for each input field last year.
Since then, inserting a new entry has been way more straightforward than ever. Here’s the entry form in action.
Adding a new entry without BeanHub is tedious, particularly if you keep your books with a version control system such as Git. There are a few steps you need to do to add a transaction with Git and store it in GitHub:
- Open the Beancount files with your editor
- Type all the content of your transaction in the Beancount file
- Save the changes
- Run Beancount commands to check the correctness
- Run
git add
the changed file - Run
git commit
to create a new commit - Run
git push
to push the new commit you just made
Even if your changes are just a single line, you still need to go through seven steps, and that’s how inefficient it is to maintain a Beancount accounting book with Git manually. Thankfully, with BeanHub and the forms for adding entries, we reduce the steps down to just a few:
- Open the BeanHub form page
- Fill the forms
- Submit
That’s it! BeanHub performs all the magic for you behind the scene. You don’t need to remember the exact account name with the input field auto-completion. When you submit, it checks the syntax and balance for you so that you can be sure that your book is always correct and balanced. The Git commit is also made for you automatically.
Custom forms
Our users genuinely love this feature, but still, there is room for improvement. We noticed that the daily transactions we add to our book are mostly identical except for the number. For example, to log the spent Software Development contracting hours in the account receivable for our clients at the end of a working day, we always create a transaction like this one:
2023-07-03 * "Hours spent on the software development project for client XYZ"
Assets:AccountsReceivable:Contracting:XYZ 17 XYZ.HOUR @ 300 USD
Income:Contracting:XYZ
With the Add Transaction form, we need to type Hours spent on the software development project for client XYZ
, and select Assets:AccountsReceivable:Contracting
as the first posting account, then Income:Contracting
as the second posting account.
Type 17
for the posting amount and pick XYZ.HOUR as the unit.
That’s still lots of repeating work, and we have to do this frequently.
We wondered, since most of the values we input are the same, why not make a custom form only for this particular purpose and require only the essential variables?
With the idea, the custom forms feature was born.
Now you define custom forms by writing a YAML file at .beanhub/forms.yaml
, add it to your local Git repository then push it to BeanHub.
Taking the contracting hours needs mentioned above as an example, here’s the form you can build.
forms:
- name: add-xyz-hours
display_name: "Hours spent on XYZ contracting project"
fields:
- name: date
type: date
display_name: "Date"
required: true
- name: hours
type: number
display_name: "Hours"
required: true
- name: rate
type: number
display_name: "Rate (USD)"
default: "300"
required: true
- name: narration
type: str
default: "Hours spent on the software development project for client XYZ"
display_name: "Narration"
operations:
- type: append
file: "books/{{ date.year }}.bean"
content: |
{{ date }} * {{ narration | tojson }}
Assets:AccountsReceivable:Contracting:XYZ {{ hours }} XYZ.HOUR @ {{ rate }} USD
Income:Contracting:XYZ
After pushing it to your BeanHub repository, you should be able to see it on the new Forms
page from the left-hand menu of your repository.
Then click on the link to the form. You can see the form like this:
Here you go!
Now, instead of typing repeating words to record contracting hours to your Beancount accounting books, you only need to type the hours and press submit, and BeanHub will do the rest for you.
As you may notice, the content
field of the form operations is some sort of template, and yes, it is.
We are using the Jinja2 template engine here.
It allows you to define your own logic for generating the Beancount entries.
As you can see, it is an extremely powerful feature, allowing you to make customized forms for your routine accounting data entry workflow effortlessly.
The syntax
Now we know how powerful and valuable the custom form is. Let’s see the syntax for the form definition YAML file.
Form Doc
The root object of the form YAML at .beanhub/forms.yaml
is a Form Doc, and at this moment, it contains only the key forms
to the list of form definitions.
One Form Doc can have multiple Form Definitions.
Form Definition
A Form Definition is an object consisting of the following keys:
name
is the unique slug (URL-friendly name string) id of this form in the Form Doc (required)fields
is the list of Field Definitions (required)operations
is the list of Operation Definitions. (required)display_name
is the display name of the form. If not provided, thename
value will be used (optional)commit
is the Commit Options object (optional)auto_format
is the boolean flag to determine whether to auto-format the Beancount files after the form operations update them. By default, it’s true. (optional)
Field Definition
A Form Definition is an object consisting of the following keys:
name
is the unique input field variable name to be referenced in the operation template (required)type
is the type of the field. It determines which kind of input UI to present in the form. For the available types of fields, please see below. (required)display_name
is the display name of the field. If not provided, thename
value will be used (optional)default
is the default value for the field (optional)required
is the boolean flag to determine whether this field is required. By default, it’s false (optional)
At this moment, we provide six field types as listed below:
str
simple string fieldnumber
number fielddate
date fieldfile
Beancount file path fieldcurrency
currency fieldaccount
account field
For the file
, currency
, and account
fields, only the current present values in your Beancount books are for selection in the input UI.
However, we provide extra parameters for these three fields to enable inputting a value that doesn’t exist in your Beancount books.
Here are the extra keys for these three fields:
File field
creatable
is a boolean flag to determine whether is inputting a currently non-existing Beancount file path allowed. By default, it’s false (optional)
Account field
creatable
is a boolean flag to determine whether is inputting a currently non-existing account allowed. By default, it’s false (optional)
Currency field
creatable
is a boolean flag to determine whether is inputting a currently non-existing currency allowed. By default, it’s false (optional)multiple
is a boolean flag to determine whether is inputting multiple currency values allowed. By default, it’s false (optional)
Operation Definition
An operation of the form is for performing update operations to your Beancount files based on the form input values. It is an object consisting of the following keys:
type
is the type of operation. Currently, onlyappend
is supported (required)file
is the path to the target file for the operation to perform. This parameter will be rendered as a template (required)content
is the content of the operation to perform, such as the text to append to the file. This parameter will be rendered as a template (required)
Commit Options
The Commit Options is an object for changing the default Git commit behavior. It’s an object consisting of the following keys:
message
is the message of the Git commit to make. This parameter will be rendered as a template (optional).
The template syntax
For more details about the template syntax, please see the Jinja2 documents.
At this moment, most of the Jinja2 syntax is supported.
When rendering a template parameter, all the available input form fields will be provided as a variable.
Please note that the Python object type of variables is going to be different based on the field’s type
value.
Here is the table of variable types for each field:
Field Type | Template variable type |
---|---|
str | str |
number | decimal.Decimal |
date | datetime.date |
account | str |
file | str |
currency (single) | str |
currency (multiple) | list[str] |
Date-based file name and the new files include issue
As you can see in our contracting hour form example, we use books/{{ date.year }}.bean
as the target file path for the append operation.
If the file exists, BeanHub will append the generated content.
Otherwise, a new file at books/<year of input date>.bean
will be created with the generated content.
This is fantastic because the correct book of the year of input date will be used.
The same trick can also be used if you would like to write entries based on a different unit of time, say a quarter.
For keeping books by different quarters, the file path template can be written like this.
books/{{ date.year }}Q{{ ((date.month - 1) // 3) + 1 }}.bean
Then you will have BeanCount book files like
- books/2023Q1.bean
- books/2023Q2.bean
- books/2023Q3.bean
- ….
This trick works great if the file already exists.
However, the new file needs a include
directive from the entry main.bean
file or an already included Beancount file for Beancount to consider it part of the books.
This can only be done manually for now.
We are working on a new feature allowing you to define rules for generating include
directives automatically soon.
Upcoming price change
We continuously improve BeanHub and adjust the price to reflect our newly added features. The monthly/annual price of the Pro plan is going to increase from $8/$10 to $9/$11 per month on August 15, 2023. Subscribe it now before the price goes up!
Final thoughts
Thank you for supporting BeanHub so far. We are looking forward to seeing how you use the new form feature. You can see the forms we created for our demo repository here. If you want to try out the custom form feature before you purchase a paid plan, you can sign-up and create a public repository and try it out there first. Just note that the public repositories are visible to the internet, so please do not share your actual personal data there. Finally, as always, if you have any feedback or need our help, please feel free to reach out to us at support@beanhub.io.