private void Import834_Unsafe() { if (!MsgBox.Show(this, true, "Importing insurance plans is a database intensive operation and can take 10 minutes or more to run. " + "It is best to import insurance plans after hours or during another time period when database usage is otherwise low.\r\n" + "Click OK to import insurance plans now, or click Cancel.")) { return; } checkIsPatientCreate.Enabled = false; gridInsPlans.Enabled = false; butOK.Enabled = false; butCancel.Enabled = false; Cursor = Cursors.WaitCursor; Prefs.UpdateBool(PrefName.Ins834IsPatientCreate, checkIsPatientCreate.Checked); int rowIndex = 1; int createdPatsCount = 0; int updatedPatsCount = 0; int skippedPatsCount = 0; int createdCarrierCount = 0; int createdPlanCount = 0; int droppedPlanCount = 0; int updatedPlanCount = 0; StringBuilder sbErrorMessages = new StringBuilder(); List <int> listImportedSegments = new List <int> (); //Used to reconstruct the 834 with imported patients removed. for (int i = 0; i < _x834.ListTransactions.Count; i++) { Hx834_Tran tran = _x834.ListTransactions[i]; for (int j = 0; j < tran.ListMembers.Count; j++) { Hx834_Member member = tran.ListMembers[j]; ShowStatus("Progress " + (rowIndex).ToString().PadLeft(6) + "/" + gridInsPlans.Rows.Count.ToString().PadLeft(6) + " Importing plans for patient " + member.Pat.GetNameLF()); //The patient's status is not affected by the maintenance code. After reviewing all of the possible maintenance codes in //member.MemberLevelDetail.MaintenanceTypeCode, we believe that all statuses suggest either insert or update, except for "Cancel". //Nathan and Derek feel that archiving the patinet in response to a Cancel request is a bit drastic. //Thus we ignore the patient maintenance code and always assume insert/update. //Even if the status was "Cancel", then updating the patient does not hurt. bool isMemberImported = false; bool isMultiMatch = false; if (member.Pat.PatNum == 0) { //The patient may need to be created below. However, the patient may have already been inserted in a pervious iteration of this loop //in following scenario: Two different 834s include updates for the same patient and both documents are being imported at the same time. //If the patient was already inserted, then they would show up in _listPatients and also in the database. Attempt to locate the patient //in _listPatients again before inserting. List <Patient> listPatientMatches = Patients.GetPatientsByNameAndBirthday(member.Pat, _listPatients); if (listPatientMatches.Count == 1) { member.Pat.PatNum = listPatientMatches[0].PatNum; } else if (listPatientMatches.Count > 1) { isMultiMatch = true; } } if (isMultiMatch) { skippedPatsCount++; } else if (member.Pat.PatNum == 0 && checkIsPatientCreate.Checked) { //The code here mimcs the behavior of FormPatientSelect.butAddPt_Click(). Patients.Insert(member.Pat, false); Patient memberPatOld = member.Pat.Copy(); member.Pat.PatStatus = PatientStatus.Patient; member.Pat.BillingType = PrefC.GetLong(PrefName.PracticeDefaultBillType); if (!PrefC.GetBool(PrefName.PriProvDefaultToSelectProv)) { //Set the patients primary provider to the practice default provider. member.Pat.PriProv = Providers.GetDefaultProvider().ProvNum; } member.Pat.ClinicNum = Clinics.ClinicNum; member.Pat.Guarantor = member.Pat.PatNum; Patients.Update(member.Pat, memberPatOld); int patIdx = _listPatients.BinarySearch(member.Pat); //Preserve sort order by locating the index in which to insert the newly added patient. int insertIdx = ~patIdx; //According to MSDN, the index returned by BinarySearch() is a "bitwise compliment", since not currently in list. _listPatients.Insert(insertIdx, member.Pat); SecurityLogs.MakeLogEntry(Permissions.PatientCreate, member.Pat.PatNum, "Created from Import Ins Plans 834.", LogSources.InsPlanImport834); isMemberImported = true; createdPatsCount++; } else if (member.Pat.PatNum == 0 && !checkIsPatientCreate.Checked) { skippedPatsCount++; } else //member.Pat.PatNum!=0 { Patient patDb = _listPatients.FirstOrDefault(x => x.PatNum == member.Pat.PatNum); //Locate by PatNum, in case user selected manually. member.MergePatientIntoDbPatient(patDb); //Also updates the patient to the database and makes log entry. _listPatients.Remove(patDb); //Remove patient from list so we can add it back in the correct location (in case name or bday changed). int patIdx = _listPatients.BinarySearch(patDb); //Preserve sort order by locating the index in which to insert the newly added patient. //patIdx could be positive if the user manually selected a patient when there were multiple matches found. //If patIdx is negative, then according to MSDN, the index returned by BinarySearch() is a "bitwise compliment". //If there were mult instances of patDb BinarySearch() would return 0, which should not be complimented (OutOfRangeException) int insertIdx = (patIdx >= 0)?patIdx:~patIdx; _listPatients.Insert(insertIdx, patDb); isMemberImported = true; updatedPatsCount++; } if (isMemberImported) { //Import insurance changes for patient. for (int k = 0; k < member.ListHealthCoverage.Count; k++) { Hx834_HealthCoverage healthCoverage = member.ListHealthCoverage[k]; if (k > 0) { rowIndex++; } List <Carrier> listCarriers = Carriers.GetByNameAndTin(tran.Payer.Name, tran.Payer.IdentificationCode); if (listCarriers.Count == 0) { Carrier carrier = new Carrier(); carrier.CarrierName = tran.Payer.Name; carrier.TIN = tran.Payer.IdentificationCode; Carriers.Insert(carrier); DataValid.SetInvalid(InvalidType.Carriers); listCarriers.Add(carrier); SecurityLogs.MakeLogEntry(Permissions.CarrierCreate, 0, "Carrier '" + carrier.CarrierName + "' created from Import Ins Plans 834.", LogSources.InsPlanImport834); createdCarrierCount++; } //Update insurance plans. Match based on carrier and SubscriberId. bool isDropping = false; if (healthCoverage.HealthCoverage.MaintenanceTypeCode == "002") { isDropping = true; } //The insPlanNew will only be inserted if necessary. Created temporarily in order to determine if insert is needed. InsPlan insPlanNew = InsertOrUpdateInsPlan(null, member, listCarriers[0], false); //Since the insurance plans in the 834 do not include very much information, it is likely that many patients will share the same exact plan. //We look for an existing plan being used by any other patinents which match the fields we typically import. List <InsPlan> listInsPlans = InsPlans.GetAllByCarrierNum(listCarriers[0].CarrierNum); InsPlan insPlanMatch = null; for (int p = 0; p < listInsPlans.Count; p++) { //Set the PlanNums equal so that AreEqualValue() will ignore this field. We must ignore PlanNum, since we do not know the PlanNum. insPlanNew.PlanNum = listInsPlans[p].PlanNum; if (InsPlans.AreEqualValue(listInsPlans[p], insPlanNew)) { insPlanMatch = listInsPlans[p]; break; } } Family fam = Patients.GetFamily(member.Pat.PatNum); List <InsSub> listInsSubs = InsSubs.RefreshForFam(fam); List <PatPlan> listPatPlans = PatPlans.Refresh(member.Pat.PatNum); InsSub insSubMatch = null; PatPlan patPlanMatch = null; for (int p = 0; p < listInsSubs.Count; p++) { InsSub insSub = listInsSubs[p]; //According to section 1.4.3 of the standard, the preferred method of matching a dependent to a subscriber is to use the subscriberId. if (insSub.SubscriberID.Trim() != member.SubscriberId.Trim()) { continue; } insSubMatch = insSub; patPlanMatch = PatPlans.GetFromList(listPatPlans, insSub.InsSubNum); break; } if (patPlanMatch == null && isDropping) //No plan match and dropping plan. //Nothing to do. The plan either never existed or is already dropped. { } else if (patPlanMatch == null && !isDropping) //No plan match and not dropping plan. Create the plan. { insPlanMatch = InsertOrUpdateInsPlan(insPlanMatch, member, listCarriers[0]); insSubMatch = InsertOrUpdateInsSub(insSubMatch, insPlanMatch, member, healthCoverage, listCarriers[0]); patPlanMatch = InsertOrUpdatePatPlan(patPlanMatch, insSubMatch, insPlanMatch, member, listCarriers[0], listPatPlans); createdPlanCount++; } else if (patPlanMatch != null && isDropping) //Plan matched and dropping plan. Drop the plan. //This code mimics the behavior of FormInsPlan.butDrop_Click(), except here we do not care if there are claims for this plan today. //We need this feature to be as streamlined as possible so that it might become an automated process later. //Testing for claims on today's date does not seem that useful anyway, or at least not as useful as checking for any claims //associated to the plan, instead of just today's date. { PatPlans.Delete(patPlanMatch.PatPlanNum); //Estimates recomputed within Delete() SecurityLogs.MakeLogEntry(Permissions.InsPlanDropPat, patPlanMatch.PatNum, "Insurance plan dropped from patient for carrier '" + listCarriers[0].CarrierName + "' and groupnum " + "'" + insPlanMatch.GroupNum + "' and subscriber ID '" + insSubMatch.SubscriberID + "' " + "from Import Ins Plans 834.", insPlanMatch.PlanNum, LogSources.InsPlanImport834, insPlanMatch.SecDateTEdit); droppedPlanCount++; } else if (patPlanMatch != null && !isDropping) //Plan matched and not dropping plan. Update the plan. { insPlanMatch = InsertOrUpdateInsPlan(insPlanMatch, member, listCarriers[0]); insSubMatch = InsertOrUpdateInsSub(insSubMatch, insPlanMatch, member, healthCoverage, listCarriers[0]); patPlanMatch = InsertOrUpdatePatPlan(patPlanMatch, insSubMatch, insPlanMatch, member, listCarriers[0], listPatPlans); updatedPlanCount++; } } //end loop k //Remove the member from the X834. int endSegIndex = 0; if (j < tran.ListMembers.Count - 1) { endSegIndex = tran.ListMembers[j + 1].MemberLevelDetail.SegmentIndex - 1; } else { X12Segment segSe = _x834.GetNextSegmentById(member.MemberLevelDetail.SegmentIndex + 1, "SE"); //SE segment is required. endSegIndex = segSe.SegmentIndex - 1; } for (int s = member.MemberLevelDetail.SegmentIndex; s <= endSegIndex; s++) { listImportedSegments.Add(s); } } rowIndex++; } //end loop j } //end loop i if (listImportedSegments.Count > 0 && skippedPatsCount > 0) //Some patients imported, while others did not. { if (MoveFileToArchiveFolder()) { //Save the unprocessed members back to the import directory, so the user can try to import them again. File.WriteAllText(_x834.FilePath, _x834.ReconstructRaw(listImportedSegments)); } } else if (listImportedSegments.Count > 0) //All patinets imported. { MoveFileToArchiveFolder(); } else if (skippedPatsCount > 0) //No patients imported. All patients were skipped. //Leave the raw file unaltered and where it is, so it can be processed again. { } Cursor = Cursors.Default; string msg = Lan.g(this, "Done."); if (createdPatsCount > 0) { msg += "\r\n" + Lan.g(this, "Number of patients created:") + " " + createdPatsCount; } if (updatedPatsCount > 0) { msg += "\r\n" + Lan.g(this, "Number of patients updated:") + " " + updatedPatsCount; } if (skippedPatsCount > 0) { msg += "\r\n" + Lan.g(this, "Number of patients skipped:") + " " + skippedPatsCount; msg += sbErrorMessages.ToString(); } if (createdCarrierCount > 0) { msg += "\r\n" + Lan.g(this, "Number of carriers created:") + " " + createdCarrierCount; msg += sbErrorMessages.ToString(); } if (createdPlanCount > 0) { msg += "\r\n" + Lan.g(this, "Number of plans created:") + " " + createdPlanCount; msg += sbErrorMessages.ToString(); } if (droppedPlanCount > 0) { msg += "\r\n" + Lan.g(this, "Number of plans dropped:") + " " + droppedPlanCount; msg += sbErrorMessages.ToString(); } if (updatedPlanCount > 0) { msg += "\r\n" + Lan.g(this, "Number of plans updated:") + " " + updatedPlanCount; msg += sbErrorMessages.ToString(); } MsgBoxCopyPaste msgBox = new MsgBoxCopyPaste(msg); msgBox.ShowDialog(); }
private Claim CreateClaim(string claimType, List <PatPlan> patPlanList, List <InsPlan> planList, List <ClaimProc> claimProcList, Procedure proc, List <InsSub> subList) { long claimFormNum = 0; InsPlan planCur = new InsPlan(); InsSub subCur = new InsSub(); Relat relatOther = Relat.Self; long clinicNum = proc.ClinicNum; PlaceOfService placeService = proc.PlaceService; switch (claimType) { case "P": subCur = InsSubs.GetSub(PatPlans.GetInsSubNum(patPlanList, PatPlans.GetOrdinal(PriSecMed.Primary, patPlanList, planList, subList)), subList); planCur = InsPlans.GetPlan(subCur.PlanNum, planList); break; case "S": subCur = InsSubs.GetSub(PatPlans.GetInsSubNum(patPlanList, PatPlans.GetOrdinal(PriSecMed.Secondary, patPlanList, planList, subList)), subList); planCur = InsPlans.GetPlan(subCur.PlanNum, planList); break; case "Med": //It's already been verified that a med plan exists subCur = InsSubs.GetSub(PatPlans.GetInsSubNum(patPlanList, PatPlans.GetOrdinal(PriSecMed.Medical, patPlanList, planList, subList)), subList); planCur = InsPlans.GetPlan(subCur.PlanNum, planList); break; } ClaimProc claimProcCur = Procedures.GetClaimProcEstimate(proc.ProcNum, claimProcList, planCur, subCur.InsSubNum); if (claimProcCur == null) { claimProcCur = new ClaimProc(); ClaimProcs.CreateEst(claimProcCur, proc, planCur, subCur); } Claim claimCur = new Claim(); claimCur.PatNum = proc.PatNum; claimCur.DateService = proc.ProcDate; claimCur.ClinicNum = proc.ClinicNum; claimCur.PlaceService = proc.PlaceService; claimCur.ClaimStatus = "W"; claimCur.DateSent = DateTimeOD.Today; claimCur.PlanNum = planCur.PlanNum; claimCur.InsSubNum = subCur.InsSubNum; InsSub sub; switch (claimType) { case "P": claimCur.PatRelat = PatPlans.GetRelat(patPlanList, PatPlans.GetOrdinal(PriSecMed.Primary, patPlanList, planList, subList)); claimCur.ClaimType = "P"; claimCur.InsSubNum2 = PatPlans.GetInsSubNum(patPlanList, PatPlans.GetOrdinal(PriSecMed.Secondary, patPlanList, planList, subList)); sub = InsSubs.GetSub(claimCur.InsSubNum2, subList); if (sub.PlanNum > 0 && InsPlans.RefreshOne(sub.PlanNum).IsMedical) { claimCur.PlanNum2 = 0; //no sec ins claimCur.PatRelat2 = Relat.Self; } else { claimCur.PlanNum2 = sub.PlanNum; //might be 0 if no sec ins claimCur.PatRelat2 = PatPlans.GetRelat(patPlanList, PatPlans.GetOrdinal(PriSecMed.Secondary, patPlanList, planList, subList)); } break; case "S": claimCur.PatRelat = PatPlans.GetRelat(patPlanList, PatPlans.GetOrdinal(PriSecMed.Secondary, patPlanList, planList, subList)); claimCur.ClaimType = "S"; claimCur.InsSubNum2 = PatPlans.GetInsSubNum(patPlanList, PatPlans.GetOrdinal(PriSecMed.Primary, patPlanList, planList, subList)); sub = InsSubs.GetSub(claimCur.InsSubNum2, subList); claimCur.PlanNum2 = sub.PlanNum; claimCur.PatRelat2 = PatPlans.GetRelat(patPlanList, PatPlans.GetOrdinal(PriSecMed.Primary, patPlanList, planList, subList)); break; case "Med": claimCur.PatRelat = PatPlans.GetFromList(patPlanList, subCur.InsSubNum).Relationship; claimCur.ClaimType = "Other"; if (PrefC.GetBool(PrefName.ClaimMedTypeIsInstWhenInsPlanIsMedical)) { claimCur.MedType = EnumClaimMedType.Institutional; } else { claimCur.MedType = EnumClaimMedType.Medical; } break; case "Other": claimCur.PatRelat = relatOther; claimCur.ClaimType = "Other"; //plannum2 is not automatically filled in. claimCur.ClaimForm = claimFormNum; if (planCur.IsMedical) { if (PrefC.GetBool(PrefName.ClaimMedTypeIsInstWhenInsPlanIsMedical)) { claimCur.MedType = EnumClaimMedType.Institutional; } else { claimCur.MedType = EnumClaimMedType.Medical; } } break; } if (planCur.PlanType == "c") //if capitation { claimCur.ClaimType = "Cap"; } claimCur.ProvTreat = proc.ProvNum; if (Providers.GetIsSec(proc.ProvNum)) { claimCur.ProvTreat = Patients.GetPat(proc.PatNum).PriProv; //OK if zero, because auto select first in list when open claim } claimCur.IsProsthesis = "N"; claimCur.ProvBill = Providers.GetBillingProvNum(claimCur.ProvTreat, claimCur.ClinicNum); //OK if zero, because it will get fixed in claim claimCur.EmployRelated = YN.No; claimCur.ClaimForm = planCur.ClaimFormNum; Claims.Insert(claimCur); //attach procedure claimProcCur.ClaimNum = claimCur.ClaimNum; if (planCur.PlanType == "c") //if capitation { claimProcCur.Status = ClaimProcStatus.CapClaim; } else { claimProcCur.Status = ClaimProcStatus.NotReceived; } if (planCur.UseAltCode && (ProcedureCodes.GetProcCode(proc.CodeNum).AlternateCode1 != "")) { claimProcCur.CodeSent = ProcedureCodes.GetProcCode(proc.CodeNum).AlternateCode1; } else if (planCur.IsMedical && proc.MedicalCode != "") { claimProcCur.CodeSent = proc.MedicalCode; } else { claimProcCur.CodeSent = ProcedureCodes.GetProcCode(proc.CodeNum).ProcCode; if (claimProcCur.CodeSent.Length > 5 && claimProcCur.CodeSent.Substring(0, 1) == "D") { claimProcCur.CodeSent = claimProcCur.CodeSent.Substring(0, 5); } if (CultureInfo.CurrentCulture.Name.EndsWith("CA")) //Canadian. en-CA or fr-CA { if (claimProcCur.CodeSent.Length > 5) //In Canadian e-claims, codes can contain letters or numbers and cannot be longer than 5 characters. { claimProcCur.CodeSent = claimProcCur.CodeSent.Substring(0, 5); } } } claimProcCur.LineNumber = (byte)1; ClaimProcs.Update(claimProcCur); return(claimCur); }