When a Dynamics AX project
comes to an end it is time to create a system configuration document and
present it to the customer. Normally, it is a time-consuming task to
manually go though all the parameter tables used and transfer their
content to a document, in most of the cases, Word document.
Just
recently, I created a small Dynamics AX job for myself, which helps me
with this task, and I think it is worth sharing. The job reads the data
from a defined list of parameter tables and dumps all the data into a Word document along with field labels. The only manual thing to do is to tidy up the document and send it to the customer.
In this recipe, we will
explore the principle behind this. To make this example more
interesting, we will integrate this function into Dynamics AX by means
of the right-click context menu.
Getting ready
Again, prepare a new Microsoft Word template, and save it as table.dotx. The template should contain one bookmark named TableName at the top and one table with a single row and two columns, as shown next:
How to do it...
1. In AOT, create a new class called CreateWordTable with the following code:
class CreateWordTable extends CreateWordLetter
{
TableId tableId;
}
protected Object dialog()
{
Dialog dialog;
;
dialog = super();
system configuration documentsystem configuration documentcreating, Word useddialog.caption("Select table template");
return dialog;
}
TableId parmTableId(TableId _tableId = tableId)
{;
tableId = _tableId;
return tableId;
}
void initCommon()
{
Query query;
QueryRun queryRun;
;
query = new Query();
query.addDataSource(tableId);
queryRun = new QueryRun(query);
queryRun.next();
common = queryRun.get(tableId);
}
public boolean validate()
{;
if (!tableId)
{
return false;
}
return true;
}
public void run()
system configuration documentsystem configuration documentcreating, Word used{
TmpSysTableField tmpSysTableField;
DictField dictField;
int i;
COM tables;
COM table;
COM rows;
COM row;
COM cells;
COM cell;
COM range;
;
this.openWord();
tables = document.tables();
table = tables.Item(1);
rows = table.rows();
bookmarks = document.bookmarks();
this.processBookmark('TableName', tableid2pname(tableId));
this.initCommon();
tmpSysTableField = TmpSysTableField::findTableFields(
null, tableId);
while select tmpSysTableField
{
dictField = new DictField(
tableId,
tmpSysTableField.FieldId);
if (dictField.isSystem())
{
continue;
}
i++;
row = rows.item(i);
cells = row.cells();
cell = cells.item(1);
range = cell.range();
range.insertAfter(tmpSysTableField.FieldLabel);
cell = cells.item(2);
range = cell.range();
system configuration documentsystem configuration documentcreating, Word usedrange.insertAfter(
strfmt('%1', common.(tmpSysTableField.FieldId)));
row = rows.add();
}
row.delete();
word.visible(true);
}
public static CreateWordTable construct(TableId _tableId)
{
CreateWordTable createWordTable;
;
createWordTable = new CreateWordTable();
createWordTable.parmTableId(_tableId);
return createWordTable;
}
public client static void main(Args _args)
{
tableId tableId;
SysContextMenu sysContextMenu;
TreeNode treeNode;
CreateWordTable createWordTable;
;
if (SysContextMenu::startedFrom(_args))
{
sysContextMenu = _args.parmObject();
treeNode = sysContextMenu.first();
if (treeNode.applObjectType() ==
UtilElementType::Table)
{
tableId = treeNode.applObjectId();
}
}
else
{
tableId = pickTable();
}
createWordTable = CreateWordTable::construct(tableId);
if (createWordTable.prompt())
{
createWordTable.run();
}
}
2. In AOT, create a new Action menu item with the following properties:
Property
|
Value
|
---|
Name
|
CreateWordTable
|
ObjectType
|
Class
|
Object
|
CreateWordTable
|
Label
|
Export to Word
|
3. Open the SysContextMenu menu in AOT, and add the created menu item to the top:
4. Locate the SysContextMenu class in AOT, and add the following code to the verifyItem() method:
case menuitemactionstr(CreateWordTable):
if (this.selectionCount() != 1 ||
firstNode.AOTIsOld())
{
return 0;
}
if (!docNode &&
_firstType == UtilElementType::Table &&
SysDictTable::newTreeNode(
firstNode).tableGroup() == TableGroup::Parameter)
{
return 1;
}
return 0;
The code has to be added to this method right after the following lines:
case MenuItemType::Action:
switch (menuItemName)
system configuration documentsystem configuration documentcreating, Word used{
5. To test the results, open AOT and find one of the parameters tables, for example, LedgerParameters. Right-click the mouse, select Add-Ins, and then choose the newly created option Export to Word:
7. The template selection dialog should appear. Choose the previously created Word template as follows:
8. Click the OK button, and view the results:
How it works...
To save our time and to reuse existing code, we are creating a new class that extends CreateWordLetter from the previous recipe. The new class contains the following methods:
dialog() only changes the dialog caption. The rest of the code comes from the parent class.
parmTableId() sets or gets the number of a table to be processed.
initCommon() sets the first record of the input table into the common variable.
validate() checks if the input table number is present.
run() does the job. We start it by opening the Word application and drilling down its object hierarchy, and we get a reference to the table row collection object rows. We are going to use it later for adding new rows to it.
Next, we initialize a bookmarks
object, and insert the input table name as the document title. Then, we
find the first record of the input table to be dumped into the
document. Our modification will only be working with parameter tables,
which have only a single record, so which is we are not expecting and
not processing other records.
findTableFields() of the TmpSysTableField
table is a useful utility, which returns all given table field
properties inserted into a temporary table, which leads us to a
following step to loop through this table and insert its data into the
document.
Inside the loop, first we exclude system fields like RecId, dataAreaId, modifiedBy, and so on. Then, we get a reference to a cell collection object cells
and insert a field label as a first cell and the field value as a
second. To insert the data into the cell, we use COM range object range, which is a part of the Word COM model. We finish each loop by adding a new empty row to the end of the table for the next loop.
At the end of the run(), we delete the last empty table row and show the Word application to the user.
construct() creates a new instance of this class.
main() puts everything together.
Next, we create a new Action menu item pointing to the new class, and we add that item to the SysContextMenu menu. This menu is actually the Add-Ins section of the right-click AOT context menu. In this way, we ensure that the user will see an option Export to Word once they right-click on some object in AOT.
The last step is to make sure that this option is only visible for parameter tables. We can achieve this by adding a new case to the switch statement of verifyItem() in the SysContextMenu class. This method along with the switch
statement is executed every time a user right-clicks the mouse button
on some object in AOT. The method is responsible for checking whether
each item of the SysContextMenu menu is relevant to the current AOT object. It returns 1 if yes and 0 if no. Our code checks the CreateWordTable
menu item and returns a positive result if the selected object is not
in the old layer (tables in old layer do not have any data) and is a
parameter table. This simply means that Export to Word will be available only for parameter tables.