Monday, December 5, 2016

Mass update Tracking dimension group

Just a job that makes the subject in three big steps.



Let's say we have a bunch of items for which we set up a wrong Tracking dimension group, 'Multi', for example.

First, it empties the current value in Product Tracking dimension for those with 'Multi' in their items, making possible to change the value in Item Tracking dimension.


Then it set up the new value, for example, 'MultiS' for items and, as the third step, for related Products.

If you press No in the dialog, it just shows the current values without updating anything. So you can estimate the scale of eventual changes.


Please make appropriate customization and use it at your own risk. It calls update().

By the way, if you need to change values en masse without calling update(), please use Universal Field Changer, which could be imported as one class that makes everything possible in any table.

Thursday, December 1, 2016

Access denied to method processReport in class whateverReportDP

If after all sorts of checking your security settings you still have the subject error, please just re-import the problematic SSRS report.

As explained by Nicolas GRANJON, under the hood, I quote, re-importing the report (re)generate data in the ModelSecurityPermission table of the model database, especially the permissions links between the report and the tables that it uses, and the data provider class.

Tuesday, November 29, 2016

How to see beginning balance journals from your projects

There is a bug in AX 2012 R2/R3.

Beginning balance journals do not create transactions in ProjJournalTrans table; therefore, they won't be selected via the standard query.



This is a small fix to show beginning balance journals from the projects forms.

In ProjJournalFormTable class we need to redo datasourceLinkActivePre() method as follows:

public void datasourceLinkActivePre()
{
    QueryBuildDataSource    qbds;
    ProjTable               callerRecord;

    if (formRun                 &&
        formRun.args()          &&
        formRun.args().record() &&
        formRun.args().dataset() == tableNum(ProjTable))
    {
        callerRecord = formRun.args().record() as ProjTable;

        // if this is a beginning balance table we have no transactions in ProjJournalTrans
        // therefore, we need to update the query so that all related journals will be shown.
        if(journalTypeId == 2) // beginning balance
        {
            SysQuery::findOrCreateRange(journalTable_ds.query().dataSourceTable(tableNum(ProjJournalTable)), fieldNum(ProjJournalTable, JournalType)).value(SysQuery::value(ProjJournalType::BegBalance));
            SysQuery::findOrCreateRange(journalTable_ds.query().dataSourceTable(tableNum(ProjJournalTable)), fieldNum(ProjJournalTable, ProjId)).value(SysQuery::value(callerRecord.ProjId));
        }
        else
        {
        // End
            qbds = journalTable_ds.query().dataSourceNo(1).addDataSource(tableNum(ProjJournalTrans));

            qbds.joinMode(JoinMode::ExistsJoin);

            qbds.addRange(fieldNum(ProjJournalTrans, ProjId)).value(callerRecord.ProjId);
            qbds.relations(true);
        }
    }

    super();
}

Saturday, November 26, 2016

Back slashing menu items

It turned out to be a wrong decision to use a back slash character in menu item label. When it is used then in a menu, nothing tells you that something is wrong.



However, no chance to run this menu item from the menu.
So, avoid this and, maybe, other illegal characters for labeling your menu items.

Wednesday, November 16, 2016

Automate Error 351

From time to time we get the lovely CIL compilation Error:351. As suggested by many, for example, by André Arnaud de Calavon, we have to recreate XppIL folder with all its guts.

The sequence is the following (quotation)

1. Stop the AOS.
2. Rename the XppIL folder (C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\XppIL) to e.g. XppIL_old.
3. Start the AOS.
4. Perform a full CIL generation.
A new XppIL folder will be created when you start the AOS.
When the CIL has completed without problems, you can delete the renamed XppIL_old folder.

Clean and simple. However, I am too impatient to wait for the end of deleting this huge folder: Windows Explorer starts time estimation and it drags on.

So, I wrote two short batch files that can be run as administrator and spare your time and nerves.

First just to rename the existing folder.


@echo off
set folder="C:\Program Files\Microsoft Dynamics AX\60\Server\CHR_AX_DEV\bin\"
echo Rename %folder%  XppIL to XppIL_old?
pause
c:
cd %folder% 
ren XppIL XppIL_old
echo %folder%XppIL to  has been renamed to XppIL_old
pause

Second to delete all the files in the 'backed up' folder and its subfolders with no questions and infolog, then to delete the folder itself. As it said here, they must work faster than just removing the folder.

@echo off
set folder="C:\Program Files\Microsoft Dynamics AX\60\Server\CHR_AX_DEV\bin\XppIL_old\"
echo Delete all the files from %folder% and remove the folder?
pause
del /f/s/q %folder% > nul
rmdir /s/q %folder%
echo Folder %folder% removed
pause

The last remarque. Be sure that your service windows account running the AOS in question had Full permission to the C:\Program Files\Microsoft Dynamics AX folder and all its subfolders.

command line

Monday, October 17, 2016

Restrict access to group form control in the grid

Basically to restrict access to a field group it is enough just to set its NeededPermission property, say, to Manual and then you can provide your users with a special privilege on this form control.

However, when it comes to a grid, it is not enough: you also need to change the same property and needed permission on all included fields or, like in my scenario, display methods.



Some helpful articles

Security Permissions Properties for a Form

How to restrict access to a button


Wednesday, July 13, 2016

SysOperationAutomaticUIBuilder (SysOperationUIBuilder) amd Nested Data Contracts

Shame on me, but this the only way I found to get access to form controls exposed from a nested data contract.

Say, we have a data contract (blue) with a nested data contract (orange).



Both will be exposed to the dialog; however, we can have access via binding info to the main contract parameters only. For example, we can change some properties of these fields in postBuild() method in its SysOperationAutomaticUIBuilder (SysOperationUIBuilder) class like follows.


public void postBuild()
{
    DialogField                             dialogField;
    SysOperationUIBindInfo                  bindInfoLoc    = this.bindInfo();

    super();

    contract = this.dataContractObject();

    dialogField = bindInfoLoc.getDialogField(contract, methodStr(****CheckPostContract, parmJournalId));
    dialogField.allowEdit(false);
}

However, we cannot do the same way for parameters from the nested data contract. To attain this goal we can, however, iterate all the form build controls to find the one we need to change. In my example, I used the label of the exposed Currency EDT.


private void setCurrencyCodeFieldMandatory(FormBuildGroupControl _nestedContractGroup //nestedContractGroup)
{
    FormBuildStringControl                  currencyFormBuildStringControl;
    int                                     i;
    Object                                  childControl;

    for (i = 1; i <= _nestedContractGroup.controlCount(); i++) 
    {
        childControl = _nestedContractGroup.controlNum( i );
        if(childControl is FormBuildStringControl)
        {
            currencyFormBuildStringControl  = childControl;
            if(currencyFormBuildStringControl.label() == "@SYS7572")
            {
                currencyFormBuildStringControl.mandatory(true);
            }
        }
    }
}




Wednesday, April 27, 2016

Update access to Financial dimensions on Project Wizard form

Thanks André I found very fast how to fix access issue to Financial dimensions on Project Wizard form. He explained how to solve a similar problem in this thread https://community.dynamics.com/ax/f/33/t/138100

All detail could be found in MSDN article https://msdn.microsoft.com/en-us/library/gg879980.aspx

Briefly we need to add associated form and their form controls to appropriate security privileges or directly to roles.







As we can see DimensionGroup control requires manually set permission. so let's add it in the same way as it done for ProjTable form.




Now Project manager and other roles containing the same privileges will be able not only to view but also to update financial dimensions in the Project Wizard.

Thursday, March 31, 2016

Date valid fields in View and AOT Query

Unfortunately, I did not manage to select addresses effective as of today from the DirPartyPostalAddressView view.



Nevertherless, its ValidTimeStateEnabled property set to Yes.



My workaround is to use SysQueryRangeUtil class providing greaterThanUtcNow and lessThanUtcNow methods to create an extended range. So my Query looks like the following.



Thursday, March 17, 2016

How to add a new field to your AIF service

Let's say we have an AIF inbound port for importing Sales order requisition from a partner, and we need to add a new field to import Contact person.

In standard we can easily import Contact person by its ID. However, it is not probable that your partner uses the same internal IDs for contacts as you do.



On the other hand, there is no standard mapping feature for contact persons via external codes, like those we have for items, customers, vendors and some other artifacts.

So, our goal is to find a way to map an internal contact person ID to a given name. Something similar to what we do by selecting a contact from the drop-down list in Sales order, which is actually an Edit-method.



Basically we will touch two classes only.

First, we add a new parm method to SalesSalesOrder_SalesTable class, which exposes data through SalesSalesOrderService. In other words, this new parameter will be available in the fields of Data policy (document scheme). Also the exist method must be added to the same class.







Then we implement the logic in AxSalesTable class, which finds contact id based on the name provided in the aforementioned parameter.









We place the new method before standard setting Contact person id because the latter will use Contact person id, if it is already found.



Compile and compile incremental CIL.

Next step is to refresh service via Register menu.



We have paved the new way: AIF will search for Contact person based on an imported Person name.




Saturday, March 12, 2016

Multi thread parallelism and a dispatching table for finding a minimum

In my free time I enjoy by solving programming puzzles from Advent of Code website. Some of them are pretty simple, though others could be tricky, however, all of them are always witty. Of course, I do it in AX so that I could use as much its power as possible.

The day 4 Ideal Stocking Stuffer became a die-hard to me. And it is not because of its "business complexity" -- you simply need to find the lowest positive number producing an MD5 hash for a given secret code, so that such a hash, in hexadecimal, starts with at least five zeroes.

Honestly, I have a vague idea about MD5 hash math -- I just took a working example and injected it into my class.

The stumbling point here was calculation time. Even for the first part of the puzzle, which is always easier than than the second one, it took so much time that I started flirting with the idea to improve performance.

Wrapping the MD5 hash calculation method so that it could be run in CIL got it faster but not enough to be happy.



The next idea was batch task execution in parallel threads, like it is brilliantly explained by Ganas1 in four chapter blog series:

Batch Bundling

However, we need to find the lowest positive number; therefore, we do not know how many tasks must be created. (Let's assume that we are limited with the maximum of Int64)


My solution is the following.

I created a table, which is to centrally dispatch creating, executing, and stopping batch tasks based on a separate, sequentially assigned positive number ranges. So, for each batch task it keeps the assigned thread number, ranges, execution status and found results, if any.



The batch task generating class creates them for a given number of logical processors, four in my environment.


Each task checks the table for a found result. If it is already found in any range, it stops.
If not, it looks for the highest range from the table and than tries to add a new record. In case of success, it runs finding the lowest number in the given range.

If such a number is found in the current thread, this value becomes a new candidate only if there are no results found in the lower ranges and no any lower ranges still running.


Now blood runs faster: even the second part of the job did not give me a pause to get another beer from the fridge.

However, it is up to your judgement to set up the right range size and number of parallel tasks. The smaller a single step is, the more the transaction cost will be. And vice-versa, the larger the range is, the longer you need to wait the higher ranges tasks to finish: the total execution time is the longest task's.


This project comprises examples of the following techniques:

  • dynamic dialog creation on RunBaseBatch
  • wrapping for execution in CIL
  • execution time calc
  • multiple batch task creation
  • try-catch exception handling for concurrent table updating
  • InteropPermission assertion


Happy AX mining!

Wednesday, March 2, 2016

Infolog in batch task history using SysOperation framework

If you add tasks to a batch job using SysOperation framework, like the following:


[...]

while (mapIterator.more())
    {
        if (!batchHeader)
        {
            batchHeader = this.getCurrentBatchHeader();
        }

        controller = wblDirPartyMergeTaskController::construct();

        [...]

        if (batchHeader)
        {
            batchInfo = controller.batchInfo();
            batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
        }
        else
        {
            setPrefix(batchInfoStr);
            controller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
            controller.run();
        }
      
        mapIterator.next();
    }

    if (batchHeader)
    {
        batchHeader.save();
    }
}

You need to set Execution mode to Synchronous while initializing your service class controller.

// class wblDirPartyMergeTaskController extends SysOperationServiceController
public void new()
{
    super();
    // default for controllers in these classes is synchronous execution
    // batch execution will be explicitly specified. The default for
    // SysOperationServiceController is ReliableAsynchronous execution
    this.parmExecutionMode(SysOperationExecutionMode::Synchronous);

    this.parmOther(newValue);
}
      
Otherwise you will see nothing in bath tasks history infolog.

Saturday, February 6, 2016

addField and extendedTypeStr

In AX 2012 we cannot assign types for dialog fields directly anymore -- only by means of extendedTypeStr precompiled function.

This is how to add a field for keeping int64 values:

DialogField     dlgBiggestNum;
int64           theBiggestNumer;

dlgBiggestNum       = dlg.addFieldValue(extendedTypeStr(RecId), theBiggestNumer);

Wednesday, January 20, 2016

Export-Import XPO files

Among our AX mundane chores I would highlight the promotion process. Nevertheless, this changed drastically since AX 2012 started using models and model stores; sometimes, the old good way is in demand, yet.

I am talking about promoting your projects between environments via exporting and importing XPO files. First, we export the project from, say, DEV environment to an XPO file. Then we import it to TEST environment with the Definition only option checked. After that we export the current content of this project as a back up. Finally, we re-import the project entirely.

Schematically the process goes as follows:

DEV-MyProject->DEV\MyProject.xpo
TEST<-DEV\MyProject.xpo (definition)
TEST-MyProject->\BackUp\MyProject
TEST<-DEV\MyProject.xpo

Not a big deal when it comes to one-two projects only. Everything changes when you need to cope with a dozen of them... I developed a small class that keeps me lazy by doing all these steps.

You need to set up its parameters for every environment where you need to promote projects.



It reads project names to export-import line by line from a plain text file and processes them one by one.

Note: it works on CUS layer only and without labels; however, it can be easily adapted to your context.

Here is the link to download.