<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="https://www.schweda.net/style_feed.css" ?>
<rss version="2.0" 
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
    xmlns:atom="http://www.w3.org/2005/Atom"	
	xmlns:dc="http://purl.org/dc/elements/1.1/" > 
<channel>
    <title>schweda.net - Blog</title>
    <link>https://www.schweda.net/</link>
    <description>schweda.net - Blog - Blog-Beitraege</description>
    <language>de-at</language>
    <copyright>Copyright 2006-2026</copyright>
    <generator>schweda.net</generator>
    <managingEditor>heinz.schweda@schweda.net (Heinz Schweda)</managingEditor>
    <webMaster>heinz.schweda@schweda.net (Heinz Schweda)</webMaster>
    <category>Blog</category>
	<atom:link href="https://www.schweda.net/blog_rss.php?bid=493" rel="self" type="application/rss+xml" />
<item>
<title><![CDATA[Erstellen einer Entität - beispielsweise eines Debitoren - per Code]]></title>
<description><![CDATA[
<p>Beim Start eines AX-Projektes ist die &Uuml;bernahme von Daten aus Vorsystemen oft ein Thema. Mittlerweile gibt es zahlreiche M&ouml;glichkeiten dies zu tun - beispielswiese das <a href="http://www.schweda.net/blog_ax.php?bsid=5" target="_self" title="DIFX">Data import Export Framework</a> (DIXF), ich m&ouml;chte in diesem Beitrag aber auf die einfachste Variante eingehen, um&nbsp;eine Entit&auml;t&nbsp;- im Beispiel einen Debitoren - in Dynamics AX per Code anzulegen.<br />
<br />
Und diese aus meiner Sicht einfachste Variante ist, mittels X++ Logik einzubinden, welche die Daten direkt in die Tabelle schreibt. Ein solches St&uuml;ck Code kann wie folgt aussehen:
</p>


<pre class="pre_blog_axcode">
static void CreateCustomerSimple_I(Args _args)
{
    custTable custTable;
    DirPartyTable dirPartyTable;

    AccountNum accountNum       = &quot;8888&quot;;
    currencyCode currencyCode   = &quot;EUR&quot;;
    custGroupId custGroupId     = &quot;&quot;; 
    taxGroup taxGroup           = &quot;&quot;;       

    // 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;
}

</pre>


<p>&nbsp;
</p>


<p><strong>Nachteile dieses einfachen Beispiels: </strong>
</p>


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

</ul>


<p><br />
<strong>Vorteile dieser Variante:</strong>
</p>


<ul>
	
<li>Das selbe Prinzip kann&nbsp;man f&uuml;r (fast) alle Tabellen verwenden
</li>

</ul>


<p>Da aus meiner Sicht die Nachteile &uuml;berwiegen, w&uuml;rde ich pers&ouml;nlich die obige Variante nur in Ausnahmef&auml;llen verwenden.
</p>


<p>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&uuml;fungen erweitert worden, soda&szlig; wir hier eher die Chance haben, da&szlig; unsere&nbsp;Entit&auml;t genauso aussieht, wie wenn diese &uuml;ber das entsprechende (Debitoren-)Formular angelegt worden w&auml;re.<br />
<br />
Es werden die Methoden <em>initValue()</em>, <em>validateField()</em>, <em>modifiedField() </em>und <em>validateWrite() </em>explizit aufgerufen. Diese wiederrum f&uuml;hren jeweils weitere Logik aus.
</p>


<pre class="pre_blog_axcode">
static void CreateCustomerSimple_II(Args _args)
{
    custTable custTable;

    AccountNum accountNum     = &quot;8888&quot;;
    currencyCode currencyCode = &quot;EUR&quot;;
    custGroupId custGroupId   = &quot;DINL&quot;;
    taxGroup taxGroup         = &quot;DINL&quot;;

        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(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, accountNum));

        custTable.Currency = currencyCode;
        if( !custTable.validateField(fieldNum(custTable, Currency)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, Currency));

        custTable.CustGroup = custGroupId;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, CustGroup));

        custTable.TaxGroup = taxGroup;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, TaxGroup));

        if(custTable.validateWrite())
        {
            custTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error(&quot;@SYS96731&quot;);
        }
        
        ttsCommit;
}
</pre>


<p>Wie gesagt, macht dieser Job&nbsp;prinzipiell genau das selbe wie die ganz oben stehende Variante, nur etwas &quot;eleganter&quot;. Allerdings haben wir die Entit&auml;t, n&auml;mlich unseren Debitoren, immer noch nicht vollst&auml;ndig angelegt!
</p>


<p>Beispielsweise ist im Datenmodell von Dynamics AX 2012 n&auml;mlich nicht vorgesehen, da&szlig; der Name des Debitoren direkt in der Tabelle <em>CustTable </em>gespeichert wird. Dieser wird im sog. Globalen Adressbuch verspeichert und diesen Umstand ber&uuml;cksichtigt der folgende Job. Auch besitzt dieser nun ein rudiment&auml;res Errorhandling.
</p>


<pre class="pre_blog_axcode">
static void CreateCustomerSimple_III(Args _args)
{
    custTable custTable;
    DirPartyTable dirPartyTable;

    AccountNum accountNum = &quot;6666&quot;;
    Name name = &quot;Debitor 47135&quot;;
    currencyCode currencyCode = &quot;EUR&quot;;
    custGroupId custGroupId = &quot;DINL&quot;;
    taxGroup taxGroup = &quot;DINL&quot;;
    languageId languageId = &quot;de-AT&quot;;

    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(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, accountNum));

        custTable.Currency = currencyCode;
        if( !custTable.validateField(fieldNum(custTable, Currency)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, Currency));

        custTable.CustGroup = custGroupId;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, CustGroup));

        custTable.TaxGroup = taxGroup;
        if( !custTable.validateField(fieldNum(custTable, CustGroup)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        custTable.modifiedField(fieldNum(custTable, TaxGroup));

        if(custTable.validateWrite())
        {
            custTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error(&quot;@SYS96731&quot;);
        }
        
        // Update party
        dirPartyTable = DirPartyTable::findRec(custTable.Party, true);

        dirPartyTable.Name = name;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, Name)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, Name));

        dirPartyTable.NameAlias = name;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, NameAlias)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, NameAlias));

        dirPartyTable.LanguageId = languageId;
        if( !dirPartyTable.validateField(fieldNum(dirPartyTable, LanguageId)))
        {
            throw error(&quot;@SYS96731&quot;);
        }
        dirPartyTable.modifiedField(fieldNum(dirPartyTable, LanguageId));

        if(dirPartyTable.validateWrite())
        {
            dirPartyTable.write();  // Calls insert() or update()
        }
        else
        {
            throw error(&quot;@SYS96731&quot;);
        }
        

        ttsCommit;
    }
    catch (Exception::Error)
    {
        error(&quot;@SYS96731&quot;);
    }
}
</pre>


<p>Letztlich haben alle oben angef&uuml;hrten Jobs zumindest einen Nachteil, versucht man n&auml;mlich beispielsweise einen 100 Zeichen langen Text in ein Textfeld einzuf&uuml;gen, welches lt. EDT nur 60 Zeichen fassen kann, so wird dieser Text kommentarlos abgeschnitten.&nbsp;
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Tue, 12 Nov 2013 15:26:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=493</link>
<comments>https://www.schweda.net/blog_ax.php?bid=493</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=493</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=493</wfw:commentRss>
</item>
</channel>
</rss>	
