Batch parallelism or multithreading in Dynamics 365 for Finance and Operations



1. Create a contract class to define the container of the item 


Notes: Do not use a list. It may cause the error: “no metadata class defined for data contract object” when the function’s running.






Code:


[DataContractAttribute]


public class ABC_MaterialBalanceContract


{ 


    container conItem;


    [DataMemberAttribute, SysOperationControlVisibilityAttribute(false)]


    public container parmConItem(container _conItem = conItem)


    {


            conItem = _conItem;

             return conItem;


    }


 


2. Create a service class to process extends SysOperationServiceBase


 





 3. Create a controller class to call the service class (step 2) extends SysOperationServiceController


 



Code:


class ABC_MaterialbalanceController extends SysOperationServiceController


{


    protected void new()

    {


         super();


         this.parmClassName(classStr(ABC_MaterialbalanceService));


         this.parmMethodName(methodStr(ABC_MaterialbalanceService, processOperation)); 


         this.parmDialogCaption("@ABC:GenerateMaterialBalance");


    }



    public static ABC_MaterialbalanceController construct()

    {


         ABC_MaterialbalanceController controller = new ABC_MaterialbalanceController();

         return controller;


    }

   


    public static void main(Args args)

    {


         ABC_MaterialbalanceController::construct().startOperation();


    }




    public ClassDescription defaultCaption()

    {

         return "@ABC:GenerateMaterialBalance";

     }

}


4. Create a contract class to define the number of tasks


 




 Code:


[DataContractAttribute]


class ABC_ScheduleMaterialBalanceContract


{


    private int tasksNumber;


    [DataMemberAttribute] 


    public int parmParallelTasksNumber(int _tasksNumber = tasksNumber)  


    {

             tasksNumber = _tasksNumber;


             return tasksNumber;

    }


}


5. Create a service class to separate the task

  • Create 1 temp table to contain all items that will be processed.
  • Insert data into the temp table.




  • Separate the number of items per task based on the number of tasks that the user input.
  • I have one task (process for non-material) and it will be processed after all task of material item has finished. I will use the method addDependency of BatchHeader class.





Code:


public class ABC_ScheduleMaterialBalanceService extends SysOperationServiceBase



    int numberOfTask;


    ABC_MaterialBalanceTmp materialBalanceTmp;


    public void processOperation(ABC_ScheduleMaterialBalanceContract _dataContract)


    {


         int eachRecordCount, numberOfOpenRecord, numberOfBundleSize;


         ABC_BOMParmLineHistoryView bomParmLineHistoryView;


         ItemId itemId;


         FromDate fromDateId;


         ToDate toDateId;


         Query q = new Query();


         QueryRun qr;


         QueryBuildDataSource qbds;


         List listItem;


         container conItem;


         ABC_MaterialBalanceContract contract;


         BatchHeader batchHeader;


         ABC_MaterialbalanceController controller;


         ABC_NonMaterialbalanceController nonMaterialController;


         ABC_MaterialBalance materialBalance;;



         delete_from materialBalance;


         numberOfTask = _dataContract.parmParallelTasksNumber();


         numberOfOpenRecord = this.countMaterialItem();



        if (numberOfOpenRecord > 0 && numberOfTask > 0)


        {


             // how many bundle will run thay will given by user.


             numberOfBundleSize = numberOfOpenRecord div numberOfTask;


             batchHeader = BatchHeader::construct();


             nonMaterialController = ABC_NonMaterialbalanceController::construct();


             batchHeader.addRuntimeTask(nonMaterialController,                 this.getCurrentBatchTask().RecId);


             while select itemId from materialBalanceTmp         


             {


                     eachRecordCount++;


                    if (eachRecordCount == 1)


                     {

                         conItem = conNull();


                     }


                     conItem += [materialBalanceTmp.ItemId];


                     if (eachRecordCount == numberOfBundleSize)


                     {


                         controller = ABC_MaterialbalanceController::construct();

                         contract = controller.getDataContractObject();

contract.parmConItem(conItem);

batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);

                         batchHeader.addDependency(nonMaterialController, controller, BatchDependencyStatus::Finished);

                         eachRecordCount = 0;


                     }


             }


             if (eachRecordCount > 0)


             {


                 controller = ABC_MaterialbalanceController::construct();


                 contract = controller.getDataContractObject();


                 contract.parmConItem(conItem);           

  

                 batchHeader.addRuntimeTask

                 (controller, this.getCurrentBatchTask().RecId);               


                 batchHeader.addDependency

                (nonMaterialController, controller, BatchDependencyStatus::Finished);


             }


             batchHeader.save();


        }


}



private int countMaterialItem()


{


         Query q = new Query();


         QueryRun qr;


         QueryBuildDataSource qbds;


         ABC_BOMParmLineHistoryView bomParmLineHistoryView;


         InventTable inventTable;


         RecordInsertList recordInsertList = 

                        new   RecordInsertList(tableNum

                        (ABC_MaterialBalanceTmp)

                        , false, false, false, false, false, materialBalanceTmp);


         int numberOfOpenRecord;


         qbds = q.addDataSource(tableNum(ABC_BOMParmLineHistoryView));


         qbds.orderMode(ordermode::GroupBy);


        qbds.addSortField(fieldnum


        (ABC_BOMParmLineHistoryView,ItemId));


         qbds.addSortField(fieldnum


        (ABC_BOMParmLineHistoryView,InventUnitId));


         qbds.addSortField(fieldnum


        (ABC_BOMParmLineHistoryView,BOMUnitId)); 

        qbds.addSortField(fieldnum

        (ABC_BOMParmLineHistoryView,RetailVariantId));


         qbds.addSortField(fieldnum

                        (ABC_BOMParmLineHistoryView,Name));


         qbds.addSortField(fieldnum

                        (ABC_BOMParmLineHistoryView,Description));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, ItemId));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, InventUnitId));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, BOMUnitId));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, RetailVariantId));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, Name));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, Description));


         qbds.addSelectionField(fieldNum

                        (ABC_BOMParmLineHistoryView, RemainTransferQty),SelectionField::Sum);


         qr = new QueryRun(q);


         while(qr.next())


         {


             bomParmLineHistoryView =  

                    qr.get(tablenum(ABC_BOMParmLineHistoryView));


             inventTable = InventTable::find( bomParmLineHistoryView.ItemId);


             if (inventTable.ItemType !=ItemType::Service)


             {


                 materialBalanceTmp.clear();


                 materialBalanceTmp.ItemId = bomParmLineHistoryView.ItemId;


                 numberOfOpenRecord++;


                recordInsertList.add(materialBalanceTmp);


             }


        }


        recordInsertList.insertDatabase();


        return numberOfOpenRecord; 


    }


} 


6. Create a controller class to call the service class (step 5) extends SysOperationServiceController


 




7. Create a menu item for the class controller (step 6)

Comments

Popular posts from this blog

How to customize electronic reporting in D365

How to create and run the Runbase batch class in D365

How to Enable/Disable a Form Button with X++

Build HTML and send email in D365 FO with X++

Difference between InMemory and TempDB tables in D365 F&O

Conditional Statements in X++

How to apply a package in LCS Dynamics 365 F&O

Customize the standard excel template in D365FO

How to write the event handler in D365