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 { 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 validTermLengths = this.GetValidTermLengthsForLoanType(loanApplication); return validTermLengths.Contains(loanApplication.TermLengthInMonths.Value); } private List 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 GetSignatureTermList(decimal loanAmount) { var applicableKeys = TermLengths.SignatureByAmount.Keys.Where(x => loanAmount >= x); var allowedTerms = new List(); foreach (var key in applicableKeys) { allowedTerms.AddRange(TermLengths.SignatureByAmount[key]); } return allowedTerms; } private static List GetSpecialTermList(decimal loanAmount) { var allowedTerms = new List { 36 }; return allowedTerms; } private static List GetMfdHomeAndRvTermList(decimal loanAmount) { var availableTermLengths = new List(); foreach (var amount in TermLengths.MfdHomeAndRvByAmount.Keys) { if (loanAmount >= amount) { availableTermLengths.AddRange(TermLengths.MfdHomeAndRvByAmount[amount]); } } return availableTermLengths; } public static List 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 value)) { return value; } else if (lookupYear >= yearList.First().Key) { return yearList.First().Value; } else { return yearList.Last().Value; } } private static List GetBoatTermList(decimal loanAmount) { var applicableKeys = TermLengths.BoatLoansByAmount.Keys.Where(x => loanAmount >= x); var allowedTerms = new List(); foreach (var key in applicableKeys) { allowedTerms.AddRange(TermLengths.BoatLoansByAmount[key]); } return allowedTerms; } private static List GetPowersportTermList(decimal loanAmount) { var applicableKeys = TermLengths.PowersportByAmount.Keys.Where(x => loanAmount >= x); var allowedTerms = new List(); 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; } } }