Monday, June 15, 2009
Thursday, May 21, 2009
Get PrintJobSettings From User
Encode Barcode String For Any BarcodeType
Un/Reservation By Code
InventSerialId _inventSerialId,
Qty _qty ,
boolean _unreserve=false)
{
InventUpd_Reservation inventUpd_Reservation;
inventMovement inventMovement;
InventSerial invserial;
inventdim inventDim,iDimNew;
SalesLine salesLine;
;
try
{
ttsbegin;
salesLine = SalesLine::findInventTransId(_inventTransId);
if (!salesline)
throw error('Sales Order Line could not be found !');
invSerial = InventSerial::find(_inventSerialId,salesLine.ItemId);
if (!invSerial)
throw error('Inventory Serial Number could not be found !');
inventDim = salesLine.inventDim();
inventdim.inventSerialId = _inventSerialId;
iDimNew = inventDim::findOrCreate(inventDim);
inventMovement = InventTrans::findTransId(salesLine.InventTransId).inventMovement(true);
inventUpd_Reservation = InventUpd_Reservation::newInventDim(inventMovement,iDimNew,_unreserve ? _qty : -_qty);
inventUpd_Reservation.updateNow();
ttscommit;
}
catch (Exception::Deadlock)
{
retry;
}
}
AX 2009 – Adding Display method to Custom Lookup
AX 2009 brings capability to add display methods as well as adding table fields to custom lookups.
SysTableLookup.addLookupMethod(identifierstr(DisplayMethod));
So you can create a display method that gets only date of datetime field and display it in Lookup
Display bitmap on report
Get the label description for any language
How to add a range for the Array EDT to a query?
How many time user logged on to AX at the time (for 3 tier structure)
for(counter1 = 1; counter1 <= conlen(con1); counter1++)
{
con2 = conpeek(con1, counter1);
if (conpeek(con2, 2) == curuserid())
{
countUser++;
}
}
Cookies usage on AX Portal
webSession().response().cookies().itemWriteCookie("InformationId=Value; expires=Fri, 31-Dec-2013 00:00:00 GMT");
//Get cookie
iis = new IISRequest();
webSession().writeTxt(iis.cookies().itemTxt(("InformationId")));
Sending XML file to webservice
What are the objects Dict* and How can we use them?
In some cases we need to write some codes in Dict objects. If we look under AOT\System Documentation\Classes node, here are the objects we will discuss in this post.
Dict objects are generally used for creating a object of related type (for DictClass e.g. CustBalanceList class), getting information about that object (for DictClass e.g. is class defined Abstract, Final or is Runnable {has main method} ) etc.
DictClass
Object can be constructed by “new” word (Params: ClassId). Mostly and effectively used for creating a child class (if we know classId of it).
Example usage in AX Classes\VenOutPaymRecord\newVendOutPaymRecord Example code //A child class will be created by using dictclass
DictClass dictClass;
VendOutPaymRecord vendOutPaymRecord; // parent class is defined
;if (! _vendPaymModeSpec.classId)
{ // Payment mode specification has the child class id
return null;
}if (! SysDictClass::isSuperclass(_vendPaymModeSpec.classId, classNum(VendOutPaymRecord)))
{ //Be sure if child class is really child
return null;
}dictClass = new DictClass(_vendPaymModeSpec.classId); //Initialize dictclass object by child class id
vendOutPaymRecord = dictClass.makeObject(); // Create real child class by calling makeObject methodif (! vendOutPaymRecord) // Check if class was made or not
{
return null;
}
DictConfigurationKey
Object can be constructed by “new” word (Params: ConfigurationKeyId). Mostly used for checking if configuration key is enabled or not
Example usage in AX Classes\PurchLineType\interCompanyMirror Example code //Check if SalesDeliveryDateControl configuration key is enabled
if (new DictConfigurationKey(configurationkeynum(SalesDeliveryDateControl)).enabled())
DictEnum
Object can be constructed by “new” word (Params: EnumId). Mostly used for returning all of the elements of any enum object and accessing name of them.
Example usage in AX Classes\InventItemType\valueCanBeProduced Example code //Check if SalesDeliveryDateControl configuration key is enabled
DictEnum dictEnum;
Counter i;
InventItemType inventItemType;
str itemTypeTxt;
;dictEnum = new DictEnum(enumnum(ItemType)); // ItemType is a variable that value determined at outer of method earlier
for (i=0;i// This block will loop until the end element of enum
{
inventItemType = InventItemType::construct(i);
if (inventItemType.canBeProduced())
{
itemTypeTxt += itemTypeTxt ? ',' : '';
itemTypeTxt += queryValue(dictEnum.value2Name(i)); // Here, enum name is extracting from enum value
}
}return itemTypeTxt ? itemTypeTxt : SysQuery::valueEmptyString();
DictField
Object can be constructed by “new” word (Params: TableId, FieldId). Mostly used for finding basetype or label etc of any table field.
Example usage in AX Classes\SysApplCheck\checkFieldDefaultReport Example code // This example will show some functionalities of DictField class
#MacroLib.DictField // will be used for flag testing
DictField dictField = new DictField(Tablenum(ContactPerson), FieldNum(ContactPerson,Name));
;
dictField.enumId(); // If that field was the type of enum, that method would return enumid (now is zero)
dictField.arraySize(); // In default array size is 1 (for EDT Dimension , array size bigger than 1)
if (bitTest(dictField.flags(),#DBF_CHANGE))
info('Allow edit property of the field is true');
dictField.relationObject(); // can be used for determining main table of EDT of the field. That expression will return dictRelation object
dictField.label(); // label of the field
dictField.stringLen(); // if field is the typeof string, that will return length of string (nonzero) value
DictFieldGroup
Object can be constructed by “new” word (Params: TableId, FieldGroupName). Mostly used for determining the fields in the fieldgroup of a table.
Example usage in AX
Classes\SysReportWizard\fieldFillContainer Example code // This example will show some functionalities of DictFieldGroup class
dictFieldGroup = new DictFieldGroup(TableNum(CustTable), ‘Address’); // Will Process Address fieldgroup of CustTable
fieldGroupName = dictFieldGroup.name(); // will return the string ‘Address’
fieldGroupElement = connull();
lastFieldElement = 0;for (j=1; j <= dictFieldGroup.numberOfFields(); j++) //Determine the # of fileds in group
{
methodName = dictFieldGroup.methodName(dictFieldGroup.field(j)); //If the element in group is display or edit method ‘methodname’ variable will be the name of the method
if (methodName) //If the element is method then do something…
{
…
}
else // otherwise this is a table field
{
sysDictField = new SysDictField(dictTable.id(), dictFieldGroup.field(j)); // get dictfield and do something…
for(k = 1; k <= sysDictField.arraySize(); k++)
{
…
}
}
}
DictIndex
Object can be constructed by “new” word (Params: TableId, IndexId). Mostly used for finding the fields of the index or checking if it allow duplicates or not.
Example usage in AX
Classes\SysDictField\isUnique
Example code
// This example will show some functionalities of DictIndex class
DictTable dictTable;
DictIndex dictIndex;
int indexId;
int i;
fieldId fieldId;if (this.mandatory())
{
dictTable = new DictTable(this.tableid());
fieldId = fieldExt2Id(this.id());
if (dictTable.primaryKeyField() == fieldId)
return true;for (indexId = dictTable.indexNext(0); indexId; indexId = dictTable.indexNext(indexId)) // loop index of table
{
dictIndex = dictTable.indexObject(indexId); // get dictIndex from dictTable
if (!dictIndex.allowDuplicates()) // check if index property allowDuplicates is true or false
{
for (i=1; i<=dictIndex.numberOfFields(); i++) // loop thru fields of index
{
if (dictIndex.field(i) == fieldId) // get fieldId of the current field
return true;
}
}
}
}
return false;
Dictionary
Object can be constructed by “new” word (no Params required). Dictionary class is the general class for getting some informations about every AOT object.
Example usage in AX
Classes\ProjTableType\returnClass
Example code
// This example will show some functionalities of Dictionary class (Only for class related methods).
Dictionary dict = new Dictionary();
int i;
;
for (i = 1; i<=dict.classCnt(); i++) // Loop thru the classes in AOT
{
print dict.classCnt2Id(i); // classid of class
print dict.classFlush(); // Restore class object
print dict.className(dict.classCnt2Id(i)); // name of class
print dict.className2Id(dict.className(dict.classCnt2Id(i))); // will print same value as dict.classCnt2Id(i)
print dict.classNext(dict.classCnt2Id(i)); // classid of the next class after the current class
print dict.classObject(dict.classCnt2Id(i)); // constructs dictClass of the current class object
}
pause;
DictLicenseCode
Object can be constructed by “new” word (Params: LicenseCodeId). Mostly used for finding licensecode group
Example usage in AX
Classes\SysLicenseCodeReadFile\createCodes
Example code
// This example will show some functionalities of DictLicenseCode class
sysDictLicenseCode = new DictLicenseCode(dictionary.licenseCodeCnt2Id(i)); //construct licensecode object
if (sysDictLicenseCode) // if constructed
{
switch (sysDictLicenseCode.group()) // get group of license code ie. System,Partner,Web etc
{
case LicenseCodeGroup::Language:
print sysDictLicenseCode.id(); // get licensecode id and printbreak;
}
}
DictMethod
Object can be constructed by “new” word (Params: UtilElementType, Id, Name). Mostly used for accessing method properties such as return type, parameters etc.
Example usage in AX
Classes\SysApplCheck\checkTableFieldPnameMustBeUnique
Example code
// This example will show some functionalities of DictMethod class
DictMethod dictMethod;
;
// If you want to create dictMethod of the type
//
// ClassInstanceMethod
// ClassStaticMethod
// then 2nd parameter will be ClassId
//
// TableInstanceMethod
// TableStaticMethod
// then 2nd parameter will be TableId
dictMethod = new DictMethod(UtilElementType::ClassInstanceMethod,
classNum(CustInPaym),
identifierstr(loadVoucherNum));
print dictMethod.returnType(); // prints return type (void)
print dictMethod.isAbstract(); // prints if the method is abstract (false)
print dictMethod.parameterCnt(); // prints parameter count of method (0)
print dictMethod.accessSpecifier(); // prints access specifier (public)
print dictMethod.returnId(); // prints EDT id of return type (in this case 0 = void)
print dictMethod.noParms(); // prints 1 which means there is no parameter required for that method
print dictMethod.parentId(); // in this case prints classid (72) (because of method is class instance method)
pause;
DictRelation
Object can be constructed by “new” word (Params: TableId). Mostly used for determining relation of two tables.
Example usage in AX
Classes\SysSeachResultForm\getMainRecord
Example code
// This example will show some functionalities of DictRelation class
private static Common getMainRecord(int iPMainTableId, int iPLinesTableId, Common CPLinesTableRecord)
{
DictRelation CDictRelation;
int i;
DictField CMainDictField;
DictField CLineDictField;
Query CQuery = new Query();
QueryRun CQrun;
Common CMainTableRecord;;
CDictRelation = new DictRelation( iPLinesTableId ); // construct dictrelation according to line table
CDictRelation.loadTableRelation( iPMainTableId ); // give tableid to be found relation// get list of fields that one table has to another
// and build query to main table
CQuery.addDataSource( iPMainTableId );
if (CDictRelation.lines())
{
for (i = 1; i <= CDictRelation.lines(); i++)
{
CMainDictField = new DictField(iPMainTableId, CDictRelation.lineExternTableValue(i)); // Here is the point, that job of dictrelation begins. CDictRelation.lineExternTableValue(i) returns maintable relation field
CLineDictField = new DictField(iPLinesTableId, CDictRelation.lineTableValue(i)); // And CDictRelation.lineTableValue(i) returns line table relation field
CQuery.dataSourceTable( iPMainTableId ).addRange( CMainDictField.id() ).value( queryValue( CPLinesTableRecord.( CLineDictField.id() ) ) );
}
}
CQrun = new QueryRun(CQuery);
CQrun.next();
CMainTableRecord = CQrun.get(iPMainTableId);return CMainTableRecord;
}
DictSecurityKey
Object can be constructed by “new” word (Params: SecurityKeyId). Mostly used for checking if the user has securitykey access.
Example usage in AX
Classes\Global\hasSecurityKeyAccess
Example code
// Below method will explains DictSecurityKey class briefly
static boolean hasSecuritykeyAccess(securityKeyId securityKeyId, AccessType neededAccessLevel)
{
DictSecurityKey dsk=new DictSecurityKey(securityKeyId); // Construct class
;
if (dsk == null) // if not constructed then there is no such a securityKey
return false;
else
return dsk.rights() >= neededAccessLevel; // rights method will return current user’s AccessType (eg. NoAccess or View etc) to SecurityKey
}
DictTable
Object can be constructed by “new” word (Params: TableId). Mostly used for getting information and calling methods of table.
Example usage in AX
Classes\SysApplCheck\checkForm
Example code
// Below method will explains DictTable class briefly
DictTable dictTable;
CustTable custTable;
CustAccount accountNum = 'CustomerNo1';
;
select custTable
where custTable.AccountNum == accountNum; // we will use that record below
// we can construct table, map, view
dictTable = new DictTable(tablenum(CustTable)); // Construct dictTable class (here we will use CustTable as an example)
print dictTable.callObject(identifierstr(countryName),custTable); // callObject will execute 'countryName' instance method using the selected record above and returns value/object (according to the method return type)
print dictTable.callStatic(identifierstr(blocked),accountNum); // this will execute 'blocked' static method and returns value/object (according to the method return type)
print dictTable.fieldGroupCnt(); // prints count of fieldgroup in that table (CustTable)
CustTable = dictTable.makeRecord(); // makes a new record
dictTable.isMap(); //is object type of Map?
dictTable.isView(); //is object type of View?
dictTable.isTmp(); // (if it is table) is it temporary table?
print dictTable.titleField1(); // prints titlefield1 (fieldnum) property of table (is it is table)
pause;
DictType
Object can be constructed by “new” word (Params: extendedTypeId). Mostly used for getting information about any type.
| Example usage in AX | Classes\AifMessage\validateString |
| Example code | // Below method will explains DictType class briefly DictType dictType = new DictType(extendedTypeNum(CustAccount)); |
Round function
Round function gets two parameters, first one is variable type of real which is the number to be rounded and the other one is decimal which will be used as sensitivity constant. What does mean sensitivity? Let’s dive into examples.
- round(23.79, 0.1) = 23.80 . The explanation of the result is:
- Closest multiple to the number of 0.1 is on the left : 23.70 and on the right : 23.80. But the closest one is 23.80 ie. the result.
- Closest multiple to the number of 0.5 is on the left : 23.50 and on the right : 24.00. But the closest one is 24.00 ie. the result.
- Closest multiple to the number of 10 is on the left : 20.00 and on the right : 30.00. But the closest one is 20.00 ie. the result.
- Closest multiple to the number of 0.1 is on the left : 23.00 and on the right : 24.00. Which have equal distance to the number. In this case bigger one will be taken into account which is 24.00 ie. the result.
- Closest multiple to the number of 50 is on the left : 0.00 and on the right : 50.00. But the closest one is 0.00 ie. the result.
| <<<<<<<<<<<<<<<<<<<<<< | >>>>>>>>>>>>>>>>>>>>>> |
Above shape shows behavior of round function. Thus, There are two boundaries, lower and upper, rounded number will be lower boundary if number is closer to lower boundary and vice versa. Special case the middle boundary ( (lower+upper) / 2 ), which the result will be upper one
KeySum Class
KeySum class is useful for summing data linked to keys. For example you may want to sum Invoice Amount while looping in CustInvoiceJour with grouping in Customer Group. So I recommend to use that handy class. No need to explain in theoretic let’s go practical.
KeySum keySum; // Define keySum class
;
// Initializing class:
// First Param: NumOfKeys : Length of key.
// If you specify that value 2, you have to give it a container which length of it is 2.
// Second Param: NumOfData : Length of data.
// If you specify that value 2, you have to give it a container which length of it is 2.
// Third Param: Sorting
// -1 : Descending order according to key
// 0 : It does not sort
// 1 : Ascending order according to key
keySum = new KeySum(1, 1, 0); // Initialize
keySum.updateNow(1, 15); // keySum = [1 -> 15]
keySum.updateNow(2, 47); // keySum = [1 -> 15, 2 -> 47]
keySum.updateNow(1, 100); // keySum = [1 -> 115, 2 -> 47]
keySum.updateNow(3, 15); // keySum = [1 -> 115, 2 -> 47, 3 -> 15]
keySum.updateNow(7, 47); // keySum = [1 -> 115, 2 -> 47, 3 -> 15, 7 -> 47]
keySum.updateNow(4, 100); // keySum = [1 -> 115, 2 -> 47, 3 -> 15, 7 -> 47, 4 -> 100]
print keySum.key2Data(1); // prints 115 = which is sum of 15 + 100 i.e. the data we loaded earlier for the key 1
print keySum.total(); // prints 324 which is grand total of all the data
print keySum.numOfTrans(); // prints 5 = which is the count of the keys (1,2,3,4,7)
keySum.keyDelete(2); // deletes key 2 -> 47. So total is decreased to 324-47 = 277
keySum.addKeySum(keySum); // addKeySum method can be merged with another keySum object. In this case we did with itself
print keySum.total(); // total is doubled so 277*2 = 554
pause;
Tuesday, May 19, 2009
Working with Dynamics AX Table Meta Data
Recently I had to create a custom database log for a client. Dynamics AX does come with a database log feature (with the proper license key). However, my client wanted something different to allow easier reporting and tracking of changes. The log must be searchable, filterable, and sortable based on any field. For instance, if the CustTable updates were being logged, the user must be able to search, filter, and/or sort by CustGroup, AccountNum, or any other field that was logged. The standard log does not have this capability.
It was decided that in order to achieve these goals, each table to be logged (source) should have its own log table. The log table would mirror the table structure of the source table, with some additional log specific fields, such as log type, date and time of log, changed by, etc. The custom log needed to be able to record the same table events as the standard log (inserts, updates, deletes, and rename keys).
Problem
I had to have a way to perform a field-to-field data transfer from the source table to the log table (ie. source.AccountNum --> log.AccountNum). My initial thought was to copy the data using the intrinsic "data" method on table buffer object. This method allows a table buffer (record) to be passed to the "data" method of another (empty) table buffer to be copied field for field, with the exception of system fields like RecId, RecVersion, etc. It turns out that a field-to-field data copy only works if the two table buffers are of the same table. Unfortunately, even if the field names were the same, the "data" method does not copy the data if the table buffers objects were for different tables.
I needed to write my own data copy function. My function had to be generic, because I didn't want to hard-coded the field name and the mappings between the source and log tables. Additionally, I didn't want to have to update my code each time a new field was added or removed from the source table. In order to make the log function in a generic way, I needed to the meta data within Dynamics AX.
Solution
Below is the code for the static method I created to transfer the data from the source table to the log table. I've placed comments in red to explain the interesting parts of the code.
Basically the method loops through the fields of the source table, and assigns it to the corresponding field in the log table. By using the SysDictTable class, I'm able to retrieve the field list for the source table. This allows me to "see" all the fields, including newly added fields. I also do not have to deal with deleted fields, because they won't appear in the field list.
The challenging part of this method was dealing with array fields. These are fields that are based on an extended data type that is an array. A prime example of this type of field is the "Dimension" field found in many master and transactions tables. Each array element translates to a single field in the table in the database. In Dynamics AX, an array field appears as a single field would. It has a field ID just like any other field, with one exception. An array field has what's called an "extended field ID". This is the combination of the field's ID and the element's position or index in the array. Therefore, to properly retrieve the data for an array field, you must properly address the field's array element to get the correct field ID. This can be done via the "fieldId2Ext" function.
static void copyDataToLog(Common _fromTable, Common _toTable)
{
SysDictTable fromDictTable;
SysDictField fromDictField;
SysDictTable toDictTable;
SysDictField toDictField;
int fields;
int fieldIndex;
int arraySize;
int arrayIndex;
int fromFieldId;
int toFieldId;
;
// Instantiate the SysDictTable object. The SysDictTable class
// extends the DictTable class. By using the SysDictTable class,
// instead of the DictTable class, you get the ability override methods,
// and get the field list, among other things.
//
// I use the SysDictField class in the same way as I use the
// SysDictTable class to process and the retrieve meta data
// about the fields. It provides information about each field.
fromDictTable = new SysDictTable(_fromTable.TableId);
toDictTable = new SysDictTable(_toTable.TableId);
// Loop through the field list for the source table.
// Note that the loop excludes the system fields, such as
// RecId, RecVersion, CreatedDate, CreatedBy, etc. Each
// table will have its own set of system fields, which will be
// populated by Dynamics AX when the record is inserted.
fields = fromDictTable.fieldCnt();
for (fieldIndex = 1; fieldIndex <= fields; fieldIndex++)
{
fromDictField = new SysDictField(fromDictTable.id(),fromDictTable.fieldCnt2Id(fieldIndex));
if (!fromDictField.isSystem())
{
// Some tables contain fields that inherit from array extended data
// types. An example of an array field is the "Dimension" field,
// found in tables throughout the database.
// Each element in the array field corresponds to a real field
// in the database. The concept of field ID still exists for array
// fields, but with a little twist. The field ID for each
// array element is called an "extended field ID". This extended
// field ID is the combination of the field ID and the array index
// for the element.
// The function "fieldId2Ext" provides a way to get an array field's
// field ID.
if (fromDictField.arraySize() > 1)
{
// Field is an array (ie. Dimension).
arraySize = fromDictField.arraySize();
// Loop through each array field's element to copy its value
// to the corresponding log array field's element.
for (arrayIndex = 1; arrayIndex <= arraySize;arrayIndex++)
{
fromFieldId = fieldId2Ext(fromDictField.id(),arrayIndex);
toFieldId = fieldId2Ext(fieldName2Id(_toTable.TableId, fromDictField.name()), arrayIndex);
// Note that field values may be assigned and retrieved using
// the field ID instead of the field name.
_toTable.(toFieldId) = _fromTable.(fromFieldId);
}
}
else
{
// Use the "fieldName2Id" function to convert the field name
// from the source table to the corresponding field ID for the
// log table. This is possible because the field names in the log
// table are the same as the source table.
toFieldId = fieldName2Id(_toTable.TableId,fromDictField.name());
_toTable.(toFieldId) = _fromTable.(fromDictField.id());
}
}
}
}
Conclusion
Using this static method, I'm able to copy the source table fields to the log table fields. This method illustrates one possible usage of system meta data as provided by the "Dict*" classes to perform generic work. These classes provided the necessary information about the table and fields to perform the task without the need to hard-code table or field names. There are several other "Dict" classes that provide information about other application objects.
Some ref to utilelement table and creation of shared project
It may also get updated when the customisation is imported into the application.
The code shown below how to delete a shared project
treenode c1,c2;
;
c1 = systemnode::getsharedproject();
c2 = c1.aotfindchild("project1");
c2.aotdelete();
c2.aotrefresh();
c2.aotsave();
Sunday, April 19, 2009
Find out the string length of an extendedDataType
{
Dictionary dict;
DictType dictType;
;
dict = new Dictionary();
dictType = dict.typeObject(dict.typeName2Id(extendedtypestr(AccountName)));
info(strfmt("Name : %1 \nId: %2 \nStringLength: %3 \nAdjustment: %4 \nLabel: %5 \nHelp: %6 \nBasetype: %7",
}
Tuesday, March 31, 2009
how to pass the parameter form form a to form b
I can easily pass value from Form1 to Form2 using:
args.parm("hello" );
and can get value on Form2 using:
info(element. args().parm( ));
But how would I pass value from Form3 to Form1? How can I pass more than one values from Form3 to Form1?
You can use args.parmObject( ) to pass object as a parameter,
or pass reference of form1 by args.caller( )
Thursday, March 5, 2009
How to create a crash dump for Axapta processes ?
Here are the steps to do so:
- Download the Microsoft debugging tools, from here:
http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx
The 64-bit Version is available here:
http://www.microsoft.com/whdc/devtools/debugging/install64bit.mspx - Install the debugging tools on the problem machine.
- Switch to the install folder of the debugging tools using the command line.
- Attach the tracing tool(adplus) to the crashing process.
- Use the following command line options to attach the debugger.
adplus –crash –pn ax32.exe
adplus –crash –pn ax32serv.exe
oradplus –crash –p
To enable the Process ID, go to Task Manager\View\Select Columns…
PID (Process Identifier) - Now the tracing tool is active and should generate a crash dump, once the process crashes.
- You will find the dump file in the installation folder of the debugging tools.
- A correct crash dump file should look like this:
PID-4580__AX32.EXE__2nd_chance_UnknownException__full_1810_2008-12-04_20-53-48-528_11e4.dmp
Please focus on the 2nd_chance in the title. Please reattach the tracing tool again in case you get only 1st_chance exception, or just a log file(has a *.log extension).
Example: - Once you have created a second chance crash dump file, send it to the Microsoft MBS support to get the file analyzed in detail.
Creation of new financial dimension
- Base enum "SysDimension" : Find this base enum and right click on this base enum -> Select option "New element". In properties window give a name to this element say "TestDim" and label as "Test dimension". Save the base enum.
- Extended data type "Dimension" : Find this EDT and then add a new array element in this EDT. Name this array element as "TestDim". In properties window specify label as "Test dimension". Now in the "Relations" tab of this new array element add a new "Normal" relation first. To this normal relation open properties window and set property Table as "Dimensions" and Related field as "Num". Now add another relation of type "Related field fixed". To this related field fixed relation open properties window and set Related field as "DimensionCode" and property value as "3" (this is the value of the new enum element created in SysDimension). Save the EDT.
- Extended data type "DimensionCriteria" : Repeat the process of modification as done for EDT "Dimension" above.
- Extended data type "XMLMapDimension" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Test document value". Save the EDT.
- Extended data type "MandatoryDimension" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Validate test dimension". Save the EDT.
- Extended data type "DimensionLedgerJournal" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Test dimension". Save the EDT.
- Extended data type "DimensionKeepFromTransaction" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Keep transaction test dimension". Save the EDT.
- Extended data type "COSAllowDimensions" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Test dimension". Save the EDT.
- Extended data type "DimensionPriority" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Test dimension". Save the EDT.
- Extended data type "DimensionAllocation" : Find this EDT in AOT and then create a new array element for this EDT. Label this array element as "Test dimension". Save the EDT.
- Table "LedgerJournalTrans" : Find this table in AOT and add a new relation in the relations tab as follows. Create a new relation in relations tab and name it as say "interCoDimension3" (You can see three more similar relations with suffix 0, 1 and 2 for three standard dimensions). Now set the property table of this relations as "Dimensions". Create a new "Normal" relation under this realtion tab and set the property "Field" = "OffsetCompany" and property "RelatedField" = "dataAreaId". Create another "Normal" relation under this relation tab and set the property "Field" = "InterCoDimension[4]" and property "RelatedField" = "Num". Now create a new "Related field fixed" relation under this tab and set property "Value" = 3 (this is the value of the new enum element created in SysDimension) and property "Related field" = "DimensionCode". Save the changes.
The new financial dimension is successfully created in AX and can be viewed in different forms through out the AX where ever dimensions are used.
Wednesday, February 4, 2009
Extracting data from Ax3.0 to Xml
============ ========= ==
static XML getAllDlvMode( )
{
DlvMode dlvMode;
XML xml;
;
xml = '
return xml;
}
============ ========= ========= =======
Sample code to load and parse XML file:
============ ========= ========= =======
XMLDocument XMLDOM;
XMLNodeList XMLNodes_ItemOut;
TextBuffer tbPurchOrder;
boolean blnXMLLoaded;
str nodeText;
;
XMLDOM = new XMLDocument( ) ;
XMLDOM.async( false);
XMLDOM.setProperty( "ServerHTTPReque st", true);
strUNCFileName_ reg = strfmt("%1", _strFileName) ;
tbPurchOrder = new TextBuffer() ;
tbPurchOrder. fromFile( strRTrim( strLTrim( strUNCFileName_ reg)));
temp = tbPurchOrder. getText() ;
blnXMLLoaded = XMLDOM.loadXML( temp);
if(blnXMLLoaded)
{
//parse the XMLDOM
nodeText = strXMLDOM.selectSin gleNode(" path/nodeName" ).text
();
}
============ ========= ========= ========= ========= =========
Sample code to write a file
============ ========= ========= ========= ========= ========= =========
AsciiIo oAsciiIo;
str strFilePath, strMode, strLog;
;
//oAsciiIo = new AsciiIo(strFileName , "A");
oAsciiIo = new AsciiIo(strFilePath , strMode);
oAsciiIo.write( strLog);
/*
You may use WinAPI static methods to create and manage folders.
e.g.: if (!WinAPI::folderExi sts(strFolderPat h))
WinAPI::createDirec tory(strFolderPa th);
*/
============ ========= ========= ========= ========= ========= ========
Working with Dynamics AX Table Meta Data
Recently I had to create a custom database log for a client. Dynamics AX does come with a database log feature (with the proper license key). However, my client wanted something different to allow easier reporting and tracking of changes. The log must be searchable, filterable, and sortable based on any field. For instance, if the CustTable updates were being logged, the user must be able to search, filter, and/or sort by CustGroup, AccountNum, or any other field that was logged. The standard log does not have this capability.
It was decided that in order to achieve these goals, each table to be logged (source) should have its own log table. The log table would mirror the table structure of the source table, with some additional log specific fields, such as log type, date and time of log, changed by, etc. The custom log needed to be able to record the same table events as the standard log (inserts, updates, deletes, and rename keys).
Problem
I had to have a way to perform a field-to-field data transfer from the source table to the log table (ie. source.AccountNum --> log.AccountNum). My initial thought was to copy the data using the intrinsic "data" method on table buffer object. This method allows a table buffer (record) to be passed to the "data" method of another (empty) table buffer to be copied field for field, with the exception of system fields like RecId, RecVersion, etc. It turns out that a field-to-field data copy only works if the two table buffers are of the same table. Unfortunately, even if the field names were the same, the "data" method does not copy the data if the table buffers objects were for different tables.
I needed to write my own data copy function. My function had to be generic, because I didn't want to hard-coded the field name and the mappings between the source and log tables. Additionally, I didn't want to have to update my code each time a new field was added or removed from the source table. In order to make the log function in a generic way, I needed to the meta data within Dynamics AX.
Solution
Below is the code for the static method I created to transfer the data from the source table to the log table. I've placed comments in red to explain the interesting parts of the code.
Basically the method loops through the fields of the source table, and assigns it to the corresponding field in the log table. By using the SysDictTable class, I'm able to retrieve the field list for the source table. This allows me to "see" all the fields, including newly added fields. I also do not have to deal with deleted fields, because they won't appear in the field list.
The challenging part of this method was dealing with array fields. These are fields that are based on an extended data type that is an array. A prime example of this type of field is the "Dimension" field found in many master and transactions tables. Each array element translates to a single field in the table in the database. In Dynamics AX, an array field appears as a single field would. It has a field ID just like any other field, with one exception. An array field has what's called an "extended field ID". This is the combination of the field's ID and the element's position or index in the array. Therefore, to properly retrieve the data for an array field, you must properly address the field's array element to get the correct field ID. This can be done via the "fieldId2Ext" function.
static void copyDataToLog(Common _fromTable, Common _toTable)
{
SysDictTable fromDictTable;
SysDictField fromDictField;
SysDictTable toDictTable;
SysDictField toDictField;
int fields;
int fieldIndex;
int arraySize;
int arrayIndex;
int fromFieldId;
int toFieldId;
;
// Instantiate the SysDictTable object. The SysDictTable class
// extends the DictTable class. By using the SysDictTable class,
// instead of the DictTable class, you get the ability override methods,
// and get the field list, among other things.
//
// I use the SysDictField class in the same way as I use the
// SysDictTable class to process and the retrieve meta data
// about the fields. It provides information about each field.
fromDictTable = new SysDictTable(_fromTable.TableId);
toDictTable = new SysDictTable(_toTable.TableId);
// Loop through the field list for the source table.
// Note that the loop excludes the system fields, such as
// RecId, RecVersion, CreatedDate, CreatedBy, etc. Each
// table will have its own set of system fields, which will be
// populated by Dynamics AX when the record is inserted.
fields = fromDictTable.fieldCnt();
for (fieldIndex = 1; fieldIndex <= fields; fieldIndex++)
{
fromDictField = new SysDictField(fromDictTable.id(),fromDictTable.fieldCnt2Id(fieldIndex));
if (!fromDictField.isSystem())
{
// Some tables contain fields that inherit from array extended data
// types. An example of an array field is the "Dimension" field,
// found in tables throughout the database.
// Each element in the array field corresponds to a real field
// in the database. The concept of field ID still exists for array
// fields, but with a little twist. The field ID for each
// array element is called an "extended field ID". This extended
// field ID is the combination of the field ID and the array index
// for the element.
// The function "fieldId2Ext" provides a way to get an array field's
// field ID.
if (fromDictField.arraySize() > 1)
{
// Field is an array (ie. Dimension).
arraySize = fromDictField.arraySize();
// Loop through each array field's element to copy its value
// to the corresponding log array field's element.
for (arrayIndex = 1; arrayIndex <= arraySize;arrayIndex++)
{
fromFieldId = fieldId2Ext(fromDictField.id(),arrayIndex);
toFieldId = fieldId2Ext(fieldName2Id(_toTable.TableId, fromDictField.name()), arrayIndex);
// Note that field values may be assigned and retrieved using
// the field ID instead of the field name.
_toTable.(toFieldId) = _fromTable.(fromFieldId);
}
}
else
{
// Use the "fieldName2Id" function to convert the field name
// from the source table to the corresponding field ID for the
// log table. This is possible because the field names in the log
// table are the same as the source table.
toFieldId = fieldName2Id(_toTable.TableId,fromDictField.name());
_toTable.(toFieldId) = _fromTable.(fromDictField.id());
}
}
}
}
Conclusion
Using this static method, I'm able to copy the source table fields to the log table fields. This method illustrates one possible usage of system meta data as provided by the "Dict*" classes to perform generic work. These classes provided the necessary information about the table and fields to perform the task without the need to hard-code table or field names. There are several other "Dict" classes that provide information about other application objects.
Installation assembly with GAC
Wednesday, January 14, 2009
How to count the number of record in a table
{
int64 getRecordCount( str _tname)
{
common common;
SysDictTable dictTable = new SysDictTable( tableName2id( _tname));
;
common = dictTable.makeRecor d();
select count(RecId) from common;
return common.RecId;
}
;
info(strfmt("Number of records in %1 is %2", "CustTable", getRecordCount("CustTable")));
}
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
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...
-
I've recently found an interesting feature of DAX4 Object Server: it can run not only as a service, but also as a console application st...
-
static void CreateTransferOrderLine(Args _args) { InventTransferTable inventTransFerTable; InventTransferLine inventTransFerLine; ...
-
Settlement feature allows you to select the open transactions(Vendor / customer) and then you can post the payment journal for the same. I...

