domingo, 5 de julho de 2020

Contas Pagar/Receber - Parte XII - Melhorias

Introdução
Nesse décimo segundo post vou dedicar totalmente para fazer alguns ajustes na aplicação. Serão algumas pequenas melhorias no produto em si e também na codificação. 

Ajustando Coluna Pessoa
A aplicação agora tem a opção do usuário utilizar os dados do Banco, Centro Custo, Categoria e Pessoa. Mas independente do parâmetro nossa tela de lançamento está mostrando a pessoa no grid de pesquisa:

Vou ajustar a aplicação para somente mostrar a coluna pessoa se o parâmetro ParametroUtilizaPessoa estiver como true. No LancamentoListAction vou carregar o parâmetro e ocultar a coluna caso o parametro esteja configurado como false.
LancamentoListAction.java:
@JArchDynamicShowDataAction(id = "idCancelarBaixaLancamento", labelMenu = "label.cancelarBaixa", 
    labelButton = "label.cancelarBaixa", nameMethodDataAction = "cancelaBaixaLancamento", confirmation = true, 
    headerConfirmation = "message.cancelarBaixa", messageConfirmation = "message.confirmarCancelamentoBaixaDessaConta", 
    order = 2, elDisabled = "#{(l -> l.aberto)(lancamento)}")
@JArchViewScoped
public class LancamentoListAction extends CrudListAction<LancamentoEntity, LancamentoFacade> {

@JArchParameter
@Inject
private ParametroUtilizaPessoa parametroUtilizaPessoa;

/** Código Ocultado **\

@PostConstruct
private void init() {
        /** Código Ocultado **\
if (!parametroUtilizaPessoa.getValue()) {
getColumnDataTable("pessoa.nome").ifPresent(c -> c.hide());
}
}
Alteração Pessoa na Conta
A aplicação está configurada para incrementar o código por pessoa. Então não posso permitir que seja alterado a pessoa depois que a conta foi incluída, porque o código não vai estar de acordo com a regra de incrementação por pessoa. Então vou implementar uma crítica no evento de alteração de pessoa:
LancamentoObserver.java

private void naoPermiteAlteracaoPessoa(@Observes @JArchEventChangeField("pessoa") LancamentoEntity lancamento) {
throw new ValidationException(BundleUtils.messageBundle("message.alteracaoPessoaNaoPermitidaDevidoRegraCodigo"));
}
bundle_pt_BR.properties:

message.alteracaoPessoaNaoPermitidaDevidoRegraCodigo=Altera\u00E7\u00E3o de Pessoa n\u00E3o permitida devido a regra \ de c\u00F3digo

Agora se tentar alterar uma pessoa (com o parametro de pessoa ativo) temos o seguinte resultado:

Mas ainda posso melhorar mais, vou desabilitar os dados da pessoa quando for alteração.
lancamentoData.xhtml:

<a:panelGrid columns="1" rendered="#{lancamentoDataAction.utilizaPessoa}">
<h:panelGroup>
<e:lookup id="idLancamentoPessoa" labelUnique="#{e:bundle('label.pessoa')}"
header="#{e:bundle('label.pessoa')}" value="#{lancamentoDataAction.entity.pessoa}"
actionFilterSelect="#{pessoaFilterSelectAction}"
disabled="#{lancamentoDataAction.blockedMaster or lancamentoDataAction.stateChange}"
required="true" createExtensionInternal="true" widthCode="120" typeCode="cpf-cnpj"/>
</h:panelGroup>
</a:panelGrid>

Repare que no atributo disabled eu adicionei o "or lancamentoDataAction.stateChange" na expression language. Mas agora que bloqueamos a tela não precisamos mais da implementação do observer ?A resposta é SIM, precisamos do observer, porque o observer é a garantia de não haver a alteração.  O bloqueio impede a alteração pela tela de lançamento, mas pode existir alguma implementação de alteração via código, ou até mesmo outra tela. O observer sempre deve ser implementado para garantir essa integridade.
Segue o print do campo pessoa desabilitado: (print com multitenant que não está habilitado a pessoa)

Correção nos Alertas
Quando fiz a implementação dos Alerts para mostrar alertas de quantidade de contas pagar vencido/ vencendo eu esqueci de adicionar o filtro de contas em aberto. No método getAlerts() no LevantamentoFacade, onde verifica as contas a pagar vencido está implementado dessa maneira:
LancamentoFacade.java

long contasPagarVencido = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.lessThan(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

Vou corrigir adicionando o filtro de aberto:

long contasPagarVencido = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.lessThan(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

E a mesma coisa no vencendo, esta assim:

long contasPagarVencendo = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

Vou corrigir adicionando o filtro de aberto:

long contasPagarVencendo = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

Enxugando Alerts
A implementação do método getAlerts() no LancamentoFacade apesar de bem simples tem mais código do que o necessário. Atualmente está assim:
LancamentoFacade.java:

public Alerts getAlerts() {
Alerts alerts = new Alerts();

long contasPagarVencido = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.lessThan(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

if (contasPagarVencido > 0) {
alerts.add(AlertBuilder
.newInstance()
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(contasPagarVencido + " " + BundleUtils.messageBundle("label.contaPagar"))
.build());
}

long contasPagarVencendo = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.vencimento, LocalDate.now())
.collect()
.count();

if (contasPagarVencendo > 0) {
alerts.add(AlertBuilder
.newInstance()
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(contasPagarVencendo + " " + BundleUtils.messageBundle("label.contaPagar"))
.build());
}

return alerts;
}

Vou refatorar esse método movendo ele para AlertaFacade no pacote alerta, porque não faz parte do caso de uso Lançamento gerar alertas:
AlertaFacade.java:
public class AlertaFacade {

@Inject
private LancamentoFacade lancamentoFacade;

public Alerts getAlerts() {
Alerts alerts = new Alerts();

long contasPagarVencido = lancamentoFacade.contaPagarAbertoAte(LocalDate.now().minusDays(1)).size();

if (contasPagarVencido > 0) {
alerts.add(AlertBuilder
.newInstance()
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(contasPagarVencido + " " + BundleUtils.messageBundle("label.contaPagar"))
.build());
}

long contasPagarVencendo = lancamentoFacade.contaPagarAbertoEm(LocalDate.now()).size();

if (contasPagarVencendo > 0) {
alerts.add(AlertBuilder
.newInstance()
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(contasPagarVencendo + " " + BundleUtils.messageBundle("label.contaPagar"))
.build());
}

return alerts;
}

}
Refatorei o levantamento de contas pagar vencida e contas pagar vencendo, criando métodos dentro do LancamentoFacade para que possam ser reaproveitados (vou reaproveita-los em breve).
LancamentoFacade.java:

public Collection<LancamentoEntity> contaPagarAbertoAte(LocalDate vencimento) {
return clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.lessOrEqualsThan(LancamentoEntity_.vencimento, vencimento)
.collect()
.list();
}

public Collection<LancamentoEntity> contaPagarAbertoEm(LocalDate vencimento) {
return clientJpaql()
.where()
.equalsTo(LancamentoEntity_.aberto, true)
.and()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.equalsTo(LancamentoEntity_.vencimento, vencimento)
.collect()
.list();
}
Enxugando Messages
A implementação do método getMessages() no LancamentoFacade apesar de bem simples tem mais código do que o necessário. Atualmente está assim:
LancamentoFacade.java:

public Messages getMessages() {
Messages messages = new Messages();

Collection<LancamentoEntity> contasPagar = clientJpaql()
.where()
.equalsTo(LancamentoEntity_.tipoLancamento, TipoLancamentoType.PAGAR)
.and()
.lessOrEqualsThan(LancamentoEntity_.vencimento, LocalDate.now())
.and()
.equalsTo(LancamentoEntity_.aberto, true)
.collect()
.list();

contasPagar
.stream()
.filter(l -> l.getVencimento().isBefore(LocalDate.now()))
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.build()));

contasPagar
.stream()
.filter(l -> l.getVencimento().isEqual(LocalDate.now()))
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.build()));

return messages;
}

Vou remover esse método para a classe MensagemFacade no pacote mensagem:
MensagemFacade.java:

public class MensagemFacade {

@Inject
private LancamentoFacade lancamentoFacade;

public Messages getMessages() {
Messages messages = new Messages();

Collection<LancamentoEntity> contasPagar = lancamentoFacade.contaPagarAbertoAte(LocalDate.now());

contasPagar
.stream()
.filter(l -> l.isVencendo())
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.build()));

contasPagar
.stream()
.filter(l -> l.isVencido())
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.build()));

return messages;
}
}
Nessa refatoração além de mover o método getMessages() para o MensagemFacade eu reutilizei o método contaPagarAbertoAte do LancamentoFacade. Implementei também os métodos isVencido() e isVencendo() no LancamentoEntity, porque estamos usando O.O. É responsabilidade da classe LancamentoEntity saber se o lançamento está vencido ou vencendo. 
LancamentoEntity.java:

public boolean isVencido() {
return aberto && vencimento.isBefore(LocalDate.now());
}

public boolean isVencendo() {
return aberto && vencimento.isEqual(LocalDate.now());
}
Agora preciso acertar o SessionListener para usar os métodos do AlertaFacade e MensagemFacade.
SessionListener.java:

@WebListener
public class SessionListener implements HttpSessionListener {

@Inject
private SessionInformation sessionInformation;

@Inject
private AlertaFacade alertaFacade;

@Inject
private MensagemFacade mensagemFacade;

@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
sessionInformation.put("alerts", alertaFacade.getAlerts());
sessionInformation.put("messages", mensagemFacade.getMessages());
}

@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
//
}
}
Atualizando Alertas e Mensagens
Atualmente a aplicação só está atualizando os alertas e mensagens quando loga na aplicação. Vou acertar para que seja atualizado os alertas e as mensagens quando houver qualquer tipo de evento de CRUD no lançamento:
LancamentoObserver.java:

private void alertaMensagem(@Observes @JArchEventInsertChange(momentPersistMerge = AFTER) LancamentoEntity lancamento, AlertaFacade alertaFacade, MensagemFacade mensagemFacade) {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}

private void alertaMensagemExclusao(@Observes @JArchEventDelete(momentRemove = AFTER) LancamentoEntity lancamento, AlertaFacade alertaFacade, MensagemFacade mensagemFacade) {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}
Refatorei também as classes AlertaFacade e MensagemFacade adicionando os métodos para atualizar a sessão.
AlertaFacade.java:

public void atualizaAlertaSessao() {
sessionInformation.put("alerts", getAlerts());
}
MensagemFacade.java:

public void atualizaMensagemSessao() {
sessionInformation.put("messages", getMessages());
}
E refatorei também o SessionListener para utilizar esses métodos: 
SessionListener.java:

@WebListener
public class SessionListener implements HttpSessionListener {

@Inject
private AlertaFacade alertaFacade;

@Inject
private MensagemFacade mensagemFacade;

@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}

@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
//
}
}
Agora sempre que uma conta for incluída, alterada ou excluída a lista de alertas e mensagens serão atualizadas.

Link Alertas
Vou ajustar agora o link de alertas para abrir a tela de lançamento quando clicado no item. Por exemplo, se clicar no item de contas vencidas somente será mostrado as contas vencidas. 
AlertaFacade.java:

public Alerts getAlerts() {
Alerts alerts = new Alerts();

long contasPagarVencido = lancamentoFacade.contaPagarAbertoAte(LocalDate.now().minusDays(1)).size();

if (contasPagarVencido > 0) {
alerts.add(AlertBuilder
.newInstance()
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(contasPagarVencido + " " + BundleUtils.messageBundle("label.contaPagar"))
.link("../lancamento/lancamentoList.jsf?tipo=" + PAGAR + "&status=" + StatusLancamentoType.VENCIDO)
.build());
}

long contasPagarVencendo = lancamentoFacade.contaPagarAbertoEm(LocalDate.now()).size();

if (contasPagarVencendo > 0) {
alerts.add(AlertBuilder
.newInstance()
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(contasPagarVencendo + " " + BundleUtils.messageBundle("label.contaPagar"))
.link("../lancamento/lancamentoList.jsf?tipo=" + PAGAR + "&status=" + StatusLancamentoType.VENCENDO)
.build());
}

return alerts;
}

Agora estou adicionado o link no builder passando o filtro de tipo e status. Agora preciso alterar o LancamentoListAction para interpretar esses parâmetros. Atente que agora estou usando o enumerado no parâmetro tipo e estou usando um novo enumerado para passar o status. Agora vou refatorar o LancamentoListAction para ler os novos parâmetros. 
LancamentoListAction.java:

@PostConstruct
private void init() {
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCIDO);
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCENDO);

tipoLancamento = TipoLancamentoType.valueOf(JsfUtils.getParameterRequest("tipo"));
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_TIPO_LANCAMENTO, "tipoLancamento", tipoLancamento);

String statusParam = JsfUtils.getParameterRequest("status");
if (statusParam != null) {
StatusLancamentoType status = StatusLancamentoType.valueOf(statusParam);
if (StatusLancamentoType.VENCIDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCIDO, "dataSistema", LocalDate.now());
} else if (StatusLancamentoType.VENCENDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCENDO, "dataSistema", LocalDate.now());
}
}

if (!parametroUtilizaPessoa.getValue()) {
getColumnDataTable("pessoa.nome").ifPresent(c -> c.hide());
}
}

Estou ativando os filtros de acordo com o status passado. Então preciso implementar o filtro no LancamentoEntity.
LancamentoEntity.java:

@JArchOrderBy(fields = @JArchOrderByField(value = "id", desc = true))
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_TIPO_LANCAMENTO,
conditionWhereJpa = "lancamento.tipoLancamento = :tipoLancamento")
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_VENCIDO,
conditionWhereJpa = "lancamento.vencimento < :dataSistema")
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_VENCENDO,
conditionWhereJpa = "lancamento.vencimento = :dataSistema")
@JArchLookup(codeAttribute = "codigo", descriptionAttribute = "descricao")
@Audited
@Table(name = "tb_lancamento",
indexes = {
@Index(columnList = "cd_lancamento", name = "dx_lancamentocdlan"),
@Index(columnList = "ds_lancamento", name = "dx_lancamentodslan"),
@Index(columnList = "dt_vencimento", name = "dx_lancamentodtven"),
@Index(columnList = "vl_lancamento", name = "dx_lancamentovllan")
})
@Entity(name = "lancamento")
@SequenceGenerator(name = "LancamentoIdSequence", sequenceName = "sq_idlancamento", allocationSize = 1)
public class LancamentoEntity extends CrudMultiTenantEntity {

public static final String FILTRO_TIPO_LANCAMENTO = "lancamentoEntity.filtroTipoLancamento";
public static final String FILTRO_VENCIDO = "lancamentoEntity.filtroVencido";
public static final String FILTRO_VENCENDO = "lancamentoEntity.filtroVencendo";

Novo enumerado:
StatusLancamentoType.java:

public enum StatusLancamentoType {
VENCIDO, VENCENDO, VENCER;
}
Como mudou o parâmetro tipo na chamada do link da página de lançamento, preciso fazer mais alguns ajustes, primeiro deles é acertar o método que retorna a pagina de lista:
LancamentoDataAction.java:

@Override
public String getPageList() {
return "lancamentoList.jsf?tipo=" + tipoLancamento;
}

Na pagina de Bem Vindo, no link dos card's do dashboard preciso ajustar também para passar os parâmetros corretos.
bemVindo.xhtml:

<e:dashboardUnity icon="fa fa-money fa-5x"
classMinHeight="h100"
styleIcon="color: red"
title="#{bemVindoAction.quantidadeContaPagar}"
labelButtonLink="#{e:bundle('label.acessar')}"
link="../lancamento/lancamentoList.jsf?tipo=PAGAR"
descriptionLink="#{e:bundle('message.acessarPagina')}"/>

<e:dashboardUnity icon="fa fa-money fa-5x"
classMinHeight="h100"
styleIcon="color: orange"
title="#{bemVindoAction.quantidadeContaPago}"
labelButtonLink="#{e:bundle('label.acessar')}"
link="../lancamento/lancamentoList.jsf?tipo=PAGAR"
descriptionLink="#{e:bundle('message.acessarPagina')}"/>

<e:dashboardUnity icon="fa fa-money fa-5x"
classMinHeight="h100"
styleIcon="color: blue"
title="#{bemVindoAction.quantidadeContaReceber}"
labelButtonLink="#{e:bundle('label.acessar')}"
link="../lancamento/lancamentoList.jsf?tipo=RECECER"
descriptionLink="#{e:bundle('message.acessarPagina')}"/>

<e:dashboardUnity icon="fa fa-money fa-5x"
classMinHeight="h100"
styleIcon="color: green"
title="#{bemVindoAction.quantidadeContaRecebido}"
labelButtonLink="#{e:bundle('label.acessar')}"
link="../lancamento/lancamentoList.jsf?tipo=RECEBER"
descriptionLink="#{e:bundle('message.acessarPagina')}"/>

Também preciso ajustar o MenuAction passando o tipo correto:
MenuAction.java:

menu.add(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.lancamento"))
.addSubMenu(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.contaPagar"))
.action("../lancamento/lancamentoList.jsf?tipo=" + TipoLancamentoType.PAGAR)
.build())
.addSubMenu(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.contaReceber"))
.action("../lancamento/lancamentoList.jsf?tipo=" + TipoLancamentoType.RECEBER)
.build())
.build());

Agora clicando no item do menu alerta será direcionado direto pra página de lista de lançamento com os registros filtrados:

Link Mensagens
Vou ajustar agora o link de mensagens para abrir a tela de lançamento quando clicado no item. Vou adicionar o filtro por ID no link da mensagem, abrindo somente com aquele registro:
MensagemFacade.java:

public Messages getMessages() {
Messages messages = new Messages();

Collection<LancamentoEntity> contasPagar = lancamentoFacade.contaPagarAbertoAte(LocalDate.now());

contasPagar
.stream()
.filter(l -> l.isVencendo())
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.warning()
.title(BundleUtils.messageBundle("label.pagarVencendo"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.link("../lancamento/lancamentoList.jsf?tipo=" + TipoLancamentoType.PAGAR + "&id=" + l.getId())
.build()));

contasPagar
.stream()
.filter(l -> l.isVencido())
.sorted(Comparator.comparing(LancamentoEntity::getVencimento).reversed())
.forEach(l -> messages.add(MessageBuilder.newInstance()
.code(l.getId())
.danger()
.title(BundleUtils.messageBundle("label.pagarVencido"))
.description(DateUtils.formatddMMyyyy(l.getVencimento()) + " " + l.getDescricao())
.link("../lancamento/lancamentoList.jsf?tipo=" + TipoLancamentoType.PAGAR + "&id=" + l.getId())
.build()));

return messages;
}

LancamentoListAction.java: 

@PostConstruct
private void init() {
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCIDO);
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCENDO);
deactiveWhereJpa(LancamentoEntity.FILTRO_ID);

tipoLancamento = TipoLancamentoType.valueOf(JsfUtils.getParameterRequest("tipo"));
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_TIPO_LANCAMENTO, "tipoLancamento", tipoLancamento);

String statusParam = JsfUtils.getParameterRequest("status");
if (statusParam != null) {
StatusLancamentoType status = StatusLancamentoType.valueOf(statusParam);
if (StatusLancamentoType.VENCIDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCIDO, "dataSistema", LocalDate.now());
} else if (StatusLancamentoType.VENCENDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCENDO, "dataSistema", LocalDate.now());
}
}

String id = JsfUtils.getParameterRequest("id");
if (id != null) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_ID, "id", Long.valueOf(id));
}

if (!parametroUtilizaPessoa.getValue()) {
getColumnDataTable("pessoa.nome").ifPresent(c -> c.hide());
}
}

Agora vou adicionar o filtro de ID no LancamentoEntity:
LancamentoEntity:
@JArchOrderBy(fields = @JArchOrderByField(value = "id", desc = true))
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_TIPO_LANCAMENTO,
conditionWhereJpa = "lancamento.tipoLancamento = :tipoLancamento")
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_VENCIDO,
conditionWhereJpa = "lancamento.vencimento < :dataSistema")
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_VENCENDO,
conditionWhereJpa = "lancamento.vencimento = :dataSistema")
@JArchSearchWhereJpa(id = LancamentoEntity.FILTRO_ID,
conditionWhereJpa = "lancamento.id = :id")
@JArchLookup(codeAttribute = "codigo", descriptionAttribute = "descricao")
@Audited
@Table(name = "tb_lancamento",
indexes = {
@Index(columnList = "cd_lancamento", name = "dx_lancamentocdlan"),
@Index(columnList = "ds_lancamento", name = "dx_lancamentodslan"),
@Index(columnList = "dt_vencimento", name = "dx_lancamentodtven"),
@Index(columnList = "vl_lancamento", name = "dx_lancamentovllan")
})
@Entity(name = "lancamento")
@SequenceGenerator(name = "LancamentoIdSequence", sequenceName = "sq_idlancamento", allocationSize = 1)
public class LancamentoEntity extends CrudMultiTenantEntity {

public static final String FILTRO_TIPO_LANCAMENTO = "lancamentoEntity.filtroTipoLancamento";
public static final String FILTRO_VENCIDO = "lancamentoEntity.filtroVencido";
public static final String FILTRO_VENCENDO = "lancamentoEntity.filtroVencendo";
public static final String FILTRO_ID = "lancamentoEntity.filtroId";

Agora ao clicar no item de mensagem abre a tela de lista filtrado na conta selecionado:

Filtro Vencimento e Valor
Na tela de lançamento vou alterar o filtro de vencimento e valor para que o combobox fique desabilitado:
LancamentoListAction.java:

@PostConstruct
private void init() {
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCIDO);
deactiveWhereJpa(LancamentoEntity.FILTRO_VENCENDO);
deactiveWhereJpa(LancamentoEntity.FILTRO_ID);

tipoLancamento = TipoLancamentoType.valueOf(JsfUtils.getParameterRequest("tipo"));
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_TIPO_LANCAMENTO, "tipoLancamento", tipoLancamento);

String statusParam = JsfUtils.getParameterRequest("status");
if (statusParam != null) {
StatusLancamentoType status = StatusLancamentoType.valueOf(statusParam);
if (StatusLancamentoType.VENCIDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCIDO, "dataSistema", LocalDate.now());
} else if (StatusLancamentoType.VENCENDO.equals(status)) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_VENCENDO, "dataSistema", LocalDate.now());
}
}

String id = JsfUtils.getParameterRequest("id");
if (id != null) {
activeAndAddParamWhereJpa(LancamentoEntity.FILTRO_ID, "id", Long.valueOf(id));
}

if (!parametroUtilizaPessoa.getValue()) {
getColumnDataTable("pessoa.nome").ifPresent(c -> c.hide());
}

getFieldSearch("vencimentoInicio").ifPresent(f -> f.disabledCondition(true));
getFieldSearch("vencimentoFim").ifPresent(f -> f.disabledCondition(true));
getFieldSearch("valorInicio").ifPresent(f -> f.disabledCondition(true));
getFieldSearch("valorFim").ifPresent(f -> f.disabledCondition(true));
}

O getFieldSearch acima está buscando o campo pelo ID porque como existe mais de uma campo com o mesmo atributo foi necessário colocar um ID pra eles. Segue a implementação:
package-info.java:
@JArchSearchTab(order = 1,
searchFields = {
@JArchSearchField(clazzEntity = LancamentoEntity.class, attribute = "codigo", label = "label.codigo",         type = FieldType.CODE, row = 1, column = 1, span = 3),
@JArchSearchField(clazzEntity = LancamentoEntity.class, attribute = "descricao", label = "label.descricao",         type = FieldType.DESCRIPTION, row = 1, column = 2, span = 9),
@JArchSearchField(clazzEntity = LancamentoEntity.class, id = "vencimentoInicio", attribute = "vencimento",         label = "label.vencimento", type = FieldType.DATE, condition = ConditionSearchType.LARGER_EQUAL,         row = 2, column = 1, span = 3),
@JArchSearchField(clazzEntity = LancamentoEntity.class, id = "vencimentoFim", attribute = "vencimento",         label = "label.vencimento", type = FieldType.DATE, condition = ConditionSearchType.LESS_EQUAL,         row = 2, column = 2, span = 3),
@JArchSearchField(clazzEntity = LancamentoEntity.class, id = "valorInicio", attribute = "valor",         label = "label.valor", type = FieldType.MONEY, condition = ConditionSearchType.LARGER_EQUAL,         row = 2, column = 2, span = 3),
@JArchSearchField(clazzEntity = LancamentoEntity.class, id = "valorFim", attribute = "valor",         label = "label.valor", type = FieldType.MONEY, condition = ConditionSearchType.LESS_EQUAL,         row = 2, column = 3, span = 3),
})
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="codigo", title = "label.codigo", width = 90,     type = FieldType.CODE)
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="descricao", title = "label.descricao",     width = 270, type = FieldType.DESCRIPTION)
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="pessoa.nome", title = "label.pessoa",     width = 270, type = FieldType.NAME)
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="aberto", title = "label.aberto",     width = 60, type = FieldType.BOOLEAN)
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="vencimento", title = "label.vencimento",     width = 90, type = FieldType.DATE)
@JArchColumnDataTable(clazzEntity = LancamentoEntity.class, attribute ="valor", title = "label.valor",     width = 100, type = FieldType.MONEY)

package br.com.jarch.contas.client.lancamento;
Executando o código acima:

Repare que agora os combobox de Vencimento e Valor estão desabilitados.

Evento Login
O levantamento de alertas e mensagens está sendo feito no momento da criação da sessão no SessionListener. Quando a aplicação possuí somente um tenant não tem problema, mas quando mais de um é usado o resultado não será o correto. Porque no momento da criação da sessão o tenant ainda não foi configurado pelo JARCH. Para corrigir isso vamos interceptar o evento de login e implementar a atualização das informações de alertas e mensagens nele. Vou criar um Observer de login no pacote de login.
LoginObserver.java:

public class LoginObserver {

private void login(@Observes @JArchEventLoginAfter IUser user, AlertaFacade alertaFacade, MensagemFacade mensagemFacade) {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();
}
}
Conclusão
Nesse post fiz diversas refatorações para reaproveitamento do código, fiz também alguns ajustes de lógica, melhoria nos alertas e mensagens, desabilitei o combobox para pesquisa por intervalo (vencimento e valor), e por último mostrei o evento de login. No próximo post vou continuar com a implementação do sistema utilizando recursos do JARCH.

Segue o link dessa video aula: https://youtu.be/khX-efWuWlA

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