Tuesday, November 6, 2018

Create LedgerDimension from main account and financial dimensions

The following code will (hopefully) keep generating valid LedgerDimension values, even after account structures or advanced rule structures are modified.

In the example below, there is an account structure with MainAccount-BusinessUnit-Department segments and an advanced rule with Project segment.

class TestLedgerDim
{        
    public static DimensionDynamicAccount generateLedgerDimension(
        MainAccountNum _mainAccountId,
        str _departmentId,
        str _businessUnit,
        str _projectId)
    {
        DimensionAttributeValueSetStorage dimensionAttributeValueSetStorage 
            = new DimensionAttributeValueSetStorage();

        void addDimensionAttributeValue(
            DimensionAttribute _dimensionAttribute, 
            str _dimValueStr)
        {
            DimensionAttributeValue dimensionAttributeValue;

            if (_dimValueStr != '')
            {
                dimensionAttributeValue = 
                    DimensionAttributeValue::findByDimensionAttributeAndValueNoError(
                        _dimensionAttribute,
                        _dimValueStr);
            }

            if (dimensionAttributeValue.RecId != 0)
            {
                dimensionAttributeValueSetStorage.addItem(dimensionAttributeValue);
            }
        }

        DimensionAttribute dimensionAttribute;

        while select dimensionAttribute
            where dimensionAttribute.ViewName == tableStr(DimAttributeOMDepartment)
               || dimensionAttribute.ViewName == tableStr(DimAttributeOMBusinessUnit)
               || dimensionAttribute.ViewName == tableStr(DimAttributeProjTable)
        {
            switch (dimensionAttribute.ViewName)
            {
                case tableStr(DimAttributeOMDepartment):
                    addDimensionAttributeValue(dimensionAttribute, _departmentId);
                    break;

                case tableStr(DimAttributeOMBusinessUnit):
                    addDimensionAttributeValue(dimensionAttribute, _businessUnit);
                    break;

                case tableStr(DimAttributeProjTable):
                    addDimensionAttributeValue(dimensionAttribute, _projectId);
                    break;
            }            
        }

        RecId defaultDimensionRecId = dimensionAttributeValueSetStorage.save();

        return LedgerDimensionFacade::serviceCreateLedgerDimension(
            LedgerDefaultAccountHelper::getDefaultAccountFromMainAccountRecId(
                MainAccount::findByMainAccountId(_mainAccountId).RecId),
            defaultDimensionRecId);
    }

    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {   
        // Prints 110110-001-022-000002
        info(
            DimensionAttributeValueCombination::find(
                TestLedgerDim::generateLedgerDimension(
                    '110110', 
                    '022', 
                    '001', 
                    '000002')).DisplayValue);
    }

}

Tuesday, November 29, 2016

Monday, October 3, 2016

Bugs in the DateTimeUtil::getSystemDateTime()

After the current system date is changed, either from the user interface or via systemDateSet function, the DateTimeUtil::getSystemDateTime() goes out of control. Don't ever use this function for unique keys generation.

Unfortunately, they use it a lot in the DIXF.

static void printDateTimeJob(Args _args)
{
    void printDateTime()
    {
        info(strFmt('systemDateGet: %1 %2', 
            systemDateGet(), 
            time2StrHMS(timeNow())));
        info(strFmt('getSystemDateTime: %1', DateTimeUtil::getSystemDateTime()));
        info(strFmt('utcNow: %1', DateTimeUtil::utcNow()));
    }
 
    warning('Before date/time change');
 
    printDateTime();
 
    sleep(2000);
 
    info('...2 seconds later:');
 
    printDateTime();
 
    systemDateSet(systemDateGet() - 3);
 
    warning('System date changed:');
 
    printDateTime();
 
    sleep(2000);
 
    info('...2 seconds later:');
 
    printDateTime();
 
    systemDateSet(systemDateGet() + 3);
 
    warning('System date is back:');
 
    printDateTime();
 
    sleep(2000);
 
    info('...2 seconds later:');
 
    printDateTime();
}



And this is the result:


P. S.: Kernel version 6.3.4000.1745, Application version 6.3.3000.110

Monday, April 4, 2016

Error executing code: The field with ID '0' does not exist in table 'SysExtensionSerializerExtensionMap'.

If you ever get "Error executing code: The field with ID '0' does not exist in table 'SysExtensionSerializerExtensionMap'." error in CIL, it may happen it is the table extension framework + the standard buf2buf function to blame.


Somehow, if you pass a Map instead of a table to buf2buf function, the following line fails:

_to.(fieldId) = _from.(fieldId);


However, if you replace this line with:


fieldName = fieldId2name(_from.TableId, fieldId);
 _to.setFieldValue(fieldName, _from.getFieldValue(fieldName));


everything is fine both in X++ and in CIL.


P.S. I didn't dare to change the standard buf2buf method. Istead, I created another method and used it in the table extension framework logic, which failed because of the issue (\Data Dictionary\Maps\SysExtensionSerializerMap\Methods\copyExtensionTableData)


P.P.S. There are a couple of other SysExtensionSerializerMap methods that should be fixed, as they call buf2buf too.


Tuesday, March 8, 2016

DIXF: wrong field values after sales order import?

The more I work with the Data Import-Export Framework in AX 2012 R3, the more fun I have.

Never ever put an initValue() method call after any initFrom<some table name> method.


Thursday, December 10, 2015

New release of the CGI Code Review Tool for Dynamics AX

I am happy to announce the new release of the CGI Code Review Tool for Dynamics AX.

Only AX 2012 version is supported this time, but should you need an AX 2009 version, please let me know and I will prepare and upload an extra XPO.

In earlier releases of the tool, user might see that comparison tool didn't show the exact change for overlayered objects in some cases. This has been fixed now.

Enjoy.