Introdução
Nesse décimo terceiro post vou mostrar como criar os relatórios de contas a pagar e receber. Vou utilizar alguns filtros e gerar o relatório dinâmicamente através da API do JARCH.
Menu
Vou adicionar o item de menu para acessar o relatório de contas a pagar e receber. Vou adicionar os itens de menu no item relatório:
MenuAction.java:
/** Código Anterior Oculto **\
menu.add(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.relatorio"))
.addSubMenu(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.contaPagar"))
.action("../lancamento/relatorioLancamento.jsf?tipo=PAGAR")
.build())
.addSubMenu(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.contaReceber"))
.action("../lancamento/relatorioLancamento.jsf?tipo=RECEBER")
.build())
.build());/** Código Posterior Oculto **\
Repare que no link estou passando o tipo como parâmetro do enumerado. Com essa implementação o menu vai ter essa aparência:
Para a geração do relatório vou criar a página onde existirá os filtros. Nessa página vou adicionar alguns composites components do JARCH. Segue o exemplo a seguir:
relatorioLancamento.css:
.panel-card, .panel-card2 {
display: flex;
}
.panel-card > div {
margin: 10px;
width: 25%;
}
.panel-card2 > div {
margin: 10px;
width: 33%;
}
relatorioLancamento.xhtml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:w="http://arch.com/jsf/ui"
xmlns:a="http://jarch.com.br/ui"
xmlns:e="http://jarch.com.br/ui/extension"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<ui:composition template="/paginas/templates/templatePrincipalV2.xhtml">
<ui:define name="afterCss">
<h:outputStylesheet name="relatorioLancamento.css" library="css"/>
</ui:define>
<ui:define name="panelBodyTemplatePrincipal">
<e:form id="frmRelatorio">
<e:divTitle title="#{e:bundle('label.relatorio')} #{relatorioLancamentoAction.nome}"
description="#{e:bundle('label.relatorio')} #{relatorioLancamentoAction.nome}"/>
<e:divFilterReport
title="#{e:bundle('label.relatorio')} #{relatorioLancamentoAction.nome}">
<h:panelGroup class="panel-card">
<e:filterSelect
rendered="#{relatorioLancamentoAction.utilizaBanco}"
colorBackgroundHeader="#2b669a"
icone="fa fa-university"
header="#{e:bundle('label.banco')}"
widgetVar="widgetVarBanco"/>
<e:filterSelect
rendered="#{relatorioLancamentoAction.utilizaCentroCusto}"
colorBackgroundHeader="lightseagreen"
icone="fa fa-balance-scale"
header="#{e:bundle('label.centroCusto')}"
widgetVar="widgetVarCentroCusto"/>
<e:filterSelect
rendered="#{relatorioLancamentoAction.utilizaCategoria}"
colorBackgroundHeader="darkorange"
icone="fa fa-object-group"
header="#{e:bundle('label.categoria')}"
widgetVar="widgetVarCategoria"/>
<e:filterSelect
rendered="#{relatorioLancamentoAction.utilizaPessoa}"
colorBackgroundHeader="indianred"
icone="fa fa-user"
header="#{e:bundle('label.pessoa')}"
widgetVar="widgetVarPessoa"/>
</h:panelGroup>
<h:panelGroup class="panel-card2">
<e:filterPeriod
valueStart="#{relatorioLancamentoAction.vencimentoInicio}"
valueEnd="#{relatorioLancamentoAction.vencimentoFim}"/>
<e:filterGroup
rendered="#{relatorioLancamentoAction.utilizaAgrupamento}"
value="#{relatorioLancamentoAction.agrupamento}"
items="#{relatorioLancamentoAction.listaAgrupamento}"
propertyShow="descricao"/>
<e:filterOrder
label="#{e:bundle('label.ordenacao')}"
value="#{relatorioLancamentoAction.ordenacao}"
items="#{relatorioLancamentoAction.listaOrdenacao}"
propertyShow="descricao"/>
</h:panelGroup>
<a:spacer rendered="#{not empty relatorioLancamentoAction.bancos
or not empty relatorioLancamentoAction.centroCustos
or not empty relatorioLancamentoAction.categorias
or not empty relatorioLancamentoAction.pessoas}"/>
<h:panelGroup id="pnlPanelSelecionados">
<e:panelSelected
header="#{e:bundle('label.banco')}"
value="#{relatorioLancamentoAction.bancos}"
widgetVar="widgetVarBanco"
valueExcept="#{relatorioLancamentoAction.excetoBanco}"
visibleList="#{not empty relatorioLancamentoAction.bancos}"
converterId="br.com.jarch.jsf.converter.baseEntityConverter"/>
<e:panelSelected
header="#{e:bundle('label.centroCusto')}"
value="#{relatorioLancamentoAction.centroCustos}"
widgetVar="widgetVarCentroCusto"
valueExcept="#{relatorioLancamentoAction.excetoCentroCusto}"
visibleList="#{not empty relatorioLancamentoAction.centroCustos}"
converterId="br.com.jarch.jsf.converter.baseEntityConverter"/>
<e:panelSelected
header="#{e:bundle('label.categoria')}"
value="#{relatorioLancamentoAction.categorias}"
widgetVar="widgetVarCategoria"
valueExcept="#{relatorioLancamentoAction.excetoCategoria}"
visibleList="#{not empty relatorioLancamentoAction.categorias}"
converterId="br.com.jarch.jsf.converter.baseEntityConverter"/>
<e:panelSelected
header="#{e:bundle('label.pessoa')}"
value="#{relatorioLancamentoAction.pessoas}"
widgetVar="widgetVarPessoa"
valueExcept="#{relatorioLancamentoAction.excetoPessoa}"
visibleList="#{not empty relatorioLancamentoAction.pessoas}"
converterId="br.com.jarch.jsf.converter.baseEntityConverter"/>
</h:panelGroup>
<f:facet name="bottomRight">
<e:commandLinkPrint streamedContent="#{relatorioLancamentoAction.geraRelatorio()}"
update="@(.base-messageheader)"/>
</f:facet>
</e:divFilterReport>
</e:form>
</ui:define>
<ui:define name="extension">
<h:form>
<e:dialogSelect
widgetVar="widgetVarPessoa"
actionFilterSelect="#{pessoaFilterSelectAction}"
header="#{e:bundle('label.pessoa')}"
value="#{relatorioLancamentoAction.pessoas}"
update="#{a:component('pnlPanelSelecionados')}"/>
<e:dialogSelect
widgetVar="widgetVarBanco"
actionFilterSelect="#{bancoFilterSelectAction}"
header="#{e:bundle('label.banco')}"
update="#{w:archComponent('pnlPanelSelecionados')}"
value="#{relatorioLancamentoAction.bancos}"/>
<e:dialogSelect
widgetVar="widgetVarCentroCusto"
actionFilterSelect="#{centroCustoFilterSelectAction}"
header="#{e:bundle('label.centroCusto')}"
update="#{w:archComponent('pnlPanelSelecionados')}"
value="#{relatorioLancamentoAction.centroCustos}"/>
<e:dialogSelect
widgetVar="widgetVarCategoria"
actionFilterSelect="#{categoriaFilterSelectAction}"
header="#{e:bundle('label.categoria')}"
update="#{w:archComponent('pnlPanelSelecionados')}"
value="#{relatorioLancamentoAction.categorias}"/>
</h:form>
</ui:define>
</ui:composition>
</html>
Dentre os diversos composites components do JARCH que está sendo usado acima vale destacar:
<e:divFilterReport>: Esse componente cria uma div com título e rodapé personalizado, muito parecido com as demais telas, mas esse componente é especifico pra filtros de relatórios.
<e:filterSelect>: Esse componente é utilizado para definir uma seleção de entidade. Estou utilizando para selecionar os Bancos, Centros Custos, Categorias e Pessoas.
<e:filterPeriod>: Esse componente é utilizado para selecionar um período de datas.
<e:filterGroup>: Esse componente é utilizado para selecionar um tipo de agrupamento.
<e:filterOrder>: Esse componente é utilizado para selecionar um tipo de ordenação.
<e:panelSelected>: Esse componente é utilizado para mostrar as entidades selecionadas.
<e:dialogSelected>: Esse componente é um dialog para definir um widgetVar, utilizado pelo <e:filterSelect> e <e:panelSelected>.
Action
Para a tela acima vou criar a action RelatorioLancamentoAction, além de criar também 2 enumerados de agrupamento e ordenação. Segue o código do primeiro enumerado:
AgrupamentoType.java:
public enum AgrupamentoType {
BANCO("label.banco", "banco.nome"),
CENTRO_CUSTO("label.centroCusto", "centroCusto.descricao"),
CATEGORIA("label.categoria", "categoria.descricao"),
PESSOA("label.pessoa", "pessoa.nomeRazaoSocial");
AgrupamentoType(String descricao, String atributo) {
this.descricao = BundleUtils.messageBundle(descricao);
this.atributo = atributo;
}
private String descricao;
private String atributo;
public String getDescricao() {
return descricao;
}
public String getAtributo() {
return atributo;
}
}
OrdenacaoType.java:
public enum OrdenacaoType {
DESCRICAO("label.descricao", "descricao"),
VALOR("label.valor", "valor"),
VENCIMENTO("label.vencimento", "vencimento");
OrdenacaoType(String descricao, String atributo) {
this.descricao = BundleUtils.messageBundle(descricao);
this.atributo = atributo;
}
private String descricao;
private String atributo;
public String getDescricao() {
return descricao;
}
public String getAtributo() {
return atributo;
}
}
RelatorioLancamentoAction.java:
@JArchViewScoped
public class RelatorioLancamentoAction implements Serializable {
@Inject
private LancamentoFacade lancamentoFacade;
@JArchParameter
@Inject
private ParametroUtilizaBanco parametroUtilizaBanco;
@JArchParameter
@Inject
private ParametroUtilizaCentroCusto parametroUtilizaCentroCusto;
@JArchParameter
@Inject
private ParametroUtilizaCategoria parametroUtilizaCategoria;
@JArchParameter
@Inject
private ParametroUtilizaPessoa parametroUtilizaPessoa;
private LocalDate vencimentoInicio;
private LocalDate vencimentoFim;
private Collection<BancoEntity> bancos;
private Collection<CentroCustoEntity> centroCustos;
private Collection<CategoriaEntity> categorias;
private Collection<PessoaEntity> pessoas;
private AgrupamentoType agrupamento;
private OrdenacaoType ordenacao;
private boolean excetoBanco;
private boolean excetoCentroCusto;
private boolean excetoCategoria;
private boolean excetoPessoa;
private String nome;
private TipoLancamentoType tipoLancamento;
@PostConstruct
private void init() {
tipoLancamento = TipoLancamentoType.valueOf(JsfUtils.getParameterRequest("tipo"));
nome = tipoLancamento.getDescription();
}
public StreamedContent geraRelatorio() {
Collection<LancamentoEntity> lancamentos = selecionaDados();
if (lancamentos.isEmpty()) {
JsfUtils.addMessageWarn(BundleUtils.messageBundle("message.naoExisteDadosImpressao"));
return null;
}
return geraPdf(lancamentos);
}
public AgrupamentoType getAgrupamento() {
return agrupamento;
}
public void setAgrupamento(AgrupamentoType agrupamento) {
this.agrupamento = agrupamento;
}
public OrdenacaoType getOrdenacao() {
return ordenacao;
}
public void setOrdenacao(OrdenacaoType ordenacao) {
this.ordenacao = ordenacao;
}
public LocalDate getVencimentoInicio() {
return vencimentoInicio;
}
public void setVencimentoInicio(LocalDate vencimentoInicio) {
this.vencimentoInicio = vencimentoInicio;
}
public LocalDate getVencimentoFim() {
return vencimentoFim;
}
public void setVencimentoFim(LocalDate vencimentoFim) {
this.vencimentoFim = vencimentoFim;
}
public Collection<BancoEntity> getBancos() {
return bancos;
}
public void setBancos(Collection<BancoEntity> bancos) {
this.bancos = bancos;
}
public Collection<CentroCustoEntity> getCentroCustos() {
return centroCustos;
}
public void setCentroCustos(Collection<CentroCustoEntity> centroCustos) {
this.centroCustos = centroCustos;
}
public Collection<CategoriaEntity> getCategorias() {
return categorias;
}
public void setCategorias(Collection<CategoriaEntity> categorias) {
this.categorias = categorias;
}
public Collection<PessoaEntity> getPessoas() {
return pessoas;
}
public void setPessoas(Collection<PessoaEntity> pessoas) {
this.pessoas = pessoas;
}
public boolean isExcetoBanco() {
return excetoBanco;
}
public void setExcetoBanco(boolean excetoBanco) {
this.excetoBanco = excetoBanco;
}
public boolean isExcetoCentroCusto() {
return excetoCentroCusto;
}
public void setExcetoCentroCusto(boolean excetoCentroCusto) {
this.excetoCentroCusto = excetoCentroCusto;
}
public boolean isExcetoCategoria() {
return excetoCategoria;
}
public void setExcetoCategoria(boolean excetoCategoria) {
this.excetoCategoria = excetoCategoria;
}
public boolean isExcetoPessoa() {
return excetoPessoa;
}
public void setExcetoPessoa(boolean excetoPessoa) {
this.excetoPessoa = excetoPessoa;
}
public String getNome() {
return nome;
}
public boolean isUtilizaBanco() {
return parametroUtilizaBanco.getValue();
}
public boolean isUtilizaCentroCusto() {
return parametroUtilizaCentroCusto.getValue();
}
public boolean isUtilizaCategoria() {
return parametroUtilizaCategoria.getValue();
}
public boolean isUtilizaPessoa() {
return parametroUtilizaPessoa.getValue();
}
public boolean isUtilizaAgrupamento() {
return isUtilizaBanco() || isUtilizaCentroCusto() || isUtilizaCategoria() || isUtilizaPessoa();
}
public Collection<AgrupamentoType> getListaAgrupamento() {
Collection<AgrupamentoType> result = Arrays
.stream(AgrupamentoType.values())
.collect(Collectors.toList());
if (!isUtilizaBanco()) result.removeIf(a -> a.equals(AgrupamentoType.BANCO));
if (!isUtilizaCentroCusto()) result.removeIf(a -> a.equals(AgrupamentoType.CENTRO_CUSTO));
if (!isUtilizaCategoria()) result.removeIf(a -> a.equals(AgrupamentoType.CATEGORIA));
if (!isUtilizaPessoa()) result.removeIf(a -> a.equals(AgrupamentoType.PESSOA));
return result;
}
public OrdenacaoType[] getListaOrdenacao() {
return OrdenacaoType.values();
}
private StreamedContent geraPdf(Collection<LancamentoEntity> lancamentos) {
ReportBuilder reportBuilder = ReportBuilder
.createInstance()
.setTitle(BundleUtils.messageBundle("label.relatorio") + " " + tipoLancamento.getDescription())
.setSubtitle(getFiltro())
.setHeaderHeight(20);
if (isUtilizaAgrupamento()) {
reportBuilder.addField(agrupamento.getDescricao(), agrupamento.getAtributo(), 200, String.class);
}
reportBuilder
.addField(BundleUtils.messageBundle("label.codigo"), "codigo", 100, String.class)
.addField(BundleUtils.messageBundle("label.descricao"), "descricao", 500, String.class)
.addField(BundleUtils.messageBundle("label.vencimento"), "vencimento", 100, LocalDate.class)
.addFieldNumber(BundleUtils.messageBundle("label.valor"), "valor", 100, BigDecimal.class, true, false)
.withListData(lancamentos);
if (isUtilizaAgrupamento()) {
reportBuilder.addGroup(ReportGroupBuilder
.createInstance(reportBuilder, agrupamento.getAtributo())
.addFooterVariable("valor", DJCalculation.SUM)
.build());
}
reportBuilder
.setGrandTotalLegend(BundleUtils.messageBundle("label.total") + ":")
.addGlobalFooterVariable("valor", DJCalculation.SUM);
try {
return reportBuilder.exportPdf("relatorio");
} catch (JRException | IOException ex) {
LogUtils.generate(ex);
JsfUtils.addMessageError(BundleUtils.messageBundle("message.erroImprevistoGeracaoRelatorio"));
return null;
}
}
private Collection<LancamentoEntity> selecionaDados() {
boolean filtraBanco = !excetoBanco && bancos != null && !bancos.isEmpty();
boolean filtraExcetoBanco = excetoBanco && bancos != null && !bancos.isEmpty();
boolean filtraCentroCusto = !excetoCentroCusto && centroCustos != null && !centroCustos.isEmpty();
boolean filtraExcetoCentroCusto = excetoCentroCusto && centroCustos != null && !centroCustos.isEmpty();
boolean filtraCategoria = !excetoCategoria && categorias != null && !categorias.isEmpty();
boolean filtraExcetoCategoria = excetoCategoria && categorias != null && !categorias.isEmpty();
boolean filtraPessoa = !excetoPessoa && pessoas != null && !pessoas.isEmpty();
boolean filtraExcetoPessoa = excetoPessoa && pessoas != null && !pessoas.isEmpty();
return lancamentoFacade
.clientJpaql()
.where()
.contains(filtraBanco, LancamentoEntity_.banco, bancos)
.and(filtraBanco)
.notContains(filtraExcetoBanco, LancamentoEntity_.banco, bancos)
.and(filtraExcetoBanco)
.contains(filtraCentroCusto, LancamentoEntity_.centroCusto, centroCustos)
.and(filtraCentroCusto)
.notContains(filtraExcetoCentroCusto, LancamentoEntity_.centroCusto, centroCustos)
.and(filtraExcetoCentroCusto)
.contains(filtraCategoria, LancamentoEntity_.categoria, categorias)
.and(filtraCategoria)
.notContains(filtraExcetoCategoria, LancamentoEntity_.categoria, categorias)
.and(filtraExcetoCategoria)
.contains(filtraPessoa, LancamentoEntity_.pessoa, pessoas)
.and(filtraPessoa)
.notContains(filtraExcetoPessoa, LancamentoEntity_.pessoa, pessoas)
.and(filtraExcetoPessoa)
.greaterOrEqualsThan(vencimentoInicio != null, LancamentoEntity_.vencimento, vencimentoInicio)
.and(vencimentoInicio != null)
.lessOrEqualsThan(vencimentoFim != null, LancamentoEntity_.vencimento, vencimentoFim)
.collect()
.list();
}
private String getFiltro() {
String result = "";
if (vencimentoInicio != null) {
result += BundleUtils.messageBundle("label.vencimento") + " >= " + DateUtils.formatddMMyyyy(vencimentoInicio) + " ";
}
if (vencimentoFim != null) {
result += BundleUtils.messageBundle("label.vencimento") + " <= " + DateUtils.formatddMMyyyy(vencimentoFim) + " ";
}
if (bancos != null && !bancos.isEmpty()) {
result += BundleUtils.messageBundle("label.bancos") + " = " + bancos.stream().map(b -> b.getNome()).collect(Collectors.joining(", ")) + " ";
}
if (centroCustos != null && !centroCustos.isEmpty()) {
result += BundleUtils.messageBundle("label.centrosCusto") + " = " + centroCustos.stream().map(b -> b.getDescricao()).collect(Collectors.joining(", ")) + " ";
}
if (categorias != null && !categorias.isEmpty()) {
result += BundleUtils.messageBundle("label.contasCaixa") + " = " + categorias.stream().map(b -> b.getDescricao()).collect(Collectors.joining(", ")) + " ";
}
if (pessoas != null && !pessoas.isEmpty()) {
result += BundleUtils.messageBundle("label.pessoas") + " = " + pessoas.stream().map(b -> b.getNome()).collect(Collectors.joining(", ")) + " ";
}
if (result.isEmpty()) {
result += BundleUtils.messageBundle("label.nenhumFiltro");
} else {
result = BundleUtils.messageBundle("label.filtro") + ": " + result;
}
return result;
}
}
No código acima eu criei os atributos de filtros (bancos, centro custos, categorias, pessoas e período vencimento), adicionei os parâmetros pra verificar a utilização. O mais importante foi a utilização da biblioteca de geração do relatório através do builder ReportBuilder. A definição do relatório foi de forma fluente sem precisar desenhar o relatório.
Compilando e executando a aplicação com o tenant = 2 (utiliza Banco, Centro Custo, Categoria e Pessoa):
Tela:
Relatório:
E agora com o tenant = 1 (Não utiliza Banco, Centro Custo, Categoria e Pessoa)
Tela:
Relatório:
Conclusão
Nesse post mostrei como gerar o filtro e relatório pela biblioteca do JARCH. Para a tela eu utilizei alguns composites components que facilitou na filtragem de dados. Na action utilizei a biblioteca de relatório, de forma fluente onde adicionei as informações de colunas e quebras. Não precisei fazer nenhum desenho no JasperStudio ou qualquer ferramenta pra desenhar o relatório, o que otimizou demais o tempo de conclusão dessa funcionalidade.
Segue o link dessa video aula: https://youtu.be/37V8bF9cqvs
Até mais,
Nenhum comentário:
Postar um comentário