Dynamics AX Blog - Dynamics AX 2009 - Seite 14

RSS-Feed dieser Version
Geänderte oder neue Methoden von Tabellen auflisten
01.11.2009Microsoft Dynamics AX (Axapta) 
Im folgenden findet ihr einen Job, der in einer adaptierten AX 2009er-Applikation alle Tabellenmethoden auflistet, die entweder erstellt wurden oder gegenüber dem Standard verändert wurden. 
static void ListChangedOrCreatedTableMethods(Args _args)
{
    SysDictTable    SysDictTable;
    treeNode        treeNode;
    treeNode        treeNodeBase;
    treeNode        treeNodeCustom;
    treeNode        treeNodeTables;
    UtilEntryLevel  CurrentUtilEntryLevel;
    #AOT
    ;
    treeNodeTables = TreeNode::findNode(#TablesPath + #AOTRootPath);
    treeNodeTables = treeNodeTables.AOTfirstChild();
    setPrefix('List changed/created tablemethods');
    while(treeNodeTables)
    {
        SysDictTable = SysDictTable::newTableId(tableName2Id(treeNodeTables.treeNodeName()));
        setPrefix(SysDictTable.name());
        treeNode = TreeNode::findNode(#TablesPath + #AOTRootPath + SysDictTable.name() + #AOTRootPath + "Methods");
        treeNode = treeNode.AOTfirstChild();
        while(treeNode)
        {
            treeNodeBase    = null;
            treeNodeCustom  = null;
            // Partner
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::bup);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::bus);
            // Solutions
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl3);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl2);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl1);
            // Hotfix
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::hfx);
            // Microsoft
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::glp);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::gls);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::syp);
            if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sys);
            // Custom layers
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::vap);   CurrentUtilEntryLevel = UtilEntryLevel::vap; }
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::var);   CurrentUtilEntryLevel = UtilEntryLevel::var; }
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::cup);   CurrentUtilEntryLevel = UtilEntryLevel::cup; }
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::cus);   CurrentUtilEntryLevel = UtilEntryLevel::cus; }
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::usp);   CurrentUtilEntryLevel = UtilEntryLevel::usp; }
            if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::usr);   CurrentUtilEntryLevel = UtilEntryLevel::usr; }
            // Changed methods
            if(treeNodeBase)
            {
                if( treeNodeBase.AOTgetSource() != treeNode.AOTgetSource())
                {
                    warning(strFmt( "Method '%1' was changed in layer '%2'.",
                                    treeNode.treeNodeName(),
                                    enum2str(CurrentUtilEntryLevel)));
                }
            }
            // New created methods
            if( !treeNodeBase)
            {
                info(strFmt( "Method '%1' was created in layer '%2'.",
                             treeNode.treeNodeName(),
                             enum2str(CurrentUtilEntryLevel)));
            }
            treeNode = treeNode.AOTnextSibling();
        }
        treeNodeTables = treeNodeTables.AOTnextSibling();
    }
}
Ein ähnlicher Job der prinzipiell das Gleiche erledigt, allerdings für alle Klassenmethoden, ist hier zu finden.  | 
Erstellen und Buchen eines Umlagerungsjournal in AX 2009 mit Erfassung und Entnahme
21.10.2009Microsoft Dynamics AX (Axapta) 
In einem früheren Beitrag habe ich anhand eines kurzen Code-Beispiels demonstriert, wie man in AX 2009 ein Umlagerungsjournal per Code erstellen und buchen kann. Im folgenden findet Ihr ein Beispiel welches, soferne es die Lagersteuerungsgruppe des Artikel erfordert, sich auch um die zugehörige Entnahme- und -Erfassungsbuchung kümmert. 
static void createAndPostInventJournalTransfer_II(Args _args)
{
    inventJournalTable  inventJournalTable;
    inventJournalTrans  inventJournalTrans;
    journalCheckPost    journalCheckPost;
    itemId              _itemId             = "9992";
    qty                 _qty                = 1;
    inventDimId         _fromInventDimId    = "00008328_069";
    inventDimId         _toInventDimId      = "00008342_069";
    tmpInventTransWMS   tmpInventTransWMS;
    inventTRans         inventTRans;
    InventTransWMS_Pick     inventTransPick;
    InventTransWMS_Register inventTransRegister;
    ;
    try
    {
        ttsbegin;
        // Create header
        inventJournalTable.clear();
        inventJournalTable.initValue();
        inventJournalTable.initFromInventJournalName(
            InventJournalName::find(InventJournalName::standardJournalName(InventJournalType::Transfer)));
        inventJournalTable.SystemBlocked    = NoYes::Yes;
        inventJournalTable.BlockUserId      = curUserId();
        inventJournalTable.insert();
        // Create line
        inventJournalTrans.clear();
        inventJournalTrans.initValue();
        inventJournalTrans.initFromInventJournalTable(inventJournalTable);
        inventJournalTrans.ItemId           = _itemId;
        inventJournalTrans.initFromInventTable(inventTable::find(inventJournalTrans.ItemId));
        inventJournalTrans.Qty              = _qty;
        inventJournalTrans.inventMovement().journalSetCostPrice();
        inventJournalTrans.inventDimId      = _fromInventDimId;
        inventJournalTrans.toinventDimId    = _toInventDimId;
        inventJournalTrans.voucher          = numberSeq::newGetVoucherFromCode(
                                                    InventJournalName::find(inventJournalTable.JournalNameId).VoucherSeqId).voucher();
        inventJournalTrans.insert();
        if(inventModelGroup::find(inventTable::find(_itemId).ModelGroupId).MandatoryPick)
        {
            // Picking
            inventTrans.clear();
            inventTrans = inventTrans::findTransId(inventJournalTrans.InventTransId);
            tmpInventTransWMS.clear();
            tmpInventTransWMS.initFromInventTrans(inventTrans);
            tmpInventTransWMS.InventQty = inventTrans.StatusIssue == StatusIssue::Picked ? inventTrans.Qty : -inventTrans.Qty;
            if (tmpInventTransWMS.validateWrite())
            {
                tmpInventTransWMS.write();
            }
            inventTranspick = new InventTransWMS_Pick(inventTRans.inventMovement(),tmpInventTransWMS);
            inventTranspick.writeTmp(tmpInventTransWMS,tmpInventTransWMS.orig(),false);
            InventTransWMS_Pick::updateInvent(inventTransPick, tmpInventTransWMS);
        }
        if(inventModelGroup::find(inventTable::find(_itemId).ModelGroupId).MandatoryRegister)
        {
            // Registration
            inventTrans.clear();
            inventTrans = inventTrans::findTransId(inventJournalTrans.toInventTransId);
            tmpInventTransWMS.clear();
            tmpInventTransWMS.initFromInventTrans(inventTrans);
            tmpInventTransWMS.InventQty = inventTrans.StatusReceipt == StatusReceipt::Ordered ? inventTrans.Qty : -inventTrans.Qty;
            if (tmpInventTransWMS.validateWrite())
            {
                tmpInventTransWMS.write();
            }
            inventTransRegister = new InventTransWMS_Register(inventTrans.inventMovement(),tmpInventTransWMS);
            inventTransRegister.writeTmp(tmpInventTransWMS,tmpInventTransWMS.orig(),false);
            InventTransWMS_Register::updateInvent(inventTransRegister, tmpInventTransWMS);
        }
        // Post journal
        journalCheckPost = InventJournalCheckPost::newJournalCheckPost(JournalCheckPostType::Post, inventJournalTable);
        journalCheckPost.run();
        ttscommit;
    }
    catch (Exception::Error)
    {
        throw exception::Error;
    }
}
 | 
Fehlerhafte str2con Funktion unter AX2009 II
16.10.2009Microsoft Dynamics AX (Axapta) 
Im vor kurzem erschienenen Hotfix Rollup 3 für AX 2009 wurde u.a. der, in diesem Artikel beschriebene, Fehler der Funktion str2con korrigiert. Wer sich nicht gleich die Mühe machen möchte, nur aufgrund dieses Fehlers das gesamte HR einzuspielen, der kann auch nur die Methode in der Klasse Global anpassen: Einfach den Aufruf str2int durch str2int64 ersetzen.  | 
Erstellen und Buchen eines Umlagerungsjournal in AX 2009
25.09.2009Microsoft Dynamics AX (Axapta) 
Nachstehend ein Codebeispiel, wie man in Dynamics AX 2009 per X++ ein Umlagerungsjournal erstellen und buchen kann. 
static void createAndPostInventJournalTransfer(Args _args)
{
    inventJournalTable  inventJournalTable;
    inventJournalTrans  inventJournalTrans;
    journalCheckPost    journalCheckPost;
    
    itemId              _itemId             = "1000";
    qty                 _qty                = 1;
    inventDimId         _fromInventDimId    = "00000070_069";
    inventDimId         _toInventDimId      = "00000063_069";
    ;
    try
    {
        ttsbegin;
        // Create header
        inventJournalTable.clear();
        inventJournalTable.initValue();
        inventJournalTable.initFromInventJournalName(
            InventJournalName::find(InventJournalName::standardJournalName(InventJournalType::Transfer)));
        inventJournalTable.SystemBlocked    = NoYes::Yes;
        inventJournalTable.BlockUserId      = curUserId();
        inventJournalTable.insert();
        // Create lines
        inventJournalTrans.clear();
        inventJournalTrans.initValue();
        inventJournalTrans.initFromInventJournalTable(inventJournalTable);
        inventJournalTrans.ItemId           = _itemId;
        inventJournalTrans.initFromInventTable(inventTable::find(inventJournalTrans.ItemId));
        inventJournalTrans.Qty              = _qty;
        inventJournalTrans.inventMovement().journalSetCostPrice();
        inventJournalTrans.inventDimId      = _fromInventDimId;
        inventJournalTrans.toinventDimId    = _toInventDimId;
        inventJournalTrans.voucher          = numberSeq::newGetVoucherFromCode(
                                                    InventJournalName::find(inventJournalTable.JournalNameId).VoucherSeqId).voucher();
        inventJournalTrans.insert();
        // Post journal
        journalCheckPost = InventJournalCheckPost::newJournalCheckPost(JournalCheckPostType::Post, inventJournalTable);
        journalCheckPost.run();
        ttscommit;
    }
    catch (Exception::Error)
    {
        throw exception::Error;
    }
}
Mit der einen oder anderen Anpassung sollte es auch möglich sein, Journale anderer Typen auf ähnliche Art & Weise zu erstellen.  | 
Fehlerhafte str2con Funktion unter AX 2009
17.09.2009Microsoft Dynamics AX (Axapta) 
Vor fast zwei Jahren habe ich schon einmal über die Funktion/Methode str2con und deren Unzulänglichkeiten in älteren Versionen von Dynamixs AX geschrieben. Leider musste ich vor kurzem feststellen, daß diese Funktion in AX 2009 auch nicht so zuverlässig ist, wie ich sie mir wünschen würde. Sind nämlich im zu zerteilenden String rein numerische Werte enthalten, die größer oder gleich 2^31 (=2147483648) sind, kann AX diese Werte nicht mehr korrekt verarbeiten. Nachstehend ein Job, der dieses Fehlverhalten demonstriert: static void str2conBug(Args _args) 
{ container con; str myStr; int i; ; myStr += "Dynamics" + ";"; myStr += "Ax 2009" + ";"; myStr += "str2con" + ";"; myStr += "does" + ";"; myStr += "not work with" + ";"; myStr += "numeric values greater" + ";"; myStr += "than" + ";"; myStr += "2 ^ 31." + ";"; myStr += "Example:" + ";"; myStr += "2147483648" + ";"; // does not work myStr += "(=2^31)" + ";"; myStr += " " + ";"; myStr += "Example:" + ";"; myStr += "5432167890" + ";"; // does not work myStr += "(>2^31)"; con = str2con(myStr, ";"); for(i=1;i<=conLen(con);i++) { info(conPeek(con, i)); } } Mir blieb also nichts anderes übrig, als mir einen Ersatz für die Funktion str2con zu schreiben:  | 
SELECT-Statements in Verbindung mit Maps
09.09.2009Microsoft Dynamics AX (Axapta) 
Wie man Maps verwenden kann, ist u.a. in der MSDN beschrieben, wie man allerdings Maps in Verbindung mit SELECT-Statements verwendet, steht auch dort nicht beschrieben. Deshalb im folgenden ein kurzes Beispiel unter Verwendung der Map bankAccountMap. 
static void selectFromMap(Args _args)
{
    bankAccountMap     bankAccountMap;
    custBankAccount    custBankAccount;
    vendBankAccount    vendBankAccount;
    ;
    while select bankAccountMap
    {
        info(bankAccountMap.AccountNum);    // Does never occur
    }
    // select records from custBankAccount
    bankAccountMap = custBankAccount.data();
    while select bankAccountMap
    {
        info(bankAccountMap.AccountNum);
    }
    // select records from vendBankAccount
    bankAccountMap = vendBankAccount.data();
    while select bankAccountMap
    {
        info(bankAccountMap.AccountNum);
    }
} | 
  | 
|
  | 
|
  | 
|
                
Im folgenden findet ihr einen Job, der in einer adaptierten Applikation alle Methoden von Klassen auflistet, die entweder erstellt wurden oder gegenüber dem Standard verändert wurden.
static void ListChangedOrCreatedClassesMethods(Args _args) { SysDictClass SysDictClass; treeNode treeNode; treeNode treeNodeBase; treeNode treeNodeCustom; treeNode treeNodeClasses; UtilEntryLevel CurrentUtilEntryLevel; #AOT ; treeNodeClasses = TreeNode::findNode(#ClassesPath + #AOTRootPath); treeNodeClasses = treeNodeClasses.AOTfirstChild(); SysDictClass = new SysDictClass((treeNodeClasses.applObjectId())); setPrefix('List changed/created classes'); while(treeNodeClasses) { SysDictClass = new SysDictClass((treeNodeClasses.applObjectId())); setPrefix(SysDictClass.name()); treeNode = TreeNode::findNode(#ClassesPath + #AOTRootPath + SysDictClass.name() + #AOTRootPath); treeNode = treeNode.AOTfirstChild(); while(treeNode) { treeNodeBase = null; treeNodeCustom = null; // Partner if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::bup); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::bus); // Solutions if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl3); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl2); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sl1); // Hotfix if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::hfx); // Microsoft if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::glp); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::gls); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::syp); if( !treeNodeBase) treeNodeBase = treeNode.getNodeInLayer(UtilEntryLevel::sys); // Custom layers if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::vap); CurrentUtilEntryLevel = UtilEntryLevel::vap; } if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::var); CurrentUtilEntryLevel = UtilEntryLevel::var; } if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::cup); CurrentUtilEntryLevel = UtilEntryLevel::cup; } if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::cus); CurrentUtilEntryLevel = UtilEntryLevel::cus; } if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::usp); CurrentUtilEntryLevel = UtilEntryLevel::usp; } if( !treeNodeCustom) { treeNodeCustom = treeNode.getNodeInLayer(UtilEntryLevel::usr); CurrentUtilEntryLevel = UtilEntryLevel::usr; } // Changed methods if(treeNodeBase) { if( treeNodeBase.AOTgetSource() != treeNode.AOTgetSource()) { warning(strFmt( "Method '%1' was changed in layer '%2'.", treeNode.treeNodeName(), enum2str(CurrentUtilEntryLevel))); } } // New created methods if( !treeNodeBase) { info(strFmt( "Method '%1' was created in layer '%2'.", treeNode.treeNodeName(), enum2str(CurrentUtilEntryLevel))); } treeNode = treeNode.AOTnextSibling(); } treeNodeClasses = treeNodeClasses.AOTnextSibling(); } }Ein ähnlicher Job der prinzipiell das Gleiche erledigt, allerdings für alle Tabellenmethoden, ist hier zu finden.