Dynamics AXBR

Blog destinado a usuários do Dynamics AX no Brasil.
Options:

Abrir formulários já filtrados com X++

Frequentemente vejo as pessoas perguntando: Tenho o formulário A com um registro selecionado, quero que quando eu clicar no botão X, abra o formulário B, já com o registro filtrado (pertinente ao registro do formulário A). Talvez a pergunta tenha ficado confusa, mas vamos lá, talvez com o código fique mais claro, ou não.

Basicamente a operação consiste em sempre usar um MenuItem e preencher os argumentos do mesmo, seja via Properties ou então via linha de código.

Quando usamos via properties (não exisge código), basta que os relacionamentos estejam corretamente setados e que você informe na propriedade DataSource do botão (menuItem) o datasource que irá enviar junto com o clique, o resto, o próprio AX já irá fazer.

O pessoal do http://www.fourone.se/ escreveu um artigo e publicou um job que:

Primeiro ira exibir o formulário CustTable filtrado pelo registro de cliente código 4004 e quando este formulário for fechado, o formulário CustTrans será exibido mostrando dados sobre o cliente que vimos no form CustTable. Também é exibido como você também pode parar o código esperando que o formulário seja fechado ou então simplesmente dar um detach e continuar independente do que aconteça no formulário.

Abaixo o código com alguns comentários:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static void FO_OpenFilteredForms(Args _args)
{
  Args		argsCust,	argsTrans;
  FormRun 	formRun, 	formRun2;
  //Aqui é feita a seleção do cliente código 4004 e atribuído a variável custTable
  CustTable 	custTable = CustTable::find("4004");
  ;
 
  //Aqui é passado o nome do formulário que será aberto, no caso, CustTable
  argsCust = new Args(formStr(CustTable));
  //Aqui é passado o registro previamente selecionado, no caso, o cliente código 4004
  argsCust.record(custTable);
 
  formRun = classFactory.formRunClass(argsCust);
  formRun.init();
  formRun.run();
  //Aqui é o comando onde a instrução de esperar pelo formulário é dada
  formRun.wait();
 
  print "Isto será impresso quando " + "o formulário CustTable for fechado.";
  pause;
 
  //Aqui é passado o nome do formulário que será aberto, no caso, CustTrans
  argsTrans = new Args(formStr(CustTrans));
  //Aqui é passado o registro previamente selecionado, no caso, o cliente código 4004
  //Perceba que o registro é o mesmo, nada mudou, o que mudou foi o formulário
  argsTrans.record(custTable);
 
  formRun2 = classFactory.formRunClass(argsTrans);
  formRun2.init();
  formRun2.run();
  //Aqui é o comando onde a instrução de não esperar (detach) pelo formulário é dada
  formRun2.detach();
 
  print "Isto será impresso quando " + "o formulário CustTrans for aberto.";
  pause;
}

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Caros,
o Matiazo publicou um artigo sobre WorkFlow que eu acho que vale bastante a pena dar uma conferida.

Para acessar: http://axaptabrasil.blogspot.com/

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Capturando o teclado no AX.

Algumas pessoas já me perguntaram como pegar as teclas que pressionamos no AX e assim tomar determinada decisão. O AX nos permite capturar algumas combinações especiais (CTRL + Tecla), ou teclas especiais (pg down, pg up, alt e etc…).

Uma das formas de pegar tecla ou combinação de teclas pressionadas é usando o método Task da classe SysSetupFormRun, como vamos ver abaixo:

(retirado do site http://www.fourone.se/blog/).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public int task(int _p1)
{
    #task
    FormDataSource formDataSource;
    int ret;
 
    // START 070921 FourOne/JoJ (FO_EnvironmentAddOns4)
    // -- Description: quick-command for checking 
    //                      field and/or form data.
    SysDictField df;    
    FormControl fc;
    formStringControl fsc;
    DictEnum dictEnum = new DictEnum(enumnum(Types));
    DictEnum dictEnums;
    ;
    if (_p1 == 769) //Ctrl + Z
    {
        fc = this.selectedControl();
        formDataSource =  this.objectSet();
 
        if(fc && formDataSource)
        {
            fsc    = fc;
            if(fsc.dataField() && formDataSource.table())
            {
                info(strfmt('Tbl. Fld  -> %2. %1', fieldId2Name(formDataSource.table(), fsc.dataField() - 65536), tableId2Name(formDataSource.table())));
                df=new SysDictField(formDataSource.table(), fsc.dataField() - 65536);
                if(df)
                {
                    info(strfmt('Type      -> %1', dictEnum.index2Symbol(df.baseType())));
                    if(df.baseType() == typeOf(Types::Enum))
                    {
                        dictEnums = new dictEnum(df.enumId());
                        info(strfmt('Enum     -> %1', dictEnums.name()));
                    }
                    info(strfmt('Ext type -> %1', extendedTypeId2name(df.typeId())));
                    info(strfmt('Size       -> %1', int2str(df.stringLen())));
                    info(strfmt('max.rght -> %1', (df.rights())));
                    info(strfmt('Label      -> %1: %2',(df.labelLabel()),(df.label()) ));
                    info(strfmt('Help       -> %1: %2', (df.helpLabelId()),(df.help()) ));
                }
            }
            if(fsc.dataMethod())
            {
                info(strfmt('METHOD %1.%2',  tableId2Name(formDataSource.table()), fsc.dataMethod()));
            }
        }
    }
 
    ret = super(_p1);
    return ret;
}

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Inside Microsoft Dynamics AX 2009

A MicroSoft liberou na quinta-feira passada (18/06/09) o primeiro lote, o meu já chegou.

Caso queiram comprar, o link para o livro no site da amazon é este aqui.

Por enquanto o preço é promocional (USD$44,09).

Obs.: Achei no site do nosso amigo Arijit Basu que a MSLearning também liberou alguns exemplos dos códigos usados no livro para o publico fazer o download, quem quiser conferir basta clicar aqui.

Abraços,
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Esta ferramenta ajuda os administradores a otimizarem a base de dados do ax monitorando inteligentemente o uso da indexação, layout da indexação, fragmentação e o modelo das queries através dos índices. O framework permite reduzir o tamanho da base de dados limpando registros de transações de uma série de entidades relacionadas, mantendo a consistência e integridade dos dados produzidos. A IDMF (Intelligent Data Management Framework) fornece a clientes e parceiros a habilidade para descobrir e identificar entidades relacionadas baseadas no metadata DAX para determinar o critério de limpeza para as entidades e transações. O IDMF também analisa a base de dados de produção para determinar o uso atual dos padrões e avaliar a saúde da aplicação DAX. O framework é compatível com todas as versões do DAX (Axapta 3.0 SP6 com o último kernel, Dynamics AX 4.0 SP2, Dynamics AX 2009 SP1). Cliente com plano de manutenção ativo podem implementar este framework para construir um gerenciamento eficiente dos dados em sua implementação.

Para fazer o download do framework: https://mbs.microsoft.com/partnersource/downloads/releases/ax_idmf.htm (requer acesso ao partnersource)

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


10 dicas de X++ em 10 minutos (ou mais) :D

Caros,
O Pessoal do channel9.msdn.com publicou um video sobre dicas, que na minha opinião vale a pena dar uma conferida.

http://channel9.msdn.com/posts/mfp/10-MorphX-tricks-in-10-minutes/

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Saber se o dado é numérico.

Esse post é antigo, tava aqui fazia tempo, mas não sei porque não publiquei.

Enfim, a solução é saber se a variável que recebemos é ou não um número, evitando assim um cast que geraria um erro.

1
2
3
4
5
6
7
8
9
10
11
12
static boolean IsNumeric(str _text)
{
    boolean     ret;
    System.Text.RegularExpressions.Regex regEx;
    System.Text.RegularExpressions.Match regMatch;
    ;
 
    regEx = new System.Text.RegularExpressions.Regex(@"[^0-9.]");
    regMatch = regEx.Match(_Text);
    ret = !regMatch.get_Success();
    return ret;
}

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


10 dicas para debugar no Dynamics Ax

Corrigir bugs requer muita experiência e conhecimento dos módulos envolvidos, tanto técnico como funcional. O primeiro passo para consertar alguma coisa é procurar a causa do problema, também conhecido como debugging (processo metódico de localizar e corrigir erros em um código de programa de computador).

Você não deve se limitar a usar o debugger somente quando as coisas dão errado, você também pode usar para entender o sistema, eu freqüentemente uso para entender como é o funcionamento standard do sistema e então aplicar as novas modificações necessárias para atingir os resultados solicitados por meus clientes. O Debugger ajuda a saber como implementar as modificações e quais as conseqüências. Que fique bem claro, o Dynamics AX é muito grande e muito complexo para você apenas fazer modificações pequenas sem saber ao certo o que está fazendo.

Abaixo algumas dicas para ajudar você na fina arte de debugar. Algumas talvez sejam grosseiramente obvias para desenvolvedores experientes, mas são dicas que eu gostaria de ter quando comecei com o AX.

Então vamos lá:

Assuma, você quem estragou!
Este provavelmente é o conselho mais importante. Nós desenvolvedores temos tendência a pensar que escrevemos bons códigos. Alguns de nós realmente escrevemos, mas ninguém faz isso sem cometer erros ou deixar espaços para discussões do tipo “poderia ser melhor“. Por padrão, assuma que você não escreve códigos perfeitos, isto irá limitar a procura por erros consideravelmente. Após debugar você provavelmente terá uma conclusão diferente.

Se o sistema estava rodando e de repente parou após você importar um código novo, o novo código provavelmente é a causa do problema. Procure remover exatamente as modificações que você fez, caso o problema persista, você encontrou um problema não relatado anteriormente, caso contrário, você sabe onde começar a procurar por erros.

Seja o Sr. Chato! Tenha uma descrição muito clara do problema.
A menos que o erro esteja claro o suficiente e você saiba imediatamente como resolver o problema, você precisará de uma descrição detalhada de como reproduzir este erro. Infelizmente isso pode ser bem difícil. Pedir aos usuários para explicar a você exatamente o que você precisa para entender sobre o problema não é uma tarefa fácil. Tenha em mente que os usuários geralmente não estão interessados nos programas que eles usam, eles simplesmente querem usar, querem que funcione e que o trabalho deles termine logo. Eles foram ensinados a usar o sistema de uma certa forma e erros inesperados podem confundi-los. Eles talvez não entendam qual é a diferença quando as coisas dão errado comparadas com quando tudo está ok.

Você precisa fazer as perguntas corretas, se necessário, sentar perto do usuário e vê-lo trabalhar. Faça anotações e tente perceber os casos especiais, que fogem do normal. Não se esqueça também de perguntar qual a comportamento normal que o sistema deveria ter. Talvez não exista uma mensagem de erro e aconteça o que acontecer talvez pareça tudo OK, mas o usuário espera um resultado diferente.

Sem um bom cenário, talvez seja impossível resolver alguns bugs.

Não se preocupe muito com erros que aconteceram apenas uma vez.
Se alguma coisa deu errado apenas uma vez e não acontece novamente, não se preocupe muito. Há provavelmente em algum lugar bugs ocultos, mas você tem que decidir se vale a pena correr atrás.

Intercepte a mensagem de erro
Tudo que foi para a janela de log passou através do método add() dentro da classe Info. Coloque um breakpoint neste método se você deseja saber onde o erro foi gerado. Usando o Stack Trace no debugger torna a procura por onde o erro foi gerado bem mais fácil.

Intercepte a modificação dos dados.
Nem todos os problemas vem com uma maneira fácil de interceptar as mensagens de erro. Algumas vezes tudo que você tem são dados errados. É possível ver quando e porque os registros são criados, modificados ou removidos colocando um breakpoint nos métodos insert(), update() ou delete() na tabela. Sobrescreva os métodos se for necessário. Basta ser capaz de olhar para o stack trace no debugger quando estes métodos são chamados.

Lembre-se que é possível alterar dados sem passar através desses método, como usando o doIsert(), doUpdate() ou doDelete(), ou ainda usando diretamente SQL. Não é muito comum, mas algumas vezes você pode esquecer-se de algo.

Intercepte as queries.
Se você suspeita que uma query não está correta, você precisará verificar seu output. Um caminho que não requer muito trabalho é usando o método postLoad(). Ele pode ser sobrescrito em cada tabela e é chamado para cada registro selecionado. Também funciona com Joins complexos. Colocando um info no método postLoad() de cada tabela da query irá te dizer muita coisa sobre “o que está acontecendo“.

A cross-reference é sua amiga.
A cross-reference é uma das mais importantes ferramentas quando você está desenvolvendo ou debugando no Dynamics AX. Sempre tente ter um ambiente com a cross-reference atualizada (não no ambiente de produção)
Precisa saber onde o campo pega seu valor? A cross-reference te fala onde cada leitura e escrita acontece.
Quer saber onde uma mensagem de erro é usada? Abra o editor de etiquetas, encontre o Label e então clique no botão “Usado por“.

Configure um ambiente separado
Quando tratamos com problemas complexos isto ajuda se você tem um ambiente separado para debugar. Este procedimento permite a você modificações livres no código e nos dados sem afetar o ambiente de produção. É muito importante quando você tem que postar notas fiscais, diários e etc que não são reversíveis ou que tem a reversão complicadas.

Isto também permite que usuários do ambiente de produção sejam bloqueados por seus breakpoints no meio de suas transações.

Lidando com muitos dados
Algumas vezes um problema só é possível de ser reproduzido em uma cópia do ambiente de produção. Você freqüentemente estará preso com uma quantidade enorme de dados que não ajuda, mas este é o caminho. Como quando você precisa debugar o MRP. Usando breakpoints não irá ajudar muito porque existe muita coisa antes do problema real.

Neste caso você precisa ter mais truques na manga para reduzir a pesquisa. Uma opção é trabalhar em vários passos. Usando a cross-reference determinamos lugares onde coisas interessantes acontecem e se você visualizar os dados usando info() ou no Debug::printDebug(). Isto deve estreitar e muito as possibilidades suspeitas. Com um pouco de sorte, procurar apenas nos dados pode ser o suficiente para identificar o problema.

Outro caminho é implementar seus breakpoints condicionais. O debugger não oferece estes recursos, mas você pode fazer você mesmo com uma instrução if e então uma instrução breakpoint. Este processo é muito efetivo se você tem mais ou menos identificadores únicos para o problema, como um record ID ou uma conta de cliente ou até mesmo uma data.

Limpeza
Não se esqueça de remover qualquer modificação que você fez enquanto estava debugando. Você provavelmente não deseja que um breakpoint inserido no código apareça no ambiente de produção. Lembre-se que isso é muito chato.

Mensagens de debug profissionais
Temos a tendência de quando não estamos conseguindo resolver o problema de colocar mensagens chulas no meio do código que com certeza iremos esquecer e elas acabarão sendo exibidas quando o cliente estiver testando.

Boa sorte caçando bugs.

Sinta-se livre para compartilhar conosco suas técnicas de debugging.

Artigo traduzido e alterado baseando-se no artigo original:
http://sysdictcoder.com/blog/10-tips-for-debugging-in-dynamics-ax/



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Refresh() -> Não faz a releitura do registro na base de dados, basicamente apenas atualiza a tela com os dados que estavam guardados no cache.

Exemplo: (fonte Microsoft)
O seguinte exemplo sobrescreve o método write do datasource e então atualiza os registros em um datasource diferente após gravar os dados.

1
2
3
4
5
6
public void write()
{
    super();
    EPParameters_ds.research();
    EPParameters_ds.refresh();
}

RefreshEx([anytype pos]) -> Atualiza a visualização de registros especificos. O paramêtro pos é opcional e representa o registro a ser atualizado. Se o valor especificado for -1, todos os registros serão atualizados, se o valor especificado for -2, todos os registros marcados serão atualizados. O default é -2.

Exemplo: (fonte msdn)
O seguinte exemplo atualiza todos os registros exibidos na grid porque usa o -1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void setColor(OLE_Color _color)
{
    GanttColorTable searchGanttColorTable;
    ;
    ganttColorTable.Color = _color;
    ganttColorTable_ds.write();
    for (searchGanttColorTable = ganttColorTable_ds.getFirst();
    {
        searchGanttColorTable;
        searchGanttColorTable = ganttColorTable_ds.getNext())
        if (searchGanttColorTable.RecId == ganttColorTable.RecId)
        {
             ganttColorTable_ds.
                 clearDisplayOption(searchGanttColorTable);
        }
    }
    ganttColorTable_ds.refreshEx(-1);
    ganttColorTable_ds.active();
}

Reread() -> Irá fazer a releitura do registro ATUAL com as últimas alterações da database. Não devemos usar esse comando quando adicionarmos ou removermos registros na tabela, ele só serve para atualizar quando fazemos alterações no registro através de um .update() via código ou algum outro processo fez algo semelhante.

Exemplo:
O seguinte exemplo usa o método Reread() como parte do método que é usado para para atualizar a tabela.

1
2
3
4
5
6
7
8
9
10
void doRefresh()
{
    salesTable_ds.reread();
    salesTable_ds.refresh();
 
    salesLine_ds.reread();
    salesLine_ds.refresh();
 
    interCompanyPurchSalesReference_ds.executeQuery();
}

Research() -> Irá retornar a query atual do formulário, irá atualizar a grid com os registros novos / removidos / alterados. Serão mantidos todos os filtros e ordenação do formulário.

Exemplo:
O exemplo abaixo atualiza o datasource após as alterações.

1
2
3
4
5
6
7
8
9
10
public void delete()
{
    super();
    if (aifWebsites.RecId != 0)
        element.adjustGUI(true);
    else
        element.adjustGUI(false);
 
    this.research();
}

ExecuteQuery() -> Deve ser usado se você modificar a query via código e precisar atualizar o que está sendo exibido no formulário.

Exemplo:
O seguinte exemplo executa a query do datasource em resposta a ativação de uma tab page.

1
2
3
4
5
6
public void pageActivated()
{
    ;
    monday_ds.executeQuery();
    super();
}

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Criando e usando um método Edit no DataSource

Estava lendo um blog outro dia e vi novamente sobre usar um método Edit em um datasource para marcar ou não um registro em uma grid, sem ter a necessidade de alterar a tabela e criar um campo só para esta seleção.

No entanto, isso é um pouco chato na primeira vez que se vai fazer, por questões de escopo (acredito eu), no meu caso se complicou mais e tive que usar o modelo abaixo, já que queria preencher um MAP com os recids que estavam marcados.

A solução:

Form: É um formulário simples, onde eu listo os itens da InventTable.

No método classdeclaration:

1
2
3
4
public class FormRun extends ObjectRun
{
    Map     myMap;
}

No DataSource InventTable eu criei o seguinte método:

1
2
3
4
5
6
7
8
edit NoYes markInvent(
    boolean         _set,
    InventTable     _invent,
    NoYes           _mark
    )
{
    return element.inventMark(_set, _invent, _mark);
}

De volta ao form, eu criei os outros dois métodos que me faltavam:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public void initMyMap()
{
    myMap = new Map(Types::Int64, Types::Int64);
}
 
edit NoYes inventMark(
    boolean         _set,
    InventTable     _invent,
    NoYes           _mark
    )
{
    NoYes   click;
    ;
 
    if(!myMap)
        element.initMyMap();
 
 
    if (_set)
    {
        if (_mark)
        {
            myMap.insert(_invent.RecId,_invent.RecId);
            click = NoYes::Yes;
        }
        else
        {
            if (myMap.exists(_invent.RecId))
                myMap.remove(_invent.RecId);
 
            click = NoYes::No;
        }
    }
    else
        click = myMap.exists(_invent.RecId);
 
    return click;
}

Explicação:

No class declaration eu declaro o meu map;
No DataSource eu crio o método e associo esse metodo a um checkbox que coloquei na minha grid;
Quando clico no checkbox, chamo o método markInvent e envio os parametros: set = true, _invent = registro atual, _mark = sim ou não;
O método markInvent por sua vez apenas chama o inventMark que irá fazer o processamento necessário e retornar um sim ou não;
Este último método apenas verifica qual o valor passado em mark, se sim, adiciona o valor ao map, se não remove o valor.

Obs.: O único motivo de eu chamar um método do formulário ao invés de executar tudo no primeiro edit é que se eu faço no datasource ele não atualizava meu MAP, eu acredito que por uma questão de escopo, mas não posso afirmar.

[]s
Pichler



1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...


Data do post

July 2009
S M T W T F S
« Jun    
 1234
567891011
12131415161718
19202122232425
262728293031  

Arquivo

Categorias

Latest Visitor Loactions

Acaxochitlán, Mexico 1
Belo Horizonte, Brazil 1