Bundling X12 Claims

May 15, 2012 at 9:49 PM

Hi! Thanks for the fantastic tool! I'm trying to bundle a bunch of claims together (basically the reverse of the unbundle tool) and I'm not sure the best way to go about it. I have a collection of multiple 5010 837 Claims, but each individual claim is wrapped in its own ISA, GS, and ST wrappers (even for those going to the same payer). I'm trying to bundle them all together into one file since the ISA, GS, ST, Loop 1000A, Loop 1000B, and Loop 2000A are all exactly the same and only Loop 2000B and its "children" vary. I can load all the claims into memory using the Multiparse method and create the destination structure with correct ISA/GS/ST/1000A/1000B/2000A using the documented Interchange model methods.

I'm just not sure of the most efficient way to transfer the Loop 2000Bs over from the source interchange list object into the destination interchange object? Do I need to crawl through the whole source claims 2000B loop and children and recreate each HL, Loop, Segment, and Element in the destination with the Interchange add methods or is there a simple way to just attach a copy of the Loop 200B HL object and its children objects onto the destination interchange. The second way seems easier and more efficient but I couldn't find a method to do this. I assume the SE segment "Number of included segments" would need to be updated as well, which is probably part of the problem. It just seems inefficient to recreate the source HL object piece by piece to attach it to the destination interchange object.

Thanks for your help in how to proceed!

Coordinator
May 15, 2012 at 10:34 PM

I might need to expose a new method for you, but I agree your second method sounds easier.  This sounds like a good documentation page once I add the support for it.

THE SE segment will automatically recalculate itself once you've added the new segments when you run the SerializeToX12 method on the Interchange object.  I'll follow up tomorrow with some code snippets.

May 23, 2012 at 8:39 PM
Edited May 23, 2012 at 8:42 PM

Hi! I found out that looping through the children segments, loops, and hierarchical loops wasn't as hard as I thought it would be with a recursive function. I've included the code to bundle a single file containing multiple claims, each wrapped in their own interchange/functional group/transaction into a single interchange/functional group/transaction with a single sender (Loop 1000A), receiver (Loop 1000B), and billing provider (HLoop 2000A) with repeating child HLoops 2000B for each claim. I hope this helps others out as much as your parser has helped us out! Thanks!

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OopFactory.X12.Transformations;
using OopFactory.X12.Parsing;
using OopFactory.X12.Parsing.Model;


namespace BundleX12
{
    class BundleX12Prog
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                System.Console.WriteLine("Run as BundleX12.exe 'InputX12File' 'OutputX12File'");
            }
            else
            {
                // Load X12 File with multiple interchanges into inputInterchangeList object
                Stream ediFile = new FileStream(args[0], FileMode.Open, FileAccess.Read);
                var parser = new X12Parser();
                List<Interchange> inputInterchangeList = parser.ParseMultiple(ediFile);
                ediFile.Close();

                /* This program bundles list of 837 claims wrapped in their own input interchanges
                 * into a single output interchange.
                 * 
                 * Limitations: This program is only tested with 837 claims and is built around them.
                 * 
                 * The values in the first interchange are used to fill in the ISA, GS
                 * ST, BHT, Loop 1000A, Loop 1000B, Loop 2000A output structures. All 2000B and child loops
                 * are copied and attached as children of the output Loop 2000A.
                 * 
                 * No checking is done to make sure all of the following interchanges have the same
                 * Loop 1000A Submitter Name, Loop 1000B Receiver Name, and Loop 2000A Billing Provider!
                 * This is assumed to be true and this information is discarded for all processed input
                 * interchanges after the first!
                 * 
                 * The order at each level is assumed to be Segments, then Loops, then Hierarchical Levels.
                 * If the order in the file is different, the output will be out of order!
                 */

                if (inputInterchangeList.Count == 1)
                {
                    Console.WriteLine("Error: There is only 1 interchange in the input!");
                    throw new System.ArgumentException("Input file must contain more than 1 interchange!", "args[0]");
                }

                BundleX12 bundleX12 = new BundleX12();
                Interchange outputInterchange = bundleX12.BundleX12Interchanges(inputInterchangeList);

                // Convert outputInterchange into text and write out
                string x12 = outputInterchange.SerializeToX12(false);
                TextWriter outFile = new StreamWriter(args[1]);
                outFile.Write(x12);
                outFile.Close();

            }
        }
    }

    class BundleX12
    {
        public Interchange BundleX12Interchanges(List<Interchange> inputInterchangeList)
        {
            // Create new single outputInterchange object to append all Transactions into and load elements from first Interchange of input interchanges
            Interchange outputInterchange = new Interchange(inputInterchangeList[0].InterchangeDate, Convert.ToInt32(inputInterchangeList[0].GetElement(13)), (inputInterchangeList[0].GetElement(15) == "P"));
            // ISA09 Date, ISA10 Time (doesn't work properly), ISA13 Interchange Control Number, ISA15 Usage Indicator for Production/Test set above when object created
            outputInterchange.AuthorInfoQualifier = inputInterchangeList[0].AuthorInfoQualifier; // ISA01
            outputInterchange.AuthorInfo = inputInterchangeList[0].AuthorInfo;  // ISA02
            outputInterchange.SecurityInfoQualifier = inputInterchangeList[0].SecurityInfoQualifier; // ISA03
            outputInterchange.SecurityInfo = inputInterchangeList[0].SecurityInfo;  // ISA04
            outputInterchange.InterchangeSenderIdQualifier = inputInterchangeList[0].InterchangeSenderIdQualifier; // ISA05
            outputInterchange.InterchangeSenderId = inputInterchangeList[0].InterchangeSenderId; // ISA06
            outputInterchange.InterchangeReceiverIdQualifier = inputInterchangeList[0].InterchangeReceiverIdQualifier; // ISA07
            outputInterchange.InterchangeReceiverId = inputInterchangeList[0].InterchangeReceiverId; // ISA08
            // Set ISA11 Control Standards Identifier, ISA12 Control Version Number '00501', ISA14 Acknowledgment Request '1'
            outputInterchange.SetElement(10, inputInterchangeList[0].GetElement(10)); // ISA10 Fix time manually since InterchangeDate property doesn't parse it correctly and resets it to 1200
            outputInterchange.SetElement(11, inputInterchangeList[0].GetElement(11)); // ISA11
            outputInterchange.SetElement(12, inputInterchangeList[0].GetElement(12)); // ISA12
            outputInterchange.SetElement(14, inputInterchangeList[0].GetElement(14)); // ISA14

            // Create new FunctionGroup (GS) in outputInterchange to contain all Transactions
            FunctionGroup outputFunctionGroup = outputInterchange.AddFunctionGroup(inputInterchangeList[0].FunctionGroups.First().FunctionalIdentifierCode, inputInterchangeList[0].FunctionGroups.First().Date, inputInterchangeList[0].FunctionGroups.First().ControlNumber, inputInterchangeList[0].FunctionGroups.First().VersionIdentifierCode); // "HC" function ID code
            // GS01 Functional Identifier Code, GS04 Date, GS05 Time (doesn't work properly), GS06 Function Group Control Number, GS08 Version Code all set above in object creation
            outputFunctionGroup.ApplicationSendersCode = inputInterchangeList[0].FunctionGroups.First().ApplicationSendersCode; // GS02
            outputFunctionGroup.ApplicationReceiversCode = inputInterchangeList[0].FunctionGroups.First().ApplicationReceiversCode; // GS03
            outputFunctionGroup.SetElement(5, inputInterchangeList[0].FunctionGroups.First().GetElement(5)); // GS05 Fix time manually since FunctionGroup Date property doesn't parse it correctly and resets it to 0000
            outputFunctionGroup.ResponsibleAgencyCode = inputInterchangeList[0].FunctionGroups.First().ResponsibleAgencyCode; // GS07

            //Create new Transaction (ST) in outputInterchange to contain all Loops
            Transaction inputTransaction = inputInterchangeList[0].FunctionGroups.First().Transactions.First();
            Transaction outputTransaction = outputFunctionGroup.AddTransaction(inputTransaction.GetElement(1), inputTransaction.ControlNumber); // ST01 "837" and ST02 Control Number
            outputTransaction.SetElement(3, inputTransaction.GetElement(3)); // ST03 "005010X222"

            // Copy BHT segment, Loop 1000A Submitter, Loop 1000B Receiver from first input to destination
            CopySegmentsAndLoops(inputTransaction, outputTransaction, true);
            
            // Copy HL-1 2000A Billing/Pay-To Provider from first input to destination
            HierarchicalLoop outputBillingProviderHL = outputTransaction.AddHLoop("1", "20", true);
            CopySegmentsAndLoops(inputTransaction.FindHLoop("1"), outputBillingProviderHL, true);
            int outputHLIdIndex = 2; // Tracks ID of next HL to add (current total of hierarchical loop IDs in output
            
            // Loop through all input interchanges, functional groups, and transactions and extract HL Loop 2000B and children and add to destination
            foreach (Interchange inputInterchangeItem in inputInterchangeList) //Interchanges
            {
                for (int functionGroupIndex = 0; functionGroupIndex < inputInterchangeItem.FunctionGroups.Count(); functionGroupIndex++) //Functional Groups
                {
                    for (int transactionIndex = 0; transactionIndex < inputInterchangeItem.FunctionGroups.ElementAt(functionGroupIndex).Transactions.Count(); transactionIndex++) //Transactions
                    {
                        for (int hLoopIndex = 0; hLoopIndex < inputInterchangeItem.FunctionGroups.ElementAt(functionGroupIndex).Transactions.ElementAt(transactionIndex).HLoops.ElementAt(0).HLoops.Count(); hLoopIndex++) //HLoops
                        {
                            HierarchicalLoop inputSubscriberHL = inputInterchangeItem.FunctionGroups.ElementAt(functionGroupIndex).Transactions.ElementAt(transactionIndex).HLoops.ElementAt(0).HLoops.ElementAt(hLoopIndex);
                            outputHLIdIndex = CopyHLoop(inputSubscriberHL, outputBillingProviderHL, outputHLIdIndex, true);
                        }
                    }
                }
            }
            return (outputInterchange);
        }

        /// <summary>Copies segments then loops (including child loops if copyChildLoops is true)
        /// inside inputContainer (ie input Transaction/Loop/HL) to outputContainer (ie output Transaction/Loop/HL)</summary>
        public void CopySegmentsAndLoops(LoopContainer inputContainer, LoopContainer outputContainer, bool copyChildLoops)
        {
            int inputSegmentCount = inputContainer.Segments.Count();
            for (int i = 0; i < inputSegmentCount; i++)
            {
                outputContainer.AddSegment(inputContainer.Segments.ElementAt(i).SegmentString);
            }
            int inputLoopCount = inputContainer.Loops.Count();
            for (int i = 0; i < inputLoopCount; i++)
            {
                outputContainer.AddLoop(inputContainer.Loops.ElementAt(i).SegmentString);
                if (copyChildLoops == true)
                {
                    CopySegmentsAndLoops(inputContainer.Loops.ElementAt(i), outputContainer.Loops.ElementAt(i), true);
                }
            }
        }

        /// <summary>Copies segments, loops, and all child loops (and all child hierarchical loops if copyChildHL is true)
        /// inside inputHLoop and appends as new child hierarchical loop to outputHLoop. Returns ID number of next available unused hierarchical loop ID.</summary>
        public int CopyHLoop(HierarchicalLoop inputHLoop, HierarchicalLoop outputHLoop, int outputHLIdIndex, bool copyChildHL)
        {
            HierarchicalLoop outputChildHLoop = outputHLoop.AddHLoop(outputHLIdIndex.ToString(), inputHLoop.LevelCode, (inputHLoop.HierarchicalChildCode == "1"));
            CopySegmentsAndLoops(inputHLoop, outputChildHLoop, true);
            outputHLIdIndex++;

            if (inputHLoop.HierarchicalChildCode == "1" && copyChildHL == true)
            {
                for (int copyHLoopIndex = 0; copyHLoopIndex < inputHLoop.HLoops.Count(); copyHLoopIndex++)
                {
                    outputHLIdIndex = CopyHLoop(inputHLoop.HLoops.ElementAt(copyHLoopIndex), outputChildHLoop, outputHLIdIndex, true);
                }
            }
            return (outputHLIdIndex);
        }
    }
}

May 23, 2012 at 8:44 PM

Also, here's a command line utility to convert the input file into HTML which I created to help me troubleshoot.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OopFactory.X12.Transformations;
using OopFactory.X12.Parsing;
using OopFactory.X12.Parsing.Model;

namespace X12ToHtml
{
    class X12ToHTML
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                System.Console.WriteLine("Run as X12ToHTML.exe 'InputX12File' 'OutputHTMLFile'");
            }
            else
            {
                // Load X12 File with multiple interchanges into inputInterchangeList object
                Stream ediFile = new FileStream(args[0], FileMode.Open, FileAccess.Read);
                var parser = new X12Parser();
                List<Interchange> inputInterchangeList = parser.ParseMultiple(ediFile);
                ediFile.Close();

                var htmlService = new X12HtmlTransformationService(new X12EdiParsingService(suppressComments: false));
                StringBuilder x12 = new StringBuilder();
                foreach (Interchange interchangeItem in inputInterchangeList)
                {
                    x12.Append(htmlService.Transform(interchangeItem.SerializeToX12(false)));
                }

                string htmlout = "<html><head></head><body>" + x12.ToString() + "</body></html>";
                TextWriter htmlFile = new StreamWriter(args[1]);
                htmlFile.Write(htmlout);
                htmlFile.Close();
                
            }
        }
    }
}

Jul 9, 2013 at 7:05 PM
Hi, Great work!

How would I go about bundling more than 1 transaction to a single function group / interchange?
Oct 28, 2013 at 4:25 PM
dstrubhar wrote:
I might need to expose a new method for you, but I agree your second method sounds easier.  This sounds like a good documentation page once I add the support for it. THE SE segment will automatically recalculate itself once you've added the new segments when you run the SerializeToX12 method on the Interchange object.  I'll follow up tomorrow with some code snippets.
Any update on exposing the new method or the documentation referred to? I am trying to combine/bundle a few hundred x12 claim files that have a single claim in them and submit to a health plan as a single file weekly.
Sep 26, 2014 at 9:29 PM
I can't get this to work at all. I have the same situation as you, multiple claims each in their own ISA/GS/ST wrapper but the end result I get from this a ISA, GS, and ST segments with nothing but three HL records in the middle. None of my data is included at all. What am I missing?
Sep 26, 2014 at 9:49 PM
Edited Sep 26, 2014 at 9:50 PM
My input file contains two claims - this is what my output from the above is:
ISA00 00 30591943502 ZZ00000 120222 {005010000000011T:~GSHC010003000 201202220223171143001X005010X223A2~ST837102221001005010X223A2~HL1201~HL21220~HL31220~HL41220~HL51220~SE7102221001~GE1*171143001~IEA1000000001~