Thanks! The only one I’m having trouble figuring out is the noteSTR. Not sure what it’s supposed to be so not sure how to handle it.
This is a Guid that is used as a unique identifier:
for the task so the report data can be found in this piece of code:
Thak you everyone for helping me. I believe I figured it out.
Last question, I think…
How do I trigger the Function? Been trying and not being very successful.
Give the full context of the problem, I’m not reading all this again. tldr
What do you mean how do you trigger it? The function is called via REST from your App / Service / Customization / BPM etc.
Hi, I am trying to achieve the same regarding invoice PDF over API.
I have used the code below in a Function which when i run using the SysRowID it starts the tasks and generates the correct invoice PDF as expected.
I am retrieving null values for Invoice Document and getReportBytes.
I thought it could have been due to a delay in the PDF creation so i added a 30 seconds sleep thread which didnt solve my problem.
Im confident my Signature > Request & Response parameters are incorrect.
this.Success=true;
try
{
this.CallService<Erp.Contracts.ARInvFormSvcContract>(arInvFrm => {
Guid newTask = Guid.NewGuid();
String noteStr= newTask.ToString();
var invcRow = Guid.Parse(this.SysRowID.ToString());
var invoiceNum = (from ih in Db.InvcHead where ih.SysRowID==invcRow select ih.InvoiceNum).FirstOrDefault();
if(invoiceNum!=null && invoiceNum !=0)
{
var arInvFrmTs = arInvFrm.GetNewParameters();
arInvFrmTs.ARInvFormParam[0].AgentID="SystemTaskAgent";
arInvFrmTs.ARInvFormParam[0].AutoAction="SSRSGenerate";
arInvFrmTs.ARInvFormParam[0].CalledFrom="Erp.UI.ARInvoiceTracker";
arInvFrmTs.ARInvFormParam[0].ReportStyleNum=1001;
arInvFrmTs.ARInvFormParam[0].SSRSRenderFormat="PDF";
arInvFrmTs.ARInvFormParam[0].SSRSEnableRouting=false;
arInvFrmTs.ARInvFormParam[0].InvoiceNum= invoiceNum;
this.TaskNoteGuid = noteStr;
this.invoiceNum = invoiceNum.ToString();
arInvFrm.RunDirect(arInvFrmTs);
{
Thread.Sleep(TimeSpan.FromSeconds(30));
}
var getReportBytes = (from b in Db.SysRptLst join st in Db.SysTask on b.SysTaskNum equals st.SysTaskNum
where st.TaskNote ==noteStr select new {b.RptDescription, b.RptData}).FirstOrDefault();
this.InvoiceDocument = Convert.ToBase64String(getReportBytes.RptData);
}
else
{
this.Success=false;
this.ErrorMessage="Invoice not Found";
}
});
}
catch(Exception ex)
{
this.Success=false;
this.ErrorMessage=ex.Message;
}
To remove the timing issue, you might want to use the RunDirect method instead of submitting to the Task Agent. The method will return when the “bytes” are ready.
I dont believe timing to be the issue as I can currently see the PDF has completed before the Response is returned.
Is this not using the RunDirect method already?
As per you original posts, do i have the response and requests parameters set up correctly?
Either RunDirect OR SubmitToAgent need to run for the report to generate. RunDirect returns the bytes directly and SubmitToAgent puts the report in the SysRptLst table.
I don’t see the SubmitToAgent in either example, so that may be an issue.
The task seems to submit ok, as i can download the PDF not problem after running the function via the API.
I have thrown a couple of checks in:
this.Success = true;
try
{
this.CallService<Erp.Contracts.ARInvFormSvcContract>(arInvFrm => {
Guid newTask = Guid.NewGuid();
String noteStr = newTask.ToString();
var invcRow = Guid.Parse(this.SysRowID.ToString());
var invoiceNum = (from ih in Db.InvcHead where ih.SysRowID == invcRow select ih.InvoiceNum).FirstOrDefault();
if (invoiceNum != null && invoiceNum != 0)
{
var arInvFrmTs = arInvFrm.GetNewParameters();
if (arInvFrmTs != null && arInvFrmTs.ARInvFormParam != null && arInvFrmTs.ARInvFormParam.Count > 0)
{
arInvFrmTs.ARInvFormParam[0].AgentID = "SystemTaskAgent";
arInvFrmTs.ARInvFormParam[0].AutoAction = "SSRSGenerate";
arInvFrmTs.ARInvFormParam[0].CalledFrom = "Erp.UI.ARInvoiceTracker";
arInvFrmTs.ARInvFormParam[0].ReportStyleNum = 1001;
arInvFrmTs.ARInvFormParam[0].SSRSRenderFormat = "PDF";
arInvFrmTs.ARInvFormParam[0].SSRSEnableRouting = false;
arInvFrmTs.ARInvFormParam[0].InvoiceNum = invoiceNum;
this.TaskNoteGuid = noteStr;
this.invoiceNum = invoiceNum.ToString();
arInvFrm.RunDirect(arInvFrmTs);
bool reportGenerated = false;
int attempts = 0;
while (!reportGenerated && attempts < 10)
{
var getReportBytes = (from b in Db.SysRptLst join st in Db.SysTask on b.SysTaskNum equals st.SysTaskNum
where st.TaskNote == noteStr select new { b.RptDescription, b.RptData }).FirstOrDefault();
if (getReportBytes != null && getReportBytes.RptData != null)
{
this.InvoiceDocument = Convert.ToBase64String(getReportBytes.RptData);
reportGenerated = true;
}
else
{
Thread.Sleep(TimeSpan.FromSeconds(10)); // Wait before trying again
attempts++;
}
}
if (!reportGenerated)
{
this.Success = false;
this.ErrorMessage = "Report generation did not complete within the expected time.";
}
}
else
{
this.Success = false;
this.ErrorMessage = "Failed to retrieve initial parameters.";
}
}
else
{
this.Success = false;
this.ErrorMessage = "Invoice not found.";
}
});
}
catch (Exception ex)
{
this.Success = false;
this.ErrorMessage = ex.Message;
// Log the exception details for further analysis
}
Error is that the Report Generation did not complete but within a seocnd or two i could download the PDF from within Epicor System Monitor.
With RunDirect, that will never be necessary. The function does not return until it either generates, or errors out.
As for the main problem, I took a look at your code and function structure and found some code errors and scope issues.
I fixed the problem and got it working, although I have decided to rewrite it and post it tomorrow, so I could properly explain what went wrong.
Jose’s code was posted more as a guide, and not a full example of a working function.
Thank you
New function, similar to yours, although I would probably look it up by Invoice Number
Success = true; //Assume true
try
{
//Generate a unique ID to locate the generated report. ("TaskNote")
TaskNoteGuid = Guid.NewGuid();
//Find the invoice number.
InvoiceNum = Db.InvcHead.Where(ih => ih.SysRowID == SysRowID).Select(ih => ih.InvoiceNum).FirstOrDefault();
//Call the AR Invoice Form BO
CallService<Erp.Contracts.ARInvFormSvcContract>(arInvFrm =>
{
if(InvoiceNum != null && InvoiceNum != 0) //Did we find the invoice?
{
//Get a new set of and fill in the parameters to run the report.
var arInvFrmTs = arInvFrm.GetNewParameters();
arInvFrmTs.ARInvFormParam[0].InvoiceNum = InvoiceNum;
arInvFrmTs.ARInvFormParam[0].TaskNote = TaskNoteGuid.ToString(); //Magic -> This is how we find the report after generate.
arInvFrmTs.ARInvFormParam[0].ReportStyleNum = ReportStyleNum; //I added this instead of hardcode.
arInvFrmTs.ARInvFormParam[0].AgentID = "SystemTaskAgent";
arInvFrmTs.ARInvFormParam[0].AutoAction = "SSRSGENERATE"; //We just want to generate it, not print or preview.
arInvFrmTs.ARInvFormParam[0].CalledFrom = "Erp.UI.ARInvoiceTracker";
arInvFrmTs.ARInvFormParam[0].SSRSRenderFormat = "PDF";
arInvFrmTs.ARInvFormParam[0].SSRSEnableRouting = false;
//Run the report
arInvFrm.RunDirect(arInvFrmTs);
//Get the report bytes.
var getReportBytes = Db.SysRptLst
.Join(Db.SysTask,
srl => srl.SysTaskNum,
st => st.SysTaskNum,
(srl, st) => new {srl, st })
.Where(joined => joined.st.TaskNote == TaskNoteGuid.ToString())
.Select(joined => joined.srl.RptData)
.FirstOrDefault();
//Convert the report byte array into a Base64 encoded string.
InvoiceDocumentB64 = Convert.ToBase64String(getReportBytes);
}
else
{
//We didn't find it...
Success = false;
ErrorMessage = "Invoice not Found";
}
});
}
catch(Exception ex)
{
//Something (We?) screwed up...
Success = false;
ErrorMessage = ex.Message;
}
What went wrong.
this.Success = true;
try
{
this.CallService<Erp.Contracts.ARInvFormSvcContract>(arInvFrm => {
//this was in your function signature as a string, and redefined as a guid.
//This brings it out of scope and mismatched.
Guid newTask = Guid.NewGuid();
//Same here, this is redefined, and now out of scope, this is not the same variable as the signature.
String noteStr = newTask.ToString();
var invcRow = Guid.Parse(this.SysRowID.ToString());
//Same here, this is redefined, and now out of scope, this is not the same variable as the signature.
var invoiceNum = (from ih in Db.InvcHead where ih.SysRowID == invcRow select ih.InvoiceNum).FirstOrDefault();
if (invoiceNum != null && invoiceNum != 0)
{
var arInvFrmTs = arInvFrm.GetNewParameters();
//if (arInvFrmTs != null && arInvFrmTs.ARInvFormParam != null && arInvFrmTs.ARInvFormParam.Count > 0)
//{
arInvFrmTs.ARInvFormParam[0].AgentID = "SystemTaskAgent";
arInvFrmTs.ARInvFormParam[0].AutoAction = "SSRSGenerate";
arInvFrmTs.ARInvFormParam[0].CalledFrom = "Erp.UI.ARInvoiceTracker";
arInvFrmTs.ARInvFormParam[0].ReportStyleNum = 2; //1001;
arInvFrmTs.ARInvFormParam[0].SSRSRenderFormat = "PDF";
arInvFrmTs.ARInvFormParam[0].SSRSEnableRouting = false;
arInvFrmTs.ARInvFormParam[0].InvoiceNum = invoiceNum;
//This was missing, this is the magic.
arInvFrmTs.ARInvFormParam[0].TaskNote = noteStr;
this.TaskNoteGuid = noteStr;
this.invoiceNum = invoiceNum.ToString();
arInvFrm.RunDirect(arInvFrmTs);
//This is in your function signature, as a string. This not only has been redefined with the var keyword,
//it is not a string, so that's why it does not show up in the output.
var getReportBytes = (from b in Db.SysRptLst join st in Db.SysTask on b.SysTaskNum equals st.SysTaskNum
where st.TaskNote == noteStr select new { b.RptDescription, b.RptData }).FirstOrDefault();
this.InvoiceDocument = Convert.ToBase64String(getReportBytes.RptData);
/*
//Not necessary, as RunDirect either succeeds, or does not
bool reportGenerated = false;
int attempts = 0;
while (!reportGenerated && attempts < 10)
{
var getReportBytes = (from b in Db.SysRptLst join st in Db.SysTask on b.SysTaskNum equals st.SysTaskNum
where st.TaskNote == noteStr select new { b.RptDescription, b.RptData }).FirstOrDefault();
if (getReportBytes != null && getReportBytes.RptData != null)
{
this.InvoiceDocument = Convert.ToBase64String(getReportBytes.RptData);
reportGenerated = true;
}
else
{
Thread.Sleep(TimeSpan.FromSeconds(10)); // Wait before trying again
attempts++;
}
}
if (!reportGenerated)
{
this.Success = false;
this.ErrorMessage = "Report generation did not complete within the expected time.";
}
*/
//}
//else
//{
// this.Success = false;
// this.ErrorMessage = "Failed to retrieve initial parameters.";
//}
}
else
{
this.Success = false;
this.ErrorMessage = "Invoice not found.";
}
});
}
catch (Exception ex)
{
this.Success = false;
this.ErrorMessage = ex.Message;
// Log the exception details for further analysis
}