public static string Validate(Appointment appt) { StringBuilder sb = new StringBuilder(); Provider provFacility = Providers.GetProv(PrefC.GetInt(PrefName.PracticeDefaultProv)); if (!Regex.IsMatch(provFacility.NationalProvID, "^(80840)?[0-9]{10}$")) { WriteError(sb, "Invalid NPI for provider '" + provFacility.Abbr + "'"); } if (PrefC.HasClinicsEnabled && appt.ClinicNum != 0) //Using clinics and a clinic is assigned. { Clinic clinic = Clinics.GetClinic(appt.ClinicNum); if (clinic.Description == "") { WriteError(sb, "Missing clinic description for clinic attached to appointment."); } } else //Not using clinics for this patient { if (PrefC.GetString(PrefName.PracticeTitle) == "") { WriteError(sb, "Missing practice title."); } } Patient pat = Patients.GetPat(appt.PatNum); if (pat.PatStatus == PatientStatus.Deceased && pat.DateTimeDeceased.Year < 1880) { WriteError(sb, "Missing date time deceased."); } List <EhrAptObs> listObservations = EhrAptObses.Refresh(appt.AptNum); if (listObservations.Count == 0) { WriteError(sb, "Missing observation."); } for (int i = 0; i < listObservations.Count; i++) { EhrAptObs obs = listObservations[i]; if (obs.ValType == EhrAptObsType.Coded) { if (obs.ValCodeSystem.Trim().ToUpper() == "LOINC") { Loinc loincVal = Loincs.GetByCode(obs.ValReported); if (loincVal == null) { WriteError(sb, "Loinc code not found '" + loincVal.LoincCode + "'. Please add by going to Setup | Chart | EHR."); } } else if (obs.ValCodeSystem.Trim().ToUpper() == "SNOMEDCT") { Snomed snomedVal = Snomeds.GetByCode(obs.ValReported); if (snomedVal == null) { WriteError(sb, "Snomed code not found '" + snomedVal.SnomedCode + "'. Please add by going to Setup | Chart | EHR."); } } else if (obs.ValCodeSystem.Trim().ToUpper() == "ICD9") { ICD9 icd9Val = ICD9s.GetByCode(obs.ValReported); if (icd9Val == null) { WriteError(sb, "ICD9 code not found '" + icd9Val.ICD9Code + "'. Please add by going to Setup | Chart | EHR."); } } else if (obs.ValCodeSystem.Trim().ToUpper() == "ICD10") { Icd10 icd10Val = Icd10s.GetByCode(obs.ValReported); if (icd10Val == null) { WriteError(sb, "ICD10 code not found '" + icd10Val.Icd10Code + "'. Please add by going to Setup | Chart | EHR."); } } } else if (obs.ValType == EhrAptObsType.Numeric && obs.UcumCode != "") //We only validate the ucum code if it will be sent out. Blank units allowed. { Ucum ucum = Ucums.GetByCode(obs.UcumCode); if (ucum == null) { WriteError(sb, "Invalid unit code '" + obs.UcumCode + "' for observation (must be UCUM code)."); } } } return(sb.ToString()); }
///<summary>Observation/result segment. Used to transmit observations related to the patient and visit. Guide page 64.</summary> private void OBX() { List <EhrAptObs> listObservations = EhrAptObses.Refresh(_appt.AptNum); for (int i = 0; i < listObservations.Count; i++) { EhrAptObs obs = listObservations[i]; _seg = new SegmentHL7(SegmentNameHL7.OBX); _seg.SetField(0, "OBX"); _seg.SetField(1, (i + 1).ToString()); //OBX-1 Set ID - OBX. Required (length 1..4). Must start at 1 and increment. //OBX-2 Value Type. Required (length 1..3). Cardinality [1..1]. Identifies the structure of data in observation value OBX-5. Values allowed: TS=Time Stamp (Date and/or Time),TX=Text,NM=Numeric,CWE=Coded with exceptions,XAD=Address. if (obs.ValType == EhrAptObsType.Coded) { _seg.SetField(2, "CWE"); } else if (obs.ValType == EhrAptObsType.DateAndTime) { _seg.SetField(2, "TS"); } else if (obs.ValType == EhrAptObsType.Numeric) { _seg.SetField(2, "NM"); } else //obs.ValType==EhrAptObsType.Text { _seg.SetField(2, "TX"); } //OBX-3 Observation Identifier. Required (length up to 478). Cardinality [1..1]. Value set is HL7 table named "Observation Identifier". Type CE. We use LOINC codes because the testing tool used LOINC codes and so do vaccines. string obsIdCode = ""; string obsIdCodeDescript = ""; string obsIdCodeSystem = "LN"; if (obs.IdentifyingCode == EhrAptObsIdentifier.BodyTemp) { obsIdCode = "11289-6"; obsIdCodeDescript = "Body temperature:Temp:Enctrfrst:Patient:Qn:"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.CheifComplaint) { obsIdCode = "8661-1"; obsIdCodeDescript = "Chief complaint:Find:Pt:Patient:Nom:Reported"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.DateIllnessOrInjury) { obsIdCode = "11368-8"; obsIdCodeDescript = "Illness or injury onset date and time:TmStp:Pt:Patient:Qn:"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.OxygenSaturation) { obsIdCode = "59408-5"; obsIdCodeDescript = "Oxygen saturation:MFr:Pt:BldA:Qn:Pulse oximetry"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.PatientAge) { obsIdCode = "21612-7"; obsIdCodeDescript = "Age Time Patient Reported"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.PrelimDiag) { obsIdCode = "44833-2"; obsIdCodeDescript = "Diagnosis.preliminary:Imp:Pt:Patient:Nom:"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.TreatFacilityID) { obsIdCode = "SS001"; obsIdCodeDescript = "Treating Facility Identifier"; obsIdCodeSystem = "PHINQUESTION"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.TreatFacilityLocation) { obsIdCode = "SS002"; obsIdCodeDescript = "Treating Facility Location"; obsIdCodeSystem = "PHINQUESTION"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.TriageNote) { obsIdCode = "54094-8"; obsIdCodeDescript = "Triage note:Find:Pt:Emergency department:Doc:"; } else if (obs.IdentifyingCode == EhrAptObsIdentifier.VisitType) { obsIdCode = "SS003"; obsIdCodeDescript = "Facility / Visit Type"; obsIdCodeSystem = "PHINQUESTION"; } WriteCE(3, obsIdCode, obsIdCodeDescript, obsIdCodeSystem); //OBX-4 Observation Sub-ID. No longer used. //OBX-5 Observation Value. Required if known (length 1..99999). Value must match type in OBX-2. if (obs.ValType == EhrAptObsType.Address) { WriteXAD(5, _sendingFacilityAddress1, _sendingFacilityAddress2, _sendingFacilityCity, _sendingFacilityState, _sendingFacilityZip); } else if (obs.ValType == EhrAptObsType.Coded) { string codeDescript = ""; string codeSystemAbbrev = ""; if (obs.ValCodeSystem.Trim().ToUpper() == "LOINC") { Loinc loincVal = Loincs.GetByCode(obs.ValReported); codeDescript = loincVal.NameShort; codeSystemAbbrev = "LN"; } else if (obs.ValCodeSystem.Trim().ToUpper() == "SNOMEDCT") { Snomed snomedVal = Snomeds.GetByCode(obs.ValReported); codeDescript = snomedVal.Description; codeSystemAbbrev = "SCT"; } else if (obs.ValCodeSystem.Trim().ToUpper() == "ICD9") { ICD9 icd9Val = ICD9s.GetByCode(obs.ValReported); codeDescript = icd9Val.Description; codeSystemAbbrev = "I9"; } else if (obs.ValCodeSystem.Trim().ToUpper() == "ICD10") { Icd10 icd10Val = Icd10s.GetByCode(obs.ValReported); codeDescript = icd10Val.Description; codeSystemAbbrev = "I10"; } WriteCE(5, obs.ValReported.Trim(), codeDescript, codeSystemAbbrev); } else if (obs.ValType == EhrAptObsType.DateAndTime) { DateTime dateVal = DateTime.Parse(obs.ValReported.Trim()); string strDateOut = dateVal.ToString("yyyyMMdd"); //The testing tool threw errors when there were trailing zeros, even though technically valid. if (dateVal.Second > 0) { strDateOut += dateVal.ToString("HHmmss"); } else if (dateVal.Minute > 0) { strDateOut += dateVal.ToString("HHmm"); } else if (dateVal.Hour > 0) { strDateOut += dateVal.ToString("HH"); } _seg.SetField(5, strDateOut); } else if (obs.ValType == EhrAptObsType.Numeric) { _seg.SetField(5, obs.ValReported.Trim()); } else //obs.ValType==EhrAptObsType.Text { _seg.SetField(5, obs.ValReported); } //OBX-6 Units. Required if OBX-2 is NM=Numeric. Cardinality [0..1]. Type CE. The guide suggests value sets: Pulse Oximetry Unit, Temperature Unit, or Age Unit. However, the testing tool used UCUM, so we will use UCUM. if (obs.ValType == EhrAptObsType.Numeric) { if (String.IsNullOrEmpty(obs.UcumCode)) //If units are required but known, we must send a null flavor. { WriteCE(6, "UNK", "", "NULLFL"); } else { Ucum ucum = Ucums.GetByCode(obs.UcumCode); WriteCE(6, ucum.UcumCode, ucum.Description, "UCUM"); } } //OBX-7 References Range. No longer used. //OBX-8 Abnormal Flags. No longer used. //OBX-9 Probability. No longer used. //OBX-10 Nature of Abnormal Test. No longer used. _seg.SetField(11, "F"); //OBX-11 Observation Result Status. Required (length 1..1). Expected value is "F". //OBX-12 Effective Date of Reference Range. No longer used. //OBX-13 User Defined Access Checks. No longer used. //OBX-14 Date/Time of the Observation. Optional. //OBX-15 Producer's ID. No longer used. //OBX-16 Responsible Observer. No longer used. //OBX-17 Observation Method. No longer used. //OBX-18 Equipment Instance Identifier. No longer used. //OBX-19 Date/Time of the Analysis. No longer used. _msg.Segments.Add(_seg); } }