Beispiel #1
0
		///<summary>Creates the Message object and fills it with data.  All panels will be for the same patient.</summary>
		public void Initialize(List<LabPanel> panels) {
			if(panels.Count==0) {
				throw new ApplicationException("Must be at least one lab panel.");
			}
			//LabPanel panel=panelList[0];
			//List<LabResult> labresultList=LabResults.GetForPanel(panel.LabPanelNum);
			//if(labresultList.Count!=1) {
			//	throw new ApplicationException("Lab panel must have exactly one lab result.");
			//}
			//LabResult labresult=labresultList[0];
			Patient pat=Patients.GetPat(panels[0].PatNum);
			msg=new MessageHL7(MessageTypeHL7.ORU);
			MSH();
			PID(pat);
			for(int p=0;p<panels.Count;p++) {
				List<LabResult> results=LabResults.GetForPanel(panels[p].LabPanelNum);
				if(results.Count==0) {
					break;//Simply shows an empty lab panel to the user.
				}
				OBR(panels[p],results[0].DateTimeTest);
				for(int r=0;r<results.Count;r++) {
					OBX(results[r],r);
				}
			}
		}
Beispiel #2
0
        //public static void ProcessIN1() {
        //	return;
        //}

        public static void ProcessMSA(HL7DefSegment segDef, SegmentHL7 seg, MessageHL7 msg)
        {
            int ackCodeOrder      = 0;
            int msgControlIdOrder = 0;

            //find position of AckCode in segDef for MSA seg
            for (int i = 0; i < segDef.hl7DefFields.Count; i++)
            {
                if (segDef.hl7DefFields[i].FieldName == "ackCode")
                {
                    ackCodeOrder = segDef.hl7DefFields[i].OrdinalPos;
                }
                if (segDef.hl7DefFields[i].FieldName == "messageControlId")
                {
                    msgControlIdOrder = segDef.hl7DefFields[i].OrdinalPos;
                }
            }
            if (ackCodeOrder == 0)           //no ackCode defined for this def of MSA, do nothing with it?
            {
                return;
            }
            if (msgControlIdOrder == 0)           //no messageControlId defined for this def of MSA, do nothing with it?
            {
                return;
            }
            //set msg.AckCode to value in position located in def of ackcode in seg
            msg.AckCode   = seg.Fields[ackCodeOrder].ToString();
            msg.ControlId = seg.Fields[msgControlIdOrder].ToString();
        }
Beispiel #3
0
        ///<summary>Creates the Message object and fills it with data.  All panels will be for the same patient.</summary>
        public void Initialize(List <LabPanel> panels)
        {
            if (panels.Count == 0)
            {
                throw new ApplicationException("Must be at least one lab panel.");
            }
            //LabPanel panel=panelList[0];
            //List<LabResult> labresultList=LabResults.GetForPanel(panel.LabPanelNum);
            //if(labresultList.Count!=1) {
            //	throw new ApplicationException("Lab panel must have exactly one lab result.");
            //}
            //LabResult labresult=labresultList[0];
            Patient pat = Patients.GetPat(panels[0].PatNum);

            msg = new MessageHL7(MessageType.ORU);
            MSH();
            PID(pat);
            for (int p = 0; p < panels.Count; p++)
            {
                List <LabResult> results = LabResults.GetForPanel(panels[p].LabPanelNum);
                OBR(panels[p], results[0].DateTimeTest);
                for (int r = 0; r < results.Count; r++)
                {
                    OBX(results[r], r);
                }
            }
        }
Beispiel #4
0
		private void BuildMessage() {
			_msg=new MessageHL7(MessageTypeHL7.VXU);//Message format for VXU is on guide page 160.
			MSH();//MSH segment. Required.  Cardinality [1..1].
			//SFT segment. Optional. Cardinality [0..*].  Undefined and may be locally specified.
			PID();//PID segment.  Required.  Cardinality [1..1].
			PD1();//PD1 segment.  Required if known.  Cardinality [0..1].
			NK1();//NK1 segment.  Required if known.  Cardinality [0..1].
			//PV1 segment.  Optional.  Cardinality [0..1].  Undefined and may be locally specified.
			//PV2 segment.  Optional.  Cardinality [0..1].  Undefined and may be locally specified.
			//GT1 segment.  Optional.  Cardinality [0..*].  Undefined and may be locally specified.
			//Begin Insurance group.  Optional.  Cardinality [0..*].
			//IN1 segment.  Optional.  Cardinality [0..1].  Undefined and may be locally specified.  Guide page 102.
			//IN2 segment.  Optional.  Cardinality [0..1].  Undefined and may be locally specified.  Guide page 102.
			//IN3 segment.  Optional.  Cardinality [0..1].  Undefined and may be locally specified.  Guide page 102.
			//End Insurance group.
			//Begin Order group.
			for(int i=0;i<_vaccines.Count;i++) {
				ORC(_vaccines[i]);//ORC segment.  Required.  Cardinality [1..1].
				//TQ1 segment.  Optional.  Cardinality [0..1]. Undefined and may be locally specified.
				//TQ2 segment.  Optional.  Cardinality [0..1]. Undefined and may be locally specified.
				RXA(_vaccines[i]);//RXA segment.  Required.  Cardinality [1..1].
				RXR(_vaccines[i]);//RXR segment.  Required if known.  Cardinality [0..1].
				OBX(_vaccines[i]);//OBX segment.  Required if known.  Cardinality [0..*].
				NTE();//NTE segment.  Required if known.  Cardinality [0..1].
			}
			//End Order group.
		}
		///<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;
		}
Beispiel #6
0
		///<summary>Creates the Message object and fills it with data.</summary>
		public void InitializeEcw(long aptNum,long provNum,Patient pat,string pdfDataAsBase64,string pdfDescription,bool justPDF){
			msg=new MessageHL7(MessageTypeHL7.DFT);
			MSH();
			EVN();
			PID(pat);
			PV1(aptNum,provNum);
			FT1(aptNum,justPDF);
			DG1();
			ZX1(pdfDataAsBase64,pdfDescription);
		}
Beispiel #7
0
 ///<summary>Creates the Message object and fills it with data.</summary>
 public void InitializeEcw(long aptNum, long provNum, Patient pat, string pdfDataAsBase64, string pdfDescription, bool justPDF, List <Procedure> listProcs)
 {
     msg = new MessageHL7(MessageTypeHL7.DFT);
     MSH();
     EVN();
     PID(pat);
     PV1(aptNum, provNum);
     FT1(listProcs, justPDF);
     DG1();
     ZX1(pdfDataAsBase64, pdfDescription);
 }
Beispiel #8
0
		private void BuildMessage() {
			_msg=new MessageHL7(MessageTypeHL7.ADT);
			MSH();//MSH segment. Required.  Cardinality [1..1].
			EVN();//EVN segment.  Required.  Cardinality [1..1].
			PID();//PID segment.  Required.  Cardinality [1..1].
			PV1();//PV1 segment.  Required.  Cardinality [1..1].
			//PV2 segment.  Required if known.  Cardinality [0..1].  Not required for ambulatory.
			//DG1 segment.  Required if known.  Cardinality [0..*].  Not required for ambulatory.
			//PR1 segment.  Optional.  Cardinality [0..*].
			OBX();//OBX segment.  Required.  Cardinality [1..*].
			//IN1 segment.  Optional.  Cardinality [0..*].
		}
Beispiel #9
0
 ///<summary>Creates the Message object and fills it with data.  Vaccines must all be for the same patient.</summary>
 public void Initialize(Patient pat,List<VaccinePat> vaccines)
 {
     if(vaccines.Count==0) {
         throw new ApplicationException("Must be at least one vaccine.");
     }
     msg=new MessageHL7(MessageType.VXU);
     MSH();
     PID(pat);
     for(int i=0;i<vaccines.Count;i++) {
         ORC();
         RXA(vaccines[i]);
     }
 }
Beispiel #10
0
 private void BuildMessage()
 {
     _msg = new MessageHL7(MessageTypeHL7.ADT);
     MSH();            //MSH segment. Required.  Cardinality [1..1].
     EVN();            //EVN segment.  Required.  Cardinality [1..1].
     PID();            //PID segment.  Required.  Cardinality [1..1].
     PV1();            //PV1 segment.  Required.  Cardinality [1..1].
     //PV2 segment.  Required if known.  Cardinality [0..1].  Not required for ambulatory.
     //DG1 segment.  Required if known.  Cardinality [0..*].  Not required for ambulatory.
     //PR1 segment.  Optional.  Cardinality [0..*].
     OBX();            //OBX segment.  Required.  Cardinality [1..*].
     //IN1 segment.  Optional.  Cardinality [0..*].
 }
Beispiel #11
0
 ///<summary>Creates the Message object and fills it with data.  Vaccines must all be for the same patient.</summary>
 public void Initialize(Patient pat, List <VaccinePat> vaccines)
 {
     if (vaccines.Count == 0)
     {
         throw new ApplicationException("Must be at least one vaccine.");
     }
     msg = new MessageHL7(MessageType.VXU);
     MSH();
     PID(pat);
     for (int i = 0; i < vaccines.Count; i++)
     {
         ORC();
         RXA(vaccines[i]);
     }
 }
        ///<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);
        }
Beispiel #13
0
		private void EcwOldProcessMessage(string fullPath) {
			string msgtext="";
			int i=0;
			while(i<5) {
				try {
					msgtext=File.ReadAllText(fullPath);
					break;
				}
				catch {
				}
				Thread.Sleep(200);
				i++;
				if(i==5) {
					EventLog.WriteEntry("Could not read text from file due to file locking issues.",EventLogEntryType.Error);
					return;
				}
			}
			try {
				MessageHL7 msg=new MessageHL7(msgtext);//this creates an entire heirarchy of objects.
				if(msg.MsgType==MessageTypeHL7.ADT) {
					if(IsVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Processed ADT message",EventLogEntryType.Information);
					}
					EcwADT.ProcessMessage(msg,ecwOldIsStandalone,IsVerboseLogging);
				}
				else if(msg.MsgType==MessageTypeHL7.SIU && !ecwOldIsStandalone) {//appointments don't get imported if standalone mode.
					if(IsVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Processed SUI message",EventLogEntryType.Information);
					}
					EcwSIU.ProcessMessage(msg,IsVerboseLogging);
				}
			}
			catch(Exception ex) {
				EventLog.WriteEntry(ex.Message+"\r\n"+ex.StackTrace,EventLogEntryType.Error);
				return;
			}
			//we won't be processing DFT messages.
			//else if(msg.MsgType==MessageType.DFT) {
				//ADT.ProcessMessage(msg);
			//}
			try {
				File.Delete(fullPath);
			}
			catch(Exception ex) {
				EventLog.WriteEntry("Delete failed for "+fullPath+"\r\n"+ex.Message,EventLogEntryType.Error);
			}
		}
Beispiel #14
0
        public static void ProcessMSH(HL7DefSegment segDef, SegmentHL7 seg, MessageHL7 msg)
        {
            int msgControlIdOrder = 0;

            //find position of messageControlId in segDef for MSH seg
            for (int i = 0; i < segDef.hl7DefFields.Count; i++)
            {
                if (segDef.hl7DefFields[i].FieldName == "messageControlId")
                {
                    msgControlIdOrder = segDef.hl7DefFields[i].OrdinalPos;
                    break;
                }
            }
            if (msgControlIdOrder == 0)
            {
                return;
            }
            msg.ControlId = seg.Fields[msgControlIdOrder].ToString();
        }
Beispiel #15
0
		///<summary>Gets the message control ID of the message we are attempting to send, for TCP/IP acknowledgment.</summary>
		public static string GetControlId(HL7Msg msg) {
			string retval="";
			if(msg==null) {
				return retval;
			}
			int controlIdOrder=0;
			MessageHL7 msgHl7=new MessageHL7(msg.MsgText);//creates the segments
			HL7Def def=HL7Defs.GetOneDeepEnabled();
			if(def==null) {
				return retval;
			}
			HL7DefMessage hl7defmsg=null;
			for(int i=0;i<def.hl7DefMessages.Count;i++) {
				if(def.hl7DefMessages[i].MessageType==msgHl7.MsgType) {
					hl7defmsg=def.hl7DefMessages[i];
					break;
				}
			}
			if(hl7defmsg==null) {//No message definition for this type of message in the enabled def
				return retval;
			}
			for(int s=0;s<hl7defmsg.hl7DefSegments.Count;s++) {//get MSH segment
				if(hl7defmsg.hl7DefSegments[s].SegmentName==SegmentNameHL7.MSH) {
					for(int f=0;f<hl7defmsg.hl7DefSegments[s].hl7DefFields.Count;f++) {//find messageControlId field in MSH segment def
						if(hl7defmsg.hl7DefSegments[s].hl7DefFields[f].FieldName=="messageControlId") {
							controlIdOrder=hl7defmsg.hl7DefSegments[s].hl7DefFields[f].OrdinalPos;
							break;
						}
					}
					break;
				}
			}
			if(controlIdOrder==0) {//No messageControlId defined for this MSH segment
				return retval;
			}
			for(int i=0;i<msgHl7.Segments.Count;i++) {//get control ID from message located in MSH segment with field determined above
				if(msgHl7.Segments[i].Name==SegmentNameHL7.MSH) {
					retval=msgHl7.Segments[i].Fields[controlIdOrder].ToString();
					break;
				}
			}
			return retval;
		}
		private void textHL7Raw_TextChanged(object sender,EventArgs e) {
			textPatName.Text="";
			textPatIDNum.Text="";
			textPatAccountNum.Text="";
			textDateTimeTest.Text="";
			textServiceID.Text="";
			textServiceName.Text="";
			try {
				//ORU-R01
				MessageHL7 msg=new MessageHL7(textHL7Raw.Text);
				SegmentHL7 segPID=msg.GetSegment(SegmentNameHL7.PID,false);
				patNum=0;
				if(segPID!=null) {
					fName=segPID.GetFieldComponent(5,1);
					lName=segPID.GetFieldComponent(5,0);
					//F M L ?
					textPatName.Text=segPID.GetFieldComponent(5,1)+" "+segPID.GetFieldComponent(5,2)+" "+segPID.GetFieldComponent(5,0);
					textPatIDNum.Text=segPID.GetFieldFullText(2);
					patNum=Patients.GetPatNumByName(lName,fName);//could be 0
					//patNum=PIn.Long(segPID.GetFieldFullText(2));
					textPatAccountNum.Text=segPID.GetFieldFullText(18);
				}
				SegmentHL7 segOBR=msg.GetSegment(SegmentNameHL7.OBR,false);
				if(segOBR!=null) {
					textServiceID.Text=segOBR.GetFieldComponent(4,0);
					textServiceName.Text=segOBR.GetFieldComponent(4,1);
				}
				SegmentHL7 segOBX=msg.GetSegment(SegmentNameHL7.OBX,false);
				if(segOBX!=null) {
					DateTime dt=segOBX.GetDateTime(14);
					if(dt.Year>1880) {
						textDateTimeTest.Text=dt.ToShortDateString();// +" "+dt.ToShortTimeString();
					}
				}
				FillPatAndGrid();
			}
			catch {
				patNum=0;
				FillPatAndGrid();
				MessageBox.Show("Error parsing HL7.");
			}
		}
Beispiel #17
0
        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>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;
		}
Beispiel #19
0
		public static void ProcessPRB(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			int probActionOrder=-1;
			int probCodeOrder=-1;
			int probStartDateOrder=-1;
			int probStopDateOrder=-1;
			int probUniqueIdOrder=-1;
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int itemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "problemAction":
						probActionOrder=itemOrder;
						continue;
					case "problemCode":
						probCodeOrder=itemOrder;
						continue;
					case "problemStartDate":
						probStartDateOrder=itemOrder;
						continue;
					case "problemStopDate":
						probStopDateOrder=itemOrder;
						continue;
					case "problemUniqueId":
						probUniqueIdOrder=itemOrder;
						continue;
					default:
						continue;
				}
			}
			//we need these 3 items defined in order to process the problem
			if(probActionOrder<0 || probCodeOrder<0 || probUniqueIdOrder<0) {
				EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed.  The segment must have an action, code, and unique ID defined.",EventLogEntryType.Information);
				return;
			}
			//get values from defined locations within the segment and validate info
			//We only add or update problems.  Other actions like delete or correct are not yet supported
			if(seg.GetFieldComponent(probActionOrder).ToLower()!="ad" && seg.GetFieldComponent(probActionOrder).ToLower()!="up") {
				EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed.  The action codes supported are 'AD' for add or 'UP' for update.",EventLogEntryType.Information);
				return;
			}
			long probDefNum=DiseaseDefs.GetNumFromSnomed(PIn.String(seg.GetFieldComponent(probCodeOrder,0)));
			//The problem must be a SNOMEDCT code, identified by the coding system table 0396 value "SNM" in component 3 of the CWE problem code field
			//There must be a disease def setup with the SNOMEDCT code in the problem list or we will ignore this problem
			if(seg.GetFieldComponent(probCodeOrder,2).ToLower()!="snm" || probDefNum==0) {
				EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed.  "
					+"The code is not attached to an existing problem definition or is not a SNOMEDCT code.",EventLogEntryType.Information);
				return;
			}
			string probIdExternal=seg.GetFieldComponent(probUniqueIdOrder,0);
			string probRootExternal=seg.GetFieldComponent(probUniqueIdOrder,2);
			if(probIdExternal=="" || probRootExternal=="") {
				EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed. "
					+" The problem does not have a unique ID with assigning authority root.",EventLogEntryType.Information);
				return;
			}
			//If problem external ID and root is in the database, but is used to identify an object other than a problem, do not process the segment
			OIDExternal probOidExt=OIDExternals.GetByRootAndExtension(probRootExternal,probIdExternal);
			if(probOidExt!=null && probOidExt.IDType!=IdentifierType.Problem) {
				EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed.  "
					+"The problem has a unique ID with assigning authority root that has already been used to identify an object of type "
					+probOidExt.IDType.ToString()+".",EventLogEntryType.Information);
				return;
			}
			long diseaseNum=0;
			if(probOidExt!=null) {//exists in oidexternal table and is of type Problem, so IDInternal is a DiseaseNum
				diseaseNum=probOidExt.IDInternal;					
			}
			Disease probCur=new Disease();
			probCur.DiseaseNum=diseaseNum;//probNum could be 0 if new
			//The problem referenced by the external root and ID is already linked in the oidexternal table, get the problem to update
			//Also make sure the problem linked by oidexternal table is for the patient identified in the PID segment
			if(diseaseNum!=0) {
				probCur=Diseases.GetOne(diseaseNum);
				if(probCur==null || probCur.PatNum!=pat.PatNum) {//should never be null if in the oidexternal table
					EventLog.WriteEntry("OpenDentHL7","The PRB segment was not processed.  "
						+"The problem referenced and the patient in the PID segment do not match.",EventLogEntryType.Information);
					return;
				}
			}
			DateTime dateProbStart=DateTime.MinValue;
			if(probStartDateOrder>-1) {
				dateProbStart=FieldParser.DateTimeParse(seg.GetFieldComponent(probStartDateOrder));
			}
			DateTime dateProbStop=DateTime.MinValue;
			if(probStopDateOrder>-1) {
				dateProbStop=FieldParser.DateTimeParse(seg.GetFieldComponent(probStopDateOrder));
			}
			//The patient may already have an active problem with this DiseaseDefNum, but it is not referenced by this problem GUID
			//Mark the existing problem inactive and add a new one with StartDate of today
			//Add an entry in the oidexternal table that will point the problem GUID to this new problem.
			List<Disease> listProbsForPat=Diseases.GetDiseasesForPatient(pat.PatNum,probDefNum,true);
			int markedInactiveCount=0;
			for(int p=0;p<listProbsForPat.Count;p++) {
				if(listProbsForPat[p].DiseaseNum==diseaseNum) {//probNum may be 0 if there was not an existing problem referenced by the GUID in the message
					continue;
				}
				listProbsForPat[p].ProbStatus=ProblemStatus.Inactive;
				Diseases.Update(listProbsForPat[p]);
				markedInactiveCount++;
			}
			if(_isVerboseLogging && markedInactiveCount>0) {
				EventLog.WriteEntry("OpenDentHL7","Updated "+markedInactiveCount.ToString()+" problems to a status of inactive due to an incoming PRB segment.",EventLogEntryType.Information);
			}
			Disease probOld=probCur.Copy();
			probCur.PatNum=pat.PatNum;
			probCur.DiseaseDefNum=probDefNum;
			probCur.ProbStatus=ProblemStatus.Active;
			probCur.DateStart=dateProbStart;//could be '0001-01-01' if not present or not the correct format, handled by FieldParser.DateTimeParse
			probCur.DateStop=dateProbStop;//could be '0001-01-01' if not present or not the correct format, handled by FieldParser.DateTimeParse
			if(probCur.DiseaseNum==0) {//new problem
				//insert new problem
				probCur.DiseaseNum=Diseases.Insert(probCur);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted a new problem for patient "+pat.GetNameFLnoPref()+" due to an incoming PRB segment.",EventLogEntryType.Information);
				}
				//using DiseaseNum from newly inserted problem, link to the external GUID and root in the oidexternals table
				probOidExt=new OIDExternal();
				probOidExt.IDType=IdentifierType.Problem;
				probOidExt.IDInternal=probCur.DiseaseNum;
				probOidExt.IDExternal=probIdExternal;
				probOidExt.rootExternal=probRootExternal;
				OIDExternals.Insert(probOidExt);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Added an external problem ID to the oidexternals table due to an incoming PRB segment.\r\nDiseaesNum: "
						+probCur.DiseaseNum.ToString()+", External problem ID: "+probOidExt.IDExternal+", External root: "+probOidExt.rootExternal+".",EventLogEntryType.Information);
				}
			}
			else {//the segment is for an existing problem, update fields if necessary
				Diseases.Update(probCur,probOld);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated an existing problem for patient "+pat.GetNameFLnoPref()+" due to an incoming PRB segment.",EventLogEntryType.Information);
				}
			}
			return;
		}
Beispiel #20
0
		public static void ProcessPR1(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			string strToothNum="";//could contain the tooth range in comma-delimited list
			string strSurf="";//used for surf/quad/sext/arch
			string strOidExt="";
			string strOidExtRoot="";
			ProcedureCode procCode=null;
			DateTime dateProc=DateTime.MinValue;
			Hashtable hashProcedureCodes=ProcedureCodeC.GetHList();
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int itemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "proccode.ProcCode":
						//ProcCode^descript (ignored)^CD2^^^^2014^^Layman Term (ignored)
						//Example: D1351^^CD2^^^^2014
						//must be CDT code (CD2 is code system abbr according to HL7 documentation) and must be version 2014
						if(seg.GetFieldComponent(itemOrder,2).ToLower()!="cd2" || seg.GetFieldComponent(itemOrder,6)!="2014") {
							EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()
								+".  Only CDT codes from code system version 2014 are currently allowed in the PR1 segment.  The code system name provided was "
								+seg.GetFieldComponent(itemOrder,2)+" and the code system version provided was "+seg.GetFieldComponent(itemOrder,6)+".",EventLogEntryType.Information);
							return;
						}
						string strProcCode=seg.GetFieldComponent(itemOrder,0);
						if(!hashProcedureCodes.ContainsKey(strProcCode)) {//code does not exist in proc code list, write entry in event log an return
							EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()
								+".  The code supplied in the PR1 segment does not exist in the database.  The code provided was "+strProcCode+".",EventLogEntryType.Information);
							return;
						}
						procCode=(ProcedureCode)hashProcedureCodes[strProcCode];
						continue;
					case "proc.procDateTime":
						dateProc=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "proc.toothSurfRange":
						char subcompSeparator='&';
						if(msg.Delimiters.Length>3) {
							subcompSeparator=msg.Delimiters[3];
						}
						string[] listSubComponents=seg.GetFieldComponent(itemOrder).Split(new char[] { subcompSeparator },StringSplitOptions.None);
						if(listSubComponents.Length>0) {
							strToothNum=listSubComponents[0];
						}
						if(listSubComponents.Length>1) {
							strSurf=listSubComponents[1];
						}
						continue;
					case "proc.uniqueId":
						//Id^^UniversalId
						strOidExt=seg.GetFieldComponent(itemOrder,0);
						strOidExtRoot=seg.GetFieldComponent(itemOrder,2);
						if(strOidExt!="" && strOidExtRoot!="" && OIDExternals.GetByRootAndExtension(strOidExtRoot,strOidExt)!=null) {
							//the universal ID and root are already in the oidexternals table, do not insert another procedure, must be a duplicate
							EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()
								+".  The universal ID and root supplied in the PR1 segment refers to an existing procedure."
								+"  Inserting another would result in a duplicate procedure.  The ID provided was "
								+strOidExt+", the root was "+strOidExtRoot+".",EventLogEntryType.Information);
							return;
						}
						continue;
					default:
						continue;
				}
			}
			if(procCode==null) {
				EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()
					+".  No procedure code was defined for a PR1 segment or was missing.",EventLogEntryType.Information);
				return;
			}
			Procedure procCur=new Procedure();
			#region Validate/Convert/Set Treatment Area
			switch(procCode.TreatArea) {
				case TreatmentArea.Arch:
					if(strSurf!="L" && strSurf!="U") {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is arch but the arch of "+strSurf+" is invalid.",EventLogEntryType.Information);
						return;
					}
					procCur.Surf=strSurf;
					break;
				case TreatmentArea.Quad:
					if(strSurf!="UL" && strSurf!="UR" && strSurf!="LL" && strSurf!="LR") {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is quadrant but the quadrant of "+strSurf+" is invalid.",EventLogEntryType.Information);
						return;
					}
					procCur.Surf=strSurf;
					break;
				case TreatmentArea.Sextant:
					bool isValidSextant=false;
					if(strSurf=="1" || strSurf=="2" || strSurf=="3" || strSurf=="4" || strSurf=="5" || strSurf=="6") {
						isValidSextant=true;
					}
					if(CultureInfo.CurrentCulture.Name.EndsWith("CA")) {//Canadian. en-CA or fr-CA
						if(strSurf=="03" || strSurf=="04" || strSurf=="05" || strSurf=="06" || strSurf=="07" || strSurf=="08") {
							isValidSextant=true;
						}
					}
					if(!isValidSextant) {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is sextant but the sextant of "+strSurf+" is invalid.",EventLogEntryType.Information);
						return;
					}
					procCur.Surf=strSurf;
					break;
				case TreatmentArea.Surf:
					if(!Tooth.IsValidEntry(strToothNum)) {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is surface but the tooth number of "+strToothNum+" is invalid.",EventLogEntryType.Information);
					}
					procCur.ToothNum=Tooth.FromInternat(strToothNum);
					string strSurfTidy=Tooth.SurfTidyFromDisplayToDb(strSurf,procCur.ToothNum);
					if(strSurfTidy=="" || strSurf=="") {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is surface but the surface of "+strSurf+" is invalid.",EventLogEntryType.Information);
						return;
					}
					procCur.Surf=strSurfTidy;
					break;
				case TreatmentArea.Tooth:
					if(!Tooth.IsValidEntry(strToothNum)) {
						EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
							+procCode.ProcCode+" is tooth but the tooth number of "+strToothNum+" is invalid.",EventLogEntryType.Information);
						return;
					}
					procCur.ToothNum=Tooth.FromInternat(strToothNum);
					break;
				case TreatmentArea.ToothRange:
					//break up the list of tooth numbers supplied and validate and convert them into universal tooth numbers for inserting into the db
					string[] listToothNums=strToothNum.Split(new char[] { ',' },StringSplitOptions.RemoveEmptyEntries);
					for(int i=0;i<listToothNums.Length;i++) {
						if(!Tooth.IsValidEntry(listToothNums[i])) {
							EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "
								+procCode.ProcCode+" is tooth range but the tooth number of "+listToothNums[i]+" is invalid.",EventLogEntryType.Information);
							return;
						}
						if(Tooth.IsPrimary(Tooth.FromInternat(listToothNums[i]))) {
							EventLog.WriteEntry("OpenDentHL7","A procedure was not added for patient "+pat.GetNameFLnoPref()+".  The treatment area for the code "+procCode.ProcCode
								+" is tooth range but the tooth number of "+listToothNums[i]+" is a primary tooth number and therefore not allowed.",EventLogEntryType.Information);
						}
						listToothNums[i]=Tooth.FromInternat(listToothNums[i]);
					}
					procCur.ToothNum=string.Join(",",listToothNums);
					break;
				//We won't validate or use the tooth number or surface fields if the treatment are of the proccode is mouth or none
				case TreatmentArea.None:
				case TreatmentArea.Mouth:
				default:
					break;
			}
			#endregion Validate/Convert/Set Treatment Area
			Procedures.SetDateFirstVisit(dateProc,1,pat);//wait until after validating, might not insert the proc, don't return after this point
			procCur.PatNum=pat.PatNum;
			procCur.CodeNum=procCode.CodeNum;
			procCur.ProcDate=dateProc;
			procCur.DateTP=dateProc;
			procCur.ProcStatus=ProcStat.TP;
			procCur.ProvNum=pat.PriProv;
			if(procCode.ProvNumDefault!=0) {
				procCur.ProvNum=procCode.ProvNumDefault;
			}
			else if(procCode.IsHygiene && pat.SecProv!=0) {
				procCur.ProvNum=pat.SecProv;
			}
			procCur.Note="";
			procCur.ClinicNum=pat.ClinicNum;
			procCur.BaseUnits=procCode.BaseUnits;
			procCur.SiteNum=pat.SiteNum;
			procCur.RevCode=procCode.RevenueCodeDefault;
			procCur.DiagnosticCode=PrefC.GetString(PrefName.ICD9DefaultForNewProcs);
			List<PatPlan> listPatPlan=PatPlans.Refresh(pat.PatNum);
			List<Benefit> listBen=Benefits.Refresh(listPatPlan,new List<InsSub>());
			#region Set ProcFee From Fee Schedule
			//check if it's a medical procedure
			bool isMed=false;
			procCur.MedicalCode=procCode.MedicalCode;
			if(procCur.MedicalCode!=null && procCur.MedicalCode!="") {
				isMed=true;
			}
			//Get fee schedule for medical or dental
			long feeSch;
			if(isMed) {
				feeSch=Fees.GetMedFeeSched(pat,new List<InsPlan>(),listPatPlan,new List<InsSub>());
			}
			else {
				feeSch=Fees.GetFeeSched(pat,new List<InsPlan>(),listPatPlan,new List<InsSub>());
			}
			//Get the fee amount for medical or dental
			double insfee;
			if(PrefC.GetBool(PrefName.MedicalFeeUsedForNewProcs) && isMed) {
				insfee=Fees.GetAmount0(ProcedureCodes.GetCodeNum(procCur.MedicalCode),feeSch);
			}
			else {
				insfee=Fees.GetAmount0(procCode.CodeNum,feeSch);
			}
			InsPlan priplan=null;
			if(listPatPlan.Count>0) {
				priplan=InsPlans.GetPlan(InsSubs.GetSub(listPatPlan[0].InsSubNum,new List<InsSub>()).PlanNum,new List<InsPlan>());
			}
			if(priplan!=null && priplan.PlanType=="p" && !isMed) {//PPO
				Provider patProv=Providers.GetProv(pat.PriProv);
				if(patProv==null) {
					patProv=Providers.GetProv(PrefC.GetLong(PrefName.PracticeDefaultProv));
				}
				double standardFee=Fees.GetAmount0(procCode.CodeNum,patProv.FeeSched);
				if(standardFee>insfee) {
					procCur.ProcFee=standardFee;
				}
				else {
					procCur.ProcFee=insfee;
				}
			}
			else {
				procCur.ProcFee=insfee;
			}
			#endregion Set ProcFee From Fee Schedule
			procCur.ProcNum=Procedures.Insert(procCur);
			if(_isVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Inserted a new procedure for patient "+pat.GetNameFLnoPref()+" due to an incoming PR1 segment.",EventLogEntryType.Information);
			}
			if(strOidExt!="" && strOidExtRoot!="") {
				OIDExternal procOidExt=new OIDExternal();
				procOidExt.IDType=IdentifierType.Procedure;
				procOidExt.IDInternal=procCur.ProcNum;
				procOidExt.IDExternal=strOidExt;
				procOidExt.rootExternal=strOidExtRoot;
				OIDExternals.Insert(procOidExt);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Added an external procedure ID to the oidexternals table due to an incoming PR1 segment.\r\nProcNum: "
						+procCur.ProcNum.ToString()+", External problem ID: "+procOidExt.IDExternal+", External root: "+procOidExt.rootExternal+".",EventLogEntryType.Information);
				}
			}
			Procedures.ComputeEstimates(procCur,pat.PatNum,new List<ClaimProc>(),true,new List<InsPlan>(),listPatPlan,listBen,pat.Age,new List<InsSub>());
			return;
		}
Beispiel #21
0
		//public static void ProcessPD1() {
		//	return;
		//}

		public static void ProcessPID(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			Patient patOld=pat.Copy();
			char escapeChar='\\';
			if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \
				escapeChar=msg.Delimiters[2];
			}
			char subcompChar='&';
			if(msg.Delimiters.Length>3) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default &
				subcompChar=msg.Delimiters[3];
			}
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int itemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "pat.addressCityStateZip":
						pat.Address=seg.GetFieldComponent(itemOrder,0);
						pat.Address2=seg.GetFieldComponent(itemOrder,1);
						pat.City=seg.GetFieldComponent(itemOrder,2);
						pat.State=seg.GetFieldComponent(itemOrder,3);
						pat.Zip=seg.GetFieldComponent(itemOrder,4);
						string strAddrNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(itemOrder,19),escapeChar);
						if(strAddrNote!="") {
							pat.AddrNote=strAddrNote;
						}						
						continue;
					case "pat.birthdateTime":
						pat.Birthdate=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "pat.ChartNumber":
						pat.ChartNumber=seg.GetFieldComponent(itemOrder);
						continue;
					case "pat.FeeSched":
						if(Programs.IsEnabled(ProgramName.eClinicalWorks) && ProgramProperties.GetPropVal(ProgramName.eClinicalWorks,"FeeSchedulesSetManually")=="1") {
							//if using eCW and FeeSchedulesSetManually
							continue;//do not process fee sched field, manually set by user
						}
						else {
							pat.FeeSched=FieldParser.FeeScheduleParse(seg.GetFieldComponent(itemOrder));
						}
						continue;
					case "pat.Gender":
						pat.Gender=FieldParser.GenderParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "pat.HmPhone":
						if(seg.GetFieldComponent(itemOrder,2)=="" && seg.GetField(itemOrder).ListRepeatFields.Count==0) {//must be eCW, process the old way
							//either no component 2 or left blank, and there are no repetitions, process the old way
							//the first repetition is considered the primary number, but if the primary number is not sent then it will be blank followed by the list of other numbers
							pat.HmPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder));
						}
						else {
							//XTN data type, repeatable.
							//Component 2 values: PH-Phone, FX-Fax, MD-Modem, CP-Cell Phone, Internet-Internet Address, X.400-X.400 email address, TDD-Tel Device for the Deaf, TTY-Teletypewriter.
							//We will support PH for pat.HmPhone, CP for pat.WirelessPhone, and Internet for pat.Email
							//Component 5 is area code, 6 is number
							//Component 3 will be Email if the type is Internet
							//Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]
							FieldHL7 phField=seg.GetField(itemOrder);
							if(phField==null) {
								continue;
							}
							string strPhType=seg.GetFieldComponent(itemOrder,2);
							string strPh=seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6);
							string strEmail=seg.GetFieldComponent(itemOrder,3);
							for(int p=-1;p<phField.ListRepeatFields.Count;p++) {
								if(p>=0) {
									strPhType=phField.ListRepeatFields[p].GetComponentVal(2);
									strPh=phField.ListRepeatFields[p].GetComponentVal(5)+phField.ListRepeatFields[p].GetComponentVal(6);
									strEmail=phField.ListRepeatFields[p].GetComponentVal(3);
								}
								switch(strPhType) {
									case "PH":
										pat.HmPhone=FieldParser.PhoneParse(strPh);
										continue;
									case "CP":
										pat.WirelessPhone=FieldParser.PhoneParse(strPh);
										continue;
									case "Internet":
										pat.Email=strEmail;
										continue;
									default:
										continue;
								}
							}
						}
						continue;
					case "pat.nameLFM":
						pat.LName=seg.GetFieldComponent(itemOrder,0);
						pat.FName=seg.GetFieldComponent(itemOrder,1);
						pat.MiddleI=seg.GetFieldComponent(itemOrder,2);
						pat.Title=seg.GetFieldComponent(itemOrder,4);
						continue;
					case "pat.PatNum":
						//pat.PatNum guaranteed to not be 0, a new patient will be inserted if the field component for PatNum is not an int, is 0, or is blank
						//if(pat.PatNum!=0 && pat.PatNum!=PIn.Long(seg.GetFieldComponent(intItemOrder))) {
						//if field component is a valid number and the patient located is not the same as the patient with the PatNum in the segment, then throw the exception, message will fail.
						long patNumFromPid=0;
						try {
							patNumFromPid=PIn.Long(seg.GetFieldComponent(itemOrder));
						}
						catch(Exception ex) {
							//do nothing, patNumFromPID will remain 0
						}
						if(patNumFromPid!=0 && pat.PatNum!=patNumFromPid) {
							throw new Exception("Invalid PatNum");
						}
						continue;
					case "pat.Position":
						pat.Position=FieldParser.MaritalStatusParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "pat.Race":
						//PID.10 is a CWE data type, with 9 defined components.  The first three are CodeValue^CodeDescription^CodeSystem
						//This field can repeat, so we will need to add an entry in the patientrace table for each repetition
						//The race code system is going to be CDCREC and the values match our enum.
						//The code description is not saved, we only require CodeValue and CodeSystem=CDCREC.  Descriptions come from the CDCREC table for display in OD.
						//Example race component: 
						string strRaceCode=seg.GetFieldComponent(itemOrder,0);
						string strRaceCodeSystem=seg.GetFieldComponent(itemOrder,2);
						//if there aren't 3 components to the race field or if the code system is not CDCREC, then parse the old way by matching to string name, i.e. white=PatientRaceOld.White
						if(strRaceCodeSystem!="CDCREC") {//must be eCW, process the old way
							PatientRaceOld patientRaceOld=FieldParser.RaceParseOld(strRaceCode);
							//Converts deprecated PatientRaceOld enum to list of PatRaces, and adds them to the PatientRaces table for the patient.
							PatientRaces.Reconcile(pat.PatNum,PatientRaces.GetPatRacesFromPatientRaceOld(patientRaceOld));
						}
						else {
							FieldHL7 fieldRace=seg.GetField(itemOrder);
							if(fieldRace==null) {
								continue;
							}
							List<PatRace> listPatRaces=new List<PatRace>();
							listPatRaces.Add(FieldParser.RaceParse(strRaceCode));
							for(int r=0;r<fieldRace.ListRepeatFields.Count;r++) {
								strRaceCode=fieldRace.ListRepeatFields[r].GetComponentVal(0);
								strRaceCodeSystem=fieldRace.ListRepeatFields[r].GetComponentVal(2);
								if(strRaceCodeSystem=="CDCREC") {
									listPatRaces.Add(FieldParser.RaceParse(strRaceCode));
								}
							}
							PatientRaces.Reconcile(pat.PatNum,listPatRaces);
						}
						continue;
					case "pat.SSN":
						pat.SSN=seg.GetFieldComponent(itemOrder);
						continue;
					case "pat.WkPhone":
						if(seg.GetFieldComponent(itemOrder,2)=="PH") {
							//Component 2 value: PH-Phone
							//Component 5 is area code, 6 is number
							//Example: ^WPN^PH^^^503^3635432
							pat.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6));
							continue;
						}
						//either no component 2 or left blank, process the old way
						pat.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder));//must be eCW
						continue;
					case "patientIds"://Not used by eCW
						//We will store the ID's from other software in the oidexternal table.  These ID's will be sent in outbound msgs and used to locate a patient for subsequent inbound msgs.
						//Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI|
						//field component values will be the first repetition, repeats will be in field.ListRepeatFields
						//1234=PatNum, 3=check digit, M11=using check digit scheme, the assigning authority universal ID is the offices oidinternal.IDRoot for IDType Patient,
						//HL7=ID type, PI=identifier type code for: Patient internal identifier (a number that is unique to a patient within an assigning authority).
						FieldHL7 fieldCur=seg.GetField(itemOrder);
						//get the id with the assigning authority equal to the internal OID root stored in their database for a patient object
						string strPatOIDRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot;
						if(strPatOIDRoot=="") {
							//if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum
							EventLog.WriteEntry("OpenDentHL7","Could not process the patientIdList field due to the internal OID for the patient object missing.",EventLogEntryType.Information);
							continue;
						}
						string strPatIdCur="";
						string strPatIdRootCur="";
						string[] arrayPatIdSubComps=fieldCur.GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None);
						if(arrayPatIdSubComps.Length>1 //so the next line doesn't throw an exception
							&& arrayPatIdSubComps[1].ToLower()!=strPatOIDRoot.ToLower()//not our PatNum
							&& fieldCur.GetComponentVal(4).ToLower()=="pi")//PI=patient internal identifier; a number that is unique to a patient within an assigning authority
						{
							int intCheckDigit=-1;
							try {
								intCheckDigit=PIn.Int(fieldCur.GetComponentVal(1));
							}
							catch(Exception ex) {
								//checkDigit will remain -1
							}
							//if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm, then use the ID
							if((fieldCur.GetComponentVal(2).ToLower()=="m10"
									&& intCheckDigit!=-1
									&& M10CheckDigit(fieldCur.GetComponentVal(0))==intCheckDigit)//using M10 scheme and the check digit is valid and matches calc
								|| (fieldCur.GetComponentVal(2).ToLower()=="m11"
									&& intCheckDigit!=-1
									&& M11CheckDigit(fieldCur.GetComponentVal(0))==intCheckDigit)//using M11 scheme and the check digit is valid and matches calc
								|| (fieldCur.GetComponentVal(2).ToLower()!="m10"
									&& fieldCur.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme
							{
								//Either passed the check digit test or not using the M10 or M11 check digit scheme, so trust the ID to be valid
								strPatIdCur=fieldCur.GetComponentVal(0);
								strPatIdRootCur=arrayPatIdSubComps[1];
							}
						}
						OIDExternal oidExtCur=new OIDExternal();
						string verboseMsg="";
						if(strPatIdCur!="" && strPatIdRootCur!="" && OIDExternals.GetByRootAndExtension(strPatIdRootCur,strPatIdCur)==null) {
							oidExtCur.IDType=IdentifierType.Patient;
							oidExtCur.IDInternal=pat.PatNum;
							oidExtCur.IDExternal=strPatIdCur;
							oidExtCur.rootExternal=strPatIdRootCur;
							OIDExternals.Insert(oidExtCur);
							verboseMsg+="\r\nExternal patient ID: "+oidExtCur.IDExternal+", External root: "+oidExtCur.rootExternal;
						}
						for(int r=0;r<fieldCur.ListRepeatFields.Count;r++) {
							arrayPatIdSubComps=fieldCur.ListRepeatFields[r].GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None);
							if(arrayPatIdSubComps.Length<2) {//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is
								continue;
							}
							if(arrayPatIdSubComps[1].ToLower()==strPatOIDRoot.ToLower() //if this is the OD patient OID root
								|| fieldCur.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi")//or not a patient identifier, then skip this repetition
							{
								continue;
							}
							int intCheckDigit=-1;
							try {
								intCheckDigit=PIn.Int(fieldCur.ListRepeatFields[r].GetComponentVal(1));
							}
							catch(Exception ex) {
								//checkDigit will remain -1
							}
							if(fieldCur.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10"
								&& (intCheckDigit==-1 || M10CheckDigit(fieldCur.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either an invalid check digit was received or it doesn't match our calc
							{
								continue;
							}
							if(fieldCur.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11"
								&& (intCheckDigit==-1 || M11CheckDigit(fieldCur.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either an invalid check digit was received or it doesn't match our calc
							{
								continue;
							}
							//if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and store as IDExternal
							strPatIdCur=fieldCur.ListRepeatFields[r].GetComponentVal(0);
							strPatIdRootCur=arrayPatIdSubComps[1];
							if(strPatIdCur!="" && strPatIdRootCur!="" && OIDExternals.GetByRootAndExtension(strPatIdRootCur,strPatIdCur)==null) {
								oidExtCur=new OIDExternal();
								oidExtCur.IDType=IdentifierType.Patient;
								oidExtCur.IDInternal=pat.PatNum;
								oidExtCur.IDExternal=strPatIdCur;
								oidExtCur.rootExternal=strPatIdRootCur;
								OIDExternals.Insert(oidExtCur);
								verboseMsg+="\r\nExternal patient ID: "+oidExtCur.IDExternal+", External root: "+oidExtCur.rootExternal;
							}
						}
						if(_isVerboseLogging && verboseMsg.Length>0) {
							EventLog.WriteEntry("OpenDentHL7","Added the following external patient ID(s) to the oidexternals table due to an incoming PID segment for PatNum: "
								+pat.PatNum+"."+verboseMsg+".",EventLogEntryType.Information);
						}
						continue;
					default:
						continue;
				}
			}
			Patients.Update(pat,patOld);
			if(_isVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref()+" due to an incoming PID segment.",EventLogEntryType.Information);
			}
			return;
		}
Beispiel #22
0
		///<summary>So far this is only used in SRM messages and saves data to the appointment note field.  If apt is null this does nothing.  The note in the NTE segment will be appended to the existing appointment note unless the existing note already contains the exact note we are attempting to append.</summary>
		public static void ProcessNTE(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			char escapeChar='\\';
			if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \
				escapeChar=msg.Delimiters[2];
			}
			if(apt==null) {
				return;
			}
			if(apt.PatNum!=pat.PatNum) {
				throw new Exception("Appointment does not match patient "+pat.GetNameFLnoPref()+", apt.PatNum: "+apt.PatNum.ToString()+", pat.PatNum: "+pat.PatNum.ToString());
			}
			string strAptNote="";
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int intItemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "apt.Note":
						strAptNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(intItemOrder),escapeChar);
						continue;
					default:
						continue;
				}
			}
			//if the existing note contains the exact text of the note in the NTE segment, don't append it again
			//replace the \r's and then \n's with a blank string before comparing to eliminate inconsistencies in new line characters
			if(apt.Note==null) {//just in case a new appointment gets here and has not been inserted yet, should never happen
				apt.Note="";
			}
			if(apt.Note.Replace("\r\n","\n").Replace("\r","\n").Contains(strAptNote.Replace("\r\n","\n").Replace("\r","\n"))) {
				return;
			}
			Appointment aptOld=apt.Clone();
			if(apt.Note.Length>0) {//if the existing note already contains some text, add a new line before appending the note from the NTE segment
				apt.Note+="\r\n";
			}
			apt.Note+=strAptNote;
			Appointments.Update(apt,aptOld);
			_aptProcessed=apt;
			if(_isVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Updated appointment for patient "+pat.GetNameFLnoPref()+" due to an incoming NTE segment.",EventLogEntryType.Information);
			}
			return;
		}
Beispiel #23
0
		public static void ProcessMSH(HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			int msgControlIdOrder=0;
			//find position of messageControlId in segDef for MSH seg
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				if(segDef.hl7DefFields[i].FieldName=="messageControlId") {
					msgControlIdOrder=segDef.hl7DefFields[i].OrdinalPos;
					continue;
				}
			}
			if(msgControlIdOrder==0) {
				return;
			}
			msg.ControlId=seg.GetFieldComponent(msgControlIdOrder).ToString();
		}
		//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);
		}
        ///<summary>Returns null if there is no HL7Def enabled or if there is no outbound DFT defined for the enabled HL7Def.</summary>
        public static MessageHL7 GenerateDFT(List <Procedure> listProcs, EventTypeHL7 eventType, Patient pat, Patient guar, long aptNum, string pdfDescription, string pdfDataString)
        {
            //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.
            MessageHL7 msgHl7 = 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 && hl7Def.hl7DefMessages[i].InOrOut == InOutHL7.Outgoing)
                {
                    hl7DefMessage = hl7Def.hl7DefMessages[i];
                    //continue;
                    break;
                }
            }
            if (hl7DefMessage == null)           //DFT message type is not defined so do nothing and return
            {
                return(null);
            }
            if (PrefC.GetBool(PrefName.ShowFeaturePatientClone))
            {
                pat = Patients.GetOriginalPatientForClone(pat);
            }
            Provider       prov         = Providers.GetProv(Patients.GetProvNum(pat));
            Appointment    apt          = Appointments.GetOneApt(aptNum);
            List <PatPlan> listPatPlans = PatPlans.Refresh(pat.PatNum);

            for (int i = 0; i < hl7DefMessage.hl7DefSegments.Count; i++)
            {
                int repeatCount = 1;
                if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.FT1)
                {
                    repeatCount = listProcs.Count;
                }
                else if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)
                {
                    repeatCount = listPatPlans.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.
                //or the patient does not have any current insplans for IN1 segments
                for (int j = 0; j < repeatCount; j++)           //FT1 is optional and can repeat so add as many FT1's as procs in procList, IN1 is optional and can repeat as well, repeat for the number of patplans in patplanList
                {
                    if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.FT1 && listProcs.Count > j)
                    {
                        prov = Providers.GetProv(listProcs[j].ProvNum);
                    }
                    Procedure proc = null;
                    if (listProcs.Count > j)                   //procList could be an empty list
                    {
                        proc = listProcs[j];
                    }
                    PatPlan patPlanCur = null;
                    InsPlan insPlanCur = null;
                    InsSub  insSubCur  = null;
                    Carrier carrierCur = null;
                    Patient subscriber = null;
                    if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)
                    {
                        patPlanCur = listPatPlans[j];
                        insSubCur  = InsSubs.GetOne(patPlanCur.InsSubNum);
                        insPlanCur = InsPlans.RefreshOne(insSubCur.PlanNum);
                        carrierCur = Carriers.GetCarrier(insPlanCur.CarrierNum);
                        subscriber = Patients.GetPat(insSubCur.Subscriber);
                    }
                    SegmentHL7 seg = new SegmentHL7(hl7DefMessage.hl7DefSegments[i].SegmentName);
                    seg.SetField(0, hl7DefMessage.hl7DefSegments[i].SegmentName.ToString());
                    for (int f = 0; f < hl7DefMessage.hl7DefSegments[i].hl7DefFields.Count; f++)
                    {
                        string fieldName = hl7DefMessage.hl7DefSegments[i].hl7DefFields[f].FieldName;
                        if (fieldName == "")                       //If fixed text instead of field name just add text to segment
                        {
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[f].OrdinalPos, hl7DefMessage.hl7DefSegments[i].hl7DefFields[f].FixedText);
                        }
                        else
                        {
                            string fieldValue = "";
                            if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)
                            {
                                fieldValue = FieldConstructor.GenerateFieldIN1(hl7Def, fieldName, j + 1, patPlanCur, insSubCur, insPlanCur, carrierCur, listPatPlans.Count, subscriber);
                            }
                            else
                            {
                                fieldValue = FieldConstructor.GenerateField(hl7Def, fieldName, MessageTypeHL7.DFT, pat, prov, proc, guar, apt, j + 1, eventType,
                                                                            pdfDescription, pdfDataString, MessageStructureHL7.DFT_P03, seg.Name);
                            }
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[f].OrdinalPos, fieldValue);
                        }
                    }
                    msgHl7.Segments.Add(seg);
                }
            }
            return(msgHl7);
        }
Beispiel #26
0
		///<summary>Appointment request segment.  Included in inbound SRM, Schedule Request Messages.  When OD is the filler application, this will identify the appointment that the placer or auxiliary aplication is trying to update.  We only support event S03 - Appt Modification requests or event S04 - Appt Cancellation requests for now.</summary>
		public static void ProcessARQ(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			long aptNum=0;
			string externAptId="";
			string externRoot="";
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int intItemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "apt.externalAptID":
						externAptId=seg.GetFieldComponent(intItemOrder,0);
						externRoot=seg.GetFieldComponent(intItemOrder,2);
						continue;
					case "apt.AptNum":
						try {
							aptNum=PIn.Long(seg.GetFieldComponent(intItemOrder,0));
						}
						catch(Exception ex) {
							//do nothing, aptNum will remain 0
						}
						if(apt!=null && apt.AptNum!=aptNum) {
							//an appointment was located from the inbound message, but the AptNum on the appointment is not the same as the AptNum in this ARQ segment (should never happen)
							throw new Exception("Invalid appointment number.");
						}
						continue;
					default:
						continue;
				}
			}
			if(externAptId!="" && externRoot!="" && OIDExternals.GetByRootAndExtension(externRoot,externAptId)==null) {
				OIDExternal oidExtCur=new OIDExternal();
				oidExtCur.IDType=IdentifierType.Appointment;
				oidExtCur.IDInternal=apt.AptNum;
				oidExtCur.IDExternal=externAptId;
				oidExtCur.rootExternal=externRoot;
				OIDExternals.Insert(oidExtCur);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Added an external appointment ID to the oidexternals table due to an incoming ARQ segment.\r\n"
						+"AptNum: "+apt.AptNum.ToString()+", External AptID: "+externAptId+", External root: "+externRoot+".",EventLogEntryType.Information);
				}
			}
			_hl7MsgCur.AptNum=apt.AptNum;
			HL7Msgs.Update(_hl7MsgCur);
			if(_isVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Updated hl7msg to include AptNum for "+pat.GetNameFLnoPref()+" due to an incoming SCH segment.",EventLogEntryType.Information);
			}
			_aptProcessed=apt;
			return;
		}
Beispiel #27
0
		public static void ProcessAck(MessageHL7 msg,bool isVerboseLogging) {
			_isVerboseLogging=isVerboseLogging;
			HL7Def hl7Def=HL7Defs.GetOneDeepEnabled();
			if(hl7Def==null) {
				throw new Exception("Could not process ACK.  No HL7 definition is enabled.");
			}
			HL7DefMessage hl7DefMsg=null;
			for(int i=0;i<hl7Def.hl7DefMessages.Count;i++) {
				if(hl7Def.hl7DefMessages[i].MessageType==MessageTypeHL7.ACK && hl7Def.hl7DefMessages[i].InOrOut==InOutHL7.Incoming) {
					hl7DefMsg=hl7Def.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,null,hl7DefMsg.hl7DefSegments[i],seg,msg);
					}
				}
				catch(ApplicationException ex) {//Required segment was missing, or other error.
					throw new Exception("Could not process HL7 message.  "+ex);
				}
			}
		}
Beispiel #28
0
		///<summary>Test 12: EcwOldTight,EcwOldFull,EcwTight,HL7DefEcwFull: Create message with multiple providers.  Change the provider for the D2332 procedure on the appointment with AptNum=500 to DOC2 from Test 10.  Create DFT message again for this patient, provider, appointment, and procedures and verify that the FT1 segment for that procedure lists the correct provider.  EcwOldStandalone,HL7DefEcwStandalone: DFT messages are not created in Standalone mode.</summary>
		public static string Test12(HL7TestInterfaceEnum hl7TestInterfaceEnum) {
			if(hl7TestInterfaceEnum==HL7TestInterfaceEnum.EcwOldStandalone 
				|| hl7TestInterfaceEnum==HL7TestInterfaceEnum.HL7DefEcwStandalone) 
			{
				return "Test 12: Passed.\r\n";
			}
			long aptNum=500;
			Procedure proc=new Procedure();
			List<Procedure> procList=Procedures.GetProcsForSingle(aptNum,false);
			if(procList==null) {
				return "Test 12: Couldn't locate procedures for appointment.\r\n";
			}
			for(int i=0;i<procList.Count;i++) {
				if(procList[i].CodeNum==ProcedureCodes.GetCodeNum("D2332")) {
					proc=procList[i];
					break;
				}
			}
			if(proc==null) {
				return "Test 12: Couldn't locate procedure D2332.\r\n";
			}
			Procedure oldProc=proc.Copy();
			Provider prov2=Providers.GetProvByEcwID("DOC2");
			if(prov2==null) {
				return "Test 12: Couldn't locate DOC2 provider.\r\n";
			}
			proc.ProvNum=prov2.ProvNum;
			Procedures.Update(proc,oldProc);
			Patient pat=Patients.GetPat(10);
			Patient guar=Patients.GetPat(11);
			if(pat==null) {
				return "Test 12: Couldn't locate patient.\r\n";
			}
			if(guar==null) {
				return "Test 12: Couldn't locate guarantor.\r\n";
			}
			Provider prov=Providers.GetProvByEcwID("DOC1");
			if(prov==null) {
				return "Test 12: Couldn't locate DOC1 provider.\r\n";
			}
			long provNum=prov.ProvNum;
			MessageHL7 msg=null;
			try {
				switch(hl7TestInterfaceEnum) {
					//EcwOldStandalone and HL7DefEcwStandalone were handled higher up
					case HL7TestInterfaceEnum.EcwOldFull:
					case HL7TestInterfaceEnum.EcwOldTight:
						OpenDentBusiness.HL7.EcwDFT dft=new OpenDentBusiness.HL7.EcwDFT();
						dft.InitializeEcw(aptNum,provNum,pat,"Test Message","treatment",false);
						msg=new MessageHL7(dft.GenerateMessage());
						break;
					case HL7TestInterfaceEnum.HL7DefEcwFull:
					case HL7TestInterfaceEnum.HL7DefEcwTight:
						msg=new MessageHL7(OpenDentBusiness.HL7.MessageConstructor.GenerateDFT(procList,EventTypeHL7.P03,pat,guar,aptNum,"treatment","Test Message").ToString());
						//msg will be null if there's not DFT defined for the def.  Should handle results for those defs higher up
						break;
					default:
						return "Test 12: interface not found.";
				}
			}
			catch(Exception ex) {
				return "Test 12: Message creation error. "+ex+".\r\n";
			}
			string provField="";
			string provField2="";
			switch(hl7TestInterfaceEnum) {
				case HL7TestInterfaceEnum.EcwOldFull:
				case HL7TestInterfaceEnum.EcwOldTight:
					provField="DOC1^Albert, Brian S^^";
					provField2="DOC2^Lexington^Sarah^J";
					break;
				default:
					provField="DOC1^Albert^Brian^S";
					provField2="DOC2^Lexington^Sarah^J";
					break;
			}
			string msgtext=@"MSH|^~\&|OD||ECW||"+msg.Segments[0].GetFieldFullText(6)+"||DFT^P03||P|2.3\r\n"
			  +"EVN|P03|"+msg.Segments[1].GetFieldFullText(2)+"|\r\n"
			  +"PID|1|A11|10||Smith^Jane^N||19760205|F||White|421 Main St^Apt 17^Dallas^OR^97338||5035554045|5035554234||Married|||111224444|||\r\n"
			  +"PV1|||||||"+provField+"||||||||||||500|||||||||||||||||||||||||||||||\r\n"
			  +"FT1|1|||20120906000000|20120906000000|CG||||1.0||||||||||"+provField+"|"+provField+"|75.00|||D0150|^\r\n"
			  +"FT1|2|||20120906000000|20120906000000|CG||||1.0||||||||||"+provField+"|"+provField+"|20.00|||D0230|^\r\n"
			  +"FT1|3|||20120906000000|20120906000000|CG||||1.0||||||||||"+provField+"|"+provField+"|20.00|||D0230|^\r\n"
			  +"FT1|4|||20120906000000|20120906000000|CG||||1.0||||||||||"+provField2+"|"+provField2+"|150.00|||D2332|26^MID\r\n"
			  +"ZX1|6|PDF|PATHOLOGY^Pathology Report^L|treatment|Test Message";
			MessageHL7 correctMsg=new MessageHL7(msgtext);
			string retval=CompareMsgs(msg,correctMsg);
			if(retval.Length>0) {
				return "Test 12: "+retval;
			}
			return "Test 12: Passed.\r\n";
		}
        ///<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);
        }
Beispiel #30
0
        public static void ProcessSeg(Patient pat, long aptNum, HL7DefSegment segDef, SegmentHL7 seg, MessageHL7 msg)
        {
            switch (segDef.SegmentName)
            {
            case SegmentNameHL7.AIG:
                ProcessAIG(pat, aptNum, segDef, seg);
                return;

            case SegmentNameHL7.GT1:
                ProcessGT1(pat, segDef, seg);
                return;

            case SegmentNameHL7.IN1:
                //ProcessIN1();
                return;

            case SegmentNameHL7.MSA:
                ProcessMSA(segDef, seg, msg);
                return;

            case SegmentNameHL7.MSH:
                ProcessMSH(segDef, seg, msg);
                return;

            case SegmentNameHL7.PD1:
                //ProcessPD1();
                return;

            case SegmentNameHL7.PID:
                ProcessPID(pat, segDef, seg);
                return;

            case SegmentNameHL7.PV1:
                ProcessPV1(pat, aptNum, segDef, seg);
                return;

            case SegmentNameHL7.SCH:
                ProcessSCH(pat, segDef, seg);
                return;

            default:
                return;
            }
        }
Beispiel #31
0
		private static Appointment _aptProcessed;//return value for ProcessSeg if an appointment was processed
		//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> listHL7Existing=HL7Msgs.GetOneExisting(_hl7MsgCur);
			if(listHL7Existing.Count>0) {//This message is already in the db
				_hl7MsgCur.HL7MsgNum=listHL7Existing[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.");
			}
			if(def.InternalType==HL7InternalType.eCWFull
				|| def.InternalType==HL7InternalType.eCWTight
				|| def.InternalType==HL7InternalType.eCWStandalone)
			{
				_isEcwHL7Def=true;
			}
			HL7DefMessage hl7defmsg=null;
			for(int i=0;i<def.hl7DefMessages.Count;i++) {
				if(def.hl7DefMessages[i].MessageType==msg.MsgType && def.hl7DefMessages[i].InOrOut==InOutHL7.Incoming) {//Ignoring event type for now, we will treat all ADT's and SIU's the same
					hl7defmsg=def.hl7DefMessages[i];
					break;
				}
			}
			if(hl7defmsg==null) {//No message definition matches this message's MessageType and is Incoming
				_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;
			long patNumFromIds=0;
			//if we cannot locate the patient by the supplied patnum, either from the PatNum field or the list of IDs in PID.3, then we will use the external IDs to attempt to locate the pat
			//we will only trust these external IDs to return the correct patient if they all refer to a single patient
			//we may want to do some other checking to ensure that the patient referred to is the right patient?
			List<OIDExternal> listOids=new List<OIDExternal>();
			DateTime birthdate=DateTime.MinValue;
			string patLName=null;
			string patFName=null;
			#region GetPatientIDs
			#region PID segmentsLoop
			//Identify the location of the PID segment based on the message definition
			//Get patient in question, incoming messages must have a PID segment so use that to find the pat in question
			int pidOrder=-1;
			int pidDefOrder=-1;
			//get the def's PID segment order and the defined intItemOrder of the PID segment in the message
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				if(hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.PID) {
					continue;
				}
				pidDefOrder=i;
				pidOrder=hl7defmsg.hl7DefSegments[i].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
				{
					_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.");
				}
				//if we get here, we've located the PID segment location within the message based on the def and it exists in the message
				break;
			}
			#endregion PID segmentsLoop
			#region PID fieldsLoop
			//Using the identified location of the PID segment, loop through the fields and find each identifier defined
			for(int f=0;f<hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields.Count;f++) {//Go through fields of PID segment and get patnum, chartnum, patient name, and/or birthdate to locate patient
				if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.ChartNumber") {
					int chartNumOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					chartNum=msg.Segments[pidOrder].GetField(chartNumOrdinal).ToString();
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.PatNum") {
					int patNumOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					try {
						patNum=PIn.Long(msg.Segments[pidOrder].GetField(patNumOrdinal).ToString());
					}
					catch(Exception ex) {
						//do nothing, patNum will remain 0
					}
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.birthdateTime") {
					int patBdayOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					birthdate=FieldParser.DateTimeParse(msg.Segments[pidOrder].GetField(patBdayOrdinal).ToString());
				}
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="pat.nameLFM") {
					int patNameOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					patLName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,0);
					patFName=msg.Segments[pidOrder].GetFieldComponent(patNameOrdinal,1);
				}
				#region patientIdList
				else if(hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].FieldName=="patientIds") {
					//get the id with the assigning authority equal to the internal OID root stored in their database for a patient object
					string patOIDRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot;
					if(patOIDRoot=="") {
						//if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum
						continue;
					}
					int patIdListOrdinal=hl7defmsg.hl7DefSegments[pidDefOrder].hl7DefFields[f].OrdinalPos;
					FieldHL7 fieldPatIds=msg.Segments[pidOrder].GetField(patIdListOrdinal);
					//Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^MR|
					//field component values will be the first repetition, repeats will be in field.ListRepeatFields
					//field 4 is an HD data type, which is composed of 3 subcomponents separated by the "&".
					//subcomponent 1 is the universal ID for the assigning authority
					//subcomponent 2 is the universal ID type for the assigning authority
					char subcompSeparator='&';
					if(msg.Delimiters.Length>3) {
						subcompSeparator=msg.Delimiters[3];
					}
					string[] arrayPatIdSubComps=fieldPatIds.GetComponentVal(3).Split(new char[] { subcompSeparator },StringSplitOptions.None);
					if(arrayPatIdSubComps.Length>1//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is
						&& fieldPatIds.GetComponentVal(4).ToLower()=="pi")//PI=patient internal identifier; a number that is unique to a patient within an assigning authority
					{
						int checkDigit=-1;
						try {
							checkDigit=PIn.Int(fieldPatIds.GetComponentVal(1));
						}
						catch(Exception ex) {
							//checkDigit will remain -1
						}
						//if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm, then use the ID
						if((fieldPatIds.GetComponentVal(2).ToLower()=="m10"
								&& checkDigit!=-1
								&& M10CheckDigit(fieldPatIds.GetComponentVal(0))==checkDigit)//using M10 scheme and the check digit is valid and matches calc
							|| (fieldPatIds.GetComponentVal(2).ToLower()=="m11"
								&& checkDigit!=-1
								&& M11CheckDigit(fieldPatIds.GetComponentVal(0))==checkDigit)//using M11 scheme and the check digit is valid and matches calc
							|| (fieldPatIds.GetComponentVal(2).ToLower()!="m10"
								&& fieldPatIds.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme
						{
							if(arrayPatIdSubComps[1].ToLower()==patOIDRoot.ToLower()) {
								try {
									patNumFromIds=PIn.Long(fieldPatIds.GetComponentVal(0));
								}
								catch(Exception ex) {
									//do nothing, patNumFromList will remain 0
								}
							}
							else {
								OIDExternal oidCur=new OIDExternal();
								oidCur.IDType=IdentifierType.Patient;
								oidCur.IDExternal=fieldPatIds.GetComponentVal(0);
								oidCur.rootExternal=arrayPatIdSubComps[1];
								listOids.Add(oidCur);
							}
						}
					}
					//patNumFromList will be 0 if the first repetition is not the OD patient id or if the check digit is incorrect based on the specified algorithm
					if(patNumFromIds!=0) {
						continue;
					}
					for(int r=0;r<fieldPatIds.ListRepeatFields.Count;r++) {
						arrayPatIdSubComps=fieldPatIds.ListRepeatFields[r].GetComponentVal(3).ToLower().Split(new char[] { subcompSeparator },StringSplitOptions.None);
						if(arrayPatIdSubComps.Length<2) {//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is
							continue;
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi")
						{
							continue;
						}
						int checkDigit=-1;
						try {
							checkDigit=PIn.Int(fieldPatIds.ListRepeatFields[r].GetComponentVal(1));
						}
						catch(Exception ex) {
							//checkDigit will remain -1
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10"
						&& (checkDigit==-1 || M10CheckDigit(fieldPatIds.ListRepeatFields[r].GetComponentVal(0))!=checkDigit))//using M10 scheme and either invalid check digit or doesn't match calc
						{
							continue;
						}
						if(fieldPatIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11"
						&& (checkDigit==-1 || M11CheckDigit(fieldPatIds.ListRepeatFields[r].GetComponentVal(0))!=checkDigit))//using M11 scheme and either invalid check digit or doesn't match calc
						{
							continue;
						}
						//if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and attempt to use
						if(arrayPatIdSubComps[1]==patOIDRoot.ToLower()) {
							if(patNumFromIds==0) {
								try {
									patNumFromIds=PIn.Long(fieldPatIds.ListRepeatFields[r].GetComponentVal(0));
								}
								catch(Exception ex) {
									//do nothing, patNumFromList will remain 0
								}
							}
						}
						else {
							OIDExternal oidCur=new OIDExternal();
							oidCur.IDType=IdentifierType.Patient;
							oidCur.IDExternal=fieldPatIds.ListRepeatFields[r].GetComponentVal(0);
							oidCur.rootExternal=arrayPatIdSubComps[1];
							listOids.Add(oidCur);
						}
					}
				}
				#endregion patientIdList
			}
			if(_isEcwHL7Def &&  (patLName=="" || patFName=="")) {
				EventLog.WriteEntry("OpenDentHL7","Message not processed due to missing first or last name. PatNum:"+patNum.ToString()
					,EventLogEntryType.Information);
				_hl7MsgCur.Note="Message not processed due to missing first or last name. PatNum:"+patNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				return;
			}
			#endregion PID fieldsLoop
			#endregion GetPatientIDs
			//We now have patnum, chartnum, patname, and/or birthdate so locate pat
			Patient pat=null;
			Patient patOld=null;
			//pat will be null if patNum==0
			pat=Patients.GetPat(patNum);
			if(pat==null && patNumFromIds>0) {
				pat=Patients.GetPat(patNumFromIds);
			}
			//If ChartNumber is a field in their defined PID segment, and they are not using eCWTight or Full internal type
			//Use the ChartNumber followed by Name and Birthdate to try to locate the patient for this message if chartNum is not null
			if(def.InternalType!=HL7InternalType.eCWFull && def.InternalType!=HL7InternalType.eCWTight && pat==null && chartNum!=null) {
				pat=Patients.GetPatByChartNumber(chartNum);
				//If not using eCWTight or Full integration, if pat is still null we need to try to locate patient by name and birthdate
				if(pat==null) {
					long patNumByName=Patients.GetPatNumByNameAndBirthday(patLName,patFName,birthdate);
					if(patNumByName>0) {
						pat=Patients.GetPat(patNumByName);
					}
				}
				if(pat!=null) {
					patOld=pat.Copy();
					pat.ChartNumber=chartNum;//from now on, we will be able to find pat by chartNumber
					Patients.Update(pat,patOld);
					if(_isVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref()+" to include ChartNumber.",EventLogEntryType.Information);
					}
				}
			}
			//Use the external OIDs stored in the oidexternal table to find the patient
			//Only trust the external IDs if all OIDs refer to the same patient
			long patNumFromExtIds=0;
			if(pat==null) {
				for(int i=0;i<listOids.Count;i++) {
					OIDExternal oidExternalCur=OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal);
					if(oidExternalCur==null || oidExternalCur.IDInternal==0 || oidExternalCur.IDType!=IdentifierType.Patient) {//must have an IDType of patient
						continue;
					}
					if(patNumFromExtIds==0) {
						patNumFromExtIds=oidExternalCur.IDInternal;
					}
					else if(patNumFromExtIds!=oidExternalCur.IDInternal) {//the current ID refers to a different patient than a previously found ID, don't trust the external IDs
						patNumFromExtIds=0;
						break;
					}
				}
				if(patNumFromExtIds>0) {//will be 0 if not in the OIDExternal table or no external IDs supplied or more than one supplied and they point to different pats (ambiguous)
					pat=Patients.GetPat(patNumFromExtIds);
				}
			}
			_isNewPat=pat==null;
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM && _isNewPat) {//SRM messages must refer to existing appointments, so there must be an existing patient as well
				MessageHL7 hl7SRR=MessageConstructor.GenerateSRR(pat,null,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
				HL7Msg hl7Msg=new HL7Msg();
				hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
				hl7Msg.MsgText=hl7SRR.ToString();
				hl7Msg.Note="Could not process HL7 SRM message due to an invalid or missing patient ID in the PID segment.";
				HL7Msgs.Insert(hl7Msg);
				throw new Exception("Could not process HL7 SRM message due to an invalid or missing patient ID in the PID segment.");
			}
			if(_isNewPat) {
				pat=new Patient();
				if(chartNum!=null) {
					pat.ChartNumber=chartNum;
				}
				if(patNum!=0) {
					pat.PatNum=patNum;
					pat.Guarantor=patNum;
				}
				else if(patNumFromIds!=0) {
					pat.PatNum=patNumFromIds;
					pat.Guarantor=patNumFromIds;
				}
				else if(patNumFromExtIds!=0) {
					pat.PatNum=patNumFromExtIds;
					pat.Guarantor=patNumFromExtIds;
				}
				pat.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				pat.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else {
				patOld=pat.Copy();
			}
			long aptNum=0;
			//If this is a message that contains an ARQ or SCH segment, loop through the fields to find the AptNum.  Pass it to other segment parsing methods that require it.
			//If this is an SRM message, and an AptNum is not included or no appointment with this AptNum is in the OD db, do not process the message.
			//We only accept SRM messages for appointments that already exist in the OD db.
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				if(hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.SCH//SIU messages will have the SCH segment, used for eCW or other interfaces where OD is an auxiliary application
					&& hl7defmsg.hl7DefSegments[i].SegmentName!=SegmentNameHL7.ARQ)//SRM messages will have the ARQ segment, used for interfaces where OD is the filler application
				{
					continue;
				}
				//we found the SCH or ARQ segment
				int segOrder=hl7defmsg.hl7DefSegments[i].ItemOrder;
				for(int j=0;j<hl7defmsg.hl7DefSegments[i].hl7DefFields.Count;j++) {//Go through fields of SCH or ARQ segment and get AptNum
					if(hl7defmsg.hl7DefSegments[i].hl7DefFields[j].FieldName=="apt.AptNum") {
						int aptNumOrdinal=hl7defmsg.hl7DefSegments[i].hl7DefFields[j].OrdinalPos;
						try {
							aptNum=PIn.Long(msg.Segments[segOrder].GetFieldComponent(aptNumOrdinal,0).ToString());
						}
						catch(Exception ex) {//PIn.Long will throw an exception if a value is not able to be parsed into a long
							//do nothing, aptNum will remain 0
						}
						break;
					}
				}
				if(aptNum>0) {
					break;
				}
			}
			Appointment aptCur=Appointments.GetOneApt(aptNum);//if aptNum=0, aptCur will be null
			//SRM messages are only for interfaces where OD is considered the 'filler' application
			//Not valid for eCW where OD is considered an 'auxiliary' application and we receive SIU messages instead.
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {
				MessageHL7 hl7Srr=null;
				HL7Msg hl7Msg=null;
				if(aptCur==null) {
					hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
					hl7Msg=new HL7Msg();
					hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
					hl7Msg.MsgText=hl7Srr.ToString();
					hl7Msg.PatNum=pat.PatNum;
					hl7Msg.Note="Could not process HL7 SRM message due to an invalid or missing appointment ID in the ARQ segment.  Appointment ID attempted: "+aptNum.ToString();
					HL7Msgs.Insert(hl7Msg);
					throw new Exception("Could not process HL7 SRM message due to an invalid or missing appointment ID in the ARQ segment.  Appointment ID attempted: "+aptNum.ToString());
				}
				if(pat.PatNum!=aptCur.PatNum) {//an SRM must refer to a valid appt, therefore the patient cannot be new and must have a PatNum
					hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
					hl7Msg=new HL7Msg();
					hl7Msg.AptNum=aptCur.AptNum;
					hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
					hl7Msg.MsgText=hl7Srr.ToString();
					hl7Msg.PatNum=pat.PatNum;
					hl7Msg.Note="Could not process HL7 SRM message.\r\n"
						+"The patient identified in the PID segment is not the same as the patient on the appointment identified in the ARQ segment.\r\n"
						+"Appointment PatNum: "+aptCur.PatNum.ToString()+".  PID segment PatNum: "+pat.PatNum.ToString()+".";
					HL7Msgs.Insert(hl7Msg);
					throw new Exception("Could not process HL7 SRM message.\r\n"
						+"The patient identified in the PID segment is not the same as the patient on the appointment identified in the ARQ segment.\r\n"
						+"Appointment PatNum: "+aptCur.PatNum.ToString()+".  PID segment PatNum: "+pat.PatNum.ToString()+".");
				}
			}
			//We now have a patient object , either loaded from the db or new, and an appointment (could be null) 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(pat.PatNum==0) {//Only eCWTight or eCWFull internal types will allow the HL7 message to dictate our PatNums.
					pat.PatNum=Patients.Insert(pat,false);
				}
				else {
					pat.PatNum=Patients.Insert(pat,true);
				}
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
				}
				patOld=pat.Copy();
			}
			//Update hl7msg table with correct PatNum for this message
			_hl7MsgCur.PatNum=pat.PatNum;
			HL7Msgs.Update(_hl7MsgCur);
			if(aptCur!=null && pat.PatNum!=aptCur.PatNum) {
				throw new Exception("Appointment does not match patient "+pat.GetNameFLnoPref()+", apt.PatNum: "+aptCur.PatNum.ToString()+", pat.PatNum: "+pat.PatNum.ToString());
			}
			for(int i=0;i<hl7defmsg.hl7DefSegments.Count;i++) {
				try {
					List<SegmentHL7> listSegments=new List<SegmentHL7>();
					if(hl7defmsg.hl7DefSegments[i].CanRepeat) {
						listSegments=msg.GetSegments(hl7defmsg.hl7DefSegments[i].SegmentName,!hl7defmsg.hl7DefSegments[i].IsOptional);
					}
					else {
						SegmentHL7 seg=msg.GetSegment(hl7defmsg.hl7DefSegments[i].SegmentName,!hl7defmsg.hl7DefSegments[i].IsOptional);
						if(seg==null) {//null if segment was not found but is optional
							continue;
						}
						listSegments.Add(seg);
					}
					for(int j=0;j<listSegments.Count;j++) {//normally only 1 or 0 in the list, but if it is a repeatable segment the list may contain multiple segments to process
						if(_isVerboseLogging) {
							EventLog.WriteEntry("OpenDentHL7","Process segment "+hl7defmsg.hl7DefSegments[i].SegmentName.ToString(),EventLogEntryType.Information);
						}
						ProcessSeg(pat,aptCur,hl7defmsg.hl7DefSegments[i],listSegments[j],msg);
						if(hl7defmsg.hl7DefSegments[i].SegmentName==SegmentNameHL7.SCH) {
							aptCur=_aptProcessed;
						}
					}
				}
				catch(ApplicationException ex) {//Required segment was missing, or other error.
					_hl7MsgCur.Note="Could not process this HL7 message.  "+ex;
					HL7Msgs.Update(_hl7MsgCur);
					if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {//SRM messages require sending an SRR response, this will be with Ack Code AE - Application Error
						MessageHL7 hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,false,msg.AckEvent);//use false to indicate AE - Application Error in SRR.MSA segment
						HL7Msg hl7Msg=new HL7Msg();
						hl7Msg.AptNum=aptCur.AptNum;
						hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
						hl7Msg.MsgText=hl7Srr.ToString();
						hl7Msg.Note="Could not process an HL7 SRM message.  Send SRR for the request.  "+ex;
						hl7Msg.PatNum=pat.PatNum;
						HL7Msgs.Insert(hl7Msg);
					}
					throw new Exception("Could not process an HL7 message.  "+ex);
				}
			}
			//We have processed the message so now update 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);
			}
			else {
				if(_isNewPat && pat.Guarantor==0) {
					pat.Guarantor=pat.PatNum;
				}
				Patients.Update(pat,patOld);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
				}
				_hl7MsgCur.HL7Status=HL7MessageStatus.InProcessed;
				HL7Msgs.Update(_hl7MsgCur);
			}
			//Schedule Request Messages require a Schedule Request Response if the data requested to be changed was successful
			//We only allow changing the appt note, setting the dentist and hygienist, updating the confirmation status, and changing the ClinicNum.
			//We also allow setting the appt status to broken if the EventType is S04 - Request Appointment Cancellation.
			//We will generate the SRR if we get here, since we will have processed the message properly.
			//The SRM will be ACK'd after returning from this Process method in ServiceHL7.
			//Our SRR will also be ACK'd by the receiving software.
			if(!_isEcwHL7Def && msg.MsgType==MessageTypeHL7.SRM) {
				if(msg.AckEvent=="S04") {
					Appointment aptOld=aptCur.Clone();
					aptCur.AptStatus=ApptStatus.Broken;
					Appointments.Update(aptCur,aptOld);
					if(_isVerboseLogging) {
						EventLog.WriteEntry("OpenDentHL7","Appointment broken due to inbound SRM message with event type S04 for patient "+pat.GetNameFLnoPref(),EventLogEntryType.Information);
					}
				}
				MessageHL7 hl7Srr=MessageConstructor.GenerateSRR(pat,aptCur,msg.EventType,msg.ControlId,true,msg.AckEvent);
				HL7Msg hl7Msg=new HL7Msg();
				hl7Msg.AptNum=aptCur.AptNum;
				hl7Msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
				hl7Msg.MsgText=hl7Srr.ToString();
				hl7Msg.PatNum=pat.PatNum;
				HL7Msgs.Insert(hl7Msg);
			}
		}
Beispiel #32
0
		public static void ProcessMessage(MessageHL7 message,bool isVerboseLogging) {
			SegmentHL7 seg=message.GetSegment(SegmentNameHL7.PID,true);
			long patNum=PIn.Long(seg.GetFieldFullText(2));
			Patient pat=Patients.GetPat(patNum);
			Patient patOld=null;
			bool isNewPat = pat==null;
			if(isNewPat) {
				pat=new Patient();
				pat.PatNum=patNum;
				pat.Guarantor=patNum;
				pat.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				pat.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else {
				patOld=pat.Copy();
			}
			List<PatRace> listPatRaces=new List<PatRace>();
			EcwSegmentPID.ProcessPID(pat,seg,false,listPatRaces);//IsStandalone=false because should never make it this far.
			//PV1-patient visit---------------------------
			//seg=message.GetSegment(SegmentName.PV1,false);
			//if(seg!=null) {
			//	SegmentPID.ProcessPV1(pat,seg);
			//}
			//SCH- Schedule Activity Information
			seg=message.GetSegment(SegmentNameHL7.SCH,true);
			//The documentation is wrong.  SCH.01 is not the appointment ID, but is instead a sequence# (always 1)
			long aptNum=PIn.Long(seg.GetFieldFullText(2));
			Appointment apt=Appointments.GetOneApt(aptNum);
			Appointment aptOld=null;
			bool isNewApt = apt==null;
			if(isNewApt) {
				apt=new Appointment();
				apt.AptNum=aptNum;
				apt.PatNum=pat.PatNum;
				apt.AptStatus=ApptStatus.Scheduled;
			}
			else{
				aptOld=apt.Clone();
			}
			if(apt.PatNum != pat.PatNum) {
				EventLog.WriteEntry("OpenDentHL7","Appointment does not match patient: "+pat.FName+" "+pat.LName
					+", apt.PatNum:"+apt.PatNum.ToString()+", pat.PatNum:"+pat.PatNum.ToString()
					,EventLogEntryType.Error);
				return;//we can't process this message because wrong patnum.
			}
			apt.Note=seg.GetFieldFullText(7);
			//apt.Pattern=ProcessDuration(seg.GetFieldFullText(9));
			//9 and 10 are not actually available, in spite of the documentation.
			//11-We need start time and stop time
			apt.AptDateTime=DateTimeParse(seg.GetFieldComponent(11,3));
			DateTime stopTime=DateTimeParse(seg.GetFieldComponent(11,4));
			apt.Pattern=ProcessPattern(apt.AptDateTime,stopTime);
			apt.ProvNum=pat.PriProv;//just in case there's not AIG segment.
			//AIG is optional, but looks like the only way to get provider for the appt-----------
			//PV1 seems to frequently be sent instead of AIG.
			SegmentHL7 segAIG=message.GetSegment(SegmentNameHL7.AIG,false);
			SegmentHL7 segPV=message.GetSegment(SegmentNameHL7.PV1,false);
			if(segAIG!=null) {
				long provNum=EcwSegmentPID.ProvProcess(segAIG.GetField(3));
				if(provNum!=0) {
					apt.ProvNum=provNum;
					pat.PriProv=provNum;
				}
			}
			else if(segPV!=null) {
				long provNum=EcwSegmentPID.ProvProcess(segPV.GetField(7));
				if(provNum!=0) {
					apt.ProvNum=provNum;
					pat.PriProv=provNum;
				}
			}
			//AIL,AIP seem to be optional, and I'm going to ignore them for now.
			if(pat.FName=="" || pat.LName=="") {
				EventLog.WriteEntry("OpenDentHL7","Appointment not processed due to missing patient first or last name. PatNum:"+pat.PatNum.ToString()
					,EventLogEntryType.Information);
				return;//this will also skip the appt insert.
			}
			if(isNewPat) {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient: "+pat.FName+" "+pat.LName+", PatNum:"+pat.PatNum.ToString()
						,EventLogEntryType.Information);
				}
				Patients.Insert(pat,true);
			}
			else {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Patients.Update(pat,patOld);
			}
			//had to move this reconcile here since we might not have a PatNum for new patients until after the insert
			PatientRaces.Reconcile(pat.PatNum,listPatRaces);
			if(isNewApt) {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted appointment for: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Appointments.InsertIncludeAptNum(apt,true);
			}
			else {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated appointment for: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Appointments.Update(apt,aptOld);
			}
		}
Beispiel #33
0
		///<summary>Returns AptNum of the incoming appointment.  apt was found using the apt.AptNum field of the SCH segment, but can be null if it's a new appointment.  Used for eCW and other interfaces where OD is not the filler application.  When OD is not the filler application, we allow appointments to be created by the interfaced software and communicated to OD with an SIU message.</summary>
		public static long ProcessSCH(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			if(pat.FName=="" || pat.LName=="") {
				throw new Exception("Appointment not processed due to missing patient first or last name. PatNum:"+pat.PatNum.ToString());
			}
			char escapeChar='\\';
			if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \
				escapeChar=msg.Delimiters[2];
			}
			string strAptNote="";
			double aptLength=0;
			long aptNum=0;
			DateTime aptStart=DateTime.MinValue;
			DateTime aptStop=DateTime.MinValue;
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int itemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "apt.AptNum":
						try {
							aptNum=PIn.Long(seg.GetFieldComponent(itemOrder));
						}
						catch(Exception ex) {
							//do nothing, aptNum will remain 0
						}
						if(apt!=null && apt.AptNum!=aptNum) {
							//an appointment was located from the inbound message, but the AptNum on the appointment is not the same as the AptNum in this SCH segment (should never happen)
							throw new Exception("Invalid appointment number.");
						}
						continue;
					case "apt.lengthStartEnd":
						aptLength=FieldParser.SecondsToMinutes(seg.GetFieldComponent(itemOrder,2));
						aptStart=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder,3));
						aptStop=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder,4));
						continue;
					case "apt.Note":
						strAptNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(itemOrder),escapeChar);
						continue;
					default:
						continue;
				}
			}
			Appointment aptOld=null;
			bool isNewApt=(apt==null);
			if(isNewApt) {
				apt=new Appointment();
				apt.AptNum=aptNum;
				apt.PatNum=pat.PatNum;
				apt.AptStatus=ApptStatus.Scheduled;
				apt.Note="";
			}
			else {
				aptOld=apt.Clone();
			}
			if(apt.PatNum!=pat.PatNum) {//we can't process this message because wrong patnum.
				throw new Exception("Appointment does not match patient "+pat.GetNameFLnoPref()+", apt.PatNum: "+apt.PatNum.ToString()+", pat.PatNum: "+pat.PatNum.ToString());
			}
			//if the existing note contains the exact text of the note in the SCH segment, don't append it again
			//replace the \r's and then \n's with a blank string before comparing to eliminate inconsistencies in new line characters
			if(!apt.Note.Replace("\r","").Replace("\n","").Contains(strAptNote.Replace("\r","").Replace("\n",""))) {
				if(apt.Note.Length>0) {//if the existing note already contains some text, add a new line before appending the note from the SCH segment
					apt.Note+="\r\n";
				}
				apt.Note+=strAptNote;
			}
			string pattern;
			//If aptStop is MinValue we know that stop time was not sent or was not in the correct format so try to use the duration field.
			if(aptStop==DateTime.MinValue && aptLength!=0) {//Stop time is optional.  If not included we will use the duration field to calculate pattern.
				pattern=FieldParser.ProcessPattern(aptStart,aptStart.AddMinutes(aptLength));
			}
			else {//We received a good stop time or stop time is MinValue but we don't have a good aptLength so ProcessPattern will return the apt length or the default 5 minutes
				pattern=FieldParser.ProcessPattern(aptStart,aptStop);
			}
			apt.AptDateTime=aptStart;
			apt.Pattern=pattern;
			apt.ProvNum=pat.PriProv;//Set apt.ProvNum to the patient's primary provider.  This may change after processing the AIG or PV1 segments, but set here in case those segs are missing.
			if(isNewApt) {
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted appointment for "+pat.GetNameFLnoPref()+" due to an incoming SCH segment.",EventLogEntryType.Information);
				}
				Appointments.InsertIncludeAptNum(apt,true);
			}
			else {
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated appointment for "+pat.GetNameFLnoPref()+" due to an incoming SCH segment.",EventLogEntryType.Information);
				}
				Appointments.Update(apt,aptOld);
			}
			_hl7MsgCur.AptNum=apt.AptNum;
			HL7Msgs.Update(_hl7MsgCur);
			if(_isVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Updated hl7msg to include AptNum for "+pat.GetNameFLnoPref()+" due to an incoming SCH segment.",EventLogEntryType.Information);
			}
			_aptProcessed=apt;
			return aptNum;
		}
Beispiel #34
0
        //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);
        }
Beispiel #35
0
		public static void ProcessSeg(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			switch(segDef.SegmentName) {
				case SegmentNameHL7.AIG:
				case SegmentNameHL7.AIP:
					ProcessAIGorAIP(pat,apt,segDef,seg);//segDef.SegmentName will be used by this function to parse the provider as ProvNum^LName^Fname^MI^^Abbr or ProvNum^LName, FName^^Abbr
					return;
				case SegmentNameHL7.AIL:
					ProcessAIL(pat,apt,segDef,seg);
					return;
				case SegmentNameHL7.AL1:
					ProcessAL1(pat,segDef,seg);
					return;
				case SegmentNameHL7.ARQ:
					ProcessARQ(pat,apt,segDef,seg,msg);
					return;
				case SegmentNameHL7.GT1:
					ProcessGT1(pat,segDef,seg,msg);
					return;
				case SegmentNameHL7.IN1:
					//ProcessIN1();
					return;
				case SegmentNameHL7.MSA:
					ProcessMSA(segDef,seg,msg);
					return;
				case SegmentNameHL7.MSH:
					ProcessMSH(segDef,seg,msg);
					return;
				case SegmentNameHL7.NTE:
					ProcessNTE(pat,apt,segDef,seg,msg);
					return;
				case SegmentNameHL7.OBX:
					ProcessOBX(pat,segDef,seg);
					return;
				case SegmentNameHL7.PD1:
					//ProcessPD1();
					return;
				case SegmentNameHL7.PID:
					ProcessPID(pat,segDef,seg,msg);
					return;
				case SegmentNameHL7.PR1:
					ProcessPR1(pat,segDef,seg,msg);
					return;
				case SegmentNameHL7.PRB:
					ProcessPRB(pat,segDef,seg,msg);
					return;
				case SegmentNameHL7.PV1:
					ProcessPV1(pat,apt,segDef,seg);
					return;
				case SegmentNameHL7.SCH:
					ProcessSCH(pat,apt,segDef,seg,msg);
					return;
				default:
					return;
			}
		}
Beispiel #36
0
		///<summary>Runs in a separate thread</summary>
		private void OnDataReceived(IAsyncResult asyncResult) {
			int byteCountReceived=socketIncomingWorker.EndReceive(asyncResult);//blocks until data is recieved.
			char[] chars=new char[byteCountReceived];
			Decoder decoder=Encoding.UTF8.GetDecoder();
			decoder.GetChars(dataBufferIncoming,0,byteCountReceived,chars,0);//doesn't necessarily get all bytes from the buffer because buffer could be half full.
			strbFullMsg.Append(chars);//strbFullMsg might already have partial data
			//I think we are guaranteed to have received at least one char.
			bool isFullMsg=false;
			bool isMalformed=false;
			if(strbFullMsg.Length==1 && strbFullMsg[0]==MLLP_ENDMSG_CHAR){//the only char in the message is the end char
				strbFullMsg.Clear();//this must be the very end of a previously processed message.  Discard.
				isFullMsg=false;
			}
			//else if(strbFullMsg[0]!=MLLP_START_CHAR) {
			else if(strbFullMsg.Length>0 && strbFullMsg[0]!=MLLP_START_CHAR) {
				//Malformed message. 
				isFullMsg=true;//we're going to do this so that the error gets saved in the database further down.
				isMalformed=true;
			}
			else if(strbFullMsg.Length>=3//so that the next two lines won't crash
				&& strbFullMsg[strbFullMsg.Length-1]==MLLP_ENDMSG_CHAR//last char is the endmsg char.
				&& strbFullMsg[strbFullMsg.Length-2]==MLLP_END_CHAR)//the second-to-the-last char is the end char.
			{
				//we have a complete message
				strbFullMsg.Remove(0,1);//strip off the start char
				strbFullMsg.Remove(strbFullMsg.Length-2,2);//strip off the end chars
				isFullMsg=true;
			}
			else if(strbFullMsg.Length>=2//so that the next line won't crash
				&& strbFullMsg[strbFullMsg.Length-1]==MLLP_END_CHAR)//the last char is the end char.
			{
				//we will treat this as a complete message, because the endmsg char is optional.
				//if the endmsg char gets sent in a subsequent block, the code above will discard it.
				strbFullMsg.Remove(0,1);//strip off the start char
				strbFullMsg.Remove(strbFullMsg.Length-1,1);//strip off the end char
				isFullMsg=true;
			}
			else {
				isFullMsg=false;//this is an incomplete message.  Continue to receive more blocks.
			}
			//end of big if statement-------------------------------------------------
			if(!isFullMsg) {
				dataBufferIncoming=new byte[8];//clear the buffer
				socketIncomingWorker.BeginReceive(dataBufferIncoming,0,dataBufferIncoming.Length,SocketFlags.None,new AsyncCallback(OnDataReceived),null);
				return;//get another block
			}
			//Prepare to save message to database if malformed and not processed
			HL7Msg hl7Msg=new HL7Msg();
			hl7Msg.MsgText=strbFullMsg.ToString();		
			strbFullMsg.Clear();//ready for the next message
			bool isProcessed=true;
			string messageControlId="";
			string ackEvent="";
			if(isMalformed){
				hl7Msg.HL7Status=HL7MessageStatus.InFailed;
				hl7Msg.Note="This message is malformed so it was not processed.";
				HL7Msgs.Insert(hl7Msg);
				isProcessed=false;
			}
			else{
				MessageHL7 messageHl7Object=new MessageHL7(hl7Msg.MsgText);//this creates an entire heirarchy of objects.
				try {
					MessageParser.Process(messageHl7Object,IsVerboseLogging);//also saves the message to the db
					messageControlId=messageHl7Object.ControlId;
					ackEvent=messageHl7Object.AckEvent;
				}
				catch(Exception ex) {
					EventLog.WriteEntry("OpenDentHL7","Error in OnDataRecieved when processing message:\r\n"+ex.Message+"\r\n"+ex.StackTrace,EventLogEntryType.Error);
					isProcessed=false;
				}
			}
			MessageHL7 hl7Ack=MessageConstructor.GenerateACK(messageControlId,isProcessed,ackEvent);
			if(hl7Ack==null) {
				EventLog.WriteEntry("OpenDentHL7","No ACK defined for the enabled HL7 definition or no HL7 definition enabled.",EventLogEntryType.Information);
				return;
			}
			byte[] ackByteOutgoing=Encoding.ASCII.GetBytes(MLLP_START_CHAR+hl7Ack.ToString()+MLLP_END_CHAR+MLLP_ENDMSG_CHAR);
			if(IsVerboseLogging) {
				EventLog.WriteEntry("OpenDentHL7","Beginning to send ACK.\r\n"+MLLP_START_CHAR+hl7Ack.ToString()+MLLP_END_CHAR+MLLP_ENDMSG_CHAR,EventLogEntryType.Information);
			}
			socketIncomingWorker.Send(ackByteOutgoing);//this is a locking call
			//eCW uses the same worker socket to send the next message. Without this call to BeginReceive, they would attempt to send again
			//and the send would fail since we were no longer listening in this thread. eCW would timeout after 30 seconds of waiting for their
			//acknowledgement, then they would close their end and create a new socket for the next message. With this call, we can accept message
			//after message without waiting for a new connection.
			dataBufferIncoming=new byte[8];//clear the buffer
			socketIncomingWorker.BeginReceive(dataBufferIncoming,0,dataBufferIncoming.Length,SocketFlags.None,new AsyncCallback(OnDataReceived),null);
		}
Beispiel #37
0
		///<summary>If relationship is self, this method does nothing.  A new pat will later change guarantor to be same as patnum. </summary>
		public static void ProcessGT1(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			char escapeChar='\\';
			if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \
				escapeChar=msg.Delimiters[2];
			}
			char subcompChar='&';
			if(msg.Delimiters.Length>3) {
				subcompChar=msg.Delimiters[3];
			}
			#region Get And Validate Definition Field Ordinals
			//Find the position of the guarNum, guarChartNum, guarName, and guarBirthdate in this HL7 segment based on the definition of a GT1
			int guarPatNumOrdinal=-1;
			int guarChartNumOrdinal=-1;
			int guarNameOrdinal=-1;
			int guarBirthdateOrdinal=-1;
			int guarIdsOrdinal=-1;
			string patOidRoot="";
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				switch(segDef.hl7DefFields[i].FieldName) {
					case "guar.PatNum":
						guarPatNumOrdinal=segDef.hl7DefFields[i].OrdinalPos;
						continue;
					case "guar.ChartNumber":
						guarChartNumOrdinal=segDef.hl7DefFields[i].OrdinalPos;
						continue;
					case "guar.nameLFM":
						guarNameOrdinal=segDef.hl7DefFields[i].OrdinalPos;
						continue;
					case "guar.birthdateTime":
						guarBirthdateOrdinal=segDef.hl7DefFields[i].OrdinalPos;
						continue;
					case "guarIdList":
						//get the id with the assigning authority equal to the internal OID root stored in their database for a patient object
						patOidRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot;
						if(patOidRoot=="") {
							//if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum, guarIdListOrdinal will remain -1
							continue;
						}
						guarIdsOrdinal=segDef.hl7DefFields[i].OrdinalPos;
						continue;
					default://not supported
						continue;
				}
			}
			//If neither guar.PatNum nor guar.ChartNumber are included in this GT1 definition log a message in the event log and return
			if(guarPatNumOrdinal==-1 && guarChartNumOrdinal==-1 && guarIdsOrdinal==-1) {
				_hl7MsgCur.Note="Guarantor not processed.  guar.PatNum, guar.ChartNumber, or guarIdList must be included in the GT1 definition.  PatNum of patient: "+pat.PatNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				EventLog.WriteEntry("OpenDentHL7","Guarantor not processed.  guar.PatNum, guar.ChartNumber, or guarIdList must be included in the GT1 definition.  "
					+"PatNum of patient: "+pat.PatNum.ToString(),EventLogEntryType.Information);
				return;
			}
			//If guar.nameLFM is not included in this GT1 definition log a message in the event log and return
			if(guarNameOrdinal==-1) {
				_hl7MsgCur.Note="Guarantor not processed due to guar.nameLFM not included in the GT1 definition.  Patnum of patient: "+pat.PatNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to guar.nameLFM not included in the GT1 definition.  Patnum of patient: "
					+pat.PatNum.ToString(),EventLogEntryType.Information);
				return;
			}
			//If the first or last name are not included in this GT1 segment, log a message in the event log and return
			if(seg.GetFieldComponent(guarNameOrdinal,0)=="" || seg.GetFieldComponent(guarNameOrdinal,1)=="") {
				_hl7MsgCur.Note="Guarantor not processed due to missing first or last name.  PatNum of patient: "+pat.PatNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to missing first or last name.  PatNum of patient: "
					+pat.PatNum.ToString(),EventLogEntryType.Information);
				return;
			}
			#endregion Get And Validate Definition Field Ordinals
			#region Get guar.PatNum, guar.ChartNumber, and guarIdList IDs
			//Only process GT1 if guar.PatNum, guar.ChartNumber, or guarIdList is included and both guar.LName and guar.FName are included
			long guarPatNum=0;
			string guarChartNum="";
			long guarPatNumFromIds=0;
			List<OIDExternal> listOids=new List<OIDExternal>();
			if(guarPatNumOrdinal!=-1) {
				try {
					guarPatNum=PIn.Long(seg.GetFieldFullText(guarPatNumOrdinal));
				}
				catch(Exception ex) {
					//do nothing, guarPatNum will remain 0
				}
			}
			if(guarChartNumOrdinal!=-1) {
				guarChartNum=seg.GetFieldComponent(guarChartNumOrdinal);
			}
			if(guarIdsOrdinal!=-1) {//if OIDInternal root for a patient object is not defined, guarIdListOrdinal will be -1
				FieldHL7 fieldGuarIds=seg.GetField(guarIdsOrdinal);
				//Example field: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI|
				//field component values will be the first repetition, repeats will be in field.ListRepeatFields
				string[] arrayGuarIdSubComps=fieldGuarIds.GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None);
				if(arrayGuarIdSubComps.Length>1 //must be at least two subcomponents in the assigning authority component so we can identify whose ID this is
					&& fieldGuarIds.GetComponentVal(4).ToLower()=="pi") //PI=patient internal identifier; a number that is unique to a patient within an assigning authority
				{
					int intCheckDigit=-1;
					try {
						intCheckDigit=PIn.Int(fieldGuarIds.GetComponentVal(1));
					}
					catch(Exception ex) {
						//checkDigit will remain -1
					}
					//if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm
					//and only if there's at least 2 components in field 3 for the assigning authority so we can identify whose ID we are dealing with, then use the ID
					if((fieldGuarIds.GetComponentVal(2).ToLower()=="m10"
							&& intCheckDigit!=-1
							&& M10CheckDigit(fieldGuarIds.GetComponentVal(0))==intCheckDigit)//using M10 scheme and the check digit is valid and matches calc
						|| (fieldGuarIds.GetComponentVal(2).ToLower()=="m11"
							&& intCheckDigit!=-1
							&& M11CheckDigit(fieldGuarIds.GetComponentVal(0))==intCheckDigit)//using M11 scheme and the check digit is valid and matches calc
						|| (fieldGuarIds.GetComponentVal(2).ToLower()!="m10"
							&& fieldGuarIds.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme
					{
						if(arrayGuarIdSubComps[1].ToLower()==patOidRoot.ToLower()) {
							try {
								guarPatNumFromIds=PIn.Long(fieldGuarIds.GetComponentVal(0));
							}
							catch(Exception ex) {
								//do nothing, guarPatNumFromList will remain 0
							}
						}
						else {
							OIDExternal oidCur=new OIDExternal();
							oidCur.IDType=IdentifierType.Patient;
							oidCur.IDExternal=fieldGuarIds.GetComponentVal(0);
							oidCur.rootExternal=arrayGuarIdSubComps[1];
							listOids.Add(oidCur);
						}
					}
				}
				for(int r=0;r<fieldGuarIds.ListRepeatFields.Count;r++) {
					arrayGuarIdSubComps=fieldGuarIds.ListRepeatFields[r].GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None);
					if(arrayGuarIdSubComps.Length<2) {//must be at least two sub-components to the assigning authority component so we can identify whose ID this is
						continue;
					}
					if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi")
					{
						continue;
					}
					int intCheckDigit=-1;
					try {
						intCheckDigit=PIn.Int(fieldGuarIds.ListRepeatFields[r].GetComponentVal(1));
					}
					catch(Exception ex) {
						//checkDigit will remain -1
					}
					if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10"
						&& (intCheckDigit==-1 || M10CheckDigit(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M10 scheme and either invalid check digit or doesn't match calc
					{
						continue;
					}
					if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11"
						&& (intCheckDigit==-1 || M11CheckDigit(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either invalid check digit or doesn't match calc
					{
						continue;
					}
					//if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and attempt to use
					if(arrayGuarIdSubComps[1].ToLower()==patOidRoot.ToLower()) {
						try {
							guarPatNumFromIds=PIn.Long(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0));
						}
						catch(Exception ex) {
							//do nothing, guarPatNumFromList will remain 0
						}
					}
					else {
						OIDExternal oidCur=new OIDExternal();
						oidCur.IDType=IdentifierType.Patient;
						oidCur.IDExternal=fieldGuarIds.ListRepeatFields[r].GetComponentVal(0);
						oidCur.rootExternal=arrayGuarIdSubComps[1];
						listOids.Add(oidCur);
					}
				}
			}
			if(guarPatNum==0 && guarChartNum=="" && guarPatNumFromIds==0 && listOids.Count==0) {//because we have an example where they sent us this (position 2 (guarPatNumOrder or guarChartNumOrder for eCW) is empty): GT1|1||^^||^^^^||||||||
				_hl7MsgCur.Note="Guarantor not processed due to missing guar.PatNum, guar.ChartNumber, and guarIdList.  One of those numbers must be included.  "
					+"PatNum of patient: "+pat.PatNum.ToString();
				HL7Msgs.Update(_hl7MsgCur);
				EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to missing guar.PatNum, guar.ChartNumber, and guarIdList.  "
					+"One of those numbers must be included.  PatNum of patient: "+pat.PatNum.ToString(),EventLogEntryType.Information);
				return;
			}
			#endregion Get guar.PatNum, guar.ChartNumber, and guarIdList IDs
			if(guarPatNum==pat.PatNum
				|| (guarChartNum!="" && guarChartNum==pat.ChartNumber)
				|| guarPatNumFromIds==pat.PatNum)
			{//if relationship is self
				return;
			}
			#region Get Patient from Ids
			//Guar must be someone else
			Patient guar=null;
			Patient guarOld=null;
			//Find guarantor by guar.PatNum if defined and in this segment
			if(guarPatNum!=0) {
				guar=Patients.GetPat(guarPatNum);
			}
			else if(guarPatNumFromIds!=0) {
				guar=Patients.GetPat(guarPatNumFromIds);
			}
			else if(guarChartNum!="") {//guarPatNum and guarPatNumFromList was 0 so try to get guar by guar.ChartNumber or name and birthdate
				//try to find guarantor using chartNumber
				guar=Patients.GetPatByChartNumber(guarChartNum);
				if(guar==null) {
					//try to find the guarantor by using name and birthdate
					string strGuarLName=seg.GetFieldComponent(guarNameOrdinal,0);
					string strGuarFName=seg.GetFieldComponent(guarNameOrdinal,1);
					DateTime guarBirthdate=FieldParser.DateTimeParse(seg.GetFieldFullText(guarBirthdateOrdinal));
					long guarNamePatNum=Patients.GetPatNumByNameAndBirthday(strGuarLName,strGuarFName,guarBirthdate);
					if(guarNamePatNum==0) {//guarantor does not exist in OD
						//so guar will still be null, triggering creation of new guarantor further down.
					}
					else {
						guar=Patients.GetPat(guarNamePatNum);
						guarOld=guar.Copy();
						guar.ChartNumber=guarChartNum;//from now on, we will be able to find guar by chartNumber
						Patients.Update(guar,guarOld);
					}
				}
			}
			long guarExtPatNum=0;
			if(guar==null) {//As a last resort, use the external IDs for this patient in the oidexternal to find the guar
				//Use the external OIDs stored in the oidexternal table to find the patient
				//Only trust the external IDs if all OIDs refer to the same patient
				for(int i=0;i<listOids.Count;i++) {
					OIDExternal oidExternalCur=OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal);
					if(oidExternalCur==null || oidExternalCur.IDInternal==0 || oidExternalCur.IDType!=IdentifierType.Patient) {//must have an IDType of Patient
						continue;
					}
					if(guarExtPatNum==0) {
						guarExtPatNum=oidExternalCur.IDInternal;
					}
					else if(guarExtPatNum!=oidExternalCur.IDInternal) {//the current ID refers to a different patient than a previously found ID, don't trust the external IDs
						guarExtPatNum=0;
						break;
					}
				}
				if(guarExtPatNum>0) {//will be 0 if not in the OIDExternal table or no external IDs supplied or more than one supplied and they point to different pats (ambiguous)
					guar=Patients.GetPat(guarExtPatNum);
				}
			}
			//At this point we have a guarantor located in OD or guar=null so guar is new patient
			bool isNewGuar=(guar==null);
			if(isNewGuar) {//then we need to add guarantor to db
				guar=new Patient();
				if(guarChartNum!="") {
					guar.ChartNumber=guarChartNum;
				}
				if(guarPatNum!=0) {
					guar.PatNum=guarPatNum;
					guar.Guarantor=guarPatNum;
				}
				else if(guarPatNumFromIds!=0) {
					guar.PatNum=guarPatNumFromIds;
					guar.Guarantor=guarPatNumFromIds;
				}
				else if(guarExtPatNum!=0) {
					guar.PatNum=guarExtPatNum;
					guar.Guarantor=guarExtPatNum;
				}
				guar.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				guar.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else {
				guarOld=guar.Copy();
			}
			#endregion Get Patient from Ids
			#region Update Guarantor Data
			//Now that we have our guarantor, process the GT1 segment
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				int itemOrder=segDef.hl7DefFields[i].OrdinalPos;
				switch(segDef.hl7DefFields[i].FieldName) {
					case "guar.addressCityStateZip":
						guar.Address=seg.GetFieldComponent(itemOrder,0);
						guar.Address2=seg.GetFieldComponent(itemOrder,1);
						guar.City=seg.GetFieldComponent(itemOrder,2);
						guar.State=seg.GetFieldComponent(itemOrder,3);
						guar.Zip=seg.GetFieldComponent(itemOrder,4);
						guar.AddrNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(itemOrder,19),escapeChar);
						continue;
					case "guar.birthdateTime":
						guar.Birthdate=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "guar.ChartNumber":
						guar.ChartNumber=seg.GetFieldComponent(itemOrder);
						continue;
					case "guar.Gender":
						guar.Gender=FieldParser.GenderParse(seg.GetFieldComponent(itemOrder));
						continue;
					case "guar.HmPhone":
						if(seg.GetFieldComponent(itemOrder,2)=="" && seg.GetField(itemOrder).ListRepeatFields.Count==0) {
							//either no component 2 or left blank, and there are no repetitions, process the old way
							//the first repetition is considered the primary number, but if the primary number is not sent then it will be blank followed by the list of other numbers
							guar.HmPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder));
						}
						else {
							//XTN data type, repeatable.
							//Component 2 values: PH-Phone, FX-Fax, MD-Modem, CP-Cell Phone, Internet-Internet Address, X.400-X.400 email address, TDD-Tel Device for the Deaf, TTY-Teletypewriter.
							//We will support PH for pat.HmPhone, CP for pat.WirelessPhone, and Internet for pat.Email
							//Component 5 is area code, 6 is number
							//Component 3 will be Email if the type is Internet
							//Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected]
							FieldHL7 fieldPh=seg.GetField(itemOrder);
							if(fieldPh==null) {
								continue;
							}
							string strPhType=seg.GetFieldComponent(itemOrder,2);
							string strPh=seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6);
							string strEmail=seg.GetFieldComponent(itemOrder,3);
							for(int p=-1;p<fieldPh.ListRepeatFields.Count;p++) {
								if(p>=0) {
									strPhType=fieldPh.ListRepeatFields[p].GetComponentVal(2);
									strPh=fieldPh.ListRepeatFields[p].GetComponentVal(5)+fieldPh.ListRepeatFields[p].GetComponentVal(6);
									strEmail=fieldPh.ListRepeatFields[p].GetComponentVal(3);
								}
								switch(strPhType) {
									case "PH":
										guar.HmPhone=FieldParser.PhoneParse(strPh);
										continue;
									case "CP":
										guar.WirelessPhone=FieldParser.PhoneParse(strPh);
										continue;
									case "Internet":
										guar.Email=strEmail;
										continue;
									default:
										continue;
								}
							}
						}
						continue;
					case "guar.nameLFM":
						guar.LName=seg.GetFieldComponent(itemOrder,0);
						guar.FName=seg.GetFieldComponent(itemOrder,1);
						guar.MiddleI=seg.GetFieldComponent(itemOrder,2);
						guar.Title=seg.GetFieldComponent(itemOrder,4);
						continue;
					//case "guar.PatNum": Maybe do nothing??
					case "guar.SSN":
						guar.SSN=seg.GetFieldComponent(itemOrder);
						continue;
					case "guar.WkPhone":
						if(seg.GetFieldComponent(itemOrder,2)=="PH") {
							//Component 2 value: PH-Phone
							//Component 5 is area code, 6 is number
							//Example: ^WPN^PH^^^503^3635432
							guar.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6));
							continue;
						}
						//either no component 2 or left blank, process the old way
						guar.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder));
						continue;
					default:
						continue;
				}
			}
			if(isNewGuar) {
				if(guar.PatNum==0) {
					guarOld=guar.Copy();
					guar.PatNum=Patients.Insert(guar,false);
					guar.Guarantor=guar.PatNum;
					Patients.Update(guar,guarOld);
				}
				else {
					guar.PatNum=Patients.Insert(guar,true);
				}
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient "+guar.GetNameFLnoPref()+" when processing a GT1 segment.",EventLogEntryType.Information);
				}
			}
			else {
				Patients.Update(guar,guarOld);
				if(_isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient "+guar.GetNameFLnoPref()+" when processing a GT1 segment.",EventLogEntryType.Information);
				}
			}
			#endregion Update Guarantor Data
			pat.Guarantor=guar.PatNum;
			//store external IDs in the oidexternal table if they do not already exist in the table
			string strVerboseMsg="";
			for(int i=0;i<listOids.Count;i++) {
				if(listOids[i].IDExternal==""//not a good external ID OR
					|| listOids[i].rootExternal==""//not a good external root OR
					|| OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal)!=null)//already exists in the oidexternal table
				{
					continue;
				}
				listOids[i].IDInternal=guar.PatNum;
				OIDExternals.Insert(listOids[i]);
				strVerboseMsg+="\r\nExternal patient ID: "+listOids[i].IDExternal+", External root: "+listOids[i].rootExternal;
			}
			if(_isVerboseLogging && strVerboseMsg.Length>0) {
				EventLog.WriteEntry("OpenDentHL7","Added the following external patient ID(s) to the oidexternals table due to an incoming GT1 segment for PatNum: "+guar.PatNum+"."
					+strVerboseMsg+".",EventLogEntryType.Information);
			}
			return;
		}
        ///<summary>Returns null if there is no HL7Def enabled or if there is no outbound SIU defined for the enabled HL7Def.</summary>
        public static MessageHL7 GenerateSIU(Patient pat, Patient guar, EventTypeHL7 eventType, Appointment apt)
        {
            HL7Def hl7Def = HL7Defs.GetOneDeepEnabled();

            if (hl7Def == null)
            {
                return(null);
            }
            //find an outbound SIU message in the def
            HL7DefMessage hl7DefMessage = null;

            for (int i = 0; i < hl7Def.hl7DefMessages.Count; i++)
            {
                if (hl7Def.hl7DefMessages[i].MessageType == MessageTypeHL7.SIU && hl7Def.hl7DefMessages[i].InOrOut == InOutHL7.Outgoing)
                {
                    hl7DefMessage = hl7Def.hl7DefMessages[i];
                    //continue;
                    break;
                }
            }
            if (hl7DefMessage == null)           //SIU message type is not defined so do nothing and return
            {
                return(null);
            }
            if (apt == null)           //SIU messages must have an appointment
            {
                return(null);
            }
            if (PrefC.GetBool(PrefName.ShowFeaturePatientClone))
            {
                pat = Patients.GetOriginalPatientForClone(pat);
            }
            MessageHL7 messageHL7 = new MessageHL7(MessageTypeHL7.SIU);
            Provider   prov       = Providers.GetProv(apt.ProvNum);

            for (int i = 0; i < hl7DefMessage.hl7DefSegments.Count; i++)
            {
                int repeatCount = 1;
                //AIP segment can repeat, once for the dentist on the appt and once for the hygienist
                if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.AIP && apt.ProvHyg > 0)
                {
                    repeatCount = 2;
                }
                for (int j = 0; j < repeatCount; j++)           //AIP will be repeated if there is a dentist and a hygienist on the appt
                {
                    if (j > 0)
                    {
                        prov = Providers.GetProv(apt.ProvHyg);
                        if (prov == null)
                        {
                            break;                            //shouldn't happen, apt.ProvHyg would have to be set to an invalid ProvNum on the appt, just in case
                        }
                    }
                    SegmentHL7 seg = new SegmentHL7(hl7DefMessage.hl7DefSegments[i].SegmentName);
                    seg.SetField(0, hl7DefMessage.hl7DefSegments[i].SegmentName.ToString());
                    for (int k = 0; k < hl7DefMessage.hl7DefSegments[i].hl7DefFields.Count; k++)
                    {
                        string fieldName = hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FieldName;
                        if (fieldName == "")                       //If fixed text instead of field name just add text to segment
                        {
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FixedText);
                        }
                        else
                        {
                            string fieldValue = FieldConstructor.GenerateFieldSIU(hl7Def, fieldName, pat, prov, guar, apt, j + 1, eventType, seg.Name);
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, fieldValue);
                        }
                    }
                    messageHL7.Segments.Add(seg);
                }
            }
            return(messageHL7);
        }
        ///<summary>Returns null if no HL7 def is enabled or no SRR is defined in the enabled def.  An SRR - Schedule Request Response message is sent when an SRM - Schedule Request Message is received.  The SRM is acknowledged just like any inbound message, but the SRR notifies the placer application that the requested modification took place.  Currently the only appointment modifications allowed are updating the appt note, setting the dentist and hygienist, updating the confirmation status, and changing the ClinicNum.  Setting the appointment status to Broken is also supported.</summary>
        public static MessageHL7 GenerateSRR(Patient pat, Appointment apt, EventTypeHL7 eventType, string controlId, bool isAck, string ackEvent)
        {
            HL7Def hl7Def = HL7Defs.GetOneDeepEnabled();

            if (hl7Def == null)
            {
                return(null);
            }
            //find an outbound SRR message in the def
            HL7DefMessage hl7DefMessage = null;

            for (int i = 0; i < hl7Def.hl7DefMessages.Count; i++)
            {
                if (hl7Def.hl7DefMessages[i].MessageType == MessageTypeHL7.SRR && hl7Def.hl7DefMessages[i].InOrOut == InOutHL7.Outgoing)
                {
                    hl7DefMessage = hl7Def.hl7DefMessages[i];
                    break;
                }
            }
            if (hl7DefMessage == null)           //SRR message type is not defined so do nothing and return
            {
                return(null);
            }
            if (PrefC.GetBool(PrefName.ShowFeaturePatientClone))
            {
                pat = Patients.GetOriginalPatientForClone(pat);
            }
            MessageHL7 msgHl7  = new MessageHL7(MessageTypeHL7.SRR);
            Provider   provPri = Providers.GetProv(apt.ProvNum);

            //go through each segment in the def
            for (int i = 0; i < hl7DefMessage.hl7DefSegments.Count; i++)
            {
                List <Provider> listProvs = new List <Provider>();
                listProvs.Add(provPri);
                if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.AIP && apt.ProvHyg > 0)
                {
                    listProvs.Add(Providers.GetProv(apt.ProvHyg));
                }
                for (int j = 0; j < listProvs.Count; j++)           //AIP will be repeated if there is a dentist and a hygienist on the appt
                {
                    Provider   prov = listProvs[j];
                    SegmentHL7 seg  = new SegmentHL7(hl7DefMessage.hl7DefSegments[i].SegmentName);
                    seg.SetField(0, hl7DefMessage.hl7DefSegments[i].SegmentName.ToString());
                    for (int k = 0; k < hl7DefMessage.hl7DefSegments[i].hl7DefFields.Count; k++)
                    {
                        string fieldName = hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FieldName;
                        if (fieldName == "")                       //If fixed text instead of field name just add text to segment
                        {
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FixedText);
                        }
                        else
                        {
                            string fieldValue = "";
                            if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.MSA)
                            {
                                fieldValue = FieldConstructor.GenerateFieldACK(hl7Def, fieldName, controlId, isAck, ackEvent);
                            }
                            else
                            {
                                fieldValue = FieldConstructor.GenerateFieldSRR(hl7Def, fieldName, pat, prov, apt, j + 1, eventType, seg.Name);
                            }
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, fieldValue);
                        }
                    }
                    msgHl7.Segments.Add(seg);
                }
            }
            return(msgHl7);
        }
Beispiel #40
0
        public static void ProcessMessage(MessageHL7 message, bool isVerboseLogging)
        {
            SegmentHL7 seg      = message.GetSegment(SegmentNameHL7.PID, true);
            long       patNum   = PIn.Long(seg.GetFieldFullText(2));
            Patient    pat      = Patients.GetPat(patNum);
            Patient    patOld   = null;
            bool       isNewPat = pat == null;

            if (isNewPat)
            {
                pat             = new Patient();
                pat.PatNum      = patNum;
                pat.Guarantor   = patNum;
                pat.PriProv     = PrefC.GetLong(PrefName.PracticeDefaultProv);
                pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType);
            }
            else
            {
                patOld = pat.Copy();
            }
            List <PatientRace> listPatRaces = new List <PatientRace>();

            EcwSegmentPID.ProcessPID(pat, seg, false, listPatRaces);         //IsStandalone=false because should never make it this far.
            //PV1-patient visit---------------------------
            //seg=message.GetSegment(SegmentName.PV1,false);
            //if(seg!=null) {
            //	SegmentPID.ProcessPV1(pat,seg);
            //}
            //SCH- Schedule Activity Information
            seg = message.GetSegment(SegmentNameHL7.SCH, true);
            //The documentation is wrong.  SCH.01 is not the appointment ID, but is instead a sequence# (always 1)
            long        aptNum   = PIn.Long(seg.GetFieldFullText(2));
            Appointment apt      = Appointments.GetOneApt(aptNum);
            Appointment aptOld   = null;
            bool        isNewApt = apt == null;

            if (isNewApt)
            {
                apt           = new Appointment();
                apt.AptNum    = aptNum;
                apt.PatNum    = pat.PatNum;
                apt.AptStatus = ApptStatus.Scheduled;
            }
            else
            {
                aptOld = apt.Copy();
            }
            if (apt.PatNum != pat.PatNum)
            {
                EventLog.WriteEntry("OpenDentHL7", "Appointment does not match patient: " + pat.FName + " " + pat.LName
                                    + ", apt.PatNum:" + apt.PatNum.ToString() + ", pat.PatNum:" + pat.PatNum.ToString()
                                    , EventLogEntryType.Error);
                return;                //we can't process this message because wrong patnum.
            }
            apt.Note = seg.GetFieldFullText(7);
            //apt.Pattern=ProcessDuration(seg.GetFieldFullText(9));
            //9 and 10 are not actually available, in spite of the documentation.
            //11-We need start time and stop time
            apt.AptDateTime = DateTimeParse(seg.GetFieldComponent(11, 3));
            DateTime stopTime = DateTimeParse(seg.GetFieldComponent(11, 4));

            apt.Pattern = ProcessPattern(apt.AptDateTime, stopTime);
            apt.ProvNum = pat.PriProv;          //just in case there's not AIG segment.
            //AIG is optional, but looks like the only way to get provider for the appt-----------
            //PV1 seems to frequently be sent instead of AIG.
            SegmentHL7 segAIG = message.GetSegment(SegmentNameHL7.AIG, false);
            SegmentHL7 segPV  = message.GetSegment(SegmentNameHL7.PV1, false);

            if (segAIG != null)
            {
                long provNum = EcwSegmentPID.ProvProcess(segAIG.GetField(3));
                if (provNum != 0)
                {
                    apt.ProvNum = provNum;
                    pat.PriProv = provNum;
                }
            }
            else if (segPV != null)
            {
                long provNum = EcwSegmentPID.ProvProcess(segPV.GetField(7));
                if (provNum != 0)
                {
                    apt.ProvNum = provNum;
                    pat.PriProv = provNum;
                }
            }
            //AIL,AIP seem to be optional, and I'm going to ignore them for now.
            if (pat.FName == "" || pat.LName == "")
            {
                EventLog.WriteEntry("OpenDentHL7", "Appointment not processed due to missing patient first or last name. PatNum:" + pat.PatNum.ToString()
                                    , EventLogEntryType.Information);
                return;                //this will also skip the appt insert.
            }
            if (isNewPat)
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName + ", PatNum:" + pat.PatNum.ToString()
                                        , EventLogEntryType.Information);
                }
                Patients.Insert(pat, true);
                SecurityLogs.MakeLogEntry(Permissions.PatientCreate, pat.PatNum, "Created from HL7 for eCW.", LogSources.HL7);
            }
            else
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Patients.Update(pat, patOld);
            }
            //had to move this reconcile here since we might not have a PatNum for new patients until after the insert
            PatientRaces.Reconcile(pat.PatNum, listPatRaces);
            if (isNewApt)
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Inserted appointment for: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Appointments.InsertIncludeAptNum(apt, true);
            }
            else
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Updated appointment for: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Appointments.Update(apt, aptOld);
            }
        }
Beispiel #41
0
		//public static void ProcessIN1() {
		//	return;
		//}

		public static void ProcessMSA(HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			int ackCodeOrder=0;
			int msgControlIdOrder=0;
			//find position of AckCode in segDef for MSA seg
			for(int i=0;i<segDef.hl7DefFields.Count;i++) {
				if(segDef.hl7DefFields[i].FieldName=="ackCode") {
					ackCodeOrder=segDef.hl7DefFields[i].OrdinalPos;
				}
				if(segDef.hl7DefFields[i].FieldName=="messageControlId") {
					msgControlIdOrder=segDef.hl7DefFields[i].OrdinalPos;
				}
			}
			if(ackCodeOrder==0) {//no ackCode defined for this def of MSA, do nothing with it?
				return;
			}
			if(msgControlIdOrder==0) {//no messageControlId defined for this def of MSA, do nothing with it?
				return;
			}
			//set msg.AckCode to value in position located in def of ackcode in seg
			msg.AckCode=seg.GetFieldComponent(ackCodeOrder).ToString();
			msg.ControlId=seg.GetFieldComponent(msgControlIdOrder).ToString();
		}
		public static void ProcessSeg(Patient pat,long aptNum,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) {
			switch(segDef.SegmentName) {
				case SegmentNameHL7.AIG:
					ProcessAIG(pat,aptNum,segDef,seg);
					return;
				case SegmentNameHL7.GT1:
					ProcessGT1(pat,segDef,seg);
					return;
				case SegmentNameHL7.IN1:
					//ProcessIN1();
					return;
				case SegmentNameHL7.MSA:
					ProcessMSA(segDef,seg,msg);
					return;
				case SegmentNameHL7.MSH:
					ProcessMSH(segDef,seg,msg);
					return;
				case SegmentNameHL7.PD1:
					//ProcessPD1();
					return;
				case SegmentNameHL7.PID:
					ProcessPID(pat,segDef,seg);
					return;
				case SegmentNameHL7.PV1:
					ProcessPV1(pat,aptNum,segDef,seg);
					return;
				case SegmentNameHL7.SCH:
					ProcessSCH(pat,segDef,seg);
					return;
				default:
					return;
			}
		}
Beispiel #43
0
        public static void ProcessMessage(MessageHL7 message, bool isStandalone, bool isVerboseLogging)
        {
            /*string triggerevent=message.Segments[0].GetFieldComponent(8,1);
             * switch(triggerevent) {
             *      case "A01"://Admit/Visit Information
             *
             *              break;
             *      case "A04"://New Patient Information
             *              ProcessNewPatient(message);
             *              break;
             *      case "A08"://Update Patient Information
             *
             *              break;
             *      case "A28"://Add Patient Information
             *
             *              break;
             *      case "A31"://Update Patient Information
             *
             *              break;
             * }*/
            //MSH-Ignore
            //EVN-Ignore
            //PID-------------------------------------
            SegmentHL7 seg    = message.GetSegment(SegmentNameHL7.PID, true);
            long       patNum = PIn.Long(seg.GetFieldFullText(2));
            Patient    pat    = null;

            if (isStandalone)
            {
                pat = Patients.GetPatByChartNumber(patNum.ToString());
                if (pat == null)
                {
                    //try to find the patient in question by using name and birthdate
                    string   lName        = seg.GetFieldComponent(5, 0);
                    string   fName        = seg.GetFieldComponent(5, 1);
                    DateTime birthdate    = EcwSegmentPID.DateParse(seg.GetFieldFullText(7));
                    long     patNumByName = Patients.GetPatNumByNameAndBirthday(lName, fName, birthdate);
                    if (patNumByName == 0)                   //patient does not exist in OD
                    //so pat will still be null, triggering creation of new patient further down.
                    {
                    }
                    else
                    {
                        pat             = Patients.GetPat(patNumByName);
                        pat.ChartNumber = patNum.ToString();                      //from now on, we will be able to find pat by chartNumber
                    }
                }
            }
            else
            {
                pat = Patients.GetPat(patNum);
            }
            Patient patOld   = null;
            bool    isNewPat = pat == null;

            if (isNewPat)
            {
                pat = new Patient();
                if (isStandalone)
                {
                    pat.ChartNumber = patNum.ToString();
                    //this line does not work if isStandalone, so moved to end
                    //pat.Guarantor=patNum;
                }
                else
                {
                    pat.PatNum    = patNum;
                    pat.Guarantor = patNum;
                }
                pat.PriProv     = PrefC.GetLong(PrefName.PracticeDefaultProv);
                pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType);
            }
            else
            {
                patOld = pat.Copy();
            }
            EcwSegmentPID.ProcessPID(pat, seg, isStandalone);
            //PV1-patient visit---------------------------
            //seg=message.GetSegment(SegmentName.PV1,false);
            //if(seg!=null) {//this seg is optional
            //	SegmentPID.ProcessPV1(pat,seg);
            //}
            //PD1-additional patient demographics------------
            //seg=message.GetSegment(SegmentName.PD1,false);
            //if(seg!=null) {//this seg is optional
            //	ProcessPD1(pat,seg);
            //}
            //GT1-Guarantor-------------------------------------
            seg = message.GetSegment(SegmentNameHL7.GT1, true);
            EcwSegmentPID.ProcessGT1(pat, seg, isStandalone);
            //IN1-Insurance-------------------------------------
            //List<SegmentHL7> segments=message.GetSegments(SegmentName.IN1);
            //for(int i=0;i<segments.Count;i++) {
            //	ProcessIN1(pat,seg);
            //}
            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);
                return;
            }
            if (isNewPat)
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Patients.Insert(pat, true);
                if (pat.Guarantor == 0)
                {
                    patOld        = pat.Copy();
                    pat.Guarantor = pat.PatNum;
                    Patients.Update(pat, patOld);
                }
            }
            else
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Patients.Update(pat, patOld);
            }
        }
Beispiel #44
0
		public static void ProcessMessage(MessageHL7 message,bool isStandalone,bool isVerboseLogging) {
			/*string triggerevent=message.Segments[0].GetFieldComponent(8,1);
			switch(triggerevent) {
				case "A01"://Admit/Visit Information

					break;
				case "A04"://New Patient Information
					ProcessNewPatient(message);
					break;
				case "A08"://Update Patient Information

					break;
				case "A28"://Add Patient Information

					break;
				case "A31"://Update Patient Information

					break;
			}*/
			//MSH-Ignore
			//EVN-Ignore
			//PID-------------------------------------
			SegmentHL7 seg=message.GetSegment(SegmentNameHL7.PID,true);
			long patNum=PIn.Long(seg.GetFieldFullText(2));
			Patient pat=null;
			if(isStandalone) {
				pat=Patients.GetPatByChartNumber(patNum.ToString());
				if(pat==null) {
					//try to find the patient in question by using name and birthdate
					string lName=seg.GetFieldComponent(5,0);
					string fName=seg.GetFieldComponent(5,1);
					DateTime birthdate=EcwSegmentPID.DateParse(seg.GetFieldFullText(7));
					long patNumByName=Patients.GetPatNumByNameAndBirthday(lName,fName,birthdate);
					if(patNumByName==0) {//patient does not exist in OD
						//so pat will still be null, triggering creation of new patient further down.
					}
					else {
						pat=Patients.GetPat(patNumByName);
						pat.ChartNumber=patNum.ToString();//from now on, we will be able to find pat by chartNumber
					}
				}
			}
			else {
				pat=Patients.GetPat(patNum);
			}
			Patient patOld=null;
			bool isNewPat = pat==null;
			if(isNewPat) {
				pat=new Patient();
				if(isStandalone) {
					pat.ChartNumber=patNum.ToString();
					//this line does not work if isStandalone, so moved to end
					//pat.Guarantor=patNum;
				}
				else {
					pat.PatNum=patNum;
					pat.Guarantor=patNum;
				}
				pat.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv);
				pat.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType);
			}
			else{
				patOld=pat.Copy();
			}
			EcwSegmentPID.ProcessPID(pat,seg,isStandalone);
			//PV1-patient visit---------------------------
			//seg=message.GetSegment(SegmentName.PV1,false);
			//if(seg!=null) {//this seg is optional
			//	SegmentPID.ProcessPV1(pat,seg);
			//}
			//PD1-additional patient demographics------------
			//seg=message.GetSegment(SegmentName.PD1,false);
			//if(seg!=null) {//this seg is optional
			//	ProcessPD1(pat,seg);
			//}
			//GT1-Guarantor-------------------------------------
			seg=message.GetSegment(SegmentNameHL7.GT1,true);
			EcwSegmentPID.ProcessGT1(pat,seg,isStandalone);
			//IN1-Insurance-------------------------------------
			//List<SegmentHL7> segments=message.GetSegments(SegmentName.IN1);
			//for(int i=0;i<segments.Count;i++) {
			//	ProcessIN1(pat,seg);
			//}
			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);
				return;
			}
			if(isNewPat) {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Inserted patient: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Patients.Insert(pat,true);
				if(pat.Guarantor==0) {
					patOld=pat.Copy();
					pat.Guarantor=pat.PatNum;
					Patients.Update(pat,patOld);
				}
			}
			else {
				if(isVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Updated patient: "+pat.FName+" "+pat.LName,EventLogEntryType.Information);
				}
				Patients.Update(pat,patOld);
			}				
		}
Beispiel #45
0
		private void ProcessMessageFile(string fullPath) {
			string msgtext="";
			int i=0;
			while(i<5) {
				try {
					msgtext=File.ReadAllText(fullPath);
					break;
				}
				catch {
				}
				Thread.Sleep(200);
				i++;
				if(i==5) {
					EventLog.WriteEntry("Could not read text from file due to file locking issues.",EventLogEntryType.Error);
					return;
				}
			}
			try {
				MessageHL7 msg=new MessageHL7(msgtext);//this creates an entire heirarchy of objects.
				MessageParser.Process(msg,IsVerboseLogging);
				if(IsVerboseLogging) {
					EventLog.WriteEntry("OpenDentHL7","Processed message "+msg.MsgType.ToString(),EventLogEntryType.Information);
				}
			}
			catch(Exception ex) {
				EventLog.WriteEntry(ex.Message+"\r\n"+ex.StackTrace,EventLogEntryType.Error);
				return;
			}
			try {
				File.Delete(fullPath);
			}
			catch(Exception ex) {
				EventLog.WriteEntry("Delete failed for "+fullPath+"\r\n"+ex.Message,EventLogEntryType.Error);
			}
		}
Beispiel #46
0
        public static void ProcessMessage(MessageHL7 message, bool isStandalone, bool isVerboseLogging)
        {
            /*string triggerevent=message.Segments[0].GetFieldComponent(8,1);
             * switch(triggerevent) {
             *      case "A01"://Admit/Visit Information
             *
             *              break;
             *      case "A04"://New Patient Information
             *              ProcessNewPatient(message);
             *              break;
             *      case "A08"://Update Patient Information
             *
             *              break;
             *      case "A28"://Add Patient Information
             *
             *              break;
             *      case "A31"://Update Patient Information
             *
             *              break;
             * }*/
            //MSH-Ignore
            //EVN-Ignore
            //PID-------------------------------------
            SegmentHL7 seg    = message.GetSegment(SegmentNameHL7.PID, true);
            long       patNum = PIn.Long(seg.GetFieldFullText(2));
            Patient    pat    = null;

            if (isStandalone)
            {
                pat = Patients.GetPatByChartNumber(patNum.ToString());
                if (pat == null)
                {
                    //try to find the patient in question by using name and birthdate
                    string   lName        = seg.GetFieldComponent(5, 0);
                    string   fName        = seg.GetFieldComponent(5, 1);
                    DateTime birthdate    = EcwSegmentPID.DateParse(seg.GetFieldFullText(7));
                    long     patNumByName = Patients.GetPatNumByNameAndBirthday(lName, fName, birthdate);
                    if (patNumByName == 0)                   //patient does not exist in OD
                    //so pat will still be null, triggering creation of new patient further down.
                    {
                    }
                    else
                    {
                        pat             = Patients.GetPat(patNumByName);
                        pat.ChartNumber = patNum.ToString();                      //from now on, we will be able to find pat by chartNumber
                    }
                }
            }
            else
            {
                pat = Patients.GetPat(patNum);
            }
            Patient patOld   = null;
            bool    isNewPat = pat == null;

            if (isNewPat)
            {
                pat = new Patient();
                if (isStandalone)
                {
                    pat.ChartNumber = patNum.ToString();
                    //this line does not work if isStandalone, so moved to end
                    //pat.Guarantor=patNum;
                }
                else
                {
                    pat.PatNum    = patNum;
                    pat.Guarantor = patNum;
                }
                pat.PriProv     = PrefC.GetLong(PrefName.PracticeDefaultProv);
                pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType);
            }
            else
            {
                patOld = pat.Copy();
            }
            List <PatientRace> listPatRaces = new List <PatientRace>();
            bool hasNoRaceInStandalone      = (isStandalone && (pat == null || pat.PatNum == 0));

            EcwSegmentPID.ProcessPID(pat, seg, isStandalone, listPatRaces);
            //PV1-patient visit---------------------------
            //seg=message.GetSegment(SegmentName.PV1,false);
            //if(seg!=null) {//this seg is optional
            //	SegmentPID.ProcessPV1(pat,seg);
            //}
            //PD1-additional patient demographics------------
            //seg=message.GetSegment(SegmentName.PD1,false);
            //if(seg!=null) {//this seg is optional
            //	ProcessPD1(pat,seg);
            //}
            //GT1-Guarantor-------------------------------------
            seg = message.GetSegment(SegmentNameHL7.GT1, true);
            EcwSegmentPID.ProcessGT1(pat, seg, isStandalone);
            //IN1-Insurance-------------------------------------
            //List<SegmentHL7> segments=message.GetSegments(SegmentName.IN1);
            //for(int i=0;i<segments.Count;i++) {
            //	ProcessIN1(pat,seg);
            //}
            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);
                return;
            }
            if (isNewPat)
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                pat.PatNum = Patients.Insert(pat, !isStandalone);             //use existing PK if not standalone, standalone will have PatNum=0, so set PatNum here
                SecurityLogs.MakeLogEntry(Permissions.PatientCreate, pat.PatNum, "Created from HL7 for eCW.", LogSources.HL7);
                if (hasNoRaceInStandalone)
                {
                    Patient patientRaceTemp = pat.Copy();                  //Make a deep copy so that we do not accidentally override something.
                    seg = message.GetSegment(SegmentNameHL7.PID, true);
                    //We have to process the PID again in order to correct the patient race.  Patient race(s) will automatically get inserted if needed.
                    EcwSegmentPID.ProcessPID(patientRaceTemp, seg, isStandalone, listPatRaces);
                }
                if (pat.Guarantor == 0)
                {
                    patOld        = pat.Copy();
                    pat.Guarantor = pat.PatNum;
                    Patients.Update(pat, patOld);
                }
            }
            else
            {
                if (isVerboseLogging)
                {
                    EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information);
                }
                Patients.Update(pat, patOld);
            }
            //had to move this reconcile here since we might not have a PatNum for new patients until after the insert
            PatientRaces.Reconcile(pat.PatNum, listPatRaces);
        }
Beispiel #47
0
		private void TimerCallbackSendTCP(Object stateInfo) {
			List<HL7Msg> list=HL7Msgs.GetOnePending();
			for(int i=0;i<list.Count;i++) {//Right now, there will only be 0 or 1 item in the list.
				string sendMsgControlId=HL7Msgs.GetControlId(list[i]);//could be empty string
				Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
				string[] strIpPort=HL7DefEnabled.OutgoingIpPort.Split(':');//this was already validated in the HL7DefEdit window.
				IPAddress ipaddress=IPAddress.Parse(strIpPort[0]);//already validated
				int port=int.Parse(strIpPort[1]);//already validated
				IPEndPoint endpoint=new IPEndPoint(ipaddress,port);
				try {
					socket.Connect(endpoint);
					if(!IsSendTCPConnected) {//Previous run failed to connect. This time connected so make log entry that connection was successful
						EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket connection failed to connect previously and was successful this time.",EventLogEntryType.Information);
						IsSendTCPConnected=true;
					}
				}
				catch(SocketException ex) {
					if(IsSendTCPConnected) {//Previous run connected fine, make log entry and set bool to false
						EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket connection failed to connect.\r\nException: "+ex.Message,EventLogEntryType.Warning);
						IsSendTCPConnected=false;
					}
					return;
				}
				string data=MLLP_START_CHAR+list[i].MsgText+MLLP_END_CHAR+MLLP_ENDMSG_CHAR;
				try {
					byte[] byteData=Encoding.ASCII.GetBytes(data);
					socket.Send(byteData);//this is a blocking call
					//For MLLP V2, do a blocking Receive here, along with a timeout.
					byte[] ackBuffer=new byte[256];//plenty big enough to receive the entire ack/nack response
					socket.ReceiveTimeout=5000;//5 second timeout. Database is polled every 6 seconds for a new message to send, 5 second wait for acknowledgment
					int byteCountReceived=socket.Receive(ackBuffer);//blocking Receive
					char[] chars=new char[byteCountReceived];
					Encoding.UTF8.GetDecoder().GetChars(ackBuffer,0,byteCountReceived,chars,0);
					StringBuilder strbAckMsg=new StringBuilder();
					strbAckMsg.Append(chars);
					if(strbAckMsg.Length>0 && strbAckMsg[0]!=MLLP_START_CHAR) {
						list[i].Note=list[i].Note+"Malformed acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Malformed acknowledgment.");
					}
					else if(strbAckMsg.Length>=3
						&& strbAckMsg[strbAckMsg.Length-1]==MLLP_ENDMSG_CHAR//last char is the endmsg char.
						&& strbAckMsg[strbAckMsg.Length-2]==MLLP_END_CHAR)//the second-to-the-last char is the end char.
					{
						//we have a complete message
						strbAckMsg.Remove(0,1);//strip off the start char
						strbAckMsg.Remove(strbAckMsg.Length-2,2);//strip off the end chars
					}
					else if(strbAckMsg.Length>=2//so that the next line won't crash
						&& strbAckMsg[strbAckMsg.Length-1]==MLLP_END_CHAR)//the last char is the end char.
					{
						//we will treat this as a complete message, because the endmsg char is optional.
						strbAckMsg.Remove(0,1);//strip off the start char
						strbAckMsg.Remove(strbFullMsg.Length-1,1);//strip off the end char
					}
					else {
						list[i].Note=list[i].Note+"Malformed acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Malformed acknowledgment.");
					}
					MessageHL7 ackMsg=new MessageHL7(strbAckMsg.ToString());
					try {
						MessageParser.ProcessAck(ackMsg,IsVerboseLogging);
					}
					catch(Exception ex) {
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nError processing acknowledgment.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Error processing acknowledgment.\r\n"+ex.Message);
					}
					if(ackMsg.AckCode=="" || ackMsg.ControlId=="") {
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nInvalid ACK message.  Attempt to resend.\r\n";
						HL7Msgs.Update(list[i]);
						throw new Exception("Invalid ACK message received.");
					}
					if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode=="AA") {//acknowledged received (Application acknowledgment: Accept)
						list[i].Note=list[i].Note+ackMsg.ToString()+"\r\nMessage ACK (acknowledgment) received.\r\n";
					}
					else if(ackMsg.ControlId==sendMsgControlId && ackMsg.AckCode!="AA") {//ACK received for this message, but ack code was not acknowledgment accepted
						if(list[i].Note.Contains("NACK4")) {//this is the 5th negative acknowledgment, don't try again
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nThis is NACK5, the message status has been changed to OutFailed. We will not attempt to send again.\r\n";
							list[i].HL7Status=HL7MessageStatus.OutFailed;
						}
						else if(list[i].Note.Contains("NACK")) {//failed sending at least once already
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nNACK"+list[i].Note.Split(new string[] { "NACK" },StringSplitOptions.None).Length+"\r\n";
						}
						else {
							list[i].Note=list[i].Note+"Ack code: "+ackMsg.AckCode+"\r\nMessage NACK (negative acknowlegment) received. We will try to send again.\r\n";
						}
						HL7Msgs.Update(list[i]);//Add NACK note to hl7msg table entry
						return;//socket closed in finally
					}
					else {//ack received for control ID that does not match the control ID of message just sent
						list[i].Note=list[i].Note+"Sent message control ID: "+sendMsgControlId+"\r\nAck message control ID: "+ackMsg.ControlId
							+"\r\nAck received for message other than message just sent.  We will try to send again.\r\n";
						HL7Msgs.Update(list[i]);
						return;
					}
				}
				catch(SocketException ex) {
					EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP socket encountered an error when sending message or receiving acknowledgment.\r\nSocket Exception: "+ex.Message,EventLogEntryType.Warning);
					return;//Try again in 3 seconds. socket closed in finally
				}
				catch(Exception ex) {
					EventLog.WriteEntry("OpenDentHL7","The HL7 send TCP/IP encountered an error when sending message or receiving acknowledgment.\r\nException: "+ex.Message,EventLogEntryType.Warning);
					return;//socket closed in finally
				}
				finally {
					if(socket!=null) {
						socket.Shutdown(SocketShutdown.Both);
						socket.Close();
					}
				}
				list[i].HL7Status=HL7MessageStatus.OutSent;
				HL7Msgs.Update(list[i]);//set the status to sent and save ack message in Note field.
				HL7Msgs.DeleteOldMsgText();//This is inside the loop so that it happens less frequently.  To clean up incoming messages, we may move this someday.
			}
		}
        ///<summary>Returns null if there is no HL7Def enabled or if there is no outbound ADT defined for the enabled HL7Def.</summary>
        public static MessageHL7 GenerateADT(Patient pat, Patient guar, EventTypeHL7 eventType)
        {
            HL7Def hl7Def = HL7Defs.GetOneDeepEnabled();

            if (hl7Def == null)
            {
                return(null);
            }
            //find an outbound ADT message in the def
            HL7DefMessage hl7DefMessage = null;

            for (int i = 0; i < hl7Def.hl7DefMessages.Count; i++)
            {
                if (hl7Def.hl7DefMessages[i].MessageType == MessageTypeHL7.ADT && hl7Def.hl7DefMessages[i].InOrOut == InOutHL7.Outgoing)
                {
                    hl7DefMessage = hl7Def.hl7DefMessages[i];
                    //continue;
                    break;
                }
            }
            if (hl7DefMessage == null)           //ADT message type is not defined so do nothing and return
            {
                return(null);
            }
            if (PrefC.GetBool(PrefName.ShowFeaturePatientClone))
            {
                pat = Patients.GetOriginalPatientForClone(pat);
            }
            MessageHL7     messageHL7   = new MessageHL7(MessageTypeHL7.ADT);
            Provider       prov         = Providers.GetProv(Patients.GetProvNum(pat));
            List <PatPlan> listPatPlans = PatPlans.Refresh(pat.PatNum);

            for (int i = 0; i < hl7DefMessage.hl7DefSegments.Count; i++)
            {
                int countRepeat = 1;
                //IN1 segment can repeat, get the number of current insurance plans attached to the patient
                if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)
                {
                    countRepeat = listPatPlans.Count;
                }
                //countRepeat is usually 1, but for repeatable/optional fields, it may be 0 or greater than 1
                //for example, countRepeat can be zero if the patient does not have any current insplans, in which case no IN1 segments will be included
                for (int j = 0; j < countRepeat; j++)           //IN1 is optional and can repeat so add as many as listPatplans
                {
                    PatPlan patplanCur = null;
                    InsPlan insplanCur = null;
                    InsSub  inssubCur  = null;
                    Carrier carrierCur = null;
                    Patient patSub     = null;
                    if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)                   //index repeat is guaranteed to be less than listPatplans.Count
                    {
                        patplanCur = listPatPlans[j];
                        inssubCur  = InsSubs.GetOne(patplanCur.InsSubNum);
                        insplanCur = InsPlans.RefreshOne(inssubCur.PlanNum);
                        carrierCur = Carriers.GetCarrier(insplanCur.CarrierNum);
                        if (pat.PatNum == inssubCur.Subscriber)
                        {
                            patSub = pat.Copy();
                        }
                        else
                        {
                            patSub = Patients.GetPat(inssubCur.Subscriber);
                        }
                    }
                    SegmentHL7 seg = new SegmentHL7(hl7DefMessage.hl7DefSegments[i].SegmentName);
                    seg.SetField(0, hl7DefMessage.hl7DefSegments[i].SegmentName.ToString());
                    for (int k = 0; k < hl7DefMessage.hl7DefSegments[i].hl7DefFields.Count; k++)
                    {
                        string fieldName = hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FieldName;
                        if (fieldName == "")                       //If fixed text instead of field name just add text to segment
                        {
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].FixedText);
                        }
                        else
                        {
                            string fieldValue = "";
                            if (hl7DefMessage.hl7DefSegments[i].SegmentName == SegmentNameHL7.IN1)
                            {
                                fieldValue = FieldConstructor.GenerateFieldIN1(hl7Def, fieldName, j + 1, patplanCur, inssubCur, insplanCur, carrierCur, listPatPlans.Count, patSub);
                            }
                            else
                            {
                                fieldValue = FieldConstructor.GenerateFieldADT(hl7Def, fieldName, pat, prov, guar, j + 1, eventType, seg.Name);
                            }
                            seg.SetField(hl7DefMessage.hl7DefSegments[i].hl7DefFields[k].OrdinalPos, fieldValue);
                        }
                    }
                    messageHL7.Segments.Add(seg);
                }
            }
            return(messageHL7);
        }