Dynamics AX Blog - Dynamics AX 2012 - Seite 24

Momentan angezeigt werden nur Beiträge, welche für die Dynamics AX-Version »Dynamics AX 2012« relevant sind. Filter entfernen

RSS-Feed dieser Version

Erstellen einer Entität - beispielsweise eines Debitoren - per Code

Beim Start eines AX-Projektes ist die Übernahme von Daten aus Vorsystemen oft ein Thema. Mittlerweile gibt es zahlreiche Möglichkeiten dies zu tun - beispielswiese das Data import Export Framework (DIXF), ich möchte in diesem Beitrag aber auf die einfachste Variante eingehen, um eine Entität - im Beispiel einen Debitoren - in Dynamics AX per Code anzulegen.

Und diese aus meiner Sicht einfachste Variante ist, mittels X++ Logik einzubinden, welche die Daten direkt in die Tabelle schreibt. Ein solches Stück Code kann wie folgt aussehen:

static void CreateCustomerSimple_I(Args _args)
{
    custTable custTable;
    DirPartyTable dirPartyTable;

    AccountNum accountNum       = "8888";
    currencyCode currencyCode   = "EUR";
    custGroupId custGroupId     = ""; 
    taxGroup taxGroup           = "";       

    // Create or update Customer
    ttsBegin;
    CustTable = CustTable::find(accountNum, true);  // Find existing record for update
    
    custTable.AccountNum = AccountNum;
    custTable.Currency   = currencyCode;
    custTable.CustGroup  = custGroupId;
    custTable.TaxGroup   = taxGroup;

    custTable.write();  // Calls insert() or update()
    ttsCommit;
}

 

Nachteile dieses einfachen Beispiels:

  • Keine Initialisierung des Datensatzes
    Es wird keine Logik aufgerufen, die bestimmte Felder eines neuen Datensatz initalisiert (CustTable.initValue())
  • Kein Prüfung von Pflichtfelder
    Weder jene die im AOT als Mandatory gekennzeichnet sind (beispielsweise CustTable.CustGroup), noch solche die über die Programmlogik nur unter bestimmten Umständen ausgefüllt werden müssen (beispielsweise CustTable.TaxGroup).
  • Keine Prüfung von Inhalten
    Man kann in die Felder - solange es der Datentyp zulässt - irgendwelche Werte reinschreiben, beispielsweise auch Debitorengruppen, die im AX gar nicht vorhanden sind.
  • Auch werden Felder auf Basis anderer Felder nicht initialisert
    Beispielsweise werden normalerweise, wenn man eine Debitorengruppe einträgt, Standardwerte diese Gruppe in den Debitoren übernommen (Feld PaymTermId über die Methode initFromCustGroup())


Vorteile dieser Variante:

  • Das selbe Prinzip kann man für (fast) alle Tabellen verwenden

Da aus meiner Sicht die Nachteile überwiegen, würde ich persönlich die obige Variante nur in Ausnahmefällen verwenden.

Etwas besser ist die folgende Logik/der folgende Job, der prinzipiell immer noch wie oben arbeitet, d.h. wir schreiben nach wie vor direkt in die Tabelle hinein. Allerdings ist dieser Job bereits um einige Prüfungen erweitert worden, sodaß wir hier eher die Chance haben, daß unsere Entität genauso aussieht, wie wenn diese über das entsprechende (Debitoren-)Formular angelegt worden wäre.

Es werden die Methoden initValue(), validateField(), modifiedField() und validateWrite() explizit aufgerufen. Diese wiederrum führen jeweils weitere Logik aus.

static void CreateCustomerSimple_II(Args _args)
{
    custTable custTable;

    AccountNum accountNum     = "8888";
    currencyCode currencyCode = "EUR";
    custGroupId custGroupId   = "DINL";
    taxGroup taxGroup         = "DINL";

        ttsBegin;

        // Create or update Customer
        CustTable = CustTable::find(accountNum, true);

        if( !CustTable)
        {
            CustTable.initValue();
        }

        custTable.AccountNum = AccountNum;
        if( !custTable.validateField(fieldNum(custTable, accountNum)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, accountNum));

        custTable.Currency = currencyCode;
        if( !custTable.validateField(fieldNum(custTable, Currency)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, Currency));

        custTable.CustGroup = custGroupId;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, CustGroup));

        custTable.TaxGroup = taxGroup;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, TaxGroup));

        if(custTable.validateWrite())
        {
            custTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error("@SYS96731");
        }
        
        ttsCommit;
}

Wie gesagt, macht dieser Job prinzipiell genau das selbe wie die ganz oben stehende Variante, nur etwas "eleganter". Allerdings haben wir die Entität, nämlich unseren Debitoren, immer noch nicht vollständig angelegt!

Beispielsweise ist im Datenmodell von Dynamics AX 2012 nämlich nicht vorgesehen, daß der Name des Debitoren direkt in der Tabelle CustTable gespeichert wird. Dieser wird im sog. Globalen Adressbuch verspeichert und diesen Umstand berücksichtigt der folgende Job. Auch besitzt dieser nun ein rudimentäres Errorhandling.

static void CreateCustomerSimple_III(Args _args)
{
    custTable custTable;
    DirPartyTable dirPartyTable;

    AccountNum accountNum = "6666";
    Name name = "Debitor 47135";
    currencyCode currencyCode = "EUR";
    custGroupId custGroupId = "DINL";
    taxGroup taxGroup = "DINL";
    languageId languageId = "de-AT";

    try
    {
        ttsBegin;

        // Create or update Customer
        CustTable = CustTable::find(accountNum, true);

        if( !CustTable)
        {
            CustTable.initValue();
        }

        custTable.AccountNum = AccountNum;
        if( !custTable.validateField(fieldNum(custTable, accountNum)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, accountNum));

        custTable.Currency = currencyCode;
        if( !custTable.validateField(fieldNum(custTable, Currency)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, Currency));

        custTable.CustGroup = custGroupId;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, CustGroup));

        custTable.TaxGroup = taxGroup;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error("@SYS96731");
        }
        custTable.modifiedField(fieldNum(custTable, TaxGroup));

        if(custTable.validateWrite())
        {
            custTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error("@SYS96731");
        }
        
        // Update party
        dirPartyTable = DirPartyTable::findRec(custTable.Party, true);

        dirPartyTable.Name = name;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, Name)))
        {
            throw error("@SYS96731");
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, Name));

        dirPartyTable.NameAlias = name;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, NameAlias)))
        {
            throw error("@SYS96731");
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, NameAlias));

        dirPartyTable.LanguageId = languageId;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, LanguageId)))
        {
            throw error("@SYS96731");
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, LanguageId));

        if(dirPartyTable.validateWrite())
        {
            dirPartyTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error("@SYS96731");
        }
        

        ttsCommit;
    }
    catch (Exception::Error)
    {
        error("@SYS96731");
    }
}

Letztlich haben alle oben angeführten Jobs zumindest einen Nachteil, versucht man nämlich beispielsweise einen 100 Zeichen langen Text in ein Textfeld einzufügen, welches lt. EDT nur 60 Zeichen fassen kann, so wird dieser Text kommentarlos abgeschnitten. 


 
 

AX 2012: Erstellen einer DefaultDimension mit mehreren Dimensionen

Nachstehend ein Code-Beispiel, wie man eine RecId vom Typ DefaultDimension für mehrere Dimensionen generieren kann.

Im Beispiel werden die Dimension Kostenstelle mit dem Wert "10", die Dimension Abteilung mit dem Wert "IT" und die Dimension Kategorie mit dem Wert "02" zu einer DefaultDimension kombiniert.

Durch den letzten Parameter von findByDimensionAttributeAndValue() wird sichergestellt, daß fehlende Dimensionen angelegt werden (soferne möglich).

static void buildDefaultDimension(Args _args)
{
    dimensionAttributeValueSetStorage dimensionAttributeValueSetStorage;
    dimensionAttribute dimensionAttribute;
    dimensionAttributeValue dimensionAttributeValue;
    dimensionDefault dimensionDefault; 
 
    dimensionAttributeValueSetStorage = new DimensionAttributeValueSetStorage();

    // Kostenstelle
    dimensionAttribute = dimensionAttribute::findByName('Kostenstelle');
    if(dimensionAttribute)
    {
        dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute, '10', false, true);
        dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);
    }

    // Abteilung
    dimensionAttribute = dimensionAttribute::findByName('Abteilung');
    if(dimensionAttribute)
    {
        dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute, 'IT', false, true);
        dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);
    }

    // Kategorie
    dimensionAttribute = dimensionAttribute::findByName('Kategorie');
    if(dimensionAttribute)
    {
        dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute, '02', false, true);
        dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);
    }

    dimensionDefault = dimensionAttributeValueSetStorage.save();

    info(strFmt("Default dimension recId: %1", dimensionDefault));

 

 
 

AX 2012: Umwandeln/Konvertieren von Werten innerhalb des Data import/export frameworks

In der Entity einer Processing group kann man über die Schaltfläche Modify source mapping in der nicht grafische Ansicht je Feld Umwandlungen durchführen lassen.

Screenshot

Dazu muss man einfach beim jeweiligen Feld über die Schaltfläche Conversion den Dialog Define conversion values öffnen und Quell- sowie Zielwert eintragen.

Screenshot


 
 

AX 2012: DIXF: Gacutil utility for registering .NET 4.0 assembly

Bei der Installation des Data import/export frameworks werden die Systemvoraussetzungen geprüft.

Tritt dabei der nachstehende Fehler auf

Gacutil utility for registering .NET 4.0 assembly

kann dies u.a. die folgenden Ursachen haben:


 
 

AX 2012: Einen Visual Studio Shortcut erstellen

ScreenshotUm sicherzustellen, daß man in Visual Studio entwickelten Code auch im richtigen Layer entwickelt, kann man sich mit der folgenden Kurzanleitung eine passende Verknüpfung erstellen:

  • Über das Microsoft Dynamics AX 2012 Configuration Utility eine AXC-Datei (Microsoft Dynamics AX Configuration File) erstellen
  • Anschliessend eine Verknüpfung zu Visual Studio erstellen und dort in Start in den Eintrag um folgendes erweitern:

    /AxConfig "Pfad zur AXC-Datei

Der finale Eintrag sollte ähnlich dem folgenden aussehen:

"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" /AxConfig "C:\Users\Michael\Desktop\AX 2012.axc"

Startet man Visual Studio nun über diese Verknüpfung, kann man im Application Explorer ganz oben Layer und ggf. Model überprüfen.

Screenshot
 


 
 

Datenbankzugriff über ADO.net

TableHier ein Beispiel wie man aus Dynamics AX heraus über ADO.net auf eine Datenbank zugreifen kann.

Man sollte nur darauf achten, daß in den Tabellen die man einliest nach Möglichkeit keine NULL-Werte enthalten sind, mit solchen kann die Methode CLRInterop::getAnyTypeForObject() nicht korrekt umgehen.

Aus diesem Grund verwende ich im Beispiel die IsNull()-Funktion um alle NULL-Werte entsprechend dem jeweiligen Datentyp vorher umzuwandeln.


 
 

AX 2012: ValidateField und ModifiedField beim Importieren von Daten über das Data import/export framework

Unter Data import Export framework > Setup > Target Entities kann man über die Schaltfläche Modify target mapping in der Detail-Ansicht die Methoden modifiedField und validateField je Feld separat ein- oder ausschalten:

Screenshot

 

Screenshot


 
 
Seiten « 1 ... 21 22 23 24 25 26 27 ... 38 » 

 

 
 
 
Beiträge des aktuellen Monats
Mai 2024
MoDiMiDoFrSaSo
 12345
6789101112
13141516171819
20212223242526
2728293031 
 
© 2006-2024 Heinz Schweda | Impressum | Kontakt | English version | Mobile Version
Diese Webseite verwendet Cookies, um Benutzern einen besseren Service anzubieten. Wenn Sie weiterhin auf der Seite bleiben, stimmen Sie der Verwendung von Cookies zu.  Mehr dazu