Ejemplo n.º 1
0
        ///<summary>Throws exception if anything about the provider information is not valid.
        ///All intended exceptions are Exceptions and are already translated.</summary>
        public static void ValidateProv(Provider prov, Clinic clinic = null)
        {
            if (prov == null)
            {
                throw new ODException(Lans.g("Erx", "Provider not found"));
            }
            ProviderClinic provClinic = ProviderClinics.GetOneOrDefault(prov.ProvNum, (clinic == null ? 0 : clinic.ClinicNum));

            if (prov.IsErxEnabled == ErxEnabledStatus.Disabled)
            {
                throw new ODException(Lans.g("Erx", "Erx is disabled for provider") + ": " + prov.Abbr + ".  " + Lans.g("Erx", "To enable, edit provider in Lists | Providers and acknowledge Electronic Prescription fees."));
            }
            if (prov.IsHidden)
            {
                throw new ODException(Lans.g("Erx", "Provider") + ": " + prov.Abbr + " " + Lans.g("Erx", "is hidden") + ".  " + Lans.g("Erx", "Unhide the provider to use Erx features."));
            }
            if (prov.IsNotPerson)
            {
                throw new ODException(Lans.g("Erx", "Provider must be a person") + ": " + prov.Abbr);
            }
            string fname = prov.FName.Trim();

            if (fname == "")
            {
                throw new ODException(Lans.g("Erx", "Provider first name missing") + ": " + prov.Abbr);
            }
            if (Regex.Replace(fname, "[^A-Za-z'\\- ]*", "") != fname)
            {
                throw new ODException(Lans.g("Erx", "Provider first name can only contain letters, dashes, apostrophes, or spaces.") + ": " + prov.Abbr);
            }
            string lname = prov.LName.Trim();

            if (lname == "")
            {
                throw new ODException(Lans.g("Erx", "Provider last name missing") + ": " + prov.Abbr);
            }
            if (Regex.Replace(lname, "[^A-Za-z'\\- ]*", "") != lname)          //Will catch situations such as "Dale Jr. III" and "Ross DMD".
            {
                throw new ODException(Lans.g("Erx", "Provider last name can only contain letters, dashes, apostrophes, or spaces.  Use the suffix box for I, II, III, Jr, or Sr") + ": " + prov.Abbr);
            }
            //prov.Suffix is not validated here. In ErxXml.cs, the suffix is converted to the appropriate suffix enumeration value, or defaults to DDS if the suffix does not make sense.
            string deaNum = prov.DEANum;

            if (provClinic != null)
            {
                deaNum = provClinic.DEANum;
            }
            if (deaNum.ToLower() != "none" && !Regex.IsMatch(deaNum, "^[A-Za-z]{2}[0-9]{7}$"))
            {
                throw new ODException(Lans.g("Erx", "Provider DEA Number must be 2 letters followed by 7 digits.  If no DEA Number, enter NONE.") + ": " + prov.Abbr);
            }
            string npi = Regex.Replace(prov.NationalProvID, "[^0-9]*", "");        //NPI with all non-numeric characters removed.

            if (npi.Length != 10)
            {
                throw new ODException(Lans.g("Erx", "Provider NPI must be exactly 10 digits") + ": " + prov.Abbr);
            }
            if (provClinic == null || provClinic.StateLicense == "")
            {
                throw new ODException(Lans.g("Erx", "Provider state license missing") + ": " + prov.Abbr);
            }
            if (provClinic == null || !USlocales.IsValidAbbr(provClinic.StateWhereLicensed))
            {
                throw new ODException(Lans.g("Erx", "Provider state where licensed invalid") + ": " + prov.Abbr);
            }
        }
Ejemplo n.º 2
0
        ///<summary>Only called from Chart for now.  No validation is performed here.  Validate before calling.  There are many validtion checks, including the NPI must be exactly 10 digits.</summary>
        public static string BuildNewCropClickThroughXml(Provider prov, Employee emp, Patient pat)
        {
            string   deaNumDefault = ProviderClinics.GetDEANum(prov.ProvNum);
            NCScript ncScript      = new NCScript();

            ncScript.Credentials                = new CredentialsType();
            ncScript.Credentials.partnerName    = NewCrop.NewCropPartnerName;
            ncScript.Credentials.name           = NewCrop.NewCropAccountName;
            ncScript.Credentials.password       = NewCrop.NewCropAccountPasssword;
            ncScript.Credentials.productName    = NewCrop.NewCropProductName;
            ncScript.Credentials.productVersion = NewCrop.NewCropProductVersion;
            ncScript.UserRole = new UserRoleType();
            bool isMidlevel = false;

            if (emp == null)           //Provider
            {
                if (prov.IsSecondary)  //Mid-level provider
                {
                    isMidlevel = true;
                    //Secondary (HYG) providers go accross to NewCrop as midlevel providers for now to satisfy the Ohio prescriber requirements.
                    //HYG providers are not normally able to click through to NewCrop because they do not have an NPI number and an NPI is required.
                    //In the future, instead of using the IsSecondary flag as as workaround, we should instead create a new field on the provider table
                    //or perhaps the userod table to allow the user to select the type of provider.
                    ncScript.UserRole.user = UserType.MidlevelPrescriber;
                    ncScript.UserRole.role = RoleType.midlevelPrescriber;
                }
                else                  //Fully licensed provider
                {
                    ncScript.UserRole.user = UserType.LicensedPrescriber;
                    ncScript.UserRole.role = RoleType.doctor;
                }
            }
            else              //Employee
            {
                ncScript.UserRole.user = UserType.Staff;
                ncScript.UserRole.role = RoleType.nurse;
            }
            ncScript.Destination = new DestinationType();
            ncScript.Destination.requestedPage = RequestedPageType.compose;                                //This is the tab that the user will want 90% of the time.
            string practiceTitle    = Tidy(PrefC.GetString(PrefName.PracticeTitle), 50);                   //May be blank.
            string practicePhone    = PrefC.GetString(PrefName.PracticePhone);                             //Validated to be 10 digits within the chart.
            string practiceFax      = PrefC.GetString(PrefName.PracticeFax);                               //Validated to be 10 digits within the chart.
            string practiceAddress  = PrefC.GetString(PrefName.PracticeAddress);                           //Validated to exist in chart.
            string practiceAddress2 = PrefC.GetString(PrefName.PracticeAddress2);                          //May be blank.
            string practiceCity     = PrefC.GetString(PrefName.PracticeCity);                              //Validated to exist in chart.
            string practiceState    = PrefC.GetString(PrefName.PracticeST).ToUpper();                      //Validated to be a US state code in chart.
            string practiceZip      = Regex.Replace(PrefC.GetString(PrefName.PracticeZip), "[^0-9]*", ""); //Zip with all non-numeric characters removed. Validated to be 9 digits in chart.
            string practiceZip4     = practiceZip.Substring(5);                                            //Last 4 digits of zip.

            practiceZip = practiceZip.Substring(0, 5);                                                     //First 5 digits of zip.
            string country = "US";                                                                         //Always United States for now.

            //if(CultureInfo.CurrentCulture.Name.Length>=2) {
            //  country=CultureInfo.CurrentCulture.Name.Substring(CultureInfo.CurrentCulture.Name.Length-2);
            //}
            ncScript.Account = new AccountTypeRx();
            //Each LicensedPrescriberID must be unique within an account. Since we send ProvNum for LicensedPrescriberID, each OD database must have a unique AccountID.
            ncScript.Account.ID                        = PrefC.GetString(PrefName.NewCropAccountId); //Customer account number then a dash then a random alpha-numeric string of 3 characters, followed by 2 digits.
            ncScript.Account.accountName               = practiceTitle;                              //May be blank.
            ncScript.Account.siteID                    = "1";                                        //Always send 1.  For each AccountID/SiteID pair, a separate database will be created in NewCrop.
            ncScript.Account.AccountAddress            = new AddressType();
            ncScript.Account.AccountAddress.address1   = practiceAddress;                            //Validated to exist in chart.
            ncScript.Account.AccountAddress.address2   = practiceAddress2;                           //May be blank.
            ncScript.Account.AccountAddress.city       = practiceCity;                               //Validated to exist in chart.
            ncScript.Account.AccountAddress.state      = practiceState;                              //Validated to be a US state code in chart.
            ncScript.Account.AccountAddress.zip        = practiceZip;                                //Validated to be 9 digits in chart. First 5 digits go in this field.
            ncScript.Account.AccountAddress.zip4       = practiceZip4;                               //Validated to be 9 digits in chart. Last 4 digits go in this field.
            ncScript.Account.AccountAddress.country    = country;                                    //Validated above.
            ncScript.Account.accountPrimaryPhoneNumber = practicePhone;                              //Validated to be 10 digits within the chart.
            ncScript.Account.accountPrimaryFaxNumber   = practiceFax;                                //Validated to be 10 digits within the chart.
            ncScript.Location = new LocationType();
            ProviderClinic provClinic = null;

            if (PrefC.GetBool(PrefName.EasyNoClinics) ||
                (!PrefC.GetBool(PrefName.ElectronicRxClinicUseSelected) && pat.ClinicNum == 0) ||
                (PrefC.GetBool(PrefName.ElectronicRxClinicUseSelected) && Clinics.ClinicNum == 0 && pat.ClinicNum == 0))
            {                                                                  //No clinic.
                ncScript.Location.ID                       = "0";              //Always 0, since clinicnums must be >= 1, will never overlap with a clinic if the office turns clinics on after first use.
                ncScript.Location.locationName             = practiceTitle;    //May be blank.
                ncScript.Location.LocationAddress          = new AddressType();
                ncScript.Location.LocationAddress.address1 = practiceAddress;  //Validated to exist in chart.
                ncScript.Location.LocationAddress.address2 = practiceAddress2; //May be blank.
                ncScript.Location.LocationAddress.city     = practiceCity;     //Validated to exist in chart.
                ncScript.Location.LocationAddress.state    = practiceState;    //Validated to be a US state code in chart.
                ncScript.Location.LocationAddress.zip      = practiceZip;      //Validated to be 9 digits in chart. First 5 digits go in this field.
                ncScript.Location.LocationAddress.zip4     = practiceZip4;     //Validated to be 9 digits in chart. Last 4 digits go in this field.
                ncScript.Location.LocationAddress.country  = country;          //Validated above.
                ncScript.Location.primaryPhoneNumber       = practicePhone;    //Validated to be 10 digits within the chart.
                ncScript.Location.primaryFaxNumber         = practiceFax;      //Validated to be 10 digits within the chart.
                ncScript.Location.pharmacyContactNumber    = practicePhone;    //Validated to be 10 digits within the chart.
            }
            else                                                               //Using clinics.
            {
                Clinic clinic = null;
                if (PrefC.GetBool(PrefName.ElectronicRxClinicUseSelected) && Clinics.ClinicNum != 0)
                {
                    clinic = Clinics.GetClinic(Clinics.ClinicNum);
                }
                else
                {
                    clinic = Clinics.GetClinic(pat.ClinicNum);
                }
                provClinic                                 = ProviderClinics.GetOne(prov.ProvNum, clinic.ClinicNum);
                ncScript.Location.ID                       = clinic.ClinicNum.ToString(); //A positive integer.
                ncScript.Location.locationName             = clinic.Description;          //May be blank.
                ncScript.Location.LocationAddress          = new AddressType();
                ncScript.Location.LocationAddress.address1 = clinic.Address;              //Validated to exist in chart.
                ncScript.Location.LocationAddress.address2 = clinic.Address2;             //May be blank.
                ncScript.Location.LocationAddress.city     = clinic.City;                 //Validated to exist in chart.
                ncScript.Location.LocationAddress.state    = clinic.State.ToUpper();      //Validated to be a US state code in chart.
                string clinicZip  = Regex.Replace(clinic.Zip, "[^0-9]*", "");             //Zip with all non-numeric characters removed. Validated to be 9 digits in chart.
                string clinicZip4 = clinicZip.Substring(5);                               //Last 4 digits of zip.
                clinicZip = clinicZip.Substring(0, 5);                                    //First 5 digits of zip.
                ncScript.Location.LocationAddress.zip     = clinicZip;                    //Validated to be 9 digits in chart. First 5 digits go in this field.
                ncScript.Location.LocationAddress.zip4    = clinicZip4;                   //Validated to be 9 digits in chart. Last 4 digits go in this field.
                ncScript.Location.LocationAddress.country = country;                      //Validated above.
                ncScript.Location.primaryPhoneNumber      = clinic.Phone;                 //Validated to be 10 digits within the chart.
                ncScript.Location.primaryFaxNumber        = clinic.Fax;                   //Validated to be 10 digits within the chart.
                ncScript.Location.pharmacyContactNumber   = clinic.Phone;                 //Validated to be 10 digits within the chart.
            }
            //Each unique provider ID sent to NewCrop will cause a billing charge.
            //Some customer databases have provider duplicates, because they have one provider record per clinic with matching NPIs.
            //We send NPI as the ID to prevent extra NewCrop charges.
            //Conversation with NewCrop:
            //Question: If one of our customers clicks through to NewCrop with 2 different LicensedPrescriber.ID values,
            //          but with the same provider name and NPI, will Open Dental be billed twice or just one time for the NPI used?
            //Answer:   "They would be billed twice. The IDs you send us should always be maintained and unique.
            //          Users are always identified by LicensedPrescriber ID, since their name or credentials could potentially change."
            if (isMidlevel)
            {
                ncScript.MidlevelPrescriber    = new MidlevelPrescriberType();
                ncScript.MidlevelPrescriber.ID = prov.NationalProvID;
                //UPIN is obsolete
                ncScript.MidlevelPrescriber.LicensedPrescriberName = NewCrop.GetPersonNameForProvider(prov);
                if (deaNumDefault.ToLower() == "none")
                {
                    ncScript.MidlevelPrescriber.dea = "NONE";
                }
                else
                {
                    ncScript.MidlevelPrescriber.dea = deaNumDefault;
                }
                if (provClinic != null)
                {
                    ncScript.MidlevelPrescriber.locationDea = provClinic.DEANum;
                }
                ncScript.MidlevelPrescriber.licenseState  = prov.StateWhereLicensed.ToUpper(); //Validated to be a US state code in the chart.
                ncScript.MidlevelPrescriber.licenseNumber = prov.StateLicense;                 //Validated to exist in chart.
                ncScript.MidlevelPrescriber.npi           = prov.NationalProvID;               //Validated to be 10 digits in chart.
            }
            else                                                                               //Licensed presriber
            {
                ncScript.LicensedPrescriber    = new LicensedPrescriberType();
                ncScript.LicensedPrescriber.ID = prov.NationalProvID;
                //UPIN is obsolete
                ncScript.LicensedPrescriber.LicensedPrescriberName = NewCrop.GetPersonNameForProvider(prov);
                if (deaNumDefault.ToLower() == "none")
                {
                    ncScript.LicensedPrescriber.dea = "NONE";
                }
                else
                {
                    ncScript.LicensedPrescriber.dea = deaNumDefault;
                }
                if (provClinic != null)
                {
                    ncScript.LicensedPrescriber.locationDea = provClinic.DEANum;
                }
                ncScript.LicensedPrescriber.licenseState  = prov.StateWhereLicensed.ToUpper(); //Validated to be a US state code in the chart.
                ncScript.LicensedPrescriber.licenseNumber = prov.StateLicense;                 //Validated to exist in chart.
                ncScript.LicensedPrescriber.npi           = prov.NationalProvID;               //Validated to be 10 digits in chart.
                //ncScript.LicensedPrescriber.freeformCredentials=;//This is where DDS and DMD should go, but we don't support this yet. Probably not necessary anyway.
            }
            if (emp != null)
            {
                ncScript.Staff                  = new StaffType();
                ncScript.Staff.ID               = "emp" + emp.EmployeeNum.ToString(); //A positive integer. Returned in the ExternalUserID field when retreiving prescriptions from NewCrop. Also, provider ID is returned in the same field if a provider created the prescription, so that we can create a distintion between employee IDs and provider IDs.
                ncScript.Staff.StaffName        = new PersonNameType();
                ncScript.Staff.StaffName.first  = emp.FName;                          //First name or last name will not be blank. Validated in Chart.
                ncScript.Staff.StaffName.last   = emp.LName;                          //First name or last name will not be blank. Validated in Chart.
                ncScript.Staff.StaffName.middle = emp.MiddleI;                        //May be blank.
            }
            ncScript.Patient                     = new PatientType();
            ncScript.Patient.ID                  = pat.PatNum.ToString(); //A positive integer.
            ncScript.Patient.PatientName         = new PersonNameType();
            ncScript.Patient.PatientName.last    = pat.LName;             //Validated to exist in Patient Edit window.
            ncScript.Patient.PatientName.first   = pat.FName;             //May be blank.
            ncScript.Patient.PatientName.middle  = pat.MiddleI;           //May be blank.
            ncScript.Patient.medicalRecordNumber = pat.PatNum.ToString(); //A positive integer.
            //NewCrop specifically requested that we do not send SSN.
            //ncScript.Patient.socialSecurityNumber=Regex.Replace(pat.SSN,"[^0-9]*","");//Removes all non-numerical characters.
            ncScript.Patient.PatientAddress          = new AddressOptionalType();
            ncScript.Patient.PatientAddress.address1 = pat.Address;         //May be blank.
            ncScript.Patient.PatientAddress.address2 = pat.Address2;        //May be blank.
            ncScript.Patient.PatientAddress.city     = pat.City;            //May be blank.
            ncScript.Patient.PatientAddress.state    = pat.State.ToUpper(); //May be blank. Validated in chart to be blank or to be a valid US state code.
            //For some reason, NewCrop will fail to load if a 9 digit zip code is sent.
            //One customer had all 9 digit zip codes entered for their patients, so we added code here to only send the first 5 digits of the zip.
            //Patient zip is validated in Chart to be blank, or #####, or #####-####, or #########.
            if (pat.Zip == "")
            {
                ncScript.Patient.PatientAddress.zip = "";                      //Blank is allowed.
            }
            else                                                               //5 or 9 digit zip. Formats are #####, or #####-####, or #########.
            {
                ncScript.Patient.PatientAddress.zip = pat.Zip.Substring(0, 5); //First 5 digts only.
            }
            ncScript.Patient.PatientAddress.country = country;                 //Validated above.
            ncScript.Patient.PatientContact         = new ContactType();
            //ncScript.Patient.PatientContact.backOfficeFax=;//We do not have a field to pull this information from.
            //ncScript.Patient.PatientContact.backOfficeTelephone=;//We do not have a field to pull this information from.
            ncScript.Patient.PatientContact.cellularTelephone = pat.WirelessPhone;              //May be blank. Does not need to be 10 digits.
            ncScript.Patient.PatientContact.email             = pat.Email;                      //May be blank, or may also contain multiple email addresses separated by commas.
            //ncScript.Patient.PatientContact.fax=;//We do not have a field to pull this information from.
            ncScript.Patient.PatientContact.homeTelephone = pat.HmPhone;                        //May be blank. Does not need to be 10 digits.
            //ncScript.Patient.PatientContact.pagerTelephone=;//We do not have a field to pull this information from.
            ncScript.Patient.PatientContact.workTelephone = pat.WkPhone;                        //May be blank. Does not need to be 10 digits.
            ncScript.Patient.PatientCharacteristics       = new PatientCharacteristicsType();
            ncScript.Patient.PatientCharacteristics.dob   = pat.Birthdate.ToString("yyyyMMdd"); //DOB must be in CCYYMMDD format.
            if (pat.Gender == PatientGender.Male)
            {
                ncScript.Patient.PatientCharacteristics.gender = GenderType.M;
            }
            else if (pat.Gender == PatientGender.Female)
            {
                ncScript.Patient.PatientCharacteristics.gender = GenderType.F;
            }
            else
            {
                ncScript.Patient.PatientCharacteristics.gender = GenderType.U;
            }
            ncScript.Patient.PatientCharacteristics.genderSpecified = true;
            //NewCrop programmer's comments regarding other fields we are not currently using (these fields are sent back when fetching prescriptions in the Chart):
            //ExternalPrescriptionId = your unique identifier for the prescription, only to be used if you are generating the prescription on your own UI.
            //	This is referenced by NewCrop, and cannot be populated with any other value.
            //EncounterIdentifier = unique ID for the patient visit (e.g. John Doe, 11/11/2013).
            //	This is used by NewCrop for reporting events against a visit, but otherwise does not impact the session.
            //EpisodeIdentifier = unique ID for the patient’s issue (e.g. John Doe’s broken leg) which may include multiple visits.
            //	Currently not used by NewCrop except for being echoed back; it is possible this functionality would be expanded in the future based on its intent as noted.
            //ExternalSource = a codified field noting the origin of the prescription. This may not be used.
            //Serialize
            MemoryStream  memoryStream  = new MemoryStream();
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(NCScript));

            xmlSerializer.Serialize(memoryStream, ncScript);
            byte[] memoryStreamInBytes = memoryStream.ToArray();
            return(Encoding.UTF8.GetString(memoryStreamInBytes, 0, memoryStreamInBytes.Length));
        }
Ejemplo n.º 3
0
        ///<summary>Returns true if we got a URL from PDMP.
        ///If true, the response will be the URL.
        ///If false, the response will be the error.</summary>
        public static bool TrySendData(Program programCur, Patient pat, out string response)
        {
            string        result    = "";
            StringBuilder sbErrors  = new StringBuilder();
            bool          isSuccess = false;

            ODProgress.ShowAction(() => {
                if (!programCur.Enabled)
                {
                    sbErrors.AppendLine(programCur.ProgName + Lans.g("PDMP", " must be enabled in Program Links."));
                    result = sbErrors.ToString();
                    return;
                }
                if (pat == null)
                {
                    sbErrors.AppendLine(Lans.g("PDMP", "Please select a patient."));
                    result = sbErrors.ToString();
                    return;
                }
                Provider prov = Providers.GetProv(pat.PriProv);
                if (prov == null)
                {
                    sbErrors.AppendLine(Lans.g("PDMP", "Patient does not have a primary provider."));
                    result = sbErrors.ToString();
                    return;
                }
                string strDeaNum = ProviderClinics.GetDEANum(prov.ProvNum, Clinics.ClinicNum);             //If no result found, retries using clinicNum=0.
                if (string.IsNullOrWhiteSpace(strDeaNum))
                {
                    sbErrors.AppendLine(Lans.g("PDMP", "Patient's provider does not have a DEA number."));
                }
                string stateWhereLicensed = ProviderClinics.GetStateWhereLicensed(pat.PriProv, Clinics.ClinicNum);
                if (string.IsNullOrWhiteSpace(stateWhereLicensed))
                {
                    sbErrors.AppendLine(Lans.g("PDMP", "Patient's provider is not licensed for any state."));
                }
                string facilityIdPropDesc = "";
                string userNamePropDesc   = "";
                string passwordPropDesc   = "";
                try {
                    facilityIdPropDesc = GetFacilityIdPropDesc(stateWhereLicensed);
                    userNamePropDesc   = GetClientUserNamePropDesc(stateWhereLicensed);
                    passwordPropDesc   = GetClientPasswordPropDesc(stateWhereLicensed);
                }
                catch (NotImplementedException niex) {
                    sbErrors.AppendLine(niex.Message);
                }
                //Validation failed.  We gave the user as much information to fix as possible.
                if (!string.IsNullOrWhiteSpace(sbErrors.ToString()))
                {
                    result = sbErrors.ToString();
                    return;
                }
                //Validation passed and we can now call the PDMP API.
                string facilityId     = ProgramProperties.GetPropVal(programCur.ProgramNum, facilityIdPropDesc);
                string clientUsername = ProgramProperties.GetPropVal(programCur.ProgramNum, userNamePropDesc);
                string clientPassword = ProgramProperties.GetPropVal(programCur.ProgramNum, passwordPropDesc);
                try {
                    //Each state may use a different API for its PDMP services.  Implement appropriate classes to handle each state we support.
                    switch (stateWhereLicensed)
                    {
                    case "IL":
                        PDMPLogicoy pdmp = new PDMPLogicoy(clientUsername, clientPassword, pat, prov, stateWhereLicensed, strDeaNum, facilityId);
                        result           = pdmp.GetURL();
                        isSuccess        = true;
                        break;

                    default:
                        result = Lans.g("PDMP", "PDMP program link has not been implemented for state: ") + stateWhereLicensed;
                        return;
                    }
                }
                catch (Exception ex) {
                    result = ex.Message;
                    return;
                }
            }, startingMessage: Lans.g("PDMP", "Fetching data..."));
            response = result;
            return(isSuccess);
        }