284 lines
11 KiB
C#
284 lines
11 KiB
C#
using FluentValidation;
|
|
using Landmark.OLA.Models.Assets;
|
|
using Landmark.OLA.Models.HomeMortgageDisclosureAct.Validators;
|
|
using Landmark.OLA.Models.Individuals.Validators;
|
|
using Landmark.OLA.Models.Loan.Enums;
|
|
|
|
namespace Landmark.OLA.Models.Loan.Validators
|
|
{
|
|
public class LoanApplicationValidator : AbstractValidator<LoanApplication>
|
|
{
|
|
public LoanApplicationValidator()
|
|
{
|
|
RuleFor(x => x.PrimaryBorrower).SetValidator(new ApplicantValidator());
|
|
|
|
//when primary is married and in a state requiring spouse information
|
|
When(x =>
|
|
(x.PrimaryBorrower.IsMarried ?? false) &&
|
|
(x.PrimaryBorrower.Address?.State.HasValue ?? false) &&
|
|
ValidationConstants.StatesRequiringSpouseInformation.Contains(x.PrimaryBorrower.Address.State.Value),
|
|
() =>
|
|
{
|
|
//Primary must have spouse data if not married to coapplicant
|
|
RuleFor(x => x.PrimaryBorrower.HasAnyNonApplicantSpouse()).Equal(true)
|
|
.Unless(x => x.CoApplicant?.IsMarriedToPrimaryBorrower ?? false)
|
|
.WithMessage("PrimaryBorrower must include spouse information due to marriage in a community property state.");
|
|
});
|
|
|
|
|
|
//when coapp is married and in a state requiring spouse information
|
|
When(x =>
|
|
(x.CoApplicant?.IsMarried ?? false) &&
|
|
(x.CoApplicant?.Address?.State.HasValue ?? false) &&
|
|
ValidationConstants.StatesRequiringSpouseInformation.Contains(x.CoApplicant.Address.State.Value),
|
|
() =>
|
|
{
|
|
//coapp must have spouse data if not married to coapplicant
|
|
RuleFor(x => x.CoApplicant.HasAnyNonApplicantSpouse()).Equal(true)
|
|
.Unless(x => x.CoApplicant?.IsMarriedToPrimaryBorrower ?? false)
|
|
.WithMessage("CoApplicant must include spouse information due to marriage in a community property state.");
|
|
});
|
|
|
|
|
|
Unless(x => x.CoApplicant is null, () =>
|
|
{
|
|
RuleFor(x => x.CoApplicant).SetValidator(new ApplicantValidator());
|
|
|
|
When(x => x.CoApplicant.IsMarriedToPrimaryBorrower, () =>
|
|
{
|
|
//If the two applicants are married there should be no other spouse information
|
|
RuleFor(x => x.PrimaryBorrower.NonApplicantSpouse).Null();
|
|
RuleFor(x => x.PrimaryBorrower.DetailedNonApplicantSpouse).Null();
|
|
|
|
RuleFor(x => x.CoApplicant.NonApplicantSpouse).Null();
|
|
RuleFor(x => x.CoApplicant.DetailedNonApplicantSpouse).Null();
|
|
});
|
|
});
|
|
|
|
When(x => (x.PrimaryBorrower?.IsMarried ?? false) == false, () =>
|
|
{
|
|
RuleFor(x => x.PrimaryBorrower.HasAnyNonApplicantSpouse()).Equal(false)
|
|
.WithMessage("PrimaryBorrower is set as not married but includes spouse information.");
|
|
});
|
|
|
|
When(x => IsHmdaLoan(x.LoanType), () =>
|
|
{
|
|
RuleFor(x => x.PrimaryBorrower.HmdaValues).SetValidator(new HmdaValidator());
|
|
|
|
RuleFor(x => x.CoApplicant.HmdaValues).SetValidator(new HmdaValidator())
|
|
.Unless(x => x.CoApplicant is null);
|
|
});
|
|
|
|
When(x => IsVehicleRequired(x.LoanType), () =>
|
|
{
|
|
RuleFor(x => x.Vehicle).NotNull();
|
|
|
|
RuleFor(x => x).SetValidator(new VehicleValidator());
|
|
});
|
|
|
|
When(x => IsRealEstateRequired(x.LoanType), () =>
|
|
{
|
|
RuleFor(x => x.RealEstate).NotNull();
|
|
});
|
|
|
|
When(x => IsTermRequired(x.LoanType), () =>
|
|
{
|
|
RuleFor(x => x.TermLengthInMonths).NotNull();
|
|
RuleFor(x => x.TermLengthInMonths).Must((x, termLength) => IsValidTermLength(x) == true)
|
|
.WithMessage("TermLengthInMonths is not set to a valid value for this loan type.");
|
|
});
|
|
|
|
RuleFor(x => x.Purpose).Must((x, purpose) => LoanPurpose.IsValidPurposeForType(x.LoanType, purpose));
|
|
|
|
Unless(x => x.LoanType.Value == LoanType.CC_CashBack || x.LoanType.Value == LoanType.CC_Rewards || x.LoanType.Value == LoanType.CC_Secure
|
|
|| x.LoanType.Value == LoanType.CC_Student, () => {
|
|
RuleFor(x => x.RequestedAmount).Must((x, requestedAmount) => LoanLimits.IsValidAmountForType(x.LoanType.Value, requestedAmount.Value))
|
|
.WithMessage("The requested loan amount is outside the limits for this product type.");
|
|
});
|
|
|
|
|
|
//They actually can, but for now that means something went wrong on the frontend.
|
|
//To allow vehicle details on preapprovals, simply remove this validation and everything should likely work.
|
|
RuleFor(x => x.Vehicle).Null().When(x => x.LoanType == LoanType.V_VehiclePreapproval)
|
|
.WithMessage("Vehicle PreApproval loans cannot include vehicle details");
|
|
|
|
When(x => IsClientMessageRequired(x.LoanType), () =>
|
|
{
|
|
RuleFor(x => x.ClientMessage).NotNull();
|
|
RuleFor(x => x.ClientMessage.Trim()).Must((x, clientMessage) => clientMessage.Length >= 5)
|
|
.WithMessage("Applications for this type of loan must include a client message");
|
|
});
|
|
}
|
|
|
|
private bool IsValidTermLength(LoanApplication loanApplication)
|
|
{
|
|
if ((!loanApplication.LoanType.HasValue) || (!loanApplication.TermLengthInMonths.HasValue))
|
|
return false;
|
|
|
|
List<int> validTermLengths = this.GetValidTermLengthsForLoanType(loanApplication);
|
|
|
|
return validTermLengths.Contains(loanApplication.TermLengthInMonths.Value);
|
|
}
|
|
|
|
private List<int> GetValidTermLengthsForLoanType(LoanApplication loanApplication)
|
|
{
|
|
return loanApplication.LoanType.Value switch
|
|
{
|
|
LoanType.EQ_Mtg_Refi_Express => TermLengths.MortgageRefiExpress,
|
|
LoanType.EQ_Home_Equity_Fixed => TermLengths.HomeEquity,
|
|
LoanType.V_VehiclePreapproval => TermLengths.VehiclePreApproval,
|
|
LoanType.V_Vehicle => GetVehicleTermList(loanApplication.Vehicle),
|
|
LoanType.EQ_Mfg_Home_With_or_Without_Land or LoanType.T_TrailerMotorhome => GetMfdHomeAndRvTermList(loanApplication.RequestedAmount.Value),
|
|
LoanType.M_Boat => GetBoatTermList(loanApplication.RequestedAmount.Value),
|
|
LoanType.P_Powersport => GetPowersportTermList(loanApplication.Vehicle.InitialEstimatedValue.Value),
|
|
LoanType.S_Signature => GetSignatureTermList(loanApplication.RequestedAmount.Value),
|
|
LoanType.S_Special => GetSpecialTermList(loanApplication.RequestedAmount.Value),
|
|
LoanType.CC_CashBack => [],
|
|
LoanType.CC_Rewards => [],
|
|
LoanType.CC_Student => [],
|
|
LoanType.CC_Secure => [],
|
|
LoanType.EQ_HELOC_Flex => [],
|
|
_ => [],
|
|
};
|
|
}
|
|
|
|
private static List<int> GetSignatureTermList(decimal loanAmount)
|
|
{
|
|
var applicableKeys = TermLengths.SignatureByAmount.Keys.Where(x => loanAmount >= x);
|
|
var allowedTerms = new List<int>();
|
|
foreach (var key in applicableKeys)
|
|
{
|
|
allowedTerms.AddRange(TermLengths.SignatureByAmount[key]);
|
|
}
|
|
return allowedTerms;
|
|
}
|
|
|
|
|
|
private static List<int> GetSpecialTermList(decimal loanAmount)
|
|
{
|
|
var allowedTerms = new List<int>
|
|
{
|
|
36
|
|
};
|
|
return allowedTerms;
|
|
}
|
|
|
|
private static List<int> GetMfdHomeAndRvTermList(decimal loanAmount)
|
|
{
|
|
var availableTermLengths = new List<int>();
|
|
|
|
foreach (var amount in TermLengths.MfdHomeAndRvByAmount.Keys)
|
|
{
|
|
if (loanAmount >= amount)
|
|
{
|
|
availableTermLengths.AddRange(TermLengths.MfdHomeAndRvByAmount[amount]);
|
|
}
|
|
}
|
|
|
|
return availableTermLengths;
|
|
}
|
|
|
|
public static List<int> GetVehicleTermList(Vehicle vehicle)
|
|
{
|
|
if ((vehicle is null) || (!vehicle.Condition.HasValue) || (!vehicle.Year.HasValue))
|
|
return [];
|
|
|
|
var yearList = TermLengths.TermsByYear;
|
|
|
|
int lookupYear = vehicle.Year.Value;
|
|
|
|
if (yearList.TryGetValue(lookupYear, out List<int> value))
|
|
{
|
|
return value;
|
|
}
|
|
else if (lookupYear >= yearList.First().Key)
|
|
{
|
|
return yearList.First().Value;
|
|
}
|
|
else
|
|
{
|
|
return yearList.Last().Value;
|
|
}
|
|
|
|
}
|
|
|
|
private static List<int> GetBoatTermList(decimal loanAmount)
|
|
{
|
|
var applicableKeys = TermLengths.BoatLoansByAmount.Keys.Where(x => loanAmount >= x);
|
|
|
|
var allowedTerms = new List<int>();
|
|
|
|
foreach (var key in applicableKeys)
|
|
{
|
|
allowedTerms.AddRange(TermLengths.BoatLoansByAmount[key]);
|
|
}
|
|
|
|
return allowedTerms;
|
|
}
|
|
|
|
private static List<int> GetPowersportTermList(decimal loanAmount)
|
|
{
|
|
var applicableKeys = TermLengths.PowersportByAmount.Keys.Where(x => loanAmount >= x);
|
|
|
|
var allowedTerms = new List<int>();
|
|
|
|
foreach (var key in applicableKeys)
|
|
{
|
|
allowedTerms.AddRange(TermLengths.PowersportByAmount[key]);
|
|
}
|
|
|
|
return allowedTerms;
|
|
}
|
|
|
|
public static bool IsTermRequired(LoanType? loanType)
|
|
{
|
|
if (!loanType.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !ValidationConstants.LoansNotRequiringTerm.Contains(loanType.Value);
|
|
}
|
|
|
|
private static bool IsVehicleRequired(LoanType? loanType)
|
|
{
|
|
if (!loanType.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ValidationConstants.LoansRequiringVehicle.Contains(loanType.Value);
|
|
}
|
|
|
|
private static bool IsRealEstateRequired(LoanType? loanType)
|
|
{
|
|
if (!loanType.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ValidationConstants.LoansRequiringRealEstate.Contains(loanType.Value);
|
|
}
|
|
|
|
private static bool IsHmdaLoan(LoanType? loanType)
|
|
{
|
|
if (!loanType.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ValidationConstants.HmdaLoans.Contains(loanType.Value);
|
|
}
|
|
|
|
private static bool IsClientMessageRequired(LoanType? loanType)
|
|
{
|
|
if (!loanType.HasValue)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return loanType.Value == LoanType.S_Special;
|
|
}
|
|
}
|
|
}
|