BPM More Than One Record Error - Help Needed

,

@knash,

image

Now what do the Widgets’ contents look like?

2 Likes

@ckrusen,

SetArgument 1 = key 1
Db.TranGLC.Where( r =>r.Company == callContextClient.CurrentCompany && r.Key1 == ttPODetailRow.PONUM.ToString() && r.Key2 == ttPODetailRow.POLine.ToString()).Select( r =>r.SegValue1).DefaultIfEmpty("").FirstOrDefault()

Condition 0 what does key 1 =?

if key1 = “1590” then true else false

set argument2 = key 2
Db.TranGLC.Where( r =>r.Company == callContextClient.CurrentCompany && r.Key1 == ttPODetailRow.PONUM.ToString() && r.Key2 == ttPODetailRow.POLine.ToString()).Select( r =>r.SegValue4).DefaultIfEmpty("").FirstOrDefault()

condition 1 what does key 2 =?

if key2 = “” true else false

raise exception text

WRONG!
Scalar Value 1 = key1
Scalar Value 2 = key2

I would put a condition at the start that the ttPODetailRow.RowMod = “A” or ttPODetailRow.RowMod=“U”. It should only fire then on the update or addition of a record.

1 Like

@Doug,

Just gave that a try and noticed that that the ttPODetailRow.RowMod is coming in blank. Is this because I am in on a post-process? I have noticed that the error I put at the end of this is not stopping the update process at all. Also, running as a base process gives an error saying there are no entries in the temp table. Thoughts?

Oh, yeah. If you’re wanting to block the save, you want this as a pre-processing BPM. RowMod being blank means that there are no changes which would be the case in the post-processing method.

1 Like

Tangentially related, you’ll also see blank RowMods in pre. When you update a row, the temp table will contain two rows: the old data with a blank RowMod and the new data with RowMod “U”. The order that the rows appear in is undefined. Don’t make the mistake of taking First, Single, or FirstOrDefault without a condition. Always use constructs like FirstOrDefault(o => o.RowMod == "U").

1 Like

@krum

This is the code im using to pull the item i need which is the segvalue1 in the TranGLC table.

Db.TranGLC.Where( r =>r.Company == callContextClient.CurrentCompany && r.Key1 == ttPODetailRow.PONUM.ToString() && r.Key2 == ttPODetailRow.POLine.ToString()).Select( r =>r.SegValue1).DefaultIfEmpty("").FirstOrDefault()

When I make the change to incorporate the condition statement that you mentioned, using the code below:

Db.TranGLC.Where( r =>r.Company == callContextClient.CurrentCompany && r.Key1 == ttPODetailRow.PONUM.ToString() && r.Key2 == ttPODetailRow.POLine.ToString()).Select( r =>r.SegValue1).FirstOrDefault(o => o.RowMod == “U”)

I get the following error:

image

Where should I position that condition statement to get around this error?

Additionally after a little troubleshooting using message boxes to capture output on several levels of the bpm it seems as though the rowmod = “;U” instead of just “U”. But when I try to set a condition statement for the “;U” to confirm it passes through false and doesn’t show.

The condition I suggested would apply to an enumerable of rows. The error comes from applying it to an enumerable of strings. In your example, you might use it before your Select. But what I described only applies to pre-processing BPMs. It’s just an important thing to know about RowMods in general. Sorry if I confused you.

1 Like

@krum

Still a little confusing. If I put that with the select statement do I put it at the end of the value like a .ToString() statement? If so do I apply it to all statements within the select statements? If so would it be smarter to simply add an && ttPoDetailRow.RowMod == “U” instead? Honestly, this is the first time I’ve seen this needed in a BPM and want to wrap my head around it as much as possible.

Additionally, is there a way to have a condition statement that checks if there are entries for a table and if not then not activate?

I would still do the above (add a condition block):

1 Like

For example, a lot of my pre BPMs start with something like var row = ttOrderDtl.SingleOrDefault(o => o.RowMod == "U"); or foreach(var row in ttOrderDtl.Where(o => o.RowMod == "U")) { ... }.

Pre: tt tables will contain two rows for every updated row, with RowMod blank for old values and “U” for new values. You can match them up by SysRowID if you need to. Naturally, rows with RowMod “A” (added) won’t have corresponding old value rows.

Post: tt tables will only contain the new values with RowMod blank.

1 Like

@Doug.C

I can agree with that. After trying to just do a condition using the condition statement, but it continues to only bring in only the blank value. I added in an assigned variable statement and then a condition statement behind it to get there.

@krum

That makes more sense. In my case we only want it to fire on updated lines not added lines. My next question has to do with the for loop. How and where do you put that into a BPM? It would be extremely useful for some of the BPMs I have created in the past!

UPDATE:
I am now fighting with the porel temp table. If the user adds a release to the po line the bpm triggers automatically and states that there is no data in the PODetail table. Is there a way to enforce this to only happen when updating the podetail table?

The addition of a PORel record may actually update the PODtl record. Typically, adding releases will add their qty’s to the PODtl’s qty. So your checks will need to determine what is being updated.

1 Like

@ckrusen

This is true and raises a new question. I see there is a trigger for ChangeApprovalSwitch. However, that dataset only seems to hold data for POHeader. Is there a way for me to link in all line information to the POHeader and check the GL account linked to each of those lines through tranglc?

@ckrusen, @krum, @Doug.C

Gentlemen, I’m taking this in a different direction as the BPM isn’t reacting the way I was hoping it would. I’ve been following these posts and trying to recreate the custom code section:

My aim now is to pull all PO lines associated with the PO Number whose GL Segments 1 and 4 do not match my criteria. I have borrowed code snippets from the before mentioned discussions and have one error remaining before I can test it:

Once this code runs I want to hard stop / error out the update process until the user goes in and adds the necessary changes to the PO Line.

Does this seem viable?

What am I missing with the TTPOHeaderRow table? It clearly exists but the code does not recognize it does.

Thanks for the assistance one and all.

I found my issue being that it needed to be TTPOHead instead.

Next question is how do I trigger an error statement with custom code? Do I do it within the custom code or add a statement to make it go to next module and add in an error module on the BPM?

This is the code to throw an exception.

throw new Ice.BLException("My awesome exception");

You could also set a variable in code, then use a condition widget and a widget for an exception. But this is easier if you are already in code.

1 Like

@Banderson,

You get the solution! I had just used the set variable option in the code and called the widget. That snip of code is going in my notes.

Just wondering is there a list of code snippets somewhere that I can bookmark and refer back to? There seems to be very little information on custom coding bpm’s, at least for the basics.

Thanks one and all for the assistance! Likes all around!

A note about throwing exceptions from custom code: throwing an exception in a pre-processing method directive prevents the method from running, and Ice.BLException is treated specially in that the error message is shown to the user and returned through the REST API. This is not true with exceptions thrown in post-processing. At least as far as the REST API is concerned, exceptions in post go into a black hole. In other words, post is not transactional with the default behavior. Throwing an exception in post does not roll anything back.

1 Like