...feel the spirit of Microsoft Dynamics AX RSS 2.0
 Saturday, August 30, 2008

Alle frei erhältlichen Dokumente über Microsoft Dynamixs AX 2009 stehen jetzt über Windows Live SkyDrive für jedermann frei zur Verfügung.

Vielen Dank an Arijit Basu, der sich die Arbeit gemacht hat, diese dort zum downlaod zur Verfügung zu stelllen. 

Mehr Informationen hierzu gibt es im Blog von Arijit Basu.

Hier der "direkt" Link zu den Dokumenten: AX 2009 Documents

Saturday, August 30, 2008 1:53:37 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

Wie bereits im diesem Artikel "Fehlermeldung beim Starten des Microsoft Dynamics AX Clients" beschrieben, kann es zu Fehlermeldungen beim Starten des Dynamixs AX 4.0 Client kommen.

Eine weitere Fehlermeldung, welche erzeugt werden kann ist "Incompatible ext. version".
Es ist auch möglich, dass diese sogar mehrfach ausgegeben wird.

Grund hierfür ist meist ein Problem mit der TAPI-Integartion des CRM Moduls, bzw. genauer gesagt, ein Problem mit den eingestellten Wählregeln/Standorte der Windows Telefon- und Modemoptionen.

Die Behebung des Fehlers ist eigentlich ganz einfach.

  1. Wenn die TAPI-Integration nicht genutzt wird, kann diese deaktiviert werden.
    Wie dies genau geht kann in diesem Artikel "Fehlermeldung beim Starten des Microsoft Dynamics AX Clients" nachgelesen werden.

  2. Wenn die TAPI-Integration verwendet werden soll, muss ein neuer Standort in den Windows Telefon- und Modemoptionen erstellt werden.
    Das Erstellen eines neuen Standorts erfolgt über "Start -> Systemsteuerung -> Telefon- und Modemoptionen" auf dem jeweiligen Clientcomputer (pro Benutzer).
    Dort sollte, wenn vorhanden, ein bestehender Standort gelöscht werden und ein neuer angelegt werden.
Saturday, August 30, 2008 12:39:27 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  | 

 Thursday, August 14, 2008

Wie bereits in diesem Artikel "Auswahl von mehreren Datensätzen in einem Grid-Control (MultiSelect)" beschrieben, kann für ein Grid-Control die Eigenschaft MultiSelect gesetzt werden, womit es ermöglicht wird, dass mehrere Datensätze für eine weitere Verarbeitung ausgewählt werden können.

Dies Funktioniert solange, bis <DataSource>_ds.research() aufgerufen wird. Dieser Aufruf hat zur Folge, dass die Daten der DataSource neu geladen werden und somit auch die Selektierung verworfen wird.

Ein Beispiel wie es nicht funktioniert:

void clicked()

   CustTable custTable; 
   ; 
   for (custTable = CustTable_ds.getFirst(true) ? CustTable_ds.getFirst(true) : CustTable_ds.cursor(); 
        custTable; 
        custTable = CustTable_ds.getNext()) 
   { 
      //do something with custTable 
      info(custTable.accountNum);
      
custTable_ds.research();    
   }
}

Es gilt also genau zu beachten zu welchem Zeitpunkt bzw. an welcher Stelle im Quelltext die Methode <DataSource>_ds.research() aufgerufen wird.

Weiterhin kann es zu Problemen beim MultiSelect kommen, wenn in den Methoden der DataSource ein Aufruf von <DataSource>_ds.research() erfolgt.
Normalerweise werden die DataSource-Methoden für jeden selektierten Datensatz ausgeführt. Wenn aber innerhalb einer der Methoden, wie z.B. Delete(), wird diese Methode nur für den ersten ausgewählten Datensatz ausgeführt und dann ein Research ausgeführt, was wie schon beschrieben zu einem Verwerfen der Selektierung führt.

Thursday, August 14, 2008 6:00:44 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 | 

 Friday, August 08, 2008

Microsoft Dynamics AX verwendet für eindeutige Kennungswerte (Id’s) die eingebauten Nummernkreise, für welche ein Feld vom Typ „String“ benötigt wird. Dies macht auch Sinn, da Nummernkreise in Dynamics AX oft ein oder mehrere alphanumerische Zeichen enthalten. Natürlich können auch rein nummerische Nummernkreise mit diesem „Framework“ erstellt werden.

Allerdings sind die Nummernkreise im Dynamics AX Standard nicht ganz optimal bei der Verwendung von einem rein nummerischen Nummernkreisen. Dies fängt z.B. schon beim Datentyp an, der für das ID-Feld der Tabelle verwendet werden muss. Bedingt dadurch, dass ein Feld vom Typ „String“ verwendet werden muss, belegt dieses Feld unnötig viel Speicher in der Datenbank. Weiterhin gestalten sich Sortierungen, Rechenoperationen, etc. erheblich schwieriger.

Diese Probleme können umgangen werden, wenn für das ID-Feld der Datentyp „Integer“ oder „Int64“ verwendet wird. Leider kann nun nicht mehr das Nummernkreis-Framework des Dynamics AX Standards verwendet werden, da dies den Datentyp „String“ für ein ID-Feld vorschreibt.

Es muss also ein eigenes, kleines Nummernkreis-Framework oder ein eigener Nummernkreis geschrieben werden, der die Verwendung des Datentyps „Integer“ für ID-Felder ermöglicht. Dies hört sich zuerst schwierig an, da Dinge wie fortlaufende Nummernvergabe oder die Wiederverwendung von freien Nummern (Löchern im Nummernkreis) berücksichtigt werden sollten.

Es ist aber ganz und gar nicht schwierig, eine eigene Nummernkreisfunktionalität zu erstellen. Das einzige was hierfür benötigt wird ist eine entsprechen aufgebaute Select-Abfrage.

Die nächste Nummer eines Nummernkreises ist immer die zuletzt vergebenen Nummer (höchste) + 1.

   Aku_TestTable t1;
   ;
   select maxof(ID) from t1;
   return t1.ID + 1;

Etwas schwieriger wird es, wenn auch die frei gewordenen Nummern des Nummernkreises wieder vergeben/verwendet werden sollen. Dann muss immer die kleinste Id aus der Tabelle gesucht werden, für die es keinen Datensatz in der Tabelle gibt. Gibt es kein "Nummernloch", muss die nächst höchste Nummer vergeben werden.

   Aku_TestTable t1;
   Aku_TestTable t2;
   ;
   select minof(ID) from t1 notexists join t2 where t2.ID == (t1.ID + 1);
   return t1.ID + 1;

Um diese ein wenig zu verdeutlichen, kann eine beispielhafte Implementierung eines eigenen Nummernkreises in diesem Demoprojekt "SharedProject_AKU_OwnIntNumberSeq" angesehen und runter geladen werden.

Friday, August 08, 2008 3:38:44 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 | 

 Wednesday, July 16, 2008

Die Benamung von Tabellenfeldern erfolgt über Labels, die in der Feldeigenschaft "Label" festgelegt werden.
Diese Benamung wird immer angezeigt, wenn das Feld in der Applikation angezeigt wird. Es gibt in den Eigenschaften der Tabellenfelder noch die Eigenschaft "GroupPrompt", die die Benamung in Masken festlegt, wenn das Feld innerhalb einer Gruppe angezeigt wird.

Die hinterlegte Benamung in der Eigenschaft GroupPrompt wird immer dann verwendet, wenn das Feld in einer Gruppe dargestellt wird. Das können Tabellenfeldgruppen oder Gruppen, die in Forms erzeugt wurden, sein.

Wednesday, July 16, 2008 7:28:11 AM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback


 Thursday, July 10, 2008

Manchmal ist es möglich, dass beim Starten des Microsoft Dynamics AX 4.0 Clients eine oder meherer Fehlermeldungen in einem Infolog-Fenster ausgegeben werden.

Diese Fehlermeldung könnten z.B. "Corrupted ini file" sein.

Die große Frage ist nun, woher kommt diese Fehlermeldung bzw. wodurch wird diese erzeugt.
Leider ist die Fehlermeldung, welche im Ereignisprotokoll gefunden werden kann, meist auch nicht besonders hilfreich.

Sollten solche, eher unerklärlichen Fehlermeldungen beim Starten des Dynamics AX Client ausgegeben werden, lohnt sich oft ein Blick in die Systemkonfiguration, welche über Verwaltung, Einstellungen, System, Konfiguration aufgerufen werden kann.

Der Grund für diese Fehlermeldung könnte die aktivierte Telefonieintegration des CRM Moduls sein.
Wird diese deaktiviert, sollte die Fehlermeldung nicht mehr erzeugt werden.

Thursday, July 10, 2008 7:13:05 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [1] - Trackback
 |  |  | 

 Thursday, June 26, 2008

In der Screencast Sektion von Channel 9 hat Mey Meenakshisundaram 6 Screencasts über die Entwicklung mit dem Enterprise Portal veröffentlicht.

Microsoft Dynamics AX 2009 – Creating simple List Page in Enterprise Portal
This how-to video demonstrates how to create a simple List Page in Enterprise Portal using AxGridView control.

Microsoft Dynamics AX 2009 – Creating simple Task Page in Enterprise Portal
This how-to video demonstrates how to create a simple Task Page in Enterprise Portal using AxForm control.

Microsoft Dynamics AX 2009 – Creating simple Tunnel(Wizard) Page in Enterprise Portal
This how-to video demonstrates how to create a simple Tunnel(Wizard) Page in Enterprise Portal using ASP.net Wizard control and EP AxForm control.

Microsoft Dynamics AX 2009 – Calling X++ classes in Enterprise Portal User Controls in C#
This how-to video demonstrates how to create a simple X++ Class in AOT and create a C# proxy for this class and call it in Enterprise Portal user control written in C#.

Microsoft Dynamics AX 2009 – Using Record Context in Enterprise Portal
Microsoft Dynamics AX developers will learn how to use record context in Enterprise Portal. Record context is used to pass currently selected record information on a page or Web part to another page or connected Web part. This how-to-video demonstrates three ways of passing record context...

Microsoft Dynamics AX 2009 – Advanced Grid in Enterprise Portal List Page
This how-to video demonstrates how to add ranges in dataset to restrict the data displayed and the different options supported (open,hidden,locked). This also demonstrates how to use display and edit methods defined in the table in the Grid.

Thursday, June 26, 2008 6:46:58 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Thursday, June 12, 2008

Wenn bei der Entwicklung mit Microsoft Dynamics AX 4.0 die Quellcodeverwaltung mittels Visual SourceSafe 2005 eingeschaltet wurde, besteht die Möglichkeit einzelne Versionen eines Objekts miteinander zu vergleichen.

Hierbei kann es aber bei einer "ungünstigen" Konfiguration des lokal Repository-Verzeichnisses sein, dass bei einem Vergleich von zwei Objektversionen die Fehlermeldung "Fehler: Fortsetzen nicht möglich" ausgegeben wird.

Diese Fehlermeldung wird immer erzeugt, wenn sich das lokale Repository-Verzeichnis und das Verzeichnis, in dem die temporären Internetdateien (Temporary Internet Files) gespeichert werden, nicht auf der gleichen Partition (Datenträger) befinden.

Beispiel:

Ordner der Temporary Internet Files = C:\Dokumente und Einstellungen\UserXY\Lokale Einstellungen\Temporary Internet Files
Ordner des lokalen Repositories = D:\VSSRepository\Test

-> Die Fehlermeldung wird ausgegeben.

Ordner der Temporary Internet Files = C:\Dokumente und Einstellungen\UserXY\Lokale Einstellungen\Temporary Internet Files
Ordner des lokalen Repositories = C:\VSSRepository\Test

-> Die Fehlermeldung wird nicht ausgegeben und der Versionvergleich funktioniert problemlos.

Dieses Problem wird durch ein Update für Visual SourceSafe 2005 behoben. Es empfiehlt sich, bei Verwendung der Quellcodeverwaltung mit Visual SourceSafe 2005 als VC-System, dieses Update einzuspielen.

Thursday, June 12, 2008 10:57:09 AM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  | 

 Wednesday, June 11, 2008
Die Eigenschaften von FormControls, wie heightMode und widthMode werden im Form Designer über Enums gesetzt. Leider hat man hier mittels X++ Probleme, denn es wird bei der Verwendung dieser Methoden nicht angegeben um welchem Enum es sich handelt. Das hat zufolge, dass man immer wieder probieren muss, um die gewünschte Eigenschaft richtig zu setzten. Desweiteren ist so auch der Quellcode schlechter lesbar.

Der Aufruf sieht normal so aus:

FormControl.heightMode(1);

Besser ist es, wenn man sprechende Parameter mit dem richtigen Enum verwendet

FormControl.heightMode(FormHeight::ColumnHeight);

Mögliche Optionen:

FormHeight::Auto
FormHeight::ColumnHeight


Ähnlich sieht der Parameter bei widthMode aus, nur wird hier der Enum FormWidth genutzt.

FormControl.witdthMode(FormWidth::ColumnHeight);

Mögliche Optionen:

FormWidth::Auto
FormWidth::ColumnHeight


Wie schon bei den letzten beiden Eigenschaft kann man bei TopMode auch nur raten, was mittels X++ übergeben werden muss.

FormControl.TopMode(0);

Besser ist, wenn auch hier ein sprechender Parameter mit dem richtigen Enum verwendet wird.

FormControl.TopMode(FormTop::Auto);

Mögliche Optionen:

FormTop::Auto
FormTop::ButtomEdge
FormTop::Center
FormTop::TopEdge

Bei FormGroupControls gibt es die Eigenschaft frameOptionButton, auch hier tritt dasselbe Problem auf.

FormControl.frameOptionButton(0);

Der Enum lautet hier FormFrameOptionButton und ermöglicht somit die korrekte Zuweisung oder besser lesbaren Quellcode zu schreiben.

FormControl.frameOptionButton(FormFrameOptionButton::None);

Mögliche Optionen:

FormFrameOptionButton::None
FormFrameOptionButton::Hide
FormFrameOptionButton::Check
FormFrameOptionButton::Radio

Es kommt nicht nur bei FormControls zu dieser Art von Problemen. Bei QueryRanges gibt es die Eigenschaft Status, auch hier kann es beim Setzen zu Schwierigkeiten dieser Eigenschaft mittels X++ kommen.

QueryBuildRange.Status(0);

Besser ist auch hier den sprechenden Parameter mit dem Richtigen Enum zu verwenden.

QueryBuildRange.Status(RangeStatus::Hidden);

Mögliche Optionen:

RangeStatus::Hidden
RangeStatus::Locked
RangeStatus::Open

Das waren erstmal die Sachen, die mir noch in Erinnerung sind. Es gibt wahrscheinlich einige andere Stellen, bei denen es Schwierigkeiten bereitet, den richtigen Parameter zu wählen.
Ich habe mit den genannten Enums bisher positive Erfahrungen sammeln können, ob es aber wirklich die Richtigen sind, wird man wohl nicht in Erfahrung bringen können.

Wednesday, June 11, 2008 7:50:59 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback


 Friday, June 06, 2008

So nach und nach erscheinen immer mehr Informationen über die Version 2009 von Dynamics AX im Internet.
Hier eine Liste der bereits verfügbaren Quellen:

Install Microsoft Dynamics AX 2009 (Informationen zur Installtion von Dynamics AX 2009)

Using Microsoft Dynamics AX 2009 (Allgemeine Informationen zu Dynamics AX 2009)

Microsoft Dynamics AX 2009 SDK

What's new for Microsoft Dynamics AX 2009 (Änderungen/Neuerungen als download)

The Microsoft Dynamics AX Enterprise Portal Blog (Informationen über das EP, direkt vom MS EP Team)

 

 

Friday, June 06, 2008 2:30:27 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  | 

 Thursday, May 29, 2008

Für jede Tabelle können Systemfelder wie Erstellt von, Geändert von, Erstellungsdatum, Erstellungszeit oder Änderungsdatum von Dynamics AX aktiviert werden.
Diese Felder werden durch Dynamics AX automatisch gefüllt. Wird zum Beispiel ein neuer Datensatz erzeugt, füllt Dynamics AX die Systemfelder mit den entsprechenden Daten.

Es gibt aber Situationen wo man selber Einfuß auf die Werte dieser Felder nehmen muss. Ein Beispiel hierfür könnte eine Datenübernahme sein, bei der die Informationen über den Ersteller oder das Erstellungsdatum des Datensatzes nicht verloren gehen dürfen.

Wie dies gehen kann zeigt dieses kleine Beispiel:

YourTable table;
;
ttsbegin;

//can only be called on server tier. -> method must be executed on server tier.
new SkipAOSValidationPermission().assert();
table.skipAosValidation(true);

table.YourField = "Value";

table.overwriteSystemfields(true);

//set your own values for the system fields.
table.(fieldnum(Table1, ModifiedDate)) = today() - 2;
table.(fieldnum(Table1, CreatedDate)) = today() - 5;
table.(fieldnum(Table1, CreatedBy)) = "TEST";

table.insert();
ttscommit;

table.skipAosValidation(false);

Allerdings können die Systemfelder nur beim Erstellen eines neuen Datensatzes "von Hand" festgelegt werden.

Wie das Ändern von Werten der Systemfelder bei bereits bestehenden Datensätzen geht demonstriert die Klasse "BatchRun", Methode "runJob" und "finishJob".
Kurz gesagt wird genau genommen der Datensatz nicht geändert, sondern es werden nur die Daten des bestehenden Datensatzes in den neuen Datensatz kopiert (mit newBuffer = oldBuffer.data()) und dann wie bereits beschrieben die Systemfelder mit eigenen Werten befüllt. Dann wird der bestehnde Datensatz gelöscht und der neue Datensatz in die Datenbank geschrieben.

Thursday, May 29, 2008 8:36:51 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Monday, May 19, 2008

So nach und nach sind immer mehr Informationen über die nächste Version von Microsoft Dynamics AX im Internet erhältlich, die einen ersten Einblick in das verschaffen, was alles an Neuerungen mit Dynamics AX 2009 kommt.

Arijit Basu hat in seinem Blog einen Artikel über einige der Änderungen gepostet.
Der Artikel beschreibt unter Anderem die Änderungen am Benutzerinterface, das neue Role Center, die neuen Workflow-Features sowie die Erweiterungen am Enterprise Portal.

Weiterhin ist auf Channel9 ist ein Video zu finden, welches das Enterprise Portal von Dynamics AX 2009 und dessen System-Architektur erläutert.
Es wird gezeigt, wie eigene Controls (mit ASP.NET) für das Enterprise Portal erstellt werden können und wie diese in das Enterprise Portal sowie das Role-Center eingebunden werden können.

 

Monday, May 19, 2008 7:41:46 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 | 

 Monday, May 05, 2008

Häufig werden Optionen (Ja/Nein-Fragen) in Microsoft Dynamics AX durch ein CheckBox Control und einem entsprechendem Feld einer Tabelle abgebildet.

Ein gutes Beispiel hierfür ist die Maske „Lagerparameter“, Reiter „Lagerungsdimensionen“. Hier kann eingestellt werden, welche Lagerungsdimension wo im System angezeigt werden soll. Zur Speicherung der gewählten Einstellungen wird die Tabelle „InventDimSetupGrid“ verwendet. Diese Tabelle enthält für jede Option jeweils ein Feld (abgeleitet vom Enum „NoYes“).
Diese Art der Speicherung von Optionswerten ist sicherlich sehr leicht zu verstehen und auch sehr einfach zu erstellen. Da aber für jede Option ein Feld in der Tabelle angelegt werden muss, kann dies relativ zeitaufwendig sein.

Es besteht aber die Möglichkeit, Optionswerte in nur einem Feld zu speichern. Diese Art der Speicherung kann unter Umständen sogar als die elegantere angesehen werden, da z.B. für einen Datensatz weniger Speicher in der Datenbank benötigt wird.

Um dies zu realisieren, wird als erstes ein Feld vom Typ „int“ in der Tabelle benötigt. Dieses Feld dient als Datenspeicher für alle benötigten Optionswerte (Ja oder Nein). Jedes Bit dieses „int“ Feldes stellt genau einen Optionswert und somit eine Option dar. Um nun die gewählten Werte der Optionen speichern zu können, müssen diese mit Bit-Operationen (right / left shift, binary and, etc.) in das „int“ Feld geschrieben werden.


Das Feld "bitMask" wird in dieser Darstellung als Datenspeicher der Optionswerte verwendet.


Auf einer Maske werden alle Optionen als einzelne CheckBoxen bereit gestellt.

Da diese Art der Speicherung in Dynamics AX nicht besonders oft verwendet wird und Quellcode oft mehr sagt als (nur) ein langer Artikel, habe ich ein kleines „Tutorial“ erstellt, um die benötigten Schritte zu beschreiben.

SharedProject_AKU_EnumControl_Frm.rar (2,03 KB)

Eine Erklärung aller Bit-Operatoren ist im Microsoft Dynamics AX Developer Center zu finden.

Monday, May 05, 2008 8:53:48 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Friday, May 02, 2008

Im Microsoft Dynamics AX Developer Center wurde für Dynamics AX Entwickler eine neue Webcast Serie gestartet.

Auszug:

"On this page you will find videos designed for all Microsoft Dynamics AX developers, from the novice to the professional. New videos are added regularly, so check back often."

Derzeit ist nur ein Webcast über "Dynamics Links between parent and child Forms" erhältlich.
Gilt zu hoffen, dass in der nächsten Zeit noch weitere nützliche Webcast folgen.

Friday, May 02, 2008 2:06:10 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Monday, April 28, 2008

Wie kann man zur Laufzeit den FormControlType einer beliebigen FormControl ermitteln?

Eine Methode auf der FormControl scheint nicht zu existieren, wie kann also der Typ einer FormControl mittels X++ ermittelt werden?
Mit Hilfe der Klasse SysFormRun ist dies möglich.

Hierzu ein kleines Stück Quellcode als Beispiel.

FormControlType formControlType;
;
formControlType = SysFormRun::controlType(classidget(MeineFormBuildControl));

Sollte keine FormControl als Parameter übergeben werden, wir eine Fehlermeldung generiert.
Nun kann entsprechend des ermittelten Types der FormControl weiter verfahren.




Monday, April 28, 2008 9:10:53 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback


 Monday, April 21, 2008

Eine Listbox ist einfach erstellt. Entweder man benutzt eine datengebundene Listbox, indem einfach die Datasource und DataField/ DataMethod im FormControl festgelegt wird oder man befüllt die Listbox manuell.

Das manuell befüllen der Listbox aus einem Tabellenfeld sieht dann beispielsweise so aus:

void initListBox()
{
counter elementCnt;
str elementStr;
;
meineListBox.clear();
for (elementCnt = 1; elementCnt <= conlen(Tabelle.ContainerFeld); elementCnt++)
{
meineListBox.add(onpeek(Tabelle.ContainerFeld, elementCnt));
}
}

Über die angegebene Methode werden alle Elemente des aktuellen Containers der Tabelle in die Listbox übertragen.

Hierbei bin ich auf ein seltsames Verhalten der Listbox gestossen.
Im aktuellen Fall ist die Darstellung der Listbox einwandfrei. Beim Datensatz wechsel habe ich die Listbox mit den neuen Werten befüllen können. Alles war gut.

Bis auf die Tatsache, dass ich bei der Auswahl in der Listbox nicht das ausgewählt Element angezeigt bekam. Es wurde immer das erste angezeigte Element der ListBox gewählt.

Grund hierfür war oder ist: Meine Listbox befindet sich in einer FormGroupControl, bei der FormGroupControl war automatisch eine DataSource hinterlegt. Das hat scheinbar zu Folge, dass das Event "SelectionChanged" der Listbox ignoriert wurde und ich somit immer der erste Wert über

meineListBox.getText(meineListBox.selection());

erhalten habe.

Nachdem die Datasource aus der FormGroupControl entfernt wurde, funktionierte alles wieder wie erwartet und gewünscht.


 

Monday, April 21, 2008 11:10:36 AM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback


 Monday, April 14, 2008

Um die FormDataSource einer Form anzusprechen benutzt man das Suffix _ds. So erhält man in der Maske CustTable mit

CustTable_ds

den Zugriff auf die FormDatasource der CustTable.

Es gibt aber noch andere Suffixe, die nicht so häufig verwendet werden und mir bis dato auch unbekannt waren.
So erhält man mit dem Suffix _q oder _qr Zugriff auf die Query oder QueryRun der aktuellen FormDataSource.

Das heisst, das mit

CustTable_q

die Query der FormDataSource CustTable und mit

CustTable_qr

die QueryRun der FormDataSource CustTable direkt angesprochen wird.

Hiermit kann man sich also verschiedene Deklarationen oder Zuweisungen sparen und hat direkten Zugriff auf diese Objekte.

  • FormDataSource = Zugriff auf den Datensatzpuffer
  • FormDataSouceName_DS = Zugriff auf FormDataSource
  • FormDataSourceName_Q = Zugriff auf Query der FormDataSource
  • FormDataSourceName_QR = Zugriff auf QueryRun der FormDataSource
Monday, April 14, 2008 9:52:06 AM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [3] - Trackback


 Saturday, April 12, 2008

Das Erstellen von Produktionsaufträgen sollte an sich kein Problem darstellen. Leider ist dem nicht ganz so.

Anders als in anderen Modulen (z.B. Aufträge) von Dynamics AX, existiert hierfür keine Klassenstruktur, welche die entsprechenden Funktionen bereit stellt.
Der Dynamics AX Standard erstellt Produktionsaufträge immer über die Maske „ProdTableCreate“. Es gibt aber Situationen, wo für die Erstellung eines Produktionsauftrages keine Maske verwendet werden kann. Ein Beispiel hierfür könnte eine Schnittstelle sein, welche über eine Textdatei die zu produzierenden Waren einließt und entsprechende Produktionsaufträge im System generiert.

Die Frage ist nun, wie erstellt man Produktionsaufträge per Quellcode, damit diese auch „richtig“ im System erzeugt werden (inkl. Stückliste, Arbeitsplan und Lagerbuchung).

  • Zuerst muss der Produktionsauftrag mit den Daten des zu produzierenden Artikels initialisiert werden.
  • Weiterhin müssen Produktionsmenge und Lieferdatum festgelegt werden.
  • Ebenfalls sind die zu verwendende Stückliste und der Arbeitsplan zu definieren.
  • Und als letzter Schritt muss der Produktionsauftrag noch erzeugt werden.

Hierbei gilt es aber zu beachten, dass die Erstellung (Speichern in der Datenbank) nicht mit der Methode „insert“ der Tabelle „ProdTable“ geschieht, sondern dass hierfür die Klasse „ProdTableType“ und deren Methode „insert“ verwendet wird. Nur so wird die entsprechende Lagerbewegung / Lagerbuchung im System erzeugt und wenn notwendig Referenzen zu einem Verkaufsauftrag oder einer anderen Produktion hinterlegt.

Um dies zu veranschaulichen ein kurzes Beispiel, in welchem ein neuer Produktionsauftrag erstellt wird.

static void CreateProductionOrder(Args _args)
{
    //Die zu produzierende Menge
    ProdQtySched productionQty = 1;
    //Der zu produzierende Artikel
    ItemId productionItem = "Artikelnummer";

    ProdTable prodTable;
    InventTable inventTable;
    ;
    inventTable = InventTable::find(productionItem);

    //Initialisierung des Produktionsauftrags
    prodTable.initValue();
    prodTable.ItemId = inventTable.ItemId;
    prodTable.initFromInventTable(inventTable);

    //Lieferdatum festlegen
    prodTable.DlvDate = today();

    prodTable.QtySched = productionQty;
    prodTable.RemainInventPhysical = prodTable.QtySched;

    //Die zu verwendende Stückliste und Arbeitsplan bestimmen
    prodTable.initRouteVersion();
    prodTable.initBOMVersion();

    //Produktionsauftrag erstellen
    prodTable.type().insert();
}

Selbstverständlich sind auch weitere Angaben bei der Erstellung des Produktionsauftrags möglich.
Z.B. kann ein Produktionsauftrag auch aus einer Verkaufsauftragsposition erzeugt werden (bei Verwendung der Methode "initFromSalesLine").

Saturday, April 12, 2008 2:30:59 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Friday, April 04, 2008

Mit Hilfe von SysDictClass kann man wertvolle Erkenntnisse über Klassen gewinnen und abfragen.
Ich habe einige Dinge, die ich am häufigsten gebrauche, anhand einfacher Beispiele zusammengetragen.

Dieses kleine Stück Quellcode ermittelt alle abgeleiteten Klassen der Klasse FormControl.

SysDictClass formCtrlClss;
ListEnumerator listEnumerator;
;

formCtrlClss = new SysDictClass(classnum(FormControl));

listEnumerator = formCtrlClss.extendedBy().getEnumerator();

while (listEnumerator.moveNext())
{
formCtrlClss = new SysDictClass(listEnumerator.current());
print formCtrlClss.name();
}
pause;

Diese Funktionalität wird auch im Standard von Microsoft Dynamics AX verwendet, um alle möglichen Funktionen der Stabelverarbeitung abzufragen/ zu ermitteln.

Man kann natürlich auch ermitteln, von welcher Klasse(n) die aktuelle Klasse abgeleitet ist.

SysDictClass dictClass = new SysDictClass(classnum(SalesFormLetter_Invoice));
;
while (dictClass.extend())
{
dictClass = new SysDictClass(dictClass.extend());
print dictClass.name();
}
pause;

Um zu ermitteln, ob eine Klasse von einer bestimmten Klasse abgeleitet wurde, muss folgendes geschrieben werden.

SysDictClass dictClass = new SysDictClass(classnum(SalesFormLetter_Invoice));
;
// Überprüfen ob die Klasse von der Klasse Object abgeleitet ist
print dictClass.isExtending(classnum(Object));
// Überprüfen ob die Klasse von der Klasse FormControl abgeleitet ist
print dictClass.isExtending(classnum(FormControl));
pause;

Es kann auch auf die Methoden der Klasse zugegriffen werden.

SysDictClass dictClass = new SysDictClass(classnum(SalesFormLetter_Invoice));
;
//Statische Methode der Klasse SalesFormLetter_Invoice aufrufen, wenn vorhanden
if (dictClass.hasStaticMethod("Description"))
print dictClass.callStatic("Description");

//Methode der Klasse SalesFormLetter_Invoice aufrufen, wenn vorhanden
if (dictClass.hasObjectMethod("canGoBatchJournal"))
print dictClass.callObject("canGoBatchJournal", dictClass.makeObject());

pause;

Das ist nur ein Teil der Funktionen, die ich persönlich am Wichtigsten empfinde und hin und wieder benötige.

Friday, April 04, 2008 9:01:19 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback


 Tuesday, April 01, 2008

Heute habe ich eine Email erhalten, in der folgendes zu lesen war:

"...Herzlichen Glückwunsch! Wir freuen uns, Ihnen den Microsoft MVP Award 2008 verleihen zu können..."

Mir wurde der MVP für Microsoft Dynamics AX verliehen. Wow!

Ich möchte mich an dieser Stelle bei allen Teilnehmern der Microsoft Dynamics AX Community bedanken, die es überhaupt ermöglich haben, dass mir dieser Titel verliehen wurde.
Besonderen Dank möchte ich an Helmut Wimmer [axaptafreak] und Mathias Füßler [jinx, meinen Co-Autor, oder bin ich seiner? :-)] aussprechen, die mich immer tatkräftig unterstützt haben.

Natürlich werde ich jetzt nicht aufhören, weiterhin Hilfestellung bei Fragen zum Thema Dynamics AX zu geben. Ich sehe diese Auszeichnung als Ansporn, mich auch weiterhin aktiv in der gesamten Dynamics AX Community zu beteiligen.

 

Nochmals, danke an euch alle.

Tuesday, April 01, 2008 9:44:51 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [1] - Trackback
 | 

 Thursday, March 27, 2008

In Foren, Newsgroups oder auch in persönlichen Gesprächen ergibt sich oft die Frage, wo man anfangen soll/kann um den Umgang mit Microsoft Dynamics AX zu lernen.

Gerade für Einsteiger oder Anfänger ist es machmal schwer, die Informationen zu finden, die gerade benötigt werden. Dank der Dynamics AX Community, welche zum Glück immer größer und besser wird, stellt das Internet mit seinen vielen Dynamics AX Blogs, Foren, Newsgroups und nicht zuletzt dem Microsoft Dynamics AX Developer Center eine sehr gut Informationsquelle dar.

Trotzdem hört man oft die Frage, ob es denn keine Bücher über Microsoft Dynamics AX gibt. Um etwas mehr Klarheit zu schaffen, welche Bücher über Microsoft Dynamics AX erhältlich sind, hier eine Liste aller Bücher die mir bekannt sind:

Thursday, March 27, 2008 8:00:07 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [1] - Trackback
 | 

 Wednesday, February 27, 2008

Ich habe die Erfahrung gemacht, dass FormControls, die zur Laufzeit direkt auf dem Design der Form erzeugt wurden, sich meisst am rechten Rand befinden.

Sollen diese FormControls nun aber nicht am Rechten, sondern am Linken Rand erscheinen, kann man dieses natürlich schon beim Erzeugen dieser Controls mittels

FormGroupControl.leftMode(FormLeft::LeftEdge) 

festlegen.

Die Probleme treten dann auf, wenn mehrere FormControls erzeugt werden und diese am Linken Rand erscheinen sollen.

Werden nun alle FormControls mit FormControl.leftMode(FormLeft::leftEdge) an den Linken Rand verschoben, erhält man dann unter Umständen erstaunliche Ergebnisse – nur nicht die Gewünschten.

Mit Hilfe der Methode

FormControl.moveControl(int _controlId [, int _insertAfterId = 0])

kann jedes Control an eine beliebige Stelle verschoben werden. Der Übergabeparameter ist hier die ID des zu verschiebenen FormControls. Diese Methode ist nur bei Container Controls, wie FormGroupControl, FormGridControl oder auf dem Design der Form verfügbar.

Wird nur 

ÜbergeordneteFormControl.moveControl(ControlIDderzuveschiebenenControl)

verwendet, wird das Control nur nach links (an die erste Position des Übergeordneten Controls) verschoben.

Soll das FormControl hinter einem bestimmten FormControl innerhalb desselben übergeordneten FormControls platziert werden, wird

Übergeordnete.moveControl(ControlIDderzuveschiebenenControl, ControlIDhinterDerdieControlangefügtwerdensoll)

benutzt.

Mit .moveControl() können auch FormControls in das übergeordnete Control eingefügt werden, die vorher nicht in dem übergeordneten Control enthalten waren. 

Anbei eine einfache Form, die schon ein FromGroupControl mit zwei Controls enthält,  bei der zur Laufzeit zwei neue Controls hinzugefügt werden und die mittels Button nach links verschoben werden.

FormDynamicControlsMove.zip (2,55 KB)
Wednesday, February 27, 2008 8:40:13 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [2] - Trackback


 Tuesday, February 12, 2008

In Kürze soll ein neues Buch über Microsoft Dynamics AX erscheinen. Im Schwerpunkt soll es sich mit dem Thema Qualitätssicherung beschäftigen. Titel des Buchs ist "Quality Assurance for Dynamics AX-Based ERP Solutions".

Ein Auszug der einzelnen Themenpunkte:

  • Customization Best Practices backed by theory
  • Learn rapidly how to test Dynamics AX applications
  • Verify Industry Builder Initiative-compliance of ERP software
  • Get ready-made testing templates
  • Code, design, and test a quality Dynamics AX-based ERP solution

     

    Genaueres über das Buch kann man hier erfahren.

  • Tuesday, February 12, 2008 8:10:36 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
     | 

     Saturday, January 26, 2008

    Das Buchen von Bestellungen in Microsoft Dynamics AX geschieht über die Klasse „PurchFormLetter“ bzw. einer ihrer konkretisierten (abgeleiteten) Klassen. Jeder Buchungstyp (z.B. Bestätigung oder Rechnung) ist durch eine eigene Klasse abgebildet, welche von der Basisklasse „PurchFormLetter“ abgeleitet ist (siehe Abbildung).

    Abbildung 1 - Klassenhierarchie der Klasse „PurchFormLetter“

    Vergleicht man die Klassenhierarchie der „PurchFormLetter“ Klassen mit der Klassenhierarchie der „SalesFormLetter“ Klassen, so ist zu erkennen, dass auch das Buchen von Bestellungen vom Prinzip her genau so funktioniert wie das Buchen von Aufträgen (Vergleiche hierzu: Microsoft Dynamics AX API – Teil 3 „Buchen von Aufträgen“).

    Deswegen sind auch für das Buchen von Bestellungen im Wesentlichen nur zwei Schritte notwendig.

    1. Über die Methode „construct“ der Klasse „PurchFormLetter“ ein dem Buchungstyp einsprechendes Objekt erzeugen.
    2. Über den Aufruf der Methode „update“ die Bestellung buchen.

    Hierzu ein Beispiel (Buchen des Lieferscheins für eine Bestellung):

    static void PurchPostPackingSlip(Args _args)
    {
       PurchFormLetter purchFormLetter;
       PurchTable purchTable;
       PurchId purchId;
       Num packingSlipId;
       ;
       //Angabe der Bestellung, für welche der Lieferschein gebucht werden soll.
       purchId = "00244_049";
       purchTable = PurchTable::find(purchId);

       //Bestimmen des Buchungstyps durch Angabe des DocumentStatus (Lieferschein).
       purchFormLetter = PurchFormLetter::construct(DocumentStatus::PackingSlip);

       //Festlegen der externen Lieferscheinnummer.
       packingSlipId = "EXT-100155L";

       //Buchen des Lieferscheins.
       purchFormLetter.update(purchTable,
                              packingSlipId,
                              SystemDateGet(),
                              PurchUpdate::All,
                              AccountOrder::Auto,
                              NoYes::No,
                              NoYes::No,
                              NoYes::No,
                              NoYes::No);
    }

    Einziger Unterschied zu den Auftragsbuchen ist, dass bei der Buchung einer Bestellung die „externe“ Nummer des Belegs (Lieferscheinnummer, Rechnungsnummer, etc.) angegeben werden muss.

    Analog zu den Auftragsbuchen, sind auch beim Buchen von Bestellungen umfangreichere oder etwas speziellere Buchungsszenarien möglich (Vergleiche hierzu: Microsoft Dynamics AX API – Teil 3 „Buchen von Aufträgen“).

    Saturday, January 26, 2008 3:12:18 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
     | 

     Thursday, January 17, 2008

    Alexei Eremenko hat auf seinem Blog einige Artikel über die kommende Version von Microsoft Dynamics AX und dessen neue Features veröffentlicht. Da diese leider in Russich geschrieben sind, hier eine kurze Zusammenfassung:

    • Aus Microsoft Dynamics AX 5.0 wird Microsoft Dynamics AX 2009.
    • Geänderte Benutzeroberfläche (Office 2007 Style, inkl. Ribbon's).
    • Neue "Funktion" Rollcenter, die dem Benutzer schnellen Zugriff auf die, für seine Arbeit, relevanten Daten geben soll.  
    • Unterstützung des UNION Befehl's für SQL Abfragen, bei Verwendung der Query-Klassen.
    • Neuer Exception-Typ "DublicateKeyException" zur Ausnahmebehandlung wenn ein Datensatz schon besteht.
    • Die SQL DML bulk Anweisungen erlauben die Verwendung von Inner- und Outer-Joins.
    • Zugriff auf das Ergebnis der "update_recordset" Anweisung, um zu bestimmen wie viele Datensätze durch die Operation geändert wurden.
    • CrossCompany-Unterstützung für Datenbankabfragen (Daten aus unterschiedlichen Mandaten können in einer SQL Anweisung behandelt werden).

    Wer die orginalen Artikel einmal selber lesen möchte, findet die einzelnen Blog-Posts hier:
    (Die Links verweisen auf die Übersetzung der Artikel ins Englische)

    Thursday, January 17, 2008 8:07:08 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
     | 

     Tuesday, January 15, 2008

    Wird in einem Bericht die Fetch Methode überschrieben sollte man drauf achten, dass die Standardfunktionen auch noch richtig funktionieren. Ein gutes Beispiel hier sind die Einstellungen im Seitenbereich.

     

    Wird in der Fetch Methode nicht super() aufgerufen und mit einer individuellen Abfrage gearbeitet, welche die Daten dem Bericht übergibt, werden die Einstellungen in der Gruppe "Seitenbereich" meist ignoriert. Es werden dann zwar nur die angebenden Seiten gedruckt, die Abfrage wird aber weiter durchgeführt. Das führt dann dazu, dass immer abgewartet werden muss bis die komplette Abfrage ausgeführt wurde, obwohl man evtl. nur einen kleinen Bruchteil der Daten (wie z.B. die erste Seite) benötigt.

    Das kann man ganz einfach verhindern indem in der Fetch Methode abgefragt wird, ob die Daten an den Bericht gesendet wurden. Hierzu wird die send(...) Methode benutzt.

    public boolean fetch()
    {
    Query queryBuild;
    QueryRun queryRunBuild;
    InventTrans iTrans;
    ;
    if (this.prompt())
    {
       queryBuild = this.query();
       queryRunBuild = new QueryRun(queryBuild);

       while (queryRunBuild.next())
       {
       iTrans = queryRunBuild.get(tablenum(InventTrans));

       // Wurde der aktuelle Datensatz ausgeben
       if (!this.send(iTrans))
          return true;
       }
    }
    return true;
    }

    Es muss also immer überprüft werden, ob die Daten gedruckt werden oder nicht. Das passiert mit:

    if (!this.send(meineDaten))
       return true;

    Tuesday, January 15, 2008 11:30:17 AM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] -