...feel the spirit of Microsoft Dynamics AX RSS 2.0
 Wednesday, November 05, 2008

Microsoft Dynamics AX 2009 bietet die Möglichkeit, den Verlauf eines Dokuments, welches über das AIF exportiert oder importiert wurde zu betrachten.
Dies war auch schon mit Microsoft Dynamics AX 4.0 möglich.

Über die Maske "Dokumentverlauf" können alle Dokumente/Nachrichten eingesehen werden, welche über das AIF verarbeitet wurden.
Über den Button "Korrelation" ist es sogar möglich, die von der Verarbeitung (schreiben, ändern, lesen, etc.) betroffenen Datensätze anzuzeigen.

So ist es zumindest in der Theorie.
In der Praxis sieht es leider etwas anders aus. Nach einem Klick auf den Button "Korrelation" öffnet sich leider nicht wie erwartet die Maske "Dokumentkorrelierung".
Stattdessen wird der Debugger (wenn installiert) geöffnet und die Fehlermeldung ausgegeben, dass ein Objekt nicht über die Methode "extendedTypeId" verfügt.

So wie es scheint, hat sich in den Quellcode ein kleiner Fehler eingeschlichen, welcher dazu führt, dass die Maske "Dokumentkorrelierung" niemals geöffnet werden kann.
Nach einem Vergleich der Funktionalitäten zwischen Dynamics AX 4.0 und Dynamics AX 2009 kann dieses Verhalten (der Fehler) aber wie folgt beschrieben behoben werden.

  1. AOT öffnen und zu der Tabelle "AifCorrelation" navigieren.
  2. Den Quelltext der Methode "displayEntityKey" anzeigen lassen bzw. diese für die Bearbeitung öffnen.
  3. Folgende Quelltextzeile suchen:
    dictField = new DictField(entityKey.parmTableId(), enumerator.currentKey());
  4. Dieses Zeile abändern in:
    dictField = new SySDictField(entityKey.parmTableId(), enumerator.currentKey());

Nach dieser kleinen Quelltextänderung sollte alles wie erwartet funktionierten und die Maske "Dokumentkorrelierung" mit den entsprechenden Datensätzen angezeigt werden.

Wednesday, November 05, 2008 7:06:49 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  | 

 Saturday, August 30, 2008

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
 | 

 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, 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 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
 |  | 

 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
 |  | 

 Wednesday, January 02, 2008

Für ein Grid-Control kann über die Einstellung "MultiSelect" gesteuert werden, od dieses Control die Auswahl von mehr als einem Datensatz erlaubt.

Gültige Einstellungen sind:
Yes - Es können mehrere Datensätze ausgewählt werden.
No - Es kann immer nur ein Datensatz ausgewählt werden.

GridSelectedOneRecord
Auswahl eines Datensatzes

GridSelectedMultipleRecords
Auswahl mehrerer Datensätze

Zugriff auf die aktuelle Selektion (einer oder mehrere) erhält man wie folgt beschrieben:

Ist nur ein Datensatz markiert, bzw. soll mit einfacher Auswahl gearbeitet werden (MultiSelect = No), kann der ausgewählte Datensatz über den aktuellen DataSource-Cursor der Grid-Control DataSource ermittelt werden.
Der DataSource-Cursor steht immer auf dem zu letzt ausgewählten Datensatz eines Grid-Control's.

Beispiel:

void clicked()
{
   //CustTable ist die DataSource des Grid-Controls 
   ;
   //Datenoperationen für den Datensatz ausführen.
   //Do something....
   info(CustTable.AccountNum);   

   //Angezeigte Datensätze im Grid Control aktualisieren
   element.lockWindowUpdate(true);
   CustTable_ds.research();
   element.lockWindowUpdate(false);
}

Soll eine Mehrfachauswahl möglich sein, reicht der Zugriff auf den aktuellen DataSource-Cursor nicht mehr aus. Um alle ausgewählten Datensätze der DataSource zu erhalten, muss diese mit einer Schleife unter Verwendung der Methoden "getFirst" und "getNext" durchlaufen werden. Hilfreich hierbei ist die Methode "anyMarked", mit welcher ermittelt werden kann ob mehrere Datensätze ausgewählt sind oder nicht.

Auch hierfür ein Beispiel:

void clicked()
{
   CustTable selectedCustTable;
   Common currentRecord;
   ;
   if (CustTable_ds.anyMarked()) //Es sind meherer Datensätze selektiert.
   {
      //Ersten selektierten Datensatz ermitteln.
      selectedCustTable = CustTable_ds.getFirst(1);

      while(selectedCustTable)
      {
         //Datenoperationen für den Datensatz ausführn.
         //Do something....
         info(selectedCustTable.AccountNum);

         //Nächsten selektieren Datensatz ermitteln.
         selectedCustTable = CustTable_ds.getNext();
      }
   }
   else //Nur ein Datensatz ist selektiert.
   {
      //Selektierten Datensatz ermitteln.
      currentRecord = CustTable_ds.cursor().data();
      selectedCustTable = CustTable_ds.cursor();

      //Datenoperationen für den Datensatz ausführen.
      //Do something....
      info(selectedCustTable.AccountNum);
   }

   //Angezeigte Datensätze im Grid Control aktualisieren   
   element.lockWindowUpdate(true);
   CustTable_ds.research();
   CustTable_ds.findRecord(currentRecord);
   CustTable_ds.refresh();
   element.lockWindowUpdate(false);
}

Der Quellcode des Beispiels:

Form_GridSelectedRecords.xpo (6.2 KB)

Wednesday, January 02, 2008 9:39:57 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [2] - Trackback
 | 

 Thursday, December 27, 2007

Von Zeit zu Zeit ist es hilfreich sich den Abfragebefehl, der durch eine Query erzeugt wird, zur Laufzeit anzusehen.

Leider unterstützt der Debugger von Microsoft Dynamics AX das Debuggen von Querys nicht. Ein Datenbanktrace durchzuführen ist auch nicht immer sinnvoll, da man durch das Traceprotokoll keine direkte "Verbindung" von Abfragebefehl und Query einsehen kann.

Einen Workaround hierfür gibt es aber.

Die Methode "toString" einer Query DataSource gibt den Abfragbefehl, der durch die Query an die Datenbank geschickt wird, zurück. Dieser kann dann, durch die statischen Methoden des "Debug" Objekts, im Debuggers ausgegeben bzw. angezeigt werden.

Ein Beispiel:

debug::printDebug(query.dataSourceTable(tablenum(CustTable)).toString())

Weiterhin ist unter http://www.axaptapedia.com/DEV_QueryBrowser ein Tool für die Analyse von Querys erhältlich. Dieses Tool funktioniert ähnlich wie der Tablebrowser von Dynamics AX. Mit dem Tool können nicht nur der Abfragebefehl, der durch eine Query erstellt wird, sondern auch die von der Query ermittelten Daten betrachtet und ausgewertet werden.

Eine ausreichende Beschreibung zur Verwendung des Tool's ist ebenfalls auf Axaptapedia (siehe Link weiter oben) zu finden.

 

Thursday, December 27, 2007 3:40:11 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Wednesday, December 19, 2007

Einen ersten Ausblick auf die "neuen" Quellcodeverwaltungsfeatures in Microsoft Dynamics AX 5.0 zeigt der Screencast "Version control in MorphX" auf Channel9.

http://channel9.msdn.com/Showpost.aspx?postid=367024

Ich zitiere:
"This screencast is a preview of the version control system integration options in the next release of MorphX - the IDE of Dynamics AX.
It shows a side-by-side comparison of the integration options with Team Foundation Server, Visual Source Safe, and MorphX VCS. 
The latter is a simple, yet powerful alternative without any additional infrastructure requirements. The last half of the screencast gives a demonstration of MorphX VCS."

Durch die neuen Features die MorphX VCS mit sich bringt, sowie die Möglichkeit Visual Studio Team System, oder genauer der Team Foundation Server, (nicht nur) als Quellcodeverwaltung zu verwenden, sollte nun für jeden ein "passendes" Quellcodeverwaltungsystem bereit stehen.

Vielen Dank an dieser Stelle an Michael Fruergaard Pontoppidan (http://blogs.msdn.com/mfp/default.aspx) für diesen und die bisherigen Screencasts über Microsoft Dynamics AX. 

Wednesday, December 19, 2007 8:24:13 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  |  |  | 

 Friday, December 14, 2007

Die Version 4.0 von Microsoft Dynamics AX enthält eine neue Funktion ("Nach Raster filtern" oder "Filter by Grid") mit der die Datenfilterung direkt in einem Grid ermöglicht wird.

Die Funktion "Nach Raster Filtern" kann entweder über die Tastenkombination "STRG + G", über die Symbolleiste oder den Menüpunkt "Bearbeiten - Filtern - Nach Raster filtern" aktiviert werden.

Das "Nach Raster filtern" ist per Standard deaktiviert und muss somit immer manuell durch den Benutzer aktiviert werden. Leider wird in den benutzerspezifischen Einstellungen für eine Maske nicht gespeichert ob das "Nach Raster filtern" aktiviert oder deaktiviert ist. Somit muss die Funktion, wenn diese verwendet werden soll, für jede Maske und nach jedem Schließen einer Maske erneut aktiviert werden.

Soll nun für eine Maske die Funktion "Nach Raster filtern" dauerhaft aktiviert sein, ist eine kleine Anpassung im Quelltext der Maske notwendig.

Als erstes muss die Eigenschaft "AutoDeclaration" für das Grid Control, bei welchem die Funktion "Nach Raster filtern" aktiviert werden soll, auf "Yes" gesetzt werden. Weiterhin muss die Methode "run" der Maske, nach dem Aufruf von "super", um diese Quellcodezeilen ergänzt werden:

Grid.enter();
this.task(2855);

"Grid" ist hierbei der Name des Grid Controls, für welches die Funktion "Nach Raster filtern" aktiviert werden soll.

Soll gleichzeitig noch ein Filter für die Datensätze aktiviert werden, erfolgt dies über die Ranges der Datasource-Querys. Hierbei kann wie gewohnt eine Range für die entsprechende Query definiert werden. Wird die Range für ein Feld gesetzt, welches in dem Grid Control angezeigt wird, wird der Wert der Range entsprechend in der "Filterzeile" angezeigt.

Friday, December 14, 2007 6:38:30 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  | 

 Friday, November 02, 2007

Man kann viel darüber lesen, dass es ab der Version 4.0 von Microsoft Dynamics AX möglich ist auch mit .NET Objekten innerhalb von Dynamics AX zu arbeiten. Leider fehlt es oft an konkreten Beispielen, die einen ersten Überblick geben, wie man was machen muss.

Jeder der sich mit dem Thema schon einmal auseinander setzen musste, wird mit hoher Wahrscheinlichkeit auf eines dieser „Probleme“ gestoßen sein:

  • Wie kann ein Datum zwischen Dynamics AX und .NET ausgetauscht werden?
  • Wie kann die aktuelle Zeit an ein .NET Objekt übermittelt werden?
  • Wie rufe ich eigentlich Werte eines .NET Enums ab?

Diese Liste könnte man noch beliebig erweitern.

Damit man einen kleinen Anhaltspunkt hat, wie die „Arbeit“ mit .NET Objekten gehen kann, soll dies anhand des Beispiels „Anlegen eines Termins in Outlook“ vorgestellt werden.

Zuerst muss eine Referenz zur der .NET Assembly erstellt werden, welche im Weitern verwendet werden soll. Hierzu muss im AOT der Zweig „References“ gewählt werden und über das Kontextmenü „Verweis hinzufügen“ ausgewählt werden.

In der neuen Maske wird die Assembly ausgewählt zu der eine Referenz erstellt werden soll. Hierzu navigiert man zu der entsprechenden Assembly und wählt diese über „Auswählen“ aus. Mit dem Drücken von „OK“ werden zu den ausgewählten Assemblys die Referenzen erstellt.

Bei diesem Beispiel wurde eine Referenz zur Assembly „Microsoft.Office.Interop.Outlook“ erstellt.

Ab jetzt können alle .NET Objekte, die in dieser Assembly enthalten sind, in Dynamics AX verwendet werden.

Wie dies im Einzelnen geschieht kann dem folgenden Beispiel entnommen werden, wo demonstriert wird, wie man aus Dynamics AX heraus ein Outlook Termin erstellt werden kann.

static void CreateOutlookAppointment(Args _args)
{
   //.NET (CLR) Datentypen
   InteropPermission permission;
   Microsoft.Office.Interop.Outlook._Application outlookApplication;
   Microsoft.Office.Interop.Outlook._AppointmentItem outlookAppointment;
   Microsoft.Office.Interop.Outlook.OlItemType outlookItemType;
   System.DateTime appointmentStartTime;
   System.DateTime appointmentEndTime;

   //Dynamics AX Datentypen
   Date1980 startDate, endDate;
   TimeExpected startTime, endTime;
   ;
   permission = new InteropPermission(InteropKind::ClrInterop);
   if (permission == null)
   {
      return;
   }
   //”Unsichere” Aufrufe starten
   permission.assert();

   //Das Outlook Application Objekt instanzieren
   //(Beispiel für Erstellung eines .NET Klassenobjekts)
   outlookApplication = new Microsoft.Office.Interop.Outlook.ApplicationClass();

   //Gewünschten Outlook-Elementtyp auswählen
   //(Beispiel für Zuweisung eines .NET Enum Wertes)
   outlookItemType = ClrInterop::parseClrEnum('Microsoft.Office.Interop.Outlook.OlItemType', 'olAppointmentItem');

   //Gewünschten Outlook-Elemnttyp erstellen (Termin)
   outlookAppointment = outlookApplication.CreateItem(outlookItemType);

   //Den Betreff zuweisen
   //(Beispiel für Zuweisung eines "einfachen" Wertes)
   outlookAppointment.set_Subject("Ein Test aus Dynamics AX");

   startDate = str2Date("25.10.2007", 123);
   startTime = str2Time("10:30:00");
   //"Beginnt um" zuweisen
   //(Beispiel für Konvertierung Dynamics AX Datum/Zeit -> .NET DateTime)
   appointmentStartTime = System.Convert::ToDateTime(strfmt("%1 %2", startDate, time2str(startTime, 0, 0)));
   outlookAppointment.set_Start(appointmentStartTime);

   endDate = str2Date("26.10.2007", 123);
   endTime = str2Time("11:00:00");
   //"Endet um" zuweisen
   appointmentEndTime = System.Convert::ToDateTime(strfmt("%1 %2", endDate, time2str(endTime, 0, 0)));
   outlookAppointment.set_End(appointmentEndTime);

   //Den Termin speichern (ab jetzt ist der Termin in Outlook zu sehen)
   outlookAppointment.Save();

   //”Unsichere” Aufrufe beenden
   CodeAccessPermission::revertAssert();
}

Friday, November 02, 2007 6:06:11 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |  |  | 

 Tuesday, October 16, 2007

Nicht nur in der Microsoft Dynamics AX Entwicklungsumgebung hat man die Möglichkeit mittels Drag & Drop Veränderungen vorzunehmen. Es ist recht einfach diese Funktion auch in der Applikation verfügbar zu machen. Hier gibt es im Standard nur sehr wenige Beispiele und die es gibt verwenden alle einen Tree oder eine List als Ziel.  Ich zeige diese Drag & Drop Funktion mit zwei Grids als Quelle und als Ziel anhand eines einfachen Beispiels.

Um diese Drag & Drop Funktion in einer Maske zu implementieren braucht es zuerst einmal zwei Datenquellen, eine als Quelle und eine als Ziel. Um die Daten auf der Maske anzuzeigen hab ich jeweils ein Grid gewählt. Als Beispiel hab ich einfach eine neue Maske erstellt, die als Datenquelle SalesLine (Zieldatenquelle) und Inventtable (Quelldatenquelle) beinhaltet. Die Anzeige der Daten spielt hier nur eine untergeordnete Rolle.



Nun muss nur noch auf den Grid (SalesLineGrid und InventTableGrid) die Eigenschaft DragDrop auf „Manual“ gestellt werden um Drag & Drop zu aktivieren (Es gibt nur die Möglichkeit auf None oder Manual).




Nun kann man auf der Maske schon die Daten mittels Drag & Drop verschieben. Die Funktion zum Einfügen des Datensatzes aus der Quelle ins Ziel ist natürlich noch nicht vorhanden, man kann aber das optische Verhalten (Datensatz in ein anderes Grid ziehen) schon betrachten. 

Nun müssen noch einige Ereignisse überschrieben werden um die Funktion zu implementieren.

dragOver = hier wird festgelegt welche Aktion durchgeführt wird (Move, Copy, None).

  • Move = Mouse+SHIFT Taste (Default)
  • Copy = Mouse+STRG Taste

So kann z.B. festgelegt werden, das auf dem Grid “InventTableGrid” kein Drag&Drop möglich sein soll. Hierzu wird diese Methode wie folgt überschrieben.

public FormDrag dragOver(FormControl _dragSource, FormDrag _dragMode, int _x, int _y)
{
   FormDrag ret;

   // ret = super(_dragSource, _dragMode, _x, _y);
   // Kein Drag&Drop zulassen
   ret = FormDrag::None;

   return ret;
}

Es können hier natürlich alle möglichen Überprüfungen stattfinden um festzulegen, wann welche Option erlaubt/ nicht erlaubt ist. 

Um die Funktion nun abzuschließen fehlt nur noch das erzeugen des Datensatzes auf der Tabelle SalesLine. Hierzu wird die Method „Drop“ auf dem Grid „SalesLineGrid“ überschrieben.

Um Auftragspositionen aus den Artikelstamm anzulegen könnte die Methode so ausehen

public void drop(FormControl _dragSource, FormDrag _dragMode, int _x, int _y)
{
SalesLine sLine;

//Ist Quelle identisch mit aktuellem Grid (SalesLineGrid)
if (_dragSource.equal(this))
{
//TODO: Hier kann bspw. das Verschieben der Auftragsposition implementiert werden (LineNum)
}
//Ist Quelle InventTableGrid
else if (_dragSource.name() == InventTableGrid.name())
{
// Nur Aktion ausführen wenn Copy oder Move
if(_dragMode == FormDrag::Copy || _dragMode == FormDrag::Move)
{
sLine.initValue();
sLine.SalesId = SalesID.valueStr();
sLine.initFromSalesTable(SalesTable::find(salesLIne.SalesId));
sLine.ItemId = inventTable.ItemId;
sLine.initFromInventTable(InventTable);
sLine.createLine(NoYes::Yes, NoYes::Yes, NoYes::Yes, NoYes::Yes, NoYes::Yes, NoYes::Yes);
salesLine_ds.executeQuery();
}
}
super(_dragSource, _dragMode, _x, _y);
}


In der Maske kann nun aus dem Artikelstamm eine neue Auftragsposition mittels Drag & Drop erstellt werden. Hierfür habe ich noch eine Vorbelegung/ Einschränkung auf die Aufragsnummer vorgenommen um die Auftragsposition erzeugen zu können.

Die Drag & Drop Funktion kann nur von der Artikeltabelle zu den Auftragsposition durchgeführt werden. Umgekehrt funktioniert das nicht, was man auch optisch sehen kann. (Screenshots haben aus irgendwelchen Gründen nicht funktioniert)

Ein weiterer Vorteil bei der Drag & Drop Funktion ist, das Sie auch Maskenübergreifend funktioniert. Hierfür sind gar keine weiteren Änderungen notwendig. Es muss in der Drop Methode, wenn dort Überprüfungen stattfinden, nur der Ursprung auch erlaubt, bzw. mit berücksichtigt wurden sein.

Die gerade erstellte Maske lässt sich schon jetzt zweimal öffnen um dort von der einen zu der anderen Maske Daten mittels Drag & Drop zu übertragen (Inventtable -> SalesLine)

Durch kleine Änderungen kann diese Funktion auch aus der Artikelmaske ausgeführt werden.

Hierzu muss in der Artikelmaske einfach auf dem Grid DragDrop auf Manual gesetzt werden und die Drop Funktion in der neu erstellten Maske leicht angepasst werden.

Beispielprojekte für das einfache Drag&Drop innerhalb einer Maske und die kleine Erweiterung für das Drag&Drop aus der Artikelmaske herraus gibts auch wieder.

In der Maske "BOMDesigner" kann die Drag&Drop Funktion noch anhand einer Baumstruktur (FormTreeControl) als Quelle bewundert werden.

Beispiel SimpleDragAndDrop
Form_SimpleDragDrop.rar (1,99 KB)

Beispiel Projekt für Drag&Drop aus Artikelmaske in die Maske SimpleDragAndDrop
SharedProject_DragAndDropInventTable.rar (11,71 KB)
Tuesday, October 16, 2007 10:27:37 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [0] - Trackback
 | 

 Wednesday, September 12, 2007

Erhält man bei seiner täglich Arbeit die Fehlermeldung „Ein Befehl der Datendefinitionssprache kann nicht für () ausgeführt werden. Die SQL Datenbank hat einen Fehler gemeldet.“ oder auf englisch „cannot excecute a data definition language command on (). The SQL database has issued an error.” stellt sich einem meist eine große Hürde in den Weg, weil aus dieser Fehlermeldung nicht eindeutig zu erkennen ist, wo ein Fehler entstanden ist bzw. wieso der Fehler auftritt.

Durch Zufall bin ich drauf gestoßen, wie man genau diese Fehlermeldung produzieren kann und somit Rückschlüsse auf die Ursache des Fehlers schließen kann.

Das Folgende Vorgehen...

  1. Erstellen eines EDT’s vom Typ „string“ mit dem Namen „MY_BaseId“.
  2. Verwenden des EDT’s in einer Tabelle (Name:  MY_BaseTable).
  3. Erzeugen von mehreren Datensätzen in der Tabelle.
  4. Löschen des EDT’s „MY_BaseId“.
  5. Erstellen eines EDT’s vom Typ „real“ mit dem Namen „MY_BaseId“ (gleicher Name wie der gelöschte EDT vom Typ „string“ hatte).
  6. Kompilieren oder synchronisieren der Tabelle „MY_BaseTable“.

...ergibt die genannte Fehlermeldung.

Daraus ist zu schließen, dass der Fehler immer Auftritt, wenn der Typ eines vorhandenen EDT’s den bereits eine oder mehrere Tabellen verwenden, geändert wird.
Ich konnte die Fehlermeldung bzw. den Fehler aber nur reproduzieren, wenn die jeweilige Tabelle ein oder mehrere Datensätze enthielt. War in der Tabelle kein Datensatz vorhanden kam es nicht zu der Fehlermeldung.

Um besagten Fehler oder besagte Fehlermeldung zu beheben müssen entweder alle Datensätze in der Tabelle oder die Tabelle selbst gelöscht werden.

Wednesday, September 12, 2007 7:14:27 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [1] - Trackback
 |  | 

 Saturday, August 04, 2007

Manchmal ist es hilfreich beim Debuggen eines Codeblocks zusätzliche Informationen im Debugger auszugeben. 
Dies kann zum Beispiel der aktuelle Wert eines Tabellenfelds sein.

Ebenfalls ist es machmal hilfreich, zusätliche Überprüfungen von Werten einzelner Variablen oder Tabellenfeldern durchzuführen, wenn der jeweilige Codeblock im Debugger ausgeführt wird.

Hierzu ein Beispiel: 

CustTable custTable;
;
while select custTable
{
   //Den Kundennamen im Debuggerfenster ausgeben.
   debug::printDebug(custTable.Name);

   //Information in einem beliebigen Debugger-Info-Tab ausgeben.
   debug::printTab(DebugPrintTab::Method, "Aufruf einer Methode");

   //Eine Überprüfung eines Wertes durchführen (nur im Debug-Mode).
   debug::assert(CustTable.Name != nullValue(CustTable.Name));

   info(custTable.Name);
}

Alle "Debug::" Anweisungen werden nur beachtet/ausgeführt, wenn der Code im Debugger ausgeführt wird (gesetzter Breakpoint). Wird der Code "normal" ausgeführt, wird in dem Beispiel nur die "info()" Anweisung ausgeführt.

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

 Tuesday, July 17, 2007

Manchmal ist es notwendig eine Abfrage (Query) über den Quellcode zu manipulieren um dort bspw. Einschränkungen vorzubelegen. Am häufigsten ist mir das in letzter Zeit bei Berichten passiert, es ist aber auch schon vorgekommen das ich komplette Abfragen in Tabellen abgespeichert habe um diese später nochmals benutzen bzw. manipulieren zu können. In beiden Fällen bin ich dabei auf die gleichen Probleme gestossen.

Möchte man Abfragen (bspw. In Berichten) mittels Quellcode manipulieren kann es Aufgrund der Nutzungsdaten zu Problemen in der Anzeige und in der Ausführung der Abfrage kommen. Denn trotz manueller Änderung an der Abfrage werden beim Aufruf des Dialoges der Abfrage noch die Nutzungsdaten des letzten Aufrufes der aktuellen Abfrage genommen/ geladen. Das führt dann dazu das die Veränderungen nicht angezeigt oder beim Ausführen benutzt werden, sondern genau die Einschränkungen/ Einstellungen die auch im Dialog erscheinen.

Um das zu verhindern können mittels der Klasse SysQueryRun noch zusätzliche Einstellungen getroffen werden. Hierzu wird eine neue Instanz von SysQueryRun angelegt, die mit der aktuellen Abfrage initialisiert wird.

Mittels

sysQueryRun.promptLoadLastUsedQuery(false);

wird festgelegt, dass die Nutzungsdaten des letzten Aufrufes nicht vorbelegt bzw. verwendet werden. Somit hat man nun die Möglichkeit die Abfrage mittels Quellcode zu manipulieren, ohne das es hier zu Problemen der Nutzungsdaten kommt, da diese nicht mehr berücksichtigt werden.

Im Bericht kann das beispielsweise so aussehen

public void init()
{
    SysQueryRun sysQueryRun;

    super();

    element.query().dataSourceNo(1).addRange(fieldnum(Tabelle, Feld)).value("NeuerWert");

    sysQueryRun = new SysQueryRun(element.query());

    sysQueryRun.promptLoadLastUsedQuery(false);

    element.queryRun(sysqueryRun);
}

Weitere nette Möglichkeiten bieten noch folgende Methoden
  • sysQueryRun.promptAllowSave(boolean); - speichern der Abfrage erlauben
  • sysQueryRun.promptShowSorting(boolean); - Sortierung anzeigen
  • sysQueryRun.promptAllowAddRange(QueryAllowadd); - Hinzufügen neuer Einschränkungen erlauben
  • sysQueryRun.promptAllowAddSorting(QueryAllowadd); - Hinzufügen neuer Sortierungen erlauben
  • sysQueryRun.promptAllowAddDataSource(boolean); - Hinzufügen neuer Tabellen erlauben#
  • sysQueryRun.promptShowReset(boolean); - Zurücksetzten der Abfrage
  • sysQueryRun.promptSaveQueryPrUser(boolean);

Da SysQueryRun von QueryRun abgeleitet ist, kann SysQueryRun von jeder Standardquery initialisiert werden um dann die erweiterten Funktionen von SysQueryRun nutzen zu können.

Noch eine kleine Besonderheit, die mir bei den Berichten und deren Aufruf aufgefallen ist:
Wird der Bericht direkt (ohne MenuItem) aufgerufen erhält man immer die Originalen inkl. der per Quellcode getroffenen Einschränkungen angezeigt. Wir der Bericht aber über ein MenuItem aufgerufen erhält man die zu letzt vom Benutzer inkl. der per Quellcode getroffenen Einschränkungen/ Einstellungen. Dabei werden die evtl. vorhandenen Einstellung überschrieben, wenn auf vorhandene Element zugriffen wird (findRange (Wert wird überschrieben) anstelle von addRange (alter Wert wird beibehalten und ein neuer hinzugefügt)). Somit gehen in diesem Fall nicht alle vom Benutzer festgelegten Einstellungen verloren.


Tuesday, July 17, 2007 4:21:13 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [1] - Trackback


 Wednesday, July 04, 2007

Microsoft Dynamics AX bietet eine performante Möglichkeit viele Datensätze einzufügen. Hierzu wird die Klasse RecordInsertList verwendet.

RecordInsertList recordList;
CustTable custtable;
;

recordList= new RecordInsertList(tableNum(custtable));

while
{
//TODO: Datensätze erzeugen ohne insert aufzurufen

recordList.add(custtable); //Datensatz der Liste übergeben
}

recordList.insertDatabase();// Datensätze einfügen

Hierbei werden die Datensätze nicht mehr sofort in die Datenbank geschrieben werden, sondern im RecordListInsert Buffer lokal zwischengespeichert. Die dort enthaltenen Datensätze werden spätestens beim Aufruf der Methode insertDatabase() -das Einfügen der Datensätze wird hier vom Kernel gesteuert, der einen geeigeneten Zeitpunkt zum Einfügen auswählt- in die Datenbank geschrieben.

Bei meinem Versuch auf einem VirtualPC habe ich eine Geschwindigkeitsvorteil von ca. 25% erzielt.

Im AXforum.info (eines der größten Dynamics Foren) kann man noch einen Testjob finden, der deutlich macht, was an Zeit eingespart werden kann, wenn mit RecordInsertList gearbeitet wird.

Update

Bei der Instanzierung gibt es noch optionale Parameter wie

  • Insert Methode der Tabelle überspringen [Default=false]
  • Datenbanklog überspringen [Default=false]
  • Alerts überspringen [Default=false]
  • AOS Validierung überspringen [Default=false]

Ist bei der aktuellen Tabelle einer dieser Punkte (Insert Methode, Datenbanklog...) vorhanden, müssen diese dann mittels der Parameter übersprungen werden, sonst wird aus dem Bulk-Insert wieder ein Single-Record-Insert!

Vielen Dank an SebDra für diese Informationen!
Wednesday, July 04, 2007 10:10:54 PM (Mitteleuropäische Zeit, UTC+01:00)  Mathias Füßler  #    Comments [2] - Trackback


 Friday, June 22, 2007

Möchte man alle Datensätze einer Tabelle löschen, kann hierfür der Befehl "Delete_From" verwendet werden.

Dies funktioniert soweit und es ist auch nichts gegen diese "Art" des Löschen einzuwenden.
Wenn allerdings in der Tabelle mehrere millionen Datensätze gespeichert sind, dauert dies schon recht lange (mehrere Stunden).

Um das Löschen aller Datensätze einer Tabelle zu beschleunigen, kann der SQL Server Befehl (Transact SQL) "TRUNCATE TABLE" verwendet werden.

Dieser Befehl erfernt alle Zeilen aus einer Tabelle, ohne die einzelnen Löschungen zu protokollieren. Der "TRUNCATE TABLE" Befehl ist wesentlich schneller und verwendet weniger Systemressourcen als der "Delete" Befehl.

Microsoft Dynamics AX unterstütz diesen Befehl leider nicht direkt.
Somit muss der Aufruf von "TRUNCATE TABLE" über eine ADO-Connection oder in einem der SQL Server Verwaltungs-Tools erfolgen.

Update:
Der "Truncate Table" Befehl ist doch in Dynamics AX implementiert. Und zwar wird er durch die Methode "tableTruncate" der Klasse "SqlDataDictionary" implementiert.

Beispiel zur Verwendung:

SqlDataDictionary sqlDict;
;
sqlDict = new SqlDataDictionary();
sqlDict.tableTruncate(tablenum(SysDataBaseLog));

Weitere Informationen zum "TRUNCATE TABLE" Befehl können über das MSDN bezogen werden.
http://msdn2.microsoft.com/de-de/library/ms177570.aspx

Friday, June 22, 2007 8:59:24 AM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [2] - Trackback
 |  | 

 Thursday, June 07, 2007

Möchte man die schon gepflegten Daten eines Dynamics AX 3.0 System über exportieren/importieren in ein Dynamics AX 4.0 SP1 System übernehmen, wird bei diesem Vorgang leider eine Fehlermeldung erzeugt, dass ein Importieren in das Dynamics AX 4.0 SP1 System nicht möglich ist, da die Daten aus einem älteren System stammen.

Dies ist soweit auch in Ordnung, da sich Tabellen und Felder von Version 3.x zu Version 4.0 SP1 verändert haben (Namensänderungen, Feldergänzungen, etc.).
Da bei einigen Tabellen aber nur Felder weggenommen wurden, könnte man theoretisch die Daten aus dem „alten“ 3.x System übernehmen. Ein Beispiel hierfür ist der Kontenplan (LedgerTable).

Damit dies funktioniert muss aber eine Änderung an der aus dem Dynamics AX 3.x System exportierten „.def“ Datei vorgenommen werden. Die erste Zeile der „.def“ Datei muss so bearbeitet werden, dass diese wie folgt lautet:

Wenn beim Export als Dateityp „Binär“ gewählt wurde:
"EXPFORMAT VER. 4.01","Binary"

Wenn beim Export als Dateityp „Komma“ gewählt wurde:
"EXPFORMAT VER. 4.01","Comma"

Wurde die „.def“ Datei entsprechend angepasst, kann es zwar sein, dass beim Import Fehlermeldungen über nicht vorhandene Felder erzeugt werden aber der Importvorgang an sich funktioniert nun.

Anzumerken ist nur noch, dass diese Version der „Datenübernahme“ nur für Spezialfälle gewählt werden sollte. Ein Datenupdate, mit den von Microsoft bereitgestellten Tools, sollte vorgezogen werden.

Thursday, June 07, 2007 7:43:25 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [1] - Trackback
 |  | 

 Thursday, May 24, 2007

Heute wurde in den Newsgroups die Frage gestellt, ob es nicht möglich sein, nach einem XPO-Import einen "automatischen" CompileForward für alle mit dem XPO-File importierten Klassen zu starten.

Ganz automatisch ist dies leider nicht möglich. Es kann aber eine Art "Installationsjob" geschrieben werden, der zusammen mit den entsprechenden Klassen durch das XPO-File weitergegeben und importiert werden kann.

Nach dem Import des XPO-File muss dieser Job nur noch gestartet werden und es werden die frisch importierten Klassen sowie alle von diesen Klassen abgeleiteten Klasse neu kompilert (CompileForward).

Ein solcher "Installtionsjob" könnte wie folgt aussehen:

static void runAfterImport(Args _args)
{
    //CompileForward all imported (base) classes
    ;
    SysCompilerOutput::compileForward(className2Id("SalesFormLetter"));
    SysCompilerOutput::compileForward(className2Id("Your next base class"));
    SysCompilerOutput::compileForward(className2Id("Your next base class"));
}

Ein manuelles Auswählen des CompileForward Menüpunktes einzelner importierter Klassen kann dadurch entfallen.

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

 Friday, April 27, 2007

Wie z.B. aus Visual Studio bekannt kann in vielen IDE's der Ausführungsmodus des Quellcodes bestimmt und gewechselt werden.
Beispiele hierfür sind Debugmode oder Releasemode.

Wenn nun im Quelltext auf den Ausführungsmodus abgefragt wird, so kann zum Beispiel im Debugmode erweiterter Quellcode mit in ein Programm kompiliert werden, welcher beispielsweise verschiedene Loggingarten implementiert um eine bessere Problemanalyse durchführen zu können. 
Dies läßt sich auch mit Microsoft Dynamics AX machen.

Als erstes muss ein neues Macro erstellt werden (in diesem Beispiel mit dem Namen RunningMode), welches als globaler Schalter zwischen den einzenen Ausführungsarten eingesetzt wird:

/* Macro for setting the running Mode. */

/* Activate for debug mode. */
#define.Debug('true')

/* Activate for production mode. */
//#define.Debug('')

Jetzt kann an beliebiger Stelle im Quellcode #Debug verwendet werden um auf den Ausführungsmodus zu prüfen: 

static void TestTheRunningMode(Args _args)
{
    #RunningMode
    ;
    print "Code der immer ausgeführt werden soll"
    if(#Debug)
    {
        print "Code der nur im Debug-Mode ausgeführt wird"
    }
    pause;
 

Abhängig davon, wie im Macro RunningMode #Debug definiert wurde, kann nun beliebiger Quellcode aktiviert (ausgeführt) oder deaktiviert (nicht ausgeführt) werden.

Friday, April 27, 2007 6:04:10 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 | 

 Wednesday, April 25, 2007

Möchte man eine Methode der Klasse Info anpassen oder erweitern, können dabei unerklärliche Fehler auftreten.

Soll zum Beispiel die Methode open(FormRun formRun) erweitert werden und man verwendet hierbei eine Variable die in der classDeclaration deklariert ist, erhält man spätestens zur Laufzeit eine Fehlermeldung (dies sogar bei fehlerfreiem Code, keine Fehlermeldung im Debugger).

Grund hierfür ist, dass in Dynamics AX 4.0 die Klasse Info immer nur beim Öffnen des Clients erzeugt wird und man somit, egal ob die Klasse neu kompiliert wurde oder nicht, immer noch mit Teilen der alten Klassenversion arbeitet.

Um die neue Version der angepassten Info Klasse aufzurufen muss der Client geschlossen und wieder neu geöffnet werden.
Erst dann funktioniert die Anpassung wie gewünscht.

Das geschilderte Verhalten kann bei:

1. Erstellen von neuen Methoden
2. Deklarieren einer Variablen in der classDeclaration und Verwendung dieser in einer anderen Methode

allerdings nicht bei:

1. Anpassungen, die nur innerhalb einer einzelnen Methode durchgeführt werden

beobachtet werden.

Wednesday, April 25, 2007 9:57:04 PM (Mitteleuropäische Zeit, UTC+01:00)  Axel Kühn  #    Comments [0] - Trackback
 |