Blog

21 09, 2016

Add AX FormGroupControl runtime

By |2016-09-21T12:20:54+02:00September 21st, 2016|Categories: AX 2012|Tags: , , , , |3 Comments

Apparently you cannot populate a form data group runtime in AX. I have been able to overcome the issue by adding the controls dynamically. In this example I have created a form with InventTable as the datasource, and added a FormGroupControl called FormDataGroup with AutoDeclare = Yes. Then I have overridden the executeQuery() on InventTable_ds to call my custom method stored on the form. Here is the source to add AX FormGroupControl runtime from code:

public void WIK_addFieldGroup()
{
    #AOT
    #Properties

    TreeNode            fieldGroupRoot = TreeNode::findNode(strFmt('%1\\%2\\Field Groups\\%3',
        #TablesPath,
        tableStr(InventTable),
        tableFieldgroupStr(InventTable, Name)));
    TreeNodeIterator    tni = fieldGroupRoot.AOTiterator();
    TreeNode            fieldGroupNode = tni ? tni.next() : null;
    int                 fieldNo;
    DictField           dictField;
    FormControlType     controlType;
    Object              formControl;
        
    formDataGroup.hideIfEmpty(false);
    formDataGroup.caption(fieldGroupRoot.AOTgetProperty(#PropertyLabel));
    
    while (fieldGroupNode)
    {
        fieldNo = (select firstOnly AxId from SysModelElement
            where  SysModelElement.ParentId     == tableNum(InventTable)
                && SysModelElement.ElementType  == UtilElementType::TableField
                && SysModelElement.Name         == fieldGroupNode.AOTname()).AxId;
        
        dictField = new DictField(tableNum(InventTable), fieldNo);
        
        if (!dictField)
        {
            // display method in field group
            fieldGroupNode = tni.next();
            continue;
        }
        
        switch (dictField.baseType())
        {
            case Types::Int64 :
                controlType = FormControlType::Int64;
                break;
            case Types::Integer :
                controlType = FormControlType::Integer;
                break;
            case Types::String :
                controlType = FormControlType::String;
                break;
            case Types::Date :
                controlType = FormControlType::Date;
                break;
            case Types::UtcDateTime :
                controlType = FormControlType::DateTime;
                break;
                
            //TODO: Implement all field types
        }

        formControl = formdatagroup.addControl(controlType, strFmt('InventTable_%1', fieldGroupNode.AOTname()));
        formControl.dataSource(InventTable_ds.id());
        formControl.dataField(fieldNo);
        formControl.label(dictField.label());

        fieldGroupNode = tni.next();
    }
}

 

19 09, 2016

AX License Configuration details

By |2016-09-19T12:27:23+02:00September 19th, 2016|Categories: AX 2012|Tags: , , , |1 Comment

In older versions of AX we could see additional details under System administration > Setup > Licensing > License configuration that has been missing from the more recent versions. Here is an update done in version 2012 R3 to have better AX License Configuration details by showing the configuration key name in the AOT, the application layer and the model:

AX License Configuration details

The heart of the code is as per below to populate the 3 form controls, called in the Tree.selectionChanged() method:

public void WIK_setElement()
{
    int                     idx             = Tree.getSelection();
    FormTreeItem            node            = Tree.getItem(idx);
    boolean                 isValidLicense  = node && !node.overlayImage();
    ConfigurationKeyName    configKeyName;

    SysModelElement     elementNode;
    SysModelElementData elementData;
    SysModelManifest    model;
    SysModelLayer       layer;

    if (isValidLicense)
    {
        configKeyName = configurationkeyId2Name(Tree.getItem(idx).data());

        select firstOnly Name from model
            join elementNode
                where  elementNode.Name         == configKeyName
                    && elementNode.ElementType  == UtilElementType::ConfigurationKey
            join elementData
                where  elementData.ModelElement == elementNode.RecId
                    && model.Model              == elementData.ModelId
            join Layer from layer
                where layer.RecId   == elementData.Layer;
    }

    WIK_LicenseConfig.text(isValidLicense ? configKeyName : '');
    WIK_LicenseLayer.text(isValidLicense ? enum2Symbol(enumNum(UtilEntryLevel), layer.Layer) : '');
    WIK_LicenseModel.text(isValidLicense ? model.Name : '');
}

Here is the XPO of the changes:

Forms_SysConfiguration.zip

5 09, 2016

Deploy all SSRS reports ignoring errors

By |2016-09-05T16:23:17+02:00September 5th, 2016|Categories: AX 2012|Tags: , , , , |1 Comment

If you try to bulk-deploy the SSRS reports from any sources (AX, VS, PowerShell) and you are not using OLAP cubes with SSAS, the deployment will fail with the error message below. Feel free to use this job to deploy all SSRS reports ignoring errors.

Microsoft.ReportingServices.Diagnostics.Utilities.InvalidItemPathException: The path of the item ‘/DynamicsAX/’ is not valid. The full path must be less than 260 characters long; other restrictions apply. If the report server is in native mode, the path must start with slash.

static void WIK_deploySSRSreports_SkipError(Args _args)
{
    #AOT
    
    TreeNodeIterator        reportIterator = TreeNode::findNode(#SSRSReportsPath).AOTiterator();
    SRSReportManager        srsReportManager = new SRSReportManager();
    SSRSReportConceptNode   ssrsReportConceptNode;
    
    if (!reportIterator)
        return;
    
    ssrsReportConceptNode = reportIterator.next();
    while (ssrsReportConceptNode)
    {
        try
        {
            srsReportManager.deploymentStart();
            srsReportManager.deployReport(ssrsReportConceptNode);
            srsReportManager.deploymentEnd();
        }
        catch
        {
            exceptionTextFallThrough();
        }
        
        ssrsReportConceptNode = reportIterator.next();
    }
}

 

17 08, 2016

Validate AIF ports if they are running in AX

By |2016-08-17T09:02:35+02:00August 17th, 2016|Categories: AX 2012|Tags: , , , , |2 Comments

It may happen that AIF ports are failing due to an error, or they do not activate when the AX AOS instance gets started. There is a way to validate AIF ports if they are running correctly using a combination of X++ code and a PowerShell script that you may find below.

Add the following to the Global class collection and do a Full CIL:

public static client boolean WIK_notStartedAIFPortExists()
{
    #Aif
    AifInboundPort  port;
    Map             serviceStatusMap;
    str             serviceTypeName, status;

    boolean ret             = false;
    boolean isPortStarted   = false;

    serviceStatusMap = appl.getServiceHostStatus();


    if (serviceStatusMap)
    {
        while select Name from port
        {
            serviceTypeName = strFmt('%1.%2', #DotNetServiceNamespace, port.Name);

            if (serviceStatusMap.exists(serviceTypeName))
            {
                status            = serviceStatusMap.lookup(serviceTypeName);
                isPortStarted     = strStartsWith(status, #WcfCommunicationState_Opened);

                if (!isPortStarted)
                {
                    ret = true;
                    break;

                }
            }
        }
    }

    return ret;
}

And here is the PowerShell script to validate AIF ports. Please make sure you provide a comma-separated list for the AOS names, and a correct path where the Business Connector DLL could be found. Also you might want to provide credential details for the AX login if the user running the script does not have access.

# Set variables

$computerName = "TESTAOS01,TESTAOS02" -split ','
$AXBCpath = "C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\Microsoft.Dynamics.BusinessConnectorNet.dll"

# Validate Administrative privileges and Elevated command prompt

Write-Host "Validating security privileges... " -NoNewline

if (-NOT [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544"))
{
    throw "You must be running the script in an Elevated command prompt using the Run as administrator option!"
}

if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
    throw "You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!"    
}

Write-Host "Done" -ForegroundColor Yellow

# Script for checking AIF

[ScriptBlock] $global:CheckAIFPorts =
{
    param ([string] $AXBCpath)
    try
    {
        $bcassembly = [Reflection.Assembly]::Loadfile($AXBCpath)      
        $ax = new-object Microsoft.Dynamics.BusinessConnectorNet.Axapta
        $ax.logon("","","","")
        $xSession = $ax.CreateAxaptaObject("XSession")   
        $AOSName = $xSession.call("AOSName")
        
        Write-Host -NoNewline "$AOSName AIF ports are ... "
        if ($ax.CallStaticClassMethod("Global", "WIK_notStartedAIFPortExists") -eq $false)
        #if ($ax.CallStaticClassMethod("Global", "isaos") -eq $true) #"WIK_notStartedAIFPortExists")
        {
            Write-Host "OK" -ForegroundColor Yellow
        }
        else
        {
            Write-Host "NOT OK" -ForegroundColor Red
        }
        $logedOff = $ax.logoff()
    }
    catch [Exception]
    {
	    Write-Host "Failed" -ForegroundColor Red
	    Write-Host $_.exception.message
    }
}

$Session = New-PSSession -ComputerName $computerName
Invoke-Command -Session $Session -ScriptBlock $CheckAIFPorts -ArgumentList "$AXBCpath"
Remove-PSSession -Session $Session

validate AIF ports

 

Go to Top