using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sdaleo; using Sdaleo.Systems.SQLServer; using Pluto.Api; using System.Data; using Sdaleo.Systems; using Yaulw.Win32; using Sdaleo.Systems.Advantage; namespace PlutoServer.MSL.Connectors { public static class MedisoftConnector { internal static string MAKE_SURE_APOSTROPHY_IS_SQL_READY(string str) { if (!String.IsNullOrEmpty(str)) { str = str.Replace("'", "''"); return str; } return String.Empty; } internal static bool IS_VALID_FOUR_DIGIT_PIN(string fourdigitpin) { if (!String.IsNullOrEmpty(fourdigitpin)) { fourdigitpin = fourdigitpin.Trim(); if (fourdigitpin.Length == 4) { foreach (char c in fourdigitpin) { if (!Char.IsDigit(c)) return false; } return true; } } return false; } #region System / Setup Specific /// /// Retrieve the entire Practice List for the given Shared DB Credential * Credential must point to Shared DB * /// /// /// public static DBRetVal GetPracticeList(AdvantageCredential credential) { if (credential != null) { DB db = DB.Create(credential); DBRetVal retVal = db.FillDataTable("SELECT [DataSet Name],[Data Path],[UserAPIKey] FROM [MWDBList]"); if (!retVal.IsValid) MSLSpecific.Logger.Error("Medisoft. Failed to retrieve Practice List for DataSource:{0}", credential.DataSource); return retVal; } return new DBRetVal(); } /// /// Retrieve the userapi key for a given practice, given Shared DB Credential * Credential must point to Shared DB * /// /// /// public static string GetUserApiKeyForPractice(AdvantageCredential credential, string DataSetOrDataBaseName, out bool bErrorOccured) { bErrorOccured = true; if (credential != null && !String.IsNullOrEmpty(DataSetOrDataBaseName)) { DB db = DB.Create(credential); DBRetVal retVal = db.ExecuteScalar(String.Format("SELECT [UserAPIKey] FROM [MWDBList] WHERE [DataSet Name]='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(DataSetOrDataBaseName))); bErrorOccured = retVal.ErrorOccured; if(retVal.IsValid) return retVal.GetScalarRetVal().Trim(); } return String.Empty; } /// /// Use this to Set the User API Key * Credential must point to Shared DB * /// /// /// /// /// public static bool SetUserApiKeyForPractice(AdvantageCredential credential, string DataSetOrDataBaseName, string UserApiKey) { if (credential != null && !String.IsNullOrEmpty(DataSetOrDataBaseName)) { DB db = DB.Create(credential); DBRetVal retVal = db.ExecuteNonQuery(String.Format("UPDATE [MWDBList] SET [UserApiKey]='{0}' WHERE [DataSet Name]='{1}'", UserApiKey, MAKE_SURE_APOSTROPHY_IS_SQL_READY(DataSetOrDataBaseName))); if (!retVal.IsValid) MSLSpecific.Logger.Error("Medisoft. Failed to set ApiKey for DataSource:{0} DataSet:{1}", credential.DataSource, DataSetOrDataBaseName); return retVal.IsValid; } return false; } /// /// we must ensure uniqueness amongst all our userapikeys (Lytec and somehow Medisoft get into an /// invalid state) where they duplicate them * Credential must point to Shared DB * /// /// /// /// /// public static void CleanUpDuplicateUserApiKeyExists(AdvantageCredential credential) { DBRetVal retVal = GetPracticeList(credential); if (retVal.IsValid) { Stack userApiStack = new Stack(); foreach (DataRow row in retVal.GetDataTableRetVal().Rows) { string UserApiKey = DataRet.Retrieve(row["UserAPIKey"]); if (!String.IsNullOrEmpty(UserApiKey)) { if (!userApiStack.Contains(UserApiKey)) { userApiStack.Push(UserApiKey); } else { // we found a duplicate!!! (Clear the UserApiKey for that Database - IMP!) string DBName = DataRet.Retrieve(row["DataSet Name"]); SetUserApiKeyForPractice(credential, DBName, String.Empty); } } } } } #endregion #region User Authentication /// /// User Authentication /// /// /// /// /// public static UserInfo1 AuthenticateUserLogin(string SystemApiKey, string User, string Password) { if (!String.IsNullOrEmpty(User) && !String.IsNullOrEmpty(Password)) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DBRetVal result = db.FillDataTable(String.Format("SELECT [Code],[Name],[Access Level],[Password] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper())); //if ((result != null) && (result.IsValid)) /* Rameshes fix that should not be needed and didn't make sense */ if (result.IsValid) { DataRow row = result.GetDataTableFirstRow(); string dbName = DataRet.Retrieve(row["Name"]); string dbPwd = DataRet.Retrieve(row["Password"]); // Authenticate! Password must match (case-insensitive) Medisoft also doesn't care about case if (!String.IsNullOrEmpty(dbName) && (String.Compare(Password.Trim(), dbPwd, true) == 0)) { UserInfo1 info = new UserInfo1(); info.AuthToken = SystemApiKey; info.Id = DataRet.Retrieve(row["Code"]); info.UserName = info.Id; // Seperate First n' Last name, if possible info.Name = dbName; int nFirst = dbName.IndexOf(' '); if (nFirst != -1) { info.FirstName = dbName.Substring(0, nFirst); info.LastName = dbName.Substring(nFirst + 1); } else { info.FirstName = dbName; info.LastName = ""; } // Authenticated, return the object info.IsProvider = true; return info; } } // Users must be setup with Mobile Privilege * ignore code below * //else //{ // // Check to see if any users are set up in the table // result = db.ExecuteScalar("SELECT COUNT([Code]) FROM [mwSEC]"); // uint nCount = 0; // // If there no users, let them in anyway // if (result.IsValid && uint.TryParse(result.GetScalarRetVal(), out nCount) && nCount == 0) // { // UserInfo1 info = new UserInfo1(); // info.AuthToken = SystemApiKey; // info.Id = "(Unspecified)"; // info.UserName = "(Unspecified)"; // // Default Authenticated, return the object (No Users exist in table) // info.IsProvider = true; // return info; // } //} } return null; } /// /// Pin Authentication /// /// /// /// /// public static UserInfo1 AuthenticateUserPIN(string SystemApiKey, string User, string pin) { if (!String.IsNullOrEmpty(User) && IS_VALID_FOUR_DIGIT_PIN(pin)) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DBRetVal result = db.FillDataTable(String.Format("SELECT [Code],[Name],[MobilePIN] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper())); if (result.IsValid) { DataRow row = result.GetDataTableFirstRow(); string dbName = DataRet.Retrieve(row["Name"]); string dbPin = DataRet.Retrieve(row["MobilePIN"]); // Authenticate! Password must match (case-insensitive) Medisoft also doesn't care about case if (!String.IsNullOrEmpty(dbName) && (String.Compare(pin.Trim(), dbPin, true) == 0)) { UserInfo1 info = new UserInfo1(); info.AuthToken = SystemApiKey; info.Id = DataRet.Retrieve(row["Code"]); info.UserName = info.Id; // Seperate First n' Last name, if possible info.Name = dbName; int nFirst = dbName.IndexOf(' '); if (nFirst != -1) { info.FirstName = dbName.Substring(0, nFirst); info.LastName = dbName.Substring(nFirst + 1); } else { info.FirstName = dbName; info.LastName = ""; } // Authenticated, return the object info.IsProvider = true; return info; } } } return null; } /// /// Create Pin /// /// /// /// /// public static bool CreatePin(string SystemApiKey, string User, string pin) { if (!String.IsNullOrEmpty(User) && IS_VALID_FOUR_DIGIT_PIN(pin)) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = String.Format("UPDATE [mwSEC] SET [MobilePIN]='{0}' WHERE upper([Code])='{1}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(pin), MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper()); DBRetVal result = db.ExecuteNonQuery(sql); return result.IsValid; } return false; } /// /// Pin is Set /// /// /// /// public static bool PinIsSet(string SystemApiKey, string User) { if (!String.IsNullOrEmpty(User)) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = String.Format("SELECT [MobilePIN] FROM [mwSEC] Where upper([Code])='{0}'", MAKE_SURE_APOSTROPHY_IS_SQL_READY(User).ToUpper()); DBRetVal result = db.ExecuteScalar(sql); if (result.IsValid) { string currentPin = DataRet.Retrieve(result.GetScalarRetVal()); return IS_VALID_FOUR_DIGIT_PIN(currentPin); } } return false; } #endregion #region Medisoft User Options internal enum DefaultCase { DateModified = 0, HighestCase = 1, LowestCase = 2 } /// /// Which is the default case to use? /// /// /// internal static DefaultCase GetDefaultCase(string SystemApiKey) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DBRetVal result = db.ExecuteScalar("SELECT [Case Default] from [MWOPT]"); if (result.IsValid) { try { DefaultCase Case = (DefaultCase) Enum.Parse(typeof(DefaultCase), result.GetScalarRetVal()); return Case; } catch (Exception e) { MSLSpecific.Logger.Error("Failed to retrieve Default Case", e); } } return DefaultCase.DateModified; } /// /// Use CopayRemainder Calculation or plain Patient Balance Calculation /// /// /// internal static bool UseCopayRemainderCalculation(string SystemApiKey) { //AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); //DB db = DB.Create(credential); ////The figureMissedCopay was the Boolean from the mwopt table //DBRetVal result = db.ExecuteScalar("SELECT [Case Default] from MWOPT"); //if (result.IsValid) //{ // bool bSomethingSomething = DataRet.Retrieve(result.GetScalarRetVal(), false); // return bSomethingSomething; //} // For Testing Purpose if ((DateTime.Now.Ticks % 2) == 0) return true; else return false; } #endregion #region Public Simple Info Getters /// /// get Providers /// /// /// public static ProviderInfo1[] GetProviderList(string SystemApiKey) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DBRetVal result = db.FillDataTable("SELECT [Code],[First Name],[Middle Name],[Last Name],[Phone] FROM [MWPHY] WHERE [Inactive] = false"); List providers = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { ProviderInfo1 info = new ProviderInfo1(); info.UserId = DataRet.Retrieve(row["Code"]); info.FirstName = DataRet.Retrieve(row["First Name"]); info.MiddleName = DataRet.Retrieve(row["Middle Name"]); info.LastName = DataRet.Retrieve(row["Last Name"]); info.Id = -1; string phone = DataRet.Retrieve(row["Phone"]); if (!String.IsNullOrEmpty(phone) && phone != "( ) -") { info.Phone = phone; } providers.Add(info); } return providers.ToArray(); } return null; } /// /// Get Diagnosis /// /// /// /// public static TerminologyInfo1[] GetDiagnosisList(string SystemApiKey, string searchStr) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = "SELECT [Code 1],[Description] FROM [MWDIA] WHERE [Inactive] = false"; if (!String.IsNullOrEmpty(searchStr)) sql += String.Format(" AND (upper([Code 1]) like '%{0}%' OR upper([Description]) like '%{0}%')", MAKE_SURE_APOSTROPHY_IS_SQL_READY(searchStr).ToUpper()); DBRetVal result = db.FillDataTable(sql); List diagnoses = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { TerminologyInfo1 info = new TerminologyInfo1(); info.Code = DataRet.Retrieve(row["Code 1"]); info.Description = DataRet.Retrieve(row["Description"]); diagnoses.Add(info); } return diagnoses.ToArray(); } return null; } /// /// Get Procedures /// /// /// /// /// public static TerminologyInfo1[] GetProceduresList(string SystemApiKey, string chart, string searchStr) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = "SELECT [Code 1],[Description],[Default Modifier 1],[Default Modifier 2],[Default Modifier 3],[Default Modifier 4],[Default Units] FROM [MWPRO] WHERE [Inactive] = 0"; if (!String.IsNullOrEmpty(searchStr)) sql += String.Format(" AND (upper([Code 1]) like '%{0}%' OR upper([Description]) like '%{0}%')", MAKE_SURE_APOSTROPHY_IS_SQL_READY(searchStr).ToUpper()); DBRetVal result = db.FillDataTable(sql); List procedures = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { TerminologyInfo1 info = new TerminologyInfo1(); info.Code = DataRet.Retrieve(row["Code 1"]); info.Description = DataRet.Retrieve(row["Description"]); info.Modifier1 = DataRet.Retrieve(row["Default Modifier 1"]); info.Modifier2 = DataRet.Retrieve(row["Default Modifier 2"]); info.Modifier3 = DataRet.Retrieve(row["Default Modifier 3"]); info.Modifier4 = DataRet.Retrieve(row["Default Modifier 4"]); info.Units = DataRet.Retrieve(row["Default Units"]); procedures.Add(info); } return procedures.ToArray(); } return null; } /// /// Get Facilities /// /// /// public static FacilityInfo1[] GetFacilitiesList(string SystemApiKey) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = "SELECT [Code],[Name],[Street 1],[Street 2],[City],[State],[Zip Code],[Phone],[Contact],[Email] FROM mwadd WHERE Type='Facility' OR Type='Laboratory' AND [Inactive] = false"; DBRetVal result = db.FillDataTable(sql); List facilities = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { FacilityInfo1 info = new FacilityInfo1(); info.Code = DataRet.Retrieve(row["Code"]); info.Name = DataRet.Retrieve(row["Name"]); // Address Info info.Address = new AddressInfo1(); info.Address.Address1 = DataRet.Retrieve(row["Street 1"]); info.Address.Address2 = DataRet.Retrieve(row["Street 2"]); info.Address.City = DataRet.Retrieve(row["City"]); info.Address.State = DataRet.Retrieve(row["State"]); info.Address.Zip = DataRet.Retrieve(row["Zip Code"]); facilities.Add(info); } return facilities.ToArray(); } return null; } /// /// Not implemented (NOT CALLED) /// /// /// public static ResourceInfo1[] GetResourcesList(string SystemApiKey) { // Not implemented return null; } /// /// Get Patients /// /// /// /// /// /// public static PatientInfo1[] GetPatientList(string SystemApiKey, string lastName, string firstName, string dateOfBirth) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = "SELECT [Chart Number],[First Name],[Middle Name],[Last Name],[Date of Birth] as [Birth Date],[Sex],[Street 1],[Street 2],[City],[State],[Zip Code],[Phone 1],[Phone 2],[Phone 3],[Social Security Number] FROM [MWPAT]"; // Allow them to search by patient name or by date of birth, // not both that is just too confusing bool bWhereIsSet = false; if (!String.IsNullOrEmpty(dateOfBirth)) { DateTime dt; if (DateTime.TryParse(dateOfBirth, out dt)) { sql = sql + String.Format(" WHERE [Date of Birth] = '{0:D4}-{1:D2}-{2:D2}' ", dt.Year, dt.Month, dt.Day); bWhereIsSet = true; } } else { if (!String.IsNullOrEmpty(lastName)) { sql = sql + String.Format(" WHERE upper([Last Name]) like '%{0}%' {1}", MAKE_SURE_APOSTROPHY_IS_SQL_READY(lastName).ToUpper(), !String.IsNullOrEmpty(firstName) ? "OR" : ""); bWhereIsSet = true; } if (!String.IsNullOrEmpty(firstName) && !String.IsNullOrEmpty(lastName)) { sql = sql + String.Format(" {1} upper([First Name]) like '%{0}%' ", MAKE_SURE_APOSTROPHY_IS_SQL_READY(firstName).ToUpper(), String.IsNullOrEmpty(lastName) ? "WHERE" : ""); } } // Only query for Active Patients if(bWhereIsSet) sql = sql + " AND [Inactive] = false "; else sql = sql + " WHERE [Inactive] = false "; // Order Correctly sql = sql + " ORDER BY [Last Name],[First Name],[Middle Name] "; DBRetVal result = db.FillDataTable(sql); List patients = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { PatientInfo1 info = FillInPatientInformation(row, false, true); patients.Add(info); } return patients.ToArray(); } return null; } #endregion #region Private Static SQL Query Helper /// /// Fills in the Patient Info from the Patient Table Columns /// /// /// If Set to true, will fill in as much patient information as possible, otherwise will on fill basic patient name / chart information /// if set to true will fill try to fill in phone information /// private static PatientInfo1 FillInPatientInformation(DataRow row, bool bFillAsMuchPatientInfoAsPossible, bool bFillInPhoneInfo) { PatientInfo1 info = new PatientInfo1(); // Basic Patient Info (Always fill that information) info.ChartId = DataRet.Retrieve(row["Chart Number"]); info.Id = DataRet.Retrieve(row["Chart Number"]); info.FirstName = DataRet.Retrieve(row["First Name"]); info.MiddleName = DataRet.Retrieve(row["Middle Name"]); info.LastName = DataRet.Retrieve(row["Last Name"]); info.Weight = ""; // we don't track weight // Birth Date & Age Calculation info.DOB = DataRet.Retrieve(row["Birth Date"]); if (info.DOB > DateTime.MinValue) { DateTime now = DateTime.Today; int age = now.Year - info.DOB.Year; if (info.DOB > now.AddYears(-age)) age--; info.Age = age.ToString(); } // Gender info.Gender = DataRet.Retrieve(row["Sex"], "Unknown", true); // If we want more details on the Patient, this will be set to false if (bFillAsMuchPatientInfoAsPossible) { // Social Security info.SocialSecurity = DataRet.Retrieve(row["Social Security Number"]); // Primary Address AddressInfo1 address = new AddressInfo1(); address.Address1 = DataRet.Retrieve(row["Street 1"]); address.Address2 = DataRet.Retrieve(row["Street 2"]); address.City = DataRet.Retrieve(row["City"]); address.State = DataRet.Retrieve(row["State"]); address.Zip = DataRet.Retrieve(row["Zip Code"]); info.PrimaryAddress = address; } // Phone Number may already be filled in for us if (bFillInPhoneInfo) { // Primary Phone string Phone = DataRet.Retrieve(row["Phone 1"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { info.PrimaryPhone = Phone; } else { Phone = DataRet.Retrieve(row["Phone 2"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { info.PrimaryPhone = Phone; } else { Phone = DataRet.Retrieve(row["Phone 3"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { info.PrimaryPhone = Phone; } } } } return info; } /// /// Basically the same query as Appointment Query except with no Appointment information because all we have /// is a Chart /// /// Patient Chart /// private static string BuildPatientQuery(string PatientChart, DefaultCase Case) { if (!String.IsNullOrEmpty(PatientChart)) { StringBuilder sb = new StringBuilder(); sb.Append("SELECT TOP 1 p.[Chart Number], p.[First Name],p.[Middle Name],p.[Last Name],p.[Date of Birth] as [Birth Date],p.[Sex],p.[Street 1],p.[Street 2],p.[City]"); //sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Work Phone],p.[Work Extension], p.[Social Security Number]"); // Not using Work Phone / Work Extension sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Social Security Number]"); sb.Append(",c.[Copayment Amount] as [Patient Copay]"); sb.Append(",i.[Name] as [Primary Insurance Name]"); sb.Append(",h.[Last Name] as [Ref Phy Last Name],h.[First Name] as [Ref Phy First Name],h.[Office] as [Ref Physician Phone],h.[Phone] as [Ref Physician Main Phone], h.[Cell] as [Ref Physician Mobile Phone]"); sb.Append(" FROM [MWPAT] p LEFT JOIN"); sb.Append(" [MWCAS] c ON p.[Chart Number] = c.[Chart Number]"); sb.Append(" LEFT JOIN [MWINS] i ON c.[Insurance Carrier #1] = i.[Code]"); sb.Append(" LEFT JOIN [MWRPH] h ON c.[Referring Provider] = h.[Code]"); sb.Append(" WHERE "); sb.Append(String.Format("p.[Chart Number] = '{0}' ", MAKE_SURE_APOSTROPHY_IS_SQL_READY(PatientChart))); // Don't forget the order by switch (Case) { case DefaultCase.DateModified: sb.Append("Order By c.[Date Modified] DESC"); break; case DefaultCase.HighestCase: sb.Append("Order By c.[Case Number] DESC"); break; case DefaultCase.LowestCase: sb.Append("Order By c.[Case Number] ASC"); break; } return sb.ToString(); } return String.Empty; } /// /// Build the Appointment / Patient SQL Query as needed, either specify a Date Range or give a specific ID /// /// /// /// private static string BuildAppointmentQuery(int nApptID, DateTime startDT, DateTime endDT, string[] providers, string[] resources) { StringBuilder sb = new StringBuilder(); sb.Append("SELECT a.[ID],a.[Resource],a.[Date], a.[Status],a.[Start Time],a.[Case Number],a.[Length],a.[Name],a.[Chart Number],a.[Provider],a.[Procedure Code],a.[Note],a.[Color],a.[Reason Code]"); sb.Append(",a.[Phone] as [Appt Home Phone],a.[Cell Phone] as [Appt Mobile Phone]"); sb.Append(",p.[First Name],p.[Middle Name],p.[Last Name],p.[Date of Birth] as [Birth Date],p.[Sex],p.[Street 1],p.[Street 2],p.[City]"); //sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Work Phone],p.[Work Extension], p.[Social Security Number]"); // Not using Work Phone / Work Extension sb.Append(",p.[State],p.[Zip Code],p.[Phone 1],p.[Phone 2],p.[Phone 3],p.[Social Security Number]"); sb.Append(",c.[Copayment Amount] as [Patient Copay]"); sb.Append(",r.[Description] as [Reason Description]"); sb.Append(",i.[Name] as [Primary Insurance Name]"); sb.Append(",h.[Last Name] as [Ref Phy Last Name],h.[First Name] as [Ref Phy First Name],h.[Office] as [Ref Physician Phone],h.[Phone] as [Ref Physician Main Phone], h.[Cell] as [Ref Physician Mobile Phone]"); sb.Append(" FROM [OHAPP] a LEFT JOIN"); sb.Append(" [MWPAT] p ON p.[Chart Number] = a.[Chart Number]"); sb.Append(" LEFT JOIN [OHRSN] r ON a.[Reason Code] = r.[Code]"); sb.Append(" LEFT JOIN [MWCAS] c ON a.[Case Number] = c.[Case Number]"); sb.Append(" LEFT JOIN [MWINS] i ON c.[Insurance Carrier #1] = i.[Code]"); sb.Append(" LEFT JOIN [MWRPH] h ON c.[Referring Provider] = h.[Code]"); sb.Append(" WHERE "); // if ID is specified, we are looking for a single appointment, // otherwise we are looking for a range if (nApptID >= 0) { sb.Append(String.Format(" [ID] = {0} ", nApptID)); } else { // [Date] between '10/24/2012' and '10/27/2012' AND const string DATE_FORMAT = "{0:D2}/{1:D2}/{2:D4}"; string StartDate = String.Format(DATE_FORMAT, startDT.Month, startDT.Day, startDT.Year); string EndDate = String.Format(DATE_FORMAT, endDT.Month, endDT.Day, endDT.Year); sb.Append(String.Format("[Date] between '{0}' AND '{1}' AND ", StartDate, EndDate)); // [Appt Time] between '9:00:00 AM' AND '12:30:00 PM' string StartTime = startDT.ToString("hh:mm:ss tt"); string EndTime = endDT.ToString("hh:mm:ss tt"); sb.Append(String.Format("[Start Time] between '{0}' AND '{1}' ", StartTime, EndTime)); // Do we have providers? // AND [Provider] in ('LAB',) if (providers != null && providers.Length > 0) { sb.Append("AND [Provider] in ('',"); // Allow '' blank providers for (int i = 0; i < providers.Length; ++i) { sb.Append("'"); sb.Append(MAKE_SURE_APOSTROPHY_IS_SQL_READY(providers[i])); sb.Append("'"); if (i != providers.Length - 1) sb.Append(","); } sb.Append(") "); } // Do we have resources? // AND [Resource] in (2, ) if (resources != null && resources.Length > 0) { sb.Append("AND [Resource] in ('',"); // Allow '' blank resources for (int i = 0; i < resources.Length; ++i) { sb.Append(resources[i]); if (i != resources.Length - 1) sb.Append(","); } sb.Append(") "); } // Filter out all Cancelled Appointments sb.Append("AND (a.[Status] <> '4' "); // Allow "Break" appointment types sb.Append("OR a.[Status] IS NULL) "); // Don't forget the order by sb.Append("Order By [Date],[Start Time],[ID]"); } return sb.ToString(); } #endregion #region Public Appointment Methods /// /// Retrieve all appointments for the given time period for the given providers or resources /// /// /// /// /// /// public static AppointmentInfo1[] GetAppointments(string SystemApiKey, ProviderInfo1[] providerInfo, ResourceInfo1[] resourceInfo, DateTime startDate, DateTime endDate) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = ""; List providers = new List(); List resources = new List(); if (providerInfo != null) { foreach (ProviderInfo1 info1 in providerInfo) { if (!String.IsNullOrEmpty(info1.UserId)) providers.Add(info1.UserId); } } if (resourceInfo != null) { foreach (ResourceInfo1 info1 in resourceInfo) { if (!String.IsNullOrEmpty(info1.Name)) resources.Add(info1.Name); } } // Build the Appointment QUERY (don't pass in an Appt ID and it will use a range) sql = BuildAppointmentQuery(-1, startDate, endDate, providers.ToArray(), resources.ToArray()); DBRetVal result = db.FillDataTable(sql); List appointments = new List(); if (result.IsValid) { foreach (DataRow row in result.GetDataTableRetVal().Rows) { AppointmentInfo1 info = new AppointmentInfo1(); info.Id = DataRet.Retrieve(row["ID"]); // Retrieving Description for the Appointment Reason Code via join info.Reason = DataRet.Retrieve(row["Reason Description"]); //Retrieving Provider code for the appointment info.ProviderCode = DataRet.Retrieve(row["Provider"]); // Let's figgure out the Appointment DTs // StartDateTime = DateTime.Parse(string.Format("{0} {1}:{2}:00 AM", today.ToShortDateString(), 2, 15)), // EndDateTime = DateTime.Parse(string.Format("{0} {1}:{2}:00 PM", today.ToShortDateString(), 2, 30)), DateTime dtDate = DataRet.Retrieve(row["Date"]); DateTime dtTime = DataRet.Retrieve(row["Start Time"]); int nDuration = DataRet.Retrieve(row["Length"]); info.StartDateTime = DateTime.Parse(string.Format("{0} {1:D2}:{2:D2}:{3:D2}", dtDate.ToShortDateString(), dtTime.Hour, dtTime.Minute, dtTime.Second)); if (nDuration == 0) { // Leave it as is (no duration was set) info.EndDateTime = info.StartDateTime; } else { dtTime = dtTime.AddMinutes(nDuration); info.EndDateTime = DateTime.Parse(string.Format("{0} {1:D2}:{2:D2}:{3:D2}", dtDate.ToShortDateString(), dtTime.Hour, dtTime.Minute, dtTime.Second)); } // Appointment Primary Phone bool bAppointmentPhoneFound = false; string Phone = DataRet.Retrieve(row["Appt Home Phone"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { bAppointmentPhoneFound = true; } else { Phone = DataRet.Retrieve(row["Appt Mobile Phone"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") bAppointmentPhoneFound = true; } // Attach Chart Information (Only the most basic information, try to attach Phone Number if none found in appointment) bool bHasChart = (DataRet.Retrieve(row["Chart Number"]) != ""); if (bHasChart) info.Patient = FillInPatientInformation(row, false, !bAppointmentPhoneFound); else info.AppointmentDescription = DataRet.Retrieve(row["Name"]); // Attach Appointment Phone always, if found if (bAppointmentPhoneFound) info.AppointmentPhone = Phone; //[Background Color] //#define DEFAULT_GRID_TEXT_COLOR (RGB(0,0,0)) //#define DEFAULT_GRID_BKGND_COLOR (RGB(255,255,255)) int ColorRef = DataRet.Retrieve(row["Color"], 0); if (ColorRef == 0) info.AppointmentColor = new AppointmentColor1() { Red = 255, Green = 255, Blue = 255 }; else info.AppointmentColor = new AppointmentColor1() { Red = Functions.GetRValue(ColorRef), Green = Functions.GetGValue(ColorRef), Blue = Functions.GetBValue(ColorRef) }; // Determine the Appointment Status /* * Medisoft Status 0 – Unconfirmed 1 – Confirmed 2 – Checked In 3 – Missed 4 – Cancelled 5 – Being Seen 6 – Checked Out 7 – Rescheduled * -------------------------- * Medisoft To Mobile Mapping 0 – Unconfirmed_Pending 1 – Confirmed 2 – CheckedIn_Waiting 3 – Missed 4 – Cancelled 5 – Being Seen 6 – CheckedOut_Completed 7 - Rescheduled * */ string status = DataRet.Retrieve(row["Status"]); switch (status) { case "0": info.AppointmentStatus = AppointmentStatusEnum.Unconfirmed_Pending; break; case "1": info.AppointmentStatus = AppointmentStatusEnum.Confirmed; break; case "2": info.AppointmentStatus = AppointmentStatusEnum.CheckedIn_Waiting; break; case "3": info.AppointmentStatus = AppointmentStatusEnum.Missed; break; case "4": info.AppointmentStatus = AppointmentStatusEnum.Cancelled; break; case "5": info.AppointmentStatus = AppointmentStatusEnum.BeingSeen; break; case "6": info.AppointmentStatus = AppointmentStatusEnum.CheckedOut_Completed; break; case "7": info.AppointmentStatus = AppointmentStatusEnum.Rescheduled; break; default: break; } // Finally, add the appointment appointments.Add(info); } return appointments.ToArray(); } return null; } /// /// Appointment Detail query. Fills in useful information for the patient regarding the Appointment /// /// /// /// public static AppointmentDetail1 GetAppointmentDetail(string SystemApiKey, int ApptID, bool bCalculateBalance) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); string sql = BuildAppointmentQuery(ApptID, DateTime.MinValue, DateTime.MinValue, null, null); DBRetVal result = db.FillDataTable(sql); if (result.IsValid) { DataRow row = result.GetDataTableFirstRow(); AppointmentDetail1 detail = new AppointmentDetail1(); // Appointment Stuff detail.ApptNote = DataRet.Retrieve(row["Note"]); detail.ApptReason = DataRet.Retrieve(row["Reason Description"]); // Chart Info bool bHasChart = (DataRet.Retrieve(row["Chart Number"]) != ""); if (bHasChart) detail.PatientDetail = GetPatientDetail(SystemApiKey, row, String.Empty, bCalculateBalance); // Return the Appointment Detail Item return detail; } return null; } /// /// Similar to GetAppointmentDetail, except all we have here is only the chart, so no appointment information /// /// /// if this is set to not null (a valid DBRetVal), it will use the DBRetVal and not re-query /// /// public static PatientDetail1 GetPatientDetail(string SystemApiKey, DataRow UseThisRowInsteadOfQuery, string PatientChart, bool bCalculateBalance) { AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DataRow row = null; // Query only if the row has not been passed in if (UseThisRowInsteadOfQuery == null) { string sql = BuildPatientQuery(PatientChart, GetDefaultCase(SystemApiKey)); DBRetVal result = db.FillDataTable(sql); if (result.IsValid) row = result.GetDataTableFirstRow(); } else { row = UseThisRowInsteadOfQuery; } // Fill in the information if (row != null) { PatientDetail1 detail = new PatientDetail1(); // Chart Info detail.Patient = FillInPatientInformation(row, true, true); // Patient Billing Information detail.PatientBilling = new PatientBillingInfo1(); detail.PatientBilling.CopayAmount = DataRet.Retrieve(row["Patient Copay"], 0); detail.PatientBilling.PrimaryInsCarrier = DataRet.Retrieve(row["Primary Insurance Name"]); // Refering Provider comes from Patient Table also detail.PatientBilling.RefProvider = DataRet.Retrieve(row["Ref Phy First Name"]) + " " + DataRet.Retrieve(row["Ref Phy Last Name"]); // Fill in Refering Provider Phone string Phone = DataRet.Retrieve(row["Ref Physician Phone"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { detail.PatientBilling.RefProviderPhone = Phone; } else { Phone = DataRet.Retrieve(row["Ref Physician Main Phone"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { detail.PatientBilling.RefProviderPhone = Phone; } else { Phone = DataRet.Retrieve(row["Ref Physician Mobile Phone"]); if (!String.IsNullOrEmpty(Phone) && Phone != "( ) -") { detail.PatientBilling.RefProviderPhone = Phone; } } } // Calculate Account Balance (May take awhile) if (bCalculateBalance) { detail.PatientBilling.AccountBalance = CalculatePatientBalance(detail.Patient.ChartId, SystemApiKey); } // Return the Appointment Detail Item return detail; } return null; } /// /// Used to calculate the Patient Balance (stripped out of Lytec) /// /// /// private static decimal CalculatePatientBalance(string Chart, string SystemApiKey) { if (!String.IsNullOrEmpty(Chart)) { // Copied from Medisoft Source Code (Sent to my by Scott Toney) // Query1: This is used to get the patient balance without the missed copays StringBuilder sb1 = new StringBuilder(); sb1.Append("SELECT SUM([Amount] + "); sb1.Append("[Insurance 1 Amount Paid] + "); sb1.Append("[Insurance 2 Amount Paid] + "); sb1.Append("[Insurance 3 Amount Paid] + "); sb1.Append("[Guarantor Amount Paid] + "); sb1.Append("[Attorney Amount Paid] + "); sb1.Append("[Adjustment Amount] "); sb1.Append(") as RemainderBalance "); sb1.Append("FROM [MWTRN] "); //sb1.Append("WHERE [Transaction type] <= " + QuotedStr('H') + " AND "); sb1.Append("WHERE [Transaction type] <= 'H' AND "); sb1.Append("( [Guarantor Responsible] = True ) "); sb1.Append("AND ([Insurance 1 Paid] = True ) "); sb1.Append("AND ([Insurance 2 Paid] = True ) "); sb1.Append("AND ([Insurance 3 Paid] = True ) "); sb1.Append("AND ([Guarantor Paid] = False ) AND "); sb1.Append("[case number] IN "); sb1.Append("(SELECT [case number] FROM [MWCAS] WHERE [guarantor] = "); sb1.Append("(SELECT TOP 1 [guarantor] FROM [MWCAS] WHERE [chart number] = '" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(Chart) + "')) "); string sqlQuery1 = sb1.ToString(); // Copied from Medisoft Source Code (Sent to my by Scott Toney) // Query2: run the query to get the missed copays StringBuilder sb2 = new StringBuilder(); sb2.Append("SELECT SUM([Copayment_Expected_Amount] + "); sb2.Append("[Copayment_Amount_Paid] "); sb2.Append(") as CopayBalance "); sb2.Append("from [MWTRN] "); //sb2.Append("WHERE [Transaction type] <= " + QuotedStr('H') + " AND "); sb2.Append("WHERE [Transaction type] <= 'H' AND "); sb2.Append("( [Guarantor Responsible] = True ) "); sb2.Append("AND ( (( [Insurance 1 Responsible] = True ) and ( [Insurance 1 Paid] <> [Insurance 1 Responsible] )) "); sb2.Append("OR (( [Insurance 2 Responsible] = True ) and ( [Insurance 2 Paid] <> [Insurance 2 Responsible] )) "); sb2.Append("OR (( [Insurance 3 Responsible] = True ) and ( [Insurance 3 Paid] <> [Insurance 3 Responsible] )) "); sb2.Append(") "); sb2.Append("AND ( [Guarantor Paid] = False ) and "); sb2.Append("[case number] in "); sb2.Append("(SELECT [case number] From [MWCAS] WHERE [guarantor] = "); sb2.Append("(SELECT TOP 1 [guarantor] from [MWCAS] where [chart number] = '" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(Chart) + "')) "); string sqlQuery2 = sb2.ToString(); // Determine which query to run (Balance or CoPay Remainder) string sql = ""; if (UseCopayRemainderCalculation(SystemApiKey)) sql = sqlQuery2; else sql = sqlQuery1; // Run the Query AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); DBRetVal result = db.ExecuteScalar(sql); if (result.IsValid) { decimal Balance = DataRet.Retrieve(result.GetScalarRetVal(), 0); return Balance; } } return 0; } #endregion #region Post Billing /// /// Post Billing to Unproccessed Transactions /// /// /// /// /// public static bool PostBilling(string SystemApiKey, string User, BillingPost1 billingInfo) { // Make sure this is a valid BillingPost we are getting passed in if ((billingInfo == null) || (billingInfo.BillingDetails == null) || (billingInfo.BillingDetails.Length == 0)) return false; // Generate new Unique ID string UniqueID = Guid.NewGuid().ToString(); UniqueID = UniqueID.Replace("-", "").Substring(0, 32); // Create encryption code. string sSource = billingInfo.ChartID + billingInfo.PatientLastName + UniqueID; string s64Trimmed = CrossProduct.Core.Utils.MD5EncryptMsg(sSource, 20); bool bSuccessfulPost = true; // Setup Main DB AdvantageCredential credential = DBCache.GetAdvantageConnection(SystemApiKey); DB db = DB.Create(credential); // for auditing * may not work * AdvantageCredential credentialUser = null; DB dbUser = null; // Try to Audit, following Scott's instructions, however, there seems to be no // guarantee that this works (Advantage can get quickly out of sync it seems) bool bTryToAudit = !String.IsNullOrEmpty(User); bTryToAudit = false; if (bTryToAudit) { credentialUser = DBCache.GetAdvantageConnection(SystemApiKey, User); dbUser = DB.Create(credentialUser); } // Deal with Notes first * Load them all up into a Memo Field * StringBuilder sbBillingNotes = new StringBuilder(); for (int i = 0; i < billingInfo.BillingDetails.Length; ++i) { BillingDetail1 detail = billingInfo.BillingDetails[i]; if (!String.IsNullOrEmpty(detail.BillingNote)) { sbBillingNotes.Append(detail.BillingNote); sbBillingNotes.Append("\n"); // Could be '\r' - test } } if(sbBillingNotes.Length > 0) sbBillingNotes.Remove(sbBillingNotes.Length - 1, 1); // remove trailing '\n' // Deal with the rest of the items, notes are stuffed into the first item only bool bFirstInsert = true; int nNotesFound = 0; for (int i = 0; i < billingInfo.BillingDetails.Length && bSuccessfulPost; ++i) { BillingDetail1 detail = billingInfo.BillingDetails[i]; // Skip Billing Notes (already dealt with) if (!String.IsNullOrEmpty(detail.BillingNote)) { nNotesFound++; continue; } StringBuilder ValueList = new StringBuilder(); string newID = CrossProduct.Core.Utils.CreateGUID(); ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(newID) + "', "); // ID // ID ValueList.Append("0, "); // Post, tinyint ValueList.Append("0, "); // Transaction status, int ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.ChartID) + "', "); // varchar15 ValueList.Append("0, "); // Billing, Case int ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.ProviderID) + "', "); // varchar 15 // Make sure valid Date From if (billingInfo.DateFrom != null && billingInfo.DateFrom != DateTime.MinValue) ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(billingInfo.DateFrom) + "', sql_timestamp), "); // datetime else ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(DateTime.Now) + "', sql_timestamp), "); // datetime ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureCode) + "', "); // varchar 15 ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier1) + "', "); // varchar 3 ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier2) + "', "); // varchar 3 ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier3) + "', "); // varchar 3 ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ProcedureModifier4) + "', "); // varchar 3 ValueList.Append(detail.Units + ", "); // real ValueList.Append((i + 1 - nNotesFound).ToString() + ", "); // Procedure Order, int ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientLastName) + "', "); // patient last name ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientFirstName) + "', "); // patient first name // Don't insert an invalid birthday (Medisoft allows nulls) if (billingInfo.BirthDate != null && billingInfo.BirthDate != DateTime.MinValue) ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTStringDateOnly(billingInfo.BirthDate) + "', sql_timestamp), "); // datetime else ValueList.Append(" NULL, "); // datetime ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.PatientSocialSecurity) + "', ");// varchar 11 ValueList.Append("-1, "); // Exception // int ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(billingInfo.Facility) + "', "); // Facility ValueList.Append("0, "); // Created By Application. // int ValueList.Append("0, "); // Amount ValueList.Append("'" + UniqueID + "', "); // varchar 32 ValueList.Append("'', "); // Error msg. varchar 80 ValueList.Append(" CONVERT('" + CrossProduct.Core.Utils.AdvDTString(DateTime.Now) + "', sql_timestamp), "); // Created Date, datetime ValueList.Append("NULL, "); // mod date, datetime ValueList.Append("0,"); // Viewed, tinyint ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(s64Trimmed) + "',"); // Encryption Code. ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.ImmunizationBatchNumber) + "', "); // Immunization Batch Number ? // On First Insert, add the billing notes to first post if (bFirstInsert) { if (sbBillingNotes.Length > 0) ValueList.Append("'" + MAKE_SURE_APOSTROPHY_IS_SQL_READY(sbBillingNotes.ToString()) + "', "); // Mobile Notes. Memo. else ValueList.Append("'', "); // Mobile Notes. Memo. bFirstInsert = false; } else ValueList.Append("'', "); // Mobile Notes. Memo. /// ******* /// Attach each Diagnosis Code (we can do up to 12) for each procedure /// Always do this last, deals with trailing Comma (,) /// ******* for (int j = 0; j < 12; ++j) { try { ValueList.Append("'"); ValueList.Append(MAKE_SURE_APOSTROPHY_IS_SQL_READY(detail.DiagnosisCodes[j])); // Diagnosis 1 - 12, varchar 15 if (j < 11) ValueList.Append("', "); else ValueList.Append("'"); } catch (IndexOutOfRangeException) { if (j < 11) ValueList.Append("', "); else ValueList.Append("'"); } catch(NullReferenceException) { if (j < 11) ValueList.Append("', "); else ValueList.Append("'"); } } // Build our sql statement. StringBuilder sb = new StringBuilder("INSERT INTO [MWUTC] "); sb.Append(" ([ID], [Post], [Transaction_Status], [Chart_Number], [Case_Number], [Provider], [Date_From], [Procedure_Code], [Modifier_1], [Modifier_2], [Modifier_3], [Modifier_4], [Units], [Procedure_Order], [Patient_Last_Name], [Patient_First_Name], [Birth_Date], [SSN], [Exception], [Facility], [Creating_App], [Amount], [HL7MessageID], [ErrorMsg], [Date_Created], [Date_Modified], [Viewed], [Checksum], [Immunization Batch Number], [Mobile Notes], [Diagnosis_Code_1], [Diagnosis_Code_2], [Diagnosis_Code_3], [Diagnosis_Code_4], [Diagnosis_Code_5], [Diagnosis_Code_6], [Diagnosis_Code_7], [Diagnosis_Code_8], [Diagnosis_Code_9], [Diagnosis_Code_10], [Diagnosis_Code_11], [Diagnosis_Code_12])"); sb.Append(" VALUES (" + ValueList.ToString() + ")"); string StrSQL = sb.ToString(); DBRetVal result = null; if (bTryToAudit) { // Post the Billing (via user way) result = dbUser.ExecuteNonQuery(StrSQL); bSuccessfulPost = result.IsValid; if (bSuccessfulPost) { // posted succesful for user moving on continue; } else { MSLSpecific.Logger.Info("submitting superbill via the user {0} didn't work, will submitt the superbill without the user credential (msg:{1})", User, result.ErrorMsg); bTryToAudit = false; } } // Post the Billing (the regular way) result = db.ExecuteNonQuery(StrSQL); bSuccessfulPost = result.IsValid; } return bSuccessfulPost; } #endregion } }