Desenvolvimento Adempiere


#1

PessoALL,

Estou com serias dificuldades de compreender as formas de desenvolvimento no Adempiere. A questão de callout, processos e PO (ModelValidator e outras se existir), nao estao devidamente claras para mim.
Alguem tem alguma documentação sobre isso, algum link que possa indicar etc.

Gostaria de entender esses conceitos para poder colaborar de forma mais efetiva.

Fiz um teste com o pacote adempierelbr, no entanto no campo de digitacao do CPF/CNPJ, o sistema aceita apenas um caracter.

Abracos,

Matheus


#2

Bem vindo a comunidade mnogueira…

Não existe nenhum documentação em português, acho que na wiki do adempiere, deve ter alguma coisa ([url:e7baa]http://www.adempiere.com/wiki[/url:e7baa])

Algumas DICAS:

Callout - só é chamada na GUI quando ocorre algum evento no campo. Passa alguns parâmetros importantes com a MTab e o Value
Processos - vc pode chamar uma procedure do Oracle/Postgres ou uma classe em java, observe alguns exemplos no package org.compiere.process, vc verá que é necessário fazer um extend da SrvProcess.
ModelValidator - pode ser executado no before e after Save, before Prepare e after Complete dos Documentos.

Estamos ainda em fase inicial do projeto adempierelbr, ainda não tive tempo de olhar como estão as coisas do projeto, sexta-feira(26/10/2007) fizemos uma reunião para definir o que cada desenvolvedor do projeto vai fazer neste etapa. Vou dar uma olhada qual Validator o Fernando colocou no adempierelbr para poder ajudar vc.

Qq dúvida, coloque aqui no fórum que estaremos respondendo na medida do possível


#3

Mario,

Obrigado pela rapida resposta.
No wiki eu ja havia procurado por documentacao e nao achei nada, na verdade eu procurei em varios lugares, mas sem sucesso.
Qual ambiente que vcs usam para desenvolvimento Windows/Linux, Eclipse/NetBeans, etc ?

Atenciosamente,

Matheus


#4

Estou usando Windows e Linux

Ainda estou desenvolvendo no Compiere 2.6.0c (Oracle) e comecei agora em um projeto com Adempiere 3.2.0 (Postgres)

Para desenvolimento estou usando o Eclipse Europa, pois é a plataforma de desenvolvimento dos Projetos, até dá para importar no NetBeans, mas nunca testei.

Para conexão com o Banco, estou usando o Aqua DataStudio (a versão 4 é free) para Postgres e para o Oracle (SQL Developer)

sds, Mario


#5

[quote=“mgrigioni”:d68b2]
Algumas DICAS:

Processos - vc pode chamar uma procedure do Oracle/Postgres ou uma classe em java, observe alguns exemplos no package org.compiere.process, vc verá que é necessário fazer um extend da SrvProcess.
ModelValidator - pode ser executado no before e after Save, before Prepare e after Complete dos Documentos.
[/quote:d68b2]
Bom, realmente dificil (para mim) conseguir enxergar diferença entre processo e validacao, sendo que os dois trabalham com alterações no banco. O modelValidator como citado ele é executado durante o processo de salvar os registros. Os processos também podem ser, como por exemplo, gerar pedido referente ao c_projectphase, mas diferença que ele é “chamado”.

A diferença esta apenas no fato de um ser chamado so durante o processo de salvar os registros e o outro poder ter start com um evento de botao ? Tendo os nomes, processo ou validaçao, uma questao de organização, sem diferenças no papel desempenhado (na essencia da lógica envolvida) ?


#6

Existe uma grande diferença entre os dois, veja algumas:

Processo - Pode ser chamado através do menu ou através de um botão. É acionado pelo usuário.
modelValidator - É transparente para o usuário, este processo é acionado diretamente pelo sistema sempre antes ou depois de alguma ação, ex: antes de salvar um cadastro de parceiro de negócios verificar se o CNPJ esta correto.


#7

“Roubando” um pouco o tópico pra tirar uma dúvida… estou customizando a janela de ordem de venda pras necessidades de minha empresa. Na realidade, criei uma sub-aba abaixo da linha da ordem de venda, onde vão ficar várias informações referentes à minha customização. Já fiz algumas callouts de cálculo entre campos e está tudo funcionando perfeitamente.

Minha dúvida é que quero passar algumas informações dessa sub-aba customizada pro registro pai, que seria a linha de ordem de venda, e isso teria que ser passado no momento do salvamento do registro. Como eu faço isso, via esse modelValidator? E desculpe a ignorância, aonde que isso é controlado ou vejo um exemplo desse modelValidator?

Obrigado.


#8

isso mesmo, é no modelValidator que vc vai fazer esta customização. abaixo a lista de quando o validator pode ser chamado.

/** Model Change Type New		*/
	public static final int TYPE_BEFORE_NEW = 1;			// teo_sarca [ 1675490 ] 
	public static final int	TYPE_NEW = 1;
	public static final int	CHANGETYPE_NEW = 1;				// Compatibility with Compiere 260c
	public static final int TYPE_AFTER_NEW = 4;			// teo_sarca [ 1675490 ] 
	/** Model Change Type Change	*/
	public static final int	TYPE_BEFORE_CHANGE = 2;		// teo_sarca [ 1675490 ] 
	public static final int	TYPE_CHANGE = 2;
	public static final int	CHANGETYPE_CHANGE = 2;			// Compatibility with Compiere 260c
	public static final int	TYPE_AFTER_CHANGE = 5;			// teo_sarca [ 1675490 ] 
	/** Model Change Type Delete	*/
	public static final int	TYPE_BEFORE_DELETE = 3;		// teo_sarca [ 1675490 ] 
	public static final int	TYPE_DELETE = 3;
	public static final int	CHANGETYPE_DELETE = 3;			// Compatibility with Compiere 260c
	public static final int	TYPE_AFTER_DELETE = 6;			// teo_sarca [ 1675490 ]

Se vc baixou os fontes do AdempiereLBR, existem 4 classes deste tipo no package, org.adempierelbr.validator, de uma olhada no ValidatorBPartner, que faz a validação do CNPJ e CPF, que acho que deve ser o exemplo mais simples, e existe o validator da order/invoice que este tbm existem exemplos na utilização no momento em que o documento é processado.

sds, Mario


#9

Complemetando, você acessa o sistema como administrador da empresa: Adminsitracao do Sistema - Regras Gerais - Empresa
Nas configurações da empresa você adiciona a classe de validacao que você criou.
No campo Model Validator separados por ponto-vírgula.

[]'s

Fernando


#10

Entendi o processo, porém estou tendo uma certa dificuldade pra implementar a coisa… Pelo que entendi vendo os exemplos de validators, todos eles pegam dados de tabelas já existentes (c_order, c_orderline, c_bpartner, etc), usando models também já existentes, como org.compiere.model.MOrder ou org.compiere.model.MBPartner, por exemplo.

Só que eu tenho duas tabelas que eu criei (pai e filho), e preciso passar um dado de uma pra outra. E aí, da onde eu tiro o model pra essas tabelas?

Pra ilustrar: as duas tabelas são C_OrderLine_VCalculo (diretamente dependente de C_OrderLine) e C_OrderLine_VCalculo_Format (diretamente dependente da C_OrderLine_VCalculo). O que eu preciso é pegar um dado da tabela C_OrderLine_VCalculo_Format e jogar na C_OrderLine_VCalculo depois que algo for inserido/alterado/excluído na C_OrderLine_VCalculo_Format.

[code] public String modelChange (PO po, int type) throws Exception
{
if (po.get_TableName().equalsIgnoreCase(“C_OrderLine_VCalculo_Format”) && (type == TYPE_AFTER_CHANGE || type == TYPE_AFTER_NEW || type == TYPE_DELETE))
{
//o que eu ponho aqui?
//MOrderLine oLine = (MOrderLine)po;
//return modelChange(oLine,type);
}

	return null;
}	//	modelChange[/code]

O MOrderLine não tem nada a ver com os dados que eu tenho na tabela que estou monitorando. Será que eu vou ter que criar models específicos pra essas tabelas? Olhei por cima os arquivos dos models no src do adempiere e achei meio complicado pra entender… É isso mesmo ou eu estou complicando a coisa? Se for pra criar models, tem algum exemplo mais simplificado?

Obrigado.


#11

Neste caso vc vai precisar rodar o org.adempiere.util.GenerateModel, passando como parâmetro o tipo de entidade que vc criou as tabelas (se vc deixou padrão = User Mantained, ou seja, ‘U’), então será criado 2 novos asquivos, X_C_OrderLine_VCalculo_Format e X_C_OrderLine_VCalculo.

Ai vc vai substituir seu código assim:

//o que eu ponho aqui? X_C_OrderLine_VCalculo_Format vCalc = (X_C_OrderLine_VCalculo_Format)po; return modelChange(vCalc,type);

Essa nova classe tem todos os gets e sets que vc precisa.

Att.
Ricardo


#12

Valeu pela informação, muito útil mesmo. Consegui dar um jeito e gerar os models (apesar de nao aparecer no console no Eclipse…).

Enfim, agora tentei criar um validator bem simples, só que na hora de salvar o registro estou tendo o erro GenericPO.saveError: Error - org.adempiere.model.GenericPO [11], que é bem genérico mesmo, fica difícil descobrir qual o erro. Aqui o código que estou experimentando pra aprender:

[code] public String modelChange (PO po, int type) throws Exception
{
//Executa quando uma OrderLine_VCalculo_Formats é salva ou atualizada
if (po.get_TableName().equalsIgnoreCase(“C_OrderLine_VCalculo_Formats”) && (type == TYPE_AFTER_CHANGE || type == TYPE_AFTER_NEW || type == TYPE_DELETE))
{
X_C_OrderLine_VCalculo_Formats vCalcFormat = (X_C_OrderLine_VCalculo_Formats)po;
return modelChange(vCalcFormat,type);
}

	return null;
}	//	modelChange

// modelChange - VCalculo_Formats
// @param X_C_OrderLine_VCalculo_Formats
private String modelChange (X_C_OrderLine_VCalculo_Formats vCalcFormat, int type) throws Exception{

	if (type == TYPE_AFTER_CHANGE)
	{
		BigDecimal Usage = (BigDecimal)vCalcFormat.get_Value("Usage");
	
		Properties ctx    = vCalcFormat.getCtx();
		String     trx    = vCalcFormat.get_TrxName();

		X_C_OrderLine_VCalculo vCalc = new X_C_OrderLine_VCalculo(ctx,vCalcFormat.getC_OrderLine_VCalculo_ID(),trx);

		vCalc.setUsage(Usage);
		vCalc.save(trx);

	}

log.info(vCalcFormat.toString());
return null;
			
}[/code]

A idéia é pegar a coluna Usage recém salva da tabela C_OrderLine_VCalculo_Formats e jogar no campo também Usage da tabela C_OrderLine_VCalculo.

Tá tarde e vou dormir, vou pensar um pouco a respeito do problema, mas quem sabe alguém pode me dar uma luz pra meu aprendizado.

Obrigado


#13

Gustavo, só por curiosidade, vc precisa alterar este campo na primeira aba ou é apenas informativo?

Se for informativo, vc pode usar uma coluna SQL mesmo, dai nem precisa alterar o código.

sds, Mario


#14

Mario,

Nao, na aba pai (vcalculo) esse campo (consumo) é read only e usado como inicio pro resto dos campos de calculo. No entanto, eu posso ter varios registros na aba de formatos que, somados, devem ser jogados na aba pai. Eu só estou jogando um registro sem levar em consideração a somatória pq quero ver se está funcionando :slight_smile:

edit: descobri mais ou menos onde tá o problema, é no código que testa pra ver se é a tabela que eu tô procurando e o tipo de mudança. Esse aqui:

[code]public String modelChange (PO po, int type) throws Exception
{
//Executa quando uma OrderLine_VCalculo_Formats é salva ou atualizada
if (po.get_TableName().equalsIgnoreCase(“C_OrderLine_VCalculo_Formats”) && (type == TYPE_AFTER_CHANGE || type == TYPE_AFTER_NEW || type == TYPE_DELETE))
{
X_C_OrderLine_VCalculo_Formats vCalcFormat = (X_C_OrderLine_VCalculo_Formats)po;
return modelChange(vCalcFormat,type);
}

  return null;

} // modelChange[/code]
Pelo que eu pude ver, o parametro po que vem tem o nome da tabela correta e o teste continua, porém o parametro type que vem é o 2 (TYPE_BEFORE_CHANGE), que não bate no teste. Então o if falha e a mensagem de erro aparece por causa do return null (se eu trocar o null por uma string qualquer ela aparece no erro).

Vendo os exemplos como ValidatorBPartner e ValidatorOrder, não achei nenhum tratamento de todas as 6 condições de mudança, então o que pode estar errado?

edit 2: desculpem o excesso de edições, mas tô aprendendo e cada hora descubro uma coisa nova hah.

Enfim, o problema não é o que descrevi acima, ele continua sim pro próximo type que é o 5 (TYPE_AFTER_CHANGE), entra no if e dá problema na instrução X_C_OrderLine_VCalculo_Formats vCalcFormat = (X_C_OrderLine_VCalculo_Formats)po. O debugger do eclipse fala que acontece um ClassCastException (o qual não tenho o source) nessa linha.

Só editei pra ficar mais claro, e se eu descobrir mais sobre isso eu faço um edit 3 :slight_smile:


#15

A exceção ClassCastException é um erro de tempo de execução que ocorre quando tentamos fazer um cast (coerção - conversão explícita) de uma classe para outra classe diferente desta ou que não seja uma superclasse desta. http://www.arquivodecodigos.net/arquivo/visualizar_dica.php?qual_dica=2141

Logo verifique se a classe que vc chama ali é uma subclasse da PO

A interface e a X_X_SuaTabela o generateModel já gera pra vc, para aparecer no console do eclipse so dar um botao direito sobre a pasta e clicar em refresh.


#16

A classe ta normal:

[code]/** Generated Model for C_OrderLine_VCalculo

  • @author Adempiere (generated)
  • @version Release 3.4.0s - $Id$ */
    public class X_C_OrderLine_VCalculo extends PO implements I_C_OrderLine_VCalculo, I_Persistent [/code]
    Eu já tinha visto essa página que você mencionou, entendi a causa do erro, mas não consigo achar uma solução. Tô travado nisso desde hoje a tarde, fico procurando e até agora não consegui passar. :S

Entre um dos testes que fiz, coloquei nesse meu validator o codigo pra instanciar algo interno (como MOrderLine oLine = (MOrderLine)po em vez do que preciso X_C_OrderLine_VCalculo vCalc = (X_C_OrderLine_VCalculo)po) e dá o mesmo erro no mesmo local. Então por isso eu concluí que o problema não deve estar nas classes geradas, e sim em algum outro lugar.

Daí pensei que pode ser algo relativo à configuração no eclipse, já que essa minha customização é um 3º projeto que chama o adempiere e o adempierelbr, que são os outros 2 projetos configurados. Como sou extremamente novato em eclipse e java (1ª vez usando), tenho que pensar nessas hipoteses, vai que esqueci alguma coisa, heh. Até agora o que fiz rodou normal, que basicamente foram algumas callouts.

Coloquei o código do ValidatorVCalculo.java e do X_C_OrderLine_VCalculo.java no pastebin, quem sabe tem algo errado neles tbem.

Qualquer ajuda é bem-vinda.

Obrigado.


#17

Gustavo, vc colocou a tabela no initialize ?

[code] /**
* Initialize Validation
* @param engine validation engine
* @param client client
*/
public void initialize (ModelValidationEngine engine, MClient client)
{
m_AD_Client_ID = client.getAD_Client_ID();

	log.info(client.toString());
	
	//	ModelChange
	engine.addModelChange("X_C_OrderLine_VCalculo", this);[/code]

e outra coisa no modelChange vc tbm precisa fazer a verificação se o que vc está validando é realmente o que vc quer.

/** * Model Change of a monitored Table. * Called after PO.beforeSave/PO.beforeDelete * when you called addModelChange for the table * @param po persistent object * @param type TYPE_ * @return error message or null * @exception Exception if the recipient wishes the change to be not accept. */ public String modelChange (PO po, int type) throws Exception { if (po.get_TableName().equalsIgnoreCase("X_C_OrderLine_VCalculo") && (type == TYPE_CHANGE || type == TYPE_NEW)) { X_C_OrderLine_VCalculo vCalc = (X_C_OrderLine_VCalculo)po; log.info(vCalc.toString()); return null; }


#18

Mario,

Estou fazendo o initialize sim:

[code] /**
* Initialize Validation
* @param engine validation engine
* @param client client
*/
public void initialize (ModelValidationEngine engine, MClient client)
{
m_AD_Client_ID = client.getAD_Client_ID();

	log.info(client.toString());
	
	//	ModelChange
	engine.addModelChange("C_OrderLine_VCalculo", this);
	//	DocValidate

// engine.addDocValidate(“C_Order”, this);
} // initialize[/code]
E o modelChange:

[code] /**
* Model Change of a monitored Table.
* Called after PO.beforeSave/PO.beforeDelete
* when you called addModelChange for the table
* @param po persistent object
* @param type TYPE_
* @return error message or null
* @exception Exception if the recipient wishes the change to be not accept.
*/
public String modelChange (PO po, int type) throws Exception
{
//Executa quando uma OrderLine_VCalculo é salva ou atualizada
if (po.get_TableName().equalsIgnoreCase(“C_OrderLine_VCalculo”) && (type == TYPE_AFTER_CHANGE || type == TYPE_AFTER_NEW || type == TYPE_DELETE))
{
X_C_OrderLine_VCalculo vCalc = (X_C_OrderLine_VCalculo)po;
return modelChange(vCalc,type);
}

	return null;
}	//	modelChange[/code]

Aqui você pode ver o código completo: pastebin.com/mc764ba6

No seu exemplo de código, você diz pra usar X_C_OrderLine_VCalculo, mas esse é o nome do model, não da tabela, que é somente C_OrderLine_VCalculo. Se eu uso o seu código, o registro salva, mas pq nem chega a passar por esse validator. E se eu uso o nome da tabela como ela é, gera esse ClassCastException… :S

Obrigado!


#19

No seu teste com a MOrderLine, coloca assim:

	public void initialize(ModelValidationEngine engine, MClient client) {

		m_AD_Client_ID = client.getAD_Client_ID();

		log.info(client.toString());

		engine.addModelChange("C_ProjectLine", this);
	}

	public String modelChange(PO po, int type) throws Exception {
		if (po.get_TableName().equalsIgnoreCase("C_ProjectLine")
				&& (type == TYPE_CHANGE || type == TYPE_NEW)) {
			MProjectLine linha = (MProjectLine) po;
			return modelChange(linha,type);
		}
		log.info(po.toString());
		return null;

	}
public String modelChange(MProjectLine linha,int type) {
 //Coloca alguma mensagem pra ver se entrou aqui
}

Só uma dica: quando vc criar tabela, use a extensao X_, exemplo X_OrderLine_VCalculo


#20

Você comentou OrderLine e colocou ProjectLine no código… é isso mesmo? Fiz o teste usando OrderLine, assim:

[code] public void initialize (ModelValidationEngine engine, MClient client)
{
m_AD_Client_ID = client.getAD_Client_ID();

	log.info(client.toString());
	
	//	ModelChange
	engine.addModelChange("C_OrderLine", this);
}	//	initialize[/code]

[code] public String modelChange (PO po, int type) throws Exception
{
if (po.get_TableName().equalsIgnoreCase(“C_OrderLine”)
&& (type == TYPE_CHANGE || type == TYPE_NEW)) {
MOrderLine linha = (MOrderLine) po;
return modelChange(linha,type);
}
log.info(po.toString());

	return null;
}	//	modelChange[/code]

[code] public String modelChange(MOrderLine linha,int type) {
//Coloca alguma mensagem pra ver se entrou aqui

	return "teste";
}[/code]

Alterei uma linha de ordem e retornou “teste” como deveria.

Sobre a nomenclatura, vou alterar aqui pra usar prefixo X então, não sabia disso… vai que é isso o problema, hah.