public void Reschedule_Annuity_ChangeInterestRate_PartiallyPaid_Overpaid() { Loan loan = _GetLoan(_GetAnnuityProduct_RoundingTypeBegin(), 10000, 0.04m, 6, new DateTime(2010, 1, 15)); loan.Repay(1, new DateTime(2010, 2, 12), 1906, false, true); loan.Repay(2, new DateTime(2010, 3, 12), 1908, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 4, 5), 1500, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 0, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-04-15", 169, 2643, 6926, 1300, 169); _AssertInstallment(loan, 3, "2010-05-17", 128, 1386, 4283, 0, 31); _AssertInstallment(loan, 4, "2010-06-15", 87, 1427, 2897, 0, 0); _AssertInstallment(loan, 5, "2010-07-15", 44, 1470, 1470, 0, 0); }
public RescheduleLoanEvent Reschedule(ReschedulingOptions ro, Loan contract, NonWorkingDateSingleton nwdS, ApplicationSettings applicationSettings) { _contract = contract; _nwdS = nwdS; _generalSettings = applicationSettings; switch (contract.Product.LoanType) { case OLoanTypes.Flat: _Reschedule_Flat(ro); break; case OLoanTypes.DecliningFixedPrincipal: _Reschedule_FixedPrincipal(ro); break; case OLoanTypes.DecliningFixedInstallments: _Reschedule_DecliningFixedInstallments(ro); break; } _Reschedule_AdjustOverpaid(); RescheduleLoanEvent rSe = new RescheduleLoanEvent { Date = ro.ReschedulingDate, Amount = contract.CalculateActualOlb(), Interest = contract.GetTotalInterestDue(), ClientType = contract.ClientType, BadLoan = contract.BadLoan, NbOfMaturity = ro.NewInstallments, DateOffset = ro.RepaymentDateOffset, GracePeriod = ro.GracePeriod, ChargeInterestDuringShift = ro.ChargeInterestDuringShift, ChargeInterestDuringGracePeriod = ro.ChargeInterestDuringGracePeriod, InstallmentNumber = contract.GetLastFullyRepaidInstallment() == null ? 1 : contract.GetLastFullyRepaidInstallment().Number + 1 }; _contract.CalculateStartDates(); return(rSe); }
public RescheduleLoanEvent Reschedule(ReschedulingOptions ro, Loan contract, NonWorkingDateSingleton nwdS, ApplicationSettings applicationSettings) { _contract = contract; _nwdS = nwdS; _generalSettings = applicationSettings; switch (contract.Product.LoanType) { case OLoanTypes.Flat: _Reschedule_Flat(ro); break; case OLoanTypes.DecliningFixedPrincipal: _Reschedule_FixedPrincipal(ro); break; case OLoanTypes.DecliningFixedInstallments: _Reschedule_DecliningFixedInstallments(ro); break; } _Reschedule_AdjustOverpaid(); RescheduleLoanEvent rSe = new RescheduleLoanEvent { Date = ro.ReschedulingDate, Amount = contract.CalculateActualOlb(), Interest = contract.GetTotalInterestDue(), ClientType = contract.ClientType, BadLoan = contract.BadLoan, NbOfMaturity = ro.NewInstallments, DateOffset = ro.RepaymentDateOffset, GracePeriod = ro.GracePeriod, ChargeInterestDuringShift = ro.ChargeInterestDuringShift, ChargeInterestDuringGracePeriod = ro.ChargeInterestDuringGracePeriod, InstallmentNumber = contract.GetLastFullyRepaidInstallment() == null ? 1 : contract.GetLastFullyRepaidInstallment().Number + 1 }; _contract.CalculateStartDates(); return rSe; }
private DateTime[] _Reschedule_AdjustDates(ReschedulingOptions ro) { DateTime[] retval = new DateTime[2]; DateTime oldDate = DateTime.MinValue; DateTime newDate = DateTime.MinValue; bool first = false; int number = 0; for (int i = 0; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.IsRepaid) { continue; } if (!first) { first = true; oldDate = installment.ExpectedDate; newDate = installment.ExpectedDate; if (0 == ro.RepaymentDateOffset) { break; } newDate = newDate.AddDays(ro.RepaymentDateOffset); DateTime actualNewDate = _nwdS.GetTheNearestValidDate(newDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); installment.ExpectedDate = actualNewDate; } installment.ExpectedDate = _contract.CalculateInstallmentDate(newDate, number); number++; } retval[0] = oldDate; retval[1] = newDate; return(retval); }
private void _Reschedule_FixedPrincipal(ReschedulingOptions ro) { _Reschedule_ExtendMaturity(ro.NewInstallments); int roundDecimals = _contract.UseCents ? 2 : 0; Installment firstUnpaidInstallment = _contract.GetFirstUnpaidInstallment(); OCurrency paidCapital = 0; if (firstUnpaidInstallment.IsPartiallyRepaid && firstUnpaidInstallment.PaidCapital != 0 && ro.GracePeriod > 0) { paidCapital = firstUnpaidInstallment.PaidCapital; firstUnpaidInstallment.CapitalRepayment = firstUnpaidInstallment.PaidCapital; firstUnpaidInstallment = _contract.GetInstallment(firstUnpaidInstallment.Number); } int firstUnpaidInstallmentNumber = firstUnpaidInstallment.Number; int toNext = firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0 ? 1 : 0; if (firstUnpaidInstallment.IsPartiallyRepaid || ro.NewInstallments > 0 || ro.GracePeriod > 0) { OCurrency unpaidAmount = 0; foreach (Installment installment in _contract.InstallmentList) { unpaidAmount += installment.IsRepaid ? 0 : installment.CapitalRepayment - installment.PaidCapital; } unpaidAmount += paidCapital; int nbOfUnpaid = _contract.InstallmentList.Count - firstUnpaidInstallmentNumber + 1 - toNext; double monthly = Math.Round(Convert.ToDouble(unpaidAmount.Value / (nbOfUnpaid - ro.GracePeriod)), roundDecimals, MidpointRounding.AwayFromZero); for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) { installment.CapitalRepayment = Convert.ToDecimal(monthly); } else { installment.CapitalRepayment = 0; } } Installment installmentOfCorrection = _contract.GetInstallment(firstUnpaidInstallmentNumber + ro.GracePeriod - 1 + toNext); installmentOfCorrection.CapitalRepayment += installmentOfCorrection.PaidCapital; double diff = Convert.ToDouble(unpaidAmount.Value) - monthly * (nbOfUnpaid - ro.GracePeriod); if (diff != 0) installmentOfCorrection.CapitalRepayment += Convert.ToDecimal(diff); OCurrency olb = firstUnpaidInstallment.OLB + paidCapital; for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); installment.OLB = olb; olb -= installment.CapitalRepayment; } } DateTime[] dates = _Reschedule_AdjustDates(ro); bool firstNonRepaid = true; if(firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0) { firstUnpaidInstallment.InterestsRepayment = firstUnpaidInstallment.PaidInterests; firstUnpaidInstallment.CapitalRepayment = firstUnpaidInstallment.PaidCapital; firstNonRepaid = false; } //interest calculation foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) continue; if (firstNonRepaid) { firstNonRepaid = false; if (ro.ChargeInterestDuringShift || _contract.InterestRate != ro.InterestRate || ro.NewInstallments > 0 || ro.GracePeriod > 0) { TimeSpan oldSpan; TimeSpan newSpan; if ((installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) || (ro.ChargeInterestDuringGracePeriod)) { if (1 == installment.Number) { DateTime endDate = _contract.StartDate.AddMonths(_contract.InstallmentType.NbOfMonths).AddDays(_contract.InstallmentType.NbOfDays); endDate = _nwdS.GetTheNearestValidDate(endDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); newSpan = (ro.ChargeInterestDuringShift ? installment.ExpectedDate : dates[0]) - _contract.StartDate; oldSpan = endDate - _contract.StartDate; } else { DateTime startDate = _contract.GetInstallment(installment.Number - 2).ExpectedDate; oldSpan = dates[0] - startDate; newSpan = (ro.ChargeInterestDuringShift ? dates[1] : dates[0]) - startDate; } OCurrency amount = installment.OLB - installment.PaidCapital; decimal interest = (amount.Value * newSpan.Days) * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = Convert.ToDecimal(interest); } else { installment.InterestsRepayment = 0; } } } else { if (_contract.InterestRate != ro.InterestRate || firstUnpaidInstallment.IsPartiallyRepaid || ro.NewInstallments > 0 || ro.GracePeriod > 0) { if ((installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) || (ro.ChargeInterestDuringGracePeriod)) { decimal interest = installment.OLB.Value * ro.InterestRate; interest = Math.Round(interest, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = Convert.ToDecimal(interest); } else { installment.InterestsRepayment = 0; } } } } }
private DateTime[] _Reschedule_AdjustDates(ReschedulingOptions ro) { DateTime[] retval = new DateTime[2]; DateTime oldDate = DateTime.MinValue; DateTime newDate = DateTime.MinValue; bool first = false; int number = 0; for (int i = 0; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.IsRepaid) continue; if (!first) { first = true; oldDate = installment.ExpectedDate; newDate = installment.ExpectedDate; if (0 == ro.RepaymentDateOffset) break; newDate = newDate.AddDays(ro.RepaymentDateOffset); DateTime actualNewDate = _nwdS.GetTheNearestValidDate(newDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); installment.ExpectedDate = actualNewDate; } installment.ExpectedDate = _contract.CalculateInstallmentDate(newDate, number); number++; } retval[0] = oldDate; retval[1] = newDate; return retval; }
public void Reschedule_Flat_ShiftDate_UseCents() { Loan loan = _GetLoan(_GetFlatProduct_WithCents(), 10000, 0.04m, 6, new DateTime(2009, 12, 24)); loan.Repay(1, new DateTime(2010, 1, 25), 2066.67m, false, true); loan.Repay(2, new DateTime(2010, 2, 24), 2066.67m, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 0, RepaymentDateOffset = 38, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 942.86m, 1666.67m); _AssertInstallment(loan, 3, "2010-06-01", 400, 1666.66m); _AssertInstallment(loan, 4, "2010-07-01", 400, 1666.67m); _AssertInstallment(loan, 5, "2010-08-02", 400, 1666.66m); }
public void Reschedule_Flat_ShiftDate_ChangeInterestRate_PartiallyPaid() { Loan loan = _GetLoan(_GetFlatProduct(), 10000, 0.04m, 6, new DateTime(2009, 12, 22)); loan.Repay(1, new DateTime(2010, 1, 22), 2067, false, true); loan.Repay(2, new DateTime(2010, 2, 22), 2067, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 3, 5), 1500, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 40, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 729, 2640, 6666); _AssertInstallment(loan, 3, "2010-06-01", 300, 1342, 4026); _AssertInstallment(loan, 4, "2010-07-01", 300, 1342, 2684); _AssertInstallment(loan, 5, "2010-08-02", 300, 1342, 1342); }
public void Reschedule_FixedPrincipal_ShiftDate_PartiallyPaid_UseCents() { Loan loan = _GetLoan(_GetFixedPrincipalProduct_WithCents(), 10000, 0.04m, 6, new DateTime(2010, 1, 11)); loan.Repay(1, new DateTime(2010, 2, 11), 2066.67m, false, true); loan.Repay(2, new DateTime(2010, 3, 11), 2000.00m, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 4, 5), 1500.00m, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 0, RepaymentDateOffset = 19, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 342.12m, 2641.65m, 6666.66m); _AssertInstallment(loan, 3, "2010-06-01", 161.00m, 1341.67m, 4025.01m); _AssertInstallment(loan, 4, "2010-07-01", 107.33m, 1341.67m, 2683.34m); _AssertInstallment(loan, 5, "2010-08-02", 53.67m, 1341.67m, 1341.67m); }
public void Test_ProvisionRescheduledLoanProcessing() { Loan myContract = _SetContract(1000, 0.03m, OLoanTypes.Flat, new NonRepaymentPenalties(0, 0, 0.003, 0), false, 1, new DateTime(2010, 6, 6), 6); OpenCBS.CoreDomain.Contracts.Loans.LoanRepayment.Repayment.RepayLateInstallments.CalculateInstallments rLI = _SetRepaymentOptions(myContract, false); ProvisionTable provisionTable = ProvisionTable.GetInstance(new User()); provisionTable.ProvisioningRates = new List<ProvisioningRate>(); provisionTable.Add(new ProvisioningRate { Number = 1, NbOfDaysMin = 0, NbOfDaysMax = 0, Rate = 0.5 }); provisionTable.Add(new ProvisioningRate { Number = 2, NbOfDaysMin = 1, NbOfDaysMax = 30, Rate = 1 }); provisionTable.Add(new ProvisioningRate { Number = 3, NbOfDaysMin = 31, NbOfDaysMax = 60, Rate = 1.5 }); provisionTable.Add(new ProvisioningRate { Number = 3, NbOfDaysMin = 60, NbOfDaysMax = 999, Rate = 2 }); provisionTable.Add(new ProvisioningRate { Number = 4, NbOfDaysMin = -1, NbOfDaysMax = -1, Rate = 1 }); Assert.AreEqual(new DateTime(2010, 7, 6), myContract.GetInstallment(0).ExpectedDate); Assert.AreEqual(new DateTime(2010, 8, 6), myContract.GetInstallment(1).ExpectedDate); myContract.Repay(1, new DateTime(2010, 8, 6), 100, false, false); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 2, RepaymentDateOffset = 0, ChargeInterestDuringShift = true, ReschedulingDate = new DateTime(2010, 12, 1) }; myContract.Reschedule(ro); myContract.Rescheduled = true; rLI.CalculateNewInstallmentsWithLateFees(new DateTime(2010, 10, 1)); ProvisionEvent e = myContract.GetProvisionEvent(new DateTime(2010, 10, 1), provisionTable); Assert.AreEqual(e.Code, "LLPE"); Assert.AreEqual(e.Amount, 1000); myContract.Events.Add(e); }
public Loan FakeReschedule(Loan contract, DateTime date, int nbOfMaturity, int dateOffset, bool pAccruedInterestDuringTheGracePeriod, decimal pNewInterestRate, int gracePeriod, bool chargeInterestDuringGracePeriod) { Loan fakeContract = contract.Copy(); ReschedulingOptions ro = new ReschedulingOptions { ChargeInterestDuringShift = pAccruedInterestDuringTheGracePeriod, NewInstallments = nbOfMaturity, InterestRate = pNewInterestRate, RepaymentDateOffset = dateOffset, ReschedulingDate = date, GracePeriod = gracePeriod, ChargeInterestDuringGracePeriod = chargeInterestDuringGracePeriod }; fakeContract.Reschedule(ro); return fakeContract; }
public Loan Reschedule(Loan pContract, DateTime pDate, int pNbOfMaturity, int dateOffset, bool pAccruedInterestDuringTheGracePeriod, decimal pNewInterestRate, int gracePeriod, bool chargeInterestDuringGracePeriod) { using (SqlConnection conn = _loanManager.GetConnection()) using (SqlTransaction sqlTransac = conn.BeginTransaction()) { try { Loan copyOfLoan = pContract.Copy(); //create the rescheduling loan event ReschedulingOptions ro = new ReschedulingOptions { ReschedulingDate = pDate, ChargeInterestDuringShift = pAccruedInterestDuringTheGracePeriod, InterestRate = pNewInterestRate, RepaymentDateOffset = dateOffset, NewInstallments = pNbOfMaturity, GracePeriod = gracePeriod, ChargeInterestDuringGracePeriod = chargeInterestDuringGracePeriod }; RescheduleLoanEvent rescheduleLoanEvent = pContract.Reschedule(ro); rescheduleLoanEvent.User = _user; //insert into table ReschedulingOfALoanEvents _ePs.FireEvent(rescheduleLoanEvent, pContract, sqlTransac); OverdueEvent overdueEvent = pContract.AddRecheduleTransformationEvent(pDate); if (overdueEvent != null) _ePs.FireEvent(overdueEvent, pContract, sqlTransac); ArchiveInstallments(copyOfLoan, rescheduleLoanEvent, sqlTransac); //delete all the old installments of the table Installments _instalmentManager.DeleteInstallments(pContract.Id, sqlTransac); //insert all the new installments in the table Installments _instalmentManager.AddInstallments(pContract.InstallmentList, pContract.Id, sqlTransac); _loanManager.UpdateLoanToRescheduled(pNewInterestRate, pNbOfMaturity, pContract, sqlTransac); sqlTransac.Commit(); return pContract; } catch (Exception ex) { sqlTransac.Rollback(); throw ex; } } }
private void _Reschedule_FixedPrincipal(ReschedulingOptions ro) { _Reschedule_ExtendMaturity(ro.NewInstallments); int roundDecimals = _contract.UseCents ? 2 : 0; Installment firstUnpaidInstallment = _contract.GetFirstUnpaidInstallment(); OCurrency paidCapital = 0; if (firstUnpaidInstallment.IsPartiallyRepaid && firstUnpaidInstallment.PaidCapital != 0 && ro.GracePeriod > 0) { paidCapital = firstUnpaidInstallment.PaidCapital; firstUnpaidInstallment.CapitalRepayment = firstUnpaidInstallment.PaidCapital; firstUnpaidInstallment = _contract.GetInstallment(firstUnpaidInstallment.Number); } int firstUnpaidInstallmentNumber = firstUnpaidInstallment.Number; int toNext = firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0 ? 1 : 0; if (firstUnpaidInstallment.IsPartiallyRepaid || ro.NewInstallments > 0 || ro.GracePeriod > 0) { OCurrency unpaidAmount = 0; foreach (Installment installment in _contract.InstallmentList) { unpaidAmount += installment.IsRepaid ? 0 : installment.CapitalRepayment - installment.PaidCapital; } unpaidAmount += paidCapital; int nbOfUnpaid = _contract.InstallmentList.Count - firstUnpaidInstallmentNumber + 1 - toNext; double monthly = Math.Round(Convert.ToDouble(unpaidAmount.Value / (nbOfUnpaid - ro.GracePeriod)), roundDecimals, MidpointRounding.AwayFromZero); for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) { installment.CapitalRepayment = Convert.ToDecimal(monthly); } else { installment.CapitalRepayment = 0; } } Installment installmentOfCorrection = _contract.GetInstallment(firstUnpaidInstallmentNumber + ro.GracePeriod - 1 + toNext); installmentOfCorrection.CapitalRepayment += installmentOfCorrection.PaidCapital; double diff = Convert.ToDouble(unpaidAmount.Value) - monthly * (nbOfUnpaid - ro.GracePeriod); if (diff != 0) { installmentOfCorrection.CapitalRepayment += Convert.ToDecimal(diff); } OCurrency olb = firstUnpaidInstallment.OLB + paidCapital; for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); installment.OLB = olb; olb -= installment.CapitalRepayment; } } DateTime[] dates = _Reschedule_AdjustDates(ro); bool firstNonRepaid = true; if (firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0) { firstUnpaidInstallment.InterestsRepayment = firstUnpaidInstallment.PaidInterests; firstUnpaidInstallment.CapitalRepayment = firstUnpaidInstallment.PaidCapital; firstNonRepaid = false; } //interest calculation foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) { continue; } if (firstNonRepaid) { firstNonRepaid = false; if (ro.ChargeInterestDuringShift || _contract.InterestRate != ro.InterestRate || ro.NewInstallments > 0 || ro.GracePeriod > 0) { TimeSpan oldSpan; TimeSpan newSpan; if ((installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) || (ro.ChargeInterestDuringGracePeriod)) { if (1 == installment.Number) { DateTime endDate = _contract.StartDate.AddMonths(_contract.InstallmentType.NbOfMonths).AddDays(_contract.InstallmentType.NbOfDays); endDate = _nwdS.GetTheNearestValidDate(endDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); newSpan = (ro.ChargeInterestDuringShift ? installment.ExpectedDate : dates[0]) - _contract.StartDate; oldSpan = endDate - _contract.StartDate; } else { DateTime startDate = _contract.GetInstallment(installment.Number - 2).ExpectedDate; oldSpan = dates[0] - startDate; newSpan = (ro.ChargeInterestDuringShift ? dates[1] : dates[0]) - startDate; } OCurrency amount = installment.OLB - installment.PaidCapital; decimal interest = (amount.Value * newSpan.Days) * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = Convert.ToDecimal(interest); } else { installment.InterestsRepayment = 0; } } } else { if (_contract.InterestRate != ro.InterestRate || firstUnpaidInstallment.IsPartiallyRepaid || ro.NewInstallments > 0 || ro.GracePeriod > 0) { if ((installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + toNext) || (ro.ChargeInterestDuringGracePeriod)) { decimal interest = installment.OLB.Value * ro.InterestRate; interest = Math.Round(interest, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = Convert.ToDecimal(interest); } else { installment.InterestsRepayment = 0; } } } } }
private void _Reschedule_Flat(ReschedulingOptions ro) { _Reschedule_ExtendMaturity(ro.NewInstallments); DateTime[] dates = _Reschedule_AdjustDates(ro); int roundDecimals = _contract.UseCents ? 2 : 0; bool firstNonRepaid = true; Installment firstUnpaidInstallment = _contract.GetFirstUnpaidInstallment(); int moveToNext = 0; foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) { continue; } if (firstNonRepaid) { firstNonRepaid = false; if (ro.ChargeInterestDuringShift || _contract.InterestRate != ro.InterestRate || ro.NewInstallments > 0 || ro.GracePeriod > 0) { TimeSpan oldSpan; TimeSpan newSpan; if (1 == installment.Number) { DateTime endDate = _contract.StartDate.AddMonths(_contract.InstallmentType.NbOfMonths).AddDays( _contract.InstallmentType.NbOfDays); endDate = _nwdS.GetTheNearestValidDate(endDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); newSpan = (ro.ChargeInterestDuringShift ? installment.ExpectedDate : dates[0]) - _contract.StartDate; oldSpan = endDate - _contract.StartDate; } else { DateTime startDate = _contract.GetInstallment(installment.Number - 2).ExpectedDate; oldSpan = dates[0] - startDate; newSpan = (ro.ChargeInterestDuringShift ? dates[1] : dates[0]) - startDate; } if (ro.GracePeriod > 0 && installment.IsPartiallyRepaid) { installment.InterestsRepayment = installment.PaidInterests; moveToNext = 1; } else { if (installment.Number >= ro.GracePeriod + firstUnpaidInstallment.Number) { OCurrency interest = _contract.Amount * newSpan.Days * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { if (ro.ChargeInterestDuringGracePeriod) { OCurrency interest = _contract.Amount * newSpan.Days * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { installment.InterestsRepayment = 0; } } } } } else { if (ro.GracePeriod > 0 && installment.IsPartiallyRepaid) { installment.InterestsRepayment = installment.PaidInterests; moveToNext = 1; } else { if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + moveToNext) { OCurrency interest = _contract.Amount * ro.InterestRate; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { if (ro.ChargeInterestDuringGracePeriod) { OCurrency interest = _contract.Amount * ro.InterestRate; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { installment.InterestsRepayment = 0; } } } } } if (null == firstUnpaidInstallment) { return; } if (!firstUnpaidInstallment.IsPartiallyRepaid && 0 == ro.NewInstallments && 0 == ro.GracePeriod) { return; } OCurrency unpaidAmount = 0; foreach (Installment installment in _contract.InstallmentList) { unpaidAmount += installment.CapitalRepayment - installment.PaidCapital; } int nbOfUnpaid = _contract.InstallmentList.Count - firstUnpaidInstallment.Number + 1; int nextNumber = 0; if (firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0) { nextNumber = 1; } double monthly = Math.Round( Convert.ToDouble(unpaidAmount.Value / (nbOfUnpaid - ro.GracePeriod - nextNumber)), roundDecimals, MidpointRounding.AwayFromZero); for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + nextNumber) { installment.CapitalRepayment = Convert.ToDecimal(monthly); } else { installment.CapitalRepayment = 0; } } firstUnpaidInstallment.CapitalRepayment += firstUnpaidInstallment.PaidCapital; double diff = Convert.ToDouble(unpaidAmount.Value) - monthly * (nbOfUnpaid - ro.GracePeriod - nextNumber); if (diff != 0) { if (nextNumber != 1) { if (ro.GracePeriod > 0) { _contract.GetInstallment(firstUnpaidInstallment.Number + ro.GracePeriod).CapitalRepayment += Convert.ToDecimal(diff); } else { firstUnpaidInstallment.CapitalRepayment += Convert.ToDecimal(diff); } } else { _contract.GetInstallment(firstUnpaidInstallment.Number + ro.GracePeriod).CapitalRepayment += Convert.ToDecimal(diff); } } OCurrency olb = firstUnpaidInstallment.OLB; for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); installment.OLB = olb; olb -= installment.CapitalRepayment; } }
public void Test_RescheduleLateLoanProcessing() { Loan myContract = _SetContract(1000, 0.03m, OLoanTypes.Flat, new NonRepaymentPenalties(0, 0, 0.003, 0), false, 1, new DateTime(2010, 6, 6), 6); OpenCBS.CoreDomain.Contracts.Loans.LoanRepayment.Repayment.RepayLateInstallments.CalculateInstallments rLI = _SetRepaymentOptions(myContract, false); Assert.AreEqual(new DateTime(2010, 7, 6), myContract.GetInstallment(0).ExpectedDate); Assert.AreEqual(new DateTime(2010, 8, 6), myContract.GetInstallment(1).ExpectedDate); myContract.Repay(1, new DateTime(2010, 7, 6), 230, false, true); myContract.Repay(2, new DateTime(2010, 8, 6), 230, false, true); rLI.CalculateNewInstallmentsWithLateFees(new DateTime(2010, 10, 1)); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 2, RepaymentDateOffset = 0, ChargeInterestDuringShift = true, ReschedulingDate = new DateTime(2010, 10, 1) }; myContract.Reschedule(ro); myContract.AddRecheduleTransformationEvent(ro.ReschedulingDate); List<OverdueEvent> list = myContract.Events.GetOverdueEvents(); Assert.AreEqual(list[0].Code, "LLRL"); // good loan to the bad loan OverdueEvent e = myContract.GetOverdueEvent(new DateTime(2010, 10, 1)); Assert.AreEqual(e.Code, "GLLL"); e = myContract.GetOverdueEvent(new DateTime(2010, 10, 20)); Assert.AreEqual(e, null); myContract.Repay(1, new DateTime(2010, 10, 20), 500, false, true); //bad loan to the late loan e = myContract.GetOverdueEvent(new DateTime(2010, 10, 21)); Assert.AreEqual(e.Code, "LLGL"); myContract.Repay(1, new DateTime(2010, 11, 6), 500, false, true); //late loan to the good loan }
public void Reschedule_FixedPrincipal_ExtendMaturity_ShiftDate_ChangeInterestRate_PartiallyPaid_UseCents() { Loan loan = _GetLoan(_GetFixedPrincipalProduct_WithCents(), 10000, 0.04m, 6, new DateTime(2010, 1, 11)); loan.Repay(1, new DateTime(2010, 2, 11), 2066.67m, false, true); loan.Repay(2, new DateTime(2010, 3, 11), 2000.00m, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 4, 5), 1500.00m, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 2, RepaymentDateOffset = 13, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-04-26", 226.41m, 2194.46m, 6666.66m, 1300.00m, 200.00m); _AssertInstallment(loan, 3, "2010-05-25", 134.17m, 894.44m, 4472.20m, 0, 0); _AssertInstallment(loan, 4, "2010-06-25", 107.33m, 894.44m, 3577.76m, 0, 0); _AssertInstallment(loan, 5, "2010-07-26", 80.50m, 894.44m, 2683.32m, 0, 0); _AssertInstallment(loan, 6, "2010-08-25", 53.67m, 894.44m, 1788.88m, 0, 0); _AssertInstallment(loan, 7, "2010-09-27", 26.83m, 894.44m, 894.44m, 0, 0); }
public void Reschedule_Annuity_ShiftDate_ChangeInterestRate() { Loan loan = _GetLoan(_GetAnnuityProduct_RoundingTypeBegin(), 10000, 0.04m, 6, new DateTime(2010, 1, 15)); loan.Repay(1, new DateTime(2010, 2, 15), 1906, false, true); loan.Repay(2, new DateTime(2010, 3, 15), 1908, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 16, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 315, 1656); _AssertInstallment(loan, 3, "2010-06-01", 158, 1705); _AssertInstallment(loan, 4, "2010-07-01", 107, 1756); _AssertInstallment(loan, 5, "2010-08-02", 54, 1809); }
public void Reschedule_Flat_ExtendMaturity_ShiftDate() { Loan loan = _GetLoan(_GetFlatProduct(), 10000, 0.04m, 6, new DateTime(2009, 12, 22)); loan.Repay(1, new DateTime(2010, 1, 22), 2067, false, true); loan.Repay(2, new DateTime(2010, 1, 22), 2067, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 2, RepaymentDateOffset = 21, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-04-12", 700, 1111, 6666, 0, 0); _AssertInstallment(loan, 3, "2010-05-12", 400, 1111, 5555, 0, 0); _AssertInstallment(loan, 4, "2010-06-14", 400, 1111, 4444, 0, 0); _AssertInstallment(loan, 5, "2010-07-12", 400, 1111, 3333, 0, 0); _AssertInstallment(loan, 6, "2010-08-12", 400, 1111, 2222, 0, 0); _AssertInstallment(loan, 7, "2010-09-13", 400, 1111, 1111, 0, 0); }
public void Reschedule_Annuity_ShiftDate_ChangeInterestRate_PartiallyPaid_UseCents() { Loan loan = _GetLoan(_GetAnnuityProduct_RoundingTypeBegin_WithCents(), 10000, 0.04m, 6, new DateTime(2010, 1, 12)); loan.Repay(1, new DateTime(2010, 2, 12), 1907.62m, false, true); loan.Repay(2, new DateTime(2010, 3, 12), 1907.62m, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 4, 5), 1500, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 19, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 272.15m, 2644.40m, 6924.46m); _AssertInstallment(loan, 3, "2010-06-01", 128.40m, 1384.73m, 4280.06m); _AssertInstallment(loan, 4, "2010-07-01", 86.86m, 1426.27m, 2895.33m); _AssertInstallment(loan, 5, "2010-08-02", 44.07m, 1469.06m, 1469.06m); }
public void Reschedule_Flat_ShiftDate_ChangeInterestRate() { Loan loan = _GetLoan(_GetFlatProduct(), 10000, 0.04m, 6, new DateTime(2009, 12, 24)); loan.Repay(1, new DateTime(2010, 1, 25), 2067, false, true); loan.Repay(2, new DateTime(2010, 2, 24), 2067, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 38, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 707, 1667); // 10_000 * 0.03 * 66 / 28 _AssertInstallment(loan, 3, "2010-06-01", 300, 1666); _AssertInstallment(loan, 4, "2010-07-01", 300, 1667); _AssertInstallment(loan, 5, "2010-08-02", 300, 1666); }
public void Reschedule_Annuity_ShiftDate_ChangeInterestRate_UseCents() { Loan loan = _GetLoan(_GetAnnuityProduct_WithCents(), 10000, 0.04m, 6, new DateTime(2010, 1, 12)); loan.Repay(1, new DateTime(2010, 2, 12), 1907.62m, false, true); loan.Repay(2, new DateTime(2010, 3, 12), 1907.62m, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 19, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 335.05m, 1655.13m); _AssertInstallment(loan, 3, "2010-06-01", 158.08m, 1704.79m); _AssertInstallment(loan, 4, "2010-07-01", 106.94m, 1755.93m); _AssertInstallment(loan, 5, "2010-08-02", 54.26m, 1808.61m); }
public void Reschedule_Flat_ShiftDate_ChangeInterestRate_PartiallyPaid_UseCents() { Assert.Ignore(); Loan loan = _GetLoan(_GetFlatProduct_WithCents(), 10000, 0.04m, 6, new DateTime(2009, 12, 22)); loan.Repay(1, new DateTime(2010, 1, 22), 2066.67m, false, true); loan.Repay(2, new DateTime(2010, 2, 22), 2066.67m, false, true); // loan.Repay(3, new DateTime(2010, 3, 5), 1500.00m, false, 0, 0, true, 200, true, false, false, OPaymentMethods.Cash); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 40, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 728.57m, 2641.65m, 6666.66m); _AssertInstallment(loan, 3, "2010-06-01", 300.00m, 1341.67m, 4025.01m); _AssertInstallment(loan, 4, "2010-07-01", 300.00m, 1341.67m, 2683.34m); _AssertInstallment(loan, 5, "2010-08-02", 300.00m, 1341.67m, 1341.67m); }
public void Reschedule_Annuity_ShiftDate_PartiallyPaid() { Loan loan = _GetLoan(_GetAnnuityProduct_RoundingTypeBegin(), 10000, 0.04m, 6, new DateTime(2010, 1, 15)); loan.Repay(1, new DateTime(2010, 2, 12), 1906, false, true); loan.Repay(2, new DateTime(2010, 3, 12), 1908, false, true); PaymentMethod paymentMethod = new PaymentMethod(1, "Cash", "", false); loan.Repay(3, new DateTime(2010, 4, 5), 1500, false, 0, 0, true, 200, true, false, false, paymentMethod); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(2).IsPartiallyRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 0, RepaymentDateOffset = 16, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-03", 341, 2625, 6926); _AssertInstallment(loan, 3, "2010-06-01", 172, 1378, 4301); _AssertInstallment(loan, 4, "2010-07-01", 117, 1433, 2923); _AssertInstallment(loan, 5, "2010-08-02", 60, 1490, 1490); }
public void Reschedule_Annuity_ExtendMaturity() { Loan loan = _GetLoan(_GetAnnuityProduct_RoundingTypeBegin(), 10000, 0.04m, 6, new DateTime(2010, 1, 15)); loan.Repay(1, new DateTime(2010, 2, 12), 1906, false, true); loan.Repay(2, new DateTime(2010, 3, 12), 1908, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 2, RepaymentDateOffset = 0, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-04-15", 277, 1046, 6926, 0, 0); _AssertInstallment(loan, 3, "2010-05-17", 235, 1086, 5880, 0, 0); _AssertInstallment(loan, 4, "2010-06-15", 192, 1129, 4794, 0, 0); _AssertInstallment(loan, 5, "2010-07-15", 147, 1174, 3665, 0, 0); _AssertInstallment(loan, 6, "2010-08-16", 100, 1221, 2491, 0, 0); _AssertInstallment(loan, 7, "2010-09-15", 51, 1270, 1270, 0, 0); }
public void Reschedule_ContractClosed_ThrowException() { Loan loan = _GetContract_6Month_Flat(); loan.Repay(1, new DateTime(2006, 1, 30), 1120, true, true); Assert.AreEqual(loan.Closed, true); ReschedulingOptions ro = new ReschedulingOptions(); loan.Reschedule(ro); }
private void _Reschedule_DecliningFixedInstallments(ReschedulingOptions ro) { int months = _contract.InstallmentType.NbOfMonths; int days = _contract.InstallmentType.NbOfDays; int rounding = _contract.UseCents ? 2 : 0; // Split installments into two lists - paid and unpaid List<Installment> paidInstallments = new List<Installment>(); List<Installment> unpaidInstallments = new List<Installment>(); foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) { paidInstallments.Add(installment); } else { unpaidInstallments.Add(installment); } } // Add new installments if (ro.NewInstallments > 0) { int lastNumber = unpaidInstallments[unpaidInstallments.Count - 1].Number; for (int i = 1; i <= ro.NewInstallments; i++) { Installment installment = new Installment(); installment.Number = lastNumber + i; installment.CapitalRepayment = 0; installment.PaidCapital = 0; installment.InterestsRepayment = 0; installment.PaidInterests = 0; installment.FeesUnpaid = 0; installment.CommissionsUnpaid = 0; unpaidInstallments.Add(installment); } } // We want to recalculate installments if new installments are introduced, // interest rate is changed, or the first unpaid installment is partially paid. int moveToNext = 0; if (unpaidInstallments[0].IsPartiallyRepaid && ro.GracePeriod > 0) { moveToNext = 1; } bool recalculate = ro.NewInstallments > 0 || _contract.InterestRate != ro.InterestRate || unpaidInstallments[0].IsPartiallyRepaid || ro.GracePeriod != ro.NewInstallments; //if (ro.NewInstallments > 0 || _contract.InterestRate != ro.InterestRate || unpaidInstallments[0].IsPartiallyRepaid) if (recalculate) { OCurrency initialOlb = unpaidInstallments[0].OLB - unpaidInstallments[0].PaidCapital; int n = unpaidInstallments.Count - ro.GracePeriod - moveToNext; // We use the formula below to calculate monthly annuity payments //OCurrency monthly = 0 == r ? initialOlb / n : initialOlb * r * Math.Pow(1 + r, n) / (Math.Pow(1 + r, n) - 1); OCurrency monthly = _contract.VPM(initialOlb, n, ro.InterestRate); monthly = Math.Round(monthly.Value, rounding, MidpointRounding.AwayFromZero); //_Reschedule_Round(monthly); // Now loop through the unpaid installments and calculate the principal and the interest OCurrency sum = 0; OCurrency olb = initialOlb; foreach (Installment installment in unpaidInstallments) { OCurrency interest = olb * ro.InterestRate; if (installment.IsPartiallyRepaid && ro.GracePeriod > 0) { installment.InterestsRepayment = installment.PaidInterests; installment.CapitalRepayment = installment.PaidCapital; } else { if (installment.Number > ro.GracePeriod + unpaidInstallments[0].Number - 1 + moveToNext) { OCurrency principal = monthly - interest; installment.CapitalRepayment = Math.Round(principal.Value, rounding, MidpointRounding.AwayFromZero); } else { installment.CapitalRepayment = 0; } if ((installment.Number > ro.GracePeriod + unpaidInstallments[0].Number - 1 + moveToNext) || (ro.ChargeInterestDuringGracePeriod)) { installment.InterestsRepayment = Math.Round(interest.Value, rounding, MidpointRounding.AwayFromZero); } else { installment.InterestsRepayment = 0; } if (ro.GracePeriod > 0) { if (!installment.IsPartiallyRepaid) { sum += installment.CapitalRepayment; installment.OLB = olb; olb -= installment.CapitalRepayment.Value; } } else { sum += installment.CapitalRepayment; installment.OLB = olb; olb -= installment.CapitalRepayment.Value; } } } // As a result of rounding there might be some difference between the // initial OLB and the sum of installment repayments. Thus we have to // compensate for this difference. OCurrency diff = sum - initialOlb; if (diff != 0) { unpaidInstallments[ro.GracePeriod + moveToNext].CapitalRepayment -= diff; int firstNumber = unpaidInstallments[ro.GracePeriod + moveToNext].Number; // Iterate through all unpaid but first foreach (Installment installment in unpaidInstallments.FindAll(x => x.Number > firstNumber)) { installment.OLB += diff; } } } // Adjust the amount of interest for the first installment if ((ro.RepaymentDateOffset > 0 && ro.ChargeInterestDuringShift) || (_contract.FirstDateChanged && 1 == unpaidInstallments[0].Number && ro.GracePeriod > 0 && ro.ChargeInterestDuringGracePeriod) || (_contract.FirstDateChanged && 1 == unpaidInstallments[0].Number && 0 == ro.GracePeriod)) { DateTime startDate; DateTime endDate1; DateTime endDate2; if (0 == paidInstallments.Count) { startDate = _contract.StartDate; endDate1 = startDate.AddMonths(months).AddDays(days); endDate2 = _contract.FirstInstallmentDate.AddDays(ro.RepaymentDateOffset * (ro.ChargeInterestDuringShift ? 1 : 0)); } else { startDate = paidInstallments[paidInstallments.Count - 1].ExpectedDate; endDate1 = startDate.AddMonths(months).AddDays(days); endDate2 = endDate1.AddDays(ro.RepaymentDateOffset * (ro.ChargeInterestDuringShift ? 1 : 0)); } TimeSpan oldSpan = endDate1 - startDate; TimeSpan newSpan = endDate2 - startDate; double olb = Convert.ToDouble(unpaidInstallments[0].OLB.Value); double ratio; if (ro.GracePeriod > 0 && !ro.ChargeInterestDuringGracePeriod) { ratio = (double)(newSpan.Days - oldSpan.Days) / oldSpan.Days; } else { ratio = (double)newSpan.Days / oldSpan.Days; } OCurrency interest = Convert.ToDecimal(olb) * ro.InterestRate * Convert.ToDecimal(ratio); interest = Math.Round(interest.Value, rounding, MidpointRounding.AwayFromZero); unpaidInstallments[0].InterestsRepayment = interest; } if (unpaidInstallments[0].IsPartiallyRepaid) { int instNum = ro.GracePeriod > 0 ? ro.GracePeriod - 1 : 0; unpaidInstallments[instNum].CapitalRepayment += unpaidInstallments[0].PaidCapital; unpaidInstallments[instNum].OLB += unpaidInstallments[0].PaidCapital; } // Adjust dates DateTime baseDate = unpaidInstallments[0].ExpectedDate.AddDays(ro.RepaymentDateOffset); int num = 0; foreach (Installment installment in unpaidInstallments) { DateTime d = baseDate.AddMonths(months * num).AddDays(days * num); d = _nwdS.GetTheNearestValidDate(d, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); installment.ExpectedDate = d; num++; } // Merge the lists back into one _contract.InstallmentList = paidInstallments; _contract.InstallmentList.AddRange(unpaidInstallments); _contract.NbOfInstallments = _contract.InstallmentList.Count(); }
public void Reschedule_FixedPrincipal_ChangeInterest() { Loan loan = _GetLoan(_GetFixedPrincipalProduct(), 10000, 0.04m, 6, new DateTime(2009, 12, 24)); loan.Repay(1, new DateTime(2010, 1, 25), 2067, false, true); loan.Repay(2, new DateTime(2010, 2, 24), 2000, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.03m, NewInstallments = 0, RepaymentDateOffset = 0, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-03-24", 200, 1666); _AssertInstallment(loan, 3, "2010-04-26", 150, 1667); _AssertInstallment(loan, 4, "2010-05-24", 100, 1666); _AssertInstallment(loan, 5, "2010-06-24", 50, 1667); }
private void _Reschedule_Flat(ReschedulingOptions ro) { _Reschedule_ExtendMaturity(ro.NewInstallments); DateTime[] dates = _Reschedule_AdjustDates(ro); int roundDecimals = _contract.UseCents ? 2 : 0; bool firstNonRepaid = true; Installment firstUnpaidInstallment = _contract.GetFirstUnpaidInstallment(); int moveToNext = 0; foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) continue; if (firstNonRepaid) { firstNonRepaid = false; if (ro.ChargeInterestDuringShift || _contract.InterestRate != ro.InterestRate || ro.NewInstallments > 0 || ro.GracePeriod > 0) { TimeSpan oldSpan; TimeSpan newSpan; if (1 == installment.Number) { DateTime endDate = _contract.StartDate.AddMonths(_contract.InstallmentType.NbOfMonths).AddDays( _contract.InstallmentType.NbOfDays); endDate = _nwdS.GetTheNearestValidDate(endDate, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); newSpan = (ro.ChargeInterestDuringShift ? installment.ExpectedDate : dates[0]) - _contract.StartDate; oldSpan = endDate - _contract.StartDate; } else { DateTime startDate = _contract.GetInstallment(installment.Number - 2).ExpectedDate; oldSpan = dates[0] - startDate; newSpan = (ro.ChargeInterestDuringShift ? dates[1] : dates[0]) - startDate; } if (ro.GracePeriod > 0 && installment.IsPartiallyRepaid) { installment.InterestsRepayment = installment.PaidInterests; moveToNext = 1; } else { if (installment.Number >= ro.GracePeriod + firstUnpaidInstallment.Number) { OCurrency interest = _contract.Amount * newSpan.Days * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { if (ro.ChargeInterestDuringGracePeriod) { OCurrency interest = _contract.Amount * newSpan.Days * ro.InterestRate / oldSpan.Days; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { installment.InterestsRepayment = 0; } } } } } else { if (ro.GracePeriod > 0 && installment.IsPartiallyRepaid) { installment.InterestsRepayment = installment.PaidInterests; moveToNext = 1; } else { if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + moveToNext) { OCurrency interest = _contract.Amount * ro.InterestRate; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { if (ro.ChargeInterestDuringGracePeriod) { OCurrency interest = _contract.Amount * ro.InterestRate; interest = Math.Round(interest.Value, roundDecimals, MidpointRounding.AwayFromZero); installment.InterestsRepayment = interest; } else { installment.InterestsRepayment = 0; } } } } } if (null == firstUnpaidInstallment) return; if (!firstUnpaidInstallment.IsPartiallyRepaid && 0 == ro.NewInstallments && 0 == ro.GracePeriod) return; OCurrency unpaidAmount = 0; foreach (Installment installment in _contract.InstallmentList) { unpaidAmount += installment.CapitalRepayment - installment.PaidCapital; } int nbOfUnpaid = _contract.InstallmentList.Count - firstUnpaidInstallment.Number + 1; int nextNumber = 0; if (firstUnpaidInstallment.IsPartiallyRepaid && ro.GracePeriod > 0) { nextNumber = 1; } double monthly = Math.Round( Convert.ToDouble(unpaidAmount.Value / (nbOfUnpaid - ro.GracePeriod - nextNumber)), roundDecimals, MidpointRounding.AwayFromZero); for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); if (installment.Number > ro.GracePeriod + firstUnpaidInstallment.Number - 1 + nextNumber) { installment.CapitalRepayment = Convert.ToDecimal(monthly); } else { installment.CapitalRepayment = 0; } } firstUnpaidInstallment.CapitalRepayment += firstUnpaidInstallment.PaidCapital; double diff = Convert.ToDouble(unpaidAmount.Value) - monthly * (nbOfUnpaid - ro.GracePeriod - nextNumber); if (diff != 0) { if (nextNumber != 1) if (ro.GracePeriod > 0) { _contract.GetInstallment(firstUnpaidInstallment.Number + ro.GracePeriod).CapitalRepayment += Convert.ToDecimal(diff); } else { firstUnpaidInstallment.CapitalRepayment += Convert.ToDecimal(diff); } else { _contract.GetInstallment(firstUnpaidInstallment.Number + ro.GracePeriod).CapitalRepayment += Convert.ToDecimal(diff); } } OCurrency olb = firstUnpaidInstallment.OLB; for (int i = firstUnpaidInstallment.Number - 1; i < _contract.InstallmentList.Count; i++) { Installment installment = _contract.GetInstallment(i); installment.OLB = olb; olb -= installment.CapitalRepayment; } }
public void Reschedule_FixedPrincipal_ExtendMaturity_ShiftDate() { Loan loan = _GetLoan(_GetFixedPrincipalProduct(), 10000, 0.04m, 6, new DateTime(2010, 1, 11)); loan.Repay(1, new DateTime(2010, 2, 11), 2067, false, true); loan.Repay(2, new DateTime(2010, 3, 11), 2000, false, true); Assert.AreEqual(loan.GetInstallment(0).IsRepaid, true); Assert.AreEqual(loan.GetInstallment(1).IsRepaid, true); ReschedulingOptions ro = new ReschedulingOptions { InterestRate = 0.04m, NewInstallments = 2, RepaymentDateOffset = 43, ChargeInterestDuringShift = true }; loan.Reschedule(ro); _AssertInstallment(loan, 2, "2010-05-25", 625, 1111, 6666, 0, 0); _AssertInstallment(loan, 3, "2010-06-25", 222, 1111, 5555, 0, 0); _AssertInstallment(loan, 4, "2010-07-26", 178, 1111, 4444, 0, 0); _AssertInstallment(loan, 5, "2010-08-25", 133, 1111, 3333, 0, 0); _AssertInstallment(loan, 6, "2010-09-27", 89, 1111, 2222, 0, 0); _AssertInstallment(loan, 7, "2010-10-25", 44, 1111, 1111, 0, 0); }
public RescheduleLoanEvent Reschedule(ReschedulingOptions ro) { if (Closed) throw new ReschedulingContractClosedException(); RescheduleLoan reschedule = new RescheduleLoan(); RescheduleLoanEvent rSe = reschedule.Reschedule(ro, this, _nwdS, _generalSettings); _nbOfInstallments = _installmentList.Count; Events.Add(rSe); return rSe; }
private void _Reschedule_DecliningFixedInstallments(ReschedulingOptions ro) { int months = _contract.InstallmentType.NbOfMonths; int days = _contract.InstallmentType.NbOfDays; int rounding = _contract.UseCents ? 2 : 0; // Split installments into two lists - paid and unpaid List <Installment> paidInstallments = new List <Installment>(); List <Installment> unpaidInstallments = new List <Installment>(); foreach (Installment installment in _contract.InstallmentList) { if (installment.IsRepaid) { paidInstallments.Add(installment); } else { unpaidInstallments.Add(installment); } } // Add new installments if (ro.NewInstallments > 0) { int lastNumber = unpaidInstallments[unpaidInstallments.Count - 1].Number; for (int i = 1; i <= ro.NewInstallments; i++) { Installment installment = new Installment(); installment.Number = lastNumber + i; installment.CapitalRepayment = 0; installment.PaidCapital = 0; installment.InterestsRepayment = 0; installment.PaidInterests = 0; installment.FeesUnpaid = 0; installment.CommissionsUnpaid = 0; unpaidInstallments.Add(installment); } } // We want to recalculate installments if new installments are introduced, // interest rate is changed, or the first unpaid installment is partially paid. int moveToNext = 0; if (unpaidInstallments[0].IsPartiallyRepaid && ro.GracePeriod > 0) { moveToNext = 1; } bool recalculate = ro.NewInstallments > 0 || _contract.InterestRate != ro.InterestRate || unpaidInstallments[0].IsPartiallyRepaid || ro.GracePeriod != ro.NewInstallments; //if (ro.NewInstallments > 0 || _contract.InterestRate != ro.InterestRate || unpaidInstallments[0].IsPartiallyRepaid) if (recalculate) { OCurrency initialOlb = unpaidInstallments[0].OLB - unpaidInstallments[0].PaidCapital; int n = unpaidInstallments.Count - ro.GracePeriod - moveToNext; // We use the formula below to calculate monthly annuity payments //OCurrency monthly = 0 == r ? initialOlb / n : initialOlb * r * Math.Pow(1 + r, n) / (Math.Pow(1 + r, n) - 1); OCurrency monthly = _contract.VPM(initialOlb, n, ro.InterestRate); monthly = Math.Round(monthly.Value, rounding, MidpointRounding.AwayFromZero); //_Reschedule_Round(monthly); // Now loop through the unpaid installments and calculate the principal and the interest OCurrency sum = 0; OCurrency olb = initialOlb; foreach (Installment installment in unpaidInstallments) { OCurrency interest = olb * ro.InterestRate; if (installment.IsPartiallyRepaid && ro.GracePeriod > 0) { installment.InterestsRepayment = installment.PaidInterests; installment.CapitalRepayment = installment.PaidCapital; } else { if (installment.Number > ro.GracePeriod + unpaidInstallments[0].Number - 1 + moveToNext) { OCurrency principal = monthly - interest; installment.CapitalRepayment = Math.Round(principal.Value, rounding, MidpointRounding.AwayFromZero); } else { installment.CapitalRepayment = 0; } if ((installment.Number > ro.GracePeriod + unpaidInstallments[0].Number - 1 + moveToNext) || (ro.ChargeInterestDuringGracePeriod)) { installment.InterestsRepayment = Math.Round(interest.Value, rounding, MidpointRounding.AwayFromZero); } else { installment.InterestsRepayment = 0; } if (ro.GracePeriod > 0) { if (!installment.IsPartiallyRepaid) { sum += installment.CapitalRepayment; installment.OLB = olb; olb -= installment.CapitalRepayment.Value; } } else { sum += installment.CapitalRepayment; installment.OLB = olb; olb -= installment.CapitalRepayment.Value; } } } // As a result of rounding there might be some difference between the // initial OLB and the sum of installment repayments. Thus we have to // compensate for this difference. OCurrency diff = sum - initialOlb; if (diff != 0) { unpaidInstallments[ro.GracePeriod + moveToNext].CapitalRepayment -= diff; int firstNumber = unpaidInstallments[ro.GracePeriod + moveToNext].Number; // Iterate through all unpaid but first foreach (Installment installment in unpaidInstallments.FindAll(x => x.Number > firstNumber)) { installment.OLB += diff; } } } // Adjust the amount of interest for the first installment if ((ro.RepaymentDateOffset > 0 && ro.ChargeInterestDuringShift) || (_contract.FirstDateChanged && 1 == unpaidInstallments[0].Number && ro.GracePeriod > 0 && ro.ChargeInterestDuringGracePeriod) || (_contract.FirstDateChanged && 1 == unpaidInstallments[0].Number && 0 == ro.GracePeriod)) { DateTime startDate; DateTime endDate1; DateTime endDate2; if (0 == paidInstallments.Count) { startDate = _contract.StartDate; endDate1 = startDate.AddMonths(months).AddDays(days); endDate2 = _contract.FirstInstallmentDate.AddDays(ro.RepaymentDateOffset * (ro.ChargeInterestDuringShift ? 1 : 0)); } else { startDate = paidInstallments[paidInstallments.Count - 1].ExpectedDate; endDate1 = startDate.AddMonths(months).AddDays(days); endDate2 = endDate1.AddDays(ro.RepaymentDateOffset * (ro.ChargeInterestDuringShift ? 1 : 0)); } TimeSpan oldSpan = endDate1 - startDate; TimeSpan newSpan = endDate2 - startDate; double olb = Convert.ToDouble(unpaidInstallments[0].OLB.Value); double ratio; if (ro.GracePeriod > 0 && !ro.ChargeInterestDuringGracePeriod) { ratio = (double)(newSpan.Days - oldSpan.Days) / oldSpan.Days; } else { ratio = (double)newSpan.Days / oldSpan.Days; } OCurrency interest = Convert.ToDecimal(olb) * ro.InterestRate * Convert.ToDecimal(ratio); interest = Math.Round(interest.Value, rounding, MidpointRounding.AwayFromZero); unpaidInstallments[0].InterestsRepayment = interest; } if (unpaidInstallments[0].IsPartiallyRepaid) { int instNum = ro.GracePeriod > 0 ? ro.GracePeriod - 1 : 0; unpaidInstallments[instNum].CapitalRepayment += unpaidInstallments[0].PaidCapital; unpaidInstallments[instNum].OLB += unpaidInstallments[0].PaidCapital; } // Adjust dates DateTime baseDate = unpaidInstallments[0].ExpectedDate.AddDays(ro.RepaymentDateOffset); int num = 0; foreach (Installment installment in unpaidInstallments) { DateTime d = baseDate.AddMonths(months * num).AddDays(days * num); d = _nwdS.GetTheNearestValidDate(d, _generalSettings.IsIncrementalDuringDayOff, _generalSettings.DoNotSkipNonWorkingDays, true); installment.ExpectedDate = d; num++; } // Merge the lists back into one _contract.InstallmentList = paidInstallments; _contract.InstallmentList.AddRange(unpaidInstallments); _contract.NbOfInstallments = _contract.InstallmentList.Count(); }