I’ve written a C# function to go through quotes with configured lines and force the Engineered checkbox (ReadyToQuote field) to be unchecked, ultimately forcing the configurator to be rerun under specific circumstances.
The code I’ve written works, but as an IT department, we’ve come to terms with trying to develop BPMs and functions through widgets for a couple of reasons. One is to make future updates easier on us, another is to ensure we’re using business objects when possible so that our logic, though it works, isn’t going to break anything. Is it possible to write this code as a widget? I’ve tried a handful of routes but have had no success.
var context = (Erp.ErpContext)Ice.Services.ContextFactory.CreateContext();
var erpContext = new Erp.Internal.Lib.CCredChk(context);
using(var txScope = Ice.IceContext.CreateDefaultTransactionScope())
{
var today = BpmFunc.Today();
//look for part numbers where the related configurator has been updated today
var configResynched = (from r in erpContext.Db.PartRev
join pc in erpContext.Db.PcStatus on new { r.Company, r.ConfigID } equals new { pc.Company, pc.ConfigID }
where r.ConfigID != "" && r.Approved == true && pc.ApprovedDate == today
select r.PartNum);
//pull back a list of quotes that have been configured, have not been ordered, and have been marked as engineered
var result = (from d in erpContext.Db.QuoteDtl
join h in erpContext.Db.QuoteHed on new { d.Company, d.QuoteNum } equals new { h.Company, h.QuoteNum }
where d.LastConfigDate != null && h.Ordered == false && d.ReadyToQuote == true
select new { Detail = d, Header = h });
//set the engineering and quoted checkboxes to false when there is
// - a quote with part number where the configurator has been updated today OR
// - a quote that hasn't been reconfigured within the last 30 days
foreach (var item in result)
{
var d = item.Detail;
var h = item.Header;
if (configResynched.Contains(d.PartNum) || today > h.ExpirationDate) //|| today > BpmFunc.AddInterval(item.LastConfigDate, 30, IntervalUnit.Days) )
{
d.QuoteComment = "TEST";
h.Quoted = false;
d.Quoted = false;
d.ReadyToQuote = false;
}
}
erpContext.Db.Validate();
txScope.Complete();
OK I better type fast before everyone says “No, widgets are the worst thing ever!”
So, I have not read through this thoroughly, but can you accomplish this with an updatable BAQ? You could call it from a Function if needed.
[Added a little more below…]
I had a similar issue with trying to set the make-direct Boolean on order releases (based on XYZ criteria), on a schedule each night.
We had a looping EFx (all widgets) that worked… till it didn’t. After failing to fix it several times, I started over with an updatable BAQ and that’s been fine for months now.
Edit: For anybody lacking a sense of humor, this was a joke. Widgets are not the worst thing ever. They have their place. But not in looping, you can’t loop with widgets.
Will try not to write a novel on this one but my immediate reaction is you should be using Business Objects to update whether you do it in code or widgets. The biggest argument against code is that it’s far easier to shoot yourself in the foot like with the code above that does direct db update. I disagree that it’s inherently less upgradable, but yes, horrendous code is going to be less upgradable than a horrendous widget…
This sounds like a pretty similar scenario to the UBAQ example in the BPM Cookbook in the kinetic help files. Setup a UBAQ to identify the records you want and make the checkboxes editable, then use Advanced BPM Update to make the changes.
Looping with widgets sounds like an absolute nightmare, but I’m a programmer sooo.
It’s not what you’re asking, but if you wish to stick with code and ensure you’re using the BOs, then you can do so with syntax such as:
Function version:
this.CallService<Erp.Contracts.QuoteSvcContract>( quoteBO =>
{
// Get the record
QuoteTableset quoteTS = quoteBO.GetByID("QuoteNumber");
// Manipulate the record (adjust to fit your needs for specific record)
quoteTS.First().QuoteComment = "You can't make me use widgets!";
quoteTS.First().RowMod = "U"; // To signal the Update BO that there were actually changes
// Call the update
quoteBO.Update(ref quoteTS);
});
BPM Version:
using( var quoteBO = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.QuoteSvcContract>(Db) )
{
}
You may still find that updates break this, BUT usually what breaks is the signature of the BO call, so a widget is going to need attention too.
Properly written code, using the business objects, with anything that would require more than a few widgets, is inherently easier to maintain.
They hype about using widgets breaking less is just that, hype.
It stems from poorly written code. Understandable.
If you write crap code, and don’t follow best practices, you’ll probably make a poor widgeteer as well.
Even I will admit they are not.
Long story short, if you have people who can code, and use best practices, use code. Document for the person after you.
And if you use widgets, for the love of all that is holy, label the damn things, and document for the person after you. Maybe take a picture too for when epicor decides to randomly reorder them on the screen for you, because reasons.
For functions, go for code only. Easier to troubleshoot without having to un-promote it. If you do not know how to do something, dump the code of a widget.
Good that you point that out Jason, I hesitated to give a reason, but now that you mention it. I always end up adding those same conditions within the custom code, otherwise it would seem incomplete, but to have a separate widget with conditions is helpful for other people trying to figure it out what criteria the custom codes needs without looking at the code (which can be quite intimidating sometimes).