Tuesday 29 May 2018

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;
}



No comments:

Post a Comment