X++

Refresh AX grid color with displayOption

We have a displayOption implemented for the Vendor transactions screen, which highlights the rows in red color where the posted transactions or the journal has a Document attachment. The requirement was that if we create a new DocuRef entry on the vendor transaction, after closing the DocuView form the grid should change color. In order to refresh AX grid color with displayOption only for the currently selected row we can use the FormDataSource.clearDisplayOption method.

Also there is a great comment on the Dynamics Community about refreshing a set of entries, instead of the current cursor:

https://community.dynamics.com/ax/f/33/t/191245

We have edited \Forms\DocuView\Methods\close as per below to achieve refreshing only the currently highlighted row:

public void close()
{
    FormRun         frm = infolog.parmLastActivatedForm().object();
    FormDataSource  fds;
 
    DocuRef::multiSelectRecordDelete();
 
    SysHelp::initWebBrowser(htmlView);
    curUrl = "";
 
    super();
 
    if (infolog.docu() && infolog.docu().docuView() && infolog.docu().docuView().object())
    {
        infolog.docu().clearDocuView(); //ensure that form is removed from infolog object
    }
 
    if (frm
        && frm.form().name() == formStr(VendTrans))
    {
        fds = frm.dataSource(1);
        fds.clearDisplayOption(fds.cursor());
    }
}
By |2017-02-20T13:32:54+01:00February 20th, 2017|Categories: AX 2012|Tags: , , , , |0 Comments

Add AX FormGroupControl runtime

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();
    }
}

 

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

AX License Configuration details

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

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

Deploy all SSRS reports ignoring errors

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();
    }
}

 

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