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",
"context": [
{ "relation": "invoiceLines", "sort": "lineNumber" }
]
}
]
}
PropertyTypeDescription
namestringUnique name for this report within the class.
scopeOBJECTOnly OBJECT is currently supported.
templateFilestringPath to a Handlebars template file in the repository, without 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 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 context to get the full nested object.

ReportContext

PropertyTypeDescription
relationstringThe relation name to include. Use the plural name for multiple-valued relations, the singular name for single-valued relations.
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": "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:

{{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 }}.
dataTypestringValue data type (e.g. TEXT, NUMBER, 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 in the repository (without 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",
"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:

<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 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"
}
]
}
]
}