Updating Order Header UD field based on Order Detail/Line

, ,

Dear Members,

I am writing to seek guidance and suggestions regarding an issue I have been facing with updating the OrderHed UD (User-Defined) field based on the OrderDtl partnum field in the Epicor ERP system.

To provide some context, my goal is to update the OrderHedUD field to “true” if any line contains the partnum with “ZZZ”. Conversely, if there are no lines with partnum “ZZZ” in the SO, I would like to update the OrderHedUD field to “false.”

Thus far, I have managed to achieve this at the line level using a DD-BPM at the OrderDtl table. Here is the code I have implemented with the condition widget as "OrderDtl.PartNum of the changed row contains “ZZZ"expression”:

Erp.Tables.OrderHed OrderHed;
var OrderDtlRow = ttOrderDtl.FirstOrDefault();
int OrderNum = (int)OrderDtlRow.OrderNum;
OrderHed = (from OrderHed_row in Db.OrderHed
where OrderHed_row.Company == Session.CompanyID
&& OrderHed_row.OrderNum == OrderNum
select OrderHed_row).FirstOrDefault();

if (OrderHed != null)
{
OrderHed.ZZZPart_c = true;
}

And if deleted: OrderDtl.PartNum of the deleted row contains "ZZZ"expression, and set this above code to false.

However, my challenge arises when there are multiple lines with partnum “ZZZ,” and if even one of those lines is deleted or updated, the OrderHedUD field should still remain “true”. Basically,it requires iterating through the OrderDtl table to assess all lines.

I have been working on this problem for several days now, and I haven’t had much luck in finding a solution. I would greatly appreciate any guidance, suggestions, or insights from those who have encountered a similar situation before.

Thank you all in advance for your time and assistance. I look forward to your valuable suggestions and recommendations.

Why? Where will the information be used?

Sorry, I missed on this point for why I want to achieve this.

Under email notifications when a SO is approved, I want to let sales team know if there is any part with required conditions(basically ZZZ part).

How are you sending the email notification, with a BPM? Wouldn’t it be easier to put the logic to detect the condition in that BPM instead of trying to maintain the value on the order header? You could use a condition widget with a designed query, put the condition there, and return true if record counts>0.

Yes, I am sending notifications through BPMs.

I tried that doing it in first place but it didn’t worked.


I was doing this at Orderdtl.update/Intransaction.

Did I do anything wrong? Should be a method ?

Part of the criteria in that query needs to be the order num. Also you need a wildcard in there for the part num begins criteria.

Sorry, but can you please tell me more about what you mean by “wildcard” ?

@itsme What happens when a sales order is “Approved”? Could that be used to do the check for any ZZZ parts and email?

Hi @gpayne, yes we can try to do it when Sales order is approved. Do you have any idea how to achieve that?

@itsme In an In trans DD on OrderHed make a condition that your approved variable has changed from false to true. Add a custom code block to the true side and this code should get you close to a generic email.

ZZZ SO.cs (1.9 KB)

Hi @gpayne, thank you for providing with the code. I tried under both In-Trans DD on OrderHed and SalesOrder.Update>Pre-Processing but no luck.

I did some changes but no luck. I guess I am missing something somewhere.

Sorry for being niave :sweat_smile:

In trans should fire, in fact when I tested without a condition it ran three times so we will probably need to put a condition on it, but first we need it running then we can fix that.

If you have access to the app server event viewer there should be the line below if the code ran.

image

If it did then it could be an issue with running async emails.

Yes, it actually fired but has errors:

Error Details:
function Toggle(node) { if (!window.fullyLoaded) return; // Expand the branch? if (node.nextSibling.style.display == ‘none’) { // Change the sign from “+” to “-”. var tBodyNode = node.childNodes[0]; var trNode = tBodyNode.childNodes[0]; var tdNode = trNode.childNodes[0]; var bNode = tdNode.childNodes[0]; var textNode = bNode.childNodes[0]; if (textNode.nodeType == 3 /* Node.TEXT_NODE /) { var s = textNode.data; if (s.length > 0 && s.charAt(0) == ‘+’) { textNode.data = ‘-’ + s.substring(1, s.length); } } // show the branch node.nextSibling.style.display = ‘’; } else // Collapse the branch { // Change the sign from “-” to “+”. var tBodyNode = node.childNodes[0]; var trNode = tBodyNode.childNodes[0]; var tdNode = trNode.childNodes[0]; var bNode = tdNode.childNodes[0]; var textNode = bNode.childNodes[0]; if (textNode.nodeType == 3 / Node.TEXT_NODE */) { var s = textNode.data; if (s.length > 0 && s.charAt(0) == ‘-’) { textNode.data = ‘+’ + s.substring(1, s.length); } } // hide the branch node.nextSibling.style.display = ‘none’; } } // Toggle “System” element by default so that it’s default status is to hide its children function ToggleSystemElement() { var body = document.getElementById(“body”); var anchor = body.getElementsByTagName(“table”)[0]; Toggle(anchor); } // If binary data is present in event XML, show it in friendly form. function ProcessBinaryData(binaryString, binaryDataCaption, wordsFormatString, bytesFormatString, normalFont, fixedWidthFont) { var bodyNode = document.getElementById(“body”); // Add a


at the end of the HTML body. bodyNode.appendChild(document.createElement(“hr”)); // This paragraph (p element) is the “Binary data:” literal string. var p = document.createElement(“p”); p.style.fontFamily = normalFont; var b = document.createElement(“b”); b.appendChild(document.createTextNode(binaryDataCaption)); p.appendChild(b); p.appendChild(document.createElement(“br”)); bodyNode.appendChild(p); // // Show binary data in Words format. // p = document.createElement(“p”); p.style.fontFamily = normalFont; p.appendChild(document.createTextNode(wordsFormatString)); bodyNode.appendChild(p); // Must use fixed-width font for binary data. p = document.createElement(“p”); p.style.fontFamily = fixedWidthFont; var i = 0; var j = 0; var s, tempS; var translatedString; var charCode; var byte1, byte2; // Each character in binaryString is a hex (16-based) representation of // 4 binary bits. So it takes 2 characters in binaryString to form a // complete byte; 4 characters for a word. while (i < binaryString.length) { s = (i / 2).toString(16); // To hex representation. while (s.length < 4) { s = “0” + s; } s += ": "; // DWords representation is simply a rearrangement of the original binaryString // For example, from: // // 0000000002005600000000000f000540 // // (which is 00 00 00 00 02 00 56 00 00 00 00 00 0f 00 05 40). // // to: // // 0000: 00000000 00560002 00000000 4005000f // 8 words per line, 4 DWords per line. for (j = 0; j < 4; j++) { s += binaryString.substring(i + 6, i + 8); s += binaryString.substring(i + 4, i + 6); s += binaryString.substring(i + 2, i + 4); s += binaryString.substring(i, i + 2) + " "; i += 8; } p.appendChild(document.createTextNode(s)); p.appendChild(document.createElement(“br”)); } bodyNode.appendChild(p); // // Show binary data in bytes format. // p = document.createElement(“p”); p.style.fontFamily = normalFont; p.appendChild(document.createTextNode(bytesFormatString)); bodyNode.appendChild(p); // Must use fixed-width font for binary data. p = document.createElement(“p”); p.style.fontFamily = fixedWidthFont; i = 0; j = 0; // Each character in binaryString is a hex (16-based) representation of // 4 binary bits. So it takes 2 characters in binaryString to form a // complete byte. while (i < binaryString.length) { translatedString = “”; // 2 characters in binaryString to form a byte s = (i / 2).toString(16); // to hex representation. // Prefix with ‘0’ until its length is 4. while (s.length < 4) { s = “0” + s; } s += ": "; // Show 8 bytes per line for (j = 0; j < 8; j++) { tempS = binaryString.substring(i, i + 2); // 2 for 1 byte i += 2; s += tempS + " "; // Treat tempS as hex integer charCode = parseInt(tempS, 16); if (charCode < 32) { translatedString += “.”; } else { translatedString += String.fromCharCode(charCode); } } while (s.length < 32) { s += " "; } s += translatedString; p.appendChild(document.createTextNode(s)); p.appendChild(document.createElement(“br”)); } bodyNode.appendChild(p); }

Description: An error occurred while sending mail Program: Ice.Internal.SysTask.SendMailAction.dll Method: ProcessItem Line Number: 127 Column Number: 9 Server Trace Stack: at Ice.Internal.SysTask.SendMailAction.ProcessItem(MailQueue queuedItem) in C:_Releases\ICE\RL10.2.500.0FW\Source\Server\Internal\SysTask\SendMailAction\SendMailAction.cs:line 127 at Ice.Internal.SysTask.SendMailAction.ProcessQueuedItems() in C:_Releases\ICE\RL10.2.500.0FW\Source\Server\Internal\SysTask\SendMailAction\SendMailAction.cs:line 52 at Ice.Internal.SysTask.QueueProcessor2.RunSystemTask() in C:\_Releases\ICE\RL10.2.500.0FW\Source\Server\Internal\SysTask\QueueProcessor\QueueProcessor.cs:line 60 at Ice.Hosting.SystemTaskCaller.RunSystemTask(Type systemTaskType) in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.Ice\Hosting\TaskCaller\SystemTaskCaller.cs:line 65 at Ice.Hosting.SystemTaskCaller.ExecuteTasks() in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.Ice\Hosting\TaskCaller\SystemTaskCaller.cs:line 47 at Ice.Services.Lib.RunTaskSvc.RunSystemTasks() in C:\_Releases\ICE\RL10.2.500.0FW\Source\Server\Services\Lib\RunTask\RunTask.cs:line 253 at Ice.Services.Lib.RunTaskSvcFacade.RunSystemTasks() in C:\_Releases\ICE\RL10.2.500.0FW\Source\Server\Services\Lib\RunTask\RunTaskSvcFacade.cs:line 104 at SyncInvokeRunSystemTasks(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 C:_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 59 at Epicor.Hosting.OperationBoundInvoker.Invoke(Object instance, Func2 func) in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 28 at Epicor.Hosting.Wcf.EpiOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs) in C:\_Releases\ICE\UD10.2.500.27FW\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)

Any-other idea for making this work?

Start with message boxes to check your logic for the conditions before you takle the email part. It’s a widget in the menu. Make sure that it’s firing when you want, and you can add in information on the message to help you troubleshoot.

Hi Banderson, I actually tried to fire a message and then my custom code. Message dialog box appears after a SO 's status has been changed from any to Approved but I don’t receive any email.

Do you have e-mail from a BPM working anywhere else? Sounds to me like your email service is mis-configured.

There are some wonky issues with async emails. Epicor recommends turning them off.
If you need to send async, I would use a function and the task scheduler.
(Send sync but in a scheduled function)(Schedule now)

@klincecum So I should not tell my app server that. It sends hundreds of async emails a day. Even the routine I posted with me@domain got my email from my user and CCd me on my test. :slight_smile:

Maybe they fixed it?

Maybe if we can stick at maintaining field at OrderHed level? I have this code working but just needs to keep at correct directive with loop/iteration when a line is deleted to check if ZZZ part is available in rest of the lines as well. If there is any line with ZZZ after deletion/update, remains true else false.

Code:

Erp.Tables.OrderHed OrderHed; 

var OrderDtlRow = ttOrderDtl.FirstOrDefault();

int OrderNum = (int)OrderDtlRow.OrderNum;

OrderHed = (from OrderHed_row in Db.OrderHed
where OrderHed_row.Company == Session.CompanyID
&& OrderHed_row.OrderNum == OrderNum
select OrderHed_row).FirstOrDefault();

if(OrderHed !=null)
{
  OrderHed.ZZZPart_c = false ;
}`

Any thoughts?