///<summary>Set x835 to view all x835.ListClaims info. Otherwise set claimPaid to a specific Hx835_Claim to view.</summary> public FormEtrans835Print(X835 x835, Hx835_Claim claimPaid = null) { InitializeComponent(); Lan.F(this); _x835 = x835; _claimPaid = claimPaid; }
private void FormEtrans835Edit_Load(object sender, EventArgs e) { if (_form835 != null && !_form835.IsDisposed) { if (!MsgBox.Show(this, true, "Opening another ERA will close the current ERA you already have open. Continue?")) { //Form already exists and user wants to keep current instance. TranSetId835 = _form835.TranSetId835; EtransCur = _form835.EtransCur; MessageText835 = _form835.MessageText835; } _form835.Close(); //Always close old form and open new form, so the new copy will come to front, since BringToFront() does not always work. } _form835 = this; //Set the static variable to this form because we're always going to show this form even if they're viewing old information. List <Etrans835Attach> listAttached = Etrans835Attaches.GetForEtrans(EtransCur.EtransNum); _x835 = new X835(EtransCur, MessageText835, TranSetId835, listAttached); RefreshFromDb(); FillAll(); Menu.MenuItemCollection menuItemCollection = new Menu.MenuItemCollection(gridClaimDetailsMenu); List <MenuItem> listMenuItems = new List <MenuItem>(); listMenuItems.Add(new MenuItem(Lan.g(this, "Go to Account"), new EventHandler(gridClaimDetails_RightClickHelper))); listMenuItems.Add(new MenuItem(Lan.g(this, "Add Tracking Status"), new EventHandler(gridClaimDetails_RightClickHelper))); //Enabled by default menuItemCollection.AddRange(listMenuItems.ToArray()); gridClaimDetails.ContextMenu = gridClaimDetailsMenu; gridClaimDetails.AllowSortingByColumn = true; }
///<summary>Returns if given claim is already recieved. Currently ERA test do not consider supplemental.</summary> public static void TryEnterPayment(X835 x835, Hx835_Claim claimPaid, Claim claim, bool isAutomatic) { if (claim.ClaimStatus == "R") //Currently we do not have test for supplemental payments. { return; } EtransL.ImportEraClaimData(x835, claimPaid, claim, isAutomatic); }
public void SetupTest() { #region Create Patient and claim: Justin Smith Patient patJustinSmith = PatientT.CreatePatient(lName: "Smith", fName: "Justin"); List <EraTestProcCodeData> listProcData = new List <EraTestProcCodeData>(); listProcData.Add(new EraTestProcCodeData("D0120", ProcStat.TP, 43, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D1110", ProcStat.TP, 110, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D0602", ProcStat.TP, 5, new DateTime(2017, 09, 26))); _claimPrimaryJustinSmith = EtransT.SetupEraClaim(patJustinSmith, listProcData, "P", "217308827", out List <InsuranceInfo> _listJustinSmithInsuranceInfo); #endregion #region Create Patient and claim: Jacob Jones Patient patJacobJones = PatientT.CreatePatient(lName: "Jones", fName: "Jacob"); listProcData.Clear(); listProcData.Add(new EraTestProcCodeData("D0120", ProcStat.TP, 43, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D1110", ProcStat.TP, 110, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D1206", ProcStat.TP, 68, new DateTime(2017, 09, 26))); _claimPrimaryJacobJones = EtransT.SetupEraClaim(patJacobJones, listProcData, "P", "217180995", out List <InsuranceInfo> _listJacobJonesInsuranceInfo); #endregion #region Create Patient and claim: Stephanie Mayer Patient patStephanieMayer = PatientT.CreatePatient(lName: "Mayer", fName: "Stephanie"); listProcData.Clear(); listProcData.Add(new EraTestProcCodeData("D0120", ProcStat.TP, 43, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D1110", ProcStat.TP, 110, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D0602", ProcStat.TP, 5, new DateTime(2017, 09, 26))); listProcData.Add(new EraTestProcCodeData("D1206", ProcStat.TP, 68, new DateTime(2017, 09, 26))); _claimPrimaryStephanieMayer = EtransT.SetupEraClaim(patStephanieMayer, listProcData, "P", "217439125", out List <InsuranceInfo> _listStephanieMayerInsuranceInfo); #endregion //Create and insert etrans entry Etrans etrans835 = EtransT.Insert835Etrans(Properties.Resources.JustinSmithERA, new DateTime(2017, 09, 30)); //Spoof etrans imported 4 days after claim sent. List <ODTuple <Patient, long> > listPats = new List <ODTuple <Patient, long> >() { new ODTuple <Patient, long>(patJustinSmith, _claimPrimaryJustinSmith.ClaimNum), new ODTuple <Patient, long>(patJacobJones, _claimPrimaryJacobJones.ClaimNum), new ODTuple <Patient, long>(patStephanieMayer, _claimPrimaryStephanieMayer.ClaimNum) }; _x835 = EtransT.Construct835(etrans835, Properties.Resources.JustinSmithERA, listPats, out _listEtrans835Attaches); foreach (Hx835_Claim eraClaim in _x835.ListClaimsPaid) { switch (eraClaim.PatientName.Fname.ToLower()) { case "justin": _eraJustinSmithClaim = eraClaim; break; case "jacob": _eraJacobJonesClaim = eraClaim; break; case "stephanie": _eraStephanieMayerClaim = eraClaim; break; } } }
private void butPrint_Click(object sender, EventArgs e) { Sheet sheet = SheetUtil.CreateSheet(SheetDefs.GetInternalOrCustom(SheetInternalType.ERA)); X835 x835 = _x835.Copy(); x835.ListClaimsPaid = new List <Hx835_Claim>() { _claimPaid }; //Only print the current claim. SheetParameter.GetParamByName(sheet.Parameters, "ERA").ParamValue = x835; //Required param SheetParameter.GetParamByName(sheet.Parameters, "IsSingleClaimPaid").ParamValue = true; //Value is null if not set SheetFiller.FillFields(sheet); SheetPrinting.Print(sheet, isPreviewMode: true); }
///<summary>Builds the X835 for given etrans835 and etransMessageText. ///Also attempts to create attaches for patients in listPatients using their first/last name and the claim num in their tuple entry.</summary> public static X835 Construct835(Etrans etrans835, string etransMessageText, List <ODTuple <Patient, long> > listPatients, out List <Etrans835Attach> listEtrans835Attaches) { listEtrans835Attaches = new List <Etrans835Attach>(); //Construct the inital X835 so that we can find the ERA claim we are testing against, needed for attach information. X835 x835 = new X835(etrans835, etransMessageText, etrans835.TranSetId835); foreach (ODTuple <Patient, long> data in listPatients) { Patient pat = data.Item1; long claimNum = data.Item2; //Then using that ERA claim we will spoof the Etrans835Attach logic that would run when associating an OD claim to the ERA claim. Hx835_Claim eraClaim = x835.ListClaimsPaid.First(x => x.PatientName.Fname.ToLower() == pat.FName.ToLower() && x.PatientName.Lname.ToLower() == pat.LName.ToLower()); //Spoof ERA claim attachement. Etrans835Attach attach = Insert835Attach(etrans835.EtransNum, claimNum, eraClaim.ClpSegmentIndex, etrans835.DateTimeTrans); listEtrans835Attaches.Add(attach); } //Finally we must reconstruct the X835 with the new spoofed attach. return(new X835(etrans835, etransMessageText, etrans835.TranSetId835, listEtrans835Attaches)); }
///<summary>Fills grid based on values in _listEtrans. ///Set isRefreshNeeded to true when we need to reinitialize local dictionarys after in memory list is also updated. Required true for first time running. ///Also allows you to passed in predetermined filter options.</summary> private void FillGrid(bool isRefreshNeeded, List <string> listSelectedStatuses, List <long> listSelectedClinicNums, string carrierName, string checkTraceNum, string amountMin, string amountMax, string controlId) { Cursor = Cursors.WaitCursor; labelControlId.Visible = PrefC.GetBool(PrefName.EraShowControlIdFilter); textControlId.Visible = PrefC.GetBool(PrefName.EraShowControlIdFilter); Action actionCloseProgress = null; if (isRefreshNeeded) { actionCloseProgress = ODProgress.Show(ODEventType.Etrans, typeof(EtransEvent), Lan.g(this, "Gathering data") + "..."); _dictEtrans835s.Clear(); _dictEtransClaims.Clear(); List <Etrans835Attach> listAttached = Etrans835Attaches.GetForEtrans(_listAllEtrans.Select(x => x.EtransNum).ToArray()); Dictionary <long, string> dictEtransMessages = new Dictionary <long, string>(); List <X12ClaimMatch> list835ClaimMatches = new List <X12ClaimMatch>(); Dictionary <long, int> dictClaimMatchCount = new Dictionary <long, int>(); //1:1 with _listEtranss. Stores how many claim matches each 835 has. int batchQueryInterval = 500; //Every 500 rows we get the next 500 message texts to save memory. int rowCur = 0; foreach (Etrans etrans in _listAllEtrans) { if (rowCur % batchQueryInterval == 0) { int range = Math.Min(batchQueryInterval, _listAllEtrans.Count - rowCur); //Either the full batchQueryInterval amount or the remaining amount of etrans. dictEtransMessages = EtransMessageTexts.GetMessageTexts(_listAllEtrans.GetRange(rowCur, range).Select(x => x.EtransMessageTextNum).ToList(), false); } rowCur++; EtransEvent.Fire(ODEventType.Etrans, Lan.g(this, "Processing 835: ") + ": " + rowCur + " out of " + _listAllEtrans.Count); List <Etrans835Attach> listAttachedTo835 = listAttached.FindAll(x => x.EtransNum == etrans.EtransNum); X835 x835 = new X835(etrans, dictEtransMessages[etrans.EtransMessageTextNum], etrans.TranSetId835, listAttachedTo835, true); _dictEtrans835s.Add(etrans.EtransNum, x835); List <X12ClaimMatch> listClaimMatches = x835.GetClaimMatches(); dictClaimMatchCount.Add(etrans.EtransNum, listClaimMatches.Count); list835ClaimMatches.AddRange(listClaimMatches); } #region Set 835 unattached in batch and build _dictEtransClaims and _dictClaimPayCheckNums. EtransEvent.Fire(ODEventType.Etrans, Lan.g(this, "Gathering internal claim matches.")); List <long> listClaimNums = Claims.GetClaimFromX12(list835ClaimMatches); //Can return null. EtransEvent.Fire(ODEventType.Etrans, Lan.g(this, "Building data sets.")); int claimIndexCur = 0; List <long> listMatchedClaimNums = new List <long>(); foreach (Etrans etrans in _listAllEtrans) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; if (listClaimNums != null) { x835.SetClaimNumsForUnattached(listClaimNums.GetRange(claimIndexCur, dictClaimMatchCount[etrans.EtransNum])); } claimIndexCur += dictClaimMatchCount[etrans.EtransNum]; listMatchedClaimNums.AddRange(x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(x => x.ClaimNum).ToList()); } List <Claim> listClaims = Claims.GetClaimsFromClaimNums(listMatchedClaimNums.Distinct().ToList()); //The following line includes manually detached and split attaches. _listAllAttaches = Etrans835Attaches.GetForEtransNumOrClaimNums(false, _listAllEtrans.Select(x => x.EtransNum).ToList(), listMatchedClaimNums.ToArray()); _listAllClaimProcs = ClaimProcs.RefreshForClaims(listMatchedClaimNums); foreach (Etrans etrans in _listAllEtrans) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; #region _dictEtransClaims, _dictClaimPayCheckNums _dictEtransClaims.Add(etrans.EtransNum, new List <Claim>()); List <long> listSubClaimNums = x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(y => y.ClaimNum).ToList(); List <Claim> listClaimsFor835 = listClaims.FindAll(x => listSubClaimNums.Contains(x.ClaimNum)); foreach (Hx835_Claim claim in x835.ListClaimsPaid) { Claim claimCur = listClaimsFor835.FirstOrDefault(x => x.ClaimNum == claim.ClaimNum); //Can be null. _dictEtransClaims[etrans.EtransNum].Add(claimCur); } #endregion } EtransEvent.Fire(ODEventType.Etrans, Lan.g(this, "Filling Grid.")); #endregion } gridMain.BeginUpdate(); #region Initilize columns gridMain.ListGridColumns.Clear(); gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Patient Name"), 250)); gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Carrier Name"), 190)); gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Status"), 80)); gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Date"), 80, GridSortingStrategy.DateParse)); gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Amount"), 80, GridSortingStrategy.AmountParse)); if (PrefC.HasClinicsEnabled) { gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Clinic"), 70)); } gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Code"), 37, HorizontalAlignment.Center)); if (PrefC.GetBool(PrefName.EraShowControlIdFilter)) { gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "ControlID"), -1)); } gridMain.ListGridColumns.Add(new GridColumn(Lan.g("TableEtrans835s", "Note"), -2)); #endregion gridMain.ListGridRows.Clear(); foreach (Etrans etrans in _listAllEtrans) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; #region Filter: Carrier Name if (carrierName != "" && !x835.PayerName.ToLower().Contains(carrierName.ToLower().Trim())) { continue; } #endregion string status = GetStringStatus(etrans.EtransNum); #region Filter: Status if (!listSelectedStatuses.Contains(status.Replace("*", ""))) //The filter will ignore finalized with detached claims. { continue; } #endregion //List of ClinicNums for the current etrans.ListClaimsPaid from the DB. List <long> listClinicNums = _dictEtransClaims[etrans.EtransNum].Select(x => x == null? 0 :x.ClinicNum).Distinct().ToList(); #region Filter: Clinics if (PrefC.HasClinicsEnabled && !listClinicNums.Exists(x => listSelectedClinicNums.Contains(x))) { continue; //The ClinicNums associated to the 835 do not match any of the selected ClinicNums, so nothing to show in this 835. } #endregion #region Filter: Check and Trace Value if (checkTraceNum != "" && !x835.TransRefNum.ToLower().Contains(checkTraceNum.ToLower().Trim())) //Trace Number does not match { continue; } #endregion #region Filter: Insurance Check Range Min and Max if (amountMin != "" && x835.InsPaid < PIn.Decimal(amountMin) || amountMax != "" && x835.InsPaid > PIn.Decimal(amountMax)) { continue; //Either the InsPaid is below or above our range. } #endregion #region Filter: ControlID if (controlId != "" && !x835.ControlId.ToLower().Contains(controlId.ToLower())) { continue; } #endregion GridRow row = new GridRow(); #region Column: Patient Name List <string> listPatNames = x835.ListClaimsPaid.Select(x => x.PatientName.ToString()).Distinct().ToList(); string patName = (listPatNames.Count > 0 ? listPatNames[0] : ""); if (listPatNames.Count > 1) { patName = "(" + POut.Long(listPatNames.Count) + ")"; } row.Cells.Add(patName); #endregion row.Cells.Add(x835.PayerName); row.Cells.Add(status); //See GetStringStatus(...) for possible values. row.Cells.Add(POut.Date(etrans.DateTimeTrans)); row.Cells.Add(POut.Decimal(x835.InsPaid)); #region Column: Clinic if (PrefC.HasClinicsEnabled) { string clinicAbbr = ""; if (listClinicNums.Count == 1) { if (listClinicNums[0] == 0) { clinicAbbr = Lan.g(this, "Unassigned"); } else { clinicAbbr = Clinics.GetAbbr(listClinicNums[0]); } } else if (listClinicNums.Count > 1) { clinicAbbr = "(" + Lan.g(this, "Multiple") + ")"; } row.Cells.Add(clinicAbbr); } #endregion row.Cells.Add(x835._paymentMethodCode); if (PrefC.GetBool(PrefName.EraShowControlIdFilter)) { row.Cells.Add(x835.ControlId); } row.Cells.Add(etrans.Note); row.Tag = etrans; gridMain.ListGridRows.Add(row); } gridMain.EndUpdate(); actionCloseProgress?.Invoke(); //When this function executes quickly this can fail rarely, fail silently because of WaitCursor. Cursor = Cursors.Default; }
private void FormEtrans835Edit_Load(object sender, EventArgs e) { MessageText = EtransMessageTexts.GetMessageText(EtransCur.EtransMessageTextNum); x835 = new X835(MessageText); FillAll(); }
///<summary>Throws exceptions. The insplan that's passed in need not be properly updated to the database first.</summary> ///<returns>The Etrans created from the request. Will be null if the request failed in any way.</returns> public static Etrans RequestBenefits(Clearinghouse clearinghouseClin, InsPlan plan, long patNum, Carrier carrier, InsSub insSub, out string error) { error = ""; Patient pat = Patients.GetPat(patNum); Patient subsc = Patients.GetPat(insSub.Subscriber); Clinic clinic = Clinics.GetClinic(pat.ClinicNum); Provider billProv = Providers.GetProv(Providers.GetBillingProvNum(pat.PriProv, pat.ClinicNum)); //validation. Throw exception if missing info---------------------------------------- string validationResult = X270.Validate(clearinghouseClin, carrier, billProv, clinic, plan, subsc, insSub, pat); if (validationResult != "") { throw new Exception(Lans.g("FormInsPlan", "Please fix the following errors first:") + "\r\n" + validationResult); } //create a 270 message--------------------------------------------------------------- string x12message = X270.GenerateMessageText(clearinghouseClin, carrier, billProv, clinic, plan, subsc, insSub, pat); EtransMessageText etransMessageText = new EtransMessageText(); etransMessageText.MessageText = x12message; EtransMessageTexts.Insert(etransMessageText); //attach it to an etrans------------------------------------------------------------- Etrans etrans = new Etrans(); etrans.PatNum = patNum; etrans.DateTimeTrans = DateTime.Now; etrans.ClearingHouseNum = clearinghouseClin.HqClearinghouseNum; etrans.Etype = EtransType.BenefitInquiry270; etrans.PlanNum = plan.PlanNum; etrans.InsSubNum = insSub.InsSubNum; etrans.EtransMessageTextNum = etransMessageText.EtransMessageTextNum; Etranss.Insert(etrans); //send the 270---------------------------------------------------------------------- string x12response = ""; Etrans etransHtml = null; //a connection error here needs to bubble up try { if (!String.IsNullOrWhiteSpace(FakeResponseOverride271)) { x12response = FakeResponseOverride271; } else if (clearinghouseClin.CommBridge == EclaimsCommBridge.ClaimConnect) { x12response = ClaimConnect.Benefits270(clearinghouseClin, x12message); } else if (clearinghouseClin.CommBridge == EclaimsCommBridge.EDS) { x12response = EDS.Benefits270(clearinghouseClin, x12message, out etransHtml); } else if (clearinghouseClin.CommBridge == EclaimsCommBridge.WebMD) { x12response = WebMD.Benefits270(clearinghouseClin, x12message); } } catch (Exception ex) { EtransMessageTexts.Delete(etrans.EtransMessageTextNum); Etranss.Delete(etrans.EtransNum); throw new ApplicationException(Lans.g("FormInsPlan", "Connection Error:") + "\r\n" + ex.GetType().Name + "\r\n" + ex.Message); } //start to process the 271---------------------------------------------------------- X271 x271 = null; if (X12object.IsX12(x12response)) { X12object x12obj = new X12object(x12response); if (x12obj.Is271()) { x271 = new X271(x12response); } } else //not a 997, 999, 277 or 271 { EtransMessageTexts.Delete(etrans.EtransMessageTextNum); Etranss.Delete(etrans.EtransNum); throw new ApplicationException(Lans.g("FormInsPlan", "Error:") + "\r\n" + x12response); } /* * //In realtime mode, X12 limits the request to one patient. * //We will always use the subscriber. * //So all EB segments are for the subscriber. * List<EB271> listEB=new List<EB271>(); * EB271 eb; * if(x271 != null) { * for(int i=0;i<x271.Segments.Count;i++) { * if(x271.Segments[i].SegmentID != "EB") { * continue; * } * eb=new EB271(x271.Segments[i]); * listEB.Add(eb); * } * }*/ //create an etrans for the 271------------------------------------------------------ etransMessageText = new EtransMessageText(); etransMessageText.MessageText = x12response; EtransMessageTexts.Insert(etransMessageText); Etrans etrans271 = new Etrans(); etrans271.PatNum = patNum; etrans271.DateTimeTrans = DateTime.Now; etrans271.ClearingHouseNum = clearinghouseClin.HqClearinghouseNum; etrans271.Etype = EtransType.TextReport; if (X12object.IsX12(x12response)) //this shouldn't need to be tested because it was tested above. { if (x271 == null) { X12object Xobj = new X12object(x12response); if (Xobj.Is997()) { etrans271.Etype = EtransType.Acknowledge_997; } else if (Xobj.Is999()) { etrans271.Etype = EtransType.Acknowledge_999; } else if (X277.Is277(Xobj)) { etrans271.Etype = EtransType.StatusNotify_277; } else if (X835.Is835(Xobj)) { etrans271.Etype = EtransType.ERA_835; } else if (Xobj.IsAckInterchange()) { etrans271.Etype = EtransType.Ack_Interchange; } } else { etrans271.Etype = EtransType.BenefitResponse271; } } etrans271.PlanNum = plan.PlanNum; etrans271.InsSubNum = insSub.InsSubNum; etrans271.EtransMessageTextNum = etransMessageText.EtransMessageTextNum; etrans271.MessageText = etransMessageText.MessageText; //Not a DB column, used to save queries for some calling methods (OpenDentalService). if (etransHtml != null) { etrans271.AckEtransNum = etransHtml.EtransNum; } Etranss.Insert(etrans271); etrans.AckEtransNum = etrans271.EtransNum; etrans.AckEtrans = etrans271; //Not a DB column, used to save queries for some calling methods (OpenDentalService). if (etrans271.Etype == EtransType.Acknowledge_997) { X997 x997 = new X997(x12response); string error997 = x997.GetHumanReadable(); etrans.Note = "Error: " + error997; //"Malformed document sent. 997 error returned."; Etranss.Update(etrans); error = etrans.Note; return(null); } else if (etrans271.Etype == EtransType.Acknowledge_999) { X999 x999 = new X999(x12response); string error999 = x999.GetHumanReadable(); etrans.Note = "Error: " + error999; //"Malformed document sent. 999 error returned."; Etranss.Update(etrans); error = etrans.Note; return(null); } else if (etrans271.Etype == EtransType.StatusNotify_277) { X277 x277 = new X277(x12response); string error277 = x277.GetHumanReadable(); etrans.Note = "Error: " + error277; //"Malformed document sent. 277 error returned."; Etranss.Update(etrans); error = etrans.Note; return(null); } else if (etrans271.Etype == EtransType.ERA_835) { X835 x835 = new X835(etrans271, x12response, ""); string error835 = x835.GetHumanReadable(); etrans.Note = "Error: " + error835; //"Malformed document sent. 835 error returned."; Etranss.Update(etrans); error = etrans.Note; return(null); } else if (etrans271.Etype == EtransType.BenefitResponse271) //271 { string processingerror = x271.GetProcessingError(); if (processingerror != "") { etrans.Note = processingerror; Etranss.Update(etrans); error = etrans.Note; return(null); } else { etrans.Note = "Normal 271 response."; //change this later to be explanatory of content. } } else if (etrans271.Etype == EtransType.Ack_Interchange) //See document "X092 Elig 270-271.pdf" pages 388 and 401. { X12object xobj = new X12object(x12response); X12Segment segTa1 = xobj.GetNextSegmentById(0, "TA1"); if (segTa1.Get(4) == "A") { etrans.Note = "The request was accepted, but the response is empty."; } else { if (segTa1.Get(4) == "E") { etrans.Note = "The request was accepted with errors: "; } else if (segTa1.Get(4) == "R") { etrans.Note = "The request was rejected with errors: "; } switch (segTa1.Get(5)) { case "000": etrans.Note += "No error"; break; case "001": etrans.Note += "The Interchange Control Number in the Header and Trailer Do Not Match. " + "The Value From the Header is Used in the Acknowledgment."; break; case "002": etrans.Note += "This Standard as Noted in the Control Standards Identifier is Not Supported."; break; case "003": etrans.Note += "This Version of the Controls is Not Supported"; break; case "004": etrans.Note += "The Segment Terminator is Invalid"; break; case "005": etrans.Note += "Invalid Interchange ID Qualifier for Sender"; break; case "006": etrans.Note += "Invalid Interchange Sender ID"; break; case "007": etrans.Note += "Invalid Interchange ID Qualifier for Receiver"; break; case "008": etrans.Note += "Invalid Interchange Receiver ID"; break; case "009": etrans.Note += "Unknown Interchange Receiver ID"; break; case "010": etrans.Note += "Invalid Authorization Information Qualifier Value"; break; case "011": etrans.Note += "Invalid Authorization Information Value"; break; case "012": etrans.Note += "Invalid Security Information Qualifier Value"; break; case "013": etrans.Note += "Invalid Security Information Value"; break; case "014": etrans.Note += "Invalid Interchange Date Value"; break; case "015": etrans.Note += "Invalid Interchange Time Value"; break; case "016": etrans.Note += "Invalid Interchange Standards Identifier Value"; break; case "017": etrans.Note += "Invalid Interchange Version ID Value"; break; case "018": etrans.Note += "Invalid Interchange Control Number Value"; break; case "019": etrans.Note += "Invalid Acknowledgment Requested Value"; break; case "020": etrans.Note += "Invalid Test Indicator Value"; break; case "021": etrans.Note += "Invalid Number of Included Groups Value"; break; case "022": etrans.Note += "Invalid Control Structure"; break; case "023": etrans.Note += "Improper (Premature) End-of-File (Transmission)"; break; case "024": etrans.Note += "Invalid Interchange Content (e.g., Invalid GS Segment)"; break; case "025": etrans.Note += "Duplicate Interchange Control Number"; break; case "026": etrans.Note += "Invalid Data Element Separator"; break; case "027": etrans.Note += "Invalid Component Element Separator"; break; case "028": etrans.Note += "Invalid Delivery Date in Deferred Delivery Request"; break; case "029": etrans.Note += "Invalid Delivery Time in Deferred Delivery Request"; break; case "030": etrans.Note += "Invalid Delivery Time Code in Deferred Delivery Request"; break; case "031": etrans.Note += "Invalid Grade of Service Code"; break; } } } else { throw new Exception("Unknown response"); } Etranss.Update(etrans); return(etrans); }
///<summary>Fills grid based on values in _listEtrans. ///Set isRefreshNeeded to true when we need to reinitialize local dictionarys after in memory list is also updated. Required true for first time running. ///Also allows you to passed in predetermined filter options.</summary> private void FillGrid(bool isRefreshNeeded, List <string> listSelectedStatuses, List <long> listSelectedClinicNums, string carrierName, string checkTraceNum, string amountMin, string amountMax) { Action actionCloseProgress = null; if (isRefreshNeeded) { actionCloseProgress = ODProgressOld.ShowProgressStatus("Etrans835", this, Lan.g(this, "Gathering data") + "...", false); _dictEtrans835s.Clear(); _dictEtransClaims.Clear(); _dictClaimPayExists.Clear(); List <Etrans835Attach> listAttached = Etrans835Attaches.GetForEtrans(_listEtranss.Select(x => x.EtransNum).ToArray()); Dictionary <long, string> dictEtransMessages = new Dictionary <long, string>(); List <X12ClaimMatch> list835ClaimMatches = new List <X12ClaimMatch>(); Dictionary <long, int> dictClaimMatchCount = new Dictionary <long, int>(); //1:1 with _listEtranss. Stores how many claim matches each 835 has. int batchQueryInterval = 500; //Every 500 rows we get the next 500 message texts to save memory. int rowCur = 0; foreach (Etrans etrans in _listEtranss) { if (rowCur % batchQueryInterval == 0) { int range = Math.Min(batchQueryInterval, _listEtranss.Count - rowCur); //Either the full batchQueryInterval amount or the remaining amount of etrans. dictEtransMessages = EtransMessageTexts.GetMessageTexts(_listEtranss.GetRange(rowCur, range).Select(x => x.EtransMessageTextNum).ToList(), false); } rowCur++; ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Processing 835: ") + ": " + rowCur + " out of " + _listEtranss.Count)); List <Etrans835Attach> listAttachedTo835 = listAttached.FindAll(x => x.EtransNum == etrans.EtransNum); X835 x835 = new X835(etrans, dictEtransMessages[etrans.EtransMessageTextNum], etrans.TranSetId835, listAttachedTo835, true); _dictEtrans835s.Add(etrans.EtransNum, x835); List <X12ClaimMatch> listClaimMatches = x835.GetClaimMatches(); dictClaimMatchCount.Add(etrans.EtransNum, listClaimMatches.Count); list835ClaimMatches.AddRange(listClaimMatches); } #region Set 835 unattached in batch and build _dictEtransClaims and _dictClaimPayCheckNums. ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Gathering internal claim matches."))); List <long> listClaimNums = Claims.GetClaimFromX12(list835ClaimMatches); //Can return null. ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Building data sets."))); int claimIndexCur = 0; List <long> listMatchedClaimNums = new List <long>(); foreach (Etrans etrans in _listEtranss) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; if (listClaimNums != null) { x835.SetClaimNumsForUnattached(listClaimNums.GetRange(claimIndexCur, dictClaimMatchCount[etrans.EtransNum])); } claimIndexCur += dictClaimMatchCount[etrans.EtransNum]; listMatchedClaimNums.AddRange(x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(x => x.ClaimNum).ToList()); } List <Claim> listClaims = Claims.GetClaimsFromClaimNums(listMatchedClaimNums.Distinct().ToList()); _dictClaimPayExists = ClaimPayments.HasClaimPayment(listMatchedClaimNums); //Every claim num is associated to a bool. True when there is an existing claimPayment. foreach (Etrans etrans in _listEtranss) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; #region _dictEtransClaims, _dictClaimPayCheckNums _dictEtransClaims.Add(etrans.EtransNum, new List <Claim>()); List <long> listSubClaimNums = x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(y => y.ClaimNum).ToList(); List <Claim> listClaimsFor835 = listClaims.FindAll(x => listSubClaimNums.Contains(x.ClaimNum)); foreach (Hx835_Claim claim in x835.ListClaimsPaid) { Claim claimCur = listClaimsFor835.FirstOrDefault(x => x.ClaimNum == claim.ClaimNum); //Can be null. if (claimCur == null && claim.IsAttachedToClaim && claim.ClaimNum == 0) { claimCur = new Claim(); //Create empty claim since user detached claim manually, will not be considered in GetStringStatus(...). } if (claimCur != null && claim.IsPreauth) //User attached preauth to internal claim, no payment needed to be considered 'Finalized' in GetStringStatus(...). { _dictClaimPayExists[claim.ClaimNum] = true; } _dictEtransClaims[etrans.EtransNum].Add(claimCur); } #endregion } ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Filling Grid."))); #endregion } gridMain.BeginUpdate(); #region Initilize columns only once if (gridMain.Columns.Count == 0) { ODGridColumn col; col = new ODGridColumn(Lan.g("TableEtrans835s", "Patient Name"), 250); gridMain.Columns.Add(col); col = new ODGridColumn(Lan.g("TableEtrans835s", "Carrier Name"), 190); gridMain.Columns.Add(col); col = new ODGridColumn(Lan.g("TableEtrans835s", "Status"), 80); gridMain.Columns.Add(col); col = new ODGridColumn(Lan.g("TableEtrans835s", "Date"), 80); gridMain.Columns.Add(col); col = new ODGridColumn(Lan.g("TableEtrans835s", "Amount"), 80); gridMain.Columns.Add(col); if (PrefC.HasClinicsEnabled) { col = new ODGridColumn(Lan.g("TableEtrans835s", "Clinic"), 70); gridMain.Columns.Add(col); } col = new ODGridColumn(Lan.g("TableEtrans835s", "Code"), 37, HorizontalAlignment.Center); gridMain.Columns.Add(col); col = new ODGridColumn(Lan.g("TableEtrans835s", "Note"), 0); gridMain.Columns.Add(col); } #endregion gridMain.Rows.Clear(); foreach (Etrans etrans in _listEtranss) { X835 x835 = _dictEtrans835s[etrans.EtransNum]; #region Filter: Carrier Name if (carrierName != "" && !x835.PayerName.ToLower().Contains(carrierName.ToLower())) { continue; } #endregion string status = GetStringStatus(etrans.EtransNum); #region Filter: Status if (!listSelectedStatuses.Contains(status.Replace("*", ""))) //The filter will ignore finalized with detached claims. { continue; } #endregion //List of ClinicNums for the current etrans.ListClaimsPaid from the DB. List <long> listClinicNums = _dictEtransClaims[etrans.EtransNum].Select(x => x == null? 0 :x.ClinicNum).Distinct().ToList(); #region Filter: Clinics if (PrefC.HasClinicsEnabled && !listClinicNums.Exists(x => listSelectedClinicNums.Contains(x))) { continue; //The ClinicNums associated to the 835 do not match any of the selected ClinicNums, so nothing to show in this 835. } #endregion #region Filter: Check and Trace Value if (checkTraceNum != "" && !x835.TransRefNum.Contains(checkTraceNum)) //Trace Number does not match { continue; } #endregion #region Filter: Insurance Check Range Min and Max if (amountMin != "" && x835.InsPaid < PIn.Decimal(amountMin) || amountMax != "" && x835.InsPaid > PIn.Decimal(amountMax)) { continue; //Either the InsPaid is below or above our range. } #endregion ODGridRow row = new ODGridRow(); #region Column: Patient Name List <string> listPatNames = x835.ListClaimsPaid.Select(x => x.PatientName.ToString()).Distinct().ToList(); string patName = (listPatNames.Count > 0 ? listPatNames[0] : ""); if (listPatNames.Count > 1) { patName = "(" + POut.Long(listPatNames.Count) + ")"; } row.Cells.Add(patName); #endregion row.Cells.Add(x835.PayerName); row.Cells.Add(status); //See GetStringStatus(...) for possible values. row.Cells.Add(POut.Date(etrans.DateTimeTrans)); row.Cells.Add(POut.Decimal(x835.InsPaid)); #region Column: Clinic if (PrefC.HasClinicsEnabled) { string clinicAbbr = ""; if (listClinicNums.Count == 1) { if (listClinicNums[0] == 0) { clinicAbbr = Lan.g(this, "Unassigned"); } else { clinicAbbr = Clinics.GetAbbr(listClinicNums[0]); } } else if (listClinicNums.Count > 1) { clinicAbbr = "(" + Lan.g(this, "Multiple") + ")"; } row.Cells.Add(clinicAbbr); } #endregion row.Cells.Add(x835._paymentMethodCode); row.Cells.Add(etrans.Note); row.Tag = etrans; gridMain.Rows.Add(row); } gridMain.EndUpdate(); actionCloseProgress?.Invoke(); }
///<summary>Etrans type will be figured out by this class. Either TextReport, Acknowledge_997, Acknowledge_999, or StatusNotify_277.</summary> public static void ProcessIncomingReport(DateTime dateTimeTrans,long hqClearinghouseNum,string messageText,long userNum) { if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) { Meth.GetVoid(MethodBase.GetCurrentMethod(),dateTimeTrans,hqClearinghouseNum,messageText,userNum); return; } Etrans etrans=CreateEtrans(dateTimeTrans,hqClearinghouseNum,messageText,userNum); string command; X12object Xobj=X12object.ToX12object(messageText); if(Xobj!=null) {//Is a correctly formatted X12 message. if(Xobj.IsAckInterchange()) { etrans.Etype=EtransType.Ack_Interchange; Etranss.Insert(etrans); //At some point in the future, we should use TA101 to match to batch number and TA104 to get the ack code, //then update historic etrans entries like we do for 997s, 999s and 277s. } else if(Xobj.Is997()) { X997 x997=new X997(messageText); etrans.Etype=EtransType.Acknowledge_997; etrans.BatchNumber=x997.GetBatchNumber(); Etranss.Insert(etrans); string batchack=x997.GetBatchAckCode(); if(batchack=="A"||batchack=="R") {//accepted or rejected command="UPDATE etrans SET AckCode='"+batchack+"', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE BatchNumber="+POut.Long(etrans.BatchNumber) +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)) +" AND AckEtransNum=0"; Db.NonQ(command); } else {//partially accepted List<int> transNums=x997.GetTransNums(); string ack; for(int i=0;i<transNums.Count;i++) { ack=x997.GetAckForTrans(transNums[i]); if(ack=="A"||ack=="R") {//accepted or rejected command="UPDATE etrans SET AckCode='"+ack+"', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE BatchNumber="+POut.Long(etrans.BatchNumber) +" AND TransSetNum="+POut.Long(transNums[i]) +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)) +" AND AckEtransNum=0"; Db.NonQ(command); } } } //none of the other fields make sense, because this ack could refer to many claims. } else if(Xobj.Is999()) { X999 x999=new X999(messageText); etrans.Etype=EtransType.Acknowledge_999; etrans.BatchNumber=x999.GetBatchNumber(); Etranss.Insert(etrans); string batchack=x999.GetBatchAckCode(); if(batchack=="A"||batchack=="R") {//accepted or rejected command="UPDATE etrans SET AckCode='"+batchack+"', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE BatchNumber="+POut.Long(etrans.BatchNumber) +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)) +" AND AckEtransNum=0"; Db.NonQ(command); } else {//partially accepted List<int> transNums=x999.GetTransNums(); string ack; for(int i=0;i<transNums.Count;i++) { ack=x999.GetAckForTrans(transNums[i]); if(ack=="A"||ack=="R") {//accepted or rejected command="UPDATE etrans SET AckCode='"+ack+"', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE BatchNumber="+POut.Long(etrans.BatchNumber) +" AND TransSetNum="+POut.Long(transNums[i]) +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)) +" AND AckEtransNum=0"; Db.NonQ(command); } } } //none of the other fields make sense, because this ack could refer to many claims. } else if(X277.Is277(Xobj)) { X277 x277=new X277(messageText); etrans.Etype=EtransType.StatusNotify_277; Etranss.Insert(etrans); List<string> listClaimIdentifiers=x277.GetClaimTrackingNumbers(); //Dictionary to run one update command per ack code for many claims. Dictionary <string,List<X12ClaimMatch>> dictClaimMatchesByAck=new Dictionary<string,List<X12ClaimMatch>>(); for(int i=0;i<listClaimIdentifiers.Count;i++) { X12ClaimMatch claimMatch=new X12ClaimMatch(); claimMatch.ClaimIdentifier=listClaimIdentifiers[i]; string[] arrayClaimInfo=x277.GetClaimInfo(claimMatch.ClaimIdentifier); claimMatch.PatFname=PIn.String(arrayClaimInfo[0]); claimMatch.PatLname=PIn.String(arrayClaimInfo[1]); claimMatch.DateServiceStart=PIn.DateT(arrayClaimInfo[6]); claimMatch.DateServiceEnd=PIn.DateT(arrayClaimInfo[7]); claimMatch.ClaimFee=PIn.Double(arrayClaimInfo[9]); claimMatch.SubscriberId=PIn.String(arrayClaimInfo[10]); claimMatch.EtransNum=etrans.EtransNum; string ack=arrayClaimInfo[3]; if(!dictClaimMatchesByAck.ContainsKey(ack)) { dictClaimMatchesByAck.Add(ack,new List<X12ClaimMatch>()); } dictClaimMatchesByAck[ack].Add(claimMatch); } foreach(string ack in dictClaimMatchesByAck.Keys) { List <long> listClaimNums=Claims.GetClaimFromX12(dictClaimMatchesByAck[ack]); if(listClaimNums!=null) { listClaimNums=listClaimNums.Where(x => x!=0).ToList(); if(listClaimNums.Count > 0) { //Locate the latest etrans entries for the claims based on DateTimeTrans with EType of ClaimSent or Claim_Ren and update the AckCode and AckEtransNum. //We overwrite existing acks from 997s, 999s and older 277s. command="UPDATE etrans SET AckCode='"+ack+"', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE EType IN ("+POut.Int((int)EtransType.ClaimSent)+","+POut.Int((int)EtransType.Claim_Ren)+") " +" AND ClaimNum IN("+String.Join(",",listClaimNums.Select(x => POut.Long(x)))+")" +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)); Db.NonQ(command); } } //none of the other fields make sense, because this ack could refer to many claims. } } else if(X835.Is835(Xobj)) { etrans.Etype=EtransType.ERA_835; List <string> listTranSetIds=Xobj.GetTranSetIds(); List <Etrans> listEtrans=new List<Etrans>(); List <X835> list835s=new List<X835>(); //We pull in the 835 data in two loops so that we can ensure the 835 is fully parsed before we create any etrans entries. for(int i=0;i<listTranSetIds.Count;i++) { etrans.TranSetId835=listTranSetIds[i]; if(i>0) { etrans.EtransNum=0;//To get a new record to insert. } X835 x835=new X835(etrans,messageText,etrans.TranSetId835);//parse. If parsing fails, then no etrans entries will be inserted. etrans.CarrierNameRaw=x835.PayerName; List<string> listUniquePatientNames=new List<string>(); for(int j=0;j<x835.ListClaimsPaid.Count;j++) { string patName=x835.ListClaimsPaid[j].PatientName.ToString(false); if(!listUniquePatientNames.Contains(patName)) { listUniquePatientNames.Add(patName); } } if(listUniquePatientNames.Count==1) { etrans.PatientNameRaw=listUniquePatientNames[0]; } else { etrans.PatientNameRaw="("+listUniquePatientNames.Count+" "+Lans.g("Etranss","patients")+")"; } listEtrans.Add(etrans.Copy()); list835s.Add(x835); } //The 835 was completely parsed. Create etrans entries. for(int i=0;i<listEtrans.Count;i++) { etrans=listEtrans[i]; X835 x835=list835s[i]; Etranss.Insert(etrans);//insert List<long> listClaimNums=x835.ListClaimsPaid.Select(x => x.ClaimNum).Where(x => x!=0).ToList(); if(listClaimNums.Count > 0) { //Locate the latest etrans entries for the claim based on DateTimeTrans with EType of ClaimSent or Claim_Ren and update the AckCode and AckEtransNum. //We overwrite existing acks from 997s, 999s, and 277s. command="UPDATE etrans SET AckCode='A', " +"AckEtransNum="+POut.Long(etrans.EtransNum) +" WHERE EType IN (0,3) "//ClaimSent and Claim_Ren +" AND ClaimNum IN("+String.Join(",",listClaimNums.Select(x => POut.Long(x)))+")" +" AND ClearinghouseNum="+POut.Long(hqClearinghouseNum) +" AND DateTimeTrans > "+POut.DateT(dateTimeTrans.AddDays(-14)) +" AND DateTimeTrans < "+POut.DateT(dateTimeTrans.AddDays(1)); Db.NonQ(command); } //none of the other fields make sense, because this ack could refer to many claims. } } else {//unknown type of X12 report. etrans.Etype=EtransType.TextReport; Etranss.Insert(etrans); } } else {//not X12 etrans.Etype=EtransType.TextReport; Etranss.Insert(etrans); } }