Exemplo n.º 1
0
		///<summary></summary>
		public static long Insert(PayPlanCharge charge) {
			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
				charge.PayPlanChargeNum=Meth.GetLong(MethodBase.GetCurrentMethod(),charge);
				return charge.PayPlanChargeNum;
			}
			return Crud.PayPlanChargeCrud.Insert(charge);
		}
Exemplo n.º 2
0
		///<summary></summary>
		public static void Update(PayPlanCharge charge){
			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
				Meth.GetVoid(MethodBase.GetCurrentMethod(),charge);
				return;
			}
			Crud.PayPlanChargeCrud.Update(charge);
		}
Exemplo n.º 3
0
		///<summary></summary>
		public static void Delete(PayPlanCharge charge){
			if(RemotingClient.RemotingRole==RemotingRole.ClientWeb) {
				Meth.GetVoid(MethodBase.GetCurrentMethod(),charge);
				return;
			}
			string command= "DELETE from payplancharge WHERE PayPlanChargeNum = '"
				+POut.Long(charge.PayPlanChargeNum)+"'";
 			Db.NonQ(command);
		}	
Exemplo n.º 4
0
        ///<summary></summary>
        public PayPlanCharge Copy()
        {
            PayPlanCharge p = new PayPlanCharge();

            p.PayPlanChargeNum = PayPlanChargeNum;
            p.PayPlanNum       = PayPlanNum;
            p.Guarantor        = Guarantor;
            p.PatNum           = PatNum;
            p.ChargeDate       = ChargeDate;
            p.Principal        = Principal;
            p.Interest         = Interest;
            p.Note             = Note;
            return(p);
        }
Exemplo n.º 5
0
		///<summary></summary>
		public FormPayPlanChargeEdit(PayPlanCharge payPlanCharge){
			InitializeComponent();
			PayPlanChargeCur=payPlanCharge;
			Lan.F(this);
		}
Exemplo n.º 6
0
		private void butDelete_Click(object sender, System.EventArgs e) {
			if(IsNew){
				DialogResult=DialogResult.Cancel;
			}
			else{
				DialogResult=DialogResult.OK;
				PayPlanChargeCur=null;//Setting this null so we know to get rid of it when the form closes. 
			}
		}
Exemplo n.º 7
0
        public static List <PaySplit> CreateSplitForPayPlan(Payment paymentCur, AccountEntry payPlanEntry, List <PayPlanCharge> listPayPlanCredits,
                                                            List <AccountEntry> listAccountCharges, decimal payAmt, bool isAutoSplit, out decimal paymentCurPayAmt)
        {
            //No remoting role check; no call to db. Plus this method has an out parameter.
            paymentCurPayAmt = (decimal)paymentCur.PayAmt;
            List <PaySplit> listSplits = new List <PaySplit>();
            DateTime        today      = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0, DateTimeKind.Unspecified);
            //if the payAmt is > 0 and it's NOT an autosplit, we only want to pay up to the payAmt.
            bool          isPartial        = (payAmt > 0) && !isAutoSplit;
            PayPlanCharge payPlanChargeCur = (PayPlanCharge)payPlanEntry.Tag;
            //amtUnattached will also include principal payments if those payments are not allocated to a procedure.
            //This is accounted for when creating an unattached split.
            double amtUnattachedAlreadyPaid = (double)payPlanEntry.SplitCollection.Where(x => x.ProcNum == 0).Sum(y => y.SplitAmt);
            double amtAttachedAlreadyPaid   = (double)payPlanEntry.SplitCollection.Where(x => x.ProcNum != 0).Sum(y => y.SplitAmt);

            //Below is the logic of this method: For the passed in charge
            //create a split for the amount of the interest
            //create a split for the amount of the charge, attach it to the procedure if possible.
            //interest split, only create if interest hasn't already been paid.
            if (amtUnattachedAlreadyPaid < payPlanChargeCur.Interest)
            {
                PaySplit interest = new PaySplit();
                interest.DatePay = today;
                //the split should always go to the payplancharge's guarantor.
                interest.PatNum  = payPlanChargeCur.Guarantor;
                interest.ProvNum = payPlanChargeCur.ProvNum;
                if (PrefC.HasClinicsEnabled)                 //Clinics
                {
                    interest.ClinicNum = payPlanChargeCur.ClinicNum;
                }
                interest.PayPlanNum = payPlanChargeCur.PayPlanNum;
                interest.PayNum     = paymentCur.PayNum;
                //if it's an autoSplit, then only use up to the global PaymentAmt.
                //else if it's a partial split, then only use up to payAmt.
                //else it's a manual split, so just add a split for the entire charge.
                if (isAutoSplit)
                {
                    interest.SplitAmt = Math.Min(payPlanChargeCur.Interest - amtUnattachedAlreadyPaid, (double)paymentCurPayAmt);
                    paymentCurPayAmt -= (decimal)interest.SplitAmt;
                }
                else if (isPartial)
                {
                    interest.SplitAmt = Math.Min(payPlanChargeCur.Interest - amtUnattachedAlreadyPaid, (double)payAmt);
                    payAmt           -= (decimal)interest.SplitAmt;
                    paymentCurPayAmt -= (decimal)interest.SplitAmt;
                }
                else
                {
                    interest.SplitAmt = payPlanChargeCur.Interest - amtUnattachedAlreadyPaid;
                }
                //this is so the paysplit says (interest) in the grid.
                interest.IsInterestSplit = true;
                payPlanEntry.AmountEnd  -= (decimal)interest.SplitAmt;
                payPlanEntry.SplitCollection.Add(interest);
                listSplits.Add(interest);
            }
            //get all procs associated to this plan
            List <AccountEntry> listPayPlanProcsEntries = new List <AccountEntry>();

            foreach (PayPlanCharge payPlanCredit in listPayPlanCredits)         //For each credit
            {
                foreach (AccountEntry entry in listAccountCharges)              //Go through account entries
                {
                    if (entry.GetType() != typeof(Procedure))                   //If it's not a proc
                    {
                        continue;
                    }
                    Procedure proc = (Procedure)entry.Tag;
                    if (proc.ProcNum == payPlanCredit.ProcNum)                   //See if the proc has the credit's procnum
                    {
                        listPayPlanProcsEntries.Add(entry);                      //if so, add it so we then have a list of payplan's procs, and we can also show how much is left due for it
                    }
                }
            }
            listPayPlanProcsEntries = listPayPlanProcsEntries.OrderBy(x => x.Date).ToList();
            if (amtAttachedAlreadyPaid < payPlanChargeCur.Principal)
            {
                if (listPayPlanProcsEntries.Count > 0)
                {
                    double amtAvail = (double)payPlanEntry.AmountEnd;
                    foreach (AccountEntry entryProc in listPayPlanProcsEntries)
                    {
                        if (isAutoSplit && paymentCurPayAmt == 0)
                        {
                            break;
                        }
                        Procedure procCur   = (Procedure)entryProc.Tag;
                        PaySplit  procSplit = new PaySplit();
                        //get the amount of this procedure that's attached to this payment plan
                        double maxSplitForCurrentProc = listPayPlanCredits.Where(x => x.ProcNum == procCur.ProcNum).Sum(y => y.Principal);
                        //get the amount of this procedure that's already been paid off in this payment plan.
                        maxSplitForCurrentProc -= entryProc.SplitCollection.Where(x => x.PayPlanNum == payPlanChargeCur.PayPlanNum).Sum(y => y.SplitAmt);
                        if (maxSplitForCurrentProc == 0)
                        {
                            continue;
                        }
                        procSplit.DatePay = today;
                        //the payment should always go to the account of the payplancharge's guarantor.
                        procSplit.PatNum     = payPlanChargeCur.Guarantor;
                        procSplit.PayPlanNum = payPlanChargeCur.PayPlanNum;
                        procSplit.PayNum     = paymentCur.PayNum;
                        if (isAutoSplit)
                        {
                            //make a split for the procedure for no more than the sum of all the credits on the payment plan for that procedure.
                            procSplit.SplitAmt = Math.Min(maxSplitForCurrentProc, Math.Min(amtAvail, (double)paymentCurPayAmt));
                            paymentCurPayAmt  -= (decimal)procSplit.SplitAmt;
                        }
                        else if (isPartial)
                        {
                            if (payAmt == 0)
                            {
                                break;
                            }
                            procSplit.SplitAmt = Math.Min(maxSplitForCurrentProc, Math.Min(amtAvail, (double)payAmt));
                            payAmt            -= (decimal)procSplit.SplitAmt;
                            paymentCurPayAmt  -= (decimal)procSplit.SplitAmt;
                        }
                        else
                        {
                            procSplit.SplitAmt = Math.Min((double)maxSplitForCurrentProc, amtAvail);                          //make a split for the procedure for no more than the sum of all the credits on the payment plan for that procedure.
                        }
                        procSplit.ProvNum   = procCur.ProvNum;
                        procSplit.ClinicNum = procCur.ClinicNum;
                        procSplit.ProcNum   = procCur.ProcNum;
                        amtAvail           -= procSplit.SplitAmt;
                        entryProc.SplitCollection.Add(procSplit);
                        payPlanEntry.AmountEnd -= (decimal)procSplit.SplitAmt;
                        payPlanEntry.SplitCollection.Add(procSplit);
                        listSplits.Add(procSplit);
                    }
                    //if they have a mix of attached and unattached credits: (if there are payplan charges not linked to a procedure)
                    bool doCreateUnattachedSplit = payPlanEntry.AmountEnd > 0 && ((isAutoSplit && Math.Min(amtAvail, (double)paymentCurPayAmt) > 0) ||
                                                                                  (!isAutoSplit && Math.Min(amtAvail, (double)payAmt) > 0));
                    if (doCreateUnattachedSplit)
                    {
                        PaySplit unattachedSplit = new PaySplit();
                        unattachedSplit.DatePay = today;
                        //the payment should always go to the account of the payplancharge's guarantor.
                        unattachedSplit.PatNum     = payPlanChargeCur.Guarantor;
                        unattachedSplit.PayPlanNum = payPlanChargeCur.PayPlanNum;
                        unattachedSplit.PayNum     = paymentCur.PayNum;
                        if (isAutoSplit)
                        {
                            unattachedSplit.SplitAmt = Math.Min(amtAvail, (double)paymentCurPayAmt);
                            paymentCurPayAmt        -= (decimal)unattachedSplit.SplitAmt;
                        }
                        else if (isPartial)
                        {
                            unattachedSplit.SplitAmt = Math.Min(amtAvail, (double)payAmt);
                            payAmt           -= (decimal)unattachedSplit.SplitAmt;
                            paymentCurPayAmt -= (decimal)unattachedSplit.SplitAmt;
                        }
                        else
                        {
                            unattachedSplit.SplitAmt = amtAvail;
                        }
                        unattachedSplit.ProvNum   = payPlanEntry.ProvNum;
                        unattachedSplit.ClinicNum = payPlanEntry.ClinicNum;
                        unattachedSplit.ProcNum   = 0;
                        amtAvail -= unattachedSplit.SplitAmt;
                        payPlanEntry.AmountEnd -= (decimal)unattachedSplit.SplitAmt;
                        payPlanEntry.SplitCollection.Add(unattachedSplit);
                        listSplits.Add(unattachedSplit);
                    }
                }
                else
                {
                    PaySplit unattachedSplit = new PaySplit();
                    //for payplans with no attached procedures, all paysplits would end up in the interestAlreadyPaid amount.
                    //any extra money that was accidentally lumped into interestAlreadyPaid should be included in principleAlreadyPaid here.
                    amtAttachedAlreadyPaid  = (amtUnattachedAlreadyPaid - payPlanChargeCur.Interest > 0) ? amtUnattachedAlreadyPaid - payPlanChargeCur.Interest : 0;
                    unattachedSplit.DatePay = today;
                    //the payment should always go to the account of the payplancharge's guarantor.
                    unattachedSplit.PatNum     = payPlanChargeCur.Guarantor;
                    unattachedSplit.PayPlanNum = payPlanChargeCur.PayPlanNum;
                    unattachedSplit.PayNum     = paymentCur.PayNum;
                    if (isAutoSplit)
                    {
                        unattachedSplit.SplitAmt = Math.Min((double)paymentCurPayAmt, payPlanChargeCur.Principal - amtAttachedAlreadyPaid);
                        paymentCurPayAmt        -= (decimal)unattachedSplit.SplitAmt;
                    }
                    else if (isPartial)
                    {
                        unattachedSplit.SplitAmt = Math.Min((double)payAmt, payPlanChargeCur.Principal - amtAttachedAlreadyPaid);
                        payAmt           -= (decimal)unattachedSplit.SplitAmt;
                        paymentCurPayAmt -= (decimal)unattachedSplit.SplitAmt;
                    }
                    else
                    {
                        unattachedSplit.SplitAmt = payPlanChargeCur.Principal - amtAttachedAlreadyPaid;
                    }
                    unattachedSplit.ProvNum   = payPlanChargeCur.ProvNum;
                    unattachedSplit.ClinicNum = payPlanChargeCur.ClinicNum;
                    payPlanEntry.AmountEnd   -= (decimal)unattachedSplit.SplitAmt;
                    payPlanEntry.SplitCollection.Add(unattachedSplit);
                    listSplits.Add(unattachedSplit);
                }
            }
            return(listSplits);
        }
Exemplo n.º 8
0
			public AccountEntry(PayPlanCharge payPlanCharge) {
				Tag=payPlanCharge;
				Date=payPlanCharge.ChargeDate;
				PriKey=payPlanCharge.PayPlanChargeNum;
				AmountOriginal=payPlanCharge.Principal+payPlanCharge.Interest;
				AmountStart=AmountOriginal;
				AmountEnd=AmountOriginal;
				ProvNum=payPlanCharge.ProvNum;
				ClinicNum=payPlanCharge.ClinicNum;
				PatNum=payPlanCharge.PatNum;
			}
Exemplo n.º 9
0
 private void butCreateSched_Click(object sender, System.EventArgs e)
 {
     //this is also where the terms get saved
     if(  textDate.errorProvider1.GetError(textDate)!=""
         || textAmount.errorProvider1.GetError(textAmount)!=""
         || textDateFirstPay.errorProvider1.GetError(textDateFirstPay)!=""
         || textDownPayment.errorProvider1.GetError(textDownPayment)!=""
         || textAPR.errorProvider1.GetError(textAPR)!=""
         || textTerm.errorProvider1.GetError(textTerm)!=""
         || textPeriodPayment.errorProvider1.GetError(textPeriodPayment)!=""
         ){
         MessageBox.Show(Lan.g(this,"Please fix data entry errors first."));
         return;
     }
     if(textAmount.Text=="" || PIn.Double(textAmount.Text)==0){
         MsgBox.Show(this,"Please enter an amount first.");
         return;
     }
     if(textDateFirstPay.Text==""){
         textDateFirstPay.Text=DateTime.Today.ToShortDateString();
     }
     if(textDownPayment.Text==""){
         textDownPayment.Text="0";
     }
     if(textAPR.Text==""){
         textAPR.Text="0";
     }
     if(textTerm.Text=="" && textPeriodPayment.Text==""){
         MsgBox.Show(this,"Please enter a term or payment amount first.");
         return;
     }
     if(textTerm.Text=="" && PIn.Double(textPeriodPayment.Text)==0){
         MsgBox.Show(this,"Payment cannot be 0.");
         return;
     }
     if(textPeriodPayment.Text=="" && PIn.Long(textTerm.Text)<1){
         MsgBox.Show(this,"Term cannot be less than 1.");
         return;
     }
     if(table.Rows.Count>0){
         if(!MsgBox.Show(this,true,"Replace existing amortization schedule?")){
             return;
         }
         PayPlanCharges.DeleteAllInPlan(PayPlanCur.PayPlanNum);
     }
     PayPlanCharge ppCharge;
     //down payment
     double downpayment=PIn.Double(textDownPayment.Text);
     if(downpayment!=0){
         ppCharge=new PayPlanCharge();
         ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
         ppCharge.Guarantor=PayPlanCur.Guarantor;
         ppCharge.PatNum=PayPlanCur.PatNum;
         ppCharge.ChargeDate=DateTime.Today;
         ppCharge.Interest=0;
         ppCharge.Principal=downpayment;
         ppCharge.Note=Lan.g(this,"Downpayment");
         ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
         ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
         PayPlanCharges.Insert(ppCharge);
     }
     double principal=PIn.Double(textAmount.Text)-PIn.Double(textDownPayment.Text);
     double APR=PIn.Double(textAPR.Text);
     double periodRate;
     double periodPayment;
     if(APR==0){
         periodRate=0;
     }
     else{
         if(radioMonthly.Checked){
             periodRate=APR/100/12;
         }
         else{
             periodRate=APR/100/4;
         }
     }
     if(textTerm.Text!=""){//Use term to determine period payment
         double term=PIn.Double(textTerm.Text);
         if(APR==0){
             periodPayment=principal/term;
         }
         else{
             periodPayment=principal*periodRate/(1-Math.Pow(1+periodRate,-term));
         }
     }
     else{//Use period payment supplied
         periodPayment=PIn.Double(textPeriodPayment.Text);
     }
     double tempP=principal;//the principal which will be decreased to zero in the loop.  Includes many decimal places.
     double roundedP=principal;//This is used to make sure that last item can handle the .01 rounding error. 2 decimal places
     int roundDec=CultureInfo.CurrentCulture.NumberFormat.NumberDecimalDigits;
     DateTime firstDate=PIn.Date(textDateFirstPay.Text);
     int countCharges=0;
     while(tempP!=0 && countCharges<100){//the 100 limit prevents infinite loop
         ppCharge=new PayPlanCharge();
         ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
         ppCharge.Guarantor=PayPlanCur.Guarantor;
         ppCharge.PatNum=PayPlanCur.PatNum;
         if(radioMonthly.Checked){
             ppCharge.ChargeDate=firstDate.AddMonths(1*countCharges);
         }
         else{
             ppCharge.ChargeDate=firstDate.AddMonths(3*countCharges);
         }
         ppCharge.Interest=Math.Round((tempP*periodRate),roundDec);//2 decimals
         ppCharge.Principal=periodPayment-ppCharge.Interest;//many decimals, but same on each payment, so rounding not noticeable.
         ppCharge.ProvNum=PatCur.PriProv;
         if(tempP<-.03){//tempP is a significantly negative number, so this charge does not get added.
             //the negative amount instead gets subtracted from the previous charge entered.
             //List<PayPlanCharge> ChargeListAll=PayPlanCharges.Refresh(PayPlanCur.Guarantor);
             List<PayPlanCharge> ChargeList=PayPlanCharges.GetForPayPlan(PayPlanCur.PayPlanNum);
             ppCharge=ChargeList[ChargeList.Count-1].Copy();
             ppCharge.Principal+=tempP;
             PayPlanCharges.Update(ppCharge);
             break;
         }
         tempP-=ppCharge.Principal;
         roundedP-=Math.Round(ppCharge.Principal,roundDec);
         if(tempP<.02 && tempP>-.02){//we are on the last loop since # so close to zero
             //We might alter this principal by a few cents to make them all match
             ppCharge.Principal+=roundedP;
             //and alter the interest by the opposite amount to keep the payment the same.
             //So in the end, the pennies got absorbed by changing the interest.
             if(APR!=0){
                 ppCharge.Interest-=roundedP;
             }
             tempP=0;//this will prevent another loop
         }
         PayPlanCharges.Insert(ppCharge);
         countCharges++;
     }
     FillCharges();
     textNote.Text+=DateTime.Today.ToShortDateString()
         +" - Date of Agreement: "+textDate.Text
         +", Total Amount: "+textAmount.Text
         +", APR: "+textAPR.Text
         +", Total Cost of Loan: "+textTotalCost.Text;
 }
Exemplo n.º 10
0
 private void butAdd_Click(object sender, System.EventArgs e)
 {
     PayPlanCharge ppCharge=new PayPlanCharge();
     ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
     ppCharge.Guarantor=PayPlanCur.Guarantor;
     ppCharge.ChargeDate=DateTime.Today;
     ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
     ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
     FormPayPlanChargeEdit FormP=new FormPayPlanChargeEdit(ppCharge);
     FormP.IsNew=true;
     FormP.ShowDialog();
     if(FormP.DialogResult==DialogResult.Cancel){
         return;
     }
     FillCharges();
 }
Exemplo n.º 11
0
		private void butCreateSched_Click(object sender, System.EventArgs e) {
			//this is also where the terms get saved
			if(  textDate.errorProvider1.GetError(textDate)!=""
				|| textAmount.errorProvider1.GetError(textAmount)!=""
				|| textDateFirstPay.errorProvider1.GetError(textDateFirstPay)!=""
				|| textDownPayment.errorProvider1.GetError(textDownPayment)!=""
				|| textAPR.errorProvider1.GetError(textAPR)!=""
				|| textTerm.errorProvider1.GetError(textTerm)!=""
				|| textPeriodPayment.errorProvider1.GetError(textPeriodPayment)!=""
				){
				MessageBox.Show(Lan.g(this,"Please fix data entry errors first."));
				return;
			}
			if(textAmount.Text=="" || PIn.Double(textAmount.Text)==0){
				MsgBox.Show(this,"Please enter an amount first.");
				return;
			}
			if(textDateFirstPay.Text==""){
				textDateFirstPay.Text=DateTime.Today.ToShortDateString();
			}
			if(textDownPayment.Text==""){
				textDownPayment.Text="0";
			}
			if(textAPR.Text==""){
				textAPR.Text="0";
			}
			if(textTerm.Text=="" && textPeriodPayment.Text==""){
				MsgBox.Show(this,"Please enter a term or payment amount first.");
				return;
			}
			if(textTerm.Text=="" && PIn.Double(textPeriodPayment.Text)==0){
				MsgBox.Show(this,"Payment cannot be 0.");
				return;
			}
			if(textPeriodPayment.Text=="" && PIn.Long(textTerm.Text)<1){
				MsgBox.Show(this,"Term cannot be less than 1.");
				return;
			}
			if(table.Rows.Count>0){
				if(!MsgBox.Show(this,true,"Replace existing amortization schedule?")){
					return;
				}
				PayPlanCharges.DeleteAllInPlan(PayPlanCur.PayPlanNum);
			}
			PayPlanCharge ppCharge;
			//down payment
			double downpayment=PIn.Double(textDownPayment.Text);
			if(downpayment!=0){
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				ppCharge.ChargeDate=DateTimeOD.Today;
				ppCharge.Interest=0;
				ppCharge.Principal=downpayment;
				ppCharge.Note=Lan.g(this,"Downpayment");
				ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
				ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
				PayPlanCharges.Insert(ppCharge);
			}
			double principal=PIn.Double(textAmount.Text)-PIn.Double(textDownPayment.Text);
			double APR=PIn.Double(textAPR.Text);
			double periodRate;
			decimal periodPayment;
			if(APR==0){
				periodRate=0;
			}
			else{
				if(FormPayPlanOpts.radioWeekly.Checked){
					periodRate=APR/100/52;
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked){
					periodRate=APR/100/26;
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked){
					periodRate=APR/100/12;
				}
				else if(FormPayPlanOpts.radioMonthly.Checked){
					periodRate=APR/100/12;
				}
				else{//quarterly
					periodRate=APR/100/4;
				}
			}
			int roundDec=CultureInfo.CurrentCulture.NumberFormat.NumberDecimalDigits;
			if(textTerm.Text!=""){//Use term to determine period payment
				double term=PIn.Double(textTerm.Text);
				if(APR==0){
					periodPayment=Decimal.Round((decimal)(principal/term),roundDec);
				}
				else{
					periodPayment=Decimal.Round((decimal)(principal*periodRate/(1-Math.Pow(1+periodRate,-term))),roundDec);
				}
			}
			else{//Use period payment supplied
				periodPayment=PIn.Decimal(textPeriodPayment.Text);
			}
			decimal principalDecrementing=(decimal)principal;//the principal which will be decreased to zero in the loop. 
			decimal periodPrincipal;
			DateTime firstDate=PIn.Date(textDateFirstPay.Text);
			int countCharges=0;
			while(principalDecrementing!=0 && countCharges<100){//the 100 limit prevents infinite loop
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				if(FormPayPlanOpts.radioWeekly.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(7*countCharges);
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(14*countCharges);
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked) {//First/second/etc Mon/Tue/etc of month
					DateTime roughMonth=firstDate.AddMonths(1*countCharges);//this just gets us into the correct month and year
					DayOfWeek dayOfWeekFirstDate=firstDate.DayOfWeek;
					//find the starting point for the given month: the first day that matches day of week
					DayOfWeek dayOfWeekFirstMonth=(new DateTime(roughMonth.Year,roughMonth.Month,1)).DayOfWeek;
					if(dayOfWeekFirstMonth==dayOfWeekFirstDate) {//1st is the proper day of the week
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,1);
					}
					else if(dayOfWeekFirstMonth<dayOfWeekFirstDate) {//Example, 1st is a Tues (2), but we need to start on a Thursday (4)
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,dayOfWeekFirstDate-dayOfWeekFirstMonth+1);//4-2+1=3.  The 3rd is a Thursday
					}
					else {//Example, 1st is a Thursday (4), but we need to start on a Monday (1) 
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,7-(dayOfWeekFirstMonth-dayOfWeekFirstDate)+1);//7-(4-1)+1=5.  The 5th is a Monday
					}
					int ordinalOfMonth=GetOrdinalOfMonth(firstDate);//for example 3 if it's supposed to be the 3rd Friday of each month
					ppCharge.ChargeDate=ppCharge.ChargeDate.AddDays(7*(ordinalOfMonth-1));//to get to the 3rd Friday, and starting from the 1st Friday, we add 2 weeks.
				}
				else if(FormPayPlanOpts.radioMonthly.Checked) {
					ppCharge.ChargeDate=firstDate.AddMonths(1*countCharges);
				}
				else {//quarterly
					ppCharge.ChargeDate=firstDate.AddMonths(3*countCharges);
				}
				ppCharge.Interest=Math.Round(((double)principalDecrementing*periodRate),roundDec);//2 decimals
				periodPrincipal=periodPayment-(decimal)ppCharge.Interest;
				ppCharge.Principal=(double)periodPrincipal;
				ppCharge.ProvNum=PatCur.PriProv;
				if(principalDecrementing<-.03m) {//principalDecrementing is a significantly negative number, so this charge does not get added.
					//js not sure when this would ever happen.  Needs better comments some day.
					//the negative amount instead gets subtracted from the previous charge entered.
					//List<PayPlanCharge> ChargeListAll=PayPlanCharges.Refresh(PayPlanCur.Guarantor);
					List<PayPlanCharge> ChargeList=PayPlanCharges.GetForPayPlan(PayPlanCur.PayPlanNum);
					ppCharge=ChargeList[ChargeList.Count-1].Copy();
					ppCharge.Principal+=(double)principalDecrementing;
					PayPlanCharges.Update(ppCharge);
					break;
				}
				principalDecrementing-=periodPrincipal;  
				if(principalDecrementing < periodPrincipal/3m){//we are on the last loop because the remaining princ will be less than 1/3 of a monthly payment.
				//if(principalDecrementing<1m && principalDecrementing>-1m){
					//If this is based on # of payments, the amount will be pennies,
					//but if this is based on monthly amount, then the last payment could be any odd number at all.
					//Alter this principal
					periodPrincipal+=principalDecrementing;
					ppCharge.Principal=(double)periodPrincipal;
					//and alter the interest by the opposite amount to keep the payment the same.
					//So in the end, the pennies got absorbed by changing the interest.
					if(APR!=0){
						ppCharge.Interest-=(double)principalDecrementing;
					}
					principalDecrementing=0;//this will prevent another loop
				}
				PayPlanCharges.Insert(ppCharge);
				countCharges++;
			}
			FillCharges();
			textNote.Text+=DateTime.Today.ToShortDateString()
				+" - Date of Agreement: "+textDate.Text
				+", Total Amount: "+textAmount.Text
				+", APR: "+textAPR.Text
				+", Total Cost of Loan: "+textTotalCost.Text;
		}
Exemplo n.º 12
0
		///<summary>Creates pay plan charges and adds them to the end of listPayPlanCharges based off of the passed in schedule terms</param></summary>
		private void CalculateScheduleCharges(bool isRecalculate) {
			PayPlanCharge ppCharge;
			//down payment
			double downpayment=PIn.Double(textDownPayment.Text);
			if(downpayment!=0 && !isRecalculate) {
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				ppCharge.ChargeDate=DateTimeOD.Today;
				ppCharge.Interest=0;
				ppCharge.Principal=downpayment;
				ppCharge.Note=Lan.g(this,"Downpayment");
				ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
				ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
				_listPayPlanCharges.Add(ppCharge);
			}
			double principal=PIn.Double(textAmount.Text);
			//Skip downpayment subtraction if the user is unable to edit downpayment, as that means a plan exists and has already calculated a downpayment.
			//The downpayment will get subtracted as a payplan charge later.
			if(!isRecalculate) {
				principal-=PIn.Double(textDownPayment.Text);//principal is always >= 0 due to validation.  
				PayPlanCur.DownPayment=PIn.Double(textDownPayment.Text);
			}
			double APR=PIn.Double(textAPR.Text);
			PayPlanCur.APR=APR;
			double periodRate;
			decimal periodPayment;
			if(APR==0){
				periodRate=0;
			}
			else{
				if(FormPayPlanOpts.radioWeekly.Checked){
					periodRate=APR/100/52;
					PayPlanCur.PaySchedule=PaymentSchedule.Weekly;
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked){
					periodRate=APR/100/26;
					PayPlanCur.PaySchedule=PaymentSchedule.BiWeekly;
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked){
					periodRate=APR/100/12;
					PayPlanCur.PaySchedule=PaymentSchedule.MonthlyDayOfWeek;
				}
				else if(FormPayPlanOpts.radioMonthly.Checked){
					periodRate=APR/100/12;
					PayPlanCur.PaySchedule=PaymentSchedule.Monthly;
				}
				else{//quarterly
					periodRate=APR/100/4;
					PayPlanCur.PaySchedule=PaymentSchedule.Quarterly;
				}
			}
			int roundDec=CultureInfo.CurrentCulture.NumberFormat.NumberDecimalDigits;
			int term=0;
			int countPayPlanCharges=0;
			double interestUnpaid=0;//Only used if recalculating.
			double amtOverPaid=0;//Only used if recalculating.
			DateTime firstDate=PIn.Date(textDateFirstPay.Text);
			double amtPastDue=0;
			double amtPaid=0;
			double amtPastDueCopy=0;
			if(isRecalculate) {
				List<PayPlanCharge> listPayPlanChargesCopy=new List<PayPlanCharge>(_listPayPlanCharges);
				_listPayPlanCharges.Clear();
				int nextChargeIdx=2000;//Guaranteed to be less than 2000 pay plan charges
				List<PaySplit> listNewPaySplits=new List<PaySplit>();
				for(int i=0;i<listPayPlanChargesCopy.Count;i++) {
					if(listPayPlanChargesCopy[i].ChargeDate<=DateTime.Today) {
						amtPastDue+=listPayPlanChargesCopy[i].Principal+listPayPlanChargesCopy[i].Interest;
						principal-=listPayPlanChargesCopy[i].Principal;
						_listPayPlanCharges.Add(listPayPlanChargesCopy[i]);
						//Don't count charges that we made in addition to the original terms
						if(!listPayPlanChargesCopy[i].Note.Contains("Recalculated based on") && !listPayPlanChargesCopy[i].Note.Contains("Downpayment")) {
							countPayPlanCharges++;
						}
					}
					else {
						interestUnpaid+=listPayPlanChargesCopy[i].Interest;//Only used if not recalculating interest
						nextChargeIdx=Math.Min(nextChargeIdx,i);//Gets the index of the next month that will be the first date due of the recalculated schedule.
					}
				}
				if(nextChargeIdx!=2000) {//Incase they are recalculating after all charges are past due
					firstDate=listPayPlanChargesCopy[nextChargeIdx].ChargeDate;//We use the next charge date as the first charge after recalculating.
				}
				else {
					//Get the last due charge date and then we will add a period based off of what was last saved as the pay period.
					firstDate=listPayPlanChargesCopy[listPayPlanChargesCopy.Count-1].ChargeDate;
					if(PayPlanCur.PaySchedule==PaymentSchedule.Weekly) {
						firstDate=firstDate.AddDays(7);
					}
					else if(PayPlanCur.PaySchedule==PaymentSchedule.BiWeekly) {
						firstDate=firstDate.AddDays(14);
					}
					else if(PayPlanCur.PaySchedule==PaymentSchedule.MonthlyDayOfWeek) {//First/second/etc Mon/Tue/etc of month
						DateTime roughMonth=firstDate.AddMonths(1);//this just gets us into the correct month and year
						DayOfWeek dayOfWeekFirstDate=firstDate.DayOfWeek;
						//find the starting point for the given month: the first day that matches day of week
						DayOfWeek dayOfWeekFirstMonth=(new DateTime(roughMonth.Year,roughMonth.Month,1)).DayOfWeek;
						if(dayOfWeekFirstMonth==dayOfWeekFirstDate) {//1st is the proper day of the week
							firstDate=new DateTime(roughMonth.Year,roughMonth.Month,1);
						}
						else if(dayOfWeekFirstMonth<dayOfWeekFirstDate) {//Example, 1st is a Tues (2), but we need to start on a Thursday (4)
							firstDate=new DateTime(roughMonth.Year,roughMonth.Month,dayOfWeekFirstDate-dayOfWeekFirstMonth+1);//4-2+1=3.  The 3rd is a Thursday
						}
						else {//Example, 1st is a Thursday (4), but we need to start on a Monday (1) 
							firstDate=new DateTime(roughMonth.Year,roughMonth.Month,7-(dayOfWeekFirstMonth-dayOfWeekFirstDate)+1);//7-(4-1)+1=5.  The 5th is a Monday
						}
						int ordinalOfMonth=GetOrdinalOfMonth(firstDate);//for example 3 if it's supposed to be the 3rd Friday of each month
						firstDate=firstDate.AddDays(7*(ordinalOfMonth-1));//to get to the 3rd Friday, and starting from the 1st Friday, we add 2 weeks.
					}
					else if(PayPlanCur.PaySchedule==PaymentSchedule.Monthly) {
						firstDate=firstDate.AddMonths(1);
					}
					else {//quarterly
						firstDate=firstDate.AddMonths(3);
					}
				}
				for(int i=0;i<_listPaySplits.Count;i++) {
					if(_listPaySplits[i].DatePay>DateTime.Today) {
						break;
					}
					amtPaid+=_listPaySplits[i].SplitAmt;
					listNewPaySplits.Add(_listPaySplits[i]);
				}
				if(amtPaid>=amtPastDue) {
					amtOverPaid=amtPaid-amtPastDue;
					ppCharge=new PayPlanCharge();
					ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
					ppCharge.Guarantor=PayPlanCur.Guarantor;
					ppCharge.PatNum=PayPlanCur.PatNum;
					ppCharge.ChargeDate=DateTimeOD.Today;
					ppCharge.Interest=0;
					ppCharge.Principal=amtOverPaid;
					string recalcType=Lan.g(this,"prepayment");
					//Only deduct the overpaid amount from principal if we aren't prepaying, otherwise the payamount per month will be different than expected.
					if(!_formPayPlanRecalculate.isPrepay) {
						principal-=amtOverPaid;
						amtOverPaid=0;
						recalcType=Lan.g(this,"paying on principal");
					}
					ppCharge.Note=Lan.g(this,"Recalculated based on ")+recalcType;
					ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
					ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
					_listPayPlanCharges.Add(ppCharge);
				}
				else {
					amtPastDueCopy=amtPastDue-amtPaid;
					int countPastDueChargesUnpaid=0;
					double estimatedPeriodPayment=listPayPlanChargesCopy[listPayPlanChargesCopy.Count-1].Principal+listPayPlanChargesCopy[listPayPlanChargesCopy.Count-1].Interest;
					while(amtPastDueCopy>0) {
						if(amtPastDueCopy>=estimatedPeriodPayment) {
							countPastDueChargesUnpaid++;
						}
						amtPastDueCopy-=estimatedPeriodPayment;
					}
					principal+=amtPastDue-amtPaid;
					countPayPlanCharges-=countPastDueChargesUnpaid;
				}
			}
			if(textTerm.Text!=""){//Use term to determine period payment
				term=PIn.Int(textTerm.Text)-countPayPlanCharges;//countPayPlanCharges will 0 unless isRecalculate=true
				double periodExactAmt=0;
				if(APR==0){
					periodExactAmt=principal/term;
				}
				else{
					periodExactAmt=principal*periodRate/(1-Math.Pow(1+periodRate,-term));
					if(isRecalculate && !_formPayPlanRecalculate.isRecalculateInterest){
						periodExactAmt=(principal+interestUnpaid)/term;
					}
				}
				//Round up to the nearest penny (or international equivalent).  
				//This causes the principal on the last payment to be less than or equal to the other principal amounts.
				periodPayment=(decimal)(Math.Ceiling(periodExactAmt*Math.Pow(10,roundDec))/Math.Pow(10,roundDec));
				PayPlanCur.NumberOfPayments=term+countPayPlanCharges;//countPayPlanCharges will 0 unless isRecalculate=true
			}
			else{//Use period payment supplied
				periodPayment=PIn.Decimal(textPeriodPayment.Text);
				PayPlanCur.PayAmt=(double)periodPayment;
			}
			decimal amtOverPaidDecrementing=(decimal)amtOverPaid;
			decimal principalDecrementing=(decimal)principal;//The principal which will be decreased to zero.  Always starts >= 0, due to validation.
			decimal interestUnpaidDecrementing=(decimal)interestUnpaid;//Only used if recalculating and _formPayPlanRecalculate.isRecalculateInterest=false
			int countCharges=0;
			while(principalDecrementing>0 && countCharges<2000){//the 2000 limit prevents infinite loop
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				if(FormPayPlanOpts.radioWeekly.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(7*countCharges);
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(14*countCharges);
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked) {//First/second/etc Mon/Tue/etc of month
					DateTime roughMonth=firstDate.AddMonths(1*countCharges);//this just gets us into the correct month and year
					DayOfWeek dayOfWeekFirstDate=firstDate.DayOfWeek;
					//find the starting point for the given month: the first day that matches day of week
					DayOfWeek dayOfWeekFirstMonth=(new DateTime(roughMonth.Year,roughMonth.Month,1)).DayOfWeek;
					if(dayOfWeekFirstMonth==dayOfWeekFirstDate) {//1st is the proper day of the week
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,1);
					}
					else if(dayOfWeekFirstMonth<dayOfWeekFirstDate) {//Example, 1st is a Tues (2), but we need to start on a Thursday (4)
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,dayOfWeekFirstDate-dayOfWeekFirstMonth+1);//4-2+1=3.  The 3rd is a Thursday
					}
					else {//Example, 1st is a Thursday (4), but we need to start on a Monday (1) 
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,7-(dayOfWeekFirstMonth-dayOfWeekFirstDate)+1);//7-(4-1)+1=5.  The 5th is a Monday
					}
					int ordinalOfMonth=GetOrdinalOfMonth(firstDate);//for example 3 if it's supposed to be the 3rd Friday of each month
					ppCharge.ChargeDate=ppCharge.ChargeDate.AddDays(7*(ordinalOfMonth-1));//to get to the 3rd Friday, and starting from the 1st Friday, we add 2 weeks.
				}
				else if(FormPayPlanOpts.radioMonthly.Checked) {
					ppCharge.ChargeDate=firstDate.AddMonths(1*countCharges);
				}
				else {//quarterly
					ppCharge.ChargeDate=firstDate.AddMonths(3*countCharges);
				}
				if(isRecalculate && !_formPayPlanRecalculate.isRecalculateInterest){
					//Spread the unpaid interest out over the term
					if(term>0) {//Specified number of payments when creating the plan
						ppCharge.Interest=Math.Round(interestUnpaid/term,roundDec);
					}
					else {
						//This will take the total interest unpaid, and divide it by the calculated term, which is total amount/amount per month
						ppCharge.Interest=Math.Round(interestUnpaid/((principal+interestUnpaid)/(double)periodPayment),roundDec);
					}
				}
				else {//Either not recalculating or is recalculating but also recalculating interest
					ppCharge.Interest=Math.Round(((double)principalDecrementing*periodRate),roundDec);//2 decimals
				}
				ppCharge.Principal=(double)periodPayment-ppCharge.Interest;
				if((double)amtOverPaidDecrementing>=ppCharge.Principal+ppCharge.Interest) {//Will only happen for prepay.  Skips a payplan charge.
					term--;//This will ensure that non-recalculated interest gets accurately distributed as well as keeps the number of payments accurate.
					amtOverPaidDecrementing-=(decimal)(ppCharge.Principal+ppCharge.Interest);
					ppCharge.Interest=0;//Interest will like it is not being accrued when looking at the charge, but it is being deducted via the line above.
					ppCharge.Principal=0;
					ppCharge.Note="Prepaid";
					ppCharge.ProvNum=PatCur.PriProv;
					_listPayPlanCharges.Add(ppCharge);
					continue;
				}
				if(amtOverPaidDecrementing>0) {
					principalDecrementing-=(decimal)amtOverPaid;//Remove the amount overpaid from current principal balance to recalculate correct interest
					ppCharge.Principal-=(double)amtOverPaidDecrementing;//Since this was a partial payment, reduce the overpayment amount from the first month of new plan.
					amtOverPaid=0;
					amtOverPaidDecrementing=0;
					if(_formPayPlanRecalculate.isRecalculateInterest) {//Calculate interest based off of the balance AFTER removing the prepayment amount.
						ppCharge.Interest=Math.Round(((double)principalDecrementing*periodRate),roundDec);//2 decimals
					}
				}
				ppCharge.Note="";
				ppCharge.ProvNum=PatCur.PriProv;
				if(term>0 && countCharges==(term-1)) {//Using # payments method and this is the last payment.
					//The purpose of this code block is to fix any rounding issues.  Corrects principal when off by a few pennies.  Principal will decrease slightly and interest will increase slightly to keep payment amounts consistent.
					ppCharge.Principal=(double)principalDecrementing;//All remaining principal.  Causes loop to exit.  This is where the rounding error is eliminated.
					if(periodRate!=0 && (!isRecalculate)) {//Interest amount on last entry must stay zero for payment plans with zero APR. When APR is zero, the interest amount is set to zero above, and the last payment amount might be less than the other payment amounts.
						ppCharge.Interest=((double)periodPayment)-ppCharge.Principal;//Force the payment amount to match the rest of the period payments.
					}
					if(isRecalculate && !_formPayPlanRecalculate.isRecalculateInterest){
						ppCharge.Interest=(double)interestUnpaidDecrementing;
					}
				}
				else if(term<=0 && principalDecrementing+((decimal)ppCharge.Interest)<=periodPayment) {//Payment amount method, last payment.
					ppCharge.Principal=(double)principalDecrementing;//All remaining principal.  Causes loop to exit.
					//Interest was calculated above.
				}
				principalDecrementing-=(decimal)ppCharge.Principal;
				interestUnpaidDecrementing-=(decimal)ppCharge.Interest;
				//If somehow principalDecrementing was slightly negative right here due to rounding errors, then at worst the last charge amount would wrong by a few pennies and the loop would immediately exit.
				_listPayPlanCharges.Add(ppCharge);
				countCharges++;
			}
			FillCharges();
			textNote.Text=_payPlanNote+DateTime.Today.ToShortDateString()
				+" - Date of Agreement: "+textDate.Text
				+", Total Amount: "+textAmount.Text
				+", APR: "+textAPR.Text
				+", Total Cost of Loan: "+textTotalCost.Text;
		}
Exemplo n.º 13
0
		private void butCreateSched_Click(object sender, System.EventArgs e) {
			//this is also where the terms get saved
			if(  textDate.errorProvider1.GetError(textDate)!=""
				|| textAmount.errorProvider1.GetError(textAmount)!=""
				|| textDateFirstPay.errorProvider1.GetError(textDateFirstPay)!=""
				|| textDownPayment.errorProvider1.GetError(textDownPayment)!=""
				|| textAPR.errorProvider1.GetError(textAPR)!=""
				|| textTerm.errorProvider1.GetError(textTerm)!=""
				|| textPeriodPayment.errorProvider1.GetError(textPeriodPayment)!=""
				|| textCompletedAmt.errorProvider1.GetError(textCompletedAmt)!=""
				){
				MessageBox.Show(Lan.g(this,"Please fix data entry errors first."));
				return;
			}
			if(textAmount.Text=="" || PIn.Double(textAmount.Text)==0){
				MsgBox.Show(this,"Please enter an amount first.");
				return;
			}
			if(textDateFirstPay.Text==""){
				textDateFirstPay.Text=DateTime.Today.ToShortDateString();
			}
			if(textDownPayment.Text==""){
				textDownPayment.Text="0";
			}
			if(textAPR.Text==""){
				textAPR.Text="0";
			}
			if(textTerm.Text=="" && textPeriodPayment.Text==""){
				MsgBox.Show(this,"Please enter a term or payment amount first.");
				return;
			}
			if(textTerm.Text=="" && PIn.Double(textPeriodPayment.Text)==0){
				MsgBox.Show(this,"Payment cannot be 0.");
				return;
			}
			if(textTerm.Text!="" && textPeriodPayment.Text!="") {
				MsgBox.Show(this,"Please choose either Number of Payments or Payment Amt.");
				return;
			}
			if(textPeriodPayment.Text=="" && PIn.Long(textTerm.Text)<1){
				MsgBox.Show(this,"Term cannot be less than 1.");
				return;
			}
			if(PIn.Double(textAmount.Text)-PIn.Double(textDownPayment.Text)<0) {
				MsgBox.Show(this,"Down payment must be less than or equal to total amount.");
				return;
			}
			if(gridCharges.Rows.Count>0){
				if(!MsgBox.Show(this,true,"Replace existing amortization schedule?")){
					return;
				}
				_listPayPlanCharges.Clear();
			}
			PayPlanCharge ppCharge;
			//down payment
			double downpayment=PIn.Double(textDownPayment.Text);
			if(downpayment!=0){
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				ppCharge.ChargeDate=DateTimeOD.Today;
				ppCharge.Interest=0;
				ppCharge.Principal=downpayment;
				ppCharge.Note=Lan.g(this,"Downpayment");
				ppCharge.ProvNum=PatCur.PriProv;//will be changed at the end.
				ppCharge.ClinicNum=PatCur.ClinicNum;//will be changed at the end.
				_listPayPlanCharges.Add(ppCharge);
			}
			double principal=PIn.Double(textAmount.Text)-PIn.Double(textDownPayment.Text);//Always >= 0 due to validation.
			PayPlanCur.DownPayment=PIn.Double(textDownPayment.Text);
			double APR=PIn.Double(textAPR.Text);
			PayPlanCur.APR=APR;
			double periodRate;
			decimal periodPayment;
			if(APR==0){
				periodRate=0;
			}
			else{
				if(FormPayPlanOpts.radioWeekly.Checked){
					periodRate=APR/100/52;
					PayPlanCur.PaySchedule=PaymentSchedule.Weekly;
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked){
					periodRate=APR/100/26;
					PayPlanCur.PaySchedule=PaymentSchedule.BiWeekly;
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked){
					periodRate=APR/100/12;
					PayPlanCur.PaySchedule=PaymentSchedule.MonthlyDayOfWeek;
				}
				else if(FormPayPlanOpts.radioMonthly.Checked){
					periodRate=APR/100/12;
					PayPlanCur.PaySchedule=PaymentSchedule.Monthly;
				}
				else{//quarterly
					periodRate=APR/100/4;
					PayPlanCur.PaySchedule=PaymentSchedule.Quarterly;
				}
			}
			int roundDec=CultureInfo.CurrentCulture.NumberFormat.NumberDecimalDigits;
			int term=0;
			if(textTerm.Text!=""){//Use term to determine period payment
				term=PIn.Int(textTerm.Text);
				double periodExactAmt=0;
				if(APR==0){
					periodExactAmt=principal/term;
				}
				else{
					periodExactAmt=principal*periodRate/(1-Math.Pow(1+periodRate,-term));
				}
				//Round up to the nearest penny (or international equivalent).  This causes the principal on the last payment to be less than or equal to the other principal amounts.
				periodPayment=(decimal)(Math.Ceiling(periodExactAmt*Math.Pow(10,roundDec))/Math.Pow(10,roundDec));
				PayPlanCur.NumberOfPayments=term;
			}
			else{//Use period payment supplied
				periodPayment=PIn.Decimal(textPeriodPayment.Text);
				PayPlanCur.PayAmt=(double)periodPayment;
			}
			decimal principalDecrementing=(decimal)principal;//The principal which will be decreased to zero in the loop.  Always starts >= 0, due to validation.
			DateTime firstDate=PIn.Date(textDateFirstPay.Text);
			int countCharges=0;
			while(principalDecrementing>0 && countCharges<2000){//the 2000 limit prevents infinite loop
				ppCharge=new PayPlanCharge();
				ppCharge.PayPlanNum=PayPlanCur.PayPlanNum;
				ppCharge.Guarantor=PayPlanCur.Guarantor;
				ppCharge.PatNum=PayPlanCur.PatNum;
				ppCharge.Note="";
				if(FormPayPlanOpts.radioWeekly.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(7*countCharges);
				}
				else if(FormPayPlanOpts.radioEveryOtherWeek.Checked) {
					ppCharge.ChargeDate=firstDate.AddDays(14*countCharges);
				}
				else if(FormPayPlanOpts.radioOrdinalWeekday.Checked) {//First/second/etc Mon/Tue/etc of month
					DateTime roughMonth=firstDate.AddMonths(1*countCharges);//this just gets us into the correct month and year
					DayOfWeek dayOfWeekFirstDate=firstDate.DayOfWeek;
					//find the starting point for the given month: the first day that matches day of week
					DayOfWeek dayOfWeekFirstMonth=(new DateTime(roughMonth.Year,roughMonth.Month,1)).DayOfWeek;
					if(dayOfWeekFirstMonth==dayOfWeekFirstDate) {//1st is the proper day of the week
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,1);
					}
					else if(dayOfWeekFirstMonth<dayOfWeekFirstDate) {//Example, 1st is a Tues (2), but we need to start on a Thursday (4)
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,dayOfWeekFirstDate-dayOfWeekFirstMonth+1);//4-2+1=3.  The 3rd is a Thursday
					}
					else {//Example, 1st is a Thursday (4), but we need to start on a Monday (1) 
						ppCharge.ChargeDate=new DateTime(roughMonth.Year,roughMonth.Month,7-(dayOfWeekFirstMonth-dayOfWeekFirstDate)+1);//7-(4-1)+1=5.  The 5th is a Monday
					}
					int ordinalOfMonth=GetOrdinalOfMonth(firstDate);//for example 3 if it's supposed to be the 3rd Friday of each month
					ppCharge.ChargeDate=ppCharge.ChargeDate.AddDays(7*(ordinalOfMonth-1));//to get to the 3rd Friday, and starting from the 1st Friday, we add 2 weeks.
				}
				else if(FormPayPlanOpts.radioMonthly.Checked) {
					ppCharge.ChargeDate=firstDate.AddMonths(1*countCharges);
				}
				else {//quarterly
					ppCharge.ChargeDate=firstDate.AddMonths(3*countCharges);
				}
				ppCharge.Interest=Math.Round(((double)principalDecrementing*periodRate),roundDec);//2 decimals
				ppCharge.Principal=(double)periodPayment-ppCharge.Interest;
				ppCharge.ProvNum=PatCur.PriProv;
				if(term>0 && countCharges==(term-1)) {//Using # payments method and this is the last payment.
					//The purpose of this code block is to fix any rounding issues.  Corrects principal when off by a few pennies.  Principal will decrease slightly and interest will increase slightly to keep payment amounts consistent.
					ppCharge.Principal=(double)principalDecrementing;//All remaining principal.  Causes loop to exit.  This is where the rounding error is eliminated.
					if(periodRate!=0) {//Interest amount on last entry must stay zero for payment plans with zero APR. When APR is zero, the interest amount is set to zero above, and the last payment amount might be less than the other payment amounts.
						ppCharge.Interest=((double)periodPayment)-ppCharge.Principal;//Force the payment amount to match the rest of the period payments.
					}
				}
				else if(term==0 && principalDecrementing+((decimal)ppCharge.Interest) <= periodPayment) {//Payment amount method, last payment.
					ppCharge.Principal=(double)principalDecrementing;//All remaining principal.  Causes loop to exit.
					//Interest was calculated above.
				}
				principalDecrementing-=(decimal)ppCharge.Principal;
				//If somehow principalDecrementing was slightly negative right here due to rounding errors, then at worst the last charge amount would wrong by a few pennies and the loop would immediately exit.
				_listPayPlanCharges.Add(ppCharge);
				countCharges++;
			}
			FillCharges();
			textNote.Text=_payPlanNote+DateTime.Today.ToShortDateString()
				+" - Date of Agreement: "+textDate.Text
				+", Total Amount: "+textAmount.Text
				+", APR: "+textAPR.Text
				+", Total Cost of Loan: "+textTotalCost.Text;
		}
Exemplo n.º 14
0
		private ODGridRow CreateRowForPayPlanCharge(PayPlanCharge payPlanCharge,int payPlanChargeOrdinal) {
			string descript="#"+payPlanChargeOrdinal;
			if(payPlanCharge.Note!="") {
				descript+=" "+payPlanCharge.Note;
				//Don't add a # if it's a recalculated charge because they aren't "true" payplan charges.
				if(payPlanCharge.Note.Trim().ToLower().Contains("recalculated based on")) {
					descript=payPlanCharge.Note;
				}
			}
			ODGridRow row=new ODGridRow();//Charge row
			row.Cells.Add(payPlanCharge.ChargeDate.ToShortDateString());//0 Date
			row.Cells.Add(Providers.GetAbbr(payPlanCharge.ProvNum));//1 Prov Abbr
			row.Cells.Add(descript);//2 Descript
			row.Cells.Add((payPlanCharge.Principal).ToString("n"));//3 Principal
			row.Cells.Add(payPlanCharge.Interest.ToString("n"));//4 Interest
			row.Cells.Add((payPlanCharge.Principal+payPlanCharge.Interest).ToString("n"));//5 Due
			row.Cells.Add("");//6 Payment
			row.Cells.Add("");//7 Balance (filled later)
			row.Tag=payPlanCharge;
			return row;
		}