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 Enable/Disable a Form Button with X++

How to create and run the Runbase batch class in D365

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