BPM Versioning & Disabling & moving to Live

In the category of Tips & Tricks…
Most BPM programmers simply create the BPM, enable, and run (pun intended)… Even I have been guilty. If it had documentation, it is sparse… and Version control? ha…:grin:

BUT LATELY, I have started adding version control & Comments directly into a BPM C# widget that is reserved for this purpose. (no code allowed per my rules). It is a detached widget (exception… see below).

When CHANGING or CREATING a BPM I do the following:

  1. create a C# Widget to hold the purpose and versioning information… update the version in the header of the widget. Make the Corner color of this widget ORANGE (to highlight it).
  2. document the purpose inside the widget with history.

When DISABLING the BPM, I do the following:

  1. put it into a special group so that when it gets moved to live, we know that it SHOULD be disabled.
  2. Disable it
  3. Disconnect the Start Widget from the main flow (just in case someone accidentally enables it)
  4. Modify my Version Widget so it says Disabled and turn it RED.
  5. Update the comments in the version widget with details.

NOW… When you move these into from TEST into LIVE, you can actually see which version has been moved:

  • Is the right version in Live? (yes, you can compare version numbers).
  • is it supposed to be disabled? (yes, you can see that now).

Of course, someday, we hope that someone will add a “Notes” or “Comments” tab to the BPM maintenance program… (safe harbor)

15 Likes

Very useful tips Tim.

But… you will get a warning for “Element has no incoming links, it will not be executed.” which I can live with. :slight_smile:

Why a C# block and not just a message widget?

I ignore the “warning”

Hmm… well, because I chose C# BECAUSE the first BPM I did this in was a Standard Data BPM and they don’t have Message Widgets…
That said… a message Widget would work fine.

1 Like

Good call, didn’t think of that. Are C# widgets available for cloud customers?

Cloud customers probably dont have C# widgets (which I neglected to mention in my OP.)… This was already highlighted internally at Epicor when I posted something similar. But for those that DO have the C# widget, this can be huge.

5 Likes

I figured I would be schooled by someone!

2 Likes

Looks like Mark has no excuse to start documenting :wink:

1 Like

Tim,
Great topic, just to add, not sure what everyone else is doing, but I export the object (whatever it is) and put the resultant file into bitbucket. It’s time consuming, but not as time consuming having to rebuild something from scratch.

Epicor please enable customization on the Method Directive and Data Directive maintenance, so we can spend some time applying our own version control hooks. :slight_smile:

1 Like

At minimum, can Epicor add the date of last save, as shown under the Dashboard module? When we search for the last modified dashboard I can sort by date… (I am not always taking notes of the names I create ! )
Having that date, in the search of a BPM would ease a lot the searching process…may as well add a file version as well as in the dashboard!
image

So the info is there probably but not shown !

The advantage of the notes would be kind of keeping track of what was changed…at the BPM level…A bit like the notes window that appears when we save as a customization. That would be great! That window opening on a save will remind you to put notes if needed! cannot forget!

But for the meantime, Tim’s suggestion is great…

2 Likes

Nice tip, Tim, thanks!

The idea of enabling and disabling the BPM before moving to a new environment and of having a custom code block at the beginning of each BPM got me thinking…couldn’t I do something similar to create a set of Feature Flags that will control whether a collection of related BPMs fire or not?

Here’s what I came up with…

  1. Create a UDCodeTypeID (I used "FeatureFlg").
  2. Populate the FeatureFlg CodeType with a list of CodeID's related to your features. Use the "Active" checkbox to indicate whether the featured should be enabled or disabled.
  3. In any BPM related to the feature, create a Boolean variable to hold the feature flag state (FeatureXEnabled).
  4. Next, include a piece of code to check the status of the feature flag:
    var featurexflag = Db.UDCodes.FirstOrDefault(u=>u.CodeTypeID=="FeatureFlg" && u.CodeID.ToLower()=="featurex");
    FeatureXEnabled = (featurexflag!=null ? featurexflag.IsActive : false);
    
  5. This will set the FeatureXEnabled variable to true only if the CodeID exists and is active. If the CodeID doesn't exist (you've just moved this feature to a new environment, for example), or if it is disabled, the FeatureXEnabled variable will be false.
  6. Add a Condition block to your BPM to check the FeatureXEnabled variable. If true, proceed with your BPM, if false, stop.

This would effectively allow you to move BPMs to your production environment, but prevent them from running until you are ready to turn on the feature. And you don’t have to go in and find all of the related BPMs and check the “enabled” flag on each one to do it.

Not sure if anyone will find this useful, but I figured I’d share just in case.

Thanks for the inspiration, Tim!

6 Likes

Love this idea Doug! I can see even doing a “limited” release by adding a Security Group in the mix so some users get a new version of code to test while others get the older code. Great idea.

Mark W.

1 Like

@Mark_Wonsil: image

Should be: “All Cloud users are treated equal. But some are more equal than others”

Trying to decide if that’s an MT threat or not…

None of the versions that I work on allow for a widget to be added for documentation. That’s my story, and I’m sticking to it.

1 Like

AWESOME idea… but lets adjust the query to make it slightly faster:

Version you showed:

var featurexflag = Db.UDCodes.FirstOrDefault(u=>
u.CodeTypeID=="FeatureFlg" && 
u.CodeID.ToLower()=="featurex");

FeatureXEnabled = (featurexflag!=null ? featurexflag.IsActive : false);

New version: Changes Made:

  1. there is no need to actually return a record. We only need to know if the system found “any” active… The Any command returns a very quick “true/false” result. The query you did will return the entire record instead of just the field(s) you need.
  2. I also added Company to the lookup so the entire key is populated.
bool FeatureXEnabled= Db.UDCodes.Any(u=>
u.Company == callContextClient.CurrentCompany &&
u.CodeTypeID=="FeatureFlg" && 
u.CodeID.ToLower()=="featurex" && u.IsActive);

Note, if you really needed to have the record and more values, you should put a “Select” to choose the fields. the example below returns two fields only. This is a common mistake where people return entire dataset when they only wanted one/more values.

var udcode = Db.UDCodes.Where(u=>
u.Company == callContextClient.CurrentCompany &&
u.CodeTypeID=="FeatureFlg" && 
u.CodeID.ToLower()=="featurex" && u.IsActive).Select(u=>new{u.CodeDesc,u.IsActive});
6 Likes

Thanks, Tim,

I appreciate the tip. I didn’t know about the .Any() method. That’s definitely a great thing to add to my arsenal! And you make a good point in adding the company, too!

I do have to admit that, although I am familiar with the .Select() method, I don’t use it nearly as often as I should. That’s something I am going to have to start using more often to make my code more efficient.

I appreciate your help!

Doug, don’t feel bad. I just really learned about .Any recently. I have known about the .Select for quite a while, and always try to use it. Recently, I found I had a query on a rather large table that I was returning 9 columns, which makes the query LOOK bigger, but it is still faster than returning everything (the lazy way).