///<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}."); }
///<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(); }
///<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(); }
///<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."); }