Tuesday, 18 April 2017

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;
}

Wednesday, 15 February 2017

How to change SID for Ax User


1.       Find SID for current user and update in Databse user info table
a.       To find SID of user
                                       i.            Open command prompt
                                     ii.            Run as administrator
                                   iii.            wmic useraccount get name,sid
 
b.       get SID of current user and update SID of administrator in user info table
c.       Also update Domain name.
 
 
2.       Go to Administrative tools > Microsoft Dynamics AX server Configuration Utility
3.       Select Database connection tab > Change Database name to partners data DB
4.       Restart AOS
 
 

Models and Model strore in Microsoft Dynamics AX


Model were introduced in Microsoft dynamics AX 2012 to help partner and customers more easily install and maintain multiple solutions side by side in the same layers.

Model Store, is a database in which all the application elements for Microsoft dynamics ax are stored.

Models, is a set of elements in a given layer. Each layer consists of one or more models. Each layer contains one system-generated model that is specific to that layer. Every element in a layer belongs to only one model. In other words, no element can belong to two models in the same layer, and every element must belong to a model.

A default model owned by Microsoft exists in each layer. Default models cannot be modified.

Models are stored in the model store. The model store is a database in which all application elements for Microsoft dynamics ax are stored. Customizations are also stored in the model store. The model store replaces the Application Object Data (AOD) files that were used in earlier versions of Microsoft dynamics ax. Models that have been installed in the model store are used at run time.

The following table describes the scenarios in which Microsoft recommend you use each installation method.

Scenario
Recommended installation method
Distributing a solution to customers
Model files
Deploying a solution in a development or test environment
Model files or XPO files
Deploying a solution to a production environment
Model store files

Wednesday, 18 January 2017

Cache Lookup Property of table in ax 2012

Cache Lookup - Ax 2012

Caches are used on both the client and the server. It increases the performance, ax will get data from cache instead of doing round trips and DB calls. So for each table it's good to use cache lookup property. 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 statement is executed from the client tier. If no records is found in the client cache, the client then searches in the server cache for the records. If the record isn't located in the server cache, it will retrieved from the database. The maximum number of records can be maintained in a client cache is 100 records per table for selected 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 found in the server cache, it will retrieve from the database. The maximum number of records maintained in a server cache is 2000 records for selected company.

Types of Cache Lookup
  • None
  • EntireTable
  • Found
  • NotInTTS
  • FoundAndEmpty
None
No data is cahce or retrieved form 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.

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. An EntireTable cahce is flushed whenever an insert, update or delete is made to the table. So first select read all records from DB for selected company and all the further selects will take data from cache instead of DB call.

Below is a list which shows to use different type of cache lookup property as per table group.

Table GroupCache Lookup
Miscellaneous* See notes below
 Parameter EntireTable
 Group Found
 Main Found
 Transaction NotInTTS
 WorksheetHeader NotInTTS
 WorksheetLine NotInTTS
 Framework N/A
 Reference Found
 Worksheet NotInTTS
 TransactionHeader NotInTTS
 TransactionLine NotInTTS


Found
All successful caching key selects are cached. All caching key selects are returned from the cache if the record exists there. A select forUpdate 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.

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 the 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 on the CustTable in the Microsoft Dynamics AX application. It is 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.

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 select forUpdate 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. 

Friday, 15 July 2016

Create MorphX Report in Ax

We're already aware of MorphX environment of Microsoft Dynamics Ax. The MorphX reports are very easy to delevelop than SSRS reports. SSRS reports are developed in visula studio while MorphX report are developed inside Microsoft Dynamics Ax (MorphX Environment) that's why these reports are named as MorphX reports or sometimes Ax Reports.

Here I would tell you step by step development of a demo MorphX report.
This example is to pick all the sales order records related to one customer. If you want to print all customer's records than don't select any parameter.
  • First of all create a new report and add it to your project.

  • Add CustTable to Report datasource
  • Now expend CustTable datasource and add SalesTable to inside CustTable datasource.
  • In salesTable datasource create a relation as shown below that will only pick those records where customer account in CustTable is equal to customer account in SalesTable. 
                                      
  • Right click on Report Design and select Generate Design. By this CustTable and SalesTable section would be automatically created in your report's design. 

  • Inside 'Section Group : CustTable', right click on 'Body: CustTable_body', select new control 'Field form CustTable' and add AccountNum and Name fields to body.

  • Similarly. add SaleTable Fields whichever you want to Body:SalesTable_body. I have added Salesid, QuotationId, CreatedDateTime, SalesSatatus. deliveryAddress and CurrencyCode.

  • Now open your report and select any customer

  • After following all the above steps successfully your report output would be as shown below.


Note: I didn't focus on report's design much so this report looks very simple. You can customize this report according to your requirement, you can also make changes for individual control's properties such as size, caption, width-height, margins, position, label etc. After changing control properties you can make your report more attractive.
If you want to use table's display methods in your report than write that display method name in data method property.

In case you have any doubt do comment below. Thank you so much. 


Thursday, 19 May 2016

Create New Table On New Record Creation Of Another Table

This post will help you if you have requirement to create a new record every time when a new record is created in another table.

Let me explain this with an example:
Assume that you have a master table 'Bank_Account' which contains a field 'AccountNumber'. Now the requirement is  when a new Account Number is added (created) related to this Account Number a new table should be created as 'Transaction_####'. The name of the Transaction_#### would be dynamic.
If the Account Number is 1001 then Transaction_#### name should be Transaction_1001.

Follow the below steps:

  • Create a table Bank_Account
    • Add a field Account_Number (String)
    • Set properties Mandatory : Yes , Allow Edit : No
    • Make Account_Number as a Primary Key 
      • PK : Go to Index node > New Index

    • Field AccountNumber has now become primary key.

  • Override modified field method in table Bank_Account and write following code.
 public void modifiedField(FieldId _fieldId)
{
    SysDictTable sysdictTable;
    Treenode treenode;// its a class
    AOTTableFieldList fieldnode;
    str Prefix,Acc,Tablename,prop;
    int pos,Account_NumberID;
    #AOT
    #Properties
    ;

    Account_NumberID = fieldNum(Bank_Account, Account_Number); // Getting Account_Number field ID
    super(Account_NumberID);
    this.insert();
    Prefix = "Transaction_";
    Acc = this.Account_Number;
    TableName= Prefix + Acc;

//#Table path refer the \\Data Dictionary\\Tables and finding the path
treenode = treenode::findNode(#TablesPath);
//AOTadd method is to add table in tables//TableName is table name
treenode.AOTadd(Tablename);
treenode = treenode.AOTfindChild(TableName);
treenode.AOTcompile(1);
treenode.AOTsave();
treenode.AOTfindChild(TableName);
fieldnode = treenode.AOTfirstChild();
fieldnode.addString('AccountNum');
fieldnode = fieldnode.AOTfindChild('AccountNum');
prop = fieldnode.AOTgetProperties();
pos = findPropertyPos(prop,#PropertyExtendeddatatype); //find right place to put extended data type
pos = strFind(prop,'ARRAY',pos,strLen(prop));
pos = strFind(prop,'#',pos,strLen(prop));
fieldnode.AOTsetProperties(prop);
treenode.AOTcompile(1);
treenode.AOTsave();
treenode.AOTRestore(); //to load assigned extended data type properties
sysdictTable = sysdictTable::newTreeNode(treenode);
appl.dbSynchronize(sysdictTable.id());

}


  • Override insert method of table Bank_Account and write following code

public void insert()
{
    super();
    info("NewTable " + "Transaction_" + this.Account_Number + " has been created");
}

  • An Account is added into Bank_Account










  • Now go to AOT > Table node , a new table named 'Transaction_101' has been created.














                     ThanYou !

Create Table Using X++ Code

static void autoTable(Args _args)
{
SysDictTable sysdictTable;
Treenode treenode;
AOTTableFieldList fieldnode;
str prop;
int pos;
#AOT
#Properties
;
//Coded by Gautam Verma

//#Table path refer the \\Data Dictionary\\Tables and finding the path
treenode = treenode::findNode(#TablesPath);
 
//AOTadd method is to add table in tables//AutoTableis table name
 
treenode.AOTadd('AutoTable');
treenode = treenode.AOTfindChild('AutoTable');
treenode.AOTcompile(1);
treenode.AOTsave();
treenode.AOTfindChild('AutoTable');
fieldnode = treenode.AOTfirstChild();
fieldnode.addString('AccountNum');
fieldnode = fieldnode.AOTfindChild('AccountNum');
prop = fieldnode.AOTgetProperties();
pos = findPropertyPos(prop,#PropertyExtendeddatatype); //find right place to put extended data type
pos = strFind(prop,'ARRAY',pos,strLen(prop));
pos = strFind(prop,'#',pos,strLen(prop));

fieldnode.AOTsetProperties(prop);
treenode.AOTcompile(1);
treenode.AOTsave();
treenode.AOTRestore(); //to load assigned extended data type properties
sysdictTable = sysdictTable::newTreeNode(treenode);
appl.dbSynchronize(sysdictTable.id());
}

                     ThanYou !