Pages

Tuesday, April 1, 2014

Models: How to enumerate every object and full path in a given Model [AX 2012]

I have some custom Best Practice checks that have required me to crawl over various specific models and paths and I needed a way to enumerate the exact paths of everything in a model, so I wrote some code to do so!

NOTE: This code won't show some objects like Jobs or Shared/Private Projects. It really shows everything you'd want. This is only because the last join to parentModelElement doesn't display records where modelElement.ParentModelElement == 0.

Just comment out the last block to get some of those odd-objects.

        /*
        join Name from parentModelElement
            where parentModelElement.RecId == modelElement.ParentModelElement
        */


static void listAllModelObjects(Args _args)
{
    SysModelElement     modelElement, parentModelElement;
    SysModelElementType modelElementType;
    SysModelElementData modelElementData;
    SysModelManifest    modelManifest;
    
    while select Name from modelElement
        join Name from modelElementType
            where modelElementType.RecId == modelElement.ElementType
        join modelElementData
            where modelElementData.ModelElement == modelElement.RecId
        join modelManifest
            where modelManifest.Name == 'USR Model'
                && modelManifest.Model == modelElementData.ModelId
        join Name from parentModelElement
            where parentModelElement.RecId == modelElement.ParentModelElement
    {
        info(strFmt("%1, %2, %3, %4",
            parentModelElement.Name, modelElementType.Name, modelElement.Name,
            SysTreeNode::modelElement2Path(modelElement)));
    }
                
}

Monday, March 31, 2014

Creating custom keyboard shortcuts

I'm working on my own custom Editor Scripts and I wanted some keyboard shortcuts for the more frequently used scripts and I came across this fantastic blog post on how to do it in AX.

Keep checking back, as I'll make my own post if/when I can get a working demo up.

See it here: http://www.agermark.com/2011/04/create-your-own-shortcuts-in-ax-forms.html

Models: Notes about models and how to find what model your object is in via X++ [AX 2012]

I'm creating custom best practice checks, and I needed to determine what model an object was in from X++. This builds off of my previous post about enumerating models http://alexondax.blogspot.com/2014/03/models-how-to-enumerate-list-of-models.html.

Important notes/conclusions about models:

  • Models are layer-specific
    • Therefore a parent object (eg \Classes\SalesFormLetter\construct) may exist in the [Foundation] model in the SYS layer, but may also exist in the [USR Model] in the USR layer
  • You can have unlimited models in any layer
  • Models can contain objects that may be parents or children
    • This code below tells you what model C\SalesFormLetter is in, in the SYS layer.  If you have your own custom method in the CUS layer on this object, you will need to use SysDictMethod for example to identify it.
    • You would need to reflect on the objects entirely to fully identify an object.  This is proof of concept code
Models are very simple once you understand their concepts.  They can appear daunting at first.  You can use the TreeNode object to get the model.


static void jobGetObjectModel(Args _args)
{
    SysDictClass        sysDictClass = new SysDictClass(classNum(SalesFormLetter));
    SysModel            sysModel;
    SysModelManifest    sysModelManifest;
    
    
    select Model, Name, Publisher, DisplayName from sysModelManifest
        where sysModelManifest.Model == sysDictClass.treeNode().AOTGetModel()
        join firstonly Layer from sysModel
        where sysModel.RecId == sysModelManifest.Model &&
              sysModel.Layer == UtilEntryLevel::sys; // Change layer here
    
    if (sysModelManifest)
        info(strFmt("Model: %1", sysModelManifest.Name));
    else
        info("Object is not apart of model in given layer");

}

Models: How to enumerate list of models from X++ [AX 2012]

I'm creating custom Best Practice checks and I needed to enumerate the existing models in my model store. Here is a simple job that demonstrates how.  My next post will build on this.  The code is a tweak of \Classes\SysModelStore\buildSelectionModels.


static void jobEnumerateModels(Args _args)
{
    SysModel sysModel;
    SysModelManifest sysModelManifest;
    container selectionList;

    // Select models from specified layer
    while select Model, Name, Publisher, DisplayName from sysModelManifest
        join firstonly Layer from sysModel
        where sysModel.RecId == sysModelManifest.Model &&
              sysModel.Layer == UtilEntryLevel::usr
    {
        selectionList += [SysListSelect::packChoice(strFmt('DisplayName: "%1"\n ModelName: "%2"\n Publisher: "%3"', sysModelManifest.DisplayName, sysModelManifest.Name, sysModelManifest.Publisher), any2int(sysModelManifest.Model), false)];
    }
    conView(selectionList);
}

Monday, February 3, 2014

Incredibly simple and useful tool for formatting SQL code

I'm often debugging SQL queries dumped out of AX by running:

info(query.dataSourceNo(1).toString());

And I get an ugly, long unformatted SQL, where I'm left pressing enter and tab a bunch to make the string somewhat readable.


After some light googleing, I came across an open source SQL Server Management Studio (SSMS) plugin called PoorSQL.  It can also be done online, hooked to Notepad++, etc, but I prefer the plugin.

After pressing Ctrl+K,F, voila!  So useful!


Download link and web link.  Not sure why they have different main URLs:

  • Download - http://architectshack.com/PoorMansTSqlFormatter.ashx#Download_5
  • Web URL - http://poorsql.com/

Wednesday, January 15, 2014

Silly/Rare TFS error with Dynamics AX "Team Server connection error. [Microsoft][ODBC SQL Server Driver]Cannot generate SSPI context"



Team Server connection error. [Microsoft][ODBC SQL Server Driver]Cannot generate SSPI context

This error, when using TFS, means that your AOS user can not authenticate. Google didn't help me much here right away, and hopefully this saves somebody some head-ache.

In my development system, I run the AOS as my user account, and my password expired over the weekend and I changed it.  And an expired password won't cause your AOS to suddenly crash either.

I just went to the AOS service, re-entered my user/password information and voila.

Tuesday, December 10, 2013

How to create an XSLT to transform AX XML data for emailing

I've been asked recently about how to send emails using an XSLT with AX for formatting and sending XML data.  I must admit, I had to visit StackOverflow for some XML pointers (credit Jason Aller).

This example will show you most of what you will need so that you can customize it to your own needs.  I will be dumping a few customer records to email with HTML formatting.

This post will build off of my previous post about properly sending emails from AX (http://alexondax.blogspot.com/2013/09/how-to-properly-send-emails-with-built.html)

After performing all of your email setup, go to Basic>Setup>Email Templates and create a new template (bottom section) under your desired email sender/language and choose under layout, XSLT.  Click "Template" on the right and put in your XSL and save. (Note: I couldn't click save, I had to close the form and it allowed me to save):


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:Table="urn:www.microsoft.com/Formats/Table">
    <xsl:output method="html"/>
    <xsl:template match="/">
        <html>
            <body>
                <xsl:for-each select="//Table:Record[@name='CustTable']">
                    <p>
                        <xsl:for-each select="Table:Field">
                            <xsl:value-of select="@name"/>
                            <xsl:text> : </xsl:text>
                            <xsl:value-of select="."/>
                            <br/>
                        </xsl:for-each>
                    </p>
                </xsl:for-each>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>



In my subject, I put "Customer: %AcctNum% - %AcctName%" so that I could demonstrate how you can still pass mappings.

Then here is a simple job to show how to generate and pass the XML so that it is formatted and sent:


static void Job10(Args _args)
{
    SysEmailTable       sysEmailTable = SysEmailTable::find('XML');
    Map                 mappings;
    CustTable           custTable;
    int                 i;
    ;
    
    while select custTable
    {
        if (i>=3)
            break;

        mappings = new Map(Types::String, Types::String);
        mappings.insert('AcctNum', custTable.AccountNum);
        mappings.insert('AcctName', custTable.Name);
        

        SysEmailTable::sendMail(sysEmailTable.EmailId,
                                'en-us',
                                'alex@fakeemail.com',
                                mappings,
                                '',
                                custTable.xml(), // XML HERE
                                false,
                                'admin',
                                false);
        i++;
    }
}

Hope this helps and as always, happy DAX'ing!