Reports
Reports generate a document (HTML or PDF) for a specific object using a Handlebars template. They are defined on a class alongside an optional context that brings in related objects, and triggered from a layout via an ACTION button.
Definition
{
"reports": [
{
"name": "invoicePDF",
"scope": "OBJECT",
"templateFile": "templates/invoicePDF",
"context": [
{ "relation": "invoiceLines", "sort": "lineNumber" }
]
}
]
}
| Property | Type | Description |
|---|---|---|
name | string | Unique name for this report within the class. |
scope | OBJECT | Only OBJECT is currently supported. |
templateFile | string | Path to a Handlebars template file in the repository, without the .hbs extension. |
context | ReportContext[] | Multiple-valued relations to include in the template context. |
parameters | Parameter[] | Computed values available in the template as {{ params.name }}. Evaluated at render time. |
What is automatically available
The root object's own value type fields are always available as top-level variables.
Single-valued relations (multiple: false) are included automatically as a name string — no declaration needed. If an invoice has a client relation, {{client}} renders the client's name. To access individual fields on the related object (e.g. {{client.city}}), declare it explicitly in context.
context is needed in two cases:
- Multiple-valued relations (one-to-many or many-to-many) — these become arrays in the template:
{{#each invoiceLines}}. - Single-valued relations where you need field access — declare the relation in
contextto get the full nested object.
ReportContext
| Property | Type | Description |
|---|---|---|
relation | string | The relation name to include. Use the plural name for multiple-valued relations, the singular name for single-valued relations. |
sort | string | Optional sort order: "fieldName" for ascending, "fieldName,desc" for descending. Only applies to multiple-valued relations. |
context | ReportContext[] | Nested context, resolved relative to each matched relation object. |
Multiple-valued relation with nested context
A report on a project that includes each task's comments:
{
"context": [
{
"relation": "tasks",
"sort": "dueDate",
"context": [
{ "relation": "comments", "sort": "createdAt,desc" }
]
}
]
}
In the template, tasks is a list and each task has a comments list.
Single-valued relation with field access
Declaring a single-valued relation in context gives access to its individual fields and any nested multiple-valued data:
{
"context": [
{
"relation": "client",
"context": [
{ "relation": "addresses" }
]
},
{ "relation": "invoiceLines", "sort": "lineNumber" }
]
}
In the template:
Parameters
Parameters pre-compute values before the template renders. Each resolved value is available in the template under the params object — a separate namespace from the root object's own fields:
Parameters share the same structure as script parameters. Each parameter must have a name and exactly one value source:
| Property | Type | Description |
|---|---|---|
name | string | Key used to access the value in the template as {{ params.name }}. |
dataType | string | Value data type (e.g. TEXT, NUMBER, CURRENCY). |
multiple | boolean | If true, the parameter resolves to a list. Default: false. |
value | any | A literal value. |
formula | string | A formula expression evaluated on the root object. |
template | string | A Handlebars template string evaluated on the root object. |
templateFile | string | Path to a Handlebars template file in the repository (without extension). |
field | string | Field name on the root object. Shorthand for a single-field formula. |
Example: category totals
A report that breaks down expense lines per category, without storing those totals in the data model:
{
"name": "expenseSummary",
"scope": "OBJECT",
"templateFile": "templates/expenseSummary",
"parameters": [
{ "name": "travelTotal", "dataType": "NUMBER", "formula": "sumIf(lines.category, 'TRAVEL', lines.amount)" },
{ "name": "hotelTotal", "dataType": "NUMBER", "formula": "sumIf(lines.category, 'HOTEL', lines.amount)" },
{ "name": "mealTotal", "dataType": "NUMBER", "formula": "sumIf(lines.category, 'MEALS', lines.amount)" }
],
"context": [
{ "relation": "lines", "sort": "date" }
]
}
In the template:
Parameters replace the older pattern of adding persistent report-only fields and formulas to the DataClass. Values that are only needed at render time belong in parameters, not in the data model.
Template
The template receives the root object's fields as top-level variables, single-valued relations as name strings, and relations declared in context as full objects or arrays.
Template files use the .hbs extension and are stored in the repository alongside the class files. Reference them without the extension in templateFile.
Triggering from a layout
Add an ACTION item with actionType: "REPORT" to open the report in a new browser tab:
{
"type": "ACTION",
"name": "invoicePDF",
"label": "Print invoice",
"actionType": "REPORT"
}
The name must match a report defined on the same class. See Layouts — ACTION for the full ACTION item reference.
Complete example
An invoice class with a PDF report that includes its lines, sorted by line number:
{
"class": "my-project.invoice",
"type": "BASIC",
"inherits": "commons.item",
"valueTypes": [
{ "name": "invoiceDate", "dataType": "DATE" },
{ "name": "total", "dataType": "CURRENCY" }
],
"reports": [
{
"name": "invoicePDF",
"scope": "OBJECT",
"templateFile": "templates/invoicePDF",
"context": [
{ "relation": "invoiceLines", "sort": "lineNumber" }
]
}
],
"layouts": [
{
"type": "OBJECT_DASHBOARD",
"items": [
{ "name": "name", "header": true },
{ "name": "invoiceDate" },
{ "name": "total" },
{
"type": "ACTION",
"name": "invoicePDF",
"label": "Print invoice",
"actionType": "REPORT"
}
]
}
]
}