Files
monorepo/LCU.WebApps.Indirect.ReserveManagementSystem/Api/ReserveManagementSystemApi/Helpers/LoanCalculationHelper.cs
T
2026-03-17 08:54:34 -05:00

121 lines
5.2 KiB
C#

using ReserveManagementSystemLib.Models;
namespace ReserveManagementSystemApi.Helpers
{
public class LoanCalculationHelper
{
private decimal _daysInterest;
private LoanReserve loanReserve = new LoanReserve();
public LoanCalculationHelper()
{
}
public LoanReserve CalculateBuyDown(CalculationConfig cc)
{
// Case - Buy down Below Threshold where difference in rates is less than or equal to 0.25 percent
if (cc.BuydownRateDifference() <= cc.NoReserveThreshold()) //we already know it's positive
{
loanReserve.ReserveAmount = 0;
loanReserve.ReserveTypeMessage = "No Reserve";
loanReserve.FlatFeePaid = false;
return loanReserve;
}
// Case - This is a regular Buy Down
_daysInterest = cc.BuydownRateDifference() / 100 * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount /
365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12);
loanReserve.ReserveAmount = -(cc.BuyFinanceCharge - cc.ContractFinanceCharge + _daysInterest);
loanReserve.ReserveTypeMessage = "Buy Down Reserve";
loanReserve.FlatFeePaid = false;
return loanReserve;
}
public LoanReserve CalculateNoMarkup(CalculationConfig cc)
{
// Case - Contract rate is equal to the Buy rate
_daysInterest = cc.StdRate() * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount /
365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12);
loanReserve.ReserveAmount = (cc.ContractFinanceCharge - cc.BuyFinanceCharge + _daysInterest);
loanReserve.ReserveTypeMessage = "No Reserve";
loanReserve.FlatFeePaid = false;
return loanReserve;
}
public LoanReserve CalculateStandardMarkup(CalculationConfig cc)
{
// Case - Contract rate is greater than Buy rate
_daysInterest = (1 - cc.ReserveDiscount()/100) * cc.StdRate() / 100 * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount /
365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12);
loanReserve.ReserveAmount = ((1 - cc.ReserveDiscount() / 100) * (cc.ContractFinanceCharge - cc.BuyFinanceCharge) + _daysInterest);
if ((double)cc.StdRate() <= 2.5)
{
loanReserve.ReserveTypeMessage = "Standard Reserve";
}
else
{
loanReserve.ReserveTypeMessage = "Standard Reserve - Exceeds Max Markup";
}
return loanReserve;
}
public LoanReserve AssignFlatFee(LoanReserve loanReserve, CalculationConfig cc)
{
loanReserve.FlatFeePaid = false;
if (loanReserve.ReserveAmount < (cc.FlatFeePercent() / 100 * cc._loanData.LoanAmount * (1 - cc.ReserveDiscount() / 100)))
{
loanReserve.ReserveAmount = (cc.FlatFeePercent() / 100 * cc._loanData.LoanAmount * (1 - cc.ReserveDiscount() / 100));
loanReserve.ReserveTypeMessage = "Flat Fee Reserve";
loanReserve.FlatFeePaid = true;
}
return loanReserve;
}
public decimal FinanceCharge(int term, decimal loanAmount, decimal rate)
{
if (rate == 0) { return 0; }
double paymentRate = (double)rate / (double)1200;
decimal rateFactor = (decimal)Math.Pow((1 + paymentRate), (double)term);
return (loanAmount * term * rate / 1200 * rateFactor) / (rateFactor - 1) - loanAmount;
}
internal DateTime GetLastChargebackDate(DateTime loanDate, DateTime firstPmtDate, int numOfChargebackPayments, Int16? numOfChargebackDays)
{
// in rare cases that date from loan date is most recent, check table values and compare between loan date vs. first payment date
int result = DateTime.Compare(loanDate.AddDays((Double)numOfChargebackDays), firstPmtDate.AddMonths(numOfChargebackPayments - 1));
if (result > 0)
{
return loanDate.AddDays((Double)numOfChargebackDays); //Loan Date is most recent
}
return firstPmtDate.AddMonths(numOfChargebackPayments - 1);
}
internal decimal CalculateChargeback(decimal reserve, int term, int remainingMonths)
{
return reserve * MonthFactor(remainingMonths) / MonthFactor(term);
}
private decimal MonthFactor(int numberOfMonths)
{
return numberOfMonths * (numberOfMonths + 1) / 2;
}
public int GetRemainingMonths(Loan loanData)
{
if (loanData.PaidLoanDate >= loanData.FinalPmtDate)
return 0;
var monthsOccurred = (loanData.PaidLoanDate.Value - loanData.FirstPmtDate).TotalDays / (365) * 12 + 1;
var remainingMonths = (int)(loanData.Term - Math.Round((decimal)monthsOccurred, 0));
return remainingMonths;
}
}
}