Custom BPM to add QuoteHed_UD field to Sales Order on Opp to Sales Order processing

Yes. Action > Create Sales Order from inside the actual quote.

Ok, it looks like what you will need is a PostProcessing Method Directive on Erp.BO.Quote.CreateOrder

In there you can access your UD field in QuoteHed:

image

Get SO uses the Erp.SalesOrder.GetByID with the orderNum variable that is in the CreateOrder definition. You can use this to create a DataSet variable that will be returned with the sales order information.
image

The Set SO_UD is used to set the Sales Order UD field to the QuoteHed UD field. Remember in a post processing there is no added/updated … rows.

image

So the Properties of Set SO_UD would look like:

Set the dsSalesOrder.OrderHed.DecorType_c field of all rows to the dsQuoteHedRow.DecorType_c

The Save SO uses the Erp.SalesOrder.Update with the DataSet created by the Get SO block.

image

That’s a real basic look at the concept I think you’re trying to get to.

I believe that @Doug.C’s solution would work, BUT, only when you convert a quote to an order from the quote… BUT there are three different ways to create an order.

  1. From Quote Entry
  2. From Order Entry/Actions
  3. from Order Entry / Get line from quote
    Each of these runs a different business object, so you will need to have three BPMs.
    Ahhh… but now we have FUNCTIONS available. One way to make this consistent would be to create a Function that does all your work, and then call that function from the three places.
    OR
    You create a DATA BPM on the OrderDtl that looks for new line items associated with a quote line, and lookup the data there.
1 Like

OH… and there might be a FOURTH way to convert quotes if you are running ECC… @Timothy_Melatis may have info on that… If I remember correctly, when ECC converts quotes to orders, it does it a different way than the previous 3 methods mentioned. We had to use a Data BPM to move data from a quote to an order.

1 Like

Here is a DD Design which I think is what you are asking for as this Sync’s up Order/Quote Comments & a PartsList_c field from the Quote to Order.

Code to grab the field data from QuoteDtl Line and store in Variable for widget access using other widget defined variables.

this.qdPartsList_c = 
  Db.QuoteDtl.Where(r => r.Company == Session.CompanyID && r.QuoteNum == this.iQuoteNum && r.QuoteLine == this.iQuoteLine)
    .Select(r => r.PartsList_c)
    .FirstOrDefault() ?? "";

FWIW, this is for QuoteDtl lines and not the Header but it should get you the idea…

Tim,

Hey thank you. I had already thought of that. Personnel in our company fortunately only know one way to do it Opp/Duote action >Create Sales Order and have been told to only use that method based on my predecessor creating another BPM for sending email to Sales Rep when converted. Whew!

Thank you. This worked well but… I failed to say there are actually two UD fields, DecorType and BldgType. Am I going to have to create Two Separate BPM for this?

Just add in another Set Field block and set the BldgType to what you need. Then do the Save SO.

Doug C.'s BPM validated and should work fine but now I cannot save the Method Directive. Of course Epicor Support says they cannot help with this as it is outside scope of maintenance. I want so bad to argue with them that NO, the method validates but wont save. Obvious some kind of bug in version 10.1.500.x or a logic error.

Anyway this is the error I receive. Any help would be appreciated.

This process validates and saves. However, when I SAVE the Method Directive itself, I receive a compilation error as follows.

Server Side Exception

There is at least one compilation error.

Exception caught in: Epicor.ServiceModel

Error Detail

Description: There is at least one compilation error.

Details:
Error CS0433: The type ‘Erp.Tablesets.ETCAddrValidationTableset’ exists in both ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.SalesOrder.dll’ and ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.Quote.dll’ [CustomizationAdapter.cs(264,30)]
Error CS0433: The type ‘Erp.Tablesets.QuoteQtyTable’ exists in both ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.SalesOrder.dll’ and ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.Quote.dll’ [CreateOrder.CommonTypes.cs(281,33)]
Error CS0433: The type ‘Erp.Tablesets.HedTaxSumTable’ exists in both ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.SalesOrder.dll’ and ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.Quote.dll’ [CreateOrder.CommonTypes.cs(301,33)]
Error CS0433: The type ‘Erp.Tablesets.PartSubsTable’ exists in both ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.SalesOrder.dll’ and ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.Quote.dll’ [CreateOrder.CommonTypes.cs(306,33)]
Error CS0433: The type ‘Erp.Tablesets.TaxConnectStatusTable’ exists in both ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.SalesOrder.dll’ and ‘d:\Websites\EpicorERP10\Server\Assemblies\Erp.Contracts.BO.Quote.dll’ [CreateOrder.CommonTypes.cs(311,33)]
Program: Epicor.Customization.dll
Method: PrepareException
Line Number: 99
Column Number: 13
Server Trace Stack: at Epicor.Customization.Standard.CustomizationCompiler.PrepareException(CompilerErrorCollection errors) in D:_Releases\ICE\3.1.500.46\Source\Framework\Epicor.Customization\Standard\CustomizationCompiler.cs:line 99
at Epicor.Customization.Standard.CustomizationCompiler.Compile(BuildEnvironment input, String outputAssembly) in D:_Releases\ICE\3.1.500.46\Source\Framework\Epicor.Customization\Standard\CustomizationCompiler.cs:line 29
at Epicor.Customization.Standard.CustomizationBuilder.Process(CustomizationProject project) in D:_Releases\ICE\3.1.500.46\Source\Framework\Epicor.Customization\Standard\CustomizationBuilder.cs:line 78
at Ice.Services.BO.BpMethodSvc.AfterUpdate() in D:_Releases\ICE\3.1.500.46\Source\Server\Services\BO\BpMethod\BpMethod.Events.cs:line 149
at Ice.Services.Trace.TablesetProfilingCollector.DoTablesetEventTrace(String tablesetName, String methodName, Action action) in D:_Releases\ICE\3.1.500.46\Source\Framework\Epicor.Ice\Services\TablesetProfilingCollector.cs:line 200
at Ice.TablesetBound3.InnerUpdate(IceDataContext dataContext, TFullTableset tableset) in D:\_Releases\ICE\3.1.500.46\Source\Framework\Epicor.Ice\Services\TablesetBound.cs:line 816 at Ice.Services.BO.BpMethodSvc.Update(BpMethodTableset& ds) in D:\_Releases\ICE\3.1.500.46\Source\Server\Services\BO\BpMethod\BpMethod.Designer.cs:line 835 at Ice.Services.BO.BpMethodSvcFacade.Update(BpMethodTableset& ds) in D:\_Releases\ICE\3.1.500.46\Source\Server\Services\BO\BpMethod\BpMethodSvcFacade.cs:line 640 at SyncInvokeUpdate(Object , Object[] , Object[] ) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) at Epicor.Hosting.OperationBoundInvoker.InnerInvoke(Object instance, Func2 func) in D:_Releases\ICE\3.1.500.46\Source\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 59
at Epicor.Hosting.OperationBoundInvoker.Invoke(Object instance, Func2 func) in D:\_Releases\ICE\3.1.500.46\Source\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 28 at Epicor.Hosting.Wcf.EpiOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) in D:\_Releases\ICE\3.1.500.46\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.SecurityChannelListener1.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 Ice.Proxy.BO.BpMethodImpl.Update(BpMethodDataSet ds)
at Ice.Adapters.BpMethodAdapter.OnUpdate()
at Ice.Lib.Framework.EpiBaseAdapter.Update()
at Ice.UI.App.BpMethodEntry.Transactions.MainTransactionBase.adapterUpdate()

HELP! I do not understand why these are stopping the compile.

Hello @bschiefen
We use the same method to convert all our Quotes (from the quote entry screen)
We also have fields to copy across to the OrderHed from the QuoteHed

We use a Method Directive on the Erp.Quote.CreateOrder directive, and use a Post-Processing type

The Post-Processing already has the orderNum variable available in it already

image

image

We added a Custom Code block that has code like this in it (I modified it to suit your fields)

UPDATED - I’ve updated this code based on @timshuwy’s comments below

//Sets SO fields from quote
var quoteNum = Db.OrderDtl.Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).Select(x => x.QuoteNum).FirstOrDefault();
if(quoteNum != null)
{
 
   var quote = Db.QuoteHed.Where(q => q.Company == Session.CompanyID && q.QuoteNum == quoteNum).Select(x => new {x.DecorType_c, x.BldgType_c}).FirstOrDefault();       
   if(quote != null)
   {
   //UD fields to copy to SO from Quote BldgType
	using(var txScope = IceContext.CreateDefaultTransactionScope())
	{ 
    var order = Db.OrderHed.Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).FirstOrDefault()

      order.DecorType_c = quote.DecorType_c; 
      order.BldgType_c = quote.BldgType_c;
      Db.Validate();
      txScope.Complete();     
    }
}
}

Thanks that’s a great example. I’m trying to make changes to the lines instead of the header. When i run this it modifies the first line of the order but not the following lines.

My Method Parameters dont look right to me.

Does this seem right?

image

Yep, looks the same as mine

Did you figure this out? I’m having same issue trying OrderDtl. Only first line gets updated

You might want to add a few additonal lines of code:

  1. a line for the Transaction Scope
  2. add a “lockHint.UpdLock” for record locking
  3. DB.Validate
  4. txScop.Complete

I also moved the read for the order down into the inside of the scop as well as inside the checking for the null quote. No need to read the order if you are not going to modify it.

var quoteNum = Db.OrderDtl.Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).Select(x => x.QuoteNum).FirstOrDefault();
if (quoteNum != null) {
    var quote = Db.QuoteHed.Where(q => q.Company == Session.CompanyID && q.QuoteNum == quoteNum).Select(x => new { x.DecorType_c, x.BldgType_c }).FirstOrDefault();
    if (quote != null) {
        //UD fields to copy to SO from Quote BldgType

        using(var txScope = IceContext.CreateDefaultTransactionScope()) {
            var order = Db.OrderHed.With(LockHint.UpdLock).Where(o => o.Company == Session.CompanyID && o.OrderNum == orderNum).FirstOrDefault();
            order.DecorType_c = quote.DecorType_c;
            order.BldgType_c = quote.BldgType_c;
            Db.Validate();
            txScope.Complete();
        }
    }
}
1 Like

Thanks @timshuwy

What are the advantages of using there two points?

This makes sure that you have the record available to update and it will reduce errors.

Thank you Sir,

Learning never stops, … I learned by copying our implementer’s coding… and mistakes

1 Like

What mean “o” “q” and “x” where are the variables???