During our code upgrade we have identified that the SysDeletedObjects configuration keys were still turned on. Also many DEL_-prefixed fields were being referred or used in various places. We had to come up with a way for finding objects marked for deletion.
The documentation gives you an idea of what to do with such elements.
https://docs.microsoft.com/en-us/dynamicsax-2012/appuser-itpro/delete-obsolete-application-objects
https://docs.microsoft.com/en-us/dynamicsax-2012/developer/best-practices-tables
The following job can produce a CSV file, which contains everything with the DEL_ prefix, their table property and layer information:
static void findDelObjectsinAOTTree(Args _args)
{
#Properties
#AOT
int row;
xRefReferences xRefReferences;
xRefPaths xrefPath;
xRefNames xrefNames;
str 4 strMatch = 'DEL_';
str 100 configKey;
TreeNode treeNode;
TreeNodeIterator treeNodeIterator;
TreeNodeTraverser treeNodeTraverser;
str 30 layerObject;
int i;
SetEnumerator setEnumerator;
Set layers;
UtilEntryLevel layerEnum;
#File
CommaTextIo commaTextIo;
FileIOPermission permission;
CustTable custTable;
str fileName = @"D:\Temp\delobjectsinAOT.csv";
MenuFunction mf;
void findNodes(TreeNode _treeNodeLoc, boolean traverse = false)
{
treeNode = TreeNode::findNode(_treeNodeLoc.treeNodePath());
treeNodeIterator = treeNode.AOTiterator();
if (traverse)
{
treeNodeTraverser = new TreeNodeTraverser(TreeNode::findNode(_treeNodeLoc.treeNodePath()));
while (treeNodeTraverser.next())
{
treeNode = treeNodeTraverser.currentNode();
if (treeNode && findProperty(treeNode.AOTgetProperties(), #PropertyName))
{
if (subStr(treeNode.AOTgetProperty(#PropertyName), 0 , strLen(strMatch)) == strMatch)
{
if (treeNode && treeNode.TreeNodeType().isLayerAware())
{
//layerObject = treeNode.AOTLayers().toString();
layers = treeNode.AOTLayers();
setEnumerator = layers.getEnumerator();
layerObject = '';
while (setEnumerator.moveNext())
{
layerEnum = SetEnumerator.current();
layerObject += enum2str(layerEnum);
}
setEnumerator = null;
}
if (findProperty(treeNode.AOTgetProperties(), #PropertyConfigurationKey))
{
commaTextIo.write(treeNode.treeNodePath(), treeNode.AOTgetProperty(#PropertyConfigurationKey), layerObject) ;
layerObject = '';
}
else
{
if (treeNode.AOTObjectNode())
{
commaTextIo.write(treeNode.treeNodePath(), '', layerObject) ;
layerObject = '';
}
}
}
}
}
}
else
{
while (treeNode)
{
if (treeNode && findProperty(treeNode.AOTgetProperties(), #PropertyName))
{
if (treeNode && treeNode.TreeNodeType().isLayerAware())
{
//layerObject = treeNode.AOTLayers().toString();
layers = treeNode.AOTLayers();
setEnumerator = layers.getEnumerator();
layerObject = '';
while (setEnumerator.moveNext())
{
layerEnum = SetEnumerator.current();
layerObject += enum2str(layerEnum);
}
setEnumerator = null;
}
if (subStr(treeNode.AOTgetProperty(#PropertyName), 0 , strLen(strMatch)) == strMatch)
{
if (findProperty(treeNode.AOTgetProperties(), #PropertyConfigurationKey))
{
commaTextIo.write(treeNode.treeNodePath(), treeNode.AOTgetProperty(#PropertyConfigurationKey), layerObject) ;
layerObject = '';
}
else
{
if (treeNode.AOTObjectNode())
{
commaTextIo.write(treeNode.treeNodePath(), '', layerObject) ;
layerObject = '';
}
}
}
}
treeNode = treeNodeIterator.next();
}
}
treeNode = null;
treeNodeIterator = null;
treeNodeTraverser = null;
}
if (!isRunningOnServer())
{
mf = ClassFactory::makeObjectOnServer(classNum(MenuFunction));
mf.objectType(MenuItemObjectType::Job);
mf.object(funcName());
mf.runOn(ClassRunMode::Server);
mf.run();
return;
}
else
{
info('running code on server side');
}
permission = new FileIOPermission(fileName,#io_write);
permission.assert();
commaTextIo = new CommaTextIo(fileName,#io_write);
commaTextIo.outFieldDelimiter('|');
treeNode = TreeNode::findNode(#AOTRootPath);
treeNodeIterator = treeNode.AOTiterator();
TreeNode = treeNodeIterator.next();
findNodes(TreeNode::findNode(#TablesPath), true);
findNodes(TreeNode::findNode(#ExtendedDataTypesPath));
findNodes(TreeNode::findNode(#BaseEnumsPath));
findNodes(TreeNode::findNode(#ViewsPath), true);
findNodes(TreeNode::findNode(#TableMapsPath), true);
findNodes(TreeNode::findNode(#FormsPath));
findNodes(TreeNode::findNode(#ClassesPath));
findNodes(TreeNode::findNode(#MenusPath));
findNodes(TreeNode::findNode(#MenuItemsDisplayPath));
findNodes(TreeNode::findNode(#MenuItemsOutputPath));
findNodes(TreeNode::findNode(#MenuItemsActionPath));
findNodes(TreeNode::findNode(#SecPrivilegesPath));
info('Export has finished');
}
The final results looks like this after executing the job for finding objects marked for deletion:
