Criando um relatório em árvore

Conceitos

Árvore é uma estrutura capaz de representar uma hierarquia em modo gráfico, sendo a sua base, a raiz, o primeiro elemento e também a origem de todos os outros elementos. As pontas da árvore são as folhas.

Vamos utilizar a figura abaixo como exemplo:

No exemplo acima temos que o elemento "RAIZ - ID: 0001 - PAI: NULO", é a raiz, pois toda a estrutura usa ele como base e o pai dele aponta pra nulo. Os elementos intermediários, os quais tem o título igual a "NÍVEL 2", estão um nível abaixo da raiz e os elementos "NÍVEL 3" são as folhas dessa árvore tendo a hierarquia mais baixa e estão abaixo dos elementos de nível 2.

Como eu indico ao sistema que o meu relatório se trata de um relatório em árvore?

O simple layout vai entender isso através do método sobrecarregado newRecord. A assinatura do método é essa:

newRecord(checkGroup, groupTotalLabel, showLineTop, showLineBottom, treeNodeId, parentTreeNodeId)

Note que os dois últimos parâmetros dizem respeito à estrutura de árvore, sendo o treeNodeId o ID do nó que se quer acrescentar ao Simple Layout e o parentTreeNodeId o pai dele.

Propriedades

O Simple Layout ainda tem atributos especiais para a nossa estrutura em árvore:

  • treeExpansionLevel: Esse atributo é um inteiro que representa até que nível da árvore ela vai estar expandida quando o relatório for aberto. Exemplo de uma árvore completamente expandida:

  • No caso acima se o atributo treeExpansionLevel estiver igual ou acima de 2 todos os níveis estarão expandidos.

  • showTreeRoot: Atributo booleano que indica se a raiz da árvore deve ser exibida. No caso abaixo ocultamos a raiz colocando um valor false nesse atributo:

Exemplo

Como você já deve ter notado estamos usando o mesmo relatório para ilustrar todos os exemplos desse manual. Que tal montarmos esse exemplo por completo para facilitar a compreensão do todo?

Mostre-me o código!

// Id do elemento raiz da árvore

var ROOT_ELEMENT_ID = 1;

var fld = vars.field("expansionLevel", "integer");

fld.label = "Número do nível de expansão";

fld.required = true;

fld.defaultValue = 0;

fld = vars.field("showTreeRoot", "boolean");

fld.label = "Mostrar a raiz da árvore ?";

fld.stringIfTrue = "Yes";

fld.defaultValue = true;

this.ds = new DataSet();

this.ds.createField("ID", "integer"); // ID do elemento

this.ds.createField("PARENT", "integer"); // ID do elemento pai

this.ds.createField("NODELABEL", "string", 50 ); // Label

this.ds.create();

this.activity("run", function () {

this.ds.empty();

var totalIdLevel_2 = 100;

// Mesmo que a linha acima aplicado ao terceiro nível

var totalIdLevel_3 = 1000;

this.ds.append();

this.ds.nodelabel = "RAIZ - ID: 0001 - PAI: NULO";

this.ds.parent = null;

this.ds.id = ROOT_ELEMENT_ID;

this.ds.post();

// Para cada elemento inserido abaixo da raiz criaremos 10 elementos utilizando esse como raiz

for (var i = 1; i <= 10; i++) {

// Para não haver elementos com o mesmo ID, os ids de segundo nível serão acima de 100

var subLevelId = totalIdLevel_2++;

this.ds.append();

this.ds.parent = ROOT_ELEMENT_ID;

this.ds.nodelabel = "NÍVEL 2 - ID: " + subLevelId.toString("%.4d") +

" - PAI: " + this.ds.parent.toString("%.4d");

this.ds.id = subLevelId;

this.ds.post();

// Para cada elemento de segundo nível iremos criar mais 5 elementos

// utilizando esse como raiz

for (var j = 1; j < 5; j++) {

this.ds.append();

this.ds.parent = subLevelId;

this.ds.nodelabel = "NÍVEL 3 - ID: " + totalIdLevel_3.toString("%.4d") +

" - PAI: " + this.ds.parent.toString("%.4d");

// Para não haver elementos com o mesmo ID, os ids de

// terceiro nível serão acima de 1000

this.ds.id = totalIdLevel_3++;

this.ds.post();

}

}

});

this.interaction("writeLayout", function () {

var sl = this.getSimpleLayout();

// Até que nível a árvore deve estar expandida

sl.treeExpansionLevel = this.expansionLevel;

// Mostrar a raiz da árvore

sl.showTreeRoot = !! this.showTreeRoot;

var col = sl.column("Node Label");

col.label = "";

this.ds.indexFieldNames = "PARENT";

var total = this.ds.recordCount;

for (this.ds.first(); !this.ds.eof; this.ds.next()){

// Na criação da nova linha do simple layout informamos qual o ID dessa

// linha e quem é o seu pai fazendo com que essa nova linha fique

// hierarquicamente correta.

sl.newRecord(null, null, null, null, this.ds.id, this.ds.parent);

sl.writeColumn(this.ds.nodelabel);

}

sl.end();

});

// Esse número vai ser usado como base para o ID dos elementos de segundo nível