Import CSV File and batch job to generate the serial number registration PO
Today we have a requirement to create the batch job to create a workflow change status and also done the serial number registration.
====================================================================
/// <summary>
/// The <c>AVAMassUpdateImportDialog</c> class is to update the Mass Purchase order detail.
/// </summary>
class AVAMassUpdateImportDialog extends RunBaseBatch implements BatchRetryable
{
DialogRunbase dialog;
Filename filename;
private str availableTypes = ".csv";
private const str OkButtonName = 'OkButton';
private const str FileUploadName = 'FileUpload';
str textFile;
Set set = new Set(Types::Record);
SetEnumerator setEnumerator;
InventDimId inventDimID;
PurchTable purchTable;
PurchLine purchLine;
PurchId purchID;
TradeLineNumber purchLineNumber;
Name manufacturersOrderNum;
TransDate confirmDeliveryDate;
inventSerialId serialID;
int fileLineNumber;
container currentLine;
#define.CurrentVersion(1)
#localmacro.CurrentList
filename
#endmacro
/// <summary>
/// Returns false.
/// </summary>
/// <returns>
/// Always returns false.
/// </returns>
/// <remarks>
/// This method must be in this class because it is called from the <c>dialogRunbase</c> class.
/// </remarks>
public boolean canGoBatch()
{
return false;
}
/// <summary>
/// Determines whether the class is shown in the list of the <c>Journal</c> types.
/// </summary>
/// <returns>
/// true if the class is shown in the list of <c>Journal</c> types; otherwise, false.
/// </returns>
/// <remarks>
/// A class that can be used in a batch journal is a class where the same parameters can be used
/// repeatedly. The dialog can be shown and the parameters can be changed but parameters of some
/// classes might build on data that is only valid for a short time. Not all classes can be run two
/// times with the same parameters. If the <c>canGoBatch</c> method returns false, this method will not
/// have any effect.
/// </remarks>
protected boolean canGoBatchJournal()
{
return true;
}
/// <summary>
/// Returns a class that contains the methods that are described by the <c>RunBaseDialogable</c>
/// interface.
/// </summary>
/// <returns>
/// A class that contains the methods that are described by the <c>RunBaseDialogable</c> interface.
/// </returns>
/// <remarks>
/// A dialog can be either built by using the <c>Dialog</c> class or by using a class that is created
/// in the Application Object Tree (AOT).
/// </remarks>
public Object dialog()
{
DialogGroup dialogGroup;
FormBuildControl formBuildControl;
FileUploadBuild dialogFileUpload;
dialog = new DialogRunbase(AVAMassUpdateImportDialog::description(), this);
dialogGroup = dialog.addGroup(AVAMassUpdateImportDialog::description());
formBuildControl = dialog.formBuildDesign().control(dialogGroup.name());
dialogFileUpload = formBuildControl.addControlEx(classstr(FileUpload), FileUploadName);
dialogFileUpload.style(FileUploadStyle::Standard);
dialogFileUpload.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
dialogFileUpload.fileTypesAccepted(availableTypes);
dialogFileUpload.fileNameLabel("@SYS308842");
return dialog;
}
/// <summary>
/// Disables the dialog Ok button until the file upload is complete.
/// </summary>
/// <param name="_dialog">The <c>Runbase</c> dialog object.</param>
public void dialogPostRun(DialogRunbase _dialog)
{
FileUpload fileUpload = this.getFormControl(_dialog, FileUploadName);
fileUpload.notifyUploadCompleted += eventhandler(this.uploadCompleted);
this.setDialogOkButtonEnabled(_dialog, false);
}
/// <summary>
/// After the file has been uploaded, the Ok button is enabled.
/// </summary>
protected void uploadCompleted()
{
FileUpload fileUpload = this.getFormControl(dialog, FileUploadName);
FileUploadTemporaryStorageResult uploadResult = fileUpload.getFileUploadResult();
if (uploadResult.getUploadStatus())
{
fileUpload.notifyUploadCompleted -= eventhandler(this.UploadCompleted);
filename = fileUpload.fileName();
this.setDialogOkButtonEnabled(dialog, true);
}
else
{
warning(uploadResult.getLogMessage());
}
}
/// <summary>
/// Enables or disables the dialog Ok button.
/// </summary>
/// <param name = "_dialog">The <c>Runbase</c> dialog object.</param>
/// <param name = "_isEnabled">Indicates to enable or disable the Ok button.</param>
protected void setDialogOkButtonEnabled(DialogRunbase _dialog, boolean _isEnabled)
{
FormControl okButtonControl = this.getFormControl(_dialog, OkButtonName);
if (okButtonControl)
{
okButtonControl.enabled(_isEnabled);
}
}
protected FormControl getFormControl(DialogRunbase _dialog, str _controlName)
{
return _dialog.formRun().control(_dialog.formRun().controlId( _controlName));
}
/// <summary>
/// Contains the code that does the actual job of the class.
/// </summary>
public void run()
{
this.processCSVFile();
}
/// <summary>
/// Determines whether the batch task is run on the server or on a client.
/// </summary>
/// <returns>
/// Always returns false.
/// </returns>
/// <remarks>
/// Your classes that extend this class must override the <c>runsImpersonated</c> method and return
/// <c>false</c>, if you want those tasks to run on a client.
/// </remarks>
public boolean runsImpersonated()
{
return true;
}
public container pack()
{
return [#CurrentVersion, #CurrentList];
}
public boolean unpack(container _packedClass)
{
Integer version = conPeek(_packedClass, 1);
switch (version)
{
case #CurrentVersion:
[version, filename] = _packedClass;
break;
default:
return false;
}
return true;
}
public boolean validate(Object _calledFrom = null)
{
boolean ret = true;
if (!filename)
{
ret = checkFailed("@SYS18624");
}
return ret;
}
public static AVAMassUpdateImportDialog construct()
{
return new AVAMassUpdateImportDialog();
}
/// <summary>
/// contains the description of the dialog when it opens.
/// </summary>
/// <returns>
/// Description of the dialog.
/// </returns>
public static ClassDescription description()
{
return ("@AVAExtension:AVAMassUpdateImportDialog_description");
}
public static void main(Args args)
{
AVAMassUpdateImportDialog massUpdateImportDialog;
massUpdateImportDialog = AVAMassUpdateImportDialog::construct();
if (massUpdateImportDialog.prompt())
{
massUpdateImportDialog.runOperation();
}
}
/// <summary>
/// Execute the code to read excel and update the record
/// </summary>
public void processCSVFile()
{
#File
CommaTextStreamIo localStream;
FileUpload fileUploadControl = this.getFormControl(dialog, FileUploadName);
FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult();
if (fileUploadResult != null && fileUploadResult.getUploadStatus())
{
textFile = fileUploadResult.getDownloadUrl();
}
localStream = CommaTextStreamIo::constructForRead(File::UseFileFromURL(textFile));
if (localStream.status() != IO_Status::Ok)
{
throw error(strfmt('Is not possible to open the file. Error %1',enum2str(localStream.status())));
}
currentLine = localStream.read();
fileLineNumber++;
if(conlen(currentLine) == 1)
{
throw error('Is not possible to import the file, incorrect format');
}
currentLine = localStream.read();
while(currentLine)
{
fileLineNumber++;
purchID = conPeek(currentLine,1);
purchLineNumber = conPeek(currentLine,2);
manufacturersOrderNum = conPeek(currentLine,3);
confirmDeliveryDate = conPeek(currentLine,4);
serialID = conPeek(currentLine,5);
if(this.validateImportFile(purchID, purchLineNumber, serialID, localStream))
{
select firstonly purchtable
where purchtable.PurchId == purchID
&& purchTable.PurchStatus == PurchStatus::Backorder;
switch(purchTable.DocumentState)
{
case VersioningDocumentState::Draft:
this.executeDraftStatus(purchID, purchLineNumber, manufacturersOrderNum, confirmDeliveryDate);
this.draftSerialIDError(serialID, purchID);
currentLine = localStream.read();
break;
case VersioningDocumentState::InReview:
this.executeInreviewStatus(purchID, purchLineNumber, manufacturersOrderNum, confirmDeliveryDate);
this.updateSerialNumberInreviewStatus(purchID, purchLineNumber, serialID, manufacturersOrderNum, confirmDeliveryDate);
currentLine = localStream.read();
break;
case VersioningDocumentState::Approved:
Case VersioningDocumentState::Confirmed:
this.updateSerialNumberApprovedStatus(purchID, purchLineNumber, serialID, manufacturersOrderNum, confirmDeliveryDate);
currentLine = localStream.read();
break;
}
}
}
// Send complete set for the PO work flow status change which is found from the above condition.
this.submitToWorkFLow(set);
}
private boolean validateImportFile(purchID _purchID, TradeLineNumber _lineNumber, inventSerialId _serialID, CommaTextStreamIo _localStream)
{
boolean isValid = true;
if(!_purchID && !_lineNumber)
{
isValid = checkFailed(strFmt('PO no. & linenumber is missing in file row no %1',(fileLineNumber)));
currentLine = _localStream.read();
}
else if(!_purchID)
{
isValid = checkFailed(strFmt('PO no. is missing in file row no %1',(fileLineNumber)));
currentLine = _localStream.read();
}
else if(!_lineNumber)
{
isValid = checkFailed(strFmt('PO linenumer is missing in file row no %1',(fileLineNumber)));
currentLine = _localStream.read();
}
select firstonly purchLine
where purchLine.PurchId == _purchID
&& purchLine.LineNumber == _lineNumber
&& !purchLine.AVAManufacturersOrderNum
&& purchLine.PurchStatus != PurchStatus::Canceled;
if(purchLine.RecId && _serialID)
{
isValid = checkFailed(strFmt('Manufacturer’s order number is missing for Po no. %1 & line number %2 ',purchID, purchLineNumber));
currentLine = _localStream.read();
}
return isValid;
}
protected boolean canRunInNewSession()
{
return false;
}
/// <summary>
/// Code to create and confirm the registration of Po lines serial number
/// </summary>
/// <param name = "_purchInventTransID">Contains inventtransID.</param>
/// <param name = "_newSerialID">Conatins serial number from the import file.</param>
public void updateSerialNumberApprovedStatus(purchid _purchID, TradeLineNumber _lineNumber, InventSerialId _newSerialID, Name _manufacturersOrderNum, transdate _confirmDelDate)
{
InventTransWMS_Register inventTransWMS_register;
InventTrans inventTrans;
TmpInventTransWMS tmpInventTransWMS;
InventDim inventDim;
purchLine purchLineUpdate;
boolean purchLineIsUpdated;
select purchLineUpdate
where purchLineUpdate.PurchId == _purchID
&& purchLineUpdate.LineNumber == _lineNumber
&& purchLineUpdate.PurchStatus != PurchStatus::Canceled;
if(purchLineUpdate.RecId)
{
inventTrans = InventTrans::findTransId(purchLineUpdate.InventTransId);
inventDim = Inventdim::find(inventTrans.inventDimId);
if(!AVAMassUpdateImportDialog::existPurchLineDetails(_purchID, _lineNumber, _manufacturersOrderNum) || (_confirmDelDate != purchLineUpdate.ConfirmedDlv && _confirmDelDate))
{
ttsbegin;
purchLineUpdate.selectForUpdate(true);
purchLineUpdate.AVAManufacturersOrderNum = _manufacturersOrderNum;
if(_confirmDelDate)
{
purchLineUpdate.ConfirmedDlv = _confirmDelDate;
}
purchLineUpdate.doUpdate();
this.savePONoForWorkFLowApprovedStatus(purchLineUpdate.PurchId);
purchLineIsUpdated = true;
ttscommit;
info(strFmt('Successfully update done against Po no. %1 & line number %2',purchID, purchLineNumber));
}
if(_newSerialID
&& (!inventDim.inventSerialId || inventDim.inventSerialId != _newSerialID)
&& !purchLineIsUpdated)
{
try
{
ttsbegin;
/// If the order status in registered first we need to update the status to "ordered" and then confirm resition with update serial number.
if(inventTrans.StatusReceipt == StatusReceipt::Registered)
{
inventTrans.selectForUpdate(true);
inventTrans.StatusReceipt = StatusReceipt::Ordered;
inventTrans.update();
}
inventTransWMS_register = inventTransWMS_register::newStandard(tmpInventTransWMS);
tmpInventTransWMS.clear();
tmpInventTransWMS.initFromInventTrans(inventTrans);
tmpInventTransWMS.itemid = inventTrans.ItemId;
tmpInventTransWMS.InventQty = inventTrans.Qty;
inventDim.inventSerialId = _newSerialID;
tmpInventTransWMS.InventDimId = inventDim::findOrCreate(inventDim).inventDimId;
tmpInventTransWMS.AVAManufacturersOrderNum = _manufacturersOrderNum;
tmpInventTransWMS.modifiedField(fieldNum(tmpInventTransWMS, AVAManufacturersOrderNum));
if(_confirmDelDate)
{
tmpInventTransWMS.AVAConfirmedDlv = _confirmDelDate;
}
this.getInventDimID(tmpInventTransWMS.InventDimId);
tmpInventTransWMS.insert();
inventTransWMS_register.writeTmpInventTransWMS(tmpInventTransWMS, inventTrans, inventTrans.inventDim());
inventTransWMS_register.updateInvent(inventTrans);
info(strFmt("Serial number update succesfully %1", _newSerialID));
ttscommit;
}
catch( Exception::Warning)
{
ttsabort;
checkFailed(strFmt("%1", AVAMassUpdateImportDialog::getErrorMessage()));
}
catch( Exception::Error)
{
ttsabort;
checkFailed(strFmt("%1", AVAMassUpdateImportDialog::getErrorMessage()));
}
}
}
}
/// <summary>
/// Update serial number if already serial number in line and status of work flow is in review.
/// </summary>
public void updateSerialNumberInreviewStatus(purchid _purchID, TradeLineNumber _lineNumber, InventSerialId _newSerialID, Name _manufacturersOrderNum, transdate _confirmDelDate)
{
InventTransWMS_Register inventTransWMS_register;
InventTrans inventTrans;
TmpInventTransWMS tmpInventTransWMS;
InventDim inventDim;
purchLine purchLineInReview;
select purchLineInReview
where purchLineInReview.PurchId == _purchID
&& purchLineInReview.LineNumber == _lineNumber
&& purchLineInReview.PurchStatus != PurchStatus::Canceled;
if(purchLineInReview.RecId)
{
inventTrans = InventTrans::findTransId(purchLineInReview.InventTransId);
inventDim = Inventdim::find(inventTrans.inventDimId);
if(_newSerialID && inventDim
&& (inventDim.inventSerialId || inventDim.inventSerialId != _newSerialID))
{
try
{
ttsbegin;
/// If the order status in registered first we need to update the status to "ordered" and then confirm resition with update serial number.
if(inventTrans.StatusReceipt == StatusReceipt::Registered)
{
inventTrans.selectForUpdate(true);
inventTrans.StatusReceipt = StatusReceipt::Ordered;
inventTrans.update();
}
inventTransWMS_register = inventTransWMS_register::newStandard(tmpInventTransWMS);
tmpInventTransWMS.clear();
tmpInventTransWMS.initFromInventTrans(inventTrans);
tmpInventTransWMS.itemid = inventTrans.ItemId;
tmpInventTransWMS.InventQty = inventTrans.Qty;
inventDim.inventSerialId = _newSerialID;
tmpInventTransWMS.InventDimId = inventDim::findOrCreate(inventDim).inventDimId;
tmpInventTransWMS.AVAManufacturersOrderNum = _manufacturersOrderNum;
tmpInventTransWMS.modifiedField(fieldNum(tmpInventTransWMS, AVAManufacturersOrderNum));
if(_confirmDelDate)
{
tmpInventTransWMS.AVAConfirmedDlv = _confirmDelDate;
}
this.getInventDimID(tmpInventTransWMS.InventDimId);
tmpInventTransWMS.insert();
inventTransWMS_register.writeTmpInventTransWMS(tmpInventTransWMS, inventTrans, inventTrans.inventDim());
inventTransWMS_register.updateInvent(inventTrans);
info(strFmt("Serial number update succesfully %1", _newSerialID));
ttscommit;
}
catch( Exception::Warning)
{
ttsabort;
checkFailed(strFmt("%1", AVAMassUpdateImportDialog::getErrorMessage()));
}
catch( Exception::Error)
{
ttsabort;
checkFailed(strFmt("%1", AVAMassUpdateImportDialog::getErrorMessage()));
}
}
else
{
if(_newSerialID && !inventDim.inventSerialId)
{
checkFailed(strFmt('Serial numbers can only be update on approved purchase orders: %1',_purchID));
}
}
}
}
/// <summary>
/// Error message when try to update the serial number hold the standard error messages.
/// </summary>
static str getErrorMessage()
{
int infoPos;
str errorNotes;
int infoLength = Global::infologLine();
for(infoPos = 1; infoPos <= infoLength; infoPos++)
{
errorNotes += infolog.text(infoPos)+ "; ";
}
return errorNotes;
}
/// <summary>
/// Inventdim hold when we update the serial number
/// </summary>
/// <param name = "_inventDimID">Inventdim of the new serial number updated.</param>
public inventDimID getInventDimID(inventDimID _inventDimID = inventDimID)
{
inventDimID = _inventDimID;
return inventDimID;
}
/// <summary>
/// Change the Po workflow status to draft and then inreview
/// </summary>
/// <param name = "_set">All values are hold in a set which conatins the unique Po numbers.</param>
public void submitToWorkFLow(Set _set)
{
purchTable purchTableLoc;
if (!_set.empty())
{
setEnumerator = _set.getEnumerator();
while (setEnumerator.moveNext())
{
purchTableLoc = setEnumerator.current();
ttsbegin;
if(purchTableLoc.DocumentState == VersioningDocumentState::InReview)
{
purchTableLoc.selectForUpdate(true);
VersioningPurchaseOrder::newPurchaseOrder(purchTableLoc).cancelChangeRequest();
/// Submit the workflow it will change the approval status to Inreview and also maintain the history of workflow.
purchTableLoc.submitToWorkflow(workFlowTypeStr(PurchTableTemplate), '', false);
}
else
{
purchTableLoc.selectForUpdate(true);
purchTableLoc.ChangeRequestRequired = NoYes::Yes;
var versioningPurchaseOrder = VersioningPurchaseOrder::newPurchaseOrder(purchTableLoc);
versioningPurchaseOrder.createChangeRequest();
versioningPurchaseOrder.resetToDraft();
/// Submit the workflow it will change the approval status to Inreview and also maintain the history of workflow.
purchTableLoc.submitToWorkflow(workFlowTypeStr(PurchTableTemplate), '', false);
}
ttscommit;
}
}
}
/// <summary>
/// When the workflow status in draft it will execute the below code.
/// </summary>
public void executeDraftStatus(purchID _purchID, TradeLineNumber _lineNumber, Name _manufacturersOrderNum, Transdate _confirmDelDate)
{
PurchLine purchLineDraft;
select firstonly purchLineDraft
where purchLineDraft.PurchId == _purchID
&& purchLineDraft.LineNumber == _lineNumber
&& purchLineDraft.PurchStatus != PurchStatus::Canceled;
if(!AVAMassUpdateImportDialog::existPurchLineDetails(_purchID, _lineNumber, _manufacturersOrderNum) || (_confirmDelDate != purchLineDraft.ConfirmedDlv && _confirmDelDate))
{
ttsbegin;
purchLineDraft.selectForUpdate(true);
purchLineDraft.AVAManufacturersOrderNum = _manufacturersOrderNum;
if(_confirmDelDate)
{
purchLineDraft.ConfirmedDlv = _confirmDelDate;
}
purchLineDraft.doUpdate();
ttscommit;
info(strFmt('Successfully update done against Po no. %1 & line number %2',purchID, purchLineNumber));
}
}
/// <summary>
/// When the workflow status in draft and if serial number in the file below error msg will popups.
/// </summary>
public void draftSerialIDError(InventSerialId _serialID, PurchId _purchID)
{
if(_serialID)
{
checkFailed(strFmt('Serial numbers can only be update on approved purchase orders: %1',_purchID));
}
}
/// <summary>
/// When the workflow status in dIn Review it will execute the below code.
/// </summary>
public void executeInreviewStatus(purchID _purchID, TradeLineNumber _lineNumber, Name _manufacturersOrderNum, transdate _confirmDelDate)
{
PurchLine purchLineInreview;
select firstonly purchLineInreview
where purchLineInreview.PurchId == _purchID
&& purchLineInreview.LineNumber == _lineNumber
&& purchLineInreview.PurchStatus != PurchStatus::Canceled;
if(purchLineInreview.RecId)
{
if(!AVAMassUpdateImportDialog::existPurchLineDetails(_purchID, _lineNumber, _manufacturersOrderNum) || (_confirmDelDate != purchLineInreview.ConfirmedDlv && _confirmDelDate))
{
ttsbegin;
purchLineInreview.selectForUpdate(true);
purchLineInreview.AVAManufacturersOrderNum = _manufacturersOrderNum;
if(_confirmDelDate)
{
purchLineInreview.ConfirmedDlv = _confirmDelDate;
}
purchLineInreview.doUpdate();
ttscommit;
this.savePONoForWorkFLowInReview(purchLineInreview.PurchId);
info(strFmt('Successfully update done against Po no. %1 & line number %2',purchID, purchLineNumber));
}
}
}
/// <summary>
/// Holds the Po to trigger the workflow when the process ends.
/// </summary>
public void savePONoForWorkFLow(PurchId _purchID, TradeLineNumber _lineNumber, Name _manufacturersOrderNum, Transdate _confirmDeliveryDate)
{
purchTable purchTableWorkFlow;
select purchTableWorkFlow
where purchTableWorkFlow.PurchId == _purchID
&& purchTableWorkFlow.PurchStatus == PurchStatus::Backorder;
if (purchTableWorkFlow && !set.in(purchTableWorkFlow) && !AVAMassUpdateImportDialog::existPurchLineDetails(_purchID, _lineNumber, _manufacturersOrderNum) || (_confirmDeliveryDate != purchTableWorkFlow.ConfirmedDlv && _confirmDeliveryDate))
{
set.add(purchTableWorkFlow);
}
}
/// <summary>
/// If the Po status in In review saved the Po and trigger the workflow accordingly to conditions.
/// </summary>
public void savePONoForWorkFLowInReview(PurchId _purchID)
{
purchTable purchTableWorkFlow;
select purchTableWorkFlow
where purchTableWorkFlow.PurchId == _purchID
&& purchTableWorkFlow.PurchStatus == PurchStatus::Backorder;
if (purchTableWorkFlow && !set.in(purchTableWorkFlow))
{
set.add(purchTableWorkFlow);
}
}
/// <summary>
/// If the Po status in Approved status saved the Po and trigger the workflow accordingly to conditions.
/// </summary>
public void savePONoForWorkFLowApprovedStatus(PurchId _purchID)
{
purchTable purchTableWorkFlow;
select purchTableWorkFlow
where purchTableWorkFlow.PurchId == _purchID
&& purchTableWorkFlow.PurchStatus == PurchStatus::Backorder;
if (purchTableWorkFlow && !set.in(purchTableWorkFlow))
{
set.add(purchTableWorkFlow);
}
}
/// <summary>
/// To check record exists or not.
/// </summary>
static boolean existPurchLineDetails(PurchId _purchId, TradeLineNumber _lineNumber, Name _manufactureNumber)
{
return (select firstonly RecId from purchLine
index hint PurchLineIdx
where purchLine.PurchId == _purchId
&& purchLine.LineNumber == _lineNumber
&& purchLine.AVAManufacturersOrderNum == _manufactureNumber).RecId != 0;
}
/// <summary>
/// Specifies if the batch task is retryable for transient exceptions or not.
/// </summary>
/// <returns>
/// If true is returned, the batch task is retryable, otherwise it is not.
/// </returns>
[Hookable(false)]
public final boolean isRetryable()
{
return true;
}
}
========================================================================
Comments
Post a Comment