Anyone who preformed Dynamics AX application installation or upgrade has to be familiar with standard checklists. Normally, a checklist is a list of menu items displayed in logical sequence. Each item represents either mandatory or optional actions to be executed by the user in order to complete the whole procedure. In custom Dynamics AX implementations, checklists can be used as a convenient way to configure non standard settings. Checklists can also be implemented as a part of third-party modules for their initial setup.
In this recipe, we will create a checklist for user-friendly ledger budget setup. The checklist will consists of two mandatory and one optional item.
interface SysCheckListInterfaceBudget
extends SysCheckListInterface
{
}
class SysCheckListItem_BudgetModel
extends SysCheckListItem
implements SysCheckListInterfaceBudget
{
}
public str getCheckListGroup()
{
return "Setup";
}
public str getHelpLink()
{
#define.TopicId('AxShared.chm::/html/' +
'84030522-0057-412C-BFC7-DBEB4D40E5A1.htm')
;
return SysCheckListItem::sharedGuide(#TopicId);
}
public MenuItemName getMenuItemName()
{
return menuitemdisplaystr(BudgetModel);
}
public MenuItemType getMenuItemType()
{
return MenuItemType::Display;
}
str label()
{
return "Models";
}
class SysCheckListItem_BudgetRevision
extends SysCheckListItem
implements SysCheckListInterfaceBudget
{
}
public void new()
{;
super();
this.placeAfter(classnum(SysCheckListItem_BudgetModel));
this.indeterminate(true);
}
public str getCheckListGroup()
{
return "Setup";
}
public str getHelpLink()
{
#define.TopicId('AxShared.chm::/html/' +
'AACC4353-C3EB-4982-BB7F-2B36D97FF25B.htm')
;
return SysCheckListItem::sharedGuide(#TopicId);
}
public MenuItemName getMenuItemName()
{
return menuitemdisplaystr(BudgetRevision);
}
public MenuItemType getMenuItemType()
{
return MenuItemType::Display;
}
str label()
{
return "Revisions";
}
class SysCheckListItem_Budget
extends SysCheckListItem
implements SysCheckListInterfaceBudget
{
}
public void new()
{;
super();
this.addDependency(
classnum(SysCheckListItem_BudgetModel));
this.placeAfter(
classnum(SysCheckListItem_BudgetRevision));
}
public str getCheckListGroup()
{
return "Create budgets";
}
public str getHelpLink()
{
#define.TopicId('AxShared.chm::/html/' +
'6A596E1E-6803-4410-B4E4-EDE4EF44AF6D.htm')
;
return SysCheckListItem::sharedGuide(#TopicId);
}
public MenuItemName getMenuItemName()
{
return menuitemdisplaystr(LedgerBudget);
}
public MenuItemType getMenuItemType()
{
return MenuItemType::Display;
}
str label()
{
return "Budgets";
}
class SysCheckList_Budget extends SysCheckList
{
container log;
}
protected str getCheckListCaption()
{
return "Budget checklist";
}
protected str getHtmlHeader()
{
return "Budget checklist";
}
protected classId getInterfaceId()
{
return classnum(SysCheckListInterfaceBudget);
}
public void save(
identifiername _name,
ClassDescription _description)
{;
if (!confind(log, _name))
{
log = conins(log, conlen(log)+1, _name);
}
}
public boolean find(
identifiername _name,
ClassDescription _description)
{
return confind(log, _name) ? true : false;
}
static void main(Args _args)
{;
SysCheckList::runCheckListSpecific(
classnum(SysCheckList_Budget),
true);
}
protected static container checkListsHook()
{
return [classnum(SysCheckList_Budget)];
}
protected static container checkListItemsHook()
{
return [classnum(SysCheckListItem_Budget),
classnum(SysCheckListItem_BudgetRevision),
classnum(SysCheckListItem_BudgetModel)];
}
public void close()
{;
super();
SysCheckList::finished(
classnum(SysCheckListItem_BudgetModel));
}
public void close()
{;
super();
SysCheckList::finished(
classnum(SysCheckListItem_BudgetRevision));
}
public void close()
{;
super();
SysCheckList::finished(
classnum(SysCheckListItem_Budget));
}
Property | Value |
Name | SysCheckList_Budget |
Label | Budget checklist |
ObjectType | Class |
Object | SysCheckList_Budget |
The main principle behind checklists is that we have to create a main class, which represents the checklist itself and a number of SysCheckListItem item classes, which act as list items. The relation between the main class and the items is made by the use of an interface, that is, each list item implements it, and the main class holds the reference to it.
In this example, we create an interface SysCheckListInterfaceBudget and specify it in the getInterfaceId() of the main checklist class SysCheckList_Budget. Next, we implement the interface in three SysCheckListItem classes, which correspond to Models, Revisions, and Budgets items in the checklist.
Each SysCheckListItem class contains a set of inherited methods, which allows us to define a number of different parameters for individual items:
Once the items are ready, we create the main checklist class SysCheckList_Budget, which extends the standard SysCheckList. We override some of the methods to add custom functionality to the checklist:
The display menu item we created is pointing to the checklist class and may be used to add the checklist to a user menu.
When building checklists, it is necessary to add them and their items to the global checklist and checklist item list. The SysCheckList class contains two methods—checkLists() and checkListItems()—where all system checklists and their items are registered. The same class provides two more methods—checkListsHook() and checkListItemsHook()—where custom checklists should be added. As a part of this example, we also add our budget checklist and its items to the SysCheckList.
Final modifications have to be done in all checklist forms. We call the finished() of the SysCheckList class in the close() of each form to update the status of the corresponding checklist item. In other words, it means that item status will be set as completed when the user closes the form. This code does not affect the normal use of the form when it is opened from the regular menu. Of course, more logic could be added here if the completion of a specific item is not that straightforward.
Also notice that the system automatically adds a link called Information, which describes the checklist statuses:
The checklist in this example stores item statuses per each run. This means that every time you close the checklist, its statuses are lost and are set to their initial states upon checklist start. By replacing save() and find() in SysCheckList_Budget with the following code, we can store statuses permanently in the SysSetupLog table:
public boolean find(
identifiername _name,
ClassDescription _description)
{
return SysSetupLog::find(_name, _description).RecId != 0;
}
public void save(
identifiername _name,
ClassDescription _description)
{;
SysSetupLog::save(_name, _description);
}
In this case, every time the checklist starts, the system will pick up its last status from the SysSetupLog table and allow the user to continue the checklist.
Go to the Main Table Form is a feature of Dynamics AX, which allows users to jump to the main record just by right-clicking on the field and selecting the Go to the Main Table Form option. It is based on table relations and is available for those controls whose data fields have foreign key relationships with other tables.
Because of the data structure integrity, this feature works most of the time. However, when it comes to complex table relations, it does not work correctly or does not work at all. Another example of when this feature does not work automatically is when the form control is not bound to a table field. In such situations, Go to the Main Table Form has to be implemented manually.
In this recipe, to demonstrate how it works, we will modify the Business relations form in the CRM module to make sure that the Employee filter at the top of the form allows users to use the Go to the Main Table Form feature from the context menu.
public void jumpRef()
{
EmplTable emplTable;
Args args;
MenuFunction menuFunction;
;
emplTable = EmplTable::find(this.text());
if (!emplTable)
{
return;
}
args = new Args();
args.caller(element);
args.record(emplTable);
menuFunction = new MenuFunction(
menuitemdisplaystr(EmplTable),
MenuItemType::Display);
menuFunction.run(args);
}
Normally, the Go to the Main Table Form feature is controlled by the relations between tables. If there are no relations or the form control is not bound to a table field, then this option is not available. But, we can force this option to appear by overriding the control’s jumpRef() method.
In this method, we have to add code that opens the relevant form. This can be done by creating, initializing, and running a FormRun object, but the easier way is to simply run the relevant menu item. In this recipe, the code in jumpRef() does exactly that.
First, we check if the value in the control is a valid employee number. If yes, then we run the Display menu item EmplTable with an Args object containing the proper employee record. The rest is done automatically by the system, that is, the Employee form is opened with the employee information.
I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…
Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…
Once we learn how to deploy an Ubuntu server, how to manage users, and how…
Key-takeaways: Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…
While developing a web application, or setting dynamic pages and meta tags we need to deal with…
Software architecture is one of the most discussed topics in the software industry today, and…