Skip to main content

Installing Feature pack on AX 2012 CU3

I recently had to install the Feature pack on CU3 environment and I thought I should share my experience.

Note: I won’t go through every single detail. Just the database and models.

 

  • Install the Feature pack for the Database using the full Feature pack disc

This will warn you that you have a new model and it may override

clip_image001

  • Install CU3 and this should import a model "Extensions update" model
  • Install your custom model - merge whatever you need to merge
  • Start your AOS - make sure your service and client are both on the CU3 build
  • Run the client and you should see "The model store has been modified" pop up

Select "Start the software update checklist" since this is a minor update

clip_image002

  • The software update checklist will start

Follow the check list - I did my merge already before so I didn’t run the "Detect code upgrade conflicts". I marked it as compete. You could do yours here.

clip_image003

  • When you get to the "Synchronize database" it may fail. It failed for me because of ID conflicts.

clip_image004

I did a bit of digging and found the application id and the SQL dictionary id was different.

clip_image005

It renumbered the field DueDateLimitGroupId_ES (Foundation KB2709934 which is CU3) from 60001 to 60003.
Then the new field "DefaultPaymTerm_PSN" (Extensions model) took 60001. That is why it tries to run an update of the field and failed.

Only thing I can point it at is that when I installed the feature pack. It removed CU3, then installed the feature pack. Which took the first free ID. Then when I installed CU3, it took the next free ID.

  • To fix this issue I had to run a clean up job to update the SQL dictionary with the new ID. If there is a name match.

Thanks to this blog http://dev.goshoom.net/en/2011/11/id-change/

I made a small change to skip views as that didn’t work.

static void UpdateDataDictionaryID(Args _args)
{
Dictionary dictionary = new Dictionary();
SysDictTable dictTable;
DictField dictField;
TableId tableId;
FieldId fieldId;
SqlDictionary sqlDictionaryTable;
SqlDictionary sqlDictionaryField;   setPrefix("Update of data dictionary IDs");
tableId = dictionary.tableNext(0);
ttsbegin;   while (tableId)
{
dictTable = new SysDictTable(tableId);
setPrefix(dictTable.name());   if (!dictTable.isSystemTable() && !dictTable.isView())
{
//Finds table in SqlDictionary by name in AOT, if ID was changed.
//Empty field ID represents a table.
select sqlDictionaryTable
where sqlDictionaryTable.name == dictTable.name()
&& sqlDictionaryTable.fieldId == 0
&& sqlDictionaryTable.tabId != dictTable.id();   if (sqlDictionaryTable)
{
//Updates table ID in SqlDictionary
if (ReleaseUpdateDB::changeTableId(
sqlDictionaryTable.tabId,
dictTable.id(),
dictTable.name()))
{
info(strFmt("Table ID changed (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id()));
}
}   fieldId = dictTable.fieldNext(0);   //For all fields in table
while (fieldId)
{
dictField = dictTable.fieldObject(fieldId);   if (dictField.isSql() && !dictField.isSystem())
{
//Finds fields in SqlDictionary by name and compares IDs
select sqlDictionaryField
where sqlDictionaryField.tabId == dictTable.id()
&& sqlDictionaryField.name == dictField.name()
&& sqlDictionaryField.fieldId != 0
&& sqlDictionaryField.fieldId != dictField.id();   if (sqlDictionaryField)
{
//Updates field ID in SqlDictionary
if (ReleaseUpdateDB::changeFieldId(
dictTable.id(),
sqlDictionaryField.fieldId,
dictField.id(),
dictTable.name(),
dictField.name()))
{
info(strFmt("Field %1 - ID changed (%2 -> %3)",
dictField.name(),
sqlDictionaryField.fieldId,
dictField.id()));
}
}
}
fieldId = dictTable.fieldNext(fieldId);
}
}
tableId = dictionary.tableNext(tableId);
}
ttscommit;   info("Done");
}


  • To deal with the views as that will error out with the sync (very weird errors but the Event viewer errors are more helpful), I wrote a job to give me an info log to drop all the views. If you are concerned about this - you can just run the sync and get a list of views it fails on and just delete them one at a time.

Copy the infolog and run it in SQL.






static void ShowViewDataDictionaryID(Args _args)
{
Dictionary dictionary = new Dictionary();
SysDictTable dictTable;
DictField dictField;
TableId tableId;
FieldId fieldId;
SqlDictionary sqlDictionaryTable;
SqlDictionary sqlDictionaryField;   tableId = dictionary.tableNext(0);   while (tableId)
{
dictTable = new SysDictTable(tableId);
setPrefix(dictTable.name());   if (!dictTable.isSystemTable() && dictTable.isView())
{
info(strFmt("DROP VIEW %1",dictTable.name()));   }
tableId = dictionary.tableNext(tableId);
}   info("Done");
}



  • Continue the sync. This time it should work fine. You may have to do it twice to get it to work smooth. This is because of the views deleted may have dependencies.

  • Complete the remaining checklist tasks

Good to see someone had a similar issue recently.

http://sashanazarov.blogspot.com/2012/09/id-change-in-dynamics-ax-data-dictionary.html

 

Final thought.

We should go with the Feature pack with all new clients. Microsoft has already put a strong statement out on partner source on why we should adopt it.

The Microsoft Dynamics AX 2012 Feature Pack—Why You Should Adopt it Moving Forward

"When Microsoft Dynamics AX 2012 R2 is released in H1 Calendar Year 2013 it will automatically install the remaining Microsoft provided country localizations (Asia, South America, Eastern Europe, etc.); so that we maintain the single consistent Microsoft Dynamics AX 2012 code base."

From this statement, it looks like Microsoft will release a single code base and their won't be a choice in the future. Everything will be installed, including the industry solutions (ie. Feature pack).

Popular posts from this blog

AX - How to use Map and MapEnumerator

Similar to Set class, Map class allows you to associate one value (the key) with another value. Both the key and value can be any valid X++ type, including objects. The types of the key and the value are specified in the declaration of the map. The way in which maps are implemented means that access to the values is very fast. Below is a sample code that sets and retrieves values from a map. static void checkItemNameAliasDuplicate(Args _args) { inventTable inventTable; Map map; MapEnumerator mapEnumerator; NameAlias nameAlias; int counter = 0; ; map = new Map(Types::String, Types::Integer); //store into map while select inventTable { nameAlias = inventTable.NameAlias; if (!map.exists(nameAlias)) { map.insert(nameAlias, 1); } else { map.insert(nameAlias, map.lookup(nameAlias) + 1); } } //retrieve fro

AX - How to use Set and SetEnumerator

The Set class is used for the storage and retrieval of data from a collection in which the values of the elements contained are unique and serve as the key values according to which the data is automatically ordered. You can create a set of primitive data types or complex data types such as a Class, Record or Container. Below is sample of a set of records. static void _Set(Args _args) {     CustTable       custTable;     Set             set = new Set(Types::Record);     SetEnumerator   setEnumerator;     ;     while select custTable     {         if (custTable && !set.in(custTable))         {             set.add(custTable);         }     }     if (!set.empty())     {         setEnumerator = set.getEnumerator();         setEnumerator.reset();         while (setEnumerator.moveNext())         {             custTable = setEnumerator.current();             info(strfmt("Customer: %1",custTable.AccountNum));         }     } } Common mistake when creating a set of recIds

Import document handling (attachment) files #MSDyn365FO

Out of the box you have limited data entities for migrating attachments. If you search what is already in the AOT, you will see a few various examples. I suggest you look at the LedgerJournalAttachmentsEntity as it is the simplest and cleans to copy from. I wont go into detail but I will give a quick run down of what it looks like. Use the DocuRefEntity as your main datasource. It does most of the work for you. Set your table you want to import for as the child datasource Add the Key You will need to add the postLoad method. There is minor code to update the virtual field FileContents. Below is an export I did for the general journal attachments. The import zip structure should be the same way. It will create the usual artifacts such as the excel, manifest and package header xml files. You will see a Resources folder under that. If you drill down to the resources you will see the attachments. This is an export and it used the document GUID for uniqueness. The other thing is the extensi