The SysOperation framework is used for tasks or jobs (operations) that can optionally run in batch. This framework is just one of the many frameworks in F&O and can be used to add your own custom code to perform operations in F&O. Menu items can be used to launch operations in F&O and also with custom code that uses the SysOperation framework. A menu item is used to launch the operation and can be added in the Main Menu or can be added to a form. The user can click on the menu item to launch the operation. Launching the operation with a menu item is not the only way, custom operations can be called in code as well and is very common in the standard application.
Benefits
- Run custom code
- Synchronous / asynchronous execution
- User interface created by framework
- Data contract for parameters
Note: even though it is possible to use the SysOperation framework with just two classes, in other words without a custom controller class, I recommend using the "three class" method. This separates everything in a separate class which you can easily maintain. This three class patterns in the SysOperation framework, is identical to the well known Model View Controller (MVC) design pattern.
The three class method uses a dedicated class for threee separate roles. In simple terms:
- Business logic
- User interface
- Orchestration
They relate to the MVC design pattern like this:
- Business logic >> Model
- User interface >> View
- Orchestration >> Controller
Okay, let's go a bit deeper and let's break this down a bit.
Business logic (model)
The business logic class contains the business logic. My service classes always just does one thing. For example, update the status of data from one status to another. Over the years, I have developed the habit of ending my business logic class names with the word "service", for service class. I could have chosen something different but I started using this word never stopped. By the way, the class name can be anything you like. But for me, ending the business logic class name with service, makes it easier to identify which class contains the business logic.
User interface (view)
The data contract class, contains class members or fields that will automatically be displayed to the user by the SysOperation framework. The data contract class is used for the user interface and the business logic. Any values entered by the user in the user interface, are saved and passed on to the business logic class. When the operation is started, the user is optionally presented with a user interface that enables the user to set some parameters prior to execution of the operation. When the user interface is displayed, the user can decide to either confirm and run the operation or cancel the operation.
Orchestration (controller)
The controller class that orchestrates the execution of the operation and the user input in the form of a graphical interface. The controller class itself does not contain any business logic and usually contains an entry point (static main method) that allows it to be invoked using a menu item from the user interface (form).
Basic example
Let's start with a basic example of using the SysOperation framework with the "three class" method. It is an example where you can enter a name and it will show you the name you entered. You might notice that class contains a member called packedQuery and some methods related to the query. In this example just ignore the query part. Usually the query is used to select data and process the data.
1. Data contract class (user interface)
[DataContractAttribute]
public class MySysOpContract
{
    private Name userName;
    private str packedQuery;
    
    [DataMemberAttribute]
    public Name parmUserName(Name _userName = userName)
    {
        userName = _userName;
        return userName;
    }
    
    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;
    }
}
The contract class contains the optional query and parameters for the operation. The contract class is basically a class with all the variables needed for the operation. E.g. if you have an operation to update the status of a record or records, you want the new status to be a parameter of the operation. You can set this status in the contract class in code or in the user interface and execute the operation. The business logic updates the data with the status provided in the contract class.
[DataContractAttribute] Required. A class decoration so that this class can be serialized by the framework.
private str packedQuery Optional. The packed query.
getQuery Optional. A method to get the query.
setQuery Optional. A method to set the query.
parmQuery Optional. A method to get/set the query.
2. Service class (business logic)
public class MySysOpService
{
    public void runSysOp(MySysOpContract _data)
    {
        info(strFmt("Hello '%1'!", _data.parmUserName()));
    }
}
The service class contains the business logic for the operation. The class method must be public and contains a single argument, the contract class. The contract class contains the query and parameters for the operation. Note: The class name should not be generic but clearly indicate its function e.g. MyCustomerDiscountUpdateService.
3. Controller class (orchestration)
public class MySysOpController extends SysOperationServiceController
{
    public static void main(Args _args)
    {
        MySysOpController operation = new MySysOpController();
        operation.startOperation();
    }
    
    public void new(IdentifierName _className = '', IdentifierName _methodName = '', SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Asynchronous)
    {
        super(_className, _methodName, _executionMode);
        
        this.parmClassName(classStr(MySysOpService));
        this.parmMethodName(methodStr(MySysOpService, runSysOp));
        this.parmDialogCaption("Hello user");
    }
}
The controller class is used to launch the operation and to connect the operation with our method in our service class.
public static void main - The entry point of the controller class, this method is called by F&O when the operation is launched. The method instantiates an instance of our operation and calls a single method startOperation. The controller class contains a main method and is called when a user clicks on the operations menu item. Note: the main method is usually not called from code.
public void new - The new method is called when the controller class is instantiated and sets the service class and class method name. Note: this is how F&O knows that our method must be called in the operation. The dialog caption is set in the new method.
x. Create a service class. Using the query and parameters to update.
public void runQuery(MySysOpContract _data)
{
	//add your custom code here    
	Query query;
	QueryRun queryRun;
	CustTable custTable;
	
	query = _data.getQuery();
	
	queryRun = new QueryRun(query);
	
	while (queryRun.next())
	{
		custTable = queryRun.get(tableNum(CustTable));
		info(custTable.AccountNum);
	}
}
Post script
I use the SysOperation framework extensively for all the custom code I write. I break down the business logic into smaller pieces and create a specific class for each operation using the SysOperation framework. This provides flexibilty and maximizes re-use of the code. I have seen the benefits of this approach many times.
Examples:
- Update the status of a record from one value to another.
To be continued...
 
No comments:
Post a Comment