Thursday, September 19, 2019

Parse JSON in pure X++

I decided to write a JSON parser in X++, because I couldn't find a useful implementation elsewhere. I wanted to avoid using external dll-based libraries.

It can handle strings, numbers (int and real), null-values and booleans. All kinds of linebreaks will be ignored.

It can be used like this:
str json = '{"foo": [1, 2, {"bar": 2}, {"bar": 3}]}';

AhkJSONObject ele = AhkJSONParser::parse(json);
AhkJSONArray arr = ele.get("foo");
AhkJSONObject obj = arr.get(3);
;

info('Demo 1: bar=' + obj.get("bar").strVal());
info('Demo 2: bar=' + AhkJSONParser::parse(json).getObjVal('foo').getArrayItem(3).getObjVal('bar').strVal());
info('Demo 3: bar=' + AhkJSONParser::parse(json).getObjVal('foo').getArrayItemWhere('bar', '3').getObjVal('bar').strVal());


Which will output:
Demo 1: bar=2
Demo 2: bar=2
Demo 3: bar=3

Please note the selection in demo 3, which makes it easy to select the correct array item. The project can be downloaded from the following url and is exported from AX 2009:
https://nc.ahk.dk/s/gwmTcb7N5y34SZH

The implementation is largely based on this python project:
https://github.com/eatonphil/pj/tree/master/pj

2020-01-20 update: I have updated the project with a few bugfixes, which should solve the two issues reported in comments.
2020-03-30 update: I have updated the project with a fix for parsing arrays as the outer type.

Friday, September 16, 2016

Fix space between menu items in the new AX7 menu

If you (like me) are bothered by the newly introduced space between menu items in the menu, then I have a solution :)


Screenshots

Original


With changed style

Procedure

Install the extension Stylish (or similar) in Chrome from the following link:
https://chrome.google.com/webstore/detail/stylish/fjnbnpbmkenffdnngjfgmeleoegfcffe?utm_source=chrome-ntp-icon

 And then set the style for cloudax.dynamics.com (or cloud.onebox.dynamics.com for local VM) to:

.modulesFlyout-last{
    margin-bottom: 0px !important;
}

.modulesFlyout-link{
    padding-bottom: 0px !important;
}

.modulesFlyout-LinkGroup.modulesFlyout-indent{
    margin-top: 0px !important;
    margin-bottom: 0px !important;
    padding-top: 1px;
    /*padding-bottom: 1px;*/
}

.modulesFlyout-LinkGroup:not(.modulesFlyout-indent){
    margin-bottom: 0px !important;
}

Thursday, June 16, 2016

Writing SSRS report directly to file / stream / byte array on AX7

If you ever need to write a report directly to a file, or for some reason get access to it before it is sent to the user, then you can replace your call to runReport on your SrsReportRun instance (similar code as when writing a report from code on AX 2012) with:

reportRun.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration());

SrsReportRunService reportRunService = new SrsReportRunService();
reportRunService.parmLocalMode(false);

reportRunService.preRunReport(reportRun.parmReportContract());


Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[]  parameterValueArray;
Map reportParametersMap;
reportParametersMap = reportRunService.createParamMapFromContract(reportRun.parmReportContract());
parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap);
SrsReportRunPrinter printer = new SrsReportRunPrinter(reportRun.parmReportContract(), parameterValueArray);

printer.parmSrsProxy().parmReportExecutionInfo(executionInfo);

System.Byte[] reportBytes = printer.parmSrsProxy().renderReportToByteArray(reportRun.parmReportContract().parmReportPath(),
                                          parameterValueArray,
                                          SRSReportFileFormat::PDF,
                                          printSettings.deviceInfo());

System.IO.File::WriteAllBytes("c:\\temp\\test.pdf", reportBytes);

If you need a stream instead, it should be trivial to create one with the byte array using the .net framework.

Thursday, November 13, 2014

Search Available Icons From Embedded Resources

Do you also hate looking for icons in AX when creating forms, because you need to scroll through all those images every time and they haven't even got a name to filter by?

Then I've finally got a solution for you! :)

I've made a new tool which allows you to search available image resources in AX 2012. The idea is that it traverses all form nodes and menu items for elements with icons and stores the icon resource id, label and help text. This is saved in a table, so it only needs to be done once.

When presenting this on a form, you can search for images using a text query!

Searching for "Refresh"


Download XPO

Monday, October 20, 2014

Show open windows in development view and switch between them

I just developed a simple form to show all open windows i development view. It has the following features:

  • Double-click an item to switch to the corresponding window.
  • Click "Close window" to close the selected window.
  • Auto-refreshes the view every 3 seconds.
  • Stays within the workspace (i.e. not "outside AX").
  • Stays open after switching windows (unlike the default Windows -> Windows)!
  • Works in both Dynamics AX 2009 and 2012.

It has greatly helped me manage my windows when developing...! :)

When imported, you should add a menu item pointing to the form (eg. in \Menus\GlobalToolsMenu on AX 2012).


Let me know if you need more features :)

Screenshot:



Wednesday, May 7, 2014

Error executing code: object does not have method 'loadProject'.

If you get the following error when attempting to open a project in AX 2012 or AX hangs indefinitely:

Error executing code:  object does not have method 'loadProject'.
Stack trace

(C)\Classes\\loadProject

Then just run:
AxUtil schema
(or initialize-axmodelstore if you are using powershell)

If you are running your AX 2012 on SQL 2012, then you also need to apply the following hotfix:
KB2680186

I have seen this error after importing a CU7 model on RTM.

Thursday, March 27, 2014

AX 2012 SSRS - Misc Problems and Solutions

Problem
You get a http 503 service unavailable during installation of Report Extensions.

Solution
Remove Report Server settings in AX.

Problem
Can't install Report Extensions. Error message:

"An error occurred during setup of Reporting Services extensions.
Reason: Could not load file or assembly 'Microsoft.SqlServer.BatchParser, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The system cannot find the file specified."

Solution
Install Microsoft® SQL Server® 2008 R2 Shared Management Objects (SMO)

You need this as well to install SMO:
Microsoft® System CLR Types for SQL Server® 2008 R2 (required package for SMO)

See: http://blogs.msdn.com/b/axsupport/archive/2012/06/26/unable-to-deploy-ax-2012-reporting-extensions-on-sql-server-2012-reporting-server.aspx

Problem
The title of the window of a SSRS report doesn't change as expected when editing it on the menu item or from Visual Studio.

Solution
Delete usage data in AX.

Problem
Error while setting server report parameters. Error message: The DefaultValue expression for the report parameter ‘AX_CompanyName’ contains an error: Request for the permission of type 'System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. (rsRuntimeErrorInExpression)

Solution
Open the file C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER\Reporting Services\ReportServer\rssrvpolicy.config
Set PermissionSetName to “FullTrust” at Name=Report_Expressions_Default_Permissions

See:
http://community.dynamics.com/product/ax/axtechnical/b/axsupport/archive/2012/02/02/microsoft-dynamics-ax-2012-reporting-extensions-error-system-security-permissions-environmentpermission-while-running-report.aspx

Problem
The reports are deployed to the wrong report folder.

Solution
AX was started with the wrong active client configuration. You need to set the client configuration to the AOS that you wish to deploy to, even though you are overriding it on the client shortcut. Don't forget to restart AX afterwards - the setting is cached when AX is started.

Problem
The report cannot be deployed because it couldn't find the network path.

Solution
Start Windows service "Remote Registry"
See: http://technet.microsoft.com/en-us/library/gg724094.aspx

Problem
Error when running SSRS in batch:
"System.InvalidCastException: Unable to cast object of type 'Microsoft.Dynamics.Ax.Xpp.DictMethod' to type 'Dynamics.Ax.Application.SysDictMethod'."

Solution
Change SysDictMethod to DictMethod on line 3 in:
\Classes\SrsReportRdpRdlWrapperContractInfo\buildMemberAndNestedObjectMap

Problem
Error while setting report parameters. Error message: An error has occurred during report processing. (rsProcessingAborted)

Solution
See: https://community.dynamics.com/ax/b/axsupport/archive/2013/03/12/cannot-be-processed-at-the-receiver-due-to-a-contractfilter-mismatch-at-the-endpointdispatcher.aspx

AX 2012 Error: An attempt has been made to use a data extension 'AXREPORTDATAPROVIDER'

If you get the following error message from SSRS:

"An attempt has been made to use a data extension 'AXREPORTDATAPROVIDER' that is either not registered for this report server or is not supported in this edition of Reporting Services. (rsDataExtensionNotFound)"

Then the solution would be to:
  1. Try reinstalling AX report extensions.
  2. If that doesn't work, try uninstalling report extensions, uninstalling SSRS, installing SSRS and finally installing report extensions again.
  3. If it still doesn't work, then you seemingly have to reinstall the entire SQL database.
Sometimes step 1 is enough, but I've seen a case where we couldn't get it to work until we completed step 3.

Let me know if you know a simpler solution. 

NB: the configuration files for SSRS were correct and didn't change after completing any of the above steps, so the source of the error is someplace else.

AX 2012: Running an AX query from C# data method in SSRS


  1. Create a new query in AX 2012 from the AOT.
  2. Open a SSRS report and create a new data method. 
  3. Replace the code with the following:
 [DataMethod(), PermissionSet(SecurityAction.Assert, Name = "FullTrust")]  
 public static string TestMethod()  
 {  
      IEnumerable dt = Microsoft.Dynamics.Framework.Reports.AxQuery.ExecuteQueryStream("SELECT * FROM EmplQuery");  
      IEnumerator e = dt.GetEnumerator();  
      if (e.MoveNext())  
           return e.Current.Field(1);  
      else  
           return "No results";  
 }  
Don't forget to rename the method to the one you created, replace "EmplQuery" with the name of your query, replace return type with your type and select the field that you want when returning. Let me know if you want an example of returning a specific field by its name.

Error: KB2390372v2 does not apply, or is blocked by another condition on your computer.

When installing AX 2012, sometimes you might get the following error when applying KB2390372v2 (required by setup). 

KB2390372v2 does not apply, or is blocked by another condition on your computer.

This can be solved by installing the most recent setup support files for AX 2012. Run the appropriate Cumulative Update (CU) setup. Yes, this can be done even when AX isn't installed yet.

Restart setup and it shouldn't need this hotfix anymore.

Danish translation of error for those searching:
KB2390372v2 kan ikke anvendes eller er blokeret af andre tilstande på computeren

Monday, January 16, 2012

Thursday, September 1, 2011

Ax 2012 - Get dimensions

If you've been working with Dynamics AX 2012 dimensions, you'll know that they are pretty hard to understand and work until you get to know them. I finally figured out how to get the dimensions and dimension values through X++.

Get dimensions:

DimensionAttribute              dimAttr;
DimensionAttributeSetItem       dimAttrSetItem;
DimensionAttributeSetItem       dimAttrSetItemEditable;
DimensionEnumeration            dimensionSetId;
dimensionSetId = DimensionCache::getDimensionAttributeSetForLedger();
while select dimAttr
    order by Name
       where dimAttr.Type != DimensionAttributeType::MainAccount
        join RecId from dimAttrSetItem
       where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&
             dimAttrSetItem.DimensionAttributeSet == dimensionSetId
  outer join RecId from dimAttrSetItemEditable
       where !dimAttrSetItemEditable.DimensionAttributeSet &&
             dimAttrSetItemEditable.DimensionAttribute == dimAttr.RecId &&
             dimAttrSetItemEditable.EnumerationValue == NoYes::Yes
{
   info(dimAttr.Name);
}
Get dimension values for the "CostCenter" dimension:
Common common;
DimensionAttribute dimAttr;
select firstOnly dimAttr where dimAttr.Name == "CostCenter";
common = new DictTable(dimAttr.BackingEntityType).makeRecord();
while select common
{
    info(strFmt("%1", common.(dimAttr.NameAttribute)));
}

Wednesday, August 31, 2011

Dynamic lookup on forms


To create a custom dynamic lookup on a form, override the lookup method on the control, and add the following code. This code allows you to select a year, but it should be clear what you need to modify to show something else:

public void lookup()
{
    Counter yearCount;
    List    valueList = new List(Types::String);

    for (yearCount = 0; yearCount < 5; yearCount++)
    {
        valueList.addEnd(strFmt("Year %1", year(SystemDateGet()) - yearCount));
    }

    SysLookup::lookupList(this, valueList, "List of years");
}

Show temporary table content


Run the following and you will be able to see the contents of a temporary table during runtime, which I find very helpful when debugging:

new SysTableBrowser().run(tmpTable.TableId, tmpTable);

To get it running, you need to overwrite the run method on SysTableBrowser with a new parameter profile:
public void run(tableId tableId, common tmpBuffer = null)
 and add the following line before formRun.run();
if (tmpBuffer.TableId < 60535)
    formRun.parmTmpData(tmpBuffer);

Overwrite license on Dynamics AX 4.0

My first post is regarding an issue I had a couple of weeks back, where AX crashed when overwriting an expired license with a new one (AX 4.0), which resulted in a broken database. After a lot of problems and not working solutions, I finally solved it, and of course I'll share the solution with you guys:
  1. Connect to the database and delete the content of the VALUE field of the first three lines in the SYSCONFIG table
  2. Stop the AOS service
  3. Delete the .aoi file in the appl directory
  4. Run "dir *.auc/s" on the client (deletes all cache files on the computer - you need to be on the drive of your Windows installation)
  5. If it detects any files, run "del *.auc/s" which deletes them.
  6. Start the AOS service
  7. Start the client and import licence again. Accept the synchronization
  8. If AX crashes again during import, do steps 2-6 again and it will work! :)
I've solved this problem successfully on several installations using the approach above, so try it! :)

Regards,
Anders