Skip to main content

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.hbs",
"context": [
{ "relation": "my-project.invoiceLine", "sort": "lineNumber" }
]
}
]
}
PropertyTypeDescription
namestringUnique name for this report within the class.
scopeOBJECTOnly OBJECT is currently supported.
templateFilestringPath to a Handlebars template file, relative to the repository root, including the .hbs extension.
contextReportContext[]Multiple-valued relations to include in the template context.
parametersParameter[]Computed values available in the template as {{ params.name }}. Evaluated at render time.

What is automatically available​

The root object's own 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 relation to my-project.client, {{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 context to get the full nested object.

ReportContext​

PropertyTypeDescription
relationQualifiedNameThe class the relation leads to, e.g. my-project.invoiceLine: a relation of this object to that class, or else the objects of that class that relate to this one. In the template it appears under its REST name — singular for a single-valued relation (client), plural for a multiple-valued one (invoiceLines).
sortstringOptional sort order: "fieldName" for ascending, "fieldName,desc" for descending. Only applies to multiple-valued relations.
contextReportContext[]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": "my-project.task",
"sort": "dueDate",
"context": [
{ "relation": "my-project.comment", "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": "my-project.client",
"context": [
{ "relation": "my-project.address" }
]
},
{ "relation": "my-project.invoiceLine", "sort": "lineNumber" }
]
}

In the template:

{{client}}            {{!-- just the name, no context declaration needed --}}
{{client.city}} {{!-- field access requires context declaration --}}
{{#each client.addresses}}
{{street}}, {{city}}
{{/each}}

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:

{{params.totalAmount}}

Parameters share the same structure as script parameters. Each parameter must have a name and exactly one value source:

PropertyTypeDescription
namestringKey used to access the value in the template as {{ params.name }}.
dataTypestringData type (e.g. TEXT, DECIMAL, CURRENCY).
multiplebooleanIf true, the parameter resolves to a list. Default: false.
valueanyA literal value.
formulastringA formula expression evaluated on the root object.
templatestringA Handlebars template string evaluated on the root object.
templateFilestringPath to a Handlebars template file, relative to the repository root, including the .hbs extension.
fieldstringField 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.hbs",
"parameters": [
{ "name": "travelTotal", "dataType": "DECIMAL", "formula": "sumIf(lines.category, 'TRAVEL', lines.amount)" },
{ "name": "hotelTotal", "dataType": "DECIMAL", "formula": "sumIf(lines.category, 'HOTEL', lines.amount)" },
{ "name": "mealTotal", "dataType": "DECIMAL", "formula": "sumIf(lines.category, 'MEALS', lines.amount)" }
],
"context": [
{ "relation": "my-project.line", "sort": "date" }
]
}

In the template:

<p>Travel: {{params.travelTotal}}</p>
<p>Hotel: {{params.hotelTotal}}</p>
<p>Meals: {{params.mealTotal}}</p>

{{#each lines}}
<p>{{date}} — {{category}} — {{amount}}</p>
{{/each}}
Replacing computed fields on the class

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.

<h1>{{name}}</h1>
<p>Date: {{invoiceDate}}</p>
<p>Client: {{client}}</p> <!-- name string, no context declaration needed -->

<table>
<thead>
<tr><th>Description</th><th>Amount</th></tr>
</thead>
<tbody>
{{#each invoiceLines}}
<tr>
<td>{{description}}</td>
<td>{{amount}}</td>
</tr>
{{/each}}
</tbody>
</table>

<p>Total: {{total}}</p>

Template files use the .hbs extension and are stored in the repository alongside the class files. Reference them by their path, including 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:

{
"version": 1,
"class": "my-project.invoice",
"kind": "BASIC",
"inherits": "commons.item",

"fields": [
{ "name": "invoiceDate", "dataType": "DATE" },
{ "name": "total", "dataType": "CURRENCY" }
],

"reports": [
{
"name": "invoicePDF",
"scope": "OBJECT",
"templateFile": "templates/invoicePDF.hbs",
"context": [
{ "relation": "my-project.invoiceLine", "sort": "lineNumber" }
]
}
],

"layouts": [
{
"kind": "OBJECT_DASHBOARD",
"items": [
{ "name": "name", "header": true },
{ "name": "invoiceDate" },
{ "name": "total" },
{
"type": "ACTION",
"name": "invoicePDF",
"label": "Print invoice",
"actionType": "REPORT"
}
]
}
]
}