terça-feira, 25 de setembro de 2018

Migração de dados

Introdução

Nesse post veremos como migrar dados de um banco de dados para outro via aplicação.

Migração

No Jarch já é nativo a página e a lógica para efetuar essa migração, basta adicionar no menu conforme abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@JArchViewScoped
public class MenuAction extends BaseMenuAction {

    @Override
    public List<IMenu> createMenu() {
        List<IMenu> menu = new ArrayList<>();

        menu.add(MenuBuilder
                .newInstance()
                .name("Migração")
                .action("../migration/migration.jsf")
                .build());

        return menu;
    }
}

Irá aparece a tela abaixo:

Todas as entidades que implementarem a interface ICrudEntity serão filtradas para mostrar na caixa de seleção, e estarão disponíveis para a migração, com exceção das classes que estiverem com a anotação @ArchIgnoreMigrate.

Conforme um item é selecionado, é feito uma busca no banco de dados para possível filtro, caso nenhum registro seja selecionado todos os dados da tabela serão migrados:


Para uma melhor visualização dos dados será necessário sobre escrever o método toString() conforme abaixo:
1
2
3
4
5
 @Override
 public String toString() {
  return BundleUtils.messageBundle("label.codigo") + ": " + codigo +
    BundleUtils.messageBundle("label.descricao") + ": " + descricao;
 }

Conforme é escolhido a(s) entidade(s) a ser migrada é gerado um arquivo "migracao.zip" contendo os objetos serializados separados por pastas:

Restauração

Na mesma página existe outra aba logo acima para fazer a restauração dos dados que anteriormente foram filtrados, conforme os passos acima.

Na aba de restauração é semelhante a aba de migração onde as entidades disponíveis serão filtradas e estarão disponíveis para a restauração, e o arquivo para fazer o upload.



Conclusão

Nesse post vimos como deixar disponível a página de migração no menu, como configurar as entidades para filtro e as que não devem estar disponíveis, como deixar os dados mais apresentáveis e como restaurar os dados.

Até mais,

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,

terça-feira, 11 de setembro de 2018

Eventos - Parte 5/5

Introdução

Nesse post vou mostrar algumas maneiras de interceptar os ações nas Actions (lista e dados). Não são necessariamente eventos, são implementações de métodos informando via anotação o momento que serão chamados, causando o mesmo efeito de um evento.

Vamos começar pelo action de lista, uma vez que a action de lista que faz a chamada a tela de dados.

@JArchCrudListBeforeCallInsert

Essa anotação deve ser usada em um método da action de lista. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento que for clicado no botão incluir da tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@JArchViewScoped
public class ListaBancoAction extends CrudListAction<BancoEntity, BancoFacade> {

 @Inject
 private UserInformation userInformation;

 @Override
 public String getPageData() {
  return "dadosBanco.jsf";
 }

 @JArchCrudListBeforeCallInsert
 public void antesInclusao() {
  if (!userInformation.get(UsuarioEntity.class).isMaster()) {
   throw new ValidationException(BundleUtils.messageBundle("message.permissaoNegadaParaEssePerfil"));
  }
 }
}

Observe que a assinatura desse método não recebe nenhum parâmetro. Desse tipo de evento somente esse método não recebe a entidade por parâmetro.

@JArchCrudListBeforeCallChange

Essa anotação deve ser usada em um método da action de lista. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento que for clicado no botão alterar da tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@JArchViewScoped
public class ListaBancoAction extends CrudListAction<BancoEntity, BancoFacade> {

 @Inject
 private UserInformation userInformation;

 @Override
 public String getPageData() {
  return "dadosBanco.jsf";
 }

 @JArchCrudListBeforeCallChange
 public void antesAlteracao(BancoEntity banco) {
  if (!userInformation.get(UsuarioEntity.class).isMaster()) {
   throw new ValidationException(BundleUtils.messageBundle("message.permissaoNegadaParaEssePerfil"));
  }
 }
}

Observe que a assinatura desse método recebe a entidade por parâmetro. Com exceção do @ArchCrudListBeforeCallInsert todos os demais recebem ese mesmo parâmetro.

@JArchCrudListBeforeCallClone

Essa anotação deve ser usada em um método da action de lista. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento que for clicado no botão clonar da tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@JArchViewScoped
public class ListaBancoAction extends CrudListAction<BancoEntity, BancoFacade> {

 @Inject
 private UserInformation userInformation;

 @Override
 public String getPageData(BancoEntity banco) {
  return "dadosBanco.jsf";
 }

 @JArchCrudListBeforeCallClone
 public void antesClonagem() {
  if (!userInformation.get(UsuarioEntity.class).isMaster()) {
   throw new ValidationException(BundleUtils.messageBundle("message.permissaoNegadaParaEssePerfil"));
  }
 }
}

@JArchCrudListBeforeCallConsult

Essa anotação deve ser usada em um método da action de lista. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento que for clicado no botão consultar da tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@JArchViewScoped
public class ListaBancoAction extends CrudListAction<BancoEntity, BancoFacade> {

 @Inject
 private UserInformation userInformation;

 @Override
 public String getPageData() {
  return "dadosBanco.jsf";
 }

 @JArchCrudListBeforeCallConsult
 public void antesConsultar(BancoEntity banco) {
  if (!userInformation.get(UsuarioEntity.class).isMaster()) {
   throw new ValidationException(BundleUtils.messageBundle("message.permissaoNegadaParaEssePerfil"));
  }
 }
}

@JArchCrudListBeforeCallDelete

Essa anotação deve ser usada em um método da action de lista. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento que for clicado no botão excluir da tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@JArchViewScoped
public class ListaBancoAction extends CrudListAction<BancoEntity, BancoFacade> {

 @Inject
 private UserInformation userInformation;

 @Override
 public String getPageData() {
  return "dadosBanco.jsf";
 }

 @JArchCrudListBeforeCallDelete
 public void antesExcluir(BancoEntity banco) {
  if (!userInformation.get(UsuarioEntity.class).isMaster()) {
   throw new ValidationException(BundleUtils.messageBundle("message.permissaoNegadaParaEssePerfil"));
  }
 }
}

Agora veremos os eventos que podem ser implementados na action de dados.

@JArchCrudDataStartEnviromentInsert

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento da abertura da tela de dados. Ele é disparado quando acionado a ação de incluir na tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataStartEnviromentInsert
 public void iniciaModoInclusao() {
  getEntity().setCodigo("XXX");
  getEntity().setCodigo("Entre com o nome do banco");
 }
}

@JArchCrudDataStartEnviromentClone

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento da abertura da tela de dados. Ele é disparado quando acionado a ação de clonar na tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataStartEnviromentClone
 public void iniciaModoClonagem() {
  getEntity().setCodigo("CLONE");
  getEntity().setNome("Nome do banco clonado");
 }
}

@JArchCrudDataStartEnviromentChange

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento da abertura da tela de dados. Ele é disparado quando acionado a ação de alterar na tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@ArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataStartEnviromentChange
 public void iniciaModoAlteracao() {
  // Carregar componentes visuais para visualização  
 }
}

@JArchCrudDataStartEnviromentConsult

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento da abertura da tela de dados. Ele é disparado quando acionado a ação de consultar na tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataStartEnviromentConsult
 public void iniciaModoConsulta() {
  // Carregar componentes visuais para visualização
 }
}

@JArchCrudDataStartEnviromentDelete

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento da abertura da tela de dados. Ele é disparado quando acionado a ação de excluir na tela de lista.
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataStartEnviromentDelete
 public void iniciaModoExclusao() {
  // Carregar componentes visuais para visualização
 }
}

Existe mais dois eventos que podem ser interceptados, mas que são em um outro momento dentro da tela dados.

@JArchCrudDataBeforeSave

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento do click de confirmação na tela de dados mas antes de efetivar a gravação dos dados. 
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @JArchCrudDataBeforeSave
 public void antesSalvar() {
  // Ajusta dados conforme seleção dos componentes visuais
 }
}

@JArchCrudDataAfterSave

Essa anotação deve ser usada em um método da action de dados. Essa anotação é uma variação da implementação do design pattern Template Method, só que com a flexibilidade de definir o nome método. Esse método anotado passa a ser um Hook Méthod e é chamado no momento do click de confirmação na tela de dados mas após a efetivar a gravação dos dados. 
Segue um exemplo abaixo:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@JArchViewScoped
public class DadosBancoAction extends CrudDataAction<BancoEntity, BancoFacade> {

 @Override
 public String getPageList() {
  return "listaBanco.jsf";
 }

 @ArchCrudDataAfterSave
 public void depoisSalvar() {
  // Ajusta dados conforme seleção dos componentes visuais
 }
}

Conclusão

Finalizamos os posts relacionados a eventos, conforme visto nos posts anteriores existem diversos tipos de eventos no framework. Esses eventos são de grande importância para manter o código com baixo acoplamento e coeso.

Até mais,

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...