Pular para o conteúdo principal
O Reporter é o mecanismo de geração de relatórios open-source da Lerian que permite que equipes criem relatórios dinâmicos e orientados a dados por meio de templates simples em texto puro (arquivos .tpl). Os templates são renderizados em diversos formatos de saída, mantendo a estrutura do documento que você define. O código-fonte está disponível publicamente no GitHub.
Formato do templateFormato de saída
.tpl estruturado como CSVArquivo CSV
.tpl estruturado como XMLArquivo XML
.tpl estruturado como HTMLArquivo HTML ou PDF
.tpl estruturado como TXTArquivo TXT

Por que usar o Reporter?


Em vez de escrever consultas SQL complexas, você referencia domínios, tabelas e campos por meio de placeholders intuitivos. Isso torna a criação de relatórios mais rápida, flexível e fácil de manter.

Como funciona


Fluxo de trabalho

O Reporter segue um fluxo de trabalho simples e eficiente que transforma seus templates em relatórios prontos para produção:
  1. Envie templates com filtros e parâmetros opcionais
  2. O Reporter recupera os dados dos bancos de dados configurados (PostgreSQL, MongoDB)
  3. A lógica do template é aplicada (loops, condições, cálculos)
  4. A saída final é gerada no formato solicitado

Arquitetura

O Reporter é construído sobre uma arquitetura em camadas que mantém as responsabilidades bem definidas e permite crescimento:
  • Camada de dados: Conecta-se aos bancos de dados por meio de fontes de dados configuradas. Suporta PostgreSQL e MongoDB, com consultas multi-schema para PostgreSQL.
  • Camada de lógica de negócio: Gerencia a análise de templates, resolução de placeholders e renderização.
  • Camada de armazenamento: Armazena templates e relatórios gerados usando armazenamento de objetos compatível com S3 (AWS S3, MinIO ou SeaweedFS).
  • Camada de apresentação: Retorna a saída formatada por meio de APIs RESTful.

O que ele pode fazer


  • Consultas dinâmicas com placeholders: Referencie qualquer ponto de dados por caminhos diretos — sem necessidade de SQL.
  • Suporte a multi-schema: Consulte tabelas em múltiplos schemas do PostgreSQL a partir de um único template usando sintaxe explícita de schema.
  • Lógica de loops e condições: Construa conteúdo dinâmico com loops for, condicionais if/elif/else e blocos com escopo definido.
  • Operações matemáticas e agregação: Realize cálculos com sum_by, avg_by, count_by, min_by, max_by, calc e aggregate_balance.
  • Rastreamento de contadores: Rastreie e exiba contadores nomeados ao longo das iterações do template com counter e counter_show.
  • Filtros de transformação de dados: Transforme valores inline com where, sum, count, replace, slice, strip_zeros e percent_of.
  • Processamento assíncrono: Relatórios pesados são processados de forma assíncrona via fila de mensagens.
  • Armazenamento compatível com S3: Templates e relatórios são armazenados em qualquer serviço compatível com S3 (AWS S3, MinIO, SeaweedFS).
  • Múltiplos formatos de saída: Gere saídas em CSV, XML, HTML, TXT ou PDF a partir de um único mecanismo de templates.

Modelo de template


O Reporter utiliza templates que espelham a estrutura final do documento. Os arquivos devem ter a extensão .tpl independentemente do formato do conteúdo interno.
Embora o conteúdo do arquivo deva seguir o formato de saída, certifique-se de salvá-lo com a extensão .tpl. Isso é necessário para que o template funcione corretamente.

Configurando seu ambiente


As referências de banco de dados devem ser renomeadas no arquivo .env do projeto para evitar conflitos quando tabelas compartilham nomes entre bancos de dados. Exemplo de nomenclatura:
  • midaz_onboarding (PostgreSQL)
  • midaz_onboarding_metadata (MongoDB)

Usando placeholders


A estrutura dos placeholders segue uma sintaxe baseada em caminhos:
{{ base.table_or_collection.field_or_document }}
Isso funciona tanto para bancos de dados SQL (tabelas e campos) quanto para MongoDB (coleções e documentos).

Placeholders com multi-schema

Quando sua fonte de dados PostgreSQL possui múltiplos schemas, use a sintaxe explícita de schema para evitar ambiguidade:
{{ base:schema.table.field }}
Por exemplo:
{{ external_db:sales.orders.total }}
{{ external_db:inventory.items.quantity }}
Se o nome de uma tabela existe em apenas um schema, a sintaxe legada {{ base.table.field }} ainda funciona — o Reporter descobre automaticamente o schema correto. Se uma tabela existe em múltiplos schemas, o Reporter usa por padrão o schema public. Se a tabela não está em public, o Reporter retorna um erro com sugestões:
ambiguous table reference: 'db.orders' exists in multiple schemas: [sales, shipping]
Please use explicit schema syntax:
  {{ db:sales.orders }}
  {{ db:shipping.orders }}

Construindo templates


Blocos comuns

  • Loop
{% for <item> in <list> %}
  ...
{% endfor %}
  • Loop com schema explícito
{% for order in external_db:sales.orders %}
  {{ order.id }} - {{ order.total }}
{% endfor %}
  • Condição simples
{% if value_a == value_b %}
  ...
{% endif %}
  • Escopo temporário
{% with <object> as <alias> %}
  ...
{% endwith %}
  • Formatação de valores
{{ field_name | floatformat:2 }}   --> renderiza 123.45

Blocos condicionais

BlocoDescriçãoExemplo
IfExecuta o bloco se a condição for verdadeira{% if condition %}...{% endif %}
If-elseExecuta um bloco se verdadeiro, outro se falso{% if condition %}...{% else %}...{% endif %}
If-else-ifPermite múltiplas verificações{% if a %}...{% elif b %}...{% else %}...{% endif %}
IgualVerifica se dois valores são iguais{% if a == b %}
DiferenteVerifica se dois valores são diferentes{% if a != b %}
Maior queVerifica se a é maior que b{% if a > b %}
Menor queVerifica se a é menor que b{% if a < b %}
Maior ou igualVerifica se a é maior ou igual a b{% if a >= b %}
Menor ou igualVerifica se a é menor ou igual a b{% if a <= b %}
AndRetorna verdadeiro se ambas as condições forem verdadeiras{% if a and b %}
OrRetorna verdadeiro se pelo menos uma for verdadeira{% if a or b %}
NotInverte o resultado booleano{% if not a %}

Referência de tags


Tags de agregação

sum_by — Soma valores numéricos de um campo em todos os itens de uma coleção.
{% sum_by <collection> by <field> %}
{% sum_by <collection> by <field> if <condition> %}
Exemplo:
<Sum>
  {% sum_by transaction.operation by "amount" if accountAlias != "@external/BRL" %}
</Sum>
count_by — Conta o número de itens em uma coleção.
{% count_by <collection> %}
{% count_by <collection> if <condition> %}
Exemplo:
<Count>
  {% count_by transaction.operation if accountAlias != "@external/BRL" %}
</Count>
avg_by — Calcula a média dos valores numéricos de um campo.
{% avg_by <collection> by <field> %}
{% avg_by <collection> by <field> if <condition> %}
min_by — Encontra o valor numérico mínimo de um campo.
{% min_by <collection> by <field> %}
{% min_by <collection> by <field> if <condition> %}
max_by — Encontra o valor numérico máximo de um campo.
{% max_by <collection> by <field> %}
{% max_by <collection> by <field> if <condition> %}
Todas as tags de agregação utilizam precisão decimal para evitar erros de arredondamento de ponto flutuante. Campos ausentes ou não numéricos são ignorados. Retorna 0 se nenhum item corresponder.

Tag de data e hora

date_time — Gera a data e hora atuais formatadas de acordo com a string de formato fornecida. O horário é gerado em UTC.
{% date_time "<format>" %}
Códigos de formato:
CódigoSignificadoExemplo
YYYYAno com 4 dígitos2025
MMMês com 2 dígitos01-12
ddDia com 2 dígitos01-31
HHHora com 2 dígitos (24h)00-23
mmMinuto com 2 dígitos00-59
ssSegundo com 2 dígitos00-59
Exemplos:
{% date_time "YYYY-MM-dd" %}           --> 2025-02-06
{% date_time "dd/MM/YYYY HH:mm:ss" %} --> 06/02/2025 14:30:45

Tag aritmética

calc — Avalia expressões matemáticas com suporte a variáveis do contexto do template.
{% calc <expression> %}
Operadores suportados:
OperadorDescriçãoPrecedência
**ExponenciaçãoMais alta (direita para esquerda)
* /Multiplicação, divisãoMédia
+ -Adição, subtraçãoMais baixa
( )ParêntesesSobrescreve a precedência
Exemplos:
{% calc 100 + 50 %}                                --> 150
{% calc balance.available * 0.5 %}                 --> valor calculado
{% calc (balance.available + 1.2) * balance.on_hold - balance.available / 2 %}
Variáveis que não podem ser resolvidas assumem o valor padrão 0. Divisão por zero produz um erro.

Tag de agregação financeira

aggregate_balance — Agrupa itens por um campo, seleciona a entrada mais recente por conta dentro de cada grupo e soma os saldos. Útil para relatórios regulatórios que exigem o saldo mais recente por conta agrupado por categoria.
{% aggregate_balance <collection> by "<balance_field>" group_by "<group_field>" order_by "<date_field>" [if <condition>] as <result_var> %}
O resultado é armazenado em uma variável que você pode iterar:
{% 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 }} contas)
{% endfor %}
Cada item do resultado contém:
CampoTipoDescrição
group_valuestringO valor do campo group_by
balancedecimalSoma dos saldos mais recentes por conta no grupo
countinteiroNúmero de contas no grupo
Tamanho máximo da coleção: 100.000 itens. Os resultados são ordenados por group_value.

Tags de contador

counter — Incrementa um contador nomeado em 1. Não produz saída. Os contadores têm escopo por renderização.
{% counter "<counter_name>" %}
counter_show — Exibe a soma de um ou mais contadores nomeados.
{% counter_show "<name1>" %}
{% counter_show "<name1>" "<name2>" "<name3>" %}
Exemplo:
{% for tx in ledger.transactions %}
  {% counter tx.type %}
{% endfor %}
Total créditos: {% counter_show "credit" %}
Total débitos: {% counter_show "debit" %}
Combinado: {% counter_show "credit" "debit" %}

Referência de filtros


percent_of

Calcula a porcentagem de um valor em relação a um total. Retorna uma string formatada com 2 casas decimais.
{{ value | percent_of: total }}
Exemplo: se category.amount = "6.00" e total.expenses = "20.00":
{{ category.amount | percent_of: total.expenses }}  --> 30.00%

strip_zeros

Remove zeros à direita de um valor numérico sem arredondamento.
{{ number | strip_zeros }}
Exemplos:
{{ "100.50000" | strip_zeros }}  --> 100.5
{{ "100.00" | strip_zeros }}     --> 100
{{ "99.990" | strip_zeros }}     --> 99.99

slice

Extrai uma substring usando índices de início e fim (base 0).
{{ string | slice:"start:end" }}
Exemplos:
{{ "hello" | slice:"0:3" }}  --> hel
{{ "12345" | slice:"1:4" }}  --> 234

replace

Substitui todas as ocorrências de uma string de busca por uma string de substituição. Formato: "busca:substituição".
{{ string | replace:"search:replacement" }}
Exemplos:
{{ "01310-100" | replace:"-:" }}      --> 01310100   (remove hifens)
{{ "1234.56" | replace:".:," }}       --> 1234,56    (ponto para vírgula)
{{ "12.345.678/0001-99" | replace:".:" }}  --> 12345678/0001-99

where

Filtra um array de objetos por igualdade de campo. Suporta campos aninhados via notação de ponto.
{{ array | where:"field:value" }}
Exemplos:
{{ holders | where:"state:SP" }}
{{ holders | where:"address.state:SP" }}
Uso dentro de loops:
{% for holder in holders|where:"state:SP" %}
  {{ holder.name }}
{% endfor %}

sum (filtro)

Soma valores numéricos de um campo em todos os itens de um array. Utiliza precisão decimal.
{{ array | sum:"field" }}
Exemplos:
{{ operations | sum:"amount" }}
{{ items | sum:"price.value" }}

count (filtro)

Conta elementos em um array onde um campo corresponde a um valor. Suporta campos aninhados.
{{ array | count:"field:value" }}
Exemplos:
{{ operations | count:"nat_oper:6" }}
{{ holders | count:"address.state:SP" }}

contains

Verifica se um valor está parcialmente contido em outro. Útil quando os dados incluem prefixos ou sufixos dinâmicos.
{% if contains(source_field, target_field) %}
Exemplo:
  • Origem: 0#@external/BRL
  • Destino: @external/BRL
Retorna true porque @external/BRL existe dentro do valor de origem.

Resumo de operadores e filtros


NomeTipoDescrição
sum_byTagSoma valores por campo com filtro opcional
count_byTagConta itens com filtro opcional
avg_byTagCalcula a média por campo
min_byTagEncontra o valor mínimo
max_byTagEncontra o valor máximo
date_timeTagFormata a data/hora atual
calcTagAvalia expressões aritméticas
aggregate_balanceTagAgregação de saldos financeiros agrupados
counterTagIncrementa um contador nomeado
counter_showTagExibe valor(es) do contador
percent_ofFiltroCalcula porcentagem
strip_zerosFiltroRemove zeros à direita
sliceFiltroExtrai substring
replaceFiltroSubstituição de string
whereFiltroFiltra array por valor de campo
sumFiltroSoma valores de campo do array
countFiltroConta itens correspondentes
containsFunçãoCorrespondência parcial de string
floatformatFiltroFormata casas decimais

Filtragem avançada


Ao gerar um relatório, você pode passar filtros no corpo da requisição para restringir os dados. Os filtros seguem uma estrutura de datasource > tabela > campo: Schema único (padrão):
{
  "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 (chave explícita schema.tabela):
{
  "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"] }
      }
    }
  }
}
Operadores suportados:
OperadorDescriçãoExemplo
eqIgual a{ "eq": ["active", "pending"] }
gtMaior que{ "gt": [100] }
gteMaior ou igual a{ "gte": ["2025-06-01"] }
ltMenor que{ "lt": [1000] }
lteMenor ou igual a{ "lte": ["2025-06-30"] }
betweenValor dentro de um intervalo{ "between": [100, 1000] }
inValor está em uma lista{ "in": ["active", "pending"] }
ninValor não está em uma lista{ "nin": ["deleted", "archived"] }

Precisa de inspiração?


Confira a página de Exemplos de templates para explorar o que você pode fazer e começar a criar seu próprio template.

Autenticação e autorização


O Reporter não exige autenticação por padrão, mas é desenvolvido com integração nativa ao Access Manager. Quando habilitado, o Access Manager fornece controle de acesso baseado em papéis (RBAC) para templates, relatórios e fontes de dados — oferecendo controle granular sobre quem pode visualizar, criar ou gerenciar recursos de relatórios.
O Access Manager é uma funcionalidade opcional disponível no modelo Enterprise. Para detalhes sobre como habilitá-lo, consulte a documentação do Access Manager.

Próximos passos