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