Logosulfite.app
rafagazani/sulfite 999999

Definição do relatório

Estrutura JSON completa de um ReportDefinition

Definição do relatório#

Todo relatório Sulfite é um JSON com esta estrutura:

{
  "reportId": "string",
  "reportName": "string",
  "version": "string",
  "pageSettings": { ... },
  "dataSources": [ ... ],
  "bands": [ ... ]
}

Campos#

CampoTipoObrigatórioDescrição
reportId string Sim Identificador único do relatório
reportName string Sim Nome legível
version string Não Versão do relatório (ex: "1.0")
pageSettings object Sim Formato da página, margens, orientação
dataSources array Sim Fontes de dados esperadas
bands array Sim Faixas horizontais que compõem o layout
parameters array Não Parâmetros interativos preenchidos antes da impressão (ver Filtros)
scripts array Não Scripts executados em hooks do ciclo de vida (afterQuery, beforeRender)
metadata object Não Dados extras livres
processingMode string Não "strict" (padrão) ou "resilient"

pageSettings#

{
  "format": "A4",
  "width": 595.28,
  "height": 841.89,
  "unit": "pt",
  "orientation": "portrait",
  "margins": {
    "left": 40,
    "right": 40,
    "top": 40,
    "bottom": 40
  }
}
CampoTipoPadrãoDescrição
format string "A4" Nome do formato ( A3 , A4 , A5 , Carta , Legal )
width double 595.28 Largura em pontos
height double 841.89 Altura em pontos
unit string "pt" Unidade de medida
orientation string "portrait" "portrait" ou "landscape"
margins object Margens (left, right, top, bottom)
labelConfig object null Configuração para modo etiqueta (ver Etiquetas)

Formatos comuns#

FormatoLargura (pt)Altura (pt)
A3841.891190.55
A4595.28841.89
A5419.53595.28
Carta612.00792.00
Legal612.001008.00

Modo de processamento#

  • strict (padrão): erros de binding, data source não encontrado ou tipo inválido interrompem o processamento.
  • resilient: registra erros em ProcessedData.errors e continua com valores de fallback.
{
  "processingMode": "resilient"
}

Scripts#

O campo scripts declara handlers de ciclo de vida executados pelo AdvancedScriptEngine. Cada script é identificado por um id e disparado em um dos dois hooks disponíveis:

HookQuando executa
afterQuery Logo após o DataProcessor carregar os dados, antes de renderizar. Use para filtrar, enriquecer ou transformar registros.
beforeRender Após o processamento, antes de montar o PDF/HTML. Use para ajustar visibilidade de bands e elementos com base em regras de negócio.
{
  "scripts": [
    {
      "id": "mask_custo",
      "hook": "afterQuery",
      "description": "Remove coluna de custo se usuário não tiver permissão"
    },
    {
      "id": "hide_empty_section",
      "hook": "beforeRender",
      "description": "Oculta band de detalhes quando não há registros"
    }
  ]
}

Os handlers são registrados em Dart via AdvancedScriptEngine.register() e associados ao relatório pelo id:

final engine = AdvancedScriptEngine(
  externalContext: {'permissions': userPermissions},
);

engine.register('mask_custo', (ctx) {
  final podeVerCusto = ctx.context('permissions').contains('financeiro');
  if (!podeVerCusto) {
    ctx.setElementVisible('col_custo', false);
  }
});

engine.register('hide_empty_section', (ctx) {
  final rows = ctx.datasource('vendas');
  ctx.setBandVisible('band_detalhe', rows.isNotEmpty);
});

final pdfBytes = await engine.generate(
  report,
  dataPayload: payload,
  format: 'pdf',
);

ScriptContext — API disponível#

MétodoDescrição
datasource(id)Retorna os registros atuais de um data source
setDatasource(id, rows)Substitui todos os registros de um data source
addRow(id, row)Adiciona um registro
removeRow(id, predicate)Remove registros que satisfaçam a condição
setBandVisible(id, visible)Mostra ou oculta uma band
setElementVisible(id, visible)Mostra ou oculta um elemento
setElementProperty(id, prop, value)Altera propriedade de um elemento
param(id)Lê o valor de um parâmetro
setParam(id, value)Define o valor de um parâmetro
context(key)Acessa o externalContext injetado no engine
query(sql, params)Executa SQL via SqlBridge (opcional)
cancel()Aborta a geração do relatório
log(msg)Log de diagnóstico

Para condicionais simples sem estado, use os campos inline dos elementos:

printWhen em qualquer elemento ou band: expressão booleana que controla visibilidade.

valueExpression em FieldElement: expressão calculada no contexto do row atual.

Prefira scripts quando a lógica precisar de acesso a parâmetros externos, múltiplos data sources ou estado acumulado.

Exemplo completo#

Um relatório de vendas com header, 50 linhas de detalhe, agregados no summary e rodapé com paginação:

{
  "reportId": "sales_report",
  "reportName": "Relatório de Vendas",
  "version": "2.0",
  "pageSettings": {
    "format": "A4",
    "orientation": "portrait",
    "margins": { "left": 30, "right": 30, "top": 30, "bottom": 30 }
  },
  "dataSources": [
    {
      "id": "sales",
      "type": "list",
      "schema": {
        "order_id": "string",
        "customer": "string",
        "amount": "number",
        "date": "string"
      }
    }
  ],
  "bands": [
    {
      "type": "header", "id": "hdr", "height": 60,
      "elements": [
        { "type": "text", "id": "t1", "x": 30, "y": 20, "content": "Relatório de Vendas", "fontSize": 24, "bold": true }
      ]
    },
    {
      "type": "detail", "id": "det", "dataSourceId": "sales", "height": 25,
      "elements": [
        { "type": "field", "id": "f1", "x": 30, "y": 5, "width": 80, "binding": "order_id" },
        { "type": "field", "id": "f2", "x": 120, "y": 5, "width": 200, "binding": "customer" },
        { "type": "field", "id": "f3", "x": 330, "y": 5, "width": 100, "binding": "amount", "format": "currency:BRL" },
        { "type": "field", "id": "f4", "x": 440, "y": 5, "width": 100, "binding": "date", "format": "date:dd/MM/yyyy" }
      ]
    },
    {
      "type": "summary", "id": "sum", "height": 40,
      "elements": [
        { "type": "aggregate", "id": "total", "x": 330, "y": 10, "width": 100, "verb": "SUM", "dataSourceId": "sales", "targetKey": "amount", "format": "currency:BRL", "bold": true }
      ]
    },
    {
      "type": "footer", "id": "ftr", "height": 30,
      "elements": [
        { "type": "text", "id": "pg", "x": 250, "y": 10, "content": "Página {page} de {totalPages}", "fontSize": 9, "color": "999999" }
      ]
    }
  ]
}

Próximo passo#

Entender as bands →