There may be a better way to do what I’m trying to do, so if you have any suggestions, I’m all ears.
For my EDI mapping requirements, I need to produce a simple count of all ShipDtl Lines in a Pack (ShipHead) to send out the RDD, but I need it at the ShipHead level. Here’s what I’ve done to accomplish this:
*BPM - Data Directive on the ShipDtl Table - When a ShipDtl line is added or deleted, it fires this BPM. I use Custom C# code to count the number of ShipDtl lines (straight from the database) for this PackNum and update a UD field on the ShipHead with the returned count.
So, any time the number of ShipDtl lines change, the ShipHead gets the new value and all is well, UNTIL…
I want to delete an entire Pack (ShipHead) that has any number of Lines (ShipDtl) in it. If I try to delete the Pack without first deleting every single Line, it throws the “Object reference not set to an instance of an object” error message.
I’m sure it’s trying to update a Line that it’s trying to also delete, so I think there HAS to be a condition I can check, but I’m all out of ideas.
Anyone have any thoughts, I’d greatly appreciate it.
When the BPM Starts, I declare 3 Variables to be used in the C# code with the this. context:
*Company (This gets set to ttShipDtlRow.Company)
*PackNum (This gets set to ttShipDtlRow.PackNum)
*Lading_Qty (This is the count I’m returning, I’ll set it in the code below)
The first block of code follows as such:
var i = 0;
foreach(var outer_row in (from row in Db.ShipDtl where
row.Company == this.Company && row.PackNum == this.PackNum
select row))
{
i++;
}
this.Lading_Qty = i;
Now my Lading_Qty variable is set. I then check to see if the action taken on the ttShipDtlRow was a “delete.” If it was, I decrement the Lading_Qty by 1.
I then write back to the database with this block of code:
using (var txScope = IceContext.CreateDefaultTransactionScope())
{
foreach(var this_row in (from row in Db.ShipHead.With(LockHint.UpdLock) where
row.Company == this.Company && row.PackNum == this.PackNum
select row))
{
this_row.my_UD_field = this.Lading_Qty;
}
Db.Validate();
txScope.Complete();
}
Before I try to analyze this code (it takes a lot of brain cells on a Saturday night) - Have you tried any debugging steps like temporarily disabling the latter portion of code to see if the error still occurs or adding ICE message boxes to indicate the state of operation? If so that really helps to narrow it down.
Also, maybe by placing this code in a method directive on Update() would be a better case than a data directive.
Finally, as a random thought - I notice you are querying from the db. Could you not simply monitor the ttTable for incoming records and act upon those? This would require all of your existing records to have the correct count stored. Then for adds, you inc the value on ship head, and dec for deletes.
I tried using the Update method, and it doesn’t trigger predictably when I’m adding/removing lines. There’s a wide variety of varying TT’s that I’d have to write logic to pull the PackNum and Company from, and it failed spectacularly, so I deleted it and went back to the current method before making the original post.
I like the idea of using the ttTable to increment and decrement, but I think I’m going to land in the same logic error when I go to delete a Pack with any Lines. The command to delete is coming from the Head level to which ultimately tries to delete the Lines, which fires the BPM at the Line level, where it wants to update a field and causes the paradox. (I’ll certainly give it a shot, though, since it’s the tt, it may be more forgiving than a DB query)
If I could somehow let the Line level BPM search for a unique condition that is only passed when someone tries to delete the Line, but from a method at the Head
I guess, in the grand scheme of things, we rarely delete Packs to begin with, second of all, I built in logic to know if this Pack is for an EDI Customer and fire the BPM, otherwise, ignore it and you can delete Packs with Lines all day long–so it only happens on Packs for 4 of our 1000+ customers.
I may also go look into the ICE boxes and trying to narrow down which block of code is throwing the Object Reference error. I’m pretty new to BPMs and C#, so I’m still learning to use the basic tools.
If you have the logic that affects only the child (detail) it seems like you could get away with this. For example, only the detail updates the head. Similar to what I described above.
You can use the ttTable on the details to check for rowmod == “A” (add) and rowmod == “D” (delete) i think.
If this is in place, then when the head deletes, it will delete it’s children, causing your code to trigger.
You may receive multiple records at once in the ttTable so you can sum them up as needed and update the related head only once.
Leaving it the way you have it now, it seems like you would just avoid the summing and head update altogether on delete. So you should be able use the same rowmod == “D” condition to skip your code.
Better yet, you could add a BPM condition block to do the rowmod check and pass that to a custom code block for execution.
I’m a noob myself so I make no guarantees to the accuracy of my statements, but it’s worth a try.
This may outside the purview , but why not get the number of lines in the SSRS itself? You can simply add a calculated field to your SSRS and count the field you need? May be a simpler solution
That would be ideal in most reporting situations, but unfortunately, I’m not sending it through SSRS, it’s going outbound through a text-file-output Report Style to be passed to the eVision mapping software. I looked into adding a calculated field to the RDD, but apparently, there’s no practical way to do that.
Would a hybrid approach using baq and calculated Fields work? If you can get a baq to compile the data, you can format it with code any way you like to satisfy your mapping. From there, it’s easy to call a baq and parameters in code
In all of our outbound maps we do these counts inside the mapping software. On the record identifier (E01) for the HL loop do an expression the is simply LineCount = LineCount + 1. Then set your CTT/E01 to LineCount. The same goes for invoices with the IT1 Loop.
I tried doing it within Integrator (give me PHP all day, but I’m just barely good enough to be dangerous with Java), and the problem I seemed to run into is that I need the Line Count (Lading Quantity) as the Header is generated, and that seems to be written before the Lines are processed, so I’m not able to report the count to the Header section recursively.
I can populate the CTT at the end, after all the lines have been counted, with the proper count, but because I need this value at the Header, it just doesn’t exist when that part of the edi file is written.
(So I thought, well, let’s calculate it and send it through a UD Field – and it’s working great—except for the fact that I can’t delete an EDI Pack that has Lines in it without deleting each Line first.)
With Integrator there is a getHLoopTotalCount() function (Methlet Manager>LinkPlan>GetHLoopTotalCount) that will return the totals for an H Loop. Have you tried using that in the TD1/E02. I haven’t used it so I don’t know if it will do the calc before or after the header is generated, the notes say its generally used for the CTT but it may work for the lading qty.
Yeah, I tried that Methlet, I use it to populate the CTT at the bottom. The first time it’s called (up in the header) it returns a 0, the second time it’s called (down at the CTT) it returns the correct number. Good idea, though
If there was something obvious that was causing the object not set exception I’m sure someone would have pointed it out by now. We’ve had some odd behaviour like this in the past, usually it’s an odd hack that gets around it.
Have you considered moving away from the RDD? In E9 we were using it to generate our ASNs and 830s. As of E10 we’ve switched to doing all the data gathering in a BPM and then outputting it to a location monitored by eVision.
It was recommended to us by someone from the Epicor Demand team and it’s made things significantly easier. Capturing details like this at time of generation becomes trivial.
Oh wow, I hadn’t really thought of doing that… I’m still new enough to Epicor that I end up spending more time fighting to solve a symptom than the actual problem, but I’m starting to come around. I would LOVE to get away from RDD’s (and SSRS honestly). We’re now two months post-go-live and the dust is starting to settle enough to starting thinking of testing unorthodox methods. I was pretty much the entire implementation team, so I got a good crash course over the last year.
Thanks for the info, I’ll chew on it in the meantime!
I can sympathize with that. We’ve had to deal with more than a few issues that were cases of ‘how the heck did that break?!’
If you do have any questions on how we implemented it let me know.