29 Jul
Posted by: Ricardo Pichler in: X++
Caros,
Recentemente me deparei com o problema de não conseguir instalar um cliente AX em windows 7, após varias tentativas frustradas, eu acabei descobrindo que o problema era porque eu tinha o microsoft .net framework 4 client profile e quando removido, a instalação prosseguia sem problemas.
Caso estejam enfrentando este tipo de problema, basta removê-lo.
[]s
Pichler
O Matiazo publicou um post sobre o assunto em seu blog e recomendo darem uma olhada, faz bastante diferença quando o assunto é tratado corretamente.
Link: http://daxdev.com.br/blog/termo-item-de-invent-aacute-rio-e-uso-de-tipos-de-opera-ccedil-atilde-o/
[]s
Pichler
Caros,
Ontem dois amgiso (Alexssander Valdoski e Francisco Oliveira) me procuraram para pedir ajuda sobre um comportamento estranho que estavam notando ao entrar no ax. O comportamento estranho era devido ao fato de que apenas o primeiro registro era exibido nos formulários que eles acessavam, enquanto nas demais maquinas, todas as linhas apareciam.
Em um primeiro momento, eu pensei que era algum problema relacionado a segurança, alguma alteração no domínio ou alguma configuração de ‘Record Security’, mas logo depois eles perceberam que o problema só acontecia nos clients que haviam sido atualizados para o Rollup 5.
O fato é que somos instruídos que os clients de versão maior devem continuar atendendo às instalações anteriores, ou seja, se eu tenho um client com o Rollup 5, ele deveria atender todas as minhas instalações com os rollups anteriores chegando até a atender uma instalação core, sem updates, mas infelizmente por algum motivo isso não acontece.
Uma solução alternativa para tal problema, é salvar a pasta atual do client anotando sua versão no momento do upgrade e caso precise acessar uma base antiga, você simplesmente executa o Bin do diretório em questão.
[]s
Pichler
A classe InventOnHand é um invólucro para a tabela InventSum que tem como único índice é a combinação do ItemId + InventDimId. Em outras palavras, a InventOnHand é usada para recuperar seu estoque disponível do item com uma dimensão específica. Por exemplo, se você precisa saber o estoque disponível para o item Bola, na cor Azul, tamanho Normal e que está no depósito 22, corredor 1, prateleira 4 então você deve usar a class InventOnHand.
Mas, se você quer por exemplo levantar o estoque disponível por localização no depósito, então a classe InventOnHand não pode lhe ajudar. Isso porque um endereço pode conter diferentes itens, ou até mesmo se você precisar do estoque disponível por palettes. E é neste caso que a classe InventDimOnHand deve ser usada, ou seja, quando você precisa saber o estoque disponível para uma Dimensão de Estoque (InventDim) especifico. A classe InventDimOnHand é mebra das classes InventDimOnHandMember, onde cada InventDimOnHandMember contem informações sobre o Item, Dimensões e Quantidade.
Já havia usado, sabia a diferença, mas achei aqui e resolvi postar.
[]s
Pichler
Pessoal,
tenho visto não só aqui no Brasil, como também em sites internacionais uma série de pessoas reclamando do problema relacionado ao DateFlags após a atualização do AX 2009 para o SP1.
O que acontece é que ao atualizar a AOS você também deve atualizar os clients! Sim, os clients! O problema é porque o client não está atualizado e por algum motivo, enquanto ele não é atualizado ele não consegue enxergar o novo enum e por consequencia vários problemas começam a acontecer.
Tenho visto algumas pessoas criando o enum na mão, sem entender o que de fato acontece e se eu pudesse dar um conselho a esses desenvolvedores, eu diria: “Em ERP não se coloca a mão sem saber o que esta fazendo!”.
Abraço,
Pichler
Meu amigo Alexssander me pediu para escrever sobre macros e logo em seguida eu vi um post muito abrangente que trata justamente de macros e resolvi traduzi-lo, vamos lá.
O X++ prove macros de uma maneira fácil e expansiva. Com eles, você pode definir e usar valores para fazer compilações condicionais e etc… Neste artigo eu vou descrever a semantica da construção e prover modos para resolver alguns dos problemas que iniciantes e experts tem com os macros.
Macros não são estruturados tanto que eles não seguem a estrutura do X++. O tratamento dos macros acontece antes que o texto chegue ao compilador.
Macros podem ser usados em métodos, declarações de classes, jobs e em todos os outros lugares onde se há código X++.
A semântica de cada chave macro é descrita abaixo:
Construtores
#define
A sintaxe é: #define.MyName(SomeValue)
Isto define um macro chamado MyName com o valor SomeValue. Quando esta definição está em uso, qualquer referencia à #MyName será substituída pelo que há em SomeValue. A definição semântica não tem outro objetivo além de definir o símbolo MyName: O texto não atinge o próprio compilador. Quando a compilação do método atual acabar, o símbolo (MyName neste caso) não é mais lembrado. Se o símbolo já estava definido, o valor antigo é descartado e substituído pelo novo valor.
#globaldefine
A sintaxe é: #globaldefine.MyName(SomeValue)
Este tem a mesma semântica do define descrito acima.
#definc
A sintaxe é: #definc.MyName
Este construtor é usado só quando o valor é um inteiro. O pré processador incrementará o valor do símbolo por um. Se o valor não foi definido antes da instrução #definc é gerado um erro. Caso o valor não é um inteiro, o valor antigo será sobrescrito pelo valor 0 e então incrementado, tornado-se 1.
#defdec
A sintaxe é: #defdec.MyName
Este é o mesmo caso do anterior, no entanto o pré processador irá decrementar o valor do símbolo por um. Caso não exista, gerará um erro e caso exista e não seja um inteiro, será convertido para 0 e então decrementado por 1, o que retorna -1.
#undef
A sintaxe é: #undef.MyName
Como o nome já diz, ele remove a definição do símbolo e caso o símbolo não tenha sido previamente declarado, nada acontece.
#if … #endif
A sintaxe é:
#if.MySymbol
…
#endif
Ou
#if.MySymbol(SomeValue)
…
#endif
No primeiro caso o texto marcado com … nos exemplos acima é inserido dentro do fonte se o MySymbol foi préviamente definido, já no segundo caso, o conteúdo é inserido no fonte apenas se o valor definido for igual ao que está sendo validado.
O construtor #if pode ser usado em qualquer nível, mas não há um construtor #else.
#ifnot … #endif
A sintaxe é: #ifnot.MySymbol
…
#endif
Ou
#ifnot.MySymbol(SomeValue)
…
#endif
É exatamente igual ao anterior, no entanto, negativo. Equivale ao != usado em ifs.
#macrolib
A sintaxe é: #macrolib.MyName
Neste caso, o nome deve referenciar um macro existente na AOT. O texto do Macro é então processado pelo pré processador. O valor da macro é inserido no código fonte onde a diretiva aparece. Caso o Macro não exista na AOT um erro será gerado.
#macro / #localmacro
As chaves #macro e #localmacro são intercambiáveis, não há diferença na semântica das duas. O construtor é usado para definir um símbolo para detonar um conteúdo que possivelmente usará várias linhas.
A sintaxe é:
#localmacro.MySymbol
….
#endmacro
Exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class MyBaseClass extends Runbase { int v1; #define.myMacro(“Hello world”) #localmacro.currentlist v1 #endmacro public container pack() { return [#currentlist]; // #currentlist expande-se para v1 } public void run() { print #myMacro; // #myMacro expande-se para “Hello world” } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class MyDerivedClass extends myBaseClass { int v2; #localmacro.currentlist v2 #endmacro public container pack() { return [super(), #currentlist]; // #currentlist expande-se para v2 } public void run() { print #myMacro; // #myMacro expande-se para “Hello world” } } |
#MySymbol
A sintaxe é: #MySymbol
Insere o valor do símbolo no código e é um erro se referir a um símbolo que não foi definido. Se o nome denotar um Macro definido na AOT, o texto do nó será processado pelo pré processador (neste caso, #MySymbol é um atalho para #macrolib.MySymbol).
Problemas comuns
Nesta parte do post, eu gostaria de descrever alguns dos problemas que os desenvolvedores tem quando utilizam macros.
Macros e parâmetros
Isto parece ser um fato pouco conhecido e a origem de certa confusão: Os valores dos símbolos podem receber os placeholders %
#MyString(X++)
o resultado será:
“Hello World from X++”
Veja que no exemplo acima não usamos aspas para passar o valor X++ como argumento. Caso usado, irá gerar um erro de compilação!
A confusão também pode acontecer por causa da notação % também é usada como parâmetro de substituição na função strFmt. E como sabem, esta função tem uma lista de parâmetros com comprimento variável, e cada referencia de %n no primeiro parâmetro (a string) será expandida para conter a representação informada no argumento n, conforme podemos ver abaixo:
print strfmt(“The value is %1″, theValue);
Agora, alguns programadores tentarão fazer isto:
#define.TheText(“The value is %1″)
print strfmt(#TheText, theValue);
Mas a substituição em macros acontece antes do compilador passar pelo código. A substituição do macro não irá encontrar o parâmetro para ser colocado onde o %1 encontra-se, então o compilador encontrará:
print strfmt(“The value is “, theValue);
o que provavelmente não é a intenção do desenvolvedor.
Macros nas declarações de classes
Algumas confusões surgem em situações que os macros são definidos na declaração das classes. Para entender como isto é tratado é necessário rever o que o compilador faz quando ele compila um método. O processo inicia por calcular a sequencia das derivações da classe que a classe faz parte. Então o processo analisa cada declaração da classe com a primeira derivação primeiro, preenchendo sua tabela interna de símbolos. Após compilar a declaração da classe o compilador compila os métodos. Qualquer símbolo definido em qualquer classe derivada será subseqüentemente disponível para uso nos métodos. Símbolos definidos na declaração da classes talvez sejam substituídos por valores definidos em mais de uma derivação da classe.
Parênteses dentro das strings nos macros
O scanner que trata com as strings nos macros é muito simplista. Ele não ira tratar a situação onde onde parênteses forem incluídosa string, ou seja
#define.Another(“(This is text in parenthesis)”)
irá gerar um erro léxico. Se você precisar fazer isto, você deve usar no lugar a diretiva #localmacro:
#localmacro.Another
“(This is text in parenthesis)”
#endmacro
Neste caso, o fim do macro é sinalizado pela string #endmacro e não no parêntese direito.
Aqui acaba a tradução.
Se navegarem pela AOT vocês vão ver exemplos de macros mais compostos e complexos, que é o exemplo do macro InventDimSelect que implementa:
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 | select #ifnot.empty(%4) %4 #endif %1 #ifnot.empty(%5) index hint %5 #endif #ifnot.empty(%6) order by %6 #endif where (%1.ConfigId == %2.ConfigId || ! %3.ConfigIdFlag) && (%1.InventSizeId == %2.InventSizeId || ! %3.InventSizeIdFlag) && (%1.InventColorId == %2.InventColorId || ! %3.InventColorIdFlag) && (%1.InventSiteId == %2.InventSiteId || ! %3.InventSiteIdFlag) && (%1.InventLocationId == %2.InventLocationId || ! %3.InventLocationIdFlag) && (%1.InventBatchId == %2.InventBatchId || ! %3.InventBatchIdFlag) && (%1.WMSLocationId == %2.WMSLocationId || ! %3.WMSLocationIdFlag) && (%1.WMSPalletId == %2.WMSPalletId || ! %3.WMSPalletIdFlag) && (%1.InventSerialId == %2.InventSerialId || ! %3.InventSerialIdFlag) #InventDimDevelop |
e é utilizado em vários lugares para se fazer algumas consultas as dimensões de estoque. Veja que ele válida se alguns parâmetros foram passados e com isso consegue fazer if no meio de instruções SQL, o que é muito útil já que em uma instrução ‘normal’, você não conseguiria.
Existem outras possibilidades, alias, as possibilidades nos usos de macros são infinitas, vai da criatividade de cada um! Eu tenho outro amigo (Daniel Zanni) que usa os macros para verificar se determinada custom está ativa nas configuration keys e caso sim, aplica a custom no que diz respeito a código.
É isso ai pessoal, espero que tenha ajudado.
Abraço,
Pichler
[]s
Pichler
Hoje saiu o rollup 5 do ax 2009, e como os rollups são cumulativos, você só precisa baixar o último.
Dynamics AX 2009 RTM RU-5 KB (65MB)
Dynamics AX 2009 SP1 RU-5 KB (146MB)
Abraço,
Pichler
Pessoal,
Saiu uma publicação que achei interessante no blog do Sasha onde mostra como subrescrever um campo de sistema em uma tabela, no exemplo abaixo, copiado do original o autor faz uma alteração em um registro e troca o usuário que modificou o registro ao invés do sistema salvar o real.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | static void job_overwireSystemFiledInTable(Args _args) { SalesTable salesTable; ; ttsbegin; salesTable = SalesTable::find("mySalesId", true); if(salesTable.RecId != 0) { salesTable.overwriteSystemfields(true); salesTable.(fieldNum(SalesTable, createdBy)) = "NewUserId"; salesTable.update(); } ttscommit; } |
É isso ai…
Obs.: Tenho recebido alguns comentários via e-mail de que estou deixando o blog de lado, mas a verdade é que me faltam assuntos, o que deveria acontecer é vocês mandarem suas questões e assim podemos escrever! Por favor, mandem suas duvidas / sugestões de artigos.
Abraço,
Pichler