quarta-feira, 12 de setembro de 2018

API de Relatório

Introdução

Nesse post vou mostrar como criar relatórios simples, dinâmicos, com agrupamentos e subrelatórios.

ReportBuilder

Para a utilização da API de relatórios temos a classe ReportBuilder que através de sua interface fluente facilita a construção do relatório, segue abaixo um exemplo de um relatório simples:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public StreamedContent relatorioSimples() {
        List<ProdutoEntity> listaProduto = criaListaProduto();

        ReportBuilder reportBuilder = ReportBuilder
                .createInstance()
                .setTitle("Relatório de Produtos")
                .setSubtitle("Período: 01/2017 até 12/2017")
                .addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
                .addField(BundleUtils.messageBundle("label.descricao"), "descricao", 200, String.class)
                .addField(BundleUtils.messageBundle("label.tipo"), "tipo", 400, TipoType.class)
                .addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
                .withListData(listaProduto);

        try {
            return reportBuilder.exportPdf("relatorioProdutoSimples");
        } catch (JRException | IOException ex) {
            LogUtils.generate(ex);
        }

        return null;
    }

    private List<ProdutoEntity> criaListaProduto() {
        List<ProdutoEntity> listaProduto = new ArrayList<>();
        listaProduto.add(criaProduto("001", "Caneta", UnidadeType.UNIDADE, "1.39"));
        listaProduto.add(criaProduto("002", "Borracha", UnidadeType.UNIDADE, "3.27"));
        listaProduto.add(criaProduto("003", "Mouse", UnidadeType.UNIDADE, "10.50"));
        listaProduto.add(criaProduto("004", "Teclado", UnidadeType.UNIDADE, "45.60"));
        listaProduto.add(criaProduto("005", "Monitor", UnidadeType.UNIDADE, "109.99"));

        return listaProduto;
    }

    private ProdutoEntity criaProduto(String codigo, String descricao, TipoType tipo, String valor){
        ProdutoEntity produtoEntity = new ProdutoEntity();
        produtoEntity.setCodigo(codigo);
        produtoEntity.setDescricao(descricao);
        produtoEntity.setTipo(tipo);
        produtoEntity.setValor(new BigDecimal(valor));

        return produtoEntity;
    }

Na linha 2 criamos uma lista simples para a saída do relatório. A partir da linha 4 começa a construção, segue um exemplo da saída acima:

Estilizando o cabeçalho da página com um template:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public StreamedContent relatorioComTemplate() {
        List<ProdutoEntity> listaProduto = criaListaProduto();

        ReportBuilder reportBuilder = ReportBuilder
                .createInstance()
                .setTitle("Relatório de Produtos")
                .setSubtitle("Período: 01/2017 até 12/2017")
                .addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
                .addField(BundleUtils.messageBundle("label.descricao"), "descricao", 200, String.class)
                .addField(BundleUtils.messageBundle("label.tipo"), "tipo", 400, TipoType.class)
                .addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
                .withListData(listaProduto)
                .addParameter("emissor", "Bruno Araujo")
                .addParameter("sistema", BundleUtils.messageBundle("label.siglaSistema"))
                .addParameter("tituloPrefeitura", BundleUtils.messageBundle("label.titulo1"))
                .addParameter("tituloSecretaria", BundleUtils.messageBundle("label.titulo2"))
                .addParameter("imagemTopo",JsfUtils.getPath("resources/images/logo-menu.png"))
                .setTemplateFile(JsfUtils.getPath("relatorios/templates/") + "templateReportLandscape.jrxml")
                .withLandscapeOrientation();

        try {
            return reportBuilder.exportPdf("relatorioProdutoSimples");
        } catch (JRException | IOException ex) {
            LogUtils.generate(ex);
        }

        return null;
    }

Saída:

ReportGroupBuilder

Para criar agrupamento no relatório utilizamos em conjunto com o ReportBuilder a classe ReportGroupBuilder conforme abaixo:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public StreamedContent relatorioComGrupo() {
        List<ProdutoEntity> listaProduto = criaListaProduto();

        ReportBuilder reportBuilder = ReportBuilder
                .createInstance()
                .setTitle("Relatório de Produtos")
                .setSubtitle("Período: 01/2017 até 12/2017")
                .addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
                .addField(BundleUtils.messageBundle("label.descricao"), "descricao", 200, String.class)
                .addField(BundleUtils.messageBundle("label.tipo"), "tipo", 400, TipoType.class)
                .addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
                .withListData(listaProduto);

        try {
            return reportBuilder
                    .addGroup(ReportGroupBuilder.createInstance(reportBuilder, "tipo")
                            .addFooterVariable("SubTotal","codigo", DJCalculation.COUNT)
                            .addFooterVariable("valor", DJCalculation.SUM)
                            .build())
                    .addGrandTotalLegend(BundleUtils.messageBundle("label.total"))
                    .addGlobalFooterVariable("valor", DJCalculation.SUM)
                    .exportPdf("relatorioProdutoGrupo");
        } catch (JRException | IOException e) {
            LogUtils.generate();
        }

        return null;
    }

Na linha 15 criamos um agrupamento para o atributo tipo da classe que esta na lista, conforme o valor desse atributo muda é gerado um totalizador no atributo valor e um totalizador geral, conforme abaixo.

Obs: para o agrupamento funcionar conforme o esperado a lista deve estar ordenada conforme a necessidade:


Com um agrupamento mais elaborado:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public StreamedContent relatorioComGrupoHeaderEFooter() {
        List<ProdutoEntity> listaProduto = criaListaProduto();

        ReportBuilder reportBuilder = ReportBuilder
                .createInstance()
                .setTitle("Relatório de Produtos")
                .setSubtitle("Período: 01/2017 até 12/2017")
                .addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
                .addField(BundleUtils.messageBundle("label.descricao"), "descricao", 200, String.class)
                .addField(BundleUtils.messageBundle("label.tipo"), "tipo", 400, TipoType.class)
                .addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
                .withListData(listaProduto);

        try {
            return reportBuilder
                    .addGroup(ReportGroupBuilder.createInstance(reportBuilder, "tipo")
                            .setGroupLayout(GroupLayout.VALUE_FOR_EACH)
                            .addHeaderVariable("tipo", DJCalculation.FIRST)
                            .addFooterVariable("SubTotal","codigo", DJCalculation.SYSTEM)
                            .addFooterVariable("descricao", DJCalculation.COUNT)
                            .addFooterVariable("valor", DJCalculation.SUM)
                            .build())
                    .addGrandTotalLegend(BundleUtils.messageBundle("label.total"))
                    .addGlobalFooterVariable("descricao", DJCalculation.COUNT)
                    .addGlobalFooterVariable("tipo", DJCalculation.SYSTEM)
                    .addGlobalFooterVariable("valor", DJCalculation.SUM)
                    .exportPdf("relatorioProdutoGrupo");
        } catch (JRException | IOException e) {
            LogUtils.generate();
        }

        return null;
    }

Saída:

ReportSubReportBuilder

Com um subrelatório é possível colocar uma lista no meio do detalhe do relatório, usaremos em conjunto com o ReportBuilder e ReportGroupBuilder, conforme abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public StreamedContent relatorioComSubRelatorio() {
        List<ProdutoEntity> listaProduto = criaListaProduto();

        ReportBuilder reportBuilder = ReportBuilder
                .createInstance()
                .setTitle("Relatório de Produtos")
                .setSubtitle("Período: 01/2017 até 12/2017")
                .addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
                .addField(BundleUtils.messageBundle("label.descricao"), "descricao", 200, String.class)
                .addField(BundleUtils.messageBundle("label.tipo"), "tipo", 400, TipoType.class)
                .addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
                .addField("listaMateriaPrima", List.class)
                .withListData(listaProduto);

        try {
            return reportBuilder
                    .addGroup(ReportGroupBuilder.createInstance(reportBuilder, "codigo").build())
                    .addSubReport(ReportSubReportBuilder
                            .createInstance()
                            .addField(BundleUtils.messageBundle("label.nome"), "nome", 300, String.class)
                            .addField(BundleUtils.messageBundle("label.quantidade"), "quantidade", 200, Long.class)
                            .build(),
                            "listaMateriaPrima",
                            1)
                    .exportPdf("relatorioProdutoSubRelatorio");
        } catch (JRException | IOException ex) {
            LogUtils.generate(ex);
        }

        return null;
    }

Saída:

Conclusão

Nesse post mostrei alguns exemplos de como implementar relatórios de forma dinâmica (Relatório simples, com template, com agrupamento e/ou subrelatório), sem muita complexidade na criação e manutenção.

Até mais,

Nenhum comentário:

Postar um comentário

Versão 23.3.0-Final

      Introdução Nesse post vou mostrar as principais novidades da versão 23.3.0, algumas correções e pequenas alterações. Alterações Além d...