Friday, October 31, 2025

The SysOperation framework

The SysOperation framework is used to implement business logic and is widely used in the standard application. It is just one of the many frameworks in the application and it simplifies the creation of tasks that can run in the background.

Like all frameworks in F&O, the SysOperation framework provides rich functionality out of the box, and lets developers focus on coding business logic. Developers do not have to worry about how the business logic is executed, that's all taken care of by the SysOperation framework!

When implementing business logic by using the SysOperation framework, I recommend creating a minimum of three classes. Let's break that down a bit, I'll explain the function of each class.

Data contract class

The data contract class contains the query and parameters required by the operation. When an operation is started interactively, the user is presented with a dialog that allows them to set some parameter values before the operation is executed. After setting the values, the user can either confirm and run the operation or cancel the operation. If the operation is started programmatically, the data contract is populated in code and the dialog is not shown.

Controller class

The controller class manages the execution of the operation. It does not contain any business logic itself and typically provides an entry point (a static main method) that allows the operation to be started from a menu item. The controller also tells the SysOperation framework which method to execute.

Business logic class

The business logic class contains the business logic of the operation. My custom business logic classes always have a single purpose. For example, update the status of data from one value to another.

Basic example

The following basic example simply displays the account number of all customers in the current legal entity. It demonstrates a minimal implementation of the SysOperation framework.

Data contract class


[DataContractAttribute]
public final class MySysOpDemoContract
{
    private boolean showName;
    private str packedQuery;

    [DataMemberAttribute, SysOperationLabel("Show customer name")]
    public boolean parmShowName(boolean _showName = showName)
    {
        showName = _showName;
        return showName;
    }

    public Query getQuery()
    {
        return new Query(SysOperationHelper::base64Decode(packedQuery));
    }

    public void setQuery(Query _query)
    {
        packedQuery = SysOperationHelper::base64Encode(_query.pack());
    }

    [DataMemberAttribute, AifQueryTypeAttribute('_packedQuery', queryStr(CustTable))]
    public str parmQuery(str _packedQuery = packedQuery)
    {
        packedQuery = _packedQuery;
        return packedQuery;
    }
}

As mentioned before, the data contract class contains the query and parameters for the operation. It also includes other variables and methods required by the system to function properly. These methods can be used by the controller class during runtime. Don’t worry too much about the details for now.

[DataContractAttribute] - This attribute is required on the data contract class so that the system knows it should be serialized and deserialized.

private boolean showName - Custom class variable that determines whether the business logic should display the customer name.

private str packedQuery - The system serializes the operation query to a string and stores it in a class variable (required).

[DataMemberAttribute] - Required attribute on the parm method to ensure the showName field is serialized and displayed in the runtime dialog. Without it, the field is excluded from serialization and will not appear in the dialog.

[SysOperationLabel("Show customer name")] - Specifies the label for showName in the runtime dialog. If omitted, the label from the parm method’s extended data type is used.

parmShowName - Runtime parm method for accessing/setting the contract class show name class variable.

getQuery - Returns the query for the contract class at runtime. Required by the system.

setQuery - Sets the query for the contract class at runtime. Required by the system.

parmQuery - Runtime parm property for accessing/setting the contract class query. Required.

Controller class


public class MySysOpDemoController extends SysOperationServiceController
{
    public static void main(Args _args)
    {
        MySysOpDemoController operation = new MySysOpDemoController();
        operation.startOperation();
    }

    public void new(IdentifierName _className = '', IdentifierName _methodName = '', SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Asynchronous)
    {
        super(_className, _methodName, _executionMode);
        
        this.parmClassName(classStr(MySysOpDemoService));
        this.parmMethodName(methodStr(MySysOpDemoService, runQuery));
        this.parmExecutionMode(SysOperationExecutionMode::Synchronous);
        this.parmDialogCaption("My SysOperation demo");
    }
}

The controller class is used to launch the operation interactively from the F&O user interface or from code. Importantly, it provides information to the system about which business logic method to execute.

public static void main(Args _args) - The entry point of the controller class, this method is called by F&O when the operation is launched interactively using a menu item. The method instantiates an instance of our controller class and calls a single method startOperation.

public void new - The new method sets the business logic class name and method name after the call to super. This is how the SysOperation framework knows that our method must be called in the operation. The dialog caption is also set in the new method.

Note: The ExecutionMode in the example is set to Synchronous. That means the operation is executed as soon as the startOperation method is called. More about this later when I update this article.

Business logic class


public class MySysOpDemoService
{
    public void runQuery(MySysOpDemoContract _data)
    {
        Query query = _data.getQuery();
        QueryRun queryRun = new QueryRun(query);

        while (queryRun.next())
        {
            CustTable custTable = queryRun.get(tableNum(CustTable));
            
            if (_data.parmShowName())
            {
                info(strFmt('%1 - %2', custTable.AccountNum, custTable.name()));
            }
            else
            {
                info(custTable.AccountNum);
            }
        }
    }
}

Contains the business logic of the operation. The method must be public and can take only one argument – the data contract. The data contract holds the query and parameters for the operation.

The business logic in the example above does the following:

  • The query is retrieved from the data contract object
  • A QueryRun object is created from the query
  • A while loop enumerates all customers in the query
  • An if statement checks the value of the showName parameter and displays the information accordingly

Note: The data contract class passed as the parameter to the operation method is automatically instantiated by the SysOperation framework and provided to the controller.

Menu item

To launch the example operation from a menu or form in F&O, create a new Action Menu Item, then set its key properties:

  • Label → My SysOperation demo
  • Object → MySysOpDemoController
  • ObjectTypeClass

Once these properties are set, the menu item will open the SysOperation dialog and execute the operation exactly like any standard F&O process.

Basic properties of the menu item.


When the operation is launched via the menu item, the SysOperation framework automatically generates and displays a dialog. Our "Show customer name" parameter appears as a checkbox (slider) control because it is of type boolean. The framework automatically chooses the appropriate dialog control based on the parameter’s data type – for example:

  • str → text box
  • int or real → numeric field
  • boolean → checkbox/slider
  • enum → drop-down list
  • etc.

Output of the operation after clicking OK (with Show customer name disabled).


Launching our operation again and enabling the show customer name parameter.


Output of the operation after clicking OK (with Show customer name enabled).

Note:The output contains the same customer account multiple times, whereas one would expect to see the customer account only once per customer. The reason for this is that we are using the standard CustTable query, which also includes data sources for the CustTrans and CustTransOpen tables. As a result, the query returns open transactions as well. The output therefore displays the customer account once for each open transaction the customer has.

No comments:

Post a Comment