315 lines
12 KiB
C#
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)
|
|
};
|
|
}
|
|
}
|
|
}
|