OrderHed UD Field BPM

I have created a date UD field on the OrderHed table. The goal is to have this UD field populate with the date in the ShipBy field prior to getting updated via updatable Dashboard. We simply want to retain the date every time it is updated. Started by creating a pre-process method directive on Erp.BO.SalesOrder.MasterUpdate. What I have created only populates the UD field if the OrderHed ShipBy field is null. It will update the UD field then, but from there as the ShipBy date is altered the change is not reflected in the UD field. Any help is much appreciated.

Is your directive enabled?

What is the entire expression in your screenshot?

It is enabled.
Expression is dsOrderHedRow.RequestDate

This should work.
Are you multi company by chance? If so, is your scope company independent?

We are multi company, but the scope is set to company specific. Let me retract/clarify what is occurring. Say said date is xx/xx/xxxx, we want that date to populate the UD field. When changing the date to yy/yy/yyyy that same date is being transferred to the UD field upon saving.

At this point, the ttOrderHed.RequestDate is already your new value.

Typically on the “pre-processing” directive, there’s actually two ttOrderHed records in the dataset. One of them is the “original” row which would contain your xx/xx/xxxx date. The RowMod is blank on this row. The other is the row being updated, and this would contain the yy/yy/yyyy date in it; RowMod is “U” on this row.

What you’re doing is setting the UD field of the changed row (the “U”) to the value of RequestDate, which is your changed date. What you really need is a widget that says “set the ds.OrderHed.PreviousDate_c field of the changed row to the dsOrderHedRow.RequestDate of the unchanged row”. Unfortunately, there’s no such widget.

You might be able to accomplish this via the query function. Or, if you know custom code, you could get the ttOrderHedRow that’s original, save the date, then get the ttOrderHedRow that’s being updated and set that date there.

1 Like

As a side note, if you want the directive to apply to all companies, make sure to set to company independent. This has bit me a few times. I have a BPM that sets the default to company independent, which are almost all of our directives anyway

1 Like

May be less hassle to just make the UD field updatable and have users input the needed date before changing the ShipBy field.

If you go the BPM route, which I think is less hassle than asking users to change it, I’d first add a condition that RequestDate has changed from any to any, then execute this custom code:

foreach (var OrderHed_iterator in (from OrderHed_Row in OrderHed
             where OrderHed_Row.RowMod = "U"
    select OrderHed_Row))
{
    var OrderHedRow = OrderHed_iterator;
    var orig = ( from drow in Db.OrderHed 
                where drow.Company == OrderHedRow.Company 
                   && drow.OrderNum == OrderHedRow.OrderNum
      select new { drow.RequestDate} ).FirstOrDefault();
    if (orig != null )   ttOrderHedRow["PreviousDate_c"] = orig.RequestDate;
}

This gets the original date from the as-yet unchanged OrderHed, but you could also probably put a query in there for ttOrderHed at the start to save it off. You might also need some logic to handle blank (null) RequestDates if someone cleared it out. Code is not tested, just threw it together, so not there might be some typos.

5 Likes

here is a different take on the above code (untested). The difference is that we only get the data from the memory version (tt) where both the unmodified version and the modified version exist. once we have both versions, then we take the data from the old original version and put it into your UD FIeld.
I also changed the queries into Lamba queries since I find them less wordy and easier to read.

newOrderHed = ttOrderHed.where(x=>x.RowMod == "U").FirstOrDefault();
if(newOrderHed != null) {
    origOrderHed = ttOrderHed.where(x=>x.RowMod == "").FirstOrDefault();
    if (origOrderHed != null) {
        newOrderHed.PreviousDate_c = origOrderHed.RequestDate;
    }
}
4 Likes

Just in case you needed a third opinion, the only difference I’d make is to add a check to see if the request date changed. Since this is fired on MasterUpdate, changing another field on the OrderHed would trigger the BPM, and PreviousDate_c would be overridden with the current RequestDate. A quick change to Tim’s lambda expression would make it only change when PreviousDate_c is not already set.

var newOrderHed = ds.OrderHed.FirstOrDefault( x => x.Updated() && x.PreviousDate_c == null );

if ( newOrderhed == null ) return;

var originalReqDate = ds.Orderhed.FirstOrDefault( x => x.Unchanged() && x.SysRowID == newOrderHed.SysRowID && x.RequestDate != newOrderHed.RequestDate ).RequestDate;

if ( originalReqDate != null && newOrderHed.RequestDate != originalReqDate )
{
    newOrderHed["PreviousDate_c"] = originalReqDate );
}
4 Likes

We are taking a shot at your code. Added one minor detail in variable "UDField (“PreviousDate_c”) to get out of some syntax errors. BPM Validates and saves but when testing on order there is no change in UD field. The condition is built into the code and therefore doesn’t need the widget before as @SimsTrak example used correct? Is there any additional code we need to add on our end?

We had to add a condition to populate the UD field to avoid a server side error with Inner Exception: ‘PreviousDate_c’ UD field of ‘OrderHed’ table either is not loaded or not defined. This error only occurred when the UD field was null and could cause errors when a user initially created a order and populated the RequestDate.

var newOrderHed = ds.OrderHed.FirstOrDefault( x => x.Updated() && x.UDField("PreviousDate_c")== null );

if ( newOrderhed == null ) return;

var originalReqDate = ds.Orderhed.FirstOrDefault( x => x.Unchanged() && x.SysRowID == newOrderHed.SysRowID && x.RequestDate != newOrderHed.RequestDate ).RequestDate;

if ( originalReqDate != null && newOrderHed.RequestDate != originalReqDate )
{
    newOrderHed["PreviousDate_c"] = originalReqDate );
}
1 Like

Well if it’s not null before that code, it’s not going to fire.

The code in that block checks and expects it to be null.

So correct me if I am wrong, and I may be misunderstanding the exception error, but if the code block is expecting the field to be null, I shouldn’t be experiencing that error when the field is in fact null, yes? How does the BPM need to be executed? This error only occur when set to synchronously.

That code block may not work for this scenario then, once our RequestDate fields are populated they will never revert back to null. (I know little code if you can tell :rofl:)

It changes the UD Field only when RequestDate changes, and RequestDate was already populated and if PreviousDate_c is null. if RequestDate is set for the first time, it does not populate. If requestDate is changed from one date to another, PreviousDate_c should populate with the original date.

@Dylan_Ainsworth The code @timshuwy posted will only fire when there is an Updated RequestDate. On the first save PreviousDate_c will not have a value as the date has not yet been updated to a new date.

If you want to set the previous to the original on the first pass we can adapt to that.

This code is a bit of all of the above ideas and it has been tested in my dev environment. The Date03 is a UD in my system.

/* update previous Date */

Ice.Diagnostics.Log.WriteEntry("start update previous Date");

var newOrderHed = ds.OrderHed.Where(x => x.Updated()).FirstOrDefault();
if(newOrderHed != null) 
{

Ice.Diagnostics.Log.WriteEntry($"found updated row in previous Date {newOrderHed.RequestDate:D}");

    var origOrderHed = ds.OrderHed.Where(x => !x.Updated()).FirstOrDefault();
    
    if (origOrderHed != null && origOrderHed.RequestDate != newOrderHed.RequestDate) 
    {
    
        Ice.Diagnostics.Log.WriteEntry($"found original row in previous Date {origOrderHed.RequestDate:d} new {newOrderHed.RequestDate:d} ");
        
        newOrderHed.SetUDField<DateTime>("newOrderHed.PreviousDate_c",origOrderHed.RequestDate);
        //newOrderHed.SetUDField<DateTime?>("Date03",origOrderHed.RequestDate);
    }
}

Ice.Diagnostics.Log.WriteEntry("End update previous Date");

This is the debugging output in the serverlog
image

So we were able to begin manually changing the RequestDate in Order Entry and accomplished populating the PreviousDate_c with the following code.

However our goal is to manipulate the RequestDate via updatable dashboard. The BAQ is ran off method UpdateExt. The RequestDate is changed and the BAQ is executed without error, but the PreviousDate_c does not reflect the change, as if the Method Directive is not being fired. My question is more general in what direction do I need to go? Do I need to configure a BPM in BAQ Designer with update method “Advanced BPM Update only” or create a new method directive with method UpdateExt? The code pasted below was attempted on both methods, but only works under MasterUpdate, will it need to be altered for UpdateExt?

var newOrderHed = ds.OrderHed.Where(x=>x.RowMod == "U").FirstOrDefault();
if(newOrderHed != null) {
    var origOrderHed = ds.OrderHed.Where(x=>x.RowMod == "").FirstOrDefault();
    if (origOrderHed != null) {
        newOrderHed["PreviousDate_c"] = origOrderHed.RequestDate;
    }
}

You can set the previous date in the ubaq preprocessing on the update.

Epicor only sends the updated rows, so you just need a bit on code or you could do a widget.

Something like this.



foreach (var ttr in result.Results)
{
	ttr.OrderHed.PreviousDate_c = ttr.OrderHed.RequestDate;
	
}

Greg, forgive me for my little knowledge on the subject.

Are you inferring to open menu “Updatable BAQ Directives Maintenance” and then choose the Update method under this query?

From there create a new Pre-Process method in designer to invoke the code you posted?

in your updatable baq you have the Update Processing tab and it has a BPM Directive configuration button on the right.


In there you can do this in code or in a set field widget
image
Code. In code you need to do a right click parameters to see what Epicor is sending in as it varies from place to place. In a preprocessing ubaq it is
image
so I change the code to match.

foreach (var ttr in queryResultDataset.Results)
{
  ttr.OrderHed.PreviousDate_c = ttr.OrderHed.RequestDate;
  
}