UtilElements stores the meta-data of all the AOT objects and even this table can also be used as log for tracking creation and modification of AOT objects.
And UtilElements is not RDBMS Table , whereas it uses filesystem to store data.
AOT Path for all system tables : \AOT\System Documentation\Tables\
Can be used for ..
1. To track the object creation and modification during development. i,e When you create a new or modify existing node in AOT , like table,field,report,index etc ax kernel creates/updates records in UtilElements table .
2. Even forms/reports accessed by the end-user can be tracked .
Limitation of this log table is we can't track the deleted objects in AOT.
So if you want to track who modified what object , this is the best place to look in.
X++ Code to Export AOT Object / automatic backup of xpo's.
we need to take daily backup of our development (interms of xpo) , but we all always forget becuase of our hectic work schedules.
So use the following job and build batch application to take backup of developement in terms of xpo .
We can take aod also but the size will be more and needs to stop AOS everytime .
Following job illustrates how to export/backup all AOT Classes object.
void DEV_ExportTreeNodeExample()
{
TreeNode treeNode;
FileIoPermission perm;
#define.ExportFile(@"c:\AOTclasses.xpo")
#define.ExportMode("w")
;
perm = new FileIoPermission(#ExportFile, #ExportMode);
if (perm == null)
{
return;
}
perm.assert();
treeNode = TreeNode::findNode(@"\Classes");
if (treeNode != null)
{
// BP deviation documented.
treeNode.treeNodeExport(#ExportFile);
}
CodeAccessPermission::revertAssert();
}
Enjoy !!!!!!!!
So use the following job and build batch application to take backup of developement in terms of xpo .
We can take aod also but the size will be more and needs to stop AOS everytime .
Following job illustrates how to export/backup all AOT Classes object.
void DEV_ExportTreeNodeExample()
{
TreeNode treeNode;
FileIoPermission perm;
#define.ExportFile(@"c:\AOTclasses.xpo")
#define.ExportMode("w")
;
perm = new FileIoPermission(#ExportFile, #ExportMode);
if (perm == null)
{
return;
}
perm.assert();
treeNode = TreeNode::findNode(@"\Classes");
if (treeNode != null)
{
// BP deviation documented.
treeNode.treeNodeExport(#ExportFile);
}
CodeAccessPermission::revertAssert();
}
Enjoy !!!!!!!!
Build your own context menu on Form field.
Context menu on field are nothing but the list of features shown when you do right click on field like filter by field,by selection ,setup etc.
Following code illustrates how you can override the standard context menu and build your own .
Best use of this is in-case of display methods where filter by field and selection are not available.
Override the standard context method available on Form fields.
public void context()
{
PopupMenu menu ;
Str filterValue;
int selectedItem;
int filter, removeFilter;
;
//super(); // Comment standard menu
menu = new PopupMenu(element.hWnd());
filter = menu.insertItem("Filter");
removeFilter= menu.insertItem("Remove Filter");
selectedItem = menu.draw();
filterValue = element.design().controlName("Table1_Usn").valueStr();
switch (selectedItem)
{
case filter :
Table1_ds.filter(fieldnum(table1,Usn),filterValue);
break;
case removeFilter :
Table1_ds.removeFilter();
break;
}
}
For illustration i have used my own objects , So please test with your own valid objects.
Following code illustrates how you can override the standard context menu and build your own .
Best use of this is in-case of display methods where filter by field and selection are not available.
Override the standard context method available on Form fields.
public void context()
{
PopupMenu menu ;
Str filterValue;
int selectedItem;
int filter, removeFilter;
;
//super(); // Comment standard menu
menu = new PopupMenu(element.hWnd());
filter = menu.insertItem("Filter");
removeFilter= menu.insertItem("Remove Filter");
selectedItem = menu.draw();
filterValue = element.design().controlName("Table1_Usn").valueStr();
switch (selectedItem)
{
case filter :
Table1_ds.filter(fieldnum(table1,Usn),filterValue);
break;
case removeFilter :
Table1_ds.removeFilter();
break;
}
}
For illustration i have used my own objects , So please test with your own valid objects.
Remove HTML Tags from String
Following Job illustrates how to strip htmltags from string.
static void Dev_StripHTML(Args _args)
{
Str htmlSource;
;
htmlSource = "Test " +
"HTML Tags Removed ";
info(Web::stripHTML(htmlSource));
}
static void Dev_StripHTML(Args _args)
{
Str htmlSource;
;
htmlSource = "
"HTML Tags Removed ";
info(Web::stripHTML(htmlSource));
}
X++ Code to Create Purchase Order and Post the Invoice.
Following Job creates the Purchase order from code and post the invoice by making use of PurchFormLetter class.
If you don't have demo data , please test with your input values.
static void Dev_CreatePO_and_Invoice(Args _args)
{
NumberSeq numberSeq;
Purchtable Purchtable;
PurchLine PurchLine;
PurchFormLetter purchFormLetter;
;
ttsbegin;
numberSeq = NumberSeq::newGetNumFromCode(purchParameters::numRefPurchaseOrderId().NumberSequence,true);
// Initialize Purchase order values
Purchtable.initValue();
Purchtable.PurchId = numberSeq.num();
Purchtable.OrderAccount = '3000';
Purchtable.initFromVendTable();
if (!Purchtable.validateWrite())
{
throw Exception::Error;
}
Purchtable.insert();
// Initialize Purchase Line items
PurchLine.PurchId = Purchtable.PurchId;
PurchLine.ItemId = 'B-R14';
PurchLine.createLine(true, true, true, true, true, false);
ttscommit;
purchFormLetter = purchFormLetter::construct(DocumentStatus::Invoice);
purchFormLetter.update(purchtable, // Purchase record Buffer
"Inv_"+purchTable.PurchId, // Invoice Number
systemdateget()); // Transaction date
if (PurchTable::find(purchTable.PurchId).DocumentStatus == DocumentStatus::Invoice)
{
info(strfmt("Posted invoiced journal for purchase order %1",purchTable.PurchId));
}
}
Change the documentstatus to packingSlip , if you want to post packing slip.
Enjoy ....Invoicing through Code.
If you don't have demo data , please test with your input values.
static void Dev_CreatePO_and_Invoice(Args _args)
{
NumberSeq numberSeq;
Purchtable Purchtable;
PurchLine PurchLine;
PurchFormLetter purchFormLetter;
;
ttsbegin;
numberSeq = NumberSeq::newGetNumFromCode(purchParameters::numRefPurchaseOrderId().NumberSequence,true);
// Initialize Purchase order values
Purchtable.initValue();
Purchtable.PurchId = numberSeq.num();
Purchtable.OrderAccount = '3000';
Purchtable.initFromVendTable();
if (!Purchtable.validateWrite())
{
throw Exception::Error;
}
Purchtable.insert();
// Initialize Purchase Line items
PurchLine.PurchId = Purchtable.PurchId;
PurchLine.ItemId = 'B-R14';
PurchLine.createLine(true, true, true, true, true, false);
ttscommit;
purchFormLetter = purchFormLetter::construct(DocumentStatus::Invoice);
purchFormLetter.update(purchtable, // Purchase record Buffer
"Inv_"+purchTable.PurchId, // Invoice Number
systemdateget()); // Transaction date
if (PurchTable::find(purchTable.PurchId).DocumentStatus == DocumentStatus::Invoice)
{
info(strfmt("Posted invoiced journal for purchase order %1",purchTable.PurchId));
}
}
Change the documentstatus to packingSlip , if you want to post packing slip.
Enjoy ....Invoicing through Code.
TextBuffer Class to replace the special characters in string.
TextBuffer class can be used to replace the text/characters in the string.
Usually its used to replace the special character with escape sequence so that string(data with special character) can be inserted into the database.
Following job illustrates how to achieve this.
static void Dev_ReplaceTxt(Args _args)
{
TextBuffer buffer = new TextBuffer();
Str message;
;
message = " hi hello's how r u's ";
message += " How r u doing's wujer's * ? ' what ur mot's anbej's";
buffer.setText(message);
buffer.replace("[*?']","\\'"); // replace special character with escape sequence
info(buffer.getText());
}
Usually its used to replace the special character with escape sequence so that string(data with special character) can be inserted into the database.
Following job illustrates how to achieve this.
static void Dev_ReplaceTxt(Args _args)
{
TextBuffer buffer = new TextBuffer();
Str message;
;
message = " hi hello's how r u's ";
message += " How r u doing's wujer's * ? ' what ur mot's anbej's";
buffer.setText(message);
buffer.replace("[*?']","\\'"); // replace special character with escape sequence
info(buffer.getText());
}
Code to Display Expiry date Message at Status Bar
Following job illustrates how you can display custom message in the status bar next to alerts. ( Mean , At bottom where you will find company,layer,currency info).
static void Dev_setStatusLineText(Args _args)
{
str msg;
;
msg = "Expiry date :"+date2str(xinfo::expireDate(),123,2,4,2,4,2);
// Force the text to be shown
xUserInfo::statusLine_CustomText(true);
infolog.writeCustomStatlineItem(msg);
}
static void Dev_setStatusLineText(Args _args)
{
str msg;
;
msg = "Expiry date :"+date2str(xinfo::expireDate(),123,2,4,2,4,2);
// Force the text to be shown
xUserInfo::statusLine_CustomText(true);
infolog.writeCustomStatlineItem(msg);
}
X++ code to write data to Event viewer
Usually it will be difficult to monitor/debug Batch Jobs running on server . For e.g if something goes wrong than we can't find out why.
So we can make use of Eventviewer instead of infolog and monitor the status by checking the eventviewr .
Following Job illustrates how to write to event viewer.
static void Dev_Write2EventViewer(Args _args)
{
System.Diagnostics.EventLog eventlog;
Str logSource;
Str logName;
;
logSource = "Dynamics Ax";
logName = "Dynamics Ax Log";
// check if the log already exists
if(!System.Diagnostics.EventLog::SourceExists(logSource))
{
// create new log
System.Diagnostics.EventLog::CreateEventSource(logSource, logName);
}
eventlog = new System.Diagnostics.EventLog();
eventlog.set_Source(logSource);
eventlog.WriteEntry("Info Message");
eventlog.WriteEntry("Warning Message" , System.Diagnostics.EventLogEntryType::Warning);
eventlog.WriteEntry("Error! Please check the stack trace below. \n\n" +
con2str(xSession::xppCallStack()), System.Diagnostics.EventLogEntryType::Error);
info("Check the event viewer!");
}
Enjoy ....!!!! Batch Jobs...
So we can make use of Eventviewer instead of infolog and monitor the status by checking the eventviewr .
Following Job illustrates how to write to event viewer.
static void Dev_Write2EventViewer(Args _args)
{
System.Diagnostics.EventLog eventlog;
Str logSource;
Str logName;
;
logSource = "Dynamics Ax";
logName = "Dynamics Ax Log";
// check if the log already exists
if(!System.Diagnostics.EventLog::SourceExists(logSource))
{
// create new log
System.Diagnostics.EventLog::CreateEventSource(logSource, logName);
}
eventlog = new System.Diagnostics.EventLog();
eventlog.set_Source(logSource);
eventlog.WriteEntry("Info Message");
eventlog.WriteEntry("Warning Message" , System.Diagnostics.EventLogEntryType::Warning);
eventlog.WriteEntry("Error! Please check the stack trace below. \n\n" +
con2str(xSession::xppCallStack()), System.Diagnostics.EventLogEntryType::Error);
info("Check the event viewer!");
}
Enjoy ....!!!! Batch Jobs...
How to Debugg Form Controls in Ax
As you all know X++ debugger won't work , when we place breakpoints in form controls. So we all end-up in writing the breakpoint statement in form control method.
So have a look
Debug Form Controls in Ax
Enjoy Debugging !!!!
So have a look
Debug Form Controls in Ax
Enjoy Debugging !!!!
DAX Form Tool - Get Form Details at One Place
As you all know in ax whenever we want to know form detail, always we used to do..
1. Right click on form and then click on Setup and then we get the AOT->Form path.
2. To check the datasource ,like which table its using and joined to which datasource.
I have developed tool called as Dev_FormTool which will help you to get all form details in one place.
Download
Have a look ....
1. Right click on form and then click on Setup and then we get the AOT->Form path.
2. To check the datasource ,like which table its using and joined to which datasource.
I have developed tool called as Dev_FormTool which will help you to get all form details in one place.
Download
Have a look ....
Make Form/Report run automatically when dynamics Ax Starts
When ax starts Kernel Creates an instance of Info class .
Info contains StartupPost() method used to execute the code every time ax starts.
Following example opens InventTable Form automatically when you start ax.
void startupPost()
{
SysSetupFormRun formRun;
Args args = new Args();
;
args.name(formstr(InventTable));
formRun = classfactory::formRunClassOnClient(args);
formRun.init();
formRun.run();
formRun.detach();
}
So if you have any task that has to be executed every time ax start , then this is the best place to put your code.
Info contains StartupPost() method used to execute the code every time ax starts.
Following example opens InventTable Form automatically when you start ax.
void startupPost()
{
SysSetupFormRun formRun;
Args args = new Args();
;
args.name(formstr(InventTable));
formRun = classfactory::formRunClassOnClient(args);
formRun.init();
formRun.run();
formRun.detach();
}
So if you have any task that has to be executed every time ax start , then this is the best place to put your code.
Import Data on Startup without User Interaction.
Particularly during the test phase , its useful to import the data without user interaction.
You can do this from command line with xml file as parameter.
Steps 1 : Create an XML File as below, which specifies
a. axapta version
b. Name and Location of LogFile
c. Path of data file to be imported.

Steps 2 : Open a commandLine window and type
ax32.exe –StartupCmd=AutoRun_c:\folder\filename.XML
SysAutRun Class conatins macro for XMl Elements.
For e.g can be used for importing XPO's at startup.
You can do this from command line with xml file as parameter.
Steps 1 : Create an XML File as below, which specifies
a. axapta version
b. Name and Location of LogFile
c. Path of data file to be imported.
Steps 2 : Open a commandLine window and type
ax32.exe –StartupCmd=AutoRun_c:\folder\filename.XML
SysAutRun Class conatins macro for XMl Elements.
For e.g
AX Application Explorer in Visual Studio
Check out the new tool developed by the Developer and partner tool group of dynamics Ax for Ax style coding in visual studio.
AX Application Explorer in Visual studio

Enjoy!!!
AX Application Explorer in Visual studio
Enjoy!!!
Pre-Requisites to install Dynamics Ax 2009 and Enterprise Portal
Dynamics Ax 2009 Installation Pre-requisites.
In all cases, you must be a member of the Administrators group on the local computer where you are installing a component. The following table lists permissions that are required in addition to administrator access on the computer.

Components Required.
In all cases, you must be a member of the Administrators group on the local computer where you are installing a component. The following table lists permissions that are required in addition to administrator access on the computer.
Components Required.
Connecting MSSQL DataBase with X++ Code.
As part of Customer requirements , sometimes we need to connect to DataBase otherthan Axapta DataBase. So let us explore how it can be done with X++ code.
In Ax using ODBCConnection and LoginProp its possible to connect to Database on remote server, as illustrated in the following example.I tried the following code on MSSQL Server.
static void test_ODBCConnection(Args _args)
{
LoginProperty loginProp;
ODBCConnection conn;
Resultset resultSet, resultSetCount; // get record
Statement statement1, statement2; // Create SQL Statement
ResultSetMetaData metaData ; // get Record metadate like columnname.
;
// Set Server Database
loginProp = new LoginProperty();
loginProp.setServer('SON15092');
loginProp.setDatabase('AdventureWorksDW');
// Create Connection and SQL Statement
conn = new ODBCConnection(loginProp);
statement1 = conn.createStatement();
resultSet = statement1.executeQuery("SELECT * from DimTime");
while (resultSet.next())
{
metaData = resultSet.getMetaData();
info("Column Name :"+metaData.getColumnName(1)+" Value ="+resultSet.getString(1));
}
}
Enjoy, X++!!!!!!
In Ax using ODBCConnection and LoginProp its possible to connect to Database on remote server, as illustrated in the following example.I tried the following code on MSSQL Server.
static void test_ODBCConnection(Args _args)
{
LoginProperty loginProp;
ODBCConnection conn;
Resultset resultSet, resultSetCount; // get record
Statement statement1, statement2; // Create SQL Statement
ResultSetMetaData metaData ; // get Record metadate like columnname.
;
// Set Server Database
loginProp = new LoginProperty();
loginProp.setServer('SON15092');
loginProp.setDatabase('AdventureWorksDW');
// Create Connection and SQL Statement
conn = new ODBCConnection(loginProp);
statement1 = conn.createStatement();
resultSet = statement1.executeQuery("SELECT * from DimTime");
while (resultSet.next())
{
metaData = resultSet.getMetaData();
info("Column Name :"+metaData.getColumnName(1)+" Value ="+resultSet.getString(1));
}
}
Enjoy, X++!!!!!!
File Operation using WinAPI in Dynamics Ax
Finding files with WinAPI
Axapta’s WinAPI class has a bunch of static methods to handle files. The code example below shows how to utilize some of these methods to find files.
The two methods used to fetch all files matching the search criteria are findFirstFile() and findNextFile(). Don’t forget to clean up after yourself with findClose().
The code also uses three different find methods:
*
fileExists(_name) returns true, if _name is an existing file
*
folderExists(_name) returns true, if _name is an existing file or folder
*
pathExists(_name) returns true, if _name is an existing folder
The example uses the infolog for output. As with any infolog beware of performance and limitation to 10.000 lines.
static void Test_WINAPI(Args _args)
{
#File
FileName fullFileName(FileName _path, FileName _fileName)
{
FileName pathName;
FileName fileName;
FileName fileExtension;
;
[pathName,fileName,fileExtension] = fileNameSplit(_fileName);
return _path + '\\' + fileName + fileExtension;
}
void findFiles(FileName _path,
FileName _fileName,
boolean _inclSubDir = true,
FileName _prefix = fullFileName(_path,_fileName))
{
FileName fileName;
int hdl;
;
setprefix(_prefix);
if (WinAPI::folderExists(_path))
{
[hdl,fileName] = WinApi::findFirstFile(fullFileName(_path,_fileName));
while (fileName)
{
if (WinAPI::fileExists(fullFileName(_path,fileName)))
info(fileName);
fileName = WinApi::findNextFile(hdl);
}
WinApi::findClose(hdl);
if (_inclSubDir)
{
[hdl, fileName] = WinAPI::findFirstFile(_path+'\\'+#AllFiles);
while (fileName)
{
if (strlwr(fileName) != strlwr(_fileName) &&
strlwr(fileName) != strlwr('.') &&
strlwr(fileName) != strlwr('..') &&
WinAPI::pathExists(fullFileName(_path,fileName)))
findFiles(fullFileName(_path,fileName), _fileName, _inclSubDir,
fileName);
fileName = WinApi::findNextFile(hdl);
}
WinApi::findClose(hdl);
}
}
}
findFiles('c:\\Program Files','*.doc');
}
Axapta’s WinAPI class has a bunch of static methods to handle files. The code example below shows how to utilize some of these methods to find files.
The two methods used to fetch all files matching the search criteria are findFirstFile() and findNextFile(). Don’t forget to clean up after yourself with findClose().
The code also uses three different find methods:
*
fileExists(_name) returns true, if _name is an existing file
*
folderExists(_name) returns true, if _name is an existing file or folder
*
pathExists(_name) returns true, if _name is an existing folder
The example uses the infolog for output. As with any infolog beware of performance and limitation to 10.000 lines.
static void Test_WINAPI(Args _args)
{
#File
FileName fullFileName(FileName _path, FileName _fileName)
{
FileName pathName;
FileName fileName;
FileName fileExtension;
;
[pathName,fileName,fileExtension] = fileNameSplit(_fileName);
return _path + '\\' + fileName + fileExtension;
}
void findFiles(FileName _path,
FileName _fileName,
boolean _inclSubDir = true,
FileName _prefix = fullFileName(_path,_fileName))
{
FileName fileName;
int hdl;
;
setprefix(_prefix);
if (WinAPI::folderExists(_path))
{
[hdl,fileName] = WinApi::findFirstFile(fullFileName(_path,_fileName));
while (fileName)
{
if (WinAPI::fileExists(fullFileName(_path,fileName)))
info(fileName);
fileName = WinApi::findNextFile(hdl);
}
WinApi::findClose(hdl);
if (_inclSubDir)
{
[hdl, fileName] = WinAPI::findFirstFile(_path+'\\'+#AllFiles);
while (fileName)
{
if (strlwr(fileName) != strlwr(_fileName) &&
strlwr(fileName) != strlwr('.') &&
strlwr(fileName) != strlwr('..') &&
WinAPI::pathExists(fullFileName(_path,fileName)))
findFiles(fullFileName(_path,fileName), _fileName, _inclSubDir,
fileName);
fileName = WinApi::findNextFile(hdl);
}
WinApi::findClose(hdl);
}
}
}
findFiles('c:\\Program Files','*.doc');
}
How to add "Go to main table Form" Link in Ax
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.
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.
1. Open the smmBusRelTable form in AOT, and override jumpRef() of the EmployeeFilter control with:
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);
}
2. To test the result, open CRM | Business Relation Details, make sure an employee number is specified in the Employee filter, and right-click on the filter control. Notice that the Go to the Main Table Form option, which will open the Employee form, is now available:
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.
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.
1. Open the smmBusRelTable form in AOT, and override jumpRef() of the EmployeeFilter control with:
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);
}
2. To test the result, open CRM | Business Relation Details, make sure an employee number is specified in the Employee filter, and right-click on the filter control. Notice that the Go to the Main Table Form option, which will open the Employee form, is now available:
Run AX as a different user from Windows Explorer
Thanks to Kashperuk.Refer the below site
http://kashperuk.blogspot.com/2010/01/run-ax-as-different-user-from-windows.html
http://kashperuk.blogspot.com/2010/01/run-ax-as-different-user-from-windows.html
How to use Dialog in Dynamics Ax.
If you came accross a requirement , where you need to collect the information and then executes a task . In such scenario Please make use of RunBase class.
Advantage of RunBase class
1. It provides dialog for presenting and collecting the information from user.
2. Pack/Unpack methods , for storing the last value entered by the user.
3. Progress bar ,for long time process where in you can show a progress bar.
4. Validate method , to validate the data entered by the user in the dialog.
you can refer tutorial in AOT->Classes->tutorial_RunBaseForm .
Advantage of RunBase class
1. It provides dialog for presenting and collecting the information from user.
2. Pack/Unpack methods , for storing the last value entered by the user.
3. Progress bar ,for long time process where in you can show a progress bar.
4. Validate method , to validate the data entered by the user in the dialog.
you can refer tutorial in AOT->Classes->tutorial_RunBaseForm .
How to Use Temporary Table in Form
Temporary table(Table whose property temporary set to "yes") is not persistent media , so at run-time you have to populate the temporary table and attach the same to the form/report.
For illustration purpose ,I am using inventTable data and attaching the items whose group is Parts.
Steps
1. Create temporary table as TmpTestTable with 3 fields(ItemId,Itemname,ItemGroup).
2. Create a form as TempTestTable and attach the table as datasource
3. Create a new method on the form and copy the following code
TmpTestTable populateRecords(ItemGroupId _itemGroupId)
{
TmpTestTable tmpTable;
InventTable inventTable;
;
while select inventTable
where inventTable.ItemGroupId == _itemGroupId
{
tmpTable.Itemid = inventTable.ItemId;
tmpTable.ItemName = inventTable.ItemName;
tmpTable.Itemgroup = inventTable.ItemGroupId;
tmpTable.insert();
}
return tmpTable;
}
4. Call the above method in init() after super as below
public void init()
{
super();
// Call the setTmpData method to attach records to datasource
TmpTestTable.setTmpData(element.populateRecords("Parts"));
}
For illustration purpose ,I am using inventTable data and attaching the items whose group is Parts.
Steps
1. Create temporary table as TmpTestTable with 3 fields(ItemId,Itemname,ItemGroup).
2. Create a form as TempTestTable and attach the table as datasource
3. Create a new method on the form and copy the following code
TmpTestTable populateRecords(ItemGroupId _itemGroupId)
{
TmpTestTable tmpTable;
InventTable inventTable;
;
while select inventTable
where inventTable.ItemGroupId == _itemGroupId
{
tmpTable.Itemid = inventTable.ItemId;
tmpTable.ItemName = inventTable.ItemName;
tmpTable.Itemgroup = inventTable.ItemGroupId;
tmpTable.insert();
}
return tmpTable;
}
4. Call the above method in init() after super as below
public void init()
{
super();
// Call the setTmpData method to attach records to datasource
TmpTestTable.setTmpData(element.populateRecords("Parts"));
}
X++ code to identify multiple selected records in Grid
Steps :
1. Create a new method on form and copy the following code.
2. Next create a button on form . and call the method on the button click event.
(for the button property makesure the Multiselect should be set to yes , or else by default for multiple selection system will disable the buttons)
void checkSelectedRecords()
{InventTable inventLocal;
;
//getFirst method gets all the selected records in the grid
inventLocal = InventTable_ds.getFirst(true);
while (inventLocal)
{
info(strfmt("You selected Item %1",inventLocal.ItemId));
// get the next selected record
inventLocal = InventTable_ds.getNext();
}
}
As shown below ..
1. Create a new method on form and copy the following code.
2. Next create a button on form . and call the method on the button click event.
(for the button property makesure the Multiselect should be set to yes , or else by default for multiple selection system will disable the buttons)
void checkSelectedRecords()
{InventTable inventLocal;
;
//getFirst method gets all the selected records in the grid
inventLocal = InventTable_ds.getFirst(true);
while (inventLocal)
{
info(strfmt("You selected Item %1",inventLocal.ItemId));
// get the next selected record
inventLocal = InventTable_ds.getNext();
}
}
As shown below ..
X++ code to hide content pane in ax2009
This is for developers , not for endusers.As you know it will irritate during developemnt since it is appearing on top of all windows when we open form.
static void hideContentPaneWindow(Args _args)
{
#WinApi
HWND contentPane = WinApi::findWindowEx(WinAPI::findWindowEx(infolog.hWnd(), 0, 'MDIClient', ''),0,'ContentFrame','' );
;
if (contentPane)
WinApi::ShowWindow(contentPane,#SW_HIDE); // To restore use #SW_RESTORE
}
static void hideContentPaneWindow(Args _args)
{
#WinApi
HWND contentPane = WinApi::findWindowEx(WinAPI::findWindowEx(infolog.hWnd(), 0, 'MDIClient', ''),0,'ContentFrame','' );
;
if (contentPane)
WinApi::ShowWindow(contentPane,#SW_HIDE); // To restore use #SW_RESTORE
}
Get AX4.0 look and feel back in Ax2009
You all might be aware that all forms in Ax2009 are behaving like independent application forms.i,e When you open any form it will get open outside the application.
So following are the two solutions to open forms in same workspace as in Ax4.0
1. Change the formdesign property windowtype(from standard to workspace).this you have to do for all the forms which is time cosuming.
2. Or else Add following line in the init method of SysSetupFormRun class.
before super() as shown in the following code
public void init()
{
//at run-time you are setting property to workspace.
this.form().design().windowType(FormWindowType::Workspace);
super();
SysSecurityFormSetup::loadSecurity(this);
this.dimensionFieldCtrls();
this.inventStorageDimFieldCtrls();
if (this.isWorkflowEnabled())
{
workflowControls = SysWorkflowFormControls::construct(this);
workflowControls.initControls();
}
}
So following are the two solutions to open forms in same workspace as in Ax4.0
1. Change the formdesign property windowtype(from standard to workspace).this you have to do for all the forms which is time cosuming.
2. Or else Add following line in the init method of SysSetupFormRun class.
before super() as shown in the following code
public void init()
{
//at run-time you are setting property to workspace.
this.form().design().windowType(FormWindowType::Workspace);
super();
SysSecurityFormSetup::loadSecurity(this);
this.dimensionFieldCtrls();
this.inventStorageDimFieldCtrls();
if (this.isWorkflowEnabled())
{
workflowControls = SysWorkflowFormControls::construct(this);
workflowControls.initControls();
}
}
Security Keys in Ax
Dynamics Ax provides following keys to provide the security for application
1.License Keys :
First Level Security , specifies what all featues/modules/developemnt tools we can access in the standard product.License Keys will be used after installation to unlock the product.License Keys will be issued by the microsoft for partners/vendors to develop vertical/generic solutions.
2.Configuration Keys :
Second Level Security , To Enable/Disable the object/feature for all the users.
3.Security Keys :
If you want to enable/disable object/features to group of users.
4. Record Level security :
Basically it deals with the data , if you want to restrict the viewing of records per user group than we setup record level security.
1.License Keys :
First Level Security , specifies what all featues/modules/developemnt tools we can access in the standard product.License Keys will be used after installation to unlock the product.License Keys will be issued by the microsoft for partners/vendors to develop vertical/generic solutions.
2.Configuration Keys :
Second Level Security , To Enable/Disable the object/feature for all the users.
3.Security Keys :
If you want to enable/disable object/features to group of users.
4. Record Level security :
Basically it deals with the data , if you want to restrict the viewing of records per user group than we setup record level security.
Dynamics Ax Systemclass for RECID
As you all know that Dynamics Ax uses recid(Record Id)to uniquelly identify a record in the database.
Advantage of RecId
1.By default system uses Recid as primary index,so if query optimizer didn't find any index to use then it will use RecId.
Dyamics Ax uses SystemSequence class(AOT->SystemDocumentation->Classes) to generate recId for table.
Using SystemSequence class you can reserve RECID's for your table.
Ax Kernel uses SystemSequences Table to generate the Recid for table as shown below
Goto AOT->SystemDocumentation->Tables->SystemSequences ,
Advantage of RecId
1.By default system uses Recid as primary index,so if query optimizer didn't find any index to use then it will use RecId.
Dyamics Ax uses SystemSequence class(AOT->SystemDocumentation->Classes) to generate recId for table.
Using SystemSequence class you can reserve RECID's for your table.
Ax Kernel uses SystemSequences Table to generate the Recid for table as shown below
Goto AOT->SystemDocumentation->Tables->SystemSequences ,
X++ code to write data to XML File
static void CreateXml(Args _args)
{
XmlDocument xmlDoc; //to create blank XMLDocument
XmlElement xmlRoot; // XML root node
XmlElement xmlField;
XmlElement xmlRecord;
XMLWriter xmlWriter;
InventTable inventTable;
DictTable dTable = new DictTable(tablenum(InventTable));
DictField dField;
int i, fieldId;
str value;
#InventTags
;
xmlDoc = XmlDocument::newBlank();
xmlRoot = xmlDoc.createElement(#ItemRootNode);
// Loop through all the records in the inventTable
while select inventTable
where inventTable.ItemGroupId == "Parts"
{
// Create a XmlElement (record) to hold the
// contents of the current record.
xmlRecord = xmlDoc.createElement(#ItemRecords);
// Loop through all the fields in the record
for (i=1; i<=dTable.fieldCnt(); i++)
{
fieldId = dTable.fieldCnt2Id(i);
// Find the DictField object that matches
// the fieldId
dField = dTable.fieldObject(fieldId);
// Skip system fields
if (dField.isSystem())
continue;
// Create a new XmlElement (field) and
// have the name equal to the name of the
// dictField
xmlField = xmlDoc.createElement(dField.name());
// Convert values to string. I have just added
// a couple of conversion as an example.
// Use tableName.(fieldId) instead of fieldname
// to get the content of the field.
switch (dField.baseType())
{
case Types::Int64 :
value = int642str(inventTable.(fieldId));
break;
case Types::Integer :
value = int2str(inventTable.(fieldId));
break;
default :
value = inventTable.(fieldId);
break;
}
// Set the innerText of the XmlElement (field)
// to the value from the table
xmlField.innerText(value);
// Append the field as a child node to the record
xmlRecord.appendChild(xmlField);
}
// Add the record as a child node to the root
xmlRoot.appendChild(xmlRecord);
}
// Add the root to the XmlDocument
xmlDoc.appendChild(xmlRoot);
// Create a new object of the XmlWriter class
// in order to be able to write the xml to a file
xmlWriter = XMLWriter::newFile(@"c:\Items.xml");
// Write the content of the XmlDocument to the
// file as specified by the XmlWriter
xmlDoc.writeTo(xmlWriter);
//Open the file in Internet Explorer
WINAPI::shellExecute("c:\Items.xml");
}
{
XmlDocument xmlDoc; //to create blank XMLDocument
XmlElement xmlRoot; // XML root node
XmlElement xmlField;
XmlElement xmlRecord;
XMLWriter xmlWriter;
InventTable inventTable;
DictTable dTable = new DictTable(tablenum(InventTable));
DictField dField;
int i, fieldId;
str value;
#InventTags
;
xmlDoc = XmlDocument::newBlank();
xmlRoot = xmlDoc.createElement(#ItemRootNode);
// Loop through all the records in the inventTable
while select inventTable
where inventTable.ItemGroupId == "Parts"
{
// Create a XmlElement (record) to hold the
// contents of the current record.
xmlRecord = xmlDoc.createElement(#ItemRecords);
// Loop through all the fields in the record
for (i=1; i<=dTable.fieldCnt(); i++)
{
fieldId = dTable.fieldCnt2Id(i);
// Find the DictField object that matches
// the fieldId
dField = dTable.fieldObject(fieldId);
// Skip system fields
if (dField.isSystem())
continue;
// Create a new XmlElement (field) and
// have the name equal to the name of the
// dictField
xmlField = xmlDoc.createElement(dField.name());
// Convert values to string. I have just added
// a couple of conversion as an example.
// Use tableName.(fieldId) instead of fieldname
// to get the content of the field.
switch (dField.baseType())
{
case Types::Int64 :
value = int642str(inventTable.(fieldId));
break;
case Types::Integer :
value = int2str(inventTable.(fieldId));
break;
default :
value = inventTable.(fieldId);
break;
}
// Set the innerText of the XmlElement (field)
// to the value from the table
xmlField.innerText(value);
// Append the field as a child node to the record
xmlRecord.appendChild(xmlField);
}
// Add the record as a child node to the root
xmlRoot.appendChild(xmlRecord);
}
// Add the root to the XmlDocument
xmlDoc.appendChild(xmlRoot);
// Create a new object of the XmlWriter class
// in order to be able to write the xml to a file
xmlWriter = XMLWriter::newFile(@"c:\Items.xml");
// Write the content of the XmlDocument to the
// file as specified by the XmlWriter
xmlDoc.writeTo(xmlWriter);
//Open the file in Internet Explorer
WINAPI::shellExecute("c:\Items.xml");
}
X++ code to Read/Write Dynamics Ax data to excel
Writing Data to Excel file
How it works
1. Use SysExcelApplication class to create excel file.
2. Use SysExcelWorkbooks and SysExcelWorkbook to create a blank workbook(by
default 3 worksheets will be available).
3. Use SysExcelWorkSheets to select worksheet for writing data.
4. SysExcelCells to select the cells in the excel for writing the data.
5. SysExcelCell to write the data in the selected cells.
6. Once you done with write operation use SysExcelApplication.visible to open
file.
static void Write2ExcelFile(Args _args)
{
InventTable inventTable;
SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
SysExcelCell cell;
int row;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
workbook = workbooks.add();
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
cells.range('A:A').numberFormat('@');
cell = cells.item(1,1);
cell.value("Item");
cell = cells.item(1,2);
cell.value("Name");
row = 1;
while select inventTable
{
row++;
cell = cells.item(row, 1);
cell.value(inventTable.ItemId);
cell = cells.item(row, 2);
cell.value(inventTable.ItemName);
}
application.visible(true);
}
Reading Data from Excel File
static void ReadExcel(Args _args)
{
SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
COMVariantType type;
int row;
ItemId itemid;
Name name;
FileName filename;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
//specify the file path that you want to read
filename = "C:\\item.xls";
try
{
workbooks.open(filename);
}
catch (Exception::Error)
{
throw error("File cannot be opened.");
}
workbook = workbooks.item(1);
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
do
{
row++;
itemId = cells.item(row, 1).value().bStr();
name = cells.item(row, 2).value().bStr();
info(strfmt('%1 - %2', itemId, name));
type = cells.item(row+1, 1).value().variantType();
}
while (type != COMVariantType::VT_EMPTY);
application.quit();
}
How it works
1. Use SysExcelApplication class to create excel file.
2. Use SysExcelWorkbooks and SysExcelWorkbook to create a blank workbook(by
default 3 worksheets will be available).
3. Use SysExcelWorkSheets to select worksheet for writing data.
4. SysExcelCells to select the cells in the excel for writing the data.
5. SysExcelCell to write the data in the selected cells.
6. Once you done with write operation use SysExcelApplication.visible to open
file.
static void Write2ExcelFile(Args _args)
{
InventTable inventTable;
SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
SysExcelCell cell;
int row;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
workbook = workbooks.add();
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
cells.range('A:A').numberFormat('@');
cell = cells.item(1,1);
cell.value("Item");
cell = cells.item(1,2);
cell.value("Name");
row = 1;
while select inventTable
{
row++;
cell = cells.item(row, 1);
cell.value(inventTable.ItemId);
cell = cells.item(row, 2);
cell.value(inventTable.ItemName);
}
application.visible(true);
}
Reading Data from Excel File
static void ReadExcel(Args _args)
{
SysExcelApplication application;
SysExcelWorkbooks workbooks;
SysExcelWorkbook workbook;
SysExcelWorksheets worksheets;
SysExcelWorksheet worksheet;
SysExcelCells cells;
COMVariantType type;
int row;
ItemId itemid;
Name name;
FileName filename;
;
application = SysExcelApplication::construct();
workbooks = application.workbooks();
//specify the file path that you want to read
filename = "C:\\item.xls";
try
{
workbooks.open(filename);
}
catch (Exception::Error)
{
throw error("File cannot be opened.");
}
workbook = workbooks.item(1);
worksheets = workbook.worksheets();
worksheet = worksheets.itemFromNum(1);
cells = worksheet.cells();
do
{
row++;
itemId = cells.item(row, 1).value().bStr();
name = cells.item(row, 2).value().bStr();
info(strfmt('%1 - %2', itemId, name));
type = cells.item(row+1, 1).value().variantType();
}
while (type != COMVariantType::VT_EMPTY);
application.quit();
}
X++ code to create a customized lookup on form
Override the lookup method on Formdatasource field(on which you want to show lookup) , and copy the following code to your method.
Comment the super() method in the lookup.
public void lookup(FormControl _formControl, str _filterStr)
{
SysTableLookup sysTableLookup; // systemclass to create //customlookup
Query query;
QueryBuildDataSource qbd;
;
sysTableLookup = SysTableLookup::newParameters(
tablenum(InventTable),
_formcontrol);
// Construct query on the table,
// whose records you want to show as lookup.
query = new Query();
qbd = query.addDataSource(tablenum(InventTable));
qbd.addRange(fieldnum(InventTable,ItemType)).value(SysQuery::value(enum2str
(ItemType::Item)));
// add the fields to the lookup list
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemId));
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemName));
// pass the query as parameter
// system will show the records in the lookup
// as per your query
sysTableLookup.parmQuery(query);
sysTableLookup.performFormLookup();
}
Comment the super() method in the lookup.
public void lookup(FormControl _formControl, str _filterStr)
{
SysTableLookup sysTableLookup; // systemclass to create //customlookup
Query query;
QueryBuildDataSource qbd;
;
sysTableLookup = SysTableLookup::newParameters(
tablenum(InventTable),
_formcontrol);
// Construct query on the table,
// whose records you want to show as lookup.
query = new Query();
qbd = query.addDataSource(tablenum(InventTable));
qbd.addRange(fieldnum(InventTable,ItemType)).value(SysQuery::value(enum2str
(ItemType::Item)));
// add the fields to the lookup list
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemId));
sysTableLookup.addLookupfield(fieldnum(InventTable,ItemName));
// pass the query as parameter
// system will show the records in the lookup
// as per your query
sysTableLookup.parmQuery(query);
sysTableLookup.performFormLookup();
}
X++ code to find the Stock of Item by Date
static void findOnHand_ByDate(Args _args)
{
InventDim inventDim;
InventDimParm inventDimParm;
Itemid itemid;
InventOnHand inventOnHand = new InventOnHand();
InventSumDateDim inventSumDateDim;
TransDate transDate;
;
// take a sample item for testing
itemid = "20 MM RMC";
transDate = 13\01\2010;
// take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = "GW";
//Set the flag for the selected dimensions as active.
inventDimParm.initFromInventDim(inventDim);
//initialize the inventSumDateDim with Date,item,dimension and dim paramter
inventSumDateDim = InventSumDateDim::newParameters(transDate,
itemid,
inventDim,
inventDimParm);
// Retrieve the onhand info
info(strfmt("PostedQty: %1",inventSumDateDim.postedQty()));
info(strfmt("DeductedQty: %1",inventSumDateDim.deductedQty()));
info(strfmt("ReceivedQty: %1",inventSumDateDim.receivedQty()));
}
{
InventDim inventDim;
InventDimParm inventDimParm;
Itemid itemid;
InventOnHand inventOnHand = new InventOnHand();
InventSumDateDim inventSumDateDim;
TransDate transDate;
;
// take a sample item for testing
itemid = "20 MM RMC";
transDate = 13\01\2010;
// take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = "GW";
//Set the flag for the selected dimensions as active.
inventDimParm.initFromInventDim(inventDim);
//initialize the inventSumDateDim with Date,item,dimension and dim paramter
inventSumDateDim = InventSumDateDim::newParameters(transDate,
itemid,
inventDim,
inventDimParm);
// Retrieve the onhand info
info(strfmt("PostedQty: %1",inventSumDateDim.postedQty()));
info(strfmt("DeductedQty: %1",inventSumDateDim.deductedQty()));
info(strfmt("ReceivedQty: %1",inventSumDateDim.receivedQty()));
}
X++ Code to find OnHand Stock for Item
In Dynamics Ax , use InventOnHand class for finding the stock of an item as shownbelow
static void findOnHand(Args _args)
{
InventDim inventDim;
InventDimParm inventDimParm;
Itemid itemid;
InventOnHand inventOnHand = new InventOnHand();
;
// take a sample item for testing
itemid = "20 MM RMC";
// take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = "GW";
//Set the flag for the selected dimensions as active.
inventDimParm.initFromInventDim(inventDim);
//initialize the inventonhand with item,dimension and dim paramter
inventOnHand.parmItemId(itemid);
inventOnHand.parmInventDim(inventDim);
inventOnHand.parmInventDimParm(inventDimParm);
// Retrieve the onhand info
info(strfmt("Available Physical: %1",
inventOnhand.availPhysical()));
info(strfmt("On order: %1",inventOnhand.onOrder()));
}
static void findOnHand(Args _args)
{
InventDim inventDim;
InventDimParm inventDimParm;
Itemid itemid;
InventOnHand inventOnHand = new InventOnHand();
;
// take a sample item for testing
itemid = "20 MM RMC";
// take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = "GW";
//Set the flag for the selected dimensions as active.
inventDimParm.initFromInventDim(inventDim);
//initialize the inventonhand with item,dimension and dim paramter
inventOnHand.parmItemId(itemid);
inventOnHand.parmInventDim(inventDim);
inventOnHand.parmInventDimParm(inventDimParm);
// Retrieve the onhand info
info(strfmt("Available Physical: %1",
inventOnhand.availPhysical()));
info(strfmt("On order: %1",inventOnhand.onOrder()));
}
Subscribe to:
Posts (Atom)