///<summary>Updates one HL7Def in the database.</summary> public static void Update(HL7Def hL7Def) { string command = "UPDATE hl7def SET " + "Description = '" + POut.String(hL7Def.Description) + "', " + "ModeTx = " + POut.Int((int)hL7Def.ModeTx) + ", " + "IncomingFolder = '" + POut.String(hL7Def.IncomingFolder) + "', " + "OutgoingFolder = '" + POut.String(hL7Def.OutgoingFolder) + "', " + "IncomingPort = '" + POut.String(hL7Def.IncomingPort) + "', " + "OutgoingIpPort = '" + POut.String(hL7Def.OutgoingIpPort) + "', " + "FieldSeparator = '" + POut.String(hL7Def.FieldSeparator) + "', " + "ComponentSeparator = '" + POut.String(hL7Def.ComponentSeparator) + "', " + "SubcomponentSeparator= '" + POut.String(hL7Def.SubcomponentSeparator) + "', " + "RepetitionSeparator = '" + POut.String(hL7Def.RepetitionSeparator) + "', " + "EscapeCharacter = '" + POut.String(hL7Def.EscapeCharacter) + "', " + "IsInternal = " + POut.Bool(hL7Def.IsInternal) + ", " + "InternalType = '" + POut.String(hL7Def.InternalType) + "', " + "InternalTypeVersion = '" + POut.String(hL7Def.InternalTypeVersion) + "', " + "IsEnabled = " + POut.Bool(hL7Def.IsEnabled) + ", " + "Note = " + DbHelper.ParamChar + "paramNote, " + "HL7Server = '" + POut.String(hL7Def.HL7Server) + "', " + "HL7ServiceName = '" + POut.String(hL7Def.HL7ServiceName) + "', " + "ShowDemographics = " + POut.Int((int)hL7Def.ShowDemographics) + ", " + "ShowAppts = " + POut.Bool(hL7Def.ShowAppts) + ", " + "ShowAccount = " + POut.Bool(hL7Def.ShowAccount) + " " + "WHERE HL7DefNum = " + POut.Long(hL7Def.HL7DefNum); if (hL7Def.Note == null) { hL7Def.Note = ""; } OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, hL7Def.Note); Db.NonQ(command, paramNote); }
///<summary>Converts a DataTable to a list of objects.</summary> public static List <HL7Def> TableToList(DataTable table) { List <HL7Def> retVal = new List <HL7Def>(); HL7Def hL7Def; for (int i = 0; i < table.Rows.Count; i++) { hL7Def = new HL7Def(); hL7Def.HL7DefNum = PIn.Long(table.Rows[i]["HL7DefNum"].ToString()); hL7Def.Description = PIn.String(table.Rows[i]["Description"].ToString()); hL7Def.ModeTx = (ModeTxHL7)PIn.Int(table.Rows[i]["ModeTx"].ToString()); hL7Def.IncomingFolder = PIn.String(table.Rows[i]["IncomingFolder"].ToString()); hL7Def.OutgoingFolder = PIn.String(table.Rows[i]["OutgoingFolder"].ToString()); hL7Def.IncomingPort = PIn.String(table.Rows[i]["IncomingPort"].ToString()); hL7Def.OutgoingIpPort = PIn.String(table.Rows[i]["OutgoingIpPort"].ToString()); hL7Def.FieldSeparator = PIn.String(table.Rows[i]["FieldSeparator"].ToString()); hL7Def.ComponentSeparator = PIn.String(table.Rows[i]["ComponentSeparator"].ToString()); hL7Def.SubcomponentSeparator = PIn.String(table.Rows[i]["SubcomponentSeparator"].ToString()); hL7Def.RepetitionSeparator = PIn.String(table.Rows[i]["RepetitionSeparator"].ToString()); hL7Def.EscapeCharacter = PIn.String(table.Rows[i]["EscapeCharacter"].ToString()); hL7Def.IsInternal = PIn.Bool(table.Rows[i]["IsInternal"].ToString()); hL7Def.InternalType = PIn.String(table.Rows[i]["InternalType"].ToString()); hL7Def.InternalTypeVersion = PIn.String(table.Rows[i]["InternalTypeVersion"].ToString()); hL7Def.IsEnabled = PIn.Bool(table.Rows[i]["IsEnabled"].ToString()); hL7Def.Note = PIn.String(table.Rows[i]["Note"].ToString()); hL7Def.HL7Server = PIn.String(table.Rows[i]["HL7Server"].ToString()); hL7Def.HL7ServiceName = PIn.String(table.Rows[i]["HL7ServiceName"].ToString()); hL7Def.ShowDemographics = (HL7ShowDemographics)PIn.Int(table.Rows[i]["ShowDemographics"].ToString()); hL7Def.ShowAppts = PIn.Bool(table.Rows[i]["ShowAppts"].ToString()); hL7Def.ShowAccount = PIn.Bool(table.Rows[i]["ShowAccount"].ToString()); retVal.Add(hL7Def); } return(retVal); }
///<summary>Inserts one HL7Def into the database. Returns the new priKey.</summary> public static long Insert(HL7Def hL7Def) { if (DataConnection.DBtype == DatabaseType.Oracle) { hL7Def.HL7DefNum = DbHelper.GetNextOracleKey("hl7def", "HL7DefNum"); int loopcount = 0; while (loopcount < 100) { try { return(Insert(hL7Def, true)); } catch (Oracle.DataAccess.Client.OracleException ex) { if (ex.Number == 1 && ex.Message.ToLower().Contains("unique constraint") && ex.Message.ToLower().Contains("violated")) { hL7Def.HL7DefNum++; loopcount++; } else { throw ex; } } } throw new ApplicationException("Insert failed. Could not generate primary key."); } else { return(Insert(hL7Def, false)); } }
///<summary>Converts a DataTable to a list of objects.</summary> public static List<HL7Def> TableToList(DataTable table){ List<HL7Def> retVal=new List<HL7Def>(); HL7Def hL7Def; for(int i=0;i<table.Rows.Count;i++) { hL7Def=new HL7Def(); hL7Def.HL7DefNum = PIn.Long (table.Rows[i]["HL7DefNum"].ToString()); hL7Def.Description = PIn.String(table.Rows[i]["Description"].ToString()); hL7Def.ModeTx = (ModeTxHL7)PIn.Int(table.Rows[i]["ModeTx"].ToString()); hL7Def.IncomingFolder = PIn.String(table.Rows[i]["IncomingFolder"].ToString()); hL7Def.OutgoingFolder = PIn.String(table.Rows[i]["OutgoingFolder"].ToString()); hL7Def.IncomingPort = PIn.String(table.Rows[i]["IncomingPort"].ToString()); hL7Def.OutgoingIpPort = PIn.String(table.Rows[i]["OutgoingIpPort"].ToString()); hL7Def.FieldSeparator = PIn.String(table.Rows[i]["FieldSeparator"].ToString()); hL7Def.ComponentSeparator = PIn.String(table.Rows[i]["ComponentSeparator"].ToString()); hL7Def.SubcomponentSeparator= PIn.String(table.Rows[i]["SubcomponentSeparator"].ToString()); hL7Def.RepetitionSeparator = PIn.String(table.Rows[i]["RepetitionSeparator"].ToString()); hL7Def.EscapeCharacter = PIn.String(table.Rows[i]["EscapeCharacter"].ToString()); hL7Def.IsInternal = PIn.Bool (table.Rows[i]["IsInternal"].ToString()); hL7Def.InternalType = PIn.String(table.Rows[i]["InternalType"].ToString()); hL7Def.InternalTypeVersion = PIn.String(table.Rows[i]["InternalTypeVersion"].ToString()); hL7Def.IsEnabled = PIn.Bool (table.Rows[i]["IsEnabled"].ToString()); hL7Def.Note = PIn.String(table.Rows[i]["Note"].ToString()); hL7Def.HL7Server = PIn.String(table.Rows[i]["HL7Server"].ToString()); hL7Def.HL7ServiceName = PIn.String(table.Rows[i]["HL7ServiceName"].ToString()); hL7Def.ShowDemographics = (HL7ShowDemographics)PIn.Int(table.Rows[i]["ShowDemographics"].ToString()); hL7Def.ShowAppts = PIn.Bool (table.Rows[i]["ShowAppts"].ToString()); hL7Def.ShowAccount = PIn.Bool (table.Rows[i]["ShowAccount"].ToString()); retVal.Add(hL7Def); } return retVal; }
private void butCopy_Click(object sender, EventArgs e) { if (grid1.GetSelectedIndex() == -1) { MsgBox.Show(this, "Please select an internal HL7Def from the list on the left first."); return; } HL7Def hl7def = ListInternal[grid1.GetSelectedIndex()].Clone(); hl7def.IsInternal = false; hl7def.IsEnabled = false; long hl7DefNum = HL7Defs.Insert(hl7def); for (int m = 0; m < hl7def.hl7DefMessages.Count; m++) { hl7def.hl7DefMessages[m].HL7DefNum = hl7DefNum; long hl7DefMessageNum = HL7DefMessages.Insert(hl7def.hl7DefMessages[m]); for (int s = 0; s < hl7def.hl7DefMessages[m].hl7DefSegments.Count; s++) { hl7def.hl7DefMessages[m].hl7DefSegments[s].HL7DefMessageNum = hl7DefMessageNum; long hl7DefSegmentNum = HL7DefSegments.Insert(hl7def.hl7DefMessages[m].hl7DefSegments[s]); for (int f = 0; f < hl7def.hl7DefMessages[m].hl7DefSegments[s].hl7DefFields.Count; f++) { hl7def.hl7DefMessages[m].hl7DefSegments[s].hl7DefFields[f].HL7DefSegmentNum = hl7DefSegmentNum; HL7DefFields.Insert(hl7def.hl7DefMessages[m].hl7DefSegments[s].hl7DefFields[f]); } } } DataValid.SetInvalid(InvalidType.HL7Defs); FillGrid2(); grid1.SetSelected(false); }
private void FillForm(){ ProgramProperties.RefreshCache(); PropertyList=ProgramProperties.GetForProgram(ProgramCur.ProgramNum); textProgName.Text=ProgramCur.ProgName; textProgDesc.Text=ProgramCur.ProgDesc; checkEnabled.Checked=ProgramCur.Enabled; if(GetProp("HideChartRxButtons")=="1") { checkHideButChartRx.Checked=true; } else { checkHideButChartRx.Checked=false; } if(GetProp("ProcRequireSignature")=="1") { checkProcRequireSignature.Checked=true; } else { checkProcRequireSignature.Checked=false; } if(GetProp("ProcNotesNoIncomplete")=="1") { checkProcNotesNoIncomplete.Checked=true; } else { checkProcNotesNoIncomplete.Checked=false; } SetModeRadioButtons(GetProp("eClinicalWorksMode")); SetModeVisibilities(); textECWServer.Text=GetProp("eCWServer");//this property will not exist if using Oracle, eCW will never use Oracle if(HL7Defs.IsExistingHL7Enabled()) { HL7Def def=HL7Defs.GetOneDeepEnabled(); textHL7Server.Text=def.HL7Server; textHL7ServiceName.Text=def.HL7ServiceName; textHL7FolderIn.Text=def.OutgoingFolder;//because these are the opposite of the way they are in the HL7Def textHL7FolderOut.Text=def.IncomingFolder; checkQuadAsToothNum.Checked=def.IsQuadAsToothNum; } else { textHL7Server.Text=GetProp("HL7Server");//this property will not exist if using Oracle, eCW will never use Oracle textHL7ServiceName.Text=GetProp("HL7ServiceName");//this property will not exist if using Oracle, eCW will never use Oracle textHL7FolderIn.Text=PrefC.GetString(PrefName.HL7FolderIn); textHL7FolderOut.Text=PrefC.GetString(PrefName.HL7FolderOut); //if a def is enabled, the value associated with the def will override this setting checkQuadAsToothNum.Checked=GetProp("IsQuadAsToothNum")=="1";//this property will not exist if using Oracle, eCW will never use Oracle } textODServer.Text=MiscData.GetODServer(); comboDefaultUserGroup.Items.Clear(); _listUserGroups=UserGroups.GetList(); for(int i=0;i<_listUserGroups.Count;i++) { comboDefaultUserGroup.Items.Add(_listUserGroups[i].Description); if(GetProp("DefaultUserGroup")==_listUserGroups[i].UserGroupNum.ToString()) { comboDefaultUserGroup.SelectedIndex=i; } } checkShowImages.Checked=GetProp("ShowImagesModule")=="1"; checkFeeSchedules.Checked=GetProp("FeeSchedulesSetManually")=="1"; textMedPanelURL.Text=GetProp("MedicalPanelUrl");//this property will not exist if using Oracle, eCW will never use Oracle checkLBSessionId.Checked=GetProp("IsLBSessionIdExcluded")=="1"; }
///<summary>Inserts one HL7Def into the database. Provides option to use the existing priKey.</summary> public static long Insert(HL7Def hL7Def, bool useExistingPK) { if (!useExistingPK && PrefC.RandomKeys) { hL7Def.HL7DefNum = ReplicationServers.GetKey("hl7def", "HL7DefNum"); } string command = "INSERT INTO hl7def ("; if (useExistingPK || PrefC.RandomKeys) { command += "HL7DefNum,"; } command += "Description,ModeTx,IncomingFolder,OutgoingFolder,IncomingPort,OutgoingIpPort,FieldSeparator,ComponentSeparator,SubcomponentSeparator,RepetitionSeparator,EscapeCharacter,IsInternal,InternalType,InternalTypeVersion,IsEnabled,Note,HL7Server,HL7ServiceName,ShowDemographics,ShowAppts,ShowAccount) VALUES("; if (useExistingPK || PrefC.RandomKeys) { command += POut.Long(hL7Def.HL7DefNum) + ","; } command += "'" + POut.String(hL7Def.Description) + "'," + POut.Int((int)hL7Def.ModeTx) + "," + "'" + POut.String(hL7Def.IncomingFolder) + "'," + "'" + POut.String(hL7Def.OutgoingFolder) + "'," + "'" + POut.String(hL7Def.IncomingPort) + "'," + "'" + POut.String(hL7Def.OutgoingIpPort) + "'," + "'" + POut.String(hL7Def.FieldSeparator) + "'," + "'" + POut.String(hL7Def.ComponentSeparator) + "'," + "'" + POut.String(hL7Def.SubcomponentSeparator) + "'," + "'" + POut.String(hL7Def.RepetitionSeparator) + "'," + "'" + POut.String(hL7Def.EscapeCharacter) + "'," + POut.Bool(hL7Def.IsInternal) + "," + "'" + POut.String(hL7Def.InternalType) + "'," + "'" + POut.String(hL7Def.InternalTypeVersion) + "'," + POut.Bool(hL7Def.IsEnabled) + "," + DbHelper.ParamChar + "paramNote," + "'" + POut.String(hL7Def.HL7Server) + "'," + "'" + POut.String(hL7Def.HL7ServiceName) + "'," + POut.Int((int)hL7Def.ShowDemographics) + "," + POut.Bool(hL7Def.ShowAppts) + "," + POut.Bool(hL7Def.ShowAccount) + ")"; if (hL7Def.Note == null) { hL7Def.Note = ""; } OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, hL7Def.Note); if (useExistingPK || PrefC.RandomKeys) { Db.NonQ(command, paramNote); } else { hL7Def.HL7DefNum = Db.NonQ(command, true, paramNote); } return(hL7Def.HL7DefNum); }
///<summary>Converts a DataTable to a list of objects.</summary> public static List <HL7Def> TableToList(DataTable table) { List <HL7Def> retVal = new List <HL7Def>(); HL7Def hL7Def; foreach (DataRow row in table.Rows) { hL7Def = new HL7Def(); hL7Def.HL7DefNum = PIn.Long(row["HL7DefNum"].ToString()); hL7Def.Description = PIn.String(row["Description"].ToString()); hL7Def.ModeTx = (OpenDentBusiness.ModeTxHL7)PIn.Int(row["ModeTx"].ToString()); hL7Def.IncomingFolder = PIn.String(row["IncomingFolder"].ToString()); hL7Def.OutgoingFolder = PIn.String(row["OutgoingFolder"].ToString()); hL7Def.IncomingPort = PIn.String(row["IncomingPort"].ToString()); hL7Def.OutgoingIpPort = PIn.String(row["OutgoingIpPort"].ToString()); hL7Def.FieldSeparator = PIn.String(row["FieldSeparator"].ToString()); hL7Def.ComponentSeparator = PIn.String(row["ComponentSeparator"].ToString()); hL7Def.SubcomponentSeparator = PIn.String(row["SubcomponentSeparator"].ToString()); hL7Def.RepetitionSeparator = PIn.String(row["RepetitionSeparator"].ToString()); hL7Def.EscapeCharacter = PIn.String(row["EscapeCharacter"].ToString()); hL7Def.IsInternal = PIn.Bool(row["IsInternal"].ToString()); string internalType = row["InternalType"].ToString(); if (internalType == "") { hL7Def.InternalType = (OpenDentBusiness.HL7InternalType) 0; } else { try{ hL7Def.InternalType = (OpenDentBusiness.HL7InternalType)Enum.Parse(typeof(OpenDentBusiness.HL7InternalType), internalType); } catch { hL7Def.InternalType = (OpenDentBusiness.HL7InternalType) 0; } } hL7Def.InternalTypeVersion = PIn.String(row["InternalTypeVersion"].ToString()); hL7Def.IsEnabled = PIn.Bool(row["IsEnabled"].ToString()); hL7Def.Note = PIn.String(row["Note"].ToString()); hL7Def.HL7Server = PIn.String(row["HL7Server"].ToString()); hL7Def.HL7ServiceName = PIn.String(row["HL7ServiceName"].ToString()); hL7Def.ShowDemographics = (OpenDentBusiness.HL7ShowDemographics)PIn.Int(row["ShowDemographics"].ToString()); hL7Def.ShowAppts = PIn.Bool(row["ShowAppts"].ToString()); hL7Def.ShowAccount = PIn.Bool(row["ShowAccount"].ToString()); hL7Def.IsQuadAsToothNum = PIn.Bool(row["IsQuadAsToothNum"].ToString()); hL7Def.LabResultImageCat = PIn.Long(row["LabResultImageCat"].ToString()); hL7Def.SftpUsername = PIn.String(row["SftpUsername"].ToString()); hL7Def.SftpPassword = PIn.String(row["SftpPassword"].ToString()); hL7Def.SftpInSocket = PIn.String(row["SftpInSocket"].ToString()); hL7Def.HasLongDCodes = PIn.Bool(row["HasLongDCodes"].ToString()); hL7Def.IsProcApptEnforced = PIn.Bool(row["IsProcApptEnforced"].ToString()); retVal.Add(hL7Def); } return(retVal); }
///<summary>Inserts one HL7Def into the database. Returns the new priKey. Doesn't use the cache.</summary> public static long InsertNoCache(HL7Def hL7Def) { if (DataConnection.DBtype == DatabaseType.MySql) { return(InsertNoCache(hL7Def, false)); } else { if (DataConnection.DBtype == DatabaseType.Oracle) { hL7Def.HL7DefNum = DbHelper.GetNextOracleKey("hl7def", "HL7DefNum"); //Cacheless method } return(InsertNoCache(hL7Def, true)); } }
///<summary>Returns null if no HL7 def is enabled or no ACK is defined in the enabled def.</summary> public static MessageHL7 GenerateACK(string controlId, bool isAck, string ackEvent) { MessageHL7 messageHL7 = new MessageHL7(MessageTypeHL7.ACK); messageHL7.ControlId = controlId; messageHL7.AckEvent = ackEvent; HL7Def hl7Def = HL7Defs.GetOneDeepEnabled(); if (hl7Def == null) { return(null); //no def enabled, return null } //find an ACK message in the def HL7DefMessage hl7DefMessage = null; for (int i = 0; i < hl7Def.hl7DefMessages.Count; i++) { if (hl7Def.hl7DefMessages[i].MessageType == MessageTypeHL7.ACK && hl7Def.hl7DefMessages[i].InOrOut == InOutHL7.Outgoing) { hl7DefMessage = hl7Def.hl7DefMessages[i]; break; } } if (hl7DefMessage == null) //ACK message type is not defined so do nothing and return { return(null); } //go through each segment in the def for (int s = 0; s < hl7DefMessage.hl7DefSegments.Count; s++) { SegmentHL7 seg = new SegmentHL7(hl7DefMessage.hl7DefSegments[s].SegmentName); seg.SetField(0, hl7DefMessage.hl7DefSegments[s].SegmentName.ToString()); for (int f = 0; f < hl7DefMessage.hl7DefSegments[s].hl7DefFields.Count; f++) { string fieldName = hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].FieldName; if (fieldName == "") //If fixed text instead of field name just add text to segment { seg.SetField(hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].OrdinalPos, hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].FixedText); } else { seg.SetField(hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].OrdinalPos, FieldConstructor.GenerateACK(hl7Def, fieldName, controlId, isAck, ackEvent)); } } messageHL7.Segments.Add(seg); } return(messageHL7); }
///<summary>Converts a DataTable to a list of objects.</summary> public static List<HL7Def> TableToList(DataTable table){ List<HL7Def> retVal=new List<HL7Def>(); HL7Def hL7Def; for(int i=0;i<table.Rows.Count;i++) { hL7Def=new HL7Def(); hL7Def.HL7DefNum = PIn.Long (table.Rows[i]["HL7DefNum"].ToString()); hL7Def.Description = PIn.String(table.Rows[i]["Description"].ToString()); hL7Def.ModeTx = (OpenDentBusiness.ModeTxHL7)PIn.Int(table.Rows[i]["ModeTx"].ToString()); hL7Def.IncomingFolder = PIn.String(table.Rows[i]["IncomingFolder"].ToString()); hL7Def.OutgoingFolder = PIn.String(table.Rows[i]["OutgoingFolder"].ToString()); hL7Def.IncomingPort = PIn.String(table.Rows[i]["IncomingPort"].ToString()); hL7Def.OutgoingIpPort = PIn.String(table.Rows[i]["OutgoingIpPort"].ToString()); hL7Def.FieldSeparator = PIn.String(table.Rows[i]["FieldSeparator"].ToString()); hL7Def.ComponentSeparator = PIn.String(table.Rows[i]["ComponentSeparator"].ToString()); hL7Def.SubcomponentSeparator= PIn.String(table.Rows[i]["SubcomponentSeparator"].ToString()); hL7Def.RepetitionSeparator = PIn.String(table.Rows[i]["RepetitionSeparator"].ToString()); hL7Def.EscapeCharacter = PIn.String(table.Rows[i]["EscapeCharacter"].ToString()); hL7Def.IsInternal = PIn.Bool (table.Rows[i]["IsInternal"].ToString()); string internalType=table.Rows[i]["InternalType"].ToString(); if(internalType==""){ hL7Def.InternalType =(HL7InternalType)0; } else try{ hL7Def.InternalType =(HL7InternalType)Enum.Parse(typeof(HL7InternalType),internalType); } catch{ hL7Def.InternalType =(HL7InternalType)0; } hL7Def.InternalTypeVersion = PIn.String(table.Rows[i]["InternalTypeVersion"].ToString()); hL7Def.IsEnabled = PIn.Bool (table.Rows[i]["IsEnabled"].ToString()); hL7Def.Note = PIn.String(table.Rows[i]["Note"].ToString()); hL7Def.HL7Server = PIn.String(table.Rows[i]["HL7Server"].ToString()); hL7Def.HL7ServiceName = PIn.String(table.Rows[i]["HL7ServiceName"].ToString()); hL7Def.ShowDemographics = (OpenDentBusiness.HL7ShowDemographics)PIn.Int(table.Rows[i]["ShowDemographics"].ToString()); hL7Def.ShowAppts = PIn.Bool (table.Rows[i]["ShowAppts"].ToString()); hL7Def.ShowAccount = PIn.Bool (table.Rows[i]["ShowAccount"].ToString()); hL7Def.IsQuadAsToothNum = PIn.Bool (table.Rows[i]["IsQuadAsToothNum"].ToString()); hL7Def.LabResultImageCat = PIn.Long (table.Rows[i]["LabResultImageCat"].ToString()); hL7Def.SftpUsername = PIn.String(table.Rows[i]["SftpUsername"].ToString()); hL7Def.SftpPassword = PIn.String(table.Rows[i]["SftpPassword"].ToString()); hL7Def.SftpInSocket = PIn.String(table.Rows[i]["SftpInSocket"].ToString()); retVal.Add(hL7Def); } return retVal; }
///<summary>Only use this constructor when generating a message instead of parsing a message.</summary> internal MessageHL7(MessageTypeHL7 msgType) { Segments = new List <SegmentHL7>(); MsgType = msgType; ControlId = ""; AckCode = ""; AckEvent = ""; Delimiters = new char[] { '^', '~', '\\', '&' }; //this is the default delimiters //if def is enabled, set delimiters to user defined values HL7Def enabledDef = HL7Defs.GetOneDeepEnabled(); if (enabledDef != null) { Delimiters = new char[4]; Delimiters[0] = enabledDef.ComponentSeparator.ToCharArray()[0]; //the enabled def is forced to have a component separator that is a single character Delimiters[1] = enabledDef.RepetitionSeparator.ToCharArray()[0]; //the enabled def is forced to have a repetition separator that is a single character Delimiters[2] = enabledDef.EscapeCharacter.ToCharArray()[0]; //the enabled def is forced to have an escape character that is a single character Delimiters[3] = enabledDef.SubcomponentSeparator.ToCharArray()[0]; //the enabled def is forced to have a subcomponent separator that is a single character } }
private void FillForm() { ProgramProperties.RefreshCache(); PropertyList = ProgramProperties.GetListForProgram(ProgramCur.ProgramNum); textProgName.Text = ProgramCur.ProgName; textProgDesc.Text = ProgramCur.ProgDesc; checkEnabled.Checked = ProgramCur.Enabled; SetModeRadioButtons(GetProp("eClinicalWorksMode")); SetModeVisibilities(); textECWServer.Text = GetProp("eCWServer"); if (HL7Defs.IsExistingHL7Enabled()) { HL7Def def = HL7Defs.GetOneDeepEnabled(); textHL7Server.Text = def.HL7Server; textHL7ServiceName.Text = def.HL7ServiceName; textHL7FolderIn.Text = def.OutgoingFolder; //because these are the opposite of the way they are in the HL7Def textHL7FolderOut.Text = def.IncomingFolder; } else { textHL7Server.Text = GetProp("HL7Server"); textHL7ServiceName.Text = GetProp("HL7ServiceName"); textHL7FolderIn.Text = PrefC.GetString(PrefName.HL7FolderIn); textHL7FolderOut.Text = PrefC.GetString(PrefName.HL7FolderOut); } textODServer.Text = MiscData.GetODServer(); comboDefaultUserGroup.Items.Clear(); for (int i = 0; i < UserGroups.List.Length; i++) { comboDefaultUserGroup.Items.Add(UserGroups.List[i].Description); if (GetProp("DefaultUserGroup") == UserGroups.List[i].UserGroupNum.ToString()) { comboDefaultUserGroup.SelectedIndex = i; } } checkShowImages.Checked = GetProp("ShowImagesModule") == "1"; checkFeeSchedules.Checked = GetProp("FeeSchedulesSetManually") == "1"; textMedPanelURL.Text = GetProp("MedicalPanelUrl"); }
public static void ProcessAck(MessageHL7 msg, bool isVerboseLogging) { IsVerboseLogging = isVerboseLogging; HL7Def def = HL7Defs.GetOneDeepEnabled(); if (def == null) { throw new Exception("Could not process ACK. No HL7 definition is enabled."); } HL7DefMessage hl7defmsg = null; for (int i = 0; i < def.hl7DefMessages.Count; i++) { if (def.hl7DefMessages[i].MessageType == MessageTypeHL7.ACK && def.hl7DefMessages[i].InOrOut == InOutHL7.Incoming) { hl7defmsg = def.hl7DefMessages[i]; break; } } if (hl7defmsg == null) //No incoming ACK defined, do nothing with it { throw new Exception("Could not process HL7 ACK message. No definition for this type of message in the enabled HL7Def."); } for (int i = 0; i < hl7defmsg.hl7DefSegments.Count; i++) { try { SegmentHL7 seg = msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName, !hl7defmsg.hl7DefSegments[i].IsOptional); if (seg != null) //null if segment was not found but is optional { ProcessSeg(null, 0, hl7defmsg.hl7DefSegments[i], seg, msg); } } catch (ApplicationException ex) { //Required segment was missing, or other error. throw new Exception("Could not process HL7 message. " + ex); } } }
///<summary>Inserts one HL7Def into the database. Returns the new priKey.</summary> public static long Insert(HL7Def hL7Def){ if(DataConnection.DBtype==DatabaseType.Oracle) { hL7Def.HL7DefNum=DbHelper.GetNextOracleKey("hl7def","HL7DefNum"); int loopcount=0; while(loopcount<100){ try { return Insert(hL7Def,true); } catch(Oracle.DataAccess.Client.OracleException ex){ if(ex.Number==1 && ex.Message.ToLower().Contains("unique constraint") && ex.Message.ToLower().Contains("violated")){ hL7Def.HL7DefNum++; loopcount++; } else{ throw ex; } } } throw new ApplicationException("Insert failed. Could not generate primary key."); } else { return Insert(hL7Def,false); } }
public static string GenerateACK(HL7Def def, string fieldName, string controlId, bool isAck, string ackEvent) { //big long list of fieldnames that we support switch (fieldName) { case "ackCode": return(gAck(isAck)); case "dateTime.Now": return(gDTM(DateTime.Now, 14)); case "messageControlId": return(controlId); case "messageType": return(gConcat(def.ComponentSeparator, "ACK", ackEvent)); case "separators^~\\&": return(gSep(def)); default: return(""); } }
///<summary>Inserts one HL7Def into the database. Provides option to use the existing priKey. Doesn't use the cache.</summary> public static long InsertNoCache(HL7Def hL7Def, bool useExistingPK) { bool isRandomKeys = Prefs.GetBoolNoCache(PrefName.RandomPrimaryKeys); string command = "INSERT INTO hl7def ("; if (!useExistingPK && isRandomKeys) { hL7Def.HL7DefNum = ReplicationServers.GetKeyNoCache("hl7def", "HL7DefNum"); } if (isRandomKeys || useExistingPK) { command += "HL7DefNum,"; } command += "Description,ModeTx,IncomingFolder,OutgoingFolder,IncomingPort,OutgoingIpPort,FieldSeparator,ComponentSeparator,SubcomponentSeparator,RepetitionSeparator,EscapeCharacter,IsInternal,InternalType,InternalTypeVersion,IsEnabled,Note,HL7Server,HL7ServiceName,ShowDemographics,ShowAppts,ShowAccount,IsQuadAsToothNum,LabResultImageCat,SftpUsername,SftpPassword,SftpInSocket,HasLongDCodes,IsProcApptEnforced) VALUES("; if (isRandomKeys || useExistingPK) { command += POut.Long(hL7Def.HL7DefNum) + ","; } command += "'" + POut.String(hL7Def.Description) + "'," + POut.Int((int)hL7Def.ModeTx) + "," + "'" + POut.String(hL7Def.IncomingFolder) + "'," + "'" + POut.String(hL7Def.OutgoingFolder) + "'," + "'" + POut.String(hL7Def.IncomingPort) + "'," + "'" + POut.String(hL7Def.OutgoingIpPort) + "'," + "'" + POut.String(hL7Def.FieldSeparator) + "'," + "'" + POut.String(hL7Def.ComponentSeparator) + "'," + "'" + POut.String(hL7Def.SubcomponentSeparator) + "'," + "'" + POut.String(hL7Def.RepetitionSeparator) + "'," + "'" + POut.String(hL7Def.EscapeCharacter) + "'," + POut.Bool(hL7Def.IsInternal) + "," + "'" + POut.String(hL7Def.InternalType.ToString()) + "'," + "'" + POut.String(hL7Def.InternalTypeVersion) + "'," + POut.Bool(hL7Def.IsEnabled) + "," + DbHelper.ParamChar + "paramNote," + "'" + POut.String(hL7Def.HL7Server) + "'," + "'" + POut.String(hL7Def.HL7ServiceName) + "'," + POut.Int((int)hL7Def.ShowDemographics) + "," + POut.Bool(hL7Def.ShowAppts) + "," + POut.Bool(hL7Def.ShowAccount) + "," + POut.Bool(hL7Def.IsQuadAsToothNum) + "," + POut.Long(hL7Def.LabResultImageCat) + "," + "'" + POut.String(hL7Def.SftpUsername) + "'," + "'" + POut.String(hL7Def.SftpPassword) + "'," + "'" + POut.String(hL7Def.SftpInSocket) + "'," + POut.Bool(hL7Def.HasLongDCodes) + "," + POut.Bool(hL7Def.IsProcApptEnforced) + ")"; if (hL7Def.Note == null) { hL7Def.Note = ""; } OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, POut.StringParam(hL7Def.Note)); if (useExistingPK || isRandomKeys) { Db.NonQ(command, paramNote); } else { hL7Def.HL7DefNum = Db.NonQ(command, true, "HL7DefNum", "hL7Def", paramNote); } return(hL7Def.HL7DefNum); }
///<summary>Processes the msg and creates the MedLab, MedLabResult, MedLabSpecimen, MedLabFacility, and MedLabAttach objects. ///Stores the msgArchiveFileName for each MedLab object created from the inbound message. ///Each message will result in one MedLab object for each repitition of the ORC/OBR observation group in the message. ///Each of the MedLab objects created will be linked to the msgArchiveFileName supplied. ///Each repetition of the OBX result group will result in a MedLabResult object. ///This returns a list of the MedLab.MedLabNums for all MedLab objects created from this message. ///Use selectedPat to manually specify the patient to attach the objects and embedded files to. Used when a patient could not be located using ///the original message PID segment info and the user has now manually selected a patient. The ZEF segments would not have been processed if a ///patient could not be located and once the user selects the patient we will need to re-process the message to create the embedded PDFs.</summary> public static List<long> Process(MessageHL7 msg,string msgArchiveFileName,bool isVerboseLogging,Patient selectedPat=null) { _isVerboseLogging=isVerboseLogging; _msgArchiveFileName=msgArchiveFileName; _medLabNumList=new List<long>(); _medLabCur=null;//make sure the last medlab object is cleared out _medLabResultCur=null; _patCur=null; HL7Def def=HL7Defs.GetOneDeepEnabled(true); if(def==null) { throw new Exception("Could not process the MedLab HL7 message. No MedLab HL7 definition is enabled."); } _defCur=def; HL7DefMessage hl7defmsg=null; for(int i=0;i<def.hl7DefMessages.Count;i++) { //for now there are only incoming ORU messages supported, so there should only be one defined message type and it should be inbound if(def.hl7DefMessages[i].MessageType==msg.MsgType && def.hl7DefMessages[i].InOrOut==InOutHL7.Incoming) { hl7defmsg=def.hl7DefMessages[i]; break; } } if(hl7defmsg==null) {//No message definition matches this message's MessageType and is Incoming throw new Exception("Could not process the MedLab HL7 message. There is no definition for this type of message in the enabled MedLab HL7Def."); } #region Locate Patient //for MedLab interfaces, we limit the ability to rearrange the message structure, so the PID segment is always in position 1 if(hl7defmsg.hl7DefSegments.Count<2 || msg.Segments.Count<2) { throw new Exception("Could not process the MedLab HL7 message. " +"The message or message definition for this type of message does not contain the correct number of segments."); } HL7DefSegment pidSegDef=hl7defmsg.hl7DefSegments[1]; SegmentHL7 pidSegCur=msg.Segments[1]; if(pidSegDef.SegmentName!=SegmentNameHL7.PID || pidSegCur.Name!=SegmentNameHL7.PID) { throw new Exception("Could not process the MedLab HL7 message. " +"The second segment in the message or message definition is not the PID segment."); } if(selectedPat==null) { //get the patient from the PID segment _patCur=GetPatFromPID(pidSegDef,pidSegCur); } if(_patCur==null) {//if no patient is located using PID segment or if selectedPat is not null, use selectedPat _patCur=selectedPat;//selectedPat could be null as well, but null _patCur is handled } #endregion Locate Patient #region Validate Message Structure if(hl7defmsg.hl7DefSegments.Count<1 || msg.Segments.Count<1) { throw new Exception("Could not process the MedLab HL7 message. " +"The message or message definition for this type of message does not contain the correct number of segments."); } SegmentHL7 mshSegCur=msg.Segments[0]; if(hl7defmsg.hl7DefSegments[0].SegmentName!=SegmentNameHL7.MSH || mshSegCur.Name!=SegmentNameHL7.MSH) { throw new Exception("Could not process the MedLab HL7 message. " +"The first segment in the message or the message definition is not the MSH segment."); } SegmentHL7 nk1SegCur=null; List<SegmentHL7> listNteSegs=new List<SegmentHL7>(); List<SegmentHL7> listZpsSegs=new List<SegmentHL7>(); int indexFirstOrc=0; int indexFirstZps=0; //the third item could be the optional NK1 segment, followed by an optional and repeatable NTE segment, so the first ORC segment //could be anywhere after the PID segment in position 2. This will tell us where the first ORC (order) repeatable group begins. for(int i=2;i<msg.Segments.Count;i++) { if(indexFirstZps>0 && msg.Segments[i].Name!=SegmentNameHL7.ZPS) { throw new Exception("Could not process the MedLab HL7 message. There is a "+msg.Segments[i].Name.ToString() +" segment after a ZPS segment. Incorrect message structure."); } if(msg.Segments[i].Name==SegmentNameHL7.NK1) { //the NK1 segment must come after the PID segment if(msg.Segments[i-1].Name!=SegmentNameHL7.PID) { throw new Exception("Could not process the MedLab HL7 message. The NK1 segment was in the wrong position. Incorrect message structure."); } nk1SegCur=msg.Segments[i]; continue; } if(indexFirstOrc==0 && msg.Segments[i].Name==SegmentNameHL7.NTE) {//if we find an ORC segment, the NTEs that follow won't be at the PID level //the PID level NTE segments can follow after the PID segment, the optional NK1 segment, or other repetitions of the NTE segment if(msg.Segments[i-1].Name!=SegmentNameHL7.PID && msg.Segments[i-1].Name!=SegmentNameHL7.NK1 && msg.Segments[i-1].Name!=SegmentNameHL7.NTE) { throw new Exception("Could not process the MedLab HL7 message. Found a NTE segment before an ORC segment but after a " +msg.Segments[i-1].Name.ToString()+" segment. Incorrect message structure."); } listNteSegs.Add(msg.Segments[i]); continue; } if(msg.Segments[i].Name==SegmentNameHL7.ZPS) { if(indexFirstZps==0) {//this is the first ZPS segment we've encountered, set the index indexFirstZps=i; } listZpsSegs.Add(msg.Segments[i]); continue; } if(indexFirstOrc==0 && msg.Segments[i].Name==SegmentNameHL7.ORC) {//this is the first ORC segment we've encountered, set the index indexFirstOrc=i; } } #endregion Validate Message Structure #region Process Order Observations //We can process the ZPS segments before any of the other segments and then link each of the //MedLab objects created to each of the MedLabFacilities referenced by the ZPSs ProcessSeg(hl7defmsg,listZpsSegs,msg); List<SegmentHL7> listRepeatSegs=new List<SegmentHL7>(); _isFirstObr=true; for(int i=indexFirstOrc;i<indexFirstZps;i++) { //for every repetition of the order observation, process the MSH, PID, NK1 and PID level NTE segments for a new MedLab object if(msg.Segments[i].Name==SegmentNameHL7.ORC) { ProcessSeg(hl7defmsg,new List<SegmentHL7> { mshSegCur },msg);//instatiates a new MedLab object _medLabCur and inserts to get PK ProcessSeg(hl7defmsg,new List<SegmentHL7> { pidSegCur },msg); //ProcessSeg(hl7defmsg,new List<SegmentHL7> { nk1SegCur },msg); if(listNteSegs.Count>0) { ProcessSeg(hl7defmsg,listNteSegs,msg,SegmentNameHL7.PID); } listRepeatSegs=new List<SegmentHL7>(); } if(msg.Segments[i].Name==SegmentNameHL7.NTE || msg.Segments[i].Name==SegmentNameHL7.ZEF || msg.Segments[i].Name==SegmentNameHL7.SPM) { SegmentNameHL7 prevSegName=msg.Segments[i-1].Name; listRepeatSegs.Add(msg.Segments[i]); for(int j=i+1;j<indexFirstZps;j++) { if(msg.Segments[j].Name!=msg.Segments[i].Name) { i=j-1; break; } listRepeatSegs.Add(msg.Segments[j]); } ProcessSeg(hl7defmsg,listRepeatSegs,msg,prevSegName); listRepeatSegs=new List<SegmentHL7>();//clear out list for next repeatable segment continue; } //if the segment is an OBX, ProcessOBX will instantiate a new MedLabResult object, one for each repetition of the OBX, ZEF, NTE group ProcessSeg(hl7defmsg,new List<SegmentHL7> { msg.Segments[i] },msg); } MedLabs.Update(_medLabCur); MedLabResults.Update(_medLabResultCur); #endregion Process Order Observations return _medLabNumList; }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null //HL7Def def=HL7Defs.GetInternalFromDb("eCWStandalone"); if(def==null) {//wasn't in the database def=new HL7Def(); def.IsNew=true; def.Description="eCW Standalone"; def.ModeTx=ModeTxHL7.File; def.IncomingFolder=""; def.OutgoingFolder=""; def.IncomingPort=""; def.OutgoingIpPort=""; def.SftpInSocket=""; def.SftpUsername=""; def.SftpPassword=""; def.FieldSeparator="|"; def.ComponentSeparator="^"; def.SubcomponentSeparator="&"; def.RepetitionSeparator="~"; def.EscapeCharacter=@"\"; def.IsInternal=true; def.InternalType=HL7InternalType.eCWStandalone; def.InternalTypeVersion=Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled=false; def.Note=""; def.ShowDemographics=HL7ShowDemographics.ChangeAndAdd; def.ShowAccount=true; def.ShowAppts=true; def.IsQuadAsToothNum=false; } def.hl7DefMessages=new List<HL7DefMessage>();//so that if this is called repeatedly, it won't pile on duplicate messages. //in either case, now get all child objects, which can't be in the database. #region Inbound Messages #region ADT - Patient Demographics (Admits, Discharges, and Transfers) //---------------------------------------------------------------------------------------------------------------------------------- //eCW incoming patient information (ADT). HL7DefMessage msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ADT,MessageStructureHL7.ADT_A01,InOutHL7.Incoming,0); //MSH segment------------------------------------------------------------------ HL7DefSegment seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //PID segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //PID.2, Patient ID seg.AddField(2,"pat.ChartNumber"); //PID.4, Alternate Patient ID, PID.4 is not saved with using standalone integration //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race seg.AddField(10,"pat.Race"); //PID.11, Patient Address seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); //PID.22, Fee Schedule seg.AddField(22,"pat.FeeSched"); //GT1 segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,5,SegmentNameHL7.GT1); //GT1.2, Guarantor Number seg.AddField(2,"guar.ChartNumber"); //GT1.3, Guarantor Name seg.AddField(3,"guar.nameLFM"); //GT1.5, Guarantor Address seg.AddField(5,"guar.addressCityStateZip"); //GT1.6, Guarantor Phone Number - Home seg.AddField(6,"guar.HmPhone"); //GT1.7, Guarantor Phone Number - Business seg.AddField(7,"guar.WkPhone"); //GT1.8, Guarantor Date/Time of Birth seg.AddField(8,"guar.birthdateTime"); //GT1.9, Guarantor Administrative Sex seg.AddField(9,"guar.Gender"); //GT1.12, Guarantor SSN seg.AddField(12,"guar.SSN"); #endregion ADT - Patient Demographics (Admits, Discharges, and Transfers) #endregion Inbound Messages return def; }
///<summary>Updates one HL7Def in the database. Uses an old object to compare to, and only alters changed fields. This prevents collisions and concurrency problems in heavily used tables.</summary> public static void Update(HL7Def hL7Def,HL7Def oldHL7Def){ string command=""; if(hL7Def.Description != oldHL7Def.Description) { if(command!=""){ command+=",";} command+="Description = '"+POut.String(hL7Def.Description)+"'"; } if(hL7Def.ModeTx != oldHL7Def.ModeTx) { if(command!=""){ command+=",";} command+="ModeTx = "+POut.Int ((int)hL7Def.ModeTx)+""; } if(hL7Def.IncomingFolder != oldHL7Def.IncomingFolder) { if(command!=""){ command+=",";} command+="IncomingFolder = '"+POut.String(hL7Def.IncomingFolder)+"'"; } if(hL7Def.OutgoingFolder != oldHL7Def.OutgoingFolder) { if(command!=""){ command+=",";} command+="OutgoingFolder = '"+POut.String(hL7Def.OutgoingFolder)+"'"; } if(hL7Def.IncomingPort != oldHL7Def.IncomingPort) { if(command!=""){ command+=",";} command+="IncomingPort = '"+POut.String(hL7Def.IncomingPort)+"'"; } if(hL7Def.OutgoingIpPort != oldHL7Def.OutgoingIpPort) { if(command!=""){ command+=",";} command+="OutgoingIpPort = '"+POut.String(hL7Def.OutgoingIpPort)+"'"; } if(hL7Def.FieldSeparator != oldHL7Def.FieldSeparator) { if(command!=""){ command+=",";} command+="FieldSeparator = '"+POut.String(hL7Def.FieldSeparator)+"'"; } if(hL7Def.ComponentSeparator != oldHL7Def.ComponentSeparator) { if(command!=""){ command+=",";} command+="ComponentSeparator = '"+POut.String(hL7Def.ComponentSeparator)+"'"; } if(hL7Def.SubcomponentSeparator != oldHL7Def.SubcomponentSeparator) { if(command!=""){ command+=",";} command+="SubcomponentSeparator = '"+POut.String(hL7Def.SubcomponentSeparator)+"'"; } if(hL7Def.RepetitionSeparator != oldHL7Def.RepetitionSeparator) { if(command!=""){ command+=",";} command+="RepetitionSeparator = '"+POut.String(hL7Def.RepetitionSeparator)+"'"; } if(hL7Def.EscapeCharacter != oldHL7Def.EscapeCharacter) { if(command!=""){ command+=",";} command+="EscapeCharacter = '"+POut.String(hL7Def.EscapeCharacter)+"'"; } if(hL7Def.IsInternal != oldHL7Def.IsInternal) { if(command!=""){ command+=",";} command+="IsInternal = "+POut.Bool(hL7Def.IsInternal)+""; } if(hL7Def.InternalType != oldHL7Def.InternalType) { if(command!=""){ command+=",";} command+="InternalType = '"+POut.String(hL7Def.InternalType)+"'"; } if(hL7Def.InternalTypeVersion != oldHL7Def.InternalTypeVersion) { if(command!=""){ command+=",";} command+="InternalTypeVersion = '"+POut.String(hL7Def.InternalTypeVersion)+"'"; } if(hL7Def.IsEnabled != oldHL7Def.IsEnabled) { if(command!=""){ command+=",";} command+="IsEnabled = "+POut.Bool(hL7Def.IsEnabled)+""; } if(hL7Def.Note != oldHL7Def.Note) { if(command!=""){ command+=",";} command+="Note = "+DbHelper.ParamChar+"paramNote"; } if(hL7Def.HL7Server != oldHL7Def.HL7Server) { if(command!=""){ command+=",";} command+="HL7Server = '"+POut.String(hL7Def.HL7Server)+"'"; } if(hL7Def.HL7ServiceName != oldHL7Def.HL7ServiceName) { if(command!=""){ command+=",";} command+="HL7ServiceName = '"+POut.String(hL7Def.HL7ServiceName)+"'"; } if(hL7Def.ShowDemographics != oldHL7Def.ShowDemographics) { if(command!=""){ command+=",";} command+="ShowDemographics = "+POut.Int ((int)hL7Def.ShowDemographics)+""; } if(hL7Def.ShowAppts != oldHL7Def.ShowAppts) { if(command!=""){ command+=",";} command+="ShowAppts = "+POut.Bool(hL7Def.ShowAppts)+""; } if(hL7Def.ShowAccount != oldHL7Def.ShowAccount) { if(command!=""){ command+=",";} command+="ShowAccount = "+POut.Bool(hL7Def.ShowAccount)+""; } if(command==""){ return; } if(hL7Def.Note==null) { hL7Def.Note=""; } OdSqlParameter paramNote=new OdSqlParameter("paramNote",OdDbType.Text,hL7Def.Note); command="UPDATE hl7def SET "+command +" WHERE HL7DefNum = "+POut.Long(hL7Def.HL7DefNum); Db.NonQ(command,paramNote); }
///<summary>Inserts one HL7Def into the database. Provides option to use the existing priKey.</summary> public static long Insert(HL7Def hL7Def,bool useExistingPK){ if(!useExistingPK && PrefC.RandomKeys) { hL7Def.HL7DefNum=ReplicationServers.GetKey("hl7def","HL7DefNum"); } string command="INSERT INTO hl7def ("; if(useExistingPK || PrefC.RandomKeys) { command+="HL7DefNum,"; } command+="Description,ModeTx,IncomingFolder,OutgoingFolder,IncomingPort,OutgoingIpPort,FieldSeparator,ComponentSeparator,SubcomponentSeparator,RepetitionSeparator,EscapeCharacter,IsInternal,InternalType,InternalTypeVersion,IsEnabled,Note,HL7Server,HL7ServiceName,ShowDemographics,ShowAppts,ShowAccount) VALUES("; if(useExistingPK || PrefC.RandomKeys) { command+=POut.Long(hL7Def.HL7DefNum)+","; } command+= "'"+POut.String(hL7Def.Description)+"'," + POut.Int ((int)hL7Def.ModeTx)+"," +"'"+POut.String(hL7Def.IncomingFolder)+"'," +"'"+POut.String(hL7Def.OutgoingFolder)+"'," +"'"+POut.String(hL7Def.IncomingPort)+"'," +"'"+POut.String(hL7Def.OutgoingIpPort)+"'," +"'"+POut.String(hL7Def.FieldSeparator)+"'," +"'"+POut.String(hL7Def.ComponentSeparator)+"'," +"'"+POut.String(hL7Def.SubcomponentSeparator)+"'," +"'"+POut.String(hL7Def.RepetitionSeparator)+"'," +"'"+POut.String(hL7Def.EscapeCharacter)+"'," + POut.Bool (hL7Def.IsInternal)+"," +"'"+POut.String(hL7Def.InternalType)+"'," +"'"+POut.String(hL7Def.InternalTypeVersion)+"'," + POut.Bool (hL7Def.IsEnabled)+"," +DbHelper.ParamChar+"paramNote," +"'"+POut.String(hL7Def.HL7Server)+"'," +"'"+POut.String(hL7Def.HL7ServiceName)+"'," + POut.Int ((int)hL7Def.ShowDemographics)+"," + POut.Bool (hL7Def.ShowAppts)+"," + POut.Bool (hL7Def.ShowAccount)+")"; if(hL7Def.Note==null) { hL7Def.Note=""; } OdSqlParameter paramNote=new OdSqlParameter("paramNote",OdDbType.Text,hL7Def.Note); if(useExistingPK || PrefC.RandomKeys) { Db.NonQ(command,paramNote); } else { hL7Def.HL7DefNum=Db.NonQ(command,true,paramNote); } return hL7Def.HL7DefNum; }
public MessageHL7(string msgtext) { AckCode = ""; ControlId = ""; AckEvent = ""; originalMsgText = msgtext; Segments = new List <SegmentHL7>(); string[] rows = msgtext.Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); //We need to get the separator characters in order to create the field objects. //The separators are part of the MSH segment and we force users to leave them in position 1 for incoming messages. Delimiters = new char[] { '^', '~', '\\', '&' }; //this is the default, but we will get them from the MSH segment of the incoming message in case they are using something unique. //if def is enabled, set delimiters to user defined values HL7Def enabledDef = HL7Defs.GetOneDeepEnabled(); if (enabledDef != null) { for (int i = 0; i < rows.Length; i++) { //we're going to assume that the user has not inserted an escaped '|' before the second field of the message and just split by '|'s without //checking for escaped '|'s. Technically '\|' would be a literal pipe and should not indicate a new field, but we only want to retrieve the //delimiters from MSH.1 and we require field 0 to be MSH and field 1 should be ^~\&. string[] fields = rows[i].Split(new string[] { "|" }, StringSplitOptions.None); if (fields.Length > 1 && fields[0] == "MSH" && fields[1].Length == 4) { //Encoding characters are in the following order: component separator, repetition separator, escape character, subcomponent separator Delimiters = fields[1].ToCharArray(); //we force users to leave the delimiters in position 1 of the MSH segment break; } } } SegmentHL7 segment; for (int i = 0; i < rows.Length; i++) { segment = new SegmentHL7(rows[i], Delimiters); //this creates the field objects. Segments.Add(segment); if (i == 0 && segment.Name == SegmentNameHL7.MSH) { //js 7/3/12 Make this more intelligent because we also now need the suffix string msgtype = segment.GetFieldComponent(8, 0); //We force the user to leave the 'messageType' field in this position, position 8 of the MSH segment string evnttype = segment.GetFieldComponent(8, 1); string msgStructure = segment.GetFieldComponent(8, 2); AckEvent = evnttype; //We will use this when constructing the acknowledgment to echo back to sender the same event type sent to us //If message type or event type are not in this list, they will default to the not supported type and will not be processed try { MsgType = (MessageTypeHL7)Enum.Parse(typeof(MessageTypeHL7), msgtype, true); } catch (Exception ex) { ex.DoNothing(); MsgType = MessageTypeHL7.NotDefined; } try { EventType = (EventTypeHL7)Enum.Parse(typeof(EventTypeHL7), evnttype, true); } catch (Exception ex) { ex.DoNothing(); EventType = EventTypeHL7.NotDefined; } try { MsgStructure = (MessageStructureHL7)Enum.Parse(typeof(MessageStructureHL7), msgStructure, true); } catch (Exception ex) { ex.DoNothing(); MsgStructure = MessageStructureHL7.NotDefined; } } } }
///<summary>Inserts one HL7Def into the database. Returns the new priKey. Doesn't use the cache.</summary> public static long InsertNoCache(HL7Def hL7Def) { return(InsertNoCache(hL7Def, false)); }
///<summary>Sends null values in for objects not required. GenerateField will return an empty string if a field requires an object and that object is null.</summary> public static string GenerateFieldSRR(HL7Def def,string fieldName,Patient pat,Provider prov,Appointment apt,int sequenceNum,EventTypeHL7 eventType,SegmentNameHL7 segName) { return GenerateField(def,fieldName,MessageTypeHL7.SRR,pat,prov,null,null,apt,sequenceNum,eventType,null,null,MessageStructureHL7.SRR_S01,segName); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if(def==null) {//wasn't in the database def=new HL7Def(); def.IsNew=true; def.Description="MedLab HL7 v2.3"; def.ModeTx=ModeTxHL7.Sftp; def.IncomingFolder=""; def.OutgoingFolder=""; def.IncomingPort=""; def.OutgoingIpPort=""; def.SftpInSocket=""; def.SftpUsername=""; def.SftpPassword=""; def.FieldSeparator="|"; def.ComponentSeparator="^"; def.SubcomponentSeparator="&"; def.RepetitionSeparator="~"; def.EscapeCharacter=@"\"; def.IsInternal=true; def.InternalType=HL7InternalType.MedLabv2_3; def.InternalTypeVersion=Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled=false; def.Note=""; def.ShowDemographics=HL7ShowDemographics.ChangeAndAdd;//these last four properties will not be editable for a lab interface type def.ShowAccount=true; def.ShowAppts=true; def.IsQuadAsToothNum=false; } def.hl7DefMessages=new List<HL7DefMessage>(); HL7DefMessage msg=new HL7DefMessage(); HL7DefSegment seg=new HL7DefSegment(); #region Inbound Messages #region ORU - Unsolicited Observation Message def.AddMessage(msg,MessageTypeHL7.ORU,MessageStructureHL7.ORU_R01,InOutHL7.Incoming,0); #region MSH - Message Header msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.2, Sending Application. To identify the LabCorp Lab System sending the results. //Possible values for LabCorp (as of their v10.7 specs): '1100' - LabCorp Lab System, 'DIANON' - DIANON Systems, //'ADL' - Acupath Diagnostic Laboratories, 'EGL' - Esoterix Genetic Laboratories. //For backward compatibility only: 'CMBP', 'LITHOLINK', 'USLABS' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility. Identifies the LabCorp laboratory responsible for the client. //It could be a LabCorp assigned 'Responsible Lab Code' representing the responsible laboratory or it could be a CLIA number. seg.AddField(3,"sendingFacility"); //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); #endregion MSH - Message Header #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.2, External Patient ID. LabCorp defines this as 'client' assigned patient id, just like they do PID.4. //This should be the Open Dental patient number, sent in outbound PID.4 and returned in PID.2. seg.AddField(2,"pat.PatNum"); //PID.3, Lab Assigned Patient ID. LabCorp assigned specimen number. seg.AddField(3,"labPatID"); //PID.4, Alternate Patient ID. LabCorp defines this as a 'client' assigned patient id, just like they do PID.2. //This will be in outbound PID.2, returned in PID.4. seg.AddField(4,"altPatID"); //PID.5, Patient Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth with Age //LabCorp uses this for the birthdate as well as the age in years, months, and days of the patient in the format bday^years^months^days. //All age components are left padded with 0's, the years is padded to 3 chars, the months and days are padded to 2 chars //Example: 19811213^033^02^19 seg.AddField(7,"patBirthdateAge"); //PID.8, Patient Gender //We use this field to assist the user in selecting a patient if one is not found when importing the message, but we don't store it seg.AddField(8,"pat.Gender"); //PID.18.1, Patient Account Number. LabCorp assigned account number. This field is also used to send the Fasting flag in component 7. //Fasting flag values are 'Y', 'N', or blank //Example: AccountNum^^^BillCode^ABNFlag^SpecimenStatus^FastingFlag seg.AddField(18,"accountNum"); //PID.19, Patient SSN Number //We use this field to assist the user in selecting a patient if one is not found when importing the message, but we don't store it seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region NK1 - Next of Kin //This segment is for future use only, nothing is currently imported from this segment seg=new HL7DefSegment(); msg.AddSegment(seg,2,false,true,SegmentNameHL7.NK1); //Fields------------------------------------------------------------------------------------------------------------- //NK1.2, Next of Kin Name //Example: LName^FName^Middle //seg.AddField(2,"nextOfKinName"); //NK1.4, Next of Kin Address //Example: Address^Address2^City^State^Zip //seg.AddField(4,"nextOfKinAddress"); //NK1.5, Next of Kin Phone //seg.AddField(5,"nextOfKinPhone"); seg.hl7DefFields=new List<HL7DefField>(); #endregion NK1 - Next of Kin #region NTE - Notes and Comments seg=new HL7DefSegment(); msg.AddSegment(seg,3,true,true,SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlab.NotePat field seg.AddField(3,"patNote"); #endregion NTE - Notes and Comments #region ORC - Common Order seg=new HL7DefSegment(); msg.AddSegment(seg,4,true,false,SegmentNameHL7.ORC); //Fields------------------------------------------------------------------------------------------------------------- //ORC.2, Unique Foreign Accession or Specimen ID //Must match the value in OBR.2 and is the ID value sent on the specimen container and is unique per patient order, not test order. //ORC.2.2 is the constant value 'LAB' //Example: L2435^LAB seg.AddField(2,"specimenID"); //ORC.3, Filler Accession ID. The LabCorp assigned specimen number. These are reused on a yearly basis, but used with the client //specific specimen ID in ORC.2, these two numbers should uniquely identify a specimen/order. ORC.3.2 is the constant value 'LAB'. //This should match OBR.3. //Example: 08599499950^LAB seg.AddField(3,"specimenIDFiller"); //ORC.12, Ordering Provider, XCN Data Type //ProvID^ProvLName^ProvFName^ProvMiddleI^^^^SourceTable //This field repeats for every ID available for the provider with the SourceTable component identifying the type of ID in each repetition. //SourceTable possible values: 'U' - UPIN, 'P' - Provider Number (Medicaid or Commercial Insurance Provider ID), //'N' - NPI Number (Required for Third Party Billing), 'L' - Local (Physician ID) //Example: A12345^LNAME^FNAME^M^^^^U~23462^LNAME^FNAME^M^^^^L~0123456789^LNAME^FNAME^M^^^^N~1234567890^LNAME^FNAME^M^^^^P seg.AddField(12,"orderingProv"); #endregion ORC - Common Order #region OBR - Observation Request seg=new HL7DefSegment(); msg.AddSegment(seg,5,true,false,SegmentNameHL7.OBR); //Fields------------------------------------------------------------------------------------------------------------- //OBR.2, Unique Foreign Accession or Specimen ID //Must match the value in ORC.2 and is the ID value sent on the specimen container and is unique per patient order, not test order. //OBR.2.2 is the constant value 'LAB'. //Example: L2435^LAB seg.AddField(2,"specimenID"); //OBR.3, Internal Accession ID. The LabCorp assigned specimen number. These are reused on a yearly basis, but used with the client //specific specimen ID in OBR.2, these two numbers should uniquely identify a specimen/order. OBR.3.2 is the constant value 'LAB'. //This should match ORC.3. //Example: 08599499950^LAB seg.AddField(3,"specimenIDFiller"); //OBR.4, Universal Service Identifier, CWE data type //This identifies the observation. This will be the ID and text description of the test, as well as the LOINC code and description. //Example: 006072^RPR^L^20507-0^Reagin Ab^LN seg.AddField(4,"obsTestID"); //OBR.7, Observation/Specimen Collection Date/Time //Format for LabCorp: yyyyMMddHHmm seg.AddField(7,"dateTimeCollected"); //OBR.9, Collection/Urine Volume seg.AddField(9,"totalVolume"); //OBR.11, Action Code //Used to identify the type of result being returned. 'A' - Add on, 'G' - Reflex, Blank for standard results seg.AddField(11,"specimenAction"); //OBR.13, Relevant Clinical Information. Used for informational purposes. seg.AddField(13,"clinicalInfo"); //OBR.14, Date/Time of Specimen Receipt in Lab. //LabCorp format: yyyMMddHHmm seg.AddField(14,"dateTimeEntered"); //OBR.16, Ordering Provider. //ProvID^ProvLName^ProvFName^ProvMiddleI^^^^SourceTable //This field repeats for every ID available for the provider with the SourceTable component identifying the type of ID in each repetition. //SourceTable possible values: 'U' - UPIN, 'P' - Provider Number (Medicaid or Commercial Insurance Provider ID), //'N' - NPI Number (Required for Third Party Billing), 'L' - Local (Physician ID) //Example: A12345^LNAME^FNAME^M^^^^U~23462^LNAME^FNAME^M^^^^L~0123456789^LNAME^FNAME^M^^^^N~1234567890^LNAME^FNAME^M^^^^P seg.AddField(16,"orderingProv"); //OBR.18, Alternate Specimen ID. seg.AddField(18,"specimenIDAlt"); //OBR.22, Date/Time Observation Reported. //LabCorp format: yyyyMMddHHmm seg.AddField(22,"dateTimeReported"); //OBR.24, Producer's Section ID, used by LabCorp to identify the facility responsible for performing the testing. //This will be the footnote ID of the ZPS segment and will be used to attach a MedLab object to a MedLabFacility object. seg.AddField(24,"facilityID"); //OBR.25, Order Result Status. //LabCorp values: 'F' - Final, 'P' - Preliminary, 'X' - Cancelled, 'C' - Corrected seg.AddField(25,"resultStatus"); //OBR.26, Link to Parent Result. //If this is a reflex result, the value from the OBX.3.1 field of the parent result will be here. seg.AddField(26,"parentObsID"); //OBR.29, Link to Parent Order. //If this is a reflex test, the value from the OBR.4.1 field of the parent test will be here. seg.AddField(29,"parentObsTestID"); #endregion OBR - Observation Request #region NTE - Notes and Comments seg=new HL7DefSegment(); msg.AddSegment(seg,6,true,true,SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlab.NoteLab field seg.AddField(3,"labNote"); #endregion NTE - Notes and Comments #region OBX - Observation/Result seg=new HL7DefSegment(); msg.AddSegment(seg,7,true,false,SegmentNameHL7.OBX); //Fields------------------------------------------------------------------------------------------------------------- //OBX.2, Value Type. This field is not stored explicitly, but it is used to determine the value type of the observation. //If this field is 'TX' for text, the value will be >21 chars and will be sent in the attached NTEs. seg.AddField(2,"obsValueType"); //OBX.3, Observation ID. This field has the same structure as the OBR.4. //ID^Text^CodeSystem^AltID^AltIDText^AltIDCodeSystem, the AltID is the LOINC code so the AltIDCodeSystem will be 'LN' //Example: 006072^RPR^L^20507-0^Reagin Ab^LN seg.AddField(3,"obsID"); //OBX.4, Observation Sub ID. This field is used to aid in the identification of results with the same observation ID (OBX.3) within a //given OBR. If OBX.5.3 is 'ORM' (organism) this field will link a result to an organism, whether this is for organism #1, organism #2, //or organism #3. seg.AddField(4,"obsIDSub"); //OBX.5, Observation Value. ObsValue^TypeOfData^DataSubtype^Encoding^Data. LabCorp report will display OBX.5.1 as the result. //For value >21 chars in length: OBX.2 will be 'TX' for text, OBX.5 will be NULL (empty field), and the value will be in attached NTEs. //"TNP" will be reported for Test Not Performed. seg.AddField(5,"obsValue"); //OBX.6, Units. //Identifier^Text^CodeSystem. Id is units of measure abbreviation, text is full text version of units, coding system is 'L' (local id). seg.AddField(6,"obsUnits"); //OBX.7, Reference Range. seg.AddField(7,"obsRefRange"); //OBX.8, Abnormal Flags. For values see enum OpenDentBusiness.AbnormalFlag seg.AddField(8,"obsAbnormalFlag"); //OBX.11, Observation Result Status. //LabCorp values: 'F' - Final, 'P' - Preliminary, 'X' - Cancelled, 'C' - Corrected, 'I' - Incomplete seg.AddField(11,"resultStatus"); //OBX.14, Date/Time of Observation. //LabCorp format yyyyMMddHHmm seg.AddField(14,"dateTimeObs"); //OBX.15, Producer's ID. //For LabCorp this is used to report the facility responsible for performing the testing. This will hold the lab ID that will reference //a ZPS segment with the lab name, address, and director details. Used to link a MedLabResult object to a MedLabFacility object. seg.AddField(15,"facilityID"); #endregion OBX - Observation/Result #region ZEF - Encapsulated Data Format seg=new HL7DefSegment(); msg.AddSegment(seg,8,true,true,SegmentNameHL7.ZEF); //Fields------------------------------------------------------------------------------------------------------------- //ZEF.1, Sequence Number, 1 through 9999 seg.AddField(1,"sequenceNum"); //ZEF.2, Embedded File. //Base64 embedded file, sent in 50k blocks and will be concatenated together to and converted back into seg.AddField(2,"base64File"); #endregion ZEF - Encapsulated Data Format #region NTE - Notes and Comments seg=new HL7DefSegment(); msg.AddSegment(seg,9,true,true,SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlabresult.Note field seg.AddField(3,"obsNote"); #endregion NTE - Notes and Comments #region SPM - Specimen seg=new HL7DefSegment(); msg.AddSegment(seg,10,true,true,SegmentNameHL7.SPM); //Fields------------------------------------------------------------------------------------------------------------- //SPM.2, Specimen ID. //Unique ID of the specimen as sent on the specimen container. Same as the value in ORC.2. seg.AddField(2,"specimenID"); //SPM.4, Specimen Type //SPM.8, Specimen Source Site //SPM.4, Specimen Source Site Modifier //SPM.14, Specimen Description. Text field used to send additional information about the specimen seg.AddField(14,"specimenDescript"); //SPM.17, Date/Time Specimen Collected seg.AddField(17,"dateTimeSpecimen"); #endregion SPM - Specimen #region ZPS - Place of Service seg=new HL7DefSegment(); msg.AddSegment(seg,11,true,false,SegmentNameHL7.ZPS); //Fields------------------------------------------------------------------------------------------------------------- //ZPS.2, Facility Mnemonic. Footnote ID for the lab facility used to reference this segment from OBX.15. seg.AddField(2,"facilityID"); //ZPS.3, Facility Name. seg.AddField(3,"facilityName"); //ZPS.4, Facility Address. //Address^^City^State^Zip seg.AddField(4,"facilityAddress"); //ZPS.5, Facility Phone. (LabCorp document says numberic characters only, so we can assume no dashes or parentheses.) seg.AddField(5,"facilityPhone"); //ZPS.7, Facility Director. //Title^LName^FName^MiddleI seg.AddField(7,"facilityDirector"); #endregion ZPS - Place of Service #endregion ORU - Unsolicited Observation Message #endregion Inbound Messages #region Outbound Messages //Results only interface for now, so no outbound messages yet //In the future, we will implement the orders portion of the interface and have outbound ORM messages #endregion Outbound Messages return def; }
private static string gSep(HL7Def def) { return(def.ComponentSeparator + def.RepetitionSeparator + def.EscapeCharacter + def.SubcomponentSeparator); }
///<summary>apt, guar, proc, prov and pdfDataString can be null and will return an empty string if a field requires that object</summary> public static string GenerateDFT(HL7Def def, string fieldName, Patient pat, Provider prov, Procedure proc, Patient guar, Appointment apt, int sequenceNum, EventTypeHL7 eventType, string pdfDescription, string pdfDataString) { //big long list of fieldnames that we support switch (fieldName) { case "apt.AptNum": if (apt == null) { return(""); } else { return(apt.AptNum.ToString()); } case "dateTime.Now": return(gDTM(DateTime.Now, 14)); case "eventType": return(eventType.ToString()); case "guar.addressCityStateZip": if (guar == null) { return(""); } else { return(gConcat(def.ComponentSeparator, guar.Address, guar.Address2, guar.City, guar.State, guar.Zip)); } case "guar.birthdateTime": if (guar == null) { return(""); } else { return(gDTM(guar.Birthdate, 8)); } case "guar.Gender": if (guar == null) { return(""); } else { return(gIS(guar)); } case "guar.HmPhone": if (guar == null) { return(""); } else { return(gXTN(guar.HmPhone, 10)); } case "guar.nameLFM": if (guar == null) { return(""); } else { return(gConcat(def.ComponentSeparator, guar.LName, guar.FName, guar.MiddleI)); } case "guar.PatNum": if (guar == null) { return(""); } else { return(guar.PatNum.ToString()); } case "guar.SSN": if (guar == null) { return(""); } else { return(guar.SSN); } case "guar.WkPhone": if (guar == null) { return(""); } else { return(gXTN(guar.WkPhone, 10)); } case "messageControlId": return(Guid.NewGuid().ToString("N")); case "messageType": return(gConcat(def.ComponentSeparator, "DFT", eventType.ToString())); case "pat.addressCityStateZip": return(gConcat(def.ComponentSeparator, pat.Address, pat.Address2, pat.City, pat.State, pat.Zip)); case "pat.birthdateTime": return(gDTM(pat.Birthdate, 8)); case "pat.ChartNumber": return(pat.ChartNumber); case "pat.Gender": return(gIS(pat)); case "pat.HmPhone": return(gXTN(pat.HmPhone, 10)); case "pat.nameLFM": return(gConcat(def.ComponentSeparator, pat.LName, pat.FName, pat.MiddleI)); case "pat.PatNum": return(pat.PatNum.ToString()); case "pat.Position": return(gPos(pat)); case "pat.Race": return(gRace(pat)); case "pat.SSN": return(pat.SSN); case "pat.WkPhone": return(gXTN(pat.WkPhone, 10)); case "pdfDescription": return(pdfDescription); case "pdfDataAsBase64": if (pdfDataString == null) { return(""); } else { return(pdfDataString); } case "proc.DiagnosticCode": if (proc == null) { return(""); } if (proc.DiagnosticCode == null) { return(""); } else { return(proc.DiagnosticCode); } case "proc.procDateTime": if (proc == null) { return(""); } else { return(gDTM(proc.ProcDate, 14)); } case "proc.ProcFee": if (proc == null) { return(""); } else { return(proc.ProcFee.ToString("F2")); } case "proc.ProcNum": if (proc == null) { return(""); } else { return(proc.ProcNum.ToString()); } case "proc.toothSurfRange": if (proc == null) { return(""); } else { return(gTreatArea(def.ComponentSeparator, proc)); } case "proccode.ProcCode": if (proc == null) { return(""); } else { return(gProcCode(proc)); } case "prov.provIdNameLFM": if (prov == null) { return(""); } else { return(gConcat(def.ComponentSeparator, prov.EcwID, prov.LName, prov.FName, prov.MI)); } case "separators^~\\&": return(gSep(def)); case "sequenceNum": return(sequenceNum.ToString()); default: return(""); } }
///<summary>Returns true if Update(HL7Def,HL7Def) would make changes to the database. ///Does not make any changes to the database and can be called before remoting role is checked.</summary> public static bool UpdateComparison(HL7Def hL7Def, HL7Def oldHL7Def) { if (hL7Def.Description != oldHL7Def.Description) { return(true); } if (hL7Def.ModeTx != oldHL7Def.ModeTx) { return(true); } if (hL7Def.IncomingFolder != oldHL7Def.IncomingFolder) { return(true); } if (hL7Def.OutgoingFolder != oldHL7Def.OutgoingFolder) { return(true); } if (hL7Def.IncomingPort != oldHL7Def.IncomingPort) { return(true); } if (hL7Def.OutgoingIpPort != oldHL7Def.OutgoingIpPort) { return(true); } if (hL7Def.FieldSeparator != oldHL7Def.FieldSeparator) { return(true); } if (hL7Def.ComponentSeparator != oldHL7Def.ComponentSeparator) { return(true); } if (hL7Def.SubcomponentSeparator != oldHL7Def.SubcomponentSeparator) { return(true); } if (hL7Def.RepetitionSeparator != oldHL7Def.RepetitionSeparator) { return(true); } if (hL7Def.EscapeCharacter != oldHL7Def.EscapeCharacter) { return(true); } if (hL7Def.IsInternal != oldHL7Def.IsInternal) { return(true); } if (hL7Def.InternalType != oldHL7Def.InternalType) { return(true); } if (hL7Def.InternalTypeVersion != oldHL7Def.InternalTypeVersion) { return(true); } if (hL7Def.IsEnabled != oldHL7Def.IsEnabled) { return(true); } if (hL7Def.Note != oldHL7Def.Note) { return(true); } if (hL7Def.HL7Server != oldHL7Def.HL7Server) { return(true); } if (hL7Def.HL7ServiceName != oldHL7Def.HL7ServiceName) { return(true); } if (hL7Def.ShowDemographics != oldHL7Def.ShowDemographics) { return(true); } if (hL7Def.ShowAppts != oldHL7Def.ShowAppts) { return(true); } if (hL7Def.ShowAccount != oldHL7Def.ShowAccount) { return(true); } if (hL7Def.IsQuadAsToothNum != oldHL7Def.IsQuadAsToothNum) { return(true); } if (hL7Def.LabResultImageCat != oldHL7Def.LabResultImageCat) { return(true); } if (hL7Def.SftpUsername != oldHL7Def.SftpUsername) { return(true); } if (hL7Def.SftpPassword != oldHL7Def.SftpPassword) { return(true); } if (hL7Def.SftpInSocket != oldHL7Def.SftpInSocket) { return(true); } if (hL7Def.HasLongDCodes != oldHL7Def.HasLongDCodes) { return(true); } if (hL7Def.IsProcApptEnforced != oldHL7Def.IsProcApptEnforced) { return(true); } return(false); }
///<summary>Updates one HL7Def in the database.</summary> public static void Update(HL7Def hL7Def){ string command="UPDATE hl7def SET " +"Description = '"+POut.String(hL7Def.Description)+"', " +"ModeTx = "+POut.Int ((int)hL7Def.ModeTx)+", " +"IncomingFolder = '"+POut.String(hL7Def.IncomingFolder)+"', " +"OutgoingFolder = '"+POut.String(hL7Def.OutgoingFolder)+"', " +"IncomingPort = '"+POut.String(hL7Def.IncomingPort)+"', " +"OutgoingIpPort = '"+POut.String(hL7Def.OutgoingIpPort)+"', " +"FieldSeparator = '"+POut.String(hL7Def.FieldSeparator)+"', " +"ComponentSeparator = '"+POut.String(hL7Def.ComponentSeparator)+"', " +"SubcomponentSeparator= '"+POut.String(hL7Def.SubcomponentSeparator)+"', " +"RepetitionSeparator = '"+POut.String(hL7Def.RepetitionSeparator)+"', " +"EscapeCharacter = '"+POut.String(hL7Def.EscapeCharacter)+"', " +"IsInternal = "+POut.Bool (hL7Def.IsInternal)+", " +"InternalType = '"+POut.String(hL7Def.InternalType.ToString())+"', " +"InternalTypeVersion = '"+POut.String(hL7Def.InternalTypeVersion)+"', " +"IsEnabled = "+POut.Bool (hL7Def.IsEnabled)+", " +"Note = "+DbHelper.ParamChar+"paramNote, " +"HL7Server = '"+POut.String(hL7Def.HL7Server)+"', " +"HL7ServiceName = '"+POut.String(hL7Def.HL7ServiceName)+"', " +"ShowDemographics = "+POut.Int ((int)hL7Def.ShowDemographics)+", " +"ShowAppts = "+POut.Bool (hL7Def.ShowAppts)+", " +"ShowAccount = "+POut.Bool (hL7Def.ShowAccount)+", " +"IsQuadAsToothNum = "+POut.Bool (hL7Def.IsQuadAsToothNum)+", " +"LabResultImageCat = "+POut.Long (hL7Def.LabResultImageCat)+", " +"SftpUsername = '******', " +"SftpPassword = '******', " +"SftpInSocket = '"+POut.String(hL7Def.SftpInSocket)+"' " +"WHERE HL7DefNum = "+POut.Long(hL7Def.HL7DefNum); if(hL7Def.Note==null) { hL7Def.Note=""; } OdSqlParameter paramNote=new OdSqlParameter("paramNote",OdDbType.Text,hL7Def.Note); Db.NonQ(command,paramNote); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if(def==null) {//wasn't in the database def=new HL7Def(); def.IsNew=true; def.Description="HL7 version 2.6"; def.ModeTx=ModeTxHL7.File; def.IncomingFolder=""; def.OutgoingFolder=""; def.IncomingPort=""; def.OutgoingIpPort=""; def.SftpInSocket=""; def.SftpUsername=""; def.SftpPassword=""; def.FieldSeparator="|"; def.ComponentSeparator="^"; def.SubcomponentSeparator="&"; def.RepetitionSeparator="~"; def.EscapeCharacter=@"\"; def.IsInternal=true; def.InternalType=HL7InternalType.HL7v2_6; def.InternalTypeVersion=Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled=false; def.Note=""; def.ShowDemographics=HL7ShowDemographics.ChangeAndAdd; def.ShowAccount=true; def.ShowAppts=true; def.IsQuadAsToothNum=false; } def.hl7DefMessages=new List<HL7DefMessage>(); HL7DefMessage msg=new HL7DefMessage(); HL7DefSegment seg=new HL7DefSegment(); #region Inbound Messages #region ACK - General Acknowledgment def.AddMessage(msg,MessageTypeHL7.ACK,MessageStructureHL7.ADT_A01,InOutHL7.Incoming,0); #region MSH - Message Header msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); #endregion MSH - Message Header #region MSA - Message Acknowledgment seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.MSA); //Fields------------------------------------------------------------------------------------------------------------- //MSA.1, Acknowledgment Code seg.AddField(1,"ackCode"); //MSA.2, Message Control ID seg.AddField(2,"messageControlId"); #endregion MSA - Message Acknowledgment #endregion ACK - General Acknowledgment #region ADT - Patient Demographics (Admits, Discharges, and Transfers) msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ADT,MessageStructureHL7.ADT_A01,InOutHL7.Incoming,1); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //HL7 documentation says field 1 is Field Separator. "This field contains the separator between the segment ID and the first real field. As such it serves as the separator and defines the character to be used as a separator for the rest of the message." (HL7 v2.6 documentation) The separator is usually | (pipes) and is part of field 0, which is the segment ID followed by a |. Encoding Characters is the first real field, so it will be numbered starting with 1 in our def. //Fields------------------------------------------------------------------------------------------------------------- //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); #endregion MSH - Message Header #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID);//order 2 since the EVN segment is order 1. We don't process anything out of the EVN segment, so only defined for outgoing messages //Fields------------------------------------------------------------------------------------------------------------- //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID but we've always used it as our PatNum for eCW. //The standard will be to continue to use PID.2 as the PatNum for incoming messages and attempt to locate the patient if this field is populated. seg.AddField(2,"pat.PatNum"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //We will use the OID root for the office, stored in the table oidinternal with IDType Patient. //The Patient ID data type CX contains the Assigning Authority data type HD - Heirarchic Designator. //Within the Assigning Authority, sub-component 2 is the Universal ID that needs to have the OID root for the office. //Sub-component 3 of the Assigning Authority, Universal ID Type, will be 'HL7' for our PatNums since our root is registered with HL7. //The Patient ID CX type will also have the Identifier Type Code of 'PI', Patient internal identifier. //Once located, PatNum is the first component //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for ChartNumber in the past and will continue to use this as a backup method for locating a patient if the ID in PID.2 is not present //or if no patient can be found using that value. seg.AddField(4,"pat.ChartNumber"); //PID.5, Patient Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI^^Title seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race //The old race parsing will still work and matches to an exact string to find the race. The string can be the only component of the field or the first of many. //The old parse translates the string to the PatRaceOld enum, then reconciles them into one or more of the PatRace enums, then adds/deletes entries in the patientrace table. //The old race strings are: "American Indian Or Alaska Native", "Asian", "Native Hawaiian or Other Pacific", "Black or African American", "White", "Hispanic", "Other Race" //The new race parse accepts the CWE - Coded with Exceptions data type with 9 fields. //The 9 fields are: Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will make sure CodeSystem, PID.10.2, is "CDCREC" and use Code, PID.10.0, to find the correct race from the new PatRace enum and update the patientrace table. //The race field can repeat, so any number of races can be sent and will add to the patientrace table. seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, is used to populate patient.AddrNote with '\.br\' signaling a line break. (the '\' is the default escape character, may be different and parses correctly) //Example: ...^^Emergency Contact: Mom Test\.br\Mother\.br\(503)555-1234 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //If this field is populated, it will set the patient.GradeLevel if the field can be converted to an integer between 1 and 12 seg.AddField(2,"pat.GradeLevel"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //Used to set patient.ClinicNum if the location description matches clinic.Description seg.AddField(3,"pat.location"); //PV1.7, Attending/Primary Care Doctor, XCN data type, ProviderId^LastName^FirstName^MI //In the PV1 segment, the primary provider will be set for the patient //The ProviderID component will hold the office's OID for a provider+"."+ProvNum if they send us a ProviderID that we have previously sent them //If the first component is not the provider root, we will assume this is an external ID and store it in the oidexternals linked to our internal ProvNum seg.AddField(7,"prov.provIdNameLFM"); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Using the location type field to identify this field as a "Site (or Grade School)", we will attempt to match the description to a site.Description in the db //If exact match is found, set the patient.SiteNum=site.SiteNum //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent //Stored in the patient.Urgency field for treatment urgnecy, used in public health screening seg.AddField(18,"pat.Urgency"); #endregion PV1 - Patient Visit #region GT1 - Guarantor seg=new HL7DefSegment(); msg.AddSegment(seg,4,false,true,SegmentNameHL7.GT1); //Fields------------------------------------------------------------------------------------------------------------- //GT1.2, Guarantor Number //This field is repeatable and will be handled the same as the patientIdList in the PID segment, so it may contain multiple CX data type identifiers that point to a patient. seg.AddField(2,"guarIdList"); //GT1.3, Guarantor Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI^^Title seg.AddField(3,"guar.nameLFM"); //GT1.5, Guarantor Address //GT1.5.20, comment, is used to populate patient.AddrNote with '\.br\' signaling a line break. //The '\' is the default escape character, may be different in the incoming message and parses correctly //Example: ...^^Emergency Contact: Mom Test\.br\Mother\.br\(503)555-1234 seg.AddField(5,"guar.addressCityStateZip"); //GT1.6, Guarantor Phone Number - Home seg.AddField(6,"guar.HmPhone"); //GT1.7, Guarantor Phone Number - Business seg.AddField(7,"guar.WkPhone"); //GT1.8, Guarantor Date/Time of Birth seg.AddField(8,"guar.birthdateTime"); //GT1.9, Guarantor Administrative Sex seg.AddField(9,"guar.Gender"); //GT1.12, Guarantor SSN seg.AddField(12,"guar.SSN"); #endregion GT1 - Guarantor #region OBX - Observation/Result seg=new HL7DefSegment(); msg.AddSegment(seg,5,true,true,SegmentNameHL7.OBX); //Fields------------------------------------------------------------------------------------------------------------- //OBX.3, Observation ID, ID^Descript^CodeSystem //For adding current medications, the value will be a CWE (OBX.2 should hold the value type 'CWE') with RXNORM as the code system and the RxCui for the ID //RxNorm Code^^RXNORM, the Description is ignored seg.AddField(3,"medicationRxNorm"); #endregion OBX - Observation/Result #region AL1 - Allergy Information seg=new HL7DefSegment(); msg.AddSegment(seg,6,true,true,SegmentNameHL7.AL1); //Fields------------------------------------------------------------------------------------------------------------- //AL1.2, Allergen Type Code, DA - Drug allergy, FA - Food allergy, MA - Miscellaneous Allergy //For now, we only allow DA for drug allergies seg.AddField(2,"allergenType"); //AL1.3, Allergen Code, currently only RxNorm codes are supported //Supplied in CWE data type format with 9 components, of which we only need 2 code^^codeSystem^^^^^^ //codeSystem must be RxNorm, code must match an existing allergydef.MedicationNum seg.AddField(3,"allergenRxNorm"); #endregion AL1 - Allergy Information #region PR1 - Procedures seg=new HL7DefSegment(); msg.AddSegment(seg,7,true,true,SegmentNameHL7.PR1); //Fields------------------------------------------------------------------------------------------------------------- //PR1.3, Procedure Code, CNE data type //ProcCode^descript (ignored)^CD2^^^^2014^^Layman Term (ignored) //Example: D1351^^CD2^^^^2014 seg.AddField(3,"proccode.ProcCode"); //PR1.5, Procedure Date/Time, DTM seg.AddField(5,"proc.procDateTime"); //PR1.16, Procedure Code Modifier, CNE data type //This will hold the treatment area for the procedure (tooth,tooth/surf,quad,sextant,arch,tooth range). //If this is empty or blank, default will be mouth and the proccode sent must have a treatment area of mouth or the code will not be inserted. //We will validate the information conforms to the tooth numbering system in use by the office and will handle international toothnums. //If it is for a tooth surface, it will be 2 subcomponents, toothnum&surface(s) //Quad, sextant, and arch will be in subcomponent 2 with the first subcomponent blank //Tooth range will be comma-delimited list of tooth numbers in subcomponent 1 //Examples: 1,2,3,4 or 12&MODL or &UL or &2 (sextant 2) seg.AddField(16,"proc.toothSurfRange"); //PR1.19, Procedure Identifier, EI data type //Id^^UniversalId //They should send us a unique ID for this procedure with their UniversalId root for a procedure object. //If we get this data, we will store our procedurelog.ProcNum linked to the external ID and root in the oidexternals table. //This will be useful in preventing multiple copies of the same procedure from being inserted due to duplicate messages. //However, if they do not specify a unique ID and root, or if it is not in our oidexternals table linked to our ProcNum with type of Procedure, we will insert a new one. //This field could be blank and we will insert a new procedure every time, regardless of whether or not there is already a procedure with this code for this patient. seg.AddField(19,"proc.uniqueId"); #endregion PR1 - Procedures #endregion ADT - Patient Demographics (Admits, Discharges, and Transfers) #region PPR - Patient Problem msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.PPR,MessageStructureHL7.PPR_PC1,InOutHL7.Incoming,2); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); #endregion MSH - Message Header #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.PID);//order 1 since PPR's don't have an EVN segment //Fields------------------------------------------------------------------------------------------------------------- //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID but we've always used it as our PatNum for eCW. //The standard will be to continue to use PID.2 as the PatNum for incoming messages and attempt to locate the patient if this field is populated. seg.AddField(2,"pat.PatNum"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //We will use the OID root for the office, stored in the table oidinternal with IDType Patient. //The Patient ID data type CX contains the Assigning Authority data type HD - Heirarchic Designator. //Within the Assigning Authority, sub-component 2 is the Universal ID that needs to have the OID root for the office. //Sub-component 3 of the Assigning Authority, Universal ID Type, will be 'HL7' for our PatNums since our root is registered with HL7. //The Patient ID CX type will also have the Identifier Type Code of 'PI', Patient internal identifier. //Once located, PatNum is the first component //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for ChartNumber in the past and will continue to use this as a backup method for locating a patient if the ID in PID.2 is not present //or if no patient can be found using that value. seg.AddField(4,"pat.ChartNumber"); //PID.5, Patient Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI^^Title seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race //The old race parsing will still work and matches to an exact string to find the race. The string can be the only component of the field or the first of many. //The old parse translates the string to the PatRaceOld enum, then reconciles them into one or more of the PatRace enums, then adds/deletes entries in the patientrace table. //The old race strings are: "American Indian Or Alaska Native", "Asian", "Native Hawaiian or Other Pacific", "Black or African American", "White", "Hispanic", "Other Race" //The new race parse accepts the CWE - Coded with Exceptions data type with 9 fields. //The 9 fields are: Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will make sure CodeSystem, PID.10.2, is "CDCREC" and use Code, PID.10.0, to find the correct race from the new PatRace enum and update the patientrace table. //The race field can repeat, so any number of races can be sent and will add to the patientrace table. seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, is used to populate patient.AddrNote with '\.br\' signaling a line break. (the '\' is the default escape character, may be different and parses correctly) //Example: ...^^Emergency Contact: Mom Test\.br\Mother\.br\(503)555-1234 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,2,false,true,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //If this field is populated, it will set the patient.GradeLevel if the field can be converted to an integer between 1 and 12 seg.AddField(2,"pat.GradeLevel"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //Used to set patient.ClinicNum if the location description matches clinic.Description seg.AddField(3,"pat.location"); //PV1.7, Attending/Primary Care Doctor, XCN data type, ProviderId^LastName^FirstName^MI^^Abbr //In the PV1 segment, the primary provider will be set for the patient if provider is found by ProviderId or by name and abbr //A new provider will not be inserted if not found, the default practice provider will be used seg.AddField(7,"prov.provIdNameLFM"); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Using the location type field to identify this field as a "Site (or Grade School)", we will attempt to match the description to a site.Description in the db //If exact match is found, set the patient.SiteNum=site.SiteNum //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent //Stored in the patient.Urgency field for treatment urgnecy, used in public health screening seg.AddField(18,"pat.Urgency"); #endregion PV1 - Patient Visit #region PRB - Problem Detail seg=new HL7DefSegment(); msg.AddSegment(seg,3,true,false,SegmentNameHL7.PRB); //Fields------------------------------------------------------------------------------------------------------------- //PRB.1, Action Code, AD-ADD,CO-Correct,DE-Delete,LI-Link,UC-Unchanged,UN-Unlink,UP-Update. AD/UP are currently supported and are treated the same seg.AddField(1,"problemAction"); //PRB.2, Action Date/Time, DTM data type seg.AddField(2,"dateTime.Now"); //PRB.3, Problem ID, CWE data type //Currently only SNOMEDCT codes are supported, code^descript^codeSystem (descript is not required and is ignored) //Example: 1234^^SNM seg.AddField(3,"problemCode"); //PRB.4, Problem Instance ID, EI data type //Uniquely identifies this instance of a problem //If we were to send this, it would be the oidinternal root for problems (root+".5") with the disease.DiseaseNum as the ID //We expect the sending software to send an ID with an assigning authority root ID and we will link that to our disease.DiseaseNum in the oidexternals table //Example: |76543^^OtherSoftwareRoot.ProblemOID| seg.AddField(4,"problemUniqueId"); //PRB.7, Problem Established Date/Time seg.AddField(7,"problemStartDate"); //PRB.9, Actual Problem Resolution Date/Time seg.AddField(9,"problemStopDate"); #endregion PRB - Problem Detail #endregion PPR - Patient Problem #region SRM - Schedule Request //The only incoming SRM event types we will accept by default will be S03 - Request Appointment Modification or S04 - Request Appointment Cancellation (for now) //The message must refer to an appointment already existing in OD by AptNum //The S03 message will only be allowed to modify a limited amount of information about the appointment //The S04 message will set the appointment status to Broken (enum 5) msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.SRM,MessageStructureHL7.SRM_S01,InOutHL7.Incoming,3); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); #endregion MSH - Message Header #region ARQ - Appointment Request Information seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.ARQ); //Fields------------------------------------------------------------------------------------------------------------- //ARQ.1, Placer Appointment ID. This will be the other software appointment ID. //We will store this in the oidexternals table linked to our AptNum. //The first component will be the external application appointment ID (stored as IDExternal) //The third component will be the external application assigning authority root ID (stored as RootExternal) //Example: |12345^^OtherSoftware.Root^| seg.AddField(1,"apt.externalAptID"); //ARQ.2, Filler Appointment ID, EI data type, EntityID^NamespaceID^UniversalID^UniversalIDType //We will expect the first component to be our AptNum //We could check to make sure the UniversalID is our OID for an appointment, but for this incoming segment we won't enforce it for now and just accept the first component as AptNum //Example: 1234^^2.16.840.1.113883.3.4337.1486.6566.6^HL7 seg.AddField(2,"apt.AptNum"); #endregion ARQ - Appointment Request Information #region NTE - Notes and Comments seg=new HL7DefSegment(); msg.AddSegment(seg,2,true,true,SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.3, Comment, FT data type (formatted text) //We will append this comment to appointment.Note if the Note for this appointment does not already contain the exact text received //This is formatted text, so we will allow new lines. //As in the address note field (see PID.11) we will accept '\.br\' (where the '\' is the defined escape char, \ by default) to signal a new line. seg.AddField(3,"apt.Note"); #endregion NTE - Notes and Comments #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID but we've always used it as our PatNum for eCW. //The standard will be to continue to use PID.2 as the PatNum for incoming messages and attempt to locate the patient if this field is populated. seg.AddField(2,"pat.PatNum"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //We will use the OID root for the office, stored in the table oidinternal with IDType Patient. //The Patient ID data type CX contains the Assigning Authority data type HD - Heirarchic Designator. //Within the Assigning Authority, sub-component 2 is the Universal ID that needs to have the OID root for the office. //Sub-component 3 of the Assigning Authority, Universal ID Type, will be 'HL7' for our PatNums since our root is registered with HL7. //The Patient ID CX type will also have the Identifier Type Code of 'PI', Patient internal identifier. //Once located, PatNum is the first component //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for ChartNumber in the past and will continue to use this as a backup method for locating a patient if the ID in PID.2 is not present //or if no patient can be found using that value. seg.AddField(4,"pat.ChartNumber"); //PID.5, Patient Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI^^Title seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race //The old race parsing will still work and matches to an exact string to find the race. The string can be the only component of the field or the first of many. //The old parse translates the string to the PatRaceOld enum, then reconciles them into one or more of the PatRace enums, then adds/deletes entries in the patientrace table. //The old race strings are: "American Indian Or Alaska Native", "Asian", "Native Hawaiian or Other Pacific", "Black or African American", "White", "Hispanic", "Other Race" //The new race parse accepts the CWE - Coded with Exceptions data type with 9 fields. //The 9 fields are: Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will make sure CodeSystem, PID.10.2, is "CDCREC" and use Code, PID.10.0, to find the correct race from the new PatRace enum and update the patientrace table. //The race field can repeat, so any number of races can be sent and will add to the patientrace table. seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, is used to populate patient.AddrNote with '\.br\' signaling a line break. (the '\' is the default escape character, may be different and parses correctly) //Example: ...^^Emergency Contact: Mom Test\.br\Mother\.br\(503)555-1234 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,4,false,true,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //If this field is populated, it will set the patient.GradeLevel if the field can be converted to an integer between 1 and 12 seg.AddField(2,"pat.GradeLevel"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //Used to set patient.ClinicNum if the location description matches clinic.Description seg.AddField(3,"pat.location"); //PV1.7, Attending/Primary Care Doctor, XCN data type, ProviderId^LastName^FirstName^MI //In the PV1 segment, the primary provider will be set for the patient and the appointment //If there is an AIL or AIG segment, the appointment.ProvNum will be set to the provider referenced in that segment. seg.AddField(7,"prov.provIdNameLFM"); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Using the location type field to identify this field as a "Site (or Grade School)", we will attempt to match the description to a site.Description in the db //If exact match is found, set the patient.SiteNum=site.SiteNum //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent //Stored in the patient.Urgency field for treatment urgnecy, used in public health screening seg.AddField(18,"pat.Urgency"); #endregion PV1 - Patient Visit #region AIG - Appointment Information - General Resource //This segment is for setting the provider(s) on the appointment and setting the confirmation status //It is repeatable so that the user can send one to set the dentist and one to set the hygienist on the appt seg=new HL7DefSegment(); msg.AddSegment(seg,5,true,true,SegmentNameHL7.AIG); //Fields------------------------------------------------------------------------------------------------------------- //AIG.3, Resource ID, CWE data type //If this is included in the AIG segment for a general resource, we will try to find the provider using the resource ID as the OD ProvNum. //If not found by ProvNum, we will try to find the provider using the second component - Text, which should be populated with 'LName, FName', //combined with the 4th component - Alternate Identifier, which should be populated with the provider.Abbr. //Only if LName, FName, and Abbr all match will we use that provider. //When the provider is identified in an AIG or AIP segment, it is used to set the provider on the appointment, not the patient's primary provider. //For setting a patient's primary provider, the PV1 segment is used. //Example: |1234^Abbott, Sarah^^DrAbbott| seg.AddField(3,"prov.provIdName"); //AIG.4, Resource Type, CWE data type //Accepted values are 'd' or 'D' for dentist and 'h' or 'H' for hygienist //If included, we will set either the dentist or the hygienist on the appt. //If not included, the provider will assumed to refer to the dentist seg.AddField(4,"prov.provType"); //AIG.14, Filler Status Code, CWE data type //We will use this to set the confirmation status of the appointment //We will use the second component (text) and attempt to match exactly the ItemName (confirm Name) or ItemValue (confirm Abbreviation) from their definition table //If there is a match, we will update the confirmation status //Example: |^Appointment Confirmed| seg.AddField(14,"apt.confirmStatus"); #endregion AIG - Appointment Information - General Resource #region AIL - Appointment Information - Location Resource //This segment is used to set the clinic on the appointment. //This may differ from the patient's assigned clinic, only the appointment.ClinicNum will be set from this segment. //This is not repeatable as we only have one clinic value to assign for the location resource seg=new HL7DefSegment(); msg.AddSegment(seg,6,false,true,SegmentNameHL7.AIL); //Fields------------------------------------------------------------------------------------------------------------- //AIL.3, Location Resource ID, PL data type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //Room-Operatory (not set from inbound messages), Facility-Practice (not set from inbound messages), Building-Clinic (used to set apt.ClinicNum) //Used to set the appointment.ClinicNum if this is included in the message seg.AddField(3,"apt.location"); //AIL.12, Filler Status Code, CWE data type //We will use this to set the confirmation status of the appointment //We will use the second component (text) and attempt to match exactly the ItemName (confirm Name) or ItemValue (confirm Abbreviation) from their definition table //If there is a match, we will update the confirmation status //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIL - Appointment Information - Location Resource #region AIP - Appointment Information - Personnel Resource //This segment is used to set the dentist and/or hygienist on an appointment //This may differ from the patient's primary provider and it could cause an appointment to be in an operatory with a provider not assigned to that operatory seg=new HL7DefSegment(); msg.AddSegment(seg,7,true,true,SegmentNameHL7.AIP); //Fields------------------------------------------------------------------------------------------------------------- //AIP.3, Personnel Resource ID, XCN data type //ProviderId^LastName^FirstName^MI^Suffix^Prefix //According to the HL7 standards, we are free to use the components as needed to identify the provider //We will use ProviderID as the OD ProvNum, and if not found in the db we will use LName, FName and the 'Prefix' component as the Abbr //Used to set the appointment.ProvNum or appointment.ProvHyg if this is included in the message //Field AIP.4 will determine whether it is the dentist or hygienist for the appt //This field is repeatable, but if they want to set both the dentist and the hygienist for the same appointment //they would have to repeat the whole segment with different provTypes in field 4 //Example: |1234^Abbott^Sarah^L^DMD^DrAbbott| seg.AddField(3,"prov.provIdNameLFM"); //AIP.4, Resource Type, CWE data type //Accepted values are 'd' or 'D' for dentist and 'h' or 'H' for hygienist seg.AddField(4,"prov.provType"); //AIP.12, Filler Status Code, CWE data type //We will use this to set the confirmation status of the appointment //We will use the second component (text) and attempt to match exactly the ItemName (confirm Name) or ItemValue (confirm Abbreviation) from their definition table //If there is a match, we will update the confirmation status //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIP - Appointment Information - Personnel Resource #endregion SRM - Schedule Request #endregion Inbound Messages #region Outbound Messages #region ACK - General Acknowledgment msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ACK,MessageStructureHL7.ADT_A01,InOutHL7.Outgoing,4); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application, HD data type, Namespace ID^Universal ID^Universal ID Type //We originally sent 'OD' in this field, but this is a HD data type, so should consist of three components //We will send the Open Dental HL7 root assigned to the office and stored in the oidinternal table with the IDType of Root //The recommeded value for this field in the table is ^2.16.840.1.113883.3.4337.1486.CustomerPatNum^HL7. //If the oidinternal row with IDType=Root does not have an IDRoot assigned, we will revert to sending 'OD' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility, not used //MSH.4, Receiving Application //This field will have to be a fixed text field and would have to be modified in a custom definition //if they want the outbound messages to have a specific receiving application identifier seg.AddField(4,"0361",DataTypeHL7.HD,"","NamespaceID^UniversalID^UniversalIDType"); //MSH.5, Receiving Facility, not used //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type, MSG data type //This segment should contain MessageType^EventType^MessageStructure, example SRR^S04^SRR_S04 seg.AddField(8,"messageType"); //MSH.9, Message Control ID //A GUID that uniquely identifies this message seg.AddField(9,"messageControlId"); //MSH.10, Processing ID, PT data type, Processing ID^Processing Mode //Processing mode is optional and not included, we will use P for production (P-Production, T-Training, D-Debugging) seg.AddField(10,"0103",DataTypeHL7.PT,"","P"); //MSH.11, Version ID, VID data type, Version ID^Internationalization Code^International Version ID //All components are optional, we will only send Version ID, which is currently 2.6 seg.AddField(11,"0104",DataTypeHL7.VID,"","2.6"); //MSH.15, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) //This is for enhanced acknowledgment mode, which we do not currently support, but we will send our mode just in case //With field MSH.15, Accept Ack Type, not present it will default to NE //Field MSH.15 set to AL means we will require an ack for every message that is sent, meaning it was processed successfully by the receiving application //But MSH.15 not present or null means we do not require a separate accept ACK message, just the accept and validate response meaning the message was accepted and processed seg.AddField(15,"0155",DataTypeHL7.ID,"","AL"); #endregion MSH - Message Header #region MSA - Message Acknowledgment seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.MSA); //Fields------------------------------------------------------------------------------------------------------------- //MSA.1, Acknowledgment Code seg.AddField(1,"ackCode"); //MSA.2, Message Control ID seg.AddField(2,"messageControlId"); #endregion MSA - Message Acknowledgment #endregion ACK - General Acknowledgment #region ADT - Patient Demographics (Admits, Discharges, and Transfers) //Outbound ADT messages will be triggered when specific data is changed in OD, but not if the data change is due to an inbound HL7 message. //ADT's created when: //1. Pressing OK from FormPatientEdit //2. Pressing OK from FormPatientAddAll //3. Pressing the Set Guarantor button (ContrFamily) //4. Pressing the Move button (ContrFamily) to move a patient to a different family //5. Retrieving web forms in FormWebForms if a form is for/creates a new patient //If the Synch Clone feature is enabled, we would not want the clone to exist in the other software, so we would want to make sure we were not sending ADT's for clones. msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ADT,MessageStructureHL7.ADT_A01,InOutHL7.Outgoing,5); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application, HD data type, Namespace ID^Universal ID^Universal ID Type //We originally sent 'OD' in this field, but this is a HD data type, so should consist of three components //We will send the Open Dental HL7 root assigned to the office and stored in the oidinternal table with the IDType of Root //The recommeded value for this field in the table is ^2.16.840.1.113883.3.4337.1486.CustomerPatNum^HL7. //If the oidinternal row with IDType=Root does not have an IDRoot assigned, we will revert to sending 'OD' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility, not used //MSH.4, Receiving Application //This field will have to be a fixed text field and would have to be modified in a custom definition //if they want the outbound messages to have a specific receiving application identifier seg.AddField(4,"0361",DataTypeHL7.HD,"","NamespaceID^UniversalID^UniversalIDType"); //MSH.5, Receiving Facility, not used //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type, MSG data type //This segment should contain MessageType^EventType^MessageStructure, example ADT^A04^ADT_A04 seg.AddField(8,"messageType"); //MSH.9, Message Control ID //A GUID that uniquely identifies this message seg.AddField(9,"messageControlId"); //MSH.10, Processing ID, PT data type, Processing ID^Processing Mode //Processing mode is optional and not included, we will use P for production (P-Production, T-Training, D-Debugging) seg.AddField(10,"0103",DataTypeHL7.PT,"","P"); //MSH.11, Version ID, VID data type, Version ID^Internationalization Code^International Version ID //All components are optional, we will only send Version ID, which is currently 2.6 seg.AddField(11,"0104",DataTypeHL7.VID,"","2.6"); //MSH.15, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) //This is for enhanced acknowledgment mode, which we do not currently support, but we will send our mode just in case //With field MSH.15, Accept Ack Type, not present it will default to NE //Field MSH.15 set to AL means we will require an ack for every message that is sent, meaning it was processed successfully by the receiving application //But MSH.15 not present or null means we do not require a separate accept ACK message, just the accept and validate response meaning the message was accepted and processed seg.AddField(15,"0155",DataTypeHL7.ID,"","AL"); #endregion MSH - Message Header #region EVN - Event Type seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.EVN); //Fields------------------------------------------------------------------------------------------------------------- //EVN.1, Event Type, example P03 //Retained for backward compatibility only, we will not add this field by default //EVN.2, Recorded Date/Time seg.AddField(2,"dateTime.Now"); //EVN.4, Event Reason Code (01 - Patient request; 02 - Physician/health practitioner order;03 - Census Management;O - Other;U - Unknown) seg.AddField(4,"0062",DataTypeHL7.IS,"","01"); #endregion EVN - Event Type #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.1, Set ID, SI data type //"This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one" (HL7 v2.6 documentation) //We only send 1 PID segment in ADT's so this number will always be 1. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID, we've always sent the ChartNumber in this field as the eCW patient ID //The standard will be to continue to use PID.2 for sending the ChartNumber, which was set from PID.4 of an incoming message. This could be the other software patient ID. seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //PatientID^IDCheckDigit^CheckDigitScheme^AssigningAuthority^IDTypeCode //Assigning Authority is a HD data type and is composed of 3 subcomponents, NamespaceID&UniversalID&UniversalIDType //Sub-component 2, UniversalID, we will use the OID root for a Patient object stored in the oidinternal table //If retrieved from OD it will be 2.16.840.1.113883.3.4337.1486.CustomerNumber.2 //Sub-component 3, Universal ID Type, will be 'HL7' since our root is registered with HL7. //The IDTypeCode will be 'PI', Patient internal identifier. //We will also get all of the identifiers in the oidexternals table for the patient and create repetitions for each external ID using the IDExternal and RootExternal //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for sending the OD PatNum in the past and will continue to send this in PID.4 seg.AddField(4,"pat.PatNum"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race, CWE - Coded with Exceptions data type //This data type is composed of 9 fields, Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will send Code^Description^CodeSystem^^^^CodeSystemVersionID //The race field can repeat, so any number of races can be sent and will come from the patientrace table. //Example: 2106-3^White^CDCREC^^^^1~2186-5^NotHispanic^CDCREC^^^^1 seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, will come from the patient.AddrNote column and each new line character will be replaced with '\.br\' where the '\' is the defined escape char //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status //S-Single, M-Married, W-Widowed, D-Divorced seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.1, Set ID - PV1 //See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per ADT message. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //Suggested values E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown //We will defualt to send 'O' for outpatient for outbound messages, but for incoming we will use this field for pat.GradeLevel if it's an integer from 1-12 seg.AddField(2,"0004",DataTypeHL7.IS,"","O"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"pat.location"); //PV1.7, Attending/Primary Care Doctor, ProviderId^LastName^FirstName^MI seg.AddField(7,"0010",DataTypeHL7.XCN,"prov.provIdNameLFM",""); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Filled with the "Site (or Grade School)" value in the database, using 'S' to designate the location type site and the site.Description value //If no site assigned in OD, this will be blank as it is optional //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, IS data type (coded value for user-defined tables) //We will send one of the following values retrieved from the patient.Urgency field for treatment urgency: 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent seg.AddField(18,"pat.Urgency"); //PV1.19, Visit Number //Outbound ADT messages will not have an appointment number, no appointment selected //seg.AddField(19,"apt.AptNum"); //PV1.44, Admit Date/Time //Outbound ADT messages will not have a procedure or appt to get the date/time from //seg.AddField(44,"proc.procDateTime"); #endregion PV1 - Patient Visit #region IN1 - Insurance seg=new HL7DefSegment(); msg.AddSegment(seg,4,true,true,SegmentNameHL7.IN1); //Fields------------------------------------------------------------------------------------------------------------- //IN1.1, Set ID (starts with 1) //This will be 1 for primary ins and increment for additional repetitions for secondary ins etc. seg.AddField(1,"sequenceNum"); //IN1.2, Insurance Plan ID, CWE data type //We do not have unique ID's for insurance plans. //We have created an oidinternal ID for insplan, root+.7, and will use this with the insplan.PlanNum to uniquely identify an insplan. //Example: 2.16.840.1.113883.3.4337.1486.6566.7.1234 seg.AddField(2,"insplan.planNum"); //IN1.3, Insurance Company ID, CX data type seg.AddField(3,"carrier.ElectID"); //IN1.4, Insurance Company Name, XON data type seg.AddField(4,"carrier.CarrierName"); //IN1.5, Insurance Company Address, XAD data type //carrier.Address^Address2^City^State^Zip seg.AddField(5,"carrier.addressCityStateZip"); //IN1.7, Insurance Company Phone Number, XTN data type //Example: ^WPN^PH^^^503^3635432 seg.AddField(7,"carrier.Phone"); //IN1.8, Group Number, ST data type seg.AddField(8,"insplan.GroupNum"); //IN1.9, Group Name, XON data type seg.AddField(9,"insplan.GroupName"); //IN1.11, Insured's Group Employer Name, XON data type //insplan.EmployerNum to employer.EmpName seg.AddField(11,"insplan.empName"); //IN1.12, Plan Effective Date, DT data type seg.AddField(12,"inssub.DateEffective"); //IN1.13, Plan Expiration Date, DT data type seg.AddField(13,"inssub.DateTerm"); //IN1.15, Plan Type, IS data type //insplan.PlanType, Category Percentage, PPO Percentage, Medicaid or Flat Copay, Capitation seg.AddField(15,"insplan.PlanType"); //IN1.16, Name of Insured, XPN data type //insplan.InsSubNum->inssub.Subscriber->patient name seg.AddField(16,"inssub.subscriberName"); //IN1.17, Insured's Relationship to Patient, CWE data type //SEL-Self, SPO-Spouse, DOM-LifePartner, CHD-Child (PAR-Parent), EME-Employee (EMR-Employer) //DEP-HandicapDep (GRD-Guardian), DEP-Dependent, OTH-SignifOther (OTH-Other), OTH-InjuredPlantiff //We store relationship to subscriber and they want subscriber's relationship to patient, therefore //Relat.Child will return "PAR" for Parent, Relat.Employee will return "EMR" for Employer, and Relat.HandicapDep and Relat.Dependent will return "GRD" for Guardian seg.AddField(17,"patplan.subRelationToPat"); //IN1.18, Insured's Date of Birth, DTM data type seg.AddField(18,"inssub.subBirthdate"); //IN1.19, Insured's Address, XAD data type seg.AddField(19,"inssub.subAddrCityStateZip"); //IN1.20, Assignment of Benefits, IS data type //inssub.AssignBen 1-Y, 0-N seg.AddField(20,"inssub.AssignBen"); //IN1.21, Coordination of Benefits, IS data type //CO-Coordination, IN-Independent. Will be CO if more than one patplan seg.AddField(21,"insplan.cob"); //IN1.22, Coordination of Benefits Priority, ST data type //1-Primary, 2-Secondary, 3,4,5... seg.AddField(22,"patplan.Ordinal"); //IN1.27, Release of Information Code, IS data type //inssub.ReleaseInfo, 1-Y, 2-N seg.AddField(27,"inssub.ReleaseInfo"); //IN1.36, Policy Number, ST data type //patplan.PatID, if blank then inssub.SubscriberID seg.AddField(36,"patplan.policyNum"); //IN1.47, Coverage Type, IS data type //D-Dental, M-Medical using insplan.IsMedical flag seg.AddField(47,"insplan.coverageType"); //IN1.49, Insured's ID Number, CX data type seg.AddField(49,"inssub.SubscriberID"); #endregion IN1 - Insurance #endregion ADT - Patient Demographics (Admits, Discharges, and Transfers) #region DFT - Detailed Financial Transaction //Outbound DFT messages will be triggered by pressing the tool bar button for this enabled definition. //In the future there may be automation for sending these when procedures are set complete. msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.DFT,MessageStructureHL7.DFT_P03,InOutHL7.Outgoing,6); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application, HD data type, Namespace ID^Universal ID^Universal ID Type //We originally sent 'OD' in this field, but this is a HD data type, so should consist of three components //We will send the Open Dental HL7 root assigned to the office and stored in the oidinternal table with the IDType of Root //The recommeded value for this field in the table is ^2.16.840.1.113883.3.4337.1486.CustomerPatNum^HL7. //If the oidinternal row with IDType=Root does not have an IDRoot assigned, we will revert to sending 'OD' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility, not used //MSH.4, Receiving Application //This field will have to be a fixed text field and would have to be modified in a custom definition //if they want the outbound messages to have a specific receiving application identifier seg.AddField(4,"0361",DataTypeHL7.HD,"","NamespaceID^UniversalID^UniversalIDType"); //MSH.5, Receiving Facility, not used //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type, MSG data type //This segment should contain MessageType^EventType^MessageStructure, example DFT^P03^DFT_P03 seg.AddField(8,"messageType"); //MSH.9, Message Control ID //A GUID that uniquely identifies this message seg.AddField(9,"messageControlId"); //MSH.10, Processing ID, PT data type, Processing ID^Processing Mode //Processing mode is optional and not included, we will use P for production (P-Production, T-Training, D-Debugging) seg.AddField(10,"0103",DataTypeHL7.PT,"","P"); //MSH.11, Version ID, VID data type, Version ID^Internationalization Code^International Version ID //All components are optional, we will only send Version ID, which is currently 2.6 seg.AddField(11,"0104",DataTypeHL7.VID,"","2.6"); //MSH.15, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) //This is for enhanced acknowledgment mode, which we do not currently support, but we will send our mode just in case //With field MSH.15, Accept Ack Type, not present it will default to NE //Field MSH.15 set to AL means we will require an ack for every message that is sent, meaning it was processed successfully by the receiving application //But MSH.15 not present or null means we do not require a separate accept ACK message, just the accept and validate response meaning the message was accepted and processed seg.AddField(15,"0155",DataTypeHL7.ID,"","AL"); #endregion MSH - Message Header #region EVN - Event Type seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.EVN); //Fields------------------------------------------------------------------------------------------------------------- //EVN.1, Event Type, example P03 //Retained for backward compatibility only, we will not add this field by default //EVN.2, Recorded Date/Time seg.AddField(2,"dateTime.Now"); //EVN.4, Event Reason Code (01 - Patient request; 02 - Physician/health practitioner order;03 - Census Management;O - Other;U - Unknown) seg.AddField(4,"0062",DataTypeHL7.IS,"","01"); #endregion EVN - Event Type #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.1, Set ID, SI data type //"This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one" (HL7 v2.6 documentation) //We only send 1 PID segment in DFT's so this number will always be 1. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID, we've always sent the ChartNumber in this field as the eCW patient ID //The standard will be to continue to use PID.2 for sending the ChartNumber, which was set from PID.4 of an incoming message. This could be the other software patient ID. seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //PatientID^IDCheckDigit^CheckDigitScheme^AssigningAuthority^IDTypeCode //Assigning Authority is a HD data type and is composed of 3 subcomponents, NamespaceID&UniversalID&UniversalIDType //Sub-component 2, UniversalID, we will use the OID root for a Patient object stored in the oidinternal table //If retrieved from OD it will be 2.16.840.1.113883.3.4337.1486.CustomerNumber.2 //Sub-component 3, Universal ID Type, will be 'HL7' since our root is registered with HL7. //The IDTypeCode will be 'PI', Patient internal identifier. //We will also get all of the identifiers in the oidexternals table for the patient and create repetitions for each external ID using the IDExternal and RootExternal //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for sending the OD PatNum in the past and will continue to send this in PID.4 seg.AddField(4,"pat.PatNum"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race, CWE - Coded with Exceptions data type //This data type is composed of 9 fields, Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will send Code^Description^CodeSystem^^^^CodeSystemVersionID //The race field can repeat, so any number of races can be sent and will come from the patientrace table. //Example: 2106-3^White^CDCREC^^^^1~2186-5^NotHispanic^CDCREC^^^^1 seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, will come from the patient.AddrNote column and each new line character will be replaced with '\.br\' where the '\' is the defined escape char //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status //S-Single, M-Married, W-Widowed, D-Divorced seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,3,false,true,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.1, Set ID - PV1 //See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per DFT message. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //Suggested values E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown //We will defualt to send 'O' for outpatient for outbound messages, but for incoming we will use this field for pat.GradeLevel if it's an integer from 1-12 seg.AddField(2,"0004",DataTypeHL7.IS,"","O"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"apt.location"); //PV1.7, Attending/Primary Care Doctor, ProviderId^LastName^FirstName^MI seg.AddField(7,"0010",DataTypeHL7.XCN,"prov.provIdNameLFM",""); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Filled with the "Site (or Grade School)" value in the database, using 'S' to designate the location type site and the site.Description value //If no site assigned in OD, this will be blank as it is optional //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, IS data type (coded value for user-defined tables) //We will send one of the following values retrieved from the patient.Urgency field for treatment urgency: 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent seg.AddField(18,"pat.Urgency"); //PV1.19, Visit Number, CX data type, AptNum^CheckDigit^CheckDigitScheme^&AssignAuthorityID&IDType^VN (VN - Visit Number) //We will use AptNum, with check digit scheme M11, with the OIDRoot for an appt object (set in oidinternal), ID Type 'HL7', and 'VN' for Visit Number //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.6&HL7^VN| seg.AddField(19,"apt.AptNum"); //PV1.44, Admit Date/Time seg.AddField(44,"proc.procDateTime"); #endregion PV1 - Patient Visit #region FT1 - Financial Transaction Information seg=new HL7DefSegment(); msg.AddSegment(seg,4,true,true,SegmentNameHL7.FT1); //Fields------------------------------------------------------------------------------------------------------------- //FT1.1, Sequence Number (starts with 1) seg.AddField(1,"sequenceNum"); //FT1.2, Transaction ID seg.AddField(2,"proc.ProcNum"); //FT1.4, Transaction Date (YYYYMMDDHHMMSS) seg.AddField(4,"proc.procDateTime"); //FT1.5, Transaction Posting Date (YYYYMMDDHHMMSS) seg.AddField(5,"proc.procDateTime"); //FT1.6, Transaction Type seg.AddFieldFixed(6,DataTypeHL7.IS,"CG"); //FT1.7, Transaction Code, CWE data type //This is a required field and should contain "the code assigned by the institution for the purpose of uniquely identifying the transaction based on the Transaction Type (FT1-6)". //We will just send the ProcNum, not sure if any receiving application will use it seg.AddField(7,"proc.ProcNum"); //FT1.10, Transaction Quantity seg.AddFieldFixed(10,DataTypeHL7.NM,"1.0"); //FT1.11, Transaction Amount Extended, CP data type //Total fee to charge for this procedure, independent of transaction quantity seg.AddField(11,"proc.ProcFee"); //FT1.12, Transaction Amount Unit, CP data type //Fee for this procedure for each transaction quantity seg.AddField(12,"proc.ProcFee"); //FT1.16, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //In the FT1 segment, we will get the clinic from the specific procedure, but if not set (0) we will get it from the patient //If no clinic on the proc or assigned to the patient, this will be blank seg.AddField(16,"proc.location"); //FT1.19, Diagnosis Code, CWE data type, repeatable if they have more than 1 code on the proc (currently 4 allowed) //Example: |520.2^Abnormal tooth size/form^I9C^^^^31^^~521.81^Cracked tooth^I9C^^^^31^^| seg.AddField(19,"proc.DiagnosticCode"); //FT1.20, Performed by Code seg.AddField(20,"prov.provIdNameLFM"); //FT1.21, Ordering Provider seg.AddField(21,"prov.provIdNameLFM"); //FT1.22, Unit Cost (procedure fee) seg.AddField(22,"proc.ProcFee"); //FT1.25, Procedure Code, CNE data type //ProcNum^Descript^CD2^^^^2014^^LaymanTerm //Example: D0150^comprehensive oral evaluation - new or established patient^CD2^^^^2014^^Comprehensive Exam seg.AddField(25,"proccode.ProcCode"); //FT1.26, Modifiers (treatment area) seg.AddField(26,"proc.toothSurfRange"); #endregion FT1 - Financial Transaction Information #region IN1 - Insurance (global across all FT1s) seg=new HL7DefSegment(); msg.AddSegment(seg,5,true,true,SegmentNameHL7.IN1); //Fields------------------------------------------------------------------------------------------------------------- //IN1.1, Set ID (starts with 1) //This will be 1 for primary ins and increment for additional repetitions for secondary ins etc. seg.AddField(1,"sequenceNum"); //IN1.2, Insurance Plan ID, CWE data type //We do not have unique ID's for insurance plans. //We have created an oidinternal ID for insplan, root+.7, and will use this with the insplan.PlanNum to uniquely identify an insplan. //Example: 2.16.840.1.113883.3.4337.1486.6566.7.1234 seg.AddField(2,"insplan.planNum"); //IN1.3, Insurance Company ID, CX data type seg.AddField(3,"carrier.ElectID"); //IN1.4, Insurance Company Name, XON data type seg.AddField(4,"carrier.CarrierName"); //IN1.5, Insurance Company Address, XAD data type //carrier.Address^Address2^City^State^Zip seg.AddField(5,"carrier.addressCityStateZip"); //IN1.7, Insurance Company Phone Number, XTN data type //Example: ^WPN^PH^^^503^3635432 seg.AddField(7,"carrier.Phone"); //IN1.8, Group Number, ST data type seg.AddField(8,"insplan.GroupNum"); //IN1.9, Group Name, XON data type seg.AddField(9,"insplan.GroupName"); //IN1.11, Insured's Group Employer Name, XON data type //insplan.EmployerNum to employer.EmpName seg.AddField(11,"insplan.empName"); //IN1.12, Plan Effective Date, DT data type seg.AddField(12,"inssub.DateEffective"); //IN1.13, Plan Expiration Date, DT data type seg.AddField(13,"inssub.DateTerm"); //IN1.15, Plan Type, IS data type //insplan.PlanType, Category Percentage, PPO Percentage, Medicaid or Flat Copay, Capitation seg.AddField(15,"insplan.PlanType"); //IN1.16, Name of Insured, XPN data type //insplan.InsSubNum->inssub.Subscriber->patient name seg.AddField(16,"inssub.subscriberName"); //IN1.17, Insured's Relationship to Patient, CWE data type //SEL-Self, SPO-Spouse, DOM-LifePartner, CHD-Child (PAR-Parent), EME-Employee (EMR-Employer) //DEP-HandicapDep (GRD-Guardian), DEP-Dependent, OTH-SignifOther (OTH-Other), OTH-InjuredPlantiff //We store relationship to subscriber and they want subscriber's relationship to patient, therefore //Relat.Child will return "PAR" for Parent, Relat.Employee will return "EMR" for Employer, and Relat.HandicapDep and Relat.Dependent will return "GRD" for Guardian //We will use the first two components, Identifier^Text //Example: |PAR^Parent| seg.AddField(17,"patplan.subRelationToPat"); //IN1.18, Insured's Date of Birth, DTM data type seg.AddField(18,"inssub.subBirthdate"); //IN1.19, Insured's Address, XAD data type seg.AddField(19,"inssub.subAddrCityStateZip"); //IN1.20, Assignment of Benefits, IS data type //inssub.AssignBen 1-Y, 0-N seg.AddField(20,"inssub.AssignBen"); //IN1.21, Coordination of Benefits, IS data type //CO-Coordination, IN-Independent. Will be CO if more than one patplan seg.AddField(21,"insplan.cob"); //IN1.22, Coordination of Benefits Priority, ST data type //1-Primary, 2-Secondary, 3,4,5... seg.AddField(22,"patplan.Ordinal"); //IN1.27, Release of Information Code, IS data type //inssub.ReleaseInfo, 1-Y, 2-N seg.AddField(27,"inssub.ReleaseInfo"); //IN1.36, Policy Number, ST data type //patplan.PatID, if blank then inssub.SubscriberID seg.AddField(36,"patplan.policyNum"); //IN1.47, Coverage Type, IS data type //D-Dental, M-Medical using insplan.IsMedical flag seg.AddField(47,"insplan.coverageType"); //IN1.49, Insured's ID Number, CX data type seg.AddField(49,"inssub.SubscriberID"); #endregion IN1 - Insurance (global across all FT1s) #endregion DFT - Detailed Financial Transaction #region SIU - Schedule Information Unsolicited //Outbound SIU messages will be triggered when specific data is changed in OD, but not if the data change is due to an inbound HL7 message. //SIU's created when creating/modifying/cancelling/breaking an appointment msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.SIU,MessageStructureHL7.SIU_S12,InOutHL7.Outgoing,7); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application, HD data type, Namespace ID^Universal ID^Universal ID Type //We originally sent 'OD' in this field, but this is a HD data type, so should consist of three components //We will send the Open Dental HL7 root assigned to the office and stored in the oidinternal table with the IDType of Root //The recommeded value for this field in the table is ^2.16.840.1.113883.3.4337.1486.CustomerPatNum^HL7. //If the oidinternal row with IDType=Root does not have an IDRoot assigned, we will revert to sending 'OD' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility, not used //MSH.4, Receiving Application //This field will have to be a fixed text field and would have to be modified in a custom definition //if they want the outbound messages to have a specific receiving application identifier seg.AddField(4,"0361",DataTypeHL7.HD,"","NamespaceID^UniversalID^UniversalIDType"); //MSH.5, Receiving Facility, not used //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type, MSG data type //This segment should contain MessageType^EventType^MessageStructure, example SIU^S12^SIU_S12 seg.AddField(8,"messageType"); //MSH.9, Message Control ID //A GUID that uniquely identifies this message seg.AddField(9,"messageControlId"); //MSH.10, Processing ID, PT data type, Processing ID^Processing Mode //Processing mode is optional and not included, we will use P for production (P-Production, T-Training, D-Debugging) seg.AddField(10,"0103",DataTypeHL7.PT,"","P"); //MSH.11, Version ID, VID data type, Version ID^Internationalization Code^International Version ID //All components are optional, we will only send Version ID, which is currently 2.6 seg.AddField(11,"0104",DataTypeHL7.VID,"","2.6"); //MSH.15, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) //This is for enhanced acknowledgment mode, which we do not currently support, but we will send our mode just in case //With field MSH.15, Accept Ack Type, not present it will default to NE //Field MSH.15 set to AL means we will require an ack for every message that is sent, meaning it was processed successfully by the receiving application //But MSH.15 not present or null means we do not require a separate accept ACK message, just the accept and validate response meaning the message was accepted and processed seg.AddField(15,"0155",DataTypeHL7.ID,"","AL"); #endregion MSH - Message Header #region SCH - Schedule Activity Information seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.SCH); //Fields------------------------------------------------------------------------------------------------------------- //SCH.1, Placer Appointment ID, EI data type, OD is the filler application //We will store the exernalAptId from an incoming SRM message ARQ segment if they send it to us, so send it here if we have one seg.AddField(1,"apt.externalAptID"); //SCH.2, Filler Appointment ID, EI data type, OD is the filler application //We will populate the first component with the AptNum, the third with the OID for an appointment object, the fourth with "HL7" //Example: 1234^^2.16.840.1.113883.3.4337.1486.6566.6^HL7 seg.AddField(2,"apt.AptNum"); //SCH.5, Schedule ID, CWE data type //We will use this to send the operatory name seg.AddField(5,"apt.operatory"); //SCH.7, Appointment Reason seg.AddField(7,"apt.Note"); //SCH.8, Appointment Type, CWE data type //Suggested values are Normal - Routine schedule request type, Tentative, or Complete - Request to add a completed appt //We will send Normal for all appointment statuses except complete. seg.AddField(8,"apt.type"); //SCH.16, Filler Contact Person, XCN data type //The OD user who created/modified the appointment seg.AddField(16,"apt.userOD"); //SCH.20, Entered by Person, XCN data type //The OD user who created/modified the appointment seg.AddField(20,"apt.userOD"); //SCH.25, Filler Status Code, CWE data type //This will signal to the other software the AptStatus of the appointment //Booked - ApptStatus.Scheduled or ApptStatus.ASAP, Complete - ApptStatus.Complete, Cancelled - ApptStatus.Broken or ApptStatus.UnscheduledList, Deleted (when deleted) seg.AddField(25,"apt.aptStatus"); #endregion SCH - Schedule Activity Information #region TQ1 - Timing/Quantity seg=new HL7DefSegment(); msg.AddSegment(seg,2,false,true,SegmentNameHL7.TQ1); //Fields------------------------------------------------------------------------------------------------------------- //TQ1.1, Set ID, SI data type //Always 1, only one timing specification in our outbound SIU messages seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //TQ1.2, Quantity, CQ data type //Always default value 1 seg.AddFieldFixed(2,DataTypeHL7.CQ,"1"); //TQ1.6, Service Duration, CQ data type //apt.Pattern is filled with X's and /'s, stored in 5 minute increments //Example: 60^min&&ANS+, ANS+ is the name of the coding system seg.AddField(6,"apt.length"); //TQ1.7, Start Date/Time, DTM data type seg.AddField(7,"apt.AptDateTime"); //TQ1.8, End Date/Time, DTM data type seg.AddField(8,"apt.endAptDateTime"); #endregion TQ1 - Timing/Quantity #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.1, Set ID, SI data type //"This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one" (HL7 v2.6 documentation) //We only send 1 PID segment in SIU's so this number will always be 1. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID, we've always sent the ChartNumber in this field as the eCW patient ID //The standard will be to continue to use PID.2 for sending the ChartNumber, which was set from PID.4 of an incoming message. This could be the other software patient ID. seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //PatientID^IDCheckDigit^CheckDigitScheme^AssigningAuthority^IDTypeCode //Assigning Authority is a HD data type and is composed of 3 subcomponents, NamespaceID&UniversalID&UniversalIDType //Sub-component 2, UniversalID, we will use the OID root for a Patient object stored in the oidinternal table //If retrieved from OD it will be 2.16.840.1.113883.3.4337.1486.CustomerNumber.2 //Sub-component 3, Universal ID Type, will be 'HL7' since our root is registered with HL7. //The IDTypeCode will be 'PI', Patient internal identifier. //We will also get all of the identifiers in the oidexternals table for the patient and create repetitions for each external ID using the IDExternal and RootExternal //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for sending the OD PatNum in the past and will continue to send this in PID.4 seg.AddField(4,"pat.PatNum"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race, CWE - Coded with Exceptions data type //This data type is composed of 9 fields, Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will send Code^Description^CodeSystem^^^^CodeSystemVersionID //The race field can repeat, so any number of races can be sent and will come from the patientrace table. //Example: 2106-3^White^CDCREC^^^^1~2186-5^NotHispanic^CDCREC^^^^1 seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, will come from the patient.AddrNote column and each new line character will be replaced with '\.br\' where the '\' is the defined escape char //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status //S-Single, M-Married, W-Widowed, D-Divorced seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,4,false,true,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.1, Set ID - PV1 //See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per SIU message. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //Suggested values E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown //We will defualt to send 'O' for outpatient for outbound messages, but for incoming we will use this field for pat.GradeLevel if it's an integer from 1-12 seg.AddField(2,"0004",DataTypeHL7.IS,"","O"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"apt.location"); //PV1.7, Attending/Primary Care Doctor, ProviderId^LastName^FirstName^MI seg.AddField(7,"0010",DataTypeHL7.XCN,"prov.provIdNameLFM",""); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Filled with the "Site (or Grade School)" value in the database, using 'S' to designate the location type site and the site.Description value //If no site assigned in OD, this will be blank as it is optional //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, IS data type (coded value for user-defined tables) //We will send one of the following values retrieved from the patient.Urgency field for treatment urgency: 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent seg.AddField(18,"pat.Urgency"); //PV1.19, Visit Number, CX data type, AptNum^CheckDigit^CheckDigitScheme^&AssignAuthorityID&IDType^VN (VN - Visit Number) //We will use AptNum, with check digit scheme M11, with the OIDRoot for an appt object (set in oidinternal), ID Type 'HL7', and 'VN' for Visit Number //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.6&HL7^VN| seg.AddField(19,"apt.AptNum"); //PV1.44, Admit Date/Time seg.AddField(44,"apt.AptDateTime"); #endregion PV1 - Patient Visit #region RGS - Resource Group seg=new HL7DefSegment(); msg.AddSegment(seg,5,SegmentNameHL7.RGS); //Fields------------------------------------------------------------------------------------------------------------- //RGS.1, Set ID, SI data type //Always 1, only one resourece group in our outbound SIU's seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //RGS.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); #endregion RGB - Resource Group #region AIL - Appointment Information - Location Resource //We will use the AIL segment to identify the clinic and operatory for the appointment seg=new HL7DefSegment(); msg.AddSegment(seg,6,false,true,SegmentNameHL7.AIL); //Fields------------------------------------------------------------------------------------------------------------- //AIL.1, Set ID, SI data type //Always 1, only one location identified by our SIU messsage seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //AIL.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); //AIL.3, Location Resource ID, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"apt.location"); //AIL.12, Filler Status Code, CWE data type //We will use this to send the confirmation status of the appointment //We will use the second component (text) and send the ItemName (confirm Name) from the definition table that is set for this appointment //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIL - Appointment Information - Location Resource #region AIP - Appointment Information - Personnel Resource //We will use the AIP segment to identify the dentist and hygienist on the appointment //If there is both a dentist and a hygienist assigned to an appointment, we will send two repetitions of the AIP segment seg=new HL7DefSegment(); msg.AddSegment(seg,7,true,true,SegmentNameHL7.AIP); //Fields------------------------------------------------------------------------------------------------------------- //AIP.1, Set ID, SI data type //This segment can repeat for an appt, sequence 1 will be the dentist, 2 will be the hygienist on the appt seg.AddField(1,"sequenceNum"); //AIP.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); //AIP.3, Personnel Resource ID, XCN data type //ProviderId^LastName^FirstName^MI^Suffix^Prefix //According to the HL7 standards, we are free to use the components as needed to identify the provider //We will use 'ProviderID' to send the OD ProvNum, LName, FName and the 'Prefix' component as the Abbr //We will retrieve the provider from the appointment and use AIP.4 to differentiate between the dentist and hygienist //Example: |1234^Abbott^Sarah^L^DMD^DrAbbott| seg.AddField(3,"prov.provIdNameLFM"); //AIP.4, Resource Type, CWE data type //We will send 'd' for dentist (apt.ProvNum) and 'h' for hygienist (apt.ProvHyg) seg.AddField(4,"prov.provType"); //AIP.12, Filler Status Code, CWE data type //We will use this to send the confirmation status of the appointment //We will use the second component (text) and send the ItemName (confirm Name) from the definition table that is set for this appointment //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIP - Appointment Information - Personnel Resource #endregion SIU - Schedule Information Unsolicited #region SRR - Schedule Request Response msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.SRR,MessageStructureHL7.SRR_S01,InOutHL7.Outgoing,8); #region MSH - Message Header seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application, HD data type, Namespace ID^Universal ID^Universal ID Type //We originally sent 'OD' in this field, but this is a HD data type, so should consist of three components //We will send the Open Dental HL7 root assigned to the office and stored in the oidinternal table with the IDType of Root //The recommeded value for this field in the table is ^2.16.840.1.113883.3.4337.1486.CustomerPatNum^HL7. //If the oidinternal row with IDType=Root does not have an IDRoot assigned, we will revert to sending 'OD' seg.AddField(2,"sendingApp"); //MSH.3, Sending Facility, not used //MSH.4, Receiving Application //This field will have to be a fixed text field and would have to be modified in a custom definition //if they want the outbound messages to have a specific receiving application identifier seg.AddField(4,"0361",DataTypeHL7.HD,"","NamespaceID^UniversalID^UniversalIDType"); //MSH.5, Receiving Facility, not used //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type, MSG data type //This segment should contain MessageType^EventType^MessageStructure, example SRR^S04^SRR_S04 seg.AddField(8,"messageType"); //MSH.9, Message Control ID //A GUID that uniquely identifies this message seg.AddField(9,"messageControlId"); //MSH.10, Processing ID, PT data type, Processing ID^Processing Mode //Processing mode is optional and not included, we will use P for production (P-Production, T-Training, D-Debugging) seg.AddField(10,"0103",DataTypeHL7.PT,"","P"); //MSH.11, Version ID, VID data type, Version ID^Internationalization Code^International Version ID //All components are optional, we will only send Version ID, which is currently 2.6 seg.AddField(11,"0104",DataTypeHL7.VID,"","2.6"); //MSH.15, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) //This is for enhanced acknowledgment mode, which we do not currently support, but we will send our mode just in case //With field MSH.15, Accept Ack Type, not present it will default to NE //Field MSH.15 set to AL means we will require an ack for every message that is sent, meaning it was processed successfully by the receiving application //But MSH.15 not present or null means we do not require a separate accept ACK message, just the accept and validate response meaning the message was accepted and processed seg.AddField(15,"0155",DataTypeHL7.ID,"","AL"); #endregion MSH - Message Header #region MSA - Message Acknowledgment seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.MSA); //Fields------------------------------------------------------------------------------------------------------------- //MSA.1, Acknowledgment Code seg.AddField(1,"ackCode"); //MSA.2, Message Control ID seg.AddField(2,"messageControlId"); #endregion MSA - Message Acknowledgment #region SCH - Schedule Activity Information seg=new HL7DefSegment(); msg.AddSegment(seg,2,false,true,SegmentNameHL7.SCH); //Fields------------------------------------------------------------------------------------------------------------- //SCH.1, Placer Appointment ID, EI data type, OD is the filler application //We will store the exernalAptId from an incoming SRM message ARQ segment if they send it to us, so send it here if we have one seg.AddField(1,"apt.externalAptID"); //SCH.2, Filler Appointment ID, EI data type, OD is the filler application //AptNum^^AssignAuthorityID^IDType //Example: |1234^^2.16.840.1.113883.3.4337.1486.6566.6^HL7| root.6 is appointment seg.AddField(2,"apt.AptNum"); //SCH.5, Schedule ID, CWE data type //We will use this to send the operatory name seg.AddField(5,"apt.operatory"); //SCH.7, Appointment Reason seg.AddField(7,"apt.Note"); //SCH.8, Appointment Type, CWE data type //Suggested values are Normal - Routine schedule request type, Tentative, or Complete - Request to add a completed appt //We will send Normal for all appointment statuses except complete. seg.AddField(8,"apt.type"); //SCH.16, Filler Contact Person, XCN data type //The OD user who created/modified the appointment seg.AddField(16,"apt.userOD"); //SCH.20, Entered by Person, XCN data type //The OD user who created/modified the appointment seg.AddField(20,"apt.userOD"); //SCH.25, Filler Status Code, CWE data type //This will signal to the other software the AptStatus of the appointment //Booked - ApptStatus.Scheduled or ApptStatus.ASAP, Complete - ApptStatus.Complete, Cancelled - ApptStatus.Broken or ApptStatus.UnscheduledList, Deleted (when deleted) seg.AddField(25,"apt.aptStatus"); #endregion SCH - Schedule Activity Information #region NTE - Notes and Comments seg=new HL7DefSegment(); msg.AddSegment(seg,2,true,true,SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.1, Set ID, SI data type //We will only ever send one NTE segment in outbound messages, so this will always be 1 seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //NTE.3, Comment, FT data type (formatted text) //We will send the appointment.Note value in this field for outbound messages //This is formatted text, so new line characters in the appointment.Note will be replaced with //the designated new line character sequence '\.br\' (where the '\' is the defined escape char, \ by default) seg.AddField(3,"apt.Note"); #endregion NTE - Notes and Comments #region PID - Patient Identification seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.1, Set ID, SI data type //"This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one" (HL7 v2.6 documentation) //We only send 1 PID segment in SRR's so this number will always be 1. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PID.2, Patient ID (retained for backward compatibility only). Defined as 'external' ID, we've always sent the ChartNumber in this field as the eCW patient ID //The standard will be to continue to use PID.2 for sending the ChartNumber, which was set from PID.4 of an incoming message. This could be the other software patient ID. seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient Identifier List, contains a list of identifiers separated by the repetition character (usually ~) //PatientID^IDCheckDigit^CheckDigitScheme^AssigningAuthority^IDTypeCode //Assigning Authority is a HD data type and is composed of 3 subcomponents, NamespaceID&UniversalID&UniversalIDType //Sub-component 2, UniversalID, we will use the OID root for a Patient object stored in the oidinternal table //If retrieved from OD it will be 2.16.840.1.113883.3.4337.1486.CustomerNumber.2 //Sub-component 3, Universal ID Type, will be 'HL7' since our root is registered with HL7. //The IDTypeCode will be 'PI', Patient internal identifier. //We will also get all of the identifiers in the oidexternals table for the patient and create repetitions for each external ID using the IDExternal and RootExternal //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| seg.AddField(3,"patientIds"); //PID.4, Alternate Patient ID, (retained for backward compatibility only). //We've used PID.4 for sending the OD PatNum in the past and will continue to send this in PID.4 seg.AddField(4,"pat.PatNum"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race, CWE - Coded with Exceptions data type //This data type is composed of 9 fields, Code^Description^CodeSystem^AlternateCode^AltDescript^AltCodeSystem^CodeSystemVersionID^AltCodeSystemVersionID^OriginalText //We will send Code^Description^CodeSystem^^^^CodeSystemVersionID //The race field can repeat, so any number of races can be sent and will come from the patientrace table. //Example: 2106-3^White^CDCREC^^^^1~2186-5^NotHispanic^CDCREC^^^^1 seg.AddField(10,"pat.Race"); //PID.11, Patient Address //PID.11.20, comment, will come from the patient.AddrNote column and each new line character will be replaced with '\.br\' where the '\' is the defined escape char //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home, XTN data type (can repeat) //This field will also contain the patient's email address as a repetition as well as the WirelessPhone as a repetition //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business, XTN data type (can repeat) //We will send just one repetition, the WkPhone, but we will adhere to the data type //WPN=Work Number //Example: ^WPN^PH^^^503^3635432 seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status //S-Single, M-Married, W-Widowed, D-Divorced seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); #endregion PID - Patient Identification #region PV1 - Patient Visit seg=new HL7DefSegment(); msg.AddSegment(seg,4,false,true,SegmentNameHL7.PV1); //Fields------------------------------------------------------------------------------------------------------------- //PV1.1, Set ID - PV1 //See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per SRR message. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PV1.2, Patient Class, IS data type (coded value for user-defined tables) //Suggested values E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown //We will defualt to send 'O' for outpatient for outbound messages, but for incoming we will use this field for pat.GradeLevel if it's an integer from 1-15 seg.AddField(2,"0004",DataTypeHL7.IS,"","O"); //PV1.3, Assigned Patient Location, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"apt.location"); //PV1.7, Attending/Primary Care Doctor, ProviderId^LastName^FirstName^MI seg.AddField(7,"0010",DataTypeHL7.XCN,"prov.provIdNameLFM",""); //PV1.11, Temporary Location, PL - Person Location data type: PointOfCare^^^^^Person Location Type //Filled with the "Site (or Grade School)" value in the database, using 'S' to designate the location type site and the site.Description value //If no site assigned in OD, this will be blank as it is optional //Example: |West Salem Elementary^^^^^S| ('S' for site) seg.AddField(11,"pat.site"); //PV1.18, Patient Type, IS data type (coded value for user-defined tables) //We will send one of the following values retrieved from the patient.Urgency field for treatment urgency: 0 - Unknown, 1 - NoProblems, 2 - NeedsCare, 3 - Urgent seg.AddField(18,"pat.Urgency"); //PV1.19, Visit Number, CX data type, AptNum^CheckDigit^CheckDigitScheme^&AssignAuthorityID&IDType^VN (VN - Visit Number) //We will use AptNum, with check digit scheme M11, with the OIDRoot for an appt object (set in oidinternal), ID Type 'HL7', and 'VN' for Visit Number //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.6&HL7^VN| seg.AddField(19,"apt.AptNum"); //PV1.44, Admit Date/Time seg.AddField(44,"apt.AptDateTime"); #endregion PV1 - Patient Visit #region RGS - Resource Group seg=new HL7DefSegment(); msg.AddSegment(seg,5,SegmentNameHL7.RGS); //Fields------------------------------------------------------------------------------------------------------------- //RGS.1, Set ID, SI data type //Always 1, only one resourece group in our outbound SRR's seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //RGS.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); #endregion RGB - Resource Group #region AIL - Appointment Information - Location Resource //We will use the AIL segment to identify the clinic and operatory for the appointment seg=new HL7DefSegment(); msg.AddSegment(seg,6,false,true,SegmentNameHL7.AIL); //Fields------------------------------------------------------------------------------------------------------------- //AIL.1, Set ID, SI data type //Always 1, only one location identified by our SRR messsage seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //AIL.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); //AIL.3, Location Resource ID, PL - Person Location Data Type: Point of Care^Room^^Facility^^Person Location Type //Facility is a HD data type, so Namespace&ID&IDType. We will just use &PracticeTitle with no namespace or IDType. //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) seg.AddField(3,"apt.location"); //AIL.12, Filler Status Code, CWE data type //We will use this to send the confirmation status of the appointment //We will use the second component (text) and send the ItemName (confirm Name) from the definition table that is set for this appointment //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIL - Appointment Information - Location Resource #region AIP - Appointment Information - Personnel Resource //We will use the AIP segment to identify the dentist and hygienist on the appointment //If there is both a dentist and a hygienist assigned to an appointment, we will send two repetitions of the AIP segment seg=new HL7DefSegment(); msg.AddSegment(seg,7,true,true,SegmentNameHL7.AIP); //Fields------------------------------------------------------------------------------------------------------------- //AIP.1, Set ID, SI data type //This segment can repeat for an appt, sequence 1 will be the dentist, 2 will be the hygienist on the appt seg.AddField(1,"sequenceNum"); //AIP.2, Segment Action Code, ID data type //A-Add/Insert, D-Delete, U-Update, X-No Change seg.AddField(2,"segmentAction"); //AIP.3, Personnel Resource ID, XCN data type //ProviderId^LastName^FirstName^MI^Suffix^Prefix //According to the HL7 standards, we are free to use the components as needed to identify the provider //We will use 'ProviderID' to send the OD ProvNum, LName, FName and the 'Prefix' component as the Abbr //We will retrieve the provider from the appointment and use AIP.4 to differentiate between the dentist and hygienist //Example: |1234^Abbott^Sarah^L^DMD^DrAbbott| seg.AddField(3,"prov.provIdNameLFM"); //AIP.4, Resource Type, CWE data type //We will send 'd' for dentist (apt.ProvNum) and 'h' for hygienist (apt.ProvHyg) seg.AddField(4,"prov.provType"); //AIP.12, Filler Status Code, CWE data type //We will use this to send the confirmation status of the appointment //We will use the second component (text) and send the ItemName (confirm Name) from the definition table that is set for this appointment //Example: |^Appointment Confirmed| seg.AddField(12,"apt.confirmStatus"); #endregion AIP - Appointment Information - Personnel Resource #endregion SRR - Schedule Request Response #endregion Outbound Messages return def; }
//Open \\SERVERFILES\storage\OPEN DENTAL\Programmers Documents\Standards (X12, ADA, etc)\HL7\Version2.6\V26_CH02_Control_M4_JAN2007.doc //At the top of page 33, there are rules for the recipient. //Basically, they state that parsing should not fail just because there are extra unexpected items. //And parsing should also not fail if expected items are missing. public static void Process(MessageHL7 msg, bool isVerboseLogging) { HL7MsgCur = new HL7Msg(); HL7MsgCur.HL7Status = HL7MessageStatus.InFailed; //it will be marked InProcessed once data is inserted. HL7MsgCur.MsgText = msg.ToString(); HL7MsgCur.PatNum = 0; HL7MsgCur.AptNum = 0; List <HL7Msg> hl7Existing = HL7Msgs.GetOneExisting(HL7MsgCur); if (hl7Existing.Count > 0) //This message is already in the db { HL7MsgCur.HL7MsgNum = hl7Existing[0].HL7MsgNum; HL7Msgs.UpdateDateTStamp(HL7MsgCur); msg.ControlId = HL7Msgs.GetControlId(HL7MsgCur); return; } else { //Insert as InFailed until processing is complete. Update once complete, PatNum will have correct value, AptNum will have correct value if SIU message or 0 if ADT, and status changed to InProcessed HL7Msgs.Insert(HL7MsgCur); } IsVerboseLogging = isVerboseLogging; IsNewPat = false; HL7Def def = HL7Defs.GetOneDeepEnabled(); if (def == null) { HL7MsgCur.Note = "Could not process HL7 message. No HL7 definition is enabled."; HL7Msgs.Update(HL7MsgCur); throw new Exception("Could not process HL7 message. No HL7 definition is enabled."); } HL7DefMessage hl7defmsg = null; for (int i = 0; i < def.hl7DefMessages.Count; i++) { if (def.hl7DefMessages[i].MessageType == msg.MsgType) //&& def.hl7DefMessages[i].EventType==msg.EventType) { //Ignoring event type for now, we will treat all ADT's and SIU's the same { hl7defmsg = def.hl7DefMessages[i]; } } if (hl7defmsg == null) //No message definition matches this message's MessageType and EventType { HL7MsgCur.Note = "Could not process HL7 message. No definition for this type of message in the enabled HL7Def."; HL7Msgs.Update(HL7MsgCur); throw new Exception("Could not process HL7 message. No definition for this type of message in the enabled HL7Def."); } string chartNum = null; long patNum = 0; DateTime birthdate = DateTime.MinValue; string patLName = null; string patFName = null; bool isExistingPID = false; //Needed to add note to hl7msg table if there isn't a PID segment in the message. //Get patient in question, incoming messages must have a PID segment so use that to find the pat in question for (int s = 0; s < hl7defmsg.hl7DefSegments.Count; s++) { if (hl7defmsg.hl7DefSegments[s].SegmentName != SegmentNameHL7.PID) { continue; } int pidOrder = hl7defmsg.hl7DefSegments[s].ItemOrder; //we found the PID segment in the def, make sure it exists in the msg if (msg.Segments.Count <= pidOrder || //If the number of segments in the message is less than the item order of the PID segment msg.Segments[pidOrder].GetField(0).ToString() != "PID" //Or if the segment we expect to be the PID segment is not the PID segment ) { break; } isExistingPID = true; for (int f = 0; f < hl7defmsg.hl7DefSegments[s].hl7DefFields.Count; f++) //Go through fields of PID segment and get patnum, chartnum, patient name, and/or birthdate to locate patient { if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.ChartNumber") { int chartNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos; chartNum = msg.Segments[pidOrder].Fields[chartNumOrdinal].ToString(); } else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.PatNum") { int patNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos; patNum = PIn.Long(msg.Segments[pidOrder].Fields[patNumOrdinal].ToString()); } else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.birthdateTime") { int patBdayOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos; birthdate = FieldParser.DateTimeParse(msg.Segments[pidOrder].Fields[patBdayOrdinal].ToString()); } else if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "pat.nameLFM") { int patNameOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos; patLName = msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal, 0); patFName = msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal, 1); } } } if (!isExistingPID) { HL7MsgCur.Note = "Could not process the HL7 message due to missing PID segment."; HL7Msgs.Update(HL7MsgCur); throw new Exception("Could not process HL7 message. Could not process the HL7 message due to missing PID segment."); } //We now have patnum, chartnum, patname, and/or birthdate so locate pat Patient pat = null; Patient patOld = null; if (patNum != 0) { pat = Patients.GetPat(patNum); } if (def.InternalType != "eCWStandalone" && pat == null) { IsNewPat = true; } //In eCWstandalone integration, if we couldn't locate patient by patNum or patNum was 0 then pat will still be null so try to locate by chartNum if chartNum is not null if (def.InternalType == "eCWStandalone" && chartNum != null) { pat = Patients.GetPatByChartNumber(chartNum); } //In eCWstandalone integration, if pat is still null we need to try to locate patient by name and birthdate if (def.InternalType == "eCWStandalone" && pat == null) { long patNumByName = Patients.GetPatNumByNameAndBirthday(patLName, patFName, birthdate); //If patNumByName is 0 we couldn't locate by patNum, chartNum or name and birthdate so this message must be for a new patient if (patNumByName == 0) { IsNewPat = true; } else { pat = Patients.GetPat(patNumByName); patOld = pat.Copy(); pat.ChartNumber = chartNum; //from now on, we will be able to find pat by chartNumber Patients.Update(pat, patOld); } } if (IsNewPat) { pat = new Patient(); if (chartNum != null) { pat.ChartNumber = chartNum; } if (patNum != 0) { pat.PatNum = patNum; pat.Guarantor = patNum; } pat.PriProv = PrefC.GetLong(PrefName.PracticeDefaultProv); pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType); } else { patOld = pat.Copy(); } //Update hl7msg table with correct PatNum for this message HL7MsgCur.PatNum = pat.PatNum; HL7Msgs.Update(HL7MsgCur); //If this is a message that contains an SCH segment, loop through to find the AptNum. Pass it to the other segments that will need it. long aptNum = 0; for (int s = 0; s < hl7defmsg.hl7DefSegments.Count; s++) { if (hl7defmsg.hl7DefSegments[s].SegmentName != SegmentNameHL7.SCH) { continue; } //we found the SCH segment int schOrder = hl7defmsg.hl7DefSegments[s].ItemOrder; for (int f = 0; f < hl7defmsg.hl7DefSegments[s].hl7DefFields.Count; f++) //Go through fields of SCH segment and get AptNum { if (hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName == "apt.AptNum") { int aptNumOrdinal = hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos; aptNum = PIn.Long(msg.Segments[schOrder].Fields[aptNumOrdinal].ToString()); } } } //We now have a patient object , either loaded from the db or new, and aptNum so process this message for this patient //We need to insert the pat to get a patnum so we can compare to guar patnum to see if relationship to guar is self if (IsNewPat) { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information); } if (pat.PatNum == 0) { pat.PatNum = Patients.Insert(pat, false); } else { pat.PatNum = Patients.Insert(pat, true); } patOld = pat.Copy(); } for (int i = 0; i < hl7defmsg.hl7DefSegments.Count; i++) { try { SegmentHL7 seg = msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName, !hl7defmsg.hl7DefSegments[i].IsOptional); if (seg != null) //null if segment was not found but is optional { ProcessSeg(pat, aptNum, hl7defmsg.hl7DefSegments[i], seg, msg); } } catch (ApplicationException ex) { //Required segment was missing, or other error. HL7MsgCur.Note = "Could not process this HL7 message. " + ex; HL7Msgs.Update(HL7MsgCur); throw new Exception("Could not process HL7 message. " + ex); } } //We have processed the message so now update or insert the patient if (pat.FName == "" || pat.LName == "") { EventLog.WriteEntry("OpenDentHL7", "Patient demographics not processed due to missing first or last name. PatNum:" + pat.PatNum.ToString() , EventLogEntryType.Information); HL7MsgCur.Note = "Patient demographics not processed due to missing first or last name. PatNum:" + pat.PatNum.ToString(); HL7Msgs.Update(HL7MsgCur); return; } if (IsNewPat) { if (pat.Guarantor == 0) { pat.Guarantor = pat.PatNum; Patients.Update(pat, patOld); } else { Patients.Update(pat, patOld); } } else { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information); } Patients.Update(pat, patOld); } HL7MsgCur.HL7Status = HL7MessageStatus.InProcessed; HL7Msgs.Update(HL7MsgCur); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null //HL7Def def=HL7Defs.GetInternalFromDb("eCWStandalone"); if (def == null) //wasn't in the database { def = new HL7Def(); def.IsNew = true; def.Description = "eCW Standalone"; def.ModeTx = ModeTxHL7.File; def.IncomingFolder = ""; def.OutgoingFolder = ""; def.IncomingPort = ""; def.OutgoingIpPort = ""; def.FieldSeparator = "|"; def.ComponentSeparator = "^"; def.SubcomponentSeparator = "&"; def.RepetitionSeparator = "~"; def.EscapeCharacter = @"\"; def.IsInternal = true; def.InternalType = "eCWStandalone"; def.InternalTypeVersion = Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled = false; def.Note = ""; def.ShowDemographics = HL7ShowDemographics.ChangeAndAdd; def.ShowAccount = true; def.ShowAppts = true; } def.hl7DefMessages = new List <HL7DefMessage>(); //so that if this is called repeatedly, it won't pile on duplicate messages. //in either case, now get all child objects, which can't be in the database. //---------------------------------------------------------------------------------------------------------------------------------- //eCW incoming patient information (ADT). HL7DefMessage msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.ADT, EventTypeHL7.A04, InOutHL7.Incoming, 0); //MSH segment------------------------------------------------------------------ HL7DefSegment seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //PID segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 2, SegmentNameHL7.PID); //PID.2, Patient ID seg.AddField(2, "pat.ChartNumber"); //PID.4, Alternate Patient ID, PID.4 is not saved with using standalone integration //PID.5, Patient Name seg.AddField(5, "pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7, "pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8, "pat.Gender"); //PID.10, Race seg.AddField(10, "pat.Race"); //PID.11, Patient Address seg.AddField(11, "pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13, "pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14, "pat.WkPhone"); //PID.16, Marital Status seg.AddField(16, "pat.Position"); //PID.19, SSN - Patient seg.AddField(19, "pat.SSN"); //PID.22, Fee Schedule seg.AddField(22, "pat.FeeSched"); //GT1 segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 5, SegmentNameHL7.GT1); //GT1.2, Guarantor Number seg.AddField(2, "guar.ChartNumber"); //GT1.3, Guarantor Name seg.AddField(3, "guar.nameLFM"); //GT1.5, Guarantor Address seg.AddField(5, "guar.addressCityStateZip"); //GT1.6, Guarantor Phone Number - Home seg.AddField(6, "guar.HmPhone"); //GT1.7, Guarantor Phone Number - Business seg.AddField(7, "guar.WkPhone"); //GT1.8, Guarantor Date/Time of Birth seg.AddField(8, "guar.birthdateTime"); //GT1.9, Guarantor Administrative Sex seg.AddField(9, "guar.Gender"); //GT1.12, Guarantor SSN seg.AddField(12, "guar.SSN"); return(def); }
///<summary>Inserts one HL7Def into the database. Returns the new priKey.</summary> public static long Insert(HL7Def hL7Def) { return(Insert(hL7Def, false)); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if (def == null) //wasn't in the database { def = new HL7Def(); def.IsNew = true; def.Description = "Centricity"; def.ModeTx = ModeTxHL7.File; def.IncomingFolder = ""; def.OutgoingFolder = ""; def.IncomingPort = ""; def.OutgoingIpPort = ""; def.SftpInSocket = ""; def.SftpUsername = ""; def.SftpPassword = ""; def.FieldSeparator = "|"; def.ComponentSeparator = "^"; def.SubcomponentSeparator = "&"; def.RepetitionSeparator = "~"; def.EscapeCharacter = @"\"; def.IsInternal = true; def.InternalType = HL7InternalType.Centricity; def.InternalTypeVersion = Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled = false; def.Note = ""; def.ShowDemographics = HL7ShowDemographics.ChangeAndAdd; def.ShowAccount = true; def.ShowAppts = true; def.IsQuadAsToothNum = false; } def.hl7DefMessages = new List <HL7DefMessage>(); HL7DefMessage msg = new HL7DefMessage(); HL7DefSegment seg = new HL7DefSegment(); #region Outbound Messages #region DFT - Detailed Financial Transaction //======================================================================================================================= //Detail financial transaction (DFT) def.AddMessage(msg, MessageTypeHL7.DFT, MessageStructureHL7.DFT_P03, InOutHL7.Outgoing, 2); //MSH (Message Header) segment------------------------------------------------- msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //HL7 documentation says field 1 is Field Separator. "This field contains the separator between the segment ID and the first real field. As such it serves as the separator and defines the character to be used as a separator for the rest of the message." (HL7 v2.6 documentation) The separator is usually | (pipes) and is part of field 0, which is the segment ID followed by a |. Encoding Characters is the first real field, so it will be numbered starting with 1 in our def. //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1, "separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2, DataTypeHL7.HD, "OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4, DataTypeHL7.HD, def.Description); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6, "dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10, DataTypeHL7.PT, "P"); //MSH.11, Version ID seg.AddFieldFixed(11, DataTypeHL7.VID, "2.3"); //MSH.16, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) seg.AddFieldFixed(15, DataTypeHL7.ID, "NE"); //EVN (Event Type) segment----------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.EVN); //EVN.1, Event Type, example P03 seg.AddField(1, "eventType"); //EVN.2, Recorded Date/Time seg.AddField(2, "dateTime.Now"); //EVN.3, Event Reason Code seg.AddFieldFixed(3, DataTypeHL7.IS, "01"); //PID (Patient Identification) segment----------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 2, SegmentNameHL7.PID); //PID.1, Sequence Number (1 for DFT's) "This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one, for the second occurrence, the sequence number shall be two, etc." (HL7 v2.6 documentation) We only send 1 PID segment in DFT's so this number will always be 1. seg.AddFieldFixed(1, DataTypeHL7.SI, "1"); //PID.2, Patient ID (External) seg.AddField(2, "pat.ChartNumber"); //PID.3, Patient ID (Internal) seg.AddField(3, "pat.PatNum"); //PV1 (Patient Visit) segment-------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 3, SegmentNameHL7.PV1); //PV1.1, Set ID - PV1 (1 for DFT's) See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per DFT message. seg.AddFieldFixed(1, DataTypeHL7.SI, "1"); //PV1.2, Patient Class (E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown) We will just send O for outpatient for every DFT message. seg.AddFieldFixed(2, DataTypeHL7.IS, "O"); //todo: ClinicNum? //PV1.3, Assigned Patient Location //PV1.7, Attending/Primary Care Doctor seg.AddField(7, "prov.provIdNameLFM"); //todo: Referring Dr? //PV1.8, Referring Doctor //PV1.19, Visit Number seg.AddField(19, "apt.AptNum"); //PV1.44, Admit Date/Time seg.AddField(44, "proc.procDateTime"); //PV1.50, Alternate Visit ID //FT1 (Financial Transaction Information) segment------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 4, true, true, SegmentNameHL7.FT1); //FT1.1, Sequence Number (starts with 1) seg.AddField(1, "sequenceNum"); //FT1.2, Transaction ID seg.AddField(2, "proc.ProcNum"); //FT1.4, Transaction Date (YYYYMMDDHHMMSS) seg.AddField(4, "proc.procDateTime"); //FT1.5, Transaction Posting Date (YYYYMMDDHHMMSS) seg.AddField(5, "proc.procDateTime"); //FT1.6, Transaction Type seg.AddFieldFixed(6, DataTypeHL7.IS, "CG"); //FT1.10, Transaction Quantity seg.AddFieldFixed(10, DataTypeHL7.NM, "1.0"); //FT1.11, Transaction Amount Extended (Total fee to charge for this procedure, independent of transaction quantity) seg.AddField(11, "proc.ProcFee"); //FT1.12, Transaction Amount Unit (Fee for this procedure for each transaction quantity) seg.AddField(12, "proc.ProcFee"); //todo: ClinicNum? //FT1.16, Assigned Patient Location //FT1.19, Diagnosis Code seg.AddField(19, "proc.DiagnosticCode"); //FT1.21, Ordering Provider seg.AddField(21, "prov.provIdNameLFM"); //FT1.22, Unit Cost (procedure fee) seg.AddField(22, "proc.ProcFee"); //FT1.25, Procedure Code seg.AddField(25, "proccode.ProcCode"); //FT1.26, Modifiers (treatment area) seg.AddField(26, "proc.toothSurfRange"); //DG1 (Diagnosis) segment is optional, skip for now //PR1 (Procedures) segment is optional, skip for now #endregion DFT - Detailed Financial Transaction #endregion Outbound Messages return(def); }
///<summary>Only for creating the IN1 segment(s).</summary> public static string GenerateFieldIN1(HL7Def def,string fieldName,int sequenceNum,PatPlan patplanCur,InsSub inssubCur,InsPlan insplanCur,Carrier carrierCur,int patplanCount,Patient patSub) { switch(fieldName) { #region Carrier case "carrier.addressCityStateZip": if(carrierCur==null) { return ""; } return gConcat(def.ComponentSeparator,carrierCur.Address,carrierCur.Address2,carrierCur.City,carrierCur.State,carrierCur.Zip); case "carrier.CarrierName": if(carrierCur==null) { return ""; } return carrierCur.CarrierName; case "carrier.ElectID": if(carrierCur==null) { return ""; } return carrierCur.ElectID; case "carrier.Phone": //Example: ^WPN^PH^^^800^3635432 if(carrierCur==null) { return ""; } string carrierPh=gXTN(carrierCur.Phone,10); if(carrierPh=="") { return ""; } return gConcat(def.ComponentSeparator,"","WPN","PH","","",carrierPh.Substring(0,3),carrierPh.Substring(3));//carrierPh guaranteed to be 10 digits or blank #endregion Carrier #region InsPlan case "insplan.cob": if(insplanCur==null) { return ""; } if(patplanCount>1) { return "CO"; } return "IN"; case "insplan.coverageType": if(insplanCur==null) { return ""; } if(insplanCur.IsMedical) { return "M"; } return "D"; case "insplan.empName": if(insplanCur==null) { return ""; } return Employers.GetName(insplanCur.EmployerNum);//will return empty string if EmployerNum=0 or not found case "insplan.GroupName": if(insplanCur==null) { return ""; } return insplanCur.GroupName; case "insplan.GroupNum": if(insplanCur==null) { return ""; } return insplanCur.GroupNum; case "insplan.planNum": //Example: 2.16.840.1.113883.3.4337.1486.6566.7.1234 //If OID for insplan is not set, then it will be ODInsPlan.1234 (where 1234=PlanNum) if(insplanCur==null) { return ""; } OIDInternal insplanOid=OIDInternals.GetForType(IdentifierType.InsPlan);//returns root+".7" or whatever they have set in their oidinternal table for type InsPlan string insplanOidRoot="ODInsPlan"; if(insplanOid!=null && insplanOid.IDRoot!="") { insplanOidRoot=insplanOid.IDRoot; } return insplanOidRoot+"."+insplanCur.PlanNum;//tack on "."+PlanNum for extension case "insplan.PlanType": if(insplanCur==null) { return ""; } if(insplanCur.PlanType=="p") { return "PPO Percentage"; } if(insplanCur.PlanType=="f") { return "Medicaid or Flat Copay"; } if(insplanCur.PlanType=="c") { return "Capitation"; } return "Category Percentage"; #endregion InsPlan #region InsSub case "inssub.AssignBen": if(inssubCur==null) { return ""; } if(inssubCur.AssignBen) { return "Y"; } return "N"; case "inssub.DateEffective": if(inssubCur==null || inssubCur.DateEffective==DateTime.MinValue) { return ""; } return gDTM(inssubCur.DateEffective,8); case "inssub.DateTerm": if(inssubCur==null || inssubCur.DateTerm==DateTime.MinValue) { return ""; } return gDTM(inssubCur.DateTerm,8); case "inssub.ReleaseInfo": if(inssubCur==null) { return ""; } if(inssubCur.ReleaseInfo) { return "Y"; } return "N"; case "inssub.subAddrCityStateZip": if(patSub==null) { return ""; } return gConcat(def.ComponentSeparator,patSub.Address,patSub.Address2,patSub.City,patSub.State,patSub.Zip); case "inssub.subBirthdate": if(patSub==null) { return ""; } return gDTM(patSub.Birthdate,8); case "inssub.SubscriberID": if(inssubCur==null) { return ""; } return inssubCur.SubscriberID; case "inssub.subscriberName": if(patSub==null) { return ""; } return gConcat(def.ComponentSeparator,patSub.LName,patSub.FName,patSub.MiddleI); #endregion InsSub #region PatPlan case "patplan.Ordinal": if(patplanCur==null) { return ""; } return patplanCur.Ordinal.ToString(); case "patplan.policyNum": if(patplanCur==null || inssubCur==null) { return ""; } if(patplanCur.PatID!="") { return patplanCur.PatID; } return inssubCur.SubscriberID; case "patplan.subRelationToPat": //SEL-Self, SPO-Spouse, DOM-LifePartner, CHD-Child (PAR-Parent), EME-Employee (EMR-Employer) //DEP-HandicapDep (GRD-Guardian), DEP-Dependent, OTH-SignifOther (OTH-Other), OTH-InjuredPlantiff //We store relationship to subscriber and they want subscriber's relationship to patient, therefore //Relat.Child will return "PAR" for Parent, Relat.Employee will return "EMR" for Employer, and Relat.HandicapDep and Relat.Dependent will return "GRD" for Guardian //Example: |PAR^Parent| if(patplanCur==null) { return ""; } if(patplanCur.Relationship==Relat.Self) { return gConcat(def.ComponentSeparator,"SEL","Self"); } if(patplanCur.Relationship==Relat.Spouse) { return gConcat(def.ComponentSeparator,"SPO","Spouse"); } if(patplanCur.Relationship==Relat.LifePartner) { return gConcat(def.ComponentSeparator,"DOM","Life Partner"); } if(patplanCur.Relationship==Relat.Child) { return gConcat(def.ComponentSeparator,"PAR","Parent"); } if(patplanCur.Relationship==Relat.Employee) { return gConcat(def.ComponentSeparator,"EMR","Employer"); } if(patplanCur.Relationship==Relat.Dependent || patplanCur.Relationship==Relat.HandicapDep) { return gConcat(def.ComponentSeparator,"GRD","Guardian"); } //if Relat.SignifOther or Relat.InjuredPlaintiff or any others return gConcat(def.ComponentSeparator,"OTH","Other"); #endregion PatPlan case "sequenceNum": return sequenceNum.ToString(); default: return ""; } }
///<summary>Updates one HL7Def in the database. Uses an old object to compare to, and only alters changed fields. This prevents collisions and concurrency problems in heavily used tables. Returns true if an update occurred.</summary> public static bool Update(HL7Def hL7Def, HL7Def oldHL7Def) { string command = ""; if (hL7Def.Description != oldHL7Def.Description) { if (command != "") { command += ","; } command += "Description = '" + POut.String(hL7Def.Description) + "'"; } if (hL7Def.ModeTx != oldHL7Def.ModeTx) { if (command != "") { command += ","; } command += "ModeTx = " + POut.Int((int)hL7Def.ModeTx) + ""; } if (hL7Def.IncomingFolder != oldHL7Def.IncomingFolder) { if (command != "") { command += ","; } command += "IncomingFolder = '" + POut.String(hL7Def.IncomingFolder) + "'"; } if (hL7Def.OutgoingFolder != oldHL7Def.OutgoingFolder) { if (command != "") { command += ","; } command += "OutgoingFolder = '" + POut.String(hL7Def.OutgoingFolder) + "'"; } if (hL7Def.IncomingPort != oldHL7Def.IncomingPort) { if (command != "") { command += ","; } command += "IncomingPort = '" + POut.String(hL7Def.IncomingPort) + "'"; } if (hL7Def.OutgoingIpPort != oldHL7Def.OutgoingIpPort) { if (command != "") { command += ","; } command += "OutgoingIpPort = '" + POut.String(hL7Def.OutgoingIpPort) + "'"; } if (hL7Def.FieldSeparator != oldHL7Def.FieldSeparator) { if (command != "") { command += ","; } command += "FieldSeparator = '" + POut.String(hL7Def.FieldSeparator) + "'"; } if (hL7Def.ComponentSeparator != oldHL7Def.ComponentSeparator) { if (command != "") { command += ","; } command += "ComponentSeparator = '" + POut.String(hL7Def.ComponentSeparator) + "'"; } if (hL7Def.SubcomponentSeparator != oldHL7Def.SubcomponentSeparator) { if (command != "") { command += ","; } command += "SubcomponentSeparator = '" + POut.String(hL7Def.SubcomponentSeparator) + "'"; } if (hL7Def.RepetitionSeparator != oldHL7Def.RepetitionSeparator) { if (command != "") { command += ","; } command += "RepetitionSeparator = '" + POut.String(hL7Def.RepetitionSeparator) + "'"; } if (hL7Def.EscapeCharacter != oldHL7Def.EscapeCharacter) { if (command != "") { command += ","; } command += "EscapeCharacter = '" + POut.String(hL7Def.EscapeCharacter) + "'"; } if (hL7Def.IsInternal != oldHL7Def.IsInternal) { if (command != "") { command += ","; } command += "IsInternal = " + POut.Bool(hL7Def.IsInternal) + ""; } if (hL7Def.InternalType != oldHL7Def.InternalType) { if (command != "") { command += ","; } command += "InternalType = '" + POut.String(hL7Def.InternalType.ToString()) + "'"; } if (hL7Def.InternalTypeVersion != oldHL7Def.InternalTypeVersion) { if (command != "") { command += ","; } command += "InternalTypeVersion = '" + POut.String(hL7Def.InternalTypeVersion) + "'"; } if (hL7Def.IsEnabled != oldHL7Def.IsEnabled) { if (command != "") { command += ","; } command += "IsEnabled = " + POut.Bool(hL7Def.IsEnabled) + ""; } if (hL7Def.Note != oldHL7Def.Note) { if (command != "") { command += ","; } command += "Note = " + DbHelper.ParamChar + "paramNote"; } if (hL7Def.HL7Server != oldHL7Def.HL7Server) { if (command != "") { command += ","; } command += "HL7Server = '" + POut.String(hL7Def.HL7Server) + "'"; } if (hL7Def.HL7ServiceName != oldHL7Def.HL7ServiceName) { if (command != "") { command += ","; } command += "HL7ServiceName = '" + POut.String(hL7Def.HL7ServiceName) + "'"; } if (hL7Def.ShowDemographics != oldHL7Def.ShowDemographics) { if (command != "") { command += ","; } command += "ShowDemographics = " + POut.Int((int)hL7Def.ShowDemographics) + ""; } if (hL7Def.ShowAppts != oldHL7Def.ShowAppts) { if (command != "") { command += ","; } command += "ShowAppts = " + POut.Bool(hL7Def.ShowAppts) + ""; } if (hL7Def.ShowAccount != oldHL7Def.ShowAccount) { if (command != "") { command += ","; } command += "ShowAccount = " + POut.Bool(hL7Def.ShowAccount) + ""; } if (hL7Def.IsQuadAsToothNum != oldHL7Def.IsQuadAsToothNum) { if (command != "") { command += ","; } command += "IsQuadAsToothNum = " + POut.Bool(hL7Def.IsQuadAsToothNum) + ""; } if (hL7Def.LabResultImageCat != oldHL7Def.LabResultImageCat) { if (command != "") { command += ","; } command += "LabResultImageCat = " + POut.Long(hL7Def.LabResultImageCat) + ""; } if (hL7Def.SftpUsername != oldHL7Def.SftpUsername) { if (command != "") { command += ","; } command += "SftpUsername = '******'"; } if (hL7Def.SftpPassword != oldHL7Def.SftpPassword) { if (command != "") { command += ","; } command += "SftpPassword = '******'"; } if (hL7Def.SftpInSocket != oldHL7Def.SftpInSocket) { if (command != "") { command += ","; } command += "SftpInSocket = '" + POut.String(hL7Def.SftpInSocket) + "'"; } if (hL7Def.HasLongDCodes != oldHL7Def.HasLongDCodes) { if (command != "") { command += ","; } command += "HasLongDCodes = " + POut.Bool(hL7Def.HasLongDCodes) + ""; } if (hL7Def.IsProcApptEnforced != oldHL7Def.IsProcApptEnforced) { if (command != "") { command += ","; } command += "IsProcApptEnforced = " + POut.Bool(hL7Def.IsProcApptEnforced) + ""; } if (command == "") { return(false); } if (hL7Def.Note == null) { hL7Def.Note = ""; } OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, POut.StringParam(hL7Def.Note)); command = "UPDATE hl7def SET " + command + " WHERE HL7DefNum = " + POut.Long(hL7Def.HL7DefNum); Db.NonQ(command, paramNote); return(true); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null //HL7Def def=HL7Defs.GetInternalFromDb("eCWTight"); if (def == null) //wasn't in the database { def = new HL7Def(); def.IsNew = true; def.Description = "eCW Tight"; def.ModeTx = ModeTxHL7.File; def.IncomingFolder = ""; def.OutgoingFolder = ""; def.IncomingPort = ""; def.OutgoingIpPort = ""; def.SftpInSocket = ""; def.SftpUsername = ""; def.SftpPassword = ""; def.FieldSeparator = "|"; def.ComponentSeparator = "^"; def.SubcomponentSeparator = "&"; def.RepetitionSeparator = "~"; def.EscapeCharacter = @"\"; def.IsInternal = true; def.InternalType = HL7InternalType.eCWTight; def.InternalTypeVersion = Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled = false; def.Note = ""; def.ShowDemographics = HL7ShowDemographics.Hide; def.ShowAccount = false; def.ShowAppts = false; def.IsQuadAsToothNum = false; } def.hl7DefMessages = new List <HL7DefMessage>(); //so that if this is called repeatedly, it won't pile on duplicate messages. //in either case, now get all child objects, which can't be in the database. #region Inbound Messages #region ADT - Patient Demographics (Admits, Discharges, and Transfers) //====================================================================================================================== //eCW incoming patient information (ADT). HL7DefMessage msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.ADT, MessageStructureHL7.ADT_A01, InOutHL7.Incoming, 0); //MSH segment------------------------------------------------------------------ HL7DefSegment seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //PID segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 2, SegmentNameHL7.PID); //PID.2, Patient ID seg.AddField(2, "pat.PatNum"); //PID.4, Alternate Patient ID seg.AddField(4, "pat.ChartNumber"); //PID.5, Patient Name seg.AddField(5, "pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7, "pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8, "pat.Gender"); //PID.10, Race seg.AddField(10, "pat.Race"); //PID.11, Patient Address seg.AddField(11, "pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13, "pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14, "pat.WkPhone"); //PID.16, Marital Status seg.AddField(16, "pat.Position"); //PID.19, SSN - Patient seg.AddField(19, "pat.SSN"); //PID.22, Fee Schedule seg.AddField(22, "pat.FeeSched"); //GT1 segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 5, SegmentNameHL7.GT1); //GT1.2, Guarantor Number seg.AddField(2, "guar.PatNum"); //GT1.3, Guarantor Name seg.AddField(3, "guar.nameLFM"); //GT1.5, Guarantor Address seg.AddField(5, "guar.addressCityStateZip"); //GT1.6, Guarantor Phone Number - Home seg.AddField(6, "guar.HmPhone"); //GT1.7, Guarantor Phone Number - Business seg.AddField(7, "guar.WkPhone"); //GT1.8, Guarantor Date/Time of Birth seg.AddField(8, "guar.birthdateTime"); //GT1.9, Guarantor Administrative Sex seg.AddField(9, "guar.Gender"); //GT1.12, Guarantor SSN seg.AddField(12, "guar.SSN"); #endregion ADT - Patient Demographics (Admits, Discharges, and Transfers) #region SIU - Schedule Information Unsolicited //====================================================================================================================== //eCW incoming appointment information (SIU - Schedule information unsolicited). msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.SIU, MessageStructureHL7.SIU_S12, InOutHL7.Incoming, 1); //MSH segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //PID segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 2, SegmentNameHL7.PID); //PID.2 seg.AddField(2, "pat.PatNum"); //PID.4, Alternate Patient ID seg.AddField(4, "pat.ChartNumber"); //PID.5, Patient Name seg.AddField(5, "pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7, "pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8, "pat.Gender"); //PID.10, Race seg.AddField(10, "pat.Race"); //PID.11, Patient Address seg.AddField(11, "pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13, "pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14, "pat.WkPhone"); //PID.16, Marital Status seg.AddField(16, "pat.Position"); //PID.19, SSN - Patient seg.AddField(19, "pat.SSN"); //PID.22, Fee Schedule seg.AddField(22, "pat.FeeSched"); //SCH segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.SCH); //SCH.2, Filler Appointment ID. In the old eCW interface, we were pulling from SCH.2, which was always the same as SCH.1. seg.AddField(2, "apt.AptNum"); //SCH.7, Appointment Reason seg.AddField(7, "apt.Note"); //SCH.11, Appointment Timing Quantity seg.AddField(11, "apt.lengthStartEnd"); //AIG segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 4, false, true, SegmentNameHL7.AIG); //AIG.3, Resource ID^Resource Name (Lname, Fname all as a string) seg.AddField(3, "prov.provIdName"); //PV1 segment.----------------------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 3, false, true, SegmentNameHL7.PV1); //PV1.7, Attending/Primary Care Doctor, UPIN^LastName^FirstName^MI seg.AddField(7, "prov.provIdNameLFM"); #endregion SIU - Schedule Information Unsolicited #region ACK - General Acknowledgment //======================================================================================================================= //Acknowledgment message (ACK) msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.ACK, MessageStructureHL7.ADT_A01, InOutHL7.Incoming, 2); //MSH segment------------------------------------------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8, "messageType"); //MSA (Message Acknowledgment) segment----------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.MSA); //MSA.1, Acknowledgment Code seg.AddField(1, "ackCode"); //MSA.2, Message Control ID seg.AddField(2, "messageControlId"); #endregion ACK - General Acknowledgment #endregion Inbound Messages #region Outbound Messages #region DFT - Detailed Financial Transaction //======================================================================================================================= //Detail financial transaction (DFT) msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.DFT, MessageStructureHL7.DFT_P03, InOutHL7.Outgoing, 3); //MSH (Message Header) segment------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1, "separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2, DataTypeHL7.HD, "OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4, DataTypeHL7.HD, "ECW"); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6, "dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10, DataTypeHL7.PT, "P"); //MSH.11, Version ID seg.AddFieldFixed(11, DataTypeHL7.VID, "2.3"); //EVN (Event Type) segment----------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.EVN); //EVN.1, Event Type, example P03 seg.AddField(1, "eventType"); //EVN.2, Recorded Date/Time seg.AddField(2, "dateTime.Now"); //PID (Patient Identification) segment----------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 2, SegmentNameHL7.PID); //PID.1, Sequence Number (1 for DFT's) seg.AddFieldFixed(1, DataTypeHL7.ST, "1"); //PID.2, Patient ID (Account number. eCW requires this to be the same # as came in on PID.4.) seg.AddField(2, "pat.ChartNumber"); //PID.3, Patient MRN number seg.AddField(3, "pat.PatNum"); //PID.5, Patient Name (Last^First^MI) seg.AddField(5, "pat.nameLFM"); //PID.7, Birthdate seg.AddField(7, "pat.birthdateTime"); //PID.8, Gender seg.AddField(8, "pat.Gender"); //PID.10, Race seg.AddField(10, "pat.Race"); //PID.11, Address seg.AddField(11, "pat.addressCityStateZip"); //PID.13, Home Phone seg.AddField(13, "pat.HmPhone"); //PID.14, Work Phone seg.AddField(14, "pat.WkPhone"); //PID.16, Marital Status seg.AddField(16, "pat.Position"); //PID.19, SSN seg.AddField(19, "pat.SSN"); //PV1 (Patient Visit) segment-------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 3, SegmentNameHL7.PV1); //PV1.7, Attending/Primary Care Doctor seg.AddField(7, "prov.provIdNameLFM"); //PV1.19, Visit Number seg.AddField(19, "apt.AptNum"); //FT1 (Financial Transaction Information) segment------------------------------ seg = new HL7DefSegment(); msg.AddSegment(seg, 4, true, true, SegmentNameHL7.FT1); //FT1.1, Sequence Number (starts with 1) seg.AddField(1, "sequenceNum"); //FT1.4, Transaction Date (YYYYMMDDHHMMSS) seg.AddField(4, "proc.procDateTime"); //FT1.5, Transaction Posting Date (YYYYMMDDHHMMSS) seg.AddField(5, "proc.procDateTime"); //FT1.6, Transaction Type seg.AddFieldFixed(6, DataTypeHL7.IS, "CG"); //FT1.10, Transaction Quantity seg.AddFieldFixed(10, DataTypeHL7.NM, "1.0"); //FT1.19, Diagnosis Code seg.AddField(19, "proc.DiagnosticCode"); //FT1.20, Performed by Code (provider) seg.AddField(20, "prov.provIdNameLFM"); //FT1.21, Ordering Provider seg.AddField(21, "prov.provIdNameLFM"); //FT1.22, Unit Cost (procedure fee) seg.AddField(22, "proc.ProcFee"); //FT1.25, Procedure Code seg.AddField(25, "proccode.ProcCode"); //FT1.26, Modifiers (treatment area) seg.AddField(26, "proc.toothSurfRange"); //DG1 (Diagnosis) segment is optional, skip for now //ZX1 (PDF Data) segment------------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 5, SegmentNameHL7.ZX1); //ZX1.1 seg.AddFieldFixed(1, DataTypeHL7.ST, "6"); //ZX1.2 seg.AddFieldFixed(2, DataTypeHL7.ST, "PDF"); //ZX1.3 seg.AddFieldFixed(3, DataTypeHL7.ST, "PATHOLOGY^Pathology Report^L"); //ZX1.4 seg.AddField(4, "pdfDescription"); //ZX1.5 seg.AddField(5, "pdfDataAsBase64"); #endregion DFT - Detailed Financial Transaction #region ACK - General Acknowledgment //======================================================================================================================= //Message Acknowledgment (ACK) msg = new HL7DefMessage(); def.AddMessage(msg, MessageTypeHL7.ACK, MessageStructureHL7.ADT_A01, InOutHL7.Outgoing, 4); //MSH (Message Header) segment------------------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1, "separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2, DataTypeHL7.HD, "OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4, DataTypeHL7.HD, "ECW"); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6, "dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10, DataTypeHL7.PT, "P"); //MSH.11, Version ID seg.AddFieldFixed(11, DataTypeHL7.VID, "2.3"); //MSA (Message Acknowledgment) segment----------------------------------------- seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.MSA); //MSA.1, Acknowledgment Code seg.AddField(1, "ackCode"); //MSA.2, Message Control ID seg.AddField(2, "messageControlId"); #endregion ACK - General Acknowledgment #endregion Outbound Messages return(def); }
private static string gSep(HL7Def def) { return def.ComponentSeparator+def.RepetitionSeparator+def.EscapeCharacter+def.SubcomponentSeparator; }
///<summary>Uses sheet framework to generate a PDF file, save it to patient's image folder, and attempt to launch file with defualt reader. ///If using ImagesStoredInDB it will not launch PDF. If no valid patient is selected you cannot perform this action.</summary> private void butPDF_Click(object sender, EventArgs e) { if (PatCur == null) //not attached to a patient when form loaded and they haven't selected a patient to attach to yet { MsgBox.Show(this, "The Medical Lab must be attached to a patient before the PDF can be saved."); return; } if (PatCur.PatNum > 0 && _medLabCur.PatNum != PatCur.PatNum) //save the current patient attached to the MedLab if it has been changed { MoveLabsAndImagesHelper(); } Cursor = Cursors.WaitCursor; SheetDef sheetDef = SheetUtil.GetMedLabResultsSheetDef(); Sheet sheet = SheetUtil.CreateSheet(sheetDef, _medLabCur.PatNum); SheetFiller.FillFields(sheet, null, null, _medLabCur); //create the file in the temp folder location, then import so it works when storing images in the db string tempPath = ODFileUtils.CombinePaths(PrefC.GetTempFolderPath(), _medLabCur.PatNum.ToString() + ".pdf"); SheetPrinting.CreatePdf(sheet, tempPath, null, _medLabCur); HL7Def defCur = HL7Defs.GetOneDeepEnabled(true); long category = defCur.LabResultImageCat; if (category == 0) { category = Defs.GetFirstForCategory(DefCat.ImageCats, true).DefNum; //put it in the first category. } //create doc-------------------------------------------------------------------------------------- OpenDentBusiness.Document docc = null; try { docc = ImageStore.Import(tempPath, category, Patients.GetPat(_medLabCur.PatNum)); } catch (Exception ex) { ex.DoNothing(); Cursor = Cursors.Default; MsgBox.Show(this, "Error saving document."); return; } finally { //Delete the temp file since we don't need it anymore. try { File.Delete(tempPath); } catch { //Do nothing. This file will likely get cleaned up later. } } docc.Description = Lan.g(this, "MedLab Result"); docc.DateCreated = DateTime.Now; Documents.Update(docc); string filePathAndName = ""; if (PrefC.AtoZfolderUsed == DataStorageType.LocalAtoZ) { string patFolder = ImageStore.GetPatientFolder(Patients.GetPat(_medLabCur.PatNum), ImageStore.GetPreferredAtoZpath()); filePathAndName = ODFileUtils.CombinePaths(patFolder, docc.FileName); } else if (CloudStorage.IsCloudStorage) { FormProgress FormP = new FormProgress(); FormP.DisplayText = "Downloading..."; FormP.NumberFormat = "F"; FormP.NumberMultiplication = 1; FormP.MaxVal = 100; //Doesn't matter what this value is as long as it is greater than 0 FormP.TickMS = 1000; OpenDentalCloud.Core.TaskStateDownload state = CloudStorage.DownloadAsync( ImageStore.GetPatientFolder(Patients.GetPat(_medLabCur.PatNum), ImageStore.GetPreferredAtoZpath()) , docc.FileName , new OpenDentalCloud.ProgressHandler(FormP.OnProgress)); if (FormP.ShowDialog() == DialogResult.Cancel) { state.DoCancel = true; return; } filePathAndName = PrefC.GetRandomTempFile(Path.GetExtension(docc.FileName)); File.WriteAllBytes(filePathAndName, state.FileContent); } Cursor = Cursors.Default; if (filePathAndName != "") { Process.Start(filePathAndName); } SecurityLogs.MakeLogEntry(Permissions.SheetEdit, sheet.PatNum, sheet.Description + " from " + sheet.DateTimeSheet.ToShortDateString() + " pdf was created"); DialogResult = DialogResult.OK; }
private static string gRace(Patient pat,HL7Def def) { //Example: 2106-3^White^CDCREC^^^^1~2186-5^NotHispanic^CDCREC^^^^1 string retval=""; List<PatientRace> listPatRaces=PatientRaces.GetForPatient(pat.PatNum); for(int i=0;i<listPatRaces.Count;i++) { if(listPatRaces[i].CdcrecCode=="") { continue; } if(retval!="") { retval+=def.RepetitionSeparator; } retval+=gConcat(def.ComponentSeparator,listPatRaces[i].CdcrecCode,listPatRaces[i].Race.ToString(),"CDCREC","","","","1"); } return retval; }
///<summary>Returns null if there is no DFT defined for the enabled HL7Def.</summary> public static MessageHL7 GenerateDFT(List <Procedure> procList, EventTypeHL7 eventType, Patient pat, Patient guar, long aptNum, string pdfDescription, string pdfDataString) //add event (A04 etc) parameters later if needed //In \\SERVERFILES\storage\OPEN DENTAL\Programmers Documents\Standards (X12, ADA, etc)\HL7\Version2.6\V26_CH02_Control_M4_JAN2007.doc //On page 28, there is a Message Construction Pseudocode as well as a flowchart which might help. { Provider prov = Providers.GetProv(Patients.GetProvNum(pat)); Appointment apt = Appointments.GetOneApt(aptNum); MessageHL7 messageHL7 = new MessageHL7(MessageTypeHL7.DFT); HL7Def hl7Def = HL7Defs.GetOneDeepEnabled(); if (hl7Def == null) { return(null); } //find a DFT message in the def HL7DefMessage hl7DefMessage = null; for (int i = 0; i < hl7Def.hl7DefMessages.Count; i++) { if (hl7Def.hl7DefMessages[i].MessageType == MessageTypeHL7.DFT) { hl7DefMessage = hl7Def.hl7DefMessages[i]; //continue; break; } } if (hl7DefMessage == null) //DFT message type is not defined so do nothing and return { return(null); } for (int s = 0; s < hl7DefMessage.hl7DefSegments.Count; s++) { int countRepeat = 1; if (hl7DefMessage.hl7DefSegments[s].SegmentName == SegmentNameHL7.FT1) { countRepeat = procList.Count; } //for example, countRepeat can be zero in the case where we are only sending a PDF of the TP to eCW, and no procs. for (int repeat = 0; repeat < countRepeat; repeat++) //FT1 is optional and can repeat so add as many FT1's as procs in procList //if(hl7DefMessage.hl7DefSegments[s].SegmentName==SegmentNameHL7.FT1) { { if (hl7DefMessage.hl7DefSegments[s].SegmentName == SegmentNameHL7.FT1 && procList.Count > repeat) { prov = Providers.GetProv(procList[repeat].ProvNum); } SegmentHL7 seg = new SegmentHL7(hl7DefMessage.hl7DefSegments[s].SegmentName); seg.SetField(0, hl7DefMessage.hl7DefSegments[s].SegmentName.ToString()); for (int f = 0; f < hl7DefMessage.hl7DefSegments[s].hl7DefFields.Count; f++) { string fieldName = hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].FieldName; if (fieldName == "") //If fixed text instead of field name just add text to segment { seg.SetField(hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].OrdinalPos, hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].FixedText); } else { //seg.SetField(hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].OrdinalPos, //FieldConstructor.GenerateDFT(hl7Def,fieldName,pat,prov,procList[repeat],guar,apt,repeat+1,eventType,pdfDescription,pdfDataString)); Procedure proc = null; if (procList.Count > repeat) //procList could be an empty list { proc = procList[repeat]; } seg.SetField(hl7DefMessage.hl7DefSegments[s].hl7DefFields[f].OrdinalPos, FieldConstructor.GenerateDFT(hl7Def, fieldName, pat, prov, proc, guar, apt, repeat + 1, eventType, pdfDescription, pdfDataString)); } } messageHL7.Segments.Add(seg); } } return(messageHL7); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if (def == null) //wasn't in the database { def = new HL7Def(); def.IsNew = true; def.Description = "MedLab HL7 v2.3"; def.ModeTx = ModeTxHL7.Sftp; def.IncomingFolder = ""; def.OutgoingFolder = ""; def.IncomingPort = ""; def.OutgoingIpPort = ""; def.SftpInSocket = ""; def.SftpUsername = ""; def.SftpPassword = ""; def.FieldSeparator = "|"; def.ComponentSeparator = "^"; def.SubcomponentSeparator = "&"; def.RepetitionSeparator = "~"; def.EscapeCharacter = @"\"; def.IsInternal = true; def.InternalType = HL7InternalType.MedLabv2_3; def.InternalTypeVersion = Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled = false; def.Note = ""; def.ShowDemographics = HL7ShowDemographics.ChangeAndAdd; //these last four properties will not be editable for a lab interface type def.ShowAccount = true; def.ShowAppts = true; def.IsQuadAsToothNum = false; } def.hl7DefMessages = new List <HL7DefMessage>(); HL7DefMessage msg = new HL7DefMessage(); HL7DefSegment seg = new HL7DefSegment(); #region Inbound Messages #region ORU - Unsolicited Observation Message def.AddMessage(msg, MessageTypeHL7.ORU, MessageStructureHL7.ORU_R01, InOutHL7.Incoming, 0); #region MSH - Message Header msg.AddSegment(seg, 0, SegmentNameHL7.MSH); //Fields------------------------------------------------------------------------------------------------------------- //MSH.2, Sending Application. To identify the LabCorp Lab System sending the results. //Possible values for LabCorp (as of their v10.7 specs): '1100' - LabCorp Lab System, 'DIANON' - DIANON Systems, //'ADL' - Acupath Diagnostic Laboratories, 'EGL' - Esoterix Genetic Laboratories. //For backward compatibility only: 'CMBP', 'LITHOLINK', 'USLABS' seg.AddField(2, "sendingApp"); //MSH.3, Sending Facility. Identifies the LabCorp laboratory responsible for the client. //It could be a LabCorp assigned 'Responsible Lab Code' representing the responsible laboratory or it could be a CLIA number. seg.AddField(3, "sendingFacility"); //MSH.8, Message Type seg.AddField(8, "messageType"); //MSH.9, Message Control ID seg.AddField(9, "messageControlId"); #endregion MSH - Message Header #region PID - Patient Identification seg = new HL7DefSegment(); msg.AddSegment(seg, 1, SegmentNameHL7.PID); //Fields------------------------------------------------------------------------------------------------------------- //PID.2, External Patient ID. LabCorp defines this as 'client' assigned patient id, just like they do PID.4. //This should be the Open Dental patient number, sent in outbound PID.4 and returned in PID.2. seg.AddField(2, "pat.PatNum"); //PID.3, Lab Assigned Patient ID. LabCorp assigned specimen number. seg.AddField(3, "labPatID"); //PID.4, Alternate Patient ID. LabCorp defines this as a 'client' assigned patient id, just like they do PID.2. //This will be in outbound PID.2, returned in PID.4. seg.AddField(4, "altPatID"); //PID.5, Patient Name //This will contain the last, first, and middle names as well as the title //Example: LName^FName^MiddleI seg.AddField(5, "pat.nameLFM"); //PID.7, Date/Time of Birth with Age //LabCorp uses this for the birthdate as well as the age in years, months, and days of the patient in the format bday^years^months^days. //All age components are left padded with 0's, the years is padded to 3 chars, the months and days are padded to 2 chars //Example: 19811213^033^02^19 seg.AddField(7, "patBirthdateAge"); //PID.8, Patient Gender //We use this field to assist the user in selecting a patient if one is not found when importing the message, but we don't store it seg.AddField(8, "pat.Gender"); //PID.18.1, Patient Account Number. LabCorp assigned account number. This field is also used to send the Fasting flag in component 7. //Fasting flag values are 'Y', 'N', or blank //Example: AccountNum^^^BillCode^ABNFlag^SpecimenStatus^FastingFlag seg.AddField(18, "accountNum"); //PID.19, Patient SSN Number //We use this field to assist the user in selecting a patient if one is not found when importing the message, but we don't store it seg.AddField(19, "pat.SSN"); #endregion PID - Patient Identification #region NK1 - Next of Kin //This segment is for future use only, nothing is currently imported from this segment seg = new HL7DefSegment(); msg.AddSegment(seg, 2, false, true, SegmentNameHL7.NK1); //Fields------------------------------------------------------------------------------------------------------------- //NK1.2, Next of Kin Name //Example: LName^FName^Middle //seg.AddField(2,"nextOfKinName"); //NK1.4, Next of Kin Address //Example: Address^Address2^City^State^Zip //seg.AddField(4,"nextOfKinAddress"); //NK1.5, Next of Kin Phone //seg.AddField(5,"nextOfKinPhone"); seg.hl7DefFields = new List <HL7DefField>(); #endregion NK1 - Next of Kin #region NTE - Notes and Comments seg = new HL7DefSegment(); msg.AddSegment(seg, 3, true, true, SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlab.NotePat field seg.AddField(3, "patNote"); #endregion NTE - Notes and Comments #region ORC - Common Order seg = new HL7DefSegment(); msg.AddSegment(seg, 4, true, false, SegmentNameHL7.ORC); //Fields------------------------------------------------------------------------------------------------------------- //ORC.2, Unique Foreign Accession or Specimen ID //Must match the value in OBR.2 and is the ID value sent on the specimen container and is unique per patient order, not test order. //ORC.2.2 is the constant value 'LAB' //Example: L2435^LAB seg.AddField(2, "specimenID"); //ORC.3, Filler Accession ID. The LabCorp assigned specimen number. These are reused on a yearly basis, but used with the client //specific specimen ID in ORC.2, these two numbers should uniquely identify a specimen/order. ORC.3.2 is the constant value 'LAB'. //This should match OBR.3. //Example: 08599499950^LAB seg.AddField(3, "specimenIDFiller"); //ORC.12, Ordering Provider, XCN Data Type //ProvID^ProvLName^ProvFName^ProvMiddleI^^^^SourceTable //This field repeats for every ID available for the provider with the SourceTable component identifying the type of ID in each repetition. //SourceTable possible values: 'U' - UPIN, 'P' - Provider Number (Medicaid or Commercial Insurance Provider ID), //'N' - NPI Number (Required for Third Party Billing), 'L' - Local (Physician ID) //Example: A12345^LNAME^FNAME^M^^^^U~23462^LNAME^FNAME^M^^^^L~0123456789^LNAME^FNAME^M^^^^N~1234567890^LNAME^FNAME^M^^^^P seg.AddField(12, "orderingProv"); #endregion ORC - Common Order #region OBR - Observation Request seg = new HL7DefSegment(); msg.AddSegment(seg, 5, true, false, SegmentNameHL7.OBR); //Fields------------------------------------------------------------------------------------------------------------- //OBR.2, Unique Foreign Accession or Specimen ID //Must match the value in ORC.2 and is the ID value sent on the specimen container and is unique per patient order, not test order. //OBR.2.2 is the constant value 'LAB'. //Example: L2435^LAB seg.AddField(2, "specimenID"); //OBR.3, Internal Accession ID. The LabCorp assigned specimen number. These are reused on a yearly basis, but used with the client //specific specimen ID in OBR.2, these two numbers should uniquely identify a specimen/order. OBR.3.2 is the constant value 'LAB'. //This should match ORC.3. //Example: 08599499950^LAB seg.AddField(3, "specimenIDFiller"); //OBR.4, Universal Service Identifier, CWE data type //This identifies the observation. This will be the ID and text description of the test, as well as the LOINC code and description. //Example: 006072^RPR^L^20507-0^Reagin Ab^LN seg.AddField(4, "obsTestID"); //OBR.7, Observation/Specimen Collection Date/Time //Format for LabCorp: yyyyMMddHHmm seg.AddField(7, "dateTimeCollected"); //OBR.9, Collection/Urine Volume seg.AddField(9, "totalVolume"); //OBR.11, Action Code //Used to identify the type of result being returned. 'A' - Add on, 'G' - Reflex, Blank for standard results seg.AddField(11, "specimenAction"); //OBR.13, Relevant Clinical Information. Used for informational purposes. seg.AddField(13, "clinicalInfo"); //OBR.14, Date/Time of Specimen Receipt in Lab. //LabCorp format: yyyMMddHHmm seg.AddField(14, "dateTimeEntered"); //OBR.16, Ordering Provider. //ProvID^ProvLName^ProvFName^ProvMiddleI^^^^SourceTable //This field repeats for every ID available for the provider with the SourceTable component identifying the type of ID in each repetition. //SourceTable possible values: 'U' - UPIN, 'P' - Provider Number (Medicaid or Commercial Insurance Provider ID), //'N' - NPI Number (Required for Third Party Billing), 'L' - Local (Physician ID) //Example: A12345^LNAME^FNAME^M^^^^U~23462^LNAME^FNAME^M^^^^L~0123456789^LNAME^FNAME^M^^^^N~1234567890^LNAME^FNAME^M^^^^P seg.AddField(16, "orderingProv"); //OBR.18, Alternate Specimen ID. seg.AddField(18, "specimenIDAlt"); //OBR.22, Date/Time Observation Reported. //LabCorp format: yyyyMMddHHmm seg.AddField(22, "dateTimeReported"); //OBR.24, Producer's Section ID, used by LabCorp to identify the facility responsible for performing the testing. //This will be the footnote ID of the ZPS segment and will be used to attach a MedLab object to a MedLabFacility object. seg.AddField(24, "facilityID"); //OBR.25, Order Result Status. //LabCorp values: 'F' - Final, 'P' - Preliminary, 'X' - Cancelled, 'C' - Corrected seg.AddField(25, "resultStatus"); //OBR.26, Link to Parent Result. //If this is a reflex result, the value from the OBX.3.1 field of the parent result will be here. seg.AddField(26, "parentObsID"); //OBR.29, Link to Parent Order. //If this is a reflex test, the value from the OBR.4.1 field of the parent test will be here. seg.AddField(29, "parentObsTestID"); #endregion OBR - Observation Request #region NTE - Notes and Comments seg = new HL7DefSegment(); msg.AddSegment(seg, 6, true, true, SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlab.NoteLab field seg.AddField(3, "labNote"); #endregion NTE - Notes and Comments #region OBX - Observation/Result seg = new HL7DefSegment(); msg.AddSegment(seg, 7, true, false, SegmentNameHL7.OBX); //Fields------------------------------------------------------------------------------------------------------------- //OBX.2, Value Type. This field is not stored explicitly, but it is used to determine the value type of the observation. //If this field is 'TX' for text, the value will be >21 chars and will be sent in the attached NTEs. seg.AddField(2, "obsValueType"); //OBX.3, Observation ID. This field has the same structure as the OBR.4. //ID^Text^CodeSystem^AltID^AltIDText^AltIDCodeSystem, the AltID is the LOINC code so the AltIDCodeSystem will be 'LN' //Example: 006072^RPR^L^20507-0^Reagin Ab^LN seg.AddField(3, "obsID"); //OBX.4, Observation Sub ID. This field is used to aid in the identification of results with the same observation ID (OBX.3) within a //given OBR. If OBX.5.3 is 'ORM' (organism) this field will link a result to an organism, whether this is for organism #1, organism #2, //or organism #3. seg.AddField(4, "obsIDSub"); //OBX.5, Observation Value. ObsValue^TypeOfData^DataSubtype^Encoding^Data. LabCorp report will display OBX.5.1 as the result. //For value >21 chars in length: OBX.2 will be 'TX' for text, OBX.5 will be NULL (empty field), and the value will be in attached NTEs. //"TNP" will be reported for Test Not Performed. seg.AddField(5, "obsValue"); //OBX.6, Units. //Identifier^Text^CodeSystem. Id is units of measure abbreviation, text is full text version of units, coding system is 'L' (local id). seg.AddField(6, "obsUnits"); //OBX.7, Reference Range. seg.AddField(7, "obsRefRange"); //OBX.8, Abnormal Flags. For values see enum OpenDentBusiness.AbnormalFlag seg.AddField(8, "obsAbnormalFlag"); //OBX.11, Observation Result Status. //LabCorp values: 'F' - Final, 'P' - Preliminary, 'X' - Cancelled, 'C' - Corrected, 'I' - Incomplete seg.AddField(11, "resultStatus"); //OBX.14, Date/Time of Observation. //LabCorp format yyyyMMddHHmm seg.AddField(14, "dateTimeObs"); //OBX.15, Producer's ID. //For LabCorp this is used to report the facility responsible for performing the testing. This will hold the lab ID that will reference //a ZPS segment with the lab name, address, and director details. Used to link a MedLabResult object to a MedLabFacility object. seg.AddField(15, "facilityID"); #endregion OBX - Observation/Result #region ZEF - Encapsulated Data Format seg = new HL7DefSegment(); msg.AddSegment(seg, 8, true, true, SegmentNameHL7.ZEF); //Fields------------------------------------------------------------------------------------------------------------- //ZEF.1, Sequence Number, 1 through 9999 seg.AddField(1, "sequenceNum"); //ZEF.2, Embedded File. //Base64 embedded file, sent in 50k blocks and will be concatenated together to and converted back into seg.AddField(2, "base64File"); #endregion ZEF - Encapsulated Data Format #region NTE - Notes and Comments seg = new HL7DefSegment(); msg.AddSegment(seg, 9, true, true, SegmentNameHL7.NTE); //Fields------------------------------------------------------------------------------------------------------------- //NTE.2, Comment Source, ID data type //LabCorp supported values: 'L' - Laboratory is the source of comment, 'AC' - Accession Comment, //'RC' - Result comment, 'RI' - Normal Comment, 'UK' - Undefined comment type //We might pull out the source and prepend it to the note, but we won't explicitly add it to the definition or store it separately //NTE.3, Comment Text, FT data type (formatted text) //Stored in the medlabresult.Note field seg.AddField(3, "obsNote"); #endregion NTE - Notes and Comments #region SPM - Specimen seg = new HL7DefSegment(); msg.AddSegment(seg, 10, true, true, SegmentNameHL7.SPM); //Fields------------------------------------------------------------------------------------------------------------- //SPM.2, Specimen ID. //Unique ID of the specimen as sent on the specimen container. Same as the value in ORC.2. seg.AddField(2, "specimenID"); //SPM.4, Specimen Type //SPM.8, Specimen Source Site //SPM.4, Specimen Source Site Modifier //SPM.14, Specimen Description. Text field used to send additional information about the specimen seg.AddField(14, "specimenDescript"); //SPM.17, Date/Time Specimen Collected seg.AddField(17, "dateTimeSpecimen"); #endregion SPM - Specimen #region ZPS - Place of Service seg = new HL7DefSegment(); msg.AddSegment(seg, 11, true, false, SegmentNameHL7.ZPS); //Fields------------------------------------------------------------------------------------------------------------- //ZPS.2, Facility Mnemonic. Footnote ID for the lab facility used to reference this segment from OBX.15. seg.AddField(2, "facilityID"); //ZPS.3, Facility Name. seg.AddField(3, "facilityName"); //ZPS.4, Facility Address. //Address^^City^State^Zip seg.AddField(4, "facilityAddress"); //ZPS.5, Facility Phone. (LabCorp document says numberic characters only, so we can assume no dashes or parentheses.) seg.AddField(5, "facilityPhone"); //ZPS.7, Facility Director. //Title^LName^FName^MiddleI seg.AddField(7, "facilityDirector"); #endregion ZPS - Place of Service #endregion ORU - Unsolicited Observation Message #endregion Inbound Messages #region Outbound Messages //Results only interface for now, so no outbound messages yet //In the future, we will implement the orders portion of the interface and have outbound ORM messages #endregion Outbound Messages return(def); }
///<summary>Sends null values in for objects not required. GenerateField will return an empty string if a field requires an object and that object is null.</summary> public static string GenerateFieldADT(HL7Def def,string fieldName,Patient pat,Provider prov,Patient guar,int sequenceNum,EventTypeHL7 eventType,SegmentNameHL7 segName) { return GenerateField(def,fieldName,MessageTypeHL7.ADT,pat,prov,null,guar,null,sequenceNum,eventType,null,null,MessageStructureHL7.ADT_A01,segName); }
///<summary>apt, guar, proc, prov and pdfDataString can be null and will return an empty string if a field requires that object</summary> public static string GenerateDFT(HL7Def def,string fieldName,Patient pat,Provider prov,Procedure proc,Patient guar,Appointment apt,int sequenceNum,EventTypeHL7 eventType,string pdfDescription,string pdfDataString) { //big long list of fieldnames that we support switch(fieldName){ case "apt.AptNum": if(apt==null) { return ""; } else { return apt.AptNum.ToString(); } case "dateTime.Now": return gDTM(DateTime.Now,14); case "eventType": return eventType.ToString(); case "guar.addressCityStateZip": if(guar==null) { return ""; } else { return gConcat(def.ComponentSeparator,guar.Address,guar.Address2,guar.City,guar.State,guar.Zip); } case "guar.birthdateTime": if(guar==null) { return ""; } else { return gDTM(guar.Birthdate,8); } case "guar.Gender": if(guar==null) { return ""; } else { return gIS(guar); } case "guar.HmPhone": if(guar==null) { return ""; } else { return gXTN(guar.HmPhone,10); } case "guar.nameLFM": if(guar==null) { return ""; } else { return gConcat(def.ComponentSeparator,guar.LName,guar.FName,guar.MiddleI); } case "guar.PatNum": if(guar==null) { return ""; } else { return guar.PatNum.ToString(); } case "guar.SSN": if(guar==null) { return ""; } else { return guar.SSN; } case "guar.WkPhone": if(guar==null) { return ""; } else { return gXTN(guar.WkPhone,10); } case "messageControlId": return Guid.NewGuid().ToString("N"); case "messageType": return gConcat(def.ComponentSeparator,"DFT",eventType.ToString()); case "pat.addressCityStateZip": return gConcat(def.ComponentSeparator,pat.Address,pat.Address2,pat.City,pat.State,pat.Zip); case "pat.birthdateTime": return gDTM(pat.Birthdate,8); case "pat.ChartNumber": return pat.ChartNumber; case "pat.Gender": return gIS(pat); case "pat.HmPhone": return gXTN(pat.HmPhone,10); case "pat.nameLFM": return gConcat(def.ComponentSeparator,pat.LName,pat.FName,pat.MiddleI); case "pat.PatNum": return pat.PatNum.ToString(); case "pat.Position": return gPos(pat); case "pat.Race": return gRace(pat); case "pat.SSN": return pat.SSN; case "pat.WkPhone": return gXTN(pat.WkPhone,10); case "pdfDescription": return pdfDescription; case "pdfDataAsBase64": if(pdfDataString==null) { return ""; } else { return pdfDataString; } case "proc.DiagnosticCode": if(proc==null) { return ""; } if(proc.DiagnosticCode==null) { return ""; } else { return proc.DiagnosticCode; } case "proc.procDateTime": if(proc==null) { return ""; } else { return gDTM(proc.ProcDate,14); } case "proc.ProcFee": if(proc==null) { return ""; } else { return proc.ProcFee.ToString("F2"); } case "proc.ProcNum": if(proc==null) { return ""; } else { return proc.ProcNum.ToString(); } case "proc.toothSurfRange": if(proc==null) { return ""; } else { return gTreatArea(def.ComponentSeparator,proc); } case "proccode.ProcCode": if(proc==null) { return ""; } else { return gProcCode(proc); } case "prov.provIdNameLFM": if(prov==null) { return ""; } else { return gConcat(def.ComponentSeparator,prov.EcwID,prov.LName,prov.FName,prov.MI); } case "separators^~\\&": return gSep(def); case "sequenceNum": return sequenceNum.ToString(); default: return ""; } }
///<summary>apt, guar, proc, prov, pdfDescription, pdfDataString, patplanCur, inssubCur, insplanCur, carrierCur, and patSub can be null and will return an empty string if a field requires that object</summary> public static string GenerateField(HL7Def def,string fieldName,MessageTypeHL7 msgType,Patient pat,Provider prov,Procedure proc,Patient guar,Appointment apt,int sequenceNum,EventTypeHL7 eventType,string pdfDescription,string pdfDataString,MessageStructureHL7 msgStructure,SegmentNameHL7 segName) { string retval=""; if(def.InternalType==HL7InternalType.eCWFull || def.InternalType==HL7InternalType.eCWTight || def.InternalType==HL7InternalType.eCWStandalone) { _isEcwDef=true; } //big long list of fieldnames that we support switch(fieldName) { #region Appointment case "apt.AptDateTime": if(apt==null) { return ""; } return gDTM(apt.AptDateTime,14); case "apt.AptNum": if(apt==null) { return ""; } if(_isEcwDef) { return apt.AptNum.ToString(); } //We will use AptNum, with the OIDRoot for an appt object (set in oidinternal), and ID Type 'HL7' OIDInternal aptOid=OIDInternals.GetForType(IdentifierType.Appointment); string aptOidRoot=""; if(aptOid!=null) { aptOidRoot=aptOid.IDRoot; } //For PV1 segments, the data type is a CX, which has the AptNum, check digit, check digit scheme, assigning authority - &universalID&universalIDType, IDType //We will use the check digit scheme M11, their appt OID with "HL7" as the type for assigning authority, and the ID type is VN - Visit Number //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.6&HL7^VN| if(segName==SegmentNameHL7.PV1) { string strCheckDigit=MessageParser.M11CheckDigit(apt.AptNum.ToString()).ToString(); string strCheckDigitScheme="M11"; if(strCheckDigit=="-1") { strCheckDigit=""; strCheckDigitScheme=""; } return gConcat(def.ComponentSeparator,apt.AptNum.ToString(),strCheckDigit,strCheckDigitScheme,gConcat(def.SubcomponentSeparator,"",aptOidRoot,"HL7"),"VN"); } //For segments other than PV1 (currently SCH or ARQ segments) the data type is EI //Example: |1234^^2.16.840.1.113883.3.4337.1486.6566.6^HL7| return gConcat(def.ComponentSeparator,apt.AptNum.ToString(),"",aptOidRoot,"HL7"); case "apt.aptStatus": if(apt==null) { return ""; } if(apt.AptStatus==ApptStatus.Complete) { return "Complete"; } if(apt.AptStatus==ApptStatus.UnschedList || apt.AptStatus==ApptStatus.Broken) { return "Cancelled"; } if(eventType==EventTypeHL7.S17) {//S17 is event type for outbound SIU messages when we delete an appointment return "Deleted"; } //apt.AptStatus==ApptStatus.Scheduled or apt.AptStatus==ApptStatus.ASAP or other status that triggers an SCH segment return "Booked"; case "apt.confirmStatus": if(apt==null) { return ""; } //Example: |^Appointment Confirmed| return gConcat(def.ComponentSeparator,"",DefC.GetName(DefCat.ApptConfirmed,apt.Confirmed));//this will return an empty string if apt.Confirmed is 0 or invalid case "apt.endAptDateTime": if(apt==null) { return ""; } return gDTM(apt.AptDateTime.AddMinutes(5*apt.Pattern.Length),14); case "apt.externalAptID": //EntityID^NamespaceID^UniversalID^UniversalIDType //Example: |12345^^OtherSoftware.Root^| if(apt==null) { return ""; } List<OIDExternal> listAptOidsExt=OIDExternals.GetByInternalIDAndType(apt.AptNum,IdentifierType.Appointment); if(listAptOidsExt.Count==0) { return ""; } return gConcat(def.ComponentSeparator,listAptOidsExt[0].IDExternal,"",listAptOidsExt[0].rootExternal,""); case "apt.location": //Point of Care^Room^^Facility^^Person Location Type //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) //use operatory and clinic from appt if(apt==null) { return ""; } string aptClinicDescript=Clinics.GetDesc(apt.ClinicNum); Operatory opCur=Operatories.GetOperatory(apt.Op); string opName=""; if(opCur!=null) { opName=opCur.OpName; } string practiceName=PrefC.GetString(PrefName.PracticeTitle); return gConcat(def.ComponentSeparator,aptClinicDescript,opName,"",def.SubcomponentSeparator+practiceName,"","C");//all of these could be empty strings and it works fine case "apt.length": //Example: 60^min&&ANS+, ANS+ is the name of the coding system if(apt==null) { return ""; } return gConcat(def.ComponentSeparator,(5*apt.Pattern.Length).ToString(),gConcat(def.SubcomponentSeparator,"min","","ANS+")); case "apt.Note": //As in the address note field (see PID.11) we will send '\.br\' (where the '\' is the defined escape char, \ by default) to signal a new line. if(apt==null) { return ""; } return gNewLines(def.EscapeCharacter,apt.Note); case "apt.operatory": if(apt==null) { return ""; } opCur=Operatories.GetOperatory(apt.Op); if(opCur==null) { return ""; } return opCur.OpName; case "apt.type": //Suggested values are Normal - Routine schedule request type, Tentative, or Complete - Request to add a completed appt //We will send Normal for all appointment statuses except complete. if(apt==null) { return ""; } if(apt.AptStatus==ApptStatus.Complete) { return "Complete"; } return "Normal"; case "apt.userOD": //The OD user who created/modified the appointment if(apt==null) { return ""; } //SRR messages are generated from the service in response to SRM messages, therefore we do not have a user logged in. if(msgType==MessageTypeHL7.SRR) { return "OpenDentalHL7"; } if(Security.CurUser!=null) { return Security.CurUser.UserName; } return ""; #endregion Appointment case "dateTime.Now": return gDTM(DateTime.Now,14); case "eventType": return eventType.ToString(); #region Guarantor case "guar.addressCityStateZip": if(guar==null) { return ""; } retval=gConcat(def.ComponentSeparator,guar.Address,guar.Address2,guar.City,guar.State,guar.Zip); if(!_isEcwDef) { //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 retval=gConcat(def.ComponentSeparator,retval,"","","","","","","","","","","","","","",gNewLines(def.EscapeCharacter,guar.AddrNote)); } return retval; case "guar.birthdateTime": if(guar==null) { return ""; } return gDTM(guar.Birthdate,8); case "guar.Gender": if(guar==null) { return ""; } return gIS(guar); case "guar.HmPhone": if(guar==null) { return ""; } string hmPh=gXTN(guar.HmPhone,10); string cPh=gXTN(guar.WirelessPhone,10); if(_isEcwDef) { return hmPh; } //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 if(hmPh!="") { retval=gConcat(def.ComponentSeparator,"","PRN","PH","","",hmPh.Substring(0,3),hmPh.Substring(3));//hmPh guaranteed to be 10 digits if not blank } if(cPh!="") { if(retval!="") { retval+=def.RepetitionSeparator; } retval+=gConcat(def.ComponentSeparator,"","PRN","CP","","",cPh.Substring(0,3),cPh.Substring(3));//cPh guaranteed to be 10 digits if not blank } if(guar.Email!="") { if(retval!="") { retval+=def.RepetitionSeparator; } retval+=gConcat(def.ComponentSeparator,"","PRN","Internet",guar.Email); } return retval; case "guar.nameLFM": if(guar==null) { return ""; } return gConcat(def.ComponentSeparator,guar.LName,guar.FName,guar.MiddleI); case "guar.PatNum": if(guar==null) { return ""; } return guar.PatNum.ToString(); case "guar.SSN": if(guar==null) { return ""; } return guar.SSN; case "guar.WkPhone": if(guar==null) { return ""; } string wkPh=gXTN(guar.WkPhone,10); if(_isEcwDef) { return wkPh; } //WPN stands for Work Number, equipment type: PH is Telephone //Example: ^WPN^PH^^^503^3635432 if(wkPh=="") { return ""; } return gConcat(def.ComponentSeparator,"","WPN","PH","","",wkPh.Substring(0,3),wkPh.Substring(3));//wkPh guaranteed to be 10 digits if not blank case "guarIdList": if(guar==null) { return ""; } //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| OIDInternal guarOid=OIDInternals.GetForType(IdentifierType.Patient); string guarOidRoot=""; if(guarOid!=null) { guarOidRoot=guarOid.IDRoot; } string guarIdCheckDigitStr=MessageParser.M11CheckDigit(guar.PatNum.ToString()).ToString(); retval=gConcat(def.ComponentSeparator,guar.PatNum.ToString(),guarIdCheckDigitStr,"M11",def.SubcomponentSeparator+guarOidRoot+def.SubcomponentSeparator+"HL7","PI"); List<OIDExternal> listGuarOidsExt=OIDExternals.GetByInternalIDAndType(guar.PatNum,IdentifierType.Patient); for(int i=0;i<listGuarOidsExt.Count;i++) { guarIdCheckDigitStr=MessageParser.M11CheckDigit(listGuarOidsExt[i].IDExternal).ToString(); if(guarIdCheckDigitStr=="-1") {//could not get a check digit from the external ID, could contain characters that are not numbers retval+=def.RepetitionSeparator+gConcat(def.ComponentSeparator,listGuarOidsExt[i].IDExternal,"","", def.SubcomponentSeparator+listGuarOidsExt[i].rootExternal+def.SubcomponentSeparator,"PI"); continue; } retval+=def.RepetitionSeparator+gConcat(def.ComponentSeparator,listGuarOidsExt[i].IDExternal,guarIdCheckDigitStr,"M11", def.SubcomponentSeparator+listGuarOidsExt[i].rootExternal+def.SubcomponentSeparator,"PI"); } return retval; #endregion Guarantor case "messageControlId": return Guid.NewGuid().ToString("N"); case "messageType": return gConcat(def.ComponentSeparator,msgType.ToString(),eventType.ToString(),msgStructure.ToString()); #region Patient case "pat.addressCityStateZip": retval=gConcat(def.ComponentSeparator,pat.Address,pat.Address2,pat.City,pat.State,pat.Zip); if(!_isEcwDef) { //Example: 123 Main St^Apt 1^Dallas^OR^97338^^^^^^^^^^^^^^^Emergency Contact: Mom Test1\.br\Mother\.br\(503)623-3072 retval=gConcat(def.ComponentSeparator,retval,"","","","","","","","","","","","","","",gNewLines(def.EscapeCharacter,pat.AddrNote)); } return retval; case "pat.birthdateTime": return gDTM(pat.Birthdate,8); case "pat.ChartNumber": return pat.ChartNumber; case "pat.Gender": return gIS(pat); case "pat.HmPhone": hmPh=gXTN(pat.HmPhone,10); cPh=gXTN(pat.WirelessPhone,10); if(_isEcwDef) { return hmPh; } //PRN stands for Primary Residence Number, equipment type: PH is Telephone, CP is Cell Phone, Internet is Internet Address (email) //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]~^PRN^CP^^^503^6895555 if(hmPh!="") { retval=gConcat(def.ComponentSeparator,"","PRN","PH","","",hmPh.Substring(0,3),hmPh.Substring(3));//hmPh guaranteed to be 10 digits if not blank } if(cPh!="") { if(retval!="") { retval+=def.RepetitionSeparator; } retval+=gConcat(def.ComponentSeparator,"","PRN","CP","","",cPh.Substring(0,3),cPh.Substring(3));//cPh guaranteed to be 10 digits if not blank } if(pat.Email!="") { if(retval!="") { retval+=def.RepetitionSeparator; } retval+=gConcat(def.ComponentSeparator,"","PRN","Internet",pat.Email); } return retval; case "pat.location": //Point of Care^Room^^Facility^^Person Location Type //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) if(pat.ClinicNum==0) { return ""; } string patClinicDescript=Clinics.GetDesc(pat.ClinicNum); practiceName=PrefC.GetString(PrefName.PracticeTitle); return gConcat(def.ComponentSeparator,patClinicDescript,"","",def.SubcomponentSeparator+practiceName,"","C"); case "pat.nameLFM": return gConcat(def.ComponentSeparator,pat.LName,pat.FName,pat.MiddleI); case "pat.PatNum": return pat.PatNum.ToString(); case "pat.Position": if(_isEcwDef) { return gPos(pat); } return gPos(pat).Substring(0,1);//S-Single, M-Married, D-Divorced, W-Widowed case "pat.Race": if(_isEcwDef) { return gRaceOld(pat); } return gRace(pat,def); case "pat.site": //Example: |West Salem Elementary^^^^^S| ('S' for site) if(pat.SiteNum==0) { return ""; } string patSiteDescript=Sites.GetDescription(pat.SiteNum); if(patSiteDescript=="") { return ""; } return gConcat(def.ComponentSeparator,patSiteDescript,"","","","","","S"); case "pat.SSN": return pat.SSN; case "pat.WkPhone": if(_isEcwDef) { return gXTN(pat.WkPhone,10); } //WPN stands for Work Number, equipment type: PH is Telephone //Example: ^WPN^PH^^^503^3635432 wkPh=gXTN(pat.WkPhone,10); if(wkPh=="") { return ""; } return gConcat(def.ComponentSeparator,"","WPN","PH","","",wkPh.Substring(0,3),wkPh.Substring(3));//wkPh guaranteed to be 10 digits if not blank case "pat.Urgency": //We will send one of the following values retrieved from the patient.Urgency field for treatment urgency: 0-Unknown, 1-NoProblems, 2-NeedsCare, 3-Urgent return ((int)pat.Urgency).ToString(); case "patientIds": //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&^PI| OIDInternal patOid=OIDInternals.GetForType(IdentifierType.Patient); string patOidRoot=""; if(patOid!=null) { patOidRoot=patOid.IDRoot; } string patIdCheckDigitStr=MessageParser.M11CheckDigit(pat.PatNum.ToString()).ToString(); retval=gConcat(def.ComponentSeparator,pat.PatNum.ToString(),patIdCheckDigitStr,"M11",def.SubcomponentSeparator+patOidRoot+def.SubcomponentSeparator+"HL7","PI"); List<OIDExternal> listPatOidsExt=OIDExternals.GetByInternalIDAndType(pat.PatNum,IdentifierType.Patient); for(int i=0;i<listPatOidsExt.Count;i++) { patIdCheckDigitStr=MessageParser.M11CheckDigit(listPatOidsExt[i].IDExternal).ToString(); if(patIdCheckDigitStr=="-1") {//could not get a check digit from the external ID, could contain characters that are not numbers retval+=def.RepetitionSeparator+gConcat(def.ComponentSeparator,listPatOidsExt[i].IDExternal,"","", def.SubcomponentSeparator+listPatOidsExt[i].rootExternal+def.SubcomponentSeparator,"PI"); continue; } retval+=def.RepetitionSeparator+gConcat(def.ComponentSeparator,listPatOidsExt[i].IDExternal,patIdCheckDigitStr,"M11", def.SubcomponentSeparator+listPatOidsExt[i].rootExternal+def.SubcomponentSeparator,"PI"); } return retval; #endregion Patient case "pdfDescription": if(pdfDescription==null) { return ""; } return pdfDescription; case "pdfDataAsBase64": if(pdfDataString==null) { return ""; } else { return pdfDataString; } #region Procedure case "proc.DiagnosticCode": if(proc==null) { return ""; } List<string> listDiagCodes=new List<string>(); if(proc.DiagnosticCode!=null && proc.DiagnosticCode!="") { listDiagCodes.Add(proc.DiagnosticCode); } if(proc.DiagnosticCode2!=null && proc.DiagnosticCode2!="") { listDiagCodes.Add(proc.DiagnosticCode2); } if(proc.DiagnosticCode3!=null && proc.DiagnosticCode3!="") { listDiagCodes.Add(proc.DiagnosticCode3); } if(proc.DiagnosticCode4!=null && proc.DiagnosticCode4!="") { listDiagCodes.Add(proc.DiagnosticCode4); } for(int i=0;i<listDiagCodes.Count;i++) { if(retval!="") { retval+=def.RepetitionSeparator; } ICD9 icd9Cur=ICD9s.GetByCode(listDiagCodes[i]); if(icd9Cur==null) {//not a valid ICD9 code or not in the ICD9 table, just stick in the code they have in OD retval+=listDiagCodes[i]; continue; } retval+=gConcat(def.ComponentSeparator,listDiagCodes[i],icd9Cur.Description,"I9C","","","","31"); } return retval; case "proc.location": //Point of Care^Room^^Facility^^Person Location Type //Example: ClinicDescript^OpName^^&PracticeTitle^^C (C for clinic) if(proc==null || (proc.ClinicNum==0 && pat.ClinicNum==0)) {//if proc is null and both pat.ClinicNum and proc.ClinicNum are 0, return empty string return ""; } string procClinicDescript=Clinics.GetDesc(proc.ClinicNum);//could be blank if proc.ClinicNum is invalid if(procClinicDescript=="") { procClinicDescript=Clinics.GetDesc(pat.ClinicNum);//could be blank if pat.ClinicNum is invalid } string procOpName=""; if(apt!=null) { Operatory procOp=Operatories.GetOperatory(apt.Op); if(procOp!=null) { procOpName=procOp.OpName; } } practiceName=PrefC.GetString(PrefName.PracticeTitle); return gConcat(def.ComponentSeparator,procClinicDescript,procOpName,"",def.SubcomponentSeparator+practiceName,"","C"); case "proc.procDateTime": if(proc==null) { return ""; } return gDTM(proc.ProcDate,8); case "proc.ProcFee": if(proc==null) { return ""; } return proc.ProcFee.ToString("F2"); case "proc.ProcNum": if(proc==null) { return ""; } return proc.ProcNum.ToString(); case "proc.toothSurfRange": if(proc==null) { return ""; } if(_isEcwDef) { return gTreatArea(def.ComponentSeparator,proc,def.IsQuadAsToothNum); } else { return gTreatArea(def.SubcomponentSeparator,proc,def.IsQuadAsToothNum); } case "proccode.ProcCode": if(proc==null) { return ""; } if(_isEcwDef) { return gProcCodeOld(proc); } //ProcNum^Descript^CD2^^^^2014^^LaymanTerm //Example: D0150^comprehensive oral evaluation - new or established patient^CD2^^^^2014^^Comprehensive Exam return gProcCode(proc,def); #endregion Procedure #region Provider case "prov.provIdNameLFM": if(prov==null) { return ""; } if(_isEcwDef) { return gConcat(def.ComponentSeparator,prov.EcwID,prov.LName,prov.FName,prov.MI); } //Will return all provider IDs in the oidexternals table linked to this provider as repetitions //For an AIG, the provider name is one component in the form LName, FName MI and the fourth component is the provider abbreviation //For a PV1 or AIP, the provider name is separated into three components like LName^FName^MI and the sixth component is the provider abbreviation //AIG Example: |2.16.840.1.113883.3.4337.1486.6566.3.1^Abbott, Sarah L, DMD^^DrAbbott~OtherSoftware.Root.Provider.ProvID^Abbott, Sarah L, DMD^^DrAbbott| //PV1 or AIP Example: 2.16.840.1.113883.3.4337.1486.6566.3.1^Abbott^Sarah^L^DMD^DrAbbott~OtherSoftware.Root.Provider.ProvID^Abbott^Sarah^L^DMD^DrAbbott List<OIDExternal> listProvOidExt=OIDExternals.GetByInternalIDAndType(prov.ProvNum,IdentifierType.Provider); string provName=""; if(segName==SegmentNameHL7.AIG) { provName=prov.LName+", "+prov.FName+" "+prov.MI+", "+prov.Suffix; } else { provName=gConcat(def.ComponentSeparator,prov.LName,prov.FName,prov.MI,prov.Suffix); } retval=gConcat(def.ComponentSeparator,OIDInternals.GetForType(IdentifierType.Provider).IDRoot+"."+prov.ProvNum,provName,prov.Abbr); for(int i=0;i<listProvOidExt.Count;i++) { retval+=def.RepetitionSeparator+gConcat(def.ComponentSeparator,listProvOidExt[i].rootExternal+"."+listProvOidExt[i].IDExternal,provName,prov.Abbr); } return retval; case "prov.provType": if(prov==null) { return ""; } if(apt==null) { if(prov.IsSecondary) { return "H"; } return "D"; } //if we have an appt, return 'D' if prov is the dentist and 'H' if prov is the hygienist, regardless of whether they are marked secondary or not if(prov.ProvNum==apt.ProvHyg) { return "H"; } return "D";//default to 'D' - dentist #endregion Provider case "segmentAction": //This is currently only supported for SIU and SRR messages in the RSG, AIL, and AIP segments //A-Add/Insert, D-Delete, U-Update, X-No Change //SIU.S12 - Create Appt, S13 - Appt Rescheduling, S14 - Appt Modification, S15 - Appt Cancellation, S17 - Appt Deletion //SRR.S03 - Request Appointment Modification, S04 - Request Appointment Cancellation if(msgType==MessageTypeHL7.SIU && eventType==EventTypeHL7.S12) { return "A"; } if(msgType==MessageTypeHL7.SRR //all SRR messages are for updating existing appts, 'U' || (msgType==MessageTypeHL7.SIU && (eventType==EventTypeHL7.S13 || eventType==EventTypeHL7.S14 || eventType==EventTypeHL7.S15))) //SIU's with event type S13, S14, or S15 are for updating existing appts { return "U"; } if(msgType==MessageTypeHL7.SIU && eventType==EventTypeHL7.S17) { return "D"; } return "";//if not an SIU or SRR or if it is not one of these event types, return empty string case "sendingApp": //HD data type, Namespace ID^UniversalID^UniversalIDType //UniversalID=oidinternal.IDRoot for IDType of Root, UniversalIDType=HL7 //If no value in oidinternal table, then revert to 'OD' OIDInternal oidRoot=OIDInternals.GetForType(IdentifierType.Root); if(oidRoot==null) { return "OD"; } return gConcat(def.ComponentSeparator,"",oidRoot.IDRoot,"HL7"); case "separators^~\\&": return gSep(def); case "sequenceNum": return sequenceNum.ToString(); default: return ""; } }
public static string GenerateACK(HL7Def def,string fieldName,string controlId,bool isAck,string ackEvent) { //big long list of fieldnames that we support switch(fieldName) { case "ackCode": return gAck(isAck); case "dateTime.Now": return gDTM(DateTime.Now,14); case "messageControlId": return controlId; case "messageType": return gConcat(def.ComponentSeparator,"ACK",ackEvent); case "separators^~\\&": return gSep(def); default: return ""; } }
public static string GenerateFieldACK(HL7Def def,string fieldName,string controlId,bool isAck,string ackEvent) { switch(fieldName) { case "ackCode": return gAck(isAck); case "dateTime.Now": return gDTM(DateTime.Now,14); case "messageControlId": return controlId; case "messageType": return gConcat(def.ComponentSeparator,MessageTypeHL7.ACK.ToString(),ackEvent,MessageTypeHL7.ACK.ToString()); case "separators^~\\&": return gSep(def); default: return ""; } }
///<summary>Returns empty string if no duplicates, otherwise returns duplicate procedure information. In all places where this is called, we are guaranteed to have the eCW bridge turned on. So this is an eCW peculiarity rather than an HL7 restriction. Other HL7 interfaces will not be checking for duplicate procedures unless we intentionally add that as a feature later.</summary> public static string ProcsContainDuplicates(List <Procedure> procs) { bool hasLongDCodes = false; HL7Def defCur = HL7Defs.GetOneDeepEnabled(); if (defCur != null) { hasLongDCodes = defCur.HasLongDCodes; } string info = ""; List <Procedure> procsChecked = new List <Procedure>(); for (int i = 0; i < procs.Count; i++) { Procedure proc = procs[i]; ProcedureCode procCode = ProcedureCodes.GetProcCode(procs[i].CodeNum); string procCodeStr = procCode.ProcCode; if (procCodeStr.Length > 5 && procCodeStr.StartsWith("D") && !hasLongDCodes) { procCodeStr = procCodeStr.Substring(0, 5); } for (int j = 0; j < procsChecked.Count; j++) { Procedure procDup = procsChecked[j]; ProcedureCode procCodeDup = ProcedureCodes.GetProcCode(procsChecked[j].CodeNum); string procCodeDupStr = procCodeDup.ProcCode; if (procCodeDupStr.Length > 5 && procCodeDupStr.StartsWith("D") && !hasLongDCodes) { procCodeDupStr = procCodeDupStr.Substring(0, 5); } if (procCodeDupStr != procCodeStr) { continue; } if (procDup.ToothNum != proc.ToothNum) { continue; } if (procDup.ToothRange != proc.ToothRange) { continue; } if (procDup.ProcFee != proc.ProcFee) { continue; } if (procDup.Surf != proc.Surf) { continue; } if (info != "") { info += ", "; } info += procCodeDupStr; } procsChecked.Add(proc); } if (info != "") { info = Lan.g("ProcedureL", "Duplicate procedures") + ": " + info; } return(info); }
private static string gProcCode(Procedure proc,HL7Def def) { //CNE data type, ProcNum^Descript^CD2^^^^2014^^LaymanTerm //Example: D0150^comprehensive oral evaluation - new or established patient^CD2^^^^2014^^Comprehensive Exam ProcedureCode procCode=ProcedureCodes.GetProcCode(proc.CodeNum); return gConcat(def.ComponentSeparator,procCode.ProcCode,procCode.Descript,"CD2","","","","2014","",procCode.LaymanTerm); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if(def==null) {//wasn't in the database def=new HL7Def(); def.IsNew=true; def.Description="eCW Full"; def.ModeTx=ModeTxHL7.File; def.IncomingFolder=""; def.OutgoingFolder=""; def.IncomingPort=""; def.OutgoingIpPort=""; def.SftpInSocket=""; def.SftpUsername=""; def.SftpPassword=""; def.FieldSeparator="|"; def.ComponentSeparator="^"; def.SubcomponentSeparator="&"; def.RepetitionSeparator="~"; def.EscapeCharacter=@"\"; def.IsInternal=true; def.InternalType=HL7InternalType.eCWFull; def.InternalTypeVersion=Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled=false; def.Note=""; def.ShowDemographics=HL7ShowDemographics.Show; def.ShowAccount=true; def.ShowAppts=false;//for now def.IsQuadAsToothNum=false; } def.hl7DefMessages=new List<HL7DefMessage>();//so that if this is called repeatedly, it won't pile on duplicate messages. //in either case, now get all child objects, which can't be in the database. #region Inbound Messages #region ADT - Patient Demographics (Admits, Discharges, and Transfers) //====================================================================================================================== //eCW incoming patient information (ADT). HL7DefMessage msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ADT,MessageStructureHL7.ADT_A01,InOutHL7.Incoming,0); //MSH segment------------------------------------------------------------------ HL7DefSegment seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //PID segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //PID.2, Patient ID seg.AddField(2,"pat.PatNum"); //PID.4, Alternate Patient ID seg.AddField(4,"pat.ChartNumber"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race seg.AddField(10,"pat.Race"); //PID.11, Patient Address seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); //PID.22, Fee Schedule seg.AddField(22,"pat.FeeSched"); //GT1 segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,5,SegmentNameHL7.GT1); //GT1.2, Guarantor Number seg.AddField(2,"guar.PatNum"); //GT1.3, Guarantor Name seg.AddField(3,"guar.nameLFM"); //GT1.5, Guarantor Address seg.AddField(5,"guar.addressCityStateZip"); //GT1.6, Guarantor Phone Number - Home seg.AddField(6,"guar.HmPhone"); //GT1.7, Guarantor Phone Number - Business seg.AddField(7,"guar.WkPhone"); //GT1.8, Guarantor Date/Time of Birth seg.AddField(8,"guar.birthdateTime"); //GT1.9, Guarantor Administrative Sex seg.AddField(9,"guar.Gender"); //GT1.12, Guarantor SSN seg.AddField(12,"guar.SSN"); #endregion ADT - Patient Demographics (Admits, Discharges, and Transfers) #region SIU - Schedule Information Unsolicited //====================================================================================================================== //eCW incoming appointment information (SIU - Schedule information unsolicited). msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.SIU,MessageStructureHL7.SIU_S12,InOutHL7.Incoming,1); //MSH segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //PID segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //PID.2 seg.AddField(2,"pat.PatNum"); //PID.4, Alternate Patient ID seg.AddField(4,"pat.ChartNumber"); //PID.5, Patient Name seg.AddField(5,"pat.nameLFM"); //PID.7, Date/Time of Birth seg.AddField(7,"pat.birthdateTime"); //PID.8, Administrative Sex seg.AddField(8,"pat.Gender"); //PID.10, Race seg.AddField(10,"pat.Race"); //PID.11, Patient Address seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Phone Number - Home seg.AddField(13,"pat.HmPhone"); //PID.14, Phone Number - Business seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN - Patient seg.AddField(19,"pat.SSN"); //PID.22, Fee Schedule seg.AddField(22,"pat.FeeSched"); //SCH segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.SCH); //SCH.2, Filler Appointment ID. In the old eCW interface, we were pulling from SCH.2, which was always the same as SCH.1. seg.AddField(2,"apt.AptNum"); //SCH.7, Appointment Reason seg.AddField(7,"apt.Note"); //SCH.11, Appointment Timing Quantity seg.AddField(11,"apt.lengthStartEnd"); //AIG segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,4,false,true,SegmentNameHL7.AIG); //AIG.3, Resource ID^Resource Name (Lname, Fname all as a string) seg.AddField(3,"prov.provIdName"); //PV1 segment.----------------------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,3,false,true,SegmentNameHL7.PV1); //PV1.7, Attending/Primary Care Doctor, UPIN^LastName^FirstName^MI seg.AddField(7,"prov.provIdNameLFM"); #endregion SIU - Schedule Information Unsolicited #region ACK - General Acknowledgment //======================================================================================================================= //Acknowledgment message (ACK) msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ACK,MessageStructureHL7.ADT_A01,InOutHL7.Incoming,2); //MSH segment------------------------------------------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.8, Message Type seg.AddField(8,"messageType"); //MSA (Message Acknowledgment) segment----------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.MSA); //MSA.1, Acknowledgment Code seg.AddField(1,"ackCode"); //MSA.2, Message Control ID seg.AddField(2,"messageControlId"); #endregion ACK - General Acknowledgment #endregion Inbound Messages #region Outbound Messages #region DFT - Detailed Financial Transaction //======================================================================================================================= //Detail financial transaction (DFT) msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.DFT,MessageStructureHL7.DFT_P03,InOutHL7.Outgoing,3); //MSH (Message Header) segment------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2,DataTypeHL7.HD,"OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4,DataTypeHL7.HD,"ECW"); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10,DataTypeHL7.PT,"P"); //MSH.11, Version ID seg.AddFieldFixed(11,DataTypeHL7.VID,"2.3"); //EVN (Event Type) segment----------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.EVN); //EVN.1, Event Type, example P03 seg.AddField(1,"eventType"); //EVN.2, Recorded Date/Time seg.AddField(2,"dateTime.Now"); //PID (Patient Identification) segment----------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //PID.1, Sequence Number (1 for DFT's) seg.AddFieldFixed(1,DataTypeHL7.ST,"1"); //PID.2, Patient ID (Account number. eCW requires this to be the same # as came in on PID.4.) seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient MRN number seg.AddField(3,"pat.PatNum"); //PID.5, Patient Name (Last^First^MI) seg.AddField(5,"pat.nameLFM"); //PID.7, Birthdate seg.AddField(7,"pat.birthdateTime"); //PID.8, Gender seg.AddField(8,"pat.Gender"); //PID.10, Race seg.AddField(10,"pat.Race"); //PID.11, Address seg.AddField(11,"pat.addressCityStateZip"); //PID.13, Home Phone seg.AddField(13,"pat.HmPhone"); //PID.14, Work Phone seg.AddField(14,"pat.WkPhone"); //PID.16, Marital Status seg.AddField(16,"pat.Position"); //PID.19, SSN seg.AddField(19,"pat.SSN"); //PV1 (Patient Visit) segment-------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PV1); //PV1.7, Attending/Primary Care Doctor seg.AddField(7,"prov.provIdNameLFM"); //PV1.19, Visit Number seg.AddField(19,"apt.AptNum"); //FT1 (Financial Transaction Information) segment------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,4,true,true,SegmentNameHL7.FT1); //FT1.1, Sequence Number (starts with 1) seg.AddField(1,"sequenceNum"); //FT1.4, Transaction Date (YYYYMMDDHHMMSS) seg.AddField(4,"proc.procDateTime"); //FT1.5, Transaction Posting Date (YYYYMMDDHHMMSS) seg.AddField(5,"proc.procDateTime"); //FT1.6, Transaction Type seg.AddFieldFixed(6,DataTypeHL7.IS,"CG"); //FT1.10, Transaction Quantity seg.AddFieldFixed(10,DataTypeHL7.NM,"1.0"); //FT1.19, Diagnosis Code seg.AddField(19,"proc.DiagnosticCode"); //FT1.20, Performed by Code (provider) seg.AddField(20,"prov.provIdNameLFM"); //FT1.21, Ordering Provider seg.AddField(21,"prov.provIdNameLFM"); //FT1.22, Unit Cost (procedure fee) seg.AddField(22,"proc.ProcFee"); //FT1.25, Procedure Code seg.AddField(25,"proccode.ProcCode"); //FT1.26, Modifiers (treatment area) seg.AddField(26,"proc.toothSurfRange"); //DG1 (Diagnosis) segment is optional, skip for now //ZX1 (PDF Data) segment------------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,5,SegmentNameHL7.ZX1); //ZX1.1 seg.AddFieldFixed(1,DataTypeHL7.ST,"6"); //ZX1.2 seg.AddFieldFixed(2,DataTypeHL7.ST,"PDF"); //ZX1.3 seg.AddFieldFixed(3,DataTypeHL7.ST,"PATHOLOGY^Pathology Report^L"); //ZX1.4 seg.AddField(4,"pdfDescription"); //ZX1.5 seg.AddField(5,"pdfDataAsBase64"); #endregion DFT - Detailed Financial Transaction #region ACK - General Acknowledgment //======================================================================================================================= //Message Acknowledgment (ACK) msg=new HL7DefMessage(); def.AddMessage(msg,MessageTypeHL7.ACK,MessageStructureHL7.ADT_A01,InOutHL7.Outgoing,4); //MSH (Message Header) segment------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,0,SegmentNameHL7.MSH); //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2,DataTypeHL7.HD,"OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4,DataTypeHL7.HD,"ECW"); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10,DataTypeHL7.PT,"P"); //MSH.11, Version ID seg.AddFieldFixed(11,DataTypeHL7.VID,"2.3"); //MSA (Message Acknowledgment) segment----------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.MSA); //MSA.1, Acknowledgment Code seg.AddField(1,"ackCode"); //MSA.2, Message Control ID seg.AddField(2,"messageControlId"); #endregion ACK - General Acknowledgment #endregion Outbound Messages return def; }
///<summary>Gets the patient info from the MedLab.OriginalPIDSegments. Returns null if there is an error processing the PID segments.</summary> private List <string[]> GetPatInfoFromPidSegments() { List <string[]> listPats = new List <string[]>(); HL7Def hl7DefCur = HL7Defs.GetOneDeepEnabled(true); if (hl7DefCur == null) { MsgBox.Show(this, "There must be an enabled MedLab HL7 interface in order to parse the message details."); return(null); } HL7DefMessage hl7defmsg = null; for (int i = 0; i < hl7DefCur.hl7DefMessages.Count; i++) { //for now there are only incoming ORU messages supported, so there should only be one defined message type and it should be inbound if (hl7DefCur.hl7DefMessages[i].MessageType == MessageTypeHL7.ORU && hl7DefCur.hl7DefMessages[i].InOrOut == InOutHL7.Incoming) { hl7defmsg = hl7DefCur.hl7DefMessages[i]; break; } } if (hl7defmsg == null) { MsgBox.Show(this, "There must be a message definition for an inbound ORU message in order to parse this message."); return(null); } //for MedLab interfaces, we limit the ability to rearrange the message structure, so the PID segment is always in position 1 if (hl7defmsg.hl7DefSegments.Count < 2) { MsgBox.Show(this, "The message definition for an inbound ORU message does not have the correct number of segments."); return(null); } HL7DefSegment pidSegDef = hl7defmsg.hl7DefSegments[1]; if (pidSegDef.SegmentName != SegmentNameHL7.PID) { MsgBox.Show(this, "The message definition for an inbound ORU message does not have the PID segment as the second segment."); return(null); } for (int i = 0; i < ListMedLabs.Count; i++) { string[] fields = ListMedLabs[i].OriginalPIDSegment.Split(new string[] { "|" }, StringSplitOptions.None); List <FieldHL7> listFields = new List <FieldHL7>(); for (int j = 0; j < fields.Length; j++) { listFields.Add(new FieldHL7(fields[j])); } string patName = ""; string birthdate = ""; string gender = ""; string ssn = ""; for (int j = 0; j < pidSegDef.hl7DefFields.Count; j++) { int itemOrder = pidSegDef.hl7DefFields[j].OrdinalPos; if (itemOrder > listFields.Count - 1) { continue; } switch (pidSegDef.hl7DefFields[j].FieldName) { case "pat.nameLFM": patName = listFields[itemOrder].GetComponentVal(1); if (patName != "" && listFields[itemOrder].GetComponentVal(2) != "") { patName += " "; } patName += listFields[itemOrder].GetComponentVal(2); if (patName != "" && listFields[itemOrder].GetComponentVal(0) != "") { patName += " "; } patName += listFields[itemOrder].GetComponentVal(0); continue; case "patBirthdateAge": //LabCorp sends the birthdate and age in years, months, and days like yyyyMMdd^YYY^MM^DD birthdate = FieldParser.DateTimeParse(listFields[itemOrder].GetComponentVal(0)).ToShortDateString(); continue; case "pat.Gender": gender = Lan.g("enumPatientGender", FieldParser.GenderParse(listFields[itemOrder].GetComponentVal(0)).ToString()); continue; case "pat.SSN": if (listFields[itemOrder].GetComponentVal(0).Length > 3) { ssn = "***-**-"; ssn += listFields[itemOrder].GetComponentVal(0).Substring(listFields[itemOrder].GetComponentVal(0).Length - 4, 4); } continue; default: continue; } } bool isDuplicate = false; for (int j = 0; j < listPats.Count; j++) { if (listPats[j].Length < 4) //should never happen { continue; } if (listPats[j][0] == patName && listPats[j][1] == birthdate && listPats[j][2] == gender && listPats[j][3] == ssn) { isDuplicate = true; } } if (!isDuplicate) { listPats.Add(new string[] { patName, birthdate, gender, ssn }); } } return(listPats); }
public static HL7Def GetDeepInternal(HL7Def def) { //ok to pass in null if(def==null) {//wasn't in the database def=new HL7Def(); def.IsNew=true; def.Description="Centricity"; def.ModeTx=ModeTxHL7.File; def.IncomingFolder=""; def.OutgoingFolder=""; def.IncomingPort=""; def.OutgoingIpPort=""; def.SftpInSocket=""; def.SftpUsername=""; def.SftpPassword=""; def.FieldSeparator="|"; def.ComponentSeparator="^"; def.SubcomponentSeparator="&"; def.RepetitionSeparator="~"; def.EscapeCharacter=@"\"; def.IsInternal=true; def.InternalType=HL7InternalType.Centricity; def.InternalTypeVersion=Assembly.GetAssembly(typeof(Db)).GetName().Version.ToString(); def.IsEnabled=false; def.Note=""; def.ShowDemographics=HL7ShowDemographics.ChangeAndAdd; def.ShowAccount=true; def.ShowAppts=true; def.IsQuadAsToothNum=false; } def.hl7DefMessages=new List<HL7DefMessage>(); HL7DefMessage msg=new HL7DefMessage(); HL7DefSegment seg=new HL7DefSegment(); #region Outbound Messages #region DFT - Detailed Financial Transaction //======================================================================================================================= //Detail financial transaction (DFT) def.AddMessage(msg,MessageTypeHL7.DFT,MessageStructureHL7.DFT_P03,InOutHL7.Outgoing,2); //MSH (Message Header) segment------------------------------------------------- msg.AddSegment(seg,0,SegmentNameHL7.MSH); //HL7 documentation says field 1 is Field Separator. "This field contains the separator between the segment ID and the first real field. As such it serves as the separator and defines the character to be used as a separator for the rest of the message." (HL7 v2.6 documentation) The separator is usually | (pipes) and is part of field 0, which is the segment ID followed by a |. Encoding Characters is the first real field, so it will be numbered starting with 1 in our def. //MSH.1, Encoding Characters (DataType.ST) seg.AddField(1,"separators^~\\&"); //MSH.2, Sending Application seg.AddFieldFixed(2,DataTypeHL7.HD,"OD"); //MSH.4, Receiving Application seg.AddFieldFixed(4,DataTypeHL7.HD,def.Description); //MSH.6, Message Date and Time (YYYYMMDDHHMMSS) seg.AddField(6,"dateTime.Now"); //MSH.8, Message Type^Event Type, example DFT^P03 seg.AddField(8,"messageType"); //MSH.9, Message Control ID seg.AddField(9,"messageControlId"); //MSH.10, Processing ID (P-production, T-test) seg.AddFieldFixed(10,DataTypeHL7.PT,"P"); //MSH.11, Version ID seg.AddFieldFixed(11,DataTypeHL7.VID,"2.3"); //MSH.16, Application Ack Type (AL=Always, NE=Never, ER=Error/reject conditions only, SU=Successful completion only) seg.AddFieldFixed(15,DataTypeHL7.ID,"NE"); //EVN (Event Type) segment----------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,1,SegmentNameHL7.EVN); //EVN.1, Event Type, example P03 seg.AddField(1,"eventType"); //EVN.2, Recorded Date/Time seg.AddField(2,"dateTime.Now"); //EVN.3, Event Reason Code seg.AddFieldFixed(3,DataTypeHL7.IS,"01"); //PID (Patient Identification) segment----------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,2,SegmentNameHL7.PID); //PID.1, Sequence Number (1 for DFT's) "This field contains the number that identifies this transaction. For the first occurrence of the segment, the sequence number shall be one, for the second occurrence, the sequence number shall be two, etc." (HL7 v2.6 documentation) We only send 1 PID segment in DFT's so this number will always be 1. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PID.2, Patient ID (External) seg.AddField(2,"pat.ChartNumber"); //PID.3, Patient ID (Internal) seg.AddField(3,"pat.PatNum"); //PV1 (Patient Visit) segment-------------------------------------------------- seg=new HL7DefSegment(); msg.AddSegment(seg,3,SegmentNameHL7.PV1); //PV1.1, Set ID - PV1 (1 for DFT's) See the comment above for the Sequence Number of the PID segment. Always 1 since we only send one PV1 segment per DFT message. seg.AddFieldFixed(1,DataTypeHL7.SI,"1"); //PV1.2, Patient Class (E=Emergency, I=Inpatient, O=Outpatient, P=Preadmit, R=Recurring patient, B=Obstetrics, C=Commercial Account, N=Not Applicable, U=Unkown) We will just send O for outpatient for every DFT message. seg.AddFieldFixed(2,DataTypeHL7.IS,"O"); //todo: ClinicNum? //PV1.3, Assigned Patient Location //PV1.7, Attending/Primary Care Doctor seg.AddField(7,"prov.provIdNameLFM"); //todo: Referring Dr? //PV1.8, Referring Doctor //PV1.19, Visit Number seg.AddField(19,"apt.AptNum"); //PV1.44, Admit Date/Time seg.AddField(44,"proc.procDateTime"); //PV1.50, Alternate Visit ID //FT1 (Financial Transaction Information) segment------------------------------ seg=new HL7DefSegment(); msg.AddSegment(seg,4,true,true,SegmentNameHL7.FT1); //FT1.1, Sequence Number (starts with 1) seg.AddField(1,"sequenceNum"); //FT1.2, Transaction ID seg.AddField(2,"proc.ProcNum"); //FT1.4, Transaction Date (YYYYMMDDHHMMSS) seg.AddField(4,"proc.procDateTime"); //FT1.5, Transaction Posting Date (YYYYMMDDHHMMSS) seg.AddField(5,"proc.procDateTime"); //FT1.6, Transaction Type seg.AddFieldFixed(6,DataTypeHL7.IS,"CG"); //FT1.10, Transaction Quantity seg.AddFieldFixed(10,DataTypeHL7.NM,"1.0"); //FT1.11, Transaction Amount Extended (Total fee to charge for this procedure, independent of transaction quantity) seg.AddField(11,"proc.ProcFee"); //FT1.12, Transaction Amount Unit (Fee for this procedure for each transaction quantity) seg.AddField(12,"proc.ProcFee"); //todo: ClinicNum? //FT1.16, Assigned Patient Location //FT1.19, Diagnosis Code seg.AddField(19,"proc.DiagnosticCode"); //FT1.21, Ordering Provider seg.AddField(21,"prov.provIdNameLFM"); //FT1.22, Unit Cost (procedure fee) seg.AddField(22,"proc.ProcFee"); //FT1.25, Procedure Code seg.AddField(25,"proccode.ProcCode"); //FT1.26, Modifiers (treatment area) seg.AddField(26,"proc.toothSurfRange"); //DG1 (Diagnosis) segment is optional, skip for now //PR1 (Procedures) segment is optional, skip for now #endregion DFT - Detailed Financial Transaction #endregion Outbound Messages return def; }