Introduction:
Welcome to Bitfreighter's EDI mapping tool! This powerful tool offers a combination of simplicity and flexibility to handle any mapping requirements you may have. In this documentation, you'll find the information you need to get started with the tool and build your mappings with confidence.
How it Works:
Before we dive into mapping EDI, let's first understand how data flows through Bitfreighter. As an integration engine, Bitfreighter can send and receive data with many different systems, some of which may not be EDI-based. Incoming data may be in various formats such as XML, JSON, or other flat file formats. To simplify the process, Bitfreighter uses a Canonical Model pattern. In other words, data flows through the following two stages:
The two yellow stages represent the parts of data that your EDI mappings are responsible for.
Canonical Mapping:
The Canonical Mapping stage is responsible for manipulating the incoming EDI message to ensure that it's compatible with Bitfreighter. While Bitfreighter is somewhat flexible in terms of EDI formatting and version usage, there are some important things to consider:
- The shipment ID number must be in the correct standard location for each transaction type (204 - B2/04, 990 - B1/02, 214 - B10/02, 210 - B3/03).
- EDI should be valid according to X12 standards, with a few exceptions:
- Non-standard L11/N9 reference number qualifiers are acceptable.
- Non-standard NTE (note) qualifiers are acceptable.
If you're unsure, go ahead and give it a try! If the canonical EDI isn't processable by Bitfreighter, it may prevent us from processing the message, which will show up as an error in the user interface. It's typically good practice to map as much correct info as possible in the Canonical Mapping stage because it will come in handy during the next phase, "Partner Mapping."
Partner Mapping:
The purpose of Partner Mapping is to manipulate the EDI document to meet any nuanced needs the message recipient may have. This may include specialized reference numbers or non-standard EDI specifications such as the removal of multiple segments. You could even add completely non-standard EDI segments if you choose to do so. Where Canonical Mapping is the place for "correctness," Partner Mapping is the place for "shenanigans."
Mapping Types:
MapInterchanges:
Select this mapping type if you need **anything** from the interchanges.
Those segments exist on every envelope, regardless of how many transaction are on that envelope :
- ISA
- GS
- GE
- IEA
Most common children mapping types include :
- MapTransactions
- GetElement
- SetElement
- Conditional
Times where you will need to start your mapping with MapInterchanges include, but are not limited to :
- Any kind of readdressing
- Any kind of conditional mapping where the condition has to do with the sender or the receiver id
- Any time you need to access the original tender (GetLoadInfo)
MapTransactions:
Select this mapping type to map a specific transaction type.
A transaction starts at the ST segment, and ends at SE. There can be multiple transactions (and transaction types) on any given envelope.
Any child of MapTransactions can only be an element contained between ST and SE. As such, for example, we cannot get the value of an element in the ISA inside of MapTransactions. We would have that step happen as a child of MapInterchanges, and it would be a sibling of MapTransactions.
GetElement:
Select this mapping type to capture the value of a specified element.
Whenever we `get` an element, we register it under a variable, which can then be reused in different mappings.
The correct syntax to get an element is to specify the segment first, and secondly, the position.
eg: AT701 -> segment AT7, position 01 -> Shipment Status Code
This element generally shouldn't have children elements.
SetElement:
Select this mapping type to assign a value to a specified element.
This element generally shouldn't have children elements.
Conditional:
This mapping type is used as a parent for other types, it cannot be used on its own. We use this to specify a condition in which the child code should be applied.
For example, we receive an AT701 with a value of CD, but the trading partner only accepts D1. Here's how you would map it
- MapTransactions
- GetElement -> to capture the value currently at AT701, let's call it "status"
- Conditional -> as a sibling to GetElement, we will pass it the following condition: ${status.value} EQUALS "CD"
Here, we're comparing the value we have at AT701 in our file, to the value of "CD", and if they match, then we will apply the following code (as a child of Conditional) - SetElement -> AT701, "D1" (this is assigning the value of D1 to the AT701 element. Since we made it happen within a Conditional, it will only be applied if the condition is met
Convert:
We use this mapping type to convert a file from one version to another. A lot of mapping happens behind the scenes for conversion, so it's important to use the convert function and not just change the version number on the file.
We use the value at ISA12 to make the change (format : 00XXX)
We pass in the version as the argument when using this mapping type, here is an example where we are converting to version 5010:
{"version":"00501"}
RemoveSegments:
This function is always used as a child of something else. We use it to remove entire transactions, or specific segments. For example, if your customer doesn't want to receive any kind of appointment (AA/AB) 214:
- MapTransactions (214)
- GetElement -> AT703, we will name this appt_status
- Conditional -> as a sibling to GetElement, we will pass it the following condition: ${appt_status.value} IN ["AA", "AB"]
- RemoveSegments
This would remove the entire 214 from the envelope, if the appt_status was equal to either AA or AB
We could also use this with MapLoops, to remove all G62 segments, for example:
- MapTransactions (214)
- MapLoops -> With Manual entry, specify G62
- RemoveSegments
This would return your transaction without any G62 segments. Like usual, you can also add a condition within the loop if you don't want this applied to everything.
MapLoops
MapLoops is used in a variety of scenarios. When we use this segment, we will "iterate" over the specified segments, which means the code will find the first thing that matches the loop, execute whatever code is its child, and then keep doing that with the next matching loop it finds, until the end of the transaction.
For example, if you want to look over all the L11s in a transaction, and when the qualifier is SI, you want to change it to MB. We would use
Manual entryYour code would look something like this:
- MapTransactions
- Map Loops -> here, we will specify the L11 segment
- GetElement -> L1102, we will name this qualifier
- Conditional -> as a sibling to GetElement, we will pass it the following condition: ${qualifier.value} EQUALS "SI"
Here, we're comparing the value we have at L1102 in our loop, to the value of "SI", and if they match, then we will apply the following code (as a child of Conditional) - SetElement -> L1102 = MB
If the condition is met, we will change the qualifier from SI to MB
Here, what happens is this: the code is running, line by line. Every time it meets an L11, it inspects it, and if the condition we specified is not met, it moves on to the next without doing anything. If the condition is met, it applies the code, and moves onto the next, and it keeps going until the end of the transaction.
In the event you want to affect only the L11s in the header, for example, in a 214, you could write it this way:
- MapTransactions
- Map Loops -> here, we will specify all the segments before L11, plus the L11, to tell our code to iterate over that loop only (on a 214, that would be B10 and L11)
- Map Loops -> This second MapLoops is going to be for the L11s of the loop specified above. This will prevent the code from running after the header. Then we do everything else the same.
- GetElement -> L1102, we will name this qualifier
- Conditional -> as a sibling to GetElement, we will pass it the following condition: ${qualifier.value} EQUALS "SI"
Here, we're comparing the value we have at L1102 in our loop, to the value of "SI", and if they match, then we will apply the following code (as a child of Conditional) - SetElement -> L1102 = MB
If the condition is met, we will change the qualifier from SI to MB
If you want to remove all S5 loops, you would use the Loop lookup , where you can specify the transaction type, the version, and the loop. If you're unsure what all is included in a loop, STEDI can help.
So if you have a 210, and you want to remove all S5 loops, this is what it would look like:
- MapTransactions
- Map Loops -> here, we will specify the transaction (210), the version (4010), and the loop(S5)
- RemoveSegments -> This here doesn't take any argument, so we'll just leave it blank
Very simple visual of a MapLoops AppendSegment:
Appending a segment means to add a segment at the end of. If you use this without any sort of condition, or without telling the mapping tool where to put it, such as within a MapLoops, it will just add it to the very bottom of the transaction.
This mapping type allows you to build a brand new segment. In some cases, trading partners want some information repeated, or hardcoded in a segment that doesn't exist on the transaction we receive.
For example, the TP requires us to send an L11 with a ZZ qualifier, and a hardcoded value of BitfreighterRocks, in the header. We know this never comes in on the transaction we receive, so our mapping would look like this.
Since we know where we need to append the segment, we will start with a MapLoops:
- MapTransactions
- Map Loops -> here, we will specify all the segments before L11, plus the L11, to tell our code to iterate over that loop only (on a 214, that would be B10 and L11)
- AppendSegment -> Here your first value would be "L11", the second "BitfreighterRocks", and the third, "ZZ".
SegmentAllowlist / SegmentBlocklist:
Both of those serve a similar purpose. When using the SegmentAllowlist/Blocklist, you are using the segment identifier (L11, N1, G62, etc) to let the mapper know what to allow (the mapper will reject any segment not starting with the values you specify), or which ones to block (a better option if you only need to block one or two types of segments)
It takes a list as an argument, which is generally built like so: ["L11", "B10", "G62"]
GetLoadInfo:
We use GetLoadInfo when we are trying to reference the information sent on the load. Depending on the trading partner/connection we are doing it for, we might want to use both an interchange id AND an SID, or, in cases where a lot of readdressing is happening before canon, just the SID.
If we do choose to include the interchange id (ISA08, trimmed), we need to start with a MapInterchanges.
- MapInterchanges
- GetElement -> ISA08, we will check the box to trim it, and also save it as interchange_id
- MapTransaction-> 214
- GetElement -> B1002 -> This is the SID, we will save it as sid
- GetLoadInfo -> {"shipment_id":"${sid.value}", "interchange_id":"${interchange_id.value}"} -> we will save this value as load so we can use it later.
To use the information on the load, we have to ask ourselves a few questions:
- Is this a unique thing on the load? (such as weight, carrier_reference_number, equipment, etc) -> If this is the case, you can access this value simply by using ${load.weight}, for example.
- Is this something that there are multiple of? Otherwise known as a list (stops, reference_numbers, etc) -> If that is the case, we will need to iterate through those values, and this is where Lua comes in handy.
- What is that value called on a load? Is it saved to the database? -> We generally follow the naming convention of STEDI, and we save a lot of information to the database.