<?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 - Axapta 2.5</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://schweda.net/blog_rss.php?vtid=ax25" rel="self" type="application/rss+xml" />
<item>
<title><![CDATA[Statt Parameter-Listen einen DataContract verwenden]]></title>
<description><![CDATA[
<p>Wer schon einmal die Parameter einer Methode erweitern musste, kennt vielleicht das Problem: Wenn man Gl&uuml;ck hat kann man seinen neuen Parameter am Ende einf&uuml;gen und mit einem Default-Wert vorbelegen.<br />
<br />
Hat man nicht ganz so viel Gl&uuml;ck und muss entweder der Parameter zwischen den vorhandenen einbauen oder darf/kann keinen Default-Wert angeben so muss man den gesamten AOT nach den Aufrufen dieser Methode durchsuchen (die Querverweise helfen hier enorm) und diese entsprechend anpassen.<br />
<br />
Aufgrund dieser Problematik gehe ich bei meinen Methoden nun oft den Weg, da&szlig; ich nur einen einzigen Parameter einf&uuml;ge, und zwar vom Typ einer - nennen wir sie einfach einmal DataContract-Klasse.<br />
<br />
DataContracts sind dem einen oder anderen vielleicht aus dem SSRS-Umfeld bekannt, aber die selbe Logik kann ich auch in vielen anderen Situationen anwenden.<br />
<br />
So sieht eine solche DataContract-Klasse beispielweise wie folgt aus:
</p>


<div class="div_blog_axcode">classDeclaration MyDataContract<br />
{<br />
&nbsp;&nbsp;&nbsp; ItemId itemId;<br />
&nbsp; &nbsp; Qty qty;<br />
}
</div>


<p>&nbsp;
</p>


<div class="div_blog_axcode">Public ItemId parmItemId(ItemId _itemId = itemId)<br />
{<br />
&nbsp;&nbsp;&nbsp; itemId = _itemId;<br />
&nbsp;&nbsp;&nbsp; return itemId;<br />
}
</div>


<p>&nbsp;
</p>


<div class="div_blog_axcode">Public Qty parmQty(Qty _qty = qty)<br />
{<br />
&nbsp;&nbsp;&nbsp; qty = _qty;<br />
&nbsp;&nbsp;&nbsp; return qty;<br />
}
</div>


<p><br />
Eine Methode k&ouml;nnte dann wie folgt aussehen:
</p>


<div class="div_blog_axcode">Public void myMethod(MyDataContract _dataContract)<br />
{<br />
&nbsp;&nbsp;&nbsp; // &hellip; do something&hellip;<br />
&nbsp;&nbsp;&nbsp; info(_dataContract.parmItemId());<br />
}
</div>


<p><br />
Aufrufen muss ich eine solche Methode so:
</p>


<div class="div_blog_axcode">MyDataContract dataContract;<br />
dataContract = new MyDataContract();<br />
dataContract.parmItemId(&quot;A1000&quot;);<br />
dataContract.parmQty(123);<br />
<br />
Object.myMethod(dataContract);
</div>


<p><br />
Kommt nun sp&auml;ter ein neuer Parameter hinzu, muss ich den lediglich entsprechend in der DataContract-Klasse einf&uuml;gen und die Logik in der Methode erweitern. Die Methodenaufrufe k&ouml;nnen u.U. so bleiben wie sie waren, lediglich die Aufrufe wo die ge&auml;nderten Parameter ber&uuml;cksichtigt werden m&uuml;ssen, m&uuml;ssen angepasst werden.<br />
<br />
Und zu guter Letzt kann ich solche DataContract-Klassen nat&uuml;rlich f&uuml;r mehrere Methoden verwenden.<br />
&nbsp;
</p>


<p>&nbsp;
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Tue, 14 Jul 2015 14:57:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=579</link>
<comments>https://www.schweda.net/blog_ax.php?bid=579</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=579</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=579</wfw:commentRss>
</item>
<item>
<title><![CDATA[Ursache von Fehlermeldungen ermitteln]]></title>
<description><![CDATA[
<p>In Dynamics AX k&ouml;nnen unterschiedliche (Fehler-)Meldungen auftreten, die meisten kommen von der Applikation, es gibt aber auch Meldungen, die vom AX-Client ausgegeben werden.
</p>

<p>Leider sind nicht alle Meldungen so sprechend, da&szlig; man (als Entwickler) sofort wei&szlig;, was nun zu tun ist. Deshalb gibt es, je nachdem um welche Art von Fehlermeldung es sich handelt, unterschiedliche Herangehensweisen, wie man die Ursache der Meldung findet.
</p>

<h2>Meldung &uuml;ber das Infolog der Applikation
</h2>

<p><a href="http://www.schweda.net/pictures/blogpics/ax_infolog_example_1.jpg"><img title="Screenshot infolog" border="0" alt="Screenshot Infolog" align="right" width="200" height="150" style="padding-bottom: 10px; padding-left: 10px" src="http://www.schweda.net/pictures/blogpics/tb_ax_infolog_example_1.jpg" /></a>Bei manchen Meldungen gen&uuml;gt ein Doppelklick auf die Meldung im <strong>Infolog </strong>und man landet im Code, welcher die Meldung geworfen hat (vorausgesetzt man befindet sich als Benutzer gerade im Entwicklungsmodus).
</p>
<br /><a class="div_blog_category_gotodetail" href="https://www.schweda.net/blog_ax.php?bid=513" target="_self" title="Weiterlesen...">Weiterlesen...</a>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Mon, 09 Jun 2014 18:30:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=513</link>
<comments>https://www.schweda.net/blog_ax.php?bid=513</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=513</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=513</wfw:commentRss>
</item>
<item>
<title><![CDATA[Objekte per Code zu einem Projekt hinzufügen]]></title>
<description><![CDATA[
<p>Nachstehend ein kurzer Job, mit dessen Hilfe man AOT-Elemente zu einem bestehenden Shared Project hinzuf&uuml;gen kann.
</p>

<div class="div_blog_axcode">static void AddNodeToSharedProject(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; projectNode projectNode;<br />
&nbsp;&nbsp;&nbsp; TreeNode treeNode;<br />
&nbsp;&nbsp;&nbsp; #AOT<br />
&nbsp;&nbsp;&nbsp; #AOTExport <br />
<br />
&nbsp;&nbsp;&nbsp; projectNode&nbsp;&nbsp;&nbsp; = infolog.projectRootNode();<br />
&nbsp;&nbsp;&nbsp; projectNode&nbsp;&nbsp;&nbsp; = projectNode.AOTfindChild(#expProjectShared);<br />
&nbsp;&nbsp;&nbsp; projectNode&nbsp;&nbsp;&nbsp; = projectNode.AOTfindChild('MyProject');<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // Add objects<br />
&nbsp;&nbsp;&nbsp; treenode = TreeNode::findNode(#TablesPath+'\\'+tableid2name(tablenum(CustGroup)));<br />
&nbsp;&nbsp;&nbsp; projectNode.addNode(treenode);<br />
<br />
&nbsp;&nbsp;&nbsp; treenode = TreeNode::findNode(#TablesPath+'\\'+tableid2name(tablenum(VendGroup)));<br />
&nbsp;&nbsp;&nbsp; projectNode.addNode(treenode);<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; treenode = TreeNode::findNode(#ClassesPath+'\\'+classStr(PriceDisc));<br />
&nbsp;&nbsp;&nbsp; projectNode.addNode(treenode);<br />
}
</div>

<p>Das ge&auml;nderte Projekt sieht beispielsweise wie folgt aus:
</p>

<p><img title="Screenshot" alt="Screenshot" style="padding-left: 10px" src="http://www.schweda.net/pictures/blogpics/ax_sharedprojectaddnode.jpg" border="0" align="left" height="158" width="245" />
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Sat, 29 Mar 2014 19:28:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=504</link>
<comments>https://www.schweda.net/blog_ax.php?bid=504</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=504</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=504</wfw:commentRss>
</item>
<item>
<title><![CDATA[Makro innerhalb eines SELECT-Statements verwenden]]></title>
<description><![CDATA[
<p>Das Beispiel listet beispielsweise nur aktive St&uuml;cklistenpositionen (Tabelle <em>BOM</em>) auf (aktiv &uuml;ber die Felder <em>FromDate </em>und <em>ToDate</em>). L&auml;sst man den zweiten Parameter des Makros leer (dateNull()), so werden alle St&uuml;cklistenpositionen gelistet.
</p>


<pre class="pre_blog_axcode">
static void useMacroInSelectStatement(Args _args)
{
    bom bom;
    date emptyDate;
    
    // parameters: %1 = table instance, %2 date, %3 empty date value
    #localmacro.bomDateFilter
        &amp;&amp; ( %2 == dateNull() || (
            ((%1.FromDate &lt;= %2) &amp;&amp; (%1.ToDate &gt;= %2)) ||
            ((%1.FromDate == %3) &amp;&amp; (%1.ToDate == %3)) ||
            ((%1.FromDate &lt;= %2) &amp;&amp; (%1.ToDate == %3)) ||
            ((%1.FromDate == %3) &amp;&amp; (%1.ToDate &gt;= %2))
            ))
    #endMacro
    ;
    
    while select bom
    where bom.ItemId == &#39;123&#39;
    #bomDateFilter(bom, systemDateGet(), emptyDate)
    {
        info(bom.bomid);
    }
}
</pre>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Sat, 25 Jan 2014 12:21:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=498</link>
<comments>https://www.schweda.net/blog_ax.php?bid=498</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=498</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=498</wfw:commentRss>
</item>
<item>
<title><![CDATA[Welche Felder werden in einem automatisch generierten Lookup angezeigt?]]></title>
<description><![CDATA[
<p>Mir wurde diese Frage vor&nbsp;kurzem selbst gestellt&nbsp;und konnte diese allerdings nur zum Teil beantworten.
</p>

<p>Ich wusste aber noch, da&szlig; ich schon einmal eine Seite gesehen hatte, wo dies genau erkl&auml;rt wird. Aber ich wusste weder noch wo, noch konnte ich sie &uuml;ber diverse Suchmaschinen finden.
</p>

<p>Aber in alten Unterlagen fand ich die Quelle dann doch, der Trick um die Quelle auch mit Google und Konsorten zu finden ist, mit dem altem Namen von Microsoft Dynamics AX - <strong>Axapta </strong>danach zu suchen!
</p>

<p>Tut man dies, so findet man rasch folgende Seite:
</p>

<p><a title="Axaptapedia" target="_blank" href="http://www.axaptapedia.com/index.php?title=Lookups">http://www.axaptapedia.com/index.php?title=Lookups</a>
</p>
<br /><a class="div_blog_category_gotodetail" href="https://www.schweda.net/blog_ax.php?bid=500" target="_self" title="Weiterlesen...">Weiterlesen...</a>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Thu, 16 Jan 2014 21:43:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=500</link>
<comments>https://www.schweda.net/blog_ax.php?bid=500</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=500</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=500</wfw:commentRss>
</item>
<item>
<title><![CDATA[Beispiel für einen Datumsfilter einer FormDataSource]]></title>
<description><![CDATA[
<p>Im folgenden ein Beispiel, wie man bei einer FormDataSource einen QueryBuildRange aufbaut, welcher nur tagesaktuelle Datens&auml;tze anzeigt.
</p>

<p>Im Beispiel enth&auml;lt unsere FormDataSource namens <em>DataSourceName </em>zwei Datumsfelder namens <em>FromDate </em>und <em>ToDate </em>und es sollen abh&auml;ngig von einer Checkbox nur Datens&auml;tze angezeigt werden, die &quot;heute&quot; g&uuml;ltig sind.
</p>

<div class="div_blog_axcode">public void applyFilter()<br />
{<br />
&nbsp;&nbsp;&nbsp; queryBuildRange qbr;<br />
<br />
&nbsp;&nbsp;&nbsp; qbr = sysQuery::findOrCreateRange(DataSourceName_ds.queryBuildDataSource(), fieldNum(DataSourceName, recId));<br />
<br />
&nbsp;&nbsp;&nbsp; if( !ShowExpiredCheckBox.checked())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qbr.value(strfmt('('+<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '((%5.%2 &lt;= %1) &amp;&amp; (%5.%3 &gt;= %1)) || ' +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '((%5.%2 == %4) &amp;&amp; (%5.%3 == %4)) || ' +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '((%5.%2 &lt;= %1) &amp;&amp; (%5.%3 == %4)) || ' +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '((%5.%3 &gt;= %1) &amp;&amp; (%5.%2 == %4)) ' +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ')',<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date2StrXpp(systemDateGet()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fieldstr(DataSourceName, FromDate),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fieldstr(DataSourceName, ToDate),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date2StrXpp(dateNull()),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tableId2name(tableNum(DataSourceName))));<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qbr.value(SysQuery::valueUnlimited());<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p>Der Aufruf obiger Methode kann beispielsweise in der <em>executeQuery()</em> der Datasource erfolgen.
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Sun, 01 Sep 2013 15:43:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=475</link>
<comments>https://www.schweda.net/blog_ax.php?bid=475</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=475</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=475</wfw:commentRss>
</item>
<item>
<title><![CDATA[Zugriff auf die Zwischenablage per X++]]></title>
<description><![CDATA[
<p>In Dynamics AX kann man selbstverst&auml;ndlich auch auf die Zwischenablage zugreifen, ein kurzes Codebeispiel dazu im folgenden. Funktioniert &uuml;brigens zumindest seit Axpata 2.5.
</p>

<div class="div_blog_axcode"><span style="color: rgb(0, 0, 255);">static void</span> useClipboard(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; textBuffer&nbsp; textBuffer = <span style="color: rgb(0, 0, 255);">new</span> textBuffer();<br />
&nbsp;&nbsp;&nbsp; ; &nbsp;&nbsp;&nbsp; <span style="color: rgb(0, 102, 0);"><br />
&nbsp;&nbsp;&nbsp; // Write to clipboard</span><br />
&nbsp;&nbsp;&nbsp; textBuffer.appendText(<span style="color: rgb(255, 0, 0);">'Hello world!'</span>);<br />
&nbsp;&nbsp;&nbsp; textBuffer.toClipboard();<br />
<br />
&nbsp;&nbsp;&nbsp; <span style="color: rgb(0, 102, 0);">// Read from clipboard</span><br />
&nbsp;&nbsp;&nbsp; textBuffer.fromClipboard();<br />
&nbsp;&nbsp;&nbsp; <span style="color: rgb(0, 0, 255);">info</span>(textBuffer.getText());<br />
}
</div>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Fri, 26 Mar 2010 19:49:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=371</link>
<comments>https://www.schweda.net/blog_ax.php?bid=371</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=371</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=371</wfw:commentRss>
</item>
<item>
<title><![CDATA[Was man bei Base Enums beachten sollte]]></title>
<description><![CDATA[
<p>Schon einmal einen Base Enum in Dynamics AX erstellt? Oder einen bestehenden erweitert?
</p>


<p>Ja?! Dabei auch auf das folgende geachtet?
</p>


<p><strong>.) Das erste Element eines Base Enums sollte den Wert 0 aufweisen</strong> und dieser sollte &quot;Undefiniert&quot;, &quot;Kein&quot;, &quot;Unbekannt&quot; oder &auml;hnlichem entsprechen. Vor allem wenn man den Base Enum sp&auml;ter in Tabellen verwenden m&ouml;chte, die bereits Daten enthalten.<br />
Selbst wenn man davon ausgehen kann, da&szlig; man einen solchen undefinierten Zustand nicht ben&ouml;tigt, sollte man die 0 reserviert halten, sprich der erste &quot;echte&quot; Wert des Base Enums sollte 1 entsprechen.
</p>

<br /><a class="div_blog_category_gotodetail" href="https://www.schweda.net/blog_ax.php?bid=345" target="_self" title="Weiterlesen...">Weiterlesen...</a>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Mon, 21 Dec 2009 19:04:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=345</link>
<comments>https://www.schweda.net/blog_ax.php?bid=345</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=345</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=345</wfw:commentRss>
</item>
<item>
<title><![CDATA[PageFooter sind nicht immer dort, wo sie sein sollten]]></title>
<description><![CDATA[
<p>Ich kenne kein einziges AX-Projekt, in dem nicht zumindest einer der Berichte Auftragsbest&auml;tigung, Ausgangslieferschein oder Ausgangsrechnung angepasst worden sind. Und deshalb stolpere ich auch immer wieder &uuml;ber das selbe Problem: Man erweitert einen Bericht (Report) um einen PageFooter, der aber nicht auf jeder Seite angedruckt werden soll.
</p>
<br /><a class="div_blog_category_gotodetail" href="https://www.schweda.net/blog_ax.php?bid=360" target="_self" title="Weiterlesen...">Weiterlesen...</a>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Fri, 04 Dec 2009 17:34:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=360</link>
<comments>https://www.schweda.net/blog_ax.php?bid=360</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=360</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=360</wfw:commentRss>
</item>
<item>
<title><![CDATA[Arbeiten mit dem aufrufenden Objekt einer Form (Caller)]]></title>
<description><![CDATA[
<p>Nachstehende Methode enth&auml;lt einige Snippets, die in einem Formular verwendet werden k&ouml;nnen, um in Dynamics AX diverse Funktionen/Methoden des Aufrufenden Objektes aufzurufen.
</p>


<p>&Auml;ndert man <em>element.args()</em> auf z.b. <em>_args</em> und &uuml;bergibt der Methode diese als Parameter, kann die selbe Logik auch aus einer Klasse heraus verwendet werden.
</p>


<pre class="pre_blog_axcode">
void workWithCallingRecord()
{
    common          common;
    object          object;
    formDataSource  formDataSource;
    formRun         formRun;
    inventDim       inventDim;
    salesTable      salesTable;
    int             i;
    ;
    // Call method from calling record
    if( element.args() &amp;&amp;
        element.args().record() )
    {
        common = element.args().record();
        if(common.isFormDataSource())
        {
            info(tableId2Name(common.TableId));
            if(formDataSourceHasMethod(common.dataSource(), identifierStr(&quot;someMethod&quot;)))
            {
                object = common.dataSource();
                object.someMethod();
            }
        }
    }
 

    // Call method from calling form
    if(element.args() &amp;&amp; element.args().caller() &amp;&amp; element.args().caller().handle() == className2Id(&#39;formRun&#39;))
    {
        formRun = element.args().caller();
        if(sysFormRun::hasMethod(formRun, identifierStr(&quot;someFormMethod&quot;)))
        {
            object = formRun;
            object.someFormMethod();
        }
    }

    // Get value from calling record
    if( element.args() &amp;&amp;
        element.args().record() )
    {
        common = element.args().record();
        if(common.TableId == tableNum(salesTable))
        {
            info(common.(fieldNum(salesTable, salesId)));
        }
    }

    // Get value from calling datasource (form with multiple datasources)
    if(element.args() &amp;&amp; element.args().caller() &amp;&amp; element.args().caller().handle() == className2Id(&#39;formRun&#39;))
    {
        formRun = element.args().caller();
        for (i = 0; i &lt;= formRun.dataSourceCount(); i++)
        {
            formDataSource = formRun.datasource(i);
            if (formDataSource &amp;&amp; formDataSource.table() == tablenum(inventDim))    // Search for specific table
            {
                inventDim = formDataSource.cursor();
                break;
            }
        }
        if(inventDim)
        {
            info(inventDim.InventLocationId);
        }
    }

    // Change data in calling datasource
    if(element.args() &amp;&amp; element.args().caller() &amp;&amp; element.args().caller().handle() == className2Id(&#39;formRun&#39;))
    {
        formRun = element.args().caller();
        for (i = 0; i &lt;= formRun.dataSourceCount(); i++)
        {
            formDataSource = formRun.datasource(i);
            if (formDataSource &amp;&amp; formDataSource.table() == tablenum(salesTable))    // Search for specific table
            {
                salesTable = formDataSource.cursor();
                break;
            }
        }
        if(salesTable)
        {
            // Update data
            salesTable.PurchOrderFormNum = &quot;Some value&quot;;
            salesTable.update();
        }
    }

    // Refresh calling datasource
    if( element.args() &amp;&amp;
        element.args().record() )
    {
        common = element.args().record();
        if(common.isFormDataSource())
        {
            formDataSource = common.dataSource();
            formDataSource.research(true);
        }
    }
}
</pre>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Sun, 15 Nov 2009 16:25:00 +0100</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=351</link>
<comments>https://www.schweda.net/blog_ax.php?bid=351</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=351</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=351</wfw:commentRss>
</item>
<item>
<title><![CDATA[Dynamics AX: Illegal property value]]></title>
<description><![CDATA[
<p>Wenn man in Dynamics AX versucht die Eigenschaft <em>AllowDuplicates </em>eines Tabellen-Indexes von <em>No </em>auf <em>Yes </em>zu &auml;ndern, kann folgende Fehlermeldung auftreten:
</p>

<blockquote>Illegal property value 
</blockquote>

<p>In diesem Fall ist vermutlich der betroffene Index als <em>PrimaryIndex </em>oder <em>ClusteredIndex </em>der Tabelle eingetragen. Eigentlich auch logisch :-)
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Wed, 13 Aug 2008 14:23:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=263</link>
<comments>https://www.schweda.net/blog_ax.php?bid=263</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=263</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=263</wfw:commentRss>
</item>
<item>
<title><![CDATA[Dynamics AX: Das RunBaseBatch-Framework]]></title>
<description><![CDATA[
<p>Man braucht es immer wieder, und genau darum findet Ihr im folgenden eine kurze - und bestimmt auch nicht ganz vollst&auml;ndige - &Uuml;bersicht, wie dieses RunBaseBatch-Framework benutzt werden kann. Erste Anlaufstelle sollte &uuml;brigens in jedem Fall die Klasse <i>Tutorial_RunBaseBatch</i> sein, dort werden die wichtigsten Methoden verwendet und man kann sich so zumindest einen groben &Uuml;berblick verschaffen, was wie m&ouml;glich ist.
</p>

<p>Um eine stapelverarbeitungsf&auml;hige Klasse zu schaffen, ist diese in erster Linie von <b>RunBaseBatch</b> abzuleiten. Dadurch stehen eine Reihe von Methoden zur Verf&uuml;gung, von welchen ich einige versuche im Folgenden zu erkl&auml;ren.
</p>
<br /><a class="div_blog_category_gotodetail" href="https://www.schweda.net/blog_ax.php?bid=254" target="_self" title="Weiterlesen...">Weiterlesen...</a>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Sat, 09 Aug 2008 09:05:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=254</link>
<comments>https://www.schweda.net/blog_ax.php?bid=254</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=254</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=254</wfw:commentRss>
</item>
<item>
<title><![CDATA[Dynamics AX: Methode des "Callers" aufrufen]]></title>
<description><![CDATA[
<p>Immer wieder besteht die Notwendigkeit, ein Formular &uuml;ber ein anderes zu &ouml;ffnen und aus diesem heraus Methoden des Aufrufers aufzurufen.
</p>


<p>Einfaches Beispiel: Ich &ouml;ffne aus der Auftragsmaske (Salestable) irgendein anderes Formular (&uuml;ber einen MenuItemButton) und m&ouml;chte nun beim Schliessen dieses Formulares eine Methode in der Salestable-Maske aufrufen.<br />
Um dies zu realisieren mu&szlig; ich einfach in der Close-Methode des Subformulars folgenden Code einbinden. <em>myMethod </em>steht dabei f&uuml;r die aufzurufende Methode des Salestable-Formulares.
</p>


<p>Achtung: IntelliSense funtioniert in diesem Fall nicht (au&szlig;er ich m&ouml;chte eine Methode aufrufen, die im FormRun-Objekt enthalten ist).
</p>


<pre class="pre_blog_axcode">
if (element.args() &amp;&amp; element.args().caller())
{
    if (element.args().caller().name() == formStr(Salestable))
    {
        element.args().caller().myMethod();
    }
}
</pre>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Tue, 17 Jun 2008 11:35:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=238</link>
<comments>https://www.schweda.net/blog_ax.php?bid=238</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=238</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=238</wfw:commentRss>
</item>
<item>
<title><![CDATA[e-Mail-Validierung in MS Dynamics AX]]></title>
<description><![CDATA[
<p>Manchmal ist es notwendig, eingegebene E-Mail-Adressen auf deren Syntax hin zu &uuml;berpr&uuml;fen. Genau f&uuml;r dieses Zweck habe ich die nachfolgende Methode geschrieben. Schliesslich verf&uuml;gt MS Dynamics AX erst ab Version 4.0 &uuml;ber eine entsprechende eigene Methode (SysEmailDistributor.validateEmail).
</p>


<pre class="pre_blog_axcode">
static boolean isValidMailAddress(str _mailAddress, str 2 _separator4MultipleAddresses = &quot;;&quot;)
{
    // Pr&uuml;ft Mail-Adressen auf ihre G&uuml;ltigkeit.
    // Es k&ouml;nnen auch mehrere Adressen auf einmal gepr&uuml;ft werden, dabei ist der Parameter _separator4MultipleAddresses zu verwenden

    boolean     ret = true;
    int         i = 0;
    str 1       buchstabe, firstZeichen = &#39;&#39;, firstDomainZeichen;
    int         firstAT = 0, firstDot = 0, lastDot = 0;
    str         errorText = &#39;&#39;;
    int         z, x, y;
    email       SubMailAddr;
    container   SubMailAddrContainer, c;

    container str2conLocal(str _value, str 10 _sep = &#39;,&#39;)    // str2con von AX30 ist fehlerhaft, wenn der ein Teilstring mit einer
    {                                                        // Zahl beginnt und als Separatir , verwendet wird
        int length = strlen(_value);
        int u = 1;
        int j = strscan(_value, _sep, 1, length);
        container retContainer;
        while (j)
        {
            retContainer+=(substr(_value, u, j-u));
            u = j+1;
            j = strscan(_value, _sep, u, length);
        }
        retContainer+=(substr(_value, u, length-u+1));
        return retContainer;
    }
    ;

    if(strLen(_MailAddress) == 0)
    {
        return true;
    }

    SubMailAddrContainer = str2conLocal(_MailAddress, _separator4MultipleAddresses);

    for(z=1;z&lt;=ConLen(SubMailAddrContainer);z++){
        SubMailAddr = ConPeek(SubMailAddrContainer,z);
        if(subMailAddr == &#39;&#39;)
        {
            continue;
        }
        // Variablen initieren
        i=0;
        firstzeichen=&#39;&#39;;
        firstDomainZeichen=&#39;&#39;;
        lastdot=0;
        firstDot=0;
        firstAT=0;
        buchstabe=&#39;&#39;;
        errorText = StrFmt(&#39;Die E-Mail-Adresse &quot;%1&quot; wurde falsch eingegeben&#39;, subMailAddr) + &#39;. &#39;;

        // ###### Start der Pr&uuml;fung ###########
        lastDot = strFind(SubMailAddr, &#39;.&#39;, strLen(SubMailAddr), -1 * strLen(SubMailAddr));

        // @ mu&szlig; zumindest einmal vorkommen
        firstAT = StrScan(SubMailAddr, &#39;@&#39;, 1, strLen(SubMailAddr));
        if(firstAT == 0)
        {
            return checkFailed(errorText + &#39;In der Mail-Adresse mu&szlig; zumindest ein @-Zeichen vorkommen&#39;);
        }
        // @ darf nur einmal vorkommen
        if(StrScan(SubMailAddr, &#39;@&#39;, firstAT + 1, strLen(SubMailAddr)) != 0)
        {
            return checkFailed(errorText + &#39;Das @-Zeichen darf nur einmal vorkommen&#39;);
        }

        // . mu&szlig; zumindest einmal vorkommen
        firstDot = StrScan(SubMailAddr, &#39;.&#39;, 1, strLen(SubMailAddr));
        if(firstDot == 0)
        {
            return checkFailed(errorText + &#39;In der Mail-Adresse mu&szlig; zumindest ein Punkt vorkommen&#39;);
        }

        // nach dem @ mu&szlig; zumindest 1 punkt kommen
        if(StrScan(SubMailAddr, &#39;.&#39;, firstAT, strLen(SubMailAddr)) == 0)
        {
            return checkFailed(errorText + &#39;Nach dem @-Zeichen muss zumindest ein Punkt vorkommen&#39;);
        }

        // nach dem letzten punkt darf nur 2 bis 4 zeichen kommen
        if(StrLen(SubStr(SubMailAddr,lastDot +1,strLen(SubMailAddr))) &gt; 4 || StrLen(SubStr(SubMailAddr,lastDot +1,strLen(SubMailAddr))) &lt; 2)
        {
            return checkFailed(errorText + &#39;Die Dom&auml;n-Endung muss aus mindestens 2 und maximal 4 Zeichen bestehen&#39;);
        }

        // Sub-Dom&auml;nen m&uuml;ssen zumindest 2 Stellen gross sein und m&uuml;ssen mit A-Z oder 0-9 anfangen
        c = str2conLocal(SubStr(SubMailAddr, firstAT + 1, strLen(SubMailAddr)), &quot;.&quot;);
        for (x=1; x&lt;=conLen(c); x++)
        {
            firstDomainZeichen = SubStr(conPeek(c,x), 1,1);
            if( !( ( char2num(firstDomainZeichen,1) &gt;= 97 &amp;&amp; char2num(firstDomainZeichen,1) &lt;= 122 ) ||
                   ( char2num(firstDomainZeichen,1) &gt;= 48 &amp;&amp; char2num(firstDomainZeichen,1) &lt;= 57 ) ||
                   ( char2num(firstDomainZeichen,1) &gt;= 65 &amp;&amp; char2num(firstDomainZeichen,1) &lt;= 90 ) ) )
            {
                return checkFailed(errorText + &#39;Jede Subdom&auml;ne muss mit einem Buchstaben oder einer Zahl beginnen&#39;);
            }

            if(strLen(conPeek(c,x)) &lt; 2)
            {
                return checkFailed(errorText + &#39;Jede Subdom&ouml;ne muss aus zumindest 2 Zeichen bestehen&#39;);
            }

            // Subdom&auml;nen d&uuml;rfen nur aus A-Z 0-9 oder - bestehen
            for(y=1;y&lt;=strLen(conPeek(c,x));y++)
            {
                buchstabe = SubStr(conPeek(c,x),y,1);
                if( !(char2num(buchstabe,1) == 45 ||    // -
                      ( char2num(buchstabe,1) &gt;= 97 &amp;&amp; char2num(buchstabe,1) &lt;= 122 ) ||
                      ( char2num(buchstabe,1) &gt;= 48 &amp;&amp; char2num(buchstabe,1) &lt;= 57 ) ||
                      ( char2num(buchstabe,1) &gt;= 65 &amp;&amp; char2num(buchstabe,1) &lt;= 90 ) ) )
                {
                    return checkFailed(errorText + strFmt(&#39;In der Subdom&auml;ne &quot;%1&quot; ist das Zeichen &quot;%2&quot; nicht erlaubt!&#39;,conPeek(c,x),buchstabe));
                }
            }
        }

        // Text vor dem @ (durch Punkte getrennt) muss mindestens 1 Stelle lang sein
        c = ConNull();
        c = str2conLocal(SubStr(SubMailAddr, 1, firstAT-1), &quot;.&quot;);
        for (x=1; x&lt;=conLen(c); x++)
        {
            if(strLen(conPeek(c,x)) &lt; 1)
            {
                return checkFailed(errorText + &#39;Der Text vor dem @-Zeichen ist ung&uuml;ltig&#39;);
            }
        }

        // mu&szlig; mit a-z oder A-Z oder 0-9 beginnen
        firstZeichen = SubStr(SubMailAddr, 1,1);
        if( !(( char2num(firstZeichen,1) &gt;= 97 &amp;&amp; char2num(firstZeichen,1) &lt;= 122 ) ||
            ( char2num(firstZeichen,1) &gt;= 48 &amp;&amp; char2num(firstZeichen,1) &lt;= 57 ) ||
            ( char2num(firstZeichen,1) &gt;= 65 &amp;&amp; char2num(firstZeichen,1) &lt;= 90 )) )
        {
            return checkFailed(errorText + &#39;Die Mailadresse muss mit einem Buchstaben oder einer Zahl beginnen&#39;);
        }

        // G&uuml;ltiges Zeichen
        i = 0;
        for(i=1;i&lt;=StrLen(SubMailAddr);i++)
        {
            buchstabe = SubStr(SubMailAddr,i,1);

            if( !(char2num(buchstabe,1) == 46 ||    // .
                  char2num(buchstabe,1) == 45 ||    // -
                  char2num(buchstabe,1) == 95 ||    // _
                  char2num(buchstabe,1) == 64 ||    // @
                  char2num(buchstabe,1) == 228 ||   // &auml;
                  char2num(buchstabe,1) == 252 ||   // &uuml;
                  char2num(buchstabe,1) == 246 ||   // &ouml;
                  char2num(buchstabe,1) == 196 ||   // &Auml;
                  char2num(buchstabe,1) == 220 ||   // &Uuml;
                  char2num(buchstabe,1) == 214 ||   // &Ouml;
                  ( char2num(buchstabe,1) &gt;= 97 &amp;&amp; char2num(buchstabe,1) &lt;= 122 ) ||
                  ( char2num(buchstabe,1) &gt;= 48 &amp;&amp; char2num(buchstabe,1) &lt;= 57 ) ||
                  ( char2num(buchstabe,1) &gt;= 65 &amp;&amp; char2num(buchstabe,1) &lt;= 90 ) ) )
            {
                return checkFailed(errorText + strFmt(&#39;Ung&uuml;ltiges Zeichen &quot;%1&quot; in der Mail-Adresse&#39;, buchstabe));
            }
        }
    }

    return true;
}
</pre>



<p>Obiger Code sollte in allen AX-Versionen funktionieren. Verwendung nat&uuml;rlich auf eigene Gefahr.
</p>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Thu, 18 Oct 2007 16:40:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=158</link>
<comments>https://www.schweda.net/blog_ax.php?bid=158</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=158</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=158</wfw:commentRss>
</item>
<item>
<title><![CDATA[Fehlerhafte str2con-Funktion]]></title>
<description><![CDATA[
<p>Wer schon mal versucht hat in Dynamics AX 2.x einen String in einen Container umzuwandeln, der hat recht bald entdecken m&uuml;ssen, da&szlig; es - anders als in den meisten anderen Programmiersprachen - in X++ der Version 2.x keine entsprechende Funktion daf&uuml;r gibt und man sich eine solche selbst schreiben mu&szlig;.<br />
<br />
Etwas besser sieht das ganze in Dynamics AX 3.x aus, dort gibt es zwar eine Funktion str2con, diese ist allerdings etwas buggy. Beginnt der umzuwandelnde String n&auml;mlich mit einer Zahl, so erfolgt die Umwandlung nicht wie vom Entwickler erwartet.<br />
<br />
Korrigiert wurde dieser Fehler erst in AX 4.0, dort arbeitet die Methode str2con meines Erachtens korrekt.<br />
<br />
Anbei der X++ Code aus AX 4.0, sollte problemlos auch in &auml;lteren Versionen arbeiten. In AX 2.x kann man die Methode einfache in die Klasse global integrieren, in der Version 3.0 sollte die bestehende &uuml;berschrieben werden.
</p>


<pre class="pre_blog_axcode">
static container str2con(str _value, str 10 _sep = &#39;,&#39;)
{
    int length = strlen(_value);
    int i = 1;
    int j = strscan(_value, _sep, 1, length);
    container ret;
    void add2Ret(str _current)
    {
        // v-artemt, 26 Jul 2004, PS#: 1741
        if (match(&#39;&lt;:d+&gt;&#39;, _current))
            ret += str2int(_current);
        else
            ret += _current;
    }
;
    while (j)
    {
        add2Ret(substr(_value, i, j-i));
        i = j+1;
        j = strscan(_value, _sep, i, length);
    }
    add2Ret(substr(_value, i, length-i+1));
    return ret;
}
</pre>]]></description>
<category>Microsoft Dynamics AX (Axapta)</category>
<pubDate>Wed, 17 Oct 2007 16:08:00 +0200</pubDate>
<link>https://www.schweda.net/blog_ax.php?bid=157</link>
<comments>https://www.schweda.net/blog_ax.php?bid=157</comments>
<guid isPermaLink="true">https://www.schweda.net/blog_ax.php?bid=157</guid>
<author>heinz.schweda@schweda.net (Heinz Schweda)</author>
<wfw:commentRss>https://www.schweda.net/blog_ax.php?bid=157</wfw:commentRss>
</item>
</channel>
</rss>	
