Invoke BPM action from within Function Code

I’ve got a function with quite a bit of custom code developed in it. However, when I perform an update to a table, I’m not getting any method directive BPM’s to fire. To simplify, I’ve got the following custom code in a Function:

var context = (Erp.ErpContext)Ice.Services.ContextFactory.CreateContext();
var erpContext = new Erp.Internal.Lib.CCredChk(context);
using(var txScope = Ice.IceContext.CreateDefaultTransactionScope())
{
        using(var ud40Svc = Ice.Assemblies.ServiceRenderer.GetService<UD40SvcContract>(erpContext.Db))
        {
            UD40Tableset udTS = new UD40Tableset();
            udTS = ud40Svc.GetByID("TEST", string.Empty, string.Empty, string.Empty, string.Empty);

            if (udTS.UD40[0].CheckBox01 == true)  udTS.UD40[0].CheckBox01 = false;
            else udTS.UD40[0].CheckBox01 = true;
            udTS.UD40[0].RowMod = "U";
            ud40Svc.Update(ref udTS);
        }
    txScope.Complete();
}  // end txScope

I then created three pretty simple BPMs. All have a condition widget where the Key1 of the changed row is equal to “TEST”, and a SET FIELDS widget to set a ShortChar field to a value:

  • A method directive on UD40.Update that sets ShortChar01 to “UPDATE”.
  • A method directive on UD40.UpdateExt that sets ShortChar02 to “UPDATEEXT”.
  • A data directive on UD40 that sets ShortChar03 to “DATA”.

When I run the function, it appears that the data directive is the only one firing. I wasn’t really expecting the UpdateExt to fire (the logic calls Update, not UpdateExt), but I’m a bit surprised the UD40.Update method is not firing.

I realize there’s a widget in functions that calls a BPM Method code, but is there a reason why when I’m using the UD40 service that doesn’t seem to be calling the method directive kicked off by function custom code?

For my purposes, I’ve got a pretty easy workaround - I’m going to move my method directive over to a data directive - problem solved. But I’m wondering why the method is not firing.

Thanks.
Kevin Simon

In any case you’re calling a BO/Service from a function incorrectly. That txscope stuff is all weird foo too. For a service call include in in your assemblies for the function and call the service like this.

this.CallService<UD40SvcContract>(ud40Svc => {
 // Service stuff
});

See what you get

4 Likes

That did the trick. Thanks!!

Interesting behavior… I’ve done quite a bit of testing, and I can get the BPM to fire based on an update coming out of the function, EXCEPT the BPM won’t recognize the “field has been changed from any to another” condition.

Here’s the function code (thanks Joshua)…

this.CallService<UD40SvcContract>(ud40Svc => {
    UD40Tableset udTS = new UD40Tableset();
    udTS = ud40Svc.GetByID("TEST", string.Empty, string.Empty, string.Empty, string.Empty);
    udTS.UD40[0].Character03 = "Try 2: " + DateTime.Now.ToString("yyyyMMdd-HHmmss");
    if (udTS.UD40[0].CheckBox01 == true ) udTS.UD40[0].CheckBox01 = false;
    else udTS.UD40[0].CheckBox01 = true;
    udTS.UD40[0].RowMod = "U";
    ud40Svc.Update(ref udTS);
});

So, I’m setting Character03 to a date/time string so I can see it’s updating, and I’m toggling the Checkbox back and forth from false to true.

I’ve got a BPM that has a condition and it sets a field to “BPM Update” if the condition is TRUE, and “BPM Not Update” if the condition is FALSE.

When the function fires, it updates the UD40 “TEST” record and changes the values of CheckBox01 and Character03.

The conditions in my BPM will evaluate “Key1 of changed row is TEST” or “CheckBox01 of changed row is true”. However, it appears to ALWAYS evaluate “Checkbox01 has changed from any to another” to FALSE, even when it’s clearly changing.

Kevin

That is because you have one row in that dataset the updated row and that’s it. You need to use a buffer copy so you have an unchanged row and a changed row. That’s how those directives work. They don’t operate by comparing an updated row to the database.

3 Likes

Great thread with great info, this is what @JasonMcD is talking about- the nuances of custom coding and having to figure out the “foo foo.” NOT a lost cause - a function for new labor dtl with no code - #49 by JasonMcD

Thanks for explaining the buffer copy @jgiese.wci

I think @hkeric.wci said that widgets don’t handle buffer copies well which is one reason why custom code is nice in this case.

Note also that because @SimsTrak was custom coding something it caused an issue that a widget based approach most likely wouldn’t have caused:

“In any case you’re calling a BO/Service from a function incorrectly. That txscope stuff is all weird foo too. For a service call include in in your assemblies for the function and call the service like this.”

Still, the approach always depends on the situation- I am not lobbying for one approach v.s. another. Thank you @SimsTrak for posting this thread so I can try and learn from your code.

-Utah

1 Like

I’m not sure if it ever got addressed by Epicor, but query widgets where you need to compare a TT table to a DB table are horribly inefficient 90% of my query based conditions are custom code.

There are just some things you have to do custom.

2 Likes

My Ticket is still in status Work In Progress and has been Accepted by Development as Performance Bug.

Title:
BPM Visual Designer OR Statements Poor Performance / Many DB Hits

Problem:
When using the Visual BPM Designer Interface and you create OR / AND Statements - the SQL Database is hit each time PER Statement. Assumption is that it’s all 1 Query. But instead each statement creates a new .Any() SQL Lookup.
See Attached Screenshots

STEPS TO DUPLICATE:
Create any BPM with a number of OR conditions that check wehther or not a tt. has been changed from any to another–one .Any() SQL lookup will occur per OR condition instead of one .Any() per condition object.

Basically some Conditions atleast in 10.1.500 compared your value against the database… If you have like 10 conditions, thats 10 database hits. Not sure which one but… sometimes a Custom Code Condition may be faster. Not even sure why it didnt compare Unchanged to Changed, it actually went to the database.

May have been even fixed, or we had a fluke. But FYI :slight_smile: I dont think its a big deal because .Any() is sorta super fast… But to Joshes point – sometimes you can get the calls down via Custom.

2 Likes

Hmm “Any to another” condition for tt-tables does not look at SQL. It works with in-memory objects.

Anyway, a condition in a condition block is a self-contained item. It knows nothing about other items.
Theoretically, some cases can be optimized (however it requires a lot of investments), but not in general.
What if you mix “Any to another” condition items with completely different ones (e.g. User Condition or Arg/Var condition)?

The ServiceRenderer and txscope stuff is straight out of the Converted Code Programming Guide, which in the 10.x days was a better tech ref for BPMs than any of the actual BPM documentation. The document has been renamed “Kinetic 2021.1” but the content has not changed substantially. Is CallService even officially documented anywhere? I only learned about it from this forum.

Not with respects to EFX that I’ve ever seen, but maybe? I don’t think EFX is even in the converted code document since it’s post ABL anyways.

As far as where I got the call service stuff from I don’t honestly remember. I was part of the Beta testing for EFX and got that information sometime back in June of 2020. That part of my memory has been long truncated so as far as origin goes, couldn’t tell you anymore lol.