Пример #1
0
        ///<summary>Used in FormPayment to sych database with changes user made to the paySplit list for a payment.  Must supply an old list for comparison.  Only the differences are saved.</summary>
        public static void UpdateList(ArrayList oldSplitList, ArrayList newSplitList)
        {
            PaySplit newPaySplit;

            for (int i = 0; i < oldSplitList.Count; i++)       //loop through the old list
            {
                newPaySplit = null;
                for (int j = 0; j < newSplitList.Count; j++)
                {
                    if (newSplitList[j] == null || ((PaySplit)newSplitList[j]).SplitNum == 0)
                    {
                        continue;
                    }
                    if (((PaySplit)oldSplitList[i]).SplitNum == ((PaySplit)newSplitList[j]).SplitNum)
                    {
                        newPaySplit = (PaySplit)newSplitList[j];
                        break;
                    }
                }
                if (newPaySplit == null)
                {
                    //PaySplit with matching SplitNum was not found, so it must have been deleted
                    PaySplits.Delete(((PaySplit)oldSplitList[i]));
                    continue;
                }
                //PaySplit was found with matching SplitNum, so check for changes
                if (newPaySplit.DateEntry != ((PaySplit)oldSplitList[i]).DateEntry ||
                    newPaySplit.DatePay != ((PaySplit)oldSplitList[i]).DatePay ||
                    newPaySplit.PatNum != ((PaySplit)oldSplitList[i]).PatNum ||
                    newPaySplit.PayNum != ((PaySplit)oldSplitList[i]).PayNum ||
                    newPaySplit.PayPlanNum != ((PaySplit)oldSplitList[i]).PayPlanNum ||
                    newPaySplit.ProcDate != ((PaySplit)oldSplitList[i]).ProcDate ||
                    newPaySplit.ProcNum != ((PaySplit)oldSplitList[i]).ProcNum ||
                    newPaySplit.ProvNum != ((PaySplit)oldSplitList[i]).ProvNum ||
                    newPaySplit.SplitAmt != ((PaySplit)oldSplitList[i]).SplitAmt)
                {
                    PaySplits.Update(newPaySplit);
                }
            }
            for (int i = 0; i < newSplitList.Count; i++)       //loop through the new list
            {
                if (newSplitList[i] == null)
                {
                    continue;
                }
                if (((PaySplit)newSplitList[i]).SplitNum != 0)
                {
                    continue;
                }
                //entry with SplitNum=0, so it's new
                PaySplits.Insert(((PaySplit)newSplitList[i]));
            }
        }
        private void butOK_Click(object sender, EventArgs e)
        {
            if (!Security.IsAuthorized(Permissions.PaymentCreate))
            {
                return;
            }
            double splitTotal = _listSplitsCur.Select(x => x.SplitAmt).Sum();

            if (!splitTotal.IsZero())              //income transfer
            {
                MsgBox.Show(this, "Income transfers must have a split total of 0.");
                return;
            }
            _listSplitsCur.RemoveAll(x => x.SplitAmt.IsZero());            //We don't want any zero splits.  They were there just for display purposes.
            if (_listSplitsCur.Count == 0)
            {
                Payments.Delete(_paymentCur);
            }
            else
            {
                foreach (PaySplit split in _listSplitsCur)
                {
                    PaySplits.Insert(split);
                }
                foreach (PaySplits.PaySplitAssociated split in _listSplitsAssociated)
                {
                    //Update the FSplitNum after inserts are made.
                    if (split.PaySplitLinked != null && split.PaySplitOrig != null)
                    {
                        PaySplits.UpdateFSplitNum(split.PaySplitOrig.SplitNum, split.PaySplitLinked.SplitNum);
                    }
                }
                if (_listSplitsCur.Count > 0)               //only make log when a payment with splits was made.
                {
                    string logText = Payments.GetSecuritylogEntryText(_paymentCur, _paymentCur, isNew: true) + ", " + Lans.g(this, "from Income Transfer Manager.");
                    SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, _paymentCur.PatNum, logText);
                }
                string strErrorMsg = Ledgers.ComputeAgingForPaysplitsAllocatedToDiffPats(_patCur.PatNum, _listSplitsCur);
                if (!string.IsNullOrEmpty(strErrorMsg))
                {
                    MessageBox.Show(strErrorMsg);
                }
            }
            DialogResult = DialogResult.OK;
        }
        ///<summary></summary>
        private void TransferUnallocatedToUnearned()
        {
            List <PaySplit> listSplitsForPats = PaySplits.GetForPats(_listFamilyPatNums);

            if (listSplitsForPats.IsNullOrEmpty())
            {
                return;
            }
            //Pass in an invalid payNum of 0 which will get set correctly later if there are in fact splits to transfer.
            PaymentEdit.IncomeTransferData unallocatedTransfers = PaymentEdit.TransferUnallocatedSplitToUnearned(listSplitsForPats, 0);
            if (unallocatedTransfers.ListSplitsCur.Count == 0)
            {
                return;
            }
            if (!unallocatedTransfers.ListSplitsCur.Sum(x => x.SplitAmt).IsZero())
            {
                //Display the UnearnedType and the SplitAmt for each split in the list.
                string splitInfo = string.Join("\r\n  ", unallocatedTransfers.ListSplitsCur
                                               .Select(x => $"SplitAmt: {x.SplitAmt}"));
                //Show the sum of all splits first and then give a breakdown of each individual split.
                string details = $"Sum of unallocatedTransfers.ListSplitsCur: {unallocatedTransfers.ListSplitsCur.Sum(x => x.SplitAmt)}\r\n"
                                 + $"Individual Split Info:\r\n  {splitInfo}";
                FriendlyException.Show("Error transferring unallocated paysplits.  Please call support.", new ApplicationException(details), "Close");
                //Close the window and do not let the user create transfers because something is wrong.
                DialogResult = DialogResult.Cancel;
                Close();
                return;
            }
            //There are unallocated paysplits that need to be transferred to unearned.
            _unallocatedPayNum = PaymentEdit.CreateAndInsertUnallocatedPayment(_patCur);
            foreach (PaySplit split in unallocatedTransfers.ListSplitsCur)
            {
                split.PayNum = _unallocatedPayNum;              //Set the PayNum because it was purposefully set to 0 above to save queries.
                PaySplits.Insert(split);
            }
            foreach (PaySplits.PaySplitAssociated splitAssociated in unallocatedTransfers.ListSplitsAssociated)
            {
                if (splitAssociated.PaySplitLinked != null && splitAssociated.PaySplitOrig != null)
                {
                    PaySplits.UpdateFSplitNum(splitAssociated.PaySplitOrig.SplitNum, splitAssociated.PaySplitLinked.SplitNum);
                }
            }
            SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, _patCur.PatNum
                                      , $"Unallocated splits automatically transferred to unearned for payment {_unallocatedPayNum}.");
        }
Пример #4
0
        ///<summary>Fills the main grid.  If reload Data is true, account data will be (re)fetched from the database.
        ///If false then data already in memory is used.</summary>
        private void FillGridMain()
        {
            gridMain.BeginUpdate();
            gridMain.ListGridColumns.Clear();
            GridColumn col;

            col = new GridColumn(Lan.g(this, "Name"), 240);
            gridMain.ListGridColumns.Add(col);
            col = new GridColumn(Lan.g(this, "Balance"), 100, GridSortingStrategy.AmountParse);
            gridMain.ListGridColumns.Add(col);
            gridMain.ListGridRows.Clear();
            GridRow row;

            PaymentEdit.ConstructResults results;
            //Make a row for every guarantor that has family members with positive and negative balances.
            List <long> listPatNumsForBatch = _dictCurrentFamilyBatch.Values.Select(x => x.ListFamilyMembers)
                                              .SelectMany(y => y.Select(z => z.PatNum)).ToList();
            List <PaySplit>  listSplitsForBatch           = PaySplits.GetForPats(listPatNumsForBatch);
            List <ClaimProc> listClaimsPayAsTotalForBatch = ClaimProcs.GetByTotForPats(listPatNumsForBatch);

            foreach (KeyValuePair <long, FamilyAccount> kvp in _dictCurrentFamilyBatch)
            {
                //Get all family members now so we cut down on memory used.
                FamilyAccount   famAccount                = kvp.Value;
                List <Patient>  listPatients              = famAccount.ListFamilyMembers;
                List <long>     listFamilyPatNums         = listPatients.Select(x => x.PatNum).ToList();
                long            guarantorNum              = kvp.Key;
                List <PaySplit> listSplitsForPats         = listSplitsForBatch.FindAll(x => x.PatNum.In(listFamilyPatNums));
                long            unallocatedTransferPayNum = PaymentEdit.CreateAndInsertUnallocatedPayment(listPatients.First(x => x.PatNum == guarantorNum));
                if (!listSplitsForPats.IsNullOrEmpty())
                {
                    PaymentEdit.IncomeTransferData txfrResults = PaymentEdit.TransferUnallocatedSplitToUnearned(listSplitsForPats, unallocatedTransferPayNum);
                    foreach (PaySplit split in txfrResults.ListSplitsCur)
                    {
                        split.PayNum = unallocatedTransferPayNum;       //Set the PayNum because it was purposefully set to 0 above to save queries.
                        PaySplits.Insert(split);                        //Need to insert in a loop to get the PrimaryKey
                    }
                    foreach (PaySplits.PaySplitAssociated splitAssociated in txfrResults.ListSplitsAssociated)
                    {
                        if (splitAssociated.PaySplitLinked != null && splitAssociated.PaySplitOrig != null)
                        {
                            PaySplits.UpdateFSplitNum(splitAssociated.PaySplitOrig.SplitNum, splitAssociated.PaySplitLinked.SplitNum);
                        }
                    }
                }
                List <ClaimProc> listClaimsAsTotalForPats = listClaimsPayAsTotalForBatch.FindAll(x => x.PatNum.In(listFamilyPatNums));
                ClaimProcs.TransferClaimsAsTotalToProcedures(listPatients.Select(x => x.PatNum).ToList(), listClaimsAsTotalForPats);
                results = PaymentEdit.ConstructAndLinkChargeCredits(listPatients.Select(x => x.PatNum).ToList(), guarantorNum, new List <PaySplit>(),
                                                                    new Payment(), new List <AccountEntry>(), true);
                famAccount.Account = results;
                List <AccountEntry> listAccountEntries = results.ListAccountCharges;
                //Get guarantor info and fill the row/grid
                Patient guarantor = listPatients.FirstOrDefault(x => x.PatNum == guarantorNum);
                row = new GridRow();
                row.Cells.Add(guarantor.GetNameLFnoPref());
                row.Cells.Add((listAccountEntries.Sum(x => x.AmountEnd)).ToString("f"));
                row.Tag = famAccount;         //Store relevant family info in the guarantor row.
                row.DropDownInitiallyDown = false;
                row.Bold = true;              //Bold parent rows to show it is giving the family balance.
                gridMain.ListGridRows.Add(row);
                //Make child rows
                foreach (Patient p in listPatients)
                {
                    GridRow rowChild = new GridRow();
                    rowChild.Cells.Add(p.GetNameLFnoPref());
                    rowChild.Cells.Add(listAccountEntries.Where(x => x.PatNum == p.PatNum).Sum(x => x.AmountEnd).ToString("f"));
                    rowChild.DropDownParent = row;
                    gridMain.ListGridRows.Add(rowChild);
                }
            }
            gridMain.EndUpdate();
            gridMain.Update();
            labelBatchCount.Text = $"Current batch: {_batchNum} Total batches: {_listBatches.Count()}";
            //Invalidate and update the label to force it to be in sync with the progress bar that is on a separate thread.
            labelBatchCount.Invalidate();
            labelBatchCount.Update();
        }
Пример #5
0
        ///<summary>Balances all selected accounts.</summary>
        private void butTransfer_Click(object sender, EventArgs e)
        {
            //FormIncomeTransferManage requires PaymentCreate to run.
            //This form requires SecurityAdmin and a password to open, and although rare,
            // a SecuirtyAdmin doesn't have to have PaymentCreate permission
            if (!Security.IsAuthorized(Permissions.PaymentCreate))
            {
                return;
            }
            //Make sure the user wants to run the tool.
            if (!MsgBox.Show(this, MsgBoxButtons.YesNo, "This process can take a long time and cannot be reversed.\r\n\r\nContinue?"))
            {
                return;
            }
            Action actionCloseProgress = ODProgress.Show(ODEventType.Billing);

            //Build list of families based off of selected rows.
            _logger.WriteLine("This is the summary of all transactions that took place.This can be saved for later reference if one or more"
                              + " transactions need to be undone outside of the tool.\r\n", LogLevel.Information);
            //Do income transfers if applicable.
            for (int i = _batchNum - 1; i < _listBatches.Count(); i++)     //_batchNum is 1-based, so drop i by 1 to make sure the current batch is included
            {
                string logText = "";
                if (checkAllocateCharges.Checked)
                {
                    ODEvent.Fire(ODEventType.Billing, Lan.g(this, $"Creating income transfers for batch {_batchNum}/{_listBatches.Count()} please wait..."));
                    foreach (FamilyAccount famAccountCur in _dictCurrentFamilyBatch.Select(x => x.Value))
                    {
                        //Clear the list of splits in case any are hanging around from a previous run.
                        famAccountCur.ListSplits.Clear();
                        famAccountCur.ListSplitsAssociated.Clear();
                        //Make lists of positive and negative charges.
                        List <AccountEntry> listPosCharges        = famAccountCur.Account.ListAccountCharges.Where(x => x.AmountEnd > 0).ToList();
                        List <AccountEntry> listNegCharges        = famAccountCur.Account.ListAccountCharges.Where(x => x.AmountEnd < 0).ToList();
                        List <long>         listPatNumsForCharges = listPosCharges.Select(x => x.PatNum).Distinct().ToList();
                        List <AccountEntry> listEntriesForPats    = famAccountCur.Account.ListAccountCharges.FindAll(x => x.PatNum.In(listPatNumsForCharges));
                        //This catch will save us some time if they run the tool on the same family twice.
                        if (listPosCharges.Count() == 0 || listNegCharges.Count() == 0)
                        {
                            continue;                            //No need to add to logText, CreateTransfers wouldn't return anything either.
                        }
                        Payment payCur = CreatePaymentTransferHelper(famAccountCur.Guarantor);
                        logText += CreateTransfers(listPosCharges, listNegCharges, listEntriesForPats, famAccountCur, payCur);
                        logText += CreditsToUnallocated(listNegCharges, famAccountCur, payCur);
                        //Remove any $0 splits created from CreateTransfers.
                        famAccountCur.ListSplits.RemoveAll(x => x.SplitAmt == 0);
                        foreach (PaySplit split in famAccountCur.ListSplits)
                        {
                            PaySplits.Insert(split);
                        }
                        //Go through family accounts and update FSplitNums.
                        foreach (PaySplits.PaySplitAssociated split in famAccountCur.ListSplitsAssociated)
                        {
                            //Update the FSplitNum after inserts are made.
                            if (split.PaySplitLinked != null && split.PaySplitOrig != null)
                            {
                                PaySplits.UpdateFSplitNum(split.PaySplitOrig.SplitNum, split.PaySplitLinked.SplitNum);
                            }
                        }
                    }
                }
                //Transfer balances to guarantor if applicable.
                if (checkGuarAllocate.Checked)
                {
                    ODEvent.Fire(ODEventType.Billing, Lan.g(this, $"Transferring remaining balance to guarantor for batch {_batchNum}/{_listBatches.Count()} please wait..."));
                    logText += "Balances transferred to Guarantor:\r\n";
                    logText += TransferToGuarantor();
                }
                //load up the next batch
                ODEvent.Fire(ODEventType.Billing, Lan.g(this, $"Loading next batch please wait..."));
                FillNextBatch();
                _logger.WriteLine(logText, LogLevel.Information);
            }
            actionCloseProgress();
        }
Пример #6
0
        ///<summary>Sets given appt.AptStatus to broken.
        ///Provide procCode that should be charted, can be null but will not chart a broken procedure.
        ///Also considers various broken procedure based prefs.
        ///Makes its own securitylog entries.</summary>
        public static void BreakApptHelper(Appointment appt, Patient pat, ProcedureCode procCode)
        {
            //suppressHistory is true due to below logic creating a log with a specific HistAppointmentAction instead of the generic changed.
            DateTime datePrevious    = appt.DateTStamp;
            bool     suppressHistory = false;

            if (procCode != null)
            {
                suppressHistory = (procCode.ProcCode.In("D9986", "D9987"));
            }
            Appointments.SetAptStatus(appt, ApptStatus.Broken, suppressHistory); //Appointments S-Class handles Signalods
            if (appt.AptStatus != ApptStatus.Complete)                           //seperate log entry for completed appointments.
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            else
            {
                SecurityLogs.MakeLogEntry(Permissions.AppointmentCompleteEdit, pat.PatNum,
                                          appt.ProcDescript + ", " + appt.AptDateTime.ToString()
                                          + ", Broken from the Appts module.", appt.AptNum, datePrevious);
            }
            #region HL7
            //If there is an existing HL7 def enabled, send a SIU message if there is an outbound SIU message defined
            if (HL7Defs.IsExistingHL7Enabled())
            {
                //S15 - Appt Cancellation event
                MessageHL7 messageHL7 = MessageConstructor.GenerateSIU(pat, Patients.GetPat(pat.Guarantor), EventTypeHL7.S15, appt);
                //Will be null if there is no outbound SIU message defined, so do nothing
                if (messageHL7 != null)
                {
                    HL7Msg hl7Msg = new HL7Msg();
                    hl7Msg.AptNum    = appt.AptNum;
                    hl7Msg.HL7Status = HL7MessageStatus.OutPending;                  //it will be marked outSent by the HL7 service.
                    hl7Msg.MsgText   = messageHL7.ToString();
                    hl7Msg.PatNum    = pat.PatNum;
                    HL7Msgs.Insert(hl7Msg);
#if DEBUG
                    MessageBox.Show("Appointments", messageHL7.ToString());
#endif
                }
            }
            #endregion
            List <Procedure> listProcedures = new List <Procedure>();
            //splits should only exist on procs if they are using tp pre-payments
            List <PaySplit> listSplitsForApptProcs = new List <PaySplit>();
            bool            isNonRefundable        = false;
            double          brokenProcAmount       = 0;
            Procedure       brokenProcedure        = new Procedure();
            bool            wasBrokenProcDeleted   = false;
            if (PrefC.GetYN(PrefName.PrePayAllowedForTpProcs))
            {
                listProcedures = Procedures.GetProcsForSingle(appt.AptNum, false);
                if (listProcedures.Count > 0)
                {
                    listSplitsForApptProcs = PaySplits.GetPaySplitsFromProcs(listProcedures.Select(x => x.ProcNum).ToList());
                }
            }
            #region Charting the proc
            if (procCode != null)
            {
                switch (procCode.ProcCode)
                {
                case "D9986":                        //Missed
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Missed);
                    break;

                case "D9987":                        //Cancelled
                    HistAppointments.CreateHistoryEntry(appt.AptNum, HistAppointmentAction.Cancelled);
                    break;
                }
                brokenProcedure.PatNum       = pat.PatNum;
                brokenProcedure.ProvNum      = (procCode.ProvNumDefault > 0 ? procCode.ProvNumDefault : appt.ProvNum);
                brokenProcedure.CodeNum      = procCode.CodeNum;
                brokenProcedure.ProcDate     = DateTime.Today;
                brokenProcedure.DateEntryC   = DateTime.Now;
                brokenProcedure.ProcStatus   = ProcStat.C;
                brokenProcedure.ClinicNum    = appt.ClinicNum;
                brokenProcedure.UserNum      = Security.CurUser.UserNum;
                brokenProcedure.Note         = Lans.g("AppointmentEdit", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                brokenProcedure.PlaceService = (PlaceOfService)PrefC.GetInt(PrefName.DefaultProcedurePlaceService);              //Default proc place of service for the Practice is used.
                List <InsSub>  listInsSubs    = InsSubs.RefreshForFam(Patients.GetFamily(pat.PatNum));
                List <InsPlan> listInsPlans   = InsPlans.RefreshForSubList(listInsSubs);
                List <PatPlan> listPatPlans   = PatPlans.Refresh(pat.PatNum);
                InsPlan        insPlanPrimary = null;
                InsSub         insSubPrimary  = null;
                if (listPatPlans.Count > 0)
                {
                    insSubPrimary  = InsSubs.GetSub(listPatPlans[0].InsSubNum, listInsSubs);
                    insPlanPrimary = InsPlans.GetPlan(insSubPrimary.PlanNum, listInsPlans);
                }
                double procFee;
                long   feeSch;
                if (insPlanPrimary == null || procCode.NoBillIns)
                {
                    feeSch = FeeScheds.GetFeeSched(0, pat.FeeSched, brokenProcedure.ProvNum);
                }
                else                  //Only take into account the patient's insurance fee schedule if the D9986 procedure is not marked as NoBillIns
                {
                    feeSch = FeeScheds.GetFeeSched(insPlanPrimary.FeeSched, pat.FeeSched, brokenProcedure.ProvNum);
                }
                procFee = Fees.GetAmount0(brokenProcedure.CodeNum, feeSch, brokenProcedure.ClinicNum, brokenProcedure.ProvNum);
                if (insPlanPrimary != null && insPlanPrimary.PlanType == "p" && !insPlanPrimary.IsMedical)         //PPO
                {
                    double provFee = Fees.GetAmount0(brokenProcedure.CodeNum, Providers.GetProv(brokenProcedure.ProvNum).FeeSched, brokenProcedure.ClinicNum,
                                                     brokenProcedure.ProvNum);
                    brokenProcedure.ProcFee = Math.Max(provFee, procFee);
                }
                else if (listSplitsForApptProcs.Count > 0 && PrefC.GetBool(PrefName.TpPrePayIsNonRefundable) && procCode.ProcCode == "D9986")
                {
                    //if there are pre-payments, non-refundable pre-payments is turned on, and the broken appointment is a missed code then auto-fill
                    //the window with the sum of the procs for the appointment. Transfer money below after broken procedure is confirmed by the user.
                    brokenProcedure.ProcFee = listSplitsForApptProcs.Sum(x => x.SplitAmt);
                    isNonRefundable         = true;
                }
                else
                {
                    brokenProcedure.ProcFee = procFee;
                }
                if (!PrefC.GetBool(PrefName.EasyHidePublicHealth))
                {
                    brokenProcedure.SiteNum = pat.SiteNum;
                }
                Procedures.Insert(brokenProcedure);
                //Now make a claimproc if the patient has insurance.  We do this now for consistency because a claimproc could get created in the future.
                List <Benefit>   listBenefits          = Benefits.Refresh(listPatPlans, listInsSubs);
                List <ClaimProc> listClaimProcsForProc = ClaimProcs.RefreshForProc(brokenProcedure.ProcNum);
                Procedures.ComputeEstimates(brokenProcedure, pat.PatNum, listClaimProcsForProc, false, listInsPlans, listPatPlans, listBenefits, pat.Age, listInsSubs);
                FormProcBroken FormPB = new FormProcBroken(brokenProcedure, isNonRefundable);
                FormPB.IsNew = true;
                FormPB.ShowDialog();
                brokenProcAmount     = FormPB.AmountTotal;
                wasBrokenProcDeleted = FormPB.IsProcDeleted;
            }
            #endregion
            #region BrokenApptAdjustment
            if (PrefC.GetBool(PrefName.BrokenApptAdjustment))
            {
                Adjustment AdjustmentCur = new Adjustment();
                AdjustmentCur.DateEntry = DateTime.Today;
                AdjustmentCur.AdjDate   = DateTime.Today;
                AdjustmentCur.ProcDate  = DateTime.Today;
                AdjustmentCur.ProvNum   = appt.ProvNum;
                AdjustmentCur.PatNum    = pat.PatNum;
                AdjustmentCur.AdjType   = PrefC.GetLong(PrefName.BrokenAppointmentAdjustmentType);
                AdjustmentCur.ClinicNum = appt.ClinicNum;
                FormAdjust FormA = new FormAdjust(pat, AdjustmentCur);
                FormA.IsNew = true;
                FormA.ShowDialog();
            }
            #endregion
            #region BrokenApptCommLog
            if (PrefC.GetBool(PrefName.BrokenApptCommLog))
            {
                Commlog commlogCur = new Commlog();
                commlogCur.PatNum       = pat.PatNum;
                commlogCur.CommDateTime = DateTime.Now;
                commlogCur.CommType     = Commlogs.GetTypeAuto(CommItemTypeAuto.APPT);
                commlogCur.Note         = Lan.g("Appointment", "Appt BROKEN for") + " " + appt.ProcDescript + "  " + appt.AptDateTime.ToString();
                commlogCur.Mode_        = CommItemMode.None;
                commlogCur.UserNum      = Security.CurUser.UserNum;
                commlogCur.IsNew        = true;
                FormCommItem FormCI = new FormCommItem(commlogCur);
                FormCI.ShowDialog();
            }
            #endregion
            #region Transfer money from TP Procedures if necessary
            //Note this MUST come after FormProcBroken since clicking cancel in that window will delete the procedure.
            if (isNonRefundable && !wasBrokenProcDeleted && listSplitsForApptProcs.Count > 0)
            {
                //transfer what the user specified in the broken appointment window.
                //transfer up to the amount specified by the user
                foreach (Procedure proc in listProcedures)
                {
                    if (brokenProcAmount == 0)
                    {
                        break;
                    }
                    List <PaySplit> listSplitsForAppointmentProcedure = listSplitsForApptProcs.FindAll(x => x.ProcNum == proc.ProcNum);
                    foreach (PaySplit split in listSplitsForAppointmentProcedure)
                    {
                        if (brokenProcAmount == 0)
                        {
                            break;
                        }
                        double amt = Math.Min(brokenProcAmount, split.SplitAmt);
                        Payments.CreateTransferForTpProcs(proc, new List <PaySplit> {
                            split
                        }, brokenProcedure, amt);
                        double amtPaidOnApt = listSplitsForApptProcs.Sum(x => x.SplitAmt);
                        if (amtPaidOnApt > amt)
                        {
                            //If the original prepayment amount is greater than the amt being specified for the appointment break, transfer
                            //the difference to an Unallocated Unearned Paysplit on the account.
                            double remainingAmt = amtPaidOnApt - amt;
                            //We have to create a new transfer payment here to correlate to the split.
                            Payment txfrPayment = new Payment();
                            txfrPayment.PayAmt    = 0;
                            txfrPayment.PayDate   = DateTime.Today;
                            txfrPayment.ClinicNum = split.ClinicNum;
                            txfrPayment.PayNote   = "Automatic transfer from treatment planned procedure prepayment.";
                            txfrPayment.PatNum    = split.PatNum;                       //ultimately where the payment ends up.
                            txfrPayment.PayType   = 0;
                            Payments.Insert(txfrPayment);
                            PaymentEdit.IncomeTransferData transferData = PaymentEdit.IncomeTransferData.CreateTransfer(split, txfrPayment.PayNum, true, remainingAmt);
                            PaySplit offset         = transferData.ListSplitsCur.FirstOrDefault(x => x.FSplitNum != 0);
                            long     offsetSplitNum = PaySplits.Insert(offset);                      //Get the FSplitNum from the offset
                            PaySplit allocation     = transferData.ListSplitsCur.FirstOrDefault(x => x.FSplitNum == 0);
                            allocation.FSplitNum = offsetSplitNum;
                            PaySplits.Insert(allocation);                            //Insert so the split is now up to date
                            SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, txfrPayment.PatNum, "Automatic transfer of funds for treatment plan procedure pre-payments.");
                        }
                        brokenProcAmount -= amt;
                    }
                }
            }
            //if broken appointment procedure was deleted (user cancelled out of the window) just keep money on the original procedure.
            #endregion
            AppointmentEvent.Fire(ODEventType.AppointmentEdited, appt);
            AutomationL.Trigger(AutomationTrigger.BreakAppointment, null, pat.PatNum);
            Recalls.SynchScheduledApptFull(appt.PatNum);
        }
        private void butSend_Click(object sender, EventArgs e)
        {
            //Assuming the use of XCharge.  If adding another vendor (PayConnect for example)
            //make sure to move XCharge validation in FillGrid() to here.
            if (prog == null)           //Gets filled in FillGrid()
            {
                return;
            }
            if (gridMain.SelectedIndices.Length < 1)
            {
                MsgBox.Show(this, "Must select at least one recurring charge.");
                return;
            }
            if (!PaymentsWithinLockDate())
            {
                return;
            }
            string recurringResultFile = "Recurring charge results for " + DateTime.Now.ToShortDateString() + " ran at " + DateTime.Now.ToShortTimeString() + "\r\n\r\n";
            int    failed   = 0;
            int    success  = 0;
            string user     = ProgramProperties.GetPropVal(prog.ProgramNum, "Username");
            string password = ProgramProperties.GetPropVal(prog.ProgramNum, "Password");

            #region Card Charge Loop
            for (int i = 0; i < gridMain.SelectedIndices.Length; i++)
            {
                #region X-Charge
                if (table.Rows[gridMain.SelectedIndices[i]]["XChargeToken"].ToString() != "" &&
                    CreditCards.IsDuplicateXChargeToken(table.Rows[gridMain.SelectedIndices[i]]["XChargeToken"].ToString()))
                {
                    MessageBox.Show(Lan.g(this, "A duplicate token was found, the card cannot be charged for customer: ") + table.Rows[i]["PatName"].ToString());
                    continue;
                }
                insertPayment = false;
                ProcessStartInfo info       = new ProcessStartInfo(xPath);
                long             patNum     = PIn.Long(table.Rows[gridMain.SelectedIndices[i]]["PatNum"].ToString());
                string           resultfile = Path.Combine(Path.GetDirectoryName(xPath), "XResult.txt");
                File.Delete(resultfile);                //delete the old result file.
                info.Arguments = "";
                double   amt        = PIn.Double(table.Rows[gridMain.SelectedIndices[i]]["ChargeAmt"].ToString());
                DateTime exp        = PIn.Date(table.Rows[gridMain.SelectedIndices[i]]["CCExpiration"].ToString());
                string   address    = PIn.String(table.Rows[gridMain.SelectedIndices[i]]["Address"].ToString());
                string   addressPat = PIn.String(table.Rows[gridMain.SelectedIndices[i]]["AddressPat"].ToString());
                string   zip        = PIn.String(table.Rows[gridMain.SelectedIndices[i]]["Zip"].ToString());
                string   zipPat     = PIn.String(table.Rows[gridMain.SelectedIndices[i]]["ZipPat"].ToString());
                info.Arguments += "/AMOUNT:" + amt.ToString("F2") + " /LOCKAMOUNT ";
                info.Arguments += "/TRANSACTIONTYPE:PURCHASE /LOCKTRANTYPE ";
                if (table.Rows[gridMain.SelectedIndices[i]]["XChargeToken"].ToString() != "")
                {
                    info.Arguments += "/XCACCOUNTID:" + table.Rows[gridMain.SelectedIndices[i]]["XChargeToken"].ToString() + " ";
                    info.Arguments += "/RECURRING ";
                }
                else
                {
                    info.Arguments += "/ACCOUNT:" + table.Rows[gridMain.SelectedIndices[i]]["CCNumberMasked"].ToString() + " ";
                }
                if (exp.Year > 1880)
                {
                    info.Arguments += "/EXP:" + exp.ToString("MMyy") + " ";
                }
                if (address != "")
                {
                    info.Arguments += "\"/ADDRESS:" + address + "\" ";
                }
                else if (addressPat != "")
                {
                    info.Arguments += "\"/ADDRESS:" + addressPat + "\" ";
                }
                if (zip != "")
                {
                    info.Arguments += "\"/ZIP:" + zip + "\" ";
                }
                else if (zipPat != "")
                {
                    info.Arguments += "\"/ZIP:" + zipPat + "\" ";
                }
                info.Arguments += "/RECEIPT:Pat" + patNum + " ";          //aka invoice#
                info.Arguments += "\"/CLERK:" + Security.CurUser.UserName + " R\" /LOCKCLERK ";
                info.Arguments += "/RESULTFILE:\"" + resultfile + "\" ";
                info.Arguments += "/USERID:" + user + " ";
                info.Arguments += "/PASSWORD:"******" ";
                info.Arguments += "/HIDEMAINWINDOW ";
                info.Arguments += "/AUTOPROCESS ";
                info.Arguments += "/SMALLWINDOW ";
                info.Arguments += "/AUTOCLOSE ";
                info.Arguments += "/NORESULTDIALOG ";
                Cursor          = Cursors.WaitCursor;
                Process process = new Process();
                process.StartInfo           = info;
                process.EnableRaisingEvents = true;
                process.Start();
                while (!process.HasExited)
                {
                    Application.DoEvents();
                }
                Thread.Sleep(200);                //Wait 2/10 second to give time for file to be created.
                Cursor = Cursors.Default;
                string line       = "";
                string resultText = "";
                recurringResultFile += "PatNum: " + patNum + " Name: " + table.Rows[i]["PatName"].ToString() + "\r\n";
                using (TextReader reader = new StreamReader(resultfile)) {
                    line = reader.ReadLine();
                    while (line != null)
                    {
                        if (resultText != "")
                        {
                            resultText += "\r\n";
                        }
                        resultText += line;
                        if (line.StartsWith("RESULT="))
                        {
                            if (line == "RESULT=SUCCESS")
                            {
                                success++;
                                labelCharged.Text = Lan.g(this, "Charged=") + success;
                                insertPayment     = true;
                            }
                            else
                            {
                                failed++;
                                labelFailed.Text = Lan.g(this, "Failed=") + failed;
                            }
                        }
                        line = reader.ReadLine();
                    }
                    recurringResultFile += resultText + "\r\n\r\n";
                }
                #endregion
                if (insertPayment)
                {
                    Patient patCur     = Patients.GetPat(patNum);
                    Payment paymentCur = new Payment();
                    paymentCur.DateEntry = nowDateTime.Date;
                    paymentCur.PayDate   = GetPayDate(PIn.Date(table.Rows[gridMain.SelectedIndices[i]]["LatestPayment"].ToString()),
                                                      PIn.Date(table.Rows[gridMain.SelectedIndices[i]]["DateStart"].ToString()));
                    paymentCur.PatNum        = patCur.PatNum;
                    paymentCur.ClinicNum     = PIn.Long(table.Rows[gridMain.SelectedIndices[i]]["ClinicNum"].ToString());
                    paymentCur.PayType       = PIn.Int(ProgramProperties.GetPropVal(prog.ProgramNum, "PaymentType"));
                    paymentCur.PayAmt        = amt;
                    paymentCur.PayNote       = resultText;
                    paymentCur.IsRecurringCC = true;
                    Payments.Insert(paymentCur);
                    long provNum = PIn.Long(table.Rows[gridMain.SelectedIndices[i]]["ProvNum"].ToString()); //for payment plans only
                    if (provNum == 0)                                                                       //Regular payments need to apply to the provider that the family owes the most money to.
                    {
                        DataTable dt         = Patients.GetPaymentStartingBalances(patCur.Guarantor, paymentCur.PayNum);
                        double    highestAmt = 0;
                        for (int j = 0; j < dt.Rows.Count; j++)
                        {
                            double afterIns = PIn.Double(dt.Rows[j]["AfterIns"].ToString());
                            if (highestAmt >= afterIns)
                            {
                                continue;
                            }
                            highestAmt = afterIns;
                            provNum    = PIn.Long(dt.Rows[j]["ProvNum"].ToString());
                        }
                    }
                    PaySplit split = new PaySplit();
                    split.PatNum     = paymentCur.PatNum;
                    split.ClinicNum  = paymentCur.ClinicNum;
                    split.PayNum     = paymentCur.PayNum;
                    split.ProcDate   = paymentCur.PayDate;
                    split.DatePay    = paymentCur.PayDate;
                    split.ProvNum    = provNum;
                    split.SplitAmt   = paymentCur.PayAmt;
                    split.PayPlanNum = PIn.Long(table.Rows[gridMain.SelectedIndices[i]]["PayPlanNum"].ToString());
                    PaySplits.Insert(split);
                    if (PrefC.GetBool(PrefName.AgingCalculatedMonthlyInsteadOfDaily))
                    {
                        Ledgers.ComputeAging(patCur.Guarantor, PrefC.GetDate(PrefName.DateLastAging), false);
                    }
                    else
                    {
                        Ledgers.ComputeAging(patCur.Guarantor, DateTimeOD.Today, false);
                        if (PrefC.GetDate(PrefName.DateLastAging) != DateTime.Today)
                        {
                            Prefs.UpdateString(PrefName.DateLastAging, POut.Date(DateTime.Today, false));
                            //Since this is always called from UI, the above line works fine to keep the prefs cache current.
                        }
                    }
                }
            }
            #endregion
            try {
                File.WriteAllText(Path.Combine(Path.GetDirectoryName(xPath), "RecurringChargeResult.txt"), recurringResultFile);
            }
            catch { }             //Do nothing cause this is just for internal use.
            FillGrid();
            labelCharged.Text = Lan.g(this, "Charged=") + success;
            labelFailed.Text  = Lan.g(this, "Failed=") + failed;
            MsgBox.Show(this, "Done charging cards.\r\nIf there are any patients remaining in list, print the list and handle each one manually.");
        }