Generating documents
In many cases, you might need to generate downloadable documents from the data of your Bobuild app. This powerful feature lets you design a completely custom template and have it filled by Bobuild on-the-fly within your app.
Bobuild supports two kinds of templates:
- Docx templates — design them in Microsoft Word or LibreOffice Writer; they can generate
.docxor.pdffiles. - Xlsx templates — design them in Microsoft Excel or LibreOffice Calc; they generate
.xlsxfiles.
Some examples of documents you might want to generate:
- invoices
- reports
- certificates
- spreadsheets and data exports
Managing templates in the Builder
Open Document templates in the app settings area to add Docx or Xlsx templates, name them, bind them to an object, upload or download the template file, and map single-value and list variables. Keep unfinished designs as separate templates until you are ready to point tasks at them — that avoids breaking live generate-document actions while you iterate.
Creating a Docx template
Use Microsoft Word or LibreOffice and design your template with no restrictions. Then, put some placeholders in the document where you want Bobuild to inject data.
In order to inject a simple variable, use this syntax:
{{ myvariable }}
Note: always put a space before and after the variable name!
If you want to use conditional logic, use this syntax:
My house is located {% if living_in_town %} in urban area
{% else %} in countryside {% endif %} and I love it.
If you want to repeat a block, use this syntax:
{% for p in products %}
- Product: {{ p.name }}
{% endfor %}
If you want to do repeat a table row, use this syntax:
| Products |
|---|
{%tr for p in products %} |
Product: {{ p.name }} |
{%tr endfor %} |
How does this work? If a tag starts with {%tr instead of just {%, the entire table row where it is located will be removed from the output. So, in this example, the second and the fourth row will be removed, and just the third row will be repeated for each item.
Creating an Xlsx template
Use Microsoft Excel or LibreOffice Calc and design your template with no restrictions. Placeholders use the same Jinja-based syntax as Docx templates.
In order to inject a simple variable:
{{ foo }}
To transform a value with a Jinja filter:
{{ foo|upper }}
For conditional logic:
{% if foo %}bar{% else %}baz{% endif %}
To repeat a block of rows, use the tr prefix so the row containing the tag itself is removed from the output:
{%tr for item in items %}
{{ item.name }}
{%tr endfor %}
If a tag starts with {%tr instead of just {%, the entire row where it is located will be removed from the output, and only the rows between the opening and closing tags will be repeated for each item in the collection.
Placeholders work in sheet names as well, so you can name tabs dynamically from your data.
Jinja syntax
Docx and Xlsx templates both use Jinja2, a widely used templating language. The examples above cover variables, conditionals, and loops; the Jinja template designer documentation is the full reference for everything else you can use in a template.
A few conventions worth remembering:
- Put a space inside double curly braces:
{{ myvariable }}, not{{myvariable}}. - Access object fields with dot notation:
{{ customer.name }}. - Chain filters with
|:{{ value|filter1|filter2 }}.
Useful filters
Filters transform a value using the pipe syntax {{ value|filter }}. These filters are especially handy in document templates:
length — count items in a collection:
You have {{ products|length }} products in this order.
round — round a number (optionally specify decimal places):
Total: {{ invoice.total|round(2) }}
format — build a string from a format pattern (similar to Python's % formatting):
{{ "Invoice #%s dated %s"|format(invoice.number, invoice.date) }}
selectattr — keep only the items in a collection whose attribute matches a test. This is useful when you need the rows that belong to a parent record (see the next section):
{% for order in orders|selectattr("customer_id", "equalto", customer.id) %}
Order: {{ order.number }} - {{ order.date }}
{% endfor %}
Reports with parent and child records
Many reports list a parent record (such as a customer) and, underneath it, the related child records (such as that customer's orders). You typically expose both collections to the template—customers and orders—and nest loops so the inner loop only prints rows that belong to the current parent:
{% for customer in customers %}
Customer: {{ customer.name }}
{% for order in orders %}
{% if order.customer_id == customer.id %}
Order: {{ order.number }} - {{ order.date }}
{% endif %}
{% endfor %}
{% endfor %}
The same result is often easier to read with selectattr, which filters the inner collection instead of wrapping every line in {% if %}:
{% for customer in customers %}
Customer: {{ customer.name }}
{% for order in orders|selectattr("customer_id", "equalto", customer.id) %}
Order: {{ order.number }} - {{ order.date }}
{% endfor %}
{% endfor %}
In Docx tables and Xlsx row ranges, use {%tr for %} / {%tr endfor %} on the rows you want repeated, as described in the sections above.
Configuring the template
Once you've crafted your template, you'll need to go to the Builder and configure the data source for the template. You'll define two things:
- which object fields shall be made available to the template (in the above examples, the
myvariable,foo, andliving_in_townfields) - which tabular data shall be also made available to the template (in the above examples, the
productsoritemsvariables which hold a collection of records extracted using some criteria)
Using the template
At this point you're all set! Just configure the task wherever you need in the app, and point it to your template. You'll need to configure the following settings:
| Setting | Description |
|---|---|
| File name | The file name to use for the generated file |
| Format | .docx or .pdf for Docx templates; .xlsx for Xlsx templates |
| Field | The object field where you want the generated document to be stored. |
When the format is PDF, Bobuild generates a PDF/A-2b file (with PDF/UA for accessibility) suitable for archival and digitally signed workflows.
If generation fails (template errors, conversion issues, and similar), the failure is recorded under Error Logs in the Builder (type Document generation), with template and record context when available. See Error Logs.
Common patterns
A common way to let users generate and download a document is to place an empty form, with just a submit button, that has a "Generate document" task, and then redirect to another page (or even to the same page) where the field holding the generated document is displayed so that users can click on it and download the document.