Tracing a part on hold message in Sales Order Entry

I’m trying to figure out where this part on hold pop-up message is generated (so I can get the same behavior in quote entry), but full trace logs don’t show anything… I click on ‘save’, MasterUpdate fires, but there don’t seem to be BPM’s that would generate this message. (note, the exception pops up when Part.OnHold=true).

The pop up’s exception shows that it’s on the SalesOrder.OrderDtlBeforeUpdate method, but I didn’t see this on any of our BPM’s (and couldn’t find this method anyways!)

Ideas?

image

Business Layer Exception

The part on this order line is on hold. Please reenter.

Exception caught in: Epicor.ServiceModel

Error Detail

Correlation ID: 28e07f2c-98bd-4482-b507-d2abd4e8e5a3
Description: The part on this order line is on hold. Please reenter.
Program: Erp.Services.BO.SalesOrder.dll
Method: OrderDtlBeforeUpdate
Line Number: 21600
Column Number: 17
Table: OrdertDtl
Field: PartNum

Client Stack Trace

at Epicor.ServiceModel.Channels.ImplBase`1.ShouldRethrowNonRetryableException(Exception ex, DataSet[] dataSets)
at Erp.Proxy.BO.SalesOrderImpl.MasterUpdate(Boolean lCheckForOrderChangedMsg, Boolean lcheckForResponse, String cTableName, Int32 iCustNum, Int32 iOrderNum, Boolean lweLicensed, Boolean& lContinue, String& cResponseMsg, String& cCreditShipAction, String& cDisplayMsg, String& cCompliantMsg, String& cResponseMsgOrdRel, String& cAgingMessage, SalesOrderDataSet ds)
at Erp.Adapters.SalesOrderAdapter.MasterUpdate(Boolean lCheckForOrderChangedMsg, Boolean lcheckForResponse, String cTableName, Int32 iCustNum, Int32 iOrderNum, Boolean lweLicensed, Boolean& lContinue, String& cResponseMsg, String& cCreditShipAction, String& cDisplayMsg, String& cCompliantMsg, String& cResponseMsgOrdRel, String& cAgingMessage)
at Erp.UI.App.SalesOrderEntry.Transaction.Update()

image

The OrderDtlBeforeUpdate, internal method, kind of a Validation Method, you won’t have a BPM for that… Basically you would just do the same in the PRE Method Directive.

The Logic is

if (Part != null && Part.OnHold && Part.OnHoldDate <= CompanyTime.Today())
	ExceptionManager.AddBLException(Strings.PartOnHold, "OrdertDtl", "PartNum");

@hkeric.wci - Thanks! That makes sense.

I tried adding that C# into Quote.Update / Pre, and get several errors. I’ll see if I can find the header info to put before the statement to declare everything (I think that’s what I need to do). Time for me to learn some C# baby steps :slight_smile:

You can do it few ways.

You can throw in a Condition Block and execute “Custom Code”.
You can create a variable isPartOnHold and use the Set Variable widget and then a condition.

For example the Variable Expression would be

Db.Part.Any(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum && x.PartOnHold == true)

or you could also just get the PartOnHold flag and store it

Db.Part.Where(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum).Select(x => x.PartOnHold).DefaultIfEmpty(false).First()

or if you do a Condition Block and use the Custom Code is Valid option then it will be something along the lines

var ttQuoteRow = ttQuoteDtl.Where(w => !w.Unchanged()).FirstOrDefault();
return Db.Part.Any(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum && x.PartOnHold == true);

Then you can use the Throw Exception Widget.


The Last option is you do it all in Custom Code period and it will be something along the lines of

var ttQuoteRow = ttQuoteDtl.Where(w => !w.Unchanged()).FirstOrDefault();
bool isPartOnHold = Db.Part.Any(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum && x.PartOnHold == true);

if (isPartOnHold) {
  throw new Ice.BLException("Part is On Hold");
}

PS im just assuming its ttQuoteRow or ttQuoteDtl - the Expression Editor will tell you what exactly it is.

As for CompanyTime it might be Ice.Lib.CompanyTime.Today()

1 Like

Hi Haso - You nailed it! I was able to get this to work using the your method, and even added checking the date (with a few struggles). Here’s the final result (if it helps anyone else).

image

2 Variables:
image

Execute Custom Code block:

var ttQuoteRow = ttQuoteDtl.Where(w => !w.Unchanged()).FirstOrDefault();

DateTime today = DateTime.Today;

bool isPartOnHold = Db.Part.Any(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum && x.OnHold == true && x.OnHoldDate <= today);

if (isPartOnHold) {
  throw new Ice.BLException("Part is on-hold and hold date has expired. Please delete line. Contact purchasing if this needs to be corrected.");
}

I found @Aaron_Moreng & @josecgomez thread helpful for the ‘today’ syntax : BPM variable scope - #2 by josecgomez

Thanks again.

Whoops - Too fast. I had to add a conditional block before the C# code widget to let me delete a row. :slight_smile:

Then I also got an inner exception when creating a new quote - Non-static method requires a target. I need to check for nulls for the quote row, but not sure how, so I added another row to the condition to check for added rows in QuoteHed. Working so far in PILOT…

image

image

var ttQuoteRow = ttQuoteDtl.Where(w => w.Added() || w.Updated()).FirstOrDefault();

if (ttQuoteRow != null) {
	DateTime today = DateTime.Today;

	bool isPartOnHold = Db.Part.Any(x => x.Company == ttQuoteRow.Company && x.PartNum == ttQuoteRow.PartNum && x.OnHold == true && x.OnHoldDate <= today);

	if (isPartOnHold) {
	  throw new Ice.BLException("Part is on-hold and hold date has expired. Please delete line. Contact purchasing if this needs to be corrected.");
	}
}

@hkeric.wci - Thank you! I get it now :slight_smile: It’s neat to see in C# code the same thing that a conditional widget block does.

I’ve signed up for Epicor’s Intro to C# Programming for Customizations course next week. Looking forward to it.