Dynamics AX Blog - Dynamics AX 2009 - Seite 4

RSS-Feed dieser Version
X++ -Statement generieren um Datensätze anzulegen
13.08.2013Microsoft Dynamics AX (Axapta)
Vor kurzem hatte ich die Notwendigkeit einige Datensätze von einer AX-Umgebung in eine andere zu kopieren. Es ging um eine sehr flache Tabelle (d.h. ohne großartige Referenzen auf andere Tabellen oder Datensätze), wodurch ich folgenden Job dafür nutzen konnte. Der Job durchläuft ein Select-Statement (ganz am Ende des Jobs) und kopiert mir in die Zwischenablage ein X++-Statement, mit welchem man die Datensätze - beispielsweise über einen eigenen Job - einfügen kann. Inspiriert ist der Code übrigens von der Methode buildInsertScript() des Formulares SysRecordInfo. static void buildInsertScriptAX2009(Args _args)
{ CustGroup CustGroup; #define.indent(' ') SysDictTable dictTable; Counter fieldCounter; Counter arrayCounter; DictEnum dictEnum; DictField dictFieldTemp; DictField dictFieldArray; str fieldValue; TextBuffer textBuffer; Source source; boolean setFieldValue(fieldId _fieldId, common _common) { boolean ret = true; fieldValue = ""; if (dictFieldTemp.baseType() != Types::Container) { fieldValue = strfmt("%1", _common.(_fieldId)); } switch (dictFieldTemp.baseType()) { case Types::String : fieldValue = strReplace(fieldValue, '\n', '\\n'); fieldValue = '\"' + fieldValue + '\"'; break; case Types::Container : fieldValue = '[' + fieldValue + ']'; break; case Types::Enum : dictEnum = new DictEnum(dictFieldTemp.enumId()); fieldValue = dictEnum.name() + '::' + dictEnum.index2Symbol(_common.(_fieldId)); break; case Types::Real : fieldValue = num2str(_common.(_fieldId), 2, -1, 1, 0); break; case Types::Date : fieldValue = date2str(_common.(_fieldId), 123,2,4,2,4,4); if (fieldValue == '') { ret = false; } fieldValue = strReplace(fieldValue, '/', '\\'); break; case Types::UtcDateTime : fieldValue = DateTimeUtil::toStr(_common.(_fieldId)); if (fieldValue == '') { ret = false; } break; } return ret; } void loopRecord(common _common) { ; source += #indent + strfmt('%1.clear();\r\n', dictTable.name()); for (fieldCounter = dictTable.fieldNext(0); fieldCounter > 0; fieldCounter = dictTable.fieldNext(fieldCounter)) { dictFieldTemp = new DictField(dictTable.id(), fieldCounter); if (dictFieldTemp.isSystem()) continue; if (dictFieldTemp.arraySize() > 1) { arrayCounter = 1; while (arrayCounter <= dictFieldTemp.arraySize()) { dictFieldArray = new DictField(dictTable.id(), fieldId2Ext(dictFieldTemp.id(),arrayCounter)); if (setFieldValue(dictFieldArray.id(), _common)) { source = source + #indent + strfmt("%1.%2 = %3;", dictTable.name(), dictFieldArray.name(DbBackend::Native, arrayCounter), fieldValue) + '\r\n'; } arrayCounter++; } } else { if (setFieldValue(fieldCounter, _common)) { source = source + #indent + strfmt("%1.%2 = %3;", dictTable.name(), dictFieldTemp.name(), fieldValue) + '\r\n'; } } } source += #indent + strfmt('%1.insert();\r\n', dictTable.name()); } ; // Modify needed records here dictTable = new SysDictTable(tableNum(CustGroup)); while select CustGroup { loopRecord(CustGroup); } textBuffer = new TextBuffer(); textBuffer.setText(source); textBuffer.toClipboard(); info("@SYS87601"); } Das Ergebnis des Jobs ist in der Zwischenablage zu finden, und sieht beispielsweise wie folgt aus: CustGroup.clear();
CustGroup.CustGroup = "K-DRITT"; CustGroup.Name = "Kunden Drittland"; CustGroup.ClearingPeriod = ""; CustGroup.PaymTermId = ""; CustGroup.TaxGroupId = ""; CustGroup.PaymIdType = ""; CustGroup.insert(); CustGroup.clear(); CustGroup.CustGroup = "K-EU"; CustGroup.Name = "Kunden EU"; CustGroup.ClearingPeriod = ""; CustGroup.PaymTermId = ""; CustGroup.TaxGroupId = ""; CustGroup.PaymIdType = ""; CustGroup.insert(); CustGroup.clear(); CustGroup.CustGroup = "K-INL"; CustGroup.Name = "Kunden Inland"; CustGroup.ClearingPeriod = ""; CustGroup.PaymTermId = ""; CustGroup.TaxGroupId = ""; CustGroup.PaymIdType = ""; CustGroup.insert(); In Dynamics AX 2012 sieht der gleiche Job etwas anders aus: static void buildInsertScriptAX2012(Args _args) { CustGroup CustGroup; #define.indent(' ') SysDictTable dictTable; Counter fieldCounter; Counter arrayCounter; DictEnum dictEnum; DictField dictFieldTemp; DictField dictFieldArray; str fieldValue; TextBuffer textBuffer; Source source; boolean setFieldValue(common _common, FieldName _fieldName, ArrayIdx _arrayIndex = 1) { boolean ret = true; fieldValue = ""; if (dictFieldTemp.baseType() != Types::Container) { fieldValue = strFmt("%1", _common.getFieldValue(_fieldName, _arrayIndex)); } switch (dictFieldTemp.baseType()) { case Types::String : fieldValue = strReplace(fieldValue, '\n', '\\n'); fieldValue = '\"' + fieldValue + '\"'; break; case Types::Container : fieldValue = '[' + fieldValue + ']'; break; case Types::Enum : dictEnum = new DictEnum(dictFieldTemp.enumId()); fieldValue = dictEnum.name() + '::' + dictEnum.value2Symbol(_common.getFieldValue(_fieldName, _arrayIndex)); break; case Types::Real : fieldValue = num2str(_common.getFieldValue(_fieldName, _arrayIndex), 2, -1, 1, 0); break; case Types::Date : fieldValue = date2str(_common.getFieldValue(_fieldName, _arrayIndex), 123,2,4,2,4,4, DateFlags::None); if (fieldValue == '') { ret = false; } fieldValue = strReplace(fieldValue, '/', '\\'); break; case Types::UtcDateTime : fieldValue = DateTimeUtil::toStr(_common.getFieldValue(_fieldName, _arrayIndex)); if (fieldValue == '') { ret = false; } break; } return ret; } void loopRecord(common _common) { ; for (fieldCounter = dictTable.fieldNext(0); fieldCounter > 0; fieldCounter = dictTable.fieldNext(fieldCounter)) { dictFieldTemp = new DictField(dictTable.id(), fieldCounter); if (dictFieldTemp.isSystem()) continue; if (dictFieldTemp.arraySize() > 1) { arrayCounter = 1; while (arrayCounter <= dictFieldTemp.arraySize()) { dictFieldArray = new DictField(dictTable.id(), dictFieldTemp.id(), arrayCounter); if (setFieldValue(_common, dictFieldArray.name(), arrayCounter)) { source = source + #indent + strFmt("%1.%2 = %3;", dictTable.name(), dictFieldArray.name(DbBackend::Native, arrayCounter), fieldValue) + '\r\n'; } arrayCounter++; } } else { if (setFieldValue(_common, dictFieldTemp.name())) { source = source + #indent + strFmt("%1.%2 = %3;", dictTable.name(), dictFieldTemp.name(), fieldValue) + '\r\n'; } } } source += #indent + strFmt('%1.insert();\r\n', dictTable.name()); } // Modify needed records here dictTable = new SysDictTable(tableNum(CustGroup)); while select CustGroup { loopRecord(CustGroup); } textBuffer = new TextBuffer(); textBuffer.setText(source); textBuffer.toClipboard(); info("@SYS87601"); } |
Tipps und Tricks zum Thema Kompilierung
03.08.2013Microsoft Dynamics AX (Axapta)
Kompilieren gehört zum täglich Brot eines Entwicklers. Leider weist genau dieser Vorgang in Dynamics AX - nennen wir es - Besonderheiten auf. Besonderheit 1: Kein Fortschrittsbalken Wir kennen es alle, man kompiliert eine grössere Menge an Objekten oder vielleicht den ganzen AOT und möchte zumindest ungefähr wissen, wie lange dies noch dauert. Standardmässig gibt’s es kaum eine Möglichkeit dies einzusehen, vor allem wenn der AX-Client seit dem Start der Kompilierung vielleicht schon "eingefroren" ist. In vielen Fällen kann man [CTRL]+[PAUSE] drücken, dadurch wird die Kompilierung kurz angehalten und man kann im Client sehen, bei welchem Objekt man gerade ist. In dem Pause-Dialog sollte man natürlich tunlichst auf [NEIN] klicken, wenn man die Kompilierung danach fortsetzen möchte. Kein echter Fortschrittsbalken, aber zumindest ein Indikator wo man gerade steht. |
Text-Datei per X++ druckenEin kurzes Beispiel, wie man aus AX heraus via Notepad eine Text-Datei ausdrucken kann. static void printTextFileFromAX(Args _args) { WinAPI::shellExecute("c:\\windows\\system32\\NOTEPAD.EXE", @'/pt "c:\temp\test.txt" "An OneNote 2010 senden"'); } Hinweise: "An OneNote 2010 senden" ist dabei der Name des Druckers. Das Beispiel wurde in Dynamics AX 2012 verwendet, sollte allerdings auch in früheren Versionen verwendbar sein. |
Markierte Datensätze eines Formulares verarbeiten/iterieren
03.07.2013Microsoft Dynamics AX (Axapta)
Um in Dynamics AX die markierten Datensätze einer Formular-Datenquelle (FormDataSource) zu verarbeiten/iterieren kann folgendes Code-Beispiel helfen: void markSelected()
{ inventTrans inventTransSelected; inventTrans inventTrans4Update; ttsbegin; inventTrans4Update.setTmpData(inventTrans); //DIES IST NUR BEI TEMPORÄREN TABELLEN NOTWEDNIG for (inventTransSelected = inventTrans_ds.getFirst(true) ? inventTrans_ds.getFirst(true) : inventTrans_ds.cursor(); inventTransSelected; inventTransSelected = inventTrans_ds.getnext()) { select firstonly forUpdate inventTrans4Update where inventTrans4Update.RecId == inventTransSelected.RecId; inventTrans4Update.Marked = NoYes::Yes; inventTrans4Update.update(); } ttscommit; inventTrans_ds.executeQuery(); } Im Beispiel werden die markierten Datensätze der Tabelle InventTrans durchlaufen und ein Feld Marked auf TRUE aktualisiert. In Dynamics AX 2012 kann übrigens statt dessen auch die Klasse MultiSelectionHelper dafür verwendet werden. |
Eine Tabelle um ein Passwort-Feld erweitern
29.06.2013Microsoft Dynamics AX (Axapta)
Im folgenden eine kurze Anleitung, wie man eine Tabelle um ein Feld erweitert, in welchem ein verschlüsseltes Passwort gespeichert werden kann.
static DEV_ServerPasswordStr password()
{ CryptoBlob cryptoBlob = connull(); ; cryptoBlob = DEV_Parameters::find().ServerPassword; if (cryptoBlob != connull()) return cryptoblob2str(WinapiServer::cryptUnProtectData(cryptoBlob)); else return ''; }
edit DEV_ServerPasswordStr setPassword(boolean _set = false, DEV_ServerPasswordStr _pwd = '')
{ CryptoBlob cryptoBlob = connull(); ; if (_set && _pwd != 'xxxxxxxx') { cryptoBlob = WinapiServer::cryptProtectData(str2cryptoblob(_pwd)); DEV_Parameters.ServerPassword = cryptoBlob; DEV_Parameters.update(); } if (DEV_Parameters.ServerPassword != connull()) { return 'xxxxxxxx'; } else { return ''; } }
|
Modal Form in Dynamics AX erstellen/aufrufenUm in Dynamics AX ein Formular modal aufzurufen, kann man folgendes Code-Fragment aus der clicked()-Methode einer Schaltfläche (Button) verwenden: void clicked() { FormRun formRun; Args args = new Args(); super(); args.name(formStr(MyFormName)); args.record(SalesLine); formRun = classFactory::formRunClassOnClient(Args); formRun.init(); formRun.run(); If (!formRun.closed()) { formRun.wait(true); } } Auf diese Art & Weise kann man praktisch jedes Formular als modales - also als ein Formular öffnen, welches erst geschlossen werden muss, bevor mit der Anwendung weitergearbeitet werden kann - öffnen. |
|
|
|
|
|
|
Im folgenden ein Beispiel, wie man bei einer FormDataSource einen QueryBuildRange aufbaut, welcher nur tagesaktuelle Datensätze anzeigt.
Im Beispiel enthält unsere FormDataSource namens DataSourceName zwei Datumsfelder namens FromDate und ToDate und es sollen abhängig von einer Checkbox nur Datensätze angezeigt werden, die "heute" gültig sind.
{
queryBuildRange qbr;
qbr = sysQuery::findOrCreateRange(DataSourceName_ds.queryBuildDataSource(), fieldNum(DataSourceName, recId));
if( !ShowExpiredCheckBox.checked())
{
qbr.value(strfmt('('+
'((%5.%2 <= %1) && (%5.%3 >= %1)) || ' +
'((%5.%2 == %4) && (%5.%3 == %4)) || ' +
'((%5.%2 <= %1) && (%5.%3 == %4)) || ' +
'((%5.%3 >= %1) && (%5.%2 == %4)) ' +
')',
Date2StrXpp(systemDateGet()),
fieldstr(DataSourceName, FromDate),
fieldstr(DataSourceName, ToDate),
Date2StrXpp(dateNull()),
tableId2name(tableNum(DataSourceName))));
}
else
{
qbr.value(SysQuery::valueUnlimited());
}
}
Der Aufruf obiger Methode kann beispielsweise in der executeQuery() der Datasource erfolgen.