Can't "find" second dataset in a BPM

I’m trying to implement a BPM in Kinetic which involves custom code and two ds files. Based on the trace I did in classic mode the dataset has both ds files available but when my code tries to access the second one I get a null result. I’ve incorporated numerous displays to try to figure it out and I have proven out the first ds call is successful.

Basically what I’m trying to do is push up the cost figures which have been entered on a quote BOM/MOM (Worksheet qty breakout) to the corresponding detail line so I can compute a VM% based on the line price entered. The trigger for the BPM is the Erp.BO.Quote.GetWSUnitPrice method associated with the change to the Quoted Unit Price field on the quote > line > worksheet page. I first went down the rabbit hole of adding a rule to the app studio version of QuoteEntry but that proved not to be the suggested route (unless someone can prove out otherwise). I feel like I’m sooooooo close but … Any assistance in pointing out my “operator error” would be appreciated.

Here’s the code:

var ttQuoteQty = (from qq in ds.QuoteQty where 
                       qq.Company == Session.CompanyID &&
                       qq.RowMod == "U" select qq).FirstOrDefault();

if (ttQuoteQty != null)
{
  this.PublishInfoMessage(ttQuoteQty.QuoteNum.ToString(), /* returns quote num */Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "FirstVar","SecondVar");
  
  var ttQuoteDtl = (from row in ds.QuoteDtl 
                    where row.Company == Session.CompanyID &&
                          row.QuoteNum == ttQuoteQty.QuoteNum &&
                          row.QuoteLine == ttQuoteQty.QuoteLine select row).FirstOrDefault();
  if (ttQuoteDtl != null)
  {
  //  ttQuoteDtl["VMPct_c"] = CalcedVMPct;
    ttQuoteDtl["VMCosts_c"] = CalcedUnitCost; /* Function output var */
    ttQuoteDtl["RowMod"] = "U";
  } 
   else
   {   this.PublishInfoMessage("Can't find QuoteDtl",   /* This is shown */Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "FirstVar","SecondVar");
   }
}

I’m on Kinetic 2025.1.11, but when I just did a trace of that method I only see ds.QuoteQty being populated, ds.QuoteDtl is empty.

Brendan

Well, that would explain why I get a null result. Not sure how your trace would be different than mine. I initiated a trace with the dataset dump checked and reviewed the results in the log file. We’re on 2024.2. I was tracing the classic version but since it’s a BPM I would think it works the same for both versions???

My guess is that if I tried to build a BPM at the QuoteDtl level to pull up the value I’d have access to the ds for QuoteDtl but the QuoteQty would be null. I’ll have to see when the update takes place and whether I’ll have access to the db vs the ds and get to the data I need.

In cases of read only database access, or writing only to User Defined fields, you could be fine accessing QuoteDtl directly via the database/linq in the bpm:

Replace this:

var ttQuoteDtl = (from row in ds.QuoteDtl where row.Company == Session.CompanyID && row.QuoteNum == ttQuoteQty.QuoteNum && row.QuoteLine == ttQuoteQty.QuoteLine select row).FirstOrDefault();

With this:

var ttQuoteDtl = Db.QuoteDtl.FirstOrDefault(x => x.Company == Session.CompanyID && x.QuoteNum == ttQuoteQty.QuoteNum && x.QuoteLine == ttQuoteQty.QuoteLine);

Then, the lines where you set stuff in QuoteDtl change:
old:

// ttQuoteDtl[“VMPct_c”] = CalcedVMPct;
ttQuoteDtl[“VMCosts_c”] = CalcedUnitCost; /* Function output var /
ttQuoteDtl[“RowMod”] = “U”;

new:

// ttQuoteDtl.SetUDField<System.Decimal>("VMPct_c", CalcedVMPct);
ttQuoteDtl.SetUDField<System.Decimal>("VMCosts_c", CalcedUnitCost);
Db.SaveChanges();

ds = DataSet in Memory, This typically represents a TableSet object in Epicor and every table in it could potentially be completely empty in some cases (rare) or ds may contain some empty tables and one or more populated ones (very common) - this may differ from what’s saved (committed) to the DataBase, as the user or a business object might have loaded it and made a change in memory before hitting save.

Db = DataBase object, this only contains stuff already written out to the database (committed) - if the data you need to work with from QuoteDtl has been changed vs. what’s saved to the database, Db object won’t reflect that changed stuff (only after save)

Also, I think the comments need cleaning up, you are starting and stopping comments right in the middle of lines of execution, sometimes this works out great and sometimes the compiler barfs on it.

Right now it looks like you’ve inadvertently commented out this code between and because the /* isn’t matched with */:

ttQuoteDtl[“VMCosts_c”] = CalcedUnitCost; <COMMENT START>/* Function output var /
ttQuoteDtl[“RowMod”] = “U”;
}
else
{ this.PublishInfoMessage(“Can’t find QuoteDtl”, / This is shown */<COMMENT STOP>Ice.Common.BusinessObjectMessageType.Information,

Well, I finally figured out how to skin this cat. For anyone who might have an interest and/or encounter a similar situation in the future I’ll tell you what I did. I structured two BPM’s - one against the dsQuoteQty dataset and another against dsQuoteDtl. To get the unit cost figure across the boundary I utilize the BPM field of “Number01” which stays populated while the QuoteEntry functionality is open. I SetValue in while the dsQuoteQty is populated and I’m working in the Quote Worksheet. When I’m loading the pricing information when updating the price in QuoteDtl I then have a pre-processing BPM to access the Number01 field in the BPM and go on my merry way. So far it works like a dream.

The UI only fills out the datasets for things that are changing. So if you are changing things in one dataset, and trying to adjust something in a dataset that isn’t being changed by the UI (for example, if in a job, if you are changing something in MTL and need to adjust something in operations based on that change), it won’t be populated.

1 Like