Prevent BPM from triggering Update Directives

I am creating a method directive on Update to add a Miscellaneous Charge at Header when the Ship Via is set to a certain code, for both New Quote and if the Ship Via is updated, removed when not that ship via code.

I thought there was a way to prevent the directives of a BPM to not fire if that BO was called inside another BPM. What is happening is I am adding the misc. charge and the final step is to Update and the update BPMs fire again and I hit an error.

Is there a way to isolate this.

Thanks,
Scott

Let’s see your code.

{
  Erp.Tables.QuoteMsc OrderMsc;
  var quoteLine = 0;
  var qtyNum = 0;
  var miscCode = "3PHF";
  var miscDesc = "3rd Party Handling Fee-Taxable";
  var miscFreq = "L";
  var amt = 100M;
  
  var QuoteTs = new QuoteTableset();
  
  // Check if the exists already
  var codeExists = Db.QuoteMsc.Any(qm => qm.Company == Session.CompanyID && qm.QuoteNum == quoteNum && qm.MiscCode == miscCode); 
    
  if (!codeExists)
  {
      quoteSvc.GetNewQuoteHedMsc(ref QuoteTs, quoteNum, quoteLine, qtyNum); 
      var newRow = QuoteTs.QuoteHedMsc.FirstOrDefault();  
      newRow.MiscCode = miscCode;
      newRow.Description = miscDesc;
      newRow.FreqCode = miscFreq;
      quoteSvc.GetMiscChrgDefaults(ref QuoteTs, "QuoteHedMsc");
      newRow = QuoteTs.QuoteHedMsc.FirstOrDefault();  
      newRow.MiscAmt = amt;
      newRow.DocMiscAmt  = amt;
  }
  
  quoteSvc.Update(ref QuoteTs);
  this.dsHolder.Attach(QuoteTs);
}

Pretty much the same thing you can find in other posts on this forum. This is being run inside Quote.Update BO directive

Thanks, Scott

Don’t try to create it and call update.

Create it, and add it to the existing dataset and let the bpm handle it.

Kevin,

Thanks. I have done that a thousands times. Here is my quandary, and maybe I am over-thinking it. I need to add this Misc. Charge even on a new Quote Header and I did not believe it was possible because the actual quote does not exist until after Pre and Base processing.

Are you stating it is possible to add this to the Quote TableSet when the record is in a “A” RowMod state when no Quote Header exists just yet.

Scott

I’m not positive but probably. Depends on the bpm.
I haven’t tried exactly what you are doing.

Is this being run in pre-processing or post-processing?

You can pass bypass facade flag to the BO instantiation at BPM.

I was trying Post because by then I have a Quote.

I thought there was a way but I could not remember how. What’s is that then.

Thanks

You must pass true into the Service constructor that will bypass Facade.

Awesome, I will give that a shot

Thanks Jose

For future reference

// The true parameter in this constructure instructs Epicor to bypass the facade (no BPMs and or related events are triggered)
using(Erp.Contracts.QuoteSvcContract hQuoteSvcContract = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.QuoteSvcContract>(Db, true))
{
 // any calls to hQuoteSvcContract  here bypass the facade
}

Just put this in post, no need to bypass the facade:

foreach(QuoteHedRow qhRow in ds.QuoteHed)
{
    int quoteNum = qhRow.QuoteNum;
    int quoteLine = 0;
    int qtyNum = 0;
    
    string miscCode = "DIS"; //NOTE MINE IS DIFFERENT
    string miscDesc = "Disposal Fee";
    string miscFreq = "L";
    decimal amt = 100M;
    
    bool codeExists = ds.QuoteHedMsc.Any(x => x.QuoteNum == quoteNum && x.QuoteLine == quoteLine && x.MiscCode == miscCode);
    
    if(!codeExists)
    {
        CallService<Erp.Contracts.QuoteSvcContract>(quoteBO =>
        {
            QuoteTableset quoteTS = quoteBO.GetByID(quoteNum);
            
            quoteBO.GetNewQuoteHedMsc(ref quoteTS, quoteNum, quoteLine, qtyNum);
            
            //Edit: Removed this line
            //QuoteHedMscRow newQuoteHedMscRow = quoteTS.QuoteHedMsc.FirstOrDefault();
            
            //Replaced with this one, so we know we get the new one
            QuoteHedMscRow newQuoteHedMscRow = quoteTS.QuoteHedMsc.Where(x => x.SysRowID == Guid.Empty).FirstOrDefault();

            newQuoteHedMscRow.MiscCode = miscCode;
            newQuoteHedMscRow.Description = miscDesc;

            quoteBO.GetMiscChrgDefaults(ref quoteTS, "QuoteHedMsc");
            
            newQuoteHedMscRow.FreqCode = miscFreq;                
            
            newQuoteHedMscRow.MiscAmt = amt;
            newQuoteHedMscRow.DocMiscAmt  = amt;                

            newQuoteHedMscRow.RowMod = "A";
            
            quoteBO.Update(ref quoteTS);
            dsHolder.Attach(quoteTS);
        });
    }
}

Edit:

//Edit: Removed this line
//QuoteHedMscRow newQuoteHedMscRow = quoteTS.QuoteHedMsc.FirstOrDefault();
            
//Replaced with this one, so we know we get the new one
QuoteHedMscRow newQuoteHedMscRow = quoteTS.QuoteHedMsc.Where(x => x.SysRowID == Guid.Empty).FirstOrDefault();

@josecgomez , this is the first I’m learning of bypass facade option and it seems very promising. For years I have been littering my bpms with call contextBpmData flags to prevent looping when calling an Update from a Post proc. It’s becoming too much to keep track of all of them.

In researching, I did see Sergey seem to badmouth using this in this thread with @hkeric.wci though… What do you make of that? Is this “safe”?

All it does is call the base method and bypass BPMs.

Bypassing facade is perfectly safe just you can’t pick or choose it will bypass ALL bpms involved in the call

Thanks, I figured it was. Just seemed odd that Sergey took issue with it.

I think he was rightly pointing out that it is generally a bad idea. You are bypassing all method / data directives for a given call.

Is this something you’d want to do always? No…

Is this something you could do selectively on occasion, Yes as long as you understand the repercussions

Understood. Although, I’d say it’s probably 50/50 for me whether I want BPMs to fire or not when I call a BO in code.

Anyway, so this works great in BPMs when using ServiceRenderer.GetService…

But in Functions it seems we need to use CallService and we end up with code looking like this:

image

I am still a little fuzzy on why that is the case, but anyways, how would we bypass facade when calling BOs from Functions?

I believe you will have to make your own context.

Oh wait, it bypasses data directives too? Hmm, I still want my change logs…