Files
monorepo/LCU.API.Internal.OnlineLoanApp/Landmark.OLA.Models/Loan/Validators/LoanApplicationValidator.cs
T
2026-03-11 11:18:25 -05:00

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;
}
}
}