Connect to ODBC from Dynamics AX through X++

Please find the below job to connect to other DB from AX DB

We need to fetch data from an external database which is installed in a remote server and populate the data into a AX table.

In our requirement, we have a External SQL server “ZSLVDYPDEV161” having a DB with name “Test” having a table “SampleTable”.

We need to fetch this data to our Dynamics Ax table named TestTable

Walkthrough:

  1. We would be writing a job to call the system classes for fetching the data.
  2. There are certain system classes that we would be utilizing for use of ODBC functionality.
  1. When declaring the method, make sure it runs on the server. Otherwise every client and batch server that runs it will need to have the SQL client and ODBC connection set up. Also, every user would need permission on the external database.

Server static void dbODBCConnection (Args _args)

Or, if it is just a onetime process from client , we can use

Static void dbODBCConnection (Args _args)

  1. We will be using LoginProperty, Statement, and ResultSet Through these you can read in data to Axapta, or perform update query’s like delete’s / updates / insert’s etc.
  1. LoginProperty    loginProp;
  2. ODBCConnection  conn;
  3. Resultset       resultSet;
  4. Statement       statement1;                                                                                                                                       These are the variables that we’ll use to create our connection to the SQL database via ODBC.

TestTable       testtable;

This is the Dynamics table where we’ll store the retrieved data.

 

LoginProp = new LoginProperty ();

loginProp.setServer (“ZSLVDYPDEV161”);

loginProp.setDatabase (“Test”);

These lines initialize the login property with the data source name and database name For simplicity, the data source and database names are hard-coded. In our production system, these are stored in a parameter table so that administrators can easily change them when necessary. 

SqlStatementExecutePermission sqlPermission;

Here’s the permission object that was introduced in version 4.0 as part of the trustworthy computing initiative. It is now necessary to grant explicit permission to make a call to an external database.

>>Due to requirements of the trustworthy computing initiative, one can no longer make calls from Dynamics AX to MS SQL server using SQL authentication, even if the database server is configured too allow it. It must be done using windows authentication, which happens automatically if the .setUsername () and .setPassword () methods are not invoked. This may not hold true for access to other ODBC data sources, in which case these lines may be helpful:

// loginProperty.setUsername (“UserID”);

// loginProperty.setPassword (“Password”);

sqlPermission = new SqlStatementExecutePermission(sqlStatement);

sqlPermission.assert();

resultSet = statement1.executeQuery(SQLStatement);

Now we use the new permission classes. We create a permission specific to the SQL select statement that we want to use, and explicitly assert the permission. Then we fetch the ResultSet by passing the same SQL statement to the executeQuery() method.

While (resultSet.next ())

{

_testtable.TestId       =    resultSet.getInt64 (1);

_testtable.TestName     =    resultSet.getString (2);

_testtable.TestAddress =    resultSet.getString (3);

_testtable.TestCity     =    resultSet.getString (4);

_testtable.insert ();

//info (resultSet.getString (2));

}

}

A while loop takes us through all the records, and assigns their values to the local table’s fields. Note that the results.getType(x) must be in numerical order, or a database error results.

Here each column of data fetched is being assigned to a field in the table

Here is the entire code,

Static void dbODBCConnection (TestTable _testtable)

{

LoginProperty                   loginProp;

ODBCConnection                  conn;

Resultset                       resultSet;

Statement                       statement1;

SqlStatementExecutePermission   sqlpermission;

Str sqlStatement;

;

loginProp = new LoginProperty();

loginProp.setServer(“ZSLVDYPDEV161”);

loginProp.setDatabase(“Test”);

conn = new ODBCConnection(loginProp);

statement1  = conn.createStatement();

sqlStatement = “SELECT * from TestTable”;

sqlPermission = new SQLStatementExecutePermission(sqlStatement);

sqlPermission.assert();

resultset = statement1.executeQuery(SQLStatement);

while (resultSet.next())

{

_testtable.TestId   =    resultSet.getInt64(1);

_testtable.TestName =    resultSet.getString(2);

_testtable.TestAddress =    resultSet.getString(3);

_testtable.TestCity =    resultSet.getString(4);

_testtable.insert();

}

}

We can use the same code for accessing the same server where the Dynamics AX is installed by just changing the Server name as done above.

These links can be also used a a reference and for in-depth understanding.

http://discoverax.blogspot.in/2007/12/get-external-data-by-odbc-from-dynamics.html

http://dynamics-ax.blogspot.in/2005/11/connecting-to-database-via-odbc.html

Microsoft Dynamics AX 2012: DateTimeUtil and HcmDateTimeUtil functions in Ax 2012

Hi,

  • DateTimeUtil::getSystemDateTime(): Gets Date and Time value of the system in GMT

Ex:  if the system user has the TimeZone ‘UTC +5’  and the current date time is 6/20/2013 5:00PM then the method will return ‘6/20/2013 12:00PM’

  • DateTimeUtil::utcNow(): Gets the Date and Time value in GMT/UTC

Ex. if the system user has the TimeZone ‘UTC +5’  and the current date time is 6/20/2013 5:00PM then the method will return ‘6/20/2013 12:00PM’.(this is similar to getSystemDateTime() but it gets the date and time value of the server.

  • HcmDateTimeUti::startofCompanyDay(): This method takes an input parameter of utcDateTime, and returns the date and time value that represent the starting of the given date after applying the companyTimeZone. 

Ex.if the value passed in the parameter is ‘6/20/2013 10:00 PM’ then it will return ‘6/20/2013 12:00:00 AM’ after applying the companyTimeZone

  • HcmDateTimeUti::startofCurrentDay(): This method returns the date and time value that represent the starting of the current  day after applying the userPreferredTimeZone. 

Ex.if the value passed in the parameter is ‘6/20/2013 10:00 PM’ then it will return ‘6/20/2013 12:00:00 AM’ after applying the userPreferredTimeZone

  • DateTimeUtil::applyTimeZoneOffset(): This method is used to applyTimeZone to the specific dateTime value.

Ex.If I have a date and time value in GMT like ‘6/20/2013’ 12:00:00 PM’ I have a time zone

‘‘UTC +5’ then after applying TimeZone like

dateTime = 6/20/2013 12:00:00 PM

DateTimeUtil::applyTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone())

will return 6/20/2013 5:00:00 PM

  •  DateTimeUtil::removeTimeZoneOffset(): This method is used to removeTimeZone to the specific dateTime value.

Ex.If I have a date and time value in GMT like ‘6/20/2013’ 12:00:00 PM’ I have a time zone

‘‘UTC +5’ then after removing TimeZone like

dateTime = 6/20/2013 12:00:00 PM

DateTimeUtil::removeTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone())

will return 6/20/2013 7:00:00 A.M

Screen1

Important about UtcDateTime table field.

 Based on above screen shot we need to understand one thing is whenever the utcDateTime value in inserted in the utcDateTime field of the buffer, offset is applied automatically.Similiary when the value is retrieved from the field of the buffer ,offset is removed automatically.

Like: (Time Zone is ‘UTC +5’)

Screen2

But since offset will be applied automatically on inserting in the table so the values in table are

Screen3

Please let me know for any queries.

Microsoft Dynamics AX 2012 DateTimeUtil and HcmDateTimeUtil functions in Ax 2012

Hi,

  • DateTimeUtil::getSystemDateTime(): Gets Date and Time value of the system in GMT

Ex:  if the system user has the TimeZone ‘UTC +5’  and the current date time is 6/20/2013 5:00PM then the method will return ‘6/20/2013 12:00PM’

  • DateTimeUtil::utcNow(): Gets the Date and Time value in GMT/UTC

Ex. if the system user has the TimeZone ‘UTC +5’  and the current date time is 6/20/2013 5:00PM then the method will return ‘6/20/2013 12:00PM’.(this is similar to getSystemDateTime() but it gets the date and time value of the server.

  • HcmDateTimeUti::startofCompanyDay(): This method takes an input parameter of utcDateTime, and returns the date and time value that represent the starting of the given date after applying the companyTimeZone.

 

Ex.if the value passed in the parameter is ‘6/20/2013 10:00 PM’ then it will return ‘6/20/2013 12:00:00 AM’ after applying the companyTimeZone

  • HcmDateTimeUti::startofCurrentDay(): This method returns the date and time value that represent the starting of the current  day after applying the userPreferredTimeZone.

 

Ex.if the value passed in the parameter is ‘6/20/2013 10:00 PM’ then it will return ‘6/20/2013 12:00:00 AM’ after applying the userPreferredTimeZone

  • DateTimeUtil::applyTimeZoneOffset(): This method is used to applyTimeZone to the specific dateTime value.

Ex.If I have a date and time value in GMT like ‘6/20/2013’ 12:00:00 PM’ I have a time zone

‘‘UTC +5’ then after applying TimeZone like

dateTime = 6/20/2013 12:00:00 PM

DateTimeUtil::applyTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone())

will return 6/20/2013 5:00:00 PM

  •  DateTimeUtil::removeTimeZoneOffset(): This method is used to removeTimeZone to the specific dateTime value.

Ex.If I have a date and time value in GMT like ‘6/20/2013’ 12:00:00 PM’ I have a time zone

‘‘UTC +5’ then after removing TimeZone like

dateTime = 6/20/2013 12:00:00 PM

DateTimeUtil::removeTimeZoneOffset(dateTime, DateTimeUtil::getUserPreferredTimeZone())

will return 6/20/2013 7:00:00 A.M

Screen1

Important about UtcDateTime table field.

 Based on above screen shot we need to understand one thing is whenever the utcDateTime value in inserted in the utcDateTime field of the buffer, offset is applied automatically.Similiary when the value is retrieved from the field of the buffer ,offset is removed automatically.

Like: (Time Zone is ‘UTC +5’)

Screen2

But since offset will be applied automatically on inserting in the table so the values in table are

Screen3

Please let me know for any queries.

AX 2012 introduced a new type of form control called “Replacement group”. It’s very handy, nevertheless quite a few developers still don’t know about it or are not sure how to use it effectively.

Let’s say that we want to create a form showing released products. We create a form with InventTableas the data source and with a grid.

We’re especially interested in the Product field, so we drag it from the data source and drop it to the grid.

Form-Product

Notice that the type of the control is ReferenceGroup – we’ll talk about it very soon.

Without changing anything, we can run the form; it successfully displays product numbers:

Let’s say that users insist on working with product names instead of product codes. Maybe they should have used a better convention for product codes, but that’s another topic. Now we have meet the request.

What should we do? Providing an edit method that would display names and find IDs from names specified by users? No, it’s much simpler with reference groups. Open properties of the reference group and change the value of ReplacementFieldGroup property from AutoIdentification toProductInformation:

ReplacementFieldGroup

Save the form and open it again – it now shows product names. It’s that simple!

ProductNames

If the product was editable (which is not normally the case in InventTable), you would also get a lookup and would be able to select (or type) product names:

LookupItemName

You could also override the lookup, if you don’t like the default one.

Now we should look more closely at how it works.

First of all, look at the Product field in InventTable. Its base type is Int64 and extended data type isEcoResProductRecId. Properties of the EDT shows that ReferenceTable = EcoResProduct, therefore the Product field contains record IDs of EcoResProduct table.

If we used the Int64Edit control to display the Product field, we would see just numbers, which wouldn’t be very useful.

RecIds

That’s why we have Reference group controls. The reference group in our form is bound to the Productfield (set in ReferenceField property), so that’s what gets saved to database. But we don’t display it to users – instead of that, we use a replacement field group. Field groups are defined on tables in AOT – here we can see the groups of InventTable that we used in our example:

InventTable-fieldGroups

AX looks at the field (or even fields) contained in the field group and displays them instead of the record ID. If you change a value of the replacement field, AX finds the corresponding RecId and put it to the underlying field.

As you can see, reference groups allow you to change fields displayed to users by a simple property change. Also notice that it has zero effect to data stored in database – it still refers to a record ID.

There is one last thing I would like to mention, because it often confuses people. You may have a reference group for a worker showing the worker name (e.g. the Sales responsible person in Sales order form). The control uses the AutoIdentification field group of the HcmWorker table. The group contains a single field, Person, which is a RecId of DirPerson table. AutoIdentification group onDirPerson is empty, so where the name comes from? The trick is that DirPerson table inherits fromDirPartyTable, which contains the Name field in its AutoIdentification group. AX deals with all this complexity for you.

Microsoft Dynamics AX 2012: How to get Company,Customer and Vendor address in AX 2012

Scenario:  “How to get Addresses ofCustomer, Vendor and Company

1)      First we need to identify which table store address of each entity

Table : LogisticsPostalAddress  : In Dynamics AX 2012 this is the main table which stores every address of each entity(customer/vendor…).This table contains fields like city,country,state,zipCode etc. required for the address.

a)      Customer/Vendor  address

Assume that we have the customer with CustAccount = 1103 and has the address and infact which is the primary/main address.(below)

Img1

Now if we want to get this specific customer address we can use the following code.

Img2

Simliar for the Vendor

Img3

Use following code:

Img4

a)      Company Address

For the Company address we can use the CompnayInfo table to get first the company and then find its address in LogisticsPostalAddress table.

We can find the LogisticsPostalAddress of the current company by Location .

This line give the location of the current company

Now get the logisticsPostalAddress reference we can use and find the address

So,

Img5

Img6

Caching techniques in AX / Cache lookup

Cache Location

Caches are used on both the client and the server. The Microsoft Dynamics AX runtime manages the cache by removing old records when new records are added to the cache.

Client Cache

A client-side cache can be used only by the client. The client cache is used when a select is executed from the client tier. If no record is found in the client cache, the client then searches the server cache for the record. If the record isn’t located in the server cache, it’s retrieved from the database. The maximum number of records maintained in a client cache is 100 records per table for a given company.

Server Cache

A server-side cache can be used by any connection to the server. The server cache is used when a select is executed on the server tier. If no record is found in the cache, it’s retrieved from the database. The maximum number of records maintained in a server cache is 2,000 records per table for a given company.

Record Caching

Microsoft Dynamics AX database record caching is a performance-enhancing feature that helps avoid database access when it’s not strictly necessary. Retrieving database records from memory instead of the database significantly speeds up data access. Caching can reduce the performance penalty for repetitively accessing the same database records.

Types of Caching

Caching is transparent to the application; however, it’s important to know how caching works to optimize its performance in Microsoft Dynamics AX. Following are the types of caching:

Single-record
Set-based
Single-record caching has the following characteristics:

Defined at design time
Moves records to the cache based on the table’s CacheLookup property and the type of SELECT statement that is used to retrieve the record

Set-based caching has the following characteristics:

Defined either at design time or in X++ code
Moves sets of records to the cache
Implemented either through the table’s CacheLookup property or in code by using the RecordViewCache class
Single-Record Caching

Record caching is enabled for a table when all the following statements are true:

The CacheLookup property on the table is enabled by setting it to one of the following values:
· notInTTS
· Found
· FoundAndEmpty

The table’s PrimaryIndex property is set to a unique index that exists on the table. The RecId index does not qualify as a caching index unless you set the table’s PrimaryIndex property to this index.
The record buffer disableCache method has not been called with a parameter of true.
The fields in the table’s unique index make up the caching key. A record is placed in the cache when the following criteria are met:

The table is cached by setting the CacheLookup property to notInTTS, Found, or FoundAndEmpty.
The SELECT statement that selects the records uses an equal operator (==) on the caching key. The fields in the WHERE clause of the SELECT statement match the fields in the index referenced by the table’s PrimaryIndex property.
The table’s CacheLookup property defines how and when records are cached as shown in the following table.

CacheLookup Property Value
Result
None
No data is cached or retrieved from the cache for this table.
This property value should be used for tables that are heavily updated or where it’s unacceptable to read outdated data.
NotInTTS
All successful caching key selects are cached.
When in a transaction (after ttsBegin), no caches made outside the transaction are used. When inside a transaction, the record is read once from database and subsequently from cache. The record is select-locked when read in a transaction, which ensures that the record cached is not updated while the transaction is active.
A typical example of the NotInTTS property is the CustTable in the Microsoft Dynamics AX standard application. It’s acceptable to read outdated data from the cache outside a transaction, but when data is used for validation or creating references, it is ensured that the data is real-time.
Found
All successful caching key selects are cached. All caching key selects are returned from the cache if the record exists there. A selectforUpdate in a transaction forces reading from the database and replaces the record in the cache.
This is typically used for static (lookup) tables, such as Unit, where the record usually exists.
FoundAndEmpty
All selects on caching keys are cached, including selects that are not returning data.
All caching key selects are returned from caching if the record exists there, or the record is marked as nonexistent in the cache. A selectforUpdate in a transaction forces reading from the database and replaces the record in the cache.
An example of FoundAndEmpty record caching is in the Discount table in the Microsoft Dynamics AX standard application. By default, the Discount table has no records. By using a FoundAndEmpty cache on this table, the keys that are queried for but not found are stored in the cache. Subsequent queries for these same non-existent records can be answered from the cache without a round trip to the database.
EntireTable
Creates a set-based cache on the server. The entire table is cached as soon as at least one record is selected from the table.

The Found and FoundAndEmpty caches cross transaction boundaries. The NotInTTS cache is newly created inside a transaction. This example, modified for the purposes of this topic, demonstrates how records are retrieved from the cache when the table’s CacheLookup property is set to NotInTTS, and the PrimaryIndex property is set to a unique index on the AccountNum field.

static void NotInTTSCache(Args _args)
{
CustTable custTable;
;
// The query looks for records in the cache.
// If records don’t exist, the query accesses the database.
select custTable
where custTable.AccountNum == ‘4000’;
// The transaction starts.
ttsbegin;
// The cache is not used. The query accesses the database
// and records are placed in the cache.
select custTable
where custTable.AccountNum == ‘4000’;

// The query uses the database because
// the forupdate keyword is used.
select forupdate custTable
where custTable.AccountNum == ‘4000’;
// The query uses the cache and not the database.
select custTable
where custTable.AccountNum == ‘4000’;
// The query uses the cache because
// the forupdate keyword was used previously.
select forupdate custTable
where custTable.AccountNum == ‘4000’;

// The transaction is committed.
ttscommit;

// The query will use the cache.
select custTable
where custTable.AccountNum == ‘4000’;
}
If the table CacheLookup property was set to Found or FoundAndEmpty, the first select statement inside the transaction (after the TTSBegin statement) would retrieve the record from the cache.

Set-Based Caching

In Microsoft Dynamics AX, groups of records can be cached all at once with set-based caching. Set-based caching can be implemented in two ways:

At design time, by setting the table’s CacheLookup property to EntireTable.
In code, by using the RecordViewCache class.

EntireTable Cache
*
When you set a table’s CacheLookup property to EntireTable, all the records in the table are placed in the cache after the first select. This type of caching follows the rules of single record caching in which the SELECT statement WHERE clause fields must match those of the unique index defined in the table’s PrimaryIndex property.

The EntireTable cache is located on the server and is shared by all connections to the Application Object Server (AOS). If a select is made on the client tier to a table that is EntireTable cached, it first looks in its own cache and then searches the server-side EntireTable cache. An EntireTable cache is created for each table for a given company. If you have two selects on the same table for different companies the entire table is cached twice.

Joins that include an EntireTable cached table are only performed against the cached copy when all tables participating in the join are EntireTable cached. Otherwise a database join is performed.

Important Note:

Avoid using EntireTable caches for large tables because once the cache size reaches 128 KB the cache is moved from memory to disk. A disk search is much slower than an in-memory search.

Flushing the Cache

An EntireTable cache is flushed whenever an insert, update, or delete is made to the table. At the same time, the AOS notifies other AOSs that their caches of the same table must be flushed. After the cache is flushed, a subsequent select on the table causes the entire table to be cached again. Therefore, avoid caching any table that’s frequently updated. Regardless of when updates are made, EntireTable caches are flushed every 24 hours by the AOS.

RecordViewCache Cache
*
Set-based caching is implemented in code by using the RecordViewCache class. You must first create a record buffer using the nofetch statement and then pass the record buffer to the RecordViewCache class when it’s instantiated.

The cache is created on the server and is only accessible by the process that creates the cache object. Once the cache is instantiated, all select statements are issued against the cache, as shown in the following

static void RecordViewCache(Args _args)
{
VendTrans    vendTrans;
RecordViewCache recordViewCache;
;
// Define records to cache.
select nofetch vendTrans
where vendTrans.AccountNum == ‘4000’;

// Cache the records.
recordViewCache = new RecordViewCache(vendTrans);

// Use cache.
select firstonly vendTrans
where vendTrans.AccountNum == ‘4000’ &&
vendTrans.CurrencyCode == ‘USD’;
}

Due to concurrency issues, the forUpdate keyword on the instantiating X++ SELECT statement should only be used when all of the records in the result set will be updated. Otherwise it’s a better strategy to use select forUpdate only for the records that are to be updated.

The RecordViewCache class is used in a select when the select is from a table that’s cached, the select statement doesn’t participate in a join and the select WHERE clause matches the WHERE clause with which the RecordViewCache was instantiated.

The cache created by the RecordViewCache class stores records in a linked list. Therefore Microsoft Dynamics AX searches the cache sequentially for records that match the search criteria. If the SELECT statement contains an ORDER BY clause, a temporary index is placed on the cache and the runtime uses the index when searching records.

Defaulting Financial Dimensions

Suppose you have a requirement wherein you need to create a customer via code and default specific dimension (say Employee) to this record.

In AX 2012 dimensions are not directly attached but the combination Record Id is stored. The name generally is DefaultDimension.

This field points to a record in DimensionAttributeValueSet table. This table holds the combination of financial dimensions that a particular record is attached to. The combination is stored in DimensionAttributeValueSetItem table.

The job below will help you in defaulting a dimension: I have put in enough comments to make the job self explanatory. This job will help you find / create a dimension combination record and get the record id to set.

static void setDefaultFinancialDimension(Args _args)

{

#LedgerSHA1Hash

DimensionSHA1Hash               hash; //To store the calculated hash for DimensionAttributeValueSet

HashKey                         valueKeyHashArray[]; //To store the has key of dimension in question

Map                             dimAttrIdx; //to store the dimension index and backing entity type

DimensionAttributeSetItem       dimAttrSetItem; // Contains the number of dimensions active for a account structure ledger

DimensionAttribute              dimAttr; // Contains the financial dimensions records

DimensionAttributeValue         dimAttrValue; // Contains used financial dimension values

DimensionAttributeValueSet      dimAttrValueSet; //Contains default dimension records

DimensionAttributeValueSetItem  dimAttrValueSetItem; //Contains individual records for default dimensions

DimAttributeHcmWorker           dimAttrWorker; //Backing entity view for Employee type dimension

DimensionEnumeration            dimensionSetId; //Record id for table that contains active dimensions for current ledger

int dimAttrCount, i;

int emplBackEntityType; //Stores the backing entity type for Employee type dimension

;

//The employee backing entity will be the view DimAttributeHcmWorker

emplBackEntityType = tableNum(DimAttributeHcmWorker);

//Initialize the map to store the backing entity types

dimAttrIdx = new Map(Types::Integer, Types::Integer);

//Get the record Id (dimension set id) for current ledger to find active dimensions

dimensionSetId = DimensionCache::getDimensionAttributeSetForLedger();

//Find all the active dimensions for current ledger except main account and store there

//backing entity type in the map

while select * from dimAttr

order by Name

where dimAttr.Type != DimensionAttributeType::MainAccount

join RecId from dimAttrSetItem

where dimAttrSetItem.DimensionAttribute == dimAttr.RecId &&

dimAttrSetItem.DimensionAttributeSet == dimensionSetId

{

dimAttrCount++;

dimAttrIdx.insert(dimAttr.BackingEntityType, dimAttrCount);

}

//initialize hash key array to null

for (i = 1; i<= dimAttrCount; i++)

valueKeyHashArray[i] = emptyGuid();

//Find the Dimension attribute record for the dimension to work on

dimAttr.clear();

select firstonly dimAttr

where dimAttr.BackingEntityType == emplBackEntityType;

//Get the backing entity type for the dimension value to process

select firstOnly dimAttrWorker

where dimAttrWorker.Value == ‘000038’;

//Find the required Dimension Attribute Value record

//Create if necessary

dimAttrValue = DimensionAttributeValue::findByDimensionAttributeAndEntityInst(dimAttr.RecId, dimAttrWorker.RecId, false, true);

//Store the required combination hash keys

valueKeyHashArray[dimAttrIdx.lookup(emplBackEntityType)] = dimAttrValue.HashKey;

//Calculate the hash for the current values

hash = DimensionAttributeValueSetStorage::getHashFromArray(valueKeyHashArray, dimAttrCount);

//Null hash indicates no values exist, which may occur if the user entered an invalid value for one dimension attribute

if (hash == conNull())

{

throw error(“Wrong value for Employee Dimension”);

}

// Search for existing value set

dimAttrValueSet = DimensionAttributeValueSet::findByHash(hash);

// This value set does not exist, so it must be persisted

if (!dimAttrValueSet)

{

ttsbegin;

// Insert the value set with appropriate hash

dimAttrValueSet.Hash = hash;

dimAttrValueSet.insert();

/*

         * This Piece of code is only meant for better understanding hence commented

         * Use this code in case you have to handle more than one dimension

         * For our example we have only employee type dimension hence we will not use this for loop

         * Value key array would be the array of different dimension values

         */

// Insert only specified set items use this

/*for (i = 1; i <= dimAttrCount; i++)

        {

            if (valueKeyArray[i] != 0)

            {

                dimAttrValueSetItem.clear();

                dimAttrValueSetItem.DimensionAttributeValueSet = valueSet.RecId;

                dimAttrValueSetItem.DimensionAttributeValue = valueKeyArray[i];

                dimAttrValueSetItem.DisplayValue = valueStrArray[i];

                dimAttrValueSetItem.insert();

            }

        }*/

//Insert Employee dimension set item

dimAttrValueSetItem.clear();

dimAttrValueSetItem.DimensionAttributeValueSet = dimAttrValueSet.RecId;

dimAttrValueSetItem.DimensionAttributeValue = dimAttrValue.RecId;

dimAttrValueSetItem.DisplayValue = dimAttrWorker.Value;

dimAttrValueSetItem.insert();

ttscommit;

}

info(strFmt(“%1”, dimAttrValueSet.RecId));

}

Getting Individual Dimension Combination Values–Dimension Storage class

In this post, I will be explaining the method to get individual values for each dimension combination that is created and stored.

Dimension combinations are stored are DimensionAttributeValueCombination class. But they are stored as a combination ex: (100010-AX-00001- – – -). How would you know the value in each combination belongs to what dimension?

The answer is through dimension storage class. This class is used to manipulate these combinations.

The job below helps you in finding out the required values. The job has lots of self explanatory comments.

static void getDimensionCombinationValues(Args _args)

{

// DimensionAttributeValueCombination stores the combinations of dimension values

// Any tables that uses dimension  combinations for main account and dimensions

// Has a reference to this table’s recid

DimensionAttributeValueCombination  dimAttrValueComb;

//GeneralJournalAccountEntry is one such tables that refrences DimensionAttributeValueCombination

GeneralJournalAccountEntry          gjAccEntry;

// Class Dimension storage is used to store and manipulate the values of combination

DimensionStorage        dimensionStorage;

// Class DimensionStorageSegment will get specfic segments based on hierarchies

DimensionStorageSegment segment;

int                     segmentCount, segmentIndex;

int                     hierarchyCount, hierarchyIndex;

str                     segmentName, segmentDescription;

SysDim                  segmentValue;

;

//Get one record for demo purpose

gjAccEntry = GeneralJournalAccountEntry::find(56375659223);

setPrefix(“Dimension values fetching”);

//Fetch the Value combination record

dimAttrValueComb = DimensionAttributeValueCombination::find(gjAccEntry.LedgerDimension);

setPrefix(“Breakup for ” + dimAttrValueComb.DisplayValue);

// Get dimension storage

dimensionStorage = DimensionStorage::findById(gjAccEntry.LedgerDimension);

if (dimensionStorage == null)

{

throw error(“@SYS83964”);

}

// Get hierarchy count

hierarchyCount = dimensionStorage.hierarchyCount();

//Loop through hierarchies to get individual segments

for(hierarchyIndex = 1; hierarchyIndex <= hierarchyCount; hierarchyIndex++)

{

setPrefix(strFmt(“Hierarchy: %1”, DimensionHierarchy::find(dimensionStorage.getHierarchyId(hierarchyIndex)).Name));

//Get segment count for hierarchy

segmentCount = dimensionStorage.segmentCountForHierarchy(hierarchyIndex);

//Loop through segments and display required values

for (segmentIndex = 1; segmentIndex <= segmentCount; segmentIndex++)

{

// Get segment

segment = dimensionStorage.getSegmentForHierarchy(hierarchyIndex, segmentIndex);

// Get the segment information

if (segment.parmDimensionAttributeValueId() != 0)

{

// Get segment name

segmentName = DimensionAttribute::find(DimensionAttributeValue::find(segment.parmDimensionAttributeValueId()).DimensionAttribute).Name;

//Get segment value (id of the dimension)

segmentValue        = segment.parmDisplayValue();

//Get segment value name (Description for dimension)

segmentDescription  = segment.getName();

info(strFmt(“%1: %2, %3”, segmentName, segmentValue, segmentDescription));

}

}

}

}

Here is a sample output after running the code:

Note: Hiearchies: CEEBD_Dept-CostCenter-Purpose and CorpShared_Dept-CostCenter-Purpose are child hierarchies of “Account structure”.

Close Methods on form level in ax 2012

Dear Friends,

we have some methods regarding how to Closing the form in ax 2012.

Here  I will give you the details of the methods ..

1. Close – close the form
2. CloseOK – close the form, and set the OK flag – called by the commandbutton: Ok
3. CloseCancel – close the form, and set the Cancel flag – called by the commandbutton: Cancel
4. CloseSelectRecord – close the lookup form, and set return record
5. CloseSelect – close the lookup form, and set return value

The methods in past-tense are used to determine if or how a form was closed:
6. Closed – The form is no longer open
7. ClosedOK – The form was closed by the user clicking ‘OK’
8. ClosedCancel – The form was closed by the user clicking ‘Cancel’

And canClose() is called before any of the close methods get called.