quinta-feira, 18 de junho de 2020

Contas Pagar/Receber - Parte II - Criação do Primeiro CRUD

Introdução

Nesse segundo post vou mostrar como criar a primeira lógica de CRUD de Banco. Esse CRUD terá somente dois campos Código e Nome. A geração de código é feito via APT (Annotation Processors Tools), onde se defini as anotações dentro package-info.java e na compilação esse código é gerado.

Gerando Anotação via IDE
Pela IDE basta acessarmos o pacote criado no módulo client (contas-client), e abrir o package-info.java


Nesse package-info.java vamos incluir a definição do nosso CRUD de Banco.

@JArchGenerateLogicCrud(nameSubPackage = "banco",
master = @JArchGenerateMaster(name = "Banco", tableName = "tb_banco",
fields = {
@JArchGenerateField(fieldName = "codigo", fieldTable = "cd_banco",
description = "Código", type = FieldType.CODE, codeLookup = true,
required = true, exclusive = true,
search = @JArchGenerateSearch(row = 1, column = 1, span = 3),
xhtml = @JArchGenerateXhtml(rowDataXhtml = 1, columnDataXhtml = 1, showDataTableList = true)),
@JArchGenerateField(fieldName = "nome", fieldTable = "nm_banco", description = "Nome",
type = FieldType.NAME, descriptionLookup = true, required = true, exclusive = true,
search = @JArchGenerateSearch(row = 1, column = 2, span = 9),
xhtml = @JArchGenerateXhtml(rowDataXhtml = 2, columnDataXhtml = 1, showDataTableList = true))
}
)
)

Esse código acima está definindo uma lógica de Banco, com os seguintes atributos:
Código: Sendo de preenchimento obrigatório, exclusivo, pesquisável e será mostrado no grid de consulta.
Nome: Sendo de preenchimento obrigatório, exclusivo, pesquisável e será mostrado no grid de consulta.

O mapeamento para JPA será feito para a tabela TB_BANCO e as colunas serão CD_BANCO e NM_BANCO

Gerando Anotação via GCA
O GCA é uma ferramenta visual que gera a anotação através de uma interface gráfica. Essa ferramenta está disponível em www.jarch.com.br

Após a criação dos campos conforme print anterior é só executar o botão Generate Code ou Geração Código (Depende da linguagem do navegador) e selecionar a log, conforme print abaixo:

Agora basta somente copiar o código gerado e colar na IDE dentro do package-info.java

Gerando o Código
Para a geração do código basta somente executar um dos seguintes passos:

1. Pela linha de comando:

2 Pela própria IDE:


Após a execução do MAVEN será gerado os seguintes artefatos:

Módulo CONTAS-CLIENT 

BancoEntity.java: Essa classe é o mapeamento objeto relacional da especificação JPA:
package br.com.jarch.conta.client.banco;

import br.com.jarch.annotation.JArchLookup;
import br.com.jarch.annotation.JArchValidExclusive;
import br.com.jarch.annotation.JArchValidRequired;
import br.com.jarch.crud.entity.CrudMultiTenantEntity;
import org.hibernate.envers.Audited;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.Size;

@JArchLookup(codeAttribute = "codigo", descriptionAttribute = "nome")
@Audited
@Table(name = "tb_banco",
indexes = {
@Index(columnList = "cd_banco", name = "dx_bancocdban"),
@Index(columnList = "nm_banco", name = "dx_banconmban")
})
@Entity(name = "banco")
@SequenceGenerator(name = "BancoIdSequence", sequenceName = "sq_idbanco", allocationSize = 1)
public class BancoEntity extends CrudMultiTenantEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "BancoIdSequence")
@Column(name = "id_banco")
private Long id;
c
@Column(name = "cd_banco", nullable = false, length = 20)
@JArchValidRequired("label.codigo")
@Size(max = 20, message = "{message.maxSizeExceeded}")
@JArchValidExclusive
private String codigo;

@Column(name = "nm_banco", nullable = false, length = 50)
@JArchValidRequired("label.nome")
@Size(max = 50, message = "{message.maxSizeExceeded}")
@JArchValidExclusive
private String nome;

public BancoEntity() {
}

public BancoEntity(Long id, String codigo, String nome) {
this.id = id;
this.codigo = codigo;
this.nome = nome;
}
@Override
public Long getId() {
return id;
}

@Override
public void setId(Long id) {
this.id = id;
}

public String getCodigo() {
return codigo;
}

public void setCodigo(String codigo) {
this.codigo = codigo;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}

}
BancoFacade.java: Essa classe é a fachada dessa lógica:
package br.com.jarch.conta.client.banco;

import br.com.jarch.crud.facade.CrudFacade;

public class BancoFacade extends CrudFacade<BancoEntity, IBancoManager> {

}
BancoJpqlBuilder.java: Essa classe é implementação da biblioteca JPQL do JARCH: 
package br.com.jarch.conta.client.banco;

import br.com.jarch.jpa.api.ClientJpaql;
import br.com.jarch.jpa.api.ClientJpaqlBuilder;

public class BancoJpqlBuilder {

private BancoJpqlBuilder() {
}

public static ClientJpaql<BancoEntity> newInstance() {
return ClientJpaqlBuilder.newInstance(BancoEntity.class);
}
}
BancoManager.java: Essa classe faz o papel do Repository (Camada de acesso ao repositório de dados): 
package br.com.jarch.conta.client.banco;

import br.com.jarch.crud.manager.CrudManager;

public class BancoManager extends CrudManager<BancoEntity> implements IBancoManager {

}
BancoObserver.java: Essa classe é para interceptar os eventos CDI disparados pelo JARCH:
package br.com.jarch.conta.client.banco;

import br.com.jarch.annotation.JArchEventLoadCrud;
import br.com.jarch.crud.util.InitializeUtils;

import javax.enterprise.event.Observes;

public class BancoObserver {

private void loadCrud(@Observes @JArchEventLoadCrud BancoEntity entity) {
InitializeUtils.initializeCollectionLazy(entity);
}
}
BancoSearch.java: Essa classe é de uso interno do JARCH, utilizado nas pesquisas:
package br.com.jarch.conta.client.banco;

import br.com.jarch.crud.search.Search;
import javax.enterprise.context.Dependent;

@Dependent
public class BancoSearch extends Search<BancoEntity> {
}
 package-info.java: Esse arquivo é o mapeamento da configuração dos campos de pesquisas bem como os campos de retorno no datatable:
@JArchSearchTab(order = 1,
searchFields = {
@JArchSearchField(clazzEntity = BancoEntity.class, attribute = "codigo", label = "label.codigo",
type = FieldType.CODE, condition = ConditionSearchType.LARGER_EQUAL, row = 1, column = 1, span = 3),
@JArchSearchField(clazzEntity = BancoEntity.class, attribute = "nome", label = "label.nome",
type = FieldType.NAME, condition = ConditionSearchType.LARGER_EQUAL, row = 1, column = 2, span = 9),
})
@JArchColumnDataTable(clazzEntity = BancoEntity.class, attribute ="codigo", title = "label.codigo", width = 100,
type = FieldType.CODE)
@JArchColumnDataTable(clazzEntity = BancoEntity.class, attribute ="nome", title = "label.nome", width = 500,
type = FieldType.NAME)

package br.com.jarch.conta.client.banco;
import br.com.jarch.annotation.JArchColumnDataTable;
import br.com.jarch.util.type.FieldType;
import br.com.jarch.annotation.JArchSearchField;
import br.com.jarch.util.type.ConditionSearchType;
import br.com.jarch.annotation.JArchSearchTab;

Módulo CONTAS-WEB 

BancoDataAction.java: Essa classe é a action da tela de dados (bancoData.xhtml):
package br.com.jarch.conta.web.banco;

import br.com.jarch.annotation.JArchViewScoped;
import br.com.jarch.crud.action.CrudDataAction;
import br.com.jarch.conta.client.banco.BancoEntity;
import br.com.jarch.conta.client.banco.BancoFacade;

@JArchViewScoped
public class BancoDataAction extends CrudDataAction<BancoEntity, BancoFacade> {

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

}
BancoDataDetail.java: Essa classe é utilizada pelo JARCH para gerenciamento de estado:
package br.com.jarch.conta.web.banco;

import br.com.jarch.conta.client.banco.BancoEntity;
import br.com.jarch.crud.action.DataDetail;
import javax.enterprise.context.Dependent;

@Dependent
public class BancoDataDetail extends DataDetail<BancoEntity> {
}
BancoFilterSelectAction.java: Essa classe é a action do popup de pesquisa (lookup, dialogSelect, etc) composites componentes de seleção do JARCH:
package br.com.jarch.conta.web.banco;

import br.com.jarch.conta.client.banco.BancoEntity;
import br.com.jarch.annotation.JArchViewScoped;
import br.com.jarch.crud.action.BaseFilterSelectAction;
import javax.annotation.Generated;

@JArchViewScoped
@Generated(value = "br.com.jarch.apt.generate.implicit.EntityProcessor", date = "18/06/2020 18:13:07")
public class BancoFilterSelectAction extends BaseFilterSelectAction<BancoEntity> {
}
BancoListAction.java: Essa classe é a action da tela de lista (bancoList.xhtml):
package br.com.jarch.conta.web.banco;

import br.com.jarch.annotation.JArchViewScoped;
import br.com.jarch.crud.action.CrudListAction;
import br.com.jarch.conta.client.banco.BancoEntity;
import br.com.jarch.conta.client.banco.BancoFacade;

@JArchViewScoped
public class BancoListAction extends CrudListAction<BancoEntity, BancoFacade> {

@Override
public String getPageData() {
return "bancoData.jsf";
}
}
bancoData.xhtml: Essa a paágina de dados:
<!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:a="http://jarch.com.br/ui"
xmlns:e="http://jarch.com.br/ui/extension">
<ui:composition template="/paginas/templates/templateDadosV2.xhtml">
<ui:define name="panelBodyTemplateDados">
<e:form>
<e:divTitle
title="#{e:bundle('label.banco')} - #{bancoDataAction.labelAction}"
description="#{e:bundle('label.manter')} #{e:bundle('label.banco')}"/>
<e:divDataMaster
title="#{e:bundle('label.banco')}"
actionData="#{bancoDataAction}">
<a:panelGrid columns="1" style="width: 100%">
<h:panelGroup>
<a:outputLabel value="#{e:bundle('label.codigo')}" for="idBancoCodigo"/>
<br/>
<a:inputText id="idBancoCodigo" styleClass="input-code"
disabled="#{bancoDataAction.blockedMaster}" label="#{e:bundle('label.codigo')}"
value="#{bancoDataAction.entity.codigo}" required="true"/>
</h:panelGroup>
</a:panelGrid>
<a:panelGrid columns="1" style="width: 100%">
<h:panelGroup>
<a:outputLabel value="#{e:bundle('label.nome')}" for="idBancoNome"/>
<br/>
<a:inputText id="idBancoNome" styleClass="input-description-large"
disabled="#{bancoDataAction.blockedMaster}" label="#{e:bundle('label.nome')}"
value="#{bancoDataAction.entity.nome}" required="true"/>
</h:panelGroup>
</a:panelGrid>

</e:divDataMaster>
</e:form>
</ui:define>
</ui:composition>
</html
bancoList.xhtml: Essa é paginda da lista:
<!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:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:e="http://jarch.com.br/ui/extension">
<ui:composition template="/paginas/templates/templateListaV2.xhtml">
<ui:define name="panelBodyTemplateLista">
<e:form>
<e:divTitle
title="#{e:bundle('label.lista')} - #{e:bundle('label.banco')}"
description="#{e:bundle('label.manter')} #{e:bundle('label.banco')}"/>
<e:divListFilter
title="#{e:bundle('label.filtro')} - #{e:bundle('label.banco')}"
actionList="#{bancoListAction}"
update="@(.list-datatable)"/>
<e:divListDatatable id="listEntityDataTableBanco"
title="#{e:bundle('label.lista')} - #{e:bundle('label.banco')}"
actionList="#{bancoListAction}" />
</e:form>
</ui:define>
</ui:composition>
</html>
BancoPage.java: Essa classe é o mapeamento do selenium para teste de sistema:
package br.com.jarch.conta.web.banco;

import br.com.jarch.test.page.CrudPage;
import br.com.jarch.util.type.FieldType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

public class BancoPage extends CrudPage {

public BancoPage(WebDriver driver) {
super(driver);
}

@FindBy(how = How.XPATH, xpath = "//input[contains(@id, 'filterSearchcodigo')]")
private WebElement webElementBancoCodigoFilterSearch;

@FindBy(how = How.XPATH, xpath = "//input[contains(@id, 'filterSearchnome')]")
private WebElement webElementBancoNomeFilterSearch;

@FindBy(how = How.XPATH, xpath = "//input[contains(@id, 'idBancoCodigo')]")
private WebElement webElementBancoCodigo;

@FindBy(how = How.XPATH, xpath = "//input[contains(@id, 'idBancoNome')]")
private WebElement webElementBancoNome;

@Override
protected String getFolderXhtml() {
return "banco";
}

@Override
protected String getPageList() {
return "bancoList.jsf";
}

@Override
protected String getPageData() {
return "bancoData.jsf";
}

@Override
public void actionInsert() {
goPageListFromUrl();
clickListActionInsert();
clickClearSendKeys(webElementBancoCodigo, FieldType.CODE.getValueInsertTest());
clickClearSendKeys(webElementBancoNome, FieldType.NAME.getValueInsertTest());
scrollBottomPage();
clickMasterSave();
waitForMessageSuccess();
}

@Override
public void actionClone() {
goPageListAndFilter(webElementBancoCodigoFilterSearch, FieldType.CODE.getValueInsertTest());
clickListActionClone();
clickClearSendKeys(webElementBancoCodigo, FieldType.CODE.getValueCloneTest());
clickClearSendKeys(webElementBancoNome, FieldType.NAME.getValueCloneTest());
scrollBottomPage();
clickMasterSave();
waitForMessageSuccess();
}

@Override
public void actionChange() {
goPageListAndFilter(webElementBancoCodigoFilterSearch, FieldType.CODE.getValueCloneTest());
clickListActionChange();
clickClearSendKeys(webElementBancoCodigo, FieldType.CODE.getValueChangeTest());
clickClearSendKeys(webElementBancoNome, FieldType.NAME.getValueChangeTest());
scrollBottomPage();
clickMasterSave();
waitForMessageSuccess();
}

@Override
public void actionConsult() {
goPageListAndFilter(webElementBancoCodigoFilterSearch, FieldType.CODE.getValueChangeTest());
clickListActionConsult();
waitForPageData();
scrollBottomPage();
clickMasterReturn();
}

@Override
public void actionDelete() {
goDeleteDataInsertAndDataChange(webElementBancoCodigoFilterSearch, FieldType.CODE, true);
}
}
BancoSystemTest: Essa classe do selenium para teste de sistema:
package br.com.jarch.conta.web.banco;

import br.com.jarch.test.BrowseFactory;
import br.com.jarch.test.TestUtils;
import br.com.jarch.test.system.CrudSystemTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;

public class BancoSystemTest extends CrudSystemTest<BancoPage> {

private static WebDriver driver;

private static BancoPage bancoPage;

static {
driver = BrowseFactory.getChromeDriver();
bancoPage = PageFactory.initElements(driver, BancoPage.class);
}

@Override
public BancoPage getPage() {
return bancoPage;
}

@BeforeClass
public static void beforeClass() {
TestUtils.login(driver, "http://localhost:8080/conta/paginas/login/login.jsf?multiTenantId=1&systemTest=S", "login", "senha");
}

@AfterClass
public static void afterClass() {
TestUtils.logout(driver);
}
}

Menu
Para configurar o cadastro de banco no menu basta somente alterar o MenuAction.java:
menu.add(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.cadastro")) // Exemplo
.addSubMenu(MenuBuilder
.newInstance()
.name(BundleUtils.messageBundle("label.banco")) // Exemplo
.action("../banco/bancoList.jsf") // Exemplo
.build())
.build());
Agora basta somente recompilar e executar a aplicação:


E pra finalizar vamos substituir as imagens que estão em contas-web/src/main/webapp/resources/images e adicionar uma nova imagem para o plano de fundo do login:

 
E agora teremos uma tela de login mais apresentável:
Conclusão
Assim finalizamos esse segundo post sobre a criação de um projeto de contas a pagar e receber. A estrutura apresentada nesse post contém diversas implementações prontas otimizando nosso tempo. No próximo post vamos criar os demais CRUD's de cadastro. 

Segue o link da video aula desse post: https://youtu.be/Ttdeaa3M_to

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