Generating EDI files with EDI Writer
Generating an EDI file with EDI Tools for .NET is a three-step process:
- Create an instance of the required EDI template (or C# class), e.g. TS837P for a medical claim or TSINVOIC for an invoice.
- Populate that instance with data, usually, this requires custom code to convert either a database/domain object or export from an ERP in CSV, XML, JSON, etc. format, to the EDI template.
- Write the instance to an EDI file or a stream using EDI writer.
The red path below depicts the generation of an EDI file:
- A .NET object (instance of its EDI template)
- Is processed through an EDI writer (EdiFabric)
- To produce an EDI file
How to create and populate .NET objects
When you need to generate, let's say an invoice in the X12 standard, you'll need to create an instance of the EDI template, that represents an invoice according to the X12 standard.
Once the instance is available, you can begin populating it with custom code, which maps your internal objects or database export, to the structure of the EDI template.
-
var result = new TS810(); result.ST = new ST(); result.ST.TransactionSetIdentifierCode_01 = "810"; result.ST.TransactionSetControlNumber_02 = controlNumber.PadLeft(9, '0'); result.BIG = new BIG(); result.BIG.Date_01 = "20000513"; result.BIG.InvoiceNumber_02 = "SG427254"; result.BIG.Date_03 = "20000506"; result.BIG.PurchaseOrderNumber_04 = "508517"; result.BIG.ReleaseNumber_05 = "1001";
-
var result = new TSINVOIC(); result.UNH = new UNH(); result.UNH.MessageReferenceNumber_01 = controlNumber.PadLeft(14, '0'); result.UNH.MessageIdentifier_02 = new S009(); result.UNH.MessageIdentifier_02.MessageType_01 = "INVOIC"; result.UNH.MessageIdentifier_02.MessageVersionNumber_02 = "D"; result.UNH.MessageIdentifier_02.MessageReleaseNumber_03 = "96A"; result.UNH.MessageIdentifier_02.ControllingAgencyCoded_04 = "UN"; result.BGM = new BGM(); result.BGM.DOCUMENTMESSAGENAME_01 = new C002(); result.BGM.DOCUMENTMESSAGENAME_01.Documentmessagenamecoded_01 = "380"; result.BGM.Documentmessagenumber_02 = "IN432097";
-
var result = new TSRDSO13(); result.MSH = new MSH(); result.MSH.SendingApplication_02 = new SendingApplication(); result.MSH.SendingApplication_02.NamespaceID_01 = senderId; result.MSH.SendingFacility_03 = new SendingFacility(); result.MSH.SendingFacility_03.NamespaceID_01 = senderFacility; result.MSH.ReceivingApplication_04 = new ReceivingApplication(); result.MSH.ReceivingApplication_04.NamespaceID_01 = receiverId; result.MSH.ReceivingFacility_05 = new ReceivingFacility(); result.MSH.ReceivingFacility_05.NamespaceID_01 = receiverFacility; result.MSH.DateTimeOfMessage_06 = DateTime.Now.Date.ToString("yyyyMMdd") + DateTime.Now.TimeOfDay.ToString("hhmm"); result.MSH.MessageType_08 = new MessageType(); result.MSH.MessageType_08.MessageCode_01 = "RDS"; result.MSH.MessageType_08.TriggerEvent_02 = "O13"; result.MSH.MessageType_08.MessageStructure_03 = "RDS_O13"; result.MSH.MessageControlID_9 = controlNr; result.MSH.ProcessingID_10 = new ProcessingID(); result.MSH.ProcessingID_10.ProcessingID_01 = "P"; result.MSH.VersionID_11 = new VersionID(); result.MSH.VersionID_11.VersionID_01 = "2.6"; result.LoopPID = new Loop_PID_TSRDSO13(); result.LoopPID.PID = new PID(); result.LoopPID.PID.PatientID_02 = new ExtendedCompositeIDWithCheckDigit(); result.LoopPID.PID.PatientID_02.IDNumber_01 = "0008064993"; result.LoopPID.PID.PatientID_02.AssigningAuthority_04 = new ExtendedAssigningAuthority(); result.LoopPID.PID.PatientID_02.AssigningAuthority_04.ExtendedAssigningAuthority01 = "ENT"; result.LoopPID.PID.PatientID_02.IdentifierTypeCode_05 = "PE"; result.LoopPID.PID.PatientID_02.EffectiveDate_07 = "20030806"; result.LoopPID.PID.PatientID_02.ExpirationDate_08 = "200507";
-
var result = new TSB1(); result.G1 = new TransactionHeader(); result.G1.TransactionReferenceNumber_1 = tranRefNumber; result.G1.BINNumber_2 = binNumber; result.G1.VersionReleaseNumber_3 = "D0"; result.G1.TransactionCode_4 = "B1"; result.G1.ProcessorControlNumber_5 = processorNumber; result.G1.TransactionCount_6 = "1"; result.G1.ReversalRequest_7 = reversalReq; result.G1.BillingRequest_8 = billingReq; result.G1.PredeterminationofBenefitsRequest_9 = preReq; result.G1.SoftwareVendorCertificationID_10 = ""; result.AM04Loop = new Loop_AM04_TSB1(); result.AM04Loop.AM04 = new AM04(); result.AM04Loop.AM04.CardholderID_C2 = "DK86342S"; result.AM04Loop.AM04.EligibilityClarificationCode_C9 = "0"; result.AM04Loop.AM04.PersonCode_C3 = "001";
-
var result = new TSNEWRX(); result.UIH = new UIH(); result.UIH.MESSAGEIDENTIFIER_01 = new S306(); result.UIH.MESSAGEIDENTIFIER_01.MessageType_01 = "SCRIPT"; result.UIH.MESSAGEIDENTIFIER_01.MessageVersionNumber_02 = "010"; result.UIH.MESSAGEIDENTIFIER_01.MessageReleaseNumber_03 = "006"; result.UIH.MESSAGEIDENTIFIER_01.MessageFunction_04 = "NEWRX"; result.UIH.MessageReferenceNumber_02 = tranRefNumber; result.UIH.DATEANDTIME_05 = new S300(); result.UIH.DATEANDTIME_05.Date_01 = "19971001"; result.UIH.DATEANDTIME_05.Time_02 = "081322";
-
var result = new TS4905(); result.S511 = new S511(); result.S511.RecordType_01 = "511"; result.S511.Version_02 = "02"; result.S511.CustomerCode_03 = "59012"; result.S511.SupplierCode_04 = "00323625"; result.S511.TransmissionNumberOld_05 = oldNumber.ToString(); result.S511.TransmissionNumberNew_06 = newNumber.ToString(); result.S511.TransmissionDate_07 = "121115"; result.S511.StartingDate_08 = "111231";
There are multiple approaches on how to convert or map between objects in .NET, however, this is not the premise of this article. To be able to properly construct a .NET object, sufficient knowledge is need for:
- The source of the data (database, CSV file, service model, XML, JSON, etc.)
- The destination EDI template, which matches the implementation guideline
- How to map the source to the destination, using custom .NET mapping code, or JSON/XML serialization
Concrete examples of creating EDI .NET objects:
It is also possible to map the internal structure to the JSON or XML representation of a particular EDI template, and then simply use the available tooling in .NET to deserialize it into a POCO.
For mapping ideas, take a look at how XSLT and Automapper can transform .NET objects: X12 mapping examples, EDIFACT mapping examples.
How to write EDI objects
The purpose of an EDI writer is to write an already populated C# instance of a particular EDI template, to a text EDI file or stream. The writer is a simple extension of the .NET's StreamWriter, which caters to a few extra EDI-related things such as:
- To convert instances of EDI templates to text layout as expected by the EDI standard.
- To automatically apply the correct message, group, and interchange trailers. This counts the number of included segments, messages, and groups and matches control numbers.
- To apply the correct EDI separators.
- To apply segment postfixes if any.
- To apply control segments, such as ISA, UNB, MSH, etc.
All Edi writers provide a fast, non-cached, forward-only way to generate streams or files that contain EDI data. It is up to the consumer to preserve the sequence of what is being written.
There is no need to write any of the EDI trailers because they are set automatically.
All writers implement IDisposable and need to be disposed of. This ensures that all buffered data is properly written and the internal buffer is cleared.
-
using (var stream = new MemoryStream()) { using(var writer = new X12Writer(stream)) { writer.Write(SegmentBuilders.BuildIsa("1")); writer.Write(SegmentBuilders.BuildGs("1")); // 1. Write the first invoice writer.Write(SegmentBuilders.BuildInvoice("1")); // 2. Write the second invoice writer.Write(SegmentBuilders.BuildInvoice("2")); }
} -
using (var stream = new MemoryStream()) { using(var writer = new EdifactWriter(stream)) { writer.Write(SegmentBuilders.BuildUnb("1")); // 1. Write the first invoice writer.Write(SegmentBuilders.BuildInvoice("1")); // 2. Write the second invoice writer.Write(SegmentBuilders.BuildInvoice("2")); } }
-
using (var writer = new Hl7Writer(stream)) { // Envelope writer.Write(SegmentBuilders.BuildFhs("LAB1", "LAB", "DEST2", "DEST", "TESTFILE", "1234")); writer.Write(SegmentBuilders.BuildBhs("LAB1", "LAB", "DEST2", "DEST", "TESTBATCH", "1234")); // Write the first dispense writer.Write(SegmentBuilders.BuildDispense("LAB1", "LAB", "DEST2", "DEST", "1")); // Write the second dispense writer.Write(SegmentBuilders.BuildDispense("LAB1", "LAB", "DEST2", "DEST", "2")); }
-
using (var writer = new NcpdpTelcoWriter(stream)) { // Write the transmission header writer.Write(SegmentBuilders.BuildTransmissionHeader()); // Write the claim 1 writer.Write(SegmentBuilders.BuildClaim("1")); // Write the claim 2 writer.Write(SegmentBuilders.BuildClaim("2")); }
-
using (var writer = new NcpdpScriptWriter(stream)) { // Write the transmission header writer.Write(SegmentBuilders.BuildInterchangeHeader("1")); // Write the prescription request 1 writer.Write(SegmentBuilders.BuildPrescriptionRequest("1")); // Write the prescription request 2 writer.Write(SegmentBuilders.BuildPrescriptionRequest("2")); }
-
using (var writer = new VdaWriter(stream, Environment.NewLine)) { writer.Write(deliveryInstruction1); writer.Write(deliveryInstruction2); }
Examples in GitHub:
- Write X12 file example
- Write X12 file example - async
- Write EDIFACT file example
- Write EDIFACT file example - async
- Write HL7 file example
- Write HL7 file example - async
- Write NCPDP Telco file example
- Write NCPDP Telco file example - async
- Write NCPDP SCRIPT file example
- Write NCPDP SCRIPT file example - async
- Write VDA file example
- Write VDA file example - async
Batches of EDI transactions
EDI writer allows multiple EDI interchanges, EDI groups, and EDI transactions to be written to the same stream or file. It doesn't matter for the EDI writer if a single item or multiple items are written out and very large sets of data can be written one by one.
All standards that begin with envelopes, like X12 (ISA, GS), EDIFACT (UNB), HL7 (FHS, BHS), need to write their envelopes first, and then all remaining EDI transactions.
Control segments
Control segments are written out using an overload of the EDI writer that takes an exact control segment type. Envelop segments must be written according to the hierarchical order specified by the EDI standard, e.g. for X12 the order is ISA, GS, then all messages, for EDIFACT the order is UNB, UNG (optional), then all messages, etc.
EDI writers
- X12Writer Reference - writes X12 or HIPAA EDI documents
- EdifactWriter Reference - writes EDIFACT or EANCOM EDI documents
- Hl7Writer Reference - writes HL7 EDI documents
- NcpdpScriptWriter Reference - writes NCPDP SCRIPT EDI documents
- NcpdpTelcoWriter Reference - writes NCPDP Telecommunications documents
- VdaWriter Reference - writes VDA documents
Configuration of EDI writers
To configure the EDI writer, follow the steps in the How to configure EDI writer tutorial.
Default EDI separators
EDI separators (or EDI delimiters) are used to separate the different EDI items from one another. It is imperative that a valid set of delimiter is used and the various types of delimiters are place at the correct position within an EDI file.
If not explicitly specified the writer will use the default separators per standard:
EDI Standard | Component | Data Element | Escape | Repetition | Segment | Subelement |
---|---|---|---|---|---|---|
X12 | > | * | ^ | ~ | ||
EDIFACT | : | + | ? | * | ' | |
HL7 | ^ | | | \ | ~ | \r | & |
NCPDP SCRIPT | : | + | / | * | ' |
X12 repetition separator was introduced in version 4020 is not available for the earlier versions.
Custom EDI separators
Sometimes the delimiters used by a trading partner are slightly different than the default separator set. To change the default separators and generate an EDI file using custom separators use the static Separators class and pass it to the EDI writer (at the first item written out, usually the interchange header).
Every segment or EDI transaction written after this point will use the new separators until a new separator set is applied.
-
using (var stream = new MemoryStream()) { using(var writer = new X12Writer(stream)) { var isa = new ISA(); // construct the interchange header ... var newSeparators = Separators.X12; // change the segment separator, the rest are the default for X12 newSeparators.Segment = '\n'; writer.Write(isa, newSeparators); // everything written after this point will use the custom separators }
} -
using (var stream = new MemoryStream()) { using(var writer = new EdifactWriter(stream)) { var unb = new UNB(); // construct the interchange header ... var newSeparators = Separators.Edifact; // change the segment separator, the rest are the default for X12 newSeparators.Segment = '\n'; writer.Write(unb, newSeparators); // everything written after this point will use the custom separators } }
-
using (var writer = new Hl7Writer(stream)) { // Set a custom subcomponent separator. var separators = Separators.Hl7; separators.SubComponent = '#'; // Write the dispense writer.Write(SegmentBuilders.BuildDispense("LAB1", "LAB", "DEST2", "DEST", "1"), true, separators); }
-
using (var writer = new NcpdpScriptWriter(stream)) { // Set a custom segment separator. var separators = Separators.NcpdpScript; separators.Segment = '|'; // Write the interchange header writer.Write(SegmentBuilders.BuildInterchangeHeader(), separators); // Write the prescription request writer.Write(SegmentBuilders.BuildPrescriptionRequest()); }
Write transactions without envelopes
It is possible to write EDI transactions without envelopes. To do so, a separator set must be supplied to the writer settings manually.
-
var settings = new X12WriterSettings() { Separators = Separators.X12 } using (var stream = new MemoryStream()) { using(var writer = new X12Writer(stream, settings)) { var m810_1 = new TS810(); // construct the first message ... await writer.WriteAsync(m810_1); var m810_2 = new TS810(); // construct other messages ... await writer.WriteAsync(m810_2); } }
-
var settings = new EdifactWriterSettings() { Separators = Separators.Edifact} using (var stream = new MemoryStream()) { using(var writer = new EdifactWriter(stream, settings)) { var invoic_1 = new TSINVOIC(); // construct the first message ... await writer.WriteAsync(invoic_1); var invoic_2 = new TSINVOIC(); // construct other messages ... await writer.WriteAsync(invoic_2); } }
Examples in GitHub
Turn the auto trailers off
In situations where auto trailers are not needed, for example when writing to the same file at different times of the day, auto trailers can be turned off. There are two modes for that - transaction trailers and interchange\group trailers.
Turn off the auto trailer on EDI transactions
Set the AutoTrailer argument of the Write method of EDI writer to false.
using (var stream = new MemoryStream())
{
using(var writer = new EdifactWriter(stream))
{
var invoic_1 = new TSINVOIC();
// construct the first message ...
await writer.WriteAsync(invoic_1, false);
}
}
Turn off the auto trailers on EDI interchanges and EDI groups
Set the AutoTrailers flag of EDI writer settings to false.
var settings = new X12WriterSettings() { AutoTrailers = false }
using (var stream = new MemoryStream())
{
using(var writer = new X12Writer(stream, settings))
{
var isa = new ISA();
// construct the interchange header ...
await writer.WriteAsync(isa);
var gs = new GS();
// construct the group header ...
await writer.WriteAsync(gs);
var m810_1 = new TS810();
// construct the first message ...
await writer.WriteAsync(m810_1);
}
// no trailers are written
}
Generating EDI files without EDI writer
EDI files can be produced without using the built-in X12 and EDIFACT writers at all. To do so use a combination of .NET's StreamWriter and the ToEdi() method which is available for all transactions and control segments.
The ToEdi() method needs a separator set to output the transaction or control segment as a string.
-
var settings = new X12WriterSettings() { Separators = Separators.X12 }
using (var stream = new MemoryStream()) { using(var writer = new StreamWriter(stream)) { var isa = new ISA(); // construct the interchange header ... writer.Write(isa.ToEdi(Separators.X12)); var gs = new GS(); // construct the group header ... writer.Write(gs.ToEdi(Separators.X12)); var m810 = new TS810(); // construct the first message ... writer.Write(m810.ToEdi(settings));
var ge = new GE(); // construct the group trailer ... writer.Write(ge.ToEdi(Separators.X12));
var iea = new IEA(); // construct the interchange trailer ... writer.Write(iea.ToEdi(Separators.X12));
writer.Flush(); } } -
var settings = new EdifactWriterSettings() { Separators = Separators.Edifact}
using (var stream = new MemoryStream()) { using(var writer = new StreamWriter(stream)) {
writer.Write(Separators.Edifact.ToUna());
var unb = new UNB(); // construct the interchange header ... writer.Write(unb.ToEdi(Separators.Edifact)); var invoic = new TSINVOIC(); // construct the first message ... writer.Write(invoic.ToEdi(settings));
var unz = new UNZ(); // construct the interchange trailer ... writer.Write(unz.ToEdi(Separators.Edifact));
writer.Flush(); } }
Comments
0 comments
Please sign in to leave a comment.