Beispiel #1
0
 ///<summary>Inserts one HL7Msg into the database.  Returns the new priKey.</summary>
 internal static long Insert(HL7Msg hL7Msg)
 {
     if(DataConnection.DBtype==DatabaseType.Oracle) {
         hL7Msg.HL7MsgNum=DbHelper.GetNextOracleKey("hl7msg","HL7MsgNum");
         int loopcount=0;
         while(loopcount<100){
             try {
                 return Insert(hL7Msg,true);
             }
             catch(Oracle.DataAccess.Client.OracleException ex){
                 if(ex.Number==1 && ex.Message.ToLower().Contains("unique constraint") && ex.Message.ToLower().Contains("violated")){
                     hL7Msg.HL7MsgNum++;
                     loopcount++;
                 }
                 else{
                     throw ex;
                 }
             }
         }
         throw new ApplicationException("Insert failed.  Could not generate primary key.");
     }
     else {
         return Insert(hL7Msg,false);
     }
 }
Beispiel #2
0
 ///<summary>Inserts one HL7Msg into the database.  Provides option to use the existing priKey.</summary>
 internal static long Insert(HL7Msg hL7Msg,bool useExistingPK)
 {
     if(!useExistingPK && PrefC.RandomKeys) {
         hL7Msg.HL7MsgNum=ReplicationServers.GetKey("hl7msg","HL7MsgNum");
     }
     string command="INSERT INTO hl7msg (";
     if(useExistingPK || PrefC.RandomKeys) {
         command+="HL7MsgNum,";
     }
     command+="HL7Status,MsgText,AptNum) VALUES(";
     if(useExistingPK || PrefC.RandomKeys) {
         command+=POut.Long(hL7Msg.HL7MsgNum)+",";
     }
     command+=
              POut.Int   ((int)hL7Msg.HL7Status)+","
         +DbHelper.ParamChar+"paramMsgText,"
         +    POut.Long  (hL7Msg.AptNum)+")";
     if(hL7Msg.MsgText==null) {
         hL7Msg.MsgText="";
     }
     OdSqlParameter paramMsgText=new OdSqlParameter("paramMsgText",OdDbType.Text,hL7Msg.MsgText);
     if(useExistingPK || PrefC.RandomKeys) {
         Db.NonQ(command,paramMsgText);
     }
     else {
         hL7Msg.HL7MsgNum=Db.NonQ(command,true,paramMsgText);
     }
     return hL7Msg.HL7MsgNum;
 }
Beispiel #3
0
        //OD accepts commandline arguments from eCW.  That's handled in FormOpenDental.

        //public static void SendHL7(Appointment apt,Patient pat) {
        //  OpenDentBusiness.HL7.DFT dft=new OpenDentBusiness.HL7.DFT(apt,pat);
        //  HL7Msg msg=new HL7Msg();
        //  msg.AptNum=apt.AptNum;
        //  msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
        //  msg.MsgText=dft.GenerateMessage();
        //  HL7Msgs.Insert(msg);
        //}

        public static void SendHL7(long aptNum, long provNum, Patient pat, string pdfDataBase64, string pdfDescription, bool justPDF, List <Procedure> listProcs)
        {
            OpenDentBusiness.HL7.EcwDFT dft = new OpenDentBusiness.HL7.EcwDFT();
            dft.InitializeEcw(aptNum, provNum, pat, pdfDataBase64, pdfDescription, justPDF, listProcs);
            HL7Msg msg = new HL7Msg();

            if (justPDF)
            {
                msg.AptNum = 0;              //Prevents the appt complete button from changing to the "Revise" button prematurely.
            }
            else
            {
                msg.AptNum = aptNum;
            }
            msg.HL7Status = HL7MessageStatus.OutPending;          //it will be marked outSent by the HL7 service.
            msg.MsgText   = dft.GenerateMessage();
            msg.PatNum    = pat.PatNum;
            HL7ProcAttach hl7ProcAttach = new HL7ProcAttach();

            hl7ProcAttach.HL7MsgNum = HL7Msgs.Insert(msg);
            if (listProcs != null)
            {
                foreach (Procedure proc in listProcs)
                {
                    hl7ProcAttach.ProcNum = proc.ProcNum;
                    HL7ProcAttaches.Insert(hl7ProcAttach);
                }
            }
        }
Beispiel #4
0
 ///<summary>Returns true if Update(HL7Msg,HL7Msg) would make changes to the database.
 ///Does not make any changes to the database and can be called before remoting role is checked.</summary>
 public static bool UpdateComparison(HL7Msg hL7Msg, HL7Msg oldHL7Msg)
 {
     if (hL7Msg.HL7Status != oldHL7Msg.HL7Status)
     {
         return(true);
     }
     if (hL7Msg.MsgText != oldHL7Msg.MsgText)
     {
         return(true);
     }
     if (hL7Msg.AptNum != oldHL7Msg.AptNum)
     {
         return(true);
     }
     //DateTStamp can only be set by MySQL
     if (hL7Msg.PatNum != oldHL7Msg.PatNum)
     {
         return(true);
     }
     if (hL7Msg.Note != oldHL7Msg.Note)
     {
         return(true);
     }
     return(false);
 }
Beispiel #5
0
 ///<summary>Inserts one HL7Msg into the database.  Returns the new priKey.</summary>
 public static long Insert(HL7Msg hL7Msg)
 {
     if (DataConnection.DBtype == DatabaseType.Oracle)
     {
         hL7Msg.HL7MsgNum = DbHelper.GetNextOracleKey("hl7msg", "HL7MsgNum");
         int loopcount = 0;
         while (loopcount < 100)
         {
             try {
                 return(Insert(hL7Msg, true));
             }
             catch (Oracle.DataAccess.Client.OracleException ex) {
                 if (ex.Number == 1 && ex.Message.ToLower().Contains("unique constraint") && ex.Message.ToLower().Contains("violated"))
                 {
                     hL7Msg.HL7MsgNum++;
                     loopcount++;
                 }
                 else
                 {
                     throw ex;
                 }
             }
         }
         throw new ApplicationException("Insert failed.  Could not generate primary key.");
     }
     else
     {
         return(Insert(hL7Msg, false));
     }
 }
Beispiel #6
0
        ///<summary>Updates one HL7Msg in the database.</summary>
        public static void Update(HL7Msg hL7Msg)
        {
            string command = "UPDATE hl7msg SET "
                             + "HL7Status =  " + POut.Int((int)hL7Msg.HL7Status) + ", "
                             + "MsgText   =  " + DbHelper.ParamChar + "paramMsgText, "
                             + "AptNum    =  " + POut.Long(hL7Msg.AptNum) + ", "
                             //DateTStamp can only be set by MySQL
                             + "PatNum    =  " + POut.Long(hL7Msg.PatNum) + ", "
                             + "Note      =  " + DbHelper.ParamChar + "paramNote "
                             + "WHERE HL7MsgNum = " + POut.Long(hL7Msg.HL7MsgNum);

            if (hL7Msg.MsgText == null)
            {
                hL7Msg.MsgText = "";
            }
            OdSqlParameter paramMsgText = new OdSqlParameter("paramMsgText", OdDbType.Text, POut.StringParam(hL7Msg.MsgText));

            if (hL7Msg.Note == null)
            {
                hL7Msg.Note = "";
            }
            OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, POut.StringParam(hL7Msg.Note));

            Db.NonQ(command, paramMsgText, paramNote);
        }
Beispiel #7
0
        ///<summary>Updates one HL7Msg in the database.  Uses an old object to compare to, and only alters changed fields.  This prevents collisions and concurrency problems in heavily used tables.</summary>
        public static void Update(HL7Msg hL7Msg, HL7Msg oldHL7Msg)
        {
            string command = "";

            if (hL7Msg.HL7Status != oldHL7Msg.HL7Status)
            {
                if (command != "")
                {
                    command += ",";
                }
                command += "HL7Status = " + POut.Int((int)hL7Msg.HL7Status) + "";
            }
            if (hL7Msg.MsgText != oldHL7Msg.MsgText)
            {
                if (command != "")
                {
                    command += ",";
                }
                command += "MsgText = " + DbHelper.ParamChar + "paramMsgText";
            }
            if (hL7Msg.AptNum != oldHL7Msg.AptNum)
            {
                if (command != "")
                {
                    command += ",";
                }
                command += "AptNum = " + POut.Long(hL7Msg.AptNum) + "";
            }
            //DateTStamp can only be set by MySQL
            if (hL7Msg.PatNum != oldHL7Msg.PatNum)
            {
                if (command != "")
                {
                    command += ",";
                }
                command += "PatNum = " + POut.Long(hL7Msg.PatNum) + "";
            }
            if (hL7Msg.Note != oldHL7Msg.Note)
            {
                if (command != "")
                {
                    command += ",";
                }
                command += "Note = '" + POut.String(hL7Msg.Note) + "'";
            }
            if (command == "")
            {
                return;
            }
            if (hL7Msg.MsgText == null)
            {
                hL7Msg.MsgText = "";
            }
            OdSqlParameter paramMsgText = new OdSqlParameter("paramMsgText", OdDbType.Text, hL7Msg.MsgText);

            command = "UPDATE hl7msg SET " + command
                      + " WHERE HL7MsgNum = " + POut.Long(hL7Msg.HL7MsgNum);
            Db.NonQ(command, paramMsgText);
        }
Beispiel #8
0
        /// <summary>Helper method to send given appt to the unscheduled list.
        /// Creates SecurityLogs and considers HL7.</summary>
        public static void SetApptUnschedHelper(Appointment appt, Patient pat = null, bool doFireApptEvent = true)
        {
            DateTime datePrevious = appt.DateTStamp;

            Appointments.SetAptStatus(appt, ApptStatus.UnschedList); //Appointments S-Class handles Signalods
            #region SecurityLogs
            if (appt.AptStatus != ApptStatus.Complete)               //seperate log entry for editing completed appts.
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentMove, appt.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString() + ", Sent to Unscheduled List",
                                          appt.AptNum, datePrevious);
            }
            else
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentCompleteEdit, appt.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString() + ", Sent to Unscheduled List",
                                          appt.AptNum, datePrevious);
            }
            #endregion
            #region HL7
            //If there is an existing HL7 def enabled, send a SIU message if there is an outbound SIU message defined
            if (HL7Defs.IsExistingHL7Enabled())
            {
                if (pat == null)
                {
                    pat = Patients.GetPat(appt.PatNum);
                }
                //S15 - Appt Cancellation event
                MessageHL7 messageHL7 = MessageConstructor.GenerateSIU(pat, Patients.GetPat(pat.Guarantor), EventTypeHL7.S15, appt);
                //Will be null if there is no outbound SIU message defined, so do nothing
                if (messageHL7 != null)
                {
                    HL7Msg hl7Msg = new HL7Msg();
                    hl7Msg.AptNum    = appt.AptNum;
                    hl7Msg.HL7Status = HL7MessageStatus.OutPending;                  //it will be marked outSent by the HL7 service.
                    hl7Msg.MsgText   = messageHL7.ToString();
                    hl7Msg.PatNum    = pat.PatNum;
                    HL7Msgs.Insert(hl7Msg);
#if DEBUG
                    MessageBox.Show("Appointments", messageHL7.ToString());
#endif
                }
            }
            #endregion
            if (doFireApptEvent)
            {
                AppointmentEvent.Fire(ODEventType.AppointmentEdited, appt);
            }
            Recalls.SynchScheduledApptFull(appt.PatNum);
        }
Beispiel #9
0
 ///<summary>Inserts one HL7Msg into the database.  Returns the new priKey.  Doesn't use the cache.</summary>
 public static long InsertNoCache(HL7Msg hL7Msg)
 {
     if (DataConnection.DBtype == DatabaseType.MySql)
     {
         return(InsertNoCache(hL7Msg, false));
     }
     else
     {
         if (DataConnection.DBtype == DatabaseType.Oracle)
         {
             hL7Msg.HL7MsgNum = DbHelper.GetNextOracleKey("hl7msg", "HL7MsgNum");                  //Cacheless method
         }
         return(InsertNoCache(hL7Msg, true));
     }
 }
Beispiel #10
0
        ///<summary>Updates one HL7Msg in the database.</summary>
        internal static void Update(HL7Msg hL7Msg)
        {
            string command = "UPDATE hl7msg SET "
                             + "HL7Status=  " + POut.Int((int)hL7Msg.HL7Status) + ", "
                             + "MsgText  =  " + DbHelper.ParamChar + "paramMsgText, "
                             + "AptNum   =  " + POut.Long(hL7Msg.AptNum) + " "
                             + "WHERE HL7MsgNum = " + POut.Long(hL7Msg.HL7MsgNum);

            if (hL7Msg.MsgText == null)
            {
                hL7Msg.MsgText = "";
            }
            OdSqlParameter paramMsgText = new OdSqlParameter("paramMsgText", OdDbType.Text, hL7Msg.MsgText);

            Db.NonQ(command, paramMsgText);
        }
Beispiel #11
0
		///<summary>Converts a DataTable to a list of objects.</summary>
		public static List<HL7Msg> TableToList(DataTable table){
			List<HL7Msg> retVal=new List<HL7Msg>();
			HL7Msg hL7Msg;
			for(int i=0;i<table.Rows.Count;i++) {
				hL7Msg=new HL7Msg();
				hL7Msg.HL7MsgNum = PIn.Long  (table.Rows[i]["HL7MsgNum"].ToString());
				hL7Msg.HL7Status = (OpenDentBusiness.HL7MessageStatus)PIn.Int(table.Rows[i]["HL7Status"].ToString());
				hL7Msg.MsgText   = PIn.String(table.Rows[i]["MsgText"].ToString());
				hL7Msg.AptNum    = PIn.Long  (table.Rows[i]["AptNum"].ToString());
				hL7Msg.DateTStamp= PIn.DateT (table.Rows[i]["DateTStamp"].ToString());
				hL7Msg.PatNum    = PIn.Long  (table.Rows[i]["PatNum"].ToString());
				hL7Msg.Note      = PIn.String(table.Rows[i]["Note"].ToString());
				retVal.Add(hL7Msg);
			}
			return retVal;
		}
Beispiel #12
0
        ///<summary>Converts a DataTable to a list of objects.</summary>
        internal static List <HL7Msg> TableToList(DataTable table)
        {
            List <HL7Msg> retVal = new List <HL7Msg>();
            HL7Msg        hL7Msg;

            for (int i = 0; i < table.Rows.Count; i++)
            {
                hL7Msg           = new HL7Msg();
                hL7Msg.HL7MsgNum = PIn.Long(table.Rows[i]["HL7MsgNum"].ToString());
                hL7Msg.HL7Status = (HL7MessageStatus)PIn.Int(table.Rows[i]["HL7Status"].ToString());
                hL7Msg.MsgText   = PIn.String(table.Rows[i]["MsgText"].ToString());
                hL7Msg.AptNum    = PIn.Long(table.Rows[i]["AptNum"].ToString());
                retVal.Add(hL7Msg);
            }
            return(retVal);
        }
Beispiel #13
0
        ///<summary>Inserts one HL7Msg into the database.  Provides option to use the existing priKey.  Doesn't use the cache.</summary>
        public static long InsertNoCache(HL7Msg hL7Msg, bool useExistingPK)
        {
            bool   isRandomKeys = Prefs.GetBoolNoCache(PrefName.RandomPrimaryKeys);
            string command      = "INSERT INTO hl7msg (";

            if (!useExistingPK && isRandomKeys)
            {
                hL7Msg.HL7MsgNum = ReplicationServers.GetKeyNoCache("hl7msg", "HL7MsgNum");
            }
            if (isRandomKeys || useExistingPK)
            {
                command += "HL7MsgNum,";
            }
            command += "HL7Status,MsgText,AptNum,PatNum,Note) VALUES(";
            if (isRandomKeys || useExistingPK)
            {
                command += POut.Long(hL7Msg.HL7MsgNum) + ",";
            }
            command +=
                POut.Int((int)hL7Msg.HL7Status) + ","
                + DbHelper.ParamChar + "paramMsgText,"
                + POut.Long(hL7Msg.AptNum) + ","
                //DateTStamp can only be set by MySQL
                + POut.Long(hL7Msg.PatNum) + ","
                + DbHelper.ParamChar + "paramNote)";
            if (hL7Msg.MsgText == null)
            {
                hL7Msg.MsgText = "";
            }
            OdSqlParameter paramMsgText = new OdSqlParameter("paramMsgText", OdDbType.Text, POut.StringParam(hL7Msg.MsgText));

            if (hL7Msg.Note == null)
            {
                hL7Msg.Note = "";
            }
            OdSqlParameter paramNote = new OdSqlParameter("paramNote", OdDbType.Text, POut.StringParam(hL7Msg.Note));

            if (useExistingPK || isRandomKeys)
            {
                Db.NonQ(command, paramMsgText, paramNote);
            }
            else
            {
                hL7Msg.HL7MsgNum = Db.NonQ(command, true, "HL7MsgNum", "hL7Msg", paramMsgText, paramNote);
            }
            return(hL7Msg.HL7MsgNum);
        }
Beispiel #14
0
        //OD accepts commandline arguments from eCW.  That's handled in FormOpenDental.

        //public static void SendHL7(Appointment apt,Patient pat) {
        //  OpenDentBusiness.HL7.DFT dft=new OpenDentBusiness.HL7.DFT(apt,pat);
        //  HL7Msg msg=new HL7Msg();
        //  msg.AptNum=apt.AptNum;
        //  msg.HL7Status=HL7MessageStatus.OutPending;//it will be marked outSent by the HL7 service.
        //  msg.MsgText=dft.GenerateMessage();
        //  HL7Msgs.Insert(msg);
        //}

        public static void SendHL7(long aptNum, long provNum, Patient pat, string pdfDataBase64, string pdfDescription, bool justPDF)
        {
            OpenDentBusiness.HL7.DFT dft = new OpenDentBusiness.HL7.DFT();
            dft.InitializeEcw(aptNum, provNum, pat, pdfDataBase64, pdfDescription, justPDF);
            HL7Msg msg = new HL7Msg();

            if (justPDF)
            {
                msg.AptNum = 0;              //Prevents the appt complete button from changing to the "Revise" button prematurely.
            }
            else
            {
                msg.AptNum = aptNum;
            }
            msg.HL7Status = HL7MessageStatus.OutPending;          //it will be marked outSent by the HL7 service.
            msg.MsgText   = dft.GenerateMessage();
            HL7Msgs.Insert(msg);
        }
Beispiel #15
0
        ///<summary>Converts a DataTable to a list of objects.</summary>
        public static List <HL7Msg> TableToList(DataTable table)
        {
            List <HL7Msg> retVal = new List <HL7Msg>();
            HL7Msg        hL7Msg;

            foreach (DataRow row in table.Rows)
            {
                hL7Msg            = new HL7Msg();
                hL7Msg.HL7MsgNum  = PIn.Long(row["HL7MsgNum"].ToString());
                hL7Msg.HL7Status  = (OpenDentBusiness.HL7MessageStatus)PIn.Int(row["HL7Status"].ToString());
                hL7Msg.MsgText    = PIn.String(row["MsgText"].ToString());
                hL7Msg.AptNum     = PIn.Long(row["AptNum"].ToString());
                hL7Msg.DateTStamp = PIn.DateT(row["DateTStamp"].ToString());
                hL7Msg.PatNum     = PIn.Long(row["PatNum"].ToString());
                hL7Msg.Note       = PIn.String(row["Note"].ToString());
                retVal.Add(hL7Msg);
            }
            return(retVal);
        }
Beispiel #16
0
        ///<summary>Inserts one HL7Msg into the database.  Provides option to use the existing priKey.</summary>
        public static long Insert(HL7Msg hL7Msg, bool useExistingPK)
        {
            if (!useExistingPK && PrefC.RandomKeys)
            {
                hL7Msg.HL7MsgNum = ReplicationServers.GetKey("hl7msg", "HL7MsgNum");
            }
            string command = "INSERT INTO hl7msg (";

            if (useExistingPK || PrefC.RandomKeys)
            {
                command += "HL7MsgNum,";
            }
            command += "HL7Status,MsgText,AptNum,PatNum,Note) VALUES(";
            if (useExistingPK || PrefC.RandomKeys)
            {
                command += POut.Long(hL7Msg.HL7MsgNum) + ",";
            }
            command +=
                POut.Int((int)hL7Msg.HL7Status) + ","
                + DbHelper.ParamChar + "paramMsgText,"
                + POut.Long(hL7Msg.AptNum) + ","
                //DateTStamp can only be set by MySQL
                + POut.Long(hL7Msg.PatNum) + ","
                + "'" + POut.String(hL7Msg.Note) + "')";
            if (hL7Msg.MsgText == null)
            {
                hL7Msg.MsgText = "";
            }
            OdSqlParameter paramMsgText = new OdSqlParameter("paramMsgText", OdDbType.Text, hL7Msg.MsgText);

            if (useExistingPK || PrefC.RandomKeys)
            {
                Db.NonQ(command, paramMsgText);
            }
            else
            {
                hL7Msg.HL7MsgNum = Db.NonQ(command, true, paramMsgText);
            }
            return(hL7Msg.HL7MsgNum);
        }
Beispiel #17
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 #18
0
 ///<summary>Converts a DataTable to a list of objects.</summary>
 internal static List<HL7Msg> TableToList(DataTable table)
 {
     List<HL7Msg> retVal=new List<HL7Msg>();
     HL7Msg hL7Msg;
     for(int i=0;i<table.Rows.Count;i++) {
         hL7Msg=new HL7Msg();
         hL7Msg.HL7MsgNum= PIn.Long  (table.Rows[i]["HL7MsgNum"].ToString());
         hL7Msg.HL7Status= (HL7MessageStatus)PIn.Int(table.Rows[i]["HL7Status"].ToString());
         hL7Msg.MsgText  = PIn.String(table.Rows[i]["MsgText"].ToString());
         hL7Msg.AptNum   = PIn.Long  (table.Rows[i]["AptNum"].ToString());
         retVal.Add(hL7Msg);
     }
     return retVal;
 }
Beispiel #19
0
        ///<summary>Sets given appt.AptStatus to broken.
        ///Provide procCode that should be charted, can be null but will not chart a broken procedure.
        ///Also considers various broken procedure based prefs.
        ///Makes its own securitylog entries.</summary>
        public static void BreakApptHelper(Appointment appt, Patient pat, ProcedureCode procCode)
        {
            //suppressHistory is true due to below logic creating a log with a specific HistAppointmentAction instead of the generic changed.
            DateTime datePrevious    = appt.DateTStamp;
            bool     suppressHistory = false;

            if (procCode != null)
            {
                suppressHistory = (procCode.ProcCode.In("D9986", "D9987"));
            }
            Appointments.SetAptStatus(appt, ApptStatus.Broken, suppressHistory); //Appointments S-Class handles Signalods
            if (appt.AptStatus != ApptStatus.Complete)                           //seperate log entry for completed appointments.
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            else
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentCompleteEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            #region HL7
            //If there is an existing HL7 def enabled, send a SIU message if there is an outbound SIU message defined
            if (HL7Defs.IsExistingHL7Enabled())
            {
                //S15 - Appt Cancellation event
                MessageHL7 messageHL7 = MessageConstructor.GenerateSIU(pat, Patients.GetPat(pat.Guarantor), EventTypeHL7.S15, appt);
                //Will be null if there is no outbound SIU message defined, so do nothing
                if (messageHL7 != null)
                {
                    HL7Msg hl7Msg = new HL7Msg();
                    hl7Msg.AptNum    = appt.AptNum;
                    hl7Msg.HL7Status = HL7MessageStatus.OutPending;                  //it will be marked outSent by the HL7 service.
                    hl7Msg.MsgText   = messageHL7.ToString();
                    hl7Msg.PatNum    = pat.PatNum;
                    HL7Msgs.Insert(hl7Msg);
#if DEBUG
                    MessageBox.Show("Appointments", messageHL7.ToString());
#endif
                }
            }
            #endregion
            #region Charting the proc
            if (procCode != null)
            {
                switch (procCode.ProcCode)
                {
                case "D9986":                        //Missed
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Missed);
                    break;

                case "D9987":                        //Cancelled
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Cancelled);
                    break;
                }
                Procedure procedureCur = new Procedure();
                procedureCur.PatNum       = pat.PatNum;
                procedureCur.ProvNum      = (procCode.ProvNumDefault > 0 ? procCode.ProvNumDefault : appt.ProvNum);
                procedureCur.CodeNum      = procCode.CodeNum;
                procedureCur.ProcDate     = DateTime.Today;
                procedureCur.DateEntryC   = DateTime.Now;
                procedureCur.ProcStatus   = ProcStat.C;
                procedureCur.ClinicNum    = appt.ClinicNum;
                procedureCur.UserNum      = Security.CurUser.UserNum;
                procedureCur.Note         = Lans.g("AppointmentEdit", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                procedureCur.PlaceService = (PlaceOfService)PrefC.GetInt(PrefName.DefaultProcedurePlaceService);              //Default proc place of service for the Practice is used.
                List <InsSub>  listInsSubs    = InsSubs.RefreshForFam(Patients.GetFamily(pat.PatNum));
                List <InsPlan> listInsPlans   = InsPlans.RefreshForSubList(listInsSubs);
                List <PatPlan> listPatPlans   = PatPlans.Refresh(pat.PatNum);
                InsPlan        insPlanPrimary = null;
                InsSub         insSubPrimary  = null;
                if (listPatPlans.Count > 0)
                {
                    insSubPrimary  = InsSubs.GetSub(listPatPlans[0].InsSubNum, listInsSubs);
                    insPlanPrimary = InsPlans.GetPlan(insSubPrimary.PlanNum, listInsPlans);
                }
                double procFee;
                long   feeSch;
                if (insPlanPrimary == null || procCode.NoBillIns)
                {
                    feeSch = FeeScheds.GetFeeSched(0, pat.FeeSched, procedureCur.ProvNum);
                }
                else                  //Only take into account the patient's insurance fee schedule if the D9986 procedure is not marked as NoBillIns
                {
                    feeSch = FeeScheds.GetFeeSched(insPlanPrimary.FeeSched, pat.FeeSched, procedureCur.ProvNum);
                }
                procFee = Fees.GetAmount0(procedureCur.CodeNum, feeSch, procedureCur.ClinicNum, procedureCur.ProvNum);
                if (insPlanPrimary != null && insPlanPrimary.PlanType == "p" && !insPlanPrimary.IsMedical)         //PPO
                {
                    double provFee = Fees.GetAmount0(procedureCur.CodeNum, Providers.GetProv(procedureCur.ProvNum).FeeSched, procedureCur.ClinicNum,
                                                     procedureCur.ProvNum);
                    procedureCur.ProcFee = Math.Max(provFee, procFee);
                }
                else
                {
                    procedureCur.ProcFee = procFee;
                }
                if (!PrefC.GetBool(PrefName.EasyHidePublicHealth))
                {
                    procedureCur.SiteNum = pat.SiteNum;
                }
                Procedures.Insert(procedureCur);
                //Now make a claimproc if the patient has insurance.  We do this now for consistency because a claimproc could get created in the future.
                List <Benefit>   listBenefits          = Benefits.Refresh(listPatPlans, listInsSubs);
                List <ClaimProc> listClaimProcsForProc = ClaimProcs.RefreshForProc(procedureCur.ProcNum);
                Procedures.ComputeEstimates(procedureCur, pat.PatNum, listClaimProcsForProc, false, listInsPlans, listPatPlans, listBenefits, pat.Age, listInsSubs);
                FormProcBroken FormPB = new FormProcBroken(procedureCur);
                FormPB.IsNew = true;
                FormPB.ShowDialog();
            }
            #endregion
            #region BrokenApptAdjustment
            if (PrefC.GetBool(PrefName.BrokenApptAdjustment))
            {
                Adjustment AdjustmentCur = new Adjustment();
                AdjustmentCur.DateEntry = DateTime.Today;
                AdjustmentCur.AdjDate   = DateTime.Today;
                AdjustmentCur.ProcDate  = DateTime.Today;
                AdjustmentCur.ProvNum   = appt.ProvNum;
                AdjustmentCur.PatNum    = pat.PatNum;
                AdjustmentCur.AdjType   = PrefC.GetLong(PrefName.BrokenAppointmentAdjustmentType);
                AdjustmentCur.ClinicNum = appt.ClinicNum;
                FormAdjust FormA = new FormAdjust(pat, AdjustmentCur);
                FormA.IsNew = true;
                FormA.ShowDialog();
            }
            #endregion
            #region BrokenApptCommLog
            if (PrefC.GetBool(PrefName.BrokenApptCommLog))
            {
                Commlog CommlogCur = new Commlog();
                CommlogCur.PatNum       = pat.PatNum;
                CommlogCur.CommDateTime = DateTime.Now;
                CommlogCur.CommType     = Commlogs.GetTypeAuto(CommItemTypeAuto.APPT);
                CommlogCur.Note         = Lan.g("Appointment", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                CommlogCur.Mode_        = CommItemMode.None;
                CommlogCur.UserNum      = Security.CurUser.UserNum;
                FormCommItem FormCI = new FormCommItem();
                FormCI.ShowDialog(new CommItemModel()
                {
                    CommlogCur = CommlogCur
                }, new CommItemController(FormCI)
                {
                    IsNew = true
                });
            }
            #endregion
            AutomationL.Trigger(AutomationTrigger.BreakAppointment, null, pat.PatNum);
            Recalls.SynchScheduledApptFull(appt.PatNum);
        }
Beispiel #20
0
 ///<summary>Updates one HL7Msg in the database.  Uses an old object to compare to, and only alters changed fields.  This prevents collisions and concurrency problems in heavily used tables.</summary>
 internal static void Update(HL7Msg hL7Msg,HL7Msg oldHL7Msg)
 {
     string command="";
     if(hL7Msg.HL7Status != oldHL7Msg.HL7Status) {
         if(command!=""){ command+=",";}
         command+="HL7Status = "+POut.Int   ((int)hL7Msg.HL7Status)+"";
     }
     if(hL7Msg.MsgText != oldHL7Msg.MsgText) {
         if(command!=""){ command+=",";}
         command+="MsgText = "+DbHelper.ParamChar+"paramMsgText";
     }
     if(hL7Msg.AptNum != oldHL7Msg.AptNum) {
         if(command!=""){ command+=",";}
         command+="AptNum = "+POut.Long(hL7Msg.AptNum)+"";
     }
     if(command==""){
         return;
     }
     if(hL7Msg.MsgText==null) {
         hL7Msg.MsgText="";
     }
     OdSqlParameter paramMsgText=new OdSqlParameter("paramMsgText",OdDbType.Text,hL7Msg.MsgText);
     command="UPDATE hl7msg SET "+command
         +" WHERE HL7MsgNum = "+POut.Long(hL7Msg.HL7MsgNum);
     Db.NonQ(command,paramMsgText);
 }
Beispiel #21
0
		///<summary>Updates one HL7Msg in the database.  Uses an old object to compare to, and only alters changed fields.  This prevents collisions and concurrency problems in heavily used tables.  Returns true if an update occurred.</summary>
		public static bool Update(HL7Msg hL7Msg,HL7Msg oldHL7Msg){
			string command="";
			if(hL7Msg.HL7Status != oldHL7Msg.HL7Status) {
				if(command!=""){ command+=",";}
				command+="HL7Status = "+POut.Int   ((int)hL7Msg.HL7Status)+"";
			}
			if(hL7Msg.MsgText != oldHL7Msg.MsgText) {
				if(command!=""){ command+=",";}
				command+="MsgText = "+DbHelper.ParamChar+"paramMsgText";
			}
			if(hL7Msg.AptNum != oldHL7Msg.AptNum) {
				if(command!=""){ command+=",";}
				command+="AptNum = "+POut.Long(hL7Msg.AptNum)+"";
			}
			//DateTStamp can only be set by MySQL
			if(hL7Msg.PatNum != oldHL7Msg.PatNum) {
				if(command!=""){ command+=",";}
				command+="PatNum = "+POut.Long(hL7Msg.PatNum)+"";
			}
			if(hL7Msg.Note != oldHL7Msg.Note) {
				if(command!=""){ command+=",";}
				command+="Note = '"+POut.String(hL7Msg.Note)+"'";
			}
			if(command==""){
				return false;
			}
			if(hL7Msg.MsgText==null) {
				hL7Msg.MsgText="";
			}
			OdSqlParameter paramMsgText=new OdSqlParameter("paramMsgText",OdDbType.Text,hL7Msg.MsgText);
			command="UPDATE hl7msg SET "+command
				+" WHERE HL7MsgNum = "+POut.Long(hL7Msg.HL7MsgNum);
			Db.NonQ(command,paramMsgText);
			return true;
		}
Beispiel #22
0
        ///<summary>Creates a single recall appointment. If it's from a double click, then it will end up on that spot in the Appts module.  If not, it will end up on the pinboard with StringDateJumpTo as due date to jump to.  ListAptNumsSelected will contain the AptNum of the new appointment.</summary>
        public void MakeRecallAppointment()
        {
            List <InsSub>  listInsSubs  = InsSubs.RefreshForFam(_famCur);
            List <InsPlan> listInsPlans = InsPlans.RefreshForSubList(listInsSubs);
            Appointment    apt          = null;
            DateTime       dateTimeApt  = DateTime.MinValue;

            if (this.IsInitialDoubleClick)
            {
                dateTimeApt = DateTimeClicked;
            }
            try{
                apt = AppointmentL.CreateRecallApt(_patCur, listInsPlans, -1, listInsSubs, dateTimeApt);
            }
            catch (Exception ex) {
                MessageBox.Show(ex.Message);
                return;
            }
            DateTime datePrevious = apt.DateTStamp;

            ListAptNumsSelected.Add(apt.AptNum);
            if (IsInitialDoubleClick)
            {
                Appointment oldApt = apt.Copy();
                if (_patCur.AskToArriveEarly > 0)
                {
                    apt.DateTimeAskedToArrive = apt.AptDateTime.AddMinutes(-_patCur.AskToArriveEarly);
                    MessageBox.Show(Lan.g(this, "Ask patient to arrive") + " " + _patCur.AskToArriveEarly
                                    + " " + Lan.g(this, "minutes early at") + " " + apt.DateTimeAskedToArrive.ToShortTimeString() + ".");
                }
                apt.AptStatus = ApptStatus.Scheduled;
                apt.ClinicNum = _patCur.ClinicNum;
                apt.Op        = OpNumClicked;
                apt           = Appointments.AssignFieldsForOperatory(apt);
                //Use apt.ClinicNum because it was just set based on Op.ClinicNum in AssignFieldsForOperatory().
                if (!AppointmentL.IsSpecialtyMismatchAllowed(_patCur.PatNum, apt.ClinicNum))
                {
                    return;
                }
                Appointments.Update(apt, oldApt);
                _otherResult = OtherResult.CreateNew;
                SecurityLogs.MakeLogEntry(Permissions.AppointmentCreate, apt.PatNum, apt.AptDateTime.ToString(), apt.AptNum, datePrevious);
                //If there is an existing HL7 def enabled, send a SIU message if there is an outbound SIU message defined
                if (HL7Defs.IsExistingHL7Enabled())
                {
                    //S12 - New Appt Booking event
                    MessageHL7 messageHL7 = MessageConstructor.GenerateSIU(_patCur, _famCur.GetPatient(_patCur.Guarantor), EventTypeHL7.S12, apt);
                    //Will be null if there is no outbound SIU message defined, so do nothing
                    if (messageHL7 != null)
                    {
                        HL7Msg hl7Msg = new HL7Msg();
                        hl7Msg.AptNum    = apt.AptNum;
                        hl7Msg.HL7Status = HL7MessageStatus.OutPending;                      //it will be marked outSent by the HL7 service.
                        hl7Msg.MsgText   = messageHL7.ToString();
                        hl7Msg.PatNum    = _patCur.PatNum;
                        HL7Msgs.Insert(hl7Msg);
#if DEBUG
                        MessageBox.Show(this, messageHL7.ToString());
#endif
                    }
                }
                DialogResult = DialogResult.OK;
                return;
            }
            //not initialClick
            _otherResult = OtherResult.PinboardAndSearch;
            Recall recall = Recalls.GetRecallProphyOrPerio(_patCur.PatNum);          //shouldn't return null.
            if (recall.DateDue < DateTime.Today)
            {
                StringDateJumpTo = DateTime.Today.ToShortDateString();              //they are overdue
            }
            else
            {
                StringDateJumpTo = recall.DateDue.ToShortDateString();
            }
            //no securitylog entry needed here.  That will happen when it's dragged off pinboard.
            DialogResult = DialogResult.OK;
        }
		//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 #24
0
 ///<summary>Inserts one HL7Msg into the database.  Returns the new priKey.  Doesn't use the cache.</summary>
 public static long InsertNoCache(HL7Msg hL7Msg)
 {
     return(InsertNoCache(hL7Msg, false));
 }
Beispiel #25
0
        /// <summary>
        /// </summary>
        private Patient CreatePatient(String LastName, String FirstName, DateTime birthDate, WebSheets.SheetAndSheetField sAnds)
        {
            Patient newPat = new Patient();

            newPat.LName     = LastName;
            newPat.FName     = FirstName;
            newPat.Birthdate = birthDate;
            newPat.ClinicNum = sAnds.web_sheet.ClinicNum;
            if (PrefC.GetBool(PrefName.EasyNoClinics))
            {
                //Set the patients primary provider to the practice default provider.
                newPat.PriProv = Providers.GetDefaultProvider().ProvNum;
            }
            else              //Using clinics.
                              //Set the patients primary provider to the clinic default provider.
            {
                newPat.PriProv = Providers.GetDefaultProvider(Clinics.ClinicNum).ProvNum;
            }
            Type t = newPat.GetType();

            FieldInfo[] fi = t.GetFields();
            foreach (FieldInfo field in fi)
            {
                // find match for fields in Patients in the web_sheetfieldlist
                var WebSheetFieldList = sAnds.web_sheetfieldlist.Where(sf => sf.FieldName.ToLower() == field.Name.ToLower());
                if (WebSheetFieldList.Count() > 0)
                {
                    // this loop is used to fill a field that may generate mutiple values for a single field in the patient.
                    //for example the field gender has 2 eqivalent sheet fields in the web_sheetfieldlist
                    for (int i = 0; i < WebSheetFieldList.Count(); i++)
                    {
                        WebSheets.webforms_sheetfield sf = WebSheetFieldList.ElementAt(i);
                        String SheetWebFieldValue        = sf.FieldValue;
                        String RadioButtonValue          = sf.RadioButtonValue;
                        FillPatientFields(newPat, field, SheetWebFieldValue, RadioButtonValue);
                    }
                }
            }
            try{
                Patients.Insert(newPat, false);
                SecurityLogs.MakeLogEntry(Permissions.PatientCreate, newPat.PatNum, "Created from Web Forms.");
                //set Guarantor field the same as PatNum
                Patient patOld = newPat.Copy();
                newPat.Guarantor = newPat.PatNum;
                Patients.Update(newPat, patOld);
                //If there is an existing HL7 def enabled, send an ADT message if there is an outbound ADT message defined
                if (HL7Defs.IsExistingHL7Enabled())
                {
                    MessageHL7 messageHL7 = MessageConstructor.GenerateADT(newPat, newPat, EventTypeHL7.A04);                //patient is guarantor
                    //Will be null if there is no outbound ADT message defined, so do nothing
                    if (messageHL7 != null)
                    {
                        HL7Msg hl7Msg = new HL7Msg();
                        hl7Msg.AptNum    = 0;
                        hl7Msg.HL7Status = HL7MessageStatus.OutPending;                      //it will be marked outSent by the HL7 service.
                        hl7Msg.MsgText   = messageHL7.ToString();
                        hl7Msg.PatNum    = newPat.PatNum;
                        HL7Msgs.Insert(hl7Msg);
#if DEBUG
                        MessageBox.Show(this, messageHL7.ToString());
#endif
                    }
                }
            }
            catch (Exception e) {
                gridMain.EndUpdate();
                MessageBox.Show(e.Message);
            }
            return(newPat);
        }
Beispiel #26
0
        /// <summary>
        private static Patient CreatePatient(String LastName, String FirstName, DateTime birthDate, WebForms_Sheet webFormSheet, Sheet sheet, string cultureName)
        {
            bool    isWebForm = webFormSheet != null;
            Patient newPat    = new Patient();

            newPat.LName     = LastName;
            newPat.FName     = FirstName;
            newPat.Birthdate = birthDate;
            if (isWebForm)
            {
                newPat.ClinicNum = webFormSheet.ClinicNum;
            }
            else
            {
                newPat.ClinicNum = sheet.ClinicNum;
            }
            if (!PrefC.HasClinicsEnabled)
            {
                //Set the patients primary provider to the practice default provider.
                newPat.PriProv = Providers.GetDefaultProvider().ProvNum;
            }
            else              //Using clinics.
            //Set the patients primary provider to the clinic default provider.
            {
                newPat.PriProv = Providers.GetDefaultProvider(Clinics.ClinicNum).ProvNum;
            }
            Type t = newPat.GetType();

            FieldInfo[] fi = t.GetFields();
            foreach (FieldInfo field in fi)
            {
                // find match for fields in Patients in the SheetFields
                if (isWebForm)
                {
                    List <WebForms_SheetField> listWebFormsSheetFields = webFormSheet.SheetFields.FindAll(x => x.FieldName.ToLower() == field.Name.ToLower());
                    if (listWebFormsSheetFields.Count() > 0)
                    {
                        // this loop is used to fill a field that may generate mutiple values for a single field in the patient.
                        //for example the field gender has 2 eqivalent sheet fields in the SheetFields
                        foreach (WebForms_SheetField webFormsSheetField in listWebFormsSheetFields)
                        {
                            FillPatientFields(newPat, field, webFormsSheetField.FieldValue, webFormsSheetField.RadioButtonValue, cultureName, isWebForm, false);
                        }
                    }
                }
                else
                {
                    List <SheetField> listSheetFields = sheet.SheetFields.FindAll(x => x.FieldName.ToLower() == field.Name.ToLower());
                    if (listSheetFields.Count() > 0)
                    {
                        // this loop is used to fill a field that may generate mutiple values for a single field in the patient.
                        //for example the field gender has 2 eqivalent sheet fields in the SheetFields
                        foreach (SheetField sheetField in listSheetFields)
                        {
                            FillPatientFields(newPat, field, sheetField.FieldValue, sheetField.RadioButtonValue, "", isWebForm, sheet.IsCemtTransfer);
                        }
                    }
                }
            }
            try {
                Patients.Insert(newPat, false);
                SecurityLogs.MakeLogEntry(Permissions.PatientCreate, newPat.PatNum, isWebForm ? "Created from Web Forms." : "Created from CEMT transfer.");
                //set Guarantor field the same as PatNum
                Patient patOld = newPat.Copy();
                newPat.Guarantor = newPat.PatNum;
                Patients.Update(newPat, patOld);
                //If there is an existing HL7 def enabled, send an ADT message if there is an outbound ADT message defined
                if (HL7Defs.IsExistingHL7Enabled())
                {
                    MessageHL7 messageHL7 = MessageConstructor.GenerateADT(newPat, newPat, EventTypeHL7.A04);                //patient is guarantor
                    //Will be null if there is no outbound ADT message defined, so do nothing
                    if (messageHL7 != null)
                    {
                        HL7Msg hl7Msg = new HL7Msg();
                        hl7Msg.AptNum    = 0;
                        hl7Msg.HL7Status = HL7MessageStatus.OutPending;                      //it will be marked outSent by the HL7 service.
                        hl7Msg.MsgText   = messageHL7.ToString();
                        hl7Msg.PatNum    = newPat.PatNum;
                        HL7Msgs.Insert(hl7Msg);
#if DEBUG
                        MessageBox.Show("FormWebForms", messageHL7.ToString());
#endif
                    }
                }
            }
            catch (Exception e) {
                MessageBox.Show(e.Message);
            }
            return(newPat);
        }
Beispiel #27
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 #28
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 #29
0
		///<summary>Updates one HL7Msg in the database.</summary>
		public static void Update(HL7Msg hL7Msg){
			string command="UPDATE hl7msg SET "
				+"HL7Status =  "+POut.Int   ((int)hL7Msg.HL7Status)+", "
				+"MsgText   =  "+DbHelper.ParamChar+"paramMsgText, "
				+"AptNum    =  "+POut.Long  (hL7Msg.AptNum)+", "
				//DateTStamp can only be set by MySQL
				+"PatNum    =  "+POut.Long  (hL7Msg.PatNum)+", "
				+"Note      = '"+POut.String(hL7Msg.Note)+"' "
				+"WHERE HL7MsgNum = "+POut.Long(hL7Msg.HL7MsgNum);
			if(hL7Msg.MsgText==null) {
				hL7Msg.MsgText="";
			}
			OdSqlParameter paramMsgText=new OdSqlParameter("paramMsgText",OdDbType.Text,hL7Msg.MsgText);
			Db.NonQ(command,paramMsgText);
		}
Beispiel #30
0
 ///<summary>Updates one HL7Msg in the database.</summary>
 internal static void Update(HL7Msg hL7Msg)
 {
     string command="UPDATE hl7msg SET "
         +"HL7Status=  "+POut.Int   ((int)hL7Msg.HL7Status)+", "
         +"MsgText  =  "+DbHelper.ParamChar+"paramMsgText, "
         +"AptNum   =  "+POut.Long  (hL7Msg.AptNum)+" "
         +"WHERE HL7MsgNum = "+POut.Long(hL7Msg.HL7MsgNum);
     if(hL7Msg.MsgText==null) {
         hL7Msg.MsgText="";
     }
     OdSqlParameter paramMsgText=new OdSqlParameter("paramMsgText",OdDbType.Text,hL7Msg.MsgText);
     Db.NonQ(command,paramMsgText);
 }
Beispiel #31
0
 ///<summary>Inserts one HL7Msg into the database.  Returns the new priKey.</summary>
 public static long Insert(HL7Msg hL7Msg)
 {
     return(Insert(hL7Msg, false));
 }
Beispiel #32
0
        ///<summary>Sets given appt.AptStatus to broken.
        ///Provide procCode that should be charted, can be null but will not chart a broken procedure.
        ///Also considers various broken procedure based prefs.
        ///Makes its own securitylog entries.</summary>
        public static void BreakApptHelper(Appointment appt, Patient pat, ProcedureCode procCode)
        {
            //suppressHistory is true due to below logic creating a log with a specific HistAppointmentAction instead of the generic changed.
            DateTime datePrevious    = appt.DateTStamp;
            bool     suppressHistory = false;

            if (procCode != null)
            {
                suppressHistory = (procCode.ProcCode.In("D9986", "D9987"));
            }
            Appointments.SetAptStatus(appt, ApptStatus.Broken, suppressHistory); //Appointments S-Class handles Signalods
            if (appt.AptStatus != ApptStatus.Complete)                           //seperate log entry for completed appointments.
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            else
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentCompleteEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            #region HL7
            //If there is an existing HL7 def enabled, send a SIU message if there is an outbound SIU message defined
            if (HL7Defs.IsExistingHL7Enabled())
            {
                //S15 - Appt Cancellation event
                MessageHL7 messageHL7 = MessageConstructor.GenerateSIU(pat, Patients.GetPat(pat.Guarantor), EventTypeHL7.S15, appt);
                //Will be null if there is no outbound SIU message defined, so do nothing
                if (messageHL7 != null)
                {
                    HL7Msg hl7Msg = new HL7Msg();
                    hl7Msg.AptNum    = appt.AptNum;
                    hl7Msg.HL7Status = HL7MessageStatus.OutPending;                  //it will be marked outSent by the HL7 service.
                    hl7Msg.MsgText   = messageHL7.ToString();
                    hl7Msg.PatNum    = pat.PatNum;
                    HL7Msgs.Insert(hl7Msg);
#if DEBUG
                    MessageBox.Show("Appointments", messageHL7.ToString());
#endif
                }
            }
            #endregion
            List <Procedure> listProcedures = new List <Procedure>();
            //splits should only exist on procs if they are using tp pre-payments
            List <PaySplit> listSplitsForApptProcs = new List <PaySplit>();
            bool            isNonRefundable        = false;
            double          brokenProcAmount       = 0;
            Procedure       brokenProcedure        = new Procedure();
            bool            wasBrokenProcDeleted   = false;
            if (PrefC.GetYN(PrefName.PrePayAllowedForTpProcs))
            {
                listProcedures = Procedures.GetProcsForSingle(appt.AptNum, false);
                if (listProcedures.Count > 0)
                {
                    listSplitsForApptProcs = PaySplits.GetPaySplitsFromProcs(listProcedures.Select(x => x.ProcNum).ToList());
                }
            }
            #region Charting the proc
            if (procCode != null)
            {
                switch (procCode.ProcCode)
                {
                case "D9986":                        //Missed
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Missed);
                    break;

                case "D9987":                        //Cancelled
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Cancelled);
                    break;
                }
                brokenProcedure.PatNum       = pat.PatNum;
                brokenProcedure.ProvNum      = (procCode.ProvNumDefault > 0 ? procCode.ProvNumDefault : appt.ProvNum);
                brokenProcedure.CodeNum      = procCode.CodeNum;
                brokenProcedure.ProcDate     = DateTime.Today;
                brokenProcedure.DateEntryC   = DateTime.Now;
                brokenProcedure.ProcStatus   = ProcStat.C;
                brokenProcedure.ClinicNum    = appt.ClinicNum;
                brokenProcedure.UserNum      = Security.CurUser.UserNum;
                brokenProcedure.Note         = Lans.g("AppointmentEdit", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                brokenProcedure.PlaceService = (PlaceOfService)PrefC.GetInt(PrefName.DefaultProcedurePlaceService);              //Default proc place of service for the Practice is used.
                List <InsSub>  listInsSubs    = InsSubs.RefreshForFam(Patients.GetFamily(pat.PatNum));
                List <InsPlan> listInsPlans   = InsPlans.RefreshForSubList(listInsSubs);
                List <PatPlan> listPatPlans   = PatPlans.Refresh(pat.PatNum);
                InsPlan        insPlanPrimary = null;
                InsSub         insSubPrimary  = null;
                if (listPatPlans.Count > 0)
                {
                    insSubPrimary  = InsSubs.GetSub(listPatPlans[0].InsSubNum, listInsSubs);
                    insPlanPrimary = InsPlans.GetPlan(insSubPrimary.PlanNum, listInsPlans);
                }
                double procFee;
                long   feeSch;
                if (insPlanPrimary == null || procCode.NoBillIns)
                {
                    feeSch = FeeScheds.GetFeeSched(0, pat.FeeSched, brokenProcedure.ProvNum);
                }
                else                  //Only take into account the patient's insurance fee schedule if the D9986 procedure is not marked as NoBillIns
                {
                    feeSch = FeeScheds.GetFeeSched(insPlanPrimary.FeeSched, pat.FeeSched, brokenProcedure.ProvNum);
                }
                procFee = Fees.GetAmount0(brokenProcedure.CodeNum, feeSch, brokenProcedure.ClinicNum, brokenProcedure.ProvNum);
                if (insPlanPrimary != null && insPlanPrimary.PlanType == "p" && !insPlanPrimary.IsMedical)         //PPO
                {
                    double provFee = Fees.GetAmount0(brokenProcedure.CodeNum, Providers.GetProv(brokenProcedure.ProvNum).FeeSched, brokenProcedure.ClinicNum,
                                                     brokenProcedure.ProvNum);
                    brokenProcedure.ProcFee = Math.Max(provFee, procFee);
                }
                else if (listSplitsForApptProcs.Count > 0 && PrefC.GetBool(PrefName.TpPrePayIsNonRefundable) && procCode.ProcCode == "D9986")
                {
                    //if there are pre-payments, non-refundable pre-payments is turned on, and the broken appointment is a missed code then auto-fill
                    //the window with the sum of the procs for the appointment. Transfer money below after broken procedure is confirmed by the user.
                    brokenProcedure.ProcFee = listSplitsForApptProcs.Sum(x => x.SplitAmt);
                    isNonRefundable         = true;
                }
                else
                {
                    brokenProcedure.ProcFee = procFee;
                }
                if (!PrefC.GetBool(PrefName.EasyHidePublicHealth))
                {
                    brokenProcedure.SiteNum = pat.SiteNum;
                }
                Procedures.Insert(brokenProcedure);
                //Now make a claimproc if the patient has insurance.  We do this now for consistency because a claimproc could get created in the future.
                List <Benefit>   listBenefits          = Benefits.Refresh(listPatPlans, listInsSubs);
                List <ClaimProc> listClaimProcsForProc = ClaimProcs.RefreshForProc(brokenProcedure.ProcNum);
                Procedures.ComputeEstimates(brokenProcedure, pat.PatNum, listClaimProcsForProc, false, listInsPlans, listPatPlans, listBenefits, pat.Age, listInsSubs);
                FormProcBroken FormPB = new FormProcBroken(brokenProcedure, isNonRefundable);
                FormPB.IsNew = true;
                FormPB.ShowDialog();
                brokenProcAmount     = FormPB.AmountTotal;
                wasBrokenProcDeleted = FormPB.IsProcDeleted;
            }
            #endregion
            #region BrokenApptAdjustment
            if (PrefC.GetBool(PrefName.BrokenApptAdjustment))
            {
                Adjustment AdjustmentCur = new Adjustment();
                AdjustmentCur.DateEntry = DateTime.Today;
                AdjustmentCur.AdjDate   = DateTime.Today;
                AdjustmentCur.ProcDate  = DateTime.Today;
                AdjustmentCur.ProvNum   = appt.ProvNum;
                AdjustmentCur.PatNum    = pat.PatNum;
                AdjustmentCur.AdjType   = PrefC.GetLong(PrefName.BrokenAppointmentAdjustmentType);
                AdjustmentCur.ClinicNum = appt.ClinicNum;
                FormAdjust FormA = new FormAdjust(pat, AdjustmentCur);
                FormA.IsNew = true;
                FormA.ShowDialog();
            }
            #endregion
            #region BrokenApptCommLog
            if (PrefC.GetBool(PrefName.BrokenApptCommLog))
            {
                Commlog commlogCur = new Commlog();
                commlogCur.PatNum       = pat.PatNum;
                commlogCur.CommDateTime = DateTime.Now;
                commlogCur.CommType     = Commlogs.GetTypeAuto(CommItemTypeAuto.APPT);
                commlogCur.Note         = Lan.g("Appointment", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                commlogCur.Mode_        = CommItemMode.None;
                commlogCur.UserNum      = Security.CurUser.UserNum;
                commlogCur.IsNew        = true;
                FormCommItem FormCI = new FormCommItem(commlogCur);
                FormCI.ShowDialog();
            }
            #endregion
            #region Transfer money from TP Procedures if necessary
            //Note this MUST come after FormProcBroken since clicking cancel in that window will delete the procedure.
            if (isNonRefundable && !wasBrokenProcDeleted && listSplitsForApptProcs.Count > 0)
            {
                //transfer what the user specified in the broken appointment window.
                //transfer up to the amount specified by the user
                foreach (Procedure proc in listProcedures)
                {
                    if (brokenProcAmount == 0)
                    {
                        break;
                    }
                    List <PaySplit> listSplitsForAppointmentProcedure = listSplitsForApptProcs.FindAll(x => x.ProcNum == proc.ProcNum);
                    foreach (PaySplit split in listSplitsForAppointmentProcedure)
                    {
                        if (brokenProcAmount == 0)
                        {
                            break;
                        }
                        double amt = Math.Min(brokenProcAmount, split.SplitAmt);
                        Payments.CreateTransferForTpProcs(proc, new List <PaySplit> {
                            split
                        }, brokenProcedure, amt);
                        double amtPaidOnApt = listSplitsForApptProcs.Sum(x => x.SplitAmt);
                        if (amtPaidOnApt > amt)
                        {
                            //If the original prepayment amount is greater than the amt being specified for the appointment break, transfer
                            //the difference to an Unallocated Unearned Paysplit on the account.
                            double remainingAmt = amtPaidOnApt - amt;
                            //We have to create a new transfer payment here to correlate to the split.
                            Payment txfrPayment = new Payment();
                            txfrPayment.PayAmt    = 0;
                            txfrPayment.PayDate   = DateTime.Today;
                            txfrPayment.ClinicNum = split.ClinicNum;
                            txfrPayment.PayNote   = "Automatic transfer from treatment planned procedure prepayment.";
                            txfrPayment.PatNum    = split.PatNum;                       //ultimately where the payment ends up.
                            txfrPayment.PayType   = 0;
                            Payments.Insert(txfrPayment);
                            PaymentEdit.IncomeTransferData transferData = PaymentEdit.IncomeTransferData.CreateTransfer(split, txfrPayment.PayNum, true, remainingAmt);
                            PaySplit offset         = transferData.ListSplitsCur.FirstOrDefault(x => x.FSplitNum != 0);
                            long     offsetSplitNum = PaySplits.Insert(offset);                      //Get the FSplitNum from the offset
                            PaySplit allocation     = transferData.ListSplitsCur.FirstOrDefault(x => x.FSplitNum == 0);
                            allocation.FSplitNum = offsetSplitNum;
                            PaySplits.Insert(allocation);                            //Insert so the split is now up to date
                            SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, txfrPayment.PatNum, "Automatic transfer of funds for treatment plan procedure pre-payments.");
                        }
                        brokenProcAmount -= amt;
                    }
                }
            }
            //if broken appointment procedure was deleted (user cancelled out of the window) just keep money on the original procedure.
            #endregion
            AppointmentEvent.Fire(ODEventType.AppointmentEdited, appt);
            AutomationL.Trigger(AutomationTrigger.BreakAppointment, null, pat.PatNum);
            Recalls.SynchScheduledApptFull(appt.PatNum);
        }