Introduction
The 856 is one of those hierarchical documents that rely on the HL segment. The HL segment specifies the hierarchy level within the data itself, and then the detail segments used depend on the level specified.Because of its innate complexity, some folks call the 856 the "document from hell". Typical implementations have either three or five hierarchy levels. Sample structures might be Shipment-Order-Item, or Shipment-Order-Tare-Pack-Item.
There is a certain temptation to use extended rules to populate the HL segments, and I've often seen this done haphazardly. There is a perfectly straight-forward way to render the HL segments using standard rules with accumulators only, and it only requires some simple organization.
In this article, I show my preferred technique for building HL segments. This method can also be used for building an 811.
This method works perfectly well in both Gentran for Windows and GIS. The sample I give here is for an 856 with a Shipment-Order-Item structure for simplicity. It can be easily adapted to the SOTPI structure. I shall present another article for how a variation of this method can be used to fill the record headers of an IDOC.
HL structure
We recall that the HL segment has this structure:- 0628 AN 1/12 Hierarchical id number
- 0734 AN 1/12 Hierarchical parent id number
- 0735 ID 1/2 Hierarchical level code
- 0736 ID 1/1 Hierarchical child code
HL*1**S*1~
HL*2*1*O*1~
HL*3*2*I*0~
HL*4*2*I*0~
HL*5*1*O*1~
HL*6*5*I*0~
Segment and Group Structure and Naming
On the output side, I will physically rearrange the data records. I will paste the HL group inside itself to the required depth. The child HL will of course be at the end of the parent's data segments.The segment naming will be impacted, since we are implementing concrete repetitions of 2010_HL_grp. My solution is to simply include the level code in the identifier. So 2010_HL_S_grp, 2010_HL_O_grp, and 2010_HL_I_grp. I do this also with the segments. If my patience is thin, I do it only with the segments that I am activating.
Accumulator Usage
What I do to implement this on output, is that I use Accumulator 0 as the HL sequence number, and then I assign the next few Accumulators to the respective levels.The accumulators will be named as follows:
- HL_id_seq
- HL_shipment_id
- HL_order_id
- HL_item_id
So on the first HL for shipment level, on the HL01 [0628] element, I create a standard rule for Use Accumulator. I name Accumulator 0 as "HL_id_seq" and then specify:
- Primary=0: HL_id_seq
- Increment primary
- Use primary
- Primary=0: HL_id_seq
- Move primary to secondary 1: HL_shipment_id
HL01 [0628] will always have the first part: inc primary; use primary. This simply generates the HL sequence numbers. The second part of the rule will change depending on the level. The secondary accumulator is used to maintain the ancestry.
At the shipment level, HL02 [0734] is unused, since it represents the root.
For HL03 [0735], I simply use a standard rule with a constant: HL_level_shipment = "S". And HL04 [0736] will be another constant: HL_child_yes = 1.
Now see what happens at the order level.
HL01 [0628] will have its usual stuff:
- Primary=0: HL_id_seq
- Increment primary
- Use primary
- Primary=0: HL_id_seq
- Move primary to secondary 2: HL_order_id
At the order level, HL02 [0734] now has a rule of its own, since it must capture the parent id, namely that of the shipment. Conveniently, we have that in Accumulator 1: HL_shipment_id. All we have to do is use it.
- Primary=1: HL_shipment_id
- Use primary
We do much the same thing at the item level.
HL01 [0628] follows its normal convention:
- Primary=0: HL_id_seq
- Increment primary
- Use primary
- Primary=0: HL_id_seq
- Move primary to secondary 3: HL_item_id
- Primary=2: HL_order_id
- Use primary
Technically there is no need to capture HL_item_id, since being a leaf it can never be a parent. The idea here is to keep the rules as consistent as possible. This keeps it much easier for the client to maintain the map. Suppose they needed to modify this map to have an SOIP hierarchy: they need only change the hierachy type in the BSN segment, and now just add another level after item. Their changes to the rules will be minimal and thus highly stable through changes.
If you need to render a CTT segment, and CTT01 expects to have the total number of HL segments, all you need to do is create a standard rule with Use primary on the HL_id_seq.
In some implementations there is a concrete 1:1 relationship between shipment and order, that is, strictly one order per shipment. You need only arrange this in your physical segment/group layout to parallel your source data. The standard rules for constructing the HL segment remain exactly the same.
Gotcha!
There is still one outstanding gotcha. There must be an actual mapping from input to output to initiate output of the segment, even though HL is the group lead.My favourite solution is to use kicker elements on input. These of course are done in temporary records with a $$$ tag. We simply install these elements at the appropriate levels in the input. I by convention will name the elements kick_HL_shipment, kick_HL_order, and kick_HL_item. I define them as string 1/1 and use an extended rule to populate them with their level codes: S, O, and I respectively, and then I map them to HL03. The constant rule on HL03 overrides the value anyway, but I use the appropriate letter for consistency.
Summary
Here now we have created an outbound 856 with the standard in-order traversal through its hierarchy.If you need to tap the accumulators in extended rules on the output side, you can conveniently use the accum(n) function. (Remember to use the integer constants that we defined to ensure that actual integers are used.) By convention, accum(0) is the global sequencer. This way, the natural numbers, 1, 2, 3, etc correspond directly with their respective levels.
Other Applications
811 Consolidated Services Invoice
Exactly the same methodology can be used to create an outbound 811 Consolidated Services Invoice. The HL03 level codes are different, and there are typically more levels, but the theory is identical.SAP IDOCs
A SAP IDOC has a segment level hierarchy. Each segment has a sequence number and a parent segment sequence number and a level number. It is easy to see how this methodology could be used to populate the IDOC segment header.(Of course if you are simply importing the IDOC into SAP, you can get away with populating only the record id, but technically that's cheating. I usually populate at least the segment sequence number.)
I intend to create a separate article dedicated to the application of this technique to fully build the IDOC segment header.
Thank you for the great stuff
ReplyDeleteThanks for helpful blog! Currently trying to replace special char in comments fields in flat file so that outbound we don't do soentign stupid like pass * or other delimiter and blow up partner's translator, and it works ok EXCEPT for ^! Can replace *, ~, <, no problem but ^ is ignored and passed as-is. Grumble. But I think the likelyhood of someone using that as puncutation is hopefully remote. Will have to read your HL notes on 856, sounds like a lot of help.
ReplyDeleteTkas care!