ice.BPDirective :Figuring out which BPMs have been copied from our sandbox to our base environment

I am running queries like this to list data directives between the environment where our development has been happening and the environmants we are now testing in nad our basedata environment that will be used to spawn production at go live. Using power query then to “left anti join” these between databases (environments)

Can anyone point me to more info on the content of this table? I’m in the “knowing just enough to be dangerous, but not quite enough to be useful” zone currently…

I compare the directives between environments using
[Source] [BpMethodCode] [DirectiveType] [Name] as the key, I was using [DirectiveID] here to compare originally

SELECT  [DirectiveID]
      ,[Source]
      ,[BpMethodCode]
      ,[DirectiveType]
      ,[Name]
      ,[Order]
      ,[IsEnabled]
      ,[ReenterMax]
      ,[PreventDeadloops]
      ,[VisibilityScope]
      ,[Company]
      ,[DirectiveGroup]
      ,[IsUpToDate]
      ,[CGCCode]
      ,[Body]
      ,[Description]
  FROM [Ice].[BpDirective]
1 Like

This should get you started.

Maybe someone can fill in my gaps.

DirectiveID → Guid

Source

  • DQ → Dynamic Query (BAQ BPM)
  • BO → Business Object BPM
  • DB → Data Directive

DirectiveType

  • Starts with Erp.[TableName] or Ice.[TableName]

    • 0 In-Transaction Data Directive
    • 1 Standard Data Directive
  • Starts with Ice.[Company]/[BAQName].[DirectiveName] or Ice._/z[EpicorBAQs].[DirectiveName] or Erp.BO.[Method][DirectiveName] or Erp.Rpt.[Method][DirectiveName] etc, etc…

    • 1 BO Pre-Processing Method Directive
    • 2 BO Base-Processing Method Directive
    • 3 BO Post-Processing Method Directive

Name → Friendly name someone gave it

Order → Order they execute in

IsEnabled → Duh

ReenterMax → I assume the max amount of recursion before it blows up ?

PreventDeadloops → Not quite sure, some safety measure we could or can use?

Company → Duh

Visibility Scope → ???hmm, who can see?

Directive Group → Friendly group name, can use with import / export

CGCCode → No clue

Body → The code and stuff, in XML

Thumbnail → The PNG that shows the diagram

Description → Duh

IsProtected → Hmmmm ???

3 Likes

Thanks Kevin, yes that covers it very well.

The only thing I’m stuck on now is identifying differences for directives that exist in both environments. I’m comparing Body, but just about all the directives seem different in their Body in the manner below relating to startNode at least, I assume something to do with bpms being created in one environment and solutionized in the other.


(Not able to follow this)

Doesn’t seem to be an easy way to look for actul C# code differences which is what I’d really like.

Hmm looking again I think if I filter out certain lines eg any lines containing StartNode or
<x:Reference>__ReferenceID0</x:Reference> or </DirectiveDefinition2.AdditionalReferences> then compare I might get some joy.

Easy? No.

Doable? Yes.

You’ll need to pre-process and read the xml.

Here is a sample of some custom code from it.

<DirectiveStep.Action>
	<CustomCodeAction PrimaryTable="{x:Null}" Code="&#xD;&#xA;  &#xD;&#xA;  //Check if we are adding any new Order Lines&#xD;&#xA;  if(ds.OrderDtl.Any(x =&gt; x.RowMod == &quot;A&quot;))&#xD;&#xA;  {&#xD;&#xA;      //Set a flag for post&#xD;&#xA;      callContextBpmData.Checkbox01 = true;&#xD;&#xA;      &#xD;&#xA;      //Get existing Order Lines&#xD;&#xA;      var exisitingOrderLines = Db.OrderDtl.Where(x =&gt; x.OrderNum == iOrderNum &amp;&amp; x.OpenLine == true &amp;&amp; x.VoidLine == false).Select(x =&gt; x.OrderLine.ToString()).ToList();&#xD;&#xA;      &#xD;&#xA;      //Get existing Order Releases&#xD;&#xA;      var exisitingOrderReleases = Db.OrderRel.Where(x =&gt; x.OrderNum == iOrderNum &amp;&amp; x.OpenRelease == true &amp;&amp; x.VoidRelease == false).Select(x =&gt; new&#xD;&#xA;      {&#xD;&#xA;        x.OrderNum,&#xD;&#xA;        x.OrderLine,&#xD;&#xA;        x.OrderRelNum&#xD;&#xA;      }).ToList();&#xD;&#xA;      &#xD;&#xA;  &#xD;&#xA;      //Pass down a json list of existing order lines&#xD;&#xA;      callContextBpmData.Character01 = JsonConvert.SerializeObject(exisitingOrderLines);&#xD;&#xA;  &#xD;&#xA;      //Pass down a json list of existing order releases&#xD;&#xA;      callContextBpmData.Character02 = JsonConvert.SerializeObject(exisitingOrderReleases);&#xD;&#xA;  &#xD;&#xA;  }&#xD;&#xA;  &#xD;&#xA;  " ExecutionRule="OnceForAllMatching" Id="0" IsAsync="False" RecordMode="Nothing" TerminateOnError="False" ValidationState="Valid" />
</DirectiveStep.Action>

Which if I get bored enough, I could probably whip something up.

I’d need some more justification that this is actually useful though.

Convince me.

Btw, I’m hoping to get back to it this week, but that was pulled by one of my new tools… which I am freaking loving.

I just added this to a function and ran it…

  string bpdn = "bpdirectivesample";
  
  Konsole.DumpAsFileTextWithName(Db.BpDirective.Where(x => x.Name == bpdn).FirstOrDefault().Body, bpdn + ".xml", false);
1 Like

I worked on a similar project to compare items between our environments during our upgrade from E10 to Kinetic. I’m on prem, so I have access to the SQL servers. Not sure if your situation allows you that access. Anyway, here’s the SQL that I used to extract the C# code from our BPMs. It’s been months since I’ve looked at this, and I’m getting older every day, so I dont recall how I came up with it. I’m a trial and error kind of guy, so…

WITH XMLNAMESPACES('clr-namespace:Ice.Lib.Bpm.Model.Actions;assembly=Ice.Lib.Bpm.Shared' AS ilbma,
DEFAULT 'clr-namespace:Ice.Lib.Bpm.Model;assembly=Ice.Lib.Bpm.Shared')            
SELECT 'BPM' AS [Category],            
				 BD.[Name] AS [ItemName],             
				 BD.[BpMethodCode] AS [ItemDescription],      
				 BD.[Source] as [ItemType],             
				 CAST(BD.Body AS nvarchar(MAX))	AS [Body],
				 BD.SysRevID as [SysRevID],      
				 BD2.C.value('(./@Code)[1]','nvarchar(max)') AS [ItemContent],      
				 CONCAT('{', BD.SysRowID, '}') as [SysRowID],             
				 BD.[DirectiveID] as [DirectiveID],             
				 BD.[Company] as [Company],             
				 BD.[Order] AS [Order],             
				 BD.[Description] as [Description],             
				 BD.[DirectiveType] as [DirectiveType],                  
				 BD.IsEnabled as [IsEnabled],             
				 BD.IsUpToDate as [IsUpToDate],             
				 RANK() OVER (PARTITION BY BD.[DirectiveID] ORDER BY BD2.C.value('(./@Code)[1]','nvarchar(max)')) AS [CodeSequence],      
				 0 As [Examined],      
				 0 AS [Newest],      
				 0 AS [Inspect]              
				 FROM Ice.BpDirective BD            
				 CROSS APPLY BD.Body.nodes('//ilbma:CustomCodeAction') AS BD2(C)            
				 where BD.Source <> 'DQ' AND BD.IsEnabled = 1

Don’t know if that will help or not, but it worked for me. The ItemContent field contains the C# code. I guess I should also clarify that I was NOT pulling the information into any Epicor related. This was part of a WPF program that I did in Visual Studio, (the first time I had ever done such a thing, so it’s pretty ugly.

2 Likes

Wow, that’s SQL Jim, but not as I know it…
I ran it though, and yep out pops the C# code :+1:
I should be able to compare the two envirnoment’s code snippets with this.

1 Like

Ha! Hope it helps! If it’s any consolation, I didn’t know it either, had to do a lot of head banging to figure it out!

Thanks again Kevin, read your post on Klient/Konsole plugin framework and it sounds exciting, but a little over my head at this point in my learning cycle. (I was hoping to hop off my learning cycle and park the damn bike months ago, but apparently that’s not to be …)

1 Like

@mccoyjf If you could wrap your code in triple grave accents beginning and end and paste it back into your post then the single quotes won’t get converted to fancy quotes that have to be fixed before the sql can be run. It will help future copy pasters with your awesome code.

like so.

WITH XMLNAMESPACES('clr-namespace:Ice.Lib.Bpm.Model.Actions;assembly=Ice.Lib.Bpm.Shared' AS ilbma,
DEFAULT 'clr-namespace:Ice.Lib.Bpm.Model;assembly=Ice.Lib.Bpm.Shared')
SELECT 'BPM' AS [Category],
BD.[Name] AS [ItemName],
BD.[BpMethodCode] AS [ItemDescription],
BD.[Source] as [ItemType],
CAST(BD.Body AS nvarchar(MAX)) AS [Body],
BD.SysRevID as [SysRevID],
BD2.C.value('(./@Code)[1]','nvarchar(max)') AS [ItemContent],
CONCAT('{', BD.SysRowID, '}') as [SysRowID],
BD.[DirectiveID] as [DirectiveID],
BD.[Company] as [Company],
BD.[Order] AS [Order],
BD.[Description] as [Description],
BD.[DirectiveType] as [DirectiveType],
BD.IsEnabled as [IsEnabled],
BD.IsUpToDate as [IsUpToDate],
RANK() OVER (PARTITION BY BD.[DirectiveID] ORDER BY BD2.C.value('(./@Code)[1]','nvarchar(max)')) AS [CodeSequence],
0 As [Examined],
0 AS [Newest],
0 AS [Inspect]
FROM Ice.BpDirective BD
CROSS APPLY BD.Body.nodes('//ilbma:CustomCodeAction') AS BD2(C)
where BD.Source <> 'DQ' AND BD.IsEnabled = 1

4 Likes

Thanks Greg! Good to know. I haven’t posted much code here. I’ll know what to do the next time!

1 Like

63 BPMs identified where the code differs in our test and development environment to our pre-go-live basedata environment. A win for epiusers, a loss for our Epicor go-live program…

eg

No worries, I haven’t finished the damn thing anyway.

Hopefully I will soon and can explain it properly.

Doing an upgrade?

Have a development moratorium.

Copy Live to Test Upgrade. Test the upgrade.

Any changes. Make them and export them.

Rinse and repeat for you cut over…

That’s the ideal…

Doing development?
Live - Development Environment
Do the work, Close it out.
Rinse and repeat.

Multiple developers?.. Starts getting more complicated but if the development is not overlapping then they could be build/Tested and deployed in isolation (i.e. separate environment per dev)… Sorry SaaSies.

There is a lot to be said for being able to have a Docker image for development, that has a copy of your live (with obfuscated data) that could be downloaded from the control panel.

3 Likes

Absolutely. A development moritorium would absolutely have been the thing to do. However, in my situation, I don’t get to control those sorts of decisions, so building a utility to monitor what was getting changed in the production environment and compare it to other environments was the only way that I could figure to have any chance of a successful upgrade. It worked well and we’re on Kinetic now.

4 Likes

I feel your pain.

1 Like

There is a school of thought that source code be stored in a repository by version to do compares and have commit IDs embedded in the environment to know which version is deployed.

Look Over Here Steve Carell GIF

Epicor Ideas 139

4 Likes

It’s had my vote for a while!

1 Like

Since there’s been no feedback from Epicor on Epicor Ideas, I’m just highlighting use cases.

:wink:

2 Likes