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