///<summary>Replaces variable tags with the information from the patient passed in.</summary> public static string ReplaceVarsForSms(string smsTemplate, Patient pat, Statement stmt) { //No need to check RemotingRole; no call to db. StringBuilder retVal = new StringBuilder(); retVal.Append(smsTemplate); if (smsTemplate.Contains("[monthlyCardsOnFile]")) { retVal.RegReplace("\\[monthlyCardsOnFile]", CreditCards.GetMonthlyCardsOnFile(pat.PatNum)); } retVal.RegReplace("\\[nameF]", pat.GetNameFirst()); retVal.RegReplace("\\[namePref]", pat.Preferred); retVal.RegReplace("\\[PatNum]", pat.PatNum.ToString()); retVal.RegReplace("\\[currentMonth]", DateTime.Now.ToString("MMMM")); Clinic clinic = Clinics.GetClinic(pat.ClinicNum) ?? Clinics.GetPracticeAsClinicZero(); string officePhone = clinic.Phone; if (string.IsNullOrEmpty(officePhone)) { officePhone = PrefC.GetString(PrefName.PracticePhone); } retVal.RegReplace("\\[OfficePhone]", TelephoneNumbers.ReFormat(officePhone)); string officeName = clinic.Description; if (string.IsNullOrEmpty(officeName)) { officeName = PrefC.GetString(PrefName.PracticeTitle); } retVal.RegReplace("\\[OfficeName]", officeName); retVal.RegReplace("\\[StatementURL]", stmt.StatementURL); retVal.RegReplace("\\[StatementShortURL]", stmt.StatementShortURL); return(retVal.ToString()); }
///<summary></summary> private static void UpdateCarrierInDb(OpenDentBusiness.Carrier odCarrier, Carrier jsonCarrier, List <CanadianNetwork> listCanadianNetworks , ItransImportFields fieldsToImport, string jsonCarrierPhone, bool isAutomatic) { OpenDentBusiness.Carrier odCarrierOld = odCarrier.Copy(); odCarrier.CanadianEncryptionMethod = 1; //Default. Deprecated for all Canadian carriers and will never be any other value. TrySetCanadianNetworkNum(jsonCarrier, odCarrier, listCanadianNetworks); odCarrier.CanadianSupportedTypes = GetSupportedTypes(jsonCarrier); odCarrier.CDAnetVersion = POut.Int(jsonCarrier.Versions.Max(x => PIn.Int(x))).PadLeft(2, '0');//Version must be in 2 digit format. ex. 02. List <ItransImportFields> listFields = Enum.GetValues(typeof(ItransImportFields)).Cast <ItransImportFields>().ToList(); foreach (ItransImportFields field in listFields) { if (fieldsToImport == ItransImportFields.None) { break; //No point in looping. } if (field == ItransImportFields.None || !fieldsToImport.HasFlag(field)) { continue; } switch (field) { case ItransImportFields.Phone: odCarrier.Phone = TelephoneNumbers.AutoFormat(jsonCarrierPhone); break; case ItransImportFields.Address: if (jsonCarrier.Address.Count() > 0) { Address add = jsonCarrier.Address.First(); odCarrier.Address = add.Street1; odCarrier.Address2 = add.Street2; odCarrier.City = add.City; odCarrier.State = add.Province; odCarrier.Zip = add.Postal_Code; } break; case ItransImportFields.Name: odCarrier.CarrierName = jsonCarrier.Name.En; break; } } try { long userNum = 0; if (!isAutomatic) { userNum = Security.CurUser.UserNum; } Carriers.Update(odCarrier, odCarrierOld, userNum); } catch (Exception ex) { ex.DoNothing(); } }
///<summary>Returns a blank string if there were no errors while attempting to update internal carriers using iTrans n-cpl.json file.</summary> public static string TryCarrierUpdate(bool isAutomatic = true, ItransImportFields fieldsToImport = ItransImportFields.None) { string json; DateTime dateTimeTrans = DateTime.Now; Clearinghouse clearinghouse = Clearinghouses.GetDefaultDental(); if (clearinghouse == null) { return(Lans.g("Clearinghosue", "Unable to update. No default dental clearinghouse set.")); } //If ITRANS2 is fully setup, then use the local ITRANS2 install on server to import carrier data. if (clearinghouse.CommBridge == EclaimsCommBridge.ITRANS && !string.IsNullOrEmpty(clearinghouse.ResponsePath)) { if (!File.Exists(ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "ITRANS Claims Director.exe"))) { return(Lans.g("Clearinghouse", "Unable to find 'ITRANS Claims Director.exe'. Make sure the file exists and the path is correct.")); } if (isAutomatic && PrefC.GetString(PrefName.WebServiceServerName).ToLower() != Dns.GetHostName().ToLower()) //Only server can run when isOnlyServer is true. { return(Lans.g("Clearinghouse", "Update can only run on the web service server " + PrefC.GetString(PrefName.WebServiceServerName)) + ". " + Lans.g("Clearinghouse", "Connect to the server and try again.")); } Process process = new Process { StartInfo = new ProcessStartInfo { FileName = ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "ITRANS Claims Director.exe"), Arguments = " --getncpl" } }; process.Start(); process.WaitForExit(); string ncplFilePath = ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "n-cpl.json"); json = File.ReadAllText(ncplFilePath); //Read n-cpl.json dateTimeTrans = File.GetCreationTime(ncplFilePath); } else //ITRANS2 not used or not setup correctly, go to HQ for file content. { try { string result = WebServiceMainHQProxy.GetWebServiceMainHQInstance().CanadaCarrierUpdate(PayloadHelper.CreatePayload("", eServiceCode.Undefined)); json = WebSerializer.DeserializePrimitiveOrThrow <string>(result); } catch (Exception ex) { return(Lans.g("Clearinghouse", "Unable to update carrier list from HQ web services.") + "\r\n" + ex.Message.ToString()); } } EtransMessageText msgTextPrev = EtransMessageTexts.GetMostRecentForType(EtransType.ItransNcpl); if (msgTextPrev != null && msgTextPrev.MessageText == json) { if (isAutomatic || ODMessageBox.Show("Carrier list has not changed since last checked.\r\nContinue?", "", MessageBoxButtons.YesNo) != DialogResult.Yes) { return(Lans.g("Clearinghouse", "Carrier list has not changed since last checked.")); //json has not changed since we last checked, no need to update. } } //Save json as new etrans entry. Etrans etrans = Etranss.CreateEtrans(dateTimeTrans, clearinghouse.HqClearinghouseNum, json, 0); etrans.Etype = EtransType.ItransNcpl; Etranss.Insert(etrans); ItransNCpl iTransNCpl = null; try { iTransNCpl = JsonConvert.DeserializeObject <ItransNCpl>(json); //Deserialize n-cpl.json } catch (Exception ex) { ex.DoNothing(); return(Lans.g("Clearinghouse", "Failed to import json.")); } List <CanadianNetwork> listCanadianNetworks = CanadianNetworks.GetDeepCopy(); //List of carriers from json file that were matched by electId to multiple internal carriers List <Carrier> listUnmatchedJsonCarriers = new List <Carrier>(); List <long> listMatchedDbCarrierNums = new List <long> (); foreach (ItransNCpl.Carrier jsonCarrier in iTransNCpl.ListCarriers) //Update carriers. { string jsonCarrierPhone = jsonCarrier.Telephone?.First().Value; //Will be empty string if not found. List <OpenDentBusiness.Carrier> listDbCarriers = Carriers.GetAllByElectId(jsonCarrier.Bin).FindAll(x => x.IsCDA); if (listDbCarriers.Count > 1) //Some Canadian carriers share ElectId, need to filter further. This happens with carrier resellers. { #region Additional matching based on phone numbers, 'continues' loop if a single match is not found. List <OpenDentBusiness.Carrier> listPhoneMatchedDbCarriers = listDbCarriers.FindAll(x => TelephoneNumbers.AreNumbersEqual(x.Phone, jsonCarrierPhone) ); if (listPhoneMatchedDbCarriers.Count != 1) //Either 0 or multiple matches, either way do not update any carriers. //When 0 matches found: jsonCarrier changed their phone number, can not determine which carrier to update. //E.G. - JsonCarrier A matched to OD carriers B and C by electId. //Phone number from JsonCarrier A did not match either carrier B or C due to jsonCarrier phone number change. //Future iterations for jsonCarrier D might match to carrier B or C if phone number for jsonCarrier D did not change. //If jsonCarrier D is matched to single OD carrier, then jsonCarrier A will attempt to match near end of method to a unmatched internal carrier. //If ther are no future matches to OD carrier B or C then all unmatched jsonCarriers will not be imported and no OD carries will not be updated. //----------------------------------------------------------------------// //When greater than 1: jsonCarrier number not changed and both internal carriers have matching electIds and phone numbers. //This should be rare, most likely a setup error. //There should not be multiple carriers that share electId and phone numbers. User should change or remove one of the matched carriers. { listUnmatchedJsonCarriers.Add(jsonCarrier); continue; } listDbCarriers = listPhoneMatchedDbCarriers; #endregion } //At this point listDbCarriers should either be empty or contain a single OD carrier. OpenDentBusiness.Carrier carrierInDb = listDbCarriers.FirstOrDefault(); //Null if list is empty. if (carrierInDb == null) //Carrier can not be matched to internal Carrier based on ElectID. { #region Insert new carrier if (!fieldsToImport.HasFlag(ItransImportFields.AddMissing)) { continue; } OpenDentBusiness.Carrier carrierNew = new OpenDentBusiness.Carrier(); carrierNew.CanadianEncryptionMethod = 1; //Default. Deprecated for all Canadian carriers and will never be any other value. TrySetCanadianNetworkNum(jsonCarrier, carrierNew, listCanadianNetworks); carrierNew.ElectID = jsonCarrier.Bin; carrierNew.IsCDA = true; carrierNew.CarrierName = jsonCarrier.Name.En; carrierNew.Phone = TelephoneNumbers.AutoFormat(jsonCarrierPhone); if (jsonCarrier.Address.Count() > 0) { Address add = jsonCarrier.Address.First(); carrierNew.Address = add.Street1; carrierNew.Address2 = add.Street2; carrierNew.City = add.City; carrierNew.State = add.Province; carrierNew.Zip = add.Postal_Code; } carrierNew.CanadianSupportedTypes = GetSupportedTypes(jsonCarrier); carrierNew.CDAnetVersion = POut.Int(jsonCarrier.Versions.Max(x => PIn.Int(x))).PadLeft(2, '0'); //Version must be in 2 digit format. ex. 02. carrierNew.CarrierName = jsonCarrier.Name.En; try { Carriers.Insert(carrierNew); } catch (Exception ex) { ex.DoNothing(); } #endregion continue; } listMatchedDbCarrierNums.Add(carrierInDb.CarrierNum); UpdateCarrierInDb(carrierInDb, jsonCarrier, listCanadianNetworks, fieldsToImport, jsonCarrierPhone, isAutomatic); } foreach (Carrier jsonCarrier in listUnmatchedJsonCarriers) { List <OpenDentBusiness.Carrier> listDbCarriers = Carriers.GetWhere(x => x.IsCDA && x.ElectID == jsonCarrier.Bin && !listMatchedDbCarrierNums.Contains(x.CarrierNum) ); if (listDbCarriers.Count != 1) //Either 0 or multiple matches, either way do not update any carriers. { continue; } OpenDentBusiness.Carrier carrierInDb = listDbCarriers.FirstOrDefault(); string jsonCarrierPhone = jsonCarrier.Telephone?.First().Value; UpdateCarrierInDb(carrierInDb, jsonCarrier, listCanadianNetworks, fieldsToImport, jsonCarrierPhone, isAutomatic); } return(""); //Blank string represents a completed update. }
///<summary>Returns a blank string if there were no errors while attempting to update internal carriers using iTrans n-cpl.json file..</summary> public static string TryCarrierUpdate(bool isAutomatic = true, ItransImportFields fieldsToImport = ItransImportFields.None) { Clearinghouse clearinghouse = Clearinghouses.GetDefaultDental(); if (clearinghouse.CommBridge != EclaimsCommBridge.ITRANS || string.IsNullOrEmpty(clearinghouse.ResponsePath) || !File.Exists(ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "ITRANS Claims Director.exe")) || (isAutomatic && PrefC.GetString(PrefName.WebServiceServerName).ToLower() != Dns.GetHostName().ToLower())) //Only server can run when isOnlyServer is true. { return(Lans.g("Clearinghouse", "ITRANS must be the default dental clearinghouse and your Report Path must be set first.")); } Process process = new Process { StartInfo = new ProcessStartInfo { FileName = ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "ITRANS Claims Director.exe"), Arguments = " --getncpl" } }; process.Start(); process.WaitForExit(); string ncplFilePath = ODFileUtils.CombinePaths(clearinghouse.ResponsePath, "n-cpl.json"); string json = File.ReadAllText(ncplFilePath);//Read n-cpl.json EtransMessageText msgTextPrev = EtransMessageTexts.GetMostRecentForType(EtransType.ItransNcpl); if (msgTextPrev != null && msgTextPrev.MessageText == json) { return(Lans.g("Clearinghouse", "Carrier list has not changed since last checked.")); //json has not changed since we last checked, no need to update. } //Save json as new etrans entry. Etrans etrans = Etranss.CreateEtrans(File.GetCreationTime(ncplFilePath), clearinghouse.HqClearinghouseNum, json, 0); etrans.Etype = EtransType.ItransNcpl; Etranss.Insert(etrans); ItransNCpl iTransNCpl = null; try { iTransNCpl = JsonConvert.DeserializeObject <ItransNCpl>(json); //Deserialize n-cpl.json } catch (Exception ex) { ex.DoNothing(); return(Lans.g("Clearinghouse", "Failed to import json.")); } foreach (ItransNCpl.Carrier jsonCarrier in iTransNCpl.ListCarriers) //Update providers. { OpenDentBusiness.Carrier odCarrier = Carriers.GetByElectId(jsonCarrier.Bin); //Cached if (odCarrier == null) //Carrier can not be matched to internal Carrier based on ElectID. { if (!fieldsToImport.HasFlag(ItransImportFields.AddMissing)) { continue; } OpenDentBusiness.Carrier carrierNew = new OpenDentBusiness.Carrier(); carrierNew.ElectID = jsonCarrier.Bin; carrierNew.IsCDA = true; carrierNew.CarrierName = jsonCarrier.Name.En; carrierNew.Phone = TelephoneNumbers.ReFormat(jsonCarrier.Telephone?.First().Value); if (jsonCarrier.Address.Count() > 0) { Address add = jsonCarrier.Address.First(); carrierNew.Address = add.Street1; carrierNew.Address2 = add.Street2; carrierNew.City = add.City; carrierNew.State = add.Province; carrierNew.Zip = add.PostalCode; } carrierNew.CanadianSupportedTypes = GetSupportedTypes(jsonCarrier); carrierNew.CarrierName = jsonCarrier.Name.En; try { Carriers.Insert(carrierNew); } catch (Exception ex) { ex.DoNothing(); } continue; } else if (!odCarrier.IsCDA) { continue; } OpenDentBusiness.Carrier odCarrierOld = odCarrier.Copy(); odCarrier.CanadianSupportedTypes = GetSupportedTypes(jsonCarrier); odCarrier.CDAnetVersion = POut.Int(jsonCarrier.Versions.Max(x => PIn.Int(x))); List <ItransImportFields> listFields = Enum.GetValues(typeof(ItransImportFields)).Cast <ItransImportFields>().ToList(); foreach (ItransImportFields field in listFields) { if (fieldsToImport == ItransImportFields.None) { break; //No point in looping. } if (field == ItransImportFields.None || !fieldsToImport.HasFlag(field)) { continue; } switch (field) { case ItransImportFields.Phone: if (jsonCarrier.Telephone.Count > 0) { odCarrier.Phone = TelephoneNumbers.ReFormat(jsonCarrier.Telephone.First().Value); } break; case ItransImportFields.Address: if (jsonCarrier.Address.Count() > 0) { Address add = jsonCarrier.Address.First(); odCarrier.Address = add.Street1; odCarrier.Address2 = add.Street2; odCarrier.City = add.City; odCarrier.State = add.Province; odCarrier.Zip = add.PostalCode; } break; case ItransImportFields.Name: odCarrier.CarrierName = jsonCarrier.Name.En; break; } } try { long userNum = 0; if (!isAutomatic) { userNum = Security.CurUser.UserNum; } Carriers.Update(odCarrier, odCarrierOld, userNum); } catch (Exception ex) { ex.DoNothing(); } } return(""); //Blank string represents a completed update. }
///<summary>Attempts to find exact match for patient. If found, creates commlog, associates Patnum, and inserts into DB. ///Otherwise, it simply inserts SmsFromMobiles into the DB. ClinicNum should have already been set before calling this function.</summary> public static void ProcessInboundSms(List <SmsFromMobile> listMessages) { if (listMessages == null || listMessages.Count == 0) { return; } List <SmsBlockPhone> listBlockedPhones = SmsBlockPhones.GetDeepCopy(); for (int i = 0; i < listMessages.Count; i++) { SmsFromMobile sms = listMessages[i]; if (listBlockedPhones.Any(x => TelephoneNumbers.AreNumbersEqual(x.BlockWirelessNumber, sms.MobilePhoneNumber))) { continue; //The office has blocked this number. } sms.DateTimeReceived = DateTime.Now; SmsPhone smsPhone = SmsPhones.GetByPhone(sms.SmsPhoneNumber); string countryCode = CultureInfo.CurrentCulture.Name.Substring(CultureInfo.CurrentCulture.Name.Length - 2); if (smsPhone != null) { sms.ClinicNum = smsPhone.ClinicNum; countryCode = smsPhone.CountryCode; } //First try the clinic that belongs to this phone. List <long> listClinicNums = new List <long>(); if (sms.ClinicNum != 0) { listClinicNums.Add(sms.ClinicNum); } List <long[]> listPatNums = FindPatNums(sms.MobilePhoneNumber, countryCode, listClinicNums); if (listPatNums.Count == 0 && listClinicNums.Count > 0) //Could not find that patient in this clinic so try again for all clinics. { listPatNums = FindPatNums(sms.MobilePhoneNumber, countryCode); } sms.MatchCount = listPatNums.Count; //Item1=PatNum; Item2=Guarantor if (listPatNums.Count == 0 || listPatNums.Select(x => x[1]).Distinct().ToList().Count != 1) { //We could not find definitive match, either 0 matches found, or more than one match found with different garantors Insert(sms); continue; } if (listPatNums.Count == 1) { sms.PatNum = listPatNums[0][0]; //PatNum } else { sms.PatNum = listPatNums[0][1]; //GuarantorNum; more than one match, but all have the same garantor. } Commlog comm = new Commlog() { CommDateTime = sms.DateTimeReceived, Mode_ = CommItemMode.Text, Note = sms.MsgText, PatNum = sms.PatNum, CommType = Commlogs.GetTypeAuto(CommItemTypeAuto.TEXT), SentOrReceived = CommSentOrReceived.Received }; sms.CommlogNum = Commlogs.Insert(comm); Insert(sms); } UpdateSmsNotification(); }
///<summary>Attempts to find exact match for patient. If found, creates commlog, associates Patnum, and inserts into DB. ///Otherwise, it simply inserts SmsFromMobiles into the DB. ClinicNum should have already been set before calling this function.</summary> public static void ProcessInboundSms(List <SmsFromMobile> listMessages) { if (listMessages == null || listMessages.Count == 0) { return; } List <SmsBlockPhone> listBlockedPhones = SmsBlockPhones.GetDeepCopy(); for (int i = 0; i < listMessages.Count; i++) { SmsFromMobile sms = listMessages[i]; if (listBlockedPhones.Any(x => TelephoneNumbers.AreNumbersEqual(x.BlockWirelessNumber, sms.MobilePhoneNumber))) { continue; //The office has blocked this number. } sms.DateTimeReceived = DateTime.Now; string countryCode = CultureInfo.CurrentCulture.Name.Substring(CultureInfo.CurrentCulture.Name.Length - 2); if (sms.SmsPhoneNumber != SmsPhones.SHORTCODE) { SmsPhone smsPhone = SmsPhones.GetByPhone(sms.SmsPhoneNumber); if (smsPhone != null) { sms.ClinicNum = smsPhone.ClinicNum; countryCode = smsPhone.CountryCode; } } if (!PrefC.HasClinicsEnabled) { //We want customer side records of this message to list SmsPhones.SHORTCODE as the number on which the message was sent. This ensures we do //not record this communication on a different valid SmsPhone/VLN that it didn't truly take place on. However, on the HQ side, we want //records of this communication to be listed as having taken place on the actual Short Code number. In the case of a Short Code, //sms.SmsPhoneNumber will read "SHORTCODE", which won't be found in the customer's SmsPhone table. As a result, the code to use the //customer's SmsPhone.ClinicNum and Country code cannot be used. Since this code was intended to handle the case where the customer had //turned clinics on->off, we will specifically check if the customer has disabled clinics and only then change the sms.ClinicNum. //Otherwise, trust HQ sent the correct ClinicNum. Since we expect to only use Short Codes in the US/Canada, we will trust the server we //are processing inbound sms will have the correct country code, which will be used here. sms.ClinicNum = 0; } //First try the clinic that belongs to this phone. List <long> listClinicNums = new List <long>(); if (sms.ClinicNum != 0) { listClinicNums.Add(sms.ClinicNum); } List <long[]> listPatNums = FindPatNums(sms.MobilePhoneNumber, countryCode, listClinicNums); if (listPatNums.Count == 0 && listClinicNums.Count > 0) //Could not find that patient in this clinic so try again for all clinics. { listPatNums = FindPatNums(sms.MobilePhoneNumber, countryCode); } sms.MatchCount = listPatNums.Count; //Item1=PatNum; Item2=Guarantor if (listPatNums.Count == 0 || listPatNums.Select(x => x[1]).Distinct().ToList().Count != 1) { //We could not find definitive match, either 0 matches found, or more than one match found with different garantors Insert(sms); //Alert ODMobile where applicable. PushNotificationUtils.ODM_NewTextMessage(sms); continue; } if (listPatNums.Count == 1) { sms.PatNum = listPatNums[0][0]; //PatNum } else { sms.PatNum = listPatNums[0][1]; //GuarantorNum; more than one match, but all have the same garantor. } Commlog comm = new Commlog() { CommDateTime = sms.DateTimeReceived, Mode_ = CommItemMode.Text, Note = sms.MsgText, PatNum = sms.PatNum, CommType = Commlogs.GetTypeAuto(CommItemTypeAuto.TEXT), SentOrReceived = CommSentOrReceived.Received }; sms.CommlogNum = Commlogs.Insert(comm); Insert(sms); //Alert ODMobile where applicable. PushNotificationUtils.ODM_NewTextMessage(sms, sms.PatNum); } //We used to update the SmsNotification indicator via a queries and a signal here. Now managed by the eConnector. }