private void FormPayPlanCredits_Load(object sender, EventArgs e) { _listPatNums = new List <long>(); _listPatNums.Add(_patCur.PatNum); _listAdjustments = Adjustments.GetAdjustForPats(_listPatNums); _listProcs = Procedures.GetCompAndTpForPats(_listPatNums); List <PayPlan> listPayPlans = PayPlans.GetForPats(_listPatNums, _patCur.PatNum); //Used to figure out how much we need to pay off procs with, also contains insurance payplans. _listPayPlanCharges = new List <PayPlanCharge>(); if (listPayPlans.Count > 0) { //get all current payplan charges for plans already on the patient, excluding the current one. _listPayPlanCharges = PayPlanCharges.GetDueForPayPlans(listPayPlans, _patCur.PatNum) //Does not get charges for the future. .Where(x => !(x.PayPlanNum == _payPlanCur.PayPlanNum && x.ChargeType == PayPlanChargeType.Credit)).ToList(); //do not get credits for current payplan } List <PaySplit> tempListPaySplits = PaySplits.GetForPats(_listPatNums).Where(x => x.UnearnedType == 0).ToList(); //Might contain payplan payments. Do not include unearned. _listPaySplits = tempListPaySplits.FindAll(x => x.PayPlanNum == 0 || listPayPlans.Exists(y => y.PayPlanNum == x.PayPlanNum)); _listPayments = Payments.GetNonSplitForPats(_listPatNums); _listInsPayAsTotal = ClaimProcs.GetByTotForPats(_listPatNums); //Claimprocs paid as total, might contain ins payplan payments. _listClaimProcs = ClaimProcs.GetForProcs(_listProcs.Select(x => x.ProcNum).ToList()); textCode.Text = Lan.g(this, "None"); FillGrid(); if (!Security.IsAuthorized(Permissions.PayPlanEdit, true)) { this.DisableForm(butCancel, checkHideUnattached, checkShowImplicit, butPrint, gridMain); } }
/// <summary>Returns true if ClaimProcAllowCreditsGreaterThanProcFee preference allows the user to add credits greater than the proc fee. Otherwise returns false </summary> private bool isClaimProcGreaterThanProcFee() { ClaimProcCreditsGreaterThanProcFee creditsGreaterPref = (ClaimProcCreditsGreaterThanProcFee)PrefC.GetInt(PrefName.ClaimProcAllowCreditsGreaterThanProcFee); if (creditsGreaterPref == ClaimProcCreditsGreaterThanProcFee.Allow) { return(true); } List <Procedure> listProcs = Procedures.GetManyProc(ClaimProcsToEdit.Select(x => x.ProcNum).ToList(), false); List <ClaimProc> listClaimProcsForPat = ClaimProcs.Refresh(PatCur.PatNum); List <PaySplit> listPaySplitForSelectedCP = PaySplits.GetPaySplitsFromProcs(ClaimProcsToEdit.Select(x => x.ProcNum).ToList()); List <Adjustment> listAdjForSelectedCP = Adjustments.GetForProcs(ClaimProcsToEdit.Select(x => x.ProcNum).ToList()); bool isCreditGreater = false; List <string> listProcDescripts = new List <string>(); for (int i = 0; i < ClaimProcsToEdit.Length; i++) { ClaimProc claimProcCur = ClaimProcsToEdit[i]; int insPayIdx = gridMain.ListGridColumns.GetIndex(Lan.g("TableClaimProc", "Ins Pay")); int writeoffIdx = gridMain.ListGridColumns.GetIndex(Lan.g("TableClaimProc", "Writeoff")); int feeAcctIdx = gridMain.ListGridColumns.GetIndex(Lan.g("TableClaimProc", "Fee")); decimal insPayAmt = (decimal)ClaimProcs.ProcInsPay(listClaimProcsForPat.FindAll(x => x.ClaimProcNum != claimProcCur.ClaimProcNum), claimProcCur.ProcNum) + PIn.Decimal(gridMain.ListGridRows[i].Cells[insPayIdx].Text); decimal writeOff = (decimal)ClaimProcs.ProcWriteoff(listClaimProcsForPat.FindAll(x => x.ClaimProcNum != claimProcCur.ClaimProcNum), claimProcCur.ProcNum) + PIn.Decimal(gridMain.ListGridRows[i].Cells[writeoffIdx].Text); decimal feeAcct = PIn.Decimal(gridMain.ListGridRows[i].Cells[feeAcctIdx].Text); decimal adj = listAdjForSelectedCP.Where(x => x.ProcNum == claimProcCur.ProcNum).Select(x => (decimal)x.AdjAmt).Sum(); decimal patPayAmt = listPaySplitForSelectedCP.Where(x => x.ProcNum == claimProcCur.ProcNum).Select(x => (decimal)x.SplitAmt).Sum(); //Any changes to this calculation should also consider FormClaimProc.IsClaimProcGreaterThanProcFee(). decimal creditRem = feeAcct - patPayAmt - insPayAmt - writeOff + adj; isCreditGreater |= (creditRem.IsLessThanZero()); if (creditRem.IsLessThanZero()) { Procedure proc = listProcs.FirstOrDefault(x => x.ProcNum == claimProcCur.ProcNum); listProcDescripts.Add((proc == null ? "" : ProcedureCodes.GetProcCode(proc.CodeNum).ProcCode) + "\t" + Lan.g(this, "Fee") + ": " + feeAcct.ToString("F") + "\t" + Lan.g(this, "Credits") + ": " + (Math.Abs(-patPayAmt - insPayAmt - writeOff + adj)).ToString("F") + "\t" + Lan.g(this, "Remaining") + ": (" + Math.Abs(creditRem).ToString("F") + ")"); } } if (!isCreditGreater) { return(true); } if (creditsGreaterPref == ClaimProcCreditsGreaterThanProcFee.Block) { MsgBoxCopyPaste msgBox = new MsgBoxCopyPaste(Lan.g(this, "Remaining amount is negative for the following procedures") + ":\r\n" + string.Join("\r\n", listProcDescripts) + "\r\n" + Lan.g(this, "Not allowed to continue.")); msgBox.Text = Lan.g(this, "Overpaid Procedure Warning"); msgBox.ShowDialog(); return(false); } if (creditsGreaterPref == ClaimProcCreditsGreaterThanProcFee.Warn) { return(MessageBox.Show(Lan.g(this, "Remaining amount is negative for the following procedures") + ":\r\n" + string.Join("\r\n", listProcDescripts.Take(10)) + "\r\n" + (listProcDescripts.Count > 10?"...\r\n":"") + Lan.g(this, "Continue?") , Lan.g(this, "Overpaid Procedure Warning"), MessageBoxButtons.OKCancel) == DialogResult.OK); } return(true); //should never get to this line, only possible if another enum value is added to allow, warn, and block }
private void FormProcSelect_Load(object sender, System.EventArgs e) { if (_isMultiSelect) { gridMain.SelectionMode = OpenDental.UI.GridSelectionMode.MultiExtended; } _listSelectedProcs = new List <Procedure>(); _listProcedures = Procedures.GetCompleteForPats(new List <long> { _patNumCur }); _listAdjustments = Adjustments.GetAdjustForPats(new List <long> { _patNumCur }); _listPayPlanCharges = PayPlanCharges.GetDueForPayPlans(PayPlans.GetForPats(null, _patNumCur), _patNumCur).ToList(); //Does not get charges for the future. _listPaySplits = PaySplits.GetForPats(new List <long> { _patNumCur }); //Might contain payplan payments. _listInsPayAsTotal = ClaimProcs.GetByTotForPats(new List <long> { _patNumCur }); _listClaimProcs = ClaimProcs.GetForProcs(_listProcedures.Select(x => x.ProcNum).ToList()); labelUnallocated.Visible = _doShowUnallocatedLabel; if (PrefC.GetInt(PrefName.RigorousAdjustments) == (int)RigorousAdjustments.DontEnforce) { radioIncludeAllCredits.Checked = true; } else { radioOnlyAllocatedCredits.Checked = true; } FillGrid(); }
private void FindSplits(bool isShowAll) { ListSelectedSplits = new List <PaySplit>(); if (IsPrePay) { if (isShowAll) //this will find all splits for the family, even if they have already been allocated. { GetSplitsForPats(isShowAll); return; } if (ListUnallocatedSplits == null) { _listPaySplits = PaySplits.GetPrepayForFam(Patients.GetFamily(_patNum)); List <PaySplit> listSplitsForSplits = PaySplits.GetSplitsForPrepay(_listPaySplits); for (int i = _listPaySplits.Count - 1; i >= 0; i--) { PaySplit split = _listPaySplits[i]; List <PaySplit> listSplitsForSplit = listSplitsForSplits.FindAll(x => x.FSplitNum == split.SplitNum); decimal splitTotal = 0; foreach (PaySplits.PaySplitAssociated splitAssociated in ListPaySplitAssociated) { if (splitAssociated.PaySplitOrig.SplitNum == split.SplitNum && splitAssociated != SplitAssociatedDetached) { //add in amounts for assoications just made within payment window splitTotal += (decimal)splitAssociated.PaySplitLinked.SplitAmt; } } foreach (PaySplit paySplit in listSplitsForSplit) { if (paySplit.SplitNum == SplitNumCur) { continue; } splitTotal += (decimal)paySplit.SplitAmt; //add in amounts associated from previous payments } decimal leftOverAmt = (decimal)split.SplitAmt + splitTotal; //splitTotal should be negative. if (leftOverAmt <= 0) { _listPaySplits.Remove(split); } else { _dictLeftOverAmounts[split.SplitNum] = leftOverAmt; } } } else { _listPaySplits = ListUnallocatedSplits; } } else { GetSplitsForPats(isShowAll); } }
/// <summary>Get all splits for family. If show all is checked, find all that are positive prepayments even if already attached.</summary> private void GetSplitsForPats(bool isShowAll) { Family fam = Patients.GetFamily(_patNum); _listPaySplits = PaySplits.GetForPats(fam.ListPats.Select(x => x.PatNum).ToList()); if (isShowAll) { _listPaySplits = _listPaySplits.FindAll(x => x.UnearnedType != 0 && x.SplitAmt > 0); //don't show the automated negative offset } }
///<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])); } }
///<summary>This method converts the AccountEntry objects we get back from AccountModules.GetListUnpaidAccountCharges() to MultiAdjEntry objects. ///These are used to fill the grid and do all relevant logic in this form. This method will return a fresh list of procedures and will not show ///any existing adjustments that may have been made in this form already. Called on load and when checkShowImplicit is clicked. ///When called from checkShowImplicit any existing adjustments made will not be shown.(Ask Andrew about this functionality)</summary> private List <MultiAdjEntry> FillListGridEntries(CreditCalcType calc) { List <MultiAdjEntry> retVal = new List <MultiAdjEntry>(); #region Get required data List <Procedure> listProcedures = Procedures.GetCompleteForPats(new List <long> { _patCur.PatNum }); //Does not get charges for the future. List <PaySplit> listPaySplits = PaySplits.GetForPats(new List <long> { _patCur.PatNum }).Where(x => x.UnearnedType == 0).ToList(); List <Adjustment> listAdjustments = Adjustments.GetAdjustForPats(new List <long> { _patCur.PatNum }); //Might contain payplan payments. Do not include unearned. List <PayPlanCharge> listPayPlanCharges = PayPlanCharges.GetDueForPayPlans(PayPlans.GetForPats(null, _patCur.PatNum), _patCur.PatNum).ToList(); List <ClaimProc> listInsPayAsTotal = ClaimProcs.GetByTotForPats(new List <long> { _patCur.PatNum }); List <ClaimProc> listClaimProcs = ClaimProcs.GetForProcs(listProcedures.Select(x => x.ProcNum).ToList()); #endregion List <AccountEntry> listAccountCharges = AccountModules.GetListUnpaidAccountCharges(listProcedures, listAdjustments , listPaySplits, listClaimProcs, listPayPlanCharges, listInsPayAsTotal, calc, new List <PaySplit>()); List <AccountEntry> listAccountChargesIncludeAll = null; if (calc == CreditCalcType.ExcludeAll) { //We need to get all credits so that our AmtRemBefore can reflect what has truly been allocated to the procedure. listAccountChargesIncludeAll = AccountModules.GetListUnpaidAccountCharges(listProcedures, listAdjustments , listPaySplits, listClaimProcs, listPayPlanCharges, listInsPayAsTotal, CreditCalcType.IncludeAll, new List <PaySplit>()); } MultiAdjEntry multiEntry = null; foreach (AccountEntry entry in listAccountCharges) { //We only want AccountEntries that are completed procedures. if (entry.GetType() != typeof(ProcExtended)) { continue; } ProcExtended procEntry = (ProcExtended)entry.Tag; if ((_listSelectedProcs != null && _listSelectedProcs.Any(x => x.ProcNum == procEntry.Proc.ProcNum)) || //Allow selected procs to show if paid off entry.AmountEnd != 0) //All unpaid procedures should always show up per Nathan { double amtRemBefore = (double)(listAccountChargesIncludeAll?.FirstOrDefault(x => x.Tag.GetType() == typeof(ProcExtended) && ((ProcExtended)x.Tag).Proc.ProcNum == procEntry.Proc.ProcNum) ?? entry).AmountStart; multiEntry = new MultiAdjEntry(procEntry.Proc, (double)entry.AmountStart, (double)entry.AmountEnd, amtRemBefore); retVal.Add(multiEntry); } } OrderListGridEntries(); return(retVal); }
private void FormProcSelect_Load(object sender, System.EventArgs e) { if (_isMultiSelect) { gridMain.SelectionMode = OpenDental.UI.GridSelectionMode.MultiExtended; } _listSelectedProcs = new List <Procedure>(); _listProcedures = Procedures.GetCompleteForPats(new List <long> { _patNumCur }); if (ShowTpProcs) { _listProcedures.AddRange(Procedures.GetTpForPats(new List <long> { _patNumCur })); } _listAdjustments = Adjustments.GetAdjustForPats(new List <long> { _patNumCur }); _listPayPlanCharges = PayPlanCharges.GetDueForPayPlans(PayPlans.GetForPats(null, _patNumCur), _patNumCur).ToList(); //Does not get charges for the future. _listPaySplits = PaySplits.GetForPats(new List <long> { _patNumCur }); //Might contain payplan payments. foreach (PaySplit split in ListSplitsCur) { //If this is a new payment, its paysplits will not be in the database yet, so we need to add them manually. We might also need to set the //ProcNum on the pay split if it has changed and has not been saved to the database. PaySplit splitDb = _listPaySplits.FirstOrDefault(x => x.IsSame(split)); if (splitDb == null) { _listPaySplits.Add(split); } else { splitDb.ProcNum = split.ProcNum; } } _listInsPayAsTotal = ClaimProcs.GetByTotForPats(new List <long> { _patNumCur }); _listClaimProcs = ClaimProcs.GetForProcs(_listProcedures.Select(x => x.ProcNum).ToList()); labelUnallocated.Visible = _doShowUnallocatedLabel; if (PrefC.GetInt(PrefName.RigorousAdjustments) == (int)RigorousAdjustments.DontEnforce) { radioIncludeAllCredits.Checked = true; } else { radioOnlyAllocatedCredits.Checked = true; } FillGrid(); }
private void butOK_Click(object sender, System.EventArgs e) { if (textDate.errorProvider1.GetError(textDate) != "") { MsgBox.Show(this, "Please fix data entry errors first."); return; } if (SplitList.Count == 0) { MsgBox.Show(this, "Please enter transfers first."); return; } double total = 0; for (int i = 0; i < SplitList.Count; i++) { total += SplitList[i].SplitAmt; } if (total != 0) { MsgBox.Show(this, "Total must equal zero."); return; } PaymentCur.PayNote = textNote.Text; PaymentCur.PayDate = PIn.PDate(textDate.Text); PaymentCur.IsSplit = true; try { Payments.Update(PaymentCur); } catch (ApplicationException ex) { //this catches bad dates. MessageBox.Show(ex.Message); return; } //Set all DatePays the same. for (int i = 0; i < SplitList.Count; i++) { SplitList[i].DatePay = PaymentCur.PayDate; SplitList[i].ProcDate = PaymentCur.PayDate; } PaySplits.UpdateList(SplitListOld, SplitList); if (IsNew) { SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, 0, Lan.g(this, "Prov income transfer.")); } else { SecurityLogs.MakeLogEntry(Permissions.PaymentEdit, 0, Lan.g(this, "Prov income transfer.")); } DialogResult = DialogResult.OK; }
private void FindSplits(bool isShowAll) { ListSelectedSplits = new List <PaySplit>(); if (IsPrePay) { if (isShowAll) //this will find all splits for the family, even if they have already been allocated. { GetSplitsForPats(isShowAll); return; } if (ListUnallocatedSplits == null) { _listPaySplits = PaySplits.GetPrepayForFam(Patients.GetFamily(_patNum)); List <PaySplit> listSplitsForSplits = PaySplits.GetSplitsForPrepay(_listPaySplits); for (int i = _listPaySplits.Count - 1; i >= 0; i--) { PaySplit split = _listPaySplits[i]; List <PaySplit> listSplitsForSplit = listSplitsForSplits.FindAll(x => x.FSplitNum == split.SplitNum); decimal splitTotal = 0; foreach (PaySplit paySplit in listSplitsForSplit) { if (paySplit.SplitNum == SplitNumCur) { continue; } splitTotal += (decimal)paySplit.SplitAmt; } decimal leftOverAmt = (decimal)split.SplitAmt + splitTotal + AmtAllocated; //splitTotal should be negative. if (leftOverAmt <= 0) { _listPaySplits.Remove(split); } else { split.SplitAmt = (double)leftOverAmt; //This will cause the left over amount to show up in the grid. We don't do any saving in this form. } } } else { _listPaySplits = ListUnallocatedSplits; } } else { GetSplitsForPats(isShowAll); } }
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}."); }
private void FillGrid() { gridMain.BeginUpdate(); GridColumn col = new GridColumn("Date", 70); gridMain.ListGridColumns.Add(col); col = new GridColumn("Patient", 100); gridMain.ListGridColumns.Add(col); col = new GridColumn("Category", 80); gridMain.ListGridColumns.Add(col); col = new GridColumn("Total Cost", 80); gridMain.ListGridColumns.Add(col); col = new GridColumn("Balance", 80); gridMain.ListGridColumns.Add(col); col = new GridColumn("Due Now", 80); gridMain.ListGridColumns.Add(col); List <long> listPayPlanNums = _listValidPayPlans.Select(x => x.PayPlanNum).ToList(); List <PaySplit> listPaySplits = PaySplits.GetForPayPlans(listPayPlanNums); List <Patient> listPats = Patients.GetLimForPats(_listValidPayPlans.Select(x => x.PatNum).ToList()); for (int i = 0; i < _listValidPayPlans.Count; i++) { //no db calls are made in this loop because we have all the necessary information already. PayPlan planCur = _listValidPayPlans[i]; Patient patCur = listPats.Where(x => x.PatNum == planCur.PatNum).FirstOrDefault(); GridRow row = new GridRow(); row.Cells.Add(planCur.PayPlanDate.ToShortDateString()); //date row.Cells.Add(patCur.LName + ", " + patCur.FName); //patient if (planCur.PlanCategory == 0) { row.Cells.Add(Lan.g(this, "None")); } else { row.Cells.Add(Defs.GetDef(DefCat.PayPlanCategories, planCur.PlanCategory).ItemName); } row.Cells.Add(PayPlans.GetTotalCost(planCur.PayPlanNum, _listPayPlanCharges).ToString("F")); //total cost row.Cells.Add(PayPlans.GetBalance(planCur.PayPlanNum, _listPayPlanCharges, listPaySplits).ToString("F")); //balance row.Cells.Add(PayPlans.GetDueNow(planCur.PayPlanNum, _listPayPlanCharges, listPaySplits).ToString("F")); //due now row.Tag = planCur.PayPlanNum; gridMain.ListGridRows.Add(row); } gridMain.EndUpdate(); }
///<summary>Computes balance for a single patient without making any calls to the database. If the balance doesn't match the stored patient balance, then it makes one update to the database and returns true to trigger calculation of aging.</summary> public static bool ComputeBalances(Procedure[] procList, ClaimProc[] claimProcList, Patient PatCur, PaySplit[] paySplitList, Adjustment[] AdjustmentList, PayPlan[] payPlanList, PayPlanCharge[] payPlanChargeList) { //must have refreshed all 5 first double calcBal = Procedures.ComputeBal(procList) + ClaimProcs.ComputeBal(claimProcList) + Adjustments.ComputeBal(AdjustmentList) - PaySplits.ComputeBal(paySplitList) + PayPlans.ComputeBal(PatCur.PatNum, payPlanList, payPlanChargeList); if (calcBal != PatCur.EstBalance) { Patient PatOld = PatCur.Copy(); PatCur.EstBalance = calcBal; Patients.Update(PatCur, PatOld); return(true); } return(false); }
private void FormAdjustSelect_Load(object sender, EventArgs e) { List <Adjustment> listAdjusts = Adjustments.GetAdjustForPats(new List <long>() { PatNumCur }).FindAll(x => x.ProcNum == 0); //Get all unallocated adjustments for current pat. foreach (Adjustment adjust in listAdjusts) { _listAdjusts.Add(new AccountEntry(adjust)); } _listSplitsForAdjusts = PaySplits.GetForAdjustments(listAdjusts.Select(x => x.AdjNum).ToList()); foreach (AccountEntry entry in _listAdjusts) { //Figure out how much each adjustment has left, not counting this payment. entry.AmountStart -= (decimal)Adjustments.GetAmtAllocated(entry.PriKey, PayNumCur, _listSplitsForAdjusts.FindAll(x => x.AdjNum == entry.PriKey)); //Reduce adjustments based on current payment's splits as well (this is in-memory list, could be new, could be modified) but not the current split entry.AmountStart -= (decimal)Adjustments.GetAmtAllocated(entry.PriKey, 0, ListSplitsForPayment.FindAll(x => x.AdjNum == entry.PriKey && x != PaySplitCur)); } FillGrid(); }
private void FormProviderIncTrans_Load(object sender, EventArgs e) { if (IsNew) { if (!Security.IsAuthorized(Permissions.PaymentCreate)) { DialogResult = DialogResult.Cancel; return; } PaymentCur.DateEntry = DateTime.Now; } else { if (!Security.IsAuthorized(Permissions.PaymentEdit, PaymentCur.DateEntry)) { butOK.Enabled = false; butDeleteAll.Enabled = false; butAdd.Enabled = false; gridMain.Enabled = false; //butPay.Enabled=false; } } for (int i = 0; i < Providers.List.Length; i++) { comboProv.Items.Add(Providers.List[i].Abbr); } FamCur = Patients.GetFamily(PatNum); PatCur = FamCur.GetPatient(PatNum); tableBalances = Patients.GetPaymentStartingBalances(PatCur.Guarantor, PaymentCur.PayNum); textDateEntry.Text = PaymentCur.DateEntry.ToShortDateString(); textDate.Text = PaymentCur.PayDate.ToShortDateString(); textNote.Text = PaymentCur.PayNote; SplitList = PaySplits.GetForPayment(PaymentCur.PayNum); //Count might be 0 SplitListOld = new List <PaySplit>(); SplitListOld.AddRange(SplitList); FillMain(); }
private void butOK_Click(object sender, System.EventArgs e) { bool isDiscountPlanAdj = (Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "dp"); if (textAdjDate.errorProvider1.GetError(textAdjDate) != "" || textProcDate.errorProvider1.GetError(textProcDate) != "" || textAmount.errorProvider1.GetError(textAmount) != "") { MsgBox.Show(this, "Please fix data entry errors first."); return; } if (PIn.Date(textAdjDate.Text).Date > DateTime.Today.Date && !PrefC.GetBool(PrefName.FutureTransDatesAllowed)) { MsgBox.Show(this, "Adjustment date can not be in the future."); return; } if (textAmount.Text == "") { MessageBox.Show(Lan.g(this, "Please enter an amount.")); return; } if (!isDiscountPlanAdj && listTypeNeg.SelectedIndex == -1 && listTypePos.SelectedIndex == -1) { MsgBox.Show(this, "Please select a type first."); return; } if (IsNew && AvaTax.IsEnabled() && listTypePos.SelectedIndex > -1 && (_listAdjPosCats[listTypePos.SelectedIndex].DefNum == AvaTax.SalesTaxAdjType || _listAdjPosCats[listTypePos.SelectedIndex].DefNum == AvaTax.SalesTaxReturnAdjType) && !Security.IsAuthorized(Permissions.SalesTaxAdjEdit)) { return; } if (PrefC.GetInt(PrefName.RigorousAdjustments) == 0 && _adjustmentCur.ProcNum == 0) { MsgBox.Show(this, "You must attach a procedure to the adjustment."); return; } if (_adjRemAmt < 0) { if (!MsgBox.Show(this, MsgBoxButtons.OKCancel, "Remaining amount is negative. Continue?", "Overpaid Procedure Warning")) { return; } } bool changeAdjSplit = false; List <PaySplit> listPaySplitsForAdjust = new List <PaySplit>(); if (IsNew) { //prevents backdating of initial adjustment if (!Security.IsAuthorized(Permissions.AdjustmentCreate, PIn.Date(textAdjDate.Text), true)) //Give message later. { if (!_checkZeroAmount) //Let user create as long as Amount is zero and has edit zero permissions. This was checked on load. { MessageBox.Show(Lans.g("Security", "Not authorized for") + "\r\n" + GroupPermissions.GetDesc(Permissions.AdjustmentCreate)); return; } } } else { //Editing an old entry will already be blocked if the date was too old, and user will not be able to click OK button //This catches it if user changed the date to be older. if (!Security.IsAuthorized(Permissions.AdjustmentEdit, PIn.Date(textAdjDate.Text))) { return; } if (_adjustmentCur.ProvNum != comboProv.GetSelectedProvNum()) { listPaySplitsForAdjust = PaySplits.GetForAdjustments(new List <long>() { _adjustmentCur.AdjNum }); foreach (PaySplit paySplit in listPaySplitsForAdjust) { if (!Security.IsAuthorized(Permissions.PaymentEdit, Payments.GetPayment(paySplit.PayNum).PayDate)) { return; } if (comboProv.GetSelectedProvNum() != paySplit.ProvNum && PrefC.GetInt(PrefName.RigorousAccounting) == (int)RigorousAdjustments.EnforceFully) { changeAdjSplit = true; break; } } if (changeAdjSplit && !MsgBox.Show(this, MsgBoxButtons.OKCancel, "The provider for the associated payment splits will be changed to match the provider on the " + "adjustment.")) { return; } } } //DateEntry not allowed to change DateTime datePreviousChange = _adjustmentCur.SecDateTEdit; _adjustmentCur.AdjDate = PIn.Date(textAdjDate.Text); _adjustmentCur.ProcDate = PIn.Date(textProcDate.Text); _adjustmentCur.ProvNum = comboProv.GetSelectedProvNum(); _adjustmentCur.ClinicNum = comboClinic.SelectedClinicNum; if (listTypePos.SelectedIndex != -1) { _adjustmentCur.AdjType = _listAdjPosCats[listTypePos.SelectedIndex].DefNum; _adjustmentCur.AdjAmt = PIn.Double(textAmount.Text); } if (listTypeNeg.SelectedIndex != -1) { _adjustmentCur.AdjType = _listAdjNegCats[listTypeNeg.SelectedIndex].DefNum; _adjustmentCur.AdjAmt = -PIn.Double(textAmount.Text); } if (isDiscountPlanAdj) { //AdjustmentCur.AdjType is already set to a "discount plan" adj type. _adjustmentCur.AdjAmt = -PIn.Double(textAmount.Text); } if (_checkZeroAmount && _adjustmentCur.AdjAmt != 0) { MsgBox.Show(this, "Amount has to be 0.00 due to security permission."); return; } _adjustmentCur.AdjNote = textNote.Text; try{ if (IsNew) { Adjustments.Insert(_adjustmentCur); SecurityLogs.MakeLogEntry(Permissions.AdjustmentCreate, _adjustmentCur.PatNum, _patCur.GetNameLF() + ", " + _adjustmentCur.AdjAmt.ToString("c")); TsiTransLogs.CheckAndInsertLogsIfAdjTypeExcluded(_adjustmentCur, _isTsiAdj); } else { Adjustments.Update(_adjustmentCur); SecurityLogs.MakeLogEntry(Permissions.AdjustmentEdit, _adjustmentCur.PatNum, _patCur.GetNameLF() + ", " + _adjustmentCur.AdjAmt.ToString("c"), 0 , datePreviousChange); } } catch (Exception ex) { //even though it doesn't currently throw any exceptions MessageBox.Show(ex.Message); return; } if (changeAdjSplit) { PaySplits.UpdateProvForAdjust(_adjustmentCur, listPaySplitsForAdjust); } DialogResult = DialogResult.OK; }
/// <summary>Only used to void or refund transactions from PayConnectPortal. Creates new cloned payment and paysplits for the refund or void. /// Returns true if the transaction was successful, otherwise false.</summary public static bool VoidOrRefundPayConnectPortalTransaction(PayConnectResponseWeb pcResponseWeb, Payment payment, PayConnectService.transType transType, string refNum, decimal amount) { if (!transType.In(PayConnectService.transType.RETURN, PayConnectService.transType.VOID)) { return(false); } List <PaySplit> listPaySplits = PaySplits.GetForPayment(payment.PayNum); PayConnectService.creditCardRequest _payConnectRequest = new PayConnectService.creditCardRequest(); PayConnectResponse response = null; string receiptStr = ""; _payConnectRequest.TransType = transType; _payConnectRequest.RefNumber = refNum; _payConnectRequest.Amount = amount; PayConnectService.transResponse transResponse = PayConnect.ProcessCreditCard(_payConnectRequest, payment.ClinicNum, x => MsgBox.Show(x)); response = new PayConnectResponse(transResponse, _payConnectRequest); receiptStr = PayConnect.BuildReceiptString(_payConnectRequest, transResponse, null, payment.ClinicNum); if (response == null || response.StatusCode != "0") //error in transaction { return(false); } //Record a new payment for the voided transaction Payment clonePayment = payment.Clone(); clonePayment.PayAmt *= -1; //The negated amount of the original payment clonePayment.PayDate = DateTime.Now; clonePayment.Receipt = receiptStr; clonePayment.PayNote = Lan.g("PayConnectL", "Transaction Type") + ": " + Enum.GetName(typeof(PayConnectService.transType), transType) + Environment.NewLine + Lan.g("PayConnectL", "Status") + ": " + response.Description + Environment.NewLine + Lan.g("PayConnectL", "Amount") + ": " + clonePayment.PayAmt + Environment.NewLine + Lan.g("PayConnectL", "Auth Code") + ": " + response.AuthCode + Environment.NewLine + Lan.g("PayConnectL", "Ref Number") + ": " + response.RefNumber; clonePayment.PaymentSource = pcResponseWeb.CCSource; clonePayment.ProcessStatus = ProcessStat.OfficeProcessed; clonePayment.PayNum = Payments.Insert(clonePayment); List <PaySplit> listClonedPaySplits = new List <PaySplit>(); foreach (PaySplit paySplit in listPaySplits) { PaySplit copy = paySplit.Copy(); copy.SplitAmt *= -1; copy.PayNum = clonePayment.PayNum; copy.DatePay = clonePayment.PayDate; listClonedPaySplits.Add(copy); } PaySplits.InsertMany(listClonedPaySplits); PayConnectResponseWeb newPCResponseWeb = new PayConnectResponseWeb() { PatNum = payment.PatNum, PayNum = clonePayment.PayNum, CCSource = pcResponseWeb.CCSource, Amount = clonePayment.PayAmt, PayNote = Lan.g("PayConnectL", clonePayment.PayNote + Environment.NewLine + "From within Open Dental Proper."), ProcessingStatus = PayConnectWebStatus.Completed, DateTimeEntry = DateTime.Now, DateTimeCompleted = DateTime.Now, IsTokenSaved = false, RefNumber = transResponse.RefNumber, TransType = transType, PaymentToken = pcResponseWeb.PaymentToken, }; PayConnectResponseWebs.Insert(newPCResponseWeb); SecurityLogs.MakeLogEntry(Permissions.PaymentCreate, clonePayment.PatNum, Patients.GetLim(clonePayment.PatNum).GetNameLF() + ", " + clonePayment.PayAmt.ToString("c")); return(true); }
///<summary>Checks to see if the appointments provider has at least one mismatch provider on all the completed procedures attached to the appointment. ///If so, checks to see if the user has permission to edit a completed procedure. If the user does, then the user has the option to change the provider to match.</summary> public static bool DoRemoveCompletedProcs(Appointment apt, List <Procedure> listProcsForAppt, bool checkForAllProcCompl = false) { if (listProcsForAppt.Count == 0) { return(false); } if (checkForAllProcCompl && (apt.AptStatus != ApptStatus.Complete || listProcsForAppt.All(x => x.ProcStatus == ProcStat.C))) { return(false); } List <Procedure> listCompletedProcWithDifferentProv = new List <Procedure>(); foreach (Procedure proc in listProcsForAppt) { if (proc.ProcStatus != ProcStat.C) //should all be complete already. { continue; } ProcedureCode procCode = ProcedureCodes.GetProcCode(proc.CodeNum); long provNum = Procedures.GetProvNumFromAppointment(apt, proc, procCode); if (provNum != proc.ProvNum) { listCompletedProcWithDifferentProv.Add(proc); } } if (listCompletedProcWithDifferentProv.Count == 0) { return(false); //no completed procedures or prov changed. } List <PaySplit> listPaySplit = PaySplits.GetPaySplitsFromProcs(listCompletedProcWithDifferentProv.Select(x => x.ProcNum).ToList()); if (listPaySplit.Count > 0) { MsgBox.Show("Procedures", "The appointment provider does not match the provider on at least one completed procedure.\r\n" + "The procedure provider cannot be changed to match the appointment provider because the paysplit provider would no longer match. " + "Any change to the provider on the completed procedure(s) or paysplit(s) will have to be made manually."); return(true); //paysplits exist on one of the completed procedures. Per Nathan, don't change the provider. User will need to change manually. } foreach (Procedure proc in listCompletedProcWithDifferentProv) { Permissions perm = Permissions.ProcComplEdit; if (proc.ProcStatus.In(ProcStat.EC, ProcStat.EO)) { perm = Permissions.ProcExistingEdit; } if (Security.IsGlobalDateLock(perm, proc.ProcDate)) { return(true); } if (!Security.IsAuthorized(perm, proc.ProcDate, true, true)) { MessageBox.Show(Lan.g("Procedures", "The appointment provider does not match the provider on at least one completed procedure.") + "\r\n" + Lans.g("Procedures", "Not authorized for") + ": " + GroupPermissions.GetDesc(perm) + "\r\n" + Lan.g("Procedures", "Any change to the provider on the completed procedure(s) will have to be made manually.")); return(true); //user does not have permission to change the provider. Don't change provider. } } //The appointment is set complete, completed procedures exist, and provider does not match appointment. //Ask if they would like to change the providers on the completed procedure to match the appointments provider if (!MsgBox.Show("Procedures", MsgBoxButtons.YesNo, "The appointment provider does not match the provider on at least one completed procedure.\r\n" + "Change the provider on the completed procedure(s) to match the provider on the appointment?")) { return(true); //user does not want to change the providers } //user wants to change the provider on the completed procedure return(false); }
///<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>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>This method converts the AccountEntry objects we get back from AccountModules.GetListUnpaidAccountCharges() to MultiAdjEntry objects. ///These are used to fill the grid and do all relevant logic in this form. This method will return a fresh list of procedures and will not show ///any existing adjustments that may have been made in this form already. Called on load and when checkShowImplicit is clicked. ///When called from checkShowImplicit any existing adjustments made will not be shown.(Ask Andrew about this functionality)</summary> private List <MultiAdjEntry> FillListGridEntries(CreditCalcType calc) { //CONSIDER ANY CHANGES MADE HERE MAY ALSO NEED TO BE ADDED TO FormProcSelect. List <MultiAdjEntry> retVal = new List <MultiAdjEntry>(); #region Get required data List <Procedure> listProcedures = Procedures.GetCompleteForPats(new List <long> { _patCur.PatNum }); //Does not get charges for the future. List <PaySplit> listPaySplits = PaySplits.GetForPats(new List <long> { _patCur.PatNum }).Where(x => x.UnearnedType == 0).ToList(); List <Adjustment> listAdjustments = Adjustments.GetAdjustForPats(new List <long> { _patCur.PatNum }); //Might contain payplan payments. Do not include unearned. List <PayPlanCharge> listPayPlanCharges = PayPlanCharges.GetDueForPayPlans(PayPlans.GetForPats(null, _patCur.PatNum), _patCur.PatNum).ToList(); List <ClaimProc> listInsPayAsTotal = ClaimProcs.GetByTotForPats(new List <long> { _patCur.PatNum }); List <ClaimProc> listClaimProcs = ClaimProcs.GetForProcs(listProcedures.Select(x => x.ProcNum).ToList()); #endregion List <AccountEntry> listAccountCharges = AccountModules.GetListUnpaidAccountCharges(listProcedures, listAdjustments, listPaySplits, listClaimProcs , listPayPlanCharges, listInsPayAsTotal, calc, new List <PaySplit>()); List <AccountEntry> listAccountChargesIncludeAll = null; if (calc == CreditCalcType.ExcludeAll) { //We need to get all credits so that our AmtRemBefore can reflect what has truly been allocated to the procedure. listAccountChargesIncludeAll = AccountModules.GetListUnpaidAccountCharges(listProcedures, listAdjustments , listPaySplits, listClaimProcs, listPayPlanCharges, listInsPayAsTotal, CreditCalcType.IncludeAll, new List <PaySplit>()); } MultiAdjEntry multiEntry = null; foreach (AccountEntry entry in listAccountCharges) { //We only want AccountEntries that are completed procedures. if (entry.GetType() != typeof(ProcExtended)) { continue; } ProcExtended procEntry = (ProcExtended)entry.Tag; bool isProcSelectedInAccount = false; if (_listSelectedProcs != null && _listSelectedProcs.Any(x => x.ProcNum == procEntry.Proc.ProcNum)) { isProcSelectedInAccount = true; } if (calc == CreditCalcType.ExcludeAll) //show everything. Regardless of procs loading window or amount remaining. { double amtRemBefore = (double)(listAccountChargesIncludeAll?.FirstOrDefault(x => x.Tag.GetType() == typeof(ProcExtended) && ((ProcExtended)x.Tag).Proc.ProcNum == procEntry.Proc.ProcNum) ?? entry).AmountStart; multiEntry = new MultiAdjEntry(procEntry.Proc, (double)entry.AmountStart, (double)entry.AmountEnd, amtRemBefore); retVal.Add(multiEntry); } else if (calc.In(CreditCalcType.IncludeAll, CreditCalcType.AllocatedOnly) && (entry.AmountEnd != 0 || isProcSelectedInAccount)) { //Unpaid procedures should always show per Nathan. If proc was specifically selected before entering window, show it anyways. multiEntry = new MultiAdjEntry(procEntry.Proc, (double)entry.AmountStart, (double)entry.AmountEnd, (double)entry.AmountStart); retVal.Add(multiEntry); } } OrderListGridEntries(); return(retVal); }
///<summary>Transfers all family balance to the guarantor, balancing the account to the best of our ability. ///Returns a log of what was moved from the family member(s) of the selected guarantor(s)</summary> private string TransferToGuarantor() { string summaryText = ""; //Iterate through every family. foreach (FamilyAccount famAccountCur in _dictCurrentFamilyBatch.Select(x => x.Value)) { double totalTransferAmount = 0; long provNum = famAccountCur.Guarantor.PriProv; Payment payCur = CreatePaymentTransferHelper(famAccountCur.Guarantor); List <PaySplit> listPaySplitsCreated = new List <PaySplit>(); #region Family PaySplits string logText = ""; foreach (Patient pat in famAccountCur.ListFamilyMembers) //Make a counteracting split for each patient. //Don't make a split for the Guarantor yet. { if (pat.PatNum == famAccountCur.Guarantor.PatNum) { continue; } //Check the family member's balance and skip if it is $0.00. double splitAmt = (double)famAccountCur.Account.ListAccountCharges.Where(x => x.PatNum == pat.PatNum).Sum(x => x.AmountEnd); if (splitAmt == 0) { continue; } PaySplit paySplit = new PaySplit(); paySplit.DatePay = datePicker.Value; paySplit.PatNum = pat.PatNum; paySplit.PayNum = payCur.PayNum; paySplit.ProvNum = pat.PriProv; paySplit.ClinicNum = payCur.ClinicNum; //Since we're transferring all family member balances to the guarantor, we set the split amount to their balance. paySplit.SplitAmt = splitAmt; totalTransferAmount += paySplit.SplitAmt; listPaySplitsCreated.Add(paySplit); if (logText != "") { logText += ", "; } logText += paySplit.SplitAmt.ToString("f"); } #endregion Family PaySplits if (listPaySplitsCreated.Count == 0) { //No splits were made, delete payment and skip guarantor. Payments.Delete(payCur); continue; } //Since we skipped the guarantor before, we make one for them now. PaySplit splitGuarantor = new PaySplit(); splitGuarantor.DatePay = datePicker.Value; splitGuarantor.PatNum = famAccountCur.Guarantor.PatNum; splitGuarantor.PayNum = payCur.PayNum; splitGuarantor.ProvNum = provNum; splitGuarantor.ClinicNum = payCur.ClinicNum; splitGuarantor.SplitAmt = 0 - totalTransferAmount; //Split is the opposite amount of the total of the other splits. if (splitGuarantor.SplitAmt != 0) { listPaySplitsCreated.Add(splitGuarantor); } //Insert paysplits all at once for speed. PaySplits.InsertMany(listPaySplitsCreated); List <Patient> listTransferredPats = famAccountCur.ListFamilyMembers.FindAll(x => x.PatNum != famAccountCur.Guarantor.PatNum && listPaySplitsCreated.Select(y => y.PatNum).ToList().Contains(x.PatNum)); payCur.PayNote = "Auto-created by Family Balancer tool " + MiscData.GetNowDateTime().ToString("MM/dd/yyyy") + "\r\n" + "Allocated " + logText + $" transfers from family member{(famAccountCur.ListFamilyMembers.Count>1 ? "s " : " ")}" + string.Join(", ", listTransferredPats.Select(x => x.FName).ToList()) + " to guarantor " + famAccountCur.Guarantor.FName + "."; //Shown after all family members have been processed. summaryText += "PatNum(s):" + string.Join(", ", listTransferredPats.Select(x => x.PatNum).ToList()) + " moved to guarantor: " + famAccountCur.Guarantor.PatNum + "; Amount(s): " + logText + "\r\n"; Payments.Update(payCur, true); } return(summaryText); }
private void FormAdjust_Load(object sender, System.EventArgs e) { if (AvaTax.IsEnabled()) { //We do not want to allow the user to make edits or delete SalesTax and SalesTaxReturn Adjustments. Popup if no permission so user knows why disabled. if (AvaTax.IsEnabled() && (_adjustmentCur.AdjType == AvaTax.SalesTaxAdjType || _adjustmentCur.AdjType == AvaTax.SalesTaxReturnAdjType) && !Security.IsAuthorized(Permissions.SalesTaxAdjEdit)) { DisableForm(textNote, butCancel); textNote.ReadOnly = true; //This will allow the user to copy the note if desired. } } if (IsNew) { if (!Security.IsAuthorized(Permissions.AdjustmentCreate, true)) //Date not checked here. Message will show later. { if (!Security.IsAuthorized(Permissions.AdjustmentEditZero, true)) //Let user create an adjustment of zero if they have this perm. { MessageBox.Show(Lans.g("Security", "Not authorized for") + "\r\n" + GroupPermissions.GetDesc(Permissions.AdjustmentCreate)); DialogResult = DialogResult.Cancel; return; } //Make sure amount is 0 after OK click. _checkZeroAmount = true; } } else { if (!Security.IsAuthorized(Permissions.AdjustmentEdit, _adjustmentCur.AdjDate)) { butOK.Enabled = false; butDelete.Enabled = false; //User can't edit but has edit zero amount perm. Allow delete only if date is today. if (Security.IsAuthorized(Permissions.AdjustmentEditZero, true) && _adjustmentCur.AdjAmt == 0 && _adjustmentCur.DateEntry.Date == MiscData.GetNowDateTime().Date) { butDelete.Enabled = true; } } bool isAttachedToPayPlan = PayPlanLinks.GetForFKeyAndLinkType(_adjustmentCur.AdjNum, PayPlanLinkType.Adjustment).Count > 0; _listSplitsForAdjustment = PaySplits.GetForAdjustments(new List <long>() { _adjustmentCur.AdjNum }); if (_listSplitsForAdjustment.Count > 0 || isAttachedToPayPlan) { butAttachProc.Enabled = false; butDetachProc.Enabled = false; labelProcDisabled.Visible = true; } //Do not let the user change the adjustment type if the current adjustment is a "discount plan" adjustment type. if (Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "dp") { labelAdditions.Text = Lan.g(this, "Discount Plan") + ": " + Defs.GetName(DefCat.AdjTypes, _adjustmentCur.AdjType); labelSubtractions.Visible = false; listTypePos.Visible = false; listTypeNeg.Visible = false; } } textDateEntry.Text = _adjustmentCur.DateEntry.ToShortDateString(); textAdjDate.Text = _adjustmentCur.AdjDate.ToShortDateString(); textProcDate.Text = _adjustmentCur.ProcDate.ToShortDateString(); if (Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "+") //pos { textAmount.Text = _adjustmentCur.AdjAmt.ToString("F"); } else if (Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "-") //neg { textAmount.Text = (-_adjustmentCur.AdjAmt).ToString("F"); //shows without the neg sign } else if (Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "dp") //Discount Plan (neg) { textAmount.Text = (-_adjustmentCur.AdjAmt).ToString("F"); //shows without the neg sign } comboClinic.SelectedClinicNum = _adjustmentCur.ClinicNum; comboProv.SetSelectedProvNum(_adjustmentCur.ProvNum); FillComboProv(); if (_adjustmentCur.ProcNum != 0 && PrefC.GetInt(PrefName.RigorousAdjustments) == (int)RigorousAdjustments.EnforceFully) { comboProv.Enabled = false; butPickProv.Enabled = false; comboClinic.Enabled = false; if (Security.IsAuthorized(Permissions.Setup, true)) { labelEditAnyway.Visible = true; butEditAnyway.Visible = true; } } //prevents FillProcedure from being called too many times. Event handlers hooked back up after the lists are filled. listTypeNeg.SelectedIndexChanged -= listTypeNeg_SelectedIndexChanged; listTypePos.SelectedIndexChanged -= listTypePos_SelectedIndexChanged; List <Def> adjCat = Defs.GetDefsForCategory(DefCat.AdjTypes, true); //Positive adjustment types _listAdjPosCats = adjCat.FindAll(x => x.ItemValue == "+"); _listAdjPosCats.ForEach(x => listTypePos.Items.Add(x.ItemName)); listTypePos.SelectedIndex = _listAdjPosCats.FindIndex(x => x.DefNum == _adjustmentCur.AdjType); //can be -1 //Negative adjustment types _listAdjNegCats = adjCat.FindAll(x => x.ItemValue == "-"); _listAdjNegCats.ForEach(x => listTypeNeg.Items.Add(x.ItemName)); listTypeNeg.SelectedIndex = _listAdjNegCats.FindIndex(x => x.DefNum == _adjustmentCur.AdjType);//can be -1 listTypeNeg.SelectedIndexChanged += listTypeNeg_SelectedIndexChanged; listTypePos.SelectedIndexChanged += listTypePos_SelectedIndexChanged; FillProcedure(); textNote.Text = _adjustmentCur.AdjNote; }
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."); }
private void FillProcedure() { if (_adjustmentCur.ProcNum == 0) { textProcDate2.Text = ""; textProcProv.Text = ""; textProcTooth.Text = ""; textProcDescription.Text = ""; textProcFee.Text = ""; textProcWriteoff.Text = ""; textProcInsPaid.Text = ""; textProcInsEst.Text = ""; textProcAdj.Text = ""; textProcPatPaid.Text = ""; textProcAdjCur.Text = ""; labelProcRemain.Text = ""; _adjRemAmt = 0; return; } Procedure procCur = Procedures.GetOneProc(_adjustmentCur.ProcNum, false); List <ClaimProc> listClaimProcs = ClaimProcs.Refresh(procCur.PatNum); List <Adjustment> listAdjustments = Adjustments.Refresh(procCur.PatNum) .Where(x => x.ProcNum == procCur.ProcNum && x.AdjNum != _adjustmentCur.AdjNum).ToList(); textProcDate.Text = procCur.ProcDate.ToShortDateString(); textProcDate2.Text = procCur.ProcDate.ToShortDateString(); textProcProv.Text = Providers.GetAbbr(procCur.ProvNum); textProcTooth.Text = Tooth.ToInternat(procCur.ToothNum); textProcDescription.Text = ProcedureCodes.GetProcCode(procCur.CodeNum).Descript; double procWO = -ClaimProcs.ProcWriteoff(listClaimProcs, procCur.ProcNum); double procInsPaid = -ClaimProcs.ProcInsPay(listClaimProcs, procCur.ProcNum); double procInsEst = -ClaimProcs.ProcEstNotReceived(listClaimProcs, procCur.ProcNum); double procAdj = listAdjustments.Sum(x => x.AdjAmt); double procPatPaid = -PaySplits.GetTotForProc(procCur); textProcFee.Text = procCur.ProcFeeTotal.ToString("F"); textProcWriteoff.Text = procWO == 0?"":procWO.ToString("F"); textProcInsPaid.Text = procInsPaid == 0?"":procInsPaid.ToString("F"); textProcInsEst.Text = procInsEst == 0?"":procInsEst.ToString("F"); textProcAdj.Text = procAdj == 0?"":procAdj.ToString("F"); textProcPatPaid.Text = procPatPaid == 0?"":procPatPaid.ToString("F"); //Intelligently sum the values above based on statuses instead of blindly adding all of the values together. //The remaining amount is typically called the "patient portion" so utilze the centralized method that gets the patient portion. decimal patPort = ClaimProcs.GetPatPortion(procCur, listClaimProcs, listAdjustments); double procAdjCur = 0; if (textAmount.errorProvider1.GetError(textAmount) == "") { if (listTypePos.SelectedIndex > -1) //pos { procAdjCur = PIn.Double(textAmount.Text); } else if (listTypeNeg.SelectedIndex > -1 || Defs.GetValue(DefCat.AdjTypes, _adjustmentCur.AdjType) == "dp") //neg or discount plan { procAdjCur = -PIn.Double(textAmount.Text); } } textProcAdjCur.Text = procAdjCur == 0?"":procAdjCur.ToString("F"); //Add the current adjustment amount to the patient portion which will give the newly calculated remaining amount. _adjRemAmt = (decimal)procAdjCur + (decimal)procPatPaid + patPort; labelProcRemain.Text = _adjRemAmt.ToString("c"); }
///<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 FillProcedure() { if (PaySplitCur.ProcNum == 0) { textProcDate2.Text = ""; textProcProv.Text = ""; textProcTooth.Text = ""; textProcDescription.Text = ""; ProcFee = 0; textProcFee.Text = ""; ProcInsPaid = 0; textProcInsPaid.Text = ""; ProcInsEst = 0; textProcInsEst.Text = ""; ProcAdj = 0; textProcAdj.Text = ""; ProcPrevPaid = 0; textProcPrevPaid.Text = ""; ProcPaidHere = 0; textProcPaidHere.Text = ""; labelProcRemain.Text = ""; //butAttach.Enabled=true; //butDetach.Enabled=false; //ComputeProcTotals(); return; } Procedure ProcCur = Procedures.GetOneProc(PaySplitCur.ProcNum, false); ClaimProc[] ClaimProcList = ClaimProcs.Refresh(ProcCur.PatNum); Adjustment[] AdjustmentList = Adjustments.Refresh(ProcCur.PatNum); PaySplit[] PaySplitList = PaySplits.Refresh(ProcCur.PatNum); textProcDate.Text = ProcCur.ProcDate.ToShortDateString(); textProcDate2.Text = ProcCur.ProcDate.ToShortDateString(); textProcProv.Text = Providers.GetAbbr(ProcCur.ProvNum); textProcTooth.Text = Tooth.ToInternat(ProcCur.ToothNum); textProcDescription.Text = ProcedureCodes.GetProcCode(ProcCur.ADACode).Descript; ProcFee = ProcCur.ProcFee; ProcInsPaid = -ClaimProcs.ProcInsPay(ClaimProcList, ProcCur.ProcNum); ProcInsEst = -ClaimProcs.ProcEstNotReceived(ClaimProcList, ProcCur.ProcNum); ProcAdj = Adjustments.GetTotForProc(ProcCur.ProcNum, AdjustmentList); //next line will still work even if IsNew ProcPrevPaid = -PaySplits.GetTotForProc(ProcCur.ProcNum, PaySplitList, PaySplitCur.SplitNum); textProcFee.Text = ProcFee.ToString("F"); if (ProcInsPaid == 0) { textProcInsPaid.Text = ""; } else { textProcInsPaid.Text = ProcInsPaid.ToString("F"); } if (ProcInsEst == 0) { textProcInsEst.Text = ""; } else { textProcInsEst.Text = ProcInsEst.ToString("F"); } if (ProcAdj == 0) { textProcAdj.Text = ""; } else { textProcAdj.Text = ProcAdj.ToString("F"); } if (ProcPrevPaid == 0) { textProcPrevPaid.Text = ""; } else { textProcPrevPaid.Text = ProcPrevPaid.ToString("F"); } ComputeProcTotals(); //butAttach.Enabled=false; //butDetach.Enabled=true; }