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;
        }