Logic works in BL Tester, but not BPM Code (Mass Shipment)

On 10.2.100.43
I am attempting to process a shipment when the user checks “Ready To Fulfill”. Because the Serial tables exist in the SalesOrder method and the CustShip Method, I must use a Standard Data Directive to get around the error. I ran a test in the BL Tester and the logic works flawlessly. The only change to the data is to set a RowMod = “U” once at a specific point in time, otherwise, the whole thing is simply calling methods.
I tried to duplicate the process in code and everything works until the last “CustShip.UpdateMaster”. I have also tried creating each line by hand instead of using the Mass Shipment. Still errors.

Here is my error:

Business Layer Exception

Pack ID references invalid value.

Exception caught in: Epicor.ServiceModel

Error Detail 
============
Description:  Pack ID references invalid value.
Program:  Epicor.Ice.dll
Method:  ValidateReferentialIntegrity
Line Number:  585
Column Number:  21
Table:  ShipDtl
Field:  PackNum
Server Trace Stack:     at Ice.Core.BizRuleEngine.ValidateReferentialIntegrity() in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\BizRuleEngine\BizRuleEngine.cs:line 585
   at Ice.Core.BizRuleEngine.ValidateRow() in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\BizRuleEngine\BizRuleEngine.cs:line 178
   at Ice.Core.BizRuleEngine.Validate(IceRow oldRow, IceRow newRow, LinqRow curRow) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\BizRuleEngine\BizRuleEngine.cs:line 123
   at Ice.Services.Trace.TablesetProfilingCollector.DoRowEventTrace(String tableName, String methodName, Int32 rowCount, Action action) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\Services\TablesetProfilingCollector.cs:line 146
   at Ice.TablesetBound`3.CreateRow(IceDataContext dataContext, Int32 tableNum, IIceTable table, IceRow newTablesetRow, TablesetProfilingCollector parentTraceCollector) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\Services\TablesetBound.cs:line 1088
   at Ice.TablesetBound`3.WriteTable(IceDataContext dataContext, Int32 tableIndex, IIceTable table, TablesetProfilingCollector parentTraceCollector) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\Services\TablesetBound.cs:line 947
   at Ice.TablesetBound`3.InnerUpdate(IceDataContext dataContext, TFullTableset tableset) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.Ice\Services\TablesetBound.cs:line 856
   at Erp.Services.BO.CustShipSvc.Update(CustShipTableset& ds) in C:\_Releases\ERP\UD10.2.100.43\Source\Server\Services\BO\CustShip\CustShip.Designer.cs:line 2987
   at Erp.Services.BO.CustShipSvcFacade.Update(CustShipTableset& ds) in C:\_Releases\ERP\UD10.2.100.43\Source\Server\Services\BO\CustShip\CustShipSvcFacade.cs:line 5816
   at Erp.Services.BO.CustShipSvc.UpdateMaster(CustShipTableset& ds, Boolean doValidateCreditHold, Boolean doCheckShipDtl, Boolean doLotValidation, Boolean doCheckOrderComplete, Boolean doPostUpdate, Boolean doCheckCompliance, Boolean ipShippedFlagChanged, Int32 ipPackNum, Int32 ipBTCustNum, String& opReleaseMessage, String& opCompleteMessage, String& opShippingMessage, String& opLotMessage, String& opInventoryMessage, String& opLockQtyMessage, String& opAllocationMessage, String& opPartListNeedsAttr, String& opLotListNeedsAttr, String& shipCreditMsg, Boolean& cError, Boolean& compError, String& msg, String& opPostUpdMessage, Boolean& updateComplete, Boolean& checkComplianceError, Boolean& changeStatusError, Boolean& checkShipDtlAgain) in C:\_Releases\ERP\UD10.2.100.43\Source\Server\Services\BO\CustShip\CustShip.cs:line 33998
   at Erp.Services.BO.CustShipSvcFacade.UpdateMaster(CustShipTableset& ds, Boolean doValidateCreditHold, Boolean doCheckShipDtl, Boolean doLotValidation, Boolean doCheckOrderComplete, Boolean doPostUpdate, Boolean doCheckCompliance, Boolean ipShippedFlagChanged, Int32 ipPackNum, Int32 ipBTCustNum, String& opReleaseMessage, String& opCompleteMessage, String& opShippingMessage, String& opLotMessage, String& opInventoryMessage, String& opLockQtyMessage, String& opAllocationMessage, String& opPartListNeedsAttr, String& opLotListNeedsAttr, String& shipCreditMsg, Boolean& cError, Boolean& compError, String& msg, String& opPostUpdMessage, Boolean& updateComplete, Boolean& checkComplianceError, Boolean& changeStatusError, Boolean& checkShipDtlAgain) in C:\_Releases\ERP\UD10.2.100.43\Source\Server\Services\BO\CustShip\CustShipSvcFacade.cs:line 4345
   at Epicor.Customization.Bpm.DBE6B891EE16334E6ABF7238B6C642AFE6.PostTranDirective_ProcessShipment_8BD17AEEA43042C1AE2BED7BA1C57DB9.A002_CustomCodeAction()
   at Epicor.Customization.Bpm.DBE6B891EE16334E6ABF7238B6C642AFE6.PostTranDirective_ProcessShipment_8BD17AEEA43042C1AE2BED7BA1C57DB9.ExecuteCore()
   at Epicor.Customization.Bpm.DirectiveBase`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE3.2.100.43\Source\Server\Internal\Lib\Epicor.Customization.BPM\DirectiveBase.Generic.cs:line 131
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Epicor.Customization.Bpm.CustomizationBase2`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE3.2.100.43\Source\Server\Internal\Lib\Epicor.Customization.BPM\CustomizationBase2.cs:line 71
   at Epicor.Customization.Bpm.Standard.MonitoringSessionManager.ProcessCollectedData(IceDataContext db, IRowChangesCollection data, IDirectiveEnabler enabler) in C:\_Releases\ICE\ICE3.2.100.43\Source\Server\Internal\Lib\Epicor.Customization.BPM\Standard\MonitoringSessionManager.cs:line 130
   at Epicor.Customization.Bpm.SvcFacadeBase`3.Epicor.Hosting.IBpmReadyService.FinalizeCall(Object state, Boolean fail) in C:\_Releases\ICE\ICE3.2.100.43\Source\Server\Internal\Lib\Epicor.Customization.BPM\SvcFacadeBase.Generic.cs:line 205
   at Epicor.Hosting.OperationBoundInvoker.Invoke(Object instance, Func`2 func) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 32
   at Epicor.Hosting.Wcf.EpiOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) in C:\_Releases\ICE\ICE3.2.100.43\Source\Framework\Epicor.System\Hosting\Wcf\EpiOperationInvoker.cs:line 23
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
   at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
   at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.SecurityChannelListener`1.ReceiveItemAndVerifySecurityAsyncResult`2.InnerTryReceiveCompletedCallback(IAsyncResult result)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.TransportDuplexSessionChannel.TryReceiveAsyncResult.OnReceive(IAsyncResult result)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.SynchronizedMessageSource.ReceiveAsyncResult.OnReceiveComplete(Object state)
   at System.ServiceModel.Channels.SessionConnectionReader.OnAsyncReadComplete(Object state)
   at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
   at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
   at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
   at System.Net.Security.NegotiateStream.ProcessFrameBody(Int32 readBytes, Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.NegotiateStream.ReadCallback(AsyncProtocolRequest asyncRequest)
   at System.Net.AsyncProtocolRequest.CompleteRequest(Int32 result)
   at System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32 bytes)
   at System.Net.FixedSizeReader.ReadCallback(IAsyncResult transportResult)
   at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
   at System.ServiceModel.Channels.ConnectionStream.IOAsyncResult.OnAsyncIOComplete(Object state)
   at System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(SocketAsyncEventArgs e)
   at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(SocketError socketError, Int32 bytesTransferred, SocketFlags flags)
   at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)



Client Stack Trace 
==================
   at Epicor.ServiceModel.Channels.ImplBase`1.ShouldRethrowNonRetryableException(Exception ex, DataSet[] dataSets)
   at Erp.Proxy.BO.SalesOrderImpl.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, SalesOrderDataSet ds)
   at Erp.Adapters.SalesOrderAdapter.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)
   at Erp.UI.App.SalesOrderEntry.Transaction.Update()

Here is my code:

var hCustShip = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.CustShipSvcContract>(this.Db);
Erp.Tablesets.CustShipTableset ds = new Erp.Tablesets.CustShipTableset();

var tt = ttOrderHed.First();

string creditMessage;
bool opPromptForNum;

string opReleaseMessage;
string opCompleteMessage;
string opShippingMessage;
string opLotMessage;
string opInventoryMessage;
string opLockQtyMessage;
string opAllocationMessage;
string opPartListNeedsAttr;
string opLotListNeedsAttr;
string shipCreditMsg;
bool cError;
bool compError;
string msg;
string opPostUpdMessage;
bool updateComplete;
bool checkComplianceError;
bool changeStatusError;
bool checkShipDtlAgain;

string warnMsg;

using (var txScope = IceContext.CreateDefaultTransactionScope())
  {
    
    hCustShip.GetNewShipHead(ref ds);
    hCustShip.GetHeadOrderInfo(tt.OrderNum, out creditMessage, ref ds);
    hCustShip.GetLegalNumGenOpts(0, ref ds, out opPromptForNum);
    hCustShip.UpdateMaster(ref ds, false, false, false, false, false, false, false, packNum, tt.CustNum, out opReleaseMessage, out opCompleteMessage, out opShippingMessage, out opLotMessage, out opInventoryMessage, out opLockQtyMessage, out opAllocationMessage, out opPartListNeedsAttr, out opLotListNeedsAttr, out shipCreditMsg, out cError, out compError, out msg, out opPostUpdMessage, out updateComplete, out checkComplianceError, out changeStatusError, out checkShipDtlAgain);
    
    packNum = ds.ShipHead[0].PackNum; 
    hCustShip.PreCreateMassShipDtl(packNum, tt.OrderNum, out warnMsg, ref ds);
    hCustShip.CreateMassShipDtl(packNum, tt.OrderNum, ref ds);
    
    ds.ShipHead[0].RowMod = "U";
    hCustShip.GetManifestInfo(tt.OrderNum, packNum, ref ds);
    hCustShip.MarkShipmentLines(ref ds);
    hCustShip.UpdateMaster(ref ds, false, true, false, false, false, false, false, packNum, tt.CustNum, out opReleaseMessage, out opCompleteMessage, out opShippingMessage, out opLotMessage, out opInventoryMessage, out opLockQtyMessage, out opAllocationMessage, out opPartListNeedsAttr, out opLotListNeedsAttr, out shipCreditMsg, out cError, out compError, out msg, out opPostUpdMessage, out updateComplete, out checkComplianceError, out changeStatusError, out checkShipDtlAgain);
  
        
    txScope.Complete();
  }

did you try pushing this into an Epicor Function, and then calling your function from the BPM?

Not in 10.2.100…

Can you verify that all ShipDtl records have RowMod = ‘A’ and that PackNum contains the same value as ShipHead.PackNum?

Also, something that happens when trying to do everything server side is that you lose the processing done by the Contract.

For example I recently had an issue because my tableset only had 1 row with rowmod U, however I wasn’t passing the unmodified row and this was triggering validations that should only happen when a record is new, so “if old record = null” means this is a new record, even if it has U as RowMod. This is usually handled by the contract when moving data between client datasets and server tablesets.

Yes and yes.

As for the Contract, isn’t that what I’m using (Erp.Contracts.CustShipSvcContract)?

Bad news. I just tested it in 10.2.500 and I have the same issue. This means something is missing and among the many fields, I can;t see what. I have used the Epicor Trace Parser and I do not see anything happening that the Methods don’t do on their own (aside from one instance that the RowMod needs to be set).
I have also tried to use the widgets in an updateable BAQ. Same error…

I’ve also been trying the “foreach” method to add the lines (the header works fine). It errors on the GetOrderRelInfo line, but if I simply set the Release (see commented line), I get the original error:

        foreach (var rel in Db.OrderRel.Where(rel => rel.Company == tt.Company && rel.OrderNum == tt.OrderNum && rel.OpenRelease).Select(rel => new{rel.OrderLine, rel.OrderRelNum, rel.PartNum}))
          {
            hCustShip.GetNewOrdrShipDtl(ref ds, packNum, tt.OrderNum);
            ds.ShipHead[0].RowMod = "U";
            hCustShip.GetManifestInfo(tt.OrderNum, packNum, ref ds);
            hCustShip.GetOrderLineInfo(ref ds, 0, rel.OrderLine, rel.PartNum);
            PublishInfoMessage(rel.PartNum,0,0,"","");
            
            hCustShip.GetOrderRelInfo(ref ds, 0, rel.OrderRelNum, true);
            //ds.ShipDtl.Where(S => S.PackLine == 0).First().PackNum = packNum;
            hCustShip.UpdateMaster(ref ds, false, true, false, false, false, false, false, packNum, tt.CustNum, out opReleaseMessage, out opCompleteMessage, out opShippingMessage, out opLotMessage, out opInventoryMessage, out opLockQtyMessage, out opAllocationMessage, out opPartListNeedsAttr, out opLotListNeedsAttr, out shipCreditMsg, out cError, out compError, out msg, out opPostUpdMessage, out updateComplete, out checkComplianceError, out changeStatusError, out checkShipDtlAgain);
            
            //break;
          }

Are you sure this is not a multi site issue. Raised the Order in one site and trying to ship from another? What about the Plant and warehouse codes on the release?

Single Site. I had that problem when testing on a separate version. :slight_smile:

1 Like

I think the issue here could be your scope. Try putting the last bit of logic into its own scope after committing the previous one.

Try putting this before the second call to UpdateMaster

var biTTShipHead = BufferCopy.Copy<Erp.Tablesets.ShipHeadRow>(ds.ShipHead[0]);
biTTShipHead.RowMod = “”;
ds.ShipHead.Add(biTTShipHead);

The BO is updating the PackNum on the second UpdateMaster because the unmodified row is not present on the tableset, so it considers the row a new one.

4 Likes

Boom! That was it! That was too many hours of different testing. Too many hours… :sob:

Free working code (Standard Data Directive on OrderHed):

var tt = ttOrderHed.First();

string creditMessage;
bool opPromptForNum;

string opReleaseMessage;
string opCompleteMessage;
string opShippingMessage;
string opLotMessage;
string opInventoryMessage;
string opLockQtyMessage;
string opAllocationMessage;
string opPartListNeedsAttr;
string opLotListNeedsAttr;
string shipCreditMsg;
bool cError;
bool compError;
string msg;
string opPostUpdMessage;
bool updateComplete;
bool checkComplianceError;
bool changeStatusError;
bool checkShipDtlAgain;

string warnMsg;

using (var txScope = IceContext.CreateDefaultTransactionScope())
  {
    using (var hCustShip = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.CustShipSvcContract>(Db))
      {
        Erp.Tablesets.CustShipTableset ds = new Erp.Tablesets.CustShipTableset();
        hCustShip.GetNewShipHead(ref ds);
        hCustShip.GetHeadOrderInfo(tt.OrderNum, out creditMessage, ref ds);
        hCustShip.GetLegalNumGenOpts(0, ref ds, out opPromptForNum);
        hCustShip.UpdateMaster(ref ds, false, false, false, false, false, false, false, packNum, tt.CustNum, out opReleaseMessage, out opCompleteMessage, out opShippingMessage, out opLotMessage, out opInventoryMessage, out opLockQtyMessage, out opAllocationMessage, out opPartListNeedsAttr, out opLotListNeedsAttr, out shipCreditMsg, out cError, out compError, out msg, out opPostUpdMessage, out updateComplete, out checkComplianceError, out changeStatusError, out checkShipDtlAgain);
        
        packNum = ds.ShipHead[0].PackNum;
        
        hCustShip.PreCreateMassShipDtl(packNum, tt.OrderNum, out warnMsg, ref ds);
        hCustShip.CreateMassShipDtl(packNum, tt.OrderNum, ref ds);
        
        ds.ShipHead[0].RowMod = "U";
        hCustShip.GetManifestInfo(tt.OrderNum, packNum, ref ds);
        hCustShip.MarkShipmentLines(ref ds);
        ds.ShipHead[0].RowMod = "U"; //May not be needed
        
        //The BO is updating the PackNum on the second UpdateMaster because the unmodified row is not present on the tableset, so it considers the row a new one.
        var unmodifiedShipHead = BufferCopy.Copy<Erp.Tablesets.ShipHeadRow>(ds.ShipHead[0]);
        unmodifiedShipHead.RowMod = "";
        ds.ShipHead.Add(unmodifiedShipHead);
        
        hCustShip.UpdateMaster(ref ds, false, true, false, false, false, false, false, packNum, tt.CustNum, out opReleaseMessage, out opCompleteMessage, out opShippingMessage, out opLotMessage, out opInventoryMessage, out opLockQtyMessage, out opAllocationMessage, out opPartListNeedsAttr, out opLotListNeedsAttr, out shipCreditMsg, out cError, out compError, out msg, out opPostUpdMessage, out updateComplete, out checkComplianceError, out changeStatusError, out checkShipDtlAgain);

      }
    txScope.Complete();
  }
2 Likes

We’re trying to take this one step further by marking it ReadyToInvoice … however as per this thread (WCF to Set Order Shipment as Ready to Invoice) we’re having a problem completing the final step.

Anyone else gone further down this line by chance?

I’ve also posted on related material here: Mark Pack slip as shipped - BPM