MSDyn365FO. AX 2012 data upgrade with virtual companies.

In Dynamics 365 for Finance and Operations virtual companies are deprecated and could not be upgraded according to the documentation that just state this fact without proposing a solution.

If we cannot upgrade, then we need to get rid of them. Here are the high level steps how you can de-virtualize a table. Let’s say that we have virtual company V with two companies: A and B. V has only one table collection with one table for simplicity, let’s call it Table1.

  1. Delete a data from Table1 that belongs to the companies A and B. When you create a virtual company, you may already have some data in the tables you want to share. That data may be orphaned, so we want to delete it to avoid duplicates. Can be done via simple T-SQL script:
    DELETE FROM Table1
    WHERE DATAAREAID = 'A' or DATAAREAID = ‘B’
    
  2. Go to System administration > Setup > Virtual company accounts and delete the virtual company.
  3. Restart AX client.
  4. Insert data from the virtual company to de-virtualized companies via X++ job:
    static void deVirtualizeTables(Args _args)
    {
        DataAreaId          virtualDataAreaId = 'V';
        container           oldCompaniesCon = ['A', 'B'];
    
        VirtualDataAreaList virtualDataAreaList;
    
        void deVirtualizeTable(TableId _tableId)
        {
            int             i;
            DataAreaId      dataAreaId;
            SysDictTable    dictTable = new SysDictTable(_tableId);
            Common          buffer = dictTable.makeRecord();
            Common          newBuffer;
    
            while select crossCompany buffer
                where buffer.dataAreaId == virtualDataAreaId
            {
                for (i = 1; i <= conLen(oldCompaniesCon); i++)
                {
                    dataAreaId = conPeek(oldCompaniesCon, i);
                    changeCompany (dataAreaId)
                    {
                        newBuffer = null;
                        newBuffer = dictTable.makeRecord();
    
                        buf2buf(buffer, newBuffer);
                        newBuffer.doInsert();
                    }
                }
            }
        }
    
        select firstOnly RecId from virtualDataAreaList;
    
        if (virtualDataAreaList)
        {
            throw error('Delete a virtual company first!');
        }
    
        ttsBegin;
    
        deVirtualizeTable(tableNum(Table1));
    
        ttsCommit;
    
    }
    
  5. Delete data from the virtual company. It cannot be done from X++ because you cannot use changeCompany with a company that does not exist. T-SQL:
    DELETE FROM Table1
    WEHERE DATAAREAID = 'V'
    

Now we are ready to run standard data upgrade procedure!

MSDyn365FO. Collation conflict during data upgrade from AX 2012

3-4.jpg

AX 2012 has well-known inconsistency in columns collation that has been reported before and now it is causing issues during data upgrade to latest version (8.0) of Dynamics 365 for Finance and Operations.

Data upgrade fails on step 9. postSync for data upgrade. From RELEASEUPDATESCRIPTSERRORLOG table you can get actual error: “[Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Cannot resolve the collation conflict between  “Latin1_General_CI_AS” and “SQL_Latin1_General_CP1_CI_AS” in the equal to operation.” It fails on the next x++ update statement in ReleaseUpdateDB80_TaxGTE_IN.updateTaxMeasureType() method:

update_recordset crosscompany taxMeasureType
    setting ClassNumber = sysModelElement.AxId
       where !taxMeasureType.ClassNumber
       join AxId from sysModelElement
       	where sysModelElement.ElementType == UtilElementType::Class
           && sysModelElement.Name == taxMeasureType.ClassName;

It was introduced in 8.0 update. Here it uses sysModelElement.Name and taxMeasureType.ClassName fields that have different collation.

Now we have several options to fix this:

  • Skip this method if you don’t use IN localization.
  • Convert source DB to D365 server collation.
  • Change collation of Name column in ModelElement table.

I already knew that we cannot simple use

ALTER DATABASE AxDB COLLATE SQL_Latin1_General_CP1_CI_AS;   <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

So, I looked at bacpac option proposed by Lane Swenka. We can export DB to a bacpac, edit collation there and import it again. But after I’ve got several thousand errors during the export I decided to switch to other options. I decided not to touch ModelElement because I hope that there is a reason behind its collation, so I went easiest rout for me – skip the code I don’t need.

I did not find nice and easy way to skip this method because everything is done via reflection and it’s hard to skip something using extension, but there is always a dirty way!

Quick extension forces D365 to run different method one more time that updates nothing instead of method I don’t want:

[ExtensionOf(classStr(SysDictClass))]
public final class SysDictClass_FUS_Extension
{
    public static anytype invokeObjectMethod(Object _object, IdentifierName _methodName, boolean _tryBaseClass)
    {
        if (_methodName == "updateTaxMeasureType")
        {
            _methodName = "updateTaxDocumentExtensionIN";
        }

        return next invokeObjectMethod(_object, _methodName, _tryBaseClass);
    }
}

That’s all. After this small hack update goes nice and smoothly.

DontTryTHisAtHome

Solution proposed here is not a real solution, but a hack. It is ok for me because I’m 100% sure that I don’t need this data to be upgraded. I would strongly recommend converting DB to SQL server collation or raise this with MS in case you hit this to get real solution that would be supported.

Happy hacking!