Exemple #1
0
        private void butActivateReminder_Click(object sender, EventArgs e)
        {
            bool isApptRemindAutoEnabled = PrefC.GetBool(PrefName.ApptRemindAutoEnabled);

            isApptRemindAutoEnabled = !isApptRemindAutoEnabled;
            Prefs.UpdateBool(PrefName.ApptRemindAutoEnabled, isApptRemindAutoEnabled);
            SecurityLogs.MakeLogEntry(Permissions.Setup, 0, "Automated appointment eReminders " + (isApptRemindAutoEnabled ? "activated" : "deactivated") + ".");
            Prefs.RefreshCache();
            Signalods.SetInvalid(InvalidType.Prefs);
            FillECRActivationButtons();
            //Add two default reminder rules if none exists.
            if (isApptRemindAutoEnabled && _dictClinicRules[0].Count(x => x.TypeCur == ApptReminderType.Reminder) == 0)
            {
                ApptReminderRule arr = ApptReminderRules.CreateDefaultReminderRule(ApptReminderType.Reminder, 0);             //defaults to 3 hours before appt
                _dictClinicRules[0].Add(arr);
                ApptReminderRule arr2 = ApptReminderRules.CreateDefaultReminderRule(ApptReminderType.Reminder, 0);
                arr2.TSPrior = TimeSpan.FromDays(2);
                _dictClinicRules[0].Add(arr2);
                FillRemindConfirmData();
            }
        }
Exemple #2
0
        private void FormTerminal_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (IsSimpleMode)
            {
                return;
            }
            Process processCur = Process.GetCurrentProcess();

            try {
                Sheets.ClearFromTerminal(PatNum);
                TerminalActives.DeleteForCmptrSessionAndId(Environment.MachineName, processCur.SessionId, processId: processCur.Id);
                //Just in case, close remaining forms that are open
                _formSheetFillEdit.ForceClose();
            }
            catch (Exception) {           //SocketException if db connection gets lost.
                //if either fail, do nothing, the terminalactives will be cleaned up the next time the kiosk mode is enabled for this computer
            }
            finally {
                Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, processCur.Id);
            }
        }
Exemple #3
0
        private void butDone_Click(object sender, EventArgs e)
        {
            Sheets.ClearFromTerminal(PatNum);
            labelForms.Visible = false;
            listForms.Visible  = false;
            butDone.Visible    = false;
            if (IsSimpleMode)             //not subscribed to signals if IsSimpleMode
            {
                PatNum                = 0;
                _listSheets           = new List <Sheet>();
                labelThankYou.Visible = true;
                return;
            }
            //NOT IsSimpleMode from here down
            TerminalActive terminal;
            Process        processCur = Process.GetCurrentProcess();

            try{
                terminal = TerminalActives.GetForCmptrSessionAndId(Environment.MachineName, processCur.SessionId, processCur.Id);
                labelConnection.Visible = false;
            }
            catch (Exception) {           //SocketException if db connection gets lost.
                labelConnection.Visible = true;
                return;
            }
            //this terminal shouldn't be running, receptionist must've deleted this kiosk, close the form (causes application exit if NOT IsSimpleMode).
            if (terminal == null)
            {
                Close();                //signal sent in form closing
                return;
            }
            if (terminal.PatNum != 0)
            {
                labelWelcome.Visible = true;
                TerminalActives.SetPatNum(terminal.TerminalActiveNum, 0);
                PatNum      = 0;
                _listSheets = new List <Sheet>();
                Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, processCur.Id);              //signal the terminal manager to refresh its grid
            }
        }
        private void butActivateInvites_Click(object sender, EventArgs e)
        {
            if (!WebServiceMainHQProxy.IsEServiceActive(_signupOut, eServiceCode.PatientPortal))             //Not yet activated with HQ.
            {
                MsgBox.Show(this, "You must first signup for Patient Portal via the Signup tab before activating Patient Portal Invites.");
                return;
            }
            bool isPatPortalInvitesEnabled = PrefC.GetBool(PrefName.PatientPortalInviteEnabled);

            isPatPortalInvitesEnabled = !isPatPortalInvitesEnabled;
            Prefs.UpdateBool(PrefName.PatientPortalInviteEnabled, isPatPortalInvitesEnabled);
            SecurityLogs.MakeLogEntry(Permissions.Setup, 0, "Patient Portal Invites " + (isPatPortalInvitesEnabled ? "activated" : "deactivated") + ".");
            Prefs.RefreshCache();
            Signalods.SetInvalid(InvalidType.Prefs);
            FillPPInviteActivationButton();
            if (isPatPortalInvitesEnabled && _listPatPortalInviteRules.Count == 0)
            {
                _listPatPortalInviteRules.Add(ApptReminderRules.CreateDefaultReminderRule(ApptReminderType.PatientPortalInvite, 0, isBeforeAppointment: false));
                _listPatPortalInviteRules.Add(ApptReminderRules.CreateDefaultReminderRule(ApptReminderType.PatientPortalInvite, 0, isBeforeAppointment: true));
                FillPatPortalInvites();
            }
        }
Exemple #5
0
        private void butAdd_Click(object sender, EventArgs e)
        {
            FormSheetPicker FormS = new FormSheetPicker();

            FormS.SheetType = SheetTypeEnum.PatientForm;
            FormS.ShowDialog();
            if (FormS.DialogResult != DialogResult.OK)
            {
                return;
            }
            SheetDef sheetDef;
            Sheet    sheet = null;       //only useful if not Terminal

            for (int i = 0; i < FormS.SelectedSheetDefs.Count; i++)
            {
                sheetDef = FormS.SelectedSheetDefs[i];
                sheet    = SheetUtil.CreateSheet(sheetDef, PatNum);
                SheetParameter.SetParameter(sheet, "PatNum", PatNum);
                SheetFiller.FillFields(sheet);
                SheetUtil.CalculateHeights(sheet);
                if (FormS.TerminalSend)
                {
                    sheet.InternalNote   = "";                //because null not ok
                    sheet.ShowInTerminal = (byte)(Sheets.GetBiggestShowInTerminal(PatNum) + 1);
                    Sheets.SaveNewSheet(sheet);               //save each sheet.
                }
            }
            if (FormS.TerminalSend)
            {
                //do not show a dialog now.  User will need to click the terminal button.
                FillGrid();
                Signalods.SetInvalid(InvalidType.Kiosk);
            }
            else
            {
                FormSheetFillEdit.ShowForm(sheet, FormSheetFillEdit_FormClosing);
            }
        }
        private void butClear_Click(object sender, EventArgs e)
        {
            if (gridMain.GetSelectedIndex() == -1 || gridMain.GetSelectedIndex() >= _terminalList.Count)
            {
                MsgBox.Show(this, "Please select a terminal first.");
                return;
            }
            TerminalActive terminal = _terminalList[gridMain.GetSelectedIndex()];

            if (terminal.PatNum == 0)
            {
                return;
            }
            if (!MsgBox.Show(this, true, "A patient is currently using the terminal.  If you continue, they will lose the information that is on their screen.  "
                             + "Continue anyway?"))
            {
                return;
            }
            TerminalActives.SetPatNum(terminal.TerminalActiveNum, 0);
            Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, Process.GetCurrentProcess().Id);
            FillGrid();
            FillPat();
        }
Exemple #7
0
        private void butActivateConfirm_Click(object sender, EventArgs e)
        {
            if (!WebServiceMainHQProxy.IsEServiceActive(_signupOut, eServiceCode.ConfirmationRequest))             //Not yet activated with HQ.
            {
                MsgBox.Show(this, "You must first signup for eConfirmations via the Signup tab before activating eConfirmations.");
                return;
            }
            bool isApptConfirmAutoEnabled = PrefC.GetBool(PrefName.ApptConfirmAutoEnabled);

            isApptConfirmAutoEnabled = !isApptConfirmAutoEnabled;
            Prefs.UpdateBool(PrefName.ApptConfirmAutoEnabled, isApptConfirmAutoEnabled);
            SecurityLogs.MakeLogEntry(Permissions.Setup, 0, "Automated appointment eConfirmations " + (isApptConfirmAutoEnabled ? "activated" : "deactivated") + ".");
            Prefs.RefreshCache();
            Signalods.SetInvalid(InvalidType.Prefs);
            FillECRActivationButtons();
            //Add a default confirmation rule if none exists.
            if (isApptConfirmAutoEnabled && _dictClinicRules[0].Count(x => x.TypeCur == ApptReminderType.ConfirmationFutureDay) == 0)
            {
                ApptReminderRule arr = ApptReminderRules.CreateDefaultReminderRule(ApptReminderType.ConfirmationFutureDay, 0);             //defaults to 7 days before appt
                _dictClinicRules[0].Add(arr);
                FillRemindConfirmData();
            }
        }
Exemple #8
0
        ///<summary>Save all pref changes relating to the given pref. PrefName must have been included in Init().
        ///It is suggested that you use SyncAllPrefs(), it is safer.</summary>>
        public bool SyncPref(PrefName prefName)
        {
            //We ensured that our list had default (ClinicNum 0) prefs when we included defaults in Init(). Should always be available.
            string hqValue = _listClinicPrefs.First(x => x.ClinicNum == 0 && x.PrefName == prefName).ValueString;
            //Save the default (HQ) pref first.
            bool didSave = prefName.Update(hqValue);

            //Our list will likely have clinic-specific entries which are identical to HQ defaults.
            //In this case, remove those duplicates so we don't save them to the db.
            _listClinicPrefs.RemoveAll(x => x.ClinicNum != 0 && x.PrefName == prefName && x.ValueString.Equals(hqValue));
            List <ClinicPref> listNonDefaultClinicPrefs = _listClinicPrefs.FindAll(x => x.ClinicNum > 0 && x.PrefName == prefName);

            if (ClinicPrefs.Sync(listNonDefaultClinicPrefs, ClinicPrefs.GetPrefAllClinics(prefName)))
            {
                didSave = true;
            }
            if (didSave)
            {
                Signalods.SetInvalid(InvalidType.ClinicPrefs);
                ClinicPrefs.RefreshCache();
            }
            return(didSave);
        }
 private void butDefaultClinicClear_Click(object sender, EventArgs e)
 {
     Prefs.UpdateLong(PrefName.TextingDefaultClinicNum, 0);
     Signalods.SetInvalid(InvalidType.Prefs);
     FillGridSmsUsage();
 }
Exemple #10
0
        ///<summary>Used in both modes.  Loads the list of sheets into the listbox.  Then launches the first sheet and goes through the sequence of sheets.
        ///If user clicks cancel, the seqeunce will exit.  If NOT IsSimpleMode, then the TerminalManager can also send a signal to immediately terminate
        ///the sequence.  If PatNum is 0, this will unload the patient by making the form list not visible and the welcome message visible.</summary>
        private void LoadPatient(bool isRefreshOnly)
        {
            TerminalActive terminal = null;

            _listSheets = new List <Sheet>();
            Process processCur = Process.GetCurrentProcess();

            if (IsSimpleMode)
            {
                if (PatNum > 0)
                {
                    _listSheets = Sheets.GetForTerminal(PatNum);
                }
            }
            else              //NOT IsSimpleMode
            {
                try{
                    terminal = TerminalActives.GetForCmptrSessionAndId(Environment.MachineName, processCur.SessionId, processCur.Id);
                    labelConnection.Visible = false;
                }
                catch (Exception) {               //SocketException if db connection gets lost.
                    labelConnection.Visible = true;
                    return;
                }
                if (terminal == null)
                {
                    //this terminal shouldn't be running, receptionist must've deleted this kiosk, close the form (causes application exit if NOT IsSimpleMode)
                    Close();                    //signal sent in form closing
                    return;
                }
                if (terminal.PatNum > 0)
                {
                    _listSheets = Sheets.GetForTerminal(terminal.PatNum);
                }
                if (_listSheets.Count == 0)               //either terminal.PatNum is 0 or no sheets for pat
                {
                    labelWelcome.Visible = true;
                    labelForms.Visible   = false;
                    listForms.Visible    = false;
                    butDone.Visible      = false;
                    if (terminal.PatNum > 0)                   //pat loaded but no sheets to show, unload pat, update db, send signal
                    {
                        TerminalActives.SetPatNum(terminal.TerminalActiveNum, 0);
                        Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, processCur.Id);
                    }
                    PatNum = 0;
                    if (_formSheetFillEdit != null && !_formSheetFillEdit.IsDisposed)
                    {
                        _formSheetFillEdit.ForceClose();
                    }
                    return;
                }
            }
            //we have a patient loaded who has some sheets to show in the terminal
            labelWelcome.Visible = false;
            labelForms.Visible   = true;
            listForms.Visible    = true;
            butDone.Visible      = true;
            listForms.Items.Clear();
            _listSheets.ForEach(x => listForms.Items.Add(x.Description));
            if (!IsSimpleMode)
            {
                if (PatNum == terminal.PatNum)
                {
                    return;                    //if the pat was not cleared or replaced just return, if sheets are currently being displayed (loop below), continue displaying them
                }
                //PatNum is changed, set it to the db terminalactive and signal others, then begin displaying sheets (loop below)
                PatNum = terminal.PatNum;
                if (_formSheetFillEdit != null && !_formSheetFillEdit.IsDisposed)
                {
                    _formSheetFillEdit.ForceClose();
                }
                Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, processCur.Id);
            }
            if (!isRefreshOnly)
            {
                AutoShowSheets();
            }
        }
Exemple #11
0
        private void FormTerminal_Load(object sender, EventArgs e)
        {
            Size     = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size;
            Location = new Point(0, 0);
            labelConnection.Visible = false;
            _listSheets             = new List <Sheet>();
            if (IsSimpleMode)
            {
                //PatNum set externally (in FormPatientForms)
                return;
            }
            //NOT SimpleMode from here down
            Process processCur = Process.GetCurrentProcess();

            //Delete all terminalactives for this computer, except new one, based on CompName and SessionID
            TerminalActives.DeleteForCmptrSessionAndId(Environment.MachineName, processCur.SessionId, excludeId: processCur.Id);
            string clientName = null;
            string userName   = null;

            try {
                clientName = Environment.GetEnvironmentVariable("ClientName");
                userName   = Environment.GetEnvironmentVariable("UserName");
            }
            catch (Exception) {
                //user may not have permission to access environment variables or another error could happen
            }
            if (string.IsNullOrWhiteSpace(clientName))
            {
                //ClientName only set for remote sessions, try to find suitable replacement.
                clientName = userName;
                if (processCur.SessionId < 2 || string.IsNullOrWhiteSpace(userName))
                {
                    //if sessionId is 0 or 1, this is not a remote session, use MachineName
                    clientName = Environment.MachineName;
                }
            }
            if (string.IsNullOrWhiteSpace(clientName) || TerminalActives.IsCompClientNameInUse(Environment.MachineName, clientName))
            {
                InputBox iBox = new InputBox("Please enter a unique name to identify this kiosk.");
                iBox.setTitle(Lan.g(this, "Kiosk Session Name"));
                iBox.ShowDialog();
                while (iBox.DialogResult == DialogResult.OK && TerminalActives.IsCompClientNameInUse(Environment.MachineName, iBox.textResult.Text))
                {
                    MsgBox.Show(this, "The name entered is invalid or already in use.");
                    iBox.ShowDialog();
                }
                if (iBox.DialogResult != DialogResult.OK)
                {
                    DialogResult = DialogResult.Cancel;
                    return;                    //not allowed to enter kiosk mode unless a unique human-readable name is entered for this computer session
                }
                clientName = iBox.textResult.Text;
            }
            //if we get here, we have a SessionId (which could be 0 if not in a remote session) and a unique client name for this kiosk
            TerminalActive terminal = new TerminalActive();

            terminal.ComputerName = Environment.MachineName;
            terminal.SessionId    = processCur.SessionId;
            terminal.SessionName  = clientName;
            terminal.ProcessId    = processCur.Id;
            TerminalActives.Insert(terminal);                                          //tell the database that a terminal is newly active on this computer
            Signalods.SetInvalid(InvalidType.Kiosk, KeyType.ProcessId, processCur.Id); //signal FormTerminalManager to re-fill grids
            timer1.Enabled = true;
        }
Exemple #12
0
 public ODForm()
 {
     this.Shown += new EventHandler((o, e) => {
         Signalods.SubscribeSignalProcessor(this);
     });
 }
        private void butMerge_Click(object sender, EventArgs e)
        {
            if (_patTo.PatNum == _patFrom.PatNum)
            {
                MsgBox.Show(this, "Cannot merge a patient account into itself. Please select a different patient to merge from.");
                return;
            }
            string msgText = "";

            if (_patFrom.FName.Trim().ToLower() != _patTo.FName.Trim().ToLower() ||
                _patFrom.LName.Trim().ToLower() != _patTo.LName.Trim().ToLower() ||
                _patFrom.Birthdate != _patTo.Birthdate)
            {            //mismatch
                msgText = Lan.g(this, "The two patients do not have the same first name, last name, and birthdate.");
                if (Programs.UsingEcwTightOrFullMode())
                {
                    msgText += "\r\n" + Lan.g(this, "The patients must first be merged from within eCW, then immediately merged in the same order in Open Dental.  "
                                              + "If the patients are not merged in this manner, some information may not properly bridge between eCW and Open Dental.");
                }
                msgText += "\r\n\r\n"
                           + Lan.g(this, "Into patient name") + ": " + Patients.GetNameFLnoPref(_patTo.LName, _patTo.FName, "") + ", "     //using Patients.GetNameFLnoPref to omit MiddleI
                           + Lan.g(this, "Into patient birthdate") + ": " + _patTo.Birthdate.ToShortDateString() + "\r\n"
                           + Lan.g(this, "From patient name") + ": " + Patients.GetNameFLnoPref(_patFrom.LName, _patFrom.FName, "") + ", " //using Patients.GetNameFLnoPref to omit MiddleI
                           + Lan.g(this, "From patient birthdate") + ": " + _patFrom.Birthdate.ToShortDateString() + "\r\n\r\n"
                           + Lan.g(this, "Merge the patient on the bottom into the patient shown on the top?");
                if (MessageBox.Show(msgText, "", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    return;                    //The user chose not to merge
                }
            }
            else              //name and bd match
            {
                if (!MsgBox.Show(this, MsgBoxButtons.YesNo, "Merge the patient at the bottom into the patient shown at the top?"))
                {
                    return;                    //The user chose not to merge.
                }
            }
            if (_patFrom.PatNum == _patFrom.Guarantor)
            {
                Family fam = Patients.GetFamily(_patFrom.PatNum);
                if (fam.ListPats.Length > 1)
                {
                    msgText = Lan.g(this, "The patient you have chosen to merge from is a guarantor.  Merging this patient into another account will cause all "
                                    + "family members of the patient being merged from to be moved into the same family as the patient account being merged into.") + "\r\n"
                              + Lan.g(this, "Do you wish to continue with the merge?");
                    if (MessageBox.Show(msgText, "", MessageBoxButtons.YesNo) != DialogResult.Yes)
                    {
                        return;                        //The user chose not to merge.
                    }
                }
            }
            Cursor = Cursors.WaitCursor;
            List <Task> listPatientTasks  = Tasks.RefreshPatientTickets(_patFrom.PatNum);        //Get this before the merge, because the merge updates Task.KeyNum.
            bool        isSuccessfulMerge = false;

            try {
                isSuccessfulMerge = Patients.MergeTwoPatients(_patTo.PatNum, _patFrom.PatNum);
            }
            catch (Exception ex) {
                SecurityLogs.MakeLogEntry(Permissions.PatientMerge, _patTo.PatNum,
                                          "Error occurred while merging Patient: " + _patFrom.GetNameFL() + "\r\nPatNum From: " + _patFrom.PatNum + "\r\nPatNum To: " + _patTo.PatNum);
                Cursor = Cursors.Default;
                FriendlyException.Show(Lan.g(this, "Unable to fully merge patients.  Contact support."), ex);
            }
            if (isSuccessfulMerge)
            {
                //The patient has been successfully merged.
                #region Refresh Patient's Tasks
                List <Signalod> listSignals = new List <Signalod>();
                foreach (Task task in listPatientTasks)
                {
                    Signalod signal = new Signalod();
                    signal.IType       = InvalidType.Task;
                    signal.FKeyType    = KeyType.Task;
                    signal.FKey        = task.TaskNum;
                    signal.DateViewing = DateTime.MinValue;                  //Mimics Signalods.SetInvalid()
                    listSignals.Add(signal);
                }
                Signalods.SetInvalid(InvalidType.TaskPatient, KeyType.Undefined, _patTo.PatNum); //Ensure anyone viewing Patient tab of new pat gets refreshed.
                Signalods.Insert(listSignals.ToArray());                                         //Refreshes existing tasks in all other tabs.
                //Causes Task area and open Task Edit windows to refresh immediately.  No popups, alright to pass empty lists for listRefreshedTaskNotes and
                //listBlockedTaskLists.
                FormOpenDental.S_HandleRefreshedTasks(listSignals, listPatientTasks.Select(x => x.TaskNum).ToList(), listPatientTasks, new List <TaskNote>()
                                                      , new List <UserOdPref>());
                #endregion
                //Now copy the physical images from the old patient to the new if they are using an AtoZ image share.
                //This has to happen in the UI because the middle tier server might not have access to the image share.
                //If the users are storing images within the database, those images have already been taken care of in the merge method above.
                int fileCopyFailures = 0;
                if (PrefC.AtoZfolderUsed == DataStorageType.LocalAtoZ)
                {
                    #region Copy AtoZ Documents
                    //Move the patient documents within the 'patFrom' A to Z folder to the 'patTo' A to Z folder.
                    //We have to be careful here of documents with the same name. We have to rename such documents
                    //so that no documents are overwritten/lost.
                    string   atoZpath  = ImageStore.GetPreferredAtoZpath();
                    string   atozFrom  = ImageStore.GetPatientFolder(_patFrom, atoZpath);
                    string   atozTo    = ImageStore.GetPatientFolder(_patTo, atoZpath);
                    string[] fromFiles = Directory.GetFiles(atozFrom);
                    if (atozFrom == atozTo)
                    {
                        //Very rarely, two patients have the same image folder.  PatFrom and PatTo both have Documents that point to the same file.  Since we
                        //are about to copy the image file for PatFrom to PatTo's directory and delete the file from PatFrom's directory, we would break the
                        //file reference for PatTo's Document.  In this case, skip deleting the original file, since PatTo's Document still references it.
                        Documents.MergePatientDocuments(_patFrom.PatNum, _patTo.PatNum);
                    }
                    else
                    {
                        foreach (string fileCur in fromFiles)
                        {
                            string fileName     = Path.GetFileName(fileCur);
                            string destFileName = fileName;
                            string destFilePath = ODFileUtils.CombinePaths(atozTo, fileName);
                            if (File.Exists(destFilePath))
                            {
                                //The file being copied has the same name as a possibly different file within the destination a to z folder.
                                //We need to copy the file under a unique file name and then make sure to update the document table to reflect
                                //the change.
                                destFileName = _patFrom.PatNum.ToString() + "_" + fileName;
                                destFilePath = ODFileUtils.CombinePaths(atozTo, destFileName);
                                while (File.Exists(destFilePath))
                                {
                                    destFileName = _patFrom.PatNum.ToString() + "_" + fileName + "_" + DateTime.Now.ToString("yyyyMMddhhmmss");
                                    destFilePath = ODFileUtils.CombinePaths(atozTo, destFileName);
                                }
                            }
                            try {
                                File.Copy(fileCur, destFilePath);                                //Will throw exception if file already exists.
                            }
                            catch (Exception ex) {
                                ex.DoNothing();
                                fileCopyFailures++;
                                continue;                                //copy failed, increment counter and move onto the next file
                            }
                            //If the copy did not fail, try to delete the old file.
                            //We can now safely update the document FileName and PatNum to the "to" patient.
                            Documents.MergePatientDocument(_patFrom.PatNum, _patTo.PatNum, fileName, destFileName);
                            try {
                                File.Delete(fileCur);
                            }
                            catch (Exception ex) {
                                ex.DoNothing();
                                //If we were unable to delete the file then it is probably because someone has the document open currently.
                                //Just skip deleting the file. This means that occasionally there will be an extra file in their backup
                                //which is just clutter but at least the merge is guaranteed this way.
                            }
                        }
                    }
                    #endregion Copy AtoZ Documents
                }                //end if AtoZFolderUsed
                else if (CloudStorage.IsCloudStorage)
                {
                    string atozFrom = ImageStore.GetPatientFolder(_patFrom, "");
                    string atozTo   = ImageStore.GetPatientFolder(_patTo, "");
                    if (atozFrom == atozTo)
                    {
                        //Very rarely, two patients have the same image folder.  PatFrom and PatTo both have Documents that point to the same file.  Since we
                        //are about to copy the image file for PatFrom to PatTo's directory and delete the file from PatFrom's directory, we would break the
                        //file reference for PatTo's Document.  In this case, skip deleting the original file, since PatTo's Document still references it.
                        Documents.MergePatientDocuments(_patFrom.PatNum, _patTo.PatNum);
                    }
                    else
                    {
                        FormProgress FormP = new FormProgress();
                        FormP.DisplayText          = "Moving Documents...";
                        FormP.NumberFormat         = "F";
                        FormP.NumberMultiplication = 1;
                        FormP.MaxVal = 100;                      //Doesn't matter what this value is as long as it is greater than 0
                        FormP.TickMS = 1000;
                        OpenDentalCloud.Core.TaskStateMove state = CloudStorage.MoveAsync(atozFrom
                                                                                          , atozTo
                                                                                          , new OpenDentalCloud.ProgressHandler(FormP.OnProgress));
                        if (FormP.ShowDialog() == DialogResult.Cancel)
                        {
                            state.DoCancel   = true;
                            fileCopyFailures = state.CountTotal - state.CountSuccess;
                        }
                        else
                        {
                            fileCopyFailures = state.CountFailed;
                        }
                    }
                }
                Cursor = Cursors.Default;
                if (fileCopyFailures > 0)
                {
                    MessageBox.Show(Lan.g(this, "Some files belonging to the from patient were not copied.") + "\r\n"
                                    + Lan.g(this, "Number of files not copied") + ": " + fileCopyFailures);
                }
                //Make log entry here not in parent form because we can merge multiple patients at a time.
                SecurityLogs.MakeLogEntry(Permissions.PatientMerge, _patTo.PatNum,
                                          "Patient: " + _patFrom.GetNameFL() + "\r\nPatNum From: " + _patFrom.PatNum + "\r\nPatNum To: " + _patTo.PatNum);
                textPatientIDFrom.Text    = "";
                textPatientNameFrom.Text  = "";
                textPatFromBirthdate.Text = "";
                //This will cause CheckUIState() to disabled the merge button until the user selects a new _patFrom.
                _patFrom = null;
                CheckUIState();
                MsgBox.Show(this, "Patients merged successfully.");
            }            //end MergeTwoPatients
            Cursor = Cursors.Default;
        }
Exemple #14
0
        private void butOK_Click(object sender, System.EventArgs e)
        {
            if (Defs.IsHidable(DefCur.Category) && checkHidden.Checked)
            {
                if (Defs.IsDefinitionInUse(DefCur))
                {
                    if (DefCur.DefNum == PrefC.GetLong(PrefName.BrokenAppointmentAdjustmentType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.AppointmentTimeArrivedTrigger) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.AppointmentTimeSeatedTrigger) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.AppointmentTimeDismissedTrigger) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.TreatPlanDiscountAdjustmentType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.BillingChargeAdjustmentType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.FinanceChargeAdjustmentType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.PrepaymentUnearnedType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.PracticeDefaultBillType) ||
                        DefCur.DefNum == PrefC.GetLong(PrefName.SalesTaxAdjustmentType))
                    {
                        MsgBox.Show(this, "You cannot hide a definition if it is in use within Module Preferences.");
                        return;
                    }
                    else if (DefCur.DefNum.In(
                                 PrefC.GetLong(PrefName.RecallStatusMailed),
                                 PrefC.GetLong(PrefName.RecallStatusTexted),
                                 PrefC.GetLong(PrefName.RecallStatusEmailed),
                                 PrefC.GetLong(PrefName.RecallStatusEmailedTexted)))
                    {
                        MsgBox.Show(this, "You cannot hide a definition that is used as a status in the Setup Recall window.");
                        return;
                    }
                    else if (DefCur.DefNum == PrefC.GetLong(PrefName.WebSchedNewPatConfirmStatus))
                    {
                        MsgBox.Show(this, "You cannot hide a definition that is used as an appointment confirmation status in Web Sched New Pat Appt.");
                        return;
                    }
                    else if (DefCur.DefNum == PrefC.GetLong(PrefName.WebSchedRecallConfirmStatus))
                    {
                        MsgBox.Show(this, "You cannot hide a definition that is used as an appointment confirmation status in Web Sched Recall Appt.");
                        return;
                    }
                    else
                    {
                        if (!MsgBox.Show(this, MsgBoxButtons.OKCancel, "Warning: This definition is currently in use within the program."))
                        {
                            return;
                        }
                    }
                }
                //Stop users from hiding the last definition in categories that must have at least one def in them.
                if (!_defsList.Any(x => x.DefNum != DefCur.DefNum && !x.IsHidden))
                {
                    MsgBox.Show(this, "You cannot hide the last definition in this category.");
                    return;
                }
            }
            if (textName.Text == "")
            {
                MsgBox.Show(this, "Name required.");
                return;
            }
            switch (DefCur.Category)
            {
            case DefCat.AccountQuickCharge:
            case DefCat.ApptProcsQuickAdd:
                string[]      procCodes     = textValue.Text.Split(',');
                List <string> listProcCodes = new List <string>();
                for (int i = 0; i < procCodes.Length; i++)
                {
                    ProcedureCode procCode = ProcedureCodes.GetProcCode(procCodes[i]);
                    if (procCode.CodeNum == 0)
                    {
                        //Now check to see if the trimmed version of the code does not exist either.
                        procCode = ProcedureCodes.GetProcCode(procCodes[i].Trim());
                        if (procCode.CodeNum == 0)
                        {
                            MessageBox.Show(Lan.g(this, "Invalid procedure code entered") + ": " + procCodes[i]);
                            return;
                        }
                    }
                    listProcCodes.Add(procCode.ProcCode);
                }
                textValue.Text = String.Join(",", listProcCodes);
                break;

            case DefCat.AdjTypes:
                if (textValue.Text != "+" && textValue.Text != "-" && textValue.Text != "dp")
                {
                    MessageBox.Show(Lan.g(this, "Valid values are +, -, or dp."));
                    return;
                }
                break;

            case DefCat.BillingTypes:
                if (textValue.Text != "" && textValue.Text != "E" && textValue.Text != "C")
                {
                    MsgBox.Show(this, "Valid values are blank, E, or C.");
                    return;
                }
                if (checkHidden.Checked && Patients.IsBillingTypeInUse(DefCur.DefNum))
                {
                    if (!MsgBox.Show(this, MsgBoxButtons.OKCancel, "Warning: Billing type is currently in use by patients, insurance plans, or preferences."))
                    {
                        return;
                    }
                }
                break;

            case DefCat.ClaimCustomTracking:
                int value = 0;
                if (!Int32.TryParse(textValue.Text, out value) || value < 0)
                {
                    MsgBox.Show(this, "Days Suppressed must be a valid non-negative number.");
                    return;
                }
                break;

            case DefCat.CommLogTypes:
                if (textValue.Text != "" && textValue.Text != "MISC" && textValue.Text != "APPT" &&
                    textValue.Text != "FIN" && textValue.Text != "RECALL" && textValue.Text != "TEXT")
                {
                    MessageBox.Show(Lan.g(this, "Valid values are blank,APPT,FIN,RECALL,MISC,or TEXT."));
                    return;
                }
                break;

            case DefCat.DiscountTypes:
                int discVal;
                if (textValue.Text == "")
                {
                    break;
                }
                try {
                    discVal = System.Convert.ToInt32(textValue.Text);
                }
                catch {
                    MessageBox.Show(Lan.g(this, "Not a valid number"));
                    return;
                }
                if (discVal < 0 || discVal > 100)
                {
                    MessageBox.Show(Lan.g(this, "Valid values are between 0 and 100"));
                    return;
                }
                textValue.Text = discVal.ToString();
                break;

            /*case DefCat.FeeSchedNames:
             *      if(textValue.Text=="C" || textValue.Text=="c") {
             *              textValue.Text="C";
             *      }
             *      else if(textValue.Text=="A" || textValue.Text=="a") {
             *              textValue.Text="A";
             *      }
             *      else textValue.Text="";
             *      break;*/
            case DefCat.ImageCats:
                textValue.Text = textValue.Text.ToUpper().Replace(",", "");
                if (!Regex.IsMatch(textValue.Text, @"^[XPS]*$"))
                {
                    textValue.Text = "";
                }
                break;

            case DefCat.InsurancePaymentType:
                if (textValue.Text != "" && textValue.Text != "N")
                {
                    MsgBox.Show(this, "Valid values are blank or N.");
                    return;
                }
                break;

            case DefCat.OperatoriesOld:
                if (textValue.Text.Length > 5)
                {
                    MessageBox.Show(Lan.g(this, "Maximum length of abbreviation is 5."));
                    return;
                }
                break;

            case DefCat.ProcCodeCats:
                if (checkHidden.Checked)
                {
                    if (IsDefCurLastShowing())
                    {
                        MsgBox.Show(this, "At least one procedure code category must be enabled.");
                        return;
                    }
                }
                break;

            case DefCat.ProviderSpecialties:
                if (checkHidden.Checked &&
                    (Providers.IsSpecialtyInUse(DefCur.DefNum) ||
                     Referrals.IsSpecialtyInUse(DefCur.DefNum)))
                {
                    MsgBox.Show(this, "You cannot hide a specialty if it is in use by a provider or a referral source.");
                    checkHidden.Checked = false;
                    return;
                }
                break;

            case DefCat.RecallUnschedStatus:
                if (textValue.Text.Length > 7)
                {
                    MessageBox.Show(Lan.g(this, "Maximum length is 7."));
                    return;
                }
                break;

            case DefCat.TaskPriorities:
                if (checkHidden.Checked)
                {
                    if (IsDefCurLastShowing())
                    {
                        MsgBox.Show(this, "You cannot hide the last priority.");
                        return;
                    }
                }
                break;

            case DefCat.TxPriorities:
                if (textValue.Text.Length > 7)
                {
                    MessageBox.Show(Lan.g(this, "Maximum length of abbreviation is 7."));
                    return;
                }
                break;

            default:
                break;
            }            //end switch DefCur.Category
            DefCur.ItemName  = textName.Text;
            DefCur.ItemValue = _selectedValueString;
            if (_defCatOptions.EnableValue && !_defCatOptions.IsValueDefNum)
            {
                DefCur.ItemValue = textValue.Text;
            }
            if (_defCatOptions.EnableColor)
            {
                DefCur.ItemColor = butColor.BackColor;
            }
            DefCur.IsHidden = checkHidden.Checked;
            if (IsNew)
            {
                Defs.Insert(DefCur);
            }
            else
            {
                Defs.Update(DefCur);
            }
            //Must be after the upsert so that we have access to the DefNum for new Defs.
            if (DefCur.Category == DefCat.ApptConfirmed)
            {
                //==================== EXCLUDE SEND ====================
                if (checkExcludeSend.Checked)
                {
                    _listExcludeSendNums.Add(DefCur.DefNum);
                }
                else
                {
                    _listExcludeSendNums.RemoveAll(x => x == DefCur.DefNum);
                }
                string sendString = string.Join(",", _listExcludeSendNums.Distinct().OrderBy(x => x));
                Prefs.UpdateString(PrefName.ApptConfirmExcludeESend, sendString);
                //==================== EXCLUDE CONFIRM ====================
                if (checkExcludeConfirm.Checked)
                {
                    _listExcludeConfirmNums.Add(DefCur.DefNum);
                }
                else
                {
                    _listExcludeConfirmNums.RemoveAll(x => x == DefCur.DefNum);
                }
                string confirmString = string.Join(",", _listExcludeConfirmNums.Distinct().OrderBy(x => x));
                Prefs.UpdateString(PrefName.ApptConfirmExcludeEConfirm, confirmString);
                //==================== EXCLUDE REMIND ====================
                if (checkExcludeRemind.Checked)
                {
                    _listExcludeRemindNums.Add(DefCur.DefNum);
                }
                else
                {
                    _listExcludeRemindNums.RemoveAll(x => x == DefCur.DefNum);
                }
                string remindString = string.Join(",", _listExcludeRemindNums.Distinct().OrderBy(x => x));
                Prefs.UpdateString(PrefName.ApptConfirmExcludeERemind, remindString);
                Signalods.SetInvalid(InvalidType.Prefs);
            }
            DialogResult = DialogResult.OK;
        }
Exemple #15
0
        ///<summary>Spawns a new thread to retrieve new signals from the DB, update caches, and broadcast signals to all subscribed forms.</summary>
        public static void SignalsTick(Action onShutdown, Action <List <ODForm>, List <Signalod> > onProcess, Action onDone)
        {
            //No need to check RemotingRole; no call to db.
            Logger.LogToPath("", LogPath.Signals, LogPhase.Start);
            List <Signalod> listSignals          = new List <Signalod>();
            ODThread        threadRefreshSignals = new ODThread((o) => {
                //Get new signals from DB.
                Logger.LogToPath("RefreshTimed", LogPath.Signals, LogPhase.Start);
                listSignals = Signalods.RefreshTimed(Signalods.SignalLastRefreshed);
                Logger.LogToPath("RefreshTimed", LogPath.Signals, LogPhase.End);
                //Only update the time stamp with signals retreived from the DB. Do NOT use listLocalSignals to set timestamp.
                if (listSignals.Count > 0)
                {
                    Signalods.SignalLastRefreshed     = listSignals.Max(x => x.SigDateTime);
                    Signalods.ApptSignalLastRefreshed = Signalods.SignalLastRefreshed;
                }
                Logger.LogToPath("Found " + listSignals.Count.ToString() + " signals", LogPath.Signals, LogPhase.Unspecified);
                if (listSignals.Count == 0)
                {
                    return;
                }
                Logger.LogToPath("Signal count(s)", LogPath.Signals, LogPhase.Unspecified, string.Join(" - ", listSignals.GroupBy(x => x.IType).Select(x => x.Key.ToString() + ": " + x.Count())));
                if (listSignals.Exists(x => x.IType == InvalidType.ShutDownNow))
                {
                    onShutdown();
                    return;
                }
                InvalidType[] cacheRefreshArray = listSignals.FindAll(x => x.FKey == 0 && x.FKeyType == KeyType.Undefined).Select(x => x.IType).Distinct().ToArray();
                //Always process signals for ClientDirect users regardless of where the RemoteRole source on the signal is from.
                //The middle tier server will have refreshed its cache already.
                bool getCacheFromDb = true;
                if (RemotingClient.RemotingRole == RemotingRole.ClientWeb &&
                    !listSignals.Any(x => x.RemoteRole == RemotingRole.ClientDirect))
                {
                    //ClientWebs do not need to tell the middle tier to go to the database unless a ClientDirect has inserted a signal.
                    getCacheFromDb = false;
                }
                Cache.Refresh(getCacheFromDb, cacheRefreshArray);
                onProcess(_listSubscribedForms, listSignals);
            });

            threadRefreshSignals.AddExceptionHandler((e) => {
                DateTime dateTimeRefreshed;
                try {
                    //Signal processing should always use the server's time.
                    dateTimeRefreshed = MiscData.GetNowDateTime();
                }
                catch {
                    //If the server cannot be reached, we still need to move the signal processing forward so use local time as a fail-safe.
                    dateTimeRefreshed = DateTime.Now;
                }
                Signalods.SignalLastRefreshed     = dateTimeRefreshed;
                Signalods.ApptSignalLastRefreshed = dateTimeRefreshed;
            });
            threadRefreshSignals.AddExitHandler((o) => {
                Logger.LogToPath("", LogPath.Signals, LogPhase.End);
                onDone();
            });
            threadRefreshSignals.Name = "SignalsTick";
            threadRefreshSignals.Start(true);
        }
Exemple #16
0
        public static Bug AddBugAndJob(ODForm form, List <BugSubmission> listSelectedSubs, Patient pat)
        {
            if (!JobPermissions.IsAuthorized(JobPerm.Concept, true) &&
                !JobPermissions.IsAuthorized(JobPerm.NotifyCustomer, true) &&
                !JobPermissions.IsAuthorized(JobPerm.FeatureManager, true) &&
                !JobPermissions.IsAuthorized(JobPerm.Documentation, true))
            {
                return(null);
            }
            if (listSelectedSubs.Count == 0)
            {
                MsgBox.Show(form, "You must select a bug submission to create a job for.");
                return(null);
            }
            Job jobNew = new Job();

            jobNew.Category = JobCategory.Bug;
            InputBox titleBox = new InputBox("Provide a brief title for the job.");

            if (titleBox.ShowDialog() != DialogResult.OK)
            {
                return(null);
            }
            if (String.IsNullOrEmpty(titleBox.textResult.Text))
            {
                MsgBox.Show(form, "You must type a title to create a job.");
                return(null);
            }
            List <Def> listJobPriorities = Defs.GetDefsForCategory(DefCat.JobPriorities, true);

            if (listJobPriorities.Count == 0)
            {
                MsgBox.Show(form, "You have no priorities setup in definitions.");
                return(null);
            }
            jobNew.Title = titleBox.textResult.Text;
            long priorityNum = 0;

            priorityNum           = listJobPriorities.FirstOrDefault(x => x.ItemValue.Contains("BugDefault")).DefNum;
            jobNew.Priority       = priorityNum == 0?listJobPriorities.First().DefNum:priorityNum;
            jobNew.PhaseCur       = JobPhase.Concept;
            jobNew.UserNumConcept = Security.CurUser.UserNum;
            Bug bugNew = new Bug();

            bugNew = Bugs.GetNewBugForUser();
            InputBox bugDescription = new InputBox("Provide a brief description for the bug. This will appear in the bug tracker.", jobNew.Title);

            if (bugDescription.ShowDialog() != DialogResult.OK)
            {
                return(null);
            }
            if (String.IsNullOrEmpty(bugDescription.textResult.Text))
            {
                MsgBox.Show(form, "You must type a description to create a bug.");
                return(null);
            }
            FormVersionPrompt FormVP = new FormVersionPrompt("Enter versions found");

            FormVP.ShowDialog();
            if (FormVP.DialogResult != DialogResult.OK || string.IsNullOrEmpty(FormVP.VersionText))
            {
                MsgBox.Show(form, "You must enter a version to create a bug.");
                return(null);
            }
            bugNew.Status_       = BugStatus.Accepted;
            bugNew.VersionsFound = FormVP.VersionText;
            bugNew.Description   = bugDescription.textResult.Text;
            BugSubmission sub = listSelectedSubs.First();

            jobNew.Requirements = BugSubmissions.GetSubmissionDescription(pat, sub);
            Jobs.Insert(jobNew);
            JobLink jobLinkNew = new JobLink();

            jobLinkNew.LinkType = JobLinkType.Bug;
            jobLinkNew.JobNum   = jobNew.JobNum;
            jobLinkNew.FKey     = Bugs.Insert(bugNew);
            JobLinks.Insert(jobLinkNew);
            BugSubmissions.UpdateBugIds(bugNew.BugId, listSelectedSubs.Select(x => x.BugSubmissionNum).ToList());
            if (MsgBox.Show(form, MsgBoxButtons.YesNo, "Would you like to create a task too?"))
            {
                long taskNum = CreateTask(pat, sub);
                if (taskNum != 0)
                {
                    jobLinkNew          = new JobLink();
                    jobLinkNew.LinkType = JobLinkType.Task;
                    jobLinkNew.JobNum   = jobNew.JobNum;
                    jobLinkNew.FKey     = taskNum;
                    JobLinks.Insert(jobLinkNew);
                }
            }
            Signalods.SetInvalid(InvalidType.Jobs, KeyType.Job, jobNew.JobNum);
            FormOpenDental.S_GoToJob(jobNew.JobNum);
            return(bugNew);
        }
        ///<summary>Function used by threads to connect to remote databases and sync user settings.</summary>
        private static void ConnectAndSyncUsers(ODThread odThread)
        {
            CentralConnection      connection          = (CentralConnection)odThread.Parameters[0];
            List <CentralUserData> listCentralUserData = (List <CentralUserData>)odThread.Parameters[1];
            string serverName = "";

            if (connection.ServiceURI != "")
            {
                serverName = connection.ServiceURI;
            }
            else
            {
                serverName = connection.ServerName + ", " + connection.DatabaseName;
            }
            if (!CentralConnectionHelper.UpdateCentralConnection(connection, false))            //No updating the cache since we're going to be connecting to multiple remote servers at the same time.
            {
                odThread.Tag = new List <string>()
                {
                    serverName + "\r\n", "", ""
                };
                connection.ConnectionStatus = "OFFLINE";
                return;
            }
            string remoteSyncCode = PrefC.GetStringNoCache(PrefName.CentralManagerSyncCode);

            if (remoteSyncCode != _syncCode)
            {
                if (remoteSyncCode == "")
                {
                    Prefs.UpdateStringNoCache(PrefName.CentralManagerSyncCode, _syncCode);                   //Lock in the sync code for the remote server.
                }
                else
                {
                    odThread.Tag = new List <string>()
                    {
                        serverName + "\r\n", "", remoteSyncCode
                    };
                    return;
                }
            }
            //Get remote users, usergroups, associated permissions, and alertsubs
            List <Userod> listRemoteUsers = Userods.GetUsersNoCache();

            #region Detect Conflicts
            //User conflicts
            bool   nameConflict  = false;
            string nameConflicts = "";
            for (int i = 0; i < _listCEMTUsers.Count; i++)
            {
                for (int j = 0; j < listRemoteUsers.Count; j++)
                {
                    if (listRemoteUsers[j].UserName == _listCEMTUsers[i].UserName && listRemoteUsers[j].UserNumCEMT == 0)                 //User doesn't belong to CEMT
                    {
                        nameConflicts += listRemoteUsers[j].UserName + " already exists in " + serverName + "\r\n";
                        nameConflict   = true;
                        break;
                    }
                }
            }
            if (nameConflict)
            {
                odThread.Tag = new List <string>()
                {
                    serverName + "\r\n", nameConflicts, ""
                };
                return;                //Skip on to the next connection.
            }
            #endregion Detect Conflicts
            List <UserGroup> listRemoteCEMTUserGroups = UserGroups.GetCEMTGroupsNoCache();
            List <UserGroup> listCEMTUserGroups       = new List <UserGroup>();
            List <AlertSub>  listRemoteAlertSubs      = AlertSubs.GetAll();
            List <Clinic>    listRemoteClinics        = Clinics.GetClinicsNoCache();
            List <AlertSub>  listAlertSubsToInsert    = new List <AlertSub>();
            for (int i = 0; i < listCentralUserData.Count; i++)
            {
                listCEMTUserGroups.Add(listCentralUserData[i].UserGroup.Copy());
            }
            //SyncUserGroups returns the list of UserGroups for deletion so it can be used after syncing Users and GroupPermissions.
            List <UserGroup> listRemoteCEMTUserGroupsForDeletion = CentralUserGroups.Sync(listCEMTUserGroups, listRemoteCEMTUserGroups);
            listRemoteCEMTUserGroups = UserGroups.GetCEMTGroupsNoCache();
            for (int i = 0; i < listCentralUserData.Count; i++)
            {
                List <GroupPermission> listGroupPerms = new List <GroupPermission>();
                for (int j = 0; j < listRemoteCEMTUserGroups.Count; j++)
                {
                    if (listCentralUserData[i].UserGroup.UserGroupNumCEMT == listRemoteCEMTUserGroups[j].UserGroupNumCEMT)
                    {
                        for (int k = 0; k < listCentralUserData[i].ListGroupPermissions.Count; k++)
                        {
                            listCentralUserData[i].ListGroupPermissions[k].UserGroupNum = listRemoteCEMTUserGroups[j].UserGroupNum;                          //fixing primary keys to be what's in remote db
                        }
                        listGroupPerms = GroupPermissions.GetPermsNoCache(listRemoteCEMTUserGroups[j].UserGroupNum);
                    }
                }
                CentralUserods.Sync(listCentralUserData[i].ListUsers, listRemoteUsers);
                CentralGroupPermissions.Sync(listCentralUserData[i].ListGroupPermissions, listGroupPerms);
            }
            //Sync usergroup attaches
            SyncUserGroupAttaches(listCentralUserData);
            for (int j = 0; j < listRemoteCEMTUserGroupsForDeletion.Count; j++)
            {
                UserGroups.DeleteNoCache(listRemoteCEMTUserGroupsForDeletion[j]);
            }
            if (_listAlertSubs.Count > 0)
            {
                listRemoteUsers = Userods.GetUsersNoCache();              //Refresh users so we can do alertsubs.
            }
            //For each AlertSub, make a copy of that AlertSub for each Clinic.
            foreach (AlertSub alertSub in _listAlertSubs)
            {
                foreach (Clinic clinic in listRemoteClinics)
                {
                    AlertSub alert = new AlertSub();
                    alert.ClinicNum = clinic.ClinicNum;
                    alert.Type      = alertSub.Type;
                    alert.UserNum   = listRemoteUsers.Find(x => x.UserName == _listCEMTUsers.Find(y => y.UserNum == alertSub.UserNum).UserName).UserNum;
                    listAlertSubsToInsert.Add(alert);
                }
            }
            AlertSubs.DeleteAndInsertForSuperUsers(_listCEMTUsers, listAlertSubsToInsert);
            //Refresh server's cache of userods
            Signalods.SetInvalidNoCache(InvalidType.Security);
            SecurityLogs.MakeLogEntryNoCache(Permissions.SecurityAdmin, 0, "Enterprise Management Tool synced users.");
            odThread.Tag = new List <string>()
            {
                "", "", ""
            };                                                       //No errors.
        }