first commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Addresses
|
||||
{
|
||||
public class Address : IAddress
|
||||
{
|
||||
[Required]
|
||||
[StringLength(40)]
|
||||
public string Address1 { get; set; }
|
||||
|
||||
[StringLength(40)]
|
||||
public string Address2 { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string City { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public StateOption? State { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(5, MinimumLength = 5)]
|
||||
public string PostalCode { get; set; }
|
||||
|
||||
[StringLength(4, MinimumLength = 4)]
|
||||
public string PostalCodeExt { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value.
|
||||
public CountryOption? Country { get; set; }
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public Address()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public Address(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models.Addresses.Enums
|
||||
{
|
||||
public enum CountryOption
|
||||
{
|
||||
//only value allowed by CRIF
|
||||
UnitedStates,
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
namespace Landmark.OLA.Models.Addresses.Enums
|
||||
{
|
||||
public enum ResidenceTypeOption
|
||||
{
|
||||
Own,
|
||||
Rent,
|
||||
Other,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
namespace Landmark.OLA.Models.Addresses.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// StateEnum values from CRIF's commonSchema.xsd
|
||||
/// </summary>
|
||||
public enum StateOption
|
||||
{
|
||||
AA,
|
||||
AE,
|
||||
AK,
|
||||
AL,
|
||||
AP,
|
||||
AR,
|
||||
AS,
|
||||
AZ,
|
||||
CA,
|
||||
CO,
|
||||
CT,
|
||||
DC,
|
||||
DE,
|
||||
FL,
|
||||
//FM, //Present in XSD but not available in CRIF's v_RptGeoCode_v001 table
|
||||
GA,
|
||||
GU,
|
||||
HI,
|
||||
IA,
|
||||
ID,
|
||||
IL,
|
||||
IN,
|
||||
KS,
|
||||
KY,
|
||||
LA,
|
||||
MA,
|
||||
MD,
|
||||
ME,
|
||||
//MH, //Present in XSD but not available in CRIF's v_RptGeoCode_v001 table
|
||||
MI,
|
||||
MN,
|
||||
MO,
|
||||
MP,
|
||||
MS,
|
||||
MT,
|
||||
NC,
|
||||
ND,
|
||||
NE,
|
||||
NH,
|
||||
NJ,
|
||||
NM,
|
||||
NV,
|
||||
NY,
|
||||
OH,
|
||||
OK,
|
||||
OR,
|
||||
PA,
|
||||
PR,
|
||||
PW,
|
||||
RI,
|
||||
SC,
|
||||
SD,
|
||||
TN,
|
||||
TX,
|
||||
UT,
|
||||
VA,
|
||||
VI,
|
||||
VT,
|
||||
WA,
|
||||
WI,
|
||||
WV,
|
||||
WY,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Addresses
|
||||
{
|
||||
public interface IAddress : IReferenceable
|
||||
{
|
||||
string Address1 { get; set; }
|
||||
|
||||
string Address2 { get; set; }
|
||||
|
||||
string City { get; set; }
|
||||
|
||||
StateOption? State { get; set; }
|
||||
|
||||
string PostalCode { get; set; }
|
||||
|
||||
string PostalCodeExt { get; set; }
|
||||
|
||||
CountryOption? Country { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models.Addresses
|
||||
{
|
||||
public class PropertyLot : Address
|
||||
{
|
||||
[Required]
|
||||
public bool? IsRented { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
public decimal? RentalAmt { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
public decimal? AnnualPropertyTax { get; set; }
|
||||
|
||||
//TODO #399
|
||||
// public LienPositionEnum lienPositionID { get; set; }
|
||||
|
||||
public PropertyLot()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public PropertyLot(Guid id)
|
||||
: base(id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Landmark.OLA.Models.Addresses.Validators
|
||||
{
|
||||
public class AddressValidator : AbstractValidator<Address>
|
||||
{
|
||||
public AddressValidator()
|
||||
{
|
||||
RuleFor(x => x.Address1).NotNull();
|
||||
RuleFor(x => x.City).NotNull();
|
||||
RuleFor(x => x.State).NotNull();
|
||||
RuleFor(x => x.PostalCode).NotNull();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public class ApplicationControlConfiguration
|
||||
{
|
||||
public bool NotifyLogging { get; set; }
|
||||
public string HomePageUrl { get; set; }
|
||||
public string ImportantTermsPdfFilePath { get; set; }
|
||||
public string WhatYouShouldKnowPdfFilePath { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum LienPositionType
|
||||
{
|
||||
First,
|
||||
Second,
|
||||
Third,
|
||||
Fourth,
|
||||
Fifth
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum ManufacturedHomeType
|
||||
{
|
||||
Single,
|
||||
Double,
|
||||
Triple,
|
||||
Quad,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum NewUsed
|
||||
{
|
||||
New,
|
||||
Used,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// OwnerOccupiedTypeEnum in CRIF's commonSchema.xsd
|
||||
/// </summary>
|
||||
public enum PropertyUse
|
||||
{
|
||||
OwnerOccupied,
|
||||
SecondHome,
|
||||
Rental,
|
||||
VacationHome,
|
||||
Vacant
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// Derived from PropertySubTypeEnum in CRIF's commonSchema.xsd
|
||||
/// </summary>
|
||||
public enum RealEstateType
|
||||
{
|
||||
Family2_4, //1-4 Family
|
||||
OneUnit,
|
||||
TwoUnit,
|
||||
ThreeUnit,
|
||||
FourUnit,
|
||||
Condo,
|
||||
CoOp,
|
||||
Other,
|
||||
SingleFamily,
|
||||
Townhouse
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum UsageType
|
||||
{
|
||||
Personal,
|
||||
Business,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum VehicleSeller
|
||||
{
|
||||
Dealer,
|
||||
Private_Party,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
public enum VehicleType
|
||||
{
|
||||
Automobile,
|
||||
Motorcycle,
|
||||
Boat,
|
||||
ATV,
|
||||
PWC,
|
||||
RecreationalVehicle, //This is *not* a motorhome/camper, but is instead things like golf carts.
|
||||
Snowmobile,
|
||||
TrailerMotorHome, //This *does* include a traditional RV motorohome.
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
namespace Landmark.OLA.Models.Assets.Enums
|
||||
{
|
||||
//CRIF's ValueTypeEnum from commonSchema.xsd
|
||||
public enum VehicleValuationType
|
||||
{
|
||||
Invoice,
|
||||
Loan,
|
||||
MSRP,
|
||||
Retail,
|
||||
TradeIn,
|
||||
Wholesale,
|
||||
Manual,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Assets
|
||||
{
|
||||
public class LienPosition : OlaModel
|
||||
{
|
||||
public bool HasFirstMortgage { get; set; }
|
||||
|
||||
public bool HasSecondMortgage { get; set; }
|
||||
|
||||
public bool PayoffFirstMortgage { get; set; }
|
||||
|
||||
public bool PayoffSecondMortgage { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Assets.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Assets
|
||||
{
|
||||
public class ManufacturedHome : RealEstateBase
|
||||
{
|
||||
[Required]
|
||||
public ManufacturedHomeType? Type { get; set; }
|
||||
|
||||
[Required]
|
||||
public PropertyLot Lot { get; set; }
|
||||
|
||||
//Slightly arbitrary values that should be safe
|
||||
[Range(0, 1000000000)]
|
||||
public decimal? EstimatedLandValuation { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
public decimal EstimatedHomeValuation { get; set; }
|
||||
|
||||
public ManufacturedHome(Guid? manufacturedHomeId = null)
|
||||
:base(manufacturedHomeId)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Assets.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Assets
|
||||
{
|
||||
public class RealEstate : RealEstateBase
|
||||
{
|
||||
public Address RealEstateAddress { get; set; }
|
||||
|
||||
[Required]
|
||||
public RealEstateType? Type { get; set; }
|
||||
|
||||
public RealEstate(Guid? realEstateId = null)
|
||||
: base(realEstateBaseId: realEstateId)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Assets.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Assets
|
||||
{
|
||||
public class RealEstateBase : OlaModel
|
||||
{
|
||||
//Slightly arbitrary values that should be safe
|
||||
[Range(0, 1000000000)]
|
||||
public decimal InitialEstimatedValue { get; set; }
|
||||
|
||||
[Required]
|
||||
public PropertyUse? Use { get; set; }
|
||||
|
||||
public bool? HasSpa { get; set; }
|
||||
|
||||
public bool? HasPool { get; set; }
|
||||
|
||||
public bool? HasBasement { get; set; }
|
||||
|
||||
public bool? HasCentralAir { get; set; }
|
||||
|
||||
public bool? HasGarage { get; set; }
|
||||
|
||||
public bool? InFloodZone { get; set; }
|
||||
|
||||
public bool? InEquity { get; set; }
|
||||
|
||||
public bool? IsPropertyHeldInTrust { get; set; }
|
||||
|
||||
public bool IsCollateral { get; set; } = true;
|
||||
|
||||
public bool IsTradeIn { get; set; }
|
||||
|
||||
public LienPosition LienPosition { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool? CurrentlyListedForSale { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool? CurrentlyBeingRemodeledOrImproved { get; set; }
|
||||
|
||||
public RealEstateBase(Guid? realEstateBaseId = null)
|
||||
{
|
||||
Id = realEstateBaseId.HasValue ? realEstateBaseId.Value.ToString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Assets.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Assets
|
||||
{
|
||||
public class Vehicle : OlaModel
|
||||
{
|
||||
//This will either hold an ToString'ed int or a 36-char long GUID
|
||||
[StringLength(36)]
|
||||
public string ValuationId { get; private set; }
|
||||
|
||||
[Required]
|
||||
public VehicleValuationType? ValuationType { get; set; }
|
||||
|
||||
[Required]
|
||||
public NewUsed? Condition { get; set; }
|
||||
|
||||
public VehicleSeller? Seller { get; set; }
|
||||
|
||||
[Required]
|
||||
public VehicleType? Type { get; set; }
|
||||
|
||||
public UsageType? Usage { get; set; }
|
||||
|
||||
public bool? HasClearTitle { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
public decimal? InitialEstimatedValue { get; set; }
|
||||
|
||||
public bool? IsForBusinessUse { get; set; }
|
||||
|
||||
public bool? IsOwnerOperator { get; set; }
|
||||
|
||||
[Required]
|
||||
//Slightly arbitrary values that should be safe
|
||||
[Range(1900, 3000)]
|
||||
public int? Year { get; set; }
|
||||
|
||||
//Slightly arbitrary values that should be safe
|
||||
[Range(0, 1000000000)]
|
||||
public int? Mileage { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Make { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100)]
|
||||
public string Model { get; set; }
|
||||
|
||||
[StringLength(20)]
|
||||
public string Color { get; set; }
|
||||
|
||||
[StringLength(400)]
|
||||
public string Body { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string Series { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string Style { get; set; }
|
||||
|
||||
[StringLength(400)]
|
||||
public string Trim { get; set; }
|
||||
|
||||
[StringLength(60)]
|
||||
public string VIN { get; set; }
|
||||
|
||||
[StringLength(8)]
|
||||
public string LicensePlateNumber { get; set; }
|
||||
|
||||
public string StateTitled { get; set; }
|
||||
|
||||
[StringLength(20)]
|
||||
public string CountyTitled { get; set; }
|
||||
|
||||
[StringLength(120)]
|
||||
public string MunicipalityType { get; set; }
|
||||
|
||||
[StringLength(120)]
|
||||
public string MunicipalityWhereKept { get; set; }
|
||||
|
||||
public string Lien { get; set; }
|
||||
|
||||
[StringLength(120)]
|
||||
public string DealerName { get; set; }
|
||||
|
||||
[StringLength(180)]
|
||||
public string DealerPhone { get; set; }
|
||||
|
||||
public Address BoatAddress { get; set; }
|
||||
|
||||
[Range(0, 1000000000000000)]
|
||||
public decimal? BoatLength { get; set; }
|
||||
|
||||
[Range(1900, 3000)]
|
||||
public int? TrailerYear { get; set; }
|
||||
|
||||
[StringLength(60)]
|
||||
public string TrailerMake { get; set; }
|
||||
|
||||
public string TrailerType { get; set; }
|
||||
|
||||
[Range(1900, 3000)]
|
||||
public int? MotorYear { get; set; }
|
||||
|
||||
[StringLength(60)]
|
||||
public string MotorMake { get; set; }
|
||||
|
||||
[StringLength(200)]
|
||||
public string MotorModel { get; set; }
|
||||
|
||||
public int? MotorType { get; set; }
|
||||
|
||||
public Vehicle(Guid? valuationId = null, Guid? vehicleId = null, Guid? sellerId = null)
|
||||
{
|
||||
ValuationId = valuationId.HasValue ? valuationId.ToString() : Guid.NewGuid().ToString();
|
||||
Id = vehicleId.HasValue ? vehicleId.ToString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public class ClientError
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string StackTrace { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Core
|
||||
{
|
||||
public class CoreAddress
|
||||
{
|
||||
public int AddressId { get; set; }
|
||||
public string Line1 { get; set; }
|
||||
public string Line2 { get; set; }
|
||||
public string Line3 { get; set; }
|
||||
public string City { get; set; }
|
||||
public string State { get; set; }
|
||||
public string ZipCode { get; set; }
|
||||
public string Country { get; set; }
|
||||
public string CountyName { get; set; }
|
||||
public string AddressType { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
public bool IsInternational { get; set; }
|
||||
public int AddressVerification { get; set; }
|
||||
public string Occupancy { get; set; }
|
||||
public System.DateTime StartDate { get; set; }
|
||||
public System.DateTime EndDate { get; set; }
|
||||
public bool UspsVerified { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Core
|
||||
{
|
||||
public class CoreIndividual
|
||||
{
|
||||
public int IndividualId { get; set; }
|
||||
public string IndividualType { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string MiddleName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string NameSuffix { get; set; }
|
||||
public string TaxId { get; set; }
|
||||
public string Gender { get; set; }
|
||||
public System.DateTime BirthDate { get; set; }
|
||||
public int Branch { get; set; }
|
||||
public string DriversLicense { get; set; }
|
||||
public string DriversLicenseState { get; set; }
|
||||
public string Ident { get; set; }
|
||||
public string PreferredContact { get; set; }
|
||||
public string MaritalStatus { get; set; }
|
||||
public bool IsUSCitizen { get; set; }
|
||||
public int PrimaryAddressId { get; set; }
|
||||
public bool IsEmployee { get; set; }
|
||||
public int EmployeeNumber { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public string EmployerName { get; set; }
|
||||
public int ApplicationCountLast90Days { get; set; }
|
||||
public int ApplicationsDeclinedLast90Days { get; set; }
|
||||
public int ApplicationsApprovedLast90Days { get; set; }
|
||||
public List<CoreAddress> Address { get; set; }
|
||||
public string Email1 { get; set; }
|
||||
public string Email2 { get; set; }
|
||||
public List<CorePhone> Phone { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool IsHomeOwner { get; set; }
|
||||
public bool IsHeadOfHouseHold { get; set; }
|
||||
public string Language { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Core
|
||||
{
|
||||
public class CoreMemberOwner
|
||||
{
|
||||
public int IndividualId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Address { get; set; }
|
||||
public string TaxId { get; set; }
|
||||
public string DriverLicense { get; set; }
|
||||
public string BirthDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Core
|
||||
{
|
||||
public class CorePhone
|
||||
{
|
||||
public string PhoneKey { get; set; }
|
||||
public bool IsPrimaryForType { get; set; }
|
||||
public bool IsInternational { get; set; }
|
||||
public string CountryCode { get; set; }
|
||||
public string AreaCode { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public string Extension { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Core
|
||||
{
|
||||
public class MemberMeta
|
||||
{
|
||||
public bool IsMember { get; set; }
|
||||
public bool IsIndividual { get; set; }
|
||||
public bool IsTwoFactor { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Landmark.OLA.Models.Decisioning.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Decisioning
|
||||
{
|
||||
|
||||
//TODO: At some point the LoanApplication class should probably have this class as a property. Might need to wait for parsing.
|
||||
public class DecisioningDetails
|
||||
{
|
||||
public DecisioningState DecisioningState { get; set; }
|
||||
public DecisioningResult? DecisioningResult { get; set; }
|
||||
|
||||
public ICollection<int> Stipulations { get; private set; }
|
||||
|
||||
public DecisioningDetails(ICollection<int> stipulations = null)
|
||||
{
|
||||
Stipulations = stipulations ?? new List<int>();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj.GetType() != this.GetType())
|
||||
return false;
|
||||
|
||||
var compare = (DecisioningDetails)obj;
|
||||
|
||||
return DecisioningState.Equals(compare.DecisioningState)
|
||||
&& DecisioningResult.Equals(compare.DecisioningResult)
|
||||
&& Stipulations.SequenceEqual(compare.Stipulations);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(DecisioningState, DecisioningResult, Stipulations);
|
||||
}
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
namespace Landmark.OLA.Models.Decisioning.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// StatusEnum values from CRIF's commonSchema.xsd
|
||||
/// </summary>
|
||||
public enum DecisioningResult
|
||||
{
|
||||
NoneSelected,
|
||||
DecisionStatus_Approved,
|
||||
DecisionStatus_Counteroffer,
|
||||
DecisionStatus_Review,
|
||||
DecisionStatus_Declined,
|
||||
DecisionStatus_Cancelled,
|
||||
DecisionStatus_Withdrawn,
|
||||
DecisionStatus_Aprvd_RejectedbyApplicant,
|
||||
DecisionStatus_CounterofferRejectedbyApplicant,
|
||||
WorkflowStatus_DataAcquisition,
|
||||
WorkflowStatus_LoanProcessing,
|
||||
WorkflowStatus_BookingFulfillment,
|
||||
ProcessStatus_Incomplete,
|
||||
ProcessStatus_Complete,
|
||||
ProcessStatus_MissingInformation,
|
||||
ResourceStatus_MissingInformation,
|
||||
ResourceStatus_RequiredInformation,
|
||||
WorkflowStatus_Decisioning,
|
||||
RiskManagerServiceRequestStatus_PendingHistory,
|
||||
RiskManagerServiceRequestStatus_HistoryComplete,
|
||||
RiskManagerServiceRequestStatus_PendingEvaluation,
|
||||
RiskManagerServiceRequestStatus_EvaluationComplete,
|
||||
RiskManagerServiceRequestStatus_ReplacedbyRiskManager_HistoryRequest,
|
||||
StipulationStatus_Open,
|
||||
StipulationStatus_Waive,
|
||||
TitleExceptionStatus_Open,
|
||||
TitleExceptionStatus_Satisfy,
|
||||
TitleExceptionStatus_Waive,
|
||||
StipulationStatus_Satisfied,
|
||||
DisbursementStatus_Pending,
|
||||
DisbursementStatus_Completed,
|
||||
DisbursementStatus_Voided,
|
||||
DisbursementStatus_Deleted,
|
||||
ApplicationStatus_Pending,
|
||||
ApplicationStatus_Complete,
|
||||
MasterApplicationStatus_Signed,
|
||||
MasterApplicationStatus_NotSigned,
|
||||
LoanDocumentStatus_PendingGeneration,
|
||||
LoanDocumentStatus_Generated,
|
||||
DecisionStatus_Pending,
|
||||
MasterApplicationStatus_Expired,
|
||||
MasterApplicationStatus_Cancelled,
|
||||
ApplicationStatus_Cancelled,
|
||||
ApplicationExtractStatus_Pending,
|
||||
ApplicationExtractStatus_Completed,
|
||||
RiskManagerServiceRequestStatus_PendingDecisioning,
|
||||
RiskManagerServiceRequestStatus_DecisioningComplete,
|
||||
LoanDocumentStatus_Viewed,
|
||||
LoanDocumentStatus_Error,
|
||||
ContractStatus_Contracted,
|
||||
ContractStatus_Funded,
|
||||
ContractStatus_Unfunded,
|
||||
ContractStatus_ReturntoChannel,
|
||||
RiskManagerServiceRequestStatus_CompletewithError,
|
||||
CollateralStatus_Active,
|
||||
CollateralStatus_Withdrawn,
|
||||
RecommendedStatus_Approved,
|
||||
RecommendedStatus_Counteroffer,
|
||||
RecommendedStatus_Declined,
|
||||
RiskManagerServiceRequestStatus_NoDiscreteMatch,
|
||||
RiskManagerServiceRequestStatus_ProcesswithoutMatch,
|
||||
FileStatus_Deleted,
|
||||
StipulationStatus_Ignore,
|
||||
RuleStatus_Pass,
|
||||
RuleStatus_Fail,
|
||||
RuleStatus_Overriden,
|
||||
ContractReviewStatus_Accept,
|
||||
ContractReviewStatus_Counteroffer,
|
||||
ContractReviewStatus_ReturntoChannel,
|
||||
DecisionStatus_Incomplete,
|
||||
IncomeStatus_Included,
|
||||
IncomeStatus_Excluded,
|
||||
DisclosureLetterStatus_Pending,
|
||||
DisclosureLetterStatus_Printed,
|
||||
DisclosureLetterStatus_Suppressed,
|
||||
DisclosureLetterStatus_Failed,
|
||||
NotificationStatus_Pending,
|
||||
NotificationStatus_Completed,
|
||||
NotificationStatus_Failed,
|
||||
NotificationStatus_Previewed,
|
||||
AcceptanceStatus_Provisional,
|
||||
AcceptanceStatus_Current,
|
||||
AcceptanceStatus_Rejected,
|
||||
AcceptanceStatus_Cancelled,
|
||||
AcceptanceStatus_Withdrawn,
|
||||
SettlementServiceRequestStatus_Submitted,
|
||||
SettlementServiceRequestStatus_CompletedReceived,
|
||||
SettlementServiceRequestStatus_Cancelled,
|
||||
SettlementServiceRequestStatus_Reviewed,
|
||||
SettlementServiceRequestStatus_Failed,
|
||||
SettlementServiceRequestStatus_CancellationRequest,
|
||||
SettlementServiceRequestStatus_Accepted,
|
||||
SettlementServiceRequestStatus_Incomplete,
|
||||
SettlementServiceRequestStatus_Pending,
|
||||
SettlementServiceRequestStatus_NoHit,
|
||||
SettlementServiceRequestStatus_NoHitPendingResearch,
|
||||
DisbursTrans_Printed,
|
||||
DisbursTrans_Reprinted,
|
||||
Contract_Unfunded,
|
||||
History_Pending,
|
||||
History_NoneRequested,
|
||||
History_Error,
|
||||
History_NoRecord,
|
||||
History_Success,
|
||||
LoanDoc_Processing,
|
||||
LoanDoc_Cancelled,
|
||||
HistoryStatus_Pending,
|
||||
HistoryStatus_NoneRequested,
|
||||
HistoryStatus_Error,
|
||||
HistoryStatus_NoRecord,
|
||||
HistoryStatus_Success,
|
||||
LoanDocumentStatus_Processing,
|
||||
LoanDocumentStatus_Cancelled,
|
||||
LoanDocumentStatus_NonApplicable,
|
||||
NotificationStatus_NonApplicable,
|
||||
ChannelStatisticStatus_Entered,
|
||||
ChannelStatisticStatus_Approved,
|
||||
ChannelStatisticStatus_Other,
|
||||
ChannelStatisticStatus_Counteroffer,
|
||||
ChannelStatisticStatus_Declined,
|
||||
ChannelStatisticStatus_Review,
|
||||
ChannelStatisticStatus_Booked,
|
||||
PrescreenStatus_PrescreenPass,
|
||||
PrescreenStatus_PrescreenFail,
|
||||
RiskManagerServiceRequestStatus_PrescreenCancel,
|
||||
RiskManagerServiceRequestStatus_ChexSystemsCancel,
|
||||
ACHExported,
|
||||
ApplicantAcceptance,
|
||||
ProcessStatus_Failed,
|
||||
DocInterfaceStatus_Pending,
|
||||
DocInterfaceStatus_Completed,
|
||||
DocInterfaceStatus_Failed,
|
||||
AFPApplicationReceived,
|
||||
AFPApplicationDecisioned,
|
||||
AFPApplicationFunded,
|
||||
AFPApplicationWithdrawn,
|
||||
IncompleteApplicationStatus_Pending,
|
||||
IncompleteApplicationStatus_Cancelled,
|
||||
eDocsRescanRequested,
|
||||
eDocsReturnToDealerRequested,
|
||||
eDocsImported,
|
||||
eDocsPendingImport,
|
||||
eDocsArchived,
|
||||
eDocsReturnedToDealer,
|
||||
eDocsRescanRequestFailed,
|
||||
eDocsReturnToDealerRequestFailed,
|
||||
eDocsPurged,
|
||||
EContractingStatus_Verified,
|
||||
EContractingStatus_VerificationError,
|
||||
EContractingStatus_ContractImported,
|
||||
EContractingStatus_EContractingError,
|
||||
EContractingStatus_EContractingCancelled,
|
||||
MultiOffer,
|
||||
MultiOfferAccepted,
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
namespace Landmark.OLA.Models.Decisioning.Enums
|
||||
{
|
||||
public enum DecisioningState
|
||||
{
|
||||
NotStarted,
|
||||
Unknown,
|
||||
Requested,
|
||||
Ready,
|
||||
MissingRequiredInformation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.HomeMortgageDisclosureAct
|
||||
{
|
||||
public class Hmda
|
||||
{
|
||||
public ICollection<Race> Races { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string AmericanIndianAlaskaNativeTribe { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string OtherAsianRace { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string OtherPacificIslanderRace { get; set; }
|
||||
|
||||
public ICollection<Ethnicity> Ethnicities { get; set; }
|
||||
|
||||
[StringLength(100)]
|
||||
public string OtherHispanicLatinoEthnicity { get; set; }
|
||||
|
||||
public List<Sex> Sexes { get; set; }
|
||||
|
||||
public bool? HmdaNoticeDisclosed { get; set; }
|
||||
|
||||
public Hmda()
|
||||
{
|
||||
Races = [];
|
||||
Ethnicities = [];
|
||||
Sexes = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using FluentValidation;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.HomeMortgageDisclosureAct.Validators
|
||||
{
|
||||
public class HmdaValidator : AbstractValidator<Hmda>
|
||||
{
|
||||
public HmdaValidator()
|
||||
{
|
||||
RuleFor(x => x.Races).NotEmpty()
|
||||
.WithMessage($"No race information was provided. Set {Race.NotProvided} if none will be provided.");
|
||||
RuleFor(x => x.Races).Must(races => races.Contains(Race.AmericanIndianAlaskaNative))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.AmericanIndianAlaskaNativeTribe))
|
||||
.WithMessage($"Include {Race.AmericanIndianAlaskaNative} in Races when a value is set in AmericanIndianAlaskaNativeTribe.");
|
||||
RuleFor(x => x.Races).Must(races => races.Contains(Race.OtherAsian))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.OtherAsianRace))
|
||||
.WithMessage($"Include {Race.OtherAsian} in Races when a value is set in OtherAsianRace.");
|
||||
RuleFor(x => x.Races).Must(races => races.Contains(Race.OtherPacificIslander))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.OtherPacificIslanderRace))
|
||||
.WithMessage($"Include {Race.OtherPacificIslander} in Races when a value is set in OtherPacificIslanderRace.");
|
||||
|
||||
RuleFor(x => x.Ethnicities).NotEmpty()
|
||||
.WithMessage($"No ethnicity information was provided. Set {Ethnicity.NotProvided} if none will be provided.");
|
||||
RuleFor(x => x.Ethnicities).Must(ethnicities => ethnicities.Contains(Ethnicity.OtherHispanicLatino))
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.OtherHispanicLatinoEthnicity))
|
||||
.WithMessage($"Include {Ethnicity.OtherHispanicLatino} in Ethnicities when a value is set in OtherHispanicLatinoEthnicity.");
|
||||
|
||||
RuleFor(x => x.Sexes).NotEmpty()
|
||||
.WithMessage($"No sex information was provided. Set {Sex.NotProvided} if none will be provided.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public interface IReferenceable
|
||||
{
|
||||
Guid Id { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class CoApplicant : Individual
|
||||
{
|
||||
public bool IsMarriedToPrimaryBorrower { get; set; }
|
||||
|
||||
public CoApplicant()
|
||||
: base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CoApplicant(Guid id)
|
||||
: base(id)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class Employer : OlaModel
|
||||
{
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
//Due to downstream CRIF issues, applications with employer names containing special characters will be denied
|
||||
[RegularExpression(@"[a-zA-Z0-9 ]*")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public bool? CurrentEmployer { get; set; } // "isCurrentFL"
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public EmploymentTypeOption EmploymentType { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Required]
|
||||
public Income Income { get; set; }
|
||||
|
||||
//[Required]
|
||||
//The current front end offers an upper limit of 10 years, 11 months.
|
||||
[Range(0, 130)]
|
||||
public int MonthsEmployed { get; set; }
|
||||
|
||||
//[Required]
|
||||
//Nullable so that there is no default value
|
||||
public DateTime? StartDate { get; set; }
|
||||
|
||||
// Required if CurrentEmployer == false
|
||||
//Nullable so that there is no default value
|
||||
public DateTime? EndDate { get; set; }
|
||||
|
||||
[MaxLength(180)]
|
||||
public string WorkPhone { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public LanguageOption? Language;
|
||||
|
||||
public Employer(Guid? employerId = null)
|
||||
{
|
||||
Id = employerId.HasValue ? employerId.Value.ToString() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
public enum EmploymentTypeOption
|
||||
{
|
||||
NoneSelected,
|
||||
Contract,
|
||||
FullTime,
|
||||
PartTime,
|
||||
SelfEmployed,
|
||||
Retired,
|
||||
Temporary
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// EthnicityEnum from CRIF's commonSchema.xsd trimmed down to HMDA form options
|
||||
/// </summary>
|
||||
public enum Ethnicity
|
||||
{
|
||||
HispanicOrLatino,
|
||||
NotHispanicOrLatino,
|
||||
Mexican,
|
||||
PuertoRican,
|
||||
Cuban,
|
||||
OtherHispanicLatino,
|
||||
NotProvided
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
public enum Frequency
|
||||
{
|
||||
Annually,
|
||||
SemiAnnually,
|
||||
Monthly,
|
||||
SemiMonthly,
|
||||
TwoWeeks,
|
||||
Weekly,
|
||||
Hourly
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
public enum LanguageOption
|
||||
{
|
||||
//A language value is required by the CRIF API
|
||||
//TODO #229: Accept other values here?
|
||||
English,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
public enum PhoneType
|
||||
{
|
||||
Mobile,
|
||||
Home,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// RaceEnum from CRIF's commonSchema.xsd trimmed down to HMDA form options
|
||||
/// </summary>
|
||||
public enum Race
|
||||
{
|
||||
AmericanIndianAlaskaNative,
|
||||
Asian,
|
||||
NativeHawaiian,
|
||||
BlackOrAfricanAmerican,
|
||||
White,
|
||||
AsianIndian,
|
||||
Chinese,
|
||||
Filipino,
|
||||
Japanese,
|
||||
Korean,
|
||||
Vietnamese,
|
||||
OtherAsian,
|
||||
Hawaiian,
|
||||
GuamanianChamorro,
|
||||
Samoan,
|
||||
OtherPacificIslander,
|
||||
NotProvided
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
/// <summary>
|
||||
/// Values from HMDA form
|
||||
/// </summary>
|
||||
public enum Sex
|
||||
{
|
||||
Female,
|
||||
Male,
|
||||
NotProvided,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
public enum Suffix
|
||||
{
|
||||
II,
|
||||
III,
|
||||
IV,
|
||||
V,
|
||||
Jr,
|
||||
Sr
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals.Enums
|
||||
{
|
||||
|
||||
public class SuffixType
|
||||
{
|
||||
public string CD { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Suffix SuffixEnum { get; set; }
|
||||
public SuffixType(string cd, string name, Suffix suffix)
|
||||
{
|
||||
SuffixEnum = suffix;
|
||||
CD = cd;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SuffixMap
|
||||
{
|
||||
public SuffixMap() {}
|
||||
|
||||
public static readonly SuffixType II = new("2", "II", Suffix.II);
|
||||
public static readonly SuffixType III = new("3", "III", Suffix.III);
|
||||
public static readonly SuffixType IV = new("4", "IV", Suffix.IV);
|
||||
public static readonly SuffixType V = new("5", "V", Suffix.V);
|
||||
public static readonly SuffixType Jr = new("J", "Jr.", Suffix.Jr);
|
||||
public static readonly SuffixType Sr = new("S", "Sr.", Suffix.Sr);
|
||||
|
||||
public static readonly SuffixType[] map = [II, III, IV, V, Jr, Sr];
|
||||
|
||||
public static SuffixType GetSuffixType(Suffix? suffix)
|
||||
{
|
||||
SuffixType match = null;
|
||||
foreach (SuffixType suff in map)
|
||||
{
|
||||
if (suff.SuffixEnum == suffix)
|
||||
{
|
||||
match = suff;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
using Landmark.OLA.Models.Residences;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class HistoryIndividual : IIndividualHistory
|
||||
{
|
||||
public Guid Id { get; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[StringLength(1, MinimumLength = 0)]
|
||||
[RegularExpression(@"[a-zA-Z]")]
|
||||
public string MiddleInitial { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string LastName { get; set; }
|
||||
|
||||
public Suffix? Suffix { get; set; }
|
||||
|
||||
[Required]
|
||||
public CurrentResidenceAddress Address { get; set; }
|
||||
|
||||
public Address MailingAddress { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public LanguageOption? Language { get; set; }
|
||||
|
||||
//A value is required for decisioning. May require input from user in the future.
|
||||
[StringLength(60)]
|
||||
public string CustomerNumber { get; set; } = "0";
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public DateTime? DOB { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(9, MinimumLength = 9)]
|
||||
[RegularExpression(@"[0-9]+")]
|
||||
public string SSN { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(180, MinimumLength = 10)]
|
||||
//CRIF accepts up to 180 characters.
|
||||
//Dashes, periods, and parentheses are accepted but ignored.
|
||||
//Numbers, letters, pound signs, and spaces are kept.
|
||||
public string Phone { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public PhoneType? PhoneType { get; set; }
|
||||
|
||||
[StringLength(180, MinimumLength = 10)]
|
||||
public string SecondaryPhone { get; set; }
|
||||
|
||||
public PhoneType? SecondaryPhoneType { get; set; }
|
||||
|
||||
[Required] //Required by Landmark
|
||||
[StringLength(180, MinimumLength = 3)]
|
||||
[RegularExpression(@".+@.+")]
|
||||
//Given that a user could enter a fake address,
|
||||
//this is not exhaustive check for a valid address form
|
||||
public string Email { get; set; }
|
||||
|
||||
[StringLength(180)]
|
||||
[RegularExpression(@".+@.+")]
|
||||
public string PrimaryApplicantEmail { get; set; }
|
||||
|
||||
[StringLength(180)]
|
||||
[RegularExpression(@".+@.+")]
|
||||
public string CoApplicantEmail { get; set; }
|
||||
|
||||
//TODO #228: Enforce time requirement of total MonthsResided across Address (on IndividualBase) and PreviousAddresses
|
||||
public ICollection<ResidenceAddress> PreviousAddresses { get; set; }
|
||||
|
||||
public ICollection<Employer> Employers { get; set; }
|
||||
|
||||
public ICollection<Income> OtherIncomes { get; set; }
|
||||
|
||||
public bool? IsLandmarkEmployee { get; set; }
|
||||
|
||||
public HistoryIndividual()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public HistoryIndividual(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
[Required]
|
||||
public bool HasOptedOutTCPA { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Landmark.OLA.Models.HomeMortgageDisclosureAct;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public interface IApplicant : IIndividualHistory
|
||||
{
|
||||
bool? IsMarried { get; set; }
|
||||
|
||||
MailableSpouse NonApplicantSpouse { get; set; }
|
||||
|
||||
HistoryIndividual DetailedNonApplicantSpouse { get; set; }
|
||||
|
||||
Hmda HmdaValues { get; set; }
|
||||
|
||||
bool HasAnyNonApplicantSpouse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public interface ICoApplicant : IApplicant
|
||||
{
|
||||
bool IsMarriedToPrimaryBorrower { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
using Landmark.OLA.Models.Residences;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public interface IIndividualHistory : IReferenceable
|
||||
{
|
||||
string FirstName { get; set; }
|
||||
|
||||
string MiddleInitial { get; set; }
|
||||
|
||||
string LastName { get; set; }
|
||||
|
||||
Suffix? Suffix { get; set; }
|
||||
|
||||
string SSN { get; set; }
|
||||
|
||||
DateTime? DOB { get; set; }
|
||||
|
||||
string Phone { get; set; }
|
||||
|
||||
PhoneType? PhoneType { get; set; }
|
||||
|
||||
string SecondaryPhone { get; set; }
|
||||
|
||||
PhoneType? SecondaryPhoneType { get; set; }
|
||||
|
||||
string Email { get; set; }
|
||||
|
||||
LanguageOption? Language { get; set; }
|
||||
|
||||
string CustomerNumber { get; set; }
|
||||
|
||||
CurrentResidenceAddress Address { get; set; }
|
||||
|
||||
ICollection<ResidenceAddress> PreviousAddresses { get; set; }
|
||||
|
||||
Address MailingAddress { get; set; }
|
||||
|
||||
ICollection<Employer> Employers { get; set; }
|
||||
|
||||
ICollection<Income> OtherIncomes { get; set; }
|
||||
|
||||
bool? IsLandmarkEmployee { get; set; }
|
||||
|
||||
bool HasOptedOutTCPA { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public interface INonApplicant : IReferenceable
|
||||
{
|
||||
string FirstName { get; set; }
|
||||
|
||||
string MiddleInitial { get; set; }
|
||||
|
||||
string LastName { get; set; }
|
||||
|
||||
Address MailingAddress { get; set; }
|
||||
|
||||
string SSN { get; set; }
|
||||
|
||||
string Email { get; set; }
|
||||
|
||||
LanguageOption? Language { get; set; }
|
||||
|
||||
bool HasOptedOutTCPA { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class Income
|
||||
{
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public IncomeType? IncomeTypeCD { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string SourceName { get; set; }
|
||||
|
||||
// required based on type?
|
||||
//Nullable so that there is no default value
|
||||
public AlimonyStatus? AlimonyStatus { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
public decimal? AmountPerPeriod { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public Frequency? Frequency { get; set; }
|
||||
|
||||
[Range(0, 168)]
|
||||
public int? HoursPerWeek { get; set; }
|
||||
|
||||
[Required]
|
||||
// Must be Excluded when part of a previous employer
|
||||
//Nullable so that there is no default value
|
||||
public IncomeStatus? IncomeStatus { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum IncomeStatus
|
||||
{
|
||||
IncomeStatus_Excluded,
|
||||
IncomeStatus_Included
|
||||
}
|
||||
|
||||
public enum IncomeType
|
||||
{
|
||||
Alimony,
|
||||
Bonuses,
|
||||
ChildSupport,
|
||||
RetirementDisability,
|
||||
Dividends,
|
||||
Commissions,
|
||||
Other,
|
||||
Spouse,
|
||||
RentalProperty,
|
||||
Interest,
|
||||
SocialSecurity,
|
||||
IndividualAccount,
|
||||
Salary
|
||||
}
|
||||
|
||||
public enum AlimonyStatus
|
||||
{
|
||||
CourtOrdered,
|
||||
OralUnderstanding,
|
||||
Written
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class IncomeLimits
|
||||
{
|
||||
private static Limit HourLimit => new() { min = 0, max = 999 };
|
||||
private static Limit AnnualLimit => new() { min = 0, max = 1000000000 };
|
||||
|
||||
public static Dictionary<Frequency, Limit> Limits => new()
|
||||
{
|
||||
{ Frequency.Hourly, HourLimit },
|
||||
{ Frequency.Annually, AnnualLimit },
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.HomeMortgageDisclosureAct;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class Individual : HistoryIndividual, IApplicant
|
||||
{
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public bool? IsMarried { get; set; }
|
||||
|
||||
public MailableSpouse NonApplicantSpouse { get; set; }
|
||||
|
||||
public HistoryIndividual DetailedNonApplicantSpouse { get; set; }
|
||||
|
||||
public Hmda HmdaValues { get; set; }
|
||||
|
||||
public Individual()
|
||||
: base()
|
||||
{
|
||||
HmdaValues = new Hmda();
|
||||
}
|
||||
|
||||
public Individual(Guid id)
|
||||
: base(id)
|
||||
{
|
||||
HmdaValues = new Hmda();
|
||||
}
|
||||
|
||||
public Individual( Hmda hmda)
|
||||
: base()
|
||||
{
|
||||
HmdaValues = hmda;
|
||||
}
|
||||
|
||||
public bool HasAnyNonApplicantSpouse()
|
||||
{
|
||||
var hasNonApplicantSpouse = NonApplicantSpouse is not null;
|
||||
var hasDetailedNonApplicantSpouse = DetailedNonApplicantSpouse is not null;
|
||||
|
||||
return hasNonApplicantSpouse || hasDetailedNonApplicantSpouse;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals
|
||||
{
|
||||
public class MailableSpouse : INonApplicant
|
||||
{
|
||||
public Guid Id { get; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[StringLength(1, MinimumLength = 0)]
|
||||
[RegularExpression(@"[a-zA-Z]")]
|
||||
public string MiddleInitial { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(60, MinimumLength = 1)]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[Required]
|
||||
public Address MailingAddress { get; set; }
|
||||
|
||||
[StringLength(9, MinimumLength = 9)]
|
||||
[RegularExpression(@"[0-9]+")]
|
||||
public string SSN { get; set; }
|
||||
|
||||
[StringLength(180, MinimumLength = 3)]
|
||||
[RegularExpression(@".+@.+")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public LanguageOption? Language { get; set; }
|
||||
|
||||
public MailableSpouse()
|
||||
{
|
||||
Id = Guid.NewGuid();
|
||||
}
|
||||
|
||||
public MailableSpouse(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
[Required]
|
||||
public bool HasOptedOutTCPA { get; set; }
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals.Validators
|
||||
{
|
||||
class ApplicantValidator : AbstractValidator<IApplicant>
|
||||
{
|
||||
public ApplicantValidator()
|
||||
{
|
||||
RuleForEach(x => x.Employers).SetValidator(new EmployerValidator());
|
||||
|
||||
When(x => x.DetailedNonApplicantSpouse is not null, () =>
|
||||
{
|
||||
RuleForEach(x => x.DetailedNonApplicantSpouse.Employers).SetValidator(new EmployerValidator());
|
||||
});
|
||||
|
||||
When(x => x.NonApplicantSpouse is not null, () =>
|
||||
{
|
||||
//An individual can only have a single spouse entry
|
||||
RuleFor(x => x.DetailedNonApplicantSpouse).Null();
|
||||
});
|
||||
|
||||
RuleFor(x => x.SecondaryPhoneType).NotNull().When(x => x.SecondaryPhone?.Length > 0)
|
||||
.WithMessage("SecondaryPhoneType must be set when a SecondaryPhone value is present.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using FluentValidation;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Individuals.Validators
|
||||
{
|
||||
class EmployerValidator : AbstractValidator<Employer>
|
||||
{
|
||||
public EmployerValidator()
|
||||
{
|
||||
When(x => x.Income.Frequency == Frequency.Hourly, () =>
|
||||
{
|
||||
RuleFor(x => x.Income.HoursPerWeek).NotNull().WithMessage("Hourly employment must state hours per week.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Core" Version="2.3.6" />
|
||||
<PackageReference Include="CoreWCF.Primitives" Version="1.8.0" />
|
||||
<PackageReference Include="CoreWCF.ConfigurationManager" Version="1.8.0" />
|
||||
<PackageReference Include="CoreWCF.Http" Version="1.8.0" />
|
||||
<PackageReference Include="CoreWCF.WebHttp" Version="1.8.0" />
|
||||
<PackageReference Include="CoreWCF.NetTcp" Version="1.8.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.9.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.29.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public struct Limit
|
||||
{
|
||||
public decimal min;
|
||||
public decimal max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
|
||||
public class BranchData
|
||||
{
|
||||
[Required]
|
||||
[StringLength(5, MinimumLength = 5, ErrorMessage = "Zipcodes must be 5 digits")]
|
||||
public string Zipcode { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(minimum: 1, maximum: 99999999)]
|
||||
public int ChannelId { get; set; }
|
||||
|
||||
public BranchData(string zip, int channel)
|
||||
{
|
||||
this.Zipcode = zip;
|
||||
this.ChannelId = channel;
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class BranchConfig : OlaModel
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Required]
|
||||
public List<BranchData> BranchCodesByZip { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public static class BranchMapping
|
||||
{
|
||||
|
||||
public static Dictionary<string, int> BranchCodesByZip = new Dictionary<string, int>
|
||||
{
|
||||
|
||||
{"53001", 1561599},
|
||||
{"53002", 1561599},
|
||||
{"53003", 1561245},
|
||||
{"53004", 1561591},
|
||||
{"53005", 1561255},
|
||||
{"53006", 1561657},
|
||||
{"53007", 1561255},
|
||||
{"53008", 1561255},
|
||||
{"53010", 1561599},
|
||||
{"53011", 1561599},
|
||||
{"53012", 1561591},
|
||||
{"53013", 1561591},
|
||||
{"53014", 1561657},
|
||||
{"53015", 1561241},
|
||||
{"53016", 1561665},
|
||||
{"53017", 1561664},
|
||||
{"53018", 1561267},
|
||||
{"53019", 1561599},
|
||||
{"53020", 1561599},
|
||||
{"53021", 1561604},
|
||||
{"53022", 1561288},
|
||||
{"53023", 1561288},
|
||||
{"53024", 1561591},
|
||||
{"53026", 1561591},
|
||||
{"53027", 1561662},
|
||||
{"53029", 1561267},
|
||||
{"53031", 1561599},
|
||||
{"53032", 1561657},
|
||||
{"53033", 1561664},
|
||||
{"53034", 1561665},
|
||||
{"53035", 1561657},
|
||||
{"53036", 1561284},
|
||||
{"53037", 1561604},
|
||||
{"53038", 1561284},
|
||||
{"53039", 1561665},
|
||||
{"53040", 1561599},
|
||||
{"53042", 1561599},
|
||||
{"53044", 1561599},
|
||||
{"53045", 1561588},
|
||||
{"53046", 1561265},
|
||||
{"53047", 1561284},
|
||||
{"53048", 1561662},
|
||||
{"53049", 1561657},
|
||||
{"53050", 1561657},
|
||||
{"53051", 1561288},
|
||||
{"53052", 1561288},
|
||||
{"53056", 1561267},
|
||||
{"53057", 1561599},
|
||||
{"53058", 1561267},
|
||||
{"53059", 1561662},
|
||||
{"53060", 1561599},
|
||||
{"53061", 1561599},
|
||||
{"53062", 1561599},
|
||||
{"53063", 1561599},
|
||||
{"53064", 1561267},
|
||||
{"53065", 1561657},
|
||||
{"53066", 1561245},
|
||||
{"53069", 1561267},
|
||||
{"53070", 1561591},
|
||||
{"53072", 1561262},
|
||||
{"53073", 1561599},
|
||||
{"53074", 1561591},
|
||||
{"53075", 1561599},
|
||||
{"53076", 1561664},
|
||||
{"53078", 1561662},
|
||||
{"53079", 1561599},
|
||||
{"53080", 1561591},
|
||||
{"53081", 1561591},
|
||||
{"53082", 1561604},
|
||||
{"53083", 1561599},
|
||||
{"53085", 1561599},
|
||||
{"53086", 1561662},
|
||||
{"53088", 1561657},
|
||||
{"53089", 1561265},
|
||||
{"53090", 1561599},
|
||||
{"53091", 1561657},
|
||||
{"53092", 1561591},
|
||||
{"53093", 1561599},
|
||||
{"53094", 1561284},
|
||||
{"53095", 1561604},
|
||||
{"53096", 1561599},
|
||||
{"53097", 1561591},
|
||||
{"53098", 1561284},
|
||||
{"53099", 1561662},
|
||||
{"53101", 1561631},
|
||||
{"53102", 1561631},
|
||||
{"53103", 2081492},
|
||||
{"53104", 1561624},
|
||||
{"53105", 1561631},
|
||||
{"53108", 1561598},
|
||||
{"53109", 1561631},
|
||||
{"53110", 1561616},
|
||||
{"53114", 1561631},
|
||||
{"53115", 1561631},
|
||||
{"53118", 1561245},
|
||||
{"53119", 1561245},
|
||||
{"53120", 1561631},
|
||||
{"53121", 1561631},
|
||||
{"53122", 1561588},
|
||||
{"53125", 1561631},
|
||||
{"53126", 1561624},
|
||||
{"53127", 1561262},
|
||||
{"53128", 1561631},
|
||||
{"53129", 1561289},
|
||||
{"53130", 1561289},
|
||||
{"53132", 1561289},
|
||||
{"53137", 1561284},
|
||||
{"53138", 1561661},
|
||||
{"53139", 1561631},
|
||||
{"53140", 1561639},
|
||||
{"53141", 1561639},
|
||||
{"53142", 1561639},
|
||||
{"53143", 1561639},
|
||||
{"53144", 1561639},
|
||||
{"53146", 1561243},
|
||||
{"53147", 1561631},
|
||||
{"53148", 1561631},
|
||||
{"53149", 2081492},
|
||||
{"53150", 2081492},
|
||||
{"53151", 1561243},
|
||||
{"53152", 1561631},
|
||||
{"53153", 1561262},
|
||||
{"53154", 1561598},
|
||||
{"53156", 1561245},
|
||||
{"53157", 1561631},
|
||||
{"53158", 1561639},
|
||||
{"53159", 1561631},
|
||||
{"53167", 1561631},
|
||||
{"53168", 1561631},
|
||||
{"53170", 1561631},
|
||||
{"53171", 1561639},
|
||||
{"53172", 1561598},
|
||||
{"53176", 1561631},
|
||||
{"53177", 1561624},
|
||||
{"53178", 1561245},
|
||||
{"53179", 1561631},
|
||||
{"53181", 1561631},
|
||||
{"53182", 1561624},
|
||||
{"53183", 1561267},
|
||||
{"53184", 1561631},
|
||||
{"53185", 1561631},
|
||||
{"53186", 1561262},
|
||||
{"53187", 1561262},
|
||||
{"53188", 1561262},
|
||||
{"53189", 1561262},
|
||||
{"53190", 1561245},
|
||||
{"53191", 1561631},
|
||||
{"53192", 1561631},
|
||||
{"53194", 1561624},
|
||||
{"53195", 1561631},
|
||||
{"53199", 1561631},
|
||||
{"53201", 1561241},
|
||||
{"53202", 1561615},
|
||||
{"53203", 1561241},
|
||||
{"53204", 1561241},
|
||||
{"53205", 1561241},
|
||||
{"53206", 1561241},
|
||||
{"53207", 1561615},
|
||||
{"53208", 1561241},
|
||||
{"53209", 1561255},
|
||||
{"53210", 1561241},
|
||||
{"53211", 1561615},
|
||||
{"53212", 1561241},
|
||||
{"53213", 1561241},
|
||||
{"53214", 1561587},
|
||||
{"53215", 1561283},
|
||||
{"53216", 1561241},
|
||||
{"53217", 1561255},
|
||||
{"53218", 1561255},
|
||||
{"53219", 1561241},
|
||||
{"53220", 1561283},
|
||||
{"53221", 1561283},
|
||||
{"53222", 1561255},
|
||||
{"53223", 1561255},
|
||||
{"53224", 1561255},
|
||||
{"53225", 1561255},
|
||||
{"53226", 1561255},
|
||||
{"53227", 1561587},
|
||||
{"53228", 1561587},
|
||||
{"53233", 1561241},
|
||||
{"53234", 1561241},
|
||||
{"53235", 1561616},
|
||||
{"53237", 1561283},
|
||||
{"53259", 1561615},
|
||||
{"53263", 1561255},
|
||||
{"53267", 1561241},
|
||||
{"53268", 1561615},
|
||||
{"53274", 1561283},
|
||||
{"53278", 1561241},
|
||||
{"53288", 1561615},
|
||||
{"53290", 1561241},
|
||||
{"53293", 1561241},
|
||||
{"53295", 1561241},
|
||||
{"53401", 1561639},
|
||||
{"53402", 1561285},
|
||||
{"53403", 1561639},
|
||||
{"53404", 1561639},
|
||||
{"53405", 1561285},
|
||||
{"53406", 1561624},
|
||||
{"53407", 1561639},
|
||||
{"53408", 1561624},
|
||||
{"53490", 1561285},
|
||||
{"53501", 1561661},
|
||||
{"53502", 1561661},
|
||||
{"53503", 1561661},
|
||||
{"53505", 1561631},
|
||||
{"53506", 1561661},
|
||||
{"53507", 1561661},
|
||||
{"53508", 1561661},
|
||||
{"53511", 1561661},
|
||||
{"53512", 1561661},
|
||||
{"53515", 1561661},
|
||||
{"53517", 1561661},
|
||||
{"53520", 1561661},
|
||||
{"53521", 1561661},
|
||||
{"53522", 1561661},
|
||||
{"53523", 1561284},
|
||||
{"53525", 1561661},
|
||||
{"53526", 1561661},
|
||||
{"53527", 1561661},
|
||||
{"53528", 1561661},
|
||||
{"53529", 1561661},
|
||||
{"53531", 1561661},
|
||||
{"53532", 1561661},
|
||||
{"53533", 1561661},
|
||||
{"53534", 1561661},
|
||||
{"53535", 1561661},
|
||||
{"53536", 1561661},
|
||||
{"53537", 1561661},
|
||||
{"53538", 1561284},
|
||||
{"53542", 1561661},
|
||||
{"53543", 1561661},
|
||||
{"53544", 1561661},
|
||||
{"53545", 1561661},
|
||||
{"53546", 1561631},
|
||||
{"53547", 1561661},
|
||||
{"53548", 1561661},
|
||||
{"53549", 1561284},
|
||||
{"53550", 1561661},
|
||||
{"53551", 1561284},
|
||||
{"53553", 1561661},
|
||||
{"53555", 1561661},
|
||||
{"53557", 1561665},
|
||||
{"53558", 1561661},
|
||||
{"53559", 1561660},
|
||||
{"53560", 1561661},
|
||||
{"53562", 1561661},
|
||||
{"53563", 1561284},
|
||||
{"53565", 1561661},
|
||||
{"53566", 1561661},
|
||||
{"53570", 1561661},
|
||||
{"53571", 1561661},
|
||||
{"53572", 1561661},
|
||||
{"53574", 1561661},
|
||||
{"53575", 1561661},
|
||||
{"53576", 1561661},
|
||||
{"53579", 1561660},
|
||||
{"53580", 1561661},
|
||||
{"53582", 1561661},
|
||||
{"53585", 1561631},
|
||||
{"53589", 1561661},
|
||||
{"53590", 1561661},
|
||||
{"53593", 1561661},
|
||||
{"53594", 1561284},
|
||||
{"53595", 1561661},
|
||||
{"53596", 1561661},
|
||||
{"53597", 1561661},
|
||||
{"53598", 1561661},
|
||||
{"53701", 1561661},
|
||||
{"53702", 1561661},
|
||||
{"53703", 1561661},
|
||||
{"53704", 1561661},
|
||||
{"53705", 1561661},
|
||||
{"53706", 1561661},
|
||||
{"53707", 1561661},
|
||||
{"53708", 1561661},
|
||||
{"53711", 1561661},
|
||||
{"53713", 1561661},
|
||||
{"53714", 1561661},
|
||||
{"53715", 1561661},
|
||||
{"53716", 1561661},
|
||||
{"53717", 1561661},
|
||||
{"53718", 1561661},
|
||||
{"53719", 1561661},
|
||||
{"53725", 1561661},
|
||||
{"53726", 1561661},
|
||||
{"53744", 1561661},
|
||||
{"53774", 1561661},
|
||||
{"53777", 1561661},
|
||||
{"53778", 1561661},
|
||||
{"53779", 1561661},
|
||||
{"53782", 1561661},
|
||||
{"53783", 1561661},
|
||||
{"53784", 1561661},
|
||||
{"53785", 1561661},
|
||||
{"53786", 1561661},
|
||||
{"53788", 1561661},
|
||||
{"53789", 1561661},
|
||||
{"53790", 1561661},
|
||||
{"53791", 1561661},
|
||||
{"53792", 1561661},
|
||||
{"53793", 1561661},
|
||||
{"53794", 1561661},
|
||||
{"53901", 1561660},
|
||||
{"53911", 1561661},
|
||||
{"53916", 1561649},
|
||||
{"53919", 1561649},
|
||||
{"53920", 1561660},
|
||||
{"53922", 1561657},
|
||||
{"53923", 1561660},
|
||||
{"53925", 1561660},
|
||||
{"53926", 1561660},
|
||||
{"53928", 1561660},
|
||||
{"53930", 1561660},
|
||||
{"53931", 1561649},
|
||||
{"53932", 1561660},
|
||||
{"53933", 1561649},
|
||||
{"53935", 1561660},
|
||||
{"53939", 1561649},
|
||||
{"53946", 1561649},
|
||||
{"53947", 1561649},
|
||||
{"53949", 1561660},
|
||||
{"53952", 1561660},
|
||||
{"53953", 1561660},
|
||||
{"53954", 1561660},
|
||||
{"53955", 1561660},
|
||||
{"53956", 1561660},
|
||||
{"53957", 1561649},
|
||||
{"53960", 1561660},
|
||||
{"53963", 1561657},
|
||||
{"53964", 1561660},
|
||||
{"53965", 1561660},
|
||||
{"53969", 1561661},
|
||||
{"54106", 1561657},
|
||||
{"54110", 1561599},
|
||||
{"54113", 1561657},
|
||||
{"54115", 1561657},
|
||||
{"54123", 1561657},
|
||||
{"54126", 1561599},
|
||||
{"54129", 1561657},
|
||||
{"54130", 1561657},
|
||||
{"54131", 1561657},
|
||||
{"54136", 1561657},
|
||||
{"54140", 1561657},
|
||||
{"54152", 1561657},
|
||||
{"54155", 1561657},
|
||||
{"54160", 1561599},
|
||||
{"54162", 1561657},
|
||||
{"54165", 1561657},
|
||||
{"54169", 1561657},
|
||||
{"54170", 1561657},
|
||||
{"54173", 1561657},
|
||||
{"54180", 1561657},
|
||||
{"54207", 1561599},
|
||||
{"54208", 1561599},
|
||||
{"54214", 1561599},
|
||||
{"54215", 1561599},
|
||||
{"54220", 1561599},
|
||||
{"54221", 1561599},
|
||||
{"54227", 1561599},
|
||||
{"54228", 1561599},
|
||||
{"54229", 1561599},
|
||||
{"54230", 1561599},
|
||||
{"54232", 1561599},
|
||||
{"54240", 1561599},
|
||||
{"54241", 1561599},
|
||||
{"54245", 1561599},
|
||||
{"54247", 1561599},
|
||||
{"54301", 1561657},
|
||||
{"54302", 1561599},
|
||||
{"54303", 1561657},
|
||||
{"54304", 1561657},
|
||||
{"54305", 1561657},
|
||||
{"54306", 1561657},
|
||||
{"54307", 1561657},
|
||||
{"54308", 1561657},
|
||||
{"54311", 1561599},
|
||||
{"54313", 1561657},
|
||||
{"54324", 1561657},
|
||||
{"54344", 1561657},
|
||||
{"54901", 1561657},
|
||||
{"54902", 1561657},
|
||||
{"54903", 1561657},
|
||||
{"54904", 1561657},
|
||||
{"54906", 1561657},
|
||||
{"54911", 1561657},
|
||||
{"54912", 1561657},
|
||||
{"54913", 1561657},
|
||||
{"54914", 1561657},
|
||||
{"54915", 1561657},
|
||||
{"54919", 1561657},
|
||||
{"54922", 1561657},
|
||||
{"54923", 1561615},
|
||||
{"54927", 1561657},
|
||||
{"54931", 1561657},
|
||||
{"54932", 1561657},
|
||||
{"54934", 1561649},
|
||||
{"54935", 1561657},
|
||||
{"54936", 1561657},
|
||||
{"54937", 1561657},
|
||||
{"54941", 1561649},
|
||||
{"54942", 1561657},
|
||||
{"54944", 1561657},
|
||||
{"54947", 1561657},
|
||||
{"54952", 1561657},
|
||||
{"54956", 1561657},
|
||||
{"54957", 1561657},
|
||||
{"54960", 1561649},
|
||||
{"54963", 1561657},
|
||||
{"54964", 1561657},
|
||||
{"54968", 1561649},
|
||||
{"54971", 1561649},
|
||||
{"54974", 1561657},
|
||||
{"54979", 1561657},
|
||||
{"54980", 1561649},
|
||||
{"54985", 1561657},
|
||||
{"54986", 1561657},
|
||||
{"60001", 1561631},
|
||||
{"60002", 1561624},
|
||||
{"60010", 1561631},
|
||||
{"60011", 1561631},
|
||||
{"60012", 1561631},
|
||||
{"60013", 1561631},
|
||||
{"60014", 1561631},
|
||||
{"60015", 1561639},
|
||||
{"60020", 1561631},
|
||||
{"60021", 1561631},
|
||||
{"60030", 1561631},
|
||||
{"60031", 1561639},
|
||||
{"60033", 1561631},
|
||||
{"60034", 1561631},
|
||||
{"60035", 1561639},
|
||||
{"60037", 1561639},
|
||||
{"60039", 1561631},
|
||||
{"60040", 1561639},
|
||||
{"60041", 1561631},
|
||||
{"60042", 1561631},
|
||||
{"60044", 1561639},
|
||||
{"60045", 1561639},
|
||||
{"60046", 1561631},
|
||||
{"60047", 1561631},
|
||||
{"60048", 1561639},
|
||||
{"60049", 1561631},
|
||||
{"60050", 1561631},
|
||||
{"60051", 1561631},
|
||||
{"60060", 1561639},
|
||||
{"60061", 1561639},
|
||||
{"60064", 1561639},
|
||||
{"60069", 1561639},
|
||||
{"60071", 1561631},
|
||||
{"60072", 1561631},
|
||||
{"60073", 1561631},
|
||||
{"60075", 1561639},
|
||||
{"60079", 1561639},
|
||||
{"60081", 1561631},
|
||||
{"60083", 1561639},
|
||||
{"60084", 1561631},
|
||||
{"60085", 1561639},
|
||||
{"60086", 1561631},
|
||||
{"60087", 1561639},
|
||||
{"60088", 1561639},
|
||||
{"60089", 1561639},
|
||||
{"60092", 1561631},
|
||||
{"60096", 1561639},
|
||||
{"60097", 1561631},
|
||||
{"60098", 1561631},
|
||||
{"60099", 1561639},
|
||||
{"60102", 1561631},
|
||||
{"60142", 1561631},
|
||||
{"60152", 1561631},
|
||||
{"60156", 1561631},
|
||||
{"60180", 1561631}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
|
||||
public class BranchNameData
|
||||
{
|
||||
[Required]
|
||||
[Range(minimum: 1, maximum: 99999999)]
|
||||
public int ChannelId { get; set; }
|
||||
|
||||
[Required]
|
||||
[RegularExpression("^[a-zA-Z ]*$")]
|
||||
[StringLength(150, ErrorMessage = "Branch Names must be less than 150 characters")]
|
||||
public string BranchName { get; set; }
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class BranchNameConfig : OlaModel
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Required]
|
||||
public List<BranchNameData> BranchNamesByChannelId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
[BsonIgnoreExtraElements]
|
||||
public class DealershipAuthRequest
|
||||
{
|
||||
[Required]
|
||||
public string DealershipCode { get; set; }
|
||||
|
||||
public string DealershipName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string DealershipPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ApplicantLastName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ApplicationId { get; set; }
|
||||
|
||||
public DealershipAuthRequest(string dealershipCode, string dealershipName, string dealershipPassword, string applicantLastName, string applicationId)
|
||||
{
|
||||
this.DealershipCode = dealershipCode;
|
||||
this.DealershipName = dealershipName;
|
||||
this.DealershipPassword = dealershipPassword;
|
||||
this.ApplicantLastName = applicantLastName;
|
||||
this.ApplicationId = applicationId;
|
||||
}
|
||||
|
||||
public static bool ValidateAuthRequest(DealershipAuthRequest dealershipRequest, DealershipAuthRequest actionResponse)
|
||||
{
|
||||
return dealershipRequest?.Equals(actionResponse) ?? false;
|
||||
}
|
||||
|
||||
public override bool Equals(Object authRequest)
|
||||
{
|
||||
if (authRequest is not DealershipAuthRequest request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return string.Equals(DealershipCode, request.DealershipCode, StringComparison.Ordinal) &&
|
||||
string.Equals(DealershipPassword, request.DealershipPassword, StringComparison.Ordinal) &&
|
||||
string.Equals(ApplicantLastName, request.ApplicantLastName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(ApplicationId, request.ApplicationId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(
|
||||
DealershipCode?.ToUpperInvariant(),
|
||||
DealershipPassword,
|
||||
ApplicantLastName?.ToUpperInvariant(),
|
||||
ApplicationId?.ToUpperInvariant()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class DocumentUploadFromLoanApplication
|
||||
{
|
||||
public IFormFile File { get; set; }
|
||||
public string Section { get; set; }
|
||||
public string SectionId { get; set; }
|
||||
public string Description { get; set; }
|
||||
public static bool IsNullOrEmpty(DocumentUploadFromLoanApplication file)
|
||||
{
|
||||
if(file == null || file.File == null || String.IsNullOrEmpty(file.SectionId) || String.IsNullOrEmpty(file.Description))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(file.File.Length == 0 || String.IsNullOrEmpty(file.File.FileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Landmark.OLA.Models.Loan.Enums
|
||||
{
|
||||
public enum LoanErrorCode
|
||||
{
|
||||
No_Error,
|
||||
Error_Updating,
|
||||
Error_Submitting,
|
||||
Error_Deleting
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Landmark.OLA.Models.Loan.Enums
|
||||
{
|
||||
public enum LoanStatus
|
||||
{
|
||||
Application_Error,
|
||||
Vendor_Error,
|
||||
In_Progress,
|
||||
Deleted,
|
||||
Submitted,
|
||||
Decisioned
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Landmark.OLA.Models.Loan.Enums
|
||||
{
|
||||
public enum LoanType
|
||||
{
|
||||
CC_CashBack,
|
||||
CC_Rewards,
|
||||
CC_Student,
|
||||
CC_Secure,
|
||||
EQ_HELOC_Flex,
|
||||
EQ_Home_Equity_Fixed,
|
||||
EQ_Mtg_Refi_Express,
|
||||
EQ_Mfg_Home_With_or_Without_Land,
|
||||
V_Vehicle,
|
||||
V_VehiclePreapproval,
|
||||
M_Boat,
|
||||
P_Powersport,
|
||||
T_TrailerMotorhome,
|
||||
S_Signature,
|
||||
S_Special
|
||||
}
|
||||
|
||||
public enum HMDALoanType
|
||||
{
|
||||
EQ_HELOC_Flex,
|
||||
EQ_Home_Equity_Fixed,
|
||||
EQ_Mtg_Refi_Express,
|
||||
EQ_Mfg_Home_With_or_Without_Land
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class Filter
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public bool SortDesc { get; set; }
|
||||
public string SortField { get; set; }
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public FilterField[] FilterField { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class FilterField
|
||||
{
|
||||
public string Field { get; set; }
|
||||
public string FilterValue { get; set; }
|
||||
public bool Equal { get; set; }
|
||||
public bool NotEqual { get; set; }
|
||||
public bool GreaterThan { get; set; }
|
||||
public bool LessThan { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Individuals;
|
||||
using Landmark.OLA.Models.Assets;
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
[BsonIgnoreExtraElements]
|
||||
public class LoanApplication : OlaModel
|
||||
{
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public LoanType? LoanType { get; set; }
|
||||
|
||||
[Required]
|
||||
//One Billion is an arbitrary limit but I'm pretty confidant that it will be an acceptable upper limit for a long time.
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
//Nullable so that there is no default value
|
||||
public decimal? RequestedAmount { get; set; }
|
||||
|
||||
//Required for Personal Signature loans.
|
||||
[StringLength(100)]
|
||||
public string PurposeOfLoanSignature { get; set; }
|
||||
|
||||
public DateTime Date { get; private set; }
|
||||
|
||||
[Range(minimum: 0, maximum: 1000000000)]
|
||||
//Required for certain loan types. Checked by FluentValidation.
|
||||
public int? TermLengthInMonths { get; set; }
|
||||
|
||||
//these could be ICollection if needed, would impact builders
|
||||
public RealEstate RealEstate { get; set; }
|
||||
public Vehicle Vehicle { get; set; }
|
||||
public ManufacturedHome ManufacturedHome { get; set; }
|
||||
public Guid SecuredCardCollateralId { get; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
//Values must match loan type. Checked by FluentValidation.
|
||||
public LoanPurpose.Purpose? Purpose { get; set; }
|
||||
|
||||
[Required]
|
||||
public Individual PrimaryBorrower { get; set; }
|
||||
|
||||
public CoApplicant CoApplicant { get; set; }
|
||||
|
||||
public string ModFeeAccount { get; set; }
|
||||
|
||||
//3500 is maximum crif note length
|
||||
[StringLength(3500)]
|
||||
[RegularExpression(@"[\w\d\s\.\,\&\n\r\@\-\;\'\!]*")]
|
||||
public string Note { get; set; }
|
||||
|
||||
public string PrimaryContactMethod { get; set; }
|
||||
public string PromoCode { get; set; }
|
||||
public string LoanTitle { get; set; }
|
||||
[StringLength(3450)]
|
||||
public string ClientMessage { get; set; }
|
||||
public DateTime DisclosureDate { get; set; }
|
||||
public string IPAddress { get; set; }
|
||||
|
||||
public IList<LoanApplicationFile> Files { get; set; }
|
||||
|
||||
public LoanApplication()
|
||||
{
|
||||
Date = DateTime.Now;
|
||||
SecuredCardCollateralId = Guid.NewGuid();
|
||||
Files = [];
|
||||
}
|
||||
|
||||
public LoanApplication(DateTime? loanDate = null, Guid? securedCardCollateralId = null, string loanId = null)
|
||||
{
|
||||
Date = loanDate ?? DateTime.Now;
|
||||
SecuredCardCollateralId = securedCardCollateralId ?? Guid.NewGuid();
|
||||
Id = loanId ?? null;
|
||||
Files = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class LoanApplicationFile
|
||||
{
|
||||
public string CosmosLoanId { get; set; }
|
||||
public Guid FileId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Section { get; set; }
|
||||
public string SectionId { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public LoanApplicationFile(string cosmosLoanId, Guid fileId, string name, string section, string sectionId, string description)
|
||||
{
|
||||
CosmosLoanId = cosmosLoanId;
|
||||
FileId = fileId;
|
||||
Name = name;
|
||||
Section = section;
|
||||
SectionId = sectionId;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public string GetBlobPath()
|
||||
{
|
||||
return $"{CosmosLoanId}/{FileId}";
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetMetadata()
|
||||
{
|
||||
var metadata = new Dictionary<string, string>
|
||||
{
|
||||
{ "Name", Name },
|
||||
{ "Section", Section },
|
||||
{ "SectionId", SectionId.ToString() },
|
||||
{ "Description", Description },
|
||||
{ "CosmosLoanId", CosmosLoanId }
|
||||
};
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
[BsonIgnoreExtraElements]
|
||||
public class LoanContainer : OlaModel
|
||||
{
|
||||
public LoanApplication LoanApplication { get; set; }
|
||||
public LoanType LoanType { get; set; }
|
||||
public LoanPage Page { get; set; }
|
||||
public LoanStatus Status { get; set; }
|
||||
public LoanError Error { get; set; }
|
||||
public DateTime Date { get; private set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Branch { get; set; }
|
||||
public string LoanId { get; set; }
|
||||
public string MemberId { get; set; }
|
||||
public string IndividualId { get; set; }
|
||||
|
||||
public LoanContainer()
|
||||
{
|
||||
Date = DateTime.UtcNow; //allows us to have control over date format
|
||||
}
|
||||
|
||||
public LoanContainer(LoanApplication loanApplication, DateTime? loanDate = null, LoanStatus status = LoanStatus.In_Progress)
|
||||
{
|
||||
LoanApplication = loanApplication ?? null;
|
||||
Date = loanDate ?? DateTime.UtcNow;
|
||||
Status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan {
|
||||
public class LoanError
|
||||
{
|
||||
public LoanErrorCode code;
|
||||
public string message;
|
||||
|
||||
public LoanError(LoanErrorCode code, string message)
|
||||
{
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class LoanLimits
|
||||
{
|
||||
private static Limit EqLimit => new() { min = 10000, max = 250000 };
|
||||
private static Limit EqHighLimit => new() { min = 10000, max = 500000 };
|
||||
private static Limit VLimit => new() { min = 250, max = 250000 };
|
||||
private static Limit MfgHome => new() { min = 250, max = 250000 };
|
||||
private static Limit SLimit => new() { min = 250, max = 25000 };
|
||||
private static Limit SpecLimit => new() { min = 250, max = 10000 };
|
||||
|
||||
public static Dictionary<LoanType, Limit> Limits => new()
|
||||
{
|
||||
{ LoanType.EQ_HELOC_Flex, EqHighLimit },
|
||||
{ LoanType.EQ_Mtg_Refi_Express, EqLimit },
|
||||
{ LoanType.EQ_Home_Equity_Fixed, EqHighLimit },
|
||||
{ LoanType.EQ_Mfg_Home_With_or_Without_Land, MfgHome },
|
||||
{ LoanType.V_Vehicle, VLimit },
|
||||
{ LoanType.V_VehiclePreapproval, VLimit },
|
||||
{ LoanType.M_Boat, VLimit },
|
||||
{ LoanType.P_Powersport, VLimit },
|
||||
{ LoanType.T_TrailerMotorhome, MfgHome }, //same limits as mfHome
|
||||
{ LoanType.S_Signature, SLimit },
|
||||
{ LoanType.S_Special, SpecLimit },
|
||||
};
|
||||
|
||||
public static bool IsValidAmountForType(LoanType loanType, decimal requestedAmount)
|
||||
{
|
||||
var limit = Limits[loanType];
|
||||
|
||||
return requestedAmount >= limit.min && requestedAmount <= limit.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class LoanPage
|
||||
{
|
||||
public bool hasPersonal;
|
||||
public bool hasAddress;
|
||||
public bool hasEmployment;
|
||||
public bool hasMemberInfo;
|
||||
public bool hasSpouseInfo;
|
||||
public bool hasTattleSpouseInfo;
|
||||
public bool hasResidenceInfo;
|
||||
public bool hasVehicleCollateral;
|
||||
public bool hasHMDA;
|
||||
public bool hasDisclosure;
|
||||
public bool hasCollateralInfo;
|
||||
public bool hasNote;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
|
||||
public static class LoanPurpose
|
||||
{
|
||||
//Acceptable values taken from CRIF database
|
||||
/*
|
||||
select p.shortname, p.name, p.productid, lp.name, lp.[description],
|
||||
lp.loanpurposeid, lp.isactivefl from v_RptProduct_v001 as p
|
||||
inner join v_RptProductLoanPurpose_v001 as plp
|
||||
on plp.productid = p.productid
|
||||
inner join v_RptLoanPurpose_v001 as lp
|
||||
on lp.loanpurposeid = plp.loanpurposeid
|
||||
order by p.productid
|
||||
*/
|
||||
private static readonly List<Purpose> _ccPurposes = new List<Purpose>
|
||||
{ Purpose.Other, Purpose.Consolidation, Purpose.Reestablish_Credit};
|
||||
|
||||
private static readonly List<Purpose> _eqPurposes = new List<Purpose>
|
||||
{ Purpose.Home_Improvement, Purpose.Home_Purchase, Purpose.Home_Refinance,
|
||||
Purpose.Other, Purpose.Cash_Out_Refinance };
|
||||
|
||||
private static readonly List<Purpose> _mfgHomePurposes = new List<Purpose>
|
||||
{ Purpose.Home_Improvement, Purpose.Purchase_Manufactured_Home, Purpose.Home_Refinance,
|
||||
Purpose.Other, Purpose.Cash_Out_Refinance };
|
||||
|
||||
private static readonly List<Purpose> _vPurposes = new List<Purpose>
|
||||
{ Purpose.Purchase, Purpose.Cash_Out_Refinance, Purpose.Refinance_Vehicle };
|
||||
|
||||
private static readonly List<Purpose> _mPurposes = new List<Purpose>
|
||||
{ Purpose.Purchase, Purpose.Refinance_Vehicle };
|
||||
|
||||
private static readonly List<Purpose> _pPurposes = new List<Purpose>
|
||||
{ Purpose.Purchase, Purpose.Refinance_Vehicle };
|
||||
|
||||
private static readonly List<Purpose> _tPurposes = new List<Purpose>
|
||||
{ Purpose.Purchase, Purpose.Refinance_Vehicle };
|
||||
|
||||
private static readonly List<Purpose> _sPurposes = new List<Purpose>
|
||||
{ Purpose.Other, Purpose.Consolidation};
|
||||
|
||||
|
||||
public static Dictionary<LoanType, List<Purpose>> TypePurposes => new Dictionary<LoanType, List<Purpose>>
|
||||
{
|
||||
{ LoanType.CC_CashBack, _ccPurposes },
|
||||
{ LoanType.CC_Rewards, _ccPurposes },
|
||||
{ LoanType.CC_Student, _ccPurposes },
|
||||
{ LoanType.CC_Secure, _ccPurposes },
|
||||
{ LoanType.EQ_HELOC_Flex, _eqPurposes },
|
||||
{ LoanType.EQ_Mtg_Refi_Express, _eqPurposes },
|
||||
{ LoanType.EQ_Home_Equity_Fixed, _eqPurposes },
|
||||
{ LoanType.EQ_Mfg_Home_With_or_Without_Land, _mfgHomePurposes },
|
||||
{ LoanType.V_Vehicle, _vPurposes },
|
||||
{ LoanType.V_VehiclePreapproval, _vPurposes },
|
||||
{ LoanType.M_Boat, _mPurposes },
|
||||
{ LoanType.P_Powersport, _pPurposes },
|
||||
{ LoanType.T_TrailerMotorhome, _tPurposes },
|
||||
{ LoanType.S_Signature, _sPurposes },
|
||||
{ LoanType.S_Special, _sPurposes },
|
||||
};
|
||||
|
||||
public static bool IsValidPurposeForType(LoanType? type, Purpose? purpose)
|
||||
{
|
||||
if (!type.HasValue)
|
||||
throw new ArgumentException("LoanType cannot be null");
|
||||
|
||||
if (!purpose.HasValue)
|
||||
throw new ArgumentException("Purpose cannot be null");
|
||||
|
||||
return IsValidPurposeForType(type.Value, purpose.Value);
|
||||
}
|
||||
|
||||
public static bool IsValidPurposeForType(LoanType type, Purpose purpose)
|
||||
{
|
||||
return TypePurposes[type].Contains(purpose);
|
||||
}
|
||||
|
||||
|
||||
//Enum names and values based on CRIF's v_RptProductLoanPurpose_v001 table
|
||||
//joined with v_RptProductLoanPurpose_v001
|
||||
//to find purposes that are active and in use
|
||||
/*
|
||||
select distinct lp.loanpurposeid, lp.name,lp.[description]
|
||||
from v_RptLoanPurpose_v001 as lp
|
||||
inner join v_RptProductLoanPurpose_v001 as plp
|
||||
on lp.loanpurposeid = plp.loanpurposeid
|
||||
where isactivefl = 1
|
||||
*/
|
||||
public enum Purpose
|
||||
{
|
||||
Home_Improvement = 3,
|
||||
Home_Purchase = 4,
|
||||
Home_Refinance = 5,
|
||||
Purchase_Boat = 8,
|
||||
Purchase = 12,
|
||||
Other = 149,
|
||||
Refinance = 141,
|
||||
Dental_Expense = 142,
|
||||
Medical_Expense = 143,
|
||||
Cash_Out_Refinance = 144,
|
||||
Refinance_Vehicle = 146,
|
||||
Consolidation = 148,
|
||||
Reestablish_Credit = 150,
|
||||
Purchase_Manufactured_Home = 151,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
using Landmark.OLA.Models.Individuals;
|
||||
using Landmark.OLA.Models.Individuals.Enums;
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
using Landmark.OLA.Models.Residences;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public class MockApplication
|
||||
{
|
||||
public readonly LoanApplication LoanApplication =
|
||||
new(DateTime.Parse("2018-02-21T16:52:47.62"), new Guid("9ed5a50f-279c-49b0-b5b4-d5b614e42501"))
|
||||
{
|
||||
LoanType = LoanType.CC_Secure,
|
||||
RequestedAmount = 1320,
|
||||
PrimaryContactMethod = "1234567890",
|
||||
Purpose = LoanPurpose.Purpose.Other,
|
||||
PrimaryBorrower = new Individual(new Guid("8066fa2a-16ba-4148-b28c-2926ba263c5f"))
|
||||
{
|
||||
FirstName = "Jane",
|
||||
MiddleInitial = "A",
|
||||
LastName = "Doe",
|
||||
DOB = new DateTime(2018, 7, 9),
|
||||
Phone = "1235555555",
|
||||
PhoneType = PhoneType.Home,
|
||||
SSN = "000090001",
|
||||
Email = "[email protected]",
|
||||
IsMarried = false,
|
||||
Language = LanguageOption.English,
|
||||
Address = new CurrentResidenceAddress(new Guid("bf7e502a-9830-43c6-b603-1a32294c7e1a"))
|
||||
{
|
||||
Address1 = "123 Main St",
|
||||
City = "New Berlin",
|
||||
State = StateOption.WI,
|
||||
Country = CountryOption.UnitedStates,
|
||||
PostalCode = "53151",
|
||||
MonthsResided = 24,
|
||||
ResidenceType = ResidenceTypeOption.Own,
|
||||
MonthlyPayment = 100,
|
||||
},
|
||||
MailingAddress = new Address(new Guid("f84db562-36a0-4039-9ce5-346a2ba6899b"))
|
||||
{
|
||||
Address1 = "321 Main St",
|
||||
City = "New Berlin",
|
||||
State = StateOption.WI,
|
||||
Country = CountryOption.UnitedStates,
|
||||
PostalCode = "53151"
|
||||
},
|
||||
Employers =
|
||||
[
|
||||
new Employer(employerId: new Guid("eee4efa3-327f-4ce5-9e8b-8c3e7a865fff"))
|
||||
{
|
||||
Name = "College Pro Painters",
|
||||
Title = "tester",
|
||||
WorkPhone = "9992245555",
|
||||
MonthsEmployed = 60,
|
||||
StartDate = new DateTime(2017, 01, 01),
|
||||
CurrentEmployer = true,
|
||||
EmploymentType = EmploymentTypeOption.FullTime,
|
||||
Language = LanguageOption.English,
|
||||
Income = new Income
|
||||
{
|
||||
IncomeTypeCD = IncomeType.Salary,
|
||||
SourceName = "Salary",
|
||||
AmountPerPeriod = 5000,
|
||||
Frequency = Frequency.Annually,
|
||||
IncomeStatus = IncomeStatus.IncomeStatus_Included
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
CoApplicant = new CoApplicant(new Guid("ba99037f-df92-46d4-a477-ffecea1f4837"))
|
||||
{
|
||||
FirstName = "John",
|
||||
LastName = "Smith",
|
||||
DOB = new DateTime(2018, 7, 25),
|
||||
SSN = "111221111",
|
||||
Phone = "5555550983",
|
||||
PhoneType = PhoneType.Mobile,
|
||||
Email = "[email protected]",
|
||||
IsMarried = true,
|
||||
IsMarriedToPrimaryBorrower = false,
|
||||
Language = LanguageOption.English,
|
||||
Address = new CurrentResidenceAddress(new Guid("ae795a5f-1ea3-4186-b4b8-e9f9b81e3059"))
|
||||
{
|
||||
Address1 = "339 1st St",
|
||||
Address2 = "#76",
|
||||
City = "Milwaukee",
|
||||
State = StateOption.IL,
|
||||
Country = CountryOption.UnitedStates,
|
||||
PostalCode = "53202",
|
||||
ResidenceType = ResidenceTypeOption.Rent,
|
||||
MonthsResided = 12,
|
||||
MonthlyPayment = 100,
|
||||
},
|
||||
PreviousAddresses =
|
||||
[
|
||||
new ResidenceAddress(new Guid("e7e881f0-d371-4371-8022-cce4f700e6d6"))
|
||||
{
|
||||
Address1 = "339 1st St",
|
||||
Address2 = "#80",
|
||||
City = "Milwaukee",
|
||||
State = StateOption.WI,
|
||||
Country = CountryOption.UnitedStates,
|
||||
PostalCode = "53202",
|
||||
ResidenceType = ResidenceTypeOption.Rent,
|
||||
MonthsResided = 28,
|
||||
},
|
||||
],
|
||||
Employers =
|
||||
[
|
||||
new Employer(employerId: new Guid("e8436884-0e6c-4144-8de5-1f44c7936a23"))
|
||||
{
|
||||
StartDate = new DateTime(2015, 01, 01),
|
||||
Name = "Chocolate Teapot Manfuacturing Inc",
|
||||
EmploymentType = EmploymentTypeOption.PartTime,
|
||||
Title = "Lead Teapot Assembler",
|
||||
WorkPhone = "5556783241",
|
||||
CurrentEmployer = true,
|
||||
Language = LanguageOption.English,
|
||||
Income = new Income
|
||||
{
|
||||
IncomeTypeCD = IncomeType.Salary,
|
||||
SourceName = "Work",
|
||||
HoursPerWeek = 24,
|
||||
AmountPerPeriod = 10,
|
||||
Frequency = Frequency.Hourly,
|
||||
IncomeStatus = IncomeStatus.IncomeStatus_Included
|
||||
},
|
||||
},
|
||||
new Employer(employerId: new Guid("58d25f44-1260-428d-ac2c-d8804b06ed66"))
|
||||
{
|
||||
StartDate = new DateTime(2015, 01, 01),
|
||||
Name = "Vanilla Teapot Manfuacturing Coop",
|
||||
EmploymentType = EmploymentTypeOption.PartTime,
|
||||
Title = "Junior Teapot Assembler",
|
||||
WorkPhone = "5559572318",
|
||||
CurrentEmployer = true,
|
||||
Language = LanguageOption.English,
|
||||
Income = new Income
|
||||
{
|
||||
IncomeTypeCD = IncomeType.Salary,
|
||||
SourceName = "Work",
|
||||
HoursPerWeek = 32,
|
||||
AmountPerPeriod = 50,
|
||||
Frequency = Frequency.Hourly,
|
||||
IncomeStatus = IncomeStatus.IncomeStatus_Included
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan
|
||||
{
|
||||
public static class TermLengths
|
||||
{
|
||||
//All lengths are in months.
|
||||
public class UiTermOption
|
||||
{
|
||||
[Required]
|
||||
public int value;
|
||||
|
||||
[Required]
|
||||
public string desc;
|
||||
}
|
||||
|
||||
public static List<UiTermOption> AvailableTermOptions =
|
||||
[
|
||||
new UiTermOption {value = 12, desc = "12 months / 1 year"},
|
||||
new UiTermOption {value = 24, desc = "24 months / 2 years"},
|
||||
new UiTermOption {value = 36, desc = "36 months / 3 years"},
|
||||
new UiTermOption {value = 48, desc = "48 months / 4 years"},
|
||||
new UiTermOption {value = 60, desc = "60 months / 5 years"},
|
||||
new UiTermOption {value = 63, desc = "63 months"},
|
||||
new UiTermOption {value = 72, desc = "72 months / 6 years"},
|
||||
new UiTermOption {value = 75, desc = "75 months"},
|
||||
new UiTermOption {value = 84, desc = "84 months / 7 years"},
|
||||
new UiTermOption {value = 96, desc = "96 months / 8 years"},
|
||||
new UiTermOption {value = 120, desc = "120 months / 10 years"},
|
||||
new UiTermOption {value = 144, desc = "144 months / 12 years"},
|
||||
new UiTermOption {value = 180, desc = "180 months / 15 years"},
|
||||
new UiTermOption {value = 240, desc = "240 months / 20 years"}
|
||||
];
|
||||
|
||||
public static List<UiTermOption> AvailableTermOptionsWithoutYears =
|
||||
[
|
||||
new UiTermOption {value = 12, desc = "12 months"},
|
||||
new UiTermOption {value = 24, desc = "24 months"},
|
||||
new UiTermOption {value = 36, desc = "36 months"},
|
||||
new UiTermOption {value = 48, desc = "48 months"},
|
||||
new UiTermOption {value = 60, desc = "60 months"},
|
||||
new UiTermOption {value = 63, desc = "63 months"},
|
||||
new UiTermOption {value = 72, desc = "72 months"},
|
||||
new UiTermOption {value = 75, desc = "75 months"},
|
||||
new UiTermOption {value = 84, desc = "84 months"},
|
||||
new UiTermOption {value = 96, desc = "96 months"},
|
||||
new UiTermOption {value = 120, desc = "120 months"},
|
||||
new UiTermOption {value = 144, desc = "144 months"},
|
||||
new UiTermOption {value = 180, desc = "180 months"},
|
||||
new UiTermOption {value = 240, desc = "240 months"}
|
||||
];
|
||||
|
||||
public static Dictionary<decimal, List<int>> BoatLoansByAmount = new()
|
||||
{
|
||||
//Loan amount can be greater than or equal to the amount listed here.
|
||||
{0,
|
||||
new List<int>
|
||||
{
|
||||
48
|
||||
}
|
||||
},
|
||||
{10000,
|
||||
new List<int>
|
||||
{
|
||||
48,
|
||||
63,
|
||||
72,
|
||||
84
|
||||
}
|
||||
},
|
||||
{20000,
|
||||
new List<int>
|
||||
{
|
||||
48,
|
||||
63,
|
||||
72,
|
||||
84,
|
||||
120,
|
||||
180,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
public static Dictionary<decimal, List<int>> MfdHomeAndRvByAmount = new()
|
||||
{
|
||||
//Loan amount can be greater than or equal to the amount listed here.
|
||||
{0,
|
||||
new List<int>
|
||||
{
|
||||
48
|
||||
}
|
||||
},
|
||||
{10000,
|
||||
new List<int>
|
||||
{
|
||||
48,
|
||||
60,
|
||||
72,
|
||||
84
|
||||
}
|
||||
},
|
||||
{20000,
|
||||
new List<int>
|
||||
{
|
||||
48,
|
||||
60,
|
||||
72,
|
||||
84,
|
||||
120,
|
||||
180
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
public static List<int> Special =
|
||||
[
|
||||
36
|
||||
];
|
||||
|
||||
public static List<int> HomeEquity =
|
||||
[
|
||||
60,
|
||||
84,
|
||||
120,
|
||||
180,
|
||||
240,
|
||||
];
|
||||
|
||||
public static List<int> MortgageRefiExpress =
|
||||
[
|
||||
96,
|
||||
120,
|
||||
144,
|
||||
];
|
||||
|
||||
public static List<int> VehiclePreApproval =
|
||||
[
|
||||
36,
|
||||
48,
|
||||
63,
|
||||
75,
|
||||
];
|
||||
|
||||
public static Dictionary<int, List<int>> TermsByYear = new()
|
||||
{
|
||||
{2023,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48,
|
||||
63,
|
||||
75,
|
||||
84
|
||||
}
|
||||
},
|
||||
{2022,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48,
|
||||
63,
|
||||
75
|
||||
}
|
||||
},
|
||||
{2021,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48,
|
||||
63
|
||||
}
|
||||
},
|
||||
{2020,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static Dictionary<decimal, List<int>> PowersportByAmount = new()
|
||||
{
|
||||
//Loan amount can be greater than or equal to the amount listed here.
|
||||
{0,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48,
|
||||
63
|
||||
}
|
||||
},
|
||||
{10000,
|
||||
new List<int>
|
||||
{
|
||||
36,
|
||||
48,
|
||||
63,
|
||||
72
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static Dictionary<decimal, List<int>> SignatureByAmount = new()
|
||||
{
|
||||
//Loan amount can be greater than or equal to the amount listed here.
|
||||
{0,
|
||||
new List<int>
|
||||
{
|
||||
12,
|
||||
24,
|
||||
36
|
||||
}
|
||||
},
|
||||
{5000,
|
||||
new List<int>
|
||||
{
|
||||
48
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using FluentValidation;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan.Validators
|
||||
{
|
||||
public class BranchConfigValidator : AbstractValidator<BranchConfig>
|
||||
{
|
||||
public BranchConfigValidator() {
|
||||
RuleFor(x => x.BranchCodesByZip).Must((x, branchCodes) => NoZipcodeDuplicates(branchCodes))
|
||||
.WithMessage("You cannot have duplicate zipcodes.");
|
||||
}
|
||||
|
||||
private static bool NoZipcodeDuplicates(List<BranchData> branchData)
|
||||
{
|
||||
var matchFound = false;
|
||||
foreach (var eachMapping in branchData)
|
||||
{
|
||||
int matchCount = 0;
|
||||
foreach (var otherMapping in branchData)
|
||||
{
|
||||
if( otherMapping.Zipcode == eachMapping.Zipcode)
|
||||
{
|
||||
matchCount++;
|
||||
}
|
||||
}
|
||||
if( matchCount > 1 )
|
||||
{
|
||||
matchFound = true;
|
||||
}
|
||||
}
|
||||
return !matchFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
using FluentValidation;
|
||||
using Landmark.OLA.Models.Assets.Enums;
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Loan.Validators
|
||||
{
|
||||
class VehicleValidator : AbstractValidator<LoanApplication>
|
||||
{
|
||||
private readonly Dictionary<VehicleType, List<LoanType>> _VehiclesToLoans = new()
|
||||
{
|
||||
{ VehicleType.Automobile,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.V_Vehicle,
|
||||
LoanType.V_VehiclePreapproval,
|
||||
}
|
||||
},
|
||||
{ VehicleType.Motorcycle,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.V_Vehicle,
|
||||
LoanType.V_VehiclePreapproval,
|
||||
}
|
||||
},
|
||||
{ VehicleType.Boat,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.M_Boat,
|
||||
}
|
||||
},
|
||||
{ VehicleType.ATV,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.P_Powersport,
|
||||
}
|
||||
},
|
||||
{ VehicleType.PWC,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.P_Powersport,
|
||||
}
|
||||
},
|
||||
{ VehicleType.RecreationalVehicle,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.P_Powersport,
|
||||
}
|
||||
},
|
||||
{ VehicleType.Snowmobile,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.P_Powersport,
|
||||
}
|
||||
},
|
||||
{ VehicleType.TrailerMotorHome,
|
||||
new List<LoanType>
|
||||
{
|
||||
LoanType.T_TrailerMotorhome,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
public VehicleValidator()
|
||||
{
|
||||
RuleFor(x => x.Vehicle.Type).Must((x, vehicleType) => IsAllowedVehicleTypeForLoan(x.LoanType.Value, vehicleType.Value))
|
||||
.WithMessage($"Vehicle type is not allowed for this loan type.");
|
||||
}
|
||||
|
||||
private bool IsAllowedVehicleTypeForLoan(LoanType loanType, VehicleType vehicleType)
|
||||
{
|
||||
if (_VehiclesToLoans.TryGetValue(vehicleType, out var loanTypeList))
|
||||
{
|
||||
return loanTypeList.Contains(loanType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public static class ModelValidator
|
||||
{
|
||||
public static bool TryValidate(Object data)
|
||||
{
|
||||
try
|
||||
{
|
||||
Validate(data);
|
||||
return true;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO #231: This will not validate properties of the object passed in - a valid Individual with an invalid Address will be deemed valid.
|
||||
//Controllers do validate all properties.
|
||||
public static void Validate(Object data)
|
||||
{
|
||||
//https://stackoverflow.com/a/3783328/266594
|
||||
var validationResults = new List<ValidationResult>(); //Can use this to inform user of exact problems
|
||||
var vc = new ValidationContext(data, null, null);
|
||||
var isValid = Validator.TryValidateObject(data, vc, validationResults, true);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
string validationProblems = String.Join('\n', validationResults);
|
||||
throw new ArgumentException($"{data.GetType()} data is invalid: \n {validationProblems}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public class OlaModel
|
||||
{
|
||||
//This will either hold an ToString'ed int or a 36-char long GUID
|
||||
[StringLength(36)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public Guid GetOrCreateGuid()
|
||||
{
|
||||
if (Guid.TryParse(Id, out Guid parsedGuid))
|
||||
{
|
||||
return parsedGuid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newGuid = Guid.NewGuid();
|
||||
Id = newGuid.ToString();
|
||||
return newGuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class AuthTokenResponse
|
||||
{
|
||||
public AuthTokenStatus Status { get; set; }
|
||||
public AuthToken Token { get; set; }
|
||||
}
|
||||
|
||||
public class AuthTokenStatus
|
||||
{
|
||||
public string StatusMessage { get; set; }
|
||||
public AuthTokenError ErrorInfo { get; set; }
|
||||
}
|
||||
|
||||
public class AuthTokenError
|
||||
{
|
||||
public string ErrorType { get; set; }
|
||||
public string ErrorCode { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
|
||||
public class AuthToken
|
||||
{
|
||||
public string DiFiid { get; set; }
|
||||
public string DiFicustomer { get; set; }
|
||||
public string DiMemberNumber { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
public string ExpiresIn { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
public string RefreshTokenExpiresIn { get; set; }
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class AuthenticationStatement
|
||||
{
|
||||
public string SessionIndex { get; set; }
|
||||
public string AuthContextClassRef { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public static readonly string TokenPayload = "TokenPayload";
|
||||
public static readonly string TokenAuthorization = "TokenAuthorization";
|
||||
public static readonly string TokenAuthorizationString = "Member";
|
||||
public static readonly string TokenNonAuthorizationString = "None";
|
||||
public static readonly string MemberID = "MemberID";
|
||||
public static readonly string IndividualID = "IndividualID";
|
||||
public static readonly string FI_ID = "GroupID";
|
||||
public static readonly string IsTwoFactor = "IsTwoFactor";
|
||||
public static readonly string InProgressLoanId = "InProgressLoanId";
|
||||
public static readonly string TokenPrefix = "Bearer "; // space is intentional
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class DISAMLAssertion
|
||||
{
|
||||
public string NameID { get; set; }
|
||||
public string Recipient { get; set; }
|
||||
public SAMLConditions Conditions { get; set; }
|
||||
public AuthenticationStatement AuthnStatement { get; set; }
|
||||
public string MemberID { get; set; }
|
||||
public string FI_ID { get; set; }
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class OnlineBankingFormData
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string DI_TID { get; set; }
|
||||
public string DI_FIID { get; set; }
|
||||
public string GrantType = "password";
|
||||
public string Authorization { get; set; }
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class OnlineBankingToken
|
||||
{
|
||||
public string MemberID { get; set; }
|
||||
public string IndividualID { get; set; }
|
||||
public string FI_ID { get; set; }
|
||||
public string LoanId { get; set; }
|
||||
public bool IsTwoFactor { get; set; }
|
||||
|
||||
public OnlineBankingToken(DISAMLAssertion assertion, string loanId, bool isTwoFactor)
|
||||
{
|
||||
this.MemberID = assertion?.MemberID;
|
||||
this.FI_ID = assertion.FI_ID;
|
||||
this.LoanId = loanId;
|
||||
this.IsTwoFactor = isTwoFactor;
|
||||
}
|
||||
|
||||
public OnlineBankingToken() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
namespace Landmark.OLA.Models.OnlineBanking
|
||||
{
|
||||
public class SAMLConditions
|
||||
{
|
||||
public DateTime NotBefore { get; set; }
|
||||
public DateTime NotAfter { get; set; }
|
||||
public string Audience { get; set; }
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models.Residences
|
||||
{
|
||||
public class CurrentResidenceAddress : ResidenceAddress, ICurrentResidenceAddress
|
||||
{
|
||||
[Required]
|
||||
//Slightly arbitrary values that should be safe
|
||||
[Range(0, 1000000000)]
|
||||
//Nullable so that there is no default value
|
||||
public decimal? MonthlyPayment { get; set; }
|
||||
|
||||
public CurrentResidenceAddress()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public CurrentResidenceAddress(Guid id)
|
||||
: base(id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Landmark.OLA.Models.Residences
|
||||
{
|
||||
public interface ICurrentResidenceAddress : IResidenceAddress
|
||||
{
|
||||
//Ten Billion is an arbitrary limit but I'm pretty confidant that it will be an acceptable upper limit for a long time.
|
||||
[Range(minimum: 0, maximum: 10000000000)]
|
||||
decimal? MonthlyPayment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Residences
|
||||
{
|
||||
public interface IResidenceAddress : IAddress
|
||||
{
|
||||
int? MonthsResided { get; set; }
|
||||
|
||||
ResidenceTypeOption? ResidenceType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models.Residences
|
||||
{
|
||||
public class ResidenceAddress : Address, IResidenceAddress
|
||||
{
|
||||
[Required]
|
||||
//TODO #227: Should MonthsResided be at least 1? What if you just moved yesterday?
|
||||
//The current front end offers an upper limit of 10 years, 11 months.
|
||||
[Range(0, 130)]
|
||||
//Nullable so that there is no default value. Could change if a value of at least 1 is required
|
||||
public int? MonthsResided { get; set; }
|
||||
|
||||
[Required]
|
||||
//Nullable so that there is no default value
|
||||
public ResidenceTypeOption? ResidenceType { get; set; }
|
||||
|
||||
public ResidenceAddress()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ResidenceAddress(Guid id)
|
||||
: base(id)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Landmark.OLA.Models.Addresses.Enums;
|
||||
using Landmark.OLA.Models.Loan.Enums;
|
||||
|
||||
namespace Landmark.OLA.Models
|
||||
{
|
||||
public static class ValidationConstants
|
||||
{
|
||||
public static readonly List<StateOption> StatesRequiringSpouseInformation = new List<StateOption>
|
||||
{
|
||||
//This is not an exhaustive list of community property states
|
||||
StateOption.WI,
|
||||
};
|
||||
|
||||
public static readonly List<LoanType> HmdaLoans = new List<LoanType>
|
||||
{
|
||||
LoanType.EQ_HELOC_Flex,
|
||||
LoanType.EQ_Home_Equity_Fixed,
|
||||
LoanType.EQ_Mtg_Refi_Express,
|
||||
LoanType.EQ_Mfg_Home_With_or_Without_Land,
|
||||
};
|
||||
|
||||
public static readonly List<LoanType> LoansNotRequiringTerm = new List<LoanType>
|
||||
{
|
||||
LoanType.CC_CashBack,
|
||||
LoanType.CC_Rewards,
|
||||
LoanType.CC_Student,
|
||||
LoanType.CC_Secure,
|
||||
LoanType.EQ_HELOC_Flex,
|
||||
};
|
||||
|
||||
public static readonly List<LoanType> LoansRequiringVehicle = new List<LoanType>
|
||||
{
|
||||
LoanType.V_Vehicle,
|
||||
};
|
||||
|
||||
public static readonly List<LoanType> LoansRequiringRealEstate = new List<LoanType>
|
||||
{
|
||||
LoanType.EQ_HELOC_Flex,
|
||||
LoanType.EQ_Home_Equity_Fixed,
|
||||
LoanType.EQ_Mtg_Refi_Express,
|
||||
};
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using Landmark.OLA.Models.Addresses;
|
||||
|
||||
namespace Landmark.OLA.Models.Vendor
|
||||
{
|
||||
public interface IUspsVendorClient
|
||||
{
|
||||
Task<Address> CompareAddress(Address address);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user