Documentation

Generate EDI files

Article author
Admin
  • Updated

Generating EDI files with EDI Writer

Generating an EDI file with EDI Tools for .NET is a three-step process:

  1. Create an instance of the required EDI template (or C# class), e.g. TS837P for a medical claim or TSINVOIC for an invoice.
  2. 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.
  3. 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

edi-template-standard.png

 

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:

 

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

 

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 : + / * '  

 

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(); } }
Share this:

Was this article helpful?

Comments

0 comments

Please sign in to leave a comment.