MultiThreading using SysOperationFrameWork

 Requirement 

 Develop batch job (Sysoperation Framework, with multi threading) to cleanup older or historical transactions. 

UI
 transaction cleanup 

Technical 
CleanupScheduler
CleanupTask


Dialog
Messages older than (In Days)  = 90
Number of tasks

Scheduler
Finds records from custom table that are older than 90 days and assign to tasks
Tasks will just delete the record
Provide info log on how many records cleaned up

we should create six classes for multithreading by using sysoperation frameworks 
1)CleanupSchedulerContract
2)CleanupSchedulerController
3)CleanupSchedulerService
4)CleanupTaskContract
5)CleanupTaskController
6)CleanupTaskService

1)CleanupSchedulerContract
[DataContractAttribute]
[SysOperationAlwaysInitializeAttribute]
class CleanupSchedulerContract implements SysOperationInitializable
{
    Integer numOfTask,numOfDays;


    [DataMemberAttribute,
        SysOperationLabel(literalStr("OlderRecords"))]
    public Integer parmNumOfDays(Integer _numOfDays = numOfDays)
    {
        numOfDays = _numOfDays;

        return numOfDays;

    }

    [DataMemberAttribute,
        SysOperationLabel(literalStr("NumOfTasks"))]
    public Integer parmNumOfTask(Integer _numOfTask = numOfTask)
    {
        numOfTask = _numOfTask;

        return numOfTask;

    }

    public void initialize()
    {
        numOfTask = 4;
        numOfDays = 90;
    }

}
2)CleanupSchedulerController

class CleanupSchedulerController extends SysOperationServiceController
{
    public static SysOperationController construct()
    {
        SysOperationController controller = new CleanupSchedulerController(
            classStr(CleanupSchedulerService),
            methodStr(CleanupSchedulerService, process));


        return controller;
    }

    public static void main(Args _args)
    {
        CleanupSchedulerController::construct().startOperation();
    }

    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    public ClassDescription caption()
    {
        ClassDescription ret;

        ret = super();
        ret = "SchedulerTransCleanup";

        return ret;
    }

}
3)CleanupSchedulerService

class CleanupSchedulerService extends SysOperationServiceBase
{
    BatchHeader                          batchHeader;
    SysOperationServiceController        controller;
   PaymentTable        paymentsTable; //customTable
   CleanupTaskContract  taskContract = new CleanupTaskContract();
    Counter                              paymRecIDs;
    RefRecId                             batchInheritedRecId;
    Counter                              totalRecords;

    public void addTask(List PaymentTableRecIdsList, str caption = '')
    {
        // Check if the list of payment record IDs is empty
        if (PaymentTableRecIdsList.elements() <= 0)
        {
            return;
        }

        ttsbegin;
        controller   = CleanupTaskController::construct();
        taskContract = controller.getDataContractObject();
        taskContract.parmRecIds(PaymentTableRecIdsList);

        batchHeader.addRuntimeTask(controller,batchInheritedRecId);
        ttsCommit;

        Info(strFmt("%1-%2", caption, PaymentTableRecIdsList.elements()));
        totalRecords += BASOFARECONRetailPaymentTableRecIdsList.elements();
    }

    public  void process(CleanupSchedulerContract _contract)
    {
        Integer        numOfTask,numOfDays,numOfRecords = 0;
        TransDateTime  transactionDate;
        TransDate      currentDate;
       
        // List to hold paymentTable record IDs
        List         recIdsList = new List(Types::Int64);

        numOfTask       = _contract.parmNumOfTask();
        numOfDays       = _contract.parmNumOfDays();
        currentDate     = DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());
        transactionDate = DateTimeUtil::newDateTime((currentDate-numOfDays),85399);
        
        // Retrieve payment records based on the provided condition
        while select RecId from paymentsTable
            where paymentsTable.CreatedDateTime < transactionDate
        {
            recIdsList.addEnd(paymentsTable.RecId);
        }

        ListIterator recIdsItr = new ListIterator(recIdsList);

        numOfRecords = recIdsList.elements();

        info(strFmt("%1",numOfRecords ));

        if (this.isExecutingInBatch())
        {
            batchHeader = this.getCurrentBatchHeader();
        }
        else
        {
            batchHeader = BatchHeader::construct();
            batchHeader.parmCaption("SchedulerTransCleanup");
        }

        batchInheritedRecId = batchHeader.parmBatchHeaderId();

        if (numOfRecords > 0)
        {
            int recordsPerTask = numOfRecords / numOfTask;
            int remainingRecords = numOfRecords - (recordsPerTask * numOfTask);// Calculate remaining records
            int taskIndex = 1;

            while (taskIndex <= numOfTask)
            {
                // List to hold record IDs for each task
                List   taskRecIdsList  = new List(Types::Int64);
                // Add records to the taskRecIdsList for each task
                for (int i = 0; i < recordsPerTask && recIdsItr.more(); i++)
                {
                    taskRecIdsList.addEnd(recIdsItr.value());
                    recIdsItr.next();
                }
                // If there are remaining records and more records in the iterator, add one record to the taskRecIdsList
                if (remainingRecords > 0 && recIdsItr.more())
                {
                    taskRecIdsList.addEnd(recIdsItr.value());
                    recIdsItr.next();
                    remainingRecords--;
                }
                this.addTask(taskRecIdsList, strFmt("Task %1", taskIndex));
                taskIndex++;
            }
        }
        if (batchHeader != null)
        {
            batchHeader.save();
        }
    }

}

4)CleanupTaskContract
[DataContractAttribute]
class CleanupTaskContract
{
    List recIds;


    [DataMemberAttribute('PaymentTableRecIdsList'),
        AifCollectionTypeAttribute('_recIds', Types::Int64),
        SysOperationControlVisibilityAttribute(false)]
    public List parmRecIds(List _recIds = recIds)
    {
        recIds = _recIds;

        return recIds;

    }

}
5)CleanupTaskController

class CleanupTaskController extends SysOperationServiceController
{
    public static SysOperationController construct()
    {
        SysOperationController controller = new CleanupTaskController(
            classStr(CleanupTaskService),
            methodStr(CleanupTaskService, process));


        return controller;
    }

    public static void main(Args _args)
    {
       CleanupTaskController::construct().startOperation();
    }

    /// <summary>
    ///
    /// </summary>
    /// <returns></returns>
    public ClassDescription caption()
    {
        ClassDescription ret;
    
        ret = super();

        ret = "SchedulerCleanupTask";
    
        return ret;
    }

}
6)CleanupTaskService

class CleanupTaskService extends SysOperationServiceBase
{
    public void process(CleanupTaskContract _contract)
    {
        PaymentTable  paymentsTable;//customTable
        // Retrieve the RecIds from the contract
        List                           recIds = _contract.parmRecIds();
        // Create a ListIterator to iterate over the RecIds
        ListIterator                   itr    = new ListIterator(recIds);

        try
        {
            while (itr.more())// Iterate through each RecId in the list
            {
                int64 recId = itr.value();
                
                ttsbegin;

                select forupdate paymentsTable
                    where paymentsTable.RecId == recId;
                //select firstonly paymentsTable
                //    where paymentsTable.RecId == recId;

                if (paymentsTable)// Check if a record was found
                {
                    paymentsTable.delete();
                }
                
                ttscommit;

                itr.next();// Move to the next RecId in the list
            }
        }
        catch
        {
            Error("CatchErrorForPayment");
        }

    }

}

Create a Action Menu Item 

Properties : Object  = CleanupSchedulerController.
                   ObjectType   = class.

Comments

Popular posts from this blog

how to post trade agreement journals automatically using x++ code

How to Create a wizard in x++ d365

x++ code to submit and approve and reject the invent movement workflow in d365 F&O