Customized report naming?

When we originally purchased epicor we wanted the ability to have our reports custom named. For example, “Order_231338.pdf” - Title followed by order number. Rather than the SalesOrderAcknowledgement_random#.pdf that Epicor puts out. We were sold the SSRS Breaking/Routing module saying that we can do this with the module. Over the years, when I had some free time I would play around with this module, never being able to do what we wanted. I have circled back to this and looking for anyone who has in fact done this. Do you have an example? Or were we sold the wrong module?

Thanks

1 Like

If you email the report, you definitely have the ability to name the attachment exactly as you described that you wanted to do. But at first glance, I’m not seeing the same ability to just print the thing to PDF with a given filename.

Yeah, I think I remember that, but we don’t want to automatically email it. The sales people want the ability to decide if the want to email it and have control of the email content. This may work for for certain reports, but in general we just want to rename it.

I completely understand. I wouldn’t give up hope just yet. It appears that the filename generated is the title of the report + task ID from the System Monitor. Perhaps at the report level (in the .RDL file) we might be able to do something to override either or both of those fields.

Can this be done?

Is there another way to do this? Is there another product add on that will do this?

I have never done this - however i’ll bet someone here has. Maybe use a BPM to modify the file name for the PDF?

1 Like

I ran a few traces and could not identify a place where the file name (or the elements of a filename) are available. I assume this must be possible, but it is not obvious how to get there. :thinking:

Using a Method Directive on Ice.BO.ReportMonitor.Update, I can see that RptDescription hold the first part of the filename, while RecSeq, and SysTaskNum hold the second part of the filename. I don’t think you can change these values at this point. Even if you could, I don’t think the system would like it very much.
It is safe to say you are out of luck on this one. You can go vote on the idea I just created for you!
Allow users to define the filename of any | Epicor Kinetic Ideas (aha.io)

1 Like

Thank You. I have added my vote.

Not so fast. I just did something a bit similar, let me re-read the original request on a full stomach and we could probably make something work.

@mchernecki , Please explain exactly what you would like to do, in detail.
Like how you would imagine it working, workflow etc.

2 Likes

As example, let’s look at Sales Order Acknowledgment. We have several reports based on this one; Order Acceptance, Ship Date Confirmation, Order Acknowledgment, Order Shipment Details and Order Write up. Depending on which we would print, it would be named for example, OrderAcceptance_##OrderNum##.pdf. There may be additional information added, but too early to know.

And by named, are you referring to the filename when it’s previewed?

Yes,so they can immediately save the pdf without having to manually rename it.

2 Likes

Sweet. I’ll tag you back.

1 Like

Got it. Post later.

Edit: Got it down to 1 BPM !

You can remove the previous ones, and control it all from one BPM.
Haven’t tested in Kinetic yet, it’s possible it behaves differently.

Post-Processing BPM on Ice.BO.ReportMonitor.Update
/***************************************************************************************
Post-Processing BPM on Ice.BO.ReportMonitor.Update 
Author: Kevin Lincecum
Purpose: Renames files previewed from the System Monitor to a custom format based on
  criteria from the SysTaskParam table.
***************************************************************************************/

try
{
    //Isolate rows for processing based on these criteria
    var rowsForProcessing = ds.SysRptLst.Where(x =>
        x.PrintDriver == "SSRS" &&
        x.LastAction == "Process");

    //Loop through each selected row 
    foreach(SysRptLstRow sysRptLstRow in rowsForProcessing)
    {
        //Pull out for convenience / readability
        long sysTaskNum = sysRptLstRow.SysTaskNum;
        
        //Get all the Parameters for this report 
        List<SysTaskParam> sysTaskParams = Db.SysTaskParam.Where(stp => stp.SysTaskNum == sysTaskNum).ToList();
        
        //Get the Report ID
        string reportID = sysTaskParams.Where(stp => stp.ParamName == "ReportID").FirstOrDefault().ParamCharacter;
        
        //Get the Report Parameter DataSetID
        //string reportParameterDataSetId = sysTaskParams.Where(stp => stp.ParamName == "ReportParameterDataSetId").FirstOrDefault().ParamCharacter;
        
        //Checks based off of the "ReportID", so you could handle different kinds of reports etc.
        if(reportID == "OrderAck")
        //Or you could check the "ReportParameterDataSetId"
        //if(reportParameterDataSetId == "SalesOrderAck")
        {
            //Get the Report Style so you can check against it.. 
            int reportStyleNum = sysTaskParams.Where(stp => stp.ParamName == "ReportStyleNum").FirstOrDefault().ParamInteger;
            
            //Get the Order Number so we can pull further data..
            int orderNum       = sysTaskParams.Where(stp => stp.ParamName == "OrderNum").FirstOrDefault().ParamInteger;
            
            //I wanted the Customer ID, but we need to get the Customer Number first...
            int custNum = Db.OrderHed.Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).FirstOrDefault().CustNum;
            
            //Get the Customer ID
            string custID = Db.Customer.Where(c => c.Company == Session.CompanyID && c.CustNum == custNum).FirstOrDefault().CustID;
            
            //Do different types of actions based on which Report Style is passed, use your imagination.
            switch(reportStyleNum)
            {
                case 2:
                    sysRptLstRow.RptDescription = $"Blistering_Donkey-{orderNum.ToString()}_CustID-{custID}";
                    break;
                    
                case 1001:
                    sysRptLstRow.RptDescription = $"Voluptuous_Mole-Rat-{orderNum.ToString()}_CustID-{custID}";
                    break;
                    
                default:
                    sysRptLstRow.RptDescription = $"I_LIKE_TURTLES";
                    break;
            }
            
            //Below is what is appended to the end of the filename.
            //Making the best of what you can do...
            
            //This will be "0";
            sysRptLstRow.SetSysTaskNumUnspecified();
            
            //Or set it to Ticks for something useful & unique 
            //sysRptLstRow.SysTaskNum = DateTime.Now.Ticks;            
            
            //Or set it to OrderNum, might be useful
            //sysRptLstRow.SysTaskNum = orderNum;
        }
    }
}
catch (Exception ex)
{
    //Do whatever you do with errors...
}

Old 2 BPM Post

Sure: 2 BPMs for a 1, 2 punch:

Fight Boxing GIF by We love MMA

Pre-Processing BPM on Erp.Rpt.SalesOrderAck.SubmitToAgent
/***************************************************************************************
Pre-Processing BPM on Erp.Rpt.SalesOrderAck.SubmitToAgent
Author: Kevin Lincecum
Purpose: Example of passing some data in the TaskNote field of a report parameter row
to be acted on in another BPM down the line.
In this case: Ice.BO.ReportMonitor.GetRowsKeepIdleTimeWithBallonInfo
***************************************************************************************/

try
{
    //Isolate our row just for readability
    SalesOrderAckParamRow salesOrderAckParamRow = ds.SalesOrderAckParam.FirstOrDefault(); 
    
    //Only process if we are previewing
    if(salesOrderAckParamRow.AutoAction == "SSRSPREVIEW")
    {
        //Pull these fields out to pass in the TaskNote field. We can pull these out in the RptNote field
        //in Ice.BO.ReportMonitor.GetRowsKeepIdleTimeWithBallonInfo
        string orderNum = salesOrderAckParamRow.OrderNum.ToString();
        string reportStyle = salesOrderAckParamRow.ReportStyleNum.ToString();
        
        //Populate TaskNote field
        //RptNote/TaskNote is small, this is about all that would fit. It should be enough.
        salesOrderAckParamRow.TaskNote = $"SO.Rename>{reportStyle}-{orderNum}";
    }  
}
catch (Exception ex)
{
    //Do whatever you do with errors...
}
Post-Processing BPM on Ice.BO.ReportMonitor.GetRowsKeepIdleTimeWithBallonInfo
/***************************************************************************************
Post-Processing BPM on Ice.BO.ReportMonitor.GetRowsKeepIdleTimeWithBallonInfo 
Author: Kevin Lincecum
Purpose: Example of pulling some data from the RptNote field of a report parameter row
passed in the TaskNote field of a SubmitToAgent call.
In this case: Erp.Rpt.SalesOrderAck.SubmitToAgent 
***************************************************************************************/

try
{
    //Isolate our rows that meet these criteria
    var salesOrderRenamingRows = result.SysRptLst.Where(x =>
        x.PrintDriver == "SSRS" &&
        x.AutoAction == "PREVIEW" &&
        x.LastAction == "SSRSREADY" &&
        x.RptNote.StartsWith("SO.Rename>"));
  
    //Loop through the selected rows and process them
    foreach(SysRptLstRow sysReportLstRow in salesOrderRenamingRows)
    {
        //Split on '>' and take the second part for parsing
        string   renamingParamsString = sysReportLstRow.RptNote.Split('>')[1];
        
        //Split on '-' and fill an array with our params.
        string[] renamingParamsArray  = renamingParamsString.Split('-'); 
        
        //RptNote/TaskNote is small, this is about all that would fit
        int reportStyle = Convert.ToInt32(renamingParamsArray[0]);
        int orderNum    = Convert.ToInt32(renamingParamsArray[1]);
        
        //I wanted the Customer ID, but we need to get the Customer Number first...
        int custNum = Db.OrderHed.Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).FirstOrDefault().CustNum;
        
        //Get the Customer ID
        string custID = Db.Customer.Where(c => c.Company == Session.CompanyID && c.CustNum == custNum).FirstOrDefault().CustID;
        
        //Do different types of actions based on which Report Style is passed, use your imagination.
        switch(reportStyle)
        {
            case 2:
                sysReportLstRow.RptDescription = $"Blistering_Donkey-{orderNum.ToString()}_CustID-{custID}__ST";
                break;
                
            case 1001:
                sysReportLstRow.RptDescription = $"Voluptuous_Mole-Rat-{orderNum.ToString()}_CustID-{custID}__ST";
                break;
                
            default:
                sysReportLstRow.RptDescription = $"I_LIKE_TURTLES";
                break;
        }
    }
}
catch (Exception ex)
{
    //Do whatever you do with errors...
}

Have fun.
:tada:

5 Likes

That worked. Thanks. I could not find those methods in 10.2.300, but it worked fine in Kinetic 2023. That’s fine we are in the process of moving to Kinetic.

2 Likes

I ran a few test and noticed the Task Id is appended to the end. Is this something the system is doing and we can’t get around?

1 Like

Yes. In my Example I put ST in there to signify what it was but didn’t explain it.

There is a hack for it that seems to work, you can replace the systasknum with anything numeric, and the update for the row must not be looking for that, so it still works properly.

I can show that if you’d like.

Be aware, if the filename exists already from the way you structure it, the system will also append _# on to it so it can write to the temp folder on your machine.

1 Like

Please, if you don’t mind? Thanks

1 Like