Using Epicor Transaction Scopes In BPMs
Epicor ERP - Kinetic, v10, & v9
I was recently tasked by one of my customers to create a BPM that, upon shipping a transfer order, would automatically create a GL journal entry that moved the cost of their internal freight from the sending location to the destination. And while that may sound daunting, if you follow the steps here to understand how to use business objects within a BPM it ends up just being some code that looks like this:
// Custom Code /////////////////////////////////////////////////////////////////////////////// var jrnGrp = ServiceRenderer.GetService<Erp.Contracts.GLJrnGrpSvcContract>(Db); var jrn = ServiceRenderer.GetService<Erp.Contracts.GLJournalEntrySvcContract>(Db); Erp.Tablesets.GLJrnGrpTableset jrnGrpTs = new Erp.Tablesets.GLJrnGrpTableset(); Erp.Tablesets.GLJournalEntryTableset jrnTs = new Erp.Tablesets.GLJournalEntryTableset(); string groupId = "MYGROUP"; // Create the journal group jrnGrp.GetNewGLJrnGrp(ref jrnGrpTs); jrnGrpTs.GLJrnGrp[0].GroupID = groupId; jrnGrp.Update(ref jrnGrpTs); // Add a new journal to the group jrn.GetNewGlJrnHedTran(ref jrnTs, groupId); bool requiresUserInput; jrnTs.GLJrnHed[0].Description = "My Cool Journal Entry"; jrn.PreUpdate(ref jrnTs, out requiresUserInput); jrn.Update(ref jrnTs); int journalNum = jrnTs.GLJrnHed[0].JournalNum; // Add the credit (GL account and amount hard coded for example) jrn.GetNewGLJrnDtlMnl(ref jrnTs, "MAIN", DateTime.Now.Year, "", "GJ", journalNum, 0); bool currCodeChanged; jrnTs.GLJrnDtlMnl[0].GLAccount = "12345|AA|123|0"; jrn.ChangeGlAcct1(1, ref jrnTs, out currCodeChanged); jrnTs.GLJrnDtlMnl[0].TotCredit = (decimal)100.00; jrn.Update(ref jrnTs); // Add the debit jrn.GetNewGLJrnDtlMnl(ref jrnTs, "MAIN", DateTime.Now.Year, "", "GJ", journalNum, 0); jrnTs.GLJrnDtlMnl[1].GLAccount = "12345|BB|123|0"; jrn.ChangeGlAcct1(2, ref jrnTs, out currCodeChanged); jrnTs.GLJrnDtlMnl[1].TotDebit = (decimal)100.00; jrn.Update(ref jrnTs); // Post it string notAllPosted; jrnGrp.CheckBeforePost(groupId); jrnGrp.PostGroupJournals(groupId, out notAllPosted); jrnGrp.UnlockGroup(groupId); ///////////////////////////////////////////////////////////////////////////////
But when I had a variation of this code in place I got this error:
The underlying provider failed on EnlistTransaction.
Clear as mud, right? You can read up on this online and get even more confused if you like, but in short Epicor is telling us to wrap our call in a transaction. This is the same sort of idea you would find in the SQL world where you put your logic into a transaction block and if anything fails it rolls everything back. With something like a journal entry, it makes complete sense that you might have a lot of cascading logic occurring and this unclear error is actually making a very clear and sensible suggestion to you. Transactions are pretty easy within BPMs - all you’ve got to do is wrap your code inside of a using statement where you validate and complete the transaction within (I chopped down the code from above just so you can see contextually where the new lines go):
// Put this using line in where transactions really start to occur using (var txscope = IceDataContext.CreateDefaultTransactionScope()) { // Create the journal group jrnGrp.GetNewGLJrnGrp(ref jrnGrpTs); // Post it jrnGrp.UnlockGroup(groupId); // Use Db.Validate() to make sure the transaction is all good Db.Validate(); // Complete the transaction txscope.Complete(); }
And that’s it, my problem went away and I was able to see those nice journal entries automatically being written. Hope this helps!