/// <summary> /// Creates a <see cref="SegmentError"/> object with the segment metadata provided /// </summary> /// <param name="segmentInfo">Segment metadata object</param> /// <param name="syntaxErrorCode">Error code detailing syntax issue</param> /// <returns>Error object created with the metadata</returns> protected SegmentError CreateSegmentError(SegmentInformation segmentInfo, string syntaxErrorCode) { return(new SegmentError { SegmentIdCode = segmentInfo.SegmentId, SegmentPosition = segmentInfo.SegmentPosition, LoopIdentifierCode = segmentInfo.LoopId, ImplementationSegmentSyntaxErrorCode = syntaxErrorCode }); }
/// <summary> /// Attempts to parse a segment and validates it againsts its specification. A collection of <see cref="SegmentError"/> objects is returned /// </summary> /// <param name="segmentInfo">Segment metadata to be validated</param> /// <returns>Collection of Segment errors, if found</returns> protected virtual IList <SegmentError> ValidateSegmentAgainstSpec(SegmentInformation segmentInfo) { var errors = new List <SegmentError>(); if (segmentInfo.Spec != null) { for (int iSpec = 0; iSpec < segmentInfo.Spec.Elements.Count; iSpec++) { var elementSpec = segmentInfo.Spec.Elements[iSpec]; if (iSpec < segmentInfo.Elements.Length - 1) { string element = segmentInfo.Elements[iSpec + 1]; if (string.IsNullOrEmpty(element) && elementSpec.Required) { errors.Add(this.CreateDataElementError(segmentInfo, iSpec + 1, "1", null)); } else if (element.Length < elementSpec.MinLength && (elementSpec.Required || element.Length > 0)) { errors.Add(this.CreateDataElementError(segmentInfo, iSpec + 1, "4", element)); } else if (element.Length > elementSpec.MaxLength && elementSpec.MaxLength > 0) { errors.Add(this.CreateDataElementError(segmentInfo, iSpec + 1, "5", element)); } } else { if (elementSpec.Required) { errors.Add(this.CreateDataElementError(segmentInfo, iSpec + 1, "1", null)); } } } if (segmentInfo.Elements.Length - 1 > segmentInfo.Spec.Elements.Count) { int elementPosition = segmentInfo.Spec.Elements.Count + 1; errors.Add(this.CreateDataElementError(segmentInfo, elementPosition, "3", segmentInfo.Elements[elementPosition])); } } return(errors); }
/// <summary> /// Creates a <see cref="SegmentError"/> object with the segment metadata provided /// </summary> /// <param name="segmentInfo">Segment metadata object</param> /// <param name="elementPositionInSegment">Element index position in segment</param> /// <param name="syntaxErrorCode">Error code detailing syntax issue</param> /// <param name="element">Element data</param> /// <returns>Error object created with the metadata</returns> protected SegmentError CreateDataElementError(SegmentInformation segmentInfo, int elementPositionInSegment, string syntaxErrorCode, string element) { var error = new SegmentError { SegmentIdCode = segmentInfo.SegmentId, SegmentPosition = segmentInfo.SegmentPosition, LoopIdentifierCode = segmentInfo.LoopId, ImplementationSegmentSyntaxErrorCode = "8" }; error.ElementNotes.Add(new DataElementNote { PositionInSegment = new PositionInSegment { ElementPositionInSegment = elementPositionInSegment }, SyntaxErrorCode = syntaxErrorCode, CopyOfBadElement = element }); return(error); }
/// <summary> /// Builds a <see cref="TransactionSetResponse"/> object from the provided stream /// </summary> /// <param name="reader">Stream to pull transaction set data from</param> /// <param name="functionalCode">Function group code to associate with transaction set</param> /// <param name="versionIdentifierCode">Specification version code</param> /// <param name="transaction">Transaction segment string to be parsed</param> /// <returns>Transaction set response whether the set is valid, and the segment data</returns> protected virtual TransactionSetResponse AcknowledgeTransaction(X12StreamReader reader, string functionalCode, string versionIdentifierCode, string transaction) { string[] transactionElements = reader.SplitSegment(transaction); var response = new TransactionSetResponse { TransactionSetIdentifierCode = transactionElements[1], TransactionSetControlNumber = transactionElements[2] }; if (transactionElements.Length >= 4) { response.ImplementationConventionReference = transactionElements[3]; } TransactionSpecification transactionSpec = this.specFinder.FindTransactionSpec( functionalCode, versionIdentifierCode, response.TransactionSetIdentifierCode); if (transactionSpec == null) { response.SyntaxErrorCodes.Add("1"); response.AcknowledgmentCode = AcknowledgmentCode.R_Rejected; return(response); } var containers = new Stack <ContainerInformation>(); var transactionContainer = new ContainerInformation { Spec = transactionSpec }; containers.Push(transactionContainer); var segmentInfos = new List <SegmentInformation>(); string[] segments = transaction.Split(new[] { reader.Delimiters.SegmentTerminator }, System.StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < segments.Length; i++) { string[] elements = segments[i].Split(reader.Delimiters.ElementSeparator); var segmentInfo = new SegmentInformation { SegmentId = elements[0], SegmentPosition = i + 1, Elements = elements }; segmentInfo.Spec = this.specFinder.FindSegmentSpec(versionIdentifierCode, segmentInfo.SegmentId); segmentInfos.Add(segmentInfo); ContainerInformation currentContainer = containers.Peek(); switch (segmentInfo.SegmentId) { case "ST": case "SE": segmentInfo.LoopId = string.Empty; transactionContainer.Segments.Add(segmentInfo); break; case "HL": string hloopNumber = segmentInfo.Elements[1]; string hloopParentNumber = segmentInfo.Elements[2]; string hloopLevelCode = segmentInfo.Elements[3]; HierarchicalLoopSpecification hloopSpec = transactionSpec.HierarchicalLoopSpecifications.FirstOrDefault(hls => hls.LevelCode == hloopLevelCode); if (hloopSpec != null) { while (!(containers.Peek().Spec is TransactionSpecification)) { if (containers.Peek().HLoopNumber == hloopParentNumber) { break; } containers.Pop(); } segmentInfo.LoopId = hloopSpec.LoopId; var hloopContainer = new ContainerInformation { Spec = hloopSpec, HLoopNumber = hloopNumber }; hloopContainer.Segments.Add(segmentInfo); containers.Peek().Containers.Add(hloopContainer); containers.Push(hloopContainer); } else { response.SegmentErrors.Add(this.CreateDataElementError(segmentInfo, 3, "I6", hloopLevelCode)); response.AcknowledgmentCode = AcknowledgmentCode.X_Rejected_ContentCouldNotBeAnalyzed; } break; default: bool matchFound = false; do { var matchingLoopSpecs = currentContainer.Spec.LoopSpecifications.Where(ls => ls.StartingSegment.SegmentId == segmentInfo.SegmentId).ToList(); if (matchingLoopSpecs.Count > 0) { IContainerSpecification matchingLoopSpec; if (matchingLoopSpecs.Count == 1) { matchingLoopSpec = matchingLoopSpecs.First(); } else { string entityCode = elements[1]; matchingLoopSpec = matchingLoopSpecs.FirstOrDefault(ls => ls.StartingSegment.EntityIdentifiers.Exists(ei => ei.Code == entityCode)); } if (matchingLoopSpec != null) { segmentInfo.LoopId = matchingLoopSpec.LoopId; var loopContainer = new ContainerInformation { Spec = matchingLoopSpec }; loopContainer.Segments.Add(segmentInfo); containers.Peek().Containers.Add(loopContainer); containers.Push(loopContainer); matchFound = true; } else { response.SegmentErrors.Add(this.CreateSegmentError(segmentInfo, "6")); response.AcknowledgmentCode = AcknowledgmentCode.X_Rejected_ContentCouldNotBeAnalyzed; return(response); } } else if (currentContainer.Spec.SegmentSpecifications.Exists(ss => ss.SegmentId == segmentInfo.SegmentId)) { segmentInfo.LoopId = currentContainer.Spec.LoopId; currentContainer.Segments.Add(segmentInfo); matchFound = true; } else { if (currentContainer.Spec is TransactionSpecification) { response.SegmentErrors.Add(this.CreateSegmentError(segmentInfo, "2")); response.AcknowledgmentCode = AcknowledgmentCode.X_Rejected_ContentCouldNotBeAnalyzed; return(response); } containers.Pop(); currentContainer = containers.Peek(); } }while (!matchFound); break; } response.SegmentErrors.AddRange(this.ValidateSegmentAgainstSpec(segmentInfo)); } response.SegmentErrors.AddRange(this.ValidateContainerAgainstSpec(transactionContainer)); var trailerSegment = segmentInfos.FirstOrDefault(si => si.SegmentId == "SE"); if (trailerSegment == null) { response.SyntaxErrorCodes.Add("2"); } else { if (trailerSegment.Elements.Length <= 2 || trailerSegment.Elements[2] != response.TransactionSetControlNumber) { response.SyntaxErrorCodes.Add("3"); } if (trailerSegment.Elements.Length >= 2) { int segmentCount; int.TryParse(trailerSegment.Elements[1], out segmentCount); if (segmentCount != segmentInfos.Count) { response.SyntaxErrorCodes.Add("4"); } } else { response.SyntaxErrorCodes.Add("4"); } } if (response.SegmentErrors.Count > 0 || response.SyntaxErrorCodes.Count > 0) { if (response.SegmentErrors.Count > 0) { response.SyntaxErrorCodes.Add("5"); } if (response.AcknowledgmentCode == AcknowledgmentCode.A_Accepted) { response.AcknowledgmentCode = AcknowledgmentCode.E_Accepted_ButErrorsWereNoted; } } return(response); }