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

315 lines
12 KiB
C#

using ReserveManagementSystemApi.Helpers;
using ReserveManagementSystemLib.Models;
namespace ReserveManagementSystemApi.Services
{
public class LoanCalculationService : ILoanCalculationService
{
private readonly LoanCalculationHelper _loanCalculationHelper;
private readonly BoatLoanCalculationHelper _boatLoanCalculationHelper;
private CalculationConfig _calculationConfig;
public LoanCalculationService()
{
_loanCalculationHelper = new LoanCalculationHelper();
_boatLoanCalculationHelper = new BoatLoanCalculationHelper();
}
public LoanReserve CalculateSingleReserve(Loan loan)
{
var result = new LoanReserve();
try
{
_calculationConfig = new CalculationConfig(loan);
if (!loan.IsBoatLoan(loan))
{
result = ReserveCalculation(loan);
}
else
{
result = BoatReserveCalculation(loan);
}
}
catch (Exception e)
{
Log.Error("Failure to Calculate Loan: {0} {1}", e.Message, e.StackTrace);
result.ReserveTypeMessage = "Exception while calculating reserve";
}
return result;
}
public LoanChargeback CalculateSingleChargeback(Loan loan)
{
var result = new LoanChargeback();
try
{
_calculationConfig = new CalculationConfig(loan);
if (!loan.IsBoatLoan(loan))
{
result = ChargebackCalculation(loan);
}
else
{
result = BoatChargebackCalculation(loan);
}
}
catch (Exception e)
{
Log.Error("Failure to Calculate Chargeback: {0} {1}", e.Message, e.StackTrace);
}
return result;
}
public Loan CalculateLoanReserve(Loan loan, decimal? originalReserve = null, DateTime? originalExportReserveDate = null)
{
try
{
_calculationConfig = new CalculationConfig(loan);
var loanReserve = new LoanReserve();
if (!loan.IsBoatLoan(loan))
{
loanReserve = ReserveCalculation(loan);
}
else
{
loanReserve = BoatReserveCalculation(loan);
}
loan = AssignReserveValues(loan, loanReserve);
if (originalReserve.HasValue)
{
CalculateReserveAdjustment(loan, originalReserve, originalExportReserveDate);
}
}
catch (Exception e)
{
Log.Error("Failure to Calculate Reserve: {0} {1}", e.Message, e.StackTrace);
loan.ReserveMessage = "Exception while calculating reserve";
}
return loan;
}
private static void CalculateReserveAdjustment(Loan loan, decimal? originalReserve, DateTime? originalExportReserveDate)
{
var updatedReserveSaved = Math.Round(loan.ReserveAmount, 4, MidpointRounding.AwayFromZero); //this represents the amount saved to DB
var updatedReserveRounded = Math.Round(updatedReserveSaved, 2, MidpointRounding.AwayFromZero);
var originalReserveRounded = Math.Round(originalReserve.Value, 2, MidpointRounding.AwayFromZero);
if (updatedReserveRounded != originalReserveRounded)
{
loan.Adjustment = (updatedReserveRounded - originalReserveRounded);
loan.ExportedReserveDate = null; //we only want to remove last export date if reserve amount changed
}
else
{
loan.ExportedReserveDate = originalExportReserveDate;
}
}
private LoanReserve ReserveCalculation(Loan loanData)
{
var loanReserve = new LoanReserve();
_calculationConfig.BuyFinanceCharge = _loanCalculationHelper.FinanceCharge(_calculationConfig.LoanData().Term, loanData.LoanAmount, loanData.BuyRate); // Finance charge using BuyRate%
_calculationConfig.ContractFinanceCharge = _loanCalculationHelper.FinanceCharge(_calculationConfig.LoanData().Term, loanData.LoanAmount, loanData.ContractRate); // Finance charge using ContractRate%
if (loanData.ContractRate < loanData.BuyRate)
{
//Calculate Buy Down.
return _loanCalculationHelper.CalculateBuyDown(_calculationConfig);
}
else
{
// Calculate Reserve Amount for all other loans.
if ((double)loanData.ContractRate == (double)loanData.BuyRate)
{
loanReserve = _loanCalculationHelper.CalculateNoMarkup(_calculationConfig);
}
else
{
loanReserve = _loanCalculationHelper.CalculateStandardMarkup(_calculationConfig);
}
//Assign any Flat Fees if applicable
return _loanCalculationHelper.AssignFlatFee(loanReserve, _calculationConfig);
}
}
private LoanReserve BoatReserveCalculation(Loan loanData)
{
if (loanData.ContractRate < loanData.BuyRate)
{
_calculationConfig.BuyFinanceCharge = _loanCalculationHelper.FinanceCharge(_calculationConfig.LoanData().Term, loanData.LoanAmount, loanData.BuyRate); // Finance charge using BuyRate%
_calculationConfig.ContractFinanceCharge = _loanCalculationHelper.FinanceCharge(_calculationConfig.LoanData().Term, loanData.LoanAmount, loanData.ContractRate); // Finance charge using ContractRate%
return _loanCalculationHelper.CalculateBuyDown(_calculationConfig);
}
else
{
// Calculate Reserve Amount for all other loans.
return _boatLoanCalculationHelper.CalculateStandardMarkup(_calculationConfig);
}
}
private Loan AssignReserveValues(Loan loan, LoanReserve loanReserve)
{
loan.ReserveAmount = loanReserve.ReserveAmount;
loan.ReserveMessage = loanReserve.ReserveTypeMessage;
loan.Term = _calculationConfig.LoanData().Term;
loan.FlatFeePaid = loanReserve.FlatFeePaid;
return loan;
}
private LoanChargeback ChargebackCalculation(Loan loanData)
{
if (loanData.ReserveAmount < 0 || loanData.ReserveMessage != null && loanData.ReserveMessage.ToUpper().Contains("BUYDOWN"))
{
return SetBuyDownChargeback(loanData);
}
if (loanData.LoanType?.ChargebackMethod == "S")
{
return CalculateBBLoans(loanData);
}
else
{
return CalculateLoanWithChargebackDate(loanData);
}
}
private LoanChargeback BoatChargebackCalculation(Loan loanData)
{
if (loanData.ReserveAmount < 0 || loanData.ReserveMessage != null && loanData.ReserveMessage.ToUpper().Contains("BUYDOWN"))
{
return SetBuyDownChargeback(loanData);
}
else
{
return CalculateBoatLoanWithChargebackDate(loanData);
}
}
private LoanChargeback CalculateBoatLoanWithChargebackDate(Loan loanData)
{
var numOfChargebackPayments = loanData.Term <= 63 ? 6 : 12; //term is 63 or less chargeback date is 6 monthly payments, otherwise 12
var lastChargebackDate = _boatLoanCalculationHelper.GetLastChargebackDate(loanData.FirstPmtDate, numOfChargebackPayments);
if (loanData.PaidLoanDate > lastChargebackDate)
{
return new LoanChargeback
{
ChargebackAmount = 0M,
ChargebackTypeMessage = loanData.ReserveAmount == 0 ? "No Reserve - No Chargeback" : "Paid After Chargeback Date",
RemainingMonthsAtClose = (byte)_loanCalculationHelper.GetRemainingMonths(loanData)
};
}
return ChargebackWithRemainingMonths(loanData);
}
private LoanChargeback CalculateLoanWithChargebackDate(Loan loanData)
{
var numOfChargebackPayments = loanData.LoanType?.NoChargebackPayments ?? 3;
var numOfChargebackDays = loanData.LoanType?.NoChargebackDays ?? 90;
var lastChargebackDate = _loanCalculationHelper.GetLastChargebackDate(loanData.LoanDate, loanData.FirstPmtDate, numOfChargebackPayments, numOfChargebackDays);
if (loanData.PaidLoanDate > lastChargebackDate)
{
return new LoanChargeback
{
ChargebackAmount = 0M,
ChargebackTypeMessage = loanData.ReserveAmount == 0 ? "No Reserve - No Chargeback" : "Paid After Chargeback Date",
RemainingMonthsAtClose = (byte)_loanCalculationHelper.GetRemainingMonths(loanData)
};
}
return ChargebackWithRemainingMonths(loanData);
}
private LoanChargeback ChargebackWithRemainingMonths(Loan loanData)
{
int remainingMonths = 0;
string message = "Paid On or Before Chargeback Date";
remainingMonths = _loanCalculationHelper.GetRemainingMonths(loanData);
return new LoanChargeback
{
//this needs to be the original term formula
ChargebackAmount = _loanCalculationHelper.CalculateChargeback(loanData.ReserveAmount, loanData.Term, remainingMonths),
ChargebackTypeMessage = loanData.ReserveAmount == 0 ? "No Reserve - No Chargeback" : message,
RemainingMonthsAtClose = (byte)remainingMonths
};
}
private LoanChargeback ChargebackWithRemainingMonthsBB(Loan loanData)
{
int remainingMonths = 0;
string message = "Paid After Chargeback Date";
//add chargeback date for BB Loans
var chargeBackDate = loanData.FirstPmtDate.AddMonths(loanData.Term - 1);
if (loanData.PaidLoanDate <= chargeBackDate)
{
remainingMonths = _loanCalculationHelper.GetRemainingMonths(loanData);
if (remainingMonths <= 0)
{
return new LoanChargeback
{
ChargebackAmount = 0M,
ChargebackTypeMessage = message,
RemainingMonthsAtClose = 0
};
}
else
{
message = "Paid On or Before Chargeback Date";
}
}
return new LoanChargeback
{
ChargebackAmount = _loanCalculationHelper.CalculateChargeback(loanData.ReserveAmount, loanData.Term, remainingMonths),
ChargebackTypeMessage = message,
RemainingMonthsAtClose = (byte)remainingMonths
};
}
private LoanChargeback CalculateBBLoans(Loan loanData)
{
if (loanData.ReserveAmount != 0)
{
return ChargebackWithRemainingMonthsBB(loanData);
}
else
{
return new LoanChargeback
{
ChargebackAmount = 0M,
ChargebackTypeMessage = "No Reserve - No Chargeback",
RemainingMonthsAtClose = (byte)_loanCalculationHelper.GetRemainingMonths(loanData)
};
}
}
private LoanChargeback SetBuyDownChargeback(Loan loanData)
{
//Buydown scenarios will not calculate a chargeback and return a message indicating this.
return new LoanChargeback
{
ChargebackAmount = 0M,
ChargebackTypeMessage = "Buy Down Reserve - No Chargeback",
RemainingMonthsAtClose = (byte)_loanCalculationHelper.GetRemainingMonths(loanData)
};
}
}
}