示例#1
0
        private void butSynch_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;
            GC.Collect();            //free up resources since this could take a lot of memory
            DataValid.SetInvalid(InvalidType.RecallTypes);
            _actionCloseRecallSyncProgress = ODProgressOld.ShowProgressStatus("RecallSyncEvent"
                                                                              , typeof(RecallSyncEvent)
                                                                              , tag: new ProgressBarHelper(Lan.g(this, "Running Prep Queries") + "...", null, 0, 100, ProgBarStyle.Marquee
                                                                                                           , progressBarEventType: ProgBarEventType.Header)
                                                                              , currentForm: this);
            bool syncCompleted = Recalls.SynchAllPatients();

            _actionCloseRecallSyncProgress?.Invoke();
            GC.Collect();            //clean up resources, force the garbage collector to collect since resources may remain tied-up
            Cursor = Cursors.Default;
            if (syncCompleted)
            {
                changed = false;
                MsgBox.Show(this, "Done.");
            }
            else
            {
                MsgBox.Show(this, "Synch is currently running from a different workstation.");
            }
        }
示例#2
0
        ///<summary>Returns false if the backup, repair, or the optimze failed.
        ///Set isSilent to true to suppress the failure message boxes.  However, progress windows will always be shown.</summary>
        public static bool BackupRepairAndOptimize(bool isSilent, BackupLocation backupLocation, bool isSecurityLogged = true)
        {
            if (!MakeABackup(isSilent, backupLocation, isSecurityLogged))
            {
                return(false);
            }
            //Create a thread that will show a window and then stay open until the closing phrase is thrown from this form.
            Action actionCloseRepairAndOptimizeProgress = ODProgressOld.ShowProgressStatus("RepairAndOptimizeProgress", null);

            try {
                DatabaseMaintenances.RepairAndOptimize();
                actionCloseRepairAndOptimizeProgress();
            }
            catch (Exception ex) {           //MiscData.MakeABackup() could have thrown an exception.
                actionCloseRepairAndOptimizeProgress();
                //Show the user that something what went wrong when not in silent mode.
                if (!isSilent)
                {
                    if (ex.Message != "")
                    {
                        MessageBox.Show(ex.Message);
                    }
                    MsgBox.Show("FormDatabaseMaintenance", "Optimize and Repair failed.");
                }
                return(false);
            }
            return(true);
        }
示例#3
0
 private void FormRecallTypes_FormClosing(object sender, FormClosingEventArgs e)
 {
     if (changed)
     {
         DataValid.SetInvalid(InvalidType.RecallTypes);
         if (MessageBox.Show(Lan.g(this, "Recalls for all patients should be synchronized.  Synchronize now?"), "", MessageBoxButtons.YesNo)
             == DialogResult.Yes)
         {
             Cursor = Cursors.WaitCursor;
             GC.Collect();                    //free up resources since this could take a lot of memory
             _actionCloseRecallSyncProgress = ODProgressOld.ShowProgressStatus("RecallSyncEvent"
                                                                               , typeof(RecallSyncEvent)
                                                                               , tag: new ProgressBarHelper(Lan.g(this, "Running Prep Queries") + "...", null, 0, 100, ProgBarStyle.Marquee
                                                                                                            , progressBarEventType: ProgBarEventType.Header)
                                                                               , currentForm: this);
             bool syncSuccessful = Recalls.SynchAllPatients();
             _actionCloseRecallSyncProgress?.Invoke();
             GC.Collect();                    //clean up resources, force the garbage collector to collect since resources may remain tied-up
             Cursor = Cursors.Default;
             if (!syncSuccessful)
             {
                 MsgBox.Show(this, "Synch is currently running from a different workstation.  Recalls should be synchronized again later.");
             }
         }
     }
 }
示例#4
0
        private void butMovePats_Click(object sender, EventArgs e)
        {
            if (gridMain.SelectedIndices.Length < 1)
            {
                MsgBox.Show(this, "You must select at least one clinic to move patients from.");
                return;
            }
            List <Clinic> listClinicsFrom = gridMain.SelectedIndices.OfType <int>().Select(x => (Clinic)gridMain.Rows[x].Tag).ToList();
            List <Clinic> listClinicsTo   = gridMain.Rows.Select(x => x.Tag as Clinic).ToList();

            if (_clinicNumTo == -1)
            {
                MsgBox.Show(this, "You must pick a 'To' clinic in the box above to move patients to.");
                return;
            }
            Clinic clinicTo = listClinicsTo.FirstOrDefault(x => x.ClinicNum == _clinicNumTo);

            if (clinicTo == null)
            {
                MsgBox.Show(this, "The clinic could not be found.");
                return;
            }
            Action actionCloseProgress = ODProgressOld.ShowProgressStatus("ClinicReassign", this, Lan.g(this, "Gathering patient data") + "...");
            Dictionary <long, List <long> > dictClinicPats = Patients.GetPatNumsByClinic(listClinicsFrom.Select(x => x.ClinicNum).ToList()).Select()
                                                             .GroupBy(x => PIn.Long(x["ClinicNum"].ToString()), x => PIn.Long(x["PatNum"].ToString()))
                                                             .ToDictionary(x => x.Key, x => x.ToList());

            actionCloseProgress?.Invoke();
            int totalPatCount = dictClinicPats.Sum(x => x.Value.Count);

            if (totalPatCount == 0)
            {
                MsgBox.Show(this, "The selected clinics are not clinics for any patients.");
                return;
            }
            string strClinicFromDesc = string.Join(", ", listClinicsFrom.FindAll(x => dictClinicPats.ContainsKey(x.ClinicNum)).Select(x => (x.ClinicNum == 0?"HQ":x.Abbr)));
            string strClinicToDesc   = clinicTo.Abbr;
            string msg = Lan.g(this, "Move all patients to") + " " + strClinicToDesc + " " + Lan.g(this, "from the following clinics") + ": " + strClinicFromDesc + "?";

            if (MessageBox.Show(msg, "", MessageBoxButtons.OKCancel) != DialogResult.OK)
            {
                return;
            }
            actionCloseProgress = ODProgressOld.ShowProgressStatus("ClinicReassign", this, Lan.g(this, "Moving patients") + "...");
            int           patsMoved   = 0;
            List <Action> listActions = dictClinicPats.Select(x => new Action(() => {
                patsMoved += x.Value.Count;
                ODEvent.Fire(new ODEventArgs("ClinicReassign", Lan.g(this, "Moving patients") + ": " + patsMoved + " out of " + totalPatCount));
                Patients.ChangeClinicsForAll(x.Key, clinicTo.ClinicNum);               //update all clinicNums to new clinic
                SecurityLogs.MakeLogEntry(Permissions.PatientEdit, 0, "Clinic changed for " + x.Value.Count + " patients from "
                                          + (x.Key == 0 ? "HQ" : Clinics.GetAbbr(x.Key)) + " to " + clinicTo.Abbr + ".");
            })).ToList();

            ODThread.RunParallel(listActions, TimeSpan.FromMinutes(2));
            actionCloseProgress?.Invoke();
            _dictClinicalCounts = Clinics.GetClinicalPatientCount();
            FillGrid();
            MsgBox.Show(this, "Done");
        }
示例#5
0
        ///<summary>If !isPreCharges, a message box will display for any errors instructing users to try again.  If the failed aging attempt is after
        ///charges have been added/deleted, we don't want to inform the user that the transaction failed so run again since the charges were successfully
        ///inserted/deleted and it was only updating the aged balances that failed.  If isPreCharges, this won't run aging again if the last aging run was
        ///today.  If !isPreCharges, we will run aging even if it was run today to update aged bals to include the charges added/deleted.</summary>
        private bool RunAgingEnterprise(bool isOnLoad = false)
        {
            DateTime dtNow         = MiscData.GetNowDateTime();
            DateTime dtToday       = dtNow.Date;
            DateTime dateLastAging = PrefC.GetDate(PrefName.DateLastAging);

            if (isOnLoad && dateLastAging.Date == dtToday)
            {
                return(true);               //this is prior to inserting/deleting charges and aging has already been run for this date
            }
            Prefs.RefreshCache();
            DateTime dateTAgingBeganPref = PrefC.GetDateT(PrefName.AgingBeginDateTime);

            if (dateTAgingBeganPref > DateTime.MinValue)
            {
                if (isOnLoad)
                {
                    MessageBox.Show(this, Lan.g(this, "In order to add finance charges, aging must be calculated, but you cannot run aging until it has finished "
                                                + "the current calculations which began on") + " " + dateTAgingBeganPref.ToString() + ".\r\n" + Lans.g(this, "If you believe the current aging "
                                                                                                                                                       + "process has finished, a user with SecurityAdmin permission can manually clear the date and time by going to Setup | Miscellaneous and "
                                                                                                                                                       + "pressing the 'Clear' button."));
                }
                return(false);
            }
            Prefs.UpdateString(PrefName.AgingBeginDateTime, POut.DateT(dtNow, false)); //get lock on pref to block others
            Signalods.SetInvalid(InvalidType.Prefs);                                   //signal a cache refresh so other computers will have the updated pref as quickly as possible
            Action actionCloseProgress = null;

            try {
                actionCloseProgress = ODProgressOld.ShowProgressStatus("FinanceCharge", this, Lan.g(this, "Calculating enterprise aging for all patients as of") + " "
                                                                       + dtToday.ToShortDateString() + "...");
                Cursor = Cursors.WaitCursor;
                Ledgers.ComputeAging(0, dtToday);
                Prefs.UpdateString(PrefName.DateLastAging, POut.Date(dtToday, false));
            }
            catch (MySqlException ex) {
                actionCloseProgress?.Invoke();                //effectively terminates progress bar
                Cursor = Cursors.Default;
                if (ex == null || ex.Number != 1213)          //not a deadlock error, just throw
                {
                    throw;
                }
                if (isOnLoad)
                {
                    MsgBox.Show(this, "Deadlock error detected in enterprise aging transaction and rolled back. Try again later.");
                }
                return(false);
            }
            finally {
                actionCloseProgress?.Invoke();                       //effectively terminates progress bar
                Cursor = Cursors.Default;
                Prefs.UpdateString(PrefName.AgingBeginDateTime, ""); //clear lock on pref whether aging was successful or not
                Signalods.SetInvalid(InvalidType.Prefs);
            }
            return(true);
        }
示例#6
0
        private bool RunAgingEnterprise(DateTime dateCalc)
        {
            DateTime dateLastAging = PrefC.GetDate(PrefName.DateLastAging);

            if (dateLastAging.Date == dateCalc.Date)
            {
                if (MessageBox.Show(this, Lan.g(this, "Aging has already been calculated for") + " " + dateCalc.ToShortDateString() + " "
                                    + Lan.g(this, "and does not normally need to run more than once per day.\r\n\r\nRun anway?"), "", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    return(false);
                }
            }
            //Refresh prefs because AgingBeginDateTime is very time sensitive
            Prefs.RefreshCache();
            DateTime dateTAgingBeganPref = PrefC.GetDateT(PrefName.AgingBeginDateTime);

            if (dateTAgingBeganPref > DateTime.MinValue)
            {
                MessageBox.Show(this, Lan.g(this, "You cannot run aging until it has finished the current calculations which began on") + " "
                                + dateTAgingBeganPref.ToString() + ".\r\n" + Lans.g(this, "If you believe the current aging process has finished, a user with SecurityAdmin permission "
                                                                                    + "can manually clear the date and time by going to Setup | Miscellaneous and pressing the 'Clear' button."));
                return(false);
            }
            Prefs.UpdateString(PrefName.AgingBeginDateTime, POut.DateT(MiscData.GetNowDateTime(), false)); //get lock on pref to block others
            Signalods.SetInvalid(InvalidType.Prefs);                                                       //signal a cache refresh so other computers will have the updated pref as quickly as possible
            Action actionCloseAgingProgress = null;

            try {
                actionCloseAgingProgress = ODProgressOld.ShowProgressStatus("ComputeAging", this,
                                                                            Lan.g(this, "Calculating enterprise aging for all patients as of") + " " + dateCalc.ToShortDateString() + "...");
                Cursor = Cursors.WaitCursor;
                Ledgers.ComputeAging(0, dateCalc);
                Prefs.UpdateString(PrefName.DateLastAging, POut.Date(dateCalc, false));
            }
            catch (MySqlException ex) {
                actionCloseAgingProgress?.Invoke();
                Cursor = Cursors.Default;
                if (ex == null || ex.Number != 1213)             //not a deadlock error, just throw
                {
                    throw;
                }
                MsgBox.Show(this, "Deadlock error detected in aging transaction and rolled back. Try again later.");
                return(false);
            }
            finally {
                actionCloseAgingProgress?.Invoke();
                Cursor = Cursors.Default;
                Prefs.UpdateString(PrefName.AgingBeginDateTime, "");               //clear lock on pref whether aging was successful or not
                Signalods.SetInvalid(InvalidType.Prefs);
            }
            return(true);
        }
示例#7
0
        private void butOK_Click(object sender, System.EventArgs e)
        {
            if (textDateCalc.errorProvider1.GetError(textDateCalc) != "")
            {
                MsgBox.Show(this, "Please fix data entry errors first.");
                return;
            }
            DateTime dateCalc = PIn.Date(textDateCalc.Text);
            Action   actionCloseAgingProgress = null;

            if (PrefC.GetBool(PrefName.AgingIsEnterprise))
            {
                //if this is true, dateCalc has to be DateTime.Today and aging calculated daily not monthly.
                if (!RunAgingEnterprise(dateCalc))
                {
                    //Errors displayed from RunAgingEnterprise
                    return;
                }
            }
            else
            {
                try {
                    actionCloseAgingProgress = ODProgressOld.ShowProgressStatus("ComputeAging", this, Lan.g(this, "Calculating aging for all patients as of") + " "
                                                                                + dateCalc.ToShortDateString() + "...");
                    Cursor = Cursors.WaitCursor;
                    Ledgers.ComputeAging(0, dateCalc);
                }
                catch (MySqlException ex) {
                    actionCloseAgingProgress?.Invoke();
                    Cursor = Cursors.Default;
                    if (ex == null || ex.Number != 1213)                 //not a deadlock error, just throw
                    {
                        throw;
                    }
                    MsgBox.Show(this, "Deadlock error detected in aging transaction and rolled back. Try again later.");
                    DialogResult = DialogResult.Cancel;
                    return;
                }
                finally {
                    actionCloseAgingProgress?.Invoke();
                    Cursor = Cursors.Default;
                }
                if (Prefs.UpdateString(PrefName.DateLastAging, POut.Date(dateCalc, false)))
                {
                    DataValid.SetInvalid(InvalidType.Prefs);
                }
            }
            MsgBox.Show(this, "Aging Complete");
            DialogResult = DialogResult.OK;
        }
示例#8
0
        ///<summary>This is a wrapper method for MiscData.MakeABackup() that will show a progress window so that the user can see progress.
        ///Set isSilent to true to suppress the failure message boxes.  However, the progress window will always be shown.
        ///Returns false if making a backup failed.</summary>
        public static bool MakeABackup(bool isSilent, BackupLocation backupLocation, bool isSecurityLogged = true)
        {
            if (DataConnection.DBtype == DatabaseType.Oracle)
            {
                return(false);               //Because MiscData.MakeABackup() is not yet Oracle compatible.
            }
#if DEBUG
            switch (MessageBox.Show("Would you like to make a backup of the DB?", "DEBUG ONLY", MessageBoxButtons.YesNoCancel))
            {
            case DialogResult.Cancel:
                return(false);

            case DialogResult.No:
                return(true);

            case DialogResult.Yes:
            default:
                //do nothing, make backup like usual.
                break;
            }
#endif
            //Create a thread that will show a window and then stay open until the closing action is called.
            Action actionCloseBackupProgress = ODProgressOld.ShowProgressStatus("BackupProgress", null);
            try {
                MiscData.MakeABackup();
                actionCloseBackupProgress(); //Close the progress window.
            }
            catch (Exception ex) {           //MiscData.MakeABackup() could have thrown an exception.
                actionCloseBackupProgress(); //Close the progress window.
                //Show the user that something what went wrong when not in silent mode.
                if (!isSilent)
                {
                    if (ex.Message != "")
                    {
                        MessageBox.Show(ex.Message);
                    }
                    //Reusing translation in ClassConvertDatabase, since it is most likely the only place a translation would have been performed previously.
                    MsgBox.Show("ClassConvertDatabase", "Backup failed. Your database has not been altered.");
                }
                return(false);
            }
            if (isSecurityLogged && PrefC.GetStringNoCache(PrefName.UpdateStreamLinePassword) != "abracadabra")
            {
                SecurityLogs.MakeLogEntryNoCache(Permissions.Backup, 0, Lan.g("Backups", "A backup was created when running the") + " " + backupLocation.ToString());
            }
            return(true);
        }
示例#9
0
        ///<summary>Imports fees into the database from the provided file.</summary>
        ///<param name="fileName">Must be a tab-delimited .xls or .csv file. Each row must have two columns. The first column must be the proc code
        ///and the second column must be the fee amount.</param>
        public static void ImportFees(long feeSchedNum, long clinicNum, long provNum, string fileName, Form currentForm)
        {
            FeeCache   _feeCache   = new FeeCache();
            List <Fee> listNewFees = _feeCache.GetListFees(feeSchedNum, clinicNum, provNum);
            Action     actionCloseFeeSchedImportProgress = ODProgressOld.ShowProgressStatus("FeeSchedImport", currentForm,
                                                                                            Lan.g(currentForm, "Importing fees, please wait") + "...");

            string[] fields;
            double   feeAmt;

            using (StreamReader sr = new StreamReader(fileName)){
                string line = sr.ReadLine();
                while (line != null)
                {
                    currentForm.Cursor = Cursors.WaitCursor;
                    fields             = line.Split(new string[1] {
                        "\t"
                    }, StringSplitOptions.None);
                    if (fields.Length > 1)                  // && fields[1]!=""){//we no longer skip blank fees
                    {
                        if (fields[1] == "")
                        {
                            feeAmt = -1;                          //triggers deletion of existing fee, but no insert.
                        }
                        else
                        {
                            feeAmt = PIn.Double(fields[1]);
                        }
                        listNewFees = Fees.Import(fields[0], feeAmt, feeSchedNum, clinicNum, provNum, listNewFees);
                    }
                    line = sr.ReadLine();
                }
            }
            currentForm.Cursor = Cursors.Default;
            actionCloseFeeSchedImportProgress();
            MsgBox.Show(currentForm, "Fee schedule imported.");
            _feeCache.BeginTransaction();
            _feeCache.RemoveFees(feeSchedNum, clinicNum, provNum);
            foreach (Fee fee in listNewFees)
            {
                _feeCache.Add(fee);
            }
            currentForm.Cursor = Cursors.WaitCursor;
            _feeCache.SaveToDb();
            currentForm.Cursor = Cursors.Default;
        }
        private void Run(DbmMode modeCur)
        {
            if (_patNum < 1)
            {
                MsgBox.Show(this, "Select a patient first.");
                return;
            }
            Cursor = Cursors.WaitCursor;
            //Clear out the result column for all rows before every "run"
            for (int i = 0; i < gridMain.Rows.Count; i++)
            {
                gridMain.Rows[i].Cells[2].Text = "";              //Don't use UpdateResultTextForRow here because users will see the rows clearing out one by one.
            }
            bool          verbose = checkShow.Checked;
            StringBuilder logText = new StringBuilder();
            //Create a thread that will show a window and then stay open until the closing phrase is thrown from this form.
            Action actionCloseCheckTableProgress    = ODProgressOld.ShowProgressStatus("CheckTableProgress", this);
            ODTuple <string, bool> tableCheckResult = DatabaseMaintenances.MySQLTables(verbose, modeCur);

            actionCloseCheckTableProgress();
            logText.Append(tableCheckResult.Item1);
            //No database maintenance methods should be run unless this passes.
            if (!tableCheckResult.Item2)
            {
                Cursor = Cursors.Default;
                MsgBoxCopyPaste msgBoxCP = new MsgBoxCopyPaste(tableCheckResult.Item1); //the Tuples result is already translated.
                msgBoxCP.Show();                                                        //Let this window be non-modal so that they can keep it open while they fix their problems.
                return;
            }
            if (gridMain.SelectedIndices.Length < 1)
            {
                //No rows are selected so the user wants to run all checks.
                gridMain.SetSelected(true);
            }
            string result;

            int[] selectedIndices = gridMain.SelectedIndices;
            for (int i = 0; i < selectedIndices.Length; i++)
            {
                long          userNum          = 0;
                DbmMethodAttr methodAttributes = (DbmMethodAttr)Attribute.GetCustomAttribute(_listDbmMethodsGrid[selectedIndices[i]], typeof(DbmMethodAttr));
                //We always send verbose and modeCur into all DBM methods.
                List <object> parameters = new List <object>()
                {
                    verbose, modeCur
                };
                //There are optional paramaters available to some methods and adding them in the following order is very important.
                if (methodAttributes.HasUserNum)
                {
                    parameters.Add(userNum);
                }
                if (methodAttributes.HasPatNum)
                {
                    parameters.Add(_patNum);
                }
                gridMain.ScrollToIndexBottom(selectedIndices[i]);
                UpdateResultTextForRow(selectedIndices[i], Lan.g("FormDatabaseMaintenance", "Running") + "...");
                try {
                    result = (string)_listDbmMethodsGrid[selectedIndices[i]].Invoke(null, parameters.ToArray());
                    if (modeCur == DbmMode.Fix)
                    {
                        DatabaseMaintenances.UpdateDateLastRun(_listDbmMethodsGrid[selectedIndices[i]].Name);
                    }
                }
                catch (Exception ex) {
                    if (ex.InnerException != null)
                    {
                        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();                        //This preserves the stack trace of the InnerException.
                    }
                    throw;
                }
                string status = "";
                if (result == "")               //Only possible if running a check / fix in non-verbose mode and nothing happened or needs to happen.
                {
                    status = Lan.g("FormDatabaseMaintenance", "Done.  No maintenance needed.");
                }
                UpdateResultTextForRow(selectedIndices[i], result + status);
                logText.Append(result);
            }
            gridMain.SetSelected(selectedIndices, true);           //Reselect all rows that were originally selected.
            try {
                DatabaseMaintenances.SaveLogToFile(logText.ToString());
            }
            catch (Exception ex) {
                Cursor = Cursors.Default;
                MessageBox.Show(ex.Message);
            }
            Cursor = Cursors.Default;
            if (modeCur == DbmMode.Fix)
            {
                //_isCacheInvalid=true;//Flag cache to be invalidated on closing.  Some DBM fixes alter cached tables.
            }
        }
示例#11
0
        ///<summary>Return false to indicate exit app.  Only called when program first starts up at the beginning of FormOpenDental.PrefsStartup.</summary>
        public bool Convert(string fromVersion, string toVersion, bool isSilent, Form currentForm = null)
        {
            FromVersion = new Version(fromVersion);
            ToVersion   = new Version(toVersion);        //Application.ProductVersion);
            if (FromVersion >= new Version("3.4.0") && PrefC.GetBool(PrefName.CorruptedDatabase))
            {
                FormOpenDental.ExitCode = 201;              //Database was corrupted due to an update failure
                if (!isSilent)
                {
                    MsgBox.Show(this, "Your database is corrupted because an update failed.  Please contact us.  This database is unusable and you will need to restore from a backup.");
                }
                return(false);               //shuts program down.
            }
            if (FromVersion == ToVersion)
            {
                return(true);                         //no conversion necessary
            }
            if (FromVersion.CompareTo(ToVersion) > 0) //"Cannot convert database to an older version."
            //no longer necessary to catch it here.  It will be handled soon enough in CheckProgramVersion
            {
                return(true);
            }
            if (FromVersion < new Version("2.8.0"))
            {
                FormOpenDental.ExitCode = 130;              //Database must be upgraded to 2.8 to continue
                if (!isSilent)
                {
                    MsgBox.Show(this, "This database is too old to easily convert in one step. Please upgrade to 2.1 if necessary, then to 2.8.  Then you will be able to upgrade to this version. We apologize for the inconvenience.");
                }
                return(false);
            }
            if (FromVersion < new Version("6.6.2"))
            {
                FormOpenDental.ExitCode = 131;              //Database must be upgraded to 11.1 to continue
                if (!isSilent)
                {
                    MsgBox.Show(this, "This database is too old to easily convert in one step. Please upgrade to 11.1 first.  Then you will be able to upgrade to this version. We apologize for the inconvenience.");
                }
                return(false);
            }
            if (FromVersion < new Version("3.0.1"))
            {
                if (!isSilent)
                {
                    MsgBox.Show(this, "This is an old database.  The conversion must be done using MySQL 4.1 (not MySQL 5.0) or it will fail.");
                }
            }
            if (FromVersion.ToString() == "2.9.0.0" || FromVersion.ToString() == "3.0.0.0" || FromVersion.ToString() == "4.7.0.0")
            {
                FormOpenDental.ExitCode = 190;              //Cannot convert this database version which was only for development purposes
                if (!isSilent)
                {
                    MsgBox.Show(this, "Cannot convert this database version which was only for development purposes.");
                }
                return(false);
            }
            if (FromVersion > new Version("4.7.0") && FromVersion.Build == 0)
            {
                FormOpenDental.ExitCode = 190;              //Cannot convert this database version which was only for development purposes
                if (!isSilent)
                {
                    MsgBox.Show(this, "Cannot convert this database version which was only for development purposes.");
                }
                return(false);
            }
            if (FromVersion >= LatestVersion)
            {
                return(true);               //no conversion necessary
            }
            if (RemotingClient.RemotingRole == RemotingRole.ClientWeb)
            {
                FormOpenDental.ExitCode = 140;              //Web client cannot convert database
                if (!isSilent)
                {
                    MsgBox.Show(this, "Web client cannot convert database.  Must be using a direct connection.");
                }
                return(false);
            }
            if (ReplicationServers.ServerIsBlocked())
            {
                FormOpenDental.ExitCode = 150;              //Replication server is blocked from performing updates
                if (!isSilent)
                {
                    MsgBox.Show(this, "This replication server is blocked from performing updates.");
                }
                return(false);
            }
#if TRIALONLY
            //Trial users should never be able to update a database.
            if (PrefC.GetString(PrefName.RegistrationKey) != "") //Allow databases with no reg key to update.  Needed by our conversion department.
            {
                FormOpenDental.ExitCode = 191;                   //Trial versions cannot connect to live databases
                if (!isSilent)
                {
                    MsgBox.Show(this, "Trial versions cannot connect to live databases.  Please run the Setup.exe in the AtoZ folder to reinstall your original version.");
                }
                return(false);
            }
#endif
            if (PrefC.GetString(PrefName.WebServiceServerName) != "" &&       //using web service
                !ODEnvironment.IdIsThisComputer(PrefC.GetString(PrefName.WebServiceServerName).ToLower()))                   //and not on web server
            {
                if (isSilent)
                {
                    FormOpenDental.ExitCode = 141;   //Updates are only allowed from a designated web server
                    return(false);                   //if you are in debug mode and you really need to update the DB, you can manually clear the WebServiceServerName preference.
                }
                //This will be handled in CheckProgramVersion, giving the user option to downgrade or exit program.
                return(true);
            }
            //If MyISAM and InnoDb mix, then try to fix
            if (DataConnection.DBtype == DatabaseType.MySql)           //not for Oracle
            {
                string namesInnodb = InnoDb.GetInnodbTableNames();     //Or possibly some other format.
                int    numMyisam   = DatabaseMaintenances.GetMyisamTableCount();
                if (namesInnodb != "" && numMyisam > 0)
                {
                    if (!isSilent)
                    {
                        MessageBox.Show(Lan.g(this, "A mixture of database tables in InnoDB and MyISAM format were found.  A database backup will now be made, and then the following InnoDB tables will be converted to MyISAM format: ") + namesInnodb);
                    }
                    if (!Shared.MakeABackup(isSilent, BackupLocation.ConvertScript, false))
                    {
                        Cursor.Current          = Cursors.Default;
                        FormOpenDental.ExitCode = 101;                      //Database Backup failed
                        return(false);
                    }
                    if (!DatabaseMaintenances.ConvertTablesToMyisam())
                    {
                        FormOpenDental.ExitCode = 102;                      //Failed to convert InnoDB tables to MyISAM format
                        if (!isSilent)
                        {
                            MessageBox.Show(Lan.g(this, "Failed to convert InnoDB tables to MyISAM format. Please contact support."));
                        }
                        return(false);
                    }
                    if (!isSilent)
                    {
                        MessageBox.Show(Lan.g(this, "All tables converted to MyISAM format successfully."));
                    }
                    namesInnodb = "";
                }
                if (namesInnodb == "" && numMyisam > 0)             //if all tables are myisam
                //but default storage engine is innodb, then kick them out.
                {
                    if (DatabaseMaintenances.GetStorageEngineDefaultName().ToUpper() != "MYISAM") //Probably InnoDB but could be another format.
                    {
                        FormOpenDental.ExitCode = 103;                                            //Default database .ini setting is innoDB
                        if (!isSilent)
                        {
                            MessageBox.Show(Lan.g(this, "The database tables are in MyISAM format, but the default database engine format is InnoDB. You must change the default storage engine within the my.ini (or my.cnf) file on the database server and restart MySQL in order to fix this problem. Exiting."));
                        }
                        return(false);
                    }
                }
            }
#if DEBUG
            if (!isSilent && MessageBox.Show("You are in Debug mode.  Your database can now be converted" + "\r"
                                             + "from version" + " " + FromVersion.ToString() + "\r"
                                             + "to version" + " " + ToVersion.ToString() + "\r"
                                             + "You can click Cancel to skip conversion and attempt to run the newer code against the older database."
                                             , "", MessageBoxButtons.OKCancel) != DialogResult.OK)
            {
                return(true);               //If user clicks cancel, then do nothing
            }
#else
            if (!isSilent && MessageBox.Show(Lan.g(this, "Your database will now be converted") + "\r"
                                             + Lan.g(this, "from version") + " " + FromVersion.ToString() + "\r"
                                             + Lan.g(this, "to version") + " " + ToVersion.ToString() + "\r"
                                             + Lan.g(this, "The conversion works best if you are on the server.  Depending on the speed of your computer, it can be as fast as a few seconds, or it can take as long as 10 minutes.")
                                             , "", MessageBoxButtons.OKCancel) != DialogResult.OK)
            {
                return(false);               //If user clicks cancel, then close the program
            }
#endif
            Cursor.Current = Cursors.WaitCursor;
            Action actionCloseConvertProgress = null;
#if !DEBUG
            if (!isSilent)
            {
                if (DataConnection.DBtype != DatabaseType.MySql &&
                    !MsgBox.Show(this, true, "If you have not made a backup, please Cancel and backup before continuing.  Continue?"))
                {
                    return(false);
                }
            }
            if (DataConnection.DBtype == DatabaseType.MySql)
            {
                if (!Shared.MakeABackup(isSilent, BackupLocation.ConvertScript, false))
                {
                    Cursor.Current          = Cursors.Default;
                    FormOpenDental.ExitCode = 101;                  //Database Backup failed
                    return(false);
                }
            }
            //We've been getting an increasing number of phone calls with databases that have duplicate preferences which is impossible
            //unless a user has gotten this far and another computer in the office is in the middle of an update as well.
            //The issue is most likely due to the blocking messageboxes above which wait indefinitely for user input right before upgrading the database.
            //This means that the cache for this computer could be stale and we need to manually refresh our cache to double check
            //that the database isn't flagged as corrupt, an update isn't in progress, or that the database version hasn't changed (someone successfully updated already).
            Prefs.RefreshCache();
            //Now check the preferences that should stop this computer from executing an update.
            if (PrefC.GetBool(PrefName.CorruptedDatabase) ||
                (PrefC.GetString(PrefName.UpdateInProgressOnComputerName) != "" && PrefC.GetString(PrefName.UpdateInProgressOnComputerName) != Environment.MachineName))
            {
                //At this point, the pref "corrupted database" being true means that a computer is in the middle of running the upgrade script.
                //There will be another corrupted database check on start up which will take care of the scenario where this is truly a corrupted database.
                //Also, we need to make sure that the update in progress preference is set to this computer because we JUST set it to that value before entering this method.
                //If it has changed, we absolutely know without a doubt that another computer is trying to update at the same time.
                FormOpenDental.ExitCode = 142;              //Update is already in progress from another computer
                if (!isSilent)
                {
                    MsgBox.Show(this, "An update is already in progress from another computer.");
                }
                return(false);
            }
            //Double check that the database version has not changed.  This check is here just in case another computer has successfully updated the database already.
            Version versionDatabase = new Version(PrefC.GetString(PrefName.DataBaseVersion));
            if (FromVersion != versionDatabase)
            {
                FormOpenDental.ExitCode = 143;              //Database has already been updated from another computer
                if (!isSilent)
                {
                    MsgBox.Show(this, "The database has already been updated from another computer.");
                }
                return(false);
            }
            try {
#endif
            if (FromVersion < new Version("7.5.17"))                     //Insurance Plan schema conversion
            {
                if (isSilent)
                {
                    FormOpenDental.ExitCode = 139;                          //Update must be done manually to fix Insurance Plan Schema
                    Application.Exit();
                    return(false);
                }
                Cursor.Current = Cursors.Default;
                YN InsPlanConverstion_7_5_17_AutoMergeYN = YN.Unknown;
                if (FromVersion < new Version("7.5.1"))
                {
                    FormInsPlanConvert_7_5_17 form = new FormInsPlanConvert_7_5_17();
                    if (PrefC.GetBoolSilent(PrefName.InsurancePlansShared, true))
                    {
                        form.InsPlanConverstion_7_5_17_AutoMergeYN = YN.Yes;
                    }
                    else
                    {
                        form.InsPlanConverstion_7_5_17_AutoMergeYN = YN.No;
                    }
                    form.ShowDialog();
                    if (form.DialogResult == DialogResult.Cancel)
                    {
                        MessageBox.Show("Your database has not been altered.");
                        return(false);
                    }
                    InsPlanConverstion_7_5_17_AutoMergeYN = form.InsPlanConverstion_7_5_17_AutoMergeYN;
                }
                ConvertDatabases.Set_7_5_17_AutoMerge(InsPlanConverstion_7_5_17_AutoMergeYN);                        //does nothing if this pref is already present for some reason.
                Cursor.Current = Cursors.WaitCursor;
            }
            if (!isSilent && FromVersion > new Version("16.3.0") && FromVersion < new Version("16.3.29") && ApptReminderRules.IsReminders)
            {
                //16.3.29 is more strict about reminder rule setup. Prompt the user and allow them to exit the update if desired.
                //Get all currently enabled reminder rules.
                List <bool> listReminderFlags = ApptReminderRules.Get_16_3_29_ConversionFlags();
                if (listReminderFlags?[0] ?? false)                        //2 reminders scheduled for same day of appointment. 1 will be converted to future day reminder.
                {
                    MsgBox.Show(this, "You have multiple appointment reminders set to send on the same day of the appointment. One of these will be converted to send 1 day prior to the appointment.  Please review automated reminder rule setup after update has finished.");
                }
                if (listReminderFlags?[1] ?? false)                        //2 reminders scheduled for future day of appointment. 1 will be converted to same day reminder.
                {
                    MsgBox.Show(this, "You have multiple appointment reminders set to send 1 or more days prior to the day of the appointment. One of these will be converted to send 1 hour prior to the appointment.  Please review automated reminder rule setup after update has finished.");
                }
            }
            if (FromVersion >= new Version("17.3.1") && FromVersion < new Version("17.3.23") && DataConnection.DBtype == DatabaseType.MySql &&
                (Tasks.HasAnyLongDescripts() || TaskNotes.HasAnyLongNotes() || Commlogs.HasAnyLongNotes()))
            {
                if (isSilent)
                {
                    FormOpenDental.ExitCode = 138;                          //Update must be done manually in order to get data loss notification(s).
                    Application.Exit();
                    return(false);
                }
                if (!MsgBox.Show(this, true, "Data will be lost during this update."
                                 + "\r\nContact support in order to retrieve the data from a backup after the update."
                                 + "\r\n\r\nContinue?"))
                {
                    MessageBox.Show("Your database has not been altered.");
                    return(false);
                }
            }
            if (FromVersion >= new Version("3.4.0"))
            {
                Prefs.UpdateBool(PrefName.CorruptedDatabase, true);
            }
            ConvertDatabases.FromVersion = FromVersion;
#if !DEBUG
            //Typically the UpdateInProgressOnComputerName preference will have already been set within FormUpdate.
            //However, the user could have cancelled out of FormUpdate after successfully downloading the Setup.exe
            //OR the Setup.exe could have been manually sent to our customer (during troubleshooting with HQ).
            //For those scenarios, the preference will be empty at this point and we need to let other computers know that an update going to start.
            //Updating the string (again) here will guarantee that all computers know an update is in fact in progress from this machine.
            Prefs.UpdateString(PrefName.UpdateInProgressOnComputerName, Environment.MachineName);
#endif
            //Show a progress window that will indecate to the user that there is an active update in progress.  Currently okay to show during isSilent.
            actionCloseConvertProgress = ODProgressOld.ShowProgressStatus("ConvertDatabases", hasMinimize: false, currentForm: currentForm);
            ConvertDatabases.To2_8_2();                //begins going through the chain of conversion steps
            InvokeConvertMethods();                    //continues going through the chain of conversion steps starting at v17.1.1 via reflection.
            actionCloseConvertProgress();
            Cursor.Current = Cursors.Default;
            if (FromVersion >= new Version("3.4.0"))
            {
                //CacheL.Refresh(InvalidType.Prefs);//or it won't know it has to update in the next line.
                Prefs.UpdateBool(PrefName.CorruptedDatabase, false, true);                      //more forceful refresh in order to properly change flag
            }
            Cache.Refresh(InvalidType.Prefs);
            if (!isSilent)
            {
                MsgBox.Show(this, "Database update successful");
            }
            return(true);

#if !DEBUG
        }

        catch (System.IO.FileNotFoundException e) {
            actionCloseConvertProgress?.Invoke();
            FormOpenDental.ExitCode = 160;                  //File not found exception
            if (!isSilent)
            {
                MessageBox.Show(e.FileName + " " + Lan.g(this, "could not be found. Your database has not been altered and is still usable if you uninstall this version, then reinstall the previous version."));
            }
            if (FromVersion >= new Version("3.4.0"))
            {
                Prefs.UpdateBool(PrefName.CorruptedDatabase, false);
            }
            return(false);
        }
        catch (System.IO.DirectoryNotFoundException) {
            actionCloseConvertProgress?.Invoke();
            FormOpenDental.ExitCode = 160;                  //ConversionFiles folder could not be found
            if (!isSilent)
            {
                MessageBox.Show(Lan.g(this, "ConversionFiles folder could not be found. Your database has not been altered and is still usable if you uninstall this version, then reinstall the previous version."));
            }
            if (FromVersion >= new Version("3.4.0"))
            {
                Prefs.UpdateBool(PrefName.CorruptedDatabase, false);
            }
            return(false);
        }
        catch (Exception ex) {
            actionCloseConvertProgress?.Invoke();
            FormOpenDental.ExitCode = 201;                  //Database was corrupted due to an update failure
            if (!isSilent)
            {
                MessageBox.Show(ex.Message + "\r\n\r\n"
                                + Lan.g(this, "Conversion unsuccessful. Your database is now corrupted and you cannot use it.  Please contact us."));
            }
            //Then, application will exit, and database will remain tagged as corrupted.
            return(false);
        }
#endif
        }
示例#12
0
        ///<summary>Makes a web call to WebServiceMainHQ to get the corresponding EServiceSetupFull information and then attempts to fill each tab.
        ///If anything goes wrong within this method a message box will show to the user and then the window will auto close via Abort.</summary>
        private void FillForm()
        {
            Action actionCloseProgress = ODProgressOld.ShowProgressStatus("EServicesSetupProgress", this, "Validating eServices...");

            try {
                if (MiscUtils.TryUpdateIeEmulation())
                {
                    throw new Exception("Browser emulation version updated.\r\nYou must restart this application before accessing the Signup Portal.");
                }
                //Send light version of clinics to HQ to be used by signup portal below. Get back all args needed from HQ in order to perform the operations of this window.
                SignupPortalPermission perm = SignupPortalPermission.ReadOnly;
                if (Security.IsAuthorized(Permissions.SecurityAdmin, true))
                {
                    perm = SignupPortalPermission.FullPermission;
                }
                else if (Security.IsAuthorized(Permissions.EServicesSetup, true))
                {
                    perm = SignupPortalPermission.ReadOnly;
                }
                else
                {
                    perm = SignupPortalPermission.Denied;
                }
                _signupOut = WebServiceMainHQProxy.GetEServiceSetupFull(perm);
                //Show user any prompts that were generated by GetEServiceSetupFull().
                if (_signupOut.Prompts.Count > 0)
                {
                    MessageBox.Show(string.Join("\r\n", _signupOut.Prompts.Select(x => Lans.g(this, x))));
                }
                #region Fill
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Signup")));
                FillTabSignup();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - eConnector Service")));
                FillTabEConnector();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Mobile Synch (old-style)")));
                FillTabMobileSynch();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Mobile Web")));
                FillTabMobileWeb();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Patient Portal")));
                FillTabPatientPortal();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Web Sched Recall")));
                FillTabWebSchedRecall();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Web Sched New Pat Appt")));
                FillTabWebSchedNewPat();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Web Sched Verify")));
                FillTabWebSchedVerify();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Texting Services")));
                FillTabTexting();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - eReminders & eConfirmations")));
                FillTabECR();
                EServicesEvent.Fire(new ODEventArgs("EServicesSetupProgress", Lan.g(this, "Loading tab - Miscellaneous")));
                FillTabMisc();
                #endregion
                #region Authorize editing
                //Disable certain buttons but let them continue to view.
                bool allowEdit = Security.IsAuthorized(Permissions.EServicesSetup, true);
                AuthorizeTabSignup(allowEdit);
                AuthorizeTabEConnector(allowEdit);
                AuthorizeTabMobileSynch(allowEdit);
                AuthorizeTabPatientPortal(allowEdit);
                AuthorizeTabWebSchedRecall(allowEdit);
                AuthorizeTabWebSchedNewPat(allowEdit);
                AuthorizeTabTexting(allowEdit);
                AuthorizeTabECR(allowEdit);
                AuthorizeTabMisc(allowEdit);
                ((Control)tabMobileSynch).Enabled = allowEdit;
                #endregion
            }
            catch (WebException we) {
                actionCloseProgress?.Invoke();
                FriendlyException.Show(Lan.g(this, "Could not reach HQ.  Please make sure you have an internet connection and try again or call support."), we);
                //Set the dialog result to Abort so that FormClosing knows to not try and save any changes.
                DialogResult = DialogResult.Abort;
                Close();
            }
            catch (Exception e) {
                actionCloseProgress?.Invoke();
                FriendlyException.Show(Lan.g(this, "There was a problem loading the eServices Setup window.  Please try again or call support."), e);
                //Set the dialog result to Abort so that FormClosing knows to not try and save any changes.
                DialogResult = DialogResult.Abort;
                Close();
            }
            actionCloseProgress?.Invoke();
        }
示例#13
0
        private void butOK_Click(object sender, System.EventArgs e)
        {
            if (textDate.errorProvider1.GetError(textDate) != "" ||
                textAPR.errorProvider1.GetError(textAPR) != "" ||
                textAtLeast.errorProvider1.GetError(textAtLeast) != "" ||
                textOver.errorProvider1.GetError(textOver) != "")
            {
                MsgBox.Show(this, "Please fix data entry errors first.");
                return;
            }
            DateTime date = PIn.Date(textDate.Text);

            if (PrefC.GetDate(PrefName.FinanceChargeLastRun).AddDays(25) > date)
            {
                if (!MsgBox.Show(this, true, "Warning.  Finance charges should not be run more than once per month.  Continue?"))
                {
                    return;
                }
            }
            else if (PrefC.GetDate(PrefName.BillingChargeLastRun).AddDays(25) > date)
            {
                if (!MsgBox.Show(this, true, "Warning.  Billing charges should not be run more than once per month.  Continue?"))
                {
                    return;
                }
            }
            if (listBillType.SelectedIndices.Count == 0)
            {
                MsgBox.Show(this, "Please select at least one billing type first.");
                return;
            }
            if (PIn.Long(textAPR.Text) < 2)
            {
                if (!MsgBox.Show(this, true, "The APR is much lower than normal. Do you wish to proceed?"))
                {
                    return;
                }
            }
            if (PrefC.GetBool(PrefName.AgingCalculatedMonthlyInsteadOfDaily) && PrefC.GetDate(PrefName.DateLastAging).AddMonths(1) <= DateTime.Today)
            {
                if (!MsgBox.Show(this, MsgBoxButtons.OKCancel, "It has been more than a month since aging has been run.  It is recommended that you update the "
                                 + "aging date and run aging before continuing."))
                {
                    return;
                }
                //we might also consider a warning if textDate.Text does not match DateLastAging.  Probably not needed for daily aging, though.
            }
            string      chargeType            = (radioFinanceCharge.Checked?"Finance":"Billing");//For display only
            List <long> listSelectedBillTypes = listBillType.SelectedIndices.OfType <int>().Select(x => _listBillingTypeDefs[x].DefNum).ToList();
            Action      actionCloseProgress   = null;
            int         chargesAdded          = 0;

            try {
                actionCloseProgress = ODProgressOld.ShowProgressStatus("FinanceCharge", this, Lan.g(this, "Gathering patients with aged balances") + "...");
                List <PatAging> listPatAgings = Patients.GetAgingListSimple(listSelectedBillTypes, new List <long> {
                });                                                                                                                //Ordered by PatNum, for thread concurrency
                long adjType = PrefC.GetLong(PrefName.FinanceChargeAdjustmentType);
                Dictionary <long, List <Adjustment> > dictPatAdjustments = new Dictionary <long, List <Adjustment> >();
                if (!checkCompound.Checked)
                {
                    int daysOver = (radio30.Checked ? 30
                                                : radio60.Checked ? 60
                                                : 90);
                    DateTime maxAdjDate = MiscData.GetNowDateTime().Date.AddDays(-daysOver);
                    dictPatAdjustments = Adjustments.GetAdjustForPatsByType(listPatAgings.Select(x => x.PatNum).ToList(), adjType, maxAdjDate);
                }
                int           chargesProcessed = 0;
                List <Action> listActions      = new List <Action>();
                foreach (PatAging patAgingCur in listPatAgings)
                {
                    listActions.Add(new Action(() => {
                        if (++chargesProcessed % 5 == 0)
                        {
                            ODEvent.Fire(new ODEventArgs("FinanceCharge", Lan.g(this, "Processing " + chargeType + " charges") + ": " + chargesProcessed + " out of "
                                                         + listPatAgings.Count));
                        }
                        //This WILL NOT be the same as the patient's total balance. Start with BalOver90 since all options include that bucket. Add others if needed.
                        double overallBalance = patAgingCur.BalOver90 + (radio60.Checked?patAgingCur.Bal_61_90:radio30.Checked?(patAgingCur.Bal_31_60 + patAgingCur.Bal_61_90):0);
                        if (overallBalance <= .01d)
                        {
                            return;
                        }
                        if (radioBillingCharge.Checked)
                        {
                            AddBillingCharge(patAgingCur.PatNum, date, textBillingCharge.Text, patAgingCur.PriProv);
                        }
                        else                                                                                 //Finance charge
                        {
                            if (dictPatAdjustments.ContainsKey(patAgingCur.PatNum))                          //Only contains key if checkCompound is not checked.
                            {
                                overallBalance -= dictPatAdjustments[patAgingCur.PatNum].Sum(x => x.AdjAmt); //Dict always contains patNum as key, but list can be empty.
                            }
                            if (!AddFinanceCharge(patAgingCur.PatNum, date, textAPR.Text, textAtLeast.Text, textOver.Text, overallBalance, patAgingCur.PriProv, adjType))
                            {
                                return;
                            }
                        }
                        chargesAdded++;
                    }));
                }
                ODThread.RunParallel(listActions, TimeSpan.FromMinutes(2));               //each group of actions gets X minutes.
                if (radioFinanceCharge.Checked)
                {
                    if (Prefs.UpdateString(PrefName.FinanceChargeAPR, textAPR.Text)
                        | Prefs.UpdateString(PrefName.FinanceChargeLastRun, POut.Date(date, false))
                        | Prefs.UpdateString(PrefName.FinanceChargeAtLeast, textAtLeast.Text)
                        | Prefs.UpdateString(PrefName.FinanceChargeOnlyIfOver, textOver.Text)
                        | Prefs.UpdateString(PrefName.BillingChargeOrFinanceIsDefault, "Finance"))
                    {
                        DataValid.SetInvalid(InvalidType.Prefs);
                    }
                }
                else if (radioBillingCharge.Checked)
                {
                    if (Prefs.UpdateString(PrefName.BillingChargeAmount, textBillingCharge.Text)
                        | Prefs.UpdateString(PrefName.BillingChargeLastRun, POut.Date(date, false))
                        | Prefs.UpdateString(PrefName.BillingChargeOrFinanceIsDefault, "Billing"))
                    {
                        DataValid.SetInvalid(InvalidType.Prefs);
                    }
                }
            }
            finally {
                actionCloseProgress?.Invoke();                //terminates progress bar
            }
            MessageBox.Show(Lan.g(this, chargeType + " charges added") + ": " + chargesAdded);
            if (PrefC.GetBool(PrefName.AgingIsEnterprise))
            {
                if (!RunAgingEnterprise())
                {
                    MsgBox.Show(this, "There was an error calculating aging after the " + chargeType.ToLower() + " charge adjustments were added.\r\n"
                                + "You should run aging later to update affected accounts.");
                }
            }
            else
            {
                DateTime asOfDate = (PrefC.GetBool(PrefName.AgingCalculatedMonthlyInsteadOfDaily)?PrefC.GetDate(PrefName.DateLastAging):DateTime.Today);
                actionCloseProgress = ODProgressOld.ShowProgressStatus("FinanceCharge", this, Lan.g(this, "Calculating aging for all patients as of") + " "
                                                                       + asOfDate.ToShortDateString() + "...");
                Cursor = Cursors.WaitCursor;
                try {
                    Ledgers.RunAging();
                }
                catch (MySqlException ex) {
                    actionCloseProgress?.Invoke();                    //terminates progress bar
                    Cursor = Cursors.Default;
                    if (ex == null || ex.Number != 1213)              //not a deadlock error, just throw
                    {
                        throw;
                    }
                    MsgBox.Show(this, "There was a deadlock error calculating aging after the " + chargeType.ToLower() + " charge adjustments were added.\r\n"
                                + "You should run aging later to update affected accounts.");
                }
                finally {
                    actionCloseProgress?.Invoke();                    //terminates progress bar
                    Cursor = Cursors.Default;
                }
            }
            DialogResult = DialogResult.OK;
        }
示例#14
0
        private void butUndo_Click(object sender, EventArgs e)
        {
            string chargeType = (radioFinanceCharge.Checked?"Finance":"Billing");

            if (MessageBox.Show(Lan.g(this, "Undo all " + chargeType.ToLower() + " charges for") + " " + textDateUndo.Text + "?", "", MessageBoxButtons.OKCancel)
                != DialogResult.OK)
            {
                return;
            }
            Action actionCloseProgress = null;
            int    rowsAffected        = 0;

            try {
                actionCloseProgress = ODProgressOld.ShowProgressStatus(chargeType + "Charge", this,
                                                                       Lan.g(this, "Deleting " + chargeType.ToLower() + " charge adjustments") + "...");
                Cursor       = Cursors.WaitCursor;
                rowsAffected = (int)Adjustments.UndoFinanceOrBillingCharges(PIn.Date(textDateUndo.Text), radioBillingCharge.Checked);
            }
            finally {
                actionCloseProgress?.Invoke();                //effectively terminates progress bar
                Cursor = Cursors.Default;
            }
            MessageBox.Show(Lan.g(this, chargeType + " charge adjustments deleted") + ": " + rowsAffected);
            if (rowsAffected == 0)
            {
                DialogResult = DialogResult.OK;
                return;
            }
            actionCloseProgress = null;
            if (PrefC.GetBool(PrefName.AgingIsEnterprise))
            {
                if (!RunAgingEnterprise())
                {
                    MsgBox.Show(this, "There was an error calculating aging after the " + chargeType.ToLower() + " charge adjustments were deleted.\r\n"
                                + "You should run aging later to update affected accounts.");
                }
            }
            else
            {
                try {
                    DateTime asOfDate = (PrefC.GetBool(PrefName.AgingCalculatedMonthlyInsteadOfDaily)?PrefC.GetDate(PrefName.DateLastAging):DateTime.Today);
                    actionCloseProgress = ODProgressOld.ShowProgressStatus("FinanceCharge", this, Lan.g(this, "Calculating aging for all patients as of") + " "
                                                                           + asOfDate.ToShortDateString() + "...");
                    Cursor = Cursors.WaitCursor;
                    Ledgers.RunAging();
                }
                catch (MySqlException ex) {
                    actionCloseProgress?.Invoke();                    //effectively terminates progress bar
                    Cursor = Cursors.Default;
                    if (ex == null || ex.Number != 1213)              //not a deadlock error, just throw
                    {
                        throw;
                    }
                    MsgBox.Show(this, "There was a deadlock error calculating aging after the " + chargeType.ToLower() + " charge adjustments were deleted.\r\n"
                                + "You should run aging later to update affected accounts.");
                }
                finally {
                    actionCloseProgress?.Invoke();                    //effectively terminates progress bar
                    Cursor = Cursors.Default;
                }
            }
            SecurityLogs.MakeLogEntry(Permissions.Setup, 0, chargeType + " Charges undo. Date " + textDateUndo.Text);
            DialogResult = DialogResult.OK;
        }
示例#15
0
        private void FormFinanceCharges_Load(object sender, System.EventArgs e)
        {
            if (PrefC.GetLong(PrefName.FinanceChargeAdjustmentType) == 0)
            {
                MsgBox.Show(this, "No finance charge adjustment type has been set.  Please go to Setup | Account to fix this.");
                DialogResult = DialogResult.Cancel;
                return;
            }
            if (PrefC.GetLong(PrefName.BillingChargeAdjustmentType) == 0)
            {
                MsgBox.Show(this, "No billing charge adjustment type has been set.  Please go to Setup | Account to fix this.");
                DialogResult = DialogResult.Cancel;
                return;
            }
            _listBillingTypeDefs = Defs.GetDefsForCategory(DefCat.BillingTypes, true);
            if (_listBillingTypeDefs.Count == 0)          //highly unlikely that this would happen
            {
                MsgBox.Show(this, "No billing types have been set up or are visible.");
                DialogResult = DialogResult.Cancel;
                return;
            }
            Action actionCloseAgingProgress = null;

            if (PrefC.GetBool(PrefName.AgingIsEnterprise))
            {
                if (!RunAgingEnterprise(true))
                {
                    DialogResult = DialogResult.Cancel;
                    return;
                }
            }
            else
            {
                try {
                    DateTime asOfDate = (PrefC.GetBool(PrefName.AgingCalculatedMonthlyInsteadOfDaily)?PrefC.GetDate(PrefName.DateLastAging):DateTime.Today);
                    actionCloseAgingProgress = ODProgressOld.ShowProgressStatus("ComputeAging", this, Lan.g(this, "Calculating aging for all patients as of") + " "
                                                                                + asOfDate.ToShortDateString() + "...");
                    Cursor = Cursors.WaitCursor;
                    Ledgers.RunAging();
                }
                catch (MySqlException ex) {
                    actionCloseAgingProgress?.Invoke();                  //effectively terminates progress bar
                    Cursor = Cursors.Default;
                    if (ex == null || ex.Number != 1213)                 //not a deadlock error, just throw
                    {
                        throw;
                    }
                    MsgBox.Show(this, "Deadlock error detected in aging transaction and rolled back. Try again later.");
                    DialogResult = DialogResult.Cancel;
                    return;
                }
                finally {
                    actionCloseAgingProgress?.Invoke();                    //effectively terminates progress bar
                    Cursor = Cursors.Default;
                }
            }
            textDate.Text          = DateTime.Today.ToShortDateString();
            textAPR.MaxVal         = 100;
            textAPR.MinVal         = 0;
            textAPR.Text           = PrefC.GetString(PrefName.FinanceChargeAPR);
            textBillingCharge.Text = PrefC.GetString(PrefName.BillingChargeAmount);
            for (int i = 0; i < _listBillingTypeDefs.Count; i++)
            {
                listBillType.Items.Add(_listBillingTypeDefs[i].ItemName);
                listBillType.SetSelected(i, true);
            }
            string defaultChargeMethod = PrefC.GetString(PrefName.BillingChargeOrFinanceIsDefault);

            if (defaultChargeMethod == "Finance")
            {
                radioFinanceCharge.Checked  = true;
                textDateLastRun.Text        = PrefC.GetDate(PrefName.FinanceChargeLastRun).ToShortDateString();
                textDateUndo.Text           = PrefC.GetDate(PrefName.FinanceChargeLastRun).ToShortDateString();
                textBillingCharge.ReadOnly  = true;
                textBillingCharge.BackColor = System.Drawing.SystemColors.Control;
            }
            else if (defaultChargeMethod == "Billing")
            {
                radioBillingCharge.Checked = true;
                textDateLastRun.Text       = PrefC.GetDate(PrefName.BillingChargeLastRun).ToShortDateString();
                textDateUndo.Text          = PrefC.GetDate(PrefName.BillingChargeLastRun).ToShortDateString();
            }
            textAtLeast.Text = PrefC.GetString(PrefName.FinanceChargeAtLeast);
            textOver.Text    = PrefC.GetString(PrefName.FinanceChargeOnlyIfOver);
        }
示例#16
0
        ///<summary>Processes a PayConnect payment via a credit card terminal.</summary>
        private bool ProcessPaymentTerminal()
        {
            PosRequest posRequest = null;

            try {
                if (radioSale.Checked)
                {
                    posRequest = PosRequest.CreateSale(PIn.Decimal(textAmount.Text));
                }
                else if (radioAuthorization.Checked)
                {
                    posRequest = PosRequest.CreateAuth(PIn.Decimal(textAmount.Text));
                }
                else if (radioVoid.Checked)
                {
                    posRequest = PosRequest.CreateVoidByReference(textRefNumber.Text);
                }
                else if (radioReturn.Checked)
                {
                    if (textRefNumber.Text == "")
                    {
                        posRequest = PosRequest.CreateRefund(PIn.Decimal(textAmount.Text));
                    }
                    else
                    {
                        posRequest = PosRequest.CreateRefund(PIn.Decimal(textAmount.Text), textRefNumber.Text);
                    }
                }
                else                  //Shouldn't happen
                {
                    MsgBox.Show(this, "Please select a transaction type");
                    return(false);
                }
                posRequest.ForceDuplicate = checkForceDuplicate.Checked;
            }
            catch (Exception ex) {
                MessageBox.Show(Lan.g(this, "Error creating request:") + " " + ex.Message);
                return(false);
            }
            Action actionCloseProgress = null;

            try {
                actionCloseProgress = ODProgressOld.ShowProgressStatus("PayConnectProcessing", this, Lan.g(this, "Processing payment on terminal"));
                _posResponse        = DpsPos.ProcessCreditCard(posRequest);
            }
            catch (Exception ex) {
                actionCloseProgress?.Invoke();
                MessageBox.Show(Lan.g(this, "Error processing card:") + " " + ex.Message);
                return(false);
            }
            actionCloseProgress?.Invoke();
            if (_posResponse == null)
            {
                MessageBox.Show(Lan.g(this, "Error processing card"));
                return(false);
            }
            if (_posResponse.ResponseCode != "0")           //"0" indicates success. May need to check the AuthCode field too to determine if this was a success.
            {
                MessageBox.Show(Lan.g(this, "Error message from Pay Connect:") + "\r\n" + _posResponse.ResponseDescription);
                return(false);
            }
            PayConnectService.signatureResponse sigResponse = null;
            try {
                Cursor      = Cursors.WaitCursor;
                sigResponse = SendSignature(_posResponse.ReferenceNumber.ToString());
                Cursor      = Cursors.Default;
            }
            catch (Exception ex) {
                Cursor = Cursors.Default;
                MessageBox.Show(Lan.g(this, "Card successfully charged. Error processing signature:") + " " + ex.Message);
            }
            textCardNumber.Text = _posResponse.CardNumber;
            textAmount.Text     = _posResponse.Amount.ToString("f");
            _receiptStr         = PayConnectUtils.BuildReceiptString(posRequest, _posResponse, sigResponse, _clinicNum);
            PrintReceipt(_receiptStr);
            return(true);
        }
示例#17
0
        ///<summary>Fills grid based on values in _listEtrans.
        ///Set isRefreshNeeded to true when we need to reinitialize local dictionarys after in memory list is also updated. Required true for first time running.
        ///Also allows you to passed in predetermined filter options.</summary>
        private void FillGrid(bool isRefreshNeeded, List <string> listSelectedStatuses, List <long> listSelectedClinicNums,
                              string carrierName, string checkTraceNum, string amountMin, string amountMax)
        {
            Action actionCloseProgress = null;

            if (isRefreshNeeded)
            {
                actionCloseProgress = ODProgressOld.ShowProgressStatus("Etrans835", this, Lan.g(this, "Gathering data") + "...", false);
                _dictEtrans835s.Clear();
                _dictEtransClaims.Clear();
                _dictClaimPayExists.Clear();
                List <Etrans835Attach>    listAttached        = Etrans835Attaches.GetForEtrans(_listEtranss.Select(x => x.EtransNum).ToArray());
                Dictionary <long, string> dictEtransMessages  = new Dictionary <long, string>();
                List <X12ClaimMatch>      list835ClaimMatches = new List <X12ClaimMatch>();
                Dictionary <long, int>    dictClaimMatchCount = new Dictionary <long, int>(); //1:1 with _listEtranss. Stores how many claim matches each 835 has.
                int batchQueryInterval = 500;                                                 //Every 500 rows we get the next 500 message texts to save memory.
                int rowCur             = 0;
                foreach (Etrans etrans in _listEtranss)
                {
                    if (rowCur % batchQueryInterval == 0)
                    {
                        int range = Math.Min(batchQueryInterval, _listEtranss.Count - rowCur);                   //Either the full batchQueryInterval amount or the remaining amount of etrans.
                        dictEtransMessages = EtransMessageTexts.GetMessageTexts(_listEtranss.GetRange(rowCur, range).Select(x => x.EtransMessageTextNum).ToList(), false);
                    }
                    rowCur++;
                    ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Processing 835: ") + ": " + rowCur + " out of " + _listEtranss.Count));
                    List <Etrans835Attach> listAttachedTo835 = listAttached.FindAll(x => x.EtransNum == etrans.EtransNum);
                    X835 x835 = new X835(etrans, dictEtransMessages[etrans.EtransMessageTextNum], etrans.TranSetId835, listAttachedTo835, true);
                    _dictEtrans835s.Add(etrans.EtransNum, x835);
                    List <X12ClaimMatch> listClaimMatches = x835.GetClaimMatches();
                    dictClaimMatchCount.Add(etrans.EtransNum, listClaimMatches.Count);
                    list835ClaimMatches.AddRange(listClaimMatches);
                }
                #region Set 835 unattached in batch and build _dictEtransClaims and _dictClaimPayCheckNums.
                ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Gathering internal claim matches.")));
                List <long> listClaimNums = Claims.GetClaimFromX12(list835ClaimMatches);             //Can return null.
                ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Building data sets.")));
                int         claimIndexCur        = 0;
                List <long> listMatchedClaimNums = new List <long>();
                foreach (Etrans etrans in _listEtranss)
                {
                    X835 x835 = _dictEtrans835s[etrans.EtransNum];
                    if (listClaimNums != null)
                    {
                        x835.SetClaimNumsForUnattached(listClaimNums.GetRange(claimIndexCur, dictClaimMatchCount[etrans.EtransNum]));
                    }
                    claimIndexCur += dictClaimMatchCount[etrans.EtransNum];
                    listMatchedClaimNums.AddRange(x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(x => x.ClaimNum).ToList());
                }
                List <Claim> listClaims = Claims.GetClaimsFromClaimNums(listMatchedClaimNums.Distinct().ToList());
                _dictClaimPayExists = ClaimPayments.HasClaimPayment(listMatchedClaimNums);              //Every claim num is associated to a bool. True when there is an existing claimPayment.
                foreach (Etrans etrans in _listEtranss)
                {
                    X835 x835 = _dictEtrans835s[etrans.EtransNum];
                    #region _dictEtransClaims, _dictClaimPayCheckNums
                    _dictEtransClaims.Add(etrans.EtransNum, new List <Claim>());
                    List <long>  listSubClaimNums = x835.ListClaimsPaid.FindAll(x => x.ClaimNum != 0).Select(y => y.ClaimNum).ToList();
                    List <Claim> listClaimsFor835 = listClaims.FindAll(x => listSubClaimNums.Contains(x.ClaimNum));
                    foreach (Hx835_Claim claim in x835.ListClaimsPaid)
                    {
                        Claim claimCur = listClaimsFor835.FirstOrDefault(x => x.ClaimNum == claim.ClaimNum);                    //Can be null.
                        if (claimCur == null && claim.IsAttachedToClaim && claim.ClaimNum == 0)
                        {
                            claimCur = new Claim();                          //Create empty claim since user detached claim manually, will not be considered in GetStringStatus(...).
                        }
                        if (claimCur != null && claim.IsPreauth)             //User attached preauth to internal claim, no payment needed to be considered 'Finalized' in GetStringStatus(...).
                        {
                            _dictClaimPayExists[claim.ClaimNum] = true;
                        }
                        _dictEtransClaims[etrans.EtransNum].Add(claimCur);
                    }
                    #endregion
                }
                ODEvent.Fire(new ODEventArgs("Etrans835", Lan.g(this, "Filling Grid.")));
                #endregion
            }
            gridMain.BeginUpdate();
            #region Initilize columns only once
            if (gridMain.Columns.Count == 0)
            {
                ODGridColumn col;
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Patient Name"), 250);
                gridMain.Columns.Add(col);
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Carrier Name"), 190);
                gridMain.Columns.Add(col);
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Status"), 80);
                gridMain.Columns.Add(col);
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Date"), 80);
                gridMain.Columns.Add(col);
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Amount"), 80);
                gridMain.Columns.Add(col);
                if (PrefC.HasClinicsEnabled)
                {
                    col = new ODGridColumn(Lan.g("TableEtrans835s", "Clinic"), 70);
                    gridMain.Columns.Add(col);
                }
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Code"), 37, HorizontalAlignment.Center);
                gridMain.Columns.Add(col);
                col = new ODGridColumn(Lan.g("TableEtrans835s", "Note"), 0);
                gridMain.Columns.Add(col);
            }
            #endregion
            gridMain.Rows.Clear();
            foreach (Etrans etrans in _listEtranss)
            {
                X835 x835 = _dictEtrans835s[etrans.EtransNum];
                #region Filter: Carrier Name
                if (carrierName != "" && !x835.PayerName.ToLower().Contains(carrierName.ToLower()))
                {
                    continue;
                }
                #endregion
                string status = GetStringStatus(etrans.EtransNum);
                #region Filter: Status
                if (!listSelectedStatuses.Contains(status.Replace("*", "")))                //The filter will ignore finalized with detached claims.
                {
                    continue;
                }
                #endregion
                //List of ClinicNums for the current etrans.ListClaimsPaid from the DB.
                List <long> listClinicNums = _dictEtransClaims[etrans.EtransNum].Select(x => x == null? 0 :x.ClinicNum).Distinct().ToList();
                #region Filter: Clinics
                if (PrefC.HasClinicsEnabled && !listClinicNums.Exists(x => listSelectedClinicNums.Contains(x)))
                {
                    continue;                    //The ClinicNums associated to the 835 do not match any of the selected ClinicNums, so nothing to show in this 835.
                }
                #endregion
                #region Filter: Check and Trace Value
                if (checkTraceNum != "" && !x835.TransRefNum.Contains(checkTraceNum))               //Trace Number does not match
                {
                    continue;
                }
                #endregion
                #region Filter: Insurance Check Range Min and Max
                if (amountMin != "" && x835.InsPaid < PIn.Decimal(amountMin) || amountMax != "" && x835.InsPaid > PIn.Decimal(amountMax))
                {
                    continue;                    //Either the InsPaid is below or above our range.
                }
                #endregion
                ODGridRow row = new ODGridRow();
                #region Column: Patient Name
                List <string> listPatNames = x835.ListClaimsPaid.Select(x => x.PatientName.ToString()).Distinct().ToList();
                string        patName      = (listPatNames.Count > 0 ? listPatNames[0] : "");
                if (listPatNames.Count > 1)
                {
                    patName = "(" + POut.Long(listPatNames.Count) + ")";
                }
                row.Cells.Add(patName);
                #endregion
                row.Cells.Add(x835.PayerName);
                row.Cells.Add(status);                //See GetStringStatus(...) for possible values.
                row.Cells.Add(POut.Date(etrans.DateTimeTrans));
                row.Cells.Add(POut.Decimal(x835.InsPaid));
                #region Column: Clinic
                if (PrefC.HasClinicsEnabled)
                {
                    string clinicAbbr = "";
                    if (listClinicNums.Count == 1)
                    {
                        if (listClinicNums[0] == 0)
                        {
                            clinicAbbr = Lan.g(this, "Unassigned");
                        }
                        else
                        {
                            clinicAbbr = Clinics.GetAbbr(listClinicNums[0]);
                        }
                    }
                    else if (listClinicNums.Count > 1)
                    {
                        clinicAbbr = "(" + Lan.g(this, "Multiple") + ")";
                    }
                    row.Cells.Add(clinicAbbr);
                }
                #endregion
                row.Cells.Add(x835._paymentMethodCode);
                row.Cells.Add(etrans.Note);
                row.Tag = etrans;
                gridMain.Rows.Add(row);
            }
            gridMain.EndUpdate();
            actionCloseProgress?.Invoke();
        }
示例#18
0
        private void FillSubGrid(bool isRefreshNeeded = false, string grouping95 = "")
        {
            Action loadingProgress = null;

            Cursor = Cursors.WaitCursor;
            bugSubmissionControl.ClearCustomerInfo();
            bugSubmissionControl.SetTextDevNoteEnabled(false);
            if (isRefreshNeeded)
            {
                loadingProgress = ODProgressOld.ShowProgressStatus("FormBugSubmissions", this, Lan.g(this, "Refreshing Data") + "...", false);
                #region Refresh Logic
                if (_viewMode.In(FormBugSubmissionMode.ViewOnly, FormBugSubmissionMode.ValidationMode))
                {
                    _listAllSubs = ListViewedSubs;
                }
                else
                {
                    _listAllSubs = BugSubmissions.GetAllInRange(dateRangePicker.GetDateTimeFrom(), dateRangePicker.GetDateTimeTo());
                }
                try {
                    _dictPatients = RegistrationKeys.GetPatientsByKeys(_listAllSubs.Select(x => x.RegKey).ToList());
                }
                catch (Exception e) {
                    e.DoNothing();
                    _dictPatients = new Dictionary <string, Patient>();
                }
                #endregion
            }
            gridSubs.BeginUpdate();
            #region gridSubs columns
            gridSubs.Columns.Clear();
            gridSubs.Columns.Add(new ODGridColumn("Submitter", 140));
            gridSubs.Columns.Add(new ODGridColumn("Vers.", 55, GridSortingStrategy.VersionNumber));
            if (comboGrouping.SelectedIndex == 0)           //Group by 'None'
            {
                gridSubs.Columns.Add(new ODGridColumn("DateTime", 75, GridSortingStrategy.DateParse));
            }
            else
            {
                gridSubs.Columns.Add(new ODGridColumn("Count", 75, GridSortingStrategy.AmountParse));
            }
            gridSubs.Columns.Add(new ODGridColumn("HasBug", 50, HorizontalAlignment.Center));
            gridSubs.Columns.Add(new ODGridColumn("Msg Text", 300));
            gridSubs.AllowSortingByColumn = true;
            #endregion
            #region Filter Logic
            ODEvent.Fire(new ODEventArgs("FormBugSubmissions", "Filtering Data"));
            List <string> listSelectedVersions = comboVersions.ListSelectedItems.Select(x => (string)x).ToList();
            if (listSelectedVersions.Contains("All"))
            {
                listSelectedVersions.Clear();
            }
            List <string> listSelectedRegKeys = comboRegKeys.ListSelectedItems.Select(x => (string)x).ToList();
            if (listSelectedRegKeys.Contains("All"))
            {
                listSelectedRegKeys.Clear();
            }
            List <string> listStackFilters = textStackFilter.Text.Split(',')
                                             .Where(x => !string.IsNullOrWhiteSpace(x))
                                             .Select(x => x.ToLower()).ToList();
            List <string> listPatNumFilters = textPatNums.Text.Split(',')
                                              .Where(x => !string.IsNullOrWhiteSpace(x))
                                              .Select(x => x.ToLower()).ToList();
            _listAllSubs.ForEach(x => x.TagOD = null);
            List <BugSubmission> listFilteredSubs = _listAllSubs.Where(x =>
                                                                       PassesFilterValidation(x, listSelectedRegKeys, listStackFilters, listPatNumFilters, listSelectedVersions, grouping95)
                                                                       ).ToList();
            if (isRefreshNeeded)
            {
                FillVersionsFilter(listFilteredSubs);
                FillRegKeyFilter(listFilteredSubs);
            }
            #endregion
            #region Grouping Logic
            List <BugSubmission> listGroupedSubs;
            int index = 0;
            List <BugSubmission> listGridSubmissions = new List <BugSubmission>();
            foreach (BugSubmission sub in listFilteredSubs)
            {
                ODEvent.Fire(new ODEventArgs("FormBugSubmissions", "Grouping Data: " + POut.Double(((double)index++ / (double)listFilteredSubs.Count) * 100) + "%"));
                if (sub.TagOD != null)
                {
                    continue;
                }
                switch (comboGrouping.SelectedIndex)
                {
                case 0:
                    #region None
                    sub.TagOD = new List <BugSubmission>()
                    {
                        sub
                    };                                                                      //Tag is a specific bugSubmission
                    listGridSubmissions.Add(sub.Copy());
                    #endregion
                    break;

                case 1:
                    #region RegKey/Ver/Stack
                    listGroupedSubs = listFilteredSubs.FindAll(x => x.TagOD == null && x.RegKey == sub.RegKey &&
                                                               x.Info.DictPrefValues[PrefName.ProgramVersion] == sub.Info.DictPrefValues[PrefName.ProgramVersion] &&
                                                               x.ExceptionStackTrace == sub.ExceptionStackTrace &&
                                                               x.BugId == sub.BugId);
                    if (listGroupedSubs.Count == 0)
                    {
                        continue;
                    }
                    listGroupedSubs = listGroupedSubs.OrderByDescending(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]))
                                      .ThenByDescending(x => x.SubmissionDateTime).ToList();
                    listGroupedSubs.ForEach(x => x.TagOD = true);                          //So we don't considered previously handled submissions.
                    listGroupedSubs.First().TagOD = listGroupedSubs;                       //First element is what is shown in grid, still wont be considered again.
                    listGridSubmissions.Add(listGroupedSubs.First().Copy());
                    #endregion
                    break;

                case 2:
                    #region StackTrace
                    listGroupedSubs = listFilteredSubs.FindAll(x => x.TagOD == null && x.ExceptionStackTrace == sub.ExceptionStackTrace && x.BugId == sub.BugId);
                    if (listGroupedSubs.Count == 0)
                    {
                        continue;
                    }
                    listGroupedSubs = listGroupedSubs.OrderByDescending(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]))
                                      .ThenByDescending(x => x.SubmissionDateTime).ToList();
                    listGroupedSubs.ForEach(x => x.TagOD = true);                          //So we don't considered previously handled submissions.
                    listGroupedSubs.First().TagOD = listGroupedSubs;                       //First element is what is shown in grid, still wont be considered again.
                    listGridSubmissions.Add(listGroupedSubs.First().Copy());
                    #endregion
                    break;

                case 3:
                    #region 95%
                    //At this point all bugSubmissions in listFilteredSubs is at least a 95% match. Group them all together in a single row.
                    listGroupedSubs = listFilteredSubs;
                    listGroupedSubs = listGroupedSubs.OrderByDescending(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]))
                                      .ThenByDescending(x => x.SubmissionDateTime).ToList();
                    listGroupedSubs.ForEach(x => x.TagOD = true);                          //So we don't considered previously handled submissions.
                    listGroupedSubs.First().TagOD = listGroupedSubs;                       //First element is what is shown in grid, still wont be considered again.
                    listGridSubmissions.Add(listGroupedSubs.First().Copy());
                    #endregion
                    break;
                }
            }
            #endregion
            #region Sorting Logic
            ODEvent.Fire(new ODEventArgs("FormBugSubmissions", "Sorting Data"));
            switch (comboSortBy.SelectedIndex)
            {
            case 0:
                listGridSubmissions = listGridSubmissions.OrderByDescending(x => new Version(x.Info.DictPrefValues[PrefName.ProgramVersion]))
                                      .ThenByDescending(x => GetGroupCount(x))
                                      .ThenByDescending(x => x.SubmissionDateTime).ToList();
                break;
            }
            #endregion
            #region Fill gridSubs
            gridSubs.Rows.Clear();
            index = 0;
            foreach (BugSubmission sub in listGridSubmissions)
            {
                ODEvent.Fire(new ODEventArgs("FormBugSubmissions", "Filling Grid: " + POut.Double(((double)index++ / (double)listFilteredSubs.Count) * 100) + "%"));
                gridSubs.Rows.Add(GetODGridRowForSub(sub));
            }
            gridSubs.EndUpdate();
            #endregion
            try {
                loadingProgress?.Invoke();                //When this function executes quickly this can fail rarely, fail silently because of WaitCursor.
            }
            catch (Exception ex) {
                ex.DoNothing();
            }
            Cursor = Cursors.Default;
        }