IndexOutOfRangeException when calling ERP Function from BPM

I have a BPM on SalesOrder.Update for creating a new ShipToMFBill when a user checks a UD checkbox on a SalesOrder. The Manifest Billing info of the Sales Order is used to create a ShipToMFBill for the Customer and ShipTo selected in the order and then Update that Customer. Because creating a ShipToMFBill is part of the Cutsomer BO, the BPM calls a Function to do the work external to SalesOrder.

The issue I’m running into is that the function works when calling strictly over REST API, and the BPM works when I don’t include the Function call … but when I do, I get an IndexOutOfRangeException after the function completes successfully. And since it’s a Pre-processing directive, the Update does not take place.

Is there something I’m missing? Am I breaking the base process by calling another BO method - even though it’s in a function?
Thanks for any input you may have!

Could you share the BPM widgets and any custom code? And the full error message?

It’s a very simple BPM:


If the checkbox is checked, clear it and run the function. Inputs are values from the OrderHed, there is one output string for messages from the function.

The function is also fairly simple:


Customer.GetByID
If the ShipToMFBill already exists, update the info
Otherwise, Customer.GetNewShipToMFBill to get a new one, a series of Set Variable widgets
Then regardless of which method was used, Customer.Update to save changes
Finally, set the value of the return string

The Custom Code widget has the following code:

var thisShipToMFBill =
  dsCustomer.ShipToMFBill
    .FirstOrDefault(stmfb => string.Equals(stmfb.ShipToNum, ipShipToNum) && string.Equals(stmfb.PayBTFlag, ipPayBTFlag));
    
if (thisShipToMFBill == null) // ShipToMFBill not found
{
  // Create it
  blnSTMFBFound = false;
  return;
}

// ShipToMFBill already exists
blnSTMFBFound = true;

// Update it
thisShipToMFBill.RowMod = "U";
thisShipToMFBill.PayAccount = ipPayAccount;
thisShipToMFBill.PayBTAddress1 = ipPayBTAddress1;
thisShipToMFBill.PayBTAddress2 = ipPayBTAddress2;
thisShipToMFBill.PayBTAddress3 = ipPayBTAddress3;
thisShipToMFBill.PayBTCity = ipPayBTCity;
thisShipToMFBill.PayBTState = ipPayBTState;
thisShipToMFBill.PayBTZip = ipPayBTZip;
thisShipToMFBill.PayBTCountryNum = ipPayBTCountryNum;
thisShipToMFBill.PayBTPhone = ipPayBTPhone;

And here is the full error message: (thanks to @klincecum for the event log dashboard!)

Ice.Common.EpicorServerException: BPM runtime caught an unexpected exception of 'IndexOutOfRangeException' type.
See more info in the Inner Exception section of Exception Details.
 ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Epicor.Customization.Bpm.BO.UpdatePreProcessingDirective_SaveBillingAddrToShipTo_9887B4D440324B0B921D9DDA3F4602F6.A002_InvokeEpicorFunctionAction2()
   at Epicor.Customization.Bpm.BO.UpdatePreProcessingDirective_SaveBillingAddrToShipTo_9887B4D440324B0B921D9DDA3F4602F6.ExecuteCore(Int32 step)
   at Epicor.Customization.Bpm.DirectiveBase`2.Execute() in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 333
   at Epicor.Customization.Bpm.DirectiveBase`2.Execute(TParam parameters) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 222
   --- End of inner exception stack trace ---
   at Epicor.Customization.Bpm.DirectiveBase`2.Execute(TParam parameters) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 222
   at Epicor.Customization.Bpm.MethodCustomizationBase2`2.<>c__DisplayClass11_0.<RunDirectives>b__3(MethodDirectiveBase`2 dir) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\MethodCustomizationBase2.cs:line 135
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at Epicor.Customization.Bpm.MethodCustomizationBase2`2.RunDirectives(TParam parameters) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\MethodCustomizationBase2.cs:line 0
   at Epicor.Customization.Bpm.CustomizationBase2`2.Execute(TParam parameters) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Internal\Lib\Epicor.Customization.Bpm\CustomizationBase2.cs:line 98
   at Epicor.Customization.Bpm.BO.SalesOrderSvcCustomization.Update(SalesOrderTableset& ds)
   at Erp.Services.BO.SalesOrderSvcFacade.Update(SalesOrderTableset& ds) in C:\_releases\ERP\ERP11.2.400.6\Source\Server\Services\BO\SalesOrder\SalesOrderSvcFacade.cs:line 11609
   at Erp.Services.BO.SalesOrderSvc.MasterUpdate(Boolean lCheckForOrderChangedMsg, Boolean lcheckForResponse, String cTableName, Int32 iCustNum, Int32 iOrderNum, Boolean lweLicensed, Boolean& lContinue, String& cResponseMsg, String& cCreditShipAction, String& cDisplayMsg, String& cCompliantMsg, String& cResponseMsgOrdRel, String& cAgingMessage, String& cShipByDateMessage, String& cNeedByDateMessage, SalesOrderTableset& ds) in C:\_releases\ERP\ERP11.2.400.6\Source\Server\Services\BO\SalesOrder\SalesOrder.cs:line 20029
   at Erp.Services.BO.SalesOrderSvcFacade.MasterUpdate(Boolean lCheckForOrderChangedMsg, Boolean lcheckForResponse, String cTableName, Int32 iCustNum, Int32 iOrderNum, Boolean lweLicensed, Boolean& lContinue, String& cResponseMsg, String& cCreditShipAction, String& cDisplayMsg, String& cCompliantMsg, String& cResponseMsgOrdRel, String& cAgingMessage, String& cShipByDateMessage, String& cNeedByDateMessage, SalesOrderTableset& ds) in C:\_releases\ERP\ERP11.2.400.6\Source\Server\Services\BO\SalesOrder\SalesOrderSvcFacade.cs:line 6792   at Ice.Hosting.RestApi.OperationInvokeHelper.CallInvoker(Type svcType, MethodInfo mi, Object[] inputs, Boolean isRoot, IceDataContext dataContext) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Framework\Epicor.Ice\Hosting\RestApi\OperationInvokeHelper.cs:line 403
   at Ice.Hosting.RestApi.OperationInvokeHelper.MakeServiceCall(IceDataContext dataContext, Type svcType, MethodInfo mi, Object[] parameters) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Framework\Epicor.Ice\Hosting\RestApi\OperationInvokeHelper.cs:line 356
   at Ice.Hosting.RestApi.OperationInvokeHelper.CallCustomMethod(String serviceName, MethodInfo mi, Object[] paramList) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Framework\Epicor.Ice\Hosting\RestApi\OperationInvokeHelper.cs:line 306
   at Epicor.RESTApi.DataSources.CustomMethodCaller.CallMethodWithDynamicData(String serviceId, String methodName, IInputParamResolver inputResolver, Action`1 headersPublisher, Action`1 verifyMethod) in C:\_releases\ICE\ICE4.2.400.0FW\Source\Server\Framework\Epicor.RESTApi.Common\DataSources\Main\CustomMethodCaller.cs:line 74
   at Ice.Hosting.AspNetCore.Controllers.ServiceMethodCallControllerBase.HandleServiceMethodCall(String serviceId, String methodName, IInputParamResolver inputResolver, Action`1 verifyMethod) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Controllers\ServiceMethodCallControllerBase.cs:line 62
   at Epicor.OData.V3.Controllers.CustomActionController.HandleCustomMethodCall(IInputParamResolver inputResolver, Action`1 verifyMethod) in C:\_releases\ICE\ICE4.2.400.0FW\Source\Server\Hosting\AspNetCore\RestApi\Epicor.OData.AspNetCore.V3\Controllers\CustomActionController.cs:line 53
   at lambda_method453(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Ice.Hosting.AspNetCore.Middleware.DynamicAssemblyPartMiddleware.Invoke(HttpContext context, CurrentCallInformationService currentCallInformation, ControllerLoader controllerLoader) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\DynamicAssemblyPartMiddleware.cs:line 33
   at Ice.Hosting.AspNetCore.ETags.ETagMiddleware.Invoke(HttpContext httpContext) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\ETags\ETagMiddleware.cs:line 110
   at Ice.Hosting.AspNetCore.ETags.ETagMiddleware.Invoke(HttpContext httpContext) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\ETags\ETagMiddleware.cs:line 110
   at Ice.Hosting.AspNetCore.Middleware.DecompressRequestMiddleware.Invoke(HttpContext context) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\DecompressRequestMiddleware.cs:line 42
   at Ice.Hosting.AspNetCore.Middleware.AuthenticationMiddleware.InvokeAsync(HttpContext httpContext, CurrentCallInformationService callInformation) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\AuthenticationMiddleware.cs:line 83
   at Ice.Hosting.AspNetCore.Middleware.CallHeaderMiddleware.InvokeAsync(HttpContext httpContext) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\CallHeaderMiddleware.cs:line 52
   at Ice.Hosting.AspNetCore.Middleware.OperationDisposerMiddleware.InvokeAsync(HttpContext httpContext) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\OperationDisposerMiddleware.cs:line 34
   at Epicor.RESTApi.Middleware.ApiKeyEnforcerMiddleware.Invoke(HttpContext context) in C:\_releases\ICE\ICE4.2.400.6\Source\Server\Hosting\AspNetCore\Ice.Hosting.AspNetCore\Middleware\ApiKeyEnforcerMiddleware.cs:line 79
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
CorrelationId: 0414c495-1195-4de7-9de3-08ac02d94ab0

I’m not directly accessing any index of any set - which is why I’m wondering if I’ve broken something else in the Epicor call sequence…

So it doesn’t look like your BPM is doing anything with the output string, is that correct? Because the function works with a REST call, I’m wondering if the BPM is having trouble with receiving a response. Is there a variable assigned to the output parameter in the InvokeFunction widget in the BPM? Since it’s a string, I’d test what happens if you add another widget with an info-message pop-up using the output string from the function as the body, and check if the info message matches your expected output.

Also, I’m not the expert in this, but is your “Requires transaction” checkbox set to true on the Function? That’s caused me some trouble in the past when calling BOs from Functions.

True. I hadn’t gotten to that point yet - it is getting captured in a variable, but not used.

I’m not sure how I could verify the value of the output variable. All I get back is to check the server logs:

I’m not sure this is necessary - the values are getting updated as expected:

Just add a Show Message widget at the end with your return string in the Body

If the Customer values are being updated correctly then I think you’re right, transaction not required.

So if that changes nothing (nothing is displayed except for the same error message), then perhaps the problem is in parsing the response from the function call?

That might do it, and it might explain why the function is successful (Function is happy with its response) but the BPM throws an error (unable to parse the response).

I’d have the function log the response to a text file before it ends, so you can see what that output looks like before it goes back to the BPM. I usually use this code for logging:

var filepath = $@"C:\EpicorData\Logs\Function{DateTime.Now:yyyyMMdd-hhmmss}.txt";

var file = new System.IO.StreamWriter( filepath );
file.WriteLine( ResponseString );
file.Close();

Thanks for the suggestion

The function response looks okay using the REST API Helper:

I’ll see if it looks any different when running from BPM.

Were you able to find a resolution to this? I am getting the same error. Function runs great until I try to attach a variable to the response in the Widget.
I can call the function from Custom Code and it returns an array that I can parse for the value, but I assumed that the Widget would do that for me since we can assign it a value to the return.

I was planning to get back into trying this today or Monday … I hadn’t found a solution and have been working on other issues, but it’s time to get back to this one.

Thank you for your input on what does/can work!
I’ll see if I can make anything of it and keep on trying

I have confirmed that everything works as expected if I ignore the output variable - which is annoying!
But as you say, I could call the function with a custom code to get out my output message … I’ll table that for now and come back to refactor later :clown_face:

It’s not the solution I’d hoped for - but it’s good enough for now.
Thank you!

Never hard code server address

Use epicors helper

string filePath = Ice.Lib.PathHelper.GetFolderPath(Epicor.ServiceModel.Utilities.SpecialFolder.CompanyData) + @"\";
3 Likes

The problem I have run into is that the source code generated by the Widget mis-indexes the result array, it sometimes starts with 1 instead of zero so it fails on the last result. I found this by dumping the source of the BPM. It also manifests itself with bad casts. The only solution I have come up with is to use custom code to call the function and pick off the results properly.