Wednesday, October 7, 2015

DIXF setup notes

File name
That was a bad idea to name an entity with characters forbidden by Windows for filenames...

It raises an error while trying to Generate source file.

 Excel version

To work well with Excel we need to have ODBC Excel 64 bit driver installed on the server

cmd (as administrator)

AccessDatabaseEngine_x64 /passive

Excel sheet name

And not to forget to add $ at the end of lookup sheet name.

Yes, $$$ rule as usual…

Saturday, October 3, 2015

How to run a report for multiple records from a grid

Let's say we want to run our report from the previous post not from a Reports menu but directly from the Sales order form. We will use MultiSelectionHelper and its magic method createQueryRanges for this goal.

First of all we need to allow multiple selection for the menu item and place it as a button on the form.

Also we need to slightly change the report controller, so that it could receive the selected records and add an appropriate range to the query before execution.

public static void main(Args _args)
    ItemSalesPriceAndBarcodeController   controller = new ItemSalesPriceAndBarcodeController();

    controller.parmReportName(ssrsReportStr(SalesPriceAndBarcodeReport, Report));
    // if it comes from the form, do not load the saved value
    if (_args && _args.dataset() == (tablenum(SalesTable)))

/// <summary>
///    Executes before the report prompts and calls the method to set the ranges to the query.
/// </summary>
public void prePromptModifyContract()

/// <summary>
/// Sets the report query ranges based on the caller.
/// </summary>
/// <param name="_query">
/// The hold the <c>Query</c> object of the report.
/// </param>
public void setRanges(Query _query)
    QueryBuildDataSource        qbds;
    QueryBuildRange             qbr;
    SalesTable                  salesTable;
    FormDataSource              salesTable_ds;
    FormRun                     caller;
    MultiSelectionHelper        helper;

    if (this.parmArgs() && this.parmArgs().dataset() == (tablenum(salesTable)))
        salesTable = this.parmArgs().record();
        salesTable_ds = salesTable.dataSource();

        caller  =  this.parmArgs().caller();
        //This method will help to get only the marked records from the Grid
        helper  = MultiSelectionHelper::createFromCaller(caller);

        qbds    = _query.dataSourceTable(tablenum(salesTable));

        //Create the Query to filter using itemId
        helper.createQueryRanges(qbds, fieldStr(salesTable, SalesId));
        qbds    = _query.dataSourceTable(tablenum(salesTable));
        qbr = qbds.addRange(fieldNum(salesTable, SalesId));

If user selected certain orders to print from the form, we do not need to restore previously saved parameters.

Feel free to download the full project here.

How to create SSRS report with multiple independent groupings

Let's say we need to print for selected sales orders all their items with sales prices and barcodes. It means our report must look like this.

One page by SalesId, some kind of a card with all items presented in the related SalesLine table. Then for each ItemId we group all its sales prices, first, and all its barcodes, second.

As suggested by Best Practice, we can create one flat temporary table (regular in my case just to ease debugging) to merge all data into one data source for our report.

Additional GroupType field is populated with different values for each table: SalesLine, Barcode, or SalesLine (could be done as enum). They will be used in report design for filtering tablix elements.

As to design we would need the main TablixSalesId with grouping by SalesId and breaking page for each instance.

Then nested TablixItemId grouped by ItemId.

Now we need to add two new ones - TablixBarcode and TablixSalesPrice without any additional grouping but with filters as follows.

This approach with adding "artificial" field to a flat table let to distinguish data and easily group and filter them.

Feel free to download the full project here.

This is the code of two principal methods of  Report data provider class.

Friday, October 2, 2015

How to lookup and set a new value for Financial dimension

Let's say we need to change a value for one of item financial dimension.

I created a simple form with the item list and their default financial dimensions that are controlled by a standard controller.

There are also two unbound controls that allows to select any related financial dimension attribute and its available value. It is done by means of two edit methods and one lookup method, which can be added to your class and used everywhere you need.

A new chosen value can be set for the item financial dimension by the Set new value button. Actually it uses a method that, again, can be added to your class a static one.

I would like to thank Carsten Glem for his comment on this topic.

Here comes the code for the main methods. Feel free also to download the whole project.

Friday, August 21, 2015

Extension Framework and SysGlobalCache issue

There is a new extension framework in AX 2012 that facilitates developing customized class hierarchies by using attributes for instantiating the needed descendant.

I used this simple and brilliant article by Joris de Gruyter while creating all the needed artefacts to test the framework.

Unfortunately, his remark in bold not to forget to Refresh elements after adding a new attribute value for a new descendant class did not help: my test class still instantiated my base TmxDynamics class!

After debugging I found that the Refresh elements does not actually flush StaticMaps for values in GlobalCache.

Neither does SysExtensionCache::clearAllScopes() nor SysFlushAOD::clearGlobalCaches() actually flush the  "SysExtAppClassSearchStratDepth.checkDerivedClasses", which is responsible for finding all inherited classes.

I do not know if this is a Microsoft bug, but if you cannot manage to get your newly added class, try to flush this value from the global cache.

I got what I want.

Happy cache-flushing!

Thursday, April 16, 2015

Make your decision on a new voucher

This is just a short note on new voucher allocation from code.

When you need to allocate a new voucher from a form, use _makeDecisionLater = true, when from code _makeDecisionLater = false; or, alternatively, it should be balanced with a used() call.

Got it from the forum.

A good point to start is the archived article (Russian).

Friday, January 23, 2015

Breakpoint; in CIL code

I needed to debug batch processing in BatchRun.ServerGetTask() method.

It was a wrong idea to add breakpoint; instruction into a managed code. Of course, after stopping my AOS did not start anymore because of the error:

Just-In-Time debugging this exception failed with the following error: The operation attempted is not supported

The easiest way to recover this problem is to delete the forementioned instruction and recompile CIL, but I have not access to AOT X++ editor anymore.

Alternatevily, as described in How-To-Debug-Managed-Code-In-AX2012, I did catch this breakpoint in Visual Studio during the short period between Starting and Stopped statutes of AOS service.

Launch Visual Studio as administrator.

Open the class method in question directly frpm xppCIL\Source folder.

Start the AOS service.

Switch back to Visual Studio and attach to the process. It appeared in a few seconds.

Then it stopped at the problematic breakpoint.

Pressing F5 each time it stopped there (actually every minute, as it supposed to be for batch processing), I changed my code back and run full CIL.

Please, do not place breakpoint; in managed code like I did.

Full MSDN article about debugging in AX 2012.

Tuesday, January 20, 2015

Fast full compilation in AX 2012

This is just a short batch that runs a full compilation by means of AxBuild with after-run cleaning in AX 2012.

cd "C:\Program Files\Microsoft Dynamics AX\60\Server\CGI_DAX62_DEVTM2_AOS1\bin"
axbuild xppcompileall  /s=01 /altbin="C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin"
@echo to start compilation log import press any key
cd "C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin"
ax32.exe -startupCmd=importandcompileaxbuildlog_C:\Program*Files\Microsoft*Dynamics*AX\60\Server\CGI_DAX62_DEVTM2_AOS1\Log

Do not forget to run it as administrator.

The cleaning requires a project installed as described in the second hyper link.