public static void ProcessPD1(Patient pat, SegmentHL7 seg) { long provNum = EcwSegmentPID.ProvProcess(seg.GetField(4)); //don't know why both if (provNum != 0) { pat.PriProv = provNum; } }
///<summary>If relationship is self, this method does nothing. A new pat will later change guarantor to be same as patnum. </summary> public static void ProcessGT1(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) { char escapeChar='\\'; if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \ escapeChar=msg.Delimiters[2]; } char subcompChar='&'; if(msg.Delimiters.Length>3) { subcompChar=msg.Delimiters[3]; } #region Get And Validate Definition Field Ordinals //Find the position of the guarNum, guarChartNum, guarName, and guarBirthdate in this HL7 segment based on the definition of a GT1 int guarPatNumOrdinal=-1; int guarChartNumOrdinal=-1; int guarNameOrdinal=-1; int guarBirthdateOrdinal=-1; int guarIdsOrdinal=-1; string patOidRoot=""; for(int i=0;i<segDef.hl7DefFields.Count;i++) { switch(segDef.hl7DefFields[i].FieldName) { case "guar.PatNum": guarPatNumOrdinal=segDef.hl7DefFields[i].OrdinalPos; continue; case "guar.ChartNumber": guarChartNumOrdinal=segDef.hl7DefFields[i].OrdinalPos; continue; case "guar.nameLFM": guarNameOrdinal=segDef.hl7DefFields[i].OrdinalPos; continue; case "guar.birthdateTime": guarBirthdateOrdinal=segDef.hl7DefFields[i].OrdinalPos; continue; case "guarIdList": //get the id with the assigning authority equal to the internal OID root stored in their database for a patient object patOidRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot; if(patOidRoot=="") { //if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum, guarIdListOrdinal will remain -1 continue; } guarIdsOrdinal=segDef.hl7DefFields[i].OrdinalPos; continue; default://not supported continue; } } //If neither guar.PatNum nor guar.ChartNumber are included in this GT1 definition log a message in the event log and return if(guarPatNumOrdinal==-1 && guarChartNumOrdinal==-1 && guarIdsOrdinal==-1) { _hl7MsgCur.Note="Guarantor not processed. guar.PatNum, guar.ChartNumber, or guarIdList must be included in the GT1 definition. PatNum of patient: "+pat.PatNum.ToString(); HL7Msgs.Update(_hl7MsgCur); EventLog.WriteEntry("OpenDentHL7","Guarantor not processed. guar.PatNum, guar.ChartNumber, or guarIdList must be included in the GT1 definition. " +"PatNum of patient: "+pat.PatNum.ToString(),EventLogEntryType.Information); return; } //If guar.nameLFM is not included in this GT1 definition log a message in the event log and return if(guarNameOrdinal==-1) { _hl7MsgCur.Note="Guarantor not processed due to guar.nameLFM not included in the GT1 definition. Patnum of patient: "+pat.PatNum.ToString(); HL7Msgs.Update(_hl7MsgCur); EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to guar.nameLFM not included in the GT1 definition. Patnum of patient: " +pat.PatNum.ToString(),EventLogEntryType.Information); return; } //If the first or last name are not included in this GT1 segment, log a message in the event log and return if(seg.GetFieldComponent(guarNameOrdinal,0)=="" || seg.GetFieldComponent(guarNameOrdinal,1)=="") { _hl7MsgCur.Note="Guarantor not processed due to missing first or last name. PatNum of patient: "+pat.PatNum.ToString(); HL7Msgs.Update(_hl7MsgCur); EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to missing first or last name. PatNum of patient: " +pat.PatNum.ToString(),EventLogEntryType.Information); return; } #endregion Get And Validate Definition Field Ordinals #region Get guar.PatNum, guar.ChartNumber, and guarIdList IDs //Only process GT1 if guar.PatNum, guar.ChartNumber, or guarIdList is included and both guar.LName and guar.FName are included long guarPatNum=0; string guarChartNum=""; long guarPatNumFromIds=0; List<OIDExternal> listOids=new List<OIDExternal>(); if(guarPatNumOrdinal!=-1) { try { guarPatNum=PIn.Long(seg.GetFieldFullText(guarPatNumOrdinal)); } catch(Exception ex) { //do nothing, guarPatNum will remain 0 } } if(guarChartNumOrdinal!=-1) { guarChartNum=seg.GetFieldComponent(guarChartNumOrdinal); } if(guarIdsOrdinal!=-1) {//if OIDInternal root for a patient object is not defined, guarIdListOrdinal will be -1 FieldHL7 fieldGuarIds=seg.GetField(guarIdsOrdinal); //Example field: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI| //field component values will be the first repetition, repeats will be in field.ListRepeatFields string[] arrayGuarIdSubComps=fieldGuarIds.GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None); if(arrayGuarIdSubComps.Length>1 //must be at least two subcomponents in the assigning authority component so we can identify whose ID this is && fieldGuarIds.GetComponentVal(4).ToLower()=="pi") //PI=patient internal identifier; a number that is unique to a patient within an assigning authority { int intCheckDigit=-1; try { intCheckDigit=PIn.Int(fieldGuarIds.GetComponentVal(1)); } catch(Exception ex) { //checkDigit will remain -1 } //if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm //and only if there's at least 2 components in field 3 for the assigning authority so we can identify whose ID we are dealing with, then use the ID if((fieldGuarIds.GetComponentVal(2).ToLower()=="m10" && intCheckDigit!=-1 && M10CheckDigit(fieldGuarIds.GetComponentVal(0))==intCheckDigit)//using M10 scheme and the check digit is valid and matches calc || (fieldGuarIds.GetComponentVal(2).ToLower()=="m11" && intCheckDigit!=-1 && M11CheckDigit(fieldGuarIds.GetComponentVal(0))==intCheckDigit)//using M11 scheme and the check digit is valid and matches calc || (fieldGuarIds.GetComponentVal(2).ToLower()!="m10" && fieldGuarIds.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme { if(arrayGuarIdSubComps[1].ToLower()==patOidRoot.ToLower()) { try { guarPatNumFromIds=PIn.Long(fieldGuarIds.GetComponentVal(0)); } catch(Exception ex) { //do nothing, guarPatNumFromList will remain 0 } } else { OIDExternal oidCur=new OIDExternal(); oidCur.IDType=IdentifierType.Patient; oidCur.IDExternal=fieldGuarIds.GetComponentVal(0); oidCur.rootExternal=arrayGuarIdSubComps[1]; listOids.Add(oidCur); } } } for(int r=0;r<fieldGuarIds.ListRepeatFields.Count;r++) { arrayGuarIdSubComps=fieldGuarIds.ListRepeatFields[r].GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None); if(arrayGuarIdSubComps.Length<2) {//must be at least two sub-components to the assigning authority component so we can identify whose ID this is continue; } if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi") { continue; } int intCheckDigit=-1; try { intCheckDigit=PIn.Int(fieldGuarIds.ListRepeatFields[r].GetComponentVal(1)); } catch(Exception ex) { //checkDigit will remain -1 } if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10" && (intCheckDigit==-1 || M10CheckDigit(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M10 scheme and either invalid check digit or doesn't match calc { continue; } if(fieldGuarIds.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11" && (intCheckDigit==-1 || M11CheckDigit(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either invalid check digit or doesn't match calc { continue; } //if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and attempt to use if(arrayGuarIdSubComps[1].ToLower()==patOidRoot.ToLower()) { try { guarPatNumFromIds=PIn.Long(fieldGuarIds.ListRepeatFields[r].GetComponentVal(0)); } catch(Exception ex) { //do nothing, guarPatNumFromList will remain 0 } } else { OIDExternal oidCur=new OIDExternal(); oidCur.IDType=IdentifierType.Patient; oidCur.IDExternal=fieldGuarIds.ListRepeatFields[r].GetComponentVal(0); oidCur.rootExternal=arrayGuarIdSubComps[1]; listOids.Add(oidCur); } } } if(guarPatNum==0 && guarChartNum=="" && guarPatNumFromIds==0 && listOids.Count==0) {//because we have an example where they sent us this (position 2 (guarPatNumOrder or guarChartNumOrder for eCW) is empty): GT1|1||^^||^^^^|||||||| _hl7MsgCur.Note="Guarantor not processed due to missing guar.PatNum, guar.ChartNumber, and guarIdList. One of those numbers must be included. " +"PatNum of patient: "+pat.PatNum.ToString(); HL7Msgs.Update(_hl7MsgCur); EventLog.WriteEntry("OpenDentHL7","Guarantor not processed due to missing guar.PatNum, guar.ChartNumber, and guarIdList. " +"One of those numbers must be included. PatNum of patient: "+pat.PatNum.ToString(),EventLogEntryType.Information); return; } #endregion Get guar.PatNum, guar.ChartNumber, and guarIdList IDs if(guarPatNum==pat.PatNum || (guarChartNum!="" && guarChartNum==pat.ChartNumber) || guarPatNumFromIds==pat.PatNum) {//if relationship is self return; } #region Get Patient from Ids //Guar must be someone else Patient guar=null; Patient guarOld=null; //Find guarantor by guar.PatNum if defined and in this segment if(guarPatNum!=0) { guar=Patients.GetPat(guarPatNum); } else if(guarPatNumFromIds!=0) { guar=Patients.GetPat(guarPatNumFromIds); } else if(guarChartNum!="") {//guarPatNum and guarPatNumFromList was 0 so try to get guar by guar.ChartNumber or name and birthdate //try to find guarantor using chartNumber guar=Patients.GetPatByChartNumber(guarChartNum); if(guar==null) { //try to find the guarantor by using name and birthdate string strGuarLName=seg.GetFieldComponent(guarNameOrdinal,0); string strGuarFName=seg.GetFieldComponent(guarNameOrdinal,1); DateTime guarBirthdate=FieldParser.DateTimeParse(seg.GetFieldFullText(guarBirthdateOrdinal)); long guarNamePatNum=Patients.GetPatNumByNameAndBirthday(strGuarLName,strGuarFName,guarBirthdate); if(guarNamePatNum==0) {//guarantor does not exist in OD //so guar will still be null, triggering creation of new guarantor further down. } else { guar=Patients.GetPat(guarNamePatNum); guarOld=guar.Copy(); guar.ChartNumber=guarChartNum;//from now on, we will be able to find guar by chartNumber Patients.Update(guar,guarOld); } } } long guarExtPatNum=0; if(guar==null) {//As a last resort, use the external IDs for this patient in the oidexternal to find the guar //Use the external OIDs stored in the oidexternal table to find the patient //Only trust the external IDs if all OIDs refer to the same patient for(int i=0;i<listOids.Count;i++) { OIDExternal oidExternalCur=OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal); if(oidExternalCur==null || oidExternalCur.IDInternal==0 || oidExternalCur.IDType!=IdentifierType.Patient) {//must have an IDType of Patient continue; } if(guarExtPatNum==0) { guarExtPatNum=oidExternalCur.IDInternal; } else if(guarExtPatNum!=oidExternalCur.IDInternal) {//the current ID refers to a different patient than a previously found ID, don't trust the external IDs guarExtPatNum=0; break; } } if(guarExtPatNum>0) {//will be 0 if not in the OIDExternal table or no external IDs supplied or more than one supplied and they point to different pats (ambiguous) guar=Patients.GetPat(guarExtPatNum); } } //At this point we have a guarantor located in OD or guar=null so guar is new patient bool isNewGuar=(guar==null); if(isNewGuar) {//then we need to add guarantor to db guar=new Patient(); if(guarChartNum!="") { guar.ChartNumber=guarChartNum; } if(guarPatNum!=0) { guar.PatNum=guarPatNum; guar.Guarantor=guarPatNum; } else if(guarPatNumFromIds!=0) { guar.PatNum=guarPatNumFromIds; guar.Guarantor=guarPatNumFromIds; } else if(guarExtPatNum!=0) { guar.PatNum=guarExtPatNum; guar.Guarantor=guarExtPatNum; } guar.PriProv=PrefC.GetLong(PrefName.PracticeDefaultProv); guar.BillingType=PrefC.GetLong(PrefName.PracticeDefaultBillType); } else { guarOld=guar.Copy(); } #endregion Get Patient from Ids #region Update Guarantor Data //Now that we have our guarantor, process the GT1 segment for(int i=0;i<segDef.hl7DefFields.Count;i++) { int itemOrder=segDef.hl7DefFields[i].OrdinalPos; switch(segDef.hl7DefFields[i].FieldName) { case "guar.addressCityStateZip": guar.Address=seg.GetFieldComponent(itemOrder,0); guar.Address2=seg.GetFieldComponent(itemOrder,1); guar.City=seg.GetFieldComponent(itemOrder,2); guar.State=seg.GetFieldComponent(itemOrder,3); guar.Zip=seg.GetFieldComponent(itemOrder,4); guar.AddrNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(itemOrder,19),escapeChar); continue; case "guar.birthdateTime": guar.Birthdate=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder)); continue; case "guar.ChartNumber": guar.ChartNumber=seg.GetFieldComponent(itemOrder); continue; case "guar.Gender": guar.Gender=FieldParser.GenderParse(seg.GetFieldComponent(itemOrder)); continue; case "guar.HmPhone": if(seg.GetFieldComponent(itemOrder,2)=="" && seg.GetField(itemOrder).ListRepeatFields.Count==0) { //either no component 2 or left blank, and there are no repetitions, process the old way //the first repetition is considered the primary number, but if the primary number is not sent then it will be blank followed by the list of other numbers guar.HmPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder)); } else { //XTN data type, repeatable. //Component 2 values: PH-Phone, FX-Fax, MD-Modem, CP-Cell Phone, Internet-Internet Address, X.400-X.400 email address, TDD-Tel Device for the Deaf, TTY-Teletypewriter. //We will support PH for pat.HmPhone, CP for pat.WirelessPhone, and Internet for pat.Email //Component 5 is area code, 6 is number //Component 3 will be Email if the type is Internet //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected] FieldHL7 fieldPh=seg.GetField(itemOrder); if(fieldPh==null) { continue; } string strPhType=seg.GetFieldComponent(itemOrder,2); string strPh=seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6); string strEmail=seg.GetFieldComponent(itemOrder,3); for(int p=-1;p<fieldPh.ListRepeatFields.Count;p++) { if(p>=0) { strPhType=fieldPh.ListRepeatFields[p].GetComponentVal(2); strPh=fieldPh.ListRepeatFields[p].GetComponentVal(5)+fieldPh.ListRepeatFields[p].GetComponentVal(6); strEmail=fieldPh.ListRepeatFields[p].GetComponentVal(3); } switch(strPhType) { case "PH": guar.HmPhone=FieldParser.PhoneParse(strPh); continue; case "CP": guar.WirelessPhone=FieldParser.PhoneParse(strPh); continue; case "Internet": guar.Email=strEmail; continue; default: continue; } } } continue; case "guar.nameLFM": guar.LName=seg.GetFieldComponent(itemOrder,0); guar.FName=seg.GetFieldComponent(itemOrder,1); guar.MiddleI=seg.GetFieldComponent(itemOrder,2); guar.Title=seg.GetFieldComponent(itemOrder,4); continue; //case "guar.PatNum": Maybe do nothing?? case "guar.SSN": guar.SSN=seg.GetFieldComponent(itemOrder); continue; case "guar.WkPhone": if(seg.GetFieldComponent(itemOrder,2)=="PH") { //Component 2 value: PH-Phone //Component 5 is area code, 6 is number //Example: ^WPN^PH^^^503^3635432 guar.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6)); continue; } //either no component 2 or left blank, process the old way guar.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder)); continue; default: continue; } } if(isNewGuar) { if(guar.PatNum==0) { guarOld=guar.Copy(); guar.PatNum=Patients.Insert(guar,false); guar.Guarantor=guar.PatNum; Patients.Update(guar,guarOld); } else { guar.PatNum=Patients.Insert(guar,true); } if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Inserted patient "+guar.GetNameFLnoPref()+" when processing a GT1 segment.",EventLogEntryType.Information); } } else { Patients.Update(guar,guarOld); if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Updated patient "+guar.GetNameFLnoPref()+" when processing a GT1 segment.",EventLogEntryType.Information); } } #endregion Update Guarantor Data pat.Guarantor=guar.PatNum; //store external IDs in the oidexternal table if they do not already exist in the table string strVerboseMsg=""; for(int i=0;i<listOids.Count;i++) { if(listOids[i].IDExternal==""//not a good external ID OR || listOids[i].rootExternal==""//not a good external root OR || OIDExternals.GetByRootAndExtension(listOids[i].rootExternal,listOids[i].IDExternal)!=null)//already exists in the oidexternal table { continue; } listOids[i].IDInternal=guar.PatNum; OIDExternals.Insert(listOids[i]); strVerboseMsg+="\r\nExternal patient ID: "+listOids[i].IDExternal+", External root: "+listOids[i].rootExternal; } if(_isVerboseLogging && strVerboseMsg.Length>0) { EventLog.WriteEntry("OpenDentHL7","Added the following external patient ID(s) to the oidexternals table due to an incoming GT1 segment for PatNum: "+guar.PatNum+"." +strVerboseMsg+".",EventLogEntryType.Information); } return; }
///<summary>This is used to set the provider (either dentist or hygienist depending on provType) and the confirmation status of the appointment. If the apt is null, does nothing. If this is an AIG, the provider field will be in the format ProvNum^LName, FName^^Abbr. AIP and PV1 have the provider field in the format ProvNum^LName^FName^^^Abbr. segDef.SegmentName will be used to determine how to parse the field.</summary> public static void ProcessAIGorAIP(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg) { if(apt==null) {//We have to have an appt to process the AIG or AIP segment return; } Appointment aptOld=apt.Clone(); long provNum=0; string strProvType=""; for(int i=0;i<segDef.hl7DefFields.Count;i++) { int itemOrder=segDef.hl7DefFields[i].OrdinalPos; if(itemOrder<1) { continue; } switch(segDef.hl7DefFields[i].FieldName) { case "prov.provIdNameLFM": case "prov.provIdName": if(_isEcwHL7Def) { //for eCW interfaces, we allow inserting a new provider from the AIG or AIP segment and store the provider ID they supply in the prov.EcwId column provNum=FieldParser.ProvProcessEcw(seg.GetField(itemOrder)); } else { //for all other interfaces, we will only accept provider ID's that match the OD ProvNum //if not found by ProvNum, we will attempt to match LName, FName, and Abbr //if still not found, we will not set the provider on the appt provNum=FieldParser.ProvParse(seg.GetField(itemOrder),segDef.SegmentName,_isVerboseLogging); } //provNum may still be 0 continue; case "prov.provType": strProvType=seg.GetFieldComponent(itemOrder); //provType could still be an empty string, which is treated the same as 'd' or 'D' for dentist continue; case "apt.confirmStatus": long aptConfirmDefNum=0; Def[] listConfirmStats=DefC.GetList(DefCat.ApptConfirmed); for(int j=0;j<listConfirmStats.Length;j++) { if(seg.GetFieldComponent(itemOrder,1).ToLower()==listConfirmStats[j].ItemName.ToLower()//ItemName is the confirmation name || (listConfirmStats[j].ItemValue!="" && seg.GetFieldComponent(itemOrder,1).ToLower()==listConfirmStats[j].ItemValue.ToLower()))//ItemValue is the confirmation abbreviation, which may be blank { aptConfirmDefNum=listConfirmStats[j].DefNum; break; } } if(aptConfirmDefNum>0) { apt.Confirmed=aptConfirmDefNum; } continue; default: continue; } } if(provNum>0) { if(strProvType.ToLower()=="h") {//only set ProvHyg if they specifically send 'h' or 'H' as the provType apt.ProvHyg=provNum; } else {//if 'd' or 'D' or not specified or invalid provType, default to setting the ProvNum apt.ProvNum=provNum; } if(_isEcwHL7Def) { pat.PriProv=provNum; } } Appointments.Update(apt,aptOld); _aptProcessed=apt; if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Updated appointment for patient "+pat.GetNameFLnoPref()+" due to an incoming AIG or AIP segment.",EventLogEntryType.Information); } return; }
///<summary>apt could be null if the PV1 segment is from a message that does not refer to an appointment like an ADT or PPR message or if it is from an SIU message and is for a new appointment. If apt is null, do not update any of the apt fields, like clinic or provider.</summary> public static void ProcessPV1(Patient pat,Appointment apt,HL7DefSegment segDef,SegmentHL7 seg) { Appointment aptOld=null; if(apt!=null) { aptOld=apt.Clone(); } Patient patOld=pat.Copy(); for(int i=0;i<segDef.hl7DefFields.Count;i++) { int itemOrder=segDef.hl7DefFields[i].OrdinalPos; switch(segDef.hl7DefFields[i].FieldName) { case "pat.GradeLevel": int intGradeLevel=0; try { intGradeLevel=PIn.Int(seg.GetFieldComponent(itemOrder)); } catch(Exception ex) { //if parsing field to int fails, do nothing continue; } if(intGradeLevel<Enum.GetNames(typeof(PatientGrade)).Length) {//if parsed value is outside the range of enum, do nothing with the data pat.GradeLevel=(PatientGrade)intGradeLevel;//0=Unknown,1-12=first-twelfth, 13=PrenatalWIC, 14=PreK, 15=Kindergarten, 16=Other } continue; case "pat.location": //Example: ClinicDescript^OpName^^PracticeTitle^^c (c for clinic) if(seg.GetFieldComponent(itemOrder,5).ToLower()!="c") {//LocationType is 'c' for clinic continue; } long clinicNum=Clinics.GetByDesc(seg.GetFieldComponent(itemOrder,0));//0 if field is blank or description doesn't match clinic description in the db if(clinicNum==0) { continue;//do nothing, either no clinic description in the message or no matching clinic found } pat.ClinicNum=clinicNum; if(apt!=null) { apt.ClinicNum=clinicNum;//the apt.ClinicNum may be set based on a different segment, like the AIL segment } continue; case "prov.provIdName": case "prov.provIdNameLFM": long provNum=0; if(_isEcwHL7Def) {//uses prov.EcwID provNum=FieldParser.ProvProcessEcw(seg.GetField(itemOrder)); } else { provNum=FieldParser.ProvParse(seg.GetField(itemOrder),SegmentNameHL7.PV1,_isVerboseLogging); } if(provNum==0) {//This segment didn't have valid provider information in it, so do nothing continue; } if(apt!=null) {//We will set the appt.ProvNum (dentist) to the provider located in the PV1 segment, but this may be changed if the AIG or AIP segments are included apt.ProvNum=provNum; } pat.PriProv=provNum; continue; case "pat.site": //Example: |West Salem Elementary^^^^^s| ('s' for site) if(seg.GetFieldComponent(itemOrder,5).ToLower()!="s") {//LocationType is 's' for site continue;//not a site description, do nothing } long siteNum=Sites.FindMatchSiteNum(seg.GetFieldComponent(itemOrder,0));//0 if component is blank, -1 if no matching site description in the db if(siteNum>0) { pat.SiteNum=siteNum; } continue; case "pat.Urgency": int intPatUrgency=-1; try { intPatUrgency=PIn.Int(seg.GetFieldComponent(itemOrder));//if field is empty, PIn.Int will return 0 which will be the Unknown default urgency } catch(Exception ex) { //do nothing, patUrgency will remain -1 } if(intPatUrgency>-1 && intPatUrgency<4) {//only cast to enum if 0-3 pat.Urgency=(TreatmentUrgency)intPatUrgency; } continue; default: continue; } } Patients.Update(pat,patOld); if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref()+" due to an incoming PV1 segment.",EventLogEntryType.Information); } if(apt!=null) { Appointments.Update(apt,aptOld); _aptProcessed=apt; } if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Updated appointment for patient "+pat.GetNameFLnoPref()+" due to an incoming PV1 segment.",EventLogEntryType.Information); } return; }
//public static void ProcessPD1() { // return; //} public static void ProcessPID(Patient pat,HL7DefSegment segDef,SegmentHL7 seg,MessageHL7 msg) { Patient patOld=pat.Copy(); char escapeChar='\\'; if(msg.Delimiters.Length>2) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default \ escapeChar=msg.Delimiters[2]; } char subcompChar='&'; if(msg.Delimiters.Length>3) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default & subcompChar=msg.Delimiters[3]; } for(int i=0;i<segDef.hl7DefFields.Count;i++) { int itemOrder=segDef.hl7DefFields[i].OrdinalPos; switch(segDef.hl7DefFields[i].FieldName) { case "pat.addressCityStateZip": pat.Address=seg.GetFieldComponent(itemOrder,0); pat.Address2=seg.GetFieldComponent(itemOrder,1); pat.City=seg.GetFieldComponent(itemOrder,2); pat.State=seg.GetFieldComponent(itemOrder,3); pat.Zip=seg.GetFieldComponent(itemOrder,4); string strAddrNote=FieldParser.StringNewLineParse(seg.GetFieldComponent(itemOrder,19),escapeChar); if(strAddrNote!="") { pat.AddrNote=strAddrNote; } continue; case "pat.birthdateTime": pat.Birthdate=FieldParser.DateTimeParse(seg.GetFieldComponent(itemOrder)); continue; case "pat.ChartNumber": pat.ChartNumber=seg.GetFieldComponent(itemOrder); continue; case "pat.FeeSched": if(Programs.IsEnabled(ProgramName.eClinicalWorks) && ProgramProperties.GetPropVal(ProgramName.eClinicalWorks,"FeeSchedulesSetManually")=="1") { //if using eCW and FeeSchedulesSetManually continue;//do not process fee sched field, manually set by user } else { pat.FeeSched=FieldParser.FeeScheduleParse(seg.GetFieldComponent(itemOrder)); } continue; case "pat.Gender": pat.Gender=FieldParser.GenderParse(seg.GetFieldComponent(itemOrder)); continue; case "pat.HmPhone": if(seg.GetFieldComponent(itemOrder,2)=="" && seg.GetField(itemOrder).ListRepeatFields.Count==0) {//must be eCW, process the old way //either no component 2 or left blank, and there are no repetitions, process the old way //the first repetition is considered the primary number, but if the primary number is not sent then it will be blank followed by the list of other numbers pat.HmPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder)); } else { //XTN data type, repeatable. //Component 2 values: PH-Phone, FX-Fax, MD-Modem, CP-Cell Phone, Internet-Internet Address, X.400-X.400 email address, TDD-Tel Device for the Deaf, TTY-Teletypewriter. //We will support PH for pat.HmPhone, CP for pat.WirelessPhone, and Internet for pat.Email //Component 5 is area code, 6 is number //Component 3 will be Email if the type is Internet //Example: ^PRN^PH^^^503^3635432~^PRN^Internet^[email protected] FieldHL7 phField=seg.GetField(itemOrder); if(phField==null) { continue; } string strPhType=seg.GetFieldComponent(itemOrder,2); string strPh=seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6); string strEmail=seg.GetFieldComponent(itemOrder,3); for(int p=-1;p<phField.ListRepeatFields.Count;p++) { if(p>=0) { strPhType=phField.ListRepeatFields[p].GetComponentVal(2); strPh=phField.ListRepeatFields[p].GetComponentVal(5)+phField.ListRepeatFields[p].GetComponentVal(6); strEmail=phField.ListRepeatFields[p].GetComponentVal(3); } switch(strPhType) { case "PH": pat.HmPhone=FieldParser.PhoneParse(strPh); continue; case "CP": pat.WirelessPhone=FieldParser.PhoneParse(strPh); continue; case "Internet": pat.Email=strEmail; continue; default: continue; } } } continue; case "pat.nameLFM": pat.LName=seg.GetFieldComponent(itemOrder,0); pat.FName=seg.GetFieldComponent(itemOrder,1); pat.MiddleI=seg.GetFieldComponent(itemOrder,2); pat.Title=seg.GetFieldComponent(itemOrder,4); continue; case "pat.PatNum": //pat.PatNum guaranteed to not be 0, a new patient will be inserted if the field component for PatNum is not an int, is 0, or is blank //if(pat.PatNum!=0 && pat.PatNum!=PIn.Long(seg.GetFieldComponent(intItemOrder))) { //if field component is a valid number and the patient located is not the same as the patient with the PatNum in the segment, then throw the exception, message will fail. long patNumFromPid=0; try { patNumFromPid=PIn.Long(seg.GetFieldComponent(itemOrder)); } catch(Exception ex) { //do nothing, patNumFromPID will remain 0 } if(patNumFromPid!=0 && pat.PatNum!=patNumFromPid) { throw new Exception("Invalid PatNum"); } continue; case "pat.Position": pat.Position=FieldParser.MaritalStatusParse(seg.GetFieldComponent(itemOrder)); continue; case "pat.Race": //PID.10 is a CWE data type, with 9 defined components. The first three are CodeValue^CodeDescription^CodeSystem //This field can repeat, so we will need to add an entry in the patientrace table for each repetition //The race code system is going to be CDCREC and the values match our enum. //The code description is not saved, we only require CodeValue and CodeSystem=CDCREC. Descriptions come from the CDCREC table for display in OD. //Example race component: string strRaceCode=seg.GetFieldComponent(itemOrder,0); string strRaceCodeSystem=seg.GetFieldComponent(itemOrder,2); //if there aren't 3 components to the race field or if the code system is not CDCREC, then parse the old way by matching to string name, i.e. white=PatientRaceOld.White if(strRaceCodeSystem!="CDCREC") {//must be eCW, process the old way PatientRaceOld patientRaceOld=FieldParser.RaceParseOld(strRaceCode); //Converts deprecated PatientRaceOld enum to list of PatRaces, and adds them to the PatientRaces table for the patient. PatientRaces.Reconcile(pat.PatNum,PatientRaces.GetPatRacesFromPatientRaceOld(patientRaceOld)); } else { FieldHL7 fieldRace=seg.GetField(itemOrder); if(fieldRace==null) { continue; } List<PatRace> listPatRaces=new List<PatRace>(); listPatRaces.Add(FieldParser.RaceParse(strRaceCode)); for(int r=0;r<fieldRace.ListRepeatFields.Count;r++) { strRaceCode=fieldRace.ListRepeatFields[r].GetComponentVal(0); strRaceCodeSystem=fieldRace.ListRepeatFields[r].GetComponentVal(2); if(strRaceCodeSystem=="CDCREC") { listPatRaces.Add(FieldParser.RaceParse(strRaceCode)); } } PatientRaces.Reconcile(pat.PatNum,listPatRaces); } continue; case "pat.SSN": pat.SSN=seg.GetFieldComponent(itemOrder); continue; case "pat.WkPhone": if(seg.GetFieldComponent(itemOrder,2)=="PH") { //Component 2 value: PH-Phone //Component 5 is area code, 6 is number //Example: ^WPN^PH^^^503^3635432 pat.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder,5)+seg.GetFieldComponent(itemOrder,6)); continue; } //either no component 2 or left blank, process the old way pat.WkPhone=FieldParser.PhoneParse(seg.GetFieldComponent(itemOrder));//must be eCW continue; case "patientIds"://Not used by eCW //We will store the ID's from other software in the oidexternal table. These ID's will be sent in outbound msgs and used to locate a patient for subsequent inbound msgs. //Example: |1234^3^M11^&2.16.840.1.113883.3.4337.1486.6566.2&HL7^PI~7684^8^M11^&Other.Software.OID&OIDType^PI| //field component values will be the first repetition, repeats will be in field.ListRepeatFields //1234=PatNum, 3=check digit, M11=using check digit scheme, the assigning authority universal ID is the offices oidinternal.IDRoot for IDType Patient, //HL7=ID type, PI=identifier type code for: Patient internal identifier (a number that is unique to a patient within an assigning authority). FieldHL7 fieldCur=seg.GetField(itemOrder); //get the id with the assigning authority equal to the internal OID root stored in their database for a patient object string strPatOIDRoot=OIDInternals.GetForType(IdentifierType.Patient).IDRoot; if(strPatOIDRoot=="") { //if they have not set their internal OID root, we cannot identify which repetition in this field contains the OD PatNum EventLog.WriteEntry("OpenDentHL7","Could not process the patientIdList field due to the internal OID for the patient object missing.",EventLogEntryType.Information); continue; } string strPatIdCur=""; string strPatIdRootCur=""; string[] arrayPatIdSubComps=fieldCur.GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None); if(arrayPatIdSubComps.Length>1 //so the next line doesn't throw an exception && arrayPatIdSubComps[1].ToLower()!=strPatOIDRoot.ToLower()//not our PatNum && fieldCur.GetComponentVal(4).ToLower()=="pi")//PI=patient internal identifier; a number that is unique to a patient within an assigning authority { int intCheckDigit=-1; try { intCheckDigit=PIn.Int(fieldCur.GetComponentVal(1)); } catch(Exception ex) { //checkDigit will remain -1 } //if using the M10 or M11 check digit algorithm and it passes the respective test, or not using either algorithm, then use the ID if((fieldCur.GetComponentVal(2).ToLower()=="m10" && intCheckDigit!=-1 && M10CheckDigit(fieldCur.GetComponentVal(0))==intCheckDigit)//using M10 scheme and the check digit is valid and matches calc || (fieldCur.GetComponentVal(2).ToLower()=="m11" && intCheckDigit!=-1 && M11CheckDigit(fieldCur.GetComponentVal(0))==intCheckDigit)//using M11 scheme and the check digit is valid and matches calc || (fieldCur.GetComponentVal(2).ToLower()!="m10" && fieldCur.GetComponentVal(2).ToLower()!="m11"))//not using either check digit scheme { //Either passed the check digit test or not using the M10 or M11 check digit scheme, so trust the ID to be valid strPatIdCur=fieldCur.GetComponentVal(0); strPatIdRootCur=arrayPatIdSubComps[1]; } } OIDExternal oidExtCur=new OIDExternal(); string verboseMsg=""; if(strPatIdCur!="" && strPatIdRootCur!="" && OIDExternals.GetByRootAndExtension(strPatIdRootCur,strPatIdCur)==null) { oidExtCur.IDType=IdentifierType.Patient; oidExtCur.IDInternal=pat.PatNum; oidExtCur.IDExternal=strPatIdCur; oidExtCur.rootExternal=strPatIdRootCur; OIDExternals.Insert(oidExtCur); verboseMsg+="\r\nExternal patient ID: "+oidExtCur.IDExternal+", External root: "+oidExtCur.rootExternal; } for(int r=0;r<fieldCur.ListRepeatFields.Count;r++) { arrayPatIdSubComps=fieldCur.ListRepeatFields[r].GetComponentVal(3).Split(new char[] { subcompChar },StringSplitOptions.None); if(arrayPatIdSubComps.Length<2) {//there must be at least 2 sub-components in the assigning authority component so we can identify whose ID this is continue; } if(arrayPatIdSubComps[1].ToLower()==strPatOIDRoot.ToLower() //if this is the OD patient OID root || fieldCur.ListRepeatFields[r].GetComponentVal(4).ToLower()!="pi")//or not a patient identifier, then skip this repetition { continue; } int intCheckDigit=-1; try { intCheckDigit=PIn.Int(fieldCur.ListRepeatFields[r].GetComponentVal(1)); } catch(Exception ex) { //checkDigit will remain -1 } if(fieldCur.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m10" && (intCheckDigit==-1 || M10CheckDigit(fieldCur.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either an invalid check digit was received or it doesn't match our calc { continue; } if(fieldCur.ListRepeatFields[r].GetComponentVal(2).ToLower()=="m11" && (intCheckDigit==-1 || M11CheckDigit(fieldCur.ListRepeatFields[r].GetComponentVal(0))!=intCheckDigit))//using M11 scheme and either an invalid check digit was received or it doesn't match our calc { continue; } //if not using the M10 or M11 check digit scheme or if the check digit is good, trust the ID in component 0 to be valid and store as IDExternal strPatIdCur=fieldCur.ListRepeatFields[r].GetComponentVal(0); strPatIdRootCur=arrayPatIdSubComps[1]; if(strPatIdCur!="" && strPatIdRootCur!="" && OIDExternals.GetByRootAndExtension(strPatIdRootCur,strPatIdCur)==null) { oidExtCur=new OIDExternal(); oidExtCur.IDType=IdentifierType.Patient; oidExtCur.IDInternal=pat.PatNum; oidExtCur.IDExternal=strPatIdCur; oidExtCur.rootExternal=strPatIdRootCur; OIDExternals.Insert(oidExtCur); verboseMsg+="\r\nExternal patient ID: "+oidExtCur.IDExternal+", External root: "+oidExtCur.rootExternal; } } if(_isVerboseLogging && verboseMsg.Length>0) { EventLog.WriteEntry("OpenDentHL7","Added the following external patient ID(s) to the oidexternals table due to an incoming PID segment for PatNum: " +pat.PatNum+"."+verboseMsg+".",EventLogEntryType.Information); } continue; default: continue; } } Patients.Update(pat,patOld); if(_isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7","Updated patient "+pat.GetNameFLnoPref()+" due to an incoming PID segment.",EventLogEntryType.Information); } return; }
public static void ProcessMessage(MessageHL7 message, bool isVerboseLogging) { SegmentHL7 seg = message.GetSegment(SegmentNameHL7.PID, true); long patNum = PIn.Long(seg.GetFieldFullText(2)); Patient pat = Patients.GetPat(patNum); Patient patOld = null; bool isNewPat = pat == null; if (isNewPat) { pat = new Patient(); pat.PatNum = patNum; pat.Guarantor = patNum; pat.PriProv = PrefC.GetLong(PrefName.PracticeDefaultProv); pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType); } else { patOld = pat.Copy(); } List <PatientRace> listPatRaces = new List <PatientRace>(); EcwSegmentPID.ProcessPID(pat, seg, false, listPatRaces); //IsStandalone=false because should never make it this far. //PV1-patient visit--------------------------- //seg=message.GetSegment(SegmentName.PV1,false); //if(seg!=null) { // SegmentPID.ProcessPV1(pat,seg); //} //SCH- Schedule Activity Information seg = message.GetSegment(SegmentNameHL7.SCH, true); //The documentation is wrong. SCH.01 is not the appointment ID, but is instead a sequence# (always 1) long aptNum = PIn.Long(seg.GetFieldFullText(2)); Appointment apt = Appointments.GetOneApt(aptNum); Appointment aptOld = null; bool isNewApt = apt == null; if (isNewApt) { apt = new Appointment(); apt.AptNum = aptNum; apt.PatNum = pat.PatNum; apt.AptStatus = ApptStatus.Scheduled; } else { aptOld = apt.Copy(); } if (apt.PatNum != pat.PatNum) { EventLog.WriteEntry("OpenDentHL7", "Appointment does not match patient: " + pat.FName + " " + pat.LName + ", apt.PatNum:" + apt.PatNum.ToString() + ", pat.PatNum:" + pat.PatNum.ToString() , EventLogEntryType.Error); return; //we can't process this message because wrong patnum. } apt.Note = seg.GetFieldFullText(7); //apt.Pattern=ProcessDuration(seg.GetFieldFullText(9)); //9 and 10 are not actually available, in spite of the documentation. //11-We need start time and stop time apt.AptDateTime = DateTimeParse(seg.GetFieldComponent(11, 3)); DateTime stopTime = DateTimeParse(seg.GetFieldComponent(11, 4)); apt.Pattern = ProcessPattern(apt.AptDateTime, stopTime); apt.ProvNum = pat.PriProv; //just in case there's not AIG segment. //AIG is optional, but looks like the only way to get provider for the appt----------- //PV1 seems to frequently be sent instead of AIG. SegmentHL7 segAIG = message.GetSegment(SegmentNameHL7.AIG, false); SegmentHL7 segPV = message.GetSegment(SegmentNameHL7.PV1, false); if (segAIG != null) { long provNum = EcwSegmentPID.ProvProcess(segAIG.GetField(3)); if (provNum != 0) { apt.ProvNum = provNum; pat.PriProv = provNum; } } else if (segPV != null) { long provNum = EcwSegmentPID.ProvProcess(segPV.GetField(7)); if (provNum != 0) { apt.ProvNum = provNum; pat.PriProv = provNum; } } //AIL,AIP seem to be optional, and I'm going to ignore them for now. if (pat.FName == "" || pat.LName == "") { EventLog.WriteEntry("OpenDentHL7", "Appointment not processed due to missing patient first or last name. PatNum:" + pat.PatNum.ToString() , EventLogEntryType.Information); return; //this will also skip the appt insert. } if (isNewPat) { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Inserted patient: " + pat.FName + " " + pat.LName + ", PatNum:" + pat.PatNum.ToString() , EventLogEntryType.Information); } Patients.Insert(pat, true); SecurityLogs.MakeLogEntry(Permissions.PatientCreate, pat.PatNum, "Created from HL7 for eCW.", LogSources.HL7); } else { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Updated patient: " + pat.FName + " " + pat.LName, EventLogEntryType.Information); } Patients.Update(pat, patOld); } //had to move this reconcile here since we might not have a PatNum for new patients until after the insert PatientRaces.Reconcile(pat.PatNum, listPatRaces); if (isNewApt) { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Inserted appointment for: " + pat.FName + " " + pat.LName, EventLogEntryType.Information); } Appointments.InsertIncludeAptNum(apt, true); } else { if (isVerboseLogging) { EventLog.WriteEntry("OpenDentHL7", "Updated appointment for: " + pat.FName + " " + pat.LName, EventLogEntryType.Information); } Appointments.Update(apt, aptOld); } }
public static void ProcessPD1(Patient pat,SegmentHL7 seg) { long provNum=EcwSegmentPID.ProvProcess(seg.GetField(4));//don't know why both if(provNum!=0) { pat.PriProv=provNum; } }
///<summary>Sets values on _medLabCur based on the PID segment fields. Also sets medlab.OriginalPIDSegment to the full PID segment.</summary> public static void ProcessPID(HL7DefSegment segDef,SegmentHL7 pidSeg) { _medLabCur.OriginalPIDSegment=pidSeg.ToString(); for(int i=0;i<segDef.hl7DefFields.Count;i++) { HL7DefField fieldDefCur=segDef.hl7DefFields[i]; switch(fieldDefCur.FieldName) { case "accountNum": _medLabCur.PatAccountNum=pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos); string fastingStr=pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos,6); //if the component is blank or has a value other than "Y" or "N", the PatFasting field will be 0 - Unknown if(fastingStr.Length>0) { fastingStr=fastingStr.ToLower().Substring(0,1); if(fastingStr=="y") { _medLabCur.PatFasting=YN.Yes; } else if(fastingStr=="n") { _medLabCur.PatFasting=YN.No; } } continue; case "altPatID": _medLabCur.PatIDAlt=pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "labPatID": _medLabCur.PatIDLab=pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "pat.nameLFM": //not currently using this to update any db fields, only used to validate the correct patient was selected continue; case "pat.PatNum": if(_patCur!=null) { //If a patient was not located, the MedLab object will have a 0 for PatNum. //We will be able to generate a list of all 'unattached' MedLab objects so the user can manually select the correct patient. _medLabCur.PatNum=_patCur.PatNum; } continue; case "patBirthdateAge": //the birthday field for MedLab is birthdate^age in years^age months^age days. //The year component is left padded with 0's to three digits, the month and day components are left padded with 0's to two digits. //If the year, age, or month components don't exist or are blank, the default will be to //Example: 19811213^033^02^19 FieldHL7 fieldCur=pidSeg.GetField(fieldDefCur.OrdinalPos); if(fieldCur==null || fieldCur.Components.Count<4) {//if there aren't even 4 components (3 ^'s) in the field, don't set the age continue; } _medLabCur.PatAge=pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos,1).PadLeft(3,'0')+"/" +pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos,2).PadLeft(2,'0')+"/" +pidSeg.GetFieldComponent(fieldDefCur.OrdinalPos,3).PadLeft(2,'0'); continue; default: continue; } } return; }
///<summary></summary> public static void ProcessORC(HL7DefSegment segDef,SegmentHL7 orcSeg) { for(int i=0;i<segDef.hl7DefFields.Count;i++) { HL7DefField fieldDefCur=segDef.hl7DefFields[i]; switch(fieldDefCur.FieldName) { case "orderingProv": FieldHL7 fieldCur=orcSeg.GetField(fieldDefCur.OrdinalPos);//fieldCur can be null if(fieldCur==null) { continue; } _medLabCur.OrderingProvNPI=FieldParserMedLab.OrderingProvIDParse(fieldCur,"N"); _medLabCur.OrderingProvLocalID=FieldParserMedLab.OrderingProvIDParse(fieldCur,"L"); _medLabCur.OrderingProvLName=fieldCur.GetComponentVal(1).Trim(); _medLabCur.OrderingProvFName=fieldCur.GetComponentVal(2).Trim(); int j=0; while(_medLabCur.OrderingProvLName=="" && j<fieldCur.ListRepeatFields.Count) {//if LName is not present in first repetition, check others _medLabCur.OrderingProvLName=fieldCur.ListRepeatFields[j].GetComponentVal(1).Trim(); j++; } j=0; while(_medLabCur.OrderingProvFName=="" && j<fieldCur.ListRepeatFields.Count) {//if FName is not present in first repetition, check others _medLabCur.OrderingProvLName=fieldCur.ListRepeatFields[j].GetComponentVal(2).Trim(); j++; } #region Locate Provider _medicaidIdCur=FieldParserMedLab.OrderingProvIDParse(fieldCur,"P"); List<Provider> listProvs=Providers.GetProvsByNpiOrMedicaidId(_medLabCur.OrderingProvNPI,_medicaidIdCur); listProvs.Sort(SortByNpiMedicaidIdMatch); if(listProvs.Count>0) {//if a provider with either a matching NPI or Medicaid ID is found, use the first matching prov _medLabCur.ProvNum=listProvs[0].ProvNum; continue; } //no provider match based on NPI or MedicaidID listProvs=Providers.GetProvsByFLName(_medLabCur.OrderingProvLName,_medLabCur.OrderingProvFName);//must have both LName and FName if(listProvs.Count>0) {//use the first provider found with matching LName and FName _medLabCur.ProvNum=listProvs[0].ProvNum; } #endregion Locate Provider continue; case "specimenID": if(_medLabCur.SpecimenID!=null && _medLabCur.SpecimenID!="") {//could be filled in by value in OBR continue; } _medLabCur.SpecimenID=orcSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "specimenIDFiller": if(_medLabCur.SpecimenIDFiller!=null && _medLabCur.SpecimenIDFiller!="") {//could be filled in by value in OBR continue; } _medLabCur.SpecimenIDFiller=orcSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; default: continue; } } return; }
///<summary></summary> public static void ProcessOBR(HL7DefSegment segDef,SegmentHL7 obrSeg,MessageHL7 obrMsg) { char subcompChar='&'; if(obrMsg.Delimiters.Length>3) {//it is possible they did not send all 4 of the delimiter chars, in which case we will use the default & subcompChar=obrMsg.Delimiters[3]; } for(int i=0;i<segDef.hl7DefFields.Count;i++) { HL7DefField fieldDefCur=segDef.hl7DefFields[i]; FieldHL7 fieldCur=obrSeg.GetField(fieldDefCur.OrdinalPos);//fieldCur can be null if(fieldCur==null) { continue; } switch(fieldDefCur.FieldName) { case "clinicalInfo": if(_isFirstObr) { _medLabCur.ClinicalInfo=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos).Trim(); } continue; case "dateTimeCollected": _medLabCur.DateTimeCollected=FieldParserMedLab.DateTimeParse(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos)); continue; case "dateTimeEntered": _medLabCur.DateTimeEntered=FieldParserMedLab.DateTimeParse(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos)); continue; case "dateTimeReported": _medLabCur.DateTimeReported=FieldParserMedLab.DateTimeParse(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos)); continue; case "facilityID": string facIdCur=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); if(facIdCur=="") { continue; } if(!_dictFacilityCodeNum.ContainsKey(facIdCur)) {//no ZPS segments with this footnote ID continue; } List<long> facNumList=_dictFacilityCodeNum[facIdCur];//should only be one facility with this footnote ID in this message for(int j=0;j<facNumList.Count;j++) { MedLabFacAttach medLabAttachCur=new MedLabFacAttach(); medLabAttachCur.MedLabFacilityNum=facNumList[j]; medLabAttachCur.MedLabNum=_medLabCur.MedLabNum; MedLabFacAttaches.Insert(medLabAttachCur); } continue; case "obsTestID": if(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,2).ToLower()=="l") { _medLabCur.ObsTestID=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); _medLabCur.ObsTestDescript=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,1); } if(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,5).ToLower()=="ln") { _medLabCur.ObsTestLoinc=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,3); _medLabCur.ObsTestLoincText=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,4); } continue; case "orderingProv": if(_medLabCur.OrderingProvNPI==null || _medLabCur.OrderingProvNPI=="") {//may be filled from the ORC segment _medLabCur.OrderingProvNPI=FieldParserMedLab.OrderingProvIDParse(fieldCur,"N"); } if(_medLabCur.OrderingProvLocalID==null || _medLabCur.OrderingProvLocalID=="") {//may be filled from the ORC segment _medLabCur.OrderingProvLocalID=FieldParserMedLab.OrderingProvIDParse(fieldCur,"L"); } int k=0; if(_medLabCur.OrderingProvLName==null || _medLabCur.OrderingProvLName=="") {//may be filled from the ORC segment _medLabCur.OrderingProvLName=fieldCur.GetComponentVal(1).Trim(); while(_medLabCur.OrderingProvLName=="" && k<fieldCur.ListRepeatFields.Count) {//if LName is not present in first repetition check others _medLabCur.OrderingProvLName=fieldCur.ListRepeatFields[k].GetComponentVal(1).Trim(); k++; } } k=0; if(_medLabCur.OrderingProvFName==null || _medLabCur.OrderingProvFName=="") {//may be filled from the ORC segment _medLabCur.OrderingProvFName=fieldCur.GetComponentVal(2).Trim(); while(_medLabCur.OrderingProvFName=="" && k<fieldCur.ListRepeatFields.Count) {//if FName is not present in first repetition check others _medLabCur.OrderingProvFName=fieldCur.ListRepeatFields[k].GetComponentVal(2).Trim(); k++; } } #region Locate Provider if(_medLabCur.ProvNum!=0) {//may have located the provider from the ORC segment, nothing left to do continue; } _medicaidIdCur=FieldParserMedLab.OrderingProvIDParse(fieldCur,"P"); List<Provider> listProvs=Providers.GetProvsByNpiOrMedicaidId(_medLabCur.OrderingProvNPI,_medicaidIdCur); listProvs.Sort(SortByNpiMedicaidIdMatch); if(listProvs.Count>0) {//if a provider with either a matching NPI or Medicaid ID is found, use the first matching prov _medLabCur.ProvNum=listProvs[0].ProvNum; } else {//no provider match based on NPI or MedicaidID listProvs=Providers.GetProvsByFLName(_medLabCur.OrderingProvLName,_medLabCur.OrderingProvFName);//must have both LName and FName if(listProvs.Count>0) {//use the first provider found with matching LName and FName _medLabCur.ProvNum=listProvs[0].ProvNum; } } #endregion Locate Provider continue; case "parentObsID": _medLabCur.ParentObsID=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "parentObsTestID": _medLabCur.ParentObsTestID=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "resultStatus": _medLabCur.ResultStatus=FieldParserMedLab.ResultStatusParse(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos)); continue; case "specimenAction": _medLabCur.ActionCode=FieldParserMedLab.ResultActionParse(obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos)); continue; case "specimenID": if(_medLabCur.SpecimenID!=null && _medLabCur.SpecimenID!="") {//could be filled by value in ORC continue; } _medLabCur.SpecimenID=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "specimenIDAlt": _medLabCur.SpecimenIDAlt=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "specimenIDFiller": if(_medLabCur.SpecimenIDFiller!=null && _medLabCur.SpecimenIDFiller!="") {//could be filled by value in ORC continue; } _medLabCur.SpecimenIDFiller=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); continue; case "totalVolume": _medLabCur.TotalVolume=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos); if(_medLabCur.TotalVolume=="") { continue; } string[] unitsSubComp=obrSeg.GetFieldComponent(fieldDefCur.OrdinalPos,1).Split(new char[] { subcompChar },StringSplitOptions.None); if(unitsSubComp.Length==0 || unitsSubComp[0]=="") { _medLabCur.TotalVolume+=" ml"; continue; } _medLabCur.TotalVolume+=" "+unitsSubComp[0]; continue; default: continue; } } _isFirstObr=false; return; }