How to trace an error to a line in my custom code

Hi All,
This is an extension from one of my last issues: How to Delete a Release Using a Custom Action

This is a very complicated BPM for me, so let me break it down.
The underlying updatable BAQ returns a list of order releases from my user defined table (UD02). Most of the releases in this table match to open releases in our Epicor system. But some of them do not. This query returns a list of orders that are not in our system.

The idea is that you can review the list of releases not in our system, then click a checkbox to add that release to our system. The query tries to find a related order and line number based on the available information.(Part, PO, XRef, etc.) Depending on what is available, the query may have to do one of a few things.

Add a release to a closed order on a new line.
Add a release to a closed order on a closed line.
Add a release to an open order on a closed line.
Add a release to an open order on an open line.
Add a release to an open order on a new line.

So working from the top of the action down through the flowchart:

  1. First, I check the actionID to make sure I process the right action. There is a second action I haven’t even gotten into yet.
  2. Next, we set myrow and totalrows to 1.
  3. Next, we “Pull In Data” using the first custom code element:

PullInData

var ttResults_xRow = (from ttResults_Row in ttResults where ttResults_Row.Calculated_NewRelease == true select ttResults_Row).First();
if (ttResults_xRow != null)
{
  IsNewLine=false;
  totalrows = ttResults.Count / 2; //not sure why this is divided by 2, but it works...
  if (myrow <= totalrows)
    {
        MyOrder = ttResults_xRow.OrderHed2_OrderNum;
        if (String.IsNullOrEmpty(Convert.ToString(ttResults_xRow.OrderDtl1_OrderLine)))
        {
          MyLine = 0;
        }
        else
        {
          MyLine = ttResults_xRow.OrderDtl1_OrderLine;
        }
        MyQty = ttResults_xRow.UD02_Number01;
        MyDate = ttResults_xRow.UD02_Date01;
        MyPart = ttResults_xRow.UD02_ShortChar01;
        ttResults_xRow.Calculated_NewRelease = false;
    }
}
  1. Next, we look to see if the order is closed, if so we reopen it.
  2. Next, if myline=0 it basically means that we have to add a new line. Otherwise we add a new release to the suggested line (returned from the query).
  3. Next, to create a new line we run the GetNewOrderDtl method.
  4. Then, we update the relevant line information and update the entire order with the Update method.
  5. Next we process the “Set myline” custom code element:

SetMyLine

var OrderDtl = (from row in Db.OrderDtl where row.Company == Session.CompanyID && row.OrderNum == MyOrder orderby row.OrderLine descending select row).FirstOrDefault();
     {
      MyLine = OrderDtl.OrderLine;
     }
IsNewLine=true;
  1. Now we are looping back into the check to see if myline=0. If we just created a line then we have a line number now so we can just add a new release.
  2. Next, we add a new release using the GetNewOrderRel method.
  3. Then, we update the required fields, and then update the entire order with the Update method.
  4. Now we process the “Delete First Rel” custom code. I only have to do this because I can’t figure out how to update the first release that gets created with a new line. So I update the new release I just added, then delete the first empty release.

DeleteFirstRel

using (Erp.Contracts.SalesOrderSvcContract soSvc =  Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.SalesOrderSvcContract>(Db))
{
  if (IsNewLine = true)
  {
    SalesOrderTableset SOTS = soSvc.GetByID(MyOrder);  
    var desiredReleases = SOTS.OrderRel.Where(r => r.OrderLine == MyLine && r.OrderRelNum == 1);
    foreach(var rel in desiredReleases)
    {
       rel.RowMod = "D";
    }
    soSvc.Update(ref SOTS);
  }
}
  1. Next, we update the order with the Update method.
  2. Next, we set myrow=myrow+1
  3. Then, we check to see if we have reached the end of myrows. If not we continue back to the “Pull In Data” custom code element to process the next record. If we are at the end, then just tell the user.

The code works well when I only check one new release for an open order on an open line. However, when I try to add two releases to a closed order on a new line, I get the error below. I tried to run a trace, but it didn’t tell me anything extra. How do I tell what line of code this error is referencing? I went through each of my custom code elements and added junk code to see if I could get the line number to change, maybe to help point me towards the right section, but no matter what I added or removed the line is always 73 and column is 17.

Business Layer Exception

The table queryResultDataset.Results has more than one record

Exception caught in: Epicor.ServiceModel

Error Detail

Description: The table queryResultDataset.Results has more than one record
Program: Epicor.Customization.BPM.dll
Method: GetSingleRow
> Line Number: 73
> Column Number: 17
Table: queryResultDataset.Results

Client Stack Trace

at Epicor.ServiceModel.Channels.ImplBase1.ShouldRethrowNonRetryableException(Exception ex, DataSet[] dataSets) at Ice.Proxy.BO.DynamicQueryImpl.RunCustomAction(DynamicQueryDataSet queryDS, String actionID, DataSet queryResultDataset) at Ice.Adapters.DynamicQueryAdapter.<>c__DisplayClass33_0.<RunCustomAction>b__0(DataSet datasetToSend) at Ice.Adapters.DynamicQueryAdapter.ProcessUbaqMethod(String methodName, DataSet updatedDS, Func2 methodExecutor, Boolean refreshQueryResultsDataset)
at Ice.Adapters.DynamicQueryAdapter.RunCustomAction(DynamicQueryDataSet queryDS, String actionId, DataSet updatedDS, Boolean refreshQueryResultsDataset)
at Ice.UI.App.BAQDesignerEntry.BAQTransaction.<>c__DisplayClass384_0.b__0(Int32& rowReturned)
at Ice.UI.App.BAQDesignerEntry.Forms.BAQDiagramForm.ShowQueryResults(DataSet dsResults, getQueryResult getResults, ReportAdditionalInfo additionalInfo)
at Ice.UI.App.BAQDesignerEntry.BAQTransaction.CallRunCustom()

Here is the flowchart image of my BPM.

Give this a shot

This will allow you write out a trace on your code every so often and see how far the code executes.

I don’t know if there’s a more “correct” Epicor way to deal with this, but I’ve taken to enclosing as much custom code as I can in try-catch blocks and using a string variable to track specific execution points. You can then show a message in the catch block that reveals the value of the variable at the error point, which can not only tell you where it occurred but also the value of anything that you think might be relevant at that point too.

I notice @josecgomez’s comment on trace logging, which I must say is really useful on an ongoing basis when something is running and you want to pick up errors without disrupting users. I just like the immediacy of the message when I’m still trying to get something working.

1 Like

The nice thing about trace logging is that you can turn it on and off and you don’t have to remove it from the code. But yes both work just as well.

I should edit my comment, because I didn’t pick up that it was traces in the Epicor sense. That thread is really useful in its own right and I take back the bit about restricting it to only when users are involved.

1 Like

This looks really helpful. I tried to add your bit of code to the top of my first custom code element. But it doesn’t like the syntax.
Can you help me design my trace syntax?

Epicor.Hosting.Trace.ServerLog.WriteTraceMessage(
“trace://ice/mytrace”,
“TraceCategoryLabel”,
() => “Some Text”
);

Once I have the syntax, I access it through my usual tracelog, right? Can you tell me which options to turn on?
img5

Try using an action

Action<string> WT = (msg) =>
    {
        Epicor.Hosting.Trace.ServerLog.WriteTraceMessage(
        "trace://ice/brandon/isspecial",
        "BrandonsSpecialBAQTrace",
        () => msg
        );
    }
};

Stick this at the very top of your code.

Then anywhere you want a trace just call

WT("My Awesome Message");

and yes @Banderson is special… it says so right up there LoL

Obviously rename your trace to something a bit less “Brandon” nobody wants Brandon all over their trace :laughing:

Now remember this is a Server Side Trace (cause it runs on a BPM)
So follow the instructions on the other post to Enable it.

BPM009 Member declaration is not allowed inside code block

Clearly I am doing something wrong here…

Needs to be at the very top of your code before any other code.

I put it at the top of my very first custom code block “Pull In Data” inside my BPM. Is there another place to put it “before” this?

no that should work… what version of Epicor?

Is it possible I do not have access to appserver.config?
I looked in the event viewer and Epicor App Server is not listed.
Sorry for all the questions. Thanks for your patience! :slight_smile:

That’s in website directory where epicor is deployed to. Typically
C:\InetPub\Epicor…?

I am in 10.2.400.4 working in Pilot. I believe we have a dedicated cloud tenancy.

Oh… DT… I’m not sure how DT handles server side tracing…
@Mark_Wonsil

This is related to another topic that I created: Create new line and release from UBAQ custom code

You cannot change the appserver.config.

As far as tracing goes, you might be able to get the tracing from the Server File Download program. Select the Company folder and browse the files under your username or the manager. You might find your traces there. I got fiscal year-end going on now otherwise I would be able to check it out for you.

Mark W.

1 Like

Thanks Mark! I understand and appreciate your time.
What is the Server Download Program? Where is the company folder?

The source of my error was in one of my field setting elements. Instead of referencing the internal variable I created, I was referencing the query results. Obviously this contained more than one record, so the untraceable error. Thanks for trying to help me get better at error tracing. I am still interested in getting at the server traces if there is any way I can.
Thanks!!!

1 Like