In-Transaction BPM - Help Needed

I have a bpm that does a date shuffle on OrderRel.ReqDate and OrderRel.NeedByDate fields when the dates are entered by an integrated program. I see the data shuffle happen, and when I build a baq to check the data it shows correctly. However, when the program is done running almost 2 hours later our MRP scheduler runs. It is treating the ReqDate and the NeedByDate like they have not been shuffled.

I asked Epicor to see if there was a temp table that would be holding data for MRP instead of it just using the OrderRel table. The answer I got was that it was a customization and they couldn’t help with that.

My question is has anyone seen this kind of behavior from an In-Transaction BPM before? If so what was the work around for it?

Use the business object to change the dates not an in Tran directive
The BO makes a couple of calls ChangeNeedByDate etc that probably do more things

2 Likes

Where would I call the BO from, a Standard Data Directive?

You shouldn’t access BO’s in Data Directives. You should probably just use a Method Directive to begin with, perhaps.


image

Method Directives:

  • Pre, Post and Base processing logic
  • Access to BOs
  • Ideal place for Validations / Exceptions / Data Changes

In-Tran:

  • Executes after standard Entity Framework data triggers
  • Executes within a transaction, as a part of the trigger pipeline
  • Immediately processes affected row
  • Processes one row at a time (two rows for update operation (RowMod = “” is old row)
  • Can change data on save
  • — The In-Tran should never if it can throw an exception, it is very very expensive rollback.
  • You shouldnt access BOs in here!

Standard:

  • Executes when service method call has completed
  • Executes only if service method completes without exception
  • Processes batch of affected rows at once
  • Does not affect data save
  • Ideal place for integration operations (Audits, Email, Logging, Notifications, API)
2 Likes

@hkeric.wci,

Thanks for laying that out!

In this case the integrated program will always run at the same time and will always delete out all sales order release information. Once that has been accomplished it will then push in new data from our customers portal. The issue is that we are only interested in one of the two date fields coming in from the integration. This is why we opted to do the date shuffle and place the customers ship by date as the date that drives MRP.

What I would like to know is would I need to do a pre, base, or post process to catch their data before it is saved into our system?

Been taking a stab at using a method directive. I was using a preprocess on SalesOrder.Update, but I keep getting generic bpm errors from our integrator. As soon as I swapped to SalesOrder.UpdateExt the bpm doesn’t do what it was designed to do. I assume the Generic error is because they are calling the UpdateExt in their code and Update cant handle multiple line updates at once.

Back to the In-Transaction route. This allows the bpm to run and the integration doesn’t error. Additionally, this get the correct dates in the correct slots.

New Goal:
Create a post method directive that picks up the reqdate for all open sales order releases and updates the table with the same value again. By invoking this with the bo method I have confirmed that this works for individual releases. But, I need a way to do this on mass.

Update:
Following the instructions on the post below I’m trying to recreate their bpm as a function:

Here is what I have so far:

image

The first invoke bo method calls the Erp.SalesOrder.Update. The Update table by query used the OrderRel table with the criteria of open releases = true and firm releases = true. I have it updating all rows of dataset.OrderRel with the following configuration:

The last bo method is the same as the first.

@Aaron_Moreng @hkeric.wci
Question:
After running the process I get no errors, but I have little to no way of seeing if the data was actually pushed/updated. Nor does it appear that any significant changes were made as the data looks correct in order tracker and within the data table, but does not show correctly within Job Manager. What can I do to help troubleshoot through this update process and see the data that should be loaded into the table? What am I doing wrong when invoking these bo methods?

Make sure you’re passing a rowmod field value as well when updating/adding records via the widgets.
Reading a trace log of the transaction and replicating the important changed field values in the BO widget is how I have Succefully implemented this kind of thing.
Here’s how I have used the Update Table by Query widget:

First, I use a designed query to select the record from the tableset variable I am working with


I display the appropriate fields to select a single record

In my case, I am updating all rows of the table set. Then, in the configured mapping, I am finding the record with the query values and setting my variables in this with the bindings

What this is doing is modifying the tableset variable. Then, you need to call the Update BO and pass in the modified table


For debugging and/or confirmation of success/failure in a function, I create an “output” variable as part of the method Out parameter signature and I set this variable inside my BPM

Here is an example of me checking my GetByID method return to evaluate if I found a record. If I don’t, I set my output variable to an error and I throw an exception. You don’t have to throw an exception, but you can.

Conversely, you can do this for success too and set your output variable(s) to “success” or whatever tells you it went through ok.

2 Likes

@Aaron_Moreng

How do I add those method parameters? I see the button on the bottom left, but it doesn’t have an add a new button or anything.

EDIT:
Additionally, what is the last set argument for and what does your first invoke method look like? Where do we set the value for eventID? Thanks a ton!

You’ll define those parameters on the function level, if that is indeed what tool you are using. If this is just a BPM, you can create variables as well but it’s a little different.
The last set argument is to set my local variable equal to a value I determined. You don’t need that to do a table update, it’s just to demonstrate how to utilize local variables for confirming and communicating outside of your BPM
In my example, eventID is a function variable that I pass into my function.

So instead of checking eventID could I instead check to see that sysrowid isn’t blank? I went ahead and filtered my baq results down to a single order release so that I can visibly see when it goes through.

I am building it on a function. Just never saw where I could define parameters.

1 Like

Yep. I’ll post a screenshot in a while of what/where to define those if you’re still looking

1 Like

@Aaron_Moreng,

Just noticed that the if I do a table query after the last invoke method I get no returns. Which makes me think that I might not be getting data at all.

FYI… on your original question. The reason that the dates didn’t flip for MRP is because MRP doesn’t read the OrderRel table directly. Instead it reads the PartDtl table for the MRP Scheduling. In your case, you probably only modified the OrderRel table, which made it “look” ok but MRP ignored it. If you do a query on PartDtl table, you will see the old dates.

2 Likes

You’re 100% correct on that. I now see the PartDtl table isn’t affected by this in transaction date flip. If I manually change the date on the sales order release in order entry I see the correct date trickle through to the partdtl table. So what I need to do is have this in transaction happen then call a function to reapply the dates using the bo method. Would this work in your opinion?

if you call the same BO that Sales Orders call to update the OrderRel, then the PartDtl table should be updated. But I did have an instance where nothing I did would fix the PartDtl table, so I had to write some C# code to fix the PartDtl table to match the OrderRel. This was a special data import that was creating OrderRel in a way that was outside of what Epicor normally does.

@timshuwy,
That sounds like the exact instance I find myself in. Just out of curiosity do you still have that custom code lying around?

Sorry, I dont have the code… but it was fairly simple. The OrderRel and the PartDtl records are a pair of data that should match. I just made a data directive that looked for changes in the OrderRel required by date, and when that happened, I updated the related PartDtl record with the same change.

1 Like

Something Like This: - but you need to make a condition before it runs to make sure that the Required By Date has changed… you dont want to run this code for EVERY change of OrderRel.

using(var txScope = IceContext.CreateDefaultTransactionScope()) {
    var ttORel = ttOrderRel.Where(x => x.Updated());
    foreach (var ttORelRow in ttORel) {

        var dbPartDtl = Db.PartDtl.With(LockHint.UpdLock)
            .Where(r2 =>
                r2.Company == ttORelRow.Company &&
                r2.OrderNum == ttORelRow.OrderNum &&
                r2.OrderLine == ttORelRow.OrderLine &&
                r2.OrderRelNum == ttORelRow.OrderRelNum
            );
        foreach (var partDtl in dbPartDtl) {
            partDtl.DueDate = ttORelRow.ReqDate;
        }

    }
    Db.Validate();
    txScope.Complete();
}
1 Like