Friday, February 17, 2012

Missing Enums in SRSAnalysisEnums

Forenote: If you are using 2012, read this post.  There is a more robust way to access enums in AX 2012.

If your business operates like any would and has the need to report on data from custom functionality then you might find this post useful.  In creating custom functionality we often have the need to create new custom Enums and store the values in a custom table.  Doing so works just fine inside the Dynamics circle.  However, if you use a separate reporting engine outside of Dynamics, like SSRS, to render your reports, you will find that your users will not understand what a ShipStatus of 2 is, and let's be serious, your proposed solution to hard code your enum labels in your report is a poor strategy.

Microsoft was nice enough to create some tables to store enum values and labels for some of these enums.  These are located in two tables, namely, SRSAnalysisEnums and SRSModelEntityCache.  But, like I mentioned earlier, there are only SOME enums in these tables.  For a long time it was mysterious to me why it was this way, but after some digging in and a few "Ah ha!" moments later I figured it out.  It also introduced me to another unknown area of Dynamics.

Today, we're going to learn about Perspectives.  It's an object in the AOT under Data Dictionary.  Perspecitves in Dynamics are a poor attempt to mimic the functionality of a database cube.  It's not as flexible as a real cube and not very customizable.  If you expand the Perspectives node, you'll see a Perspective object for each Module.  Each of these perspectives has a list of tables.  Any enums in these tables listed under a perspective are the enums that show up in the SRS tables.  To get your new enums to show up you just need to have your table included in one of these perspectives or create your own, but try to put your table in the most relevant module, pertaining to the data that's stored in the table.

Note: If your table is already in these perspectives, read ahead.

Now, putting the table in the perspective alone won't put your enums in the table.  You also have to update the model so the SRS tables get populated correctly.  The way to do this is Tools->Reporting tools->Update models.  You can choose to update all languages or just one, as you feel is necessary for your business.  Running this will take awhile, but when it is finished, you will now see your new enums show up in the SRS tables.  Here's a script that will get your enum labels from a value:
SELECT ae.EnumItemName
FROM SRSModelEntityCache ec
JOIN SRSAnalysisEnums ae
    ON ae.EnumId = ec.EntityId
WHERE ec.PhysicalName = 'BaseEnum_ShipStatus'
    AND ae.EnumItemValue = 2

Thursday, February 9, 2012

Kernel Function Madness: any2str()

I'm a developer.  I'd treat this function as any would.  Here we have a function called any2str and takes in an anytype object and returns a string representation.  That's great because I have a dynamically changing anytype object that could be an integer one time and a date the next.  I also need to get a string representation of each type to I can put the values into the same column of a table to report on later.  So like any developer would do you code up your logic and write:
table.stringField = any2str(dynamicObject);
Sweet.  Everything compiles, let's continue writing code.
...
***Days pass***
...
Ok, time to run a test.  What?  Run-time error?  Method is expecting object of type string?  Wait, what line is it talking about?

For a moment I refused to believe that someone wouldn't have been so stupid to code up this method to expect only strings to be passed in.  And even after I realized that was the truth, I still couldn't believe it.  Yes, the method does in fact take in an anytype.  However, the method only works if that anytype is of type string.  So, instead of using the intuitive knowledge us developers have been taught to use, we have to now second guess every kernel function we call into no matter how we much "know" (think) how a method should work.  We've been duped, we've been given a useless str2str() method.

The workaround is below.  Use this as a local function (or as a method in the Global class to make a global method) inside the methods you often would use any2str(), that is, before understanding what the method really does.
str theRealAny2str(anytype _val)
{
    switch (typeof(_val))
    {
        case Types::Date:
            return date2str(any2date(_val), 213, 2, -1, 2, -1, 2);
        case Types::Enum:
            return enum2str(any2enum(_val));
        case Types::Guid:
            return guid2str(any2guid(_val));
        case Types::Int64:
            return int642str(any2int64(_val));
        case Types::Integer:
            return int2str(any2int(_val));
        case Types::Real:
            return num2str(any2real(_val), 1, 2, 1, 0);
    }
    return any2str(_val);
}