///<summary>Called once for each claim to be created. For claims with a lot of procedures, this may actually create multiple claims. ///Normally returns empty string unless something went wrong.</summary> public static bool CreateClaim(Clearinghouse clearinghouseClin, ClaimSendQueueItem queueItem, int batchNum) { StringBuilder strb = new StringBuilder(); string t = "\t"; strb.Append("110\t111\t112\t118\t203/403\tF108/204/404\t205/405\t206\t207\t208\t209\t210\t211\t212\t215\t217\t219\t406\t408\t409\t410\t411\t413\t414\t415\t416\t418\t419\t420\t421\t422\t423\t424\t425\t426\t428\t429\t430\t432\t433\r\n"); Claim claim = Claims.GetClaim(queueItem.ClaimNum); Provider provBill = Providers.GetProv(claim.ProvBill); Patient pat = Patients.GetPat(claim.PatNum); InsPlan insplan = InsPlans.GetPlan(claim.PlanNum, new List <InsPlan>()); InsSub insSub = InsSubs.GetSub(claim.InsSubNum, new List <InsSub>()); Carrier carrier = Carriers.GetCarrier(insplan.CarrierNum); List <ClaimProc> claimProcList = ClaimProcs.Refresh(pat.PatNum); List <ClaimProc> claimProcsForClaim = ClaimProcs.GetForSendClaim(claimProcList, claim.ClaimNum); List <Procedure> procList = Procedures.Refresh(claim.PatNum); Procedure proc; ProcedureCode procCode; //ProcedureCode procCode; for (int i = 0; i < claimProcsForClaim.Count; i++) { //claimProcsForClaim already excludes any claimprocs with ProcNum=0, so no payments etc. proc = Procedures.GetProcFromList(procList, claimProcsForClaim[i].ProcNum); //procCode=Pro strb.Append(provBill.SSN + t); //110 strb.Append(provBill.MedicaidID + t); //111 strb.Append(t); //112 strb.Append(t); //118 strb.Append(pat.SSN + t); //203/403 strb.Append(carrier.CarrierName + t); //carrier name? strb.Append(insSub.SubscriberID + t); strb.Append(pat.PatNum.ToString() + t); strb.Append(pat.Birthdate.ToString("dd-MM-yyyy") + t); if (pat.Gender == PatientGender.Female) { strb.Append("2" + t); //"V"+t); } else { strb.Append("1" + t); //M"+t); } strb.Append("1" + t); strb.Append(DutchLName(pat.LName) + t); //last name without prefix strb.Append(DutchLNamePrefix(pat.LName) + t); //prefix strb.Append("2" + t); strb.Append(DutchInitials(pat) + t); //215. initials strb.Append(pat.Zip + t); strb.Append(DutchAddressNumber(pat.Address) + t); //219 house number. Already validated. strb.Append(t); strb.Append(proc.ProcDate.ToString("dd-MM-yyyy") + t); //procDate procCode = ProcedureCodes.GetProcCode(proc.CodeNum); string strProcCode = procCode.ProcCode; if (strProcCode.EndsWith("00")) //ending with 00 indicates it's a lab code. { strb.Append("02" + t); } else { strb.Append("01" + t); //409. Procedure code (01) or lab costs (02) } strb.Append(t); strb.Append(t); strb.Append(strProcCode + t); strb.Append(GetUL(proc, procCode) + t); //414. U/L. strb.Append(Tooth.ToInternat(proc.ToothNum) + t); strb.Append(Tooth.SurfTidyForClaims(proc.Surf, proc.ToothNum) + t); //needs validation strb.Append(t); if (claim.AccidentRelated == "") //not accident { strb.Append("N" + t); } else { strb.Append("J" + t); } strb.Append(pat.SSN + t); strb.Append(t); strb.Append(t); strb.Append(t); strb.Append(proc.ProcFee.ToString("F") + t); strb.Append("1" + t); strb.Append(proc.ProcFee.ToString("F") + t); strb.Append(t); strb.Append(t); strb.Append(proc.ProcFee.ToString("F") + t); strb.Append(t); strb.Append(t); strb.Append("\r\n"); } string saveFolder = clearinghouseClin.ExportPath; if (!Directory.Exists(saveFolder)) { MessageBox.Show(saveFolder + " " + Lans.g("Dutch", "not found.")); return(false); } string saveFile = ODFileUtils.CombinePaths(saveFolder, "claims" + claim.ClaimNum.ToString() + ".txt"); File.WriteAllText(saveFile, strb.ToString()); //MessageBox.Show(strb.ToString()); return(true); }
///<summary>Called from Eclaims and includes multiple claims.</summary> public static string SendBatch(Clearinghouse clearinghouseClin, List <ClaimSendQueueItem> queueItems, int batchNum) { //STEP 1 - Build XML output. List <DP_RACINDP> listDps = new List <DP_RACINDP>(); List <ProcedureCode> listProcCodes = ProcedureCodes.GetAllCodes(); List <Etrans> listEtrans = new List <Etrans>(); foreach (ClaimSendQueueItem queueItem in queueItems) { Etrans etrans = Etranss.SetClaimSentOrPrinted(queueItem.ClaimNum, queueItem.PatNum, clearinghouseClin.HqClearinghouseNum, EtransType.Claim_Ramq, batchNum, Security.CurUser.UserNum); listEtrans.Add(etrans); //Now we need to update our cache of claims to reflect the change that took place in the database above in Etranss.SetClaimSentOrPrinted() queueItem.ClaimStatus = "S"; Claim claim = Claims.GetClaim(queueItem.ClaimNum); Provider provClaimTreat = Providers.GetProv(claim.ProvTreat); DP_RACINDP dp = new DP_RACINDP(); #region Header dp.CHN = DP_RACINDPCHN.Item06; dp.CHNSpecified = true; dp.ENRG = DP_RACINDPENRG.Item1; dp.ENRGSpecified = true; //We hijack the TaxID number for the TRNSM field. The TRNSM is a office identifying number. Test range for developers is 18000 to 18999. dp.TRNSM = clearinghouseClin.SenderTIN; dp.DISP = provClaimTreat.NationalProvID; //dp.CPTE_ADMN=;//Administrative account number. Not currently used. JulianCalendar calendar = new JulianCalendar(); dp.ATTES = (DateTime.Now.Year % 10).ToString() //One digit for year + calendar.GetDayOfYear(DateTime.Now).ToString().PadLeft(3, '0') //3 digits for Julian day of year. + (etrans.CarrierTransCounter % 1000).ToString().PadLeft(3, '0'); //3 digits for sequence number. dp.NCE = (etrans.CarrierTransCounter % 10000).ToString().PadLeft(4, '0'); dp.DISP_REFNT = claim.CanadianReferralProviderNum.Trim(); //dp.DIAGN=;//Diagnostic code. Not currently used. dp.ETAB = provClaimTreat.CanadianOfficeNum; //Usually empty. //dp.ADMIS=;//Date of patient admission. Not currently used. This would be the same as the date of service for dental claims anyway. //dp.SORTI=;//Date patient discharged. Not currently used. This would be the same as the date of service for dental claims anyway. dp.TOT_DEM = claim.ClaimFee.ToString().Replace(".", "").PadLeft(6, '0'); dp.COMPL = TidyStr(claim.ClaimNote, 200); //dp.CS=;//Not sure what this is. Not currently used. //dp.AUTOR=claim.PreAuthString;//Authorization number when invoicing acrylic prostheses. Required if DAT_AUTOR is present. Not currently used. //dp.DAT_AUTOR=claim.CanadianDateInitialLower;//Date of authorization when invoicing acrylic prostheses. Format YYMMDD. Not currently used. dp.SERV = claim.DateService.ToString("yyMMdd"); #endregion Header #region Insurance //Most fields in the insuranace section are optional. InsSub insSub = InsSubs.GetOne(claim.InsSubNum); dp.PERS_ASSU = new DP_RACINDPPERS_ASSU(); dp.PERS_ASSU.NAM = insSub.SubscriberID; Patient pat = Patients.GetPat(claim.PatNum); dp.PERS_ASSU.PRE = TidyStr(pat.FName, 20); dp.PERS_ASSU.NOM = TidyStr(pat.LName, 30); if (pat.Birthdate.Year > 1880) { dp.PERS_ASSU.NAISS = pat.Birthdate.ToString("yyyyMMdd"); } if (pat.Gender == PatientGender.Male) { dp.PERS_ASSU.SEXE = DP_RACINDPPERS_ASSUSEXE.M; } else if (pat.Gender == PatientGender.Female) { dp.PERS_ASSU.SEXE = DP_RACINDPPERS_ASSUSEXE.F; } else { //There is no value for UNKNOWN. This field is optional if the subscriber ID is present anyway. } List <PatPlan> listPatPlans = PatPlans.Refresh(claim.PatNum); PatPlan patPlan = PatPlans.GetByInsSubNum(listPatPlans, insSub.InsSubNum); dp.PERS_ASSU.CAM = patPlan.PatID; if (insSub.DateTerm.Year > 1880) { dp.PERS_ASSU.EXPIR_CAM = insSub.DateTerm.ToString("yyMM"); } InsPlan insPlan = InsPlans.RefreshOne(claim.PlanNum); InsPlan insPlan2 = InsPlans.RefreshOne(claim.PlanNum2); Carrier carrier = null; if (claim.ClaimType == "S") { carrier = Carriers.GetCarrier(insPlan2.CarrierNum); } else { carrier = Carriers.GetCarrier(insPlan.CarrierNum); } if (carrier.Address.Trim() != "") { dp.PERS_ASSU.ADR_1 = carrier.Address; dp.PERS_ASSU.ADR_2 = carrier.Address2; dp.PERS_ASSU.CP = carrier.Zip; } #endregion Insurance #region Procedures List <ClaimProc> listClaimProcsForPat = ClaimProcs.Refresh(claim.PatNum); List <ClaimProc> listClaimProcsForClaim = ClaimProcs.GetForSendClaim(listClaimProcsForPat, claim.ClaimNum); //Excludes labs. List <Procedure> listProcsForPat = Procedures.Refresh(claim.PatNum); List <DP_RACINDPACTE> listProcs = new List <DP_RACINDPACTE>(); foreach (ClaimProc claimProc in listClaimProcsForClaim) { Procedure proc = Procedures.GetProcFromList(listProcsForPat, claimProc.ProcNum); if (proc.ProcFee == 0) { continue; } ProcedureCode procCode = ProcedureCodes.GetProcCode(proc.CodeNum, listProcCodes); if (procCode.NoBillIns) { continue; } DP_RACINDPACTE acteProc = new DP_RACINDPACTE(); acteProc.ACTE = procCode.ProcCode; if (procCode.ProcCode.Length > 5) { acteProc.ACTE = procCode.ProcCode.Substring(0, 5); } acteProc.ROLE = "1"; //1 for principal role and 2 for assistant role. //acte.MODIF=;//Optional. Not sure what to put here, so leaving blank for now. acteProc.UNIT = proc.UnitQty.ToString().PadLeft(3, '0'); acteProc.MNT = proc.ProcFee.ToString("F").Replace(".", "").PadLeft(6, '0'); acteProc.DENT = proc.ToothNum.ToString().PadLeft(2, '0'); acteProc.SURF = proc.Surf.ToString().PadLeft(2, '0'); listProcs.Add(acteProc); List <Procedure> listLabProcs = Procedures.GetCanadianLabFees(proc.ProcNum, listProcsForPat); foreach (Procedure labProc in listLabProcs) { if (labProc.ProcFee == 0) { continue; } ProcedureCode labProcCode = ProcedureCodes.GetProcCode(labProc.CodeNum, listProcCodes); DP_RACINDPACTE acteLab = new DP_RACINDPACTE(); acteLab.ACTE = labProcCode.ProcCode; if (labProcCode.ProcCode.Length > 5) { acteLab.ACTE = labProcCode.ProcCode.Substring(0, 5); } acteLab.ROLE = "1"; //1 for principal role and 2 for assistant role. acteLab.MNT = labProc.ProcFee.ToString("F").Replace(".", "").PadLeft(6, '0'); listProcs.Add(acteLab); } } dp.ACTE = listProcs.ToArray(); #endregion Procedures listDps.Add(dp); } DP_RACIN batch = new DP_RACIN(); batch.DP = listDps.ToArray(); StringWriter sw = new StringWriter(); XmlSerializer serializer = new XmlSerializer(typeof(DP_RACIN)); serializer.Serialize(sw, batch); string xml = sw.ToString(); //Save a copy of the batch xml to each etrans entry (one per claim). EtransMessageText etransMsgText = new EtransMessageText(); etransMsgText.MessageText = xml; EtransMessageTexts.Insert(etransMsgText); foreach (Etrans etrans in listEtrans) { etrans.EtransMessageTextNum = etransMsgText.EtransMessageTextNum; Etranss.Update(etrans); } //Step 2 - ZIP XML and save to report path. The zip file name and file name within the zip file do not matter. string zipFilePath = CodeBase.ODFileUtils.CreateRandomFile(clearinghouseClin.ExportPath, ".zip", "claims"); ZipFile zip = null; try { zip = new ZipFile(); zip.UseZip64WhenSaving = Zip64Option.Always; zip.AddEntry("claims" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xml", xml); zip.Save(zipFilePath); zip.Dispose(); } catch (Exception ex) { ex.ToString(); if (zip != null) { zip.Dispose(); } if (File.Exists(zipFilePath)) { try { File.Delete(zipFilePath); } catch (Exception ex2) { ex2.ToString(); } } } return(xml); }
///<summary>Sets the MissingData and Warnings fields on the queueItem as appropriate.</summary> public static void GetMissingData(Clearinghouse clearinghouseClin, ClaimSendQueueItem queueItem) { StringBuilder sbErrors = new StringBuilder(); StringBuilder sbWarnings = new StringBuilder(); Claim claim = Claims.GetClaim(queueItem.ClaimNum); Provider provClaimTreat = Providers.GetProv(claim.ProvTreat); InsSub insSub = InsSubs.GetOne(claim.InsSubNum); #region Header //TRNSM if (!Regex.IsMatch(clearinghouseClin.SenderTIN, @"^[0-9]+$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Tax ID Number (RAMQ TRNSM) is invalid"); } else { int trnsm = PIn.Int(clearinghouseClin.SenderTIN); if (clearinghouseClin.ISA15 != "T" && trnsm >= 18000 && trnsm <= 18999) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Tax ID Number (RAMQ TRNSM) is a test value on this production claim"); } else if (clearinghouseClin.ISA15 == "T" && (trnsm < 18000 || trnsm > 18999)) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Tax ID Number (RAMQ TRNSM) must be between 18000 and 18999 for this test claim"); } } //DISP if (!Regex.IsMatch(provClaimTreat.NationalProvID, @"^[27][0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim treating provider CDA Number for RAMQ is invalid"); } //DISP_REFNT if (claim.CanadianReferralProviderNum.Trim().Length > 0 && !Regex.IsMatch(claim.CanadianReferralProviderNum.Trim(), @"^[0-9]{6}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Referral provider CDA Number for RAMQ is invalid"); } //ETAB if (!Regex.IsMatch(provClaimTreat.CanadianOfficeNum, @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim treating provider Office Number for RAMQ is invalid"); } //SERV if (claim.DateService.Year < 1880) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim date of service is invalid or missing"); } #endregion Header #region Insurance //Most fields in the insuranace section are optional and thus do not require validation. //NAM if (!Regex.IsMatch(insSub.SubscriberID, @"^[a-zA-Z]{4}[0-9]{6}[a-zA-Z0-9][0-9]$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Subscriber ID for RAMQ is invalid"); } #endregion Insurance #region Procedures List <ProcedureCode> listProcCodes = ProcedureCodes.GetAllCodes(); List <ClaimProc> listClaimProcsForPat = ClaimProcs.Refresh(claim.PatNum); List <ClaimProc> listClaimProcsForClaim = ClaimProcs.GetForSendClaim(listClaimProcsForPat, claim.ClaimNum); //Excludes labs. List <Procedure> listProcsForPat = Procedures.Refresh(claim.PatNum); foreach (ClaimProc claimProc in listClaimProcsForClaim) { Procedure proc = Procedures.GetProcFromList(listProcsForPat, claimProc.ProcNum); if (proc.ProcFee == 0) { continue; } ProcedureCode procCode = ProcedureCodes.GetProcCode(proc.CodeNum, listProcCodes); if (procCode.NoBillIns) { continue; } //ACTE if (procCode.ProcCode.Length < 5 || !Regex.IsMatch(procCode.ProcCode.Substring(0, 5), @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Procedure code invalid '" + procCode.ProcCode + "'"); } List <Procedure> listLabProcs = Procedures.GetCanadianLabFees(proc.ProcNum, listProcsForPat); foreach (Procedure labProc in listLabProcs) { if (labProc.ProcFee == 0) { continue; } ProcedureCode labProcCode = ProcedureCodes.GetProcCode(labProc.CodeNum, listProcCodes); if (labProcCode.ProcCode.Length < 5 || !Regex.IsMatch(labProcCode.ProcCode.Substring(0, 5), @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Lab code invalid '" + labProcCode.ProcCode + "'"); } } } #endregion Procedures queueItem.MissingData = sbErrors.ToString(); queueItem.Warnings = sbWarnings.ToString(); }
///<summary>Sets the MissingData and Warnings fields on the queueItem as appropriate.</summary> public static void GetMissingData(ClaimSendQueueItem queueItem) { StringBuilder sbErrors = new StringBuilder(); StringBuilder sbWarnings = new StringBuilder(); Claim claim = Claims.GetClaim(queueItem.ClaimNum); Provider provClaimTreat = Providers.GetProv(claim.ProvTreat); InsSub insSub = InsSubs.GetOne(claim.InsSubNum); Patient pat = Patients.GetPat(claim.PatNum); #region Header //DISP if (!Regex.IsMatch(provClaimTreat.NationalProvID, @"^[27][0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim treating provider ID for RAMQ is invalid"); } //DISP_REFNT if (claim.CanadianReferralProviderNum.Trim().Length > 0 && !Regex.IsMatch(claim.CanadianReferralProviderNum.Trim(), @"^[0-9]{6}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Referral provider ID for RAMQ is invalid"); } //ETAB if (!Regex.IsMatch(provClaimTreat.CanadianOfficeNum, @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim treating provider office number is invalid"); } //SERV if (claim.DateService.Year < 1880) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Claim date of service is invalid or missing"); } #endregion Header #region Insurance //Most fields in the insuranace section are optional and thus do not require validation. //NAM if (!Regex.IsMatch(insSub.SubscriberID, @"^[a-zA-Z]{4}[0-9]{6}[a-zA-Z0-9][0-9]$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Subscriber ID invalid"); } #endregion Insurance #region Procedures List <ProcedureCode> listProcCodes = ProcedureCodes.GetAllCodes(); List <ClaimProc> listClaimProcsForPat = ClaimProcs.Refresh(claim.PatNum); List <ClaimProc> listClaimProcsForClaim = ClaimProcs.GetForSendClaim(listClaimProcsForPat, claim.ClaimNum); //Excludes labs. List <Procedure> listProcsForPat = Procedures.Refresh(claim.PatNum); foreach (ClaimProc claimProc in listClaimProcsForClaim) { Procedure proc = Procedures.GetProcFromList(listProcsForPat, claimProc.ProcNum); if (proc.ProcFee == 0) { continue; } ProcedureCode procCode = ProcedureCodes.GetProcCode(proc.CodeNum, listProcCodes); if (procCode.NoBillIns) { continue; } //ACTE if (procCode.ProcCode.Length < 5 || !Regex.IsMatch(procCode.ProcCode.Substring(0, 5), @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Procedure code invalid '" + procCode.ProcCode + "'"); } List <Procedure> listLabProcs = Procedures.GetCanadianLabFees(proc.ProcNum, listProcsForPat); foreach (Procedure labProc in listLabProcs) { if (labProc.ProcFee == 0) { continue; } ProcedureCode labProcCode = ProcedureCodes.GetProcCode(labProc.CodeNum, listProcCodes); if (labProcCode.ProcCode.Length < 5 || !Regex.IsMatch(labProcCode.ProcCode.Substring(0, 5), @"^[0-9]{5}$")) { if (sbErrors.Length != 0) { sbErrors.Append(","); } sbErrors.Append("Lab code invalid '" + labProcCode.ProcCode + "'"); } } } #endregion Procedures queueItem.MissingData = sbErrors.ToString(); queueItem.Warnings = sbWarnings.ToString(); }