blog

Automating Beancount data input with custom forms makes your life 10 times easier!

July 31, 2023
form
new-feature
automation

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.

The screenshot of BeanHub generic form buttons

Since then, inserting a new entry has been way more straightforward than ever. Here’s the entry form in action.

The screencast of operating a BeanHub 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:

  1. Open the Beancount files with your editor
  2. Type all the content of your transaction in the Beancount file
  3. Save the changes
  4. Run Beancount commands to check the correctness
  5. Run git add the changed file
  6. Run git commit to create a new commit
  7. 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:

  1. Open the BeanHub form page
  2. Fill the forms
  3. 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:

The screencast of BeanHub custom form

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, the name 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, the name 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 field
  • number number field
  • date date field
  • file Beancount file path field
  • currency currency field
  • account 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, only append 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 TypeTemplate variable type
strstr
numberdecimal.Decimal
datedatetime.date
accountstr
filestr
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.