public PP_LedgerItem(PP_LedgerItem DLI) { PP_LedgerItem CloneCopy = (PP_LedgerItem)DLI.MemberwiseClone(); SetConstructorValues(CloneCopy.m_LedgerItemType, CloneCopy.m_Guarantor, CloneCopy.m_PatNum, CloneCopy.m_ProvNum, CloneCopy.m_ProcNum, CloneCopy.m_ItemDate, CloneCopy.m_ItemAmt, CloneCopy.m_LedgerItemNum, CloneCopy.m_TableSource); }
/* * /// <summary> * /// Splits the current ledger item up into an array of items with different * /// payments and providers. Each string in parameters should be of the form * /// xxx,yyyy * /// xxx= provider num * /// yyyy = amount * /// Throws exception if 'this' is not a payment or writeoff type. * /// </summary> * /// <param name="Parameters"></param> * /// <returns></returns> * public PP_LedgerItem[] SplitPaymentWriteOffItem(string[] Parameters) * { * // bool b1 = this.m_LedgerItemType != LedgerItemTypes.NegAdjustment; * // bool b2 = this.m_LedgerItemType != LedgerItemTypes.Payment; * // bool b3 = b1 || b2; * if (!(this.m_LedgerItemType != LedgerItemTypes.NegAdjustment || this.m_LedgerItemType != LedgerItemTypes.Payment)) * throw new Exception("SplitPaymentWriteoffItem in DansLedger called \nfrom an object not of Payment or writeoff type!"); * if (Parameters[0] == "") * return null; * List<PP_LedgerItem> rList = new List<PP_LedgerItem>(); * PP_LedgerItem temp = null; * ushort CurrentProvider = UInt16.Parse(Parameters[0].Split(',')[0]); * //decimal runningBalance = 0; * for (int i = 0; i < Parameters.Length; i++) * { * string[] ProvAmount = Parameters[i].Split(','); * ushort Prov = UInt16.Parse(ProvAmount[0]); * decimal ammount = Decimal.Parse(ProvAmount[1]); * if (temp == null) * { * temp = new PP_LedgerItem(this.ITEMTYPE, this.GUARANTOR, this.PATNUM, * Prov, this.PROCNUM, this.ITEMDATE, 0, 0); * rList.Add(temp); * } * if (temp.PROVNUM == Prov) * { * temp.AMMOUNT += ammount; * temp.ALLOCATED_AMMOUNT += ammount; * * } * if (Prov != temp.PROVNUM) * { * temp.ALLOCATION_STRING = temp.PROVNUM.ToString() + "," + temp.AMMOUNT; * temp.m_IsAllocated = true; * temp.ALLOCATED_AMMOUNT = temp.m_AmtAllocated; * * temp = new PP_LedgerItem(this.ITEMTYPE, this.GUARANTOR, this.PATNUM, * Prov, this.PROCNUM, this.ITEMDATE, ammount, 0); * rList.Add(temp); * } * * temp.ALLOCATION_STRING = temp.PROVNUM.ToString() + "," + temp.AMMOUNT; * temp.m_IsAllocated = true; * // temp.AmtAllocated += -37; * // rList.Add(temp); * } * return rList.ToArray(); * * * } */ #endregion #endregion #region IComparable Members /// <summary> /// Returns -1 if Date of this is sooner than obj, 1 if later /// If Dates are the same then returns -1,0,1 by giving -1 /// to the first items in this order Charge->Adjustment->Payment /// if ItemTypes are the same 0 is returned /// /// </summary> public int CompareTo(object obj) { /* elected to elimiate this item and let the system throw the exception * because this method will get called a lot and * I want to reduce overhead. * if (!(obj is DansLedgerItem)) * throw new Exception("Obj passed to DansLedgerItem.CompareTo() is not a DansLedgerItem"); */ PP_LedgerItem item2 = (PP_LedgerItem)obj; int rValue = this.ITEMDATE.CompareTo(item2.ITEMDATE); if (rValue == 0) { switch (this.ITEMTYPE) { case LedgerItemTypes.PosAdjustment: if (item2.ITEMTYPE == LedgerItemTypes.PosAdjustment) { return(0); } else if (item2.ITEMTYPE == LedgerItemTypes.Charge) { return(1); } else { return(-1); } //break; case LedgerItemTypes.Charge: if (item2.ITEMTYPE == LedgerItemTypes.Charge) { rValue = 0; } else { rValue = 1; // Charge should always come first. So compared object should compare bigger. } break; case LedgerItemTypes.Payment: if (item2.ITEMTYPE == LedgerItemTypes.Payment) { rValue = 0; } else { rValue = 1; // Payments always last } break; case LedgerItemTypes.NegAdjustment: if (item2.ITEMTYPE == LedgerItemTypes.NegAdjustment) { rValue = 0; } else if (item2.ITEMTYPE == LedgerItemTypes.Charge || item2.ITEMTYPE == LedgerItemTypes.PosAdjustment) { rValue = 1; //ie this Should appear later then item2 } else // item2.ITEMTYPE == LedgerItemType.Payment { rValue = -1; } break; default: break; } } // if (rValue == 0) return(rValue); } //public int CompareTo(object obj)
/// <summary> /// Fill the Ledger Object with data from the database. /// Flushes old data in this object first /// If you are running lots of data you may want to control the GC /// to collect when you want. /// /// /// /// Method Roadmap: /// /// 1. Generate 2 Tables /// i. Table1: dt_ODItems Contains all the items of Ledger Interest from OpenDental Tables /// ii. Table2: dt2_AllocTbl_Items Contains all the Allocation Data that exists in the allocation_provider table /// 2. Look For Differences /// i. Build a Ledger Item from the dt_ODItems (ie from the opendental tables) /// ii. Find the Splits in dt2_AllocTbl_Items that are associated with the dt_ODItems that are payments /// a. For Payment items (in dt_ODItems)Find he dt2_AllocTbl_Items that have the same PayTableSource and PaySourceNumber /// b. Make a split [] out of the dt2_AllocTbl_Items that were found /// iii. Add LI and Splits to MasterList (this) with call to AddToCollection(LI,splits); /// a. The method will determine if LI is a payment and if splits are not null create a PP_PaymentItem and attach the splits to this PP_PaymentItem and then add the PP_PaymentItem to the master list /// b. Adding PaySplits needs to calculate the allocated ammount /// /// Need to determine what to do if an item exists in the allocation_provider (ie dt2_AllocTble_Items) table but not in the /// opendental data (ie dt_ODItems) // use Update2() to Clear these entries from data that /// has been deleted from OD but not from allocation_provider. /// </summary> private bool _Fill(bool Suppress_GC_Collection) { //PU.Ex = "Not Implemented Yet Flag 6"; Flush(Suppress_GC_Collection); bool rValSuccess = true; try { // QueryResult qr = QueryResult.RunQuery(PP_ODSQLPullStrings_LedgerItem.PullAll_FromOD(m_GuarantorNumber, false)); DataTable dt_ODItems = Db.GetTableOld(PP_ODSQLPullStrings_LedgerItem.PullAll_FromOD(m_GuarantorNumber,false)); string cmd2 = "SELECT PayTableSource, PaySourceNum, " //0 ,1 + " IsFullyAllocated, Amount, ProvNum, " // 2, 3, 4 + " AllocToTableSource, AllocToSourceNum FROM " // 5, 6 + MyAllocator1_ProviderPayment.TABLENAME + "\nWHERE Guarantor = " + this.GUARANTOR_NUMBER; DataTable dt2_AllocTbl_Items = Db.GetTableOld(cmd2); if(dt_ODItems.Rows.Count != 0 && dt_ODItems.Columns.Count != 0) { #if DEBUG // just check to see if columns line up. PP_ODSQLPullStrings_LedgerItem.SetColumnNames(ref dt_ODItems); #endif for(int i = 0;i < dt_ODItems.Rows.Count;i++) { //public enum HeaderOfQueryEnum // { LedgerItemType, Guarantor, PatNum, // ProvNum, ItemDate, Ammount, // ItemNum, TableSource }; DataRow dr = dt_ODItems.Rows[i]; LedgerItemTypes type = (LedgerItemTypes)Int32.Parse(dr[0].ToString()); uint uiGuarantor = uint.Parse(dr[1].ToString()); uint uiPatnum = uint.Parse(dr[2].ToString()); ; ushort usProvNum = ushort.Parse(dr[3].ToString()); DateTime ItemDate = (DateTime)dr[4]; // need to make sure this doesn't crash here. Decimal dAmmount = Decimal.Parse(dr[5].ToString()); uint uiItemNum = uint.Parse(dr[6].ToString()); ; MyAllocator1.ODTablesUsed.ODTablesPulled iTableEnum = (MyAllocator1.ODTablesUsed.ODTablesPulled)int.Parse(dr[7].ToString()); PP_LedgerItem LI = new PP_LedgerItem( type,uiGuarantor,uiPatnum,usProvNum, uiItemNum,ItemDate,dAmmount,0,iTableEnum); //if (uiItemNum == 95693) // 1.ToString(); /////////////////////////////////////////////////////////////////// // Before make an Item Load the Allocation Status for Payments from Allocation_Provider Table. // Remember All payments get fully split // If there is no provider to allocate split to ProvNum is // assigned to zero. /////////////////////////////////////////////////////////////////// PP_PaySplitItem[] splits = null; if(type == LedgerItemTypes.Payment && dt2_AllocTbl_Items.Rows.Count != 0) { //if (i == 66) // 1.ToString(); string s1 = "PayTableSource = " + ((int)iTableEnum).ToString() + " AND PaySourceNum = " + uiItemNum; DataRow[] rows = dt2_AllocTbl_Items.Select(s1); #region Just Code so I can use the Visual Studio Runtime DataSet Visulizer on the DataTable //DataTable dtTest = new DataTable(); //for (int jj =0; jj < dt2_AllocTbl_Items.Columns.Count; jj++) //{ // dtTest.Columns.Add(new DataColumn(dt2_AllocTbl_Items.Columns[jj].ColumnName, typeof(string)) ); //} //for (int jj = 0; jj < rows.Length; jj++) //{ // DataRow drkk = dtTest.NewRow(); // for (int kk = 0; kk < dtTest.Columns.Count; kk++) // { // drkk[kk] = rows[jj][kk].ToString(); // } // dtTest.Rows.Add(drkk); //} #endregion #if DEBUG bool MarkedAllocated; decimal sum = 0; // don't need the sum just checking things out. MarkedAllocated = true; // can be used to make sure there is not a miss recorrded IsAllocated. #endif bool fullyAllocated = false; if(rows != null && rows.Length != 0) { splits = new PP_PaySplitItem[rows.Length]; fullyAllocated = true; for(int j = 0;j < rows.Length;j++) { decimal amount2 = decimal.Parse(rows[j][3].ToString()); int prov2 = int.Parse(rows[j][4].ToString()); MyAllocator1.ODTablesUsed.ODTablesPulled AllocatedToTable = (MyAllocator1.ODTablesUsed.ODTablesPulled)int.Parse(rows[j][5].ToString()); ulong AllocatedToNum = ulong.Parse(rows[j][6].ToString()); splits[j] = new PP_PaySplitItem(amount2,prov2,AllocatedToNum,AllocatedToTable); fullyAllocated = fullyAllocated && (prov2 != 0); // If one item == 0 then it has not been allocated. #if DEBUG // checking method only sum += amount2; //MarkedAllocated = MarkedAllocated && (rows[i][2].ToString() == "1"); #endif } //LI.IS_ALLOCATED = fullyAllocated; } #if DEBUG else MarkedAllocated = false; // no splits to match against payment item (should be a new payment) #endif //if (fullyAllocated) // LI.ALLOCATED_AMMOUNT = sum; // if Allocated_ammount = amount the fully allocated = true (property of LI) #if DEBUG if(sum == LI.AMMOUNT) { if(!MarkedAllocated) PU.Ex = "Record states that allocation is not fully allocated but summed ammounts in the\n" + "allocation table show that the allocation is fully allocated."; } else if(MarkedAllocated) PU.Ex = "Allocation Table shows that the procedures are fully allocated\n" + "but sums do not match. Sums should always match"; #endif } /////////////////////////////////////////////////////////////////// // Now add item to collection /////////////////////////////////////////////////////////////////// AddToCollection(LI,splits); // Just Makes a PaymentItem out of the LI and Splits then adds the PaymentItem to the list } // What about the case of an Item being in the Allocation_Provider Table but not in the // ODTable data? Let Update2() take care of these } } catch(Exception exc) { PU.MB = "Error in method: " + PU.Method + "\n\n" + exc.Message; rValSuccess = false; } if(rValSuccess) this.m_isFilled = true; //DataTable dtv1 = ViewLedgerObject(true); //DataTable dtv1 = ViewLedgerObject(new LedgerItemTypes[] { LedgerItemTypes.Payment }); return rValSuccess; }
/// <summary> /// Adds to the full list /// adds to chargesandrefunds list or paymentsandadjustments lists as /// apporpriate. /// </summary> /// <param name="dli"></param> private void AddToCollection(PP_LedgerItem dli,PP_PaySplitItem[] splits) { PP_LedgerItem item_to_add = dli; if(dli.ITEMTYPE == LedgerItemTypes.Charge) m_ChargesAndRefundsList.Add(dli); else if(dli.ITEMTYPE == LedgerItemTypes.Payment) { PP_PaymentItem ppi = dli.CreatePaymentItem(); if(splits != null) ppi.PAYMENT_SPLITS.AddRange(splits); if(ppi.ALLOCATED_AMMOUNT != 0) 1.ToString(); for(int i = 0;i < ppi.PAYMENT_SPLITS.Count;i++) { ppi.ALLOCATED_AMMOUNT += ppi.PAYMENT_SPLITS[i].AMMOUNT; } item_to_add = ppi; m_PaymentsAndAdjustList.Add(ppi); } else if(dli.ITEMTYPE == LedgerItemTypes.NegAdjustment) if(dli.AMMOUNT < 0) { PP_PaymentItem ppi = dli.CreatePaymentItem(); item_to_add = ppi; m_PaymentsAndAdjustList.Add(ppi); } else m_ChargesAndRefundsList.Add(dli);//ie +ve adjusment reflects a refund else if(dli.ITEMTYPE == LedgerItemTypes.PosAdjustment) m_ChargesAndRefundsList.Add(dli); // ie treat positive adjustment just like a charge m_FullLedgerList.Add(item_to_add); this.m_isEqualized = false; }
/// <summary> /// Used in EqualizePayments. Recursive Method /// </summary> private decimal AllocateToCharge(List<PP_LedgerItem> dliChargeRefundList,PP_LedgerItem dliPaymentItem) { ////////////-----------> Recursive Method !!! if(dliPaymentItem.AmtUnallocated == 0) return 0; decimal AmtAllocated = 0; PP_LedgerItem dliNext = NextUnallocatedItem(dliChargeRefundList); if(dliNext != null) { AmtAllocated = dliNext.AddAllocation(dliPaymentItem.ITEMNUM,dliPaymentItem.AmtUnallocated); if(AmtAllocated != 0) { // dliPaymentItem.PROVNUM = dliNext.PROVNUM; dliPaymentItem.AddAllocation(dliNext.ITEMNUM,AmtAllocated,dliNext.PROVNUM); } else dliNext.IsAllocated_OLD = true; // if nothing to allocate if(dliNext.IsAllocated_OLD == true) dliChargeRefundList.Remove(dliNext); if(dliPaymentItem.AmtUnallocated != 0) // will loop infinately if no allocation occurs and Chargeitems are still left { AmtAllocated += AllocateToCharge(dliChargeRefundList,dliPaymentItem); //recursion } } return AmtAllocated; }
/// <summary> /// Provides a data table that gives a running balance for each provider. /// /// Will run the EqualizeGuarantorPayments(guarantor) first if this instance has not /// done so yet. /// /// // Setup DataTable /// /// Date Provider # Provider # /// Charges Payments Adjustments Balances Charges Payments Adjustments Balances /// [ 0 ][1][ 2 ][ 3 ][ 4 ][ 5 ][6][ 7 ][ 8 ][ etc...... /// </summary> /// <param name="uGuarantor"></param> /// <returns></returns> public DataTable ProviderBalancesDetail() //uint uGuarantor) { DataTable dt = new DataTable(); string [] cols = { "Date", "Provider" }; string[] subcols = { "Charges", "Payments", "Adjustment", "Balance" }; if (Ledger == null) { PU.Ex = "Guarantor not set in " + PU.Method; } if (!Ledger.IS_FILLED) { Ledger.Fill(false); } if (!Ledger.IS_EQUALIZED) { Ledger.EqualizePaymentsV2(); } if (!Ledger.IS_FILLED || !Ledger.IS_EQUALIZED) { dt.Columns.Add("ERROR"); dt.Rows.Add(dt.NewRow()[0] = "Error Getting table filled"); return(dt); } Ledger.FullLedger.Sort(); // Generate List of Providers List <int> ProviderNums = new List <int>(); foreach (PP_LedgerItem li in Ledger.FullLedger) { if (li is PP_PaymentItem) { foreach (PP_PaySplitItem psi in ((PP_PaymentItem)li).PAYMENT_SPLITS) { if (!ProviderNums.Contains(psi.PROVNUM)) { ProviderNums.Add(psi.PROVNUM); } } } else if (!ProviderNums.Contains(li.PROVNUM)) { ProviderNums.Add(li.PROVNUM); } } // Setup DataTable /// /// Date Provider # Provider # /// Charges Payments Adjustments Balances Charges Payments Adjustments Balances /// [ 0 ][1][ 2 ][ 3 ][ 4 ][ 5 ][6][ 7 ][ 8 ][ etc...... dt.Columns.Add(new DataColumn("Date")); System.Collections.Hashtable ht_dtProvNumOffsets = new System.Collections.Hashtable(); for (int i = 0; i < ProviderNums.Count; i++) { dt.Columns.Add(new DataColumn("")); // Blank Column dt.Columns.Add(new DataColumn("")); dt.Columns.Add(new DataColumn("")); dt.Columns.Add(new DataColumn()); dt.Columns.Add(new DataColumn("")); } // Add Header Rows DataRow dr1 = dt.NewRow(); DataRow dr2 = dt.NewRow(); dr2[0] = "Date"; for (int i = 0; i < ProviderNums.Count; i++) { dr1[3 + 5 * i] = "Provider"; dr1[4 + 5 * i] = "# " + ProviderNums[i].ToString(); for (int j = 0; j < subcols.Length; j++) { dr2[2 + 5 * i + j] = subcols[j]; // Charges, Payments, Adjustments, Balances } } dt.Rows.Add(dr1); dt.Rows.Add(dr2); // Generate Provider Balances System.Collections.Hashtable htProvBalance = new System.Collections.Hashtable(); System.Collections.Hashtable htProvBalance_Cummulative = new System.Collections.Hashtable(); DateTime curDate = DateTime.MinValue; if (Ledger.FullLedger.Count != 0) { curDate = Ledger.FullLedger[0].ITEMDATE; } foreach (int ProvNum in ProviderNums) { htProvBalance[ProvNum] = new ProviderBalance(ProvNum, curDate); htProvBalance_Cummulative[ProvNum] = new ProviderBalance(ProvNum, curDate); } for (int i = 0; i < Ledger.FullLedger.Count; i++) { PP_LedgerItem li = Ledger.FullLedger[i]; #region Making the DataRow if (curDate < li.ITEMDATE || i == Ledger.FullLedger.Count - 1) { DataRow dr3 = this.MakeProviderBalanceDataRow(curDate, dt, htProvBalance, htProvBalance_Cummulative, subcols, ProviderNums); dt.Rows.Add(dr3); foreach (int ProvNum in ProviderNums) { htProvBalance[ProvNum] = new ProviderBalance(ProvNum, li.ITEMDATE); ((ProviderBalance)htProvBalance_Cummulative[ProvNum]).Date_of_Balance = li.ITEMDATE; } curDate = li.ITEMDATE; } #endregion #region Calculate Balances if (li is PP_PaymentItem) { foreach (PP_PaySplitItem psi in ((PP_PaymentItem)li).PAYMENT_SPLITS) { ProviderBalance pb = (ProviderBalance)htProvBalance[psi.PROVNUM]; ProviderBalance pb_Cumulative = (ProviderBalance)htProvBalance_Cummulative[psi.PROVNUM]; pb.Ammounts[(int)li.ITEMTYPE] += psi.AMMOUNT; pb_Cumulative.Ammounts[(int)li.ITEMTYPE] += psi.AMMOUNT; } } else { ProviderBalance pb = (ProviderBalance)htProvBalance[li.PROVNUM]; ProviderBalance pb_Cumulative = (ProviderBalance)htProvBalance_Cummulative[li.PROVNUM]; pb.Ammounts[(int)li.ITEMTYPE] += li.AMMOUNT; pb_Cumulative.Ammounts[(int)li.ITEMTYPE] += li.AMMOUNT; } #endregion } #region Adding the End DataRow { DataRow dr3 = this.MakeProviderBalanceDataRow(curDate, dt, htProvBalance, htProvBalance_Cummulative, subcols, ProviderNums); dt.Rows.Add(dr3); } #endregion return(dt); }