Sunday, May 22, 2016

AX 2012 - sysOperation Framework - Use controller class to open dialog from AX form

In my previous post on sysOperation framework Example I described how the sysOperation framework can be used for the batch functionality in contrast to RunBaseBatch framework. In this post I will explain how you can use sysOperation Framework classes to open a dialog from a AX form using contract and controller classes and then run logic in Service class.

In this example I added a new menu item "Example" on projProjectsListPage form which will call controller class. This example is create project transactions using service calls at the end.








First we need to create a contract class which holds parameters (dialog fields value or user input). To keep it simple I only added two parameters ProjId (Project Id) and ProjContractId (Project Contract Id). That's the model part of MVC pattern and view part is automatically created by using these data member attributes.
[DataContractAttribute]
class FF_ExampleContract
{
    ProjId              projId;
    ProjInvoiceProjId   projContractId;
}
[DataMemberAttribute,
SysOperationLabelAttribute(literalStr("Selected Project Id")),
SysOperationHelpTextAttribute(literalStr("Selected Project Id from projects")),
SysOperationDisplayOrderAttribute('1')
]
public ProjId parmProjId(ProjId _projId = projId)
{
    projId = _projId;
    return projId;
}
[DataMemberAttribute,
SysOperationLabelAttribute(literalStr("Selected Project contract Id")),
SysOperationHelpTextAttribute(literalStr("Selected Project Contract Id from projects")),
SysOperationDisplayOrderAttribute('2')
]
public ProjInvoiceProjId parmProjContractId(ProjInvoiceProjId _projContractId = projContractId)
{
    projContractId = _projContractId;
    return projContractId;
}

Let's move to controller part of sysOperation framework and create a new controller class.

For this example I don't need to have batch tab on dialog, so I set return value to FALSE in canGoBatch() method.
class FF_ExampleController extends SysOperationServiceController
{
    Common callerRecord;
}
public static void main(Args _args)
{
    FF_ExampleController controller;
    controller = FF_ExampleController::newFromArgs(_args);
    controller.parmShowDialog(true);
    controller.initFromCaller();
    controller.startOperation();
    controller.refreshCallerRecord();
}
public static FF_ExampleController newFromArgs(Args _args)
{
    FF_ExampleController        controller;
    IdentifierName              className;
    IdentifierName              methodName;
    SysOperationExecutionMode   executionMode;
    [className, methodName, executionMode] = SysOperationServiceController::parseServiceInfo(_args);
    if (_args.record())
    {
        executionMode = SysOperationExecutionMode::Synchronous;
    }
    controller = new FF_ExampleController(className, methodName, executionMode);
    controller.parmArgs(_args);
    controller.parmCallerRecord(_args.record());
    return controller;
}
public LabelType parmDialogCaption(LabelType _dialogCaption = "")
{
    LabelType caption;
    // This appears as the window name
    caption = "Create project transaction";
    return caption;
}
protected void new(
    IdentifierName _className = classStr(FF_ExampleService),
    IdentifierName _methodName = methodStr(FF_ExampleService, example),
    SysOperationExecutionMode _executionMode = SysOperationExecutionMode::ReliableAsynchronous
    )
{
    super(_className, _methodName, _executionMode);
}
private void initFromCaller()
{
    #define.dataContractKey('_contract')
    FF_ExampleContract contract;
    ProjTable       projTable;
    contract = this.getDataContractObject(#dataContractKey) as FF_ExampleContract;
    switch (this.parmCallerRecord().TableId)
    {
        case tableNum(ProjTable) :
            projTable = this.parmCallerRecord() as ProjTable;
            contract.parmProjId(projTable.ProjId);
            contract.parmProjContractId(projTable.ProjInvoiceProjId);
            break;
    }
}
public boolean canGoBatch()
{
    return false;
}
public Common parmCallerRecord(
    Common _callerRecord = callerRecord
    )
{
    callerRecord = _callerRecord;
    return callerRecord;
}
/// <summary>
///    Refreshes the calling form data source.
/// </summary>
protected void refreshCallerRecord()
{
    FormDataSource callerDataSource;
    if (this.parmCallerRecord() && this.parmCallerRecord().dataSource())
    {
        callerDataSource = this.parmCallerRecord().dataSource();
        callerDataSource.research(true);
    }
}

In the new method of controller class it is eventually calling service class method. We could create a method in controller class itself and refer that method into the new method of controller class to process business logic. This seems to be a lot for controller class, first to handle all parameters and then process business logic, recommended way is to separate the business logic in service class and another advantage of having service class is, these operations can also be used in web services (AIF) operations. That's the separate topic and we will talk about it later...

Let's create new service class and add entry point method to process business logic.
class FF_ExampleService
{
    FF_ExampleContract contract;
}
[SysEntryPointAttribute]
public void createProjectTransaction(FF_ExampleContract _contract)
{
    ProjId                  projId;
    ProjInvoiceProjId       projContractId;
    contract            = _contract;
    projId          = contract.parmProjId();
    projContractId  = contract.parmProjContractId();
    /*
   
    .... create project transactions
   
    */
}

Have we achieved what we aimed at start?

We are almost there, just need to add a new action menu item and assign controller & service class to it.




















That's it!!! run the menu item and see how it works...




Click on Example menu item from Project list page, it opened a dialog with correct title 'Create project transactions' and with correct Project Id passed from calling form (ProProjectsListPage). 

Happy Dax!ng...