
What is SysOperation framework and how to develop a batch job using SysOperation framework in D365FnO and AX 2012 using X++.
In-Dynamics 365 for Finance and Operations (D365FnO), a batch job is a process that is executed in the background at a scheduled time or on-demand. Batch jobs can be used to perform a variety of tasks, such as data import/export, report generation, or system maintenance, and SysOperation Framework is a development framework that enables developers to create custom processes that can be run as batch jobs or as services.
The main components of the SysOperation framework are:
Data contracts: A data contract is a class that defines the input parameters of a process. These parameters are defined as properties of the class and are used to pass data between the user interface, the batch job, and the service.
Controllers: This is a class that extends SysOperationServiceController, which interacts with the user interface, the batch job, or the contract input parameters and processing class and exceptions.
Batch framework: The batch framework is a class that enable a process to be run as a batch job. This is a class that actually does the work we write all the logic in that class.
For development of batch job add following listed classes in your project.
- VendorGroupChangeContract
- VendorGroupChangeController
- VendorGroupChange
Add the following code in the classes.
VendorGroupChangeContract:
[
DataContract
]
class VendorGroupChangeContract extends SysOperationDataContractBase implements SysOperationValidatable
{
VendAccount vendAccount;
VendGroupId vendGroupId;
NoYesId showVendAccount;
[
DataMember
]
public VendAccount parmVendAccount(VendAccount _vendAccount = vendAccount)
{
vendAccount = _vendAccount;
return vendAccount;
}
[
DataMember,
SysOperationLabel(literalStr("Vendor group")),
SysOperationDisplayOrder('1')
]
public VendGroupId parmVendGroupId(VendGroupId _vendGroupId = vendGroupId)
{
vendGroupId = _vendGroupId;
return vendGroupId;
}
[
DataMemberAttribute,
SysOperationControlVisibilityAttribute(false)
]
public NoYesId parmShowVendAccount(NoYesId _showVendAccount = showVendAccount)
{
showVendAccount = _showVendAccount;
return showVendAccount;
}
public boolean validate()
{
if (vendAccount == '')
{
//Vend account must be specified
return checkFailed("Vend account must be specified.");
}
else if (!VendTable::exist(vendAccount))
{
// Vendor account %1 does not exist
return checkFailed(strFmt("Vendor account %1 does not exist.", vendAccount));
}
else if (vendGroupId == '')
{
// Vendor group must be specified.
return checkFailed(strFmt("Vendor group must be specified."));
}
else if (!VendGroup::exist(vendGroupId))
{
// Vendor group %1 does not exist
return checkFailed(strFmt("Vendor group %1 does not exist.", vendGroupId));
}
else
{
return true;
}
}
}
VendorGroupChangeController:
class VendorGroupChangeController extends SysOperationServiceController
{
public static void main(Args _args)
{
VendorGroupChangeController controller = new VendorGroupChangeController(classStr(VendorGroupChange),
methodStr(VendorGroupChange, Run),
SysOperationExecutionMode::Synchronous);
controller.parmDialogCaption("Vendor group change process");
controller.startOperation();
}
}
VendorGroupChange:
class VendorGroupChange
{
VendAccount vendAccount;
VendGroupId vendGroupId;
protected void InitFromContract(VendorGroupChangeContract _contract)
{
vendAccount = _contract.parmVendAccount();
vendGroupId = _contract.parmVendGroupId();
}
protected void UpdateVedorGroup()
{
VendTable vendTable = VendTable::find(vendAccount, true);
VendTable vendTableOrig = vendTable.orig();
if (vendTable.RecId > 0)
{
if (vendTableOrig.VendGroup != vendGroupId)
{
vendTable.VendGroup = vendGroupId;
vendTable.update();
if(vendTable)
Info(strFmt("Group of vendor account %1 has been changed from %2 to %3.", vendAccount, vendTableOrig.VendGroup, vendTable.VendGroup));
}
}
}
public boolean Validate()
{
return true;
}
public void Run(VendorGroupChangeContract _contract)
{
if (!_contract.validate())
{
return;
}
this.InitFromContract(_contract);
if (!this.Validate())
{
return;
}
ttsbegin;
this.UpdateVedorGroup();
ttscommit;
}
}
Add action menu item for the controller class in your project.
Create an action menu item. Set the properties of action menu item as follows:
- Object: VendorGroupChangeController
- Object Type: Class
- Subscriber access level: Unset
- Label: Change vendor group
See the below screenshot for reference.

Create an extention of menu where you want to show the action menu item in my example I am using AccountPayable. In the form, drag the action menu item from the project and drop the required position.
See the below screenshot of project structure and AccountPayable menu for reference.

Run the batch job from the user interface.
Go to Account payable > Vendor > Change vendor group press the button. See the below screenshot for reference.

You can also set Batch processing toggle button ON and select Batch group for run the batch job in recurrence mode.