Sunday, December 21, 2008
Singleton pattern
Mandatory DialogField
Determining if an object is a subclass
Set Class
Set consist of a data set that contains values of the same type, where value is unique.
A Set is alway sorted on the value.
Define
Set s = new Set(Types::STRING);
Insert a value
s.add("Wassini");
s.add("Eric");
Exists value
To see if a value already is added, use the in method:
if (s.in("Wassini"))
print "Yes!";
else
print "No!";
Getting values
There are several ways to get the values in the set.
Using a SetIterator
Using a SetEnumerator
SetIterator
The SetIterator loops throug the complete set:
SetIterator si;
si = new SetIterator(s);
while (si.more())
{
print si.value();
si.next();
}
SetEnumerator
SetEnumerator class is like SetIterator class, but allows the deletion of elements during enumeration and SetIterator does not.
SetEnumerator se=s.getEnumerator();
while (se.moveNext())
{
print se.current();
}
Removing values
Just use the remove method to remove the active value.
s.remove("Wassini");
Other methods
// Get number of elements:
print s.elements();
// Get the used types:
print s.definitionString();
// Dump the whole set as a string:
print s.toString();
Map Class
A Map is alway sorted on the key value.
Contents [hide]
1 How to use
1.1 Define
1.2 Insert a value
1.3 Exists value
1.4 Getting values
1.4.1 MapIterator
1.4.2 MapEnumerator
1.4.3 Direct method
1.5 Removing values
1.6 Updating values
1.7 Other methods
2 See also
How to use
Define
Map m = new Map(Types::STRING, Types::INTEGER); Insert a value
m.insert("Wassini", 37);
m.insert("Eric", 102); Exists value
To see if a value already is added, use the exists method:
if (m.exists("Wassini"))
print "Yes!";
else
print "No!"; Getting values
There are several ways to get the values in the map.
Using a MapIterator
Using a direct method
MapIterator
The MapIterator loops throug the complete map:
MapIterator mi;
mi = new MapIterator(m);
while (mi.more())
{
print mi.key();
print mi.value();
mi.next();
} MapEnumerator
MapEnumerator class is like MapIterator class, but allows the deletion of elements during enumeration and MapIterator does not.
MapEnumerator me = m.getEnumerator();
while (me.moveNext())
{
print me.currentKey();
print me.currentValue();
} Direct method
It is possible to get find a specific value from the key:
print m.lookup("Wassini"); Removing values
Just use the remove method to remove the active key/value pair.
m.remove("Wassini"); Updating values
It is not possible to update a value directly:
int age;
str keyid = "Wassini";
age = m.exists(keyid) ? m.lookup(keyid) : 0;
m.insert(keyid, age + 1); Other methods
// Get number of elements:
print m.elements();
// Get the used types:
print m.definitionString();
// Dump the whole map as a string:
print m.toString();
Image Class
The class can construct and manipulate Image objects of the following file types:
Raster (bitmap) formats - .bmp, .gif, .jpg, .png, .tiff, and .exif
Vector formats - .emf and .wmf
Because of security reasons the class in Dynamics Ax can not be run from the server and is bound to the client.
The image object can be created from scratch, captured from the screen or loaded from a file. It can then be cropped, resized, flipped, rotated etc...
Examples
Load an image from file and place it in a table:
Image image;
str fileName;
;
filename = myImagePath + myImageFile;
if (Image::canLoad(Filename))
{
image.loadImage(Filename);
myTable.imageContainer = image.getData();
}...and to display the image on a form:
Image Image;
container imageContainer;
;
imageContainer = myTable.imageContainer;
Image = new Image();
Image.setData(imageContainer);
imageWindow.image(Image);
imageWindow.widthValue(image.width());
imageWindow.heightValue(image.height());
Array Class
The array is created with a type that defines what contents the array contains. It can not use Types::Anytype!
Contents
1 Example
2 Tips
3 Methods
4 See also
Example
array a = new array(Types::string);
a.value(1, "First");
a.value(2, "Second");
a.value(3, "Third");
print a.lastIndex();
print a.toString();
pause;
Tips
When inserting the array is dynamicly expanded. If you just want to add an element to the end of the array it is possible to use:
a.value(a.lastIndex()+1, "element");
If a container is needed use the pack method:
c = a.pack();
Methods
definitionString
exists
lastIndex
pack
toString
typeId
Validate field values on form
Example:
Change the behavior of the "Customer Group" on "CustTable" form.
Overwrite the method "validate" on the field "CustGroup" on the data source on "CustTable" public boolean validate()
{
boolean ret;
ret = super();
return ret;
}
Change method:
public boolean validate()
{
boolean ret;
;
if(custTable.orig().CustGroup == '30') // Non changeable group.
{
info('You cannot change this Customer group');
ret = false;
} else {
ret = super();
}
return ret;
}
Note: I've explained the general principle for validating a field against it's old value, however there are some issues with the data source not refreshing data set when changing the value back and forth.
How to count records in a Query
QueryRun qr;
QueryBuildDataSource qbd;
;
q = new Query();
qbd = q.addDataSource(TableNum(CustTable));
qr = new QueryRun(q);
info(int2str(SysQuery::countTotal(qr)));
As seen you can use SysQuery object.
Runtime / Performance considerations
The call you are doing here works well if and only if you have exactly one datasource in the query. In the case you are starting to join up stuff here, things get complicated, as the implementation behind this call (specifically SysQuery::countPrim()) does actually loop over the entire resultset. It has to be taken with care therefore.
Retrieved from "http://www.axaptapedia.com/How_to_count_records_in_a_Query"
Create New AOT Project From Template
// and automatically creates a new Project from a Template Project.
//
static void CreateNewAOTProjectFromTemplate(Args _args)
{
ProjectNode templateproject;
ProjectNode pretargetproject;
ProjectNode targetproject;
TreeNode sharedProject;
UtilElements utilElement;
str templateprojectname;
str pretargetprojectname;
str targetprojectname = 'AATest';
str pretargetprojectname()
{
return pretargetprojectname;
}
;
// Get the templateprojectname from someplace where the name of a Template project can be found.
// NOTE: You'll need to change this Line and assign your own Template name.
templateprojectname = DPA_Parameters::find().AOTProjectTemplate;
sharedProject = SysTreeNode::getSharedProject();
if (sharedProject.AOTfindChild(targetprojectname))
return;
// Find the Template Project and load it for inspection
templateproject = sharedProject.AOTfindChild(templateprojectname);
if (!templateproject)
return;
templateproject.loadForInspection();
// Duplicate the Template Project using an intermediary ProjectNode
pretargetproject = templateproject.AOTDuplicate();
pretargetproject.AOTsave();
// Get the name of the duplicated project - CopyOf%1 (%1 = whatever the Template name is)
pretargetprojectname = pretargetproject.AOTname();
// Rename the duplicated Project to the desired project name.
// NOTE: I tried other ways, but this is the only way that worked!
// And, it's fast!
ttsbegin;
select firstonly forupdate utilElement
where utilElement.recordType == UtilElementType::SharedProject
&& utilElement.name == pretargetprojectname();
if (utilElement)
{
utilElement.name = targetprojectname;
utilElement.doUpdate();
}
ttscommit;
// Have to refresh the SharedProject Node - Very important!
sharedProject.AOTrefresh();
// Now just get the new project and open it.
targetproject = sharedProject.AOTfindChild(targetprojectname);
targetproject.AOTrun();
}
Friday, December 19, 2008
working on Bits
importing chart of accounts from CSV file
working on container
Active X create Analog Meter
delete Files In Folder
ExecuteCodeFromFile
FileOperations
getTableProperty
Set Examples
realPrecision
setTableProperty
Test Query
TestDateTimeConversion
Thursday, December 18, 2008
Using global search for virtual tables
Now, if some of your tables that are searched belong to a virtual company, the data crawler will mark them as records of the company it is running in. That means that if you want to search for your virtual data from a different company, it will return no results.
Example: you have the companies xxx and yyy and a virtual company vir. Table CustTable is virtual (dataareaid of the records is vir). The data crawler runs in company xxx and will mark the CustTable records as belonging to company xxx .
Now, if you start a search in company yyy, it will not find the Custtable records as they seem to belong to a different company. You could only set up a second data crwaler for company yyy which would collect exactly the same records and you would need to store them twice in your database.
The following changes will circumvent that: you will be able to see data from different companies. There are some drawbacks, however: you will be able to see search results from your "data crawler company". But it the data is from a non-virtual table, you will not be able to see the results. But I hope it will lead you to a way where you can make your own modifications to get the best out of the global search. Remember: all changes you make are at your own risk.
Here are the changes you have to do:
Class SysGSSearchStart, method startSearch
comment the following line:
infolog.add(Exception::Warning,"@SYS98793");
With that, there will be no warning if you are working in a company where the data crawler is not running.
Class SysSearch, method search:
at line 28, just after "if (!searchname)" add:
select firstonly RecId from sysSearchName
where sysSearchName.Design == 'SDS_xxx_default'
&& sysSearchName.LanguageId == this.languageId();
if (!sysSearchName)
replace the xxx in the 'SDS_xxx_default' with the company id where the data crawler is running.
Class SysSearch, methods searchWord and searchExactWord:
at line 11, replace the "where sysSearchName.Design == this.design() &&" with:
where (sysSearchName.Design == this.design()
sysSearchName.Design == 'SDS_xxx_default') &&
again, replace the xxx in the 'SDS_xxx_default' with the company id where the data crawler is running.
Class SysSearchDoDataSearch, method buildItemListXML:
at line 11, after a while select indextable from sysDataSearch block, add the following code:
changecompany('xxx')
{
sysDataSearch = null;
while select IndexTable from sysDataSearch
{
dictTable = new DictTable(sysDataSearch.IndexTable);
if (dictTable.rights() != AccessType::NoAccess)
searchTableMap.insert(dictTable.id(),0);
}
}
sysDataSearch = null;
Replace 'xxx' with the company id where the data crawler is running.
At line 77, after select sysDataSearch where sysDataSearch.SearchGroupId == m_eSearchGroupDef && sysDataSearch.IndexTable == tableid; add the following code:
if (!sysDataSearch)
{
changecompany('xxx')
{
sysDataSearch = null;
select sysDataSearch
where sysDataSearch.SearchGroupId == SearchGroupDef
&& sysDataSearch.IndexTable == tableid;
}
}
Replace 'xxx' with the company id where the data crawler is running.
Using global search for virtual tables
Now, if some of your tables that are searched belong to a virtual company, the data crawler will mark them as records of the company it is running in. That means that if you want to search for your virtual data from a different company, it will return no results.
Example: you have the companies xxx and yyy and a virtual company vir. Table CustTable is virtual (dataareaid of the records is vir). The data crawler runs in company xxx and will mark the CustTable records as belonging to company xxx .
Now, if you start a search in company yyy, it will not find the Custtable records as they seem to belong to a different company. You could only set up a second data crwaler for company yyy which would collect exactly the same records and you would need to store them twice in your database.
The following changes will circumvent that: you will be able to see data from different companies. There are some drawbacks, however: you will be able to see search results from your "data crawler company". But it the data is from a non-virtual table, you will not be able to see the results. But I hope it will lead you to a way where you can make your own modifications to get the best out of the global search. Remember: all changes you make are at your own risk.
Here are the changes you have to do:
Class SysGSSearchStart, method startSearch
comment the following line:
infolog.add(Exception::Warning,"@SYS98793");
With that, there will be no warning if you are working in a company where the data crawler is not running.
Class SysSearch, method search:
at line 28, just after "if (!searchname)" add:
select firstonly RecId from sysSearchName
where sysSearchName.Design == 'SDS_xxx_default'
&& sysSearchName.LanguageId == this.languageId();
if (!sysSearchName)
replace the xxx in the 'SDS_xxx_default' with the company id where the data crawler is running.
Class SysSearch, methods searchWord and searchExactWord:
at line 11, replace the "where sysSearchName.Design == this.design() &&" with:
where (sysSearchName.Design == this.design()
sysSearchName.Design == 'SDS_xxx_default') &&
again, replace the xxx in the 'SDS_xxx_default' with the company id where the data crawler is running.
Class SysSearchDoDataSearch, method buildItemListXML:
at line 11, after a while select indextable from sysDataSearch block, add the following code:
changecompany('xxx')
{
sysDataSearch = null;
while select IndexTable from sysDataSearch
{
dictTable = new DictTable(sysDataSearch.IndexTable);
if (dictTable.rights() != AccessType::NoAccess)
searchTableMap.insert(dictTable.id(),0);
}
}
sysDataSearch = null;
Replace 'xxx' with the company id where the data crawler is running.
At line 77, after select sysDataSearch where sysDataSearch.SearchGroupId == m_eSearchGroupDef && sysDataSearch.IndexTable == tableid; add the following code:
if (!sysDataSearch)
{
changecompany('xxx')
{
sysDataSearch = null;
select sysDataSearch
where sysDataSearch.SearchGroupId == SearchGroupDef
&& sysDataSearch.IndexTable == tableid;
}
}
Replace 'xxx' with the company id where the data crawler is running.
Send message to online user in Dynamics AX 4.0 (quick & dirty)
{
EventInbox inbox;
EventInboxId inboxId;
inboxId = EventInbox::nextEventId();
inbox.initValue();
inbox.ShowPopup = NoYes::Yes;
inbox.Subject = "Message to online user";
inbox.Message = "Message you want to send to user";
inbox.SendEmail = false;
inbox.UserId = curUserID();
inbox.InboxId = inboxId;
inbox.AlertCreatedDate = systemdateget();
inbox.AlertCreateTime = timeNow();
inbox.insert();
}
Be careful when resetting your usage data
But here's another thing for you:
in Dynamics AX 4.0, a "Favorites" menu was introduced. You can drag a form to your favorites pane. Then you will be asked to give it a name, and: you can add a saved query to it. With that, you can easily make favorites the open the forms filtered on the data you just need.
But there's another thing: as long as your system is still in change, everybody recommends that you delete your usage data from time to time (in User/Options).
But what happens to your favorites if you delete all your usage data?
The answer is: you'll get an error message. The system will tell you that it cannot find your saved query anymore. So, the whole work saving your queries and making your favorites is lost.
A small little addition to the method "reset" in the form "SysLastValue" will help you avoid that.
Add the line
&& _sysLastValue.recordType != UtilElementType::UserSetupQuery;
in the "delete_from" statement.
So, the deletion will not delete your saved queries.
Things that could be improved:
- Only skip queries that have been saved with a name
- Only skip queries that are used for a favorite menu item
Automatically open grid filter when opening form
this.task(2855);
So, your run() method should look like:
public void run()
{
..... // some programming lines....
super();
this.task(2855);
}
Activating query tracing for all users
static void Set_SQLTrace_AllUsers(Args _args)
{
#LOCALMACRO.FLAG_SQLTrace (1 << 8) #ENDMACRO
#LOCALMACRO.FLAG_TraceInfoQueryTable (1 << 11) #ENDMACRO
boolean set;
UserInfo userInfo;
;
set = true;
ttsbegin;
while select forupdate userinfo
{
userinfo.querytimeLimit = 1000;
if (set)
{
userInfo.DebugInfo = userInfo.DebugInfo | #FLAG_SQLTrace;
userInfo.TraceInfo = userInfo.TraceInfo | #FLAG_TraceInfoQueryTable;
}
else
{
userInfo.DebugInfo = userInfo.DebugInfo ^ #FLAG_SQLTrace;
userInfo.TraceInfo = userInfo.TraceInfo ^ #FLAG_TraceInfoQueryTable;
}
userinfo.update();
}
ttscommit;
}
AD user creation
Basics of changecompany
Browse the data dictionary with X++
Caching in Microsoft Axapta
Class InventOnhand
Depending on the source operating system to execute mode
Get label string in different languages
How to create a new EventType
Intercompany with buf2buf
Packing Slip posting
Berechtigungen eines Benutzer mit X++ Code abfragen Permissions a user with X + + Code
Query or Not
How to identify the user that was used to change an object from AOT in AX2012
Get the object name for which we need to track these (user and date&time) information's. Login to SQL Server Management Studio an...
-
Combo boxes in Dialogs are added by adding a DialogField object with the type of an enum. The enum is then controlling what items you can pi...
-
This article explains: How to picking list through a job using X++ in Dynamics AX. Applied on: Dynamics AX 2009 SP1 Create a job a...
-
A while ago I had the need to translate labels, I was creating eMail bodys while using SysMailer and wanted to use different languages for d...