Dynamics AX Blog - Beiträge von 2009 - Seite 2
Arbeiten mit dem aufrufenden Objekt einer Form (Caller)Nachstehende Methode enthält einige Snippets, die in einem Formular verwendet werden können, um in Dynamics AX diverse Funktionen/Methoden des Aufrufenden Objektes aufzurufen. Ändert man element.args() auf z.b. _args und übergibt der Methode diese als Parameter, kann die selbe Logik auch aus einer Klasse heraus verwendet werden. void workWithCallingRecord() { common common; object object; formDataSource formDataSource; formRun formRun; inventDim inventDim; salesTable salesTable; int i; ; // Call method from calling record if( element.args() && element.args().record() ) { common = element.args().record(); if(common.isFormDataSource()) { info(tableId2Name(common.TableId)); if(formDataSourceHasMethod(common.dataSource(), identifierStr("someMethod"))) { object = common.dataSource(); object.someMethod(); } } } // Call method from calling form if(element.args() && element.args().caller() && element.args().caller().handle() == className2Id('formRun')) { formRun = element.args().caller(); if(sysFormRun::hasMethod(formRun, identifierStr("someFormMethod"))) { object = formRun; object.someFormMethod(); } } // Get value from calling record if( element.args() && element.args().record() ) { common = element.args().record(); if(common.TableId == tableNum(salesTable)) { info(common.(fieldNum(salesTable, salesId))); } } // Get value from calling datasource (form with multiple datasources) if(element.args() && element.args().caller() && element.args().caller().handle() == className2Id('formRun')) { formRun = element.args().caller(); for (i = 0; i <= formRun.dataSourceCount(); i++) { formDataSource = formRun.datasource(i); if (formDataSource && formDataSource.table() == tablenum(inventDim)) // Search for specific table { inventDim = formDataSource.cursor(); break; } } if(inventDim) { info(inventDim.InventLocationId); } } // Change data in calling datasource if(element.args() && element.args().caller() && element.args().caller().handle() == className2Id('formRun')) { formRun = element.args().caller(); for (i = 0; i <= formRun.dataSourceCount(); i++) { formDataSource = formRun.datasource(i); if (formDataSource && formDataSource.table() == tablenum(salesTable)) // Search for specific table { salesTable = formDataSource.cursor(); break; } } if(salesTable) { // Update data salesTable.PurchOrderFormNum = "Some value"; salesTable.update(); } } // Refresh calling datasource if( element.args() && element.args().record() ) { common = element.args().record(); if(common.isFormDataSource()) { formDataSource = common.dataSource(); formDataSource.research(true); } } } |
Dynamics AX mit einem bestimmten Windowslogin starten
08.11.2009Microsoft Dynamics AX (Axapta)
Gegenüber früheren Versionen von Dynamics AX hat man - dank Integration des Active Directories - in AX 2009 kaum Möglichkeiten, die Anwendung als ein bestimmter Benutzer zu starten. Gerade dies ist aber notwendig, wenn man z.B. Änderungen in den Sicherheitseinstellungen/Berechtigungen überprüfen möchte. Für solche Fälle habe ich mir eine Batch-Datei (*.bat) geschrieben, die mir eine lokale AX-Installation mit einem bestimmten Windows-Login startet. Diese Batch-Datei öffnet ein Windows-Kommandozeilen-Fenster in welchem man zur Eingabe des Windows-Passworts aufgefordert wird. runas /user:domain\username "C:\Programme\Microsoft Dynamics AX\50\Client\Bin\Ax32.exe "C:\Programme\Microsoft Dynamics AX\50\MyConfigurationfile.axc"" Wenn man diese Befehlszeile direkt in der Eingabeaufforderung eingeben will, muss man den Befehl wie folgt leicht abwandeln: runas /user:domain\username "C:\Programme\Microsoft Dynamics AX\50\Client\Bin\Ax32.exe \"C:\Programme\Microsoft Dynamics AX\50\MyConfigurationfile.axc\"" Muss man ausser der Konfigurationsdatei noch weitere Parameter übergeben, kann man die Batch-Datei wie folgt erweitern: runas /user:domain\username "C:\Programme\Microsoft Dynamics AX\50\Client\Bin\Ax32.exe "C:\Programme\Microsoft Dynamics AX\50\MyConfigurationfile.axc" -aol=var -aolcode=j+eA4566458bsZReSO1Q==" Der selbe Befehl sähe, wenn man ihn über die Eingabeaufforderung eingeben wollte, wie folgt aus: runas /user:domain\username "C:\Programme\Microsoft Dynamics AX\50\Client\Bin\Ax32.exe \"C:\Programme\Microsoft Dynamics AX\50\MyConfigurationfile.axc\" -aol=var -aolcode=j+eA4566458bsZReSO1Q==" Alternativ dazu kann man sich auch der hier beschriebenen Lösung bedienen. Übrigens: Sollte die Konfigurationsdatei auf einem Netzlaufwerk liegen, so sollte man bei der Angabe des Dateipfades nicht dessen Laufwerksbuchstaben verwenden, sondern den vollen UNC-Pfad. Denn möglicherweise hat der Benutzer, mit dem ich die Applikation starten möchte, den einzelnen Netzwerklaufwerken andere Laufwerksbuchstaben zugewiesen als man selbst ;-) |
Geänderte oder neue Methoden von Klassen auflisten
01.11.2009Microsoft Dynamics AX (Axapta)
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. |
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. |
|
|
|
|
|
|
Kurzes Codebeispiel, wie man alle Felder einer Tabelle auflistet:
{
dictTable dictTable;
int currFieldId = 0;
counter c = 0;
;
dictTable = new dictTable(tableNum(custtable));
do
{
currFieldId = dictTable.fieldNext(currFieldId);
info(dictTable.fieldName(currFieldId));
c++;
}
while (c < dictTable.fieldCnt());
}