Introdução
Nesse vigésimo sexto post vou criar um subprocesso de evento, onde será informado ao operador que o lançamento foi reprovado e na sequência esse Lançamento será excluído.
SubProcesso Evento
Vou adicionar uma nova Lane para indicar o operador, nessa Lane vou criar um subprocesso de reprovação. Vou alterar também o evento de inicialização do fluxo para ficar nessa nova Lane.

O Sub Process de evento precisa estar com a seguinte configuração:
O Sub Process de evento precisa estar com a seguinte configuração:
No componente de Escalation dentro do Sub Process vou colocar o atributo Name como Escalation_Reprovado
No componente Message vou configurar o seguinte Delegate #{mensagemReprovacaoDelegate}:
No componente Service Task vou atribuir o Delegate Expression #{apagaLancamentoDelegate}
ApagaLancamentoDelegate.java:
@Named
public class ApagaLancamentoDelegate implements JavaDelegate {
@Inject
private LancamentoFacade lancamentoFacade;
@Override
public void execute(DelegateExecution execution) {
CDI.current().select(UserInformation.class).get().setUserSystem();
CDI.current().select(MultiTenant.class).get().set((Long) execution.getVariable(Constant.ID_MULTITENANT));
Long idLancamento = Long.valueOf(execution.getBusinessKey());
lancamentoFacade.delete(idLancamento);
}
}
Disparando Evento Escalation
Nas Lanes de Supervisor, Gerente e Diretor vou alterar os Sequences Flow de Reprovação para disparar o evento de Escalation, e vou ajustar as tarefas de Aprovar Conta para finalizar o fluxo:
Implementando MensagemReprovacaoDelegate
Agora vou implementar o Delegate MensagemAprovacaoDelegate para gerar uma mensagem para o Operador informando que a conta não foi aprovada. Vou utilizar a biblioteca de Communication do JARCH que serve para armazenar e gerenciar mensagens:
MensagemReprovacaoDelegate.java:
@Named
public class MensagemReprovacaoDelegate implements JavaDelegate {
@Inject
private CommunicationFacade communicationFacade;
@Inject
private LancamentoFacade lancamentoFacade;
@Override
public void execute(DelegateExecution execution) {
CDI.current().select(UserInformation.class).get().setUserSystem();
CDI.current().select(MultiTenant.class).get().set((Long) execution.getVariable(Constant.ID_MULTITENANT));
Long idOperador = (Long) execution.getVariable(Constant.ID_OPERADOR);
Long idLancamento = Long.valueOf(execution.getBusinessKey());
LancamentoEntity lancamento = lancamentoFacade.find(idLancamento);
communicationFacade.insert(new CommunicationEntity(MessageBuilder
.newInstance()
.type(0)
.to(idOperador)
.code(lancamento.getId())
.title(BundleUtils.messageBundle("message.lancamentoReprovado"))
.body(BundleUtils.messageBundle("message.corpoMensagemReprovacao", lancamento.getCodigo(),
lancamento.getDescricao(), DateUtils.formatddMMyyyy(lancamento.getVencimento()),
NumberUtils.formatMoney(lancamento.getValor())))
.build()));
}
}
Regularizando Banco Dados
Vou apagar a tabela TB_COMUNICACAOMENSAGEM, para que na próxima publicação seja regularizado.
DROP TABLE TB_COMUNICACAOMENSAGEM;
Ajustes Finais
Vou adicionar mais duas constantes no Constant:
Constant.java:
public final class Constant {
public static final String PROCESS_AVALIACAO_CONTA = "process-avaliacao-conta";
public static final String ID_AVALIAR_LANCAMENTO = "idAvaliarLancamento";
public static final String ID_CANCELAR_BAIXA_LANCAMENTO = "idCancelarBaixaLancamento";
public static final String ID_OPERADOR = "idOperador";
public static final String ID_MULTITENANT = "idMultitenant";
private Constant() {
}
}
Vou ajustar o método do observer de Lançamento para adicionar o ID do multitenant e o ID do operador dentro da instância do fluxo BPM:
LancamentoObserver.java:
private void instanciaFluxoLancamentoOperador(@Observes @JArchEventInsert(momentPersist = MomentType.AFTER)
LancamentoEntity lancamento, UserInformation userInformation, MultiTenant multiTenant,
RuntimeService runtimeService) {
if (userInformation.get(UsuarioEntity.class).getCargo() != CargoType.OPERADOR) {
return;
}
if (!parametroUtilizaBpmAutorizacaoLancamento.getValue()) {
return;
}
runtimeService
.createProcessInstanceByKey(Constant.PROCESS_AVALIACAO_CONTA)
.businessKey(lancamento.getId().toString())
.setVariable(Constant.ID_MULTITENANT, multiTenant.get())
.setVariable(Constant.ID_OPERADOR, userInformation.get().getId())
.setVariable("descricao", lancamento.getDescricao())
.setVariable("valor", lancamento.getValor().doubleValue())
.setVariable("vencimento", lancamento.getVencimento())
.execute();
}
E vou ajustar o AutorizacaoObserver para verificar o usuário logado e permitir manutenção na entidade CommunicationEntity:
AutorizacaoObserver.java:
public class AutorizacaoObserver {
private void validaAlteracaoViaOperador(@Observes @JArchEventValidChange CrudMultiTenantEntity entity, UserInformation userInformation) {
if (CommunicationEntity.class.isAssignableFrom(entity.getClass())) {
return;
}
UsuarioEntity usuarioSistema = userInformation.get(UsuarioEntity.class);
if (UsuarioEntity.class.isAssignableFrom(entity.getClass()) && usuarioSistema.equals(entity)) {
return;
}
if (usuarioSistema.getCargo() == CargoType.OPERADOR) {
throw new ValidationException(BundleUtils.messageBundle("message.naoAutorizadoEntreContatoSupervisor"));
}
}
private void validaExclusaoViaOperador(@Observes @JArchEventValidDelete CrudMultiTenantEntity entity, UserInformation userInformation) {
if (CommunicationEntity.class.isAssignableFrom(entity.getClass())) {
return;
}
if (userInformation.isSystem()) {
return;
}
UsuarioEntity usuarioSistema = userInformation.get(UsuarioEntity.class);
if (usuarioSistema != null && usuarioSistema.getCargo() == CargoType.OPERADOR) {
throw new ValidationException(BundleUtils.messageBundle("message.naoAutorizadoEntreContatoSupervisor"));
}
}
}
AlertaFacade.java:
Agora vou alterar a aplicação para utilizar o GlobalInformation no lugar do SessionInformation:
public class AlertaFacade {
@Inject
private LancamentoFacade lancamentoFacade;
@Inject
private GlobalInformation globalInformation;
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=" + TipoLancamentoType.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=" + TipoLancamentoType.PAGAR + "&status=" + StatusLancamentoType.VENCENDO)
.build());
}
return alerts;
}
public void atualizaAlertaSessao() {
globalInformation.set("alerts", getAlerts());
}
}
MessagesProduces.java:
Agora vou alterar a aplicação para utilizar o GlobalInformation no lugar do SessionInformation e ajustar para levantar as mensagens gravadas na entidade CommunicationEntity do usuário logado:
public class MessagesProduces {
@Produces
public Messages messages(GlobalInformation globalInformation) {
return (Messages) globalInformation.get("messages");
}
}
MensagemFacade.java:
Agora vou alterar a aplicação para utilizar o GlobalInformation no lugar do SessionInformation:
public class MensagemFacade {
@Inject
private LancamentoFacade lancamentoFacade;
@Inject
private CommunicationFacade communicationFacade;
@Inject
private GlobalInformation globalInformation;
@Inject
private UserInformation userInformation;
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()));
if (userInformation.exists()) {
communicationFacade
.searchAllFilter("to", userInformation.get().getId())
.forEach(c ->
messages.add(MessageBuilder.newInstance()
.code(c.getCode())
.warning()
.title(c.getTitle())
.description(c.getTitle())
.link("../communication/communicationList.jsf?type=" + c.getType() + "&code=" + c.getCode())
.build()));
}
return messages;
}
public void atualizaMensagemSessao() {
globalInformation.set("messages", getMessages());
}
}
AlertsProduces.java:
public class AlertsProduces {
@Produces
public Alerts alerts(GlobalInformation globalInformation) {
return (Alerts) globalInformation.get("alerts");
}
}
E pra finalizar vou criar o pacote comunicacao no contas-client e implementar um observer no CommunicatioEntity para atualizar a lista de mensagens:
ComunicacaoObserver.java:
public class ComunicacaoObserver {
private void inclusaoAlteracao(@Observes @JArchEventInsertChange(momentPersistMerge = MomentType.AFTER) CommunicationEntity communicationEntity,
MensagemFacade mensagemFacade) {
mensagemFacade.atualizaMensagemSessao();
}
private void exclusao(@Observes @JArchEventDelete(momentRemove = MomentType.AFTER) CommunicationEntity communicationEntity,
MensagemFacade mensagemFacade) {
mensagemFacade.atualizaMensagemSessao();
}
}
bundle_pt_BR.properties:
Agora compilando e publicando vou lançar uma conta como Operador:
message.lancamentoReprovado=Lan\u00E7amento Reprovado
message.corpoMensagemReprovacao=C\u00F3digo: %s<br/>Descri\u00E7\u00E3o: %s<br/>Vencimento: %s<br/>Valor: %s \ <br/><br/><h1>N\u00E3o foi aprovado</h1>
Agora vou logar como Supervisor e reprovar a conta:
Reprovando o lançamento:
Agora vou logar novamente como o Operador e verificar que a mensagem foi enviada:
E acessando o link da mensagem:
Conclusão
Nesse post criei um subprocesso de evento que gera a mensagem de reprovação para o operador e apaga o lançamento. Para o envio da mensagem para o operador eu utilizei a entidade CommunicationEntity do JARCH, para que essa mensagem fique gravada até a leitura e exclusão pelo Operador.
Nenhum comentário:
Postar um comentário