> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Template reference

> Reference every template tag, filter, and operator available in Reporter — loops, conditionals, placeholders, and built-in formatting helpers.

This page is a complete reference for all template tags, filters, and operators available in Reporter. For an introduction to templates and placeholders, see [What is Reporter](/en/reporter/what-is-reporter).

## Building templates

***

### Common blocks

* **Loop**

```
{% for <item> in <list> %}
  ...
{% endfor %}
```

* **Loop with explicit schema**

```
{% for order in external_db:sales.orders %}
  {{ order.id }} - {{ order.total }}
{% endfor %}
```

* **Simple condition**

```
{% if value_a == value_b %}
  ...
{% endif %}
```

* **Temporary scope**

```
{% with <object> as <alias> %}
  ...
{% endwith %}
```

* **Value formatting**

```
{{ field_name | floatformat:2 }}   --> renders 123.45
```

### Conditional blocks

| Block                 | Description                               | Example                                                |
| --------------------- | ----------------------------------------- | ------------------------------------------------------ |
| If                    | Runs block if condition is true           | `{% if condition %}...{% endif %}`                     |
| If-else               | Runs one block if true, another if false  | `{% if condition %}...{% else %}...{% endif %}`        |
| If-else-if            | Allows multiple checks                    | `{% if a %}...{% elif b %}...{% else %}...{% endif %}` |
| Equal                 | Checks if two values are equal            | `{% if a == b %}`                                      |
| Not equal             | Checks if two values are different        | `{% if a != b %}`                                      |
| Greater than          | Checks if a is greater than b             | `{% if a > b %}`                                       |
| Less than             | Checks if a is less than b                | `{% if a < b %}`                                       |
| Greater than or equal | Checks if a is greater than or equal to b | `{% if a >= b %}`                                      |
| Less than or equal    | Checks if a is less than or equal to b    | `{% if a <= b %}`                                      |
| And                   | Returns true if both conditions true      | `{% if a and b %}`                                     |
| Or                    | Returns true if at least one true         | `{% if a or b %}`                                      |
| Not                   | Inverts Boolean result                    | `{% if not a %}`                                       |

## Tags reference

***

### Aggregation tags

**sum\_by** -- Sums numeric values from a field across all items in a collection.

```
{% sum_by <collection> by <field> %}
{% sum_by <collection> by <field> if <condition> %}
```

**Example:**

```xml theme={null}
<Sum>
  {% sum_by transaction.operation by "amount" if accountAlias != "@external/BRL" %}
</Sum>
```

**count\_by** -- Counts the number of items in a collection.

```
{% count_by <collection> %}
{% count_by <collection> if <condition> %}
```

**Example:**

```xml theme={null}
<Count>
  {% count_by transaction.operation if accountAlias != "@external/BRL" %}
</Count>
```

**avg\_by** -- Calculates the average of numeric values in a field.

```
{% avg_by <collection> by <field> %}
{% avg_by <collection> by <field> if <condition> %}
```

**min\_by** -- Finds the minimum numeric value in a field.

```
{% min_by <collection> by <field> %}
{% min_by <collection> by <field> if <condition> %}
```

**max\_by** -- Finds the maximum numeric value in a field.

```
{% max_by <collection> by <field> %}
{% max_by <collection> by <field> if <condition> %}
```

<Note>
  All aggregation tags use decimal precision to avoid floating-point rounding errors. Missing or non-numeric fields are skipped. Returns `0` if no items match.
</Note>

### Date and time tag

**date\_time** -- Outputs the current date and time formatted according to the provided format string. Time is generated in UTC.

```
{% date_time "<format>" %}
```

**Format codes:**

| Code   | Meaning            | Example |
| ------ | ------------------ | ------- |
| `YYYY` | 4-digit year       | 2025    |
| `MM`   | 2-digit month      | 01-12   |
| `dd`   | 2-digit day        | 01-31   |
| `HH`   | 2-digit hour (24h) | 00-23   |
| `mm`   | 2-digit minute     | 00-59   |
| `ss`   | 2-digit second     | 00-59   |

**Examples:**

```
{% date_time "YYYY-MM-dd" %}           --> 2025-02-06
{% date_time "dd/MM/YYYY HH:mm:ss" %} --> 06/02/2025 14:30:45
```

### Arithmetic tag

**calc** -- Evaluates mathematical expressions with support for variables from the template context.

```
{% calc <expression> %}
```

**Supported operators:**

| Operator | Description              | Precedence              |
| -------- | ------------------------ | ----------------------- |
| `**`     | Exponentiation           | Highest (right-to-left) |
| `*` `/`  | Multiplication, division | Middle                  |
| `+` `-`  | Addition, subtraction    | Lowest                  |
| `( )`    | Parentheses              | Override precedence     |

**Examples:**

```
{% calc 100 + 50 %}                                --> 150
{% calc balance.available * 0.5 %}                 --> calculated value
{% calc (balance.available + 1.2) * balance.on_hold - balance.available / 2 %}
```

<Note>
  Variables that cannot be resolved default to `0`. Division by zero produces an error.
</Note>

### Financial aggregation tag

**aggregate\_balance** -- Groups items by a field, selects the most recent entry per account within each group, and sums the balances. Useful for regulatory reports that require the latest balance per account grouped by category.

```
{% aggregate_balance <collection> by "<balance_field>" group_by "<group_field>" order_by "<date_field>" [if <condition>] as <result_var> %}
```

The result is stored in a variable you can iterate over:

```
{% aggregate_balance accounts by "balance" group_by "cosif_code" order_by "created_at" as balances %}
{% for b in balances %}
  {{ b.group_value }}: {{ b.balance }} ({{ b.count }} accounts)
{% endfor %}
```

Each result item contains:

| Field         | Type    | Description                                     |
| ------------- | ------- | ----------------------------------------------- |
| `group_value` | string  | The value from the `group_by` field             |
| `balance`     | decimal | Sum of latest balances per account in the group |
| `count`       | integer | Number of accounts in the group                 |

<Note>
  Maximum collection size: 100,000 items. Results are sorted by `group_value`.
</Note>

### Counter tags

**counter** -- Increments a named counter by 1. Produces no output. Counters are scoped per render.

```
{% counter "<counter_name>" %}
```

**counter\_show** -- Displays the sum of one or more named counters.

```
{% counter_show "<name1>" %}
{% counter_show "<name1>" "<name2>" "<name3>" %}
```

**Example:**

```
{% for tx in ledger.transactions %}
  {% counter tx.type %}
{% endfor %}
Total credits: {% counter_show "credit" %}
Total debits: {% counter_show "debit" %}
Combined: {% counter_show "credit" "debit" %}
```

## Filters reference

***

### percent\_of

Calculates the percentage of a value relative to a total. Returns a formatted string with 2 decimal places.

```
{{ value | percent_of: total }}
```

Example: if `category.amount = "6.00"` and `total.expenses = "20.00"`:

```
{{ category.amount | percent_of: total.expenses }}  --> 30.00%
```

### strip\_zeros

Removes trailing zeros from a numeric value without rounding.

```
{{ number | strip_zeros }}
```

**Examples:**

```
{{ "100.50000" | strip_zeros }}  --> 100.5
{{ "100.00" | strip_zeros }}     --> 100
{{ "99.990" | strip_zeros }}     --> 99.99
```

### slice

Extracts a substring using start and end indices (0-based).

```
{{ string | slice:"start:end" }}
```

**Examples:**

```
{{ "hello" | slice:"0:3" }}  --> hel
{{ "12345" | slice:"1:4" }}  --> 234
```

### replace

Replaces all occurrences of a search string with a replacement string. Format: `"search:replacement"`.

```
{{ string | replace:"search:replacement" }}
```

**Examples:**

```
{{ "01310-100" | replace:"-:" }}      --> 01310100   (removes hyphens)
{{ "1234.56" | replace:".:," }}       --> 1234,56    (dot to comma)
{{ "12.345.678/0001-99" | replace:".:" }}  --> 12345678/0001-99
```

### where

Filters an array of objects by field equality. Supports nested fields via dot notation.

```
{{ array | where:"field:value" }}
```

**Examples:**

```
{{ holders | where:"state:SP" }}
{{ holders | where:"address.state:SP" }}
```

Use inside loops:

```
{% for holder in holders|where:"state:SP" %}
  {{ holder.name }}
{% endfor %}
```

### sum (filter)

Sums numeric values from a field across all items in an array. Uses decimal precision.

```
{{ array | sum:"field" }}
```

**Examples:**

```
{{ operations | sum:"amount" }}
{{ items | sum:"price.value" }}
```

### count (filter)

Counts elements in an array where a field matches a value. Supports nested fields.

```
{{ array | count:"field:value" }}
```

**Examples:**

```
{{ operations | count:"nat_oper:6" }}
{{ holders | count:"address.state:SP" }}
```

### contains

Checks if one value is partially included in another. Useful when data includes dynamic prefixes or suffixes.

```
{% if contains(source_field, target_field) %}
```

**Example:**

* Source: `0#@external/BRL`
* Target: `@external/BRL`

Returns `true` because `@external/BRL` exists within the source value.

## Operators and filters summary

***

| Name                | Type     | Description                              |
| ------------------- | -------- | ---------------------------------------- |
| `sum_by`            | Tag      | Sum values by field with optional filter |
| `count_by`          | Tag      | Count items with optional filter         |
| `avg_by`            | Tag      | Calculate average by field               |
| `min_by`            | Tag      | Find minimum value                       |
| `max_by`            | Tag      | Find maximum value                       |
| `date_time`         | Tag      | Format current date/time                 |
| `calc`              | Tag      | Evaluate arithmetic expressions          |
| `aggregate_balance` | Tag      | Grouped financial balance aggregation    |
| `counter`           | Tag      | Increment a named counter                |
| `counter_show`      | Tag      | Display counter value(s)                 |
| `percent_of`        | Filter   | Calculate percentage                     |
| `strip_zeros`       | Filter   | Remove trailing zeros                    |
| `slice`             | Filter   | Extract substring                        |
| `replace`           | Filter   | String replacement                       |
| `where`             | Filter   | Filter array by field value              |
| `sum`               | Filter   | Sum array field values                   |
| `count`             | Filter   | Count matching items                     |
| `contains`          | Function | Partial string match                     |
| `floatformat`       | Filter   | Format decimal places                    |

## Advanced filtering

***

When generating a report, you can pass filters in the request body to narrow the data. Filters follow a structure of datasource > table > field:

**Single schema (default):**

```json theme={null}
{
  "templateId": "00000000-0000-0000-0000-000000000000",
  "filters": {
    "midaz_onboarding": {
      "account": {
        "id": { "eq": ["123", "456"] },
        "createdAt": { "between": ["2023-01-01", "2023-01-31"] },
        "status": { "in": ["active", "pending"] }
      }
    }
  }
}
```

**Multi-schema (explicit schema.table key):**

```json theme={null}
{
  "templateId": "00000000-0000-0000-0000-000000000000",
  "filters": {
    "external_db": {
      "sales.orders": {
        "total": { "gt": [100] },
        "created_at": { "gte": ["2025-01-01"] }
      },
      "finance.invoices": {
        "status": { "eq": ["paid"] }
      }
    }
  }
}
```

**Supported operators:**

| Operator  | Description                | Example                              |
| --------- | -------------------------- | ------------------------------------ |
| `eq`      | Equal to                   | `{ "eq": ["active", "pending"] }`    |
| `gt`      | Greater than               | `{ "gt": [100] }`                    |
| `gte`     | Greater than or equal to   | `{ "gte": ["2025-06-01"] }`          |
| `lt`      | Less than                  | `{ "lt": [1000] }`                   |
| `lte`     | Less than or equal to      | `{ "lte": ["2025-06-30"] }`          |
| `between` | Value falls within a range | `{ "between": [100, 1000] }`         |
| `in`      | Value is within a list     | `{ "in": ["active", "pending"] }`    |
| `nin`     | Value is not within a list | `{ "nin": ["deleted", "archived"] }` |
