Friday, 20 March 2015

Layer Configuration in AX 2012

Changing Layer in Dynamics AX 2012:


1.      Click Start, point to Administrative Tools, and then click Microsoft Dynamics AX 2012 Configuration. The Microsoft Dynamics AX Configuration Utility opens.





In the Microsoft Dynamics AX Configuration Utility, click Manage, and then click Create configuration. The Create Configuration window opens
In the Configuration name field, enter a name for the configuration, and then click OK. The Create Configuration window closes
On the Developer tab, in the Application object layer to open field, select a new layer from the drop-down list.
On the Developer tab, in the Application object layer to open field, select a new layer from the drop-down list.


In the Development license code field type the license code and  confirm license field also give the same




Click Apply and  Click OK to save the configuration. The Microsoft Dynamics AX Configuration Utility window closes.
Close and restart the client in the new layer.

Share Data to many Companies in Ax 2012

This article describes how Dynamics Ax Virtual Company and Table Collection work. We will also discuss how to move data from normal company to virtual company when you introduce virtual company in existing Ax implementation. 

Virtual Company:
Dynamics Ax stores data as per company in tables. But there might be occasions when you want to share data across companies, like country, state, zip codes data. This sharing of data is achieved by creating a virtual company and storing data in this virtual company. Normal companies are then configured to read/write data from this virtual company. The only purpose of virtual company is to share data across companies, you cannot log into this virtual company.

Before seeing how to do virtual company setup, I would like you to show another trick that can be used to share data across Ax. There is a property on Ax tables called "SaveDataPerCompany", you can use this property to save data globally in Ax. To share data set this property to "No".





Note: This data is shared by all the companies in Ax. This option will delete DataAreaId field and default (DataAreaId, RecId) index from the table. If you want more control on shared data, like which companies can share and which can not then use virtual company. 

Virtual Company setup:



Step 1: Create Table Collection

Decide which tables you want to share and create a table collection for these functionally related tables. For example; if you want to share Global Address Book across companies then you can utilize the existing table collection "DirPartyCollection".


To create a table collection, go to AOT\Data Dictionary\Table Collections and on right click select "New Table Collection", then just drag your required tables in this collection.

Step 2: Create Virtual Company, configure/attach normal companies and table collection


Create a virtual company that will hold the shared data for normal companies.
Note: Before doing the below steps, make sure you are the Ax administrator and the only user online.
  1. Go to Administration -- Setup -- Virtual company accounts, and create a virtual company.

  2. Decide which companies needs to share data and attach those normal companies with this virtual company.

  3. Attach the table collection with this virtual company.



Your Ax client will re-start and you are done with setting up the virtual company account.

Now, when you have virtual company in place, all new data will be saved in this virtual company. Only companies attached to the virtual company can use this shared data. All other companies which are not attached will work normally, these companies will continue to read/write data as per company bases.

How to move existing data to virtual company?
When you setup a new virtual company, Ax does not move data automatically from normal company to virtual company. This is done by system administrator manually.

There are many ways to do this data move, but I will discuss only two approaches here.

Ax Import / Export:


This is standard Ax approach.

  1. Manually export existing normal company data from Ax.
  2. Remove duplicate records from this exported data set. 
  3. Delete exported data from normal companies.
  4. Import the exported data back in Ax, while logged into one of the participating companies. 
  5. Create records deleted in point 2 again in Ax using your logic. How you want to handle duplicate? For example, if you have customer 'Rah' in more than one normal company, what you want to do with this?
Direct SQL:

Use this approach if you have good knowledge about SQL queries and Ax table structures/relationships. Below are few points that will help you understand what to do and how to do.

  • All Ax tables store data as per company unless otherwise specified. For this, Ax uses a special field called DataAreaId. In case of virtual company, it does not matter from which normal company you log-in, it is always the virtual company id which is stored in DataAreaId field of shared tables. 
  • Ax also assigns a unique 64bit number to each record in table. For this, Ax uses a special field called RecId. This RecId is unique in the table and is generated by Ax when you insert a new record in Ax. It is not related to DataAreaId / Company.
  • For unique records between all participating normal companies, update the DataAreaId to the virtual company id.
  • For duplicate records, create them again in Ax using some Ax job or Ax import/export technique.

Attached below are some SQL queries that might help you moving the Global Address Book records from normal companies to the virtual company by changing the DataAreaId.

Use of Super() in Ax 2012

Basically we can use super() in terms of inheritance.It is nothing just a parent call from a child method.Lets take couple of examples

Example 1:

Use of super() in Form’s method.
I have a form called ‘JournalizingDefinition’ ,contains many methods  under a Methods node.







When click on method node we will see
















So lets view the method called ‘init’(when form initializes ,this first method that is called is init method) 




So there is super method, so the reason why super() is here,is that on creation of an instance of the any form, this super() method called the parent kernel method ‘loadUserSetting’ method of the SysSetupFormRun class,which creates and loads the form which is necessary otherwise form wont be initalize.Try to comment this super() method and open the form…

Example 2:
Use of super() in Form’s datasource method JournnalizingDefinition form has multiple datasources(tables) attach to it.
















So lets focus on ‘JournalizingDefinition; datasource and click the methods notes you will see the above methods.
Notice there is a ‘validateWrite’ ,method in which a super() is called



Now navigate to the tables node and search for ‘JournalizingDefinition’ and click method nodes












You will see the same method ‘validateWrite as well.But this is the parent method.

Now go to Form’s dataSource JournalizingDefinition open the validateWrite method and select the super() method and insert breakpoint.



Now open the form and insert the record and save button.I will open the debug mode and super() method is higlighted



Now press F11.Now it will take you the parent method validateWrite of the table journalizingDefinition,
Below is the parent method under ‘JournalizingDefinition’ method

public boolean validateWrite()
{
    boolean ret;

    ret = super();

    return ret;
}

Table Methods in Ax 2012

Methods are used for adding X++ code to your application. The code in methods is also referred to as business logic. Whenever records are changed, inserted or deleted from a table various default methods are executed.
We can change the default methods and by doing so we are overriding the default methods.To override a method go to the Methods node of a table, right click and choose Override Method. Below are few examples of Overriding commonly used Table methods:


initValue():

If we create a new record from the table browser or a form the table method initValue() is executed. It is used to set a default value for the fields
Example (1): Let’s override intiValue for MyFirstTable and set default value for custGroupId

public void initValue()
{
super();
this.custGroupId = "10";
}

After adding this method, open table MyFirstTable  through Table browser and press ctrl+n to create a new record. The field custGroupId will now have the default value 10.


modifiedField():

Each time the value of a field is changed the method modifiedField() is called. It is useful to initialize the values of other fields if the value of the current field is changed.


Example (2): Let’s now override modifiedField method for MyFirstTable and target is to set CurrencyCode to null when CustGroupId is modified.

public void modifiedField(fieldId _fieldId)
{
switch(_fieldId)
{
case fieldnum(MyFirstTable, custGroupId):
    this.CurrencyCode="";
    break;
default:
    super(_fieldId);
}
}
After adding this method, open table MyFirstTable using Table browser and try to modify custGroupId of an existing record, then you will notice that CurrencyCode is immediately set to blank.


ModifiedField() receives the field number of the active field as parameter. A switch statement is used to check which field is active. If none of the checked fields are active the super() call is executed instead.


orig():

A nice feature in Dynamics Ax is when a field value is modified, it is possible to re-call the value before the field was modified. This is made possible using orig() method. The field values can  retain their last committed value. The method orig() is used to get the stored value. Orig() will return an instance of the current table.
A single field value from orig() is retained by specifying the field. And When the record is committed orig() will be updated.

Syntax: print this.orig().custCurrencyCode;


validateField():

Method validateField() is used for validation only and will return true or false. If the return value is false, the application user will be prevented to continue changing a field value.


Example (3): Let’s override validateField for MyFirstTable to verify the condition that CustName must be have >3 characters.

public boolean validateField(fieldId _fieldIdToCheck)
{
    boolean ret;
    ret = super(_fieldIdToCheck);
    if (ret)
    {
    switch (_fieldIdToCheck)
    {
    case fieldnum(MyFirstTable, custName):
        if (strlen(this.custName) <= 3)
        ret = checkFailed("Customer name must be longer than 3 characters.");
    }
    }
    return ret;
}

After adding this method, open table MyFirstTable using Table browser and press Ctrl+N, in the new record try to enter less than 3 characters for field custName, Ax will throw warning message stating “Customer name must be longer than 3 characters.” And you will be asked to enter value again. Thus we validate the data to be entered for a specific field.


validateWrite():

Method validateWrite() will just check mandatory fields and is triggered when the record . Checks made by validateWrite() are the same as the super() call in validateField().So if your condition is not related to the value an application user enters in a specific field, you should put the validation in validateWrite().


validateDelete():

When deleting a record the method validateDelete() is first executed. If true, the method delete() will be called.


insert() and update():

Insert() and update() are rarely overridden. However, if you need to ensure a field has a certain value upon inserting a record, you can initialize your field before calling super() in insert().  Some special cases might also require overriding these methods; for example, if you need to synchronize the content of a saved record to another table.


Using X++ for entering data requires a bit more than using the user interface like forms. Only the table methods called will be executed.


Example (4): In this example, let’s see how to use the table methods to insert a record in MyFirstTable.

static void DataDic_InsertRecord(Args _args)
{
MyFirstTable myFirstTable;
;
ttsbegin;
myFirstTable.initValue();
myFirstTable.accountNum = "100";
myFirstTable.custName = "Alt. customer id 100";
myFirstTable.CurrencyCode = "USD";
if (myFirstTable.validateWrite())
myFirstTable.insert();
ttscommit;
}

InitValue() is called and will set the value of the field custGroupId. The record will only be inserted if validateWrite() is true. As all mandatory fields have a value, the record will be inserted.


Instead of calling insert() you could call write(). This will update an existing record, but if the record does not exist, a new record will be inserted.


The select keywords delete_from, insert_recordset and update_recordset make only one call to the database from the client when processing multiple records.

ValidateDelete():

While deleting a record if we want to put any validation we can use this method. Here once I delete a record populating a info that deleted record.

public boolean validateDelete()
{
boolean ret;
ret = super();
info(this.AccountNum);
return ret;
}


ValidateWrite():

This method will get to fire when we update a record. here I am using to check mandatory field for address AccountNum

public boolean validateWrite()
{
boolean ret;
;
if(this.Address != "")
ret = super();
else
warning(" Please fill the address value");
return ret;
}

find() :-

All tables should have at least one find method that selects and returns one record
from the table that matches the unique index specified by the input parameters.
The last input parameter in a find method should be a Boolean variable called
'forupdate' or 'update' that is defaulted to false. When it is set to true, the caller object
can update the record that is returned by the find method.
See the next example from the InventTable:

static InventTable find(ItemId itemId, boolean update = false)
{
InventTable inventTable;
;
inventTable.selectForUpdate(update);
if (itemId)
{
select firstonly inventTable
index hint ItemIdx
where inventTable.ItemId == itemId;
}
return inventTable;
}

exists() :-

As with the find method, there should also exist an exists method.
It basically works the same as the find method, except that it just returns true if a
record with the unique index specified by the input parameter(s) is found.
In the next example from the InventTable you can see that it returns true if the
input parameter has a value AND the select statement returns a value.

static boolean exist(ItemId itemId)
{
return itemId && (select RecId from inventTable
index hint ItemIdx
where inventTable.ItemId == itemId
).RecId != 0;
}

Display Method:

         Indicates that the methods return value is to be displayed on a forms (or) Reports .The value cannot be altered in the form or report

Take the new method in a table, and then drag that method into the grid and set data source properties. In that field is non-editable.

We can create display method on the
1. Table methods
2. Form methods
3. Form data source methods
4. Report methods
5. Report design methods

Display Name names ()
{
    CustTable   custTable;
    ;
    return  CustTable::find(this.CustAccount).Name;
}

Edit Method:

        Indicates that the methods return type is to be use to provide information for a field that is used in a form only

 We can create edit method on the

1. Table methods
2. Form methods
3. Form datasoruce methods

        Take the new method in the table, and then drag that method into the grid and set data source properties. In that field is user can edit it and accept the values to the user and save it CustTable.

 Edit Name name(boolean _set , Name _name)
{
    Name    name    = _name;
    CustTable   custTable;
    ;
    if(_set)
    {
        if(name)
        {
            ttsbegin;
            custTable   = CustTable::find(this.CustAccount,true);
            custTable.Name  = name;
            custTable.update();
            ttscommit;
        }
    }
    else
    {
        name    = CustTable::find(this.CustAccount).Name;
    }
    return name;
}


ModifiedFieldValue() : 

this method takes field name and array index as arguments

public void modifiedFieldValue(FieldName _fieldName, int _arrayIndex = 1)
{
    super(_fieldName, _arrayIndex);

    if(_fieldName == fieldStr(HD_BankCustomersTable, AccountType))
    {
        switch(this.AccountType)
        {
            case HD_AccountType::Current:
                this.MinBalance = 5000;
                break;

            case HD_AccountType::Fixed:
                this.MinBalance = 10000;
                break;

            case HD_AccountType::Recurring:
                this.MinBalance = 500;
                break;

            case HD_AccountType::Savings:
                this.MinBalance = 1000;
                break;
        }
    }

}

ValidateFieldValue():

public boolean validateFieldValue(FieldName _fieldName, int _arrayIndex = 1)
{
    boolean ret;

    ret = super(_fieldName, _arrayIndex);

    if(_fieldName == fieldStr(HD_BankCustomersTable, DOAC))
    {
        //info("");
    }

    return ret;
}