Sunday, December 8, 2013

How to call events on a Dialog in Ax2012

I found a way in which we can override the form  events like modified, lookup, clicked etc. on a Dialog. We used to create event method on a dialog by using  “FieldNumber_1_event”  format. I got a requirement in which I have to show the lookup for service orders on the basis of project id on a dialog. I did it in a following way  instead of  we used to do in Ax2009.

·         Overwrite dialogPostRun method and wrote the following code

public void dialogPostRun(DialogRunbase _dialog)
{
    FormRun formRun;

    super(_dialog);

    formRun = _dialog.dialogForm().formRun();
    formRun.controlMethodOverload(true);
    formRun.controlMethodOverloadObject(this);

    fsCtrlSmmActivityNumber = formRun.design().control(fbsCtrlSmmActivityNumber.id());
    fsCtrlProjCategoryId = formRun.design().control(fbsCtrlProjCategoryId.id());


    _dialog.dialogForm().formRun().controlMethodOverload(false);
    _dialog.dialogForm().formRun().controlMethodOverloadObject(this);

         // 2012-10-02 #18679 pgad: Project adjustments -->
    dialogServiceOrderId.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(ProjAdjustmentSplit, serviceOrderLookup), this);

    // 2012-10-02 #18679 pgad: Project adjustments <-- o:p="">
}
·         Here I have registered  event lookup.
·          Created one  static method serviceOrderLookup.


public  void serviceOrderLookup(FormStringControl   _formStringControl)
{
    boolean ret;
    ;

    ret = _formStringControl.modified();

    if (ret)
    {
        this.lookup(); ( this will call the lookup method on a ProjAdjustmentSplit)
    }
}



·         Now  it will call  lookup event automatically. We need not to handle this

AX 2012 Buffer size exceeded

Issue encountered where a query exceeded the Buffer Size. Error reported as Statement too long and AX suggests increasing the size of the buffer in the AOS database configuration​ tab.
The default for this buffer is 24K and most documentation suggests if there is a need to increase the size of this buffer as a result of either a query or select statement that is should only be increased by small increments.
However, there is an alternative method of resolving this and that is to use the forceLiterals attribute/property. To do this on a AX query, set the Literal property of the query from Default to forceLiteral. If the issue arises on a AX X++ select statement then use the keyword forceliterals eg
select forceliterals custtable;
Note

You are advised not to use the forceLiterals keyword in X++ select statements, because it could expose code to an SQL injection security threat.

AX 2012:- Business Framework

We as AX developers at some point have developed functionalities wherein we need to take some input from the user and based on that input some processing needs to be done in batch. When such requirements come up, the first thing that comes to our mind is RunBase and RunBaseBatch.
I would like to introduce a framework introduced in AX2012, the still is derived from RunBaseBatch but it is a lot easier to perform tasks like building the dialog UI, validate the user entered input and retrieve user entered input for processing. Well in the introduction (to keep it simple), we’ll take only these three objectives.
Requirement
Suppose, I have a requirement of displaying to user a dialog in which he/she can enter following fields through a dialog
i. Name (String)
ii. Greeting Count (Any number not greater than 10)
After pressing OK, user sees an info log of a greeting, as many numbers of times as he had entered in the second field.
Solution
The above solution when build by deriving class from RunBaseBatch will need a class extended from RunBaseBatch. That single class will handle building of user interface in the dialog, validate user input in the dialog fields and perform required processing on the user input. However, when using Business Operation Framework, we can split this into individual tasks. Let’s build the above requirements with Business Operation Framework.
Data Contract Class

First we need a data contract class for the operation framework. The data contract class will contain parm methods to retrieve user entered values from the dialog. Assuming we have an integer based EDT named GreetCount.

[DataContractAttribute]
class GreetOperationDataContract
{
GreetCount greetCount;
Name usersName;
}
[DataMemberAttribute]
public GreetCount parmGreetCount(greetCount _greetCount = greetCount)
{
greetCount = _greetCount;
return greetCount;
}
[DataMemberAttribute]
public Name parmUsersName(Name _usersName = usersName)
{
usersName = _usersName;
return usersName;
}

Service Class
Next we a class that performs the required operation of displaying a greeting infolog the required number of times.

class GreetService
{
}
public void showGreetingInfolog(GreetOperationDataContract _dataContract)
{
int i;
for(i = 0; i < _dataContract.parmGreetCount(); i++)
{
info(strFmt("Hello %1. No. %2", _dataContract.parmUsersName(), i));
}
}

Service Controller Class

class GreetServiceController extends SysOperationServiceController
{
}
public static void main(args _args)
{
GreetServiceController greetController;
greetController = GreetServiceController::construct();
greetController.parmExecutionMode(SysOperationExecutionMode::Synchronous);
greetController.startOperation();
}
public static GreetServiceController construct()
{
return new GreetServiceController();
}
public ClassDescription caption()
{
return "Greeting to Operation framework";
}
public void new()
{
super();
this.parmClassName(classstr(GreetService));
this.parmMethodName(methodStr(GreetService, showGreetingInfolog));
}
public LabelType parmDialogCaption(LabelType _dialogCaption = "")
{
LabelType ret;
ret = this.caption();
return ret;
}


User Interface Builder Class
The user interface builder class is needed only if you wish to perform some validations on the user input, or create a custom lookup on some dialog field.

class GreetUIBuilder extends SysOperationAutomaticUIBuilder
{
DialogField dlgGreetCount;
}
public boolean greetCountValidate(FormIntControl _control)
{
if (_control.value() > 10)
{
return checkFailed("Please enter a number less than 10");
}
return true;
}
public void postBuild()
{
super();
dlgGreetCount = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(GreetOperationDataContract, parmGreetCount));
}
public void postRun()
{
super();
dlgGreetCount.registerOverrideMethod(methodStr(FormIntControl, validate), methodStr(GreetUIBuilder, greetCountValidate), this);
}


Output
Run the controller class to see the output.




And in the Greet # field try to enter a value greater than 10. If you choose to run this operation in batch, you can go to System Administration -> Inquiries -> Batch jobs -> Batch jobs or (Batch jobs history) to view the status of the batch job. Select the operation "Greeting to operation framework" and click button Log. Will see the output.




Enhancement in Financial Dimension Lookup functionality

The below form shows all financial dimensions used in client’s company. I will show the functionality related to dimension Avdeling, which is a custom dimension.










After clicking the button Financial dimension values on the above form, the below form gets open:


















On the left side of the above form, all dimension values are being shown and on the right side all the properties of the selected dimension value are displayed. There is a field on right side “Select the level of dimension value to display” which contains 3 values: Shared Values, Chart of Accounts and Companies.
                In std AX 2012, if the field Suspended is ticked while Shared Values is selected in the above mentioned field then this dimension value will not be displayed in the look up of Avdeling dimension in segmented control as well as in dimension look up in customer/vendor master etc. form.             When the Companies option is selected then we can set properties/values for all companies separately as shown below:



















The client requirement was that if the Companies is selected in level field and the suspended check box is marked for some particular company then the selected dimension value should not be displayed in dimension lookup in that company.
So if the value 01 is suspended in company 001 then it should not be displayed in the lookup in company 001 and if it is not suspended in 002 then it should be displayed in 002.
Below form is General Journal lines form which is using segmented entry control to enter dimension values.




















Now I will show you the code which is used to show the dimensions in look up of segmented controls:

















In std AX 2012, there was only below code written in red area:
LedgerDimensionController::restrictQueryDimensionAttributeValues(_qbds, _dimensionAttributeId, false, false);
I created one more method LedgerDimensionController::restrictQueryDimensionAttributeValuesLed which will be called in case of dimensions (Not in case of Main Account selection).

Both methods have been shown below

























I added one more data source(DimensionAttrValueLedgerOverride) to the main data source and put the condition of not to show the suspended dimension here. The changes, I made in std method, have been shown below in red area:





















By using the above method, the dimension lookup in segmented control was showing only dimensions which were not suspended in that company.

                The client wanted the same logic of dimensions in normal dimension look up also e.g. in Journals/Customer/Vendor etc. forms as shown below:



I have shown the code below to be used in normal dimension lookup. The below changes are done in init method of DimensionDefaultLookup form. My changes are shown in red area.







Count of number of pages printed on to SSRS report



public void reportViewerRefreshComplete(SRSReportExecutionInfo _executionInfo)
{
    int page;

    super(_executionInfo);

    page = this.getReportContract().parmReportExecutionInfo().parmPrintedPhysicalPages();

    info("Total number of pages:" + int2str(page));

}

Code to copy data from one table to another even though the tables are different provided the field names are same. in Ax2012



public void copyData(Common _from, Common _to)
{
    DictTable   dictTable   = new DictTable(_from.TableId);
    FieldId     fromFieldId = dictTable.fieldNext(0);
    fieldId     toFIeldid;
    DictField   dictField;

    while (fromFieldId)
    {
         If(! isSysId(fromFieldId))
         {
                 dictField       = new DictField(dictTable.id(), fromFieldId);
        toFIeldid       = fieldName2id(_to.TableId, fieldId2name(_from.TableId, fromFieldId));
        if (toFIeldid)
        {
            _to.(toFIeldid) = _from.(fromFieldId);
        }
        }       
        fromFieldId = dictTable.fieldNext(fromFieldId);
    }

}

Code to connect to an External Database from X++ Code (AX 2012)



static void theAxapta_ODBCConnection(Args _args)
{
    LoginProperty   loginProp;
    ODBCConnection  conn;
    Resultset       resultSet, resultSetCount;
    Statement       statement1;
    ;
    loginProp = new LoginProperty();
    loginProp.setServer('theAxapta');//you can use IP address as well
    loginProp.setDatabase('AXDEVDB');
    conn = new ODBCConnection(loginProp);
    statement1  = conn.createStatement();
    resultSet   = statement1.executeQuery("SELECT * from CustTable where DATAAREAID = 'CEU'");
    while (resultSet.next())
    {
        info(resultSet.getString(1));
    }

}