<?xml version="1.0" encoding="iso-8859-1"?><?xml-stylesheet type="text/css" href="http://www.schweda.net/style_feed.css" ?>


<feed version="0.3" xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
  <title>Blog von Heinz Schweda</title>
  <link rel="alternate" type="text/html" hreflang="de" href="http://www.schweda.net/" />
 <link rel="self" type="application/atom+xml" 
   href="http://www.schweda.net/blog_atom.php"/>
  <rights>Copyright (c) 2006, Heinz Schweda</rights>

  <updated>2006-08-01T12:00:00Z</updated>
  <author>
    <name>Heinz Schweda</name>
  </author>
<id>tag:schweda.net,2006:1</id>



 
<entry>
<id>tag:schweda.net,2011-12-02:id418</id><title type="text">Windows 7: Als anderer Benutzer ausführen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Als Entwickler muss ich Dynamics AX immer wieder mal mit einem anderen Windows-Login starten, als dem eigenen. Deshalb habe ich in Windows 7 die Option <strong>Als anderer Benutzer ausf&uuml;hren</strong> im Kontext-Men&uuml; schmerzlich vermisst und statt dessen&nbsp; immer <a title="Dynamics AX mit einem bestimmten Windowslogin starten" target="_self" href="http://www.schweda.net/blog_ax.php?nid=runas1">Batch-Dateien</a> daf&uuml;r verwendet.
</p>

<p>&Uuml;ber einen Blog-Eintrag bin ich aber auf einen wertvollen Tip gestossen: Wenn man die [SHIFT]-Taste h&auml;lt, w&auml;hrend man &uuml;ber die rechte Maustaste das Kontext-Men&uuml; einer Applikation aufruft, steht die Option <strong>Als anderer Benutzer ausf&uuml;hren</strong> wieder zur Verf&uuml;gung.
</p>

<p><img border="0" align="left" width="334" height="161" title="Als anderer Benutzer ausf&uuml;hren" alt="Als anderer Benutzer ausf&uuml;hren" src="http://www.schweda.net/pictures/blogpics/dynamics_ax_runas_shift.jpg" />
</p>]]></content>
<published>2011-12-02T23:01:04Z</published>
<updated>2011-12-02T23:01:04Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=418"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-11-04:id417</id><title type="text">AX 2012: Einen Bericht mit den SQL Reporting Services erstellen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Mit der Version Dynamics AX 2012 halten die SQL Reporting Services (SSRS) nun endg&uuml;ltig Einzug im Leben eines jeden AX-Entwicklers. Zeit also, sich mit diesem Thema etwas intensiver zu besch&auml;ftigen. Die Ergebnisse meiner ersten Gehversuche mit den SSRS pr&auml;sentiere ich Euch nun in diesem Beitrag.
</p>

<p>Szenario: Es soll ein Bericht erstellt werden, bei welchem der Umsatz pro Verkaufsgruppe ausgewertet werden kann.
</p>

<p>&nbsp;
</p>

<h2>Schritt 1: Erstellen eines Queries in Dynamics AX
</h2>

<table width="100%" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td valign="top" align="left">Ich gehe davon aus, da&szlig; der geneigte Leser schon einmal einen Query  erstellt hat, deshalb werde ich darauf nur sehr oberfl&auml;chlich eingehen.<br />
            <br />
            Basis-Tabellen meines Queries sind die Tabellen<br />
            
<ul>
                
<li>CustInvoiceTrans
</li>
                
<li>CustInvoiceJour
</li>
                
<li>CommissionSalesGroup
</li>
            
</ul>
            
<p>Die Tabelle <em>CustInvoiceTrans </em>wird per InnerJoin mit der <em>CustInvoiceJour </em>&uuml;ber die im Screenshot gezeigten Felder verkn&uuml;pft, die Tabelle <em>CommissionSalesGroup </em>wird per OuterJoin &uuml;ber das Feld <em>SalesGroup </em>verkn&uuml;pft.
</p>
            
</td>
            
<td valign="top">&nbsp;
</td>
            
<td valign="top" style="text-align: right;"><a target="_blank" href="http://www.schweda.net/pictures/blogpics/ax_query.jpg"><img width="232" align="right" height="179" title="Query" alt="Query" src="http://www.schweda.net/pictures/blogpics/ax_query_small.jpg" /></a>
</td>
        </tr>
    </tbody>

</table>

<p>Seit AX 2012 muss die Eigenschaft <em>Dynamic</em> der Fields-Gruppe aller beteiligten Tabellen entsprechend gesetzt werden, im einfachsten Fall wird diese auf <em>Yes </em>gesetzt, um im Visual Studio sp&auml;ter alle Felder der Tabellen verwenden zu k&ouml;nnen.
</p>


<p>&nbsp;
</p>

<h2>Schritt 2: Erstellen eines Projektes im Visual Studio
</h2>

<table width="100%" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td valign="top">&Uuml;ber <em>File &gt; New &gt; Project</em> das Template Microsoft Dynamics AX  ausw&auml;hlen, Report Model ausw&auml;hlen und einen Namen und Speicherort  angeben.
</td>
            
<td valign="top">&nbsp;
</td>
            
<td valign="top"><a target="_blank" href="http://www.schweda.net/pictures/blogpics/vs_solutionexplorer_project.jpg"><img width="216" height="115" title="Solution Explorer" alt="Solution Explorer" src="http://www.schweda.net/pictures/blogpics/vs_solutionexplorer_project_small.jpg" /></a>
</td>
        </tr>
    </tbody>

</table>

<p>&nbsp;
</p>

<h2>Schritt 3: Erzeugen eines DataSets
</h2>

<table width="100%" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td><a target="_blank" href="http://www.schweda.net/pictures/blogpics/vs_dataset_fields.jpg"><img width="191" height="316" title="DataSet" alt="DataSet" src="http://www.schweda.net/pictures/blogpics/vs_dataset_fields_small.jpg" /></a>
</td>
            
<td>
            
<p>Innerhalb dieses Reports ist nun ein neues DataSet zu erstellen.  Diesem geben wir den Namen <strong>CustInvoiceDataSet </strong>und tragen als Query den  zuvor in Dynamics AX erstellten ein. Letzteres erfolgt &uuml;ber einen Dialog  wo auch jene Felder oder Methoden gew&auml;hlt werden k&ouml;nnen, die im Bericht  ausgegeben werden sollen.
</p>
            
<p>In unserem Fall sind dies:
</p>
            
<p>Aus der Tabelle <em>CustInvoiceTrans </em>die Felder
</p>
            
<ul>
                
<li>InvocieId
</li>
                
<li>InvocieDate
</li>
                
<li>ItemId
</li>
                
<li>SalesGroup
</li>
                
<li>LineAmountMST
</li>
            
</ul>
            
<p>und die Methode
</p>
            
<ul>
                
<li>itemName()
</li>
            
</ul>
            
<p>Aus der Tabelle <em>CommissionSalesGroup</em> ben&ouml;tige ich das Feld
</p>
            
<ul>
                
<li>Name
</li>
            
</ul>
            
<p>Gleichzeitig habe ich das DataSet &uuml;ber die Properties auf <strong>CustInvoiceDataSet </strong>umbenannt.
</p>
            
</td>
        </tr>
    </tbody>

</table>

<p>Die Eigenschaften des DataSet stellen sich nach Abschlu&szlig; des Dialoges wie folgt dar:
</p>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/vs_dataset_properties.jpg"><img width="465" height="177" title="DataSet Properties" alt="DataSet Properties" src="http://www.schweda.net/pictures/blogpics/vs_dataset_properties_small.jpg" /></a>
</p>

<p>&nbsp;
</p>

<h2>Schritt 4: Erzeugen eines Designs
</h2>

<p>Nun kann das DataSet per Drag &amp; Drop in den Knoten <strong>Designs </strong>gezogen werden. Dadurch wird ein neues Design namens <strong>AutoDesign1 </strong>erstellt.
</p>

<p>Nun gilt es einige Eigenschaften dieses Designs anzupassen. Dazu z&auml;hlen das Seitenformat, Bezeichnungen (<em>Title</em>) des Berichtes aber auch das Layout (<em>LayoutTemplate</em>), im Beispiel habe ich als Seitenformat die Einstellungen f&uuml;r DIN-A4 eingetragen.
</p>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/vs_design_properties.jpg"><img width="465" height="263" title="Properties" alt="Properties" src="http://www.schweda.net/pictures/blogpics/vs_design_properties_small.jpg" /></a>&nbsp;
</p>

<p>Der automatisch generierten Tabelle <strong>CustInvoiceDataSetTable </strong>weisen wir ebenfalls &uuml;ber die Eigenschaft <em>StyleTemplate </em>ein Layout zu. Im Beispiel habe ich das Layout so gew&auml;hlt, da&szlig; die einzelnen Zeilen sp&auml;ter abwechselnd grau oder wei&szlig; unterlegt werden.
</p>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/vs_design_dataset_properties.jpg"><img width="465" height="327" alt="" src="http://www.schweda.net/pictures/blogpics/vs_design_dataset_properties_small.jpg" /></a>
</p>

<p>&nbsp;
</p>

<h2>Schritt 5:Gruppierung einrichten
</h2>

<table width="100%" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td><a href="http://www.schweda.net/pictures/blogpics/vs_design_groupings.jpg" target="_blank"><img width="213" height="400" src="http://www.schweda.net/pictures/blogpics/vs_design_groupings_small.jpg" alt="Gruppierung" title="Gruppierung" /></a>
</td>
            
<td valign="top" align="left">Nun gilt es, die geforderte Gruppierung nach der Verkaufsgruppe (Feld <em> SalesGroup</em>) abzubilden. Hierzu muss im Knoten Groupings der <strong> CustInvoiceDataSetTable</strong> benannten Tabelle ein neuer Knoten erstellt  werden. Diesen nennen wir <strong>SalesGroupGrouping </strong>und vergeben im einen  sprechenden Label, z.B. Verkaufsgruppe.<br />
            <br />
            In diesem Knoten k&ouml;nnen wir nun einen Unterknoten erstellen, in  dessen Expression wir das Feld <em>SalesGroup </em>eintragen. Dieser Knoten  bekommt nun noch den Namen <strong>SalesGroup</strong>.<br />
            <br />
            Danach erstellen wir jeweils einen neuen Knoten im Header und  Footer-Bereich. Auch diese bekommen einen m&ouml;glichst selbstsprechenden  Namen (<strong>SalesGroupHeader </strong>bzw. <strong>SalesGroupFooter</strong>).<br />
            <br />
            Im <strong>SalesGroupHeader </strong>erzeugen wir zwei Unterknoten. Beim Ersten tragen  wir in der Eigenschaft <em>Expression </em>das Feld <em>SalesGroup</em> ein, beim Zweiten  das Feld <em>Name</em>.
</td>
        </tr>
    </tbody>

</table>

<p>Im <strong>SalesGroupFooter </strong>erzeugen wie nun in Summe sieben Unterknoten. Nur beim siebenten tragen wir &uuml;ber die Eigenschaft <em>Expression </em>einen Wert ein, die anderen taufen wir <strong>Dummy1 </strong>bis <strong>Dummy6</strong>.
</p>

<p><a href="http://www.schweda.net/pictures/blogpics/vs_data_dummy_properties.jpg" target="_blank"><img width="465" height="177" src="http://www.schweda.net/pictures/blogpics/vs_data_dummy_properties_small.jpg" alt="Properties" title="Properties" /></a>
</p>

<p>Damit diese Summe je Verkaufsgruppe korrekt formatiert wird, passen wir die Eigenschaften entsprechend an:
</p>

<p><a href="http://www.schweda.net/pictures/blogpics/vs_groupsum_properties.jpg" target="_blank"><img width="465" height="252" src="http://www.schweda.net/pictures/blogpics/vs_groupsum_properties_small.jpg" alt="Properties" title="Properties" /></a>
</p>

<p>Hintergrund der Dummy-Felder ist, da&szlig; Visual-Studio die Spalten im Footer automatisch unter den Spalten im Data-Bereich versucht auszurichten. W&uuml;rden wir diese Dummy-Felder nicht erstellen, w&uuml;rde die Summe unterhalb der ersten Spalte&nbsp; angedruckt.
</p>

<p>In fr&uuml;heren Versionen von Dynamics AX h&auml;tten wir einfach die Eigenschaft <em>ModelFieldName </em>f&uuml;r solcherart Positionierung herangezogen, eine solche Eigenschaft scheint es aber in Visual Studio nicht zu geben.
</p>

<p>&nbsp;
</p>

<h2>Schritt 6: Reihenfolge der Spalten und Detail-Eigenschaften anpassen
</h2>

<p><a href="http://www.schweda.net/pictures/blogpics/vs_sorting_salesgroup_properties.jpg" target="_blank"><img width="465" height="118" src="http://www.schweda.net/pictures/blogpics/vs_sorting_salesgroup_properties_small.jpg" alt="Properties" title="Properties" /></a>
</p>

<p>Damit die Daten in einer bestimmten Reihenfolge sortiert angedruckt werden, erstellen wir im Sorting-Knoten entsprechende Unterknoten und tragen &uuml;ber deren Eigenschaft <em>SortBy </em>das jeweilige Feld ein. Wir sortieren nach Verkaufsgruppe, Rechnungsnummer und Rechnungsdatum.
</p>

<p>Abschlie&szlig;end pr&uuml;fen bzw. &auml;ndern wir die Reihenfolge der anzudruckenden Felder im Data-Bereich und passen &uuml;ber die jeweiligen Eigenschaften Spaltenbreiten und ggf. die Bezeichnungen an. So w&auml;hle ich zum Beispiel f&uuml;r die Spalte <em>LineAmountMST </em>als Bezeichnung <em>Betrag </em>statt dem vom Visual Studio vorgeschlagenen Label.
</p>

<p>&nbsp;
</p>

<h2>Schritt 7: Spalte Betrag einf&auml;rben
</h2>

<p>Damit Gutschriften bzw. Rechnungen mit negativen Werten rot dargestellt  werden, &auml;ndern wir die Eigenschaft <em>Style &gt; Font &gt; Color</em> &uuml;ber eine  entsprechende Expression:
</p>

<p><a href="http://www.schweda.net/pictures/blogpics/vs_data_lineamountmst_fontcolor_expression.jpg" target="_blank"><img width="465" height="377" src="http://www.schweda.net/pictures/blogpics/vs_data_lineamountmst_fontcolor_expression_small.jpg" alt="Expression" title="Expression" /></a>&nbsp;
</p>

<p>Diese &Auml;nderung bewirkt, da&szlig; im Bericht negative Betr&auml;ge sp&auml;ter wie folgt dargestellt werden:
</p>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/ax_report_colored_lines.jpg"><img width="465" height="52" title="Bericht mit eingef&auml;rbten Werten" alt="Bericht mit eingef&auml;rbten Werten" src="http://www.schweda.net/pictures/blogpics/ax_report_colored_lines_small.jpg" /></a>
</p>

<p>&nbsp;
</p>

<h2>Schritt 8: MenuItem in Dynamics AX erzeugen
</h2>

<table width="100%" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td valign="top">Abschlie&szlig;end erstellen wir in Dynamics AX ein neues MenuItem, verkn&uuml;pfen  dieses mit einem Men&uuml; um den Bericht aus Dynamics AX heraus starten zu k&ouml;nnen.<br />
            <br />
            AX erzeugt - wie schon in fr&uuml;heren Versionen - automatisch einen  Dialog, wo man die anzuzeigenden Daten filtern kann.
</td>
            
<td>&nbsp;
</td>
            
<td><a href="http://www.schweda.net/pictures/blogpics/ax_menuitem.jpg" target="_blank"><img width="232" height="118" src="http://www.schweda.net/pictures/blogpics/ax_menuitem_small.jpg" alt="Menuitem" title="Menuitem" /></a>
</td>
        </tr>
    </tbody>

</table>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/ax_dialog.jpg"><img title="Dialog" alt="Dialog" src="http://www.schweda.net/pictures/blogpics/ax_dialog_small.jpg" /></a>&nbsp;
</p>

<p>&nbsp;
</p>

<h2>Schritt 9: Aufrufen des Berichtes aus Dynamics AX heraus - Fertig
</h2>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/ax_report_examplepage.jpg"><img width="465" height="266" title="Bericht" alt="Bericht" src="http://www.schweda.net/pictures/blogpics/ax_report_examplepage_small.jpg" /></a>&nbsp;
</p>]]></content>
<published>2011-11-04T19:06:59Z</published>
<updated>2011-11-04T19:06:59Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=417"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-09-27:id416</id><title type="text">SID für einen Active-Directory-User auslesen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wer schon einmal eine Applikation inkl. Datenbank von einer Netzwerk-Dom&auml;ne in eine andere &uuml;bertragen hat, der kennt vielleicht die Situation. Man hat die Applikation und die Datenbank eingerichtet, kann sich in Dynamics AX aber nicht anmelden und erh&auml;lt die Fehlermeldung:
</p>

<blockquote>

<p>You are not recognized user of Dynamics AX. Please contact your system administrator
</p>

</blockquote>

<p>Die Ursache daf&uuml;r ist, da&szlig; in der Tabelle <strong>UserInfo </strong>- das ist jene Tabelle wo die AD-Benutzer verwaltet werden - nat&uuml;rlich noch die Benutzer der Ursprungsdom&auml;ne hinterlegt sind. Im einfachsten Fall &auml;ndert man in dieser Tabelle den Eintrag f&uuml;r den Administrator, indem man die Felder <em>NetworkDomain</em>, <em>NetworkAlias </em>und <em>SID </em>entsprechend &auml;ndert. <em>NetworkDomain </em>und <em>NetworkAlias </em>sind selbsterkl&auml;rend, woher bekommt man aber die <em>SID</em>?
</p>

<p>Eine einfache Internet-Recherche nach &quot;GET SID&quot; liefert einige M&ouml;glichkeiten, weniger bekannt ist aber, da&szlig; auch Dynamics AX 2009 selbst eine Methode zur Verf&uuml;gung stellt, um die SID f&uuml;r einen Benutzer auszulesen.
</p>

<p>Jetzt braucht man also nur noch ein anderes, bereits lauff&auml;higes AX, wo man sich anmelden und den Job absetzen kann ;-)
</p>

<div style="padding: 5px; width: 460px; height: 100px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">static void getUserSid(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; info(new xAxaptaUsermanager().getUserSid('h.schweda', 'schweda.net'));<br />
}
</div>]]></content>
<published>2011-09-27T21:33:35Z</published>
<updated>2011-09-27T21:33:35Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=416"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-08-21:id415</id><title type="text">Bearbeiten von Formularfeldern beschränken II</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Sollen in einem Formular nur bestimmte Felder zur Bearbeitung freigegeben sein, kann man die Eigenschaft <em>allowEdit </em>s&auml;mtlicher Felder der DataSource der Tabelle entsprechend umsetzen.
</p>

<p>Einfacher geht&rsquo;s mit folgendem Codebeispiel, welches in der <em>init</em>-Methode der DataSource eingebunden wurde und - im konkreten Fall in der Tabelle <em>SalesLine </em>- nur bei einem einzigen Feld die Bearbeitung erlaubt.
</p>

<p>Im Gegensatz zu einem <a title="Bearbeiten von Formularfeldern beschr&auml;nken" target="_self" href="http://www.schweda.net/blog_ax.php?nid=allowedit1">fr&uuml;her ver&ouml;ffentlichten Beitrag</a> mit dem gleichen Thema werden bei diesem Codebeispiel auch Array-Felder korrekt ber&uuml;cksichtigt.
</p>

<div style="padding: 5px; width: 460px; height: 290px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public void init()<br />
{<br />
&nbsp;&nbsp;&nbsp; FormDataObject&nbsp; fdo;<br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f;<br />
&nbsp;&nbsp;&nbsp; SysDictTable&nbsp;&nbsp;&nbsp; sysDictTable;<br />
&nbsp;&nbsp;&nbsp; MapEnumerator&nbsp;&nbsp; fdoMapEnumerator;<br />
&nbsp;&nbsp;&nbsp; ; <br />
&nbsp;&nbsp;&nbsp; sysDictTable = new SysDictTable(tablenum(SalesLine));<br />
&nbsp;&nbsp;&nbsp; for(f=1;f&lt;=sysDictTable.fieldCnt();f++)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fdoMapEnumerator = new MapEnumerator(formDataSourceArrayFieldExtObjects(salesLine_ds, sysDictTable.fieldCnt2Id(f)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (fdoMapEnumerator.moveNext())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fdo = fdoMapEnumerator.currentValue();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(fdo)<br />
&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; fdo.allowEdit(false);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; fdo = salesLine_ds.object(fieldnum(SalesLine, QtyOrdered));<br />
&nbsp;&nbsp;&nbsp; fdo.allowEdit(true);<br />
}
</div>

<p>Getestet in Dynamics AX 2009
</p>]]></content>
<published>2011-08-21T19:34:01Z</published>
<updated>2011-08-21T19:34:01Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=415"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-08-12:id414</id><title type="text">Datensatzvorlagen ermitteln</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wer einmal in die Verlegenheit kommt, ermitteln zu m&uuml;ssen ob f&uuml;r eine bestimmte Tabelle Datensatzvorlagen vorhanden sind, f&uuml;r den kann folgendes Code-Beispiel hilfreich sein.
</p>

<p>Im Beispiel wird ermittelt, wieviele Benutzer- bzw. Unternehmensvorlagen es f&uuml;r die Tabelle <em>InventTable </em>gibt und ob der aktuelle Benutzer sich den Vorlagendialog anzeigen l&auml;sst, wenn er/sie einen neuen Artikel anlegt.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">static void SysRecordTemplatesActive(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; tableId tableId = tableNum(inventTable);<br />
&nbsp;&nbsp;&nbsp; common common = new sysdictTable(tableId).makeRecord();<br />
&nbsp;&nbsp;&nbsp; SysRecordTemplateStorageUser storageUser&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = SysRecordTemplateStorage::newCommon(common, SysRecordTemplateType::User);<br />
&nbsp;&nbsp;&nbsp; SysRecordTemplateStorageCompany storageCompany = SysRecordTemplateStorage::newCommon(common, SysRecordTemplateType::Company);<br />
&nbsp;&nbsp;&nbsp; sysRecordTemplateSelect sysRecordTemplateSelect;<br />
&nbsp;&nbsp;&nbsp; container userTemplates;<br />
&nbsp;&nbsp;&nbsp; container companyTemplates;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Liste der Vorlagen<br />
&nbsp;&nbsp;&nbsp; userTemplates = storageUser.get();<br />
&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Anzahl Benutzervorlagen f&uuml;r Tabelle %1: %2&quot;, new sysdictTable(tableId).label(),<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;&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; conLen(userTemplates)));<br />
<br />
&nbsp;&nbsp;&nbsp; companyTemplates = storageCompany.get();<br />
&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Anzahl Unternehmensvorlagen f&uuml;r Tabelle %1: %2&quot;, new sysdictTable(tableId).label(), <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;&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;&nbsp;&nbsp;&nbsp; conLen(companyTemplates)));<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // Soll der aktive Benutzer nach Vorlagen gefragt werden?<br />
&nbsp;&nbsp;&nbsp; sysRecordTemplateSelect = SysRecordTemplateSelect::newTableId(tableId);<br />
&nbsp;&nbsp;&nbsp; sysRecordTemplateSelect.load();<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Datensatzvorlagen f&uuml;r die Tabelle %1 in Verwendung: %2&quot;, new sysdictTable(tableId).label(),<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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enum2str(sysRecordTemplateSelect.parmPrompt()))); <br />
}
</div>]]></content>
<published>2011-08-12T20:08:40Z</published>
<updated>2011-08-12T20:08:40Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=414"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-07-06:id411</id><title type="text">Berichte lassen sich nicht über die Stapelverarbeitung ausführen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wenn bei einem Benutzer der Register Stapel beim Aufruf eines Berichtes nicht angezeigt wird, kann dies u.U. daran liegen, da&szlig; der Benutzer keine oder unzureichende&nbsp;Berechtigung f&uuml;r den Sicherheitsschl&uuml;ssel <em>Stapelverarbeitungsbericht</em> (SecurityKey <em>BatchReport</em>) hat.
</p>

<p><img width="465" height="206" border="0" alt="" title="Screenshot" src="http://www.schweda.net/pictures/blogpics/axReportBatchTab.jpg" />
</p>

<p><br />
&nbsp;
</p>]]></content>
<published>2011-07-06T19:24:09Z</published>
<updated>2011-07-06T19:24:09Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=411"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-06-29:id343</id><title type="text">Projekte nach einem bestimmten Objekt durchsuchen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Nachstehender Job durchsucht alle Projekte nach einem bestimmten Element des AOT.
</p>

<p>Daf&uuml;r wird ein Dialog verwendet, wo man lediglich den Namen und den Typ des zu suchenden Elementes angeben muss. Au&szlig;erdem kann man noch einstellen, ob auch die privaten Projektknoten durchsucht werden sollen.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">static void findObjectWithinProjects(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; UtilElementType utilElementType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = UtilElementType::Table;<br />
&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objectName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 'custTable';<br />
&nbsp;&nbsp;&nbsp; boolean&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; includePrivateProjects = false;<br />
&nbsp;&nbsp;&nbsp; Dialog&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dialog;<br />
&nbsp;&nbsp;&nbsp; DialogField&nbsp;&nbsp;&nbsp;&nbsp; df_objectName;<br />
&nbsp;&nbsp;&nbsp; DialogField&nbsp;&nbsp;&nbsp;&nbsp; df_utilElementType;<br />
&nbsp;&nbsp;&nbsp; DialogField&nbsp;&nbsp;&nbsp;&nbsp; df_privateProjects;<br />
&nbsp;&nbsp;&nbsp; container&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conProjects;<br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c;<br />
&nbsp;&nbsp;&nbsp; TreeNode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNodeRoot;<br />
&nbsp;&nbsp;&nbsp; SysOperationProgress&nbsp;&nbsp;&nbsp; sysOperationProgress;<br />
&nbsp;&nbsp;&nbsp; UtilElementType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; utilElementTypeSelection;&nbsp;&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; #Aot<br />
&nbsp;&nbsp;&nbsp; #TreeNodeSysNodeType<br />
&nbsp;&nbsp;&nbsp; #AviFiles<br />
&nbsp;&nbsp;&nbsp; #define.objectNameField(&quot;Name of object&quot;)<br />
&nbsp;&nbsp;&nbsp; #define.utilElementTypeField(&quot;Type of object&quot;)<br />
<br />
&nbsp;&nbsp;&nbsp; void findChildNodes(TreeNode _treeNodeParent, ProjectNode _projectNode, str _objectName)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TreeNode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TreeNodeIterator&nbsp;&nbsp;&nbsp; treeNodeIterator;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNodeIterator = _treeNodeParent.AOTiterator();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNode = treeNodeIterator.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (treeNode)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (treeNode.AOTgetNodeType() == #NT_PROJECT_GROUP)<br />
&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; findChildNodes(treeNode, _projectNode, _objectName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (treeNode.AOTname() like _objectName)<br />
&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; utilElementTypeSelection = str2enum(utilElementTypeSelection, enum2str(utilElementType)); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!utilElementType || treeNode.applObjectType() == utilElementTypeSelection)<br />
&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; if (!confind(conProjects, _projectNode.AOTname()))<br />
&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; conProjects = conins(conProjects, conlen(conProjects)+1, _projectNode.AOTname());<br />
&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; return;<br />
&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; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNode.treeNodeRelease();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNode = treeNodeIterator.next();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; void loopProjectsNode(TreeNode _treeNode)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProjectNode projectNode;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TreeNode treeNodeProject; <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_treeNode)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNodeProject = _treeNode.AOTfirstChild();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (treeNodeProject)<br />
&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; projectNode = treeNodeProject; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysOperationProgress.setText(projectNode.name()); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; findChildNodes(projectNode.loadForInspection(), treeNodeProject, objectName); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; treeNodeProject = treeNodeProject.AOTnextSibling();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; ; <br />
&nbsp;&nbsp;&nbsp; dialog = new Dialog();<br />
&nbsp;&nbsp;&nbsp; dialog.caption(&quot;Find projects containing specific object&quot;);<br />
&nbsp;&nbsp;&nbsp; df_objectName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = dialog.addField(Types::String, #objectNameField);<br />
&nbsp;&nbsp;&nbsp; df_utilElementType = dialog.addField(typeid(UtilElementType), #utilElementTypeField);<br />
&nbsp;&nbsp;&nbsp; df_privateProjects = dialog.addField(typeid(NoYesId), &quot;Include Private projects&quot;); <br />
&nbsp;&nbsp;&nbsp; df_objectName.value(objectName);<br />
&nbsp;&nbsp;&nbsp; df_utilElementType.value(utilElementType);<br />
&nbsp;&nbsp;&nbsp; df_privateProjects.value(includePrivateProjects); <br />
&nbsp;&nbsp;&nbsp; if( !dialog.run())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; objectName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = df_objectName.value();<br />
&nbsp;&nbsp;&nbsp; utilElementType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = df_utilElementType.value();<br />
&nbsp;&nbsp;&nbsp; includePrivateProjects = df_privateProjects.value(); <br />
&nbsp;&nbsp;&nbsp; if (objectName == '*' || objectName == '')<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw error(strfmt(&quot;@SYS26332&quot;, #objectNameField));<br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; setprefix(strfmt(&quot;Projects containing %1 '%2'&quot;, utilElementType, objectName)); <br />
<br />
&nbsp;&nbsp;&nbsp; startLengthyOperation(); <br />
<br />
&nbsp;&nbsp;&nbsp; sysOperationProgress = new SysOperationProgress();<br />
&nbsp;&nbsp;&nbsp; sysOperationProgress.setCaption(&quot;Searching&quot;);<br />
&nbsp;&nbsp;&nbsp; sysOperationProgress.setAnimation(#AviSearch); <br />
<br />
&nbsp;&nbsp;&nbsp; // Private projects<br />
&nbsp;&nbsp;&nbsp; if(includePrivateProjects)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; loopProjectsNode(SysTreeNode::getPrivateProject());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; // Shared projects<br />
&nbsp;&nbsp;&nbsp; loopProjectsNode(SysTreeNode::getSharedProject()); <br />
&nbsp;&nbsp;&nbsp; sysOperationProgress.kill();<br />
&nbsp;&nbsp;&nbsp; endLengthyOperation(); <br />
<br />
&nbsp;&nbsp;&nbsp; // List projects<br />
&nbsp;&nbsp;&nbsp; for(c=1;c&lt;=conlen(conProjects);c++)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info(conpeek(conProjects, c));<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p>Wer eine etwas komfortablere M&ouml;glichkeit vorzieht, der kann sich bei Firma Loncar Technologies Inc. ein entsprechendes XPO herunterladen und ins AX einspielen. AUf Basis dieses XPOs ist auch obiger Job entstanden.
</p>

<p><a href="https://www.loncartechnologies.com/download.php">https://www.loncartechnologies.com/download.php</a>
</p>]]></content>
<published>2011-06-29T19:36:37Z</published>
<updated>2011-06-29T19:36:37Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=343"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-06-24:id410</id><title type="text">Berechtigung eines Benutzers für einen Sicherheitsschlüssel abfragen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Mit diesem St&uuml;ckchen Code kann man in Dynamics AX pr&uuml;fen, welche Berechtigung ein Benutzer f&uuml;r einen bestimmten Sicherheitsschl&uuml;ssel (Securitykey) hat.
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">static void GetSecurityKeyAccess4User(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; Dictionary&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dictionary = new Dictionary();<br />
&nbsp;&nbsp;&nbsp; securityKeyId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; securityKeyId;<br />
&nbsp;&nbsp;&nbsp; SecurityKeySet&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; securityKeySet = new SecurityKeySet();<br />
&nbsp;&nbsp;&nbsp; UserId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; userId = 'user1';<br />
&nbsp;&nbsp;&nbsp; SelectableDataArea&nbsp; dataArea = 'ceu';<br />
&nbsp;&nbsp;&nbsp; AccessType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; accessType;<br />
&nbsp;&nbsp;&nbsp; ;<br />
<br />
&nbsp;&nbsp;&nbsp; securityKeyId = Dictionary.securityKeyName2Id(&quot;BatchReport&quot;);<br />
<br />
&nbsp;&nbsp;&nbsp; securityKeySet.loadUserRights(userId, dataArea);<br />
<br />
&nbsp;&nbsp;&nbsp; AccessType = securityKeySet.access(securityKeyId);<br />
<br />
&nbsp;&nbsp;&nbsp; info(enum2str(AccessType));<br />
}
</div>]]></content>
<published>2011-06-24T20:49:29Z</published>
<updated>2011-06-24T20:49:29Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=410"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-06-19:id409</id><title type="text">HDC-350: Kopieren der Aufnahmen von einer auf die andere Festplatte</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wer einmal die am Kabelreceiver <strong>HDC-350</strong> angeschlossene Festplatte gegen ein gr&ouml;ssere auszutauschen m&ouml;chte, der wird m&ouml;glicherweise vor der gleichen Frage stehen wie ich: Wie kopiere ich meine Aufnahmen von der alten auf die neue Festplatte?<br />
<br />
<a target="_blank" href="http://www.schweda.net/pictures/blogpics/pc2box21.jpg"><img width="160" height="139" border="0" align="right" alt="PC2Box 2.1" src="http://www.schweda.net/pictures/blogpics/pc2box21_small.jpg" style="padding-left: 10px; padding-bottom: 5px;" /></a>Erste Anlaufstelle war das Programm <strong>PC2Box </strong>in der Version 2.0. Leider stellt dieses Programm offenbar nur die M&ouml;glichkeit zur Verf&uuml;gung, die Aufnahmen auf dem PC zu sichern. In die umgekehrte Richtung scheint dies nicht vorgesehen zu sein. <br />
<br />
&Uuml;ber einen <a target="_blank" href="http://www.mgdjs.de/mgdjs/22/08/2010/pc2box-filme-vom-externen-speicher-des-dr-hd-201-auf-den-pc-kopieren-auf-cddvd-brennen-oder-anschauen/">Blogbeitrag</a> bin ich aber auf eine <a target="_blank" href="http://developer.berlios.de/forum/forum.php?forum_id=35951">die Version 2.1</a> des Programmes gestossen, welches u.a. genau diese M&ouml;glichkeit bietet. Und diese k&ouml;nnte einfacher nicht sein, daher im folgenden also eine kurze Anleitung, wie man die Aufnahmen von einer auf die andere Festplatte &uuml;bertr&auml;gt:
</p>

<ol>
    
<li>Sichern der Aufnahmen der alten Festplatte &uuml;ber die Schaltfl&auml;che &quot;speichere rec-Dateien&quot; in einem Verzeichnis seiner Wahl
</li>
    
<li>&Uuml;bertragen der gesicherten Aufnahmen &uuml;ber die Schaltfl&auml;che &quot;&uuml;bertrage rec-Dateien&quot; auf die neue Festplatte
</li>

</ol>

<p>Jetzt nur noch die neue Festplatte am Receiver anschliessen und schon kann man sich seine Aufnahmen ansehen. Super!
</p>

<p>Ein paar kleine Bugs beim Zur&uuml;ckschreiben der Aufnahmen auf die Festplatte sind mir dann aber doch aufgefallen:
</p>

<p>Zum einen bekommen die Aufnahmen einen neuen Namen, dieser scheinbar aus dem urspr&uuml;nglichen und dem jeweiligen Sicherungslaufwerk zusammengesetzt sind. Da man &uuml;ber PC2Box die Aufnahmen auch umbenennen kann ist dies aber kein wirkliches Problem. Ausserdem k&ouml;nnen aber u.U. die Sicherungen der Aufnahmen nicht immer zur&uuml;ckgespielt werden. Ich hatte z.b. mit einer Aufnahme die Sonderzeichen im Namen enthalten hat Schwierigkeiten. Erst als ich die Aufnahme im Verzeichnis des PCs entsprechend umbenannt habe, konnte ich die Aufnahme zur&uuml;ckspielen.
</p>

<p>Ein weiterer Sch&ouml;nheitsfehler war auch die Anzeige der L&auml;nge der Aufnahmen. Lt. Anzeige in PC2Box aber auch im Men&uuml; der DVB-C-Receivers wurde eine wesentlich k&uuml;rzere Laufzeit angezeigt. Beim Abspielen dieser Aufnahmen wurde aber dennoch die gesamt Laufzeit problemlos abgespielt.
</p>]]></content>
<published>2011-06-19T15:50:53Z</published>
<updated>2011-06-19T15:50:53Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=409"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-06-03:id405</id><title type="text">AX 2012: Having-Clause über addHavingFilter</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Ein neues Feature von Dynamics AX 2012 ist, da&szlig; man nun bei einem Query auch eine HAVING-Clause hinzuf&uuml;gen kann.
</p>

<p>Diese Bedingung dient dazu, berechnete Werte einer GROUP BY-Clause in der WHERE-Clause ber&uuml;cksichtigen zu k&ouml;nnen. Dieses Feature habe ich in der Vergangenheit vor allem dazu gerne verwendet, wenn es darum ging &uuml;ber eine SQL-Abfrage doppelte (bzw. vielfache) Datens&auml;tze in einer Tabelle zu finden. In den bisherigen Versionen von Dynamics AX war dieses Unterfangen &uuml;ber X++ nur etwas umst&auml;ndlich abzubilden.
</p>


<p>Microsoft hat dies scheinbar eingesehen, und stellt nun bei der Verwendung von Queries die Methode <strong>addHavingFilter </strong>zur Verf&uuml;gung.
</p>

<p>Im folgenden Beispiel m&ouml;chte ich mir z.B. alle Kundengruppen ausgeben lassen, die bei mehr als nur einem Kunden hinterlegt sind.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void AX2012_QueryHavingFilter(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; query query;<br />
&nbsp;&nbsp;&nbsp; QueryRun QueryRun;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource QueryBuildDataSource;<br />
&nbsp;&nbsp;&nbsp; CustTable custTable;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; setPrefix(funcName());<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // Build query<br />
&nbsp;&nbsp;&nbsp; query = new query();<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource = query.addDataSource(tableNum(CustTable));<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // Add fields to query<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource.addGroupByField(fieldNum(CustTable, CustGroup));<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource.addSelectionField(fieldNum(custTable, recId), SelectionField::Count);

<p>&nbsp;
</p>

<p>&nbsp;&nbsp;&nbsp; // Add having filter to query&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; query.addHavingFilter(QueryBuildDataSource, &quot;recId&quot;, AggregateFunction::Count).value(&quot;&gt;1&quot;);<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; // Run query<br />
&nbsp;&nbsp;&nbsp; QueryRun = new QueryRun(query);<br />
&nbsp;&nbsp;&nbsp; while(QueryRun.next())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; custTable = QueryRun.get(tableNum(custTable));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Custgroup %1: %2 records&quot;, custTable.CustGroup, custTable.RecId));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }<br />
}
</p>

</div>

<p>Das Ergebnis der oben stehenden Abfrage ohne <strong>addHavingFilter </strong>w&uuml;rde wie folgt aussehen:
</p>

<p><img width="232" height="170" border="0" src="http://www.schweda.net/pictures/blogpics/ax2012_addhavingfilter_1.jpg" alt="" />
</p>

<p>Mit <strong>addHavingFilter </strong>&auml;ndert sich das Ergebnis/Infolog:
</p>

<p><img width="228" height="131" border="0" src="http://www.schweda.net/pictures/blogpics/ax2012_addhavingfilter_2.jpg" alt="" />
</p>

<p>Nat&uuml;rlich kann man dieses neue Feature auch bei einem im AOT definierten Query einsetzen.&nbsp;Dazu gibt es einen neuen, entsprechend benannten&nbsp;Knoten:
</p>

<p><img width="296" height="241" border="0" src="http://www.schweda.net/pictures/blogpics/ax2012_addhavingfilter_aot.jpg" alt="" />&nbsp;
</p>]]></content>
<published>2011-06-03T21:41:54Z</published>
<updated>2011-06-03T21:41:54Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=405"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-06-02:id408</id><title type="text">AX 2012: AddQueryFilter</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>In Dynamics AX 2012 verf&uuml;gt das Query-Objekt nun &uuml;ber eine neue Methode <strong>addQueryFilter</strong>, mit dessen Hilfe man die Datenbank anweisen kann, Filterkriterien zur WHERE-Clause statt wie in bisherigen Versionen zur ON-Clause einer OUTER-JOIN-Verkn&uuml;pfung hinzuzuf&uuml;gen. Dies wiederum bewirkt, da&szlig; keine &quot;leeren&quot; Datens&auml;tze verarbeitet werden.
</p>


<p>Mit folgendem Beispiel m&ouml;chte ich die Anwendung dieser neuen Methode demonstrieren.
</p>

<p>Aufgabenstellung: Es sollen alle Auftr&auml;ge des Kunden 1102 ausgegeben werden, bei denen zumindest eine Auftragsposition existiert, bei der die bestellte Menge gr&ouml;&szlig;er 1 ist.
</p>

<p>Die Datenbank enth&auml;lt im Beispiel folgende Auftragsk&ouml;pfe und die zugeh&ouml;rigen Positionen. Der Auftrag 00000011_050 besitzt keine Positionen (und ist somit ein Auftrag, der nicht ausgegeben werden soll).
</p>

<table width="382" cellspacing="1" cellpadding="1" border="0">
    <tbody>
        <tr>
            
<td><strong>SalesId</strong>
</td>
            
<td><strong>SalesName</strong>
</td>
            
<td><strong>CustAccount</strong>
</td>
        </tr>
        <tr>
            
<td>00000002_050
</td>
            
<td>Adventure Works
</td>
            
<td>1102
</td>
        </tr>
        <tr>
            
<td>00000010_050
</td>
            
<td>Adventure Works
</td>
            
<td>1102
</td>
        </tr>
        <tr>
            
<td>00000011_050
</td>
            
<td>Adventure Works
</td>
            
<td>1102
</td>
        </tr>
        <tr>
            
<td>&nbsp;
</td>
            
<td>&nbsp;
</td>
            
<td>&nbsp;
</td>
        </tr>
        <tr>
            
<td><strong>SalesId</strong>
</td>
            
<td><strong>ItemId</strong>
</td>
            
<td><strong>QtyOrdered</strong>
</td>
        </tr>
        <tr>
            
<td>00000002_050
</td>
            
<td>1290
</td>
            
<td>10
</td>
        </tr>
        <tr>
            
<td>00000010_050
</td>
            
<td>1291
</td>
            
<td>20
</td>
        </tr>
    </tbody>

</table>

<p>Ohne die Methode <strong>addQueryFilter </strong>w&uuml;rde der nachstehende Job alle Auftr&auml;ge ausgeben, bei denen dies zutrifft. Es w&uuml;rden aber auch alle Auftragsk&ouml;pfe ohne Auftragspositionen ausgegeben.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 174px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void ax2012_addQueryFilter(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; Query query;<br />
&nbsp;&nbsp;&nbsp; QueryRun queryRun;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource qbs_SalesTable;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource qbs_SalesLine;<br />
&nbsp;&nbsp;&nbsp; SalesTable salesTable;<br />
&nbsp;&nbsp;&nbsp; SalesLine salesLine;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; query = new Query();<br />
&nbsp;&nbsp;&nbsp; qbs_SalesTable = query.addDataSource(tableNum(SalesTable));<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine = qbs_SalesTable.addDataSource(tableNum(SalesLine));<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine.joinMode(JoinMode::OuterJoin);&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine.addLink(fieldNum(SalesTable, SalesId), fieldNum(SalesLine, SalesId));<br />
<br />
&nbsp;&nbsp;&nbsp; SysQuery::findOrCreateRange(qbs_SalesTable, fieldNum(SalesTable, CustAccount)).value('1102');<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>SysQuery::findOrCreateRange(qbs_SalesLine, fieldNum(salesLine, QtyOrdered)).value('1..');<br />
<br />
</strong>&nbsp;&nbsp;&nbsp; queryRun = new QueryRun(query);<br />
<br />
&nbsp;&nbsp;&nbsp; while(queryRun.next())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesTable = queryRun.get(tableNum(SalesTable));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesLine = queryRun.get(tableNum(SalesLine));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Sales order %1: %2&quot;, salesTable.SalesId, salesLine.QtyOrdered));<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p><u>Resultierendes SQL-Statement</u>
</p>

<p>SELECT * FROM SalesTable(SalesTable_1) <br />
WHERE ((CustAccount = N'1102')) <br />
OUTER JOIN * FROM SalesLine(SalesLine_1)&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; ON SalesTable.SalesId = SalesLine.SalesId AND ((QtyOrdered&gt;=1.E0))
</p>

<p><u>Ergebnis</u>
</p>

<p>Info&nbsp;Meldung (18:39:50)&nbsp;Sales order 00000002_050: 10,00<br />
Info&nbsp;Meldung (18:39:50)&nbsp;Sales order 00000010_050: 20,00<br />
Info&nbsp;Meldung (18:39:50)&nbsp;Sales order 00000011_050: 0,00
</p>

<p>Bei Verwendung von <strong>addQueryFilter </strong>werden nur noch jene Auftr&auml;ge ausgegeben, die eine Position mit der gew&uuml;nschten Menge enthalten. Auftr&auml;ge ohne Positionen werden allerdings nicht verarbeitet.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 174px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void ax2012_addQueryFilter(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; Query query;<br />
&nbsp;&nbsp;&nbsp; QueryRun queryRun;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource qbs_SalesTable;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource qbs_SalesLine;<br />
&nbsp;&nbsp;&nbsp; SalesTable salesTable;<br />
&nbsp;&nbsp;&nbsp; SalesLine salesLine;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; query = new Query();<br />
&nbsp;&nbsp;&nbsp; qbs_SalesTable = query.addDataSource(tableNum(SalesTable));<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine = qbs_SalesTable.addDataSource(tableNum(SalesLine));<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine.joinMode(JoinMode::OuterJoin);<br />
<br />
&nbsp;&nbsp;&nbsp; qbs_SalesLine.addLink(fieldNum(SalesTable, SalesId), fieldNum(SalesLine, SalesId));<br />
<br />
&nbsp;&nbsp;&nbsp; SysQuery::findOrCreateRange(qbs_SalesTable, fieldNum(SalesTable, CustAccount)).value('1102');<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>query.addQueryFilter(qbs_SalesLine, identifierStr(QtyOrdered)).value(&quot;1..&quot;);<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; queryRun = new QueryRun(query);<br />
<br />
&nbsp;&nbsp;&nbsp; while(queryRun.next())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesTable = queryRun.get(tableNum(SalesTable));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesLine = queryRun.get(tableNum(SalesLine));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info(strFmt(&quot;Sales order %1: %2&quot;, salesTable.SalesId, salesLine.QtyOrdered));<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p><u>Resultierendes SQL-Statement</u>
</p>

<p>SELECT * FROM SalesTable(SalesTable_1) <br />
WHERE ((CustAccount = N'1102')) <br />
OUTER JOIN * FROM SalesLine(SalesLine_1) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ON SalesTable.SalesId = SalesLine.SalesId <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WHERE ((SalesLine(SalesLine_1).QtyOrdered&gt;=1.E0))
</p>

<p><u>Ergebnis</u>
</p>

<p>Info&nbsp;Meldung (18:41:05)&nbsp;Sales order 00000002_050: 10,00<br />
Info&nbsp;Meldung (18:41:05)&nbsp;Sales order 00000010_050: 20,00
</p>

<p>Der Auftrag 00000011_050 wird nicht mehr aufgef&uuml;hrt.<br />
&nbsp;
</p>]]></content>
<published>2011-06-02T19:18:37Z</published>
<updated>2011-06-02T19:18:37Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=408"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-05-27:id406</id><title type="text">AX 2012: ValidTimeState</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wer schon einmal eine Tabelle aufbauen musste, wo die einzelnen Datens&auml;tze &uuml;ber Datumsfelder nur f&uuml;r einen bestimmten Zeitraum g&uuml;ltig sind - &auml;hnlich den Handelsvereinbarungen -, wei&szlig; wieviel Entwicklungsaufwand daintersteckt, wenn sichergestellt werden muss, da&szlig; sich die G&uuml;ltigkeitsbereiche der einzelnen Datens&auml;tze nicht &uuml;berschneiden.
</p>

<p>Dynamics AX 2012 stellt hierf&uuml;r eine neue Tabelleneigenschaft <strong>validTimeStateFieldType </strong>zur Verf&uuml;gung, mit deren Hilfe sich Dynamics AX selbst um vieles k&uuml;mmert.
</p>

<p>Beim Anlegen einer solchen Tabelle muss nur auf die folgenden Punkte geachtet werden:
</p>

<ol>
    
<li>Eigenschaft <strong>ValidTimeStateFieldType </strong>der Tabelle auf <em>Date </em>oder <em>UtcDateTime </em>setzen. Dadurch erstellt AX zwei neue Felder <em>ValidFrom </em>und <em>ValidTo </em>in der Tabelle. <br />
    <br />
    <img width="321" height="53" border="0" alt="" src="http://www.schweda.net/pictures/blogpics/ax2012_validtimestate_tableproperties.jpg" /><br />
    &nbsp;
</li>
    
<li>Man muss einen eindeutigen Index erstellen, der die entscheidenden Felder der Tabelle sowie die Felder <em>ValidFrom </em>und<em> ValidTo </em>enth&auml;lt. <br />
    <br />
    <img width="342" height="309" border="0" alt="" src="http://www.schweda.net/pictures/blogpics/ax2012_validtimestate_table.jpg" /><br />
    &nbsp;
</li>
    
<li>Bei diesem Index m&uuml;ssen bestimmte Eigenschaften gesetzt werden. <br />
    <br />
    <img width="247" height="213" border="0" alt="" src="http://www.schweda.net/pictures/blogpics/ax2012_validtimestate_indexproperties.jpg" />
</li>

</ol>


<p>Ab diesem Zeitpunkt k&uuml;mmert sich Dynamics AX vollst&auml;ndig um das Aktualisieren von nicht mehr g&uuml;ltigen Datens&auml;tzen und die entscheidenden Pr&uuml;fungen beim Anlegen von neuen Datens&auml;tzen.
</p>

<p>Aber auch das Selektieren des jeweils g&uuml;ltigen Datensatzes f&uuml;r ein bestimmtes Datum wird wesentlich erleichert. Daf&uuml;r stellt Dynamics AX das Keyword <strong>validTimeState </strong>zur Verf&uuml;gung.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 174px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void AX2012_ValidTimeStateUtcDateTime(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; AX2012_ValidTimeStateUtcDateTime AX2012_ValidTimeStateUtcDateTime;<br />
&nbsp;&nbsp;&nbsp; utcDateTime myUtcDateTime = 1988-07-20T13:34:45;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; select validTimeState(myUtcDateTime) * from AX2012_ValidTimeStateUtcDateTime;<br />
<br />
&nbsp;&nbsp;&nbsp; info(strFmt(&quot;%1&quot;, AX2012_ValidTimeStateUtcDateTime.salesPrice));<br />
}
</div>

<p>&nbsp;
</p>]]></content>
<published>2011-05-27T18:08:04Z</published>
<updated>2011-05-27T18:08:04Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=406"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-05-22:id359</id><title type="text">Notizen zu Systemdatum und -zeit</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Im folgenden sind einige Gedankennotizen zum Thema Datum und Uhrzeit in Dynamics AX notiert.
</p>

<p>In allen mir bekannten Versionen von Dynamics AX bzw. Axapta gibt es die nachstehenden Funktionen:
</p>

<table width="460" cellspacing="0" cellpadding="2" valign="top" style="border-bottom: rgb(235,235,235) thin solid; border-left: rgb(235,235,235) thin solid; margin-left: 10px; border-top: rgb(235,235,235) thin solid; margin-right: 10px; border-right: rgb(235,235,235) thin solid">
    <tbody>
        <tr>
            
<td valign="top" bgcolor="#ebebeb" style="border-bottom: rgb(235,235,235) thin solid"><strong>Funktionsaufruf</strong>
</td>
            
<td valign="top" bgcolor="#ebebeb" style="border-bottom: rgb(235,235,235) thin solid"><strong>Beschreibung</strong>
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">today()
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Datum des Clients
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">systemDateGet()
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Sitzungsdatum von AX
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">timeNow()
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Zeit des Clients
</td>
        </tr>
    </tbody>

</table>

<p>Ab Dynamics AX 2009 stehen u.a. zus&auml;tzliche folgende Funktionen zur Verf&uuml;gung:&nbsp;
</p>

<table width="460" cellspacing="0" cellpadding="2" valign="top" style="border-bottom: rgb(235,235,235) thin solid; border-left: rgb(235,235,235) thin solid; margin-left: 10px; border-top: rgb(235,235,235) thin solid; margin-right: 10px; border-right: rgb(235,235,235) thin solid">
    <tbody>
        <tr>
            
<td width="260" valign="top" bgcolor="#ebebeb" style="border-bottom: rgb(235,235,235) thin solid"><strong>Funktionsaufruf</strong>
</td>
            
<td valign="top" bgcolor="#ebebeb" style="border-bottom: rgb(235,235,235) thin solid"><strong>Beschreibung</strong>
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">DateTimeUtil::getSystemDateTime()
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Sitzungsdatum/-zeit von AX in koordinierter Weltzeit&nbsp;
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">DateTimeUtil::applyTimeZoneOffset<br />
            (<br />
            &nbsp;&nbsp;&nbsp; DateTimeUtil::getSystemDateTime(),<br />
            &nbsp;&nbsp;&nbsp; DateTimeUtil::getUserPreferredTimeZone()<br />
            )
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Sitzungsdatum/-zeit von AX f&uuml;r die aktuelle Zeitzone des Benutzers
</td>
        </tr>
        <tr>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">&nbsp;DateTimeUtil::date(_utcDateTime)
</td>
            
<td valign="top" style="border-bottom: rgb(235,235,235) thin solid">Wandelt einen UTC-Datetime-Wert in ein Datum um
</td>
        </tr>
    </tbody>

</table>

<p>In allen bisherigen Dynamics AX-Versionen wird das Systemdatum und die Systemzeit von der Zeit des Clients&nbsp;initialisiert. Diese Werte kann sich der Benutzer - entsprechende Rechte vorausgesetzt - jederzeit selbst &auml;ndern (die Uhrzeit allerdings erst seit Dynamics AX 2009). Sie gelten aber nur f&uuml;r die aktuelle Sitzung und nur f&uuml;r diesen Benutzer.<br />
&nbsp;<br />
&Uuml;brigens: Erh&auml;lt man unter AX 2009 beim Starten eines AX-Clients die nachstehende Fehlermeldung, sollte man die Zeitzone des aktuellen Benutzers in den Benutzeroptionen pr&uuml;fen.
</p>

<blockquote>

<p>Die Zeitzone des lokalen Comuters stimmt nicht mit den Einstellungen f&uuml;r die bevorzugte Zeitzone &uuml;berein.
</p>

</blockquote>

<p>&nbsp;
</p>]]></content>
<published>2011-05-22T19:22:55Z</published>
<updated>2011-05-22T19:22:55Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=359"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-05-08:id401</id><title type="text">Druckeinstellungen und Filterkriterien eines Reports vorbelegen II</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wie startet man einen Bericht, bei dem eine von <em>RunBaseReport </em>abgeleitete Klasse vorgeschalten ist, per X++ und gibt diesem Bericht bereits den Query und die Druckeinstellungen vor?
</p>

<p>Wer sich diese Frage schon einmal stellen musste, f&uuml;r den liefert dieser Blogbeitrag vielleicht einen L&ouml;sungsansatz:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">static void setPrintJobSettingsQuery4ReportClass_II(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; custReport&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; custReport = new custReport();<br />
&nbsp;&nbsp;&nbsp; printJobSettings&nbsp;&nbsp;&nbsp; printJobSettings = new printJobSettings();<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; custReport.makeReportRun();<br />
<br />
&nbsp;&nbsp;&nbsp; // Modify Query<br />
&nbsp;&nbsp;&nbsp; sysQuery::findOrCreateRange(custReport.reportRun().query().dataSourceTable(tableNum(custTable)), fieldNum(custTable, custGroup)).value(queryValue('10'));<br />
&nbsp;&nbsp;&nbsp; custReport.reportRun().query().interactive(false);<br />
&nbsp;&nbsp;&nbsp; // Create printJobSettings<br />
&nbsp;&nbsp;&nbsp; printJobSettings.setTarget(PrintMedium::File);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.format(PrintFormat::PDF);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.fileName(@&quot;C:\Temp\CustTableReport.pdf&quot;);<br />
<br />
&nbsp;&nbsp;&nbsp; // Apply printJobSettings<br />
&nbsp;&nbsp;&nbsp; custReport.reportRun().printJobSettings(printJobsettings.packPrintJobSettings());<br />
&nbsp;&nbsp;&nbsp; custReport.reportRun().report().interactive(false);&nbsp; // Disable default printer-dialog<br />
&nbsp;&nbsp;&nbsp; custReport.reportRun().run();<br />
}
</div>

<p>Obiger Code instanziiert ein Objekt der Klasse <em>custReport</em>, deren wesentliche Methoden wie folgt aussehen:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">class CustReport extends runBaseReport<br />
{<br />
}<br />
public identifiername lastValueElementName()<br />
{<br />
&nbsp;&nbsp;&nbsp; identifiername ret;<br />
<br />
&nbsp;&nbsp;&nbsp; //ret = super();<br />
<br />
&nbsp;&nbsp;&nbsp; ret = reportStr(Cust);<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}<br />
static ClassDescription description()<br />
{<br />
&nbsp;&nbsp;&nbsp; return &quot;Custreport&quot;;<br />
}<br />
static void main(Args args)<br />
{<br />
&nbsp;&nbsp;&nbsp; CustReport&nbsp; CustReport;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; CustReport = new CustReport();<br />
&nbsp;&nbsp;&nbsp; if (CustReport.prompt())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CustReport.run();<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p>&nbsp;
</p>]]></content>
<published>2011-05-08T16:00:08Z</published>
<updated>2011-05-08T16:00:08Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=401"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-05-04:id407</id><title type="text">Tabellen mit doppelten RecIDs ermitteln</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Wenn man in Dynamics AX (4.0&nbsp;bzw. 2009) einmal in die Verlegenheit kommen sollte, pr&uuml;fen zu m&uuml;ssen, ob es Tabellen mit doppelten RecIDs gibt, kann folgendes SQL-Script dabei behilflich sein:
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">use ax2009_standard_sp1<br />
<br />
set nocount on<br />
<br />
declare @tableName char(100)<br />
declare&nbsp;db_cursor CURSOR FOR&nbsp; <br />
&nbsp;select name from dbo.sysobjects<br />
&nbsp;where xtype = 'U'<br />
&nbsp;and name &lt;&gt; 'ROWSTAT'<br />
&nbsp;and name &lt;&gt; 'ROWSTATUS'<br />
&nbsp;and name &lt;&gt; 'SQLSYSTEMVARIABLES'<br />
&nbsp;and name &lt;&gt; 'SYSTEMSEQUENCES'&nbsp;-- SYSTEMSEQUENCES enth&auml;lt immer doppelte RecIDs<br />
&nbsp;order by name asc<br />
<br />
-- Tempor&auml;re Tabelle aufbauen<br />
IF OBJECT_ID(N'tempdb..#tmp_duplrecid', N'U') IS NOT NULL <br />
&nbsp;begin<br />
&nbsp;drop table #tmp_duplrecid<br />
&nbsp;end<br />
<br />
create table #tmp_duplrecid (tablename char(100), recordcounter bigint)&nbsp;&nbsp;&nbsp;<br />
<br />
OPEN db_cursor&nbsp;&nbsp; <br />
FETCH NEXT FROM db_cursor INTO @tableName&nbsp;&nbsp;<br />
<br />
WHILE @@FETCH_STATUS = 0&nbsp;&nbsp; <br />
&nbsp;BEGIN&nbsp;&nbsp; <br />
&nbsp;&nbsp;-- Tabellen mit doppelten RecIds ermitteln<br />
&nbsp;&nbsp;exec('insert into #tmp_duplrecid select ''' + @tableName + ''', COUNT(*)' + <br />
&nbsp;&nbsp;&nbsp; ' from ' + @tableName +<br />
&nbsp;&nbsp;&nbsp; ' group by RECID ' + <br />
&nbsp;&nbsp;&nbsp; ' having COUNT(*) &gt; 1')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;FETCH NEXT FROM db_cursor INTO @tableName&nbsp;&nbsp; <br />
&nbsp;END&nbsp;&nbsp;<br />
<br />
CLOSE db_cursor&nbsp;&nbsp; <br />
DEALLOCATE db_cursor<br />
<br />
set nocount off<br />
<br />
-- Tabellen mit doppelten RecIDs<br />
select 'Tabelle enth&auml;lt doppelte RecIDs: ' + tablename<br />
from #tmp_duplrecid
</div>

<p><br />
&nbsp;
</p>]]></content>
<published>2011-05-04T20:06:34Z</published>
<updated>2011-05-04T20:06:34Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=407"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-04-30:id400</id><title type="text">Report auf Basis eines Query erstellen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Die Feldgruppe <em>AutoReport </em>einer Tabelle und deren Verwendung beim Drucken von Daten aus einem Formular heraus sollte wohl jedem Dynamics AX-Entwickler bekannt sein. Auf Basis dieser Standard-Funktionalit&auml;t habe ich versucht, selbst einen Report per X++ auf Basis eines Query zu erstellen.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 260px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void createReportFromQuery(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; query&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query;<br />
&nbsp;&nbsp;&nbsp; report&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; report;<br />
&nbsp;&nbsp;&nbsp; reportDesign&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportDesign;<br />
&nbsp;&nbsp;&nbsp; reportRun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportRun;<br />
&nbsp;&nbsp;&nbsp; reportSection&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportSection;<br />
&nbsp;&nbsp;&nbsp; sysReportRun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun;<br />
&nbsp;&nbsp;&nbsp; reportName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportTemplateName = 'FrontPage';<br />
&nbsp;&nbsp;&nbsp; reportAutoDesignSpecs&nbsp;&nbsp; reportAutoDesignSpecs;<br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ds;<br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f;<br />
&nbsp;&nbsp;&nbsp; printJobSettings&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printJobSettings = new printJobSettings();<br />
&nbsp;&nbsp;&nbsp; ;<br />
<br />
&nbsp;&nbsp;&nbsp; // Build query<br />
&nbsp;&nbsp;&nbsp; query = new query();<br />
&nbsp;&nbsp;&nbsp; query.addDataSource(tableNum(vendTable));<br />
<br />
&nbsp;&nbsp;&nbsp; query.dataSourceTable(tableNum(vendTable)).addRange(fieldNum(vendTable, vendGroup)).value('10');<br />
<br />
&nbsp;&nbsp;&nbsp; // Add Fieldlist<br />
&nbsp;&nbsp;&nbsp; query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, accountNum));<br />
&nbsp;&nbsp;&nbsp; query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, name));<br />
&nbsp;&nbsp;&nbsp; query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, vendgroup));<br />
<br />
&nbsp;&nbsp;&nbsp; // Build printJobSettings<br />
&nbsp;&nbsp;&nbsp; printJobSettings.setTarget(PrintMedium::Screen);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.format(PrintFormat::PDF);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.fileName(@&quot;C:\Temp\TempReportFromQuery.pdf&quot;);<br />
<br />
&nbsp;&nbsp;&nbsp; // Create report<br />
&nbsp;&nbsp;&nbsp; if( hasSecurityKeyAccess(securityKeyNum(SysDevelopment),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AccessType::Edit))<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; report = new report();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = classfactory.reportRunClass(new Args(reportstr(SysReportAuto)));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun.init();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun.query(query);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!sysReportRun.queryRun())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun.queryRun(new SysQueryRun(query));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sysReportRun.queryRun().query().interactive(false); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Report.interactive(!printJobSettings);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Report.query(sysReportRun.queryRun().query());<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportDesign = report.addDesign();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportDesign.caption(&quot;TmpReportFromQuery&quot;);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportDesign.reportTemplate(reportTemplateName);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportDesign.orientation(printerOrientation::Auto);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportAutoDesignSpecs = reportDesign.autoDesignSpecs();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Add body<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(ds = 1;ds &lt;= sysReportRun.queryRun().query().dataSourceCount(); ds++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportSection = reportAutoDesignSpecs.addSection(reportBlockType::Body, sysReportRun.queryRun().query().dataSourceNo(ds).table());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportSection.arrangeMethod(arrangeMethod::Vertical);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(f=1;f&lt;=sysReportRun.queryRun().query().dataSourceNo(ds).selectionCount();f++)<br />
&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; reportSection.addControl(sysReportRun.queryRun().query().dataSourceNo(ds).table(), sysReportRun.queryRun().query().dataSourceNo(ds).fields().field(f));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Run the report<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportRun = new reportRun(report);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportRun.fetch();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Print the report<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(printJobSettings)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportRun.printJobSettings(printJobSettings.packPrintJobSettings());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reportRun.print();<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p>Das Ergebnis des obigen Codes ist ein sehr einfacher Report:
</p>


<p><a href="http://www.schweda.net/pictures/blogpics/ax_report_from_query.jpg" target="_blank"><img width="465" height="220" border="0" src="http://www.schweda.net/pictures/blogpics/ax_report_from_query_small.jpg" alt="" /></a>
</p>]]></content>
<published>2011-04-30T15:35:21Z</published>
<updated>2011-04-30T15:35:21Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=400"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-03-26:id404</id><title type="text">Arbeiten mit dem aufrufenden Objekt einer Form (Caller) II</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Um in einem Objekt auf das aufrufende Objekte zuzugreifen, habe ich anhand von einigen Zeilen Code ja schon demonstriert (siehe <a href="http://www.schweda.net/blog_ax.php?nid=caller1" target="_self">hier</a> und <a href="http://www.schweda.net/blog_ax.php?nid=caller2" target="_self">hier</a>). Dabei hatte ich aber immer ausser Acht gelassen, da&szlig; das aufrufende Objekt ja auch eine Klasse sein kann.
</p>

<p>Nun also hier ein entsprechendes Beispiel:
</p>

<div style="padding: 5px; overflow: scroll; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 140px; color: rgb(0, 0, 0); font-size: 12px;">if(element.args() &amp;&amp; <br />
&nbsp;&nbsp; element.args().caller() &amp;&amp; <br />
&nbsp;&nbsp; classidget(element.args().caller()) == classnum(<em>nameOfClass</em> )) <br />
{<br />
&nbsp;&nbsp;&nbsp; <em>nameOfClass</em> = element.args().caller();<br />
&nbsp;&nbsp;&nbsp; <em>someValue</em> = <em>nameOfClass</em>.<em>someMethod</em>();<br />
}
</div>

<p>&nbsp;
</p>]]></content>
<published>2011-03-26T18:31:34Z</published>
<updated>2011-03-26T18:31:34Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=404"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-03-20:id403</id><title type="text">Praktisches Beispiel für die Verwendung des runBaseBatch-Frameworks</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Vor einiger Zeit habe ich schon einmal einen <a title="Das runBaseBatch-Framework" target="_self" href="http://www.schweda.net/blog_ax.php?nid=runbasebatch1">Eintrag &uuml;ber das runBaseBatch-Framework</a> geschrieben, damals ging's haupts&auml;chlich um die Aufrufreihenfolge der einzelnen Methoden und wof&uuml;r einige von diesen Methoden verwendet werden k&ouml;nnen bzw. verwendet werden sollten.
</p>

<p>Nun habe ich mir basierend auf diesem Eintrag und vor allem aufgrund von Erfahrungswerten eine eigene Klasse namens <strong>t</strong><strong>utorial_ClassWithQueryRun</strong> geschrieben, die einige oft ben&ouml;tigte Anforderungen vereinigt.
</p>

<p>Diese Klasse vereint folgende F&auml;higkeiten:
</p>

<ul>
    
<li>Stapelf&auml;higkeit
</li>
    
<li>Arbeiten mit einem Query
</li>
    
<li>Arbeiten mit den Nutzungsdaten
</li>
    
<li>Schreiben einer Datei via textIo
</li>
    
<li>Arbeiten mit Fortschrittsanzeigen (progress)
</li>

</ul>

<p><a target="_blank" href="http://www.schweda.net/pictures/blogpics/Tutorial_ClassWithQueryRun.jpg"><img width="465" height="176" title="Screenshot" alt="Screenshot" border="0" src="http://www.schweda.net/pictures/blogpics/Tutorial_ClassWithQueryRun_small.jpg" /></a>
</p>


<p>Die erste Anforderung an meine Klasse war, da&szlig; sie stapelf&auml;hig sein musste. Dazu muss sie in erster Linie von runBaseBatch abgeleitet sein, und einige Methoden wie&nbsp;<a href="#pack">pack</a>, <a href="#unpack">unPack</a>, <a href="#cangobatch">canGoBatch</a>&nbsp;m&uuml;ssen entsprechend &uuml;berschrieben werden.
</p>

<p>Die n&auml;chste Anforderung war, da&szlig; der Benutzer &uuml;ber einen Dialog Parameter festlegen kann. Daf&uuml;r verantwortlich sind u.a. <a href="#dialog">dialog</a>, <a href="#getfromdialog">getFromDialog</a>, <a href="#puttodialog">putToDialog</a> und nat&uuml;rlich die <a href="#main">main</a>-Methode.
</p>

<p>Weiters sollte &uuml;ber diesen Dialog der Benutzer die M&ouml;glichkeit erhalten, einen definierten Query durch Filter und drgl. zu erweitern. Dazu mussten folgende Methoden angepasst/erstellt werden: <a href="#initquery">initQuery</a>, <a href="#showqueryvalues">showQueryValues</a> und <a href="#showqueryselectbutton">showQuerySelectButton</a>.
</p>

<p>Die Methoden <a href="#filenamelookupfilter">filenameLookupFilter</a>, <a href="#filenamelookupinitialpath">filenameLookupInitialPath</a> und <a href="#filenamelookuptitle">filenameLookupTitle</a> wurden dazu verwendet, dem Benutzer im Dialog die Eingabe des Dateinamens zu erleichtern.
</p>

<p>Starten wir also mit der <strong>classDeclaration</strong>. Hier erfolgt die Ableitung von <em>runBaseBatch</em>, weiters werden s&auml;mtliche Objektvariablen deklariert, die innerhalb der Methoden ben&ouml;tigt werden. Au&szlig;erdem wird hier &uuml;ber das Makro <em>currentList </em>festgelegt, welche dieser Variablen in den Nutzungsdaten gespeichert werden.
</p>

<div style="padding: 5px; width: 460px; height: 284px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">class tutorial_ClassWithQueryRun extends RunBaseBatch<br />
{<br />
&nbsp;&nbsp;&nbsp; FilenameSave&nbsp;&nbsp;&nbsp; filenameSave;<br />
&nbsp;&nbsp;&nbsp; DialogField&nbsp;&nbsp;&nbsp;&nbsp; df_FileNameSave;<br />
&nbsp;&nbsp;&nbsp; DialogField&nbsp;&nbsp;&nbsp;&nbsp; df_DialogDate;<br />
&nbsp;&nbsp;&nbsp; date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dialogDate;<br />
&nbsp;&nbsp;&nbsp; SysQueryRun&nbsp;&nbsp;&nbsp;&nbsp; queryrun;<br />
&nbsp;&nbsp;&nbsp; TextIo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textIo;<br />
&nbsp;&nbsp;&nbsp; #file<br />
&nbsp;&nbsp;&nbsp; #define.CurrentVersion(2)<br />
&nbsp;&nbsp;&nbsp; #define.Version1(1)<br />
&nbsp;&nbsp;&nbsp; #localmacro.CurrentList<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fileNameSave,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dialogDate,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryRun<br />
&nbsp;&nbsp;&nbsp; #endmacro<br />
}
</div>

<p><a name="pack"></a>In der <strong>pack</strong>-Methode werden nun &uuml;ber einen Container jene Variablen in die Nutzungsdaten geschrieben, die z.B. f&uuml;r die korrekte Abarbeitung innerhalb der Stapelverarbeitung von N&ouml;ten sind.
</p>

<div style="padding: 5px; width: 460px; height: 149px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public container pack()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; return [ #CurrentVersion,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filenameSave,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dialogDate,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryrun.pack() ];<br />
}
</div>

<p><a name="unpack"></a>Die Methode <strong>unPack </strong>ist das Gegenst&uuml;ck zur pack-Methode, ist sie doch f&uuml;r das Auslesen der Werte aus den Nutzungsdaten zust&auml;ndig. Wichtig hierbei ist nat&uuml;rlich, da&szlig; die Werte innerhalb des Containers in der gleichen Reihenfolge ausgelesen werden, wie sie geschrieben wurden. Weiters ist hier daf&uuml;r zu sorgen, da&szlig; unterschiedliche Nutzungsdaten der Klasse &uuml;ber die entsprechende Versionsinfo korrekt ber&uuml;cksichtigt werden. Wird zum Beispiel die Klasse insofern angepasst, da&szlig; entweder die Reihenfolge der Variablen im Macro <em>currentList </em>ge&auml;ndert wird, oder - und das ist der wohl &uuml;blichere Fall - da&szlig; neue Variablen in die Nutzungsdaten aufgenommen werden,&nbsp;&nbsp;ist das Makro <em>currentVersion </em>anzupassen.
</p>

<p>In nachstehender Methode wurde beispielsweise ber&uuml;cksichtigt, da&szlig;&nbsp;in der&nbsp;ersten ausgelieferten Version der Klasse die Variable <em>dialogDate </em>noch nicht in den Nutzungsdaten gespeichert wurde. Erst mit Version 2 (=aktuelle Version) wurde auch diese Variable&nbsp;&uuml;ber die&nbsp;<a href="#pack">pack</a>-Methode in die Nutzungsdaten geschrieben.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean unpack(container packedClass)<br />
{<br />
&nbsp;&nbsp;&nbsp; Integer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version&nbsp;&nbsp;&nbsp;&nbsp; = conpeek(packedClass,1);<br />
&nbsp;&nbsp;&nbsp; PackedQueryRun&nbsp; packedQueryRun;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; switch (version)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case(#CurrentVersion) :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [version,filenameSave,dialogDate,packedQueryRun] = packedClass;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (queryIsPackedOk(packedQueryRun))<br />
&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; queryrun = new QueryRun(packedQueryRun);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!queryrun)<br />
&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; this.initQuery();<br />
&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; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case(#Version1) :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // New dialog-field &quot;date&quot; was added in classversion 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [version,filenameSave,packedQueryRun] = packedClass;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (queryIsPackedOk(packedQueryRun))<br />
&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; queryrun = new QueryRun(packedQueryRun);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!queryrun)<br />
&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; this.initQuery();<br />
&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; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dialogDate = systemdateget();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default :<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; return true;<br />
}
</div>

<p><a name="allowsavelast"></a>&Uuml;ber die Methode <strong>allowSaveLast </strong>kann gesteuert werden, ob&nbsp;Werte aus den Nutzungsdaten oder die Standardwerte aus der Methode <a href="#initparmdefault">initParmDefault</a> verwendet werden sollen.
</p>

<div style="padding: 5px; width: 460px; height: 200px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean allowSaveLast()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // true&nbsp; = Gets the last choice stored in the last value table<br />
&nbsp;&nbsp;&nbsp; // false = call initParmDefault()<br />
<br />
&nbsp;&nbsp;&nbsp; ret = super();<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="cangobatch"></a>In der Methode <strong>canGoBatch </strong>legt man fest, ob eine Klasse im Stapel ausgef&uuml;hrt werden k&ouml;nnen soll.
</p>

<div style="padding: 5px; width: 460px; height: 163px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean canGoBatch()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Enable/disable batch<br />
&nbsp;&nbsp;&nbsp; ret = true;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="runImpersonated"></a>Die Methode <strong>runImpersonated </strong>steuert (unter AX 2009), ob die Klasse, sofern sie im Stapel ausgef&uuml;hrt wird, am AOS ausgef&uuml;hrt wird, oder &uuml;ber die manuell vom Benutzer zu startende Stapelverarbeitung.
</p>

<div style="padding: 5px; width: 460px; height: 198px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean runsImpersonated()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
<br />
&nbsp;&nbsp;&nbsp; ret = super();<br />
<br />
&nbsp;&nbsp;&nbsp; // ret = TRUE:&nbsp; Runs on server (default)<br />
&nbsp;&nbsp;&nbsp; // ret = FALSE: Runs only if started through Basic &gt; Periodic &gt; Batch &gt; Processing<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="main"></a>Die <strong>main</strong>-Methode wird aufgerufen, wenn die Klasse &uuml;ber eine Schaltfl&auml;che in einem Formular, einen Eintrag im Menu (beides setzt sein entsprechendes MenuItem voraus) oder aber direkt aus dem AOT aufgerufen wird. &Uuml;ber den Aufruf von <em>prompt </em>wird der Dialog der Klasse ge&ouml;ffnet.
</p>

<div style="padding: 5px; width: 460px; height: 181px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public static void main(Args args)<br />
{<br />
&nbsp;&nbsp;&nbsp; Tutorial_ClassWithQueryRun tutorial_ClassWithQueryRun = Tutorial_ClassWithQueryRun::construct();<br />
&nbsp;&nbsp;&nbsp; ;<br />
<br />
&nbsp;&nbsp;&nbsp; if(tutorial_ClassWithQueryRun.prompt())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun.run();<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>

<p><a name="dialog"></a>Die <strong>dialog</strong>-Methode dient dazu, dem Benutzer die M&ouml;glichkeit zu geben, bestimmte Parameter und dergleichen festzulegen. Dazu werden dem Dialog die gew&uuml;nschten Dialog-Felder hinzugef&uuml;gt.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">protected Object dialog(DialogRunbase dialog, boolean forceOnClient)<br />
{<br />
&nbsp;&nbsp;&nbsp; DialogRunbase ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; ret = super(dialog, forceOnClient);<br />
<br />
&nbsp;&nbsp;&nbsp; // Note: Don't use addFieldValue when using Default-Button<br />
<br />
&nbsp;&nbsp;&nbsp; df_FileNameSave = ret.addField(typeid(FilenameSave));<br />
&nbsp;&nbsp;&nbsp; df_DialogDate&nbsp;&nbsp; = ret.addField(typeid(TransDate));<br />
<br />
&nbsp;&nbsp;&nbsp; df_FileNameSave.value(this.parmFilename());<br />
&nbsp;&nbsp;&nbsp; df_DialogDate.value(&nbsp; this.parmDialogDate());<br />
<br />
&nbsp;&nbsp;&nbsp; // Add helptext<br />
&nbsp;&nbsp;&nbsp; df_FileNameSave.helpText('Enter filename');<br />
&nbsp;&nbsp;&nbsp; df_DialogDate.helpText('Field is not used. Demonstration purpose only');<br />
<br />
&nbsp;&nbsp;&nbsp; ret.filenameLookupFilter(this.filenameLookupFilter());<br />
&nbsp;&nbsp;&nbsp; ret.filenameLookupInitialPath(this.filenameLookupInitialPath());<br />
&nbsp;&nbsp;&nbsp; ret.filenameLookupTitle(this.filenameLookupTitle());<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="getfromdialog"></a><strong>GetFromDialog</strong> wird aufgerufen, wenn der Benutzer den Dialog mit OK best&auml;tigt.&nbsp;Die Methode&nbsp;dient dazu, die eingetragenen Werte/Parameter auszulesen und wie im Beispiel&nbsp;&uuml;ber entsprechende <a href="#parm">parm</a>-Methoden der Instanz zu &uuml;bergeben.&nbsp;
</p>

<div style="padding: 5px; width: 460px; height: 203px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean getFromDialog()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; ret = super(); <br />

<p>&nbsp;&nbsp;&nbsp; this.parmFilename(df_FileNameSave.value());<br />
&nbsp;&nbsp;&nbsp; this.parmDialogDate(df_DialogDate.value());<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</p>

</div>

<p><a name="puttodialog"></a>Die Methode <strong>putToDialog </strong>dient dazu, um die Dialogfelder des Dialogs mit Werten zu bef&uuml;llen. Diese Methode wird unter anderem beim Bet&auml;tigen der Schaltfl&auml;chen <em>L&ouml;schen</em> und <em>Standard </em>aufgerufen.
</p>

<div style="padding: 5px; width: 460px; height: 155px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">protected void putToDialog()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; df_FileNameSave.value(this.parmFilename());<br />
&nbsp;&nbsp;&nbsp; df_DialogDate.value(this.parmDialogDate());<br />
<br />
&nbsp;&nbsp;&nbsp; super();<br />
}
</div>

<p><a name="dialogclear"></a>Die Methode <strong>dialogClear </strong>kann dazu verwendet werden, um Dialogfelder bzw. deren Inhalten zur&uuml;ckzusetzen. Diese Methode zu &uuml;berschreiben macht nat&uuml;rlich nur dann Sinn, wenn die Methode <a href="#showclearbutton">showClearButton</a> entsprechend <em>true </em>retourniert und somit die Schaltfl&auml;che <em>L&ouml;schen </em>im Dialog angezeigt wird.
</p>

<div style="padding: 5px; width: 460px; height: 196px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public void dialogClear()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; super();<br />
&nbsp;&nbsp;&nbsp; // Rebuild query/queryRun<br />
&nbsp;&nbsp;&nbsp; this.initQuery();<br />
<br />
&nbsp;&nbsp;&nbsp; // Clear parameters<br />
&nbsp;&nbsp;&nbsp; filenameSave = &quot;&quot;;<br />
&nbsp;&nbsp;&nbsp; dialogDate&nbsp;&nbsp; = dateNull();<br />
}
</div>

<p><a name="filenamelookupfilter"></a>Die folgenden drei Methoden werden dazu verwendet, den Benutzer bei der Eingabe des Speicherortes f&uuml;r die durch die Klasse zu erstellende Datei zu unterst&uuml;tzen. So werden der auszuw&auml;hlende Dateityp genauso gesteuert, wie der Initial-Ablagepfad und der Titel des &quot;Speichern untern&quot;-Windows-Dialoges.
</p>

<div style="padding: 5px; width: 460px; height: 90px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">FilenameFilter filenameLookupFilter()<br />
{<br />
&nbsp;&nbsp;&nbsp; return [&quot;Tab-Separated Files;Text-Files&quot;,'*.tsv;*.txt'];<br />
}
</div>

<p>&nbsp;<a name="filenamelookupinitialpath"></a>
</p>

<div style="padding: 5px; width: 460px; height: 118px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">Filename filenameLookupInitialPath()<br />
{<br />
&nbsp;&nbsp;&nbsp; #WinApi<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; return winApi::getFolderPath(#CSIDL_DESKTOP);<br />
}
</div>

<p><a name="filenamelookuptitle"></a>&nbsp;
</p>

<div style="padding: 5px; width: 460px; height: 90px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">str filenameLookupTitle()<br />
{<br />
&nbsp;&nbsp;&nbsp; return &quot;@SYS63229&quot;;<br />
}
</div>

<p><a name="showclearbutton"></a>Die Schaltfl&auml;che <em>L&ouml;schen </em>im Dialog kann &uuml;ber die Methode <a href="#showclearbutton">showClearButton</a> gesteuert werden. Wird die Schaltfl&auml;che eingeblendet, sollte die Methode <a href="#dialogclear">dialogClear</a> entsprechend &uuml;berschrieben worden sein.
</p>

<div style="padding: 5px; width: 460px; height: 164px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">protected boolean showClearButton()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Enable/disable Clear-Button<br />
&nbsp;&nbsp;&nbsp; ret = true;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="showdefaultbutton"></a>Soll im Dialog die Schaltfl&auml;che <em>Standard </em>angezeigt werden, so muss die Methode <strong>showDefaultButton </strong>entsprechend &uuml;bersteuert werden. Diese Schaltfl&auml;che setzt die Felder des Dialoges auf die Standard-Werte zur&uuml;ck. In diesem Fall sollte aber auch die Methode <a href="#initparmdefault">initParmDefault</a> entsprechenden Code enthalten.
</p>

<div style="padding: 5px; width: 460px; height: 167px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean showDefaultButton()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Enable/disable Default-Button<br />
&nbsp;&nbsp;&nbsp; ret = true;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="showqueryselectbutton"></a>Ob man dem Benutzer die M&ouml;glichkeit gibt, den Query mit Filterkriterien, Sortierung usw. zu erweitern kann man &uuml;ber die Methode <strong>showQuerySelectButton </strong>steuern. Diese legt fest, ob die Schaltfl&auml;che <em>Ausw&auml;hlen </em>im Dialog angezeigt wird.
</p>

<div style="padding: 5px; width: 460px; height: 166px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">boolean showQuerySelectButton()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // User can change query<br />
&nbsp;&nbsp;&nbsp; ret = true;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="showqueryvalues"></a>In dieser Methode wird festgelegt, ob die <em>Ausw&auml;hlen</em>-Schaltfl&auml;che im Dialog angezeigt wird. Soll diese angezeigt werden, so muss &uuml;brigens auch die Methode <a href="#queryrun">queryRun</a> &uuml;berschrieben sein
</p>

<div style="padding: 5px; width: 460px; height: 163px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean showQueryValues()<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Determines whether a Select button is to be added to the dialog. If you change the return value to true, the button will be added.<br />
&nbsp;&nbsp;&nbsp; ret = true;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="validate"></a>Best&auml;tigt der Benutzer den Dialog mit OK, so wird die <strong>validate</strong>-Methode aufgerufen. Hier k&ouml;nnen Dialogfelder bzw. deren Inhalt gepr&uuml;ft werden. Bevorzugterweise sollte man jedoch die Variablen der Instanz pr&uuml;fen und nicht die Felder selbst. Auf diese Art und Weise kann man - so wie ich dies beinahe immer mache - die validate-Methode nochmals manuell in der <a href="#run">run</a>-Methode aufrufen, um vor der Abarbeitung der Klasse zu pr&uuml;fen, ob alle notwendigen Parameter &uuml;bergeben w&uuml;rden. Unabh&auml;ngig davon, ob die Klasse &uuml;ber den Dialog (also &uuml;ber ein MenuItem) oder aus X++-Code heraus aufgerufen wurde.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public boolean validate(Object calledFrom)<br />
{<br />
&nbsp;&nbsp;&nbsp; boolean ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Method is called from dialog, and manually called in run-method<br />
<br />
&nbsp;&nbsp;&nbsp; ret = super(calledFrom);<br />
<br />
&nbsp;&nbsp;&nbsp; if(ret &amp;&amp; !this.parmFilename())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = checkFailed(strfmt(&quot;@SYS89866&quot;, &quot;@SYS16423&quot;));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; if(ret &amp;&amp; !this.queryRun())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = checkFailed(strfmt(&quot;@SYS89866&quot;, &quot;@SYS25531&quot;));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}}
</div>

<p><a name="construct"></a>Die <strong>construct</strong>-Methode muss &uuml;berschrieben werden, um eine Instanz der Klasse bilden zu k&ouml;nnen.
</p>

<div style="padding: 5px; width: 460px; height: 109px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public static Tutorial_ClassWithQueryRun construct()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; return new Tutorial_ClassWithQueryRun();<br />
}&nbsp;
</div>

<p><a name="initparmdefault"></a>Die Methode <strong>initParmDefault </strong>wird u.a. ausgef&uuml;hrt, wenn die Klasse erstmalig &uuml;ber ein MenuItem aufgerufen wird und es somit noch keine Nutzungsdaten gibt. Au&szlig;erdem wird sie aufgerufen, wenn der Benutzer die Schaltfl&auml;che <em>Standard </em>bet&auml;tigt.
</p>

<p>Im Beispiel werden hier all jene Variablen, die der Benutzer auch im Dialog ggf. &auml;ndern kann, durch Standard-Werte bef&uuml;llt. Wichtig ist der Aufruf der <a href="#initquery">initQuery</a>, die den zu verwendenden Query erstmalig initialisiert.
</p>

<div style="padding: 5px; width: 460px; height: 210px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public void initParmDefault()<br />
{<br />
&nbsp;&nbsp;&nbsp; #WINAPI<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; super();<br />
<br />
&nbsp;&nbsp;&nbsp; filenameSave = WinAPI::getFolderPath(#CSIDL_DESKTOP) + #FilePathDelimiter + &quot;exportFile.tsv&quot;;<br />
&nbsp;&nbsp;&nbsp; dialogDate&nbsp;&nbsp; = systemdateget();<br />
<br />
&nbsp;&nbsp;&nbsp; // Build query/queryrun<br />
&nbsp;&nbsp;&nbsp; this.initQuery();<br />
}
</div>

<p><a name="initquery"></a>Die <strong>initQuery </strong>wird u.a. beim erstmaligen Aufruf der Klasse &uuml;ber ein Menuitem von der <a href="#initparmdefault">initParmDefault</a> aufgerufen und generiert ein Query- sowie ein QueryRun-Objekt. Hier werden im Beispiel diverse QueryBuildRanges (Filter) aufgebaut. Auch die Sortierung des Queries kann hier festgelegt werden.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public Query initQuery()<br />
{<br />
&nbsp;&nbsp;&nbsp; Query&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query;<br />
&nbsp;&nbsp;&nbsp; QueryBuildDataSource queryBuildDataSource;<br />
&nbsp;&nbsp;&nbsp; QueryBuildRange&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryBuildRangeInvoiceDate;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; query = new Query();<br />
<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource = query.addDataSource(tablenum(CustInvoiceJour));<br />
<br />
&nbsp;&nbsp;&nbsp; // Build ranges for SELECT-Button<br />
&nbsp;&nbsp;&nbsp; SysQuery::findOrCreateRange(queryBuildDataSource, fieldnum(CustInvoiceJour, OrderAccount));<br />
&nbsp;&nbsp;&nbsp; SysQuery::findOrCreateRange(queryBuildDataSource, fieldnum(CustInvoiceJour, InvoiceAccount));<br />
<br />
&nbsp;&nbsp;&nbsp; // Add locked Range<br />
&nbsp;&nbsp;&nbsp; queryBuildRangeInvoiceDate = query.dataSourceTable(tablenum(CustInvoiceJour)).addRange(fieldnum(CustInvoiceJour, InvoiceDate));<br />
&nbsp;&nbsp;&nbsp; queryBuildRangeInvoiceDate.value(&quot;01.01.2010..&quot;);<br />
&nbsp;&nbsp;&nbsp; queryBuildRangeInvoiceDate.status(RangeStatus::Locked);<br />
<br />
&nbsp;&nbsp;&nbsp; // Add sort fields<br />
query.dataSourceTable(tablenum(CustInvoiceJour)).addSortField(fieldnum(CustInvoiceJour, InvoiceDate), SortOrder::Ascending);<br />
<br />
&nbsp;&nbsp;&nbsp; queryrun = new SysQueryRun(query);<br />
<br />
&nbsp;&nbsp;&nbsp; return query;<br />
}
</div>

<p><a name="parm"></a>Die <strong>parm</strong>-Methoden werden u.a. dazu verwendet, um innerhalb einer Instanz der Klasse die Variablen abzufragen bzw. zu setzen.
</p>

<div style="padding: 5px; width: 460px; height: 138px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public TransDate parmDialogDate(TransDate _dialogDate = dialogDate)<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; dialogDate = _dialogDate;<br />
<br />
&nbsp;&nbsp;&nbsp; return dialogDate;<br />
}
</div>

<p>&nbsp;&nbsp;
</p>

<div style="padding: 5px; width: 460px; height: 135px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">Filename parmFilename(FilenameSave _filename = filenameSave)<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; filenameSave = _filename;<br />
<br />
&nbsp;&nbsp;&nbsp; return filenameSave;<br />
}
</div>

<p><a name="queryrun"></a>In der Methode <strong>queryRun </strong>wird das in der <a href="#initquery">initQuery</a> aufgebaute <em>queryRun</em>-Objekt zur&uuml;ckgegeben.
</p>

<div style="padding: 5px; width: 460px; height: 148px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public QueryRun queryRun()<br />
{<br />
&nbsp;&nbsp;&nbsp; QueryRun ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; ret = queryrun;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="run"></a>Das eigentliche Herzst&uuml;ck der Klasse ist nat&uuml;rlich die <strong>run</strong>-Methode. Hier ist die eigentliche Logik zu implementieren. Im konkreten Fall wird nach der Pr&uuml;fung der Parameter eine Datei erstellt. Danach wird der Query durchlaufen und bestimmte Werte werden in die Datei geschrieben. Der Benutzer wird w&auml;hrenddessen &uuml;ber eine Fortschrittsanzeige informiert, wie weit der Vorgang fortgeschritten ist.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public void run()<br />
{<br />
&nbsp;&nbsp;&nbsp; CustInvoiceJour custInvoiceJour;<br />
&nbsp;&nbsp;&nbsp; #AviFiles<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; super();<br />
<br />
&nbsp;&nbsp;&nbsp; setprefix(Tutorial_ClassWithQueryRun::description());<br />
<br />
&nbsp;&nbsp;&nbsp; // Check Parameters<br />
&nbsp;&nbsp;&nbsp; if(!this.validate(this))<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; // Create/Open File<br />
&nbsp;&nbsp;&nbsp; if(this.isInBatch())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textIo = Tutorial_ClassWithQueryRun::createFileServer(this.parmFilename());<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textIo = Tutorial_ClassWithQueryRun::createFileClient(this.parmFilename());<br />
&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp; // Lets go<br />
&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Init progress<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.progressInit(this.caption(),SysQuery::countTotal(this.queryRun()), #AviTransfer);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(this.queryRun().next())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; custInvoiceJour = queryrun.get(tablenum(CustInvoiceJour));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Update progress<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.parmProgress().incCount();<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Write file<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textIo.write(custInvoiceJour.InvoiceAccount + #delimiterTab +<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; custInvoiceJour.InvoiceId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + #delimiterTab);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; catch (Exception::Error)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.closeFile();<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; this.closeFile();<br />
<br />
&nbsp;&nbsp;&nbsp; info(&quot;@SYS92126&quot;);<br />
}
</div>

<p><a name="createfile"></a>Da&nbsp;es das Ziel der Klasse ist, eine Datei auf Basis eines Queries zu erstellen, muss diese Datei zu einem bestimmten Zeitpunkt erstellt werden. Daf&uuml;r&nbsp;sind die Methoden <strong>createFileServer </strong>bzw. <strong>createFileClient </strong>zust&auml;ndig.
</p>

<div style="padding: 5px; width: 460px; height: 260px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public static server textio createFileServer(fileName _fileNameSave)<br />
{<br />
&nbsp;&nbsp;&nbsp; fileIOPermission&nbsp;&nbsp;&nbsp; fileIOPermission;<br />
&nbsp;&nbsp;&nbsp; textio&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textio;<br />
&nbsp;&nbsp;&nbsp; #file<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; fileIOPermission = new fileIOPermission(_fileNameSave, #io_write);<br />
&nbsp;&nbsp;&nbsp; fileIOPermission.assert();<br />
<br />
&nbsp;&nbsp;&nbsp; textIo = new TextIo(_fileNameSave, #io_write);<br />
&nbsp;&nbsp;&nbsp; if( !textIo)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw error(strfmt(&quot;@SYS72245&quot;, _fileNameSave));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; return textIo;<br />
}
</div>

<p>&nbsp;
</p>

<div style="padding: 5px; width: 470px; height: 207px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public static&nbsp;client textio createFileClient(fileName _fileNameSave)<br />
{<br />
&nbsp;&nbsp;&nbsp; fileIOPermission&nbsp;&nbsp;&nbsp; fileIOPermission;<br />
&nbsp;&nbsp;&nbsp; textio&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; textio;<br />
&nbsp;&nbsp;&nbsp; #file<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; textIo = new TextIo(_fileNameSave, #io_write);<br />
&nbsp;&nbsp;&nbsp; if( !textIo)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw error(strfmt(&quot;@SYS72245&quot;, _fileNameSave));<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; return textIo;<br />
}&nbsp;
</div>

<p><a name="closefile"></a>Die Methode <strong>closeFile </strong>wird lediglich dazu verwendet, die Instanz des <em>textIO </em>zu entfernen.
</p>

<div style="padding: 5px; width: 460px; height: 106px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">private void closeFile()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; textIo = null;<br />
}
</div>

<p><a name="caption"></a>Welcher Titel im Dialog angezeigt werden, kann mit Hilfe der Methode <strong>caption</strong> gesteuert werden.
</p>

<div style="padding: 5px; width: 460px; height: 166px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public ClassDescription caption()<br />
{<br />
&nbsp;&nbsp;&nbsp; ClassDescription ret;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Here goes the caption of the class (used ex. for dialog-title)<br />
&nbsp;&nbsp;&nbsp; ret = &quot;Tutorial-Class&quot;;<br />
<br />
&nbsp;&nbsp;&nbsp; return ret;<br />
}
</div>

<p><a name="description"></a>Die Methode <strong>description </strong>sollte dazu verwendet werden, um dem Benutzer im Formular Stapelverarbeitungsliste eine Beschreibung des Stapelverarbeitungsauftrages zur Verf&uuml;gung zu stellen.
</p>

<div style="padding: 5px; width: 460px; height: 123px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">static ClassDescription description()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; // Here goes a description of the class<br />
&nbsp;&nbsp;&nbsp; return &quot;Tutorial for classes with queryRun and file-export&quot;;<br />
}
</div>

<p><a name="howtouseclass"></a>Die Methode <strong>howToUseClass </strong>wird zu keinem Zeitpunkt automatisch aufgerufen, sie dient dem Entwickler lediglich als Information, wie die Klasse &uuml;ber X++ aufzurufen ist.
</p>

<div style="padding: 5px; width: 460px; height: 184px; color: rgb(0, 0, 0); overflow: scroll; font-family: courier new,helvetica; font-size: 12px; white-space: nowrap; background-color: rgb(255, 255, 204);">public static str howToUseClass()<br />
{<br />
&nbsp;&nbsp;&nbsp; Tutorial_ClassWithQueryRun tutorial_ClassWithQueryRun;<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun = Tutorial_ClassWithQueryRun::construct();<br />
&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun.parmFilename(@'c:\temp\temp_123.tsv');<br />
&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun.parmDialogDate(systemdateget());<br />
&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun.initQuery();<br />
&nbsp;&nbsp;&nbsp; tutorial_ClassWithQueryRun.run();<br />
}
</div>

<p>Die oben beschriebene Klasse wurde in Dynamics AX 2009 entwickelt, sie l&auml;uft aber auch in Dynamics AX 4.0.&nbsp;<br />
Die Klasse&nbsp;steht <a title="Download XPO" target="_blank" href="http://www.schweda.net/download/Class_Tutorial_ClassWithQueryRun.xpo">hier als XPO-File zum Download</a> zur Verf&uuml;gung, Verwendung nat&uuml;rlich auf eigene Gefahr!
</p>

<p>&nbsp;
</p>]]></content>
<published>2011-03-20T11:46:22Z</published>
<updated>2011-03-20T11:46:22Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=403"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-02-06:id387</id><title type="text">Beispiel für einen Query mit Gruppierung</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Der nachstehende Code zeigt wie man einen Query mit Gruppierung, Sortierung und verschiedenen Filtern aufbaut und diesen mit Hilfe eines QueryRun abarbeitet.
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 300px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">static void tutorialQueryGroupBy(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; Query&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query;<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource queryBuildDataSource;<br />
&nbsp;&nbsp;&nbsp; queryBuildRange&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryBuildRangeInvoiceDate;<br />
&nbsp;&nbsp;&nbsp; queryrun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryrun;<br />
&nbsp;&nbsp;&nbsp; Salesline&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Salesline;<br />
&nbsp;&nbsp;&nbsp; ;<br />
<br />
&nbsp;&nbsp;&nbsp; query = new query();<br />
<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource = query.addDataSource(tableNum(Salesline));<br />
<br />
&nbsp;&nbsp;&nbsp; // Add group by-clause<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addGroupByField(fieldNum(Salesline, custAccount));<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addGroupByField(fieldNum(Salesline, currencyCode));<br />
<br />
&nbsp;&nbsp;&nbsp; // Add fields to select-clause<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addSelectionField(fieldNum(Salesline, custAccount),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SelectionField::Database);<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addSelectionField(fieldNum(Salesline, currencyCode),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SelectionField::Database);<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addSelectionField(fieldNum(Salesline, lineAmount),&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SelectionField::Sum);<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addSelectionField(fieldNum(Salesline, createdDateTime),&nbsp;&nbsp;&nbsp; SelectionField::Min);<br />
<br />
&nbsp;&nbsp;&nbsp; // Add sort by-clause<br />
&nbsp;&nbsp;&nbsp; queryBuildDataSource.addSortField(fieldNum(Salesline, custAccount), SortOrder::Ascending);<br />
<br />
&nbsp;&nbsp;&nbsp; // Add where-clause<br />
&nbsp;&nbsp;&nbsp; sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, DeliveryCountryRegionId)).value(&quot;AT, US&quot;);&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, createdDateTime)).value(SysQueryRangeUtil::lessThanDate(-365));&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; sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, salesStatus)).value(strFmt(&quot;%1, %2&quot;, SalesStatus::Invoiced, SalesStatus::Delivered));<br />
<br />
&nbsp;&nbsp;&nbsp; // Represents Select<br />
&nbsp;&nbsp;&nbsp; // SELECT FIRSTFAST CustAccount, CurrencyCode, SUM(LineAmount), MIN(createdDateTime)<br />
&nbsp;&nbsp;&nbsp; // FROM SalesLine<br />
&nbsp;&nbsp;&nbsp; // GROUP BY SalesLine.CustAccount, SalesLine.CurrencyCode<br />
&nbsp;&nbsp;&nbsp; // ORDER BY SalesLine.CustAccount ASC<br />
&nbsp;&nbsp;&nbsp; // WHERE ((DeliveryCountryRegionId = N'AT' OR DeliveryCountryRegionId = N'US'))<br />
&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; AND ((createdDateTime&lt;'2009-07-06T22:00:00'))<br />
&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; AND ((SalesStatus = 3 OR SalesStatus = 2))<br />
<br />
<br />
&nbsp;&nbsp;&nbsp; queryrun = new SysQueryRun(query);<br />
&nbsp;&nbsp;&nbsp; while(queryRun.next())<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Salesline = queryRun.get(tableNum(SalesLine));<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info(strFmt(&quot;%1: %2 %3 (%4)&quot;, salesLine.CustAccount, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesLine.LineAmount, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesLine.CurrencyCode, <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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; salesLine.createdDateTime));<br />
&nbsp;&nbsp;&nbsp; }<br />
}
</div>]]></content>
<published>2011-02-06T19:56:51Z</published>
<updated>2011-02-06T19:56:51Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=387"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2011-01-05:id402</id><title type="text">Clientseitige Mini-Stapelverarbeitung über die Methode setTimeOut</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Vor kurzem stand ich vor der Herausforderung, in einem Formular alle x Sekunden etwas tun zu m&uuml;ssen (Beispielsweise sollen alle&nbsp;2 Sekunden der Inhalt einer angezeigten Datenquelle/Tabelle aktualisiert werden).
</p>

<p>L&ouml;sen kann man so etwas mit Hilfe der Methode <strong>setTimeOut</strong>, die z.B. auch von der clientseitigen Stapelverarbeitung unter <strong>Grundeinstellungen &gt; Periodisch &gt; Stapel &gt; Bearbeitung </strong>verwendet wird.
</p>

<p>Dazu muss z.B. im Formular eine Methode&nbsp;angelegt werden, die sich selbst&nbsp;&uuml;ber&nbsp;die <strong>setTimeOut</strong>-Methode nach einer definierten Zeitspanne (in tausendstel-Sekunden anzugeben)&nbsp;aufruft:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 150px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">void runEvery2Seconds()<br />
{<br />
&nbsp;&nbsp;&nbsp; // Set a Time Out with the idle flag set to false<br />
&nbsp;&nbsp;&nbsp; this.setTimeOut(funcname(), 2000, false);<br />
<br />
&nbsp;&nbsp;&nbsp; // ...do something...<br />
&nbsp;&nbsp;&nbsp; <em>dataSourceName</em>_ds.executeQuery();<br />
}
</div>


<p>Diese Methode muss nun einmalig aufgerufen werden, z.B. in der run-Methode des Formulares:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 130px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">public void run()<br />
{<br />
&nbsp;&nbsp;&nbsp; super();<br />
<br />
&nbsp;&nbsp;&nbsp; element.runEvery2Seconds();<br />
}
</div>

<p>Mehr zu dieser Methode gibts hier: <br />
<a href="http://msdn.microsoft.com/en-us/library/aa857865.aspx">http://msdn.microsoft.com/en-us/library/aa857865.aspx</a>
</p>]]></content>
<published>2011-01-05T19:07:00Z</published>
<updated>2011-01-05T19:07:00Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=402"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2010-12-22:id393</id><title type="text">SELECT-Statements mit Datumseinschränkung in AX 2009</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Im Zuge eines Upgrades&nbsp;einer Applikation auf&nbsp;Dynamics AX&nbsp;2009 ist mir ein St&uuml;ckchen Code&nbsp;&uuml;ber den Weg gelaufen,&nbsp;dessen Code-Upgrade auf den ersten Blick recht einfach sein sollte, sich aber bei genauerer Betrachtung als doch gar nicht so einfach herausgestellt hat.
</p>

<p>Und zwar gehts um ein einfaches SELECT-Statement, z.B. wie das folgende:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 110px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">date&nbsp;myDate = str2date(<span style="color: rgb(255, 0, 0);">'07.05.2010'</span>, 123);<br />
<br />
select count(recid)<br />
from salesline<br />
where salesline.createdDate == myDate;
</div>

<p>Obiges Statement soll mir einfach eine Zahl der Auftragszeilen liefern, die an einem bestimmten Tag erstellt wurden.
</p>

<p>In Dynamics AX 2009 wurde ja das Feld <em>createdDate </em>durch <em>createdDateTime </em>und einen vollst&auml;ndigen neuen Datentyp, der nun auch die Zeit mitspeichert, ersetzt.
</p>


<p>Will ich nun in AX 2009 das selbe Statement absetzten, so&nbsp;k&ouml;nnte dies wie folgt aussehen:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 250px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">date&nbsp;myDate = str2date(<span style="color: rgb(255, 0, 0);">'07.05.2010'</span>, 123);<br />
<br />
<span style="color: rgb(0, 102, 0);">// version 1</span><br />
select count(recid)<br />
from salesline<br />
where salesline.createdDateTime &gt;= DateTimeUtil::newDateTime(myDate,0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 07.05.2010 00:00:00<br />
&nbsp;&nbsp; &amp;&amp; salesline.createdDateTime &lt;= DateTimeUtil::newDateTime(myDate,60*60*24-1) // 07.05.2010 23:59:59<br />
;<br />
<br />
<span style="color: rgb(0, 102, 0);">// version 2</span><br />
select count(recid)<br />
from salesline<br />
where salesline.createdDateTime &gt;= DateTimeUtil::newDateTime(myDate,0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 07.05.2010 00:00:00<br />
&nbsp;&nbsp; &amp;&amp; salesline.createdDateTime &lt;&nbsp; DateTimeUtil::newDateTime(myDate+1,0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 08.05.2010 00:00:00<br />
;
</div>

<p>Ist das wirklich so, oder &uuml;bersehe ich hier etwas?
</p>]]></content>
<published>2010-12-22T19:28:28Z</published>
<updated>2010-12-22T19:28:28Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=393"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2010-12-08:id399</id><title type="text">Druckeinstellungen und Filterkriterien eines Reports vorbelegen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>In einem fr&uuml;heren <a href="http://www.schweda.net/blog_ax.php?bid=221" target="_self" title="Report per Code aurfufen">Beitrag</a> habe ich ja schon demonstriert, wie man einen Report in Dynamics AX per Code aufruft, und diesem dabei einen Query &uuml;bergibt. Der folgende Code erweitert diesen Aufruf um die Druckeinstellungen (<em>printJobSettings</em>), soda&szlig; es nun m&ouml;glich ist, einen Report vollautomatisch mit vorgegebenen Filterkriterien und definierten Druckeinstellungen aus X++ heraus aufzurufen.
</p>


<p>Im Beispiel wird der Standardbericht <em>Cust </em>verwendet, wobei nur Debitoren der Debitorengruppe <em>10 </em>in eine PDF-Datei ausgegeben werden sollen.
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 381px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void setPrintJobSettingsQuery4Report(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; ReportRun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReportRun = new ReportRun(new Args(ReportStr(Cust)));<br />
&nbsp;&nbsp;&nbsp; printJobSettings&nbsp;&nbsp;&nbsp; printJobSettings = new printJobSettings();<br />
&nbsp;&nbsp;&nbsp; ;<br />
<br />
&nbsp;&nbsp;&nbsp; // Modify Query<br />
&nbsp;&nbsp;&nbsp; sysQuery::findOrCreateRange(ReportRun.query().dataSourceTable(tableNum(custTable)), fieldNum(custTable, custGroup)).value(queryValue('10'));<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.query().interactive(false);<br />
<br />
&nbsp;&nbsp;&nbsp; // Create printJobSettings<br />
&nbsp;&nbsp;&nbsp; printJobSettings.setTarget(PrintMedium::File);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.format(PrintFormat::PDF);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.fileName(@&quot;C:\Temp\CustTableReport.pdf&quot;);<br />
<br />
&nbsp;&nbsp;&nbsp; // Apply printJobSettings<br />
&nbsp;&nbsp;&nbsp; ReportRun.printJobSettings(printJobsettings.packPrintJobSettings());<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.report().interactive(false);&nbsp; // Disable default printer-dialog<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.run();<br />
}
</div>

<p><strong>Update 12.12.2010<br />
</strong>Inspiriert von einem Kommentar von Andi habe ich obigen Job etwas abgewandelt und verwende nun die&nbsp;classFactory&nbsp;um den ReportRun zu instanzieren:
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 381px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px">static void setPrintJobSettingsQuery4Report_II(Args _args)<br />
{<br />
&nbsp;&nbsp;&nbsp; ReportRun&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReportRun;<br />
&nbsp;&nbsp;&nbsp; printJobSettings&nbsp;&nbsp;&nbsp; printJobSettings = new printJobSettings();<br />
&nbsp;&nbsp;&nbsp; args&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args;<br />
&nbsp;&nbsp;&nbsp; ;

<p>&nbsp;
</p>

<p>&nbsp;&nbsp;&nbsp; args = new args();<br />
&nbsp;&nbsp;&nbsp; args.name(reportstr(Cust));<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun = classFactory.reportRunClass(args);<br />
<br />
&nbsp;&nbsp;&nbsp; // Modify Query<br />
&nbsp;&nbsp;&nbsp; sysQuery::findOrCreateRange(ReportRun.query().dataSourceTable(tableNum(custTable)), fieldNum(custTable, custGroup)).value(queryValue('10'));<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.query().interactive(false);<br />
<br />
&nbsp;&nbsp;&nbsp; // Create printJobSettings<br />
&nbsp;&nbsp;&nbsp; printJobSettings.setTarget(PrintMedium::File);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.format(PrintFormat::PDF);<br />
&nbsp;&nbsp;&nbsp; printJobSettings.fileName(@&quot;C:\Temp\CustTableReport.pdf&quot;);<br />
<br />
&nbsp;&nbsp;&nbsp; // Apply printJobSettings<br />
&nbsp;&nbsp;&nbsp; ReportRun.printJobSettings(printJobsettings.packPrintJobSettings());<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.report().interactive(false);&nbsp; // Disable default printer-dialog<br />
<br />
&nbsp;&nbsp;&nbsp; ReportRun.run();<br />
}
</p>

</div>]]></content>
<published>2010-12-08T19:16:46Z</published>
<updated>2010-12-08T19:16:46Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=399"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2010-12-04:id398</id><title type="text">Stapelverarbeitung für einen Bericht/Report verhindern</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Um bei einem Bericht/Report, der direkt &uuml;ber ein MenuItem (d.h. nicht &uuml;ber eine von <em>RunBaseReport </em>abgeleitete Klasse) aufgerufen wird, die M&ouml;glichkeit zu unterbinden, diesen im Stapel auszuf&uuml;hren, muss nur die Methode <em>canGoBatch </em>des Reports &uuml;berschrieben werden.
</p>

<p>Diese Methode scheint allerdings nicht in den &uuml;berschreibbaren Methoden auf, also muss man einfach eine solche wie folgt anlegen:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 96px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;">public boolean canGoBatch()<br />
{<br />
&nbsp;&nbsp;&nbsp; ;<br />
&nbsp;&nbsp;&nbsp; return false;<br />
}
</div>

<p>Wird der Report &uuml;ber eine Klasse aufgerufen, dann muss in dieser die gleichnamige Methode entsprechend &uuml;berschrieben werden.
</p>

<p>Getestet in Dynamics AX 2009
</p>]]></content>
<published>2010-12-04T17:53:25Z</published>
<updated>2010-12-04T17:53:25Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=398"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2010-11-21:id396</id><title type="text">Tipp: Label eines Extended Datatypes (EDT) ermitteln</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>Manchmal braucht man einfach nur den Label eines Extended Datatypes (EDT), um diesen zum Beispiel als Bestandteil einer Info-/Fehlermeldung ausgeben zu k&ouml;nnen:
</p>

<div style="padding: 5px; background-color: rgb(255, 255, 204); width: 460px; font-family: courier new,helvetica; white-space: nowrap; height: 35px; color: rgb(0, 0, 0); font-size: 12px; overflow: scroll;"><span style="color: rgb(0, 0, 255);">new </span>SysDictType(extendedtypenum(<em>costingversionid</em>)).label()
</div>]]></content>
<published>2010-11-21T11:40:48Z</published>
<updated>2010-11-21T11:40:48Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=396"  rel="alternate" />
</entry>
<entry>
<id>tag:schweda.net,2010-11-17:id397</id><title type="text">Eingeblendete Dimensionen in Reports abfragen</title>
<author><name>Heinz Schweda</name></author>
<content type="html"><![CDATA[
<p>In manchen Berichten ist es m&ouml;glich, Lagerdimensionen &uuml;ber den Dialog ein- bzw. auszublenden. Um im Bericht selbst abzufragen, ob eine bestimmte Dimension eingeblendet wurde, kann man den unten stehenden Code verwenden, der eine Instanz der Klasse <strong>InventReport_OnHand</strong> voraussetzt. Dieser fragt zum Beispiel ab, ob die Palettennummer eingeblendet wurde,
</p>

<div style="padding-bottom: 5px; background-color: rgb(255,255,204); padding-left: 5px; width: 460px; padding-right: 5px; font-family: courier new,helvetica; white-space: nowrap; height: 60px; color: rgb(0,0,0); font-size: 12px; overflow: scroll; padding-top: 5px"><span style="color: rgb(0,0,255)">if</span>(InventReport_OnHand.parmInventDimParm().WMSPalletIdFlag))<br />
&nbsp;&nbsp; <span style="color: rgb(0,102,0)">// ...do something</span>
</div>

<p>&nbsp;
</p>]]></content>
<published>2010-11-17T20:04:32Z</published>
<updated>2010-11-17T20:04:32Z</updated>
<link href="http://www.schweda.net/blog_atom.php" rel="self" />
<link href="http://www.schweda.net/blog.php?bid=397"  rel="alternate" />
</entry>

</feed>	



