Thursday 31 May 2018

Add new Methods in Ax7


AX7 / D365 Extensions: Add a Field and a Method to a Table

[ExtensionOf(tableStr(InventTable))]
final class InventTable_Extension
{
    public static boolean isMyFieldEmpty(InventTable _this)
    {
        return _this.MyExtField == '' ? true : false;
    }

}

D365/ AX 7 - Adding method to table using extension class

D365/ AX 7 - Adding method to table using extension class

AX 7 won’t allow user to add new methods to table or table extension. So here I will be demonstrating how to add new methods to table using extension class.

Lets say our requirement is to add new method to VendTable to get address of Vendors.

First step is to create a class, have a name that has the _Extension suffix and should be static.

public static class VendTable_Extension
{
}

Next Step is to create method, method should always be public static and first parameter should be Table buffer.

public static class VendTable_Extension
{
    public static Notes getVendAddress(VendTable vendTable)
    {
        DirPartyTable       dirPartyTable;
        LogisticsLocation   logisticsLocation;

        select dirPartyTable where dirPartyTable.RecId == vendTable.Party
        join logisticsLocation
        where dirPartyTable.PrimaryAddressLocation == logisticsLocation.RecId;      
       
        return logisticsLocation.postalAddress();
    }

}

Once we will build our project, this method will be usable as if it is method of VendTable.

I have tested the same using job.

class VendTable_Demo
{        
    /// <summary>
    /// job to test vendtable extension method
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {    
        VendTable vendTable;

        vendTable = VendTable::find("1001"); 
        
        info(strFmt("Vendor Address - %1",vendTable.getVendAddress()));
      
    }



Output:-


Wednesday 30 May 2018

Get Company Information using x++

tatic void CompanyInfo(Args _args)
{
    CompanyInfo                     companyInfo;
    DirPartyTable                   dirPartyTable;
    LogisticsElectronicAddress      electronic;
    LogisticsPostalAddress          postal;
    LogisticsLocation               location;

    select companyInfo where companyInfo.Name== "Bharat Financial Inclusion Limited"

     Join dirPartyTable

         where companyInfo.RecId == dirPartyTable.RecId

               Join electronic

                   where electronic.RecId == dirPartyTable.PrimaryContactEmail
                        || electronic.RecId == dirPartyTable.PrimaryContactFax
                        || electronic.RecId == dirPartyTable.PrimaryContactPhone
                        || electronic.RecId == dirPartyTable.PrimaryContactURL

                       Join location

                           where location.RecId == dirPartyTable.PrimaryAddressLocation

                                join postal

                                    where postal.Location == location.RecId;

        info(strFmt("%1--%2--%3",dirPartyTable.Name, postal.Address, electronic.Locator));


}

Check Mandatory fields in table level in Ax 2012

static void checkMandatoryFields(Args _args)
{
    DictTable       dictTable;
    DictField       dictField;
    int             i, cnt;

    dictTable = new DictTable(tableNum(HcmWorker));
    cnt = dictTable.fieldCnt();
    for (i= 1; i<=cnt;i++)
    {
        dictField = new DictField(tableNum(HcmWorker),dictTable.fieldCnt2Id(i));
        if (dictField.mandatory())
        {
            info (strFmt("Field %1 is mandatory.",dictField.label()));
        }
    }

}

Add Image for Item in Ax 2012

static void AddImage(Args _args)
{

    DocuActionArchive                           docuActionArchive;
    EcoResProductImageManagement    productImageManagement;
    EcoResProductImageThumbnail       ecoResProductImageThumbnail;
    DocuRef                                            docuRef;
    DocuValue                                        docuValue;
    EcoResProductImage                       ecoResProductImage;
    InventTable                                       inventTable;
   
// Specify the display product number
   
    InventTable = InventTable::find("25231");//Item ID
    ttsBegin;
    docuRef.TypeId     = "File";
    docuRef.RefTableId = inventTable.TableId;
    docuRef.RefRecId   = InventTable.RecId;
    docuRef.RefCompanyId = inventTable.dataAreaId;
    docuRef.ActualCompanyId = curext();
    docuRef.insert();

    docuActionArchive = DocuAction::newDocuRef(docuRef);
    docuActionArchive.add(docuRef,"C:\\Users\\Sandeep.madupu\\Desktop\\image.jpg");

    ecoResProductImage.RefRecId         = docuRef.RecId;
    ecoResProductImage.RefRecord        = docuRef.RefRecId;
    ecoResProductImage.FileName         = "image.jpg";
    ecoResProductImage.Usage            = EcoResProductImageUsage::External;
    ecoResProductImageThumbnail         = new EcoResProductImageThumbnail(false);
    ecoResProductImage.MediumSize       =     ecoResProductImageThumbnail.generateThumbnail(250,250,docuRef);
    ecoResProductImage.ThumbnailSize    = ecoResProductImageThumbnail.generateThumbnail(50,50,docuRef);

    if (ecoResProductImage.MediumSize == connull())
    {
        info("Medium size image cannot be generated for this file");
    }
    if (ecoResProductImage.ThumbnailSize == connull())
    {
        info("Thumbnail cannot be generated for this file");
    }
    ecoResProductImage.insert();
    ttsCommit;
}

Tuesday 29 May 2018

Multi Select Look up in Class level in X++

                                 Multi Select Look Up in Class

class Nar_productvarientsDialog extends Runbase
{

    DialogRunbase                dialog;
    DialogGroup                 dialogGrp;

    FormBuildStringControl      fbsCtrlPromotion;
    FormStringControl           fsCtrlPromotion;
    container                   returnPromotion;
    SysLookupMultiSelectCtrl    msCtrlPromotion;
    str                         promotionRange;
}
---------------------------------------------------------------------------------------------------
public query buildQuery()
{
    Query                   q          = new Query();
    QueryBuildDataSource    qbds;
    InventTable             inventTable;

    ;
    qbds    = q.addDataSource(tablenum(InventTable));
    qbds.addSelectionField(fieldNum(InventTable,ItemId));
    while select inventTable
    {
        qbds.addRange(fieldNum(InventTable,ItemId)).value(inventTable.ItemId);
    }
    return q;

}
--------------------------------------------------------------------------------------------------------
protected Object dialog()
{
    FormBuildControl    setupGroupControl;
    ;
    dialog = super();
    dialog.alwaysOnTop(true);
    dialog.windowType(FormWindowType::Standard);
    dialogGrp = dialog.addGroup('Promotion');
    setupGroupControl = dialog.formBuildDesign().control(dialogGrp.formBuildGroup().id());
    fbsCtrlPromotion = setupGroupControl.addControl(FormControlType::String, identifierstr(DICPromotionID));
    fbsCtrlPromotion.label('Promotion id');
    dialog.allowUpdateOnSelectCtrl(true);
    this.dialogSelectCtrl();

    return dialog;
}
---------------------------------------------------------------------------------------------------------
public void dialogPostRun(DialogRunbase _dialog)
{
    FormRun formRun;

    super(dialog);

    formRun = _dialog.dialogForm().formRun();

    if (formRun)
    {
        fsCtrlPromotion = formRun.design().control(fbsCtrlPromotion.id());
        /// you can build with your own query, or created query in system by method construct
        msCtrlPromotion = SysLookupMultiSelectCtrl::constructWithQuery(formRun, fsCtrlPromotion, this.buildQuery());
    }
}
--------------------------------------------------------------------------------------------------------
public boolean getFromDialog()
{
    boolean         ret;
    #Characters

    ret = super();

    if (msCtrlPromotion)
    {
        returnPromotion = msCtrlPromotion.getSelectedFieldValues(); // get actual value of the selected rows
        //returnPeriodRecId = msCtrlPromotion.get();  // get RecIds of the selected rows  (it's very useful when you want to look one field and return another field value)
    }
    promotionRange    = con2StrUnlimited(returnPromotion,#comma);

    return ret;
}
----------------------------------------------------------------------------------------------------------------
public static void main(Args _args)
{
    Nar_productvarientsDialog           nar_productvarientsDialog;

    nar_productvarientsDialog   = new Nar_productvarientsDialog();
    if(nar_productvarientsDialog.prompt())
    {
      nar_productvarientsDialog.run();
    }
}

Item Variants Exporting Using X++

                               Item Variants Exporting 

public void ProductVarientsExport(ItemId    _itemid)
{
    EcoResProduct                           ecoResProduct;
    EcoResProductMaster                ecoResProductMaster;
    InventDimCombination              inventDimCombination;
    EcoResProductTranslation          ecoResProductTranslation;
    EcoResDistinctProductVariant    ecoResDistinctProductVariant;
    InventDim                                    inventDim;
    InventTable                                  inventTable;

    SysExcelApplication                    excel;
    SysExcelWorkbooks                    workbooks;
    SysExcelWorkbook                      workbook;
    SysExcelWorksheets                    worksheets;
    SysExcelWorksheet                      worksheet;
    SysExcelFont                               font;
    SysExcelStyles                            styles;
    SysExcelStyle                              style;
    SysExcelCells                              cells;
    SysExcelCell                                cell;
    int                                                 row,i;
    container                                      con;
    ItemId                                          items;

    excel       = SysExcelApplication::construct();
    workbooks   = excel.workbooks();
    workbook    = workbooks.add();
    worksheets  = workbook.worksheets();
    worksheet   = worksheets.itemFromNum(1);
    //add bold and color
    //worksheet.rows().item(1).style('Header');
    styles      = workbook.styles();
    style       = styles.add("Header");
    font        = style.font();
    font.bold(true);
    font.color(255);

    cells = worksheet.cells();
    cells.range('A:A').numberFormat('@');
    cells.range('C:C').numberFormat('@');

    cell = cells.item(row+1, 1);
    cell.value("Item Number");
    cell = cells.item(row+1, 2);
    cell.value("Item Name");
    cell = cells.item(row+1, 3);
    cell.value("item ConfigurationId");
    cell = cells.item(row+1, 4);
    cell.value("item ColorId");
    cell = cells.item(row+1, 5);
    cell.value("item SizeId");
    cell = cells.item(row+1, 6);
    cell.value("item StyleId");
    cell = cells.item(row+1, 7);
    cell.value("item BatchId");
    cell = cells.item(row+1, 8);
    cell.value("item SiteId");
    cell = cells.item(row+1, 8);
    cell.value("item WareHouse");


    con = str2con(_itemid);
    for(i=1;i<=conLen(con);i++)
    {
        items = conPeek(con,i);
            while select ecoResProductMaster
                        where ecoResProductMaster.DisplayProductNumber ==items //"C0003"
                            join ecoResDistinctProductVariant
                                where ecoResDistinctProductVariant.ProductMaster == ecoResProductMaster.RecId
                                    join inventDimCombination
                                        where inventDimCombination.DistinctProductVariant == ecoResDistinctProductVariant.RecId
                                            join inventDim
                                                where inventDim.inventDimId == inventDimCombination.InventDimId
                                                    join  ecoResProductTranslation
                                                        where  ecoResProductTranslation.Product== ecoResProductMaster.RecId
            {
                row++;
                cell = cells.item(row+1, 1);
                cell.value(inventDimCombination.ItemId);
                cell = cells.item(row+1, 2);
                cell.value(ecoResProductTranslation.Name);
                cell = cells.item(row+1, 3);
                cell.value(inventDim.configId);
                cell = cells.item(row+1, 4);
                cell.value(inventDim.InventColorId);
                cell = cells.item(row+1, 5);
                cell.value(inventDim.InventSizeId);
                cell = cells.item(row+1, 6);
                cell.value(inventDim.InventStyleId);
                cell = cells.item(row+1, 7);
                cell.value(inventDim.inventBatchId);
                cell = cells.item(row+1, 8);
                cell.value(inventDim.InventSiteId);
                cell = cells.item(row+1, 9);
                cell.value(inventDim.InventLocationId);
            }
    }
    excel.visible(true);
    worksheet.columns().autoFit();
}

Get Purchase Order Financial Dimensions Using X++

Public void run()
{
    Di_FinancialDimensionsTmp         Di_FinancialDimensionsTmp;
    PurchLine                                        purchLine;
    PurchTable                                      purchTable;
    DimensionAttribute                        dimensionAttribute;
    DimensionAttributeValue               dimensionAttributeValue;
    Dimensionattributevalueset            dimensionattributevalueset;
    DimensionAttributeValueSetItem   dimensionAttributeValueSetItem;


    while select PurchLine
    {
     while select dimensionAttributeValueSetItem
        where dimensionAttributeValueSetItem.DimensionAttributeValueSet == purchLine.DefaultDimension
          join dimensionAttributeValue
            where dimensionAttributeValue.RecId == dimensionAttributeValueSetItem.DimensionAttributeValue
              join  dimensionAttribute
                where dimensionAttribute.RecId == dimensionAttributeValue.DimensionAttribute
     {
        switch (DimensionAttribute.Name)
        {
            case BusinessUnit     :
            Di_FinancialDimensionsTmp.BusinessUnit = dimensionAttributeValueSetItem.DisplayValue;
            break;
            case CostCenter :
            Di_FinancialDimensionsTmp.CostCenter    = dimensionAttributeValueSetItem.DisplayValue;
            break;
            case Department :
            Di_FinancialDimensionsTmp.Department    = dimensionAttributeValueSetItem.DisplayValue;
            break;
            case ItemGroup :
            Di_FinancialDimensionsTmp.ItemGroup     = dimensionAttributeValueSetItem.DisplayValue;
            break;
            case Project:
            Di_FinancialDimensionsTmp.Project         = dimensionAttributeValueSetItem.DisplayValue;
            break;
        }
        Di_FinancialDimensionsTmp.LineNumber = PurchLine.LineNumber;
     }
    Di_FinancialDimensionsTmp.Purchid = purchLine.PurchId;
    Di_FinancialDimensionsTmp.insert();
}

User Based Security Roles,Duties,Privileges in Ax 2012

                   User Base Security Roles,Duties,Privileges

public class FormRun extends ObjectRun
{
    SysLookupMultiSelectCtrl        selectuser;
}
-----------------------------------
public void init()
{
    DI_UserSecurity                 dI_UserSecurities;

    Query                           query;
    QueryBuildDataSource            qbds;
    QueryBuildFieldList             qbfl;

    query = new Query();
    super();

    qbds  = query.addDataSource(tableNum(UserInfo));
    qbds.fields().dynamic(NoYes::Yes);
    qbfl  =   qbds.fields().addField(fieldNum(UserInfo, id));
    selectuser =  SysLookupMultiSelectCtrl::constructWithQuery(element,UserID,query);

    delete_from dI_UserSecurities;
}
-------------------------------------------
void clicked()
{
    DI_UserSecurity             userSecurity;
    SecurityRole                securityRole,securityRole1;
    SecurityUserRole            securityUserRole;
    SecurityRoleTaskGrant       securityRoleTaskGrant;
    SecurityTask                securityTask,securityTask1;
    securitySubTask             securitySubTask;
    SecuritysubRole             SecuritysubRole;
    SysUserInfo                 sysUserInfo;

    int                         i;
    str 100                     dutyName;
    container                   con;
    Description                 value;
    super();

    delete_from userSecurity;

    con = selectuser.getSelectedFieldValues();
    if(UserId.valueStr() != "")
    {
      for (i = 1; i <= conLen(con); i++)
        {
           userSecurity.clear();
           value = conPeek(con,i);
           while select RecId,Name from securityRole
                    join User,SecurityRole from securityUserRole
                         where securityRole.RecId == SecurityUserRole.SecurityRole
                         && SecurityUserRole.User == value
            {
               if(securityRole.RecId)
               {
                 while select SecurityRole,SecurityTask from  securityRoleTaskGrant
                         where securityRoleTaskGrant.SecurityRole == securityRole.RecId
                    join securityTask
                         where securityTask.RecId == securityRoleTaskGrant.SecurityTask
                             && securityTask.Type == SecurityTaskType::Duty
                    join SecurityTask,SecuritySubTask from securitySubTask
                         where securitySubTask.SecurityTask == securityTask.RecId
                    join securityTask1
                         where  securityTask1.RecId == securitySubTask.SecuritySubTask
                    {
                        userSecurity.DI_UserId      = value;
                        userSecurity.DI_Role        = securityRole.Name;
                        userSecurity.DI_Duty        = SysLabel::labelId2String(securityTask.Name);
                        userSecurity.DI_Privilege   = SysLabel::labelId2String(securityTask1.Name);
                        userSecurity.insert();
                    }
                }
                if(userSecurity.DI_Duty == "" && userSecurity.DI_Privilege == "")
                {
                    userSecurity.DI_UserId      = value;
                    userSecurity.DI_Role        = securityRole.Name;
                    userSecurity.DI_Duty        = SysLabel::labelId2String(securityTask.Name);
                    userSecurity.DI_Privilege   = SysLabel::labelId2String(securityTask1.Name);
                    userSecurity.insert();
                }
             }
        }
    }
    else
    {
        while select Id from sysUserInfo
        {
            if(sysUserInfo.Id)
            {
              userSecurity.clear();
              while select RecId,Name from securityRole
                    join User,SecurityRole from securityUserRole
                         where securityRole.RecId == SecurityUserRole.SecurityRole
                         && SecurityUserRole.User == sysUserInfo.Id
              {
               if(securityRole.RecId)
               {
                 while select SecurityRole,SecurityTask from  securityRoleTaskGrant
                         where securityRoleTaskGrant.SecurityRole == securityRole.RecId
                    join securityTask
                         where securityTask.RecId == securityRoleTaskGrant.SecurityTask
                             && securityTask.Type == SecurityTaskType::Duty
                    join SecurityTask,SecuritySubTask from securitySubTask
                         where securitySubTask.SecurityTask == securityTask.RecId
                    join securityTask1
                         where  securityTask1.RecId == securitySubTask.SecuritySubTask
                        {
                            userSecurity.DI_UserId      = sysUserInfo.Id;
                            userSecurity.DI_Role        = securityRole.Name;
                            userSecurity.DI_Duty        = SysLabel::labelId2String(securityTask.Name);
                            userSecurity.DI_Privilege   = SysLabel::labelId2String(securityTask1.Name);
                            userSecurity.insert();
                        }
                    }
                    if(userSecurity.DI_Duty == "" && userSecurity.DI_Privilege == "")
                    {
                        userSecurity.DI_UserId      = sysUserInfo.Id;
                        userSecurity.DI_Role        = securityRole.Name;
                        userSecurity.DI_Duty        = SysLabel::labelId2String(securityTask.Name);
                        userSecurity.DI_Privilege   = SysLabel::labelId2String(securityTask1.Name);
                        userSecurity.insert();

                    }
                }
            }
        }
    }
    DI_UserSecurity_ds.research();
}

Multi Select Look up in form Level in Ax 2012

Multi Select Look Up in Form

public class FormRun extends ObjectRun
{
    SysLookupMultiSelectCtrl       msCtrlCust;
    QueryBuildDataSource           qbds;
    QueryBuildFieldList            qbfl;
} 
------------------------------------------
public void init()
{
    Query query = new Query();
    super();

    qbds = query.addDataSource(tableNum(CustTable));
    qbds.fields().dynamic(NoYes::Yes);
    qbfl   =   qbds.fields().addField(fieldNum(CustTable, AccountNum));

    msCtrlCust = SysLookupMultiSelectCtrl::constructWithQuery(element, CustMultiLookUp_AccountNum, query);

}

---------------------------------------
void clicked()
{
    container   con;
    int         i;
    str         multiSelectString;
    Args        args;
    container   values = msCtrlCust.get();

    super();

    args = new Args();

    for (i = 1; i <= conLen(values); i++)
    {
        con = conIns(con,1,conPeek(values,i));
    }
    multiSelectString = con2Str(con,',');
    args.parm(multiSelectString);
    new MenuFunction(menuitemDisplayStr(CustMultiLookUpData), MenuItemType::Display).run(args);
}
---------------------------------------------

PO Invoicing Using SysOpration Frame work and Multi Threading in AX 2012

SysOperation Frame Work-Multi Threading

1. Purchase order invoicing using SysOperation Frame Work
2. Batch should be run Multi Threading based.




step1: Create New Query add purchTable .
       and Set Ranges  1.PurchStatus -->Received(Should Be locked)
                                  2.InterCompanyOrder -->No(Should be hide)


Step2:
Create 5 Classes:
1. PurchInvoiceDataContract
2. PurchInvoiceUIBuilder
3.PurchInvoiceController
4.PurchInvoiceTaskService
5.PurchInvoiceTaskContract

Code:
PurchInvoiceDataContract:

/// <summary>
/// Class that generates parameters for the Purchase Order Invoicing
/// </summary>
/// <remarks>
/// Developed for PurchaseOrder_Invoice_Batch on 05/23/2018
/// </remarks>
[
    DataContractAttribute,
    SysOperationContractProcessingAttribute(classStr(CBDPurchInvoiceUIBuilder), SysOperationDataContractProcessingMode::CreateUIBuilderForRootContractOnly)
]
public class  PurchInvoiceDataContract
{
    PurchStatus         purchStatus;
    PurchUpdate         purchUpdate;
    str                 packedQuery;
}
---------------------
/// <summary>
/// sets the query
/// </summary>
/// <param name="_query">
/// Query input to set
/// </param>
/// <remarks>
/// Retains the last values in the query
/// </remarks>
public void setQuery(Query _query)
{
     packedQuery = SysOperationHelper::base64Encode(_query.pack());
}
----------------
/// <summary>
/// gets the query
/// </summary>
/// <returns>
/// new query
/// </returns>
/// <remarks>
/// Used to select values form the query.
/// </remarks>
public Query getQuery()
{
    return new Query(SysOperationHelper::base64Decode(packedQuery));
}

--------
[
    DataMemberAttribute,
    AifQueryTypeAttribute('_packedQuery', querystr(CBDPurchTable))
]
public str parmQuery(str _packedQuery = packedQuery)
{
        packedQuery = _packedQuery;
        return packedQuery;
}
----------------
[
    DataMemberAttribute,
    SysOperationLabelAttribute(literalStr('Status')),
    SysOperationHelpTextAttribute(literalStr('Purchase Status')),
    SysOperationDisplayOrderAttribute('1')
]
public PurchStatus parmpurchstatus(PurchStatus _PurchStatus = PurchStatus::Received)
{
    PurchStatus = _PurchStatus;

    return PurchStatus;
}
-------------
[
    DataMemberAttribute,
    SysOperationLabelAttribute(literalStr('Default Quantity for lines')),
    SysOperationHelpTextAttribute(literalStr('Default Quantity for lines.')),
    SysOperationDisplayOrderAttribute('2')
]
public PurchUpdate parmpurchupdate(PurchUpdate _purchUpdate = purchUpdate::PackingSlip)
{
    purchUpdate = _purchUpdate;
    return purchUpdate;
}

 PurchInvoiceUIBuilder:

/// <summary>
/// This class is UI Builder for the Purch Invoice
/// </summary>
/// <remarks>
/// <c>CBDPurchInvoiceDataContract</c> is used.
/// </remarks>
/// N Developed for PurchInvoice_Batch on 5/21/2018
class  PurchInvoiceUIBuilder extends SysOperationAutomaticUIBuilder
{
DialogField                         dialogFieldPurchStatus;
DialogField                         dialogFieldsSpecQty;

   PurchInvoiceDataContract     contract;
}
------------
/// <summary>
/// Methods used to set the propertes of the dialog fields.
/// </summary>
/// <remarks>
/// <c>CBDPurchInvoiceDataContract</c> is used.
/// </remarks>
public void postBuild()
{
    super();
    contract = this.dataContractObject();

    dialogFieldPurchStatus     = this.bindInfo().getDialogField(contract, methodstr(CBDPurchInvoiceDataContract, parmpurchstatus));
    dialogFieldsSpecQty        = this.bindInfo().getDialogField(contract, methodstr(CBDPurchInvoiceDataContract, parmpurchupdate));

    dialogFieldPurchStatus.allowEdit(false);
    dialogFieldsSpecQty.allowEdit(false);

}
PurchInvoiceController

/// <summary>
/// Invoicing  Purchase order for each purchase order having status received
/// </summary>
/// <remarks>
/// Developed for  PurchaseOrder_Invoice_Batch on 05/23/2018
/// </remarks>
class  PurchInvoiceController extends SysOperationServiceController//MultiThreadProcessController
{
    PositiveNumber                  TaskVolume;
    PositiveNumber                  NoOfThreads ;
    PositiveNumber                  noOfRecordstoProcess;

}
----------------
/// <summary>
/// Used for returning the batch execution mode
/// </summary>
/// <returns>
/// true
/// </returns>
/// <remarks>
/// Used for returning the batch execution mode
/// </remarks>

public boolean canGoBatch()
{
    return true;
}
-------------------
public ClassDescription caption()
{
    ClassDescription ret;

    ret = "Purchase invoice";

    return ret;
}
--------------------
protected void new(IdentifierName _className = '',
                   IdentifierName _methodName = '',
                   SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Synchronous)
{
    super();

    this.parmClassName(_className);
    this.parmMethodName(_methodName);
    this.parmExecutionMode(_executionMode);

    this.parmRegisterCallbackForReliableAsyncCall(false);
}
---------------------
/// <summary>
/// Invoicing of Purchase orders
/// </summary>
/// <param name="_contract">
/// Data Contract
/// </param>
/// NS Developed for PurchaseOrder_Invoice_Batch 05/23/2018s
[
    SysEntryPointAttribute(true),
    AifCollectionTypeAttribute('CBDPurchInvoiceDataContract', Types::Class, classStr(CBDPurchInvoiceDataContract))
]
public void process(PurchInvoiceDataContract        _contract)
{
    Counter                                 maxThreadsAllowed;
    Counter                                 maxVolumePerRun;
    Counter                                 maxBundlePerThread;
    Counter                                 taskCounter = 0, bundleCounter = 0;
    BatchHeader                             batchHeader;
    BatchGroupId                            groupID;
    SysOperationServiceController           syscontroller;
    int                                     noOfRecords, noOfValues;
    Query                                   query = _contract.getQuery();
    QueryRun                                queryRun;
    PurchTable                              purchTable;
    Batch                                   batch;
    List                                    poList = new List(Types::String);
    CBDPurchInvoiceTaskContract             taskContract;
    Counter                                 taskRemainder, recordRemainder;
    boolean                                 isExecutingBatch = BatchHeader::isExecutingInBatch();

    queryRun    = new queryRun(query);
    noOfRecords = SysQuery::countTotal(queryRun);

    taskVolume  = 30;//this.getVolumePerTask(MultiThreadProcessType::PurchaseInvoice);
    noOfThreads = 2;//this.getNoOfTasksAlloted(MultiThreadProcessType::PurchaseInvoice);
    noOfValues  = taskVolume * noOfThreads;

    maxVolumePerRun     = max (noOfRecords, taskVolume);
    maxThreadsAllowed   = noOfThreads;

    if( maxVolumePerRun >= noOfValues )
    {
        maxBundlePerThread = taskVolume;
    }
    else
    {
        taskRemainder       = maxVolumePerRun mod noOfThreads;
        recordRemainder     = maxVolumePerRun div noOfThreads;
        maxBundlePerThread  = taskRemainder? recordRemainder + 1 : recordRemainder;
    }
    while(queryRun.next())
    {
        purchTable = queryRun.get(tableNum(PurchTable));
        poList.addEnd(purchTable.PurchId);

        bundleCounter++;
        if(isExecutingBatch)
        {
            if(!BatchHeader)
            {
                BatchHeader = BatchHeader::getCurrentBatchHeader();
                groupID     = BatchHeader::getCurrentBatchTask().GroupId;
            }

            if (bundleCounter == maxBundlePerThread)
            {
                bundleCounter = 0;
                taskCounter++;

                syscontroller = new SysOperationServiceController(classStr(CBDPurchInvoiceTaskService), methodStr(CBDPurchInvoiceTaskService, PoInvoice));
                taskContract = Syscontroller.getDataContractObject('_taskcontract');
                taskContract.parmPurchIDCon(poList.pack());
                taskContract.parmpurchupdate();
                syscontroller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
                syscontroller.batchInfo().parmGroupId(groupID);

                BatchInfo  = Syscontroller.batchInfo();
                BatchInfo.parmCaption(strFmt("PO invoice multithreading: %1",taskCounter));
                batch = batchHeader::getCurrentBatchTask();
                batchHeader.addRuntimeTask(Syscontroller, batch.RecId);
                poList = new List(Types::String);

            }
            if (taskCounter >= maxThreadsAllowed)
            {
                break;
            }
        }
    }
    if (bundleCounter && batchHeader)
    {
        // need to write same logic

    }
    if (batchHeader)
    {
        // save the batchheader
        batchHeader.save();
    }
    else
    {
         Syscontroller = new SysOperationServiceController(classStr(CBDPurchInvoiceTaskService), methodStr(CBDPurchInvoiceTaskService, PoInvoice));
         taskContract = Syscontroller.getDataContractObject('_taskcontract');//('_contract');
         taskContract.parmPurchIDCon(poList.pack());
         taskContract.parmpurchupdate();
         syscontroller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
         Syscontroller.run();

    }
}
----------------------
/// <summary>
/// Used For constructing the class PurchInvoiceController
/// </summary>
/// <param name="_mode">
/// Synchronous mode execution
/// </param>
/// <returns>
/// returns PurchInvoiceController.
/// </returns>
/// <remarks>
/// Used for construction of class.
/// </remarks>

public static PurchInvoiceController construct(SysOperationExecutionMode  _mode = SysOperationExecutionMode::Synchronous)
{

    return new PurchInvoiceController(classStr(PurchInvoiceController),
                                        methodStr(PurchInvoiceController, process),
                                        _mode);

}
--------------------
public static void main(Args _args)
{
    PurchInvoiceController            controller = new  PurchInvoiceController();
    controller = CBDPurchInvoiceController::construct();
    controller.parmDialogCaption("PO invoicing");
    controller.startOperation();

}
PurchInvoiceTaskService
/// <summary>
/// Task Service class. This will run as separate thread to execute Purchase invoice posting based on InvoiceId.
/// </summary>
/// <remarks>
/// Task Service class. This will run as separate thread to execute Purchase invoice posting based on InvoiceId.
/// </remarks>
//N Developed for PurchaseOrder_Invoice_Batch on 05/23/2018
public class  PurchInvoiceTaskService
{
}
-------------------------
/// <summary>
/// Entry point for task Service class. This will run as separate thread to execute Purchase invoice posting based on InvoiceId.
/// </summary>
/// <param name="_taskcontract">
/// object of data contract class to retrieve Invoice ids
/// </param>
/// <remarks>
/// Entry point for task Service class. This will run as separate thread to execute Purchase invoice posting based on InvoiceId.
/// </remarks>
[
    SysEntryPointAttribute(true),
    AifCollectionTypeAttribute('PurchInvoiceTaskContract', Types::Class, classStr(PurchInvoiceTaskContract))
]
public void POInvoice(PurchInvoiceTaskContract   _taskcontract)
{
    PurchId                           purchid;
    NumberSeq                     numberSeq;
    PurchTable                      purchTable;
    PurchFormLetter             purchFormLetter;
    VendInvoiceInfoTable     vendInvoiceInfoTable;
    PurchUpdate                   purchUpdate;
    Counter                          retryCount;
    List                                poList = new List(Types::Container);
    ListEnumerator             pickListEnumerator;

    #OCCRetryCount

    numberSeq           = NumberSeq::newGetNum(PurchParameters::numRefCBDPurchInvoiceId());
    purchUpdate         = _taskcontract.parmpurchupdate();
    poList              = List::create(_taskcontract.parmpurchidcon());
    pickListEnumerator  = poList.getEnumerator();
    while (pickListEnumerator.moveNext())
    {
        purchid      = pickListEnumerator.current();
        purchTable   = PurchTable::find(purchid);
        if(purchTable)
        {
            try
            {
                purchFormLetter    = PurchFormLetter::construct(DocumentStatus::Invoice);

                purchFormLetter.purchTable(purchTable);
                purchFormLetter.initParmDefault();
                purchFormLetter.transDate          (systemdateget());
                purchFormLetter.specQty            (purchUpdate);
                purchFormLetter.parmParmTableNum   (numberSeq.num());
                purchFormLetter.proforma           (false);
                purchFormLetter.printFormLetter    (false);
                purchFormLetter.initParameters     (purchFormLetter.purchParmUpdate(),             PrintOut::Current);  // Printout
                purchFormLetter.initLinesQuery();

                select forUpdate vendInvoiceInfoTable where vendInvoiceInfoTable.ParmId == purchFormLetter.parmId();
                ttsBegin;
                    if (vendInvoiceInfoTable)
                    {
                        vendInvoiceInfoTable.DocumentDate = systemdateget();
                        vendInvoiceInfoTable.update();
                    }
                ttsCommit;
                purchFormLetter.run();
                if (purchtable::find(purchTable.PurchId).documentstatus == documentstatus::invoice)
                {
                    info(strfmt("%1 order invoiced",purchTable.PurchId));
                }
            }
            catch (Exception::Deadlock)
            {
                retry;
            }
            catch (Exception::UpdateConflict)
            {
                if (retryCount < #RetryNum)
                {
                    retry;
                }
                else
                {
                    throw Exception::UpdateConflictNotRecovered;
                }

            }
            catch
            {
                info(purchTable.PurchId);
                continue;
            }
        }
    }
}
PurchInvoiceTaskContract
/// <summary>
/// Class that generates parameters for the Purchase Invoicing
/// </summary>
/// N Developed for  PurchaseOrder_Invoice_Batch on 05/23/2018
[DataContractAttribute]
public class PurchInvoiceTaskContract
{
    container               purchIdCon;
    PurchUpdate         purchUpdate;
    str                          packedQuery;
}
--------------------------------
[
     DataMemberAttribute,
     SysOperationLabelAttribute(literalStr('Purchase order')),
     SysOperationHelpTextAttribute(literalStr('Purchase order number')),
     SysOperationDisplayOrderAttribute('1')
]
public container parmpurchidcon(container  _purchIdCon = purchIdCon)
{
    purchIdCon = _purchIdCon;

    return purchIdCon;
}
----------------------------
[
    DataMemberAttribute,
    SysOperationLabelAttribute(literalStr('Default Quantity for lines')),
    SysOperationHelpTextAttribute(literalStr('Default Quantity for lines.')),
    SysOperationDisplayOrderAttribute('2')
]
public PurchUpdate parmpurchupdate(PurchUpdate _purchUpdate = purchUpdate::PackingSlip)
{
    purchUpdate = _purchUpdate;
    return purchUpdate;
}